render2dsentence.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WW3D *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/render2dsentence.cpp $*
  25. * *
  26. * $Author:: Patrick $*
  27. * *
  28. * $Modtime:: 8/29/01 11:16a $*
  29. * *
  30. * $Revision:: 13 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "render2dsentence.h"
  36. #include "surfaceclass.h"
  37. #include "texture.h"
  38. #include "wwprofile.h"
  39. #include "wwmemlog.h"
  40. #include "dx8wrapper.h"
  41. #ifdef _INTERNAL
  42. // for occasional debugging...
  43. //#pragma optimize("", off)
  44. //#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
  45. #endif
  46. ////////////////////////////////////////////////////////////////////////////////////
  47. // Local constants
  48. ////////////////////////////////////////////////////////////////////////////////////
  49. #define no_TEST_PLACEMENT 1 // Shows alignment markers for text.
  50. #define TEXTURE_OFFSET 2
  51. ////////////////////////////////////////////////////////////////////////////////////
  52. //
  53. // Render2DSentenceClass
  54. //
  55. ////////////////////////////////////////////////////////////////////////////////////
  56. Render2DSentenceClass::Render2DSentenceClass (void) :
  57. Font (NULL),
  58. Location (0.0F,0.0F),
  59. Cursor (0.0F,0.0F),
  60. TextureOffset (0, 0),
  61. TextureStartX (0),
  62. CurSurface (NULL),
  63. CurrTextureSize (0),
  64. MonoSpaced (false),
  65. IsClippedEnabled (false),
  66. ClipRect (0, 0, 0, 0),
  67. BaseLocation (0, 0),
  68. LockedPtr (NULL),
  69. LockedStride (0),
  70. TextureSizeHint (0),
  71. WrapWidth (0),
  72. Centered (false),
  73. DrawExtents (0, 0, 0, 0),
  74. ParseHotKey( false ),
  75. useHardWordWrap( false)
  76. {
  77. Shader = Render2DClass::Get_Default_Shader ();
  78. return ;
  79. }
  80. ////////////////////////////////////////////////////////////////////////////////////
  81. //
  82. // ~Render2DSentenceClass
  83. //
  84. ////////////////////////////////////////////////////////////////////////////////////
  85. Render2DSentenceClass::~Render2DSentenceClass (void)
  86. {
  87. REF_PTR_RELEASE (Font);
  88. Reset ();
  89. return ;
  90. }
  91. ////////////////////////////////////////////////////////////////////////////////////
  92. //
  93. // Set_Font
  94. //
  95. ////////////////////////////////////////////////////////////////////////////////////
  96. void
  97. Render2DSentenceClass::Set_Font (FontCharsClass *font)
  98. {
  99. Reset ();
  100. REF_PTR_SET (Font, font);
  101. return ;
  102. }
  103. ////////////////////////////////////////////////////////////////////////////////////
  104. //
  105. // Reset_Polys
  106. //
  107. ////////////////////////////////////////////////////////////////////////////////////
  108. void
  109. Render2DSentenceClass::Reset_Polys (void)
  110. {
  111. for (int index = 0; index < Renderers.Count (); index ++) {
  112. Renderers[index].Renderer->Reset ();
  113. }
  114. return ;
  115. }
  116. ////////////////////////////////////////////////////////////////////////////////////
  117. //
  118. // Reset
  119. //
  120. ////////////////////////////////////////////////////////////////////////////////////
  121. void
  122. Render2DSentenceClass::Reset (void)
  123. {
  124. //
  125. // Make sure we unlock the current surface (if necessary)
  126. //
  127. if (LockedPtr != NULL) {
  128. CurSurface->Unlock ();
  129. LockedPtr = NULL;
  130. }
  131. //
  132. // Release our hold on the current surface
  133. //
  134. REF_PTR_RELEASE (CurSurface);
  135. //
  136. // Free each renderer
  137. //
  138. while (Renderers.Count () > 0) {
  139. delete Renderers[0].Renderer;
  140. Renderers.Delete(0);
  141. }
  142. Cursor.Set (0, 0);
  143. MonoSpaced = false;
  144. ParseHotKey = false;
  145. Release_Pending_Surfaces ();
  146. Reset_Sentence_Data ();
  147. return ;
  148. }
  149. ////////////////////////////////////////////////////////////////////////////////////
  150. //
  151. // Make_Additive
  152. //
  153. ////////////////////////////////////////////////////////////////////////////////////
  154. void
  155. Render2DSentenceClass::Make_Additive (void)
  156. {
  157. Shader.Set_Dst_Blend_Func (ShaderClass::DSTBLEND_ONE);
  158. Shader.Set_Src_Blend_Func (ShaderClass::SRCBLEND_ONE);
  159. Shader.Set_Primary_Gradient (ShaderClass::GRADIENT_MODULATE);
  160. Shader.Set_Secondary_Gradient (ShaderClass::SECONDARY_GRADIENT_DISABLE);
  161. Set_Shader (Shader);
  162. return ;
  163. }
  164. ////////////////////////////////////////////////////////////////////////////////////
  165. //
  166. // Make_Additive
  167. //
  168. ////////////////////////////////////////////////////////////////////////////////////
  169. void
  170. Render2DSentenceClass::Set_Shader (ShaderClass shader)
  171. {
  172. Shader = shader;
  173. //
  174. // Change each renderer's shader
  175. //
  176. for (int i = 0; i < Renderers.Count (); i ++) {
  177. ShaderClass *curr_shader = Renderers[i].Renderer->Get_Shader ();
  178. (*curr_shader) = Shader;
  179. }
  180. return ;
  181. }
  182. ////////////////////////////////////////////////////////////////////////////////////
  183. //
  184. // Render
  185. //
  186. ////////////////////////////////////////////////////////////////////////////////////
  187. void
  188. Render2DSentenceClass::Render (void)
  189. {
  190. //
  191. // Build any textures that are pending
  192. //
  193. Build_Textures ();
  194. //
  195. // Ask each renderer to draw its contents
  196. //
  197. for (int i = 0; i < Renderers.Count (); i ++) {
  198. Renderers[i].Renderer->Render ();
  199. }
  200. return ;
  201. }
  202. ////////////////////////////////////////////////////////////////////////////////////
  203. //
  204. // Set_Base_Location
  205. //
  206. ////////////////////////////////////////////////////////////////////////////////////
  207. void
  208. Render2DSentenceClass::Set_Base_Location (const Vector2 &loc)
  209. {
  210. Vector2 dif = loc - BaseLocation;
  211. BaseLocation = loc;
  212. for (int i = 0; i < Renderers.Count (); i ++) {
  213. Renderers[i].Renderer->Move (dif);
  214. }
  215. return ;
  216. }
  217. ////////////////////////////////////////////////////////////////////////////////////
  218. //
  219. // Set_Location
  220. //
  221. ////////////////////////////////////////////////////////////////////////////////////
  222. void
  223. Render2DSentenceClass::Set_Location (const Vector2 &loc)
  224. {
  225. Location = loc;
  226. return ;
  227. }
  228. ////////////////////////////////////////////////////////////////////////////////////
  229. //
  230. // Get_Text_Extents
  231. //
  232. ////////////////////////////////////////////////////////////////////////////////////
  233. Vector2
  234. Render2DSentenceClass::Get_Text_Extents (const WCHAR *text)
  235. {
  236. Vector2 extent (0, Font->Get_Char_Height());
  237. while (*text) {
  238. WCHAR ch = *text++;
  239. if ( ch != (WCHAR)'\n' ) {
  240. extent.X += Font->Get_Char_Spacing( ch );
  241. }
  242. }
  243. return extent;
  244. }
  245. ////////////////////////////////////////////////////////////////////////////////////
  246. //
  247. // Get_Formatted_Text_Extents
  248. //
  249. ////////////////////////////////////////////////////////////////////////////////////
  250. Vector2
  251. Render2DSentenceClass::Get_Formatted_Text_Extents (const WCHAR *text)
  252. {
  253. return Build_Sentence_Not_Centered(text, NULL, NULL, true);
  254. }
  255. ////////////////////////////////////////////////////////////////////////////////////
  256. //
  257. // Reset_Sentence_Data
  258. //
  259. ////////////////////////////////////////////////////////////////////////////////////
  260. void
  261. Render2DSentenceClass::Reset_Sentence_Data (void)
  262. {
  263. //
  264. // Release our hold on each texture used in the sentence
  265. //
  266. for (int index = 0; index < SentenceData.Count (); index ++) {
  267. REF_PTR_RELEASE (SentenceData[index].Surface);
  268. }
  269. if (SentenceData.Count()>0) {
  270. SentenceData.Delete_All ();
  271. }
  272. return ;
  273. }
  274. ////////////////////////////////////////////////////////////////////////////////////
  275. //
  276. // Release_Pending_Surfaces
  277. //
  278. ////////////////////////////////////////////////////////////////////////////////////
  279. void
  280. Render2DSentenceClass::Release_Pending_Surfaces (void)
  281. {
  282. //
  283. // Release our hold on each pending surface
  284. //
  285. for (int index = 0; index < PendingSurfaces.Count (); index ++) {
  286. SurfaceClass *curr_surface = PendingSurfaces[index].Surface;
  287. REF_PTR_RELEASE (curr_surface);
  288. }
  289. if (PendingSurfaces.Count()>0) PendingSurfaces.Delete_All ();
  290. return;
  291. }
  292. ////////////////////////////////////////////////////////////////////////////////////
  293. //
  294. // Build_Textures
  295. //
  296. ////////////////////////////////////////////////////////////////////////////////////
  297. void
  298. Render2DSentenceClass::Build_Textures (void)
  299. {
  300. WWMEMLOG(MEM_TEXTURE);
  301. //
  302. // Make sure we unlock the current surface
  303. //
  304. if (LockedPtr != NULL) {
  305. CurSurface->Unlock ();
  306. LockedPtr = NULL;
  307. }
  308. //
  309. // Release our hold on the current surface
  310. //
  311. REF_PTR_RELEASE (CurSurface);
  312. TextureOffset.Set (0, 0);
  313. TextureStartX = 0;
  314. //
  315. // Convert all pending surfaces to textures
  316. //
  317. for (int index = 0; index < PendingSurfaces.Count (); index ++) {
  318. PendingSurfaceStruct &surface_info = PendingSurfaces[index];
  319. SurfaceClass *curr_surface = surface_info.Surface;
  320. //
  321. // Get the dimensions of the surface
  322. //
  323. SurfaceClass::SurfaceDescription desc;
  324. curr_surface->Get_Description (desc);
  325. //
  326. // Create the new texture
  327. //
  328. TextureClass *new_texture = W3DNEW TextureClass (desc.Width, desc.Width, WW3D_FORMAT_A4R4G4B4, TextureClass::MIP_LEVELS_1);
  329. SurfaceClass *texture_surface = new_texture->Get_Surface_Level ();
  330. new_texture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
  331. new_texture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
  332. new_texture->Set_Min_Filter (TextureClass::FILTER_TYPE_NONE);
  333. new_texture->Set_Mag_Filter (TextureClass::FILTER_TYPE_NONE);
  334. new_texture->Set_Mip_Mapping (TextureClass::FILTER_TYPE_NONE);
  335. //
  336. // Copy the contents of the texture from the surface
  337. //
  338. DX8Wrapper::_Copy_DX8_Rects (curr_surface->Peek_D3D_Surface (), NULL, 0, texture_surface->Peek_D3D_Surface (), NULL);
  339. REF_PTR_RELEASE (texture_surface);
  340. //
  341. // Assign this texture to any renderers that need it
  342. //
  343. for (int renderer_index = 0; renderer_index < surface_info.Renderers.Count (); renderer_index ++) {
  344. Render2DClass *renderer = surface_info.Renderers[renderer_index];
  345. renderer->Set_Texture (new_texture);
  346. }
  347. //
  348. // Release our hold on the objects
  349. //
  350. REF_PTR_RELEASE (new_texture);
  351. REF_PTR_RELEASE (curr_surface);
  352. }
  353. //
  354. // Reset the list
  355. //
  356. if (PendingSurfaces.Count()>0) {
  357. PendingSurfaces.Delete_All ();
  358. }
  359. return ;
  360. }
  361. ////////////////////////////////////////////////////////////////////////////////////
  362. //
  363. // Draw_Sentence
  364. //
  365. ////////////////////////////////////////////////////////////////////////////////////
  366. void
  367. Render2DSentenceClass::Draw_Sentence (uint32 color)
  368. {
  369. Render2DClass *curr_renderer = NULL;
  370. SurfaceClass *curr_surface = NULL;
  371. DrawExtents.Set (0, 0, 0, 0);
  372. int offset = 0;
  373. //
  374. // Loop over all the parts of the sentence
  375. //
  376. for (int index = 0; index < SentenceData.Count (); index ++) {
  377. SentenceDataStruct &data = SentenceData[index];
  378. //
  379. // Has the surface changed?
  380. //
  381. if (data.Surface != curr_surface) {
  382. curr_surface = data.Surface;
  383. //
  384. // Try to find a renderer that uses the same "texture"
  385. //
  386. bool found = false;
  387. for (int renderer_index = 0; renderer_index < Renderers.Count (); renderer_index ++) {
  388. if (Renderers[renderer_index].Surface == curr_surface) {
  389. found = true;
  390. curr_renderer = Renderers[renderer_index].Renderer;
  391. break;
  392. }
  393. }
  394. //
  395. // Create a new renderer if we couldn't find an appropriate one
  396. //
  397. if (found == false) {
  398. //
  399. // Allocate a new renderer
  400. //
  401. curr_renderer = W3DNEW Render2DClass;
  402. curr_renderer->Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution ());
  403. ShaderClass *curr_shader = curr_renderer->Get_Shader ();
  404. (*curr_shader) = Shader;
  405. //
  406. // Add it to our list
  407. //
  408. RendererDataStruct render_info;
  409. render_info.Renderer = curr_renderer;
  410. render_info.Surface = curr_surface;
  411. Renderers.Add (render_info);
  412. //
  413. // Now, add this renderer to the surface pending list
  414. //
  415. for (int surface_index = 0; surface_index < PendingSurfaces.Count (); surface_index ++) {
  416. PendingSurfaceStruct &surface_info = PendingSurfaces[surface_index];
  417. if (surface_info.Surface == curr_surface) {
  418. surface_info.Renderers.Add (curr_renderer);
  419. }
  420. }
  421. }
  422. }
  423. //
  424. // Get the dimensions of the surface
  425. //
  426. SurfaceClass::SurfaceDescription desc;
  427. curr_surface->Get_Description (desc);
  428. //
  429. // Add a quad that contains this sentence chunk
  430. //
  431. RectClass screen_rect = data.ScreenRect;
  432. screen_rect += Location;
  433. RectClass uv_rect = data.UVRect;
  434. //
  435. // Clip the quad (as necessary)
  436. //
  437. bool add_quad = true;
  438. if (IsClippedEnabled) {
  439. //
  440. // Check for completely clipped
  441. //
  442. if ( screen_rect.Right <= ClipRect.Left ||
  443. screen_rect.Bottom <= ClipRect.Top)
  444. {
  445. add_quad = false;
  446. } else {
  447. //
  448. // Clip the polygons to the specified area
  449. //
  450. RectClass clipped_rect;
  451. clipped_rect.Left = max (screen_rect.Left, ClipRect.Left);
  452. clipped_rect.Right = min (screen_rect.Right, ClipRect.Right);
  453. clipped_rect.Top = max (screen_rect.Top, ClipRect.Top);
  454. clipped_rect.Bottom = min (screen_rect.Bottom, ClipRect.Bottom);
  455. //
  456. // Clip the texture to the specified area
  457. //
  458. RectClass clipped_uv_rect;
  459. float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ());
  460. clipped_uv_rect.Left = uv_rect.Left + (uv_rect.Width () * percent);
  461. percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ());
  462. clipped_uv_rect.Right = uv_rect.Left + (uv_rect.Width () * percent);
  463. percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ());
  464. clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent);
  465. percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ());
  466. clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent);
  467. //
  468. // Use the clipped rectangles to render
  469. //
  470. screen_rect = clipped_rect;
  471. uv_rect = clipped_uv_rect;
  472. if (screen_rect.Right <= screen_rect.Left ||
  473. screen_rect.Bottom <= screen_rect.Top)
  474. {
  475. add_quad = false;
  476. }
  477. }
  478. }
  479. if (add_quad) {
  480. //uv_rect.Bottom += 0.5f;
  481. uv_rect *= 1.0F / ((float)desc.Width);
  482. #ifdef TEST_PLACEMENT
  483. screen_rect.Left += offset*3;
  484. screen_rect.Right += offset*3;
  485. #endif
  486. offset++;
  487. curr_renderer->Add_Quad (screen_rect, uv_rect, color);
  488. //
  489. // Add this rectangle to the total draw extents
  490. //
  491. if (DrawExtents.Width () == 0) {
  492. DrawExtents = screen_rect;
  493. } else {
  494. DrawExtents += screen_rect;
  495. }
  496. }
  497. }
  498. return ;
  499. }
  500. ////////////////////////////////////////////////////////////////////////////////////
  501. //
  502. // Record_Sentence_Chunk
  503. //
  504. ////////////////////////////////////////////////////////////////////////////////////
  505. void
  506. Render2DSentenceClass::Record_Sentence_Chunk (void)
  507. {
  508. //
  509. // Do we have anything to store?
  510. //
  511. int width = TextureOffset.I - TextureStartX;
  512. if (width > 0) {
  513. float char_height = Font->Get_Char_Height ();
  514. //
  515. // Build a structure that contains enough information
  516. // to hold this portion of the sentence
  517. //
  518. SentenceDataStruct sentence_data;
  519. sentence_data.Surface = CurSurface;
  520. sentence_data.Surface->Add_Ref ();
  521. sentence_data.ScreenRect.Left = Cursor.X;
  522. sentence_data.ScreenRect.Right = Cursor.X + width;
  523. sentence_data.ScreenRect.Top = Cursor.Y;
  524. sentence_data.ScreenRect.Bottom = Cursor.Y + char_height;
  525. sentence_data.UVRect.Left = TextureStartX;
  526. sentence_data.UVRect.Top = TextureOffset.J;
  527. sentence_data.UVRect.Right = TextureOffset.I;
  528. sentence_data.UVRect.Bottom = TextureOffset.J + char_height;
  529. //
  530. // Add this information to our list
  531. //
  532. SentenceData.Add (sentence_data);
  533. }
  534. return ;
  535. }
  536. ////////////////////////////////////////////////////////////////////////////////////
  537. //
  538. // Allocate_New_Surface
  539. //
  540. ////////////////////////////////////////////////////////////////////////////////////
  541. void
  542. Render2DSentenceClass::Allocate_New_Surface (const WCHAR *text, bool justCalcExtents)
  543. {
  544. if (!justCalcExtents)
  545. {
  546. //
  547. // Unlock the last surface (if necessary)
  548. //
  549. if (LockedPtr != NULL) {
  550. CurSurface->Unlock ();
  551. LockedPtr = NULL;
  552. }
  553. }
  554. //
  555. // Calculate the width of the text
  556. //
  557. int text_width = 0;
  558. for (int index = 0; text[index] != 0; index ++) {
  559. text_width += Font->Get_Char_Spacing (text[index]);
  560. }
  561. int char_height = Font->Get_Char_Height ();
  562. //
  563. // Find the best texture size for the remaining text
  564. //
  565. CurrTextureSize = 256;
  566. int best_tex_mem_usage = 999999999;
  567. for (int pow2 = 6; pow2 <= 8; pow2 ++) {
  568. int size = 1 << pow2;
  569. int row_count = (text_width / size) + 1;
  570. int rows_per_texture = size / (char_height + 1);
  571. //
  572. // Can we even fit one character on this texture?
  573. //
  574. if (rows_per_texture > 0) {
  575. //
  576. // How many textures (at this size) would it take to render
  577. // the remaining text?
  578. //
  579. int texture_count = row_count / rows_per_texture;
  580. texture_count = max (texture_count, 1);
  581. //
  582. // Is this the best usage of texture memory we've found yet?
  583. //
  584. int texture_mem_usage = (texture_count * size * size);
  585. if (texture_mem_usage < best_tex_mem_usage) {
  586. CurrTextureSize = size;
  587. best_tex_mem_usage = texture_mem_usage;
  588. }
  589. }
  590. }
  591. //
  592. // Use whichever is larger, the hint or the calculated size
  593. //
  594. CurrTextureSize = max (TextureSizeHint, CurrTextureSize);
  595. if (!justCalcExtents)
  596. {
  597. //
  598. // Release our extra hold on the old surface
  599. //
  600. REF_PTR_RELEASE (CurSurface);
  601. //
  602. // Create the new surface
  603. //
  604. CurSurface = NEW_REF (SurfaceClass, (CurrTextureSize, CurrTextureSize, WW3D_FORMAT_A4R4G4B4));
  605. WWASSERT (CurSurface != NULL);
  606. CurSurface->Add_Ref ();
  607. //
  608. // Add this surface to our list
  609. //
  610. PendingSurfaceStruct surface_info;
  611. surface_info.Surface = CurSurface;
  612. PendingSurfaces.Add (surface_info);
  613. }
  614. //
  615. // Reset to the upper left corner
  616. //
  617. TextureOffset.Set (0, 0);
  618. TextureStartX = 0;
  619. return ;
  620. }
  621. float FindStartingXPos( const WCHAR *text )
  622. {
  623. return 1;
  624. }
  625. ////////////////////////////////////////////////////////////////////////////////////
  626. //
  627. // Build_Sentence_Centered
  628. //
  629. ////////////////////////////////////////////////////////////////////////////////////
  630. void Render2DSentenceClass::Build_Sentence_Centered (const WCHAR *text, int *hkX, int *hkY)
  631. {
  632. float char_height = Font->Get_Char_Height ();
  633. int wordWidth = 0;
  634. int notCenteredHotkeyX = 0;
  635. int notCenteredHotkeyY = 0;
  636. Vector2 extent = Build_Sentence_Not_Centered(text,&notCenteredHotkeyX, &notCenteredHotkeyY, TRUE); //Get_Formatted_Text_Extents(text);
  637. //
  638. // Start fresh
  639. //
  640. Reset_Sentence_Data ();
  641. Cursor.Set (0, 0);
  642. //
  643. // Ensure we have a surface to start with
  644. //
  645. if (CurSurface == NULL) {
  646. Allocate_New_Surface (text);
  647. }
  648. //
  649. // Loop over all the characters in the string
  650. //
  651. bool end = false;
  652. const WCHAR *word;
  653. int word_width = 0;
  654. int line_width = 0;
  655. int charCount = 0;
  656. int wordCount = 0;
  657. int hotKeyPosX = 0;
  658. int hotKeyPosY = 0;
  659. bool calcHotKeyX = false;
  660. bool dontBlit = false;
  661. while (!end)
  662. {
  663. //
  664. // Re-init everything for the next line
  665. //
  666. word = text;
  667. word_width = 0;
  668. line_width = 0;
  669. charCount = 0;
  670. wordCount = 0;
  671. //
  672. //first find the length of the line till we wrap
  673. //
  674. while ( 1 )
  675. {
  676. //
  677. // read a word
  678. //
  679. int charWidth = 0;
  680. while ((*word != 0) && (*word > L' ') && (*word != L'\n')) {
  681. if( ParseHotKey && (*word == L'&') && (*word+1 != 0) && (*word+1 > L' ') && (*word+1 != L'\n'))
  682. {
  683. int offset = 0;
  684. //Added By Saad
  685. if (word_width != 0 )
  686. {
  687. const WCHAR *word_back = word;
  688. *word_back--;
  689. if (*word_back == L' ')
  690. {
  691. line_width -= word_width;
  692. offset =-1;
  693. }
  694. }
  695. //
  696. *word++;
  697. calcHotKeyX = true;
  698. }
  699. charWidth = Font->Get_Char_Spacing (*word++);
  700. word_width += charWidth;
  701. wordCount++;
  702. if (WrapWidth > 0 && word_width >= WrapWidth && useHardWordWrap)
  703. break;
  704. }
  705. //
  706. // If this word is unworthy to be on the current line, decrement the space and break
  707. //
  708. if(WrapWidth > 0 && (line_width + word_width >= WrapWidth))
  709. {
  710. //
  711. //Take care of the case that the word is too big for the allocated space...
  712. //If that's the case, drop out and process the word anyway
  713. //
  714. if(charCount == 0)
  715. {
  716. charCount +=wordCount - 1;
  717. line_width += word_width - charWidth;
  718. if(*word == 0)
  719. end = true;
  720. break;
  721. }
  722. charCount--;
  723. break;
  724. }
  725. //
  726. // if we reached the end of the text, set the values and break, also set the end flag
  727. //
  728. if( *word == 0 )
  729. {
  730. charCount +=wordCount;
  731. line_width += word_width;
  732. end = true;
  733. break;
  734. }
  735. //
  736. // otherwise, increment the counts
  737. //
  738. charCount +=wordCount + 1;
  739. line_width += word_width;
  740. //
  741. // We were some a new line character break and process
  742. //
  743. if(*word != L' ')
  744. break;
  745. //
  746. // add the space to our width
  747. //
  748. word_width = Font->Get_Char_Spacing (*word++);
  749. wordCount = 0;
  750. //Added By Saad
  751. line_width += word_width;
  752. }
  753. //
  754. // we now hold the length of the line and it's width lets set our cursor position to center it
  755. //
  756. Cursor.X = (int)((extent.X - line_width) / 2);
  757. if(Cursor.X < 0)
  758. Cursor.X = 0;
  759. if(calcHotKeyX)
  760. {
  761. calcHotKeyX = false;
  762. hotKeyPosX = Cursor.X + notCenteredHotkeyX;
  763. }
  764. for(int i = 0; i <= charCount; i++) {
  765. WCHAR ch = *text++;
  766. dontBlit = false;
  767. //
  768. // Determine how much horizontal space this character requires
  769. //
  770. if(ParseHotKey && (ch == L'&') && (*text != 0) && (*text > L' ') && (*text != L'\n'))
  771. {
  772. ch = *text++;
  773. dontBlit = true;
  774. }
  775. float char_spacing = Font->Get_Char_Spacing (ch);
  776. bool exceeded_texture_width = ((TextureOffset.I + char_spacing) >= CurrTextureSize);
  777. bool encountered_break_char = (ch == L' ' || ch == L'\n' || ch == 0);
  778. //
  779. // Do we need to record this portion of the sentence to its own chunk?
  780. //
  781. if (exceeded_texture_width || encountered_break_char) {
  782. Record_Sentence_Chunk ();
  783. //
  784. // Adjust the positions
  785. //
  786. Cursor.X += (TextureOffset.I - TextureStartX);
  787. TextureStartX = TextureOffset.I;
  788. //
  789. // Adjust the output coordinates
  790. //
  791. if (ch == L' ') {
  792. Cursor.X += char_spacing;
  793. } else if ((ch == 0 )|| (ch == L'\n')) {
  794. break;
  795. }
  796. //
  797. // Did the text extend past the edge of the texture?
  798. //
  799. if (exceeded_texture_width) {
  800. TextureStartX = 0;
  801. TextureOffset.I = TextureStartX;
  802. TextureOffset.J += char_height;
  803. //
  804. // Did the text extent completely off the texture?
  805. //
  806. if ((TextureOffset.J + char_height) >= CurrTextureSize) {
  807. Allocate_New_Surface (text);
  808. }
  809. }
  810. }
  811. //
  812. // Adjust the output coordinates
  813. //
  814. if (ch != L'\n' && ch != L' ') {
  815. //
  816. // Ensure the surface is locked
  817. //
  818. if (LockedPtr == NULL) {
  819. LockedPtr = (uint16 *)CurSurface->Lock (&LockedStride);
  820. WWASSERT (LockedPtr != NULL);
  821. }
  822. //
  823. // Check to ensure the text will fit on this texture
  824. //
  825. WWASSERT (((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize));
  826. //
  827. // Blit the character to the surface
  828. //
  829. if(!dontBlit)
  830. Font->Blit_Char (ch, LockedPtr, LockedStride, TextureOffset.I, TextureOffset.J);
  831. if (dontBlit) {
  832. // we don't blit for a hot key character. So add extra spacing.
  833. char_spacing += Font->Get_Extra_Overlap();
  834. // Brutal hack #27 Gamma - Bolded M's are just a problem. jba.
  835. if (ch=='M') {
  836. char_spacing++;
  837. }
  838. }
  839. TextureOffset.I += char_spacing;
  840. }
  841. }
  842. //
  843. // reset our cursor and add a line of text to the cursor position
  844. //
  845. Cursor.X = 0;
  846. Cursor.Y += char_height;
  847. line_width = 0;
  848. }
  849. if(hkX)
  850. *hkX = hotKeyPosX;
  851. if(hkX)
  852. *hkY = hotKeyPosY;
  853. return ;
  854. }
  855. ////////////////////////////////////////////////////////////////////////////////////
  856. //
  857. // Build_Sentence_NotCentered
  858. //
  859. ////////////////////////////////////////////////////////////////////////////////////
  860. Vector2 Render2DSentenceClass::Build_Sentence_Not_Centered (const WCHAR *text, int *hkX, int *hkY, bool justCalcExtents)
  861. {
  862. Vector2 cursor = Cursor;
  863. int textureStartX = TextureStartX;
  864. float maxX = 0;
  865. int hotKeyPosX = 0;
  866. int hotKeyPosY = 0;
  867. bool calcHotKeyX = false;
  868. bool dontBlit = false;
  869. Vector2i textureOffset = TextureOffset;
  870. //
  871. // Start fresh
  872. //
  873. if (!justCalcExtents)
  874. {
  875. Reset_Sentence_Data ();
  876. }
  877. Cursor.Set (0, 0);
  878. //
  879. // Ensure we have a surface to start with
  880. //
  881. if (CurSurface == NULL) {
  882. Allocate_New_Surface (text, justCalcExtents);
  883. }
  884. TextureOffset.Set (TEXTURE_OFFSET, 0);
  885. TextureStartX = TEXTURE_OFFSET;
  886. float char_height = Font->Get_Char_Height ();
  887. //
  888. // Loop over all the characters in the string
  889. //
  890. while (text != NULL) {
  891. WCHAR ch = *text++;
  892. dontBlit = false;
  893. //
  894. // Determine how much horizontal space this character requires
  895. //
  896. if(ParseHotKey && (ch == L'&') && (*text != 0) && (*text > L' ') && (*text != L'\n'))
  897. {
  898. hotKeyPosY = Cursor.Y;
  899. if (calcHotKeyX)
  900. hotKeyPosX = 0;
  901. else
  902. hotKeyPosX = Cursor.X + TextureOffset.I -TextureStartX;//TextureOffset.I;
  903. ch = *text++;
  904. dontBlit = true;
  905. }
  906. float char_spacing = Font->Get_Char_Spacing (ch);
  907. bool exceeded_texture_width = ((TextureOffset.I + char_spacing) >= CurrTextureSize);
  908. bool encountered_break_char = (ch == L' ' || ch == L'\n' || ch == 0);
  909. bool wordBiggerThenLine = ((useHardWordWrap) && ( WrapWidth != 0 ) &&((Cursor.X + TextureOffset.I -TextureStartX + char_spacing) >= WrapWidth));
  910. //
  911. // Do we need to record this portion of the sentence to its own chunk?
  912. //
  913. if (exceeded_texture_width || encountered_break_char|| wordBiggerThenLine) {
  914. if (!justCalcExtents)
  915. {
  916. Record_Sentence_Chunk ();
  917. }
  918. //
  919. // Adjust the positions
  920. //
  921. Cursor.X += (TextureOffset.I - TextureStartX);
  922. maxX = max(maxX, Cursor.X);
  923. TextureStartX = TextureOffset.I;
  924. //
  925. // Adjust the output coordinates
  926. //
  927. if (ch == L' ') {
  928. //Cursor.X += char_spacing;
  929. //maxX = max(maxX, Cursor.X);
  930. //
  931. // Check to see if we need to wrap on this word-break
  932. //
  933. if (WrapWidth > 0) {
  934. //
  935. // Find the length of the next word
  936. //
  937. const WCHAR *word = text;
  938. float word_width = char_spacing;
  939. while ((*word != 0) && (*word > L' ')) {
  940. if(ParseHotKey && (*word == L'&') && (*word+1 != 0) && (*word+1 > L' ') && (*word+1 != L'\n'))
  941. *word++;
  942. word_width += Font->Get_Char_Spacing (*word++);
  943. }
  944. //
  945. // Should we wrap the next word?
  946. //
  947. if ((Cursor.X + word_width) >= WrapWidth) {
  948. Cursor.X = 0;
  949. Cursor.Y += char_height;
  950. calcHotKeyX = true;
  951. }
  952. }
  953. } else if (ch == L'\n') {
  954. Cursor.X = 0;
  955. Cursor.Y += char_height;
  956. } else if (ch == 0) {
  957. break;
  958. } else if (wordBiggerThenLine){ // we've entered this loop because we're greater then the wordwrap so we need to force a wordwrap
  959. Cursor.X = 0;
  960. Cursor.Y += char_height;
  961. }
  962. //
  963. // Did the text extend past the edge of the texture?
  964. //
  965. if (exceeded_texture_width) {
  966. TextureStartX = TEXTURE_OFFSET;
  967. TextureOffset.I = TextureStartX;
  968. TextureOffset.J += char_height;
  969. //
  970. // Did the text extent completely off the texture?
  971. //
  972. if ((TextureOffset.J + char_height) >= CurrTextureSize) {
  973. Allocate_New_Surface (text, justCalcExtents);
  974. }
  975. }
  976. }
  977. if (ch != L'\n' ) {
  978. //
  979. // Ensure the surface is locked
  980. //
  981. if (!justCalcExtents)
  982. {
  983. if (LockedPtr == NULL) {
  984. LockedPtr = (uint16 *)CurSurface->Lock (&LockedStride);
  985. WWASSERT (LockedPtr != NULL);
  986. }
  987. }
  988. //
  989. // Check to ensure the text will fit on this texture
  990. //
  991. WWASSERT (((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize));
  992. //
  993. // Blit the character to the surface
  994. //
  995. if (!justCalcExtents && !dontBlit )
  996. {
  997. Font->Blit_Char (ch, LockedPtr, LockedStride, TextureOffset.I, TextureOffset.J);
  998. }
  999. TextureOffset.I += char_spacing;
  1000. }
  1001. }
  1002. Vector2 extent;
  1003. extent.X = maxX + Font->Get_Extra_Overlap();
  1004. extent.Y = Cursor.Y + char_height;
  1005. Cursor = cursor;
  1006. TextureOffset = textureOffset;
  1007. TextureStartX = textureStartX;
  1008. if(hkX)
  1009. *hkX = hotKeyPosX;
  1010. if(hkX)
  1011. *hkY = hotKeyPosY;
  1012. return extent;
  1013. }
  1014. ////////////////////////////////////////////////////////////////////////////////////
  1015. //
  1016. // Build_Sentence
  1017. //
  1018. ////////////////////////////////////////////////////////////////////////////////////
  1019. void
  1020. Render2DSentenceClass::Build_Sentence (const WCHAR *text, int *hkX, int *hkY)
  1021. {
  1022. if (text == NULL) {
  1023. return ;
  1024. }
  1025. if(Centered && (WrapWidth > 0 || wcschr(text,L'\n')))
  1026. Build_Sentence_Centered(text, hkX, hkY);
  1027. else
  1028. Build_Sentence_Not_Centered(text, hkX, hkY);
  1029. return;
  1030. }
  1031. ////////////////////////////////////////////////////////////////////////////////////
  1032. //
  1033. // FontCharsClass
  1034. //
  1035. ////////////////////////////////////////////////////////////////////////////////////
  1036. FontCharsClass::FontCharsClass (void) :
  1037. OldGDIFont( NULL ),
  1038. OldGDIBitmap( NULL ),
  1039. GDIFont( NULL ),
  1040. GDIBitmap( NULL ),
  1041. GDIBitmapBits ( NULL ),
  1042. MemDC( NULL ),
  1043. CurrPixelOffset( 0 ),
  1044. PointSize( 0 ),
  1045. CharHeight( 0 ),
  1046. UnicodeCharArray( NULL ),
  1047. FirstUnicodeChar( 0xFFFF ),
  1048. LastUnicodeChar( 0 ),
  1049. IsBold (false)
  1050. {
  1051. AlternateUnicodeFont = NULL;
  1052. ::memset( ASCIICharArray, 0, sizeof (ASCIICharArray) );
  1053. return ;
  1054. }
  1055. ////////////////////////////////////////////////////////////////////////////////////
  1056. //
  1057. // ~FontCharsClass
  1058. //
  1059. ////////////////////////////////////////////////////////////////////////////////////
  1060. FontCharsClass::~FontCharsClass (void)
  1061. {
  1062. while ( BufferList.Count() ) {
  1063. delete [] BufferList[0];
  1064. BufferList.Delete(0);
  1065. }
  1066. Free_GDI_Font();
  1067. Free_Character_Arrays();
  1068. return ;
  1069. }
  1070. ////////////////////////////////////////////////////////////////////////////////////
  1071. //
  1072. // Get_Char_Data
  1073. //
  1074. ////////////////////////////////////////////////////////////////////////////////////
  1075. const FontCharsClassCharDataStruct *
  1076. FontCharsClass::Get_Char_Data (WCHAR ch)
  1077. {
  1078. const FontCharsClassCharDataStruct *retval = NULL;
  1079. if ( ch < 256 )
  1080. {
  1081. retval = ASCIICharArray[ch];
  1082. }
  1083. else if ( AlternateUnicodeFont && this != AlternateUnicodeFont )
  1084. {
  1085. return AlternateUnicodeFont->Get_Char_Data( ch );
  1086. }
  1087. else
  1088. {
  1089. Grow_Unicode_Array( ch );
  1090. retval = UnicodeCharArray[ch - FirstUnicodeChar];
  1091. }
  1092. //
  1093. // If the character wasn't found, then add it to our list
  1094. //
  1095. if ( retval == NULL ) {
  1096. retval = Store_GDI_Char( ch );
  1097. }
  1098. WWASSERT( retval->Value == ch );
  1099. return retval;
  1100. }
  1101. ////////////////////////////////////////////////////////////////////////////////////
  1102. //
  1103. // Get_Char_Width
  1104. //
  1105. ////////////////////////////////////////////////////////////////////////////////////
  1106. int
  1107. FontCharsClass::Get_Char_Width (WCHAR ch)
  1108. {
  1109. const FontCharsClassCharDataStruct * data = Get_Char_Data( ch );
  1110. if ( data != NULL ) {
  1111. return data->Width;
  1112. }
  1113. return 0;
  1114. }
  1115. ////////////////////////////////////////////////////////////////////////////////////
  1116. //
  1117. // Get_Char_Spacing
  1118. //
  1119. ////////////////////////////////////////////////////////////////////////////////////
  1120. int
  1121. FontCharsClass::Get_Char_Spacing (WCHAR ch)
  1122. {
  1123. const FontCharsClassCharDataStruct * data = Get_Char_Data( ch );
  1124. if ( data != NULL ) {
  1125. if ( data->Width != 0 ) {
  1126. return data->Width - PixelOverlap - CharOverhang;
  1127. }
  1128. }
  1129. return 0;
  1130. }
  1131. ////////////////////////////////////////////////////////////////////////////////////
  1132. //
  1133. // Blit_Char
  1134. //
  1135. ////////////////////////////////////////////////////////////////////////////////////
  1136. void
  1137. FontCharsClass::Blit_Char (WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, int y)
  1138. {
  1139. const FontCharsClassCharDataStruct * data = Get_Char_Data( ch );
  1140. if ( data != NULL && data->Width != 0 ) {
  1141. //
  1142. // Setup the src and destination pointers
  1143. //
  1144. int dest_inc = (dest_stride >> 1);
  1145. uint16 *src_ptr = data->Buffer;
  1146. dest_ptr += (dest_inc * y) + x;
  1147. //
  1148. // Simply copy the data from the src buffer to the destination
  1149. //
  1150. for ( int row = 0; row < CharHeight; row ++ ) {
  1151. for ( int col = 0; col < data->Width; col ++ ) {
  1152. uint16 curData = *src_ptr;
  1153. if (col<PixelOverlap) {
  1154. curData |= dest_ptr[col];
  1155. }
  1156. dest_ptr[col] = curData;
  1157. src_ptr++;
  1158. }
  1159. dest_ptr += dest_inc;
  1160. }
  1161. }
  1162. return ;
  1163. }
  1164. ////////////////////////////////////////////////////////////////////////////////////
  1165. //
  1166. // Store_GDI_Char
  1167. //
  1168. ////////////////////////////////////////////////////////////////////////////////////
  1169. const FontCharsClassCharDataStruct *
  1170. FontCharsClass::Store_GDI_Char (WCHAR ch)
  1171. {
  1172. int width = PointSize * 2;
  1173. int height = PointSize * 2;
  1174. //
  1175. // Draw the character into the memory DC
  1176. //
  1177. RECT rect = { 0, 0, width, height };
  1178. int xOrigin = 0;
  1179. if (ch == 'W') {
  1180. xOrigin = 1;
  1181. }
  1182. ::ExtTextOutW( MemDC, xOrigin, 0, ETO_OPAQUE, &rect, &ch, 1, NULL);
  1183. //
  1184. // Get the size of the character we just drew
  1185. //
  1186. SIZE char_size = { 0 };
  1187. ::GetTextExtentPoint32W( MemDC, &ch, 1, &char_size );
  1188. char_size.cx += PixelOverlap + xOrigin;
  1189. //
  1190. // Get a pointer to the surface that this character should use
  1191. //
  1192. Update_Current_Buffer( char_size.cx );
  1193. uint16* curr_buffer_p = BufferList[BufferList.Count () - 1]->Buffer;
  1194. curr_buffer_p += CurrPixelOffset;
  1195. //
  1196. // Copy the BMP contents to the buffer
  1197. //
  1198. int stride = (((width * 3) + 3) & ~3);
  1199. for (int row = 0; row < char_size.cy; row ++) {
  1200. //
  1201. // Compute the indices into the BMP and surface
  1202. //
  1203. int index = (row * stride);
  1204. //
  1205. // Loop over each column
  1206. //
  1207. for (int col = 0; col < char_size.cx; col ++) {
  1208. //
  1209. // Get the pixel color at this location
  1210. //
  1211. uint8 pixel_value = GDIBitmapBits[index];
  1212. index += 3;
  1213. #ifdef TEST_PLACEMENT
  1214. if (row==CharHeight-1&&col==0) {
  1215. pixel_value = 0xff;
  1216. }
  1217. if (row==CharHeight-2&&col==1) {
  1218. pixel_value = 0xff;
  1219. }
  1220. if (row==0&&col==0) {
  1221. pixel_value = 0xff;
  1222. }
  1223. if (row==1&&col==1) {
  1224. pixel_value = 0xff;
  1225. }
  1226. if (row==CharHeight-1&&col==char_size.cx-1-PixelOverlap) {
  1227. pixel_value = 0xff;
  1228. }
  1229. if (row==CharHeight-2&&col==char_size.cx-2-PixelOverlap) {
  1230. pixel_value = 0xff;
  1231. }
  1232. if (row==0&&col==char_size.cx-1-PixelOverlap) {
  1233. pixel_value = 0xff;
  1234. }
  1235. if (row==1&&col==char_size.cx-2-PixelOverlap) {
  1236. pixel_value = 0xff;
  1237. }
  1238. if (pixel_value == 0x00) {
  1239. pixel_value = 0x40;
  1240. }
  1241. #endif
  1242. uint16 pixel_color = 0;
  1243. if (pixel_value != 0) {
  1244. pixel_color = 0x0FFF;
  1245. }
  1246. //
  1247. // Convert the pixel intensity from 8bit to 4bit and
  1248. // store it in our buffer
  1249. //
  1250. uint8 alpha_value = ((pixel_value >> 4) & 0xF);
  1251. *curr_buffer_p++ = pixel_color | (alpha_value << 12);
  1252. }
  1253. }
  1254. //
  1255. // Save information about this character in our list
  1256. //
  1257. FontCharsClassCharDataStruct *char_data = W3DNEW FontCharsClassCharDataStruct;
  1258. char_data->Value = ch;
  1259. char_data->Width = char_size.cx;
  1260. char_data->Buffer = BufferList[BufferList.Count () - 1]->Buffer + CurrPixelOffset;
  1261. //
  1262. // Insert this character into our array
  1263. //
  1264. if ( ch < 256 ) {
  1265. ASCIICharArray[ch] = char_data;
  1266. } else {
  1267. UnicodeCharArray[ch - FirstUnicodeChar] = char_data;
  1268. }
  1269. //
  1270. // Advance the character position
  1271. //
  1272. CurrPixelOffset += ((char_size.cx+PixelOverlap) * CharHeight);
  1273. //
  1274. // Return the index of the entry we just added
  1275. //
  1276. return char_data;
  1277. }
  1278. ////////////////////////////////////////////////////////////////////////////////////
  1279. //
  1280. // Update_Current_Buffer
  1281. //
  1282. ////////////////////////////////////////////////////////////////////////////////////
  1283. void
  1284. FontCharsClass::Update_Current_Buffer (int char_width)
  1285. {
  1286. //
  1287. // Check to see if we need to allocate a new buffer
  1288. //
  1289. bool needs_new_buffer = (BufferList.Count () == 0);
  1290. if (needs_new_buffer == false) {
  1291. //
  1292. // Would we extend past this buffer?
  1293. //
  1294. if ( (CurrPixelOffset + (char_width * CharHeight)) > CHAR_BUFFER_LEN ) {
  1295. needs_new_buffer = true;
  1296. }
  1297. }
  1298. //
  1299. // Do we need to create a new surface?
  1300. //
  1301. if (needs_new_buffer)
  1302. {
  1303. FontCharsBuffer* new_buffer = W3DNEW FontCharsBuffer;
  1304. BufferList.Add( new_buffer );
  1305. CurrPixelOffset = 0;
  1306. }
  1307. return ;
  1308. }
  1309. ////////////////////////////////////////////////////////////////////////////////////
  1310. //
  1311. // Create_GDI_Font
  1312. //
  1313. ////////////////////////////////////////////////////////////////////////////////////
  1314. void
  1315. FontCharsClass::Create_GDI_Font (const char *font_name)
  1316. {
  1317. HDC screen_dc = ::GetDC ((HWND)WW3D::Get_Window());
  1318. const char *fontToUseForGenerals = "Arial";
  1319. bool doingGenerals = false;
  1320. if (strcmp(font_name, "Generals")==0) {
  1321. font_name = fontToUseForGenerals;
  1322. doingGenerals = true;
  1323. }
  1324. //
  1325. // Calculate the height of the font in logical units
  1326. //
  1327. const int dotsPerInch = 96; // always use 96. jba.
  1328. int font_height = -MulDiv (PointSize, dotsPerInch, 72);
  1329. int fontWidth = 0; // use font default.
  1330. if (doingGenerals) {
  1331. //fontWidth = -font_height*0.35f; //2 pixels tighter.
  1332. fontWidth = -font_height*0.40f; // one pixel tighter
  1333. }
  1334. PixelOverlap = (-font_height)/8;
  1335. // Sanity check in case of perversion. :)
  1336. if (PixelOverlap<0) PixelOverlap = 0;
  1337. if (PixelOverlap>4) PixelOverlap = 4;
  1338. //
  1339. // Create the Windows font
  1340. //
  1341. DWORD bold = IsBold ? FW_BOLD : FW_NORMAL;
  1342. DWORD italic = 0;
  1343. GDIFont = ::CreateFont (font_height, fontWidth, 0, 0, bold, italic,
  1344. FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
  1345. CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
  1346. VARIABLE_PITCH, font_name);
  1347. //
  1348. // Set-up the fields of the BITMAPINFOHEADER
  1349. // Note: Top-down DIBs use negative height in Win32.
  1350. //
  1351. BITMAPINFOHEADER bitmap_info = { 0 };
  1352. bitmap_info.biSize = sizeof (BITMAPINFOHEADER);
  1353. bitmap_info.biWidth = PointSize * 2;
  1354. bitmap_info.biHeight = -(PointSize * 2);
  1355. bitmap_info.biPlanes = 1;
  1356. bitmap_info.biBitCount = 24;
  1357. bitmap_info.biCompression = BI_RGB;
  1358. bitmap_info.biSizeImage = ((PointSize * PointSize * 4) * 3);
  1359. bitmap_info.biXPelsPerMeter = 0;
  1360. bitmap_info.biYPelsPerMeter = 0;
  1361. bitmap_info.biClrUsed = 0;
  1362. bitmap_info.biClrImportant = 0;
  1363. //
  1364. // Create a bitmap that we can access the bits directly of
  1365. //
  1366. GDIBitmap = ::CreateDIBSection ( screen_dc,
  1367. (const BITMAPINFO *)&bitmap_info,
  1368. DIB_RGB_COLORS,
  1369. (void **)&GDIBitmapBits,
  1370. NULL,
  1371. 0L);
  1372. //
  1373. // Create a device context we can select the font and bitmap into
  1374. //
  1375. MemDC = ::CreateCompatibleDC (screen_dc);
  1376. //
  1377. // Release our temporary screen DC
  1378. //
  1379. ::ReleaseDC ((HWND)WW3D::Get_Window(), screen_dc);
  1380. //
  1381. // Now select the BMP and font into the DC
  1382. //
  1383. OldGDIBitmap = (HBITMAP)::SelectObject (MemDC, GDIBitmap);
  1384. OldGDIFont = (HFONT)::SelectObject (MemDC, GDIFont);
  1385. ::SetBkColor (MemDC, RGB (0, 0, 0));
  1386. ::SetTextColor (MemDC, RGB (255, 255, 255));
  1387. //
  1388. // Lookup the pixel height of the font
  1389. //
  1390. TEXTMETRIC text_metric = { 0 };
  1391. ::GetTextMetrics (MemDC, &text_metric);
  1392. CharHeight = text_metric.tmHeight;
  1393. CharAscent = text_metric.tmAscent;
  1394. CharOverhang = text_metric.tmOverhang;
  1395. if (doingGenerals) {
  1396. CharOverhang = 0;
  1397. }
  1398. }
  1399. ////////////////////////////////////////////////////////////////////////////////////
  1400. //
  1401. // Free_GDI_Font
  1402. //
  1403. ////////////////////////////////////////////////////////////////////////////////////
  1404. void
  1405. FontCharsClass::Free_GDI_Font (void)
  1406. {
  1407. //
  1408. // Select the old font back into the DC and delete
  1409. // our font object
  1410. //
  1411. if ( GDIFont != NULL ) {
  1412. ::SelectObject( MemDC, OldGDIFont );
  1413. ::DeleteObject( GDIFont );
  1414. GDIFont = NULL;
  1415. }
  1416. //
  1417. // Select the old bitmap back into the DC and delete
  1418. // our bitmap object
  1419. //
  1420. if ( GDIBitmap != NULL ) {
  1421. ::SelectObject( MemDC, OldGDIBitmap );
  1422. ::DeleteObject( GDIBitmap );
  1423. GDIBitmap = NULL;
  1424. }
  1425. //
  1426. // Delete our memory DC
  1427. //
  1428. if ( MemDC != NULL ) {
  1429. ::DeleteDC( MemDC );
  1430. MemDC = NULL;
  1431. }
  1432. return ;
  1433. }
  1434. ////////////////////////////////////////////////////////////////////////////////////
  1435. //
  1436. // Initialize_GDI_Font
  1437. //
  1438. ////////////////////////////////////////////////////////////////////////////////////
  1439. void
  1440. FontCharsClass::Initialize_GDI_Font (const char *font_name, int point_size, bool is_bold)
  1441. {
  1442. //
  1443. // Build a unique name from the font name and its size
  1444. //
  1445. Name.Format ("%s%d", font_name, point_size);
  1446. //
  1447. // Remember these settings
  1448. //
  1449. GDIFontName = font_name;
  1450. PointSize = point_size;
  1451. IsBold = is_bold;
  1452. //
  1453. // Create the actual font object
  1454. //
  1455. Create_GDI_Font (font_name);
  1456. return ;
  1457. }
  1458. ////////////////////////////////////////////////////////////////////////////////////
  1459. //
  1460. // Is_Font
  1461. //
  1462. ////////////////////////////////////////////////////////////////////////////////////
  1463. bool
  1464. FontCharsClass::Is_Font (const char *font_name, int point_size, bool is_bold)
  1465. {
  1466. bool retval = false;
  1467. //
  1468. // Check to see if both the name and height matches...
  1469. //
  1470. if ( (GDIFontName.Compare_No_Case (font_name) == 0) &&
  1471. (point_size == PointSize) &&
  1472. (is_bold == IsBold))
  1473. {
  1474. retval = true;
  1475. }
  1476. return retval;
  1477. }
  1478. ////////////////////////////////////////////////////////////////////////////////////
  1479. //
  1480. // Grow_Unicode_Array
  1481. //
  1482. ////////////////////////////////////////////////////////////////////////////////////
  1483. void
  1484. FontCharsClass::Grow_Unicode_Array (WCHAR ch)
  1485. {
  1486. //
  1487. // Don't do anything if character is in the ASCII range
  1488. //
  1489. if ( ch < 256 ) {
  1490. return ;
  1491. }
  1492. //
  1493. // Don't do anything if character is in the currently allocated range
  1494. //
  1495. if ( ch >= FirstUnicodeChar && ch <= LastUnicodeChar ) {
  1496. return ;
  1497. }
  1498. uint16 first_index = min( FirstUnicodeChar, ch );
  1499. uint16 last_index = max( LastUnicodeChar, ch );
  1500. uint16 count = (last_index - first_index) + 1;
  1501. //
  1502. // Allocate enough memory to hold the new cells
  1503. //
  1504. FontCharsClassCharDataStruct **new_array = W3DNEWARRAY FontCharsClassCharDataStruct *[count];
  1505. ::memset (new_array, 0, sizeof (FontCharsClassCharDataStruct *) * count);
  1506. //
  1507. // Copy the contents of the old array into the new array
  1508. //
  1509. if ( UnicodeCharArray != NULL ) {
  1510. int start_offset = (FirstUnicodeChar - first_index);
  1511. int old_count = (LastUnicodeChar - FirstUnicodeChar) + 1;
  1512. ::memcpy (&new_array[start_offset], UnicodeCharArray, sizeof (FontCharsClassCharDataStruct *) * old_count);
  1513. //
  1514. // Delete the old array
  1515. //
  1516. delete [] UnicodeCharArray;
  1517. UnicodeCharArray = NULL;
  1518. }
  1519. FirstUnicodeChar = first_index;
  1520. LastUnicodeChar = last_index;
  1521. UnicodeCharArray = new_array;
  1522. return ;
  1523. }
  1524. ////////////////////////////////////////////////////////////////////////////////////
  1525. //
  1526. // Free_Character_Arrays
  1527. //
  1528. ////////////////////////////////////////////////////////////////////////////////////
  1529. void
  1530. FontCharsClass::Free_Character_Arrays (void)
  1531. {
  1532. if ( UnicodeCharArray != NULL ) {
  1533. int count = (LastUnicodeChar - FirstUnicodeChar) + 1;
  1534. //
  1535. // Delete each member of the unicode array
  1536. //
  1537. for (int index = 0; index < count; index ++) {
  1538. if ( UnicodeCharArray[index] != NULL ) {
  1539. delete UnicodeCharArray[index];
  1540. UnicodeCharArray[index] = NULL;
  1541. }
  1542. }
  1543. //
  1544. // Delete the array itself
  1545. //
  1546. delete [] UnicodeCharArray;
  1547. UnicodeCharArray = NULL;
  1548. }
  1549. //
  1550. // Delete each member of the ascii character array
  1551. //
  1552. for (int index = 0; index < 256; index ++) {
  1553. if ( ASCIICharArray[index] != NULL ) {
  1554. delete ASCIICharArray[index];
  1555. ASCIICharArray[index] = NULL;
  1556. }
  1557. }
  1558. return ;
  1559. }