Font.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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. : rasterizer(r)
  39. , height(r->getHeight())
  40. , lineHeight(1)
  41. , textureWidth(128)
  42. , textureHeight(128)
  43. , filter(filter)
  44. , useSpacesAsTab(false)
  45. , indexBuffer(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. GLenum format = type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA;
  92. size_t bpp = format == GL_LUMINANCE_ALPHA ? 2 : 4;
  93. size_t prevmemsize = textureMemorySize;
  94. if (prevmemsize > 0)
  95. {
  96. textureMemorySize -= (textureWidth * textureHeight * bpp);
  97. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  98. }
  99. GLuint t = 0;
  100. TextureSize size = {textureWidth, textureHeight};
  101. TextureSize nextsize = getNextTextureSize();
  102. bool recreatetexture = false;
  103. // If we have an existing texture already, we'll try replacing it with a
  104. // larger-sized one rather than creating a second one. Having a single
  105. // texture reduces texture switches and draw calls when rendering.
  106. if ((nextsize.width > size.width || nextsize.height > size.height)
  107. && !textures.empty())
  108. {
  109. recreatetexture = true;
  110. size = nextsize;
  111. t = textures.back();
  112. }
  113. else
  114. glGenTextures(1, &t);
  115. gl.bindTexture(t);
  116. gl.setTextureFilter(filter);
  117. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  118. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  119. GLenum internalformat = type == FONT_TRUETYPE ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
  120. // in GLES2, the internalformat and format params of TexImage have to match.
  121. if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0)
  122. internalformat = format;
  123. // Initialize the texture with transparent black.
  124. std::vector<GLubyte> emptydata(size.width * size.height * bpp, 0);
  125. // Clear errors before initializing.
  126. while (glGetError() != GL_NO_ERROR);
  127. glTexImage2D(GL_TEXTURE_2D, 0, internalformat, size.width, size.height, 0,
  128. format, GL_UNSIGNED_BYTE, &emptydata[0]);
  129. if (glGetError() != GL_NO_ERROR)
  130. {
  131. if (!recreatetexture)
  132. gl.deleteTexture(t);
  133. throw love::Exception("Could not create font texture!");
  134. }
  135. textureWidth = size.width;
  136. textureHeight = size.height;
  137. rowHeight = textureX = textureY = TEXTURE_PADDING;
  138. prevmemsize = textureMemorySize;
  139. textureMemorySize += emptydata.size();
  140. gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
  141. // Re-add the old glyphs if we re-created the existing texture object.
  142. if (recreatetexture)
  143. {
  144. textureCacheID++;
  145. std::vector<uint32> glyphstoadd;
  146. for (const auto &glyphpair : glyphs)
  147. glyphstoadd.push_back(glyphpair.first);
  148. glyphs.clear();
  149. for (uint32 g : glyphstoadd)
  150. addGlyph(g);
  151. }
  152. else
  153. textures.push_back(t);
  154. }
  155. love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph)
  156. {
  157. // Use spaces for the tab 'glyph'.
  158. if (glyph == 9 && useSpacesAsTab)
  159. {
  160. love::font::GlyphData *spacegd = rasterizer->getGlyphData(32);
  161. love::font::GlyphData::Format fmt = spacegd->getFormat();
  162. love::font::GlyphMetrics gm = {};
  163. gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
  164. gm.bearingX = spacegd->getBearingX();
  165. gm.bearingY = spacegd->getBearingY();
  166. spacegd->release();
  167. return new love::font::GlyphData(glyph, gm, fmt);
  168. }
  169. return rasterizer->getGlyphData(glyph);
  170. }
  171. const Font::Glyph &Font::addGlyph(uint32 glyph)
  172. {
  173. love::font::GlyphData *gd = getRasterizerGlyphData(glyph);
  174. int w = gd->getWidth();
  175. int h = gd->getHeight();
  176. if (textureX + w + TEXTURE_PADDING > textureWidth)
  177. {
  178. // out of space - new row!
  179. textureX = TEXTURE_PADDING;
  180. textureY += rowHeight;
  181. rowHeight = TEXTURE_PADDING;
  182. }
  183. if (textureY + h + TEXTURE_PADDING > textureHeight)
  184. {
  185. // totally out of space - new texture!
  186. try
  187. {
  188. createTexture();
  189. }
  190. catch (love::Exception &)
  191. {
  192. gd->release();
  193. throw;
  194. }
  195. }
  196. Glyph g;
  197. g.texture = 0;
  198. g.spacing = gd->getAdvance();
  199. memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
  200. // don't waste space for empty glyphs. also fixes a divide by zero bug with ATI drivers
  201. if (w > 0 && h > 0)
  202. {
  203. const GLuint t = textures.back();
  204. gl.bindTexture(t);
  205. glTexSubImage2D(GL_TEXTURE_2D, 0, textureX, textureY, w, h,
  206. (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
  207. GL_UNSIGNED_BYTE, gd->getData());
  208. g.texture = t;
  209. float tX = (float) textureX, tY = (float) textureY;
  210. float tWidth = (float) textureWidth, tHeight = (float) textureHeight;
  211. const GlyphVertex verts[4] = {
  212. { 0.0f, 0.0f, tX/tWidth, tY/tHeight},
  213. { 0.0f, float(h), tX/tWidth, (tY+h)/tHeight},
  214. {float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight},
  215. {float(w), 0.0f, (tX+w)/tWidth, tY/tHeight}
  216. };
  217. // Copy vertex data to the glyph and set proper bearing.
  218. for (int i = 0; i < 4; i++)
  219. {
  220. g.vertices[i] = verts[i];
  221. g.vertices[i].x += gd->getBearingX();
  222. g.vertices[i].y -= gd->getBearingY();
  223. }
  224. }
  225. if (w > 0)
  226. textureX += (w + TEXTURE_PADDING);
  227. if (h > 0)
  228. rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
  229. gd->release();
  230. const auto p = glyphs.insert(std::make_pair(glyph, g));
  231. return p.first->second;
  232. }
  233. const Font::Glyph &Font::findGlyph(uint32 glyph)
  234. {
  235. const auto it = glyphs.find(glyph);
  236. if (it != glyphs.end())
  237. return it->second;
  238. return addGlyph(glyph);
  239. }
  240. float Font::getHeight() const
  241. {
  242. return (float) height;
  243. }
  244. std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
  245. {
  246. // Spacing counter and newline handling.
  247. float dx = offset.x;
  248. float dy = offset.y;
  249. float lineheight = getBaseline();
  250. int maxwidth = 0;
  251. // Keeps track of when we need to switch textures in our vertex array.
  252. std::vector<DrawCommand> drawcommands;
  253. // Pre-allocate space for the maximum possible number of vertices.
  254. size_t vertstartsize = vertices.size();
  255. vertices.reserve(vertstartsize + text.length() * 4);
  256. try
  257. {
  258. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  259. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  260. while (i != end)
  261. {
  262. uint32 g = *i++;
  263. if (g == '\n')
  264. {
  265. if (dx > maxwidth)
  266. maxwidth = (int) dx;
  267. // Wrap newline, but do not print it.
  268. dy += floorf(getHeight() * getLineHeight() + 0.5f);
  269. dx = offset.x;
  270. continue;
  271. }
  272. uint32 cacheid = textureCacheID;
  273. const Glyph &glyph = findGlyph(g);
  274. // If findGlyph invalidates the texture cache, re-start the loop.
  275. if (cacheid != textureCacheID)
  276. {
  277. i = utf8::iterator<std::string::const_iterator>(text.begin(), text.begin(), text.end());
  278. maxwidth = 0;
  279. dx = offset.x;
  280. dy = offset.y;
  281. drawcommands.clear();
  282. vertices.resize(vertstartsize);
  283. continue;
  284. }
  285. if (glyph.texture != 0)
  286. {
  287. // Copy the vertices and set their proper relative positions.
  288. for (int j = 0; j < 4; j++)
  289. {
  290. vertices.push_back(glyph.vertices[j]);
  291. vertices.back().x += dx;
  292. vertices.back().y += dy + lineheight;
  293. }
  294. // Check if glyph texture has changed since the last iteration.
  295. if (drawcommands.empty() || drawcommands.back().texture != glyph.texture)
  296. {
  297. // Add a new draw command if the texture has changed.
  298. DrawCommand cmd;
  299. cmd.startvertex = (int) vertices.size() - 4;
  300. cmd.vertexcount = 0;
  301. cmd.texture = glyph.texture;
  302. drawcommands.push_back(cmd);
  303. }
  304. drawcommands.back().vertexcount += 4;
  305. }
  306. // Advance the x position for the next glyph.
  307. dx += glyph.spacing;
  308. // Account for extra spacing given to space characters.
  309. if (g == ' ' && extra_spacing != 0.0f)
  310. dx = floorf(dx + extra_spacing);
  311. }
  312. }
  313. catch (utf8::exception &e)
  314. {
  315. throw love::Exception("UTF-8 decoding error: %s", e.what());
  316. }
  317. // Sort draw commands by texture first, and quad position in memory second
  318. // (using the struct's < operator).
  319. std::sort(drawcommands.begin(), drawcommands.end());
  320. if (dx > maxwidth)
  321. maxwidth = (int) dx;
  322. if (info != nullptr)
  323. {
  324. info->width = maxwidth - offset.x;;
  325. info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
  326. }
  327. return drawcommands;
  328. }
  329. std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
  330. {
  331. if (wrap < 0.0f)
  332. wrap = std::numeric_limits<float>::max();
  333. uint32 cacheid = textureCacheID;
  334. std::vector<DrawCommand> drawcommands;
  335. vertices.reserve(text.length() * 4);
  336. // wrappedlines indicates which lines were automatically wrapped. It
  337. // has the same number of elements as lines_to_draw.
  338. std::vector<bool> wrappedlines;
  339. std::vector<int> widths;
  340. std::vector<std::string> lines;
  341. // We only need the list of wrapped lines in 'justify' mode.
  342. getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr);
  343. float extraspacing = 0.0f;
  344. int numspaces = 0;
  345. int i = 0;
  346. float y = 0.0f;
  347. float maxwidth = 0;
  348. for (const std::string &line : lines)
  349. {
  350. extraspacing = 0.0f;
  351. float width = (float) widths[i];
  352. love::Vector offset(0.0f, floorf(y));
  353. if (width > maxwidth)
  354. maxwidth = width;
  355. switch (align)
  356. {
  357. case ALIGN_RIGHT:
  358. offset.x = floorf(wrap - width);
  359. break;
  360. case ALIGN_CENTER:
  361. offset.x = floorf((wrap - width) / 2.0f);
  362. break;
  363. case ALIGN_JUSTIFY:
  364. numspaces = std::count(line.begin(), line.end(), ' ');
  365. if (wrappedlines[i] && numspaces >= 1)
  366. extraspacing = (wrap - width) / float(numspaces);
  367. else
  368. extraspacing = 0.0f;
  369. break;
  370. case ALIGN_LEFT:
  371. default:
  372. break;
  373. }
  374. std::vector<DrawCommand> commands = generateVertices(line, vertices, extraspacing, offset);
  375. if (!commands.empty())
  376. {
  377. // If the first draw command in the new list has the same texture
  378. // as the last one in the existing list we're building and its
  379. // vertices are in-order, we can combine them (saving a draw call.)
  380. auto firstcmd = commands.begin();
  381. auto prevcmd = drawcommands.back();
  382. if (!drawcommands.empty() && prevcmd.texture == firstcmd->texture
  383. && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  384. {
  385. drawcommands.back().vertexcount += firstcmd->vertexcount;
  386. ++firstcmd;
  387. }
  388. // Append the new draw commands to the list we're building.
  389. drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
  390. }
  391. y += getHeight() * getLineHeight();
  392. i++;
  393. }
  394. if (info != nullptr)
  395. {
  396. info->width = (int) maxwidth;
  397. info->height = (int) y;
  398. }
  399. if (cacheid != textureCacheID)
  400. {
  401. vertices.clear();
  402. drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  403. }
  404. return drawcommands;
  405. }
  406. void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
  407. {
  408. // Vertex attribute pointers need to be set before calling this function.
  409. // This assumes that the attribute pointers are constant for all vertices.
  410. int totalverts = 0;
  411. for (const DrawCommand &cmd : drawcommands)
  412. totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
  413. if ((size_t) totalverts / 4 > indexBuffer.getSize())
  414. indexBuffer = VertexIndex((size_t) totalverts / 4);
  415. gl.prepareDraw();
  416. const GLenum gltype = indexBuffer.getType();
  417. const size_t elemsize = indexBuffer.getElementSize();
  418. VertexBuffer::Bind bind(*indexBuffer.getVertexBuffer());
  419. // We need a separate draw call for every section of the text which uses a
  420. // different texture than the previous section.
  421. for (const DrawCommand &cmd : drawcommands)
  422. {
  423. GLsizei count = (cmd.vertexcount / 4) * 6;
  424. size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
  425. // TODO: Use glDrawElementsBaseVertex when supported?
  426. gl.bindTexture(cmd.texture);
  427. gl.drawElements(GL_TRIANGLES, count, gltype, indexBuffer.getPointer(offset));
  428. }
  429. }
  430. void Font::printv(const Matrix &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
  431. {
  432. if (vertices.empty() || drawcommands.empty())
  433. return;
  434. OpenGL::TempTransform transform(gl);
  435. transform.get() *= t;
  436. glEnableVertexAttribArray(ATTRIB_POS);
  437. glEnableVertexAttribArray(ATTRIB_TEXCOORD);
  438. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
  439. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
  440. try
  441. {
  442. drawVertices(drawcommands);
  443. }
  444. catch (love::Exception &)
  445. {
  446. glDisableVertexAttribArray(ATTRIB_TEXCOORD);
  447. glDisableVertexAttribArray(ATTRIB_POS);
  448. throw;
  449. }
  450. glDisableVertexAttribArray(ATTRIB_TEXCOORD);
  451. glDisableVertexAttribArray(ATTRIB_POS);
  452. }
  453. 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)
  454. {
  455. std::vector<GlyphVertex> vertices;
  456. std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
  457. Matrix t;
  458. t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  459. printv(t, drawcommands, vertices);
  460. }
  461. 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)
  462. {
  463. std::vector<GlyphVertex> vertices;
  464. std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  465. Matrix t;
  466. t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  467. printv(t, drawcommands, vertices);
  468. }
  469. int Font::getWidth(const std::string &str)
  470. {
  471. if (str.size() == 0) return 0;
  472. std::istringstream iss(str);
  473. std::string line;
  474. int max_width = 0;
  475. while (getline(iss, line, '\n'))
  476. {
  477. int width = 0;
  478. try
  479. {
  480. utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
  481. utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
  482. while (i != end)
  483. {
  484. uint32 c = *i++;
  485. const Glyph &g = findGlyph(c);
  486. width += g.spacing;
  487. }
  488. }
  489. catch(utf8::exception &e)
  490. {
  491. throw love::Exception("UTF-8 decoding error: %s", e.what());
  492. }
  493. if (width > max_width)
  494. max_width = width;
  495. }
  496. return max_width;
  497. }
  498. int Font::getWidth(char character)
  499. {
  500. const Glyph &g = findGlyph(character);
  501. return g.spacing;
  502. }
  503. void Font::getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *linewidths, std::vector<bool> *wrappedlines)
  504. {
  505. using namespace std;
  506. const float width_space = (float) getWidth(' ');
  507. //split text at newlines
  508. istringstream iss(text);
  509. string line;
  510. ostringstream string_builder;
  511. while (getline(iss, line, '\n'))
  512. {
  513. // split line into words
  514. vector<string> words;
  515. istringstream word_iss(line);
  516. copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
  517. back_inserter< vector<string> >(words));
  518. // put words back together until a wrap occurs
  519. float width = 0.0f;
  520. float oldwidth = 0.0f;
  521. string_builder.str("");
  522. vector<string>::const_iterator word_iter, wend = words.end();
  523. for (word_iter = words.begin(); word_iter != wend; ++word_iter)
  524. {
  525. const string &word = *word_iter;
  526. width += getWidth(word);
  527. // on wordwrap, push line to line buffer and clear string builder
  528. if (width > wrap && oldwidth > 0)
  529. {
  530. int realw = (int) width;
  531. // remove trailing space
  532. string tmp = string_builder.str();
  533. lines.push_back(tmp.substr(0,tmp.size()-1));
  534. string_builder.str("");
  535. width = static_cast<float>(getWidth(word));
  536. realw -= (int) width;
  537. if (linewidths)
  538. linewidths->push_back(realw);
  539. // Indicate that this line was automatically wrapped.
  540. if (wrappedlines)
  541. wrappedlines->push_back(true);
  542. }
  543. string_builder << word << " ";
  544. width += width_space;
  545. oldwidth = width;
  546. }
  547. // push last line
  548. if (linewidths)
  549. linewidths->push_back(width);
  550. string tmp = string_builder.str();
  551. lines.push_back(tmp.substr(0,tmp.size()-1));
  552. // Indicate that this line was not automatically wrapped.
  553. if (wrappedlines)
  554. wrappedlines->push_back(false);
  555. }
  556. }
  557. void Font::setLineHeight(float height)
  558. {
  559. lineHeight = height;
  560. }
  561. float Font::getLineHeight() const
  562. {
  563. return lineHeight;
  564. }
  565. void Font::setFilter(const Texture::Filter &f)
  566. {
  567. if (!Texture::validateFilter(f, false))
  568. throw love::Exception("Invalid texture filter.");
  569. filter = f;
  570. for (GLuint texture : textures)
  571. {
  572. gl.bindTexture(texture);
  573. gl.setTextureFilter(filter);
  574. }
  575. }
  576. const Texture::Filter &Font::getFilter()
  577. {
  578. return filter;
  579. }
  580. bool Font::loadVolatile()
  581. {
  582. createTexture();
  583. textureCacheID++;
  584. return true;
  585. }
  586. void Font::unloadVolatile()
  587. {
  588. // nuke everything from orbit
  589. glyphs.clear();
  590. for (GLuint texture : textures)
  591. gl.deleteTexture(texture);
  592. textures.clear();
  593. gl.updateTextureMemorySize(textureMemorySize, 0);
  594. textureMemorySize = 0;
  595. }
  596. int Font::getAscent() const
  597. {
  598. return rasterizer->getAscent();
  599. }
  600. int Font::getDescent() const
  601. {
  602. return rasterizer->getDescent();
  603. }
  604. float Font::getBaseline() const
  605. {
  606. // 1.25 is magic line height for true type fonts
  607. return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
  608. }
  609. bool Font::hasGlyph(uint32 glyph) const
  610. {
  611. return rasterizer->hasGlyph(glyph);
  612. }
  613. bool Font::hasGlyphs(const std::string &text) const
  614. {
  615. return rasterizer->hasGlyphs(text);
  616. }
  617. uint32 Font::getTextureCacheID() const
  618. {
  619. return textureCacheID;
  620. }
  621. bool Font::getConstant(const char *in, AlignMode &out)
  622. {
  623. return alignModes.find(in, out);
  624. }
  625. bool Font::getConstant(AlignMode in, const char *&out)
  626. {
  627. return alignModes.find(in, out);
  628. }
  629. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
  630. {
  631. { "left", Font::ALIGN_LEFT },
  632. { "right", Font::ALIGN_RIGHT },
  633. { "center", Font::ALIGN_CENTER },
  634. { "justify", Font::ALIGN_JUSTIFY },
  635. };
  636. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
  637. } // opengl
  638. } // graphics
  639. } // love