gFont.cc 29 KB

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