gFont.cc 38 KB

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