gFont.cc 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "platform/platformFont.h"
  24. #include "debug/profiler.h"
  25. #include "platform/threads/mutex.h"
  26. #include "console/console.h"
  27. #include "io/stream.h"
  28. #include "graphics/gBitmap.h"
  29. #include "io/fileStream.h"
  30. #include "string/findMatch.h"
  31. #include "graphics/TextureManager.h"
  32. #include "graphics/gFont.h"
  33. #include "memory/safeDelete.h"
  34. #include "memory/frameAllocator.h"
  35. #include "string/unicode.h"
  36. #include "zlib.h"
  37. #include "ctype.h" // Needed for isupper and tolower
  38. S32 GFont::smSheetIdCount = 0;
  39. const U32 GFont::csm_fileVersion = 3;
  40. ConsoleFunction(populateFontCacheString, void, 4, 4, "(faceName, size, string) "
  41. "Populate the font cache for the specified font with characters from the specified string."
  42. "@param faceName The font's name\n"
  43. "@param size The size of the font.\n"
  44. "@param string The string to use to fill font cache\n"
  45. "@return No return value.")
  46. {
  47. Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
  48. if(f.isNull())
  49. {
  50. Con::errorf("populateFontCacheString - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
  51. return;
  52. }
  53. if(!f->hasPlatformFont())
  54. {
  55. Con::errorf("populateFontCacheString - font '%s %d' has no platform font! Cannot generate more characters.", argv[1], dAtoi(argv[2]));
  56. return;
  57. }
  58. // This has the side effect of generating character info, including the bitmaps.
  59. f->getStrWidthPrecise(argv[3]);
  60. }
  61. ConsoleFunction(populateFontCacheRange, void, 5, 5, "(faceName, size, rangeStart, rangeEnd) - "
  62. "Populate the font cache for the specified font with Unicode code points in the specified range. "
  63. "Note we only support BMP-0, so code points range from 0 to 65535."
  64. "@param faceName The name of the font\n"
  65. "@param size The size of the font.\n"
  66. "@param rangeStart The initial Unicode point\n"
  67. "@param rangeEnd The final Unicode point in range\n"
  68. "@return No return value")
  69. {
  70. Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
  71. if(f.isNull())
  72. {
  73. Con::errorf("populateFontCacheRange - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
  74. return;
  75. }
  76. U32 rangeStart = dAtoi(argv[3]);
  77. U32 rangeEnd = dAtoi(argv[4]);
  78. if(rangeStart > rangeEnd)
  79. {
  80. Con::errorf("populateFontCacheRange - range start is after end!");
  81. return;
  82. }
  83. if(!f->hasPlatformFont())
  84. {
  85. Con::errorf("populateFontCacheRange - font '%s %d' has no platform font! Cannot generate more characters.", argv[1], dAtoi(argv[2]));
  86. return;
  87. }
  88. // This has the side effect of generating character info, including the bitmaps.
  89. for(U32 i=rangeStart; i<rangeEnd; i++)
  90. {
  91. if(f->isValidChar(i))
  92. f->getCharWidth(i);
  93. else
  94. Con::warnf("populateFontCacheRange - skipping invalid char 0x%x", i);
  95. }
  96. // All done!
  97. }
  98. ConsoleFunction(dumpFontCacheStatus, void, 1, 1, "() Dump a full description "
  99. "of all cached fonts, along with info on the codepoints each contains.\n"
  100. "@return No return value")
  101. {
  102. FindMatch match("*.uft", 4096);
  103. ResourceManager->findMatches(&match);
  104. Con::printf("--------------------------------------------------------------------------");
  105. Con::printf(" Font Cache Usage Report (%d fonts found)", match.numMatches());
  106. for (U32 i = 0; i < (U32)match.numMatches(); i++)
  107. {
  108. char *curMatch = match.matchList[i];
  109. Resource<GFont> font = ResourceManager->load(curMatch);
  110. // Deal with inexplicably missing or failed to load fonts.
  111. if (font.isNull())
  112. {
  113. Con::errorf(" o Couldn't load font : %s", curMatch);
  114. continue;
  115. }
  116. // Ok, dump info!
  117. font->dumpInfo();
  118. }
  119. }
  120. ConsoleFunction(writeFontCache, void, 1, 1, "() force all cached fonts to"
  121. "serialize themselves to the cache."
  122. "@return No return value")
  123. {
  124. FindMatch match("*.uft", 4096);
  125. ResourceManager->findMatches(&match);
  126. Con::printf("--------------------------------------------------------------------------");
  127. Con::printf(" Writing font cache to disk (%d fonts found)", match.numMatches());
  128. for (U32 i = 0; i < (U32)match.numMatches(); i++)
  129. {
  130. char *curMatch = match.matchList[i];
  131. Resource<GFont> font = ResourceManager->load(curMatch);
  132. // Deal with inexplicably missing or failed to load fonts.
  133. if (font.isNull())
  134. {
  135. Con::errorf(" o Couldn't find font : %s", curMatch);
  136. continue;
  137. }
  138. // Ok, dump info!
  139. FileStream stream;
  140. if(ResourceManager->openFileForWrite(stream, curMatch))
  141. {
  142. Con::printf(" o Writing '%s' to disk...", curMatch);
  143. font->write(stream);
  144. stream.close();
  145. }
  146. else
  147. {
  148. Con::errorf(" o Could not open '%s' for write!", curMatch);
  149. }
  150. }
  151. }
  152. ConsoleFunction(populateAllFontCacheString, void, 2, 2, "(string inString) "
  153. "Populate the font cache for all fonts with characters from the specified string.\n"
  154. "@param inString The string to use to set the font caches\n"
  155. "@return No return value.")
  156. {
  157. FindMatch match("*.uft", 4096);
  158. ResourceManager->findMatches(&match);
  159. Con::printf("Populating font cache with string '%s' (%d fonts found)", argv[1], match.numMatches());
  160. for (U32 i = 0; i < (U32)match.numMatches(); i++)
  161. {
  162. char *curMatch = match.matchList[i];
  163. Resource<GFont> font = ResourceManager->load(curMatch);
  164. // Deal with inexplicably missing or failed to load fonts.
  165. if (font.isNull())
  166. {
  167. Con::errorf(" o Couldn't load font : %s", curMatch);
  168. continue;
  169. }
  170. if(!font->hasPlatformFont())
  171. {
  172. Con::errorf("populateAllFontCacheString - font '%s' has no platform font! Cannot generate more characters.", curMatch);
  173. continue;
  174. }
  175. // This has the side effect of generating character info, including the bitmaps.
  176. font->getStrWidthPrecise(argv[1]);
  177. }
  178. }
  179. ConsoleFunction(populateAllFontCacheRange, void, 3, 3, "(rangeStart, rangeEnd) "
  180. "Populate the font cache for all fonts with Unicode code points in the specified range. "
  181. "Note we only support BMP-0, so code points range from 0 to 65535.\n"
  182. "@param rangeStart, rangeEnd The range of the unicode points to populate caches with\n"
  183. "@return No return value")
  184. {
  185. U32 rangeStart = dAtoi(argv[1]);
  186. U32 rangeEnd = dAtoi(argv[2]);
  187. if(rangeStart > rangeEnd)
  188. {
  189. Con::errorf("populateAllFontCacheRange - range start is after end!");
  190. return;
  191. }
  192. FindMatch match("*.uft", 4096);
  193. ResourceManager->findMatches(&match);
  194. Con::printf("Populating font cache with range 0x%x to 0x%x (%d fonts found)", rangeStart, rangeEnd, match.numMatches());
  195. for (U32 i = 0; i < (U32)match.numMatches(); i++)
  196. {
  197. char *curMatch = match.matchList[i];
  198. Resource<GFont> font = ResourceManager->load(curMatch);
  199. // Deal with inexplicably missing or failed to load fonts.
  200. if (font.isNull())
  201. {
  202. Con::errorf(" o Couldn't load font : %s", curMatch);
  203. continue;
  204. }
  205. if(!font->hasPlatformFont())
  206. {
  207. Con::errorf("populateAllFontCacheRange - font '%s' has no platform font! Cannot generate more characters.", curMatch);
  208. continue;
  209. }
  210. // This has the side effect of generating character info, including the bitmaps.
  211. Con::printf(" o Populating font '%s'", curMatch);
  212. for(U32 i=rangeStart; i<rangeEnd; i++)
  213. {
  214. if(font->isValidChar(i))
  215. font->getCharWidth(i);
  216. else
  217. Con::warnf("populateAllFontCacheRange - skipping invalid char 0x%x", i);
  218. }
  219. }
  220. // All done!
  221. }
  222. ConsoleFunction(exportCachedFont, void, 6, 6, "(fontName, size, fileName, padding, kerning) - "
  223. "Export specified font to the specified filename as a PNG. The "
  224. "image can then be processed in Photoshop or another tool and "
  225. "reimported using importCachedFont. Characters in the font are"
  226. "exported as one long strip.\n"
  227. "@param fontName The name of the font to export.\n"
  228. "@param size The size of the font\n"
  229. "@param fileName The export file name.\n"
  230. "@param padding Desired padding settings.\n"
  231. "@param kerning Kerning settings (space between elements)\n"
  232. "@return No return value.")
  233. {
  234. // Read in some params.
  235. const char *fileName = argv[3];
  236. S32 padding = dAtoi(argv[4]);
  237. S32 kerning = dAtoi(argv[5]);
  238. // Tell the font to export itself.
  239. Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
  240. if(f.isNull())
  241. {
  242. Con::errorf("populateFontCacheString - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
  243. return;
  244. }
  245. f->exportStrip(fileName, padding, kerning);
  246. }
  247. ConsoleFunction(importCachedFont, void, 6, 6, "(fontName, size, fileName, padding, kerning) "
  248. "Import an image strip from exportCachedFont. Call with the "
  249. "same parameters you called exportCachedFont."
  250. "@param fontName The name of the font to import.\n"
  251. "@param size The size of the font\n"
  252. "@param fileName The imported file name.\n"
  253. "@param padding Desired padding settings.\n"
  254. "@param kerning Kerning settings (space between elements)\n"
  255. "@return No return value.")
  256. {
  257. // Read in some params.
  258. const char *fileName = argv[3];
  259. S32 padding = dAtoi(argv[4]);
  260. S32 kerning = dAtoi(argv[5]);
  261. // Tell the font to import itself.
  262. Resource<GFont> f = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
  263. if(f.isNull())
  264. {
  265. Con::errorf("populateFontCacheString - could not load font '%s %d'!", argv[1], dAtoi(argv[2]));
  266. return;
  267. }
  268. f->importStrip(fileName, padding, kerning);
  269. }
  270. ConsoleFunction(duplicateCachedFont, void, 4, 4, "(oldFontName, oldFontSize, newFontName) "
  271. "Copy the specified old font to a new name. The new copy will not have a "
  272. "platform font backing it, and so will never have characters added to it. "
  273. "But this is useful for making copies of fonts to add postprocessing effects "
  274. "to via exportCachedFont.\n"
  275. "@param oldFontName The original font.\n"
  276. "@param oldFontSize The original font's size property.\n"
  277. "@param newFontName The name to set the copy to.\n"
  278. "@return No return value.")
  279. {
  280. char newFontFile[256];
  281. GFont::getFontCacheFilename(argv[3], dAtoi(argv[2]), 256, newFontFile);
  282. // Load the original font.
  283. Resource<GFont> font = GFont::create(argv[1], dAtoi(argv[2]), Con::getVariable("$GUI::fontCacheDirectory"));
  284. // Deal with inexplicably missing or failed to load fonts.
  285. if (font.isNull())
  286. {
  287. Con::errorf(" o Couldn't find font : %s", newFontFile);
  288. return;
  289. }
  290. // Ok, dump info!
  291. FileStream stream;
  292. if(ResourceManager->openFileForWrite(stream, newFontFile))
  293. {
  294. Con::printf(" o Writing duplicate font '%s' to disk...", newFontFile);
  295. font->write(stream);
  296. stream.close();
  297. }
  298. else
  299. {
  300. Con::errorf(" o Could not open '%s' for write!", newFontFile);
  301. }
  302. }
  303. static PlatformFont* createSafePlatformFont(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET)
  304. {
  305. PlatformFont *platFont = createPlatformFont(name, size, charset);
  306. if (platFont == NULL)
  307. {
  308. Con::errorf("Loading platform font failed, trying font fallbacks...");
  309. // Couldn't load the requested font. This probably will be common
  310. // since many unix boxes don't have arial or lucida console installed.
  311. // Attempt to map the font name into a font we're pretty sure exist
  312. // Lucida Console is a common code & console font on windows, and
  313. // Monaco is the recommended code & console font on mac.
  314. // this is the name of the final fallback font.
  315. const char* fallback = "Helvetica";
  316. if(dStricmp(name, fallback) == 0)
  317. {
  318. Con::errorf("Font fallback utterly failed.");
  319. return NULL;
  320. }
  321. else if (dStricmp(name, "arial") == 0)
  322. fallback = "Helvetica";
  323. else if (dStricmp(name, "lucida console") == 0)
  324. fallback = "Monaco";
  325. else if (dStricmp(name, "monaco") == 0)
  326. fallback = "Courier";
  327. platFont = createSafePlatformFont(fallback, size, charset);
  328. }
  329. return platFont;
  330. }
  331. ResourceInstance* constructNewFont(Stream& stream)
  332. {
  333. GFont *ret = new GFont;
  334. if(!ret->read(stream))
  335. {
  336. SAFE_DELETE(ret);
  337. }
  338. if(ret)
  339. {
  340. ret->mPlatformFont = createSafePlatformFont(ret->mFaceName, ret->mSize, ret->mCharSet);
  341. }
  342. return ret;
  343. }
  344. void GFont::getFontCacheFilename(const char *faceName, U32 size, U32 buffLen, char *outBuff)
  345. {
  346. dSprintf(outBuff, buffLen, "%s/%s %d (%s).uft", Con::getVariable("$GUI::fontCacheDirectory"), faceName, size, getFontCharSetName(0));
  347. }
  348. Resource<GFont> GFont::create(const char *faceName, U32 size, const char *cacheDirectory, U32 charset /* = TGE_ANSI_CHARSET */)
  349. {
  350. char buf[256];
  351. dSprintf(buf, sizeof(buf), "%s/%s %d (%s).uft", cacheDirectory, faceName, size, getFontCharSetName(charset));
  352. Resource<GFont> ret = ResourceManager->load(buf);
  353. if(bool(ret))
  354. {
  355. ret->mGFTFile = StringTable->insert(buf);
  356. return ret;
  357. }
  358. PlatformFont *platFont = createSafePlatformFont(faceName, size, charset);
  359. AssertFatal(platFont, "platFont is null");
  360. GFont *resFont = new GFont;
  361. resFont->mPlatformFont = platFont;
  362. resFont->addSheet();
  363. resFont->mGFTFile = StringTable->insert(buf);
  364. resFont->mFaceName = StringTable->insert(faceName);
  365. resFont->mSize = size;
  366. resFont->mCharSet = charset;
  367. resFont->mHeight = platFont->getFontHeight();
  368. resFont->mBaseline = platFont->getFontBaseLine();
  369. resFont->mAscent = platFont->getFontBaseLine();
  370. resFont->mDescent = platFont->getFontHeight() - platFont->getFontBaseLine();
  371. ResourceManager->add(buf, resFont, false);
  372. return ResourceManager->load(buf);
  373. }
  374. //-------------------------------------------------------------------------
  375. GFont::GFont()
  376. {
  377. VECTOR_SET_ASSOCIATION(mCharInfoList);
  378. VECTOR_SET_ASSOCIATION(mTextureSheets);
  379. for (U32 i = 0; i < (sizeof(mRemapTable) / sizeof(S32)); i++)
  380. mRemapTable[i] = -1;
  381. mCurX = mCurY = mCurSheet = -1;
  382. mPlatformFont = NULL;
  383. mGFTFile = NULL;
  384. mFaceName = NULL;
  385. mSize = 0;
  386. mCharSet = 0;
  387. mNeedSave = false;
  388. mMutex = Mutex::createMutex();
  389. }
  390. GFont::~GFont()
  391. {
  392. // Need to stop this for now!
  393. mNeedSave = false;
  394. if(mNeedSave)
  395. {
  396. FileStream stream;
  397. if(ResourceManager->openFileForWrite(stream, mGFTFile))
  398. {
  399. write(stream);
  400. stream.close();
  401. }
  402. }
  403. S32 i;
  404. for(i = 0;i < mCharInfoList.size();i++)
  405. {
  406. SAFE_DELETE_ARRAY(mCharInfoList[i].bitmapData);
  407. }
  408. //Luma: decrement reference of the texture handles too
  409. for(i=0;i<mTextureSheets.size();i++)
  410. {
  411. mTextureSheets[i] = 0;
  412. }
  413. SAFE_DELETE(mPlatformFont);
  414. Mutex::destroyMutex(mMutex);
  415. }
  416. void GFont::dumpInfo()
  417. {
  418. // Number and extent of mapped characters?
  419. U32 mapCount = 0, mapBegin=0xFFFF, mapEnd=0;
  420. for(U32 i=0; i<0x10000; i++)
  421. {
  422. if(mRemapTable[i] != -1)
  423. {
  424. mapCount++;
  425. if(i<mapBegin) mapBegin = i;
  426. if(i>mapEnd) mapEnd = i;
  427. }
  428. }
  429. // Let's write out all the info we can on this font.
  430. Con::printf(" '%s' %dpt", mFaceName, mSize);
  431. Con::printf(" - %d texture sheets, %d mapped characters.", mTextureSheets.size(), mapCount);
  432. if(mapCount)
  433. Con::printf(" - Codepoints range from 0x%x to 0x%x.", mapBegin, mapEnd);
  434. else
  435. Con::printf(" - No mapped codepoints.", mapBegin, mapEnd);
  436. Con::printf(" - Platform font is %s.", (mPlatformFont ? "present" : "not present") );
  437. }
  438. //////////////////////////////////////////////////////////////////////////
  439. bool GFont::loadCharInfo(const UTF16 ch)
  440. {
  441. if(mRemapTable[ch] != -1)
  442. return true; // Not really an error
  443. if(mPlatformFont && mPlatformFont->isValidChar(ch))
  444. {
  445. Mutex::lockMutex(mMutex); // the CharInfo returned by mPlatformFont is static data, must protect from changes.
  446. PlatformFont::CharInfo &ci = mPlatformFont->getCharInfo(ch);
  447. if(ci.bitmapData)
  448. addBitmap(ci);
  449. mCharInfoList.push_back(ci);
  450. mRemapTable[ch] = mCharInfoList.size() - 1;
  451. //don't save UFTs on the iPhone
  452. #ifndef TORQUE_OS_IOS
  453. mNeedSave = true;
  454. #endif
  455. Mutex::unlockMutex(mMutex);
  456. return true;
  457. }
  458. return false;
  459. }
  460. void GFont::addBitmap(PlatformFont::CharInfo &charInfo)
  461. {
  462. // If this is called inside a glBegin - glEnd block, the texture will not be
  463. // updated properly.
  464. U32 nextCurX = U32(mCurX + charInfo.width ); /*7) & ~0x3;*/
  465. U32 nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
  466. // These are here for postmortem debugging.
  467. bool routeA = false, routeB = false;
  468. if(mCurSheet == -1 || nextCurY >= TextureSheetSize)
  469. {
  470. routeA = true;
  471. addSheet();
  472. // Recalc our nexts.
  473. nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
  474. nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
  475. }
  476. if( nextCurX >= TextureSheetSize)
  477. {
  478. routeB = true;
  479. mCurX = 0;
  480. mCurY = nextCurY;
  481. // Recalc our nexts.
  482. nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
  483. nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
  484. }
  485. // Check the Y once more - sometimes we advance to a new row and run off
  486. // the end.
  487. if(nextCurY >= TextureSheetSize)
  488. {
  489. routeA = true;
  490. addSheet();
  491. // Recalc our nexts.
  492. nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3;
  493. nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3;
  494. }
  495. charInfo.bitmapIndex = mCurSheet;
  496. charInfo.xOffset = mCurX;
  497. charInfo.yOffset = mCurY;
  498. mCurX = nextCurX;
  499. GBitmap *bmp = mTextureSheets[mCurSheet].getBitmap();
  500. AssertFatal(bmp, "GFont::addBitmap - null texture sheet bitmap!");
  501. AssertFatal(bmp->getFormat() == GBitmap::Alpha, "GFont::addBitmap - cannot added characters to non-greyscale textures!");
  502. // [neo, 5/7/2007 - #3050]
  503. // If we get large font sizes charInfo.height/width will be larger than TextureSheetSize
  504. // and as GBitmap::getAddress() does no range checking the following will overrun memory!
  505. // Added checks against TextureSheetSize
  506. for( U32 y = 0; y < charInfo.height; y++ )
  507. {
  508. S32 dy = y + charInfo.yOffset;
  509. if( dy >= TextureSheetSize )
  510. break;
  511. for( U32 x = 0; x < charInfo.width; x++ )
  512. {
  513. S32 dx = x + charInfo.xOffset;
  514. if( dx >= TextureSheetSize )
  515. break;
  516. *bmp->getAddress( dx, dy ) = charInfo.bitmapData[ y * charInfo.width + x ];
  517. }
  518. }
  519. mTextureSheets[mCurSheet].refresh();
  520. }
  521. void GFont::addSheet()
  522. {
  523. char buf[30];
  524. dSprintf(buf, sizeof(buf), "newfont_%d", smSheetIdCount++);
  525. GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, GBitmap::Alpha);
  526. // Set everything to transparent.
  527. U8 *bits = bitmap->getWritableBits();
  528. dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize);
  529. TextureHandle handle = TextureHandle(buf, bitmap, TextureHandle::BitmapKeepTexture);
  530. handle.setFilter(GL_NEAREST);
  531. mTextureSheets.increment();
  532. constructInPlace(&mTextureSheets.last());
  533. mTextureSheets.last() = handle;
  534. mCurX = 0;
  535. mCurY = 0;
  536. mCurSheet = mTextureSheets.size() - 1;
  537. }
  538. //////////////////////////////////////////////////////////////////////////
  539. const PlatformFont::CharInfo &GFont::getCharInfo(const UTF16 in_charIndex)
  540. {
  541. PROFILE_START(NewFontGetCharInfo);
  542. AssertFatal(in_charIndex, "GFont::getCharInfo - can't get info for char 0!");
  543. if(mRemapTable[in_charIndex] == -1)
  544. {
  545. loadCharInfo(in_charIndex);
  546. }
  547. AssertFatal(mRemapTable[in_charIndex] != -1, "No remap info for this character");
  548. PROFILE_END();
  549. // if we still have no character info, return the default char info.
  550. if(mRemapTable[in_charIndex] == -1)
  551. return getDefaultCharInfo();
  552. else
  553. return mCharInfoList[mRemapTable[in_charIndex]];
  554. }
  555. const PlatformFont::CharInfo &GFont::getDefaultCharInfo()
  556. {
  557. static PlatformFont::CharInfo c;
  558. // c is initialized by the CharInfo default constructor.
  559. return c;
  560. }
  561. //////////////////////////////////////////////////////////////////////////
  562. U32 GFont::getStrWidth(const UTF8* in_pString)
  563. {
  564. AssertFatal(in_pString != NULL, "GFont::getStrWidth: String is NULL, width is undefined");
  565. // If we ain't running debug...
  566. if (in_pString == NULL || *in_pString == '\0')
  567. return 0;
  568. return getStrNWidth(in_pString, dStrlen(in_pString));
  569. }
  570. U32 GFont::getStrWidthPrecise(const UTF8* in_pString)
  571. {
  572. AssertFatal(in_pString != NULL, "GFont::getStrWidth: String is NULL, height is undefined");
  573. // If we ain't running debug...
  574. if (in_pString == NULL)
  575. return 0;
  576. return getStrNWidthPrecise(in_pString, dStrlen(in_pString));
  577. }
  578. //////////////////////////////////////////////////////////////////////////
  579. U32 GFont::getStrNWidth(const UTF8 *str, U32 n)
  580. {
  581. // UTF8 conversion is expensive. Avoid converting in a tight loop.
  582. FrameTemp<UTF16> str16(n + 1);
  583. convertUTF8toUTF16(str, str16, n+1);
  584. return getStrNWidth(str16, dStrlen(str16));
  585. }
  586. U32 GFont::getStrNWidth(const UTF16 *str, U32 n)
  587. {
  588. AssertFatal(str != NULL, "GFont::getStrNWidth: String is NULL");
  589. if (str == NULL || str[0] == '\0' || n == 0)
  590. return 0;
  591. U32 totWidth = 0;
  592. UTF16 curChar;
  593. U32 charCount;
  594. for(charCount = 0; charCount < n; charCount++)
  595. {
  596. curChar = str[charCount];
  597. if(curChar == '\0')
  598. break;
  599. if(isValidChar(curChar))
  600. {
  601. const PlatformFont::CharInfo& rChar = getCharInfo(curChar);
  602. totWidth += rChar.xIncrement;
  603. }
  604. else if (curChar == dT('\t'))
  605. {
  606. const PlatformFont::CharInfo& rChar = getCharInfo(dT(' '));
  607. totWidth += rChar.xIncrement * TabWidthInSpaces;
  608. }
  609. }
  610. return(totWidth);
  611. }
  612. U32 GFont::getStrNWidthPrecise(const UTF8 *str, U32 n)
  613. {
  614. FrameTemp<UTF16> str16(n + 1);
  615. convertUTF8toUTF16(str, str16, n);
  616. return getStrNWidthPrecise(str16, n);
  617. }
  618. U32 GFont::getStrNWidthPrecise(const UTF16 *str, U32 n)
  619. {
  620. AssertFatal(str != NULL, "GFont::getStrNWidth: String is NULL");
  621. if (str == NULL || str[0] == '\0' || n == 0)
  622. return(0);
  623. U32 totWidth = 0;
  624. UTF16 curChar;
  625. U32 charCount = 0;
  626. for(charCount = 0; charCount < n; charCount++)
  627. {
  628. curChar = str[charCount];
  629. if(curChar == '\0')
  630. break;
  631. if(isValidChar(curChar))
  632. {
  633. const PlatformFont::CharInfo& rChar = getCharInfo(curChar);
  634. totWidth += rChar.xIncrement;
  635. }
  636. else if (curChar == dT('\t'))
  637. {
  638. const PlatformFont::CharInfo& rChar = getCharInfo(dT(' '));
  639. totWidth += rChar.xIncrement * TabWidthInSpaces;
  640. }
  641. }
  642. UTF16 endChar = str[getMin(charCount,n-1)];
  643. if (isValidChar(endChar))
  644. {
  645. const PlatformFont::CharInfo& rChar = getCharInfo(endChar);
  646. if ((S32)rChar.width > rChar.xIncrement)
  647. totWidth += (rChar.width - rChar.xIncrement);
  648. }
  649. return(totWidth);
  650. }
  651. U32 GFont::getBreakPos(const UTF16 *str16, U32 slen, U32 width, bool breakOnWhitespace)
  652. {
  653. // Some early out cases.
  654. if(slen==0)
  655. return 0;
  656. U32 ret = 0;
  657. U32 lastws = 0;
  658. UTF16 c;
  659. U32 charCount = 0;
  660. for( charCount=0; charCount < slen; charCount++)
  661. {
  662. c = str16[charCount];
  663. if(c == '\0')
  664. break;
  665. if(c == dT('\t'))
  666. c = dT(' ');
  667. if(!isValidChar(c))
  668. {
  669. ret++;
  670. continue;
  671. }
  672. if(c == dT(' '))
  673. lastws = ret+1;
  674. const PlatformFont::CharInfo& rChar = getCharInfo(c);
  675. if(rChar.width > width || rChar.xIncrement > (S32)width)
  676. {
  677. if(lastws && breakOnWhitespace)
  678. return lastws;
  679. return ret;
  680. }
  681. width -= rChar.xIncrement;
  682. ret++;
  683. }
  684. return ret;
  685. }
  686. void GFont::wrapString(const UTF8 *txt, U32 lineWidth, Vector<U32> &startLineOffset, Vector<U32> &lineLen)
  687. {
  688. Con::errorf("GFont::wrapString(): Not yet converted to be UTF-8 safe");
  689. startLineOffset.clear();
  690. lineLen.clear();
  691. if (!txt || !txt[0] || lineWidth < getCharWidth('W')) //make sure the line width is greater then a single character
  692. return;
  693. U32 len = dStrlen(txt);
  694. U32 startLine;
  695. for (U32 i = 0; i < len;)
  696. {
  697. startLine = i;
  698. startLineOffset.push_back(startLine);
  699. // loop until the string is too large
  700. bool needsNewLine = false;
  701. U32 lineStrWidth = 0;
  702. for (; i < len; i++)
  703. {
  704. if(isValidChar(txt[i]))
  705. {
  706. lineStrWidth += getCharInfo(txt[i]).xIncrement;
  707. if ( txt[i] == '\n' || lineStrWidth > lineWidth )
  708. {
  709. needsNewLine = true;
  710. break;
  711. }
  712. }
  713. }
  714. if (!needsNewLine)
  715. {
  716. // we are done!
  717. lineLen.push_back(i - startLine);
  718. return;
  719. }
  720. // now determine where to put the newline
  721. // else we need to backtrack until we find a either space character
  722. // or \\ character to break up the line.
  723. S32 j;
  724. for (j = i - 1; j >= (S32)startLine; j--)
  725. {
  726. if (dIsspace(txt[j]))
  727. break;
  728. }
  729. if (j < (S32)startLine)
  730. {
  731. // the line consists of a single word!
  732. // So, just break up the word
  733. j = i - 1;
  734. }
  735. lineLen.push_back(j - startLine);
  736. i = j;
  737. // now we need to increment through any space characters at the
  738. // beginning of the next line
  739. for (i++; i < len; i++)
  740. {
  741. if (!dIsspace(txt[i]) || txt[i] == '\n')
  742. break;
  743. }
  744. }
  745. }
  746. //////////////////////////////////////////////////////////////////////////
  747. bool GFont::read(Stream& io_rStream)
  748. {
  749. // Handle versioning
  750. U32 version;
  751. io_rStream.read(&version);
  752. if(version != csm_fileVersion)
  753. return false;
  754. char buf[256];
  755. io_rStream.readString(buf);
  756. mFaceName = StringTable->insert(buf);
  757. io_rStream.read(&mSize);
  758. io_rStream.read(&mCharSet);
  759. io_rStream.read(&mHeight);
  760. io_rStream.read(&mBaseline);
  761. io_rStream.read(&mAscent);
  762. io_rStream.read(&mDescent);
  763. U32 size = 0;
  764. io_rStream.read(&size);
  765. mCharInfoList.setSize(size);
  766. U32 i;
  767. for(i = 0; i < size; i++)
  768. {
  769. PlatformFont::CharInfo *ci = &mCharInfoList[i];
  770. io_rStream.read(&ci->bitmapIndex);
  771. io_rStream.read(&ci->xOffset);
  772. io_rStream.read(&ci->yOffset);
  773. io_rStream.read(&ci->width);
  774. io_rStream.read(&ci->height);
  775. io_rStream.read(&ci->xOrigin);
  776. io_rStream.read(&ci->yOrigin);
  777. io_rStream.read(&ci->xIncrement);
  778. ci->bitmapData = NULL;
  779. }
  780. U32 numSheets = 0;
  781. io_rStream.read(&numSheets);
  782. for(i = 0; i < numSheets; i++)
  783. {
  784. GBitmap *bmp = new GBitmap;
  785. if(!bmp->readPNG(io_rStream))
  786. {
  787. delete bmp;
  788. return false;
  789. }
  790. char buf[30];
  791. dSprintf(buf, sizeof(buf), "font_%d", smSheetIdCount++);
  792. mTextureSheets.increment();
  793. constructInPlace(&mTextureSheets.last());
  794. mTextureSheets.last() = TextureHandle(buf, bmp, TextureHandle::BitmapKeepTexture);
  795. mTextureSheets.last().setFilter(GL_NEAREST);;
  796. }
  797. // Read last position info
  798. io_rStream.read(&mCurX);
  799. io_rStream.read(&mCurY);
  800. io_rStream.read(&mCurSheet);
  801. // Read the remap table.
  802. U32 minGlyph, maxGlyph;
  803. io_rStream.read(&minGlyph);
  804. io_rStream.read(&maxGlyph);
  805. if(maxGlyph >= minGlyph)
  806. {
  807. // Length of buffer..
  808. U32 buffLen;
  809. io_rStream.read(&buffLen);
  810. // Read the buffer.
  811. FrameTemp<S32> inBuff(buffLen);
  812. io_rStream.read(buffLen, inBuff);
  813. // Decompress.
  814. uLongf destLen = (maxGlyph-minGlyph+1)*sizeof(S32);
  815. uncompress((Bytef*)&mRemapTable[minGlyph], &destLen, (Bytef*)(S32*)inBuff, buffLen);
  816. AssertISV(destLen == (maxGlyph-minGlyph+1)*sizeof(S32), "GFont::read - invalid remap table data!");
  817. // Make sure we've got the right endianness.
  818. for(i = minGlyph; i <= maxGlyph; i++) {
  819. mRemapTable[i] = convertBEndianToHost(mRemapTable[i]);
  820. if( mRemapTable[i] == -1 ) {
  821. Con::errorf( "bogus mRemapTable[i] value in %s %i", mFaceName, mSize );
  822. }
  823. }
  824. }
  825. return (io_rStream.getStatus() == Stream::Ok);
  826. }
  827. bool GFont::write(Stream& stream)
  828. {
  829. // Handle versioning
  830. stream.write(csm_fileVersion);
  831. // Write font info
  832. stream.writeString(mFaceName);
  833. stream.write(mSize);
  834. stream.write(mCharSet);
  835. stream.write(mHeight);
  836. stream.write(mBaseline);
  837. stream.write(mAscent);
  838. stream.write(mDescent);
  839. // Get the min/max we have values for, and only write that range out.
  840. S32 minGlyph = S32_MAX, maxGlyph = 0;
  841. S32 i;
  842. for(i = 0; i < 65536; i++)
  843. {
  844. if(mRemapTable[i] != -1)
  845. {
  846. if(i > maxGlyph) maxGlyph = i;
  847. if(i < minGlyph) minGlyph = i;
  848. }
  849. }
  850. //-Mat make sure all our character info is good before writing it
  851. for(i = minGlyph; i <= maxGlyph; i++) {
  852. if( mRemapTable[i] == -1 ) {
  853. //-Mat get info and try this again
  854. getCharInfo(i);
  855. if( mRemapTable[i] == -1 ) {
  856. Con::errorf( "GFont::write() couldn't get character info for char %i", i);
  857. }
  858. }
  859. }
  860. // Write char info list
  861. stream.write(U32(mCharInfoList.size()));
  862. for(i = 0; i < mCharInfoList.size(); i++)
  863. {
  864. const PlatformFont::CharInfo *ci = &mCharInfoList[i];
  865. stream.write(ci->bitmapIndex);
  866. stream.write(ci->xOffset);
  867. stream.write(ci->yOffset);
  868. stream.write(ci->width);
  869. stream.write(ci->height);
  870. stream.write(ci->xOrigin);
  871. stream.write(ci->yOrigin);
  872. stream.write(ci->xIncrement);
  873. }
  874. stream.write(mTextureSheets.size());
  875. for(i = 0; i < mTextureSheets.size(); i++) {
  876. mTextureSheets[i].getBitmap()->writePNG(stream);
  877. }
  878. stream.write(mCurX);
  879. stream.write(mCurY);
  880. stream.write(mCurSheet);
  881. stream.write(minGlyph);
  882. stream.write(maxGlyph);
  883. // Skip it if we don't have any glyphs to do...
  884. if(maxGlyph >= minGlyph)
  885. {
  886. // Put everything big endian, to be consistent. Do this inplace.
  887. for(i = minGlyph; i <= maxGlyph; i++)
  888. mRemapTable[i] = convertHostToBEndian(mRemapTable[i]);
  889. {
  890. // Compress.
  891. const U32 buffSize = 128 * 1024;
  892. FrameTemp<S32> outBuff(buffSize);
  893. uLongf destLen = buffSize * sizeof(S32);
  894. compress2((Bytef*)(S32*)outBuff, &destLen, (Bytef*)(S32*)&mRemapTable[minGlyph], (maxGlyph-minGlyph+1)*sizeof(S32), 9);
  895. // Write out.
  896. stream.write((U32)destLen);
  897. stream.write(destLen, outBuff);
  898. }
  899. // Put us back to normal.
  900. for(i = minGlyph; i <= maxGlyph; i++) {
  901. mRemapTable[i] = convertBEndianToHost(mRemapTable[i]);
  902. if( mRemapTable[i] == -1 ) {
  903. Con::errorf( "bogus mRemapTable[i] value in %s %i", mFaceName, mSize );
  904. }
  905. }
  906. }
  907. return (stream.getStatus() == Stream::Ok);
  908. }
  909. void GFont::exportStrip(const char *fileName, U32 padding, U32 kerning)
  910. {
  911. // Figure dimensions of our strip by iterating over all the char infos.
  912. U32 totalHeight = 0;
  913. U32 totalWidth = 0;
  914. S32 heightMin=0, heightMax=0;
  915. for(S32 i=0; i<mCharInfoList.size(); i++)
  916. {
  917. totalWidth += mCharInfoList[i].width + kerning + 2*padding;
  918. heightMin = getMin((S32)heightMin, (S32)getBaseline() - (S32)mCharInfoList[i].yOrigin);
  919. heightMax = getMax((S32)heightMax, (S32)getBaseline() - (S32)mCharInfoList[i].yOrigin + (S32)mCharInfoList[i].height);
  920. }
  921. totalHeight = heightMax - heightMin + 2*padding;
  922. // Make the bitmap.
  923. GBitmap gb(totalWidth, totalHeight, false, mTextureSheets[0].getBitmap()->getFormat());
  924. dMemset(gb.getWritableBits(), 0, sizeof(U8) * totalHeight * totalWidth );
  925. // Ok, copy some rects, taking into account padding, kerning, offset.
  926. U32 curWidth = kerning + padding;
  927. for(S32 i=0; i<mCharInfoList.size(); i++)
  928. {
  929. // Skip invalid stuff.
  930. if(mCharInfoList[i].bitmapIndex == -1 || mCharInfoList[i].height == 0 || mCharInfoList[i].width == 0)
  931. continue;
  932. // Copy the rect.
  933. U32 bitmap = mCharInfoList[i].bitmapIndex;
  934. RectI ri(mCharInfoList[i].xOffset, mCharInfoList[i].yOffset, mCharInfoList[i].width, mCharInfoList[i].height );
  935. Point2I outRi(curWidth, padding + getBaseline() - mCharInfoList[i].yOrigin);
  936. gb.copyRect(mTextureSheets[bitmap].getBitmap(), ri, outRi);
  937. // Advance.
  938. curWidth += mCharInfoList[i].width + kerning + 2*padding;
  939. }
  940. // Write the image!
  941. FileStream fs;
  942. if(!ResourceManager->openFileForWrite(fs, fileName))
  943. {
  944. Con::errorf("GFont::exportStrip - failed to open '%s' for writing.", fileName);
  945. return;
  946. }
  947. // Done!
  948. gb.writePNG(fs, false);
  949. }
  950. /// Used for repacking in GFont::importStrip.
  951. struct GlyphMap
  952. {
  953. U32 charId;
  954. GBitmap *bitmap;
  955. };
  956. static S32 QSORT_CALLBACK GlyphMapCompare(const void *a, const void *b)
  957. {
  958. S32 ha = ((GlyphMap *) a)->bitmap->height;
  959. S32 hb = ((GlyphMap *) b)->bitmap->height;
  960. return hb - ha;
  961. }
  962. void GFont::importStrip(const char *fileName, U32 padding, U32 kerning)
  963. {
  964. // Wipe our texture sheets, and reload bitmap data from the specified file.
  965. // Also deal with kerning.
  966. // Also, we may have to load RGBA instead of RGB.
  967. // Wipe our texture sheets.
  968. mCurSheet = mCurX = mCurY = 0;
  969. mTextureSheets.clear();
  970. // Now, load the font strip.
  971. GBitmap *strip = GBitmap::load(fileName);
  972. if(!strip)
  973. {
  974. Con::errorf("GFont::importStrip - could not load file '%s'!", fileName);
  975. return;
  976. }
  977. // And get parsing and copying - load up all the characters as separate
  978. // GBitmaps, sort, then pack. Not terribly efficient but this is basically
  979. // on offline task anyway.
  980. // Ok, snag some glyphs.
  981. Vector<GlyphMap> glyphList;
  982. glyphList.reserve(mCharInfoList.size());
  983. U32 curWidth = 0;
  984. for(S32 i=0; i<mCharInfoList.size(); i++)
  985. {
  986. // Skip invalid stuff.
  987. if(mCharInfoList[i].bitmapIndex == -1 || mCharInfoList[i].height == 0 || mCharInfoList[i].width == 0)
  988. continue;
  989. // Allocate a new bitmap for this glyph, taking into account kerning and padding.
  990. glyphList.increment();
  991. glyphList.last().bitmap = new GBitmap(mCharInfoList[i].width + kerning + 2*padding, mCharInfoList[i].height + 2*padding, false, strip->getFormat());
  992. glyphList.last().charId = i;
  993. // Copy the rect.
  994. RectI ri(curWidth, getBaseline() - mCharInfoList[i].yOrigin, glyphList.last().bitmap->width, glyphList.last().bitmap->height);
  995. Point2I outRi(0,0);
  996. glyphList.last().bitmap->copyRect(strip, ri, outRi);
  997. // Update glyph attributes.
  998. mCharInfoList[i].width = glyphList.last().bitmap->width;
  999. mCharInfoList[i].height = glyphList.last().bitmap->height;
  1000. mCharInfoList[i].xOffset -= kerning + padding;
  1001. mCharInfoList[i].xIncrement += kerning;
  1002. mCharInfoList[i].yOffset -= padding;
  1003. // Advance.
  1004. curWidth += ri.extent.x;
  1005. }
  1006. // Ok, we have a big list of glyphmaps now. So let's sort them, then pack them.
  1007. dQsort(glyphList.address(), glyphList.size(), sizeof(GlyphMap), GlyphMapCompare);
  1008. // They're sorted by height, so now we can do some sort of awesome packing.
  1009. Point2I curSheetSize(256, 256);
  1010. Vector<U32> sheetSizes;
  1011. S32 curY = 0;
  1012. S32 curX = 0;
  1013. S32 curLnHeight = 0;
  1014. S32 maxHeight = 0;
  1015. for(U32 i = 0; i < (U32)glyphList.size(); i++)
  1016. {
  1017. PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId];
  1018. if(ci->height > (U32)maxHeight)
  1019. maxHeight = ci->height;
  1020. if(curX + ci->width > (U32)curSheetSize.x)
  1021. {
  1022. curY += curLnHeight;
  1023. curX = 0;
  1024. curLnHeight = 0;
  1025. }
  1026. if(curY + ci->height > (U32)curSheetSize.y)
  1027. {
  1028. sheetSizes.push_back(curSheetSize.y);
  1029. curX = 0;
  1030. curY = 0;
  1031. curLnHeight = 0;
  1032. }
  1033. if(ci->height > (U32)curLnHeight)
  1034. curLnHeight = ci->height;
  1035. ci->bitmapIndex = sheetSizes.size();
  1036. ci->xOffset = curX;
  1037. ci->yOffset = curY;
  1038. curX += ci->width;
  1039. }
  1040. // Terminate the packing loop calculations.
  1041. curY += curLnHeight;
  1042. if(curY < 64)
  1043. curSheetSize.y = 64;
  1044. else if(curY < 128)
  1045. curSheetSize.y = 128;
  1046. sheetSizes.push_back(curSheetSize.y);
  1047. if(getHeight() + padding * 2 > (U32)maxHeight)
  1048. maxHeight = getHeight() + padding * 2;
  1049. // Allocate texture pages.
  1050. for(S32 i=0; i<sheetSizes.size(); i++)
  1051. {
  1052. char buf[30];
  1053. dSprintf(buf, sizeof(buf), "newfont_%d", smSheetIdCount++);
  1054. GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, strip->getFormat());
  1055. // Set everything to transparent.
  1056. U8 *bits = bitmap->getWritableBits();
  1057. dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize * strip->bytesPerPixel);
  1058. TextureHandle handle = TextureHandle( buf, bitmap, TextureHandle::BitmapKeepTexture );
  1059. mTextureSheets.increment();
  1060. constructInPlace(&mTextureSheets.last());
  1061. mTextureSheets.last() = handle;
  1062. }
  1063. // Alright, we're ready to copy bits!
  1064. for(S32 i=0; i<glyphList.size(); i++)
  1065. {
  1066. // Copy each glyph into the appropriate place.
  1067. PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId];
  1068. U32 bi = ci->bitmapIndex;
  1069. mTextureSheets[bi].getBitmap()->copyRect(glyphList[i].bitmap, RectI(0,0, glyphList[i].bitmap->width,glyphList[i].bitmap->height), Point2I(ci->xOffset, ci->yOffset));
  1070. }
  1071. // Ok, all done! Just refresh some textures and we're set.
  1072. for(S32 i=0; i<sheetSizes.size(); i++)
  1073. mTextureSheets[i].refresh();
  1074. }