Font.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. #include "Base.h"
  2. #include "Font.h"
  3. #include "Game.h"
  4. #include "FileSystem.h"
  5. #include "Package.h"
  6. // Default font vertex shader
  7. #define FONT_VSH \
  8. "uniform mat4 u_projectionMatrix;\n" \
  9. "attribute vec3 a_position;\n" \
  10. "attribute vec2 a_texcoord;\n" \
  11. "attribute vec4 a_color;\n" \
  12. "varying vec2 v_texcoord;\n" \
  13. "varying vec4 v_color;\n" \
  14. "void main()\n" \
  15. "{\n" \
  16. "gl_Position = u_projectionMatrix * vec4(a_position, 1);\n" \
  17. "v_texcoord = a_texcoord;\n" \
  18. "v_color = a_color;\n" \
  19. "}\n"
  20. // Default font fragment shader
  21. #define FONT_FSH \
  22. "#ifdef OPENGL_ES\n" \
  23. "precision highp float;\n" \
  24. "#endif\n" \
  25. "varying vec2 v_texcoord;\n" \
  26. "varying vec4 v_color;\n" \
  27. "uniform sampler2D u_texture;\n" \
  28. "void main()\n" \
  29. "{\n" \
  30. "gl_FragColor = v_color;\n" \
  31. "gl_FragColor.a = texture2D(u_texture, v_texcoord).a;\n" \
  32. "}"
  33. namespace gameplay
  34. {
  35. static std::vector<Font*> __fontCache;
  36. static Effect* __fontEffect = NULL;
  37. Font::Font() :
  38. _style(PLAIN), _size(0), _glyphs(NULL), _glyphCount(0), _texture(NULL), _batch(NULL)
  39. {
  40. }
  41. Font::Font(const Font& copy)
  42. {
  43. // hidden
  44. }
  45. Font::~Font()
  46. {
  47. // Remove this Font from the font cache.
  48. std::vector<Font*>::iterator itr = find(__fontCache.begin(), __fontCache.end(), this);
  49. if (itr != __fontCache.end())
  50. {
  51. __fontCache.erase(itr);
  52. }
  53. SAFE_DELETE(_batch);
  54. SAFE_DELETE_ARRAY(_glyphs);
  55. SAFE_RELEASE(_texture);
  56. }
  57. Font* Font::create(const char* path, const char* id)
  58. {
  59. // Search the font cache for a font with the given path and ID.
  60. for (unsigned int i = 0, count = __fontCache.size(); i < count; ++i)
  61. {
  62. Font* f = __fontCache[i];
  63. if (f->_path == path && (id == NULL || f->_id == id))
  64. {
  65. // Found a match.
  66. f->addRef();
  67. return f;
  68. }
  69. }
  70. // Load the package.
  71. Package* pkg = Package::create(path);
  72. if (pkg == NULL)
  73. {
  74. return NULL;
  75. }
  76. Font* font = NULL;
  77. if (id == NULL)
  78. {
  79. // Get the ID of the first/only object in the package (assume it's a Font).
  80. const char* id;
  81. if (pkg->getObjectCount() != 1 || (id = pkg->getObjectID(0)) == NULL)
  82. {
  83. return NULL;
  84. }
  85. // Load the font using the ID of the first object in the package.
  86. font = pkg->loadFont(pkg->getObjectID(0));
  87. }
  88. else
  89. {
  90. // Load the font with the given ID.
  91. font = pkg->loadFont(id);
  92. }
  93. if (font)
  94. {
  95. // Add this font to the cache.
  96. __fontCache.push_back(font);
  97. }
  98. SAFE_RELEASE(pkg);
  99. return font;
  100. }
  101. Font* Font::create(const char* family, Style style, unsigned int size, Glyph* glyphs, int glyphCount, Texture* texture)
  102. {
  103. // Create the effect for the font's sprite batch.
  104. if (__fontEffect == NULL)
  105. {
  106. __fontEffect = Effect::createFromSource(FONT_VSH, FONT_FSH);
  107. if (__fontEffect == NULL)
  108. {
  109. LOG_ERROR("Failed to create effect for font.");
  110. SAFE_RELEASE(texture);
  111. return NULL;
  112. }
  113. }
  114. else
  115. {
  116. __fontEffect->addRef();
  117. }
  118. // Create batch for the font.
  119. SpriteBatch* batch = SpriteBatch::create(texture, __fontEffect, 128);
  120. // Release __fontEffect since the SpriteBatch keeps a reference to it
  121. SAFE_RELEASE(__fontEffect);
  122. if (batch == NULL)
  123. {
  124. LOG_ERROR("Failed to create batch for font.");
  125. return NULL;
  126. }
  127. // Increase the ref count of the texture to retain it.
  128. texture->addRef();
  129. Font* font = new Font();
  130. font->_family = family;
  131. font->_style = style;
  132. font->_size = size;
  133. font->_texture = texture;
  134. font->_batch = batch;
  135. // Copy the glyphs array.
  136. font->_glyphs = new Glyph[glyphCount];
  137. memcpy(font->_glyphs, glyphs, sizeof(Glyph) * glyphCount);
  138. font->_glyphCount = glyphCount;
  139. return font;
  140. }
  141. void Font::begin()
  142. {
  143. _batch->begin();
  144. }
  145. void Font::drawText(const char* text, int x, int y, const Vector4& color, float scale, bool rightToLeft)
  146. {
  147. char* rightToLeftText = NULL;
  148. if (rightToLeft)
  149. {
  150. rightToLeftText = new char[strlen(text) + 1];
  151. strcpy(rightToLeftText, text);
  152. reverseLines(rightToLeftText);
  153. }
  154. const int length = strlen(text);
  155. const int size = (int)_size * scale;
  156. int xPos = x, yPos = y;
  157. for (int i = 0; i < length; ++i)
  158. {
  159. char c = 0;
  160. if (rightToLeft)
  161. {
  162. c = rightToLeftText[i];
  163. }
  164. else
  165. {
  166. c = text[i];
  167. }
  168. // Draw this character.
  169. switch (c)
  170. {
  171. case ' ':
  172. xPos += size>>1;
  173. break;
  174. case '\r':
  175. case '\n':
  176. yPos += size;
  177. xPos = x;
  178. break;
  179. case '\t':
  180. xPos += (size>>1)*4;
  181. break;
  182. default:
  183. int index = c - 32; // HACK for ASCII
  184. if (index >= 0 && index < (int)_glyphCount)
  185. {
  186. Glyph& g = _glyphs[index];
  187. _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
  188. xPos += g.width * scale + (size>>3);
  189. break;
  190. }
  191. }
  192. }
  193. SAFE_DELETE(rightToLeftText);
  194. }
  195. void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, float scale, Justify justify, bool wrap, bool rightToLeft)
  196. {
  197. char* token = const_cast<char*>(text);
  198. const int length = strlen(text);
  199. const int fontSize = (int)(_size * scale);
  200. int yPos = area.y;
  201. Justify vAlign = static_cast<Justify>(justify & 0xF0);
  202. if (vAlign == 0)
  203. {
  204. vAlign = ALIGN_TOP;
  205. }
  206. Justify hAlign = static_cast<Justify>(justify & 0x0F);
  207. if (hAlign == 0)
  208. {
  209. hAlign = ALIGN_LEFT;
  210. }
  211. char* rightToLeftText = NULL;
  212. if (rightToLeft)
  213. {
  214. rightToLeftText = new char[strlen(text) + 1];
  215. strcpy(rightToLeftText, text);
  216. token = rightToLeftText;
  217. }
  218. else
  219. {
  220. token = const_cast<char*>(text);
  221. }
  222. // For alignments other than top-left, need to calculate the y position to begin drawing from
  223. // and the starting x position of each line. For right-to-left text that wraps, need to insert
  224. // newlines where lines wrap even for top-left alignment.
  225. std::vector<int> xPositions;
  226. if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || (rightToLeft && wrap))
  227. {
  228. int lineWidth = 0;
  229. int delimWidth = 0;
  230. if (wrap)
  231. {
  232. // Go a word at a time.
  233. bool reachedEOF = false;
  234. while (token[0] != 0)
  235. {
  236. unsigned int tokenWidth = 0;
  237. // Handle delimiters until next token.
  238. char delimiter = token[0];
  239. while (delimiter == ' ' ||
  240. delimiter == '\t' ||
  241. delimiter == '\r' ||
  242. delimiter == '\n' ||
  243. delimiter == 0)
  244. {
  245. switch (delimiter)
  246. {
  247. case ' ':
  248. delimWidth += fontSize>>1;
  249. break;
  250. case '\r':
  251. case '\n':
  252. yPos += fontSize;
  253. if (lineWidth > 0)
  254. {
  255. int hWhitespace = area.width - lineWidth;
  256. if (hAlign == ALIGN_HCENTER)
  257. {
  258. xPositions.push_back(area.x + hWhitespace / 2);
  259. }
  260. else if (hAlign == ALIGN_RIGHT)
  261. {
  262. xPositions.push_back(area.x + hWhitespace);
  263. }
  264. }
  265. lineWidth = 0;
  266. delimWidth = 0;
  267. break;
  268. case '\t':
  269. delimWidth += (fontSize>>1)*4;
  270. break;
  271. case 0:
  272. reachedEOF = true;
  273. break;
  274. }
  275. if (reachedEOF)
  276. {
  277. break;
  278. }
  279. token++;
  280. delimiter = token[0];
  281. }
  282. if (reachedEOF || token == NULL)
  283. {
  284. break;
  285. }
  286. unsigned int tokenLength = strcspn(token, " \r\n\t");
  287. tokenWidth += getTokenWidth(token, tokenLength, scale);
  288. // Wrap if necessary.
  289. if (lineWidth + tokenWidth + delimWidth > area.width)
  290. {
  291. yPos += fontSize;
  292. // Push position of current line.
  293. int hWhitespace = area.width - lineWidth;
  294. if (hAlign == ALIGN_HCENTER)
  295. {
  296. xPositions.push_back(area.x + hWhitespace / 2);
  297. }
  298. else if (hAlign == ALIGN_RIGHT)
  299. {
  300. xPositions.push_back(area.x + hWhitespace);
  301. }
  302. if (rightToLeft)
  303. {
  304. // Is this sane?
  305. token[-1] = '\n';
  306. }
  307. // Move token to the next line.
  308. lineWidth = 0;
  309. delimWidth = 0;
  310. }
  311. else
  312. {
  313. lineWidth += delimWidth;
  314. delimWidth = 0;
  315. }
  316. lineWidth += tokenWidth;
  317. token += tokenLength;
  318. }
  319. // Final calculation of vertical position.
  320. int textHeight = yPos - area.y;
  321. int vWhiteSpace = area.height - textHeight;
  322. if (vAlign == ALIGN_VCENTER)
  323. {
  324. yPos = area.y + vWhiteSpace / 2;
  325. }
  326. else if (vAlign == ALIGN_BOTTOM)
  327. {
  328. yPos = area.y + vWhiteSpace;
  329. }
  330. // Calculation of final horizontal position.
  331. int hWhitespace = area.width - lineWidth;
  332. if (hAlign == ALIGN_HCENTER)
  333. {
  334. xPositions.push_back(area.x + hWhitespace / 2);
  335. }
  336. else if (hAlign == ALIGN_RIGHT)
  337. {
  338. xPositions.push_back(area.x + hWhitespace);
  339. }
  340. }
  341. else
  342. {
  343. // Go a line at a time.
  344. while (token[0] != 0)
  345. {
  346. char delimiter = token[0];
  347. while (delimiter == '\n')
  348. {
  349. yPos += fontSize;
  350. ++token;
  351. delimiter = token[0];
  352. }
  353. unsigned int tokenLength = strcspn(token, "\n");
  354. if (tokenLength == 0)
  355. {
  356. tokenLength = strlen(token);
  357. }
  358. int lineWidth = getTokenWidth(token, tokenLength, scale);
  359. int whitespace = area.width - lineWidth;
  360. if (hAlign == ALIGN_HCENTER)
  361. {
  362. xPositions.push_back(area.x + whitespace / 2);
  363. }
  364. else if (hAlign == ALIGN_RIGHT)
  365. {
  366. xPositions.push_back(area.x + whitespace);
  367. }
  368. token += tokenLength;
  369. }
  370. int textHeight = yPos - area.y;
  371. int vWhiteSpace = area.height - textHeight;
  372. if (vAlign == ALIGN_VCENTER)
  373. {
  374. yPos = area.y + vWhiteSpace / 2;
  375. }
  376. else if (vAlign == ALIGN_BOTTOM)
  377. {
  378. yPos = area.y + vWhiteSpace;
  379. }
  380. }
  381. if (vAlign == ALIGN_TOP)
  382. {
  383. yPos = area.y;
  384. }
  385. }
  386. // Now we have the info we need in order to render.
  387. int xPos = area.x;
  388. std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
  389. if (xPositionsIt != xPositions.end())
  390. {
  391. xPos = *xPositionsIt++;
  392. }
  393. if (rightToLeft)
  394. {
  395. reverseLines(rightToLeftText);
  396. token = rightToLeftText;
  397. }
  398. else
  399. {
  400. token = const_cast<char*>(text);
  401. }
  402. while (token[0] != 0)
  403. {
  404. // Handle delimiters until next token.
  405. char delimiter = token[0];
  406. bool nextLine = true;
  407. bool reachedEOF = false;
  408. while (delimiter == ' ' ||
  409. delimiter == '\t' ||
  410. delimiter == '\r' ||
  411. delimiter == '\n' ||
  412. delimiter == 0)
  413. {
  414. switch (delimiter)
  415. {
  416. case ' ':
  417. xPos += fontSize>>1;
  418. break;
  419. case '\r':
  420. case '\n':
  421. yPos += fontSize;
  422. // Only use next xPos for first newline character (in case of multiple consecutive newlines).
  423. if (nextLine)
  424. {
  425. if (xPositionsIt != xPositions.end())
  426. {
  427. xPos = *xPositionsIt++;
  428. }
  429. else
  430. {
  431. xPos = area.x;
  432. }
  433. nextLine = false;
  434. }
  435. break;
  436. case '\t':
  437. xPos += (fontSize>>1)*4;
  438. break;
  439. case 0:
  440. reachedEOF = true;
  441. break;
  442. }
  443. if (reachedEOF)
  444. {
  445. break;
  446. }
  447. token++;
  448. delimiter = token[0];
  449. }
  450. if (reachedEOF || token == NULL)
  451. {
  452. break;
  453. }
  454. unsigned int tokenLength = strcspn(token, " \r\n\t");
  455. delimiter = token[tokenLength];
  456. bool truncated = false;
  457. unsigned int tokenWidth = getTokenWidth(token, tokenLength, scale);
  458. // Wrap if necessary.
  459. if (wrap &&
  460. xPos + tokenWidth > area.x + area.width)
  461. {
  462. yPos += fontSize;
  463. if (xPositionsIt != xPositions.end())
  464. {
  465. xPos = *xPositionsIt++;
  466. }
  467. else
  468. {
  469. xPos = area.x;
  470. }
  471. }
  472. bool draw = true;
  473. if (yPos < area.y)
  474. {
  475. // Skip drawing until linebreak or wrap.
  476. draw = false;
  477. }
  478. else if (yPos > area.y + area.height)
  479. {
  480. // Truncate below area's vertical limit.
  481. break;
  482. }
  483. for (unsigned int i = 0; i < tokenLength; ++i)
  484. {
  485. char c = token[i];
  486. int glyphIndex = c - 32; // HACK for ASCII
  487. if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
  488. {
  489. Glyph& g = _glyphs[glyphIndex];
  490. if (xPos + (int)(g.width*scale) >= area.x + area.width)
  491. {
  492. // Truncate this line and go on to the next one.
  493. truncated = true;
  494. break;
  495. }
  496. else if (xPos >= area.x)
  497. {
  498. // Draw this character.
  499. if (draw)
  500. {
  501. _batch->draw(xPos, yPos, g.width * scale, fontSize, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
  502. }
  503. }
  504. xPos += g.width*scale + (fontSize>>3);
  505. }
  506. }
  507. if (!truncated)
  508. {
  509. // Get next token.
  510. token += tokenLength;
  511. }
  512. else
  513. {
  514. // Skip the rest of this line.
  515. unsigned int tokenLength = strcspn(token, "\n");
  516. if (tokenLength > 0)
  517. {
  518. // Get first token of next line.
  519. token += tokenLength;
  520. }
  521. }
  522. }
  523. SAFE_DELETE(rightToLeftText);
  524. }
  525. void Font::end()
  526. {
  527. _batch->end();
  528. }
  529. void Font::measureText(const char* text, unsigned int* width, unsigned int* height, float scale)
  530. {
  531. const int length = strlen(text);
  532. char* token = const_cast<char*>(text);
  533. unsigned int fontSize = _size * scale;
  534. *width = 0;
  535. *height = 0;
  536. // Measure a line at a time.
  537. while (token[0] != 0)
  538. {
  539. while (token[0] == '\n')
  540. {
  541. *height += fontSize;
  542. ++token;
  543. }
  544. unsigned int tokenLength = strcspn(token, "\n");
  545. unsigned int tokenWidth = getTokenWidth(token, tokenLength, scale);
  546. if (tokenWidth > *width)
  547. {
  548. *width = tokenWidth;
  549. }
  550. token += tokenLength;
  551. }
  552. }
  553. void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewport, float scale, Justify justify, bool wrap, bool clipped)
  554. {
  555. Justify vAlign = static_cast<Justify>(justify & 0xF0);
  556. if (vAlign == 0)
  557. {
  558. vAlign = ALIGN_TOP;
  559. }
  560. Justify hAlign = static_cast<Justify>(justify & 0x0F);
  561. if (hAlign == 0)
  562. {
  563. hAlign = ALIGN_LEFT;
  564. }
  565. int fontSize = (int)(_size * scale);
  566. char* token = const_cast<char*>(text);
  567. std::vector<bool> emptyLines;
  568. std::vector<Vector2> lines;
  569. unsigned int lineWidth = 0;
  570. int yPos = viewport.y;
  571. if (wrap)
  572. {
  573. unsigned int delimWidth = 0;
  574. bool reachedEOF = false;
  575. while (token[0] != 0)
  576. {
  577. // Handle delimiters until next token.
  578. char delimiter = token[0];
  579. while (delimiter == ' ' ||
  580. delimiter == '\t' ||
  581. delimiter == '\r' ||
  582. delimiter == '\n' ||
  583. delimiter == 0)
  584. {
  585. switch (delimiter)
  586. {
  587. case ' ':
  588. delimWidth += fontSize>>1;
  589. break;
  590. case '\r':
  591. case '\n':
  592. // Add line-height to vertical cursor.
  593. yPos += fontSize;
  594. if (lineWidth > 0)
  595. {
  596. // Determine horizontal position and width.
  597. int hWhitespace = viewport.width - lineWidth;
  598. int xPos = viewport.x;
  599. if (hAlign == ALIGN_HCENTER)
  600. {
  601. xPos += hWhitespace / 2;
  602. }
  603. else if (hAlign == ALIGN_RIGHT)
  604. {
  605. xPos += hWhitespace;
  606. }
  607. // Record this line's size.
  608. emptyLines.push_back(false);
  609. lines.push_back(Vector2(xPos, lineWidth));
  610. }
  611. else
  612. {
  613. // Record the existence of an empty line.
  614. emptyLines.push_back(true);
  615. lines.push_back(Vector2(FLT_MAX, 0));
  616. }
  617. lineWidth = 0;
  618. delimWidth = 0;
  619. break;
  620. case '\t':
  621. delimWidth += (fontSize>>1)*4;
  622. break;
  623. case 0:
  624. reachedEOF = true;
  625. break;
  626. }
  627. if (reachedEOF)
  628. {
  629. break;
  630. }
  631. token++;
  632. delimiter = token[0];
  633. }
  634. if (reachedEOF)
  635. {
  636. break;
  637. }
  638. // Measure the next token.
  639. unsigned int tokenLength = strcspn(token, " \r\n\t");
  640. unsigned int tokenWidth = getTokenWidth(token, tokenLength, scale);
  641. // Wrap if necessary.
  642. if (lineWidth + tokenWidth + delimWidth > viewport.width)
  643. {
  644. // Add line-height to vertical cursor.
  645. yPos += fontSize;
  646. // Determine horizontal position and width.
  647. int hWhitespace = viewport.width - lineWidth;
  648. int xPos = viewport.x;
  649. if (hAlign == ALIGN_HCENTER)
  650. {
  651. xPos += hWhitespace / 2;
  652. }
  653. else if (hAlign == ALIGN_RIGHT)
  654. {
  655. xPos += hWhitespace;
  656. }
  657. // Record this line's size.
  658. emptyLines.push_back(false);
  659. lines.push_back(Vector2(xPos, lineWidth));
  660. lineWidth = 0;
  661. }
  662. else
  663. {
  664. lineWidth += delimWidth;
  665. }
  666. delimWidth = 0;
  667. lineWidth += tokenWidth;
  668. token += tokenLength;
  669. }
  670. }
  671. else
  672. {
  673. // Measure a whole line at a time.
  674. int emptyLinesCount = 0;
  675. while (token[0] != 0)
  676. {
  677. // Handle any number of consecutive newlines.
  678. bool nextLine = true;
  679. while (token[0] == '\n')
  680. {
  681. if (nextLine)
  682. {
  683. // Add line-height to vertical cursor.
  684. yPos += fontSize * (emptyLinesCount+1);
  685. nextLine = false;
  686. emptyLinesCount = 0;
  687. emptyLines.push_back(false);
  688. }
  689. else
  690. {
  691. // Record the existence of an empty line.
  692. ++emptyLinesCount;
  693. emptyLines.push_back(true);
  694. lines.push_back(Vector2(FLT_MAX, 0));
  695. }
  696. token++;
  697. }
  698. // Measure the next line.
  699. unsigned int tokenLength = strcspn(token, "\n");
  700. lineWidth = getTokenWidth(token, tokenLength, scale);
  701. // Determine horizontal position and width.
  702. int xPos = viewport.x;
  703. int hWhitespace = viewport.width - lineWidth;
  704. if (hAlign == ALIGN_HCENTER)
  705. {
  706. xPos += hWhitespace / 2;
  707. }
  708. else if (hAlign == ALIGN_RIGHT)
  709. {
  710. xPos += hWhitespace;
  711. }
  712. // Record this line's size.
  713. lines.push_back(Vector2(xPos, lineWidth));
  714. token += tokenLength;
  715. }
  716. yPos += fontSize;
  717. }
  718. if (wrap)
  719. {
  720. // Record the size of the last line.
  721. int hWhitespace = viewport.width - lineWidth;
  722. int xPos = viewport.x;
  723. if (hAlign == ALIGN_HCENTER)
  724. {
  725. xPos += hWhitespace / 2;
  726. }
  727. else if (hAlign == ALIGN_RIGHT)
  728. {
  729. xPos += hWhitespace;
  730. }
  731. lines.push_back(Vector2(xPos, lineWidth));
  732. }
  733. int x = INT_MAX;
  734. int y = viewport.y;
  735. unsigned int width = 0;
  736. int height = yPos - viewport.y;
  737. // Calculate top of text without clipping.
  738. int vWhitespace = viewport.height - height;
  739. if (vAlign == ALIGN_VCENTER)
  740. {
  741. y += vWhitespace / 2;
  742. }
  743. else if (vAlign == ALIGN_BOTTOM)
  744. {
  745. y += vWhitespace;
  746. }
  747. int clippedTop = 0;
  748. int clippedBottom = 0;
  749. if (clipped)
  750. {
  751. // Trim rect to fit text that would actually be drawn within the given viewport.
  752. if (y >= viewport.y)
  753. {
  754. // Text goes off the bottom of the viewport.
  755. clippedBottom = (height - viewport.height) / fontSize + 1;
  756. if (clippedBottom > 0)
  757. {
  758. // Also need to crop empty lines above non-empty lines that have been clipped.
  759. unsigned int emptyIndex = emptyLines.size() - clippedBottom;
  760. while (emptyIndex < emptyLines.size() && emptyLines[emptyIndex] == true)
  761. {
  762. height -= fontSize;
  763. emptyIndex++;
  764. }
  765. height -= fontSize * clippedBottom;
  766. }
  767. else
  768. {
  769. clippedBottom = 0;
  770. }
  771. }
  772. else
  773. {
  774. // Text goes above the top of the viewport.
  775. clippedTop = (viewport.y - y) / fontSize + 1;
  776. if (clippedTop < 0)
  777. {
  778. clippedTop = 0;
  779. }
  780. // Also need to crop empty lines below non-empty lines that have been clipped.
  781. unsigned int emptyIndex = clippedTop;
  782. while (emptyIndex < emptyLines.size() && emptyLines[emptyIndex] == true)
  783. {
  784. y += fontSize;
  785. height -= fontSize;
  786. emptyIndex++;
  787. }
  788. if (vAlign == ALIGN_VCENTER)
  789. {
  790. // In this case lines may be clipped off the bottom as well.
  791. clippedBottom = (height - viewport.height + vWhitespace/2 + 0.01) / fontSize + 1;
  792. if (clippedBottom > 0)
  793. {
  794. emptyIndex = emptyLines.size() - clippedBottom;
  795. while (emptyIndex < emptyLines.size() && emptyLines[emptyIndex] == true)
  796. {
  797. height -= fontSize;
  798. emptyIndex++;
  799. }
  800. height -= fontSize * clippedBottom;
  801. }
  802. else
  803. {
  804. clippedBottom = 0;
  805. }
  806. }
  807. y = y + fontSize * clippedTop;
  808. height = height - fontSize * clippedTop;
  809. }
  810. }
  811. // Determine left-most x coordinate and largest width out of lines that have not been clipped.
  812. for (unsigned int i = clippedTop; i < lines.size() - clippedBottom; ++i)
  813. {
  814. if (lines[i].x < x)
  815. {
  816. x = lines[i].x;
  817. }
  818. if (lines[i].y > width)
  819. {
  820. width = lines[i].y;
  821. }
  822. }
  823. if (clipped)
  824. {
  825. // Guarantee that the output rect will fit within the viewport.
  826. out->x = (x >= viewport.x)? x : viewport.x;
  827. out->y = (y >= viewport.y)? y : viewport.y;
  828. out->width = (width <= viewport.width)? width : viewport.width;
  829. out->height = (height <= viewport.height)? height : viewport.height;
  830. }
  831. else
  832. {
  833. out->x = x;
  834. out->y = y;
  835. out->width = width;
  836. out->height = height;
  837. }
  838. }
  839. unsigned int Font::getTokenWidth(const char* token, unsigned int length, float scale)
  840. {
  841. // Calculate width of word or line.
  842. unsigned int tokenWidth = 0;
  843. const int size = (int)(_size * scale);
  844. for (unsigned int i = 0; i < length; ++i)
  845. {
  846. char c = token[i];
  847. switch (c)
  848. {
  849. case ' ':
  850. tokenWidth += size>>1;
  851. break;
  852. case '\t':
  853. tokenWidth += (size>>1)*4;
  854. break;
  855. default:
  856. int glyphIndex = c - 32;
  857. if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
  858. {
  859. Glyph& g = _glyphs[glyphIndex];
  860. tokenWidth += g.width * scale + (size>>3);
  861. }
  862. break;
  863. }
  864. }
  865. return tokenWidth;
  866. }
  867. void Font::reverseLines(char* text)
  868. {
  869. // Naive approach: reverse each line, then render left-to-right as usual.
  870. while (text[0] != 0)
  871. {
  872. while (text[0] == '\n')
  873. {
  874. ++text;
  875. }
  876. unsigned int textLength = strcspn(text, "\n");
  877. std::string line = std::string(text, textLength);
  878. std::string reversedLine = std::string(line.rbegin(), line.rend());
  879. memcpy(text, reversedLine.c_str(), textLength);
  880. text += textLength;
  881. }
  882. }
  883. }