SWF_Render.cpp 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "../renderer/tr_local.h"
  23. idCVar swf_timescale( "swf_timescale", "1", CVAR_FLOAT, "timescale for swf files" );
  24. idCVar swf_stopat( "swf_stopat", "0", CVAR_FLOAT, "stop at a specific frame" );
  25. idCVar swf_titleSafe( "swf_titleSafe", "0.005", CVAR_FLOAT, "space between UI elements and screen edge", 0.0f, 0.075f );
  26. idCVar swf_forceAlpha( "swf_forceAlpha", "0", CVAR_FLOAT, "force an alpha value on all elements, useful to show invisible animating elements", 0.0f, 1.0f );
  27. extern idCVar swf_textStrokeSize;
  28. extern idCVar swf_textStrokeSizeGlyphSpacer;
  29. extern idCVar in_useJoystick;
  30. #define ALPHA_EPSILON 0.001f
  31. #define STENCIL_DECR -1
  32. #define STENCIL_INCR -2
  33. /*
  34. ========================
  35. idSWF::DrawStretchPic
  36. ========================
  37. */
  38. void idSWF::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial *material ) {
  39. renderSystem->DrawStretchPic( x * scaleToVirtual.x, y * scaleToVirtual.y, w * scaleToVirtual.x, h * scaleToVirtual.y, s1, t1, s2, t2, material );
  40. }
  41. /*
  42. ========================
  43. idSWF::DrawStretchPic
  44. ========================
  45. */
  46. void idSWF::DrawStretchPic( const idVec4 & topLeft, const idVec4 & topRight, const idVec4 & bottomRight, const idVec4 & bottomLeft, const idMaterial * material ) {
  47. renderSystem->DrawStretchPic(
  48. idVec4( topLeft.x * scaleToVirtual.x, topLeft.y * scaleToVirtual.y, topLeft.z, topLeft.w ),
  49. idVec4( topRight.x * scaleToVirtual.x, topRight.y * scaleToVirtual.y, topRight.z, topRight.w ),
  50. idVec4( bottomRight.x * scaleToVirtual.x, bottomRight.y * scaleToVirtual.y, bottomRight.z, bottomRight.w ),
  51. idVec4( bottomLeft.x * scaleToVirtual.x, bottomLeft.y * scaleToVirtual.y, bottomLeft.z, bottomLeft.w ),
  52. material );
  53. }
  54. /*
  55. ========================
  56. idSWF::Render
  57. ========================
  58. */
  59. void idSWF::Render( idRenderSystem * gui, int time, bool isSplitscreen ) {
  60. if ( !IsLoaded() ) {
  61. return;
  62. }
  63. if ( !IsActive() ) {
  64. return;
  65. }
  66. if ( swf_stopat.GetInteger() > 0 ) {
  67. if ( mainspriteInstance->currentFrame == swf_stopat.GetInteger() ) {
  68. swf_timescale.SetFloat( 0.0f );
  69. }
  70. }
  71. int currentTime = Sys_Milliseconds();
  72. int framesToRun = 0;
  73. if ( paused ) {
  74. lastRenderTime = currentTime;
  75. }
  76. if ( swf_timescale.GetFloat() > 0.0f ) {
  77. if ( lastRenderTime == 0 ) {
  78. lastRenderTime = currentTime;
  79. framesToRun = 1;
  80. } else {
  81. float deltaTime = ( currentTime - lastRenderTime );
  82. float fr = ( (float)frameRate / 256.0f ) * swf_timescale.GetFloat();
  83. framesToRun = idMath::Ftoi( ( fr * deltaTime ) / 1000.0f );
  84. lastRenderTime += ( framesToRun * ( 1000.0f / fr ) );
  85. if ( framesToRun > 10 ) {
  86. framesToRun = 10;
  87. }
  88. }
  89. for ( int i = 0; i < framesToRun; i++ ) {
  90. mainspriteInstance->Run();
  91. mainspriteInstance->RunActions();
  92. }
  93. }
  94. const float pixelAspect = renderSystem->GetPixelAspect();
  95. const float sysWidth = renderSystem->GetWidth() * ( pixelAspect > 1.0f ? pixelAspect : 1.0f );
  96. const float sysHeight = renderSystem->GetHeight() / ( pixelAspect < 1.0f ? pixelAspect : 1.0f );
  97. float scale = swfScale * sysHeight / (float)frameHeight;
  98. swfRenderState_t renderState;
  99. renderState.stereoDepth = (stereoDepthType_t)mainspriteInstance->GetStereoDepth();
  100. renderState.matrix.xx = scale;
  101. renderState.matrix.yy = scale;
  102. renderState.matrix.tx = 0.5f * ( sysWidth - ( frameWidth * scale ) );
  103. renderState.matrix.ty = 0.5f * ( sysHeight - ( frameHeight * scale ) );
  104. renderBorder = renderState.matrix.tx / scale;
  105. scaleToVirtual.Set( (float)SCREEN_WIDTH / sysWidth, (float)SCREEN_HEIGHT / sysHeight );
  106. RenderSprite( gui, mainspriteInstance, renderState, time, isSplitscreen );
  107. if ( blackbars ) {
  108. float barWidth = renderState.matrix.tx + 0.5f;
  109. float barHeight = renderState.matrix.ty + 0.5f;
  110. if ( barWidth > 0.0f ) {
  111. gui->SetColor( idVec4( 0.0f, 0.0f, 0.0f, 1.0f ) );
  112. DrawStretchPic( 0.0f, 0.0f, barWidth, sysHeight, 0, 0, 1, 1, white );
  113. DrawStretchPic( sysWidth - barWidth, 0.0f, barWidth, sysHeight, 0, 0, 1, 1, white );
  114. }
  115. if ( barHeight > 0.0f ) {
  116. gui->SetColor( idVec4( 0.0f, 0.0f, 0.0f, 1.0f ) );
  117. DrawStretchPic( 0.0f, 0.0f, sysWidth, barHeight, 0, 0, 1, 1, white );
  118. DrawStretchPic( 0.0f, sysHeight - barHeight, sysWidth, barHeight, 0, 0, 1, 1, white );
  119. }
  120. }
  121. if ( isMouseInClientArea && ( mouseEnabled && useMouse ) && ( InhibitControl() || ( !InhibitControl() && !useInhibtControl ) ) ) {
  122. gui->SetGLState( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
  123. gui->SetColor( idVec4( 1.0f, 1.0f, 1.0f, 1.0f ) );
  124. idVec2 mouse = renderState.matrix.Transform( idVec2( mouseX - 1, mouseY - 2 ) );
  125. //idSWFScriptObject * hitObject = HitTest( mainspriteInstance, swfRenderState_t(), mouseX, mouseY, NULL );
  126. if ( !hasHitObject ) { //hitObject == NULL ) {
  127. DrawStretchPic( mouse.x, mouse.y, 32.0f, 32.0f, 0, 0, 1, 1, guiCursor_arrow );
  128. } else {
  129. DrawStretchPic( mouse.x, mouse.y, 32.0f, 32.0f, 0, 0, 1, 1, guiCursor_hand );
  130. }
  131. }
  132. // restore the GL State
  133. gui->SetGLState( 0 );
  134. }
  135. /*
  136. ========================
  137. idSWF::RenderMask
  138. ========================
  139. */
  140. void idSWF::RenderMask( idRenderSystem * gui, const swfDisplayEntry_t * mask, const swfRenderState_t & renderState, const int stencilMode ) {
  141. swfRenderState_t renderState2;
  142. renderState2.stereoDepth = renderState.stereoDepth;
  143. renderState2.matrix = mask->matrix.Multiply( renderState.matrix );
  144. renderState2.cxf = mask->cxf.Multiply( renderState.cxf );
  145. renderState2.ratio = mask->ratio;
  146. renderState2.material = guiSolid;
  147. renderState2.activeMasks = stencilMode;
  148. idSWFDictionaryEntry & entry = dictionary[ mask->characterID ];
  149. if ( entry.type == SWF_DICT_SHAPE ) {
  150. RenderShape( gui, entry.shape, renderState2 );
  151. } else if ( entry.type == SWF_DICT_MORPH ) {
  152. RenderMorphShape( gui, entry.shape, renderState2 );
  153. }
  154. }
  155. /*
  156. ========================
  157. idSWF::RenderSprite
  158. ========================
  159. */
  160. void idSWF::RenderSprite( idRenderSystem * gui, idSWFSpriteInstance * spriteInstance, const swfRenderState_t & renderState, int time, bool isSplitscreen ) {
  161. if ( spriteInstance == NULL ) {
  162. idLib::Warning( "%s: RenderSprite: spriteInstance == NULL", filename.c_str() );
  163. return;
  164. }
  165. if ( !spriteInstance->isVisible ) {
  166. return;
  167. }
  168. if ( ( ( renderState.cxf.mul.w + renderState.cxf.add.w ) <= ALPHA_EPSILON ) && ( swf_forceAlpha.GetFloat() <= 0.0f ) ) {
  169. return;
  170. }
  171. idStaticList<const swfDisplayEntry_t *, 256> activeMasks;
  172. for ( int i = 0; i < spriteInstance->displayList.Num(); i++ ) {
  173. const swfDisplayEntry_t & display = spriteInstance->displayList[i];
  174. for ( int j = 0; j < activeMasks.Num(); j++ ) {
  175. const swfDisplayEntry_t * mask = activeMasks[ j ];
  176. if ( display.depth > mask->clipDepth ) {
  177. RenderMask( gui, mask, renderState, STENCIL_DECR );
  178. activeMasks.RemoveIndexFast( j );
  179. }
  180. }
  181. if ( display.clipDepth > 0 ) {
  182. activeMasks.Append( &display );
  183. RenderMask( gui, &display, renderState, STENCIL_INCR );
  184. continue;
  185. }
  186. idSWFDictionaryEntry * entry = FindDictionaryEntry( display.characterID );
  187. if ( entry == NULL ) {
  188. continue;
  189. }
  190. swfRenderState_t renderState2;
  191. if ( spriteInstance->stereoDepth != STEREO_DEPTH_TYPE_NONE ) {
  192. renderState2.stereoDepth = ( stereoDepthType_t )spriteInstance->stereoDepth;
  193. } else if ( renderState.stereoDepth != STEREO_DEPTH_TYPE_NONE ) {
  194. renderState2.stereoDepth = renderState.stereoDepth;
  195. }
  196. renderState2.matrix = display.matrix.Multiply( renderState.matrix );
  197. renderState2.cxf = display.cxf.Multiply( renderState.cxf );
  198. renderState2.ratio = display.ratio;
  199. if ( display.blendMode != 0 ) {
  200. renderState2.blendMode = display.blendMode;
  201. } else {
  202. renderState2.blendMode = renderState.blendMode;
  203. }
  204. renderState2.activeMasks = renderState.activeMasks + activeMasks.Num();
  205. if ( spriteInstance->materialOverride != NULL ) {
  206. renderState2.material = spriteInstance->materialOverride;
  207. renderState2.materialWidth = spriteInstance->materialWidth;
  208. renderState2.materialHeight = spriteInstance->materialHeight;
  209. } else {
  210. renderState2.material = renderState.material;
  211. renderState2.materialWidth = renderState.materialWidth;
  212. renderState2.materialHeight = renderState.materialHeight;
  213. }
  214. float xOffset = 0.0f;
  215. float yOffset = 0.0f;
  216. if ( entry->type == SWF_DICT_SPRITE ) {
  217. display.spriteInstance->SetAlignment( spriteInstance->xOffset, spriteInstance->yOffset );
  218. if ( display.spriteInstance->name[0] == '_' ) {
  219. //if ( display.spriteInstance->name.Icmp( "_leftAlign" ) == 0 ) {
  220. // float adj = (float)frameWidth * 0.10;
  221. // renderState2.matrix.tx = ( display.matrix.tx - adj ) * renderState.matrix.xx;
  222. //}
  223. //if ( display.spriteInstance->name.Icmp( "_rightAlign" ) == 0 ) {
  224. // renderState2.matrix.tx = ( (float)renderSystem->GetWidth() - ( ( (float)frameWidth - display.matrix.tx - adj ) * renderState.matrix.xx ) );
  225. //}
  226. float widthAdj = swf_titleSafe.GetFloat() * frameWidth;
  227. float heightAdj = swf_titleSafe.GetFloat() * frameHeight;
  228. const float pixelAspect = renderSystem->GetPixelAspect();
  229. const float sysWidth = renderSystem->GetWidth() * ( pixelAspect > 1.0f ? pixelAspect : 1.0f );
  230. const float sysHeight = renderSystem->GetHeight() / ( pixelAspect < 1.0f ? pixelAspect : 1.0f );
  231. if ( display.spriteInstance->name.Icmp( "_fullScreen" ) == 0 ) {
  232. renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
  233. renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
  234. float xScale = sysWidth / (float)frameWidth;
  235. float yScale = sysHeight / (float)frameHeight;
  236. renderState2.matrix.xx = xScale;
  237. renderState2.matrix.yy = yScale;
  238. }
  239. if ( display.spriteInstance->name.Icmp( "_absTop" ) == 0 ) {
  240. renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
  241. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  242. } else if ( display.spriteInstance->name.Icmp( "_top" ) == 0 ) {
  243. renderState2.matrix.ty = ( display.matrix.ty + heightAdj ) * renderState.matrix.yy;
  244. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  245. } else if ( display.spriteInstance->name.Icmp( "_topLeft" ) == 0 ) {
  246. renderState2.matrix.tx = ( display.matrix.tx + widthAdj ) * renderState.matrix.xx;
  247. renderState2.matrix.ty = ( display.matrix.ty + heightAdj ) * renderState.matrix.yy;
  248. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  249. } else if ( display.spriteInstance->name.Icmp( "_left" ) == 0 ) {
  250. float prevX = renderState2.matrix.tx;
  251. renderState2.matrix.tx = ( display.matrix.tx + widthAdj ) * renderState.matrix.xx;
  252. xOffset = (( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
  253. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  254. } else if ( idStr::FindText( display.spriteInstance->name, "_absLeft", false ) >= 0 ) {
  255. float prevX = renderState2.matrix.tx;
  256. renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
  257. xOffset = (( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
  258. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  259. } else if ( display.spriteInstance->name.Icmp( "_bottomLeft" ) == 0 ) {
  260. float prevX = renderState2.matrix.tx;
  261. renderState2.matrix.tx = ( display.matrix.tx + widthAdj ) * renderState.matrix.xx;
  262. xOffset = (( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
  263. float prevY = renderState2.matrix.ty;
  264. renderState2.matrix.ty = ( (float)sysHeight - ( ( (float)frameHeight - display.matrix.ty + heightAdj ) * renderState.matrix.yy ) );
  265. yOffset = (( renderState2.matrix.ty - prevY ) / renderState.matrix.yy );
  266. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  267. } else if ( display.spriteInstance->name.Icmp( "_absBottom" ) == 0 ) {
  268. renderState2.matrix.ty = ( (float)sysHeight - ( ( (float)frameHeight - display.matrix.ty ) * renderState.matrix.yy ) );
  269. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  270. } else if ( display.spriteInstance->name.Icmp( "_bottom" ) == 0 ) {
  271. renderState2.matrix.ty = ( (float)sysHeight - ( ( (float)frameHeight - display.matrix.ty + heightAdj ) * renderState.matrix.yy ) );
  272. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  273. } else if ( display.spriteInstance->name.Icmp( "_topRight" ) == 0 ) {
  274. renderState2.matrix.tx = ( (float)sysWidth - ( ( (float)frameWidth - display.matrix.tx + widthAdj ) * renderState.matrix.xx ) );
  275. renderState2.matrix.ty = ( display.matrix.ty + heightAdj ) * renderState.matrix.yy;
  276. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  277. } else if ( display.spriteInstance->name.Icmp( "_right" ) == 0 ) {
  278. float prevX = renderState2.matrix.tx;
  279. renderState2.matrix.tx = ( (float)sysWidth - ( ( (float)frameWidth - display.matrix.tx + widthAdj ) * renderState.matrix.xx ) );
  280. xOffset = (( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
  281. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  282. } else if ( idStr::FindText( display.spriteInstance->name, "_absRight", true ) >= 0 ) {
  283. float prevX = renderState2.matrix.tx;
  284. renderState2.matrix.tx = ( (float)sysWidth - ( ( (float)frameWidth - display.matrix.tx ) * renderState.matrix.xx ) );
  285. xOffset = (( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
  286. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  287. } else if ( display.spriteInstance->name.Icmp( "_bottomRight" ) == 0 ) {
  288. renderState2.matrix.tx = ( (float)sysWidth - ( ( (float)frameWidth - display.matrix.tx + widthAdj ) * renderState.matrix.xx ) );
  289. renderState2.matrix.ty = ( (float)sysHeight - ( ( (float)frameHeight - display.matrix.ty + heightAdj ) * renderState.matrix.yy ) );
  290. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  291. } else if ( display.spriteInstance->name.Icmp( "_absTopLeft" ) == 0 ) { // ABSOLUTE CORNERS OF SCREEN
  292. renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
  293. renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
  294. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  295. } else if ( display.spriteInstance->name.Icmp( "_absTopRight" ) == 0 ) {
  296. renderState2.matrix.tx = ( (float)sysWidth - ( ( (float)frameWidth - display.matrix.tx ) * renderState.matrix.xx ) );
  297. renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
  298. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  299. } else if ( display.spriteInstance->name.Icmp( "_absBottomLeft" ) == 0 ) {
  300. renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
  301. renderState2.matrix.ty = ( (float)sysHeight - ( ( (float)frameHeight - display.matrix.ty ) * renderState.matrix.yy ) );
  302. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  303. } else if ( display.spriteInstance->name.Icmp( "_absBottomRight" ) == 0 ) {
  304. renderState2.matrix.tx = ( (float)sysWidth - ( ( (float)frameWidth - display.matrix.tx ) * renderState.matrix.xx ) );
  305. renderState2.matrix.ty = ( (float)sysHeight - ( ( (float)frameHeight - display.matrix.ty ) * renderState.matrix.yy ) );
  306. display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
  307. }
  308. }
  309. RenderSprite( gui, display.spriteInstance, renderState2, time, isSplitscreen );
  310. } else if ( entry->type == SWF_DICT_SHAPE ) {
  311. RenderShape( gui, entry->shape, renderState2 );
  312. } else if ( entry->type == SWF_DICT_MORPH ) {
  313. RenderMorphShape( gui, entry->shape, renderState2 );
  314. } else if ( entry->type == SWF_DICT_EDITTEXT ) {
  315. RenderEditText( gui, display.textInstance, renderState2, time, isSplitscreen );
  316. } else {
  317. //idLib::Warning( "%s: Tried to render an unrenderable character %d", filename.c_str(), entry->type );
  318. }
  319. }
  320. for ( int j = 0; j < activeMasks.Num(); j++ ) {
  321. const swfDisplayEntry_t * mask = activeMasks[ j ];
  322. RenderMask( gui, mask, renderState, STENCIL_DECR );
  323. }
  324. }
  325. /*
  326. ========================
  327. idSWF::GLStateForBlendMode
  328. ========================
  329. */
  330. uint64 idSWF::GLStateForRenderState( const swfRenderState_t & renderState ) {
  331. uint64 extraGLState = GLS_OVERRIDE | GLS_DEPTHFUNC_LESS | GLS_DEPTHMASK; // SWF GL State always overrides what's set in the material
  332. if ( renderState.activeMasks > 0 ) {
  333. extraGLState |= GLS_STENCIL_FUNC_EQUAL | GLS_STENCIL_MAKE_REF( 128 + renderState.activeMasks ) | GLS_STENCIL_MAKE_MASK( 255 );
  334. } else if ( renderState.activeMasks == STENCIL_INCR ) {
  335. return GLS_COLORMASK | GLS_ALPHAMASK | GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_KEEP | GLS_STENCIL_OP_PASS_INCR;
  336. } else if ( renderState.activeMasks == STENCIL_DECR ) {
  337. return GLS_COLORMASK | GLS_ALPHAMASK | GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_KEEP | GLS_STENCIL_OP_PASS_DECR;
  338. }
  339. switch ( renderState.blendMode ) {
  340. case 7: // difference : dst = abs( dst - src )
  341. case 9: // subtract : dst = dst - src
  342. return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_BLENDOP_SUB );
  343. case 8: // add : dst = dst + src
  344. return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
  345. case 6: // darken : dst = min( dst, src )
  346. return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_BLENDOP_MIN );
  347. case 5: // lighten : dst = max( dst, src )
  348. return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_BLENDOP_MAX );
  349. case 4: // screen : dst = dst + src - dst*src ( we only do dst - dst * src, we could do the extra + src with another pass if we need to)
  350. return extraGLState | ( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_BLENDOP_SUB );
  351. case 14: // hardlight : src < 0.5 ? multiply : screen
  352. case 13: // overlay : dst < 0.5 ? multiply : screen
  353. case 3: // multiply : dst = ( dst * src ) + ( dst * (1-src.a) )
  354. return extraGLState | ( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
  355. case 12: // erase
  356. case 11: // alpha
  357. case 10: // invert
  358. case 2: // layer
  359. case 1: // normal
  360. case 0: // normaler
  361. default:
  362. return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
  363. }
  364. }
  365. /*
  366. ========================
  367. idSWF::RenderMorphShape
  368. ========================
  369. */
  370. void idSWF::RenderMorphShape( idRenderSystem * gui, const idSWFShape * shape, const swfRenderState_t & renderState ) {
  371. if ( shape == NULL ) {
  372. idLib::Warning( "%s: RenderMorphShape: shape == NULL", filename.c_str() );
  373. return;
  374. }
  375. for ( int i = 0; i < shape->fillDraws.Num(); i++ ) {
  376. const idSWFShapeDrawFill & fill = shape->fillDraws[i];
  377. const idMaterial * material = NULL;
  378. swfColorXform_t color;
  379. if ( renderState.material != NULL ) {
  380. material = renderState.material;
  381. } else if ( fill.style.type == 0 ) {
  382. material = guiSolid;
  383. idVec4 startColor = fill.style.startColor.ToVec4();
  384. idVec4 endColor = fill.style.endColor.ToVec4();
  385. color.mul = Lerp( startColor, endColor, renderState.ratio );
  386. } else if ( fill.style.type == 4 && fill.style.bitmapID != 65535 ) {
  387. material = dictionary[ fill.style.bitmapID ].material;
  388. } else {
  389. material = guiSolid;
  390. }
  391. color = color.Multiply( renderState.cxf );
  392. if ( swf_forceAlpha.GetFloat() > 0.0f ) {
  393. color.mul.w = swf_forceAlpha.GetFloat();
  394. color.add.w = 0.0f;
  395. }
  396. if ( ( color.mul.w + color.add.w ) <= ALPHA_EPSILON ) {
  397. continue;
  398. }
  399. uint32 packedColorM = LittleLong( PackColor( color.mul ) );
  400. uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1
  401. swfRect_t bounds;
  402. bounds.tl = Lerp( shape->startBounds.tl, shape->endBounds.tl, renderState.ratio );
  403. bounds.br = Lerp( shape->startBounds.br, shape->endBounds.br, renderState.ratio );
  404. idVec2 size( material->GetImageWidth(), material->GetImageHeight() );
  405. if ( renderState.materialWidth > 0 ) {
  406. size.x = renderState.materialWidth;
  407. }
  408. if ( renderState.materialHeight > 0 ) {
  409. size.y = renderState.materialHeight;
  410. }
  411. idVec2 oneOverSize( 1.0f / size.x, 1.0f / size.y );
  412. swfMatrix_t styleMatrix;
  413. styleMatrix.xx = Lerp( fill.style.startMatrix.xx, fill.style.endMatrix.xx, renderState.ratio );
  414. styleMatrix.yy = Lerp( fill.style.startMatrix.yy, fill.style.endMatrix.yy, renderState.ratio );
  415. styleMatrix.xy = Lerp( fill.style.startMatrix.xy, fill.style.endMatrix.xy, renderState.ratio );
  416. styleMatrix.yx = Lerp( fill.style.startMatrix.yx, fill.style.endMatrix.yx, renderState.ratio );
  417. styleMatrix.tx = Lerp( fill.style.startMatrix.tx, fill.style.endMatrix.tx, renderState.ratio );
  418. styleMatrix.ty = Lerp( fill.style.startMatrix.ty, fill.style.endMatrix.ty, renderState.ratio );
  419. swfMatrix_t invMatrix = styleMatrix.Inverse();
  420. gui->SetGLState( GLStateForRenderState( renderState ) );
  421. idDrawVert * verts = gui->AllocTris( fill.startVerts.Num(), fill.indices.Ptr(), fill.indices.Num(), material, renderState.stereoDepth );
  422. if ( verts == NULL ) {
  423. continue;
  424. }
  425. for ( int j = 0; j < fill.startVerts.Num(); j++ ) {
  426. idVec2 xy = Lerp( fill.startVerts[j], fill.endVerts[j], renderState.ratio );
  427. idVec2 st;
  428. st.x = ( ( xy.x - bounds.tl.x ) * oneOverSize.x ) * 20.0f;
  429. st.y = ( ( xy.y - bounds.tl.y ) * oneOverSize.y ) * 20.0f;
  430. idVec2 adjust( 0.5f * oneOverSize.x, 0.5f * oneOverSize.y );
  431. ALIGNTYPE16 idDrawVert tempVert;
  432. tempVert.Clear();
  433. tempVert.xyz.ToVec2() = renderState.matrix.Transform( xy ).Scale( scaleToVirtual );
  434. tempVert.xyz.z = 0.0f;
  435. tempVert.SetTexCoord( invMatrix.Transform( st ) + adjust );
  436. tempVert.SetNativeOrderColor( packedColorM );
  437. tempVert.SetNativeOrderColor2( packedColorA );
  438. WriteDrawVerts16( & verts[j], & tempVert, 1 );
  439. }
  440. }
  441. }
  442. /*
  443. ========================
  444. idSWF::RenderShape
  445. ========================
  446. */
  447. void idSWF::RenderShape( idRenderSystem * gui, const idSWFShape * shape, const swfRenderState_t & renderState ) {
  448. if ( shape == NULL ) {
  449. idLib::Warning( "%s: RenderShape: shape == NULL", filename.c_str() );
  450. return;
  451. }
  452. for ( int i = 0; i < shape->fillDraws.Num(); i++ ) {
  453. const idSWFShapeDrawFill & fill = shape->fillDraws[i];
  454. const idMaterial * material = NULL;
  455. swfColorXform_t color;
  456. swfMatrix_t invMatrix;
  457. idVec2 atlasScale( 0.0f, 0.0f );
  458. idVec2 atlasBias( 0.0f, 0.0f );
  459. bool useAtlas = false;
  460. idVec2 size( 1.0f, 1.0f );
  461. if ( renderState.material != NULL ) {
  462. material = renderState.material;
  463. invMatrix.xx = invMatrix.yy = ( 1.0f / 20.0f );
  464. } else if ( fill.style.type == 0 ) {
  465. material = guiSolid;
  466. color.mul = fill.style.startColor.ToVec4();
  467. } else if ( fill.style.type == 4 && fill.style.bitmapID != 65535 ) {
  468. // everything in a single image atlas
  469. idSWFDictionaryEntry * entry = &dictionary[ fill.style.bitmapID ];
  470. material = atlasMaterial;
  471. idVec2i atlasSize( material->GetImageWidth(), material->GetImageHeight() );
  472. for ( int i = 0 ; i < 2 ; i++ ) {
  473. size[i] = entry->imageSize[i];
  474. atlasScale[i] = (float)size[i] / atlasSize[i];
  475. atlasBias[i] = (float)entry->imageAtlasOffset[i] / atlasSize[i];
  476. }
  477. // de-normalize color channels after DXT decompression
  478. color.mul = entry->channelScale;
  479. useAtlas = true;
  480. const swfMatrix_t & styleMatrix = fill.style.startMatrix;
  481. invMatrix = styleMatrix.Inverse();
  482. } else {
  483. material = guiSolid;
  484. }
  485. color = color.Multiply( renderState.cxf );
  486. if ( swf_forceAlpha.GetFloat() > 0.0f ) {
  487. color.mul.w = swf_forceAlpha.GetFloat();
  488. color.add.w = 0.0f;
  489. }
  490. if ( ( color.mul.w + color.add.w ) <= ALPHA_EPSILON ) {
  491. continue;
  492. }
  493. uint32 packedColorM = LittleLong( PackColor( color.mul ) );
  494. uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1
  495. const swfRect_t & bounds = shape->startBounds;
  496. if ( renderState.materialWidth > 0 ) {
  497. size.x = renderState.materialWidth;
  498. }
  499. if ( renderState.materialHeight > 0 ) {
  500. size.y = renderState.materialHeight;
  501. }
  502. idVec2 oneOverSize( 1.0f / size.x, 1.0f / size.y );
  503. gui->SetGLState( GLStateForRenderState( renderState ) );
  504. idDrawVert * verts = gui->AllocTris( fill.startVerts.Num(), fill.indices.Ptr(), fill.indices.Num(), material, renderState.stereoDepth );
  505. if ( verts == NULL ) {
  506. continue;
  507. }
  508. ALIGNTYPE16 idDrawVert tempVerts[4];
  509. for ( int j = 0; j < fill.startVerts.Num(); j++ ) {
  510. const idVec2 & xy = fill.startVerts[j];
  511. idDrawVert & vert = tempVerts[j & 3];
  512. vert.Clear();
  513. vert.xyz.ToVec2() = renderState.matrix.Transform( xy ).Scale( scaleToVirtual );
  514. vert.xyz.z = 0.0f;
  515. vert.SetNativeOrderColor( packedColorM );
  516. vert.SetNativeOrderColor2( packedColorA );
  517. // For some reason I don't understand, having texcoords
  518. // in the range of 2000 or so causes what should be solid
  519. // fill areas to have horizontal bands on nvidia, but not 360.
  520. // Forcing the texcoords to zero fixes it.
  521. if ( fill.style.type != 0 ) {
  522. idVec2 st;
  523. // all the swf vertexes have an implicit scale of 1/20 for some reason...
  524. st.x = ( ( xy.x - bounds.tl.x ) * oneOverSize.x ) * 20.0f;
  525. st.y = ( ( xy.y - bounds.tl.y ) * oneOverSize.y ) * 20.0f;
  526. st = invMatrix.Transform( st );
  527. if ( useAtlas ) {
  528. st = st.Scale( atlasScale ) + atlasBias;
  529. }
  530. // inset the tc - the gui may use a vmtr and the tc might end up
  531. // crossing page boundaries if using [0.0,1.0]
  532. st.x = idMath::ClampFloat( 0.001f, 0.999f, st.x );
  533. st.y = idMath::ClampFloat( 0.001f, 0.999f, st.y );
  534. vert.SetTexCoord( st );
  535. }
  536. // write four verts at a time to video memory
  537. if ( ( j & 3 ) == 3 ) {
  538. WriteDrawVerts16( & verts[j & ~3], tempVerts, 4 );
  539. }
  540. }
  541. // write any remaining verts to video memory
  542. WriteDrawVerts16( & verts[fill.startVerts.Num() & ~3], tempVerts, fill.startVerts.Num() & 3 );
  543. }
  544. for ( int i = 0; i < shape->lineDraws.Num(); i++ ) {
  545. const idSWFShapeDrawLine & line = shape->lineDraws[i];
  546. swfColorXform_t color;
  547. color.mul = line.style.startColor.ToVec4();
  548. color = color.Multiply( renderState.cxf );
  549. if ( swf_forceAlpha.GetFloat() > 0.0f ) {
  550. color.mul.w = swf_forceAlpha.GetFloat();
  551. color.add.w = 0.0f;
  552. }
  553. if ( ( color.mul.w + color.add.w ) <= ALPHA_EPSILON ) {
  554. continue;
  555. }
  556. uint32 packedColorM = LittleLong( PackColor( color.mul ) );
  557. uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1
  558. gui->SetGLState( GLStateForRenderState( renderState ) | GLS_POLYMODE_LINE );
  559. idDrawVert * verts = gui->AllocTris( line.startVerts.Num(), line.indices.Ptr(), line.indices.Num(), white, renderState.stereoDepth );
  560. if ( verts == NULL ) {
  561. continue;
  562. }
  563. for ( int j = 0; j < line.startVerts.Num(); j++ ) {
  564. const idVec2 & xy = line.startVerts[j];
  565. ALIGNTYPE16 idDrawVert tempVert;
  566. tempVert.Clear();
  567. tempVert.xyz.ToVec2() = renderState.matrix.Transform( xy ).Scale( scaleToVirtual );
  568. tempVert.xyz.z = 0.0f;
  569. tempVert.SetTexCoord( 0.0f, 0.0f );
  570. tempVert.SetNativeOrderColor( packedColorM );
  571. tempVert.SetNativeOrderColor2( packedColorA );
  572. WriteDrawVerts16( & verts[j], & tempVert, 1 );
  573. }
  574. }
  575. }
  576. /*
  577. ========================
  578. idSWF::DrawEditCursor
  579. ========================
  580. */
  581. void idSWF::DrawEditCursor( idRenderSystem * gui, float x, float y, float w, float h, const swfMatrix_t & matrix ) {
  582. idVec2 topl = matrix.Transform( idVec2( x, y ) );
  583. idVec2 topr = matrix.Transform( idVec2( x + w, y ) );
  584. idVec2 br = matrix.Transform( idVec2( x + w, y + h ) );
  585. idVec2 bl = matrix.Transform( idVec2( x, y + h ) );
  586. DrawStretchPic( idVec4( topl.x, topl.y, 0.0f, 0.0f ), idVec4( topr.x, topr.y, 1.0f, 0.0f ), idVec4( br.x, br.y, 1.0f, 1.0f ), idVec4( bl.x, bl.y, 0.0f, 1.0f ), white );
  587. }
  588. /*
  589. ========================
  590. idSWF::RenderEditText
  591. ========================
  592. */
  593. void idSWF::RenderEditText( idRenderSystem * gui, idSWFTextInstance * textInstance, const swfRenderState_t & renderState, int time, bool isSplitscreen ) {
  594. if ( textInstance == NULL ) {
  595. idLib::Warning( "%s: RenderEditText: textInstance == NULL", filename.c_str() );
  596. return;
  597. }
  598. if ( !textInstance->visible ) {
  599. return;
  600. }
  601. const idSWFEditText * shape = textInstance->editText;
  602. idStr text;
  603. if ( textInstance->variable.IsEmpty() ) {
  604. if ( textInstance->renderMode == SWF_TEXT_RENDER_PARAGRAPH ) {
  605. if ( textInstance->NeedsGenerateRandomText() ) {
  606. textInstance->StartParagraphText( Sys_Milliseconds() );
  607. }
  608. text = textInstance->GetParagraphText( Sys_Milliseconds() );
  609. } else if ( textInstance->renderMode == SWF_TEXT_RENDER_RANDOM_APPEAR || textInstance->renderMode == SWF_TEXT_RENDER_RANDOM_APPEAR_CAPS ) {
  610. if ( textInstance->NeedsGenerateRandomText() ) {
  611. textInstance->StartRandomText( Sys_Milliseconds() );
  612. }
  613. text = textInstance->GetRandomText( Sys_Milliseconds() );
  614. } else {
  615. text = idLocalization::GetString( textInstance->text );
  616. }
  617. } else {
  618. idSWFScriptVar var = globals->Get( textInstance->variable );
  619. if ( var.IsUndefined() ) {
  620. text = idLocalization::GetString( textInstance->text );
  621. } else {
  622. text = idLocalization::GetString( var.ToString() );
  623. }
  624. }
  625. if ( text.Length() == 0 ) {
  626. textInstance->selectionEnd = -1;
  627. textInstance->selectionStart = -1;
  628. }
  629. if ( textInstance->NeedsSoundPlayed() ) {
  630. PlaySound( textInstance->GetSoundClip() );
  631. textInstance->ClearPlaySound();
  632. }
  633. if ( textInstance->tooltip ) {
  634. FindTooltipIcons( &text );
  635. } else {
  636. tooltipIconList.Clear();
  637. }
  638. int selStart = textInstance->selectionStart;
  639. int selEnd = textInstance->selectionEnd;
  640. int cursorPos = selEnd;
  641. bool inputField = false;
  642. idSWFScriptVar focusWindow = globals->Get( "focusWindow" );
  643. if ( focusWindow.IsObject() && focusWindow.GetObject() == &textInstance->scriptObject ) {
  644. inputField = true;
  645. }
  646. bool drawCursor = false;
  647. if ( inputField && ( ( idLib::frameNumber >> 4 ) & 1 ) == 0 ) {
  648. cursorPos = selEnd;
  649. drawCursor = true;
  650. }
  651. if ( selStart > selEnd ) {
  652. SwapValues( selStart, selEnd );
  653. }
  654. idVec2 xScaleVec = renderState.matrix.Scale( idVec2( 1.0f, 0.0f ) );
  655. idVec2 yScaleVec = renderState.matrix.Scale( idVec2( 0.0f, 1.0f ) );
  656. float xScale = xScaleVec.Length();
  657. float yScale = yScaleVec.Length();
  658. if ( isSplitscreen ) {
  659. yScale *= 0.5f;
  660. }
  661. float invXScale = 1.0f / xScale;
  662. float invYScale = 1.0f / yScale;
  663. swfMatrix_t matrix = renderState.matrix;
  664. matrix.xx *= invXScale;
  665. matrix.xy *= invXScale;
  666. matrix.yy *= invYScale;
  667. matrix.yx *= invYScale;
  668. idSWFDictionaryEntry * fontEntry = FindDictionaryEntry( shape->fontID, SWF_DICT_FONT );
  669. if ( fontEntry == NULL ) {
  670. idLib::Warning( "idSWF::RenderEditText: NULL Font" );
  671. return;
  672. }
  673. idSWFFont * swfFont = fontEntry->font;
  674. float postTransformHeight = SWFTWIP( shape->fontHeight ) * yScale;
  675. const idFont * fontInfo = swfFont->fontID;
  676. float glyphScale = postTransformHeight / 48.0f;
  677. float imageScale = postTransformHeight / 24.0f;
  678. textInstance->glyphScale = glyphScale;
  679. idVec4 defaultColor = textInstance->color.ToVec4();
  680. defaultColor = defaultColor.Multiply( renderState.cxf.mul ) + renderState.cxf.add;
  681. if ( swf_forceAlpha.GetFloat() > 0.0f ) {
  682. defaultColor.w = swf_forceAlpha.GetFloat();
  683. }
  684. if ( defaultColor.w <= ALPHA_EPSILON ) {
  685. return;
  686. }
  687. idVec4 selColor( defaultColor );
  688. selColor.w *= 0.5f;
  689. gui->SetColor( defaultColor );
  690. gui->SetGLState( GLStateForRenderState( renderState ) );
  691. swfRect_t bounds;
  692. bounds.tl.x = xScale * ( shape->bounds.tl.x + SWFTWIP( shape->leftMargin ) );
  693. bounds.br.x = xScale * ( shape->bounds.br.x - SWFTWIP( shape->rightMargin ) );
  694. float linespacing = fontInfo->GetAscender( 1.15f * glyphScale );
  695. if ( shape->leading != 0 ) {
  696. linespacing += SWFTWIP( shape->leading );
  697. }
  698. bounds.tl.y = yScale * ( shape->bounds.tl.y + ( 1.15f * glyphScale ) );
  699. bounds.br.y = yScale * ( shape->bounds.br.y );
  700. textInstance->linespacing = linespacing;
  701. textInstance->bounds = bounds;
  702. if ( shape->flags & SWF_ET_AUTOSIZE ) {
  703. bounds.br.x = frameWidth;
  704. bounds.br.y = frameHeight;
  705. }
  706. if ( drawCursor && cursorPos <= 0 ) {
  707. float yPos = 0.0f;
  708. scaledGlyphInfo_t glyph;
  709. fontInfo->GetScaledGlyph( glyphScale, ' ', glyph );
  710. yPos = glyph.height / 2.0f;
  711. DrawEditCursor( gui, bounds.tl.x, yPos, 1.0f, linespacing, matrix );
  712. }
  713. if ( textInstance->IsSubtitle() ) {
  714. if ( text.IsEmpty() && textInstance->subtitleText.IsEmpty() ) {
  715. return;
  716. }
  717. } else if ( text.IsEmpty() ) {
  718. return;
  719. }
  720. float x = bounds.tl.x;
  721. float y = bounds.tl.y;
  722. int maxLines = idMath::Ftoi( ( bounds.br.y - bounds.tl.y ) / linespacing );
  723. if ( maxLines == 0 ) {
  724. maxLines = 1;
  725. }
  726. textInstance->maxLines = maxLines;
  727. idList< idStr > textLines;
  728. idStr * currentLine = &textLines.Alloc();
  729. // tracks the last breakable character we found
  730. int lastbreak = 0;
  731. float lastbreakX = 0;
  732. bool insertingImage = false;
  733. int iconIndex = 0;
  734. int charIndex = 0;
  735. if ( textInstance->IsSubtitle() ) {
  736. charIndex = textInstance->GetSubStartIndex();
  737. }
  738. while ( charIndex < text.Length() ) {
  739. if ( text[ charIndex ] == '\n' ) {
  740. if ( shape->flags & SWF_ET_MULTILINE ) {
  741. currentLine->Append( '\n' );
  742. x = bounds.tl.x;
  743. y += linespacing;
  744. currentLine = &textLines.Alloc();
  745. lastbreak = 0;
  746. charIndex++;
  747. continue;
  748. } else {
  749. break;
  750. }
  751. }
  752. int glyphStart = charIndex;
  753. uint32 tc = text.UTF8Char( charIndex );
  754. scaledGlyphInfo_t glyph;
  755. fontInfo->GetScaledGlyph( glyphScale, tc, glyph );
  756. float glyphSkip = glyph.xSkip;
  757. if ( textInstance->HasStroke() ) {
  758. glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
  759. }
  760. tooltipIcon_t iconCheck;
  761. if ( iconIndex < tooltipIconList.Num() ) {
  762. iconCheck = tooltipIconList[iconIndex];
  763. }
  764. float imageSkip = 0.0f;
  765. if ( charIndex - 1 == iconCheck.startIndex ) {
  766. insertingImage = true;
  767. imageSkip = iconCheck.imageWidth * imageScale;
  768. } else if ( charIndex - 1 == iconCheck.endIndex ) {
  769. insertingImage = false;
  770. iconIndex++;
  771. glyphSkip = 0.0f;
  772. }
  773. if ( insertingImage ) {
  774. glyphSkip = 0.0f;
  775. }
  776. if ( !inputField ) { // only break lines of text when we are not inputting data
  777. if ( x + glyphSkip > bounds.br.x || x + imageSkip > bounds.br.x ) {
  778. if ( shape->flags & ( SWF_ET_MULTILINE | SWF_ET_WORDWRAP ) ) {
  779. if ( lastbreak > 0 ) {
  780. int curLineIndex = currentLine - &textLines[0];
  781. idStr * newline = &textLines.Alloc();
  782. currentLine = &textLines[ curLineIndex ];
  783. if ( maxLines == 1 ) {
  784. currentLine->CapLength( currentLine->Length() - 3 );
  785. currentLine->Append( "..." );
  786. break;
  787. } else {
  788. *newline = currentLine->c_str() + lastbreak;
  789. currentLine->CapLength( lastbreak );
  790. currentLine = newline;
  791. x -= lastbreakX;
  792. }
  793. } else {
  794. currentLine = &textLines.Alloc();
  795. x = bounds.tl.x;
  796. }
  797. lastbreak = 0;
  798. } else {
  799. break;
  800. }
  801. }
  802. }
  803. while ( glyphStart < charIndex && glyphStart < text.Length() ) {
  804. currentLine->Append( text[ glyphStart++ ] );
  805. }
  806. x += glyphSkip + imageSkip;
  807. if ( tc == ' ' || tc == '-' ) {
  808. lastbreak = currentLine->Length();
  809. lastbreakX = x;
  810. }
  811. }
  812. // Subtitle functionality
  813. if ( textInstance->IsSubtitle() && textInstance->IsUpdatingSubtitle() ) {
  814. if ( textLines.Num() > 0 && textInstance->SubNeedsSwitch() ) {
  815. int lastWordIndex = textInstance->GetApporoximateSubtitleBreak( time );
  816. int newEndChar = textInstance->GetSubStartIndex() + textLines[0].Length();
  817. int wordCount = 0;
  818. bool earlyOut = false;
  819. for ( int index = 0; index < textLines[0].Length(); ++index ) {
  820. if ( textLines[0][index] == ' ' || textLines[0][index] == '-' ) {
  821. if ( index != 0 ) {
  822. if ( wordCount == lastWordIndex ) {
  823. newEndChar = textInstance->GetSubStartIndex() + index;
  824. earlyOut = true;
  825. break;
  826. }
  827. // cover the double space at the beginning of sentences
  828. if ( index > 0 && textLines[0][index - 1 ] != ' ' ) {
  829. wordCount++;
  830. }
  831. }
  832. } else if ( index == textLines[0].Length() ) {
  833. if ( wordCount == lastWordIndex ) {
  834. newEndChar = textInstance->GetSubStartIndex() + index;
  835. earlyOut = true;
  836. break;
  837. }
  838. wordCount++;
  839. }
  840. }
  841. if ( wordCount <= 0 && textLines[0].Length() > 0 ) {
  842. wordCount = 1;
  843. }
  844. if ( !earlyOut ) {
  845. textInstance->LastWordChanged( wordCount, time );
  846. }
  847. textInstance->SetSubEndIndex( newEndChar, time );
  848. idStr subText = textLines[0].Left( newEndChar - textInstance->GetSubStartIndex() );
  849. idSWFParmList parms;
  850. parms.Append( subText );
  851. parms.Append( textInstance->GetSpeaker().c_str() );
  852. parms.Append( textInstance->GetSubAlignment() );
  853. Invoke( "subtitleChanged", parms );
  854. parms.Clear();
  855. textInstance->SetSubNextStartIndex( textInstance->GetSubEndIndex() );
  856. textInstance->SwitchSubtitleText( time );
  857. }
  858. if ( !textInstance->UpdateSubtitle( time ) ) {
  859. textInstance->SubtitleComplete();
  860. idSWFParmList parms;
  861. parms.Append( textInstance->GetSubAlignment() );
  862. Invoke( "subtitleComplete", parms );
  863. parms.Clear();
  864. textInstance->SubtitleCleanup();
  865. }
  866. }
  867. //*************************************************
  868. // CALCULATE THE NUMBER OF SCROLLS LINES LEFT
  869. //*************************************************
  870. textInstance->CalcMaxScroll( textLines.Num() - maxLines );
  871. int c = 1;
  872. int textLine = textInstance->scroll;
  873. if ( textLine + maxLines > textLines.Num() && maxLines < textLines.Num() ) {
  874. textLine = textLines.Num() - maxLines;
  875. textInstance->scroll = textLine;
  876. } else if ( textLine < 0 || textLines.Num() <= maxLines ) {
  877. textLine = 0;
  878. textInstance->scroll = textLine;
  879. } else if ( textInstance->renderMode == SWF_TEXT_RENDER_AUTOSCROLL ) {
  880. textLine = textLines.Num() - maxLines;
  881. textInstance->scroll = textInstance->maxscroll;
  882. }
  883. // END SCROLL CALCULATION
  884. //*************************************************
  885. int index = 0;
  886. int startCharacter = 0;
  887. int endCharacter = 0;
  888. int inputEndChar = 0;
  889. iconIndex = 0;
  890. int overallIndex = 0;
  891. int curIcon = 0;
  892. float yPrevBottomOffset = 0.0f;
  893. float yOffset = 0;
  894. int strokeXOffsets[] = { -1, 1, -1, 1 };
  895. int strokeYOffsets[] = { -1, -1, 1, 1 };
  896. idStr inputText;
  897. if ( inputField ) {
  898. if ( textLines.Num() > 0 ) {
  899. idStr & text = textLines[0];
  900. float left = bounds.tl.x;
  901. int startCheckIndex = textInstance->GetInputStartChar();
  902. if ( startCheckIndex >= text.Length() ) {
  903. startCheckIndex = 0;
  904. }
  905. if ( cursorPos < startCheckIndex && cursorPos >= 0 ) {
  906. startCheckIndex = cursorPos;
  907. }
  908. bool endFound = false;
  909. int c = startCheckIndex;
  910. while ( c < text.Length() ) {
  911. uint32 tc = text.UTF8Char( c );
  912. scaledGlyphInfo_t glyph;
  913. fontInfo->GetScaledGlyph( glyphScale, tc, glyph );
  914. float glyphSkip = glyph.xSkip;
  915. if ( textInstance->HasStroke() ) {
  916. glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
  917. }
  918. if ( left + glyphSkip > bounds.br.x ) {
  919. if ( cursorPos > c && cursorPos != endCharacter ) {
  920. float removeSize = 0.0f;
  921. while ( removeSize < glyphSkip ) {
  922. if ( endCharacter == c ) {
  923. break;
  924. }
  925. scaledGlyphInfo_t removeGlyph;
  926. fontInfo->GetScaledGlyph( glyphScale, inputText[ endCharacter++ ], removeGlyph );
  927. removeSize += removeGlyph.xSkip;
  928. }
  929. left -= removeSize;
  930. } else {
  931. inputEndChar = c;
  932. endFound = true;
  933. break;
  934. }
  935. }
  936. inputText.AppendUTF8Char( tc );
  937. left += glyphSkip;
  938. }
  939. if ( !endFound ) {
  940. inputEndChar = text.Length();
  941. }
  942. startCheckIndex += endCharacter;
  943. textInstance->SetInputStartCharacter( startCheckIndex );
  944. endCharacter = startCheckIndex;
  945. }
  946. }
  947. for ( int t = 0; t < textLines.Num(); t++ ) {
  948. if ( textInstance->IsSubtitle() && t > 0 ) {
  949. break;
  950. }
  951. if ( t < textLine ) {
  952. idStr & text = textLines[t];
  953. c += text.Length();
  954. startCharacter = endCharacter;
  955. endCharacter = startCharacter + text.Length();
  956. overallIndex += text.Length();
  957. // find the right icon index if we scrolled passed the previous ones
  958. for ( int iconChar = curIcon; iconChar < tooltipIconList.Num(); ++iconChar ) {
  959. if ( endCharacter > tooltipIconList[iconChar].startIndex ) {
  960. curIcon++;
  961. } else {
  962. break;
  963. }
  964. }
  965. continue;
  966. }
  967. if ( index == maxLines ) {
  968. break;
  969. }
  970. startCharacter = endCharacter;
  971. idStr & text = textLines[textLine];
  972. int lastChar = text.Length();
  973. if ( textInstance->IsSubtitle() ) {
  974. lastChar = textInstance->GetSubEndIndex();
  975. }
  976. textLine++;
  977. if ( inputField ) {
  978. if ( inputEndChar == 0 ) {
  979. inputEndChar += 1;
  980. }
  981. selStart -= startCharacter;
  982. selEnd -= startCharacter;
  983. cursorPos -= startCharacter;
  984. endCharacter = inputEndChar;
  985. lastChar = endCharacter;
  986. text = text.Mid( startCharacter, endCharacter - startCharacter );
  987. } else {
  988. if ( lastChar == 0 ) {
  989. // blank line so add space char
  990. endCharacter = startCharacter + 1;
  991. } else {
  992. endCharacter = startCharacter + lastChar;
  993. }
  994. }
  995. float width = 0.0f;
  996. insertingImage = false;
  997. int i = 0;
  998. while ( i < lastChar ) {
  999. if ( curIcon < tooltipIconList.Num() && tooltipIconList[curIcon].startIndex == startCharacter + i ) {
  1000. width += tooltipIconList[curIcon].imageWidth * imageScale;
  1001. i += tooltipIconList[curIcon].endIndex - tooltipIconList[curIcon].startIndex - 1;
  1002. curIcon++;
  1003. } else {
  1004. if ( i < text.Length() ) {
  1005. scaledGlyphInfo_t glyph;
  1006. fontInfo->GetScaledGlyph( glyphScale, text.UTF8Char( i ), glyph );
  1007. width += glyph.xSkip;
  1008. if ( textInstance->HasStroke() ) {
  1009. width += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
  1010. }
  1011. } else {
  1012. i++;
  1013. }
  1014. }
  1015. }
  1016. y = bounds.tl.y + ( index * linespacing );
  1017. float biggestGlyphHeight = 0.0f;
  1018. /*for ( int image = 0; image < tooltipIconList.Num(); ++image ) {
  1019. if ( tooltipIconList[image].startIndex >= startCharacter && tooltipIconList[image].endIndex < endCharacter ) {
  1020. biggestGlyphHeight = tooltipIconList[image].imageHeight > biggestGlyphHeight ? tooltipIconList[image].imageHeight : biggestGlyphHeight;
  1021. }
  1022. }*/
  1023. float yBottomOffset = 0.0f;
  1024. float yTopOffset = 0.0f;
  1025. if ( biggestGlyphHeight > 0.0f ) {
  1026. float topSpace = 0.0f;
  1027. float bottomSpace = 0.0f;
  1028. int idx = 0;
  1029. scaledGlyphInfo_t glyph;
  1030. fontInfo->GetScaledGlyph( glyphScale, text.UTF8Char( idx ), glyph );
  1031. topSpace = ( ( biggestGlyphHeight * imageScale ) - glyph.height ) / 2.0f;
  1032. bottomSpace = topSpace;
  1033. if ( topSpace > 0.0f && t != 0 ) {
  1034. yTopOffset += topSpace;
  1035. }
  1036. if ( bottomSpace > 0.0f ) {
  1037. yBottomOffset += bottomSpace;
  1038. }
  1039. } else {
  1040. yBottomOffset = 0.0f;
  1041. }
  1042. if ( t != 0 ) {
  1043. if ( yPrevBottomOffset > 0 || yTopOffset > 0 ) {
  1044. yOffset += yTopOffset > yPrevBottomOffset ? yTopOffset : yPrevBottomOffset;
  1045. }
  1046. }
  1047. y += yOffset;
  1048. yPrevBottomOffset = yBottomOffset;
  1049. float extraSpace = 0.0f;
  1050. switch ( shape->align ) {
  1051. case SWF_ET_ALIGN_LEFT:
  1052. x = bounds.tl.x;
  1053. break;
  1054. case SWF_ET_ALIGN_RIGHT:
  1055. x = bounds.br.x - width;
  1056. break;
  1057. case SWF_ET_ALIGN_CENTER:
  1058. x = ( bounds.tl.x + bounds.br.x - width ) * 0.5f;
  1059. break;
  1060. case SWF_ET_ALIGN_JUSTIFY:
  1061. x = bounds.tl.x;
  1062. if ( width > ( bounds.br.x - bounds.tl.x ) * 0.5f && index < textLines.Num() - 1 ) {
  1063. extraSpace = ( ( bounds.br.x - bounds.tl.x ) - width ) / ( (float) lastChar - 1.0f );
  1064. }
  1065. break;
  1066. }
  1067. tooltipIcon_t icon;
  1068. insertingImage = false;
  1069. // find the right icon index if we scrolled passed the previous ones
  1070. for ( int iconChar = iconIndex; iconChar < tooltipIconList.Num(); ++iconChar ) {
  1071. if ( overallIndex > tooltipIconList[iconChar].startIndex ) {
  1072. iconIndex++;
  1073. } else {
  1074. break;
  1075. }
  1076. }
  1077. float baseLine = y + ( fontInfo->GetAscender( glyphScale ) );
  1078. i = 0;
  1079. int overallLineIndex = 0;
  1080. idVec4 textColor = defaultColor;
  1081. while ( i < lastChar ) {
  1082. if ( i >= text.Length() ) {
  1083. break;
  1084. }
  1085. // Support colors
  1086. if ( !textInstance->ignoreColor ) {
  1087. if ( text[ i ] == C_COLOR_ESCAPE ) {
  1088. if ( idStr::IsColor( text.c_str() + i++ ) ) {
  1089. if ( text[ i ] == C_COLOR_DEFAULT ) {
  1090. i++;
  1091. textColor = defaultColor;
  1092. } else {
  1093. textColor = idStr::ColorForIndex( text[ i++ ] );
  1094. textColor.w = defaultColor.w;
  1095. }
  1096. continue;
  1097. }
  1098. }
  1099. }
  1100. uint32 character = text.UTF8Char( i );
  1101. if ( character == '\n' ) {
  1102. c++;
  1103. overallIndex += i - overallLineIndex;
  1104. overallLineIndex = i;;
  1105. continue;
  1106. }
  1107. // Skip a single leading space
  1108. if ( character == ' ' && i == 1 ) {
  1109. c++;
  1110. overallIndex += i - overallLineIndex;
  1111. overallLineIndex = i;
  1112. continue;
  1113. }
  1114. if ( iconIndex < tooltipIconList.Num() ) {
  1115. icon = tooltipIconList[iconIndex];
  1116. }
  1117. if ( overallIndex == icon.startIndex ) {
  1118. insertingImage = true;
  1119. scaledGlyphInfo_t glyph;
  1120. fontInfo->GetScaledGlyph( glyphScale, character, glyph );
  1121. float imageHeight = icon.imageHeight * imageScale;
  1122. float glyphHeight = glyph.height;
  1123. float imageY = 0.0f;
  1124. if ( icon.baseline == 0 ) {
  1125. imageY = baseLine - glyph.top;
  1126. imageY += ( glyphHeight - imageHeight ) * 0.5f;
  1127. imageY += 2.0f;
  1128. } else {
  1129. imageY = ( y + glyphHeight ) - ( ( icon.imageHeight * imageScale ) - ( glyphHeight ) );
  1130. }
  1131. float imageX = x + glyph.left;
  1132. float imageW = icon.imageWidth * imageScale;
  1133. float imageH = icon.imageHeight * imageScale;
  1134. idVec2 topl = matrix.Transform( idVec2( imageX, imageY ) );
  1135. idVec2 topr = matrix.Transform( idVec2( imageX + imageW, imageY ) );
  1136. idVec2 br = matrix.Transform( idVec2( imageX + imageW, imageY + imageH ) );
  1137. idVec2 bl = matrix.Transform( idVec2( imageX, imageY + imageH ) );
  1138. float s1 = 0.0f;
  1139. float t1 = 0.0f;
  1140. float s2 = 1.0f;
  1141. float t2 = 1.0f;
  1142. //uint32 color = gui->GetColor();
  1143. idVec4 imgColor = colorWhite;
  1144. imgColor.w = defaultColor.w;
  1145. gui->SetColor( imgColor );
  1146. DrawStretchPic( idVec4( topl.x, topl.y, s1, t1 ), idVec4( topr.x, topr.y, s2, t1 ), idVec4( br.x, br.y, s2, t2 ), idVec4( bl.x, bl.y, s1, t2 ), icon.material );
  1147. gui->SetColor( defaultColor );
  1148. x += icon.imageWidth * imageScale;
  1149. x += extraSpace;
  1150. } else if ( overallIndex == icon.endIndex ) {
  1151. insertingImage = false;
  1152. iconIndex++;
  1153. }
  1154. if ( insertingImage ) {
  1155. overallIndex += i - overallLineIndex;
  1156. overallLineIndex = i;
  1157. continue;
  1158. }
  1159. // the glyphs texcoords assume nearest filtering, to get proper
  1160. // bilinear support we need to go an extra half texel on each side
  1161. scaledGlyphInfo_t glyph;
  1162. fontInfo->GetScaledGlyph( glyphScale, character, glyph );
  1163. float glyphSkip = glyph.xSkip;
  1164. if ( textInstance->HasStroke() ) {
  1165. glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
  1166. }
  1167. float glyphW = glyph.width + 1.0f; // +1 for bilinear half texel on each side
  1168. float glyphH = glyph.height + 1.0f;
  1169. float glyphY = baseLine - glyph.top;
  1170. float glyphX = x + glyph.left;
  1171. idVec2 topl = matrix.Transform( idVec2( glyphX, glyphY ) );
  1172. idVec2 topr = matrix.Transform( idVec2( glyphX + glyphW, glyphY ) );
  1173. idVec2 br = matrix.Transform( idVec2( glyphX + glyphW, glyphY + glyphH ) );
  1174. idVec2 bl = matrix.Transform( idVec2( glyphX, glyphY + glyphH ) );
  1175. float s1 = glyph.s1;
  1176. float t1 = glyph.t1;
  1177. float s2 = glyph.s2;
  1178. float t2 = glyph.t2;
  1179. if ( c > selStart && c <= selEnd ) {
  1180. idVec2 topl = matrix.Transform( idVec2( x, y ) );
  1181. idVec2 topr = matrix.Transform( idVec2( x + glyphSkip, y ) );
  1182. idVec2 br = matrix.Transform( idVec2( x + glyphSkip, y + linespacing ) );
  1183. idVec2 bl = matrix.Transform( idVec2( x, y + linespacing ) );
  1184. gui->SetColor( selColor );
  1185. DrawStretchPic( idVec4( topl.x, topl.y, 0, 0 ), idVec4( topr.x, topr.y, 1, 0 ), idVec4( br.x, br.y, 1, 1 ), idVec4( bl.x, bl.y, 0, 1 ), white );
  1186. gui->SetColor( textColor );
  1187. }
  1188. if ( textInstance->GetHasDropShadow() ) {
  1189. float dsY = glyphY + glyphScale * 2.0f;
  1190. float dsX = glyphX + glyphScale * 2.0f;
  1191. idVec2 dstopl = matrix.Transform( idVec2( dsX, dsY ) );
  1192. idVec2 dstopr = matrix.Transform( idVec2( dsX + glyphW, dsY ) );
  1193. idVec2 dsbr = matrix.Transform( idVec2( dsX + glyphW, dsY + glyphH ) );
  1194. idVec2 dsbl = matrix.Transform( idVec2( dsX, dsY + glyphH ) );
  1195. idVec4 dsColor = colorBlack;
  1196. dsColor.w = defaultColor.w;
  1197. gui->SetColor( dsColor );
  1198. DrawStretchPic( idVec4( dstopl.x, dstopl.y, s1, t1 ), idVec4( dstopr.x, dstopr.y, s2, t1 ), idVec4( dsbr.x, dsbr.y, s2, t2 ), idVec4( dsbl.x, dsbl.y, s1, t2 ), glyph.material );
  1199. gui->SetColor( textColor );
  1200. } else if ( textInstance->HasStroke() ) {
  1201. idVec4 strokeColor = colorBlack;
  1202. strokeColor.w = textInstance->GetStrokeStrength() * defaultColor.w;
  1203. gui->SetColor( strokeColor );
  1204. for ( int index = 0; index < 4; ++index ) {
  1205. float xPos = glyphX + ( ( strokeXOffsets[ index ] * textInstance->GetStrokeWeight() ) * glyphScale );
  1206. float yPos = glyphY + ( ( strokeYOffsets[ index ] * textInstance->GetStrokeWeight() ) * glyphScale );
  1207. idVec2 topLeft = matrix.Transform( idVec2( xPos, yPos ) );
  1208. idVec2 topRight = matrix.Transform( idVec2( xPos + glyphW, yPos ) );
  1209. idVec2 botRight = matrix.Transform( idVec2( xPos + glyphW, yPos + glyphH ) );
  1210. idVec2 botLeft = matrix.Transform( idVec2( xPos, yPos + glyphH ) );
  1211. DrawStretchPic( idVec4( topLeft.x, topLeft.y, s1, t1 ), idVec4( topRight.x, topRight.y, s2, t1 ), idVec4( botRight.x, botRight.y, s2, t2 ), idVec4( botLeft.x, botLeft.y, s1, t2 ), glyph.material );
  1212. }
  1213. gui->SetColor( textColor );
  1214. }
  1215. DrawStretchPic( idVec4( topl.x, topl.y, s1, t1 ), idVec4( topr.x, topr.y, s2, t1 ), idVec4( br.x, br.y, s2, t2 ), idVec4( bl.x, bl.y, s1, t2 ), glyph.material );
  1216. x += glyphSkip;
  1217. x += extraSpace;
  1218. if ( cursorPos == c ) {
  1219. DrawEditCursor( gui, x - 1.0f, y, 1.0f, linespacing, matrix );
  1220. }
  1221. c++;
  1222. overallIndex += i - overallLineIndex;
  1223. overallLineIndex = i;
  1224. }
  1225. index++;
  1226. }
  1227. }
  1228. /*
  1229. ========================
  1230. idSWF::FindTooltipIcons
  1231. This replaces text like "_use" with platform specific text like "<JOY1>"
  1232. ========================
  1233. */
  1234. void idSWF::FindTooltipIcons( idStr * text ) {
  1235. tooltipIconList.Clear();
  1236. for ( int i = UB_MAX_BUTTONS - 1; i >= 0; i-- ) {
  1237. //for ( userCmdString_t * ucs = userCmdStrings ; ucs->string ; ucs++ ) {
  1238. userCmdString_t ucs = userCmdStrings[i];
  1239. if ( ucs.string && idStr::FindText( text->c_str(), ucs.string, false ) != idStr::INVALID_POSITION ) {
  1240. idStr replacement;
  1241. keyBindings_t bind = idKeyInput::KeyBindingsFromBinding( ucs.string, true );
  1242. idStr gamepad = "<";
  1243. gamepad.Append( bind.gamepad );
  1244. gamepad.Append( ">" );
  1245. if ( !in_useJoystick.GetBool() ) {
  1246. if ( !bind.mouse.IsEmpty() ) {
  1247. replacement.Format( "<%s>", bind.mouse.c_str() );
  1248. } else if ( !bind.keyboard.IsEmpty() ) {
  1249. replacement = bind.keyboard;
  1250. }
  1251. if ( replacement.IsEmpty() ) {
  1252. text->Replace( ucs.string, idStrId( "#str_swf_unbound" ).GetLocalizedString() );
  1253. }
  1254. } else {
  1255. replacement = gamepad;
  1256. }
  1257. if ( !replacement.IsEmpty() ) {
  1258. replacement.ToUpper();
  1259. text->Replace( ucs.string, replacement.c_str() );
  1260. }
  1261. }
  1262. }
  1263. for ( int count = 0; count < tooltipButtonImage.Num(); ++count ) {
  1264. int index = -1;
  1265. while ( ( index = idStr::FindText( text->c_str(), tooltipButtonImage[count].key, false, index + 1 ) ) != idStr::INVALID_POSITION ) {
  1266. tooltipIcon_t icon;
  1267. icon.startIndex = index;
  1268. icon.endIndex = index + idStr::Length(tooltipButtonImage[count].key);
  1269. icon.material = declManager->FindMaterial( tooltipButtonImage[count].xbImage );
  1270. if ( icon.material ) {
  1271. icon.imageWidth = tooltipButtonImage[count].width;
  1272. icon.imageHeight = tooltipButtonImage[count].height;
  1273. icon.baseline = tooltipButtonImage[count].baseline;
  1274. } else {
  1275. icon.imageWidth = 0;
  1276. icon.imageHeight = 0;
  1277. icon.baseline = 0;
  1278. }
  1279. bool inserted = false;
  1280. if ( tooltipIconList.Num() > 0 ) {
  1281. for ( int i = 0; i < tooltipIconList.Num(); ++i ) {
  1282. if ( tooltipIconList[i].startIndex > icon.startIndex ) {
  1283. tooltipIconList.Insert( icon, i );
  1284. inserted = true;
  1285. break;
  1286. }
  1287. }
  1288. }
  1289. if ( !inserted ) {
  1290. tooltipIconList.Append( icon );
  1291. }
  1292. }
  1293. }
  1294. }