Font.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. /**
  2. * Copyright (c) 2006-2015 LOVE Development Team
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. *
  8. * Permission is granted to anyone to use this software for any purpose,
  9. * including commercial applications, and to alter it and redistribute it
  10. * freely, subject to the following restrictions:
  11. *
  12. * 1. The origin of this software must not be misrepresented; you must not
  13. * claim that you wrote the original software. If you use this software
  14. * in a product, an acknowledgment in the product documentation would be
  15. * appreciated but is not required.
  16. * 2. Altered source versions must be plainly marked as such, and must not be
  17. * misrepresented as being the original software.
  18. * 3. This notice may not be removed or altered from any source distribution.
  19. **/
  20. #include "common/config.h"
  21. #include "Font.h"
  22. #include "font/GlyphData.h"
  23. #include "libraries/utf8/utf8.h"
  24. #include "common/math.h"
  25. #include "common/Matrix.h"
  26. #include <math.h>
  27. #include <sstream>
  28. #include <algorithm> // for max
  29. #include <limits>
  30. namespace love
  31. {
  32. namespace graphics
  33. {
  34. namespace opengl
  35. {
  36. int Font::fontCount = 0;
  37. Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
  38. : rasterizers({r})
  39. , height(r->getHeight())
  40. , lineHeight(1)
  41. , textureWidth(128)
  42. , textureHeight(128)
  43. , filter(filter)
  44. , useSpacesAsTab(false)
  45. , quadIndices(20) // We make this bigger at draw-time, if needed.
  46. , textureCacheID(0)
  47. , textureMemorySize(0)
  48. {
  49. this->filter.mipmap = Texture::FILTER_NONE;
  50. // Try to find the best texture size match for the font size. default to the
  51. // largest texture size if no rough match is found.
  52. while (true)
  53. {
  54. if ((height * 0.8) * height * 30 <= textureWidth * textureHeight)
  55. break;
  56. TextureSize nextsize = getNextTextureSize();
  57. if (nextsize.width <= textureWidth && nextsize.height <= textureHeight)
  58. break;
  59. textureWidth = nextsize.width;
  60. textureHeight = nextsize.height;
  61. }
  62. love::font::GlyphData *gd = r->getGlyphData(32); // Space character.
  63. type = (gd->getFormat() == font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
  64. gd->release();
  65. if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
  66. useSpacesAsTab = true;
  67. loadVolatile();
  68. ++fontCount;
  69. }
  70. Font::~Font()
  71. {
  72. unloadVolatile();
  73. --fontCount;
  74. }
  75. Font::TextureSize Font::getNextTextureSize() const
  76. {
  77. TextureSize size = {textureWidth, textureHeight};
  78. int maxsize = std::min(4096, gl.getMaxTextureSize());
  79. if (size.width * 2 <= maxsize || size.height * 2 <= maxsize)
  80. {
  81. // {128, 128} -> {256, 128} -> {256, 256} -> {512, 256} -> etc.
  82. if (size.width == size.height)
  83. size.width *= 2;
  84. else
  85. size.height *= 2;
  86. }
  87. return size;
  88. }
  89. void Font::createTexture()
  90. {
  91. OpenGL::TempDebugGroup debuggroup("Font create texture");
  92. GLenum format = type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA;
  93. size_t bpp = format == GL_LUMINANCE_ALPHA ? 2 : 4;
  94. size_t prevmemsize = textureMemorySize;
  95. if (prevmemsize > 0)
  96. {
  97. textureMemorySize -= (textureWidth * textureHeight * bpp);
  98. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  99. }
  100. GLuint t = 0;
  101. TextureSize size = {textureWidth, textureHeight};
  102. TextureSize nextsize = getNextTextureSize();
  103. bool recreatetexture = false;
  104. // If we have an existing texture already, we'll try replacing it with a
  105. // larger-sized one rather than creating a second one. Having a single
  106. // texture reduces texture switches and draw calls when rendering.
  107. if ((nextsize.width > size.width || nextsize.height > size.height)
  108. && !textures.empty())
  109. {
  110. recreatetexture = true;
  111. size = nextsize;
  112. t = textures.back();
  113. }
  114. else
  115. glGenTextures(1, &t);
  116. gl.bindTexture(t);
  117. gl.setTextureFilter(filter);
  118. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  119. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  120. GLenum internalformat = type == FONT_TRUETYPE ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
  121. // In GLES2, the internalformat and format params of TexImage have to match.
  122. // There is still no GL_LUMINANCE8_ALPHA8 in GLES3, so we have to use
  123. // GL_LUMINANCE_ALPHA even on ES3.
  124. if (GLAD_ES_VERSION_2_0)
  125. internalformat = format;
  126. // Initialize the texture with transparent black.
  127. std::vector<GLubyte> emptydata(size.width * size.height * bpp, 0);
  128. // Clear errors before initializing.
  129. while (glGetError() != GL_NO_ERROR);
  130. glTexImage2D(GL_TEXTURE_2D, 0, internalformat, size.width, size.height, 0,
  131. format, GL_UNSIGNED_BYTE, &emptydata[0]);
  132. if (glGetError() != GL_NO_ERROR)
  133. {
  134. if (!recreatetexture)
  135. gl.deleteTexture(t);
  136. throw love::Exception("Could not create font texture!");
  137. }
  138. textureWidth = size.width;
  139. textureHeight = size.height;
  140. rowHeight = textureX = textureY = TEXTURE_PADDING;
  141. prevmemsize = textureMemorySize;
  142. textureMemorySize += emptydata.size();
  143. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  144. // Re-add the old glyphs if we re-created the existing texture object.
  145. if (recreatetexture)
  146. {
  147. textureCacheID++;
  148. std::vector<uint32> glyphstoadd;
  149. for (const auto &glyphpair : glyphs)
  150. glyphstoadd.push_back(glyphpair.first);
  151. glyphs.clear();
  152. for (uint32 g : glyphstoadd)
  153. addGlyph(g);
  154. }
  155. else
  156. textures.push_back(t);
  157. }
  158. love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
  159. {
  160. // Use spaces for the tab 'glyph'.
  161. if (glyph == 9 && useSpacesAsTab)
  162. {
  163. love::font::GlyphData *spacegd = rasterizers[0]->getGlyphData(32);
  164. love::font::GlyphData::Format fmt = spacegd->getFormat();
  165. love::font::GlyphMetrics gm = {};
  166. gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
  167. gm.bearingX = spacegd->getBearingX();
  168. gm.bearingY = spacegd->getBearingY();
  169. spacegd->release();
  170. return new love::font::GlyphData(glyph, gm, fmt);
  171. }
  172. for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
  173. {
  174. if (r->hasGlyph(glyph))
  175. return r->getGlyphData(glyph);
  176. }
  177. return rasterizers[0]->getGlyphData(glyph);
  178. }
  179. const Font::Glyph &Font::addGlyph(uint32 glyph)
  180. {
  181. love::font::GlyphData *gd = getRasterizerGlyphData(glyph);
  182. int w = gd->getWidth();
  183. int h = gd->getHeight();
  184. if (textureX + w + TEXTURE_PADDING > textureWidth)
  185. {
  186. // out of space - new row!
  187. textureX = TEXTURE_PADDING;
  188. textureY += rowHeight;
  189. rowHeight = TEXTURE_PADDING;
  190. }
  191. if (textureY + h + TEXTURE_PADDING > textureHeight)
  192. {
  193. // totally out of space - new texture!
  194. try
  195. {
  196. createTexture();
  197. }
  198. catch (love::Exception &)
  199. {
  200. gd->release();
  201. throw;
  202. }
  203. }
  204. Glyph g;
  205. g.texture = 0;
  206. g.spacing = gd->getAdvance();
  207. memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
  208. // don't waste space for empty glyphs. also fixes a divide by zero bug with ATI drivers
  209. if (w > 0 && h > 0)
  210. {
  211. const GLuint t = textures.back();
  212. gl.bindTexture(t);
  213. glTexSubImage2D(GL_TEXTURE_2D, 0, textureX, textureY, w, h,
  214. (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
  215. GL_UNSIGNED_BYTE, gd->getData());
  216. g.texture = t;
  217. float tX = (float) textureX, tY = (float) textureY;
  218. float tWidth = (float) textureWidth, tHeight = (float) textureHeight;
  219. // 0----2
  220. // | / |
  221. // | / |
  222. // 1----3
  223. const GlyphVertex verts[4] = {
  224. { 0.0f, 0.0f, tX/tWidth, tY/tHeight},
  225. { 0.0f, float(h), tX/tWidth, (tY+h)/tHeight},
  226. {float(w), 0.0f, (tX+w)/tWidth, tY/tHeight},
  227. {float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight}
  228. };
  229. // Copy vertex data to the glyph and set proper bearing.
  230. for (int i = 0; i < 4; i++)
  231. {
  232. g.vertices[i] = verts[i];
  233. g.vertices[i].x += gd->getBearingX();
  234. g.vertices[i].y -= gd->getBearingY();
  235. }
  236. }
  237. if (w > 0)
  238. textureX += (w + TEXTURE_PADDING);
  239. if (h > 0)
  240. rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
  241. gd->release();
  242. const auto p = glyphs.insert(std::make_pair(glyph, g));
  243. return p.first->second;
  244. }
  245. const Font::Glyph &Font::findGlyph(uint32 glyph)
  246. {
  247. const auto it = glyphs.find(glyph);
  248. if (it != glyphs.end())
  249. return it->second;
  250. return addGlyph(glyph);
  251. }
  252. float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
  253. {
  254. uint64 packedglyphs = ((uint64) leftglyph << 32) | (uint64) rightglyph;
  255. const auto it = kerning.find(packedglyphs);
  256. if (it != kerning.end())
  257. return it->second;
  258. float k = rasterizers[0]->getKerning(leftglyph, rightglyph);
  259. for (const auto &r : rasterizers)
  260. {
  261. if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
  262. {
  263. k = r->getKerning(leftglyph, rightglyph);
  264. break;
  265. }
  266. }
  267. kerning[packedglyphs] = k;
  268. return k;
  269. }
  270. float Font::getHeight() const
  271. {
  272. return (float) height;
  273. }
  274. std::vector<Font::DrawCommand> Font::generateVertices(const Codepoints &codepoints, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
  275. {
  276. // Spacing counter and newline handling.
  277. float dx = offset.x;
  278. float dy = offset.y;
  279. float lineheight = getBaseline();
  280. int maxwidth = 0;
  281. // Keeps track of when we need to switch textures in our vertex array.
  282. std::vector<DrawCommand> drawcommands;
  283. // Pre-allocate space for the maximum possible number of vertices.
  284. size_t vertstartsize = vertices.size();
  285. vertices.reserve(vertstartsize + codepoints.size());
  286. uint32 prevglyph = 0;
  287. for (int i = 0; i < (int) codepoints.size(); i++)
  288. {
  289. uint32 g = codepoints[i];
  290. if (g == '\n')
  291. {
  292. if (dx > maxwidth)
  293. maxwidth = (int) dx;
  294. // Wrap newline, but do not print it.
  295. dy += floorf(getHeight() * getLineHeight() + 0.5f);
  296. dx = offset.x;
  297. continue;
  298. }
  299. uint32 cacheid = textureCacheID;
  300. const Glyph &glyph = findGlyph(g);
  301. // If findGlyph invalidates the texture cache, re-start the loop.
  302. if (cacheid != textureCacheID)
  303. {
  304. i = 0;
  305. maxwidth = 0;
  306. dx = offset.x;
  307. dy = offset.y;
  308. drawcommands.clear();
  309. vertices.resize(vertstartsize);
  310. prevglyph = 0;
  311. continue;
  312. }
  313. // Add kerning to the current horizontal offset.
  314. dx += getKerning(prevglyph, g);
  315. if (glyph.texture != 0)
  316. {
  317. // Copy the vertices and set their proper relative positions.
  318. for (int j = 0; j < 4; j++)
  319. {
  320. vertices.push_back(glyph.vertices[j]);
  321. vertices.back().x += dx;
  322. vertices.back().y += dy + lineheight;
  323. }
  324. // Check if glyph texture has changed since the last iteration.
  325. if (drawcommands.empty() || drawcommands.back().texture != glyph.texture)
  326. {
  327. // Add a new draw command if the texture has changed.
  328. DrawCommand cmd;
  329. cmd.startvertex = (int) vertices.size() - 4;
  330. cmd.vertexcount = 0;
  331. cmd.texture = glyph.texture;
  332. drawcommands.push_back(cmd);
  333. }
  334. drawcommands.back().vertexcount += 4;
  335. }
  336. // Advance the x position for the next glyph.
  337. dx += glyph.spacing;
  338. // Account for extra spacing given to space characters.
  339. if (g == ' ' && extra_spacing != 0.0f)
  340. dx = floorf(dx + extra_spacing);
  341. prevglyph = g;
  342. }
  343. // Sort draw commands by texture first, and quad position in memory second
  344. // (using the struct's < operator).
  345. std::sort(drawcommands.begin(), drawcommands.end());
  346. if (dx > maxwidth)
  347. maxwidth = (int) dx;
  348. if (info != nullptr)
  349. {
  350. info->width = maxwidth - offset.x;;
  351. info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
  352. }
  353. return drawcommands;
  354. }
  355. std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
  356. {
  357. Codepoints codepoints;
  358. codepoints.reserve(text.size());
  359. try
  360. {
  361. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  362. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  363. while (i != end)
  364. {
  365. uint32 g = *i++;
  366. codepoints.push_back(g);
  367. }
  368. }
  369. catch (utf8::exception &e)
  370. {
  371. throw love::Exception("UTF-8 decoding error: %s", e.what());
  372. }
  373. return generateVertices(codepoints, vertices, extra_spacing, offset, info);
  374. }
  375. std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
  376. {
  377. wrap = std::max(wrap, 0.0f);
  378. uint32 cacheid = textureCacheID;
  379. std::vector<DrawCommand> drawcommands;
  380. vertices.reserve(text.length() * 4);
  381. std::vector<int> widths;
  382. std::vector<Codepoints> lines;
  383. getWrap(text, wrap, lines, &widths);
  384. float y = 0.0f;
  385. float maxwidth = 0.0f;
  386. for (int i = 0; i < (int) lines.size(); i++)
  387. {
  388. const auto &line = lines[i];
  389. float width = (float) widths[i];
  390. love::Vector offset(0.0f, floorf(y));
  391. float extraspacing = 0.0f;
  392. maxwidth = std::max(width, maxwidth);
  393. switch (align)
  394. {
  395. case ALIGN_RIGHT:
  396. offset.x = floorf(wrap - width);
  397. break;
  398. case ALIGN_CENTER:
  399. offset.x = floorf((wrap - width) / 2.0f);
  400. break;
  401. case ALIGN_JUSTIFY:
  402. {
  403. float numspaces = (float) std::count(line.begin(), line.end(), ' ');
  404. if (width < wrap && numspaces >= 1)
  405. extraspacing = (wrap - width) / numspaces;
  406. else
  407. extraspacing = 0.0f;
  408. break;
  409. }
  410. case ALIGN_LEFT:
  411. default:
  412. break;
  413. }
  414. std::vector<DrawCommand> commands = generateVertices(line, vertices, extraspacing, offset);
  415. if (!commands.empty())
  416. {
  417. auto firstcmd = commands.begin();
  418. // If the first draw command in the new list has the same texture
  419. // as the last one in the existing list we're building and its
  420. // vertices are in-order, we can combine them (saving a draw call.)
  421. if (!drawcommands.empty())
  422. {
  423. auto prevcmd = drawcommands.back();
  424. if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  425. {
  426. drawcommands.back().vertexcount += firstcmd->vertexcount;
  427. ++firstcmd;
  428. }
  429. }
  430. // Append the new draw commands to the list we're building.
  431. drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
  432. }
  433. y += getHeight() * getLineHeight();
  434. }
  435. if (info != nullptr)
  436. {
  437. info->width = (int) maxwidth;
  438. info->height = (int) y;
  439. }
  440. if (cacheid != textureCacheID)
  441. {
  442. vertices.clear();
  443. drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  444. }
  445. return drawcommands;
  446. }
  447. void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
  448. {
  449. // Vertex attribute pointers need to be set before calling this function.
  450. // This assumes that the attribute pointers are constant for all vertices.
  451. int totalverts = 0;
  452. for (const DrawCommand &cmd : drawcommands)
  453. totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
  454. if ((size_t) totalverts / 4 > quadIndices.getSize())
  455. quadIndices = QuadIndices((size_t) totalverts / 4);
  456. gl.prepareDraw();
  457. const GLenum gltype = quadIndices.getType();
  458. const size_t elemsize = quadIndices.getElementSize();
  459. GLBuffer::Bind bind(*quadIndices.getBuffer());
  460. // We need a separate draw call for every section of the text which uses a
  461. // different texture than the previous section.
  462. for (const DrawCommand &cmd : drawcommands)
  463. {
  464. GLsizei count = (cmd.vertexcount / 4) * 6;
  465. size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
  466. // TODO: Use glDrawElementsBaseVertex when supported?
  467. gl.bindTexture(cmd.texture);
  468. gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
  469. }
  470. }
  471. void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
  472. {
  473. if (vertices.empty() || drawcommands.empty())
  474. return;
  475. OpenGL::TempDebugGroup debuggroup("Font print");
  476. OpenGL::TempTransform transform(gl);
  477. transform.get() *= t;
  478. gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
  479. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
  480. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
  481. drawVertices(drawcommands);
  482. }
  483. void Font::print(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  484. {
  485. std::vector<GlyphVertex> vertices;
  486. std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
  487. Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  488. printv(t, drawcommands, vertices);
  489. }
  490. void Font::printf(const std::string &text, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  491. {
  492. std::vector<GlyphVertex> vertices;
  493. std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  494. Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  495. printv(t, drawcommands, vertices);
  496. }
  497. int Font::getWidth(const std::string &str)
  498. {
  499. if (str.size() == 0) return 0;
  500. std::istringstream iss(str);
  501. std::string line;
  502. int max_width = 0;
  503. while (getline(iss, line, '\n'))
  504. {
  505. int width = 0;
  506. uint32 prevglyph = 0;
  507. try
  508. {
  509. utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
  510. utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
  511. while (i != end)
  512. {
  513. uint32 c = *i++;
  514. const Glyph &g = findGlyph(c);
  515. width += g.spacing + getKerning(prevglyph, c);
  516. prevglyph = c;
  517. }
  518. }
  519. catch (utf8::exception &e)
  520. {
  521. throw love::Exception("UTF-8 decoding error: %s", e.what());
  522. }
  523. max_width = std::max(max_width, width);
  524. }
  525. return max_width;
  526. }
  527. int Font::getWidth(char character)
  528. {
  529. const Glyph &g = findGlyph(character);
  530. return g.spacing;
  531. }
  532. void Font::getWrap(const std::string &text, float wraplimit, std::vector<Codepoints> &lines, std::vector<int> *linewidths)
  533. {
  534. // Split text at newlines.
  535. std::string line;
  536. std::istringstream iss(text);
  537. // A wrapped line of text.
  538. Codepoints wline;
  539. while (std::getline(iss, line, '\n'))
  540. {
  541. float width = 0.0f;
  542. uint32 prevglyph = 0;
  543. wline.clear();
  544. wline.reserve(line.length());
  545. try
  546. {
  547. utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
  548. utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
  549. utf8::iterator<std::string::const_iterator> lastspaceit = end;
  550. while (i != end)
  551. {
  552. uint32 c = *i;
  553. const Glyph &g = findGlyph(c);
  554. float newwidth = width + g.spacing + getKerning(prevglyph, c);
  555. // Wrap the line if it exceeds the wrap limit. Don't wrap yet if
  556. // we're processing a newline character, though.
  557. if (c != ' ' && newwidth > wraplimit)
  558. {
  559. // If this is the first character in the line and it exceeds
  560. // the limit, skip it completely.
  561. if (wline.empty())
  562. ++i;
  563. else if (lastspaceit != end)
  564. {
  565. // 'Rewind' to the last seen space, if the line has one.
  566. // FIXME: This should preferably use vector::erase.
  567. while (!wline.empty() && wline.back() != ' ')
  568. wline.pop_back();
  569. i = lastspaceit;
  570. ++i; // Start the next line after the space.
  571. }
  572. lines.push_back(wline);
  573. if (linewidths)
  574. linewidths->push_back(width);
  575. prevglyph = 0; // Reset kerning information.
  576. width = 0.0f;
  577. wline.clear();
  578. lastspaceit = end;
  579. continue;
  580. }
  581. width = newwidth;
  582. prevglyph = c;
  583. wline.push_back(c);
  584. // Keep track of the last seen space, so we can "rewind" to it
  585. // when wrapping.
  586. if (c == ' ')
  587. lastspaceit = i;
  588. ++i;
  589. }
  590. }
  591. catch (utf8::exception &e)
  592. {
  593. throw love::Exception("UTF-8 decoding error: %s", e.what());
  594. }
  595. // Remove trailing newlines.
  596. if (!wline.empty() && wline.back() == '\n')
  597. wline.pop_back();
  598. lines.push_back(wline);
  599. if (linewidths)
  600. linewidths->push_back(width);
  601. }
  602. }
  603. void Font::getWrap(const std::string &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
  604. {
  605. std::vector<Codepoints> codepointlines;
  606. getWrap(text, wraplimit, codepointlines, linewidths);
  607. std::string line;
  608. for (const Codepoints &codepoints : codepointlines)
  609. {
  610. line.clear();
  611. line.reserve(codepoints.size());
  612. for (uint32 codepoint : codepoints)
  613. {
  614. char character[5] = {'\0'};
  615. char *end = utf8::unchecked::append(codepoint, character);
  616. line.append(character, end - character);
  617. }
  618. lines.push_back(line);
  619. }
  620. }
  621. void Font::setLineHeight(float height)
  622. {
  623. lineHeight = height;
  624. }
  625. float Font::getLineHeight() const
  626. {
  627. return lineHeight;
  628. }
  629. void Font::setFilter(const Texture::Filter &f)
  630. {
  631. if (!Texture::validateFilter(f, false))
  632. throw love::Exception("Invalid texture filter.");
  633. filter = f;
  634. for (GLuint texture : textures)
  635. {
  636. gl.bindTexture(texture);
  637. gl.setTextureFilter(filter);
  638. }
  639. }
  640. const Texture::Filter &Font::getFilter()
  641. {
  642. return filter;
  643. }
  644. bool Font::loadVolatile()
  645. {
  646. createTexture();
  647. textureCacheID++;
  648. return true;
  649. }
  650. void Font::unloadVolatile()
  651. {
  652. // nuke everything from orbit
  653. glyphs.clear();
  654. for (GLuint texture : textures)
  655. gl.deleteTexture(texture);
  656. textures.clear();
  657. gl.updateTextureMemorySize(textureMemorySize, 0);
  658. textureMemorySize = 0;
  659. }
  660. int Font::getAscent() const
  661. {
  662. return rasterizers[0]->getAscent();
  663. }
  664. int Font::getDescent() const
  665. {
  666. return rasterizers[0]->getDescent();
  667. }
  668. float Font::getBaseline() const
  669. {
  670. // 1.25 is magic line height for true type fonts
  671. return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
  672. }
  673. bool Font::hasGlyph(uint32 glyph) const
  674. {
  675. for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
  676. {
  677. if (r->hasGlyph(glyph))
  678. return true;
  679. }
  680. return false;
  681. }
  682. bool Font::hasGlyphs(const std::string &text) const
  683. {
  684. if (text.size() == 0)
  685. return false;
  686. try
  687. {
  688. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  689. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  690. while (i != end)
  691. {
  692. uint32 codepoint = *i++;
  693. if (!hasGlyph(codepoint))
  694. return false;
  695. }
  696. }
  697. catch (utf8::exception &e)
  698. {
  699. throw love::Exception("UTF-8 decoding error: %s", e.what());
  700. }
  701. return true;
  702. }
  703. void Font::setFallbacks(const std::vector<Font *> &fallbacks)
  704. {
  705. for (const Font *f : fallbacks)
  706. {
  707. if (f->type != this->type)
  708. throw love::Exception("Font fallbacks must be of the same font type.");
  709. }
  710. rasterizers.resize(1);
  711. // NOTE: this won't invalidate already-rasterized glyphs.
  712. for (const Font *f : fallbacks)
  713. rasterizers.push_back(f->rasterizers[0]);
  714. }
  715. uint32 Font::getTextureCacheID() const
  716. {
  717. return textureCacheID;
  718. }
  719. bool Font::getConstant(const char *in, AlignMode &out)
  720. {
  721. return alignModes.find(in, out);
  722. }
  723. bool Font::getConstant(AlignMode in, const char *&out)
  724. {
  725. return alignModes.find(in, out);
  726. }
  727. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
  728. {
  729. { "left", Font::ALIGN_LEFT },
  730. { "right", Font::ALIGN_RIGHT },
  731. { "center", Font::ALIGN_CENTER },
  732. { "justify", Font::ALIGN_JUSTIFY },
  733. };
  734. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
  735. } // opengl
  736. } // graphics
  737. } // love