Font.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  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 std::string &text, 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 + text.length() * 4);
  286. uint32 prevglyph = 0;
  287. try
  288. {
  289. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  290. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  291. while (i != end)
  292. {
  293. uint32 g = *i++;
  294. if (g == '\n')
  295. {
  296. if (dx > maxwidth)
  297. maxwidth = (int) dx;
  298. // Wrap newline, but do not print it.
  299. dy += floorf(getHeight() * getLineHeight() + 0.5f);
  300. dx = offset.x;
  301. continue;
  302. }
  303. uint32 cacheid = textureCacheID;
  304. const Glyph &glyph = findGlyph(g);
  305. // If findGlyph invalidates the texture cache, re-start the loop.
  306. if (cacheid != textureCacheID)
  307. {
  308. i = utf8::iterator<std::string::const_iterator>(text.begin(), text.begin(), text.end());
  309. maxwidth = 0;
  310. dx = offset.x;
  311. dy = offset.y;
  312. drawcommands.clear();
  313. vertices.resize(vertstartsize);
  314. prevglyph = 0;
  315. continue;
  316. }
  317. // Add kerning to the current horizontal offset.
  318. dx += getKerning(prevglyph, g);
  319. if (glyph.texture != 0)
  320. {
  321. // Copy the vertices and set their proper relative positions.
  322. for (int j = 0; j < 4; j++)
  323. {
  324. vertices.push_back(glyph.vertices[j]);
  325. vertices.back().x += dx /*+ kerning*/;
  326. vertices.back().y += dy /*+ kerning.y*/ + lineheight;
  327. }
  328. // Check if glyph texture has changed since the last iteration.
  329. if (drawcommands.empty() || drawcommands.back().texture != glyph.texture)
  330. {
  331. // Add a new draw command if the texture has changed.
  332. DrawCommand cmd;
  333. cmd.startvertex = (int) vertices.size() - 4;
  334. cmd.vertexcount = 0;
  335. cmd.texture = glyph.texture;
  336. drawcommands.push_back(cmd);
  337. }
  338. drawcommands.back().vertexcount += 4;
  339. }
  340. // Advance the x position for the next glyph.
  341. dx += glyph.spacing;
  342. // Account for extra spacing given to space characters.
  343. if (g == ' ' && extra_spacing != 0.0f)
  344. dx = floorf(dx + extra_spacing);
  345. prevglyph = g;
  346. }
  347. }
  348. catch (utf8::exception &e)
  349. {
  350. throw love::Exception("UTF-8 decoding error: %s", e.what());
  351. }
  352. // Sort draw commands by texture first, and quad position in memory second
  353. // (using the struct's < operator).
  354. std::sort(drawcommands.begin(), drawcommands.end());
  355. if (dx > maxwidth)
  356. maxwidth = (int) dx;
  357. if (info != nullptr)
  358. {
  359. info->width = maxwidth - offset.x;;
  360. info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
  361. }
  362. return drawcommands;
  363. }
  364. std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
  365. {
  366. if (wrap < 0.0f)
  367. wrap = std::numeric_limits<float>::max();
  368. uint32 cacheid = textureCacheID;
  369. std::vector<DrawCommand> drawcommands;
  370. vertices.reserve(text.length() * 4);
  371. // wrappedlines indicates which lines were automatically wrapped. It
  372. // has the same number of elements as lines_to_draw.
  373. std::vector<bool> wrappedlines;
  374. std::vector<int> widths;
  375. std::vector<std::string> lines;
  376. // We only need the list of wrapped lines in 'justify' mode.
  377. getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr);
  378. float extraspacing = 0.0f;
  379. int numspaces = 0;
  380. int i = 0;
  381. float y = 0.0f;
  382. float maxwidth = 0;
  383. for (const std::string &line : lines)
  384. {
  385. extraspacing = 0.0f;
  386. float width = (float) widths[i];
  387. love::Vector offset(0.0f, floorf(y));
  388. if (width > maxwidth)
  389. maxwidth = width;
  390. switch (align)
  391. {
  392. case ALIGN_RIGHT:
  393. offset.x = floorf(wrap - width);
  394. break;
  395. case ALIGN_CENTER:
  396. offset.x = floorf((wrap - width) / 2.0f);
  397. break;
  398. case ALIGN_JUSTIFY:
  399. numspaces = (int) std::count(line.begin(), line.end(), ' ');
  400. if (wrappedlines[i] && numspaces >= 1)
  401. extraspacing = (wrap - width) / float(numspaces);
  402. else
  403. extraspacing = 0.0f;
  404. break;
  405. case ALIGN_LEFT:
  406. default:
  407. break;
  408. }
  409. std::vector<DrawCommand> commands = generateVertices(line, vertices, extraspacing, offset);
  410. if (!commands.empty())
  411. {
  412. auto firstcmd = commands.begin();
  413. // If the first draw command in the new list has the same texture
  414. // as the last one in the existing list we're building and its
  415. // vertices are in-order, we can combine them (saving a draw call.)
  416. if (!drawcommands.empty())
  417. {
  418. auto prevcmd = drawcommands.back();
  419. if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  420. {
  421. drawcommands.back().vertexcount += firstcmd->vertexcount;
  422. ++firstcmd;
  423. }
  424. }
  425. // Append the new draw commands to the list we're building.
  426. drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
  427. }
  428. y += getHeight() * getLineHeight();
  429. i++;
  430. }
  431. if (info != nullptr)
  432. {
  433. info->width = (int) maxwidth;
  434. info->height = (int) y;
  435. }
  436. if (cacheid != textureCacheID)
  437. {
  438. vertices.clear();
  439. drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  440. }
  441. return drawcommands;
  442. }
  443. void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
  444. {
  445. // Vertex attribute pointers need to be set before calling this function.
  446. // This assumes that the attribute pointers are constant for all vertices.
  447. int totalverts = 0;
  448. for (const DrawCommand &cmd : drawcommands)
  449. totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
  450. if ((size_t) totalverts / 4 > quadIndices.getSize())
  451. quadIndices = QuadIndices((size_t) totalverts / 4);
  452. gl.prepareDraw();
  453. const GLenum gltype = quadIndices.getType();
  454. const size_t elemsize = quadIndices.getElementSize();
  455. GLBuffer::Bind bind(*quadIndices.getBuffer());
  456. // We need a separate draw call for every section of the text which uses a
  457. // different texture than the previous section.
  458. for (const DrawCommand &cmd : drawcommands)
  459. {
  460. GLsizei count = (cmd.vertexcount / 4) * 6;
  461. size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
  462. // TODO: Use glDrawElementsBaseVertex when supported?
  463. gl.bindTexture(cmd.texture);
  464. gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
  465. }
  466. }
  467. void Font::printv(const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
  468. {
  469. if (vertices.empty() || drawcommands.empty())
  470. return;
  471. OpenGL::TempDebugGroup debuggroup("Font print");
  472. OpenGL::TempTransform transform(gl);
  473. transform.get() *= t;
  474. gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
  475. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
  476. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
  477. drawVertices(drawcommands);
  478. }
  479. 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)
  480. {
  481. std::vector<GlyphVertex> vertices;
  482. std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
  483. Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  484. printv(t, drawcommands, vertices);
  485. }
  486. 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)
  487. {
  488. std::vector<GlyphVertex> vertices;
  489. std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  490. Matrix4 t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  491. printv(t, drawcommands, vertices);
  492. }
  493. int Font::getWidth(const std::string &str)
  494. {
  495. if (str.size() == 0) return 0;
  496. std::istringstream iss(str);
  497. std::string line;
  498. int max_width = 0;
  499. while (getline(iss, line, '\n'))
  500. {
  501. int width = 0;
  502. try
  503. {
  504. uint32 prevglyph = 0;
  505. utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
  506. utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
  507. while (i != end)
  508. {
  509. uint32 c = *i++;
  510. const Glyph &g = findGlyph(c);
  511. width += g.spacing + getKerning(prevglyph, c);
  512. prevglyph = c;
  513. }
  514. }
  515. catch(utf8::exception &e)
  516. {
  517. throw love::Exception("UTF-8 decoding error: %s", e.what());
  518. }
  519. if (width > max_width)
  520. max_width = width;
  521. }
  522. return max_width;
  523. }
  524. int Font::getWidth(char character)
  525. {
  526. const Glyph &g = findGlyph(character);
  527. return g.spacing;
  528. }
  529. void Font::getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *linewidths, std::vector<bool> *wrappedlines)
  530. {
  531. using namespace std;
  532. const float width_space = (float) getWidth(' ');
  533. //split text at newlines
  534. istringstream iss(text);
  535. string line;
  536. ostringstream string_builder;
  537. while (getline(iss, line, '\n'))
  538. {
  539. // split line into words
  540. vector<string> words;
  541. istringstream word_iss(line);
  542. copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
  543. back_inserter< vector<string> >(words));
  544. // put words back together until a wrap occurs
  545. float width = 0.0f;
  546. float oldwidth = 0.0f;
  547. string_builder.str("");
  548. vector<string>::const_iterator word_iter, wend = words.end();
  549. for (word_iter = words.begin(); word_iter != wend; ++word_iter)
  550. {
  551. const string &word = *word_iter;
  552. width += getWidth(word);
  553. // on wordwrap, push line to line buffer and clear string builder
  554. if (width > wrap && oldwidth > 0)
  555. {
  556. int realw = (int) width;
  557. // remove trailing space
  558. string tmp = string_builder.str();
  559. lines.push_back(tmp.substr(0,tmp.size()-1));
  560. string_builder.str("");
  561. width = static_cast<float>(getWidth(word));
  562. realw -= (int) width;
  563. if (linewidths)
  564. linewidths->push_back(realw);
  565. // Indicate that this line was automatically wrapped.
  566. if (wrappedlines)
  567. wrappedlines->push_back(true);
  568. }
  569. string_builder << word << " ";
  570. width += width_space;
  571. oldwidth = width;
  572. }
  573. // push last line
  574. if (linewidths)
  575. linewidths->push_back(width);
  576. string tmp = string_builder.str();
  577. lines.push_back(tmp.substr(0,tmp.size()-1));
  578. // Indicate that this line was not automatically wrapped.
  579. if (wrappedlines)
  580. wrappedlines->push_back(false);
  581. }
  582. }
  583. void Font::setLineHeight(float height)
  584. {
  585. lineHeight = height;
  586. }
  587. float Font::getLineHeight() const
  588. {
  589. return lineHeight;
  590. }
  591. void Font::setFilter(const Texture::Filter &f)
  592. {
  593. if (!Texture::validateFilter(f, false))
  594. throw love::Exception("Invalid texture filter.");
  595. filter = f;
  596. for (GLuint texture : textures)
  597. {
  598. gl.bindTexture(texture);
  599. gl.setTextureFilter(filter);
  600. }
  601. }
  602. const Texture::Filter &Font::getFilter()
  603. {
  604. return filter;
  605. }
  606. bool Font::loadVolatile()
  607. {
  608. createTexture();
  609. textureCacheID++;
  610. return true;
  611. }
  612. void Font::unloadVolatile()
  613. {
  614. // nuke everything from orbit
  615. glyphs.clear();
  616. for (GLuint texture : textures)
  617. gl.deleteTexture(texture);
  618. textures.clear();
  619. gl.updateTextureMemorySize(textureMemorySize, 0);
  620. textureMemorySize = 0;
  621. }
  622. int Font::getAscent() const
  623. {
  624. return rasterizers[0]->getAscent();
  625. }
  626. int Font::getDescent() const
  627. {
  628. return rasterizers[0]->getDescent();
  629. }
  630. float Font::getBaseline() const
  631. {
  632. // 1.25 is magic line height for true type fonts
  633. return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
  634. }
  635. bool Font::hasGlyph(uint32 glyph) const
  636. {
  637. for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
  638. {
  639. if (r->hasGlyph(glyph))
  640. return true;
  641. }
  642. return false;
  643. }
  644. bool Font::hasGlyphs(const std::string &text) const
  645. {
  646. if (text.size() == 0)
  647. return false;
  648. try
  649. {
  650. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  651. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  652. while (i != end)
  653. {
  654. uint32 codepoint = *i++;
  655. if (!hasGlyph(codepoint))
  656. return false;
  657. }
  658. }
  659. catch (utf8::exception &e)
  660. {
  661. throw love::Exception("UTF-8 decoding error: %s", e.what());
  662. }
  663. return true;
  664. }
  665. void Font::setFallbacks(const std::vector<Font *> &fallbacks)
  666. {
  667. for (const Font *f : fallbacks)
  668. {
  669. if (f->type != this->type)
  670. throw love::Exception("Font fallbacks must be of the same font type.");
  671. }
  672. rasterizers.resize(1);
  673. // NOTE: this won't invalidate already-rasterized glyphs.
  674. for (const Font *f : fallbacks)
  675. rasterizers.push_back(f->rasterizers[0]);
  676. }
  677. uint32 Font::getTextureCacheID() const
  678. {
  679. return textureCacheID;
  680. }
  681. bool Font::getConstant(const char *in, AlignMode &out)
  682. {
  683. return alignModes.find(in, out);
  684. }
  685. bool Font::getConstant(AlignMode in, const char *&out)
  686. {
  687. return alignModes.find(in, out);
  688. }
  689. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
  690. {
  691. { "left", Font::ALIGN_LEFT },
  692. { "right", Font::ALIGN_RIGHT },
  693. { "center", Font::ALIGN_CENTER },
  694. { "justify", Font::ALIGN_JUSTIFY },
  695. };
  696. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
  697. } // opengl
  698. } // graphics
  699. } // love