BsTextData.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #include "BsTextData.h"
  2. #include "BsFont.h"
  3. #include "BsVector2.h"
  4. #include "BsDebug.h"
  5. namespace BansheeEngine
  6. {
  7. const int SPACE_CHAR = 32;
  8. const int TAB_CHAR = 9;
  9. void TextDataBase::TextWord::init(bool spacer)
  10. {
  11. mWidth = mHeight = 0;
  12. mSpacer = spacer;
  13. mSpaceWidth = 0;
  14. mCharsStart = 0;
  15. mCharsEnd = 0;
  16. mLastChar = nullptr;
  17. }
  18. // Assumes charIdx is an index right after last char in the list (if any). All chars need to be sequential.
  19. UINT32 TextDataBase::TextWord::addChar(UINT32 charIdx, const CHAR_DESC& desc)
  20. {
  21. UINT32 charWidth = calcCharWidth(mLastChar, desc);
  22. mWidth += charWidth;
  23. mHeight = std::max(mHeight, desc.height);
  24. if(mLastChar == nullptr) // First char
  25. mCharsStart = mCharsEnd = charIdx;
  26. else
  27. mCharsEnd = charIdx;
  28. mLastChar = &desc;
  29. return charWidth;
  30. }
  31. UINT32 TextDataBase::TextWord::calcWidthWithChar(const CHAR_DESC& desc)
  32. {
  33. return mWidth + calcCharWidth(mLastChar, desc);
  34. }
  35. UINT32 TextDataBase::TextWord::calcCharWidth(const CHAR_DESC* prevDesc, const CHAR_DESC& desc)
  36. {
  37. UINT32 charWidth = desc.xAdvance;
  38. if (prevDesc != nullptr)
  39. {
  40. UINT32 kerning = 0;
  41. for (size_t j = 0; j < prevDesc->kerningPairs.size(); j++)
  42. {
  43. if (prevDesc->kerningPairs[j].otherCharId == desc.charId)
  44. {
  45. kerning = prevDesc->kerningPairs[j].amount;
  46. break;
  47. }
  48. }
  49. charWidth += kerning;
  50. }
  51. return charWidth;
  52. }
  53. void TextDataBase::TextWord::addSpace(UINT32 spaceWidth)
  54. {
  55. mSpaceWidth += spaceWidth;
  56. mWidth = mSpaceWidth;
  57. mHeight = 0;
  58. }
  59. void TextDataBase::TextLine::init(TextDataBase* textData)
  60. {
  61. mWidth = 0;
  62. mHeight = 0;
  63. mIsEmpty = true;
  64. mTextData = textData;
  65. mWordsStart = mWordsEnd = 0;
  66. }
  67. void TextDataBase::TextLine::finalize(bool hasNewlineChar)
  68. {
  69. mHasNewline = hasNewlineChar;
  70. }
  71. void TextDataBase::TextLine::add(UINT32 charIdx, const CHAR_DESC& charDesc)
  72. {
  73. UINT32 charWidth = 0;
  74. if(mIsEmpty)
  75. {
  76. mWordsStart = mWordsEnd = MemBuffer->allocWord(false);
  77. mIsEmpty = false;
  78. }
  79. else
  80. {
  81. if(MemBuffer->WordBuffer[mWordsEnd].isSpacer())
  82. mWordsEnd = MemBuffer->allocWord(false);
  83. }
  84. TextWord& lastWord = MemBuffer->WordBuffer[mWordsEnd];
  85. charWidth = lastWord.addChar(charIdx, charDesc);
  86. mWidth += charWidth;
  87. mHeight = std::max(mHeight, lastWord.getHeight());
  88. }
  89. void TextDataBase::TextLine::addSpace(UINT32 spaceWidth)
  90. {
  91. if(mIsEmpty)
  92. {
  93. mWordsStart = mWordsEnd = MemBuffer->allocWord(true);
  94. mIsEmpty = false;
  95. }
  96. else
  97. mWordsEnd = MemBuffer->allocWord(true); // Each space is counted as its own word, to make certain operations easier
  98. TextWord& lastWord = MemBuffer->WordBuffer[mWordsEnd];
  99. lastWord.addSpace(spaceWidth);
  100. mWidth += spaceWidth;
  101. }
  102. // Assumes wordIdx is an index right after last word in the list (if any). All words need to be sequential.
  103. void TextDataBase::TextLine::addWord(UINT32 wordIdx, const TextWord& word)
  104. {
  105. if(mIsEmpty)
  106. {
  107. mWordsStart = mWordsEnd = wordIdx;
  108. mIsEmpty = false;
  109. }
  110. else
  111. mWordsEnd = wordIdx;
  112. mWidth += word.getWidth();
  113. mHeight = std::max(mHeight, word.getHeight());
  114. }
  115. UINT32 TextDataBase::TextLine::removeLastWord()
  116. {
  117. if(mIsEmpty)
  118. {
  119. assert(false);
  120. return 0;
  121. }
  122. UINT32 lastWord = mWordsEnd--;
  123. if(mWordsStart == lastWord)
  124. {
  125. mIsEmpty = true;
  126. mWordsStart = mWordsEnd = 0;
  127. }
  128. calculateBounds();
  129. return lastWord;
  130. }
  131. UINT32 TextDataBase::TextLine::calcWidthWithChar(const CHAR_DESC& desc)
  132. {
  133. UINT32 charWidth = 0;
  134. UINT32 word = mWordsEnd;
  135. if (!mIsEmpty)
  136. {
  137. TextWord& lastWord = MemBuffer->WordBuffer[mWordsEnd];
  138. if (lastWord.isSpacer())
  139. charWidth = TextWord::calcCharWidth(nullptr, desc);
  140. else
  141. charWidth = lastWord.calcWidthWithChar(desc) - lastWord.getWidth();
  142. }
  143. else
  144. {
  145. charWidth = TextWord::calcCharWidth(nullptr, desc);
  146. }
  147. return mWidth + charWidth;
  148. }
  149. bool TextDataBase::TextLine::isAtWordBoundary() const
  150. {
  151. return mIsEmpty || MemBuffer->WordBuffer[mWordsEnd].isSpacer();
  152. }
  153. UINT32 TextDataBase::TextLine::fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const
  154. {
  155. UINT32 numQuads = 0;
  156. if(mIsEmpty)
  157. return numQuads;
  158. UINT32 penX = 0;
  159. for(UINT32 i = mWordsStart; i <= mWordsEnd; i++)
  160. {
  161. const TextWord& word = mTextData->getWord(i);
  162. if(word.isSpacer())
  163. {
  164. // We store invisible space quads in the first page. Even though they aren't needed
  165. // for rendering and we could just leave an empty space, they are needed for intersection tests
  166. // for things like determining caret placement and selection areas
  167. if(page == 0)
  168. {
  169. INT32 curX = penX;
  170. INT32 curY = 0;
  171. UINT32 curVert = offset * 4;
  172. UINT32 curIndex = offset * 6;
  173. vertices[curVert + 0] = Vector2((float)curX, (float)curY);
  174. vertices[curVert + 1] = Vector2((float)(curX + word.getWidth()), (float)curY);
  175. vertices[curVert + 2] = Vector2((float)curX, (float)curY + (float)mTextData->getLineHeight());
  176. vertices[curVert + 3] = Vector2((float)(curX + word.getWidth()), (float)curY + (float)mTextData->getLineHeight());
  177. if(uvs != nullptr)
  178. {
  179. uvs[curVert + 0] = Vector2(0.0f, 0.0f);
  180. uvs[curVert + 1] = Vector2(0.0f, 0.0f);
  181. uvs[curVert + 2] = Vector2(0.0f, 0.0f);
  182. uvs[curVert + 3] = Vector2(0.0f, 0.0f);
  183. }
  184. // Triangles are back-facing which makes them invisible
  185. if(indexes != nullptr)
  186. {
  187. indexes[curIndex + 0] = curVert + 0;
  188. indexes[curIndex + 1] = curVert + 2;
  189. indexes[curIndex + 2] = curVert + 1;
  190. indexes[curIndex + 3] = curVert + 1;
  191. indexes[curIndex + 4] = curVert + 2;
  192. indexes[curIndex + 5] = curVert + 3;
  193. }
  194. offset++;
  195. numQuads++;
  196. if(offset > size)
  197. BS_EXCEPT(InternalErrorException, "Out of buffer bounds. Buffer size: " + toString(size));
  198. }
  199. penX += word.getWidth();
  200. }
  201. else
  202. {
  203. const CHAR_DESC& firstChar = mTextData->getChar(word.getCharsStart());
  204. if (firstChar.xOffset < 0)
  205. penX += -firstChar.xOffset; // Offset characters so that they start at 0
  206. UINT32 kerning = 0;
  207. for(UINT32 j = word.getCharsStart(); j <= word.getCharsEnd(); j++)
  208. {
  209. const CHAR_DESC& curChar = mTextData->getChar(j);
  210. INT32 curX = penX + curChar.xOffset;
  211. INT32 curY = ((INT32) mTextData->getBaselineOffset() - curChar.yOffset);
  212. penX += curChar.xAdvance + kerning;
  213. kerning = 0;
  214. if((j + 1) <= word.getCharsEnd())
  215. {
  216. const CHAR_DESC& nextChar = mTextData->getChar(j + 1);
  217. for(size_t j = 0; j < curChar.kerningPairs.size(); j++)
  218. {
  219. if(curChar.kerningPairs[j].otherCharId == nextChar.charId)
  220. {
  221. kerning = curChar.kerningPairs[j].amount;
  222. break;
  223. }
  224. }
  225. }
  226. if(curChar.page != page)
  227. continue;
  228. UINT32 curVert = offset * 4;
  229. UINT32 curIndex = offset * 6;
  230. vertices[curVert + 0] = Vector2((float)curX, (float)curY);
  231. vertices[curVert + 1] = Vector2((float)(curX + curChar.width), (float)curY);
  232. vertices[curVert + 2] = Vector2((float)curX, (float)curY + (float)curChar.height);
  233. vertices[curVert + 3] = Vector2((float)(curX + curChar.width), (float)curY + (float)curChar.height);
  234. if(uvs != nullptr)
  235. {
  236. uvs[curVert + 0] = Vector2(curChar.uvX, curChar.uvY);
  237. uvs[curVert + 1] = Vector2(curChar.uvX + curChar.uvWidth, curChar.uvY);
  238. uvs[curVert + 2] = Vector2(curChar.uvX, curChar.uvY + curChar.uvHeight);
  239. uvs[curVert + 3] = Vector2(curChar.uvX + curChar.uvWidth, curChar.uvY + curChar.uvHeight);
  240. }
  241. if(indexes != nullptr)
  242. {
  243. indexes[curIndex + 0] = curVert + 0;
  244. indexes[curIndex + 1] = curVert + 1;
  245. indexes[curIndex + 2] = curVert + 2;
  246. indexes[curIndex + 3] = curVert + 1;
  247. indexes[curIndex + 4] = curVert + 3;
  248. indexes[curIndex + 5] = curVert + 2;
  249. }
  250. offset++;
  251. numQuads++;
  252. if(offset > size)
  253. BS_EXCEPT(InternalErrorException, "Out of buffer bounds. Buffer size: " + toString(size));
  254. }
  255. }
  256. }
  257. return numQuads;
  258. }
  259. UINT32 TextDataBase::TextLine::getNumChars() const
  260. {
  261. if(mIsEmpty)
  262. return 0;
  263. UINT32 numChars = 0;
  264. for(UINT32 i = mWordsStart; i <= mWordsEnd; i++)
  265. {
  266. TextWord& word = MemBuffer->WordBuffer[i];
  267. if(word.isSpacer())
  268. numChars++;
  269. else
  270. numChars += (UINT32)word.getNumChars();
  271. }
  272. return numChars;
  273. }
  274. void TextDataBase::TextLine::calculateBounds()
  275. {
  276. mWidth = 0;
  277. mHeight = 0;
  278. if(mIsEmpty)
  279. return;
  280. for(UINT32 i = mWordsStart; i <= mWordsEnd; i++)
  281. {
  282. TextWord& word = MemBuffer->WordBuffer[i];
  283. mWidth += word.getWidth();
  284. mHeight = std::max(mHeight, word.getHeight());
  285. }
  286. }
  287. TextDataBase::TextDataBase(const WString& text, const HFont& font, UINT32 fontSize, UINT32 width, UINT32 height, bool wordWrap, bool wordBreak)
  288. :mFont(font), mChars(nullptr), mFontData(nullptr),
  289. mNumChars(0), mWords(nullptr), mNumWords(0), mLines(nullptr), mNumLines(0), mPageInfos(nullptr), mNumPageInfos(0)
  290. {
  291. // In order to reduce number of memory allocations algorithm first calculates data into temporary buffers and then copies the results
  292. initAlloc();
  293. if(font != nullptr)
  294. {
  295. UINT32 nearestSize = font->getClosestSize(fontSize);
  296. mFontData = font->getBitmap(nearestSize);
  297. }
  298. if(mFontData == nullptr || mFontData->texturePages.size() == 0)
  299. return;
  300. if(mFontData->size != fontSize)
  301. {
  302. LOGWRN("Unable to find font with specified size (" + toString(fontSize) + "). Using nearest available size: " + toString(mFontData->size));
  303. }
  304. bool widthIsLimited = width > 0;
  305. mFont = font;
  306. UINT32 curLineIdx = MemBuffer->allocLine(this);
  307. UINT32 curHeight = mFontData->fontDesc.lineHeight;
  308. UINT32 charIdx = 0;
  309. while(true)
  310. {
  311. if(charIdx >= text.size())
  312. break;
  313. UINT32 charId = text[charIdx];
  314. const CHAR_DESC& charDesc = mFontData->getCharDesc(charId);
  315. TextLine* curLine = &MemBuffer->LineBuffer[curLineIdx];
  316. if(text[charIdx] == '\n' || text[charIdx] == '\r')
  317. {
  318. curLine->finalize(true);
  319. curLineIdx = MemBuffer->allocLine(this);
  320. curLine = &MemBuffer->LineBuffer[curLineIdx];
  321. curHeight += mFontData->fontDesc.lineHeight;
  322. charIdx++;
  323. // Check for \r\n
  324. if (charIdx < text.size())
  325. {
  326. if (text[charIdx] == '\n')
  327. charIdx++;
  328. }
  329. continue;
  330. }
  331. if (widthIsLimited && wordWrap)
  332. {
  333. UINT32 widthWithChar = 0;
  334. if (charIdx == SPACE_CHAR)
  335. widthWithChar = curLine->getWidth() + getSpaceWidth();
  336. else if (charIdx == TAB_CHAR)
  337. widthWithChar = curLine->getWidth() + getSpaceWidth() * 4;
  338. else
  339. widthWithChar = curLine->calcWidthWithChar(charDesc);
  340. if (widthWithChar > width && !curLine->isEmpty())
  341. {
  342. bool atWordBoundary = charId == SPACE_CHAR || charId == TAB_CHAR || curLine->isAtWordBoundary();
  343. if (!atWordBoundary) // Need to break word into multiple pieces, or move it to next line
  344. {
  345. UINT32 lastWordIdx = curLine->removeLastWord();
  346. TextWord& lastWord = MemBuffer->WordBuffer[lastWordIdx];
  347. bool wordFits = lastWord.calcWidthWithChar(charDesc) <= width;
  348. if (wordFits && !curLine->isEmpty())
  349. {
  350. curLine->finalize(false);
  351. curLineIdx = MemBuffer->allocLine(this);
  352. curLine = &MemBuffer->LineBuffer[curLineIdx];
  353. curHeight += mFontData->fontDesc.lineHeight;
  354. curLine->addWord(lastWordIdx, lastWord);
  355. }
  356. else
  357. {
  358. if (wordBreak)
  359. {
  360. curLine->addWord(lastWordIdx, lastWord);
  361. curLine->finalize(false);
  362. curLineIdx = MemBuffer->allocLine(this);
  363. curLine = &MemBuffer->LineBuffer[curLineIdx];
  364. curHeight += mFontData->fontDesc.lineHeight;
  365. }
  366. else
  367. {
  368. if (!curLine->isEmpty()) // Add new line unless current line is empty (to avoid constantly moving the word to new lines)
  369. {
  370. curLine->finalize(false);
  371. curLineIdx = MemBuffer->allocLine(this);
  372. curLine = &MemBuffer->LineBuffer[curLineIdx];
  373. curHeight += mFontData->fontDesc.lineHeight;
  374. }
  375. curLine->addWord(lastWordIdx, lastWord);
  376. }
  377. }
  378. }
  379. else
  380. {
  381. curLine->finalize(false);
  382. curLineIdx = MemBuffer->allocLine(this);
  383. curLine = &MemBuffer->LineBuffer[curLineIdx];
  384. curHeight += mFontData->fontDesc.lineHeight;
  385. }
  386. }
  387. }
  388. if(charId == SPACE_CHAR)
  389. {
  390. curLine->addSpace(getSpaceWidth());
  391. MemBuffer->addCharToPage(0, *mFontData);
  392. }
  393. else if (charId == TAB_CHAR)
  394. {
  395. curLine->addSpace(getSpaceWidth() * 4);
  396. MemBuffer->addCharToPage(0, *mFontData);
  397. }
  398. else
  399. {
  400. curLine->add(charIdx, charDesc);
  401. MemBuffer->addCharToPage(charDesc.page, *mFontData);
  402. }
  403. charIdx++;
  404. }
  405. MemBuffer->LineBuffer[curLineIdx].finalize(true);
  406. // Now that we have all the data we need, allocate the permanent buffers and copy the data
  407. mNumChars = (UINT32)text.size();
  408. mNumWords = MemBuffer->NextFreeWord;
  409. mNumLines = MemBuffer->NextFreeLine;
  410. mNumPageInfos = MemBuffer->NextFreePageInfo;
  411. }
  412. void TextDataBase::generatePersistentData(const WString& text, UINT8* buffer, UINT32& size, bool freeTemporary)
  413. {
  414. UINT32 charArraySize = mNumChars * sizeof(const CHAR_DESC*);
  415. UINT32 wordArraySize = mNumWords * sizeof(TextWord);
  416. UINT32 lineArraySize = mNumLines * sizeof(TextLine);
  417. UINT32 pageInfoArraySize = mNumPageInfos * sizeof(PageInfo);
  418. if (buffer == nullptr)
  419. {
  420. size = charArraySize + wordArraySize + lineArraySize + pageInfoArraySize;;
  421. return;
  422. }
  423. UINT8* dataPtr = (UINT8*)buffer;
  424. mChars = (const CHAR_DESC**)dataPtr;
  425. for (UINT32 i = 0; i < mNumChars; i++)
  426. {
  427. UINT32 charId = text[i];
  428. const CHAR_DESC& charDesc = mFontData->getCharDesc(charId);
  429. mChars[i] = &charDesc;
  430. }
  431. dataPtr += charArraySize;
  432. mWords = (TextWord*)dataPtr;
  433. memcpy(mWords, &MemBuffer->WordBuffer[0], wordArraySize);
  434. dataPtr += wordArraySize;
  435. mLines = (TextLine*)dataPtr;
  436. memcpy(mLines, &MemBuffer->LineBuffer[0], lineArraySize);
  437. dataPtr += lineArraySize;
  438. mPageInfos = (PageInfo*)dataPtr;
  439. memcpy(mPageInfos, &MemBuffer->PageBuffer[0], pageInfoArraySize);
  440. if (freeTemporary)
  441. MemBuffer->deallocAll();
  442. }
  443. const HTexture& TextDataBase::getTextureForPage(UINT32 page) const
  444. {
  445. return mFontData->texturePages[page];
  446. }
  447. INT32 TextDataBase::getBaselineOffset() const
  448. {
  449. return mFontData->fontDesc.baselineOffset;
  450. }
  451. UINT32 TextDataBase::getLineHeight() const
  452. {
  453. return mFontData->fontDesc.lineHeight;
  454. }
  455. UINT32 TextDataBase::getSpaceWidth() const
  456. {
  457. return mFontData->fontDesc.spaceWidth;
  458. }
  459. void TextDataBase::initAlloc()
  460. {
  461. if (MemBuffer == nullptr)
  462. MemBuffer = bs_new<BufferData>();
  463. }
  464. TextDataBase::BufferData* TextDataBase::MemBuffer = nullptr;
  465. TextDataBase::BufferData::BufferData()
  466. {
  467. WordBufferSize = 2000;
  468. LineBufferSize = 500;
  469. PageBufferSize = 20;
  470. NextFreeWord = 0;
  471. NextFreeLine = 0;
  472. NextFreePageInfo = 0;
  473. WordBuffer = bs_newN<TextWord>(WordBufferSize);
  474. LineBuffer = bs_newN<TextLine>(LineBufferSize);
  475. PageBuffer = bs_newN<PageInfo>(PageBufferSize);
  476. }
  477. TextDataBase::BufferData::~BufferData()
  478. {
  479. bs_deleteN(WordBuffer, WordBufferSize);
  480. bs_deleteN(LineBuffer, LineBufferSize);
  481. bs_deleteN(PageBuffer, PageBufferSize);
  482. }
  483. UINT32 TextDataBase::BufferData::allocWord(bool spacer)
  484. {
  485. if(NextFreeWord >= WordBufferSize)
  486. {
  487. UINT32 newBufferSize = WordBufferSize * 2;
  488. TextWord* newBuffer = bs_newN<TextWord>(newBufferSize);
  489. memcpy(WordBuffer, newBuffer, WordBufferSize);
  490. bs_deleteN(WordBuffer, WordBufferSize);
  491. WordBuffer = newBuffer;
  492. WordBufferSize = newBufferSize;
  493. }
  494. WordBuffer[NextFreeWord].init(spacer);
  495. return NextFreeWord++;
  496. }
  497. UINT32 TextDataBase::BufferData::allocLine(TextDataBase* textData)
  498. {
  499. if(NextFreeLine >= LineBufferSize)
  500. {
  501. UINT32 newBufferSize = LineBufferSize * 2;
  502. TextLine* newBuffer = bs_newN<TextLine>(newBufferSize);
  503. memcpy(LineBuffer, newBuffer, LineBufferSize);
  504. bs_deleteN(LineBuffer, LineBufferSize);
  505. LineBuffer = newBuffer;
  506. LineBufferSize = newBufferSize;
  507. }
  508. LineBuffer[NextFreeLine].init(textData);
  509. return NextFreeLine++;
  510. }
  511. void TextDataBase::BufferData::deallocAll()
  512. {
  513. NextFreeWord = 0;
  514. NextFreeLine = 0;
  515. NextFreePageInfo = 0;
  516. }
  517. void TextDataBase::BufferData::addCharToPage(UINT32 page, const FontBitmap& fontData)
  518. {
  519. if(NextFreePageInfo >= PageBufferSize)
  520. {
  521. UINT32 newBufferSize = PageBufferSize * 2;
  522. PageInfo* newBuffer = bs_newN<PageInfo>(newBufferSize);
  523. memcpy(PageBuffer, newBuffer, PageBufferSize);
  524. bs_deleteN(PageBuffer, PageBufferSize);
  525. PageBuffer = newBuffer;
  526. PageBufferSize = newBufferSize;
  527. }
  528. while(page >= NextFreePageInfo)
  529. {
  530. PageBuffer[NextFreePageInfo].numQuads = 0;
  531. NextFreePageInfo++;
  532. }
  533. PageBuffer[page].numQuads++;
  534. }
  535. UINT32 TextDataBase::getWidth() const
  536. {
  537. UINT32 width = 0;
  538. for(UINT32 i = 0; i < mNumLines; i++)
  539. width = std::max(width, mLines[i].getWidth());
  540. return width;
  541. }
  542. UINT32 TextDataBase::getHeight() const
  543. {
  544. UINT32 height = 0;
  545. for(UINT32 i = 0; i < mNumLines; i++)
  546. height += mLines[i].getHeight();
  547. return height;
  548. }
  549. }