Font.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  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. // 0----2
  212. // | / |
  213. // | / |
  214. // 1----3
  215. const GlyphVertex verts[4] = {
  216. { 0.0f, 0.0f, tX/tWidth, tY/tHeight},
  217. { 0.0f, float(h), tX/tWidth, (tY+h)/tHeight},
  218. {float(w), 0.0f, (tX+w)/tWidth, tY/tHeight},
  219. {float(w), float(h), (tX+w)/tWidth, (tY+h)/tHeight}
  220. };
  221. // Copy vertex data to the glyph and set proper bearing.
  222. for (int i = 0; i < 4; i++)
  223. {
  224. g.vertices[i] = verts[i];
  225. g.vertices[i].x += gd->getBearingX();
  226. g.vertices[i].y -= gd->getBearingY();
  227. }
  228. }
  229. if (w > 0)
  230. textureX += (w + TEXTURE_PADDING);
  231. if (h > 0)
  232. rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
  233. gd->release();
  234. const auto p = glyphs.insert(std::make_pair(glyph, g));
  235. return p.first->second;
  236. }
  237. const Font::Glyph &Font::findGlyph(uint32 glyph)
  238. {
  239. const auto it = glyphs.find(glyph);
  240. if (it != glyphs.end())
  241. return it->second;
  242. return addGlyph(glyph);
  243. }
  244. float Font::getHeight() const
  245. {
  246. return (float) height;
  247. }
  248. std::vector<Font::DrawCommand> Font::generateVertices(const std::string &text, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector offset, TextInfo *info)
  249. {
  250. // Spacing counter and newline handling.
  251. float dx = offset.x;
  252. float dy = offset.y;
  253. float lineheight = getBaseline();
  254. int maxwidth = 0;
  255. // Keeps track of when we need to switch textures in our vertex array.
  256. std::vector<DrawCommand> drawcommands;
  257. // Pre-allocate space for the maximum possible number of vertices.
  258. size_t vertstartsize = vertices.size();
  259. vertices.reserve(vertstartsize + text.length() * 4);
  260. try
  261. {
  262. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  263. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  264. while (i != end)
  265. {
  266. uint32 g = *i++;
  267. if (g == '\n')
  268. {
  269. if (dx > maxwidth)
  270. maxwidth = (int) dx;
  271. // Wrap newline, but do not print it.
  272. dy += floorf(getHeight() * getLineHeight() + 0.5f);
  273. dx = offset.x;
  274. continue;
  275. }
  276. uint32 cacheid = textureCacheID;
  277. const Glyph &glyph = findGlyph(g);
  278. // If findGlyph invalidates the texture cache, re-start the loop.
  279. if (cacheid != textureCacheID)
  280. {
  281. i = utf8::iterator<std::string::const_iterator>(text.begin(), text.begin(), text.end());
  282. maxwidth = 0;
  283. dx = offset.x;
  284. dy = offset.y;
  285. drawcommands.clear();
  286. vertices.resize(vertstartsize);
  287. continue;
  288. }
  289. if (glyph.texture != 0)
  290. {
  291. // Copy the vertices and set their proper relative positions.
  292. for (int j = 0; j < 4; j++)
  293. {
  294. vertices.push_back(glyph.vertices[j]);
  295. vertices.back().x += dx;
  296. vertices.back().y += dy + lineheight;
  297. }
  298. // Check if glyph texture has changed since the last iteration.
  299. if (drawcommands.empty() || drawcommands.back().texture != glyph.texture)
  300. {
  301. // Add a new draw command if the texture has changed.
  302. DrawCommand cmd;
  303. cmd.startvertex = (int) vertices.size() - 4;
  304. cmd.vertexcount = 0;
  305. cmd.texture = glyph.texture;
  306. drawcommands.push_back(cmd);
  307. }
  308. drawcommands.back().vertexcount += 4;
  309. }
  310. // Advance the x position for the next glyph.
  311. dx += glyph.spacing;
  312. // Account for extra spacing given to space characters.
  313. if (g == ' ' && extra_spacing != 0.0f)
  314. dx = floorf(dx + extra_spacing);
  315. }
  316. }
  317. catch (utf8::exception &e)
  318. {
  319. throw love::Exception("UTF-8 decoding error: %s", e.what());
  320. }
  321. // Sort draw commands by texture first, and quad position in memory second
  322. // (using the struct's < operator).
  323. std::sort(drawcommands.begin(), drawcommands.end());
  324. if (dx > maxwidth)
  325. maxwidth = (int) dx;
  326. if (info != nullptr)
  327. {
  328. info->width = maxwidth - offset.x;;
  329. info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
  330. }
  331. return drawcommands;
  332. }
  333. std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const std::string &text, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
  334. {
  335. if (wrap < 0.0f)
  336. wrap = std::numeric_limits<float>::max();
  337. uint32 cacheid = textureCacheID;
  338. std::vector<DrawCommand> drawcommands;
  339. vertices.reserve(text.length() * 4);
  340. // wrappedlines indicates which lines were automatically wrapped. It
  341. // has the same number of elements as lines_to_draw.
  342. std::vector<bool> wrappedlines;
  343. std::vector<int> widths;
  344. std::vector<std::string> lines;
  345. // We only need the list of wrapped lines in 'justify' mode.
  346. getWrap(text, wrap, lines, &widths, align == ALIGN_JUSTIFY ? &wrappedlines : nullptr);
  347. float extraspacing = 0.0f;
  348. int numspaces = 0;
  349. int i = 0;
  350. float y = 0.0f;
  351. float maxwidth = 0;
  352. for (const std::string &line : lines)
  353. {
  354. extraspacing = 0.0f;
  355. float width = (float) widths[i];
  356. love::Vector offset(0.0f, floorf(y));
  357. if (width > maxwidth)
  358. maxwidth = width;
  359. switch (align)
  360. {
  361. case ALIGN_RIGHT:
  362. offset.x = floorf(wrap - width);
  363. break;
  364. case ALIGN_CENTER:
  365. offset.x = floorf((wrap - width) / 2.0f);
  366. break;
  367. case ALIGN_JUSTIFY:
  368. numspaces = (int) std::count(line.begin(), line.end(), ' ');
  369. if (wrappedlines[i] && numspaces >= 1)
  370. extraspacing = (wrap - width) / float(numspaces);
  371. else
  372. extraspacing = 0.0f;
  373. break;
  374. case ALIGN_LEFT:
  375. default:
  376. break;
  377. }
  378. std::vector<DrawCommand> commands = generateVertices(line, vertices, extraspacing, offset);
  379. if (!commands.empty())
  380. {
  381. auto firstcmd = commands.begin();
  382. // If the first draw command in the new list has the same texture
  383. // as the last one in the existing list we're building and its
  384. // vertices are in-order, we can combine them (saving a draw call.)
  385. if (!drawcommands.empty())
  386. {
  387. auto prevcmd = drawcommands.back();
  388. if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  389. {
  390. drawcommands.back().vertexcount += firstcmd->vertexcount;
  391. ++firstcmd;
  392. }
  393. }
  394. // Append the new draw commands to the list we're building.
  395. drawcommands.insert(drawcommands.end(), firstcmd, commands.end());
  396. }
  397. y += getHeight() * getLineHeight();
  398. i++;
  399. }
  400. if (info != nullptr)
  401. {
  402. info->width = (int) maxwidth;
  403. info->height = (int) y;
  404. }
  405. if (cacheid != textureCacheID)
  406. {
  407. vertices.clear();
  408. drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  409. }
  410. return drawcommands;
  411. }
  412. void Font::drawVertices(const std::vector<DrawCommand> &drawcommands)
  413. {
  414. // Vertex attribute pointers need to be set before calling this function.
  415. // This assumes that the attribute pointers are constant for all vertices.
  416. int totalverts = 0;
  417. for (const DrawCommand &cmd : drawcommands)
  418. totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
  419. if ((size_t) totalverts / 4 > indexBuffer.getSize())
  420. indexBuffer = VertexIndex((size_t) totalverts / 4);
  421. gl.prepareDraw();
  422. const GLenum gltype = indexBuffer.getType();
  423. const size_t elemsize = indexBuffer.getElementSize();
  424. GLBuffer::Bind bind(*indexBuffer.getBuffer());
  425. // We need a separate draw call for every section of the text which uses a
  426. // different texture than the previous section.
  427. for (const DrawCommand &cmd : drawcommands)
  428. {
  429. GLsizei count = (cmd.vertexcount / 4) * 6;
  430. size_t offset = (cmd.startvertex / 4) * 6 * elemsize;
  431. // TODO: Use glDrawElementsBaseVertex when supported?
  432. gl.bindTexture(cmd.texture);
  433. gl.drawElements(GL_TRIANGLES, count, gltype, indexBuffer.getPointer(offset));
  434. }
  435. }
  436. void Font::printv(const Matrix &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
  437. {
  438. if (vertices.empty() || drawcommands.empty())
  439. return;
  440. OpenGL::TempTransform transform(gl);
  441. transform.get() *= t;
  442. glEnableVertexAttribArray(ATTRIB_POS);
  443. glEnableVertexAttribArray(ATTRIB_TEXCOORD);
  444. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
  445. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
  446. try
  447. {
  448. drawVertices(drawcommands);
  449. }
  450. catch (love::Exception &)
  451. {
  452. glDisableVertexAttribArray(ATTRIB_TEXCOORD);
  453. glDisableVertexAttribArray(ATTRIB_POS);
  454. throw;
  455. }
  456. glDisableVertexAttribArray(ATTRIB_TEXCOORD);
  457. glDisableVertexAttribArray(ATTRIB_POS);
  458. }
  459. 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)
  460. {
  461. std::vector<GlyphVertex> vertices;
  462. std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
  463. Matrix t;
  464. t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  465. printv(t, drawcommands, vertices);
  466. }
  467. 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)
  468. {
  469. std::vector<GlyphVertex> vertices;
  470. std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
  471. Matrix t;
  472. t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  473. printv(t, drawcommands, vertices);
  474. }
  475. int Font::getWidth(const std::string &str)
  476. {
  477. if (str.size() == 0) return 0;
  478. std::istringstream iss(str);
  479. std::string line;
  480. int max_width = 0;
  481. while (getline(iss, line, '\n'))
  482. {
  483. int width = 0;
  484. try
  485. {
  486. utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
  487. utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
  488. while (i != end)
  489. {
  490. uint32 c = *i++;
  491. const Glyph &g = findGlyph(c);
  492. width += g.spacing;
  493. }
  494. }
  495. catch(utf8::exception &e)
  496. {
  497. throw love::Exception("UTF-8 decoding error: %s", e.what());
  498. }
  499. if (width > max_width)
  500. max_width = width;
  501. }
  502. return max_width;
  503. }
  504. int Font::getWidth(char character)
  505. {
  506. const Glyph &g = findGlyph(character);
  507. return g.spacing;
  508. }
  509. void Font::getWrap(const std::string &text, float wrap, std::vector<std::string> &lines, std::vector<int> *linewidths, std::vector<bool> *wrappedlines)
  510. {
  511. using namespace std;
  512. const float width_space = (float) getWidth(' ');
  513. //split text at newlines
  514. istringstream iss(text);
  515. string line;
  516. ostringstream string_builder;
  517. while (getline(iss, line, '\n'))
  518. {
  519. // split line into words
  520. vector<string> words;
  521. istringstream word_iss(line);
  522. copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
  523. back_inserter< vector<string> >(words));
  524. // put words back together until a wrap occurs
  525. float width = 0.0f;
  526. float oldwidth = 0.0f;
  527. string_builder.str("");
  528. vector<string>::const_iterator word_iter, wend = words.end();
  529. for (word_iter = words.begin(); word_iter != wend; ++word_iter)
  530. {
  531. const string &word = *word_iter;
  532. width += getWidth(word);
  533. // on wordwrap, push line to line buffer and clear string builder
  534. if (width > wrap && oldwidth > 0)
  535. {
  536. int realw = (int) width;
  537. // remove trailing space
  538. string tmp = string_builder.str();
  539. lines.push_back(tmp.substr(0,tmp.size()-1));
  540. string_builder.str("");
  541. width = static_cast<float>(getWidth(word));
  542. realw -= (int) width;
  543. if (linewidths)
  544. linewidths->push_back(realw);
  545. // Indicate that this line was automatically wrapped.
  546. if (wrappedlines)
  547. wrappedlines->push_back(true);
  548. }
  549. string_builder << word << " ";
  550. width += width_space;
  551. oldwidth = width;
  552. }
  553. // push last line
  554. if (linewidths)
  555. linewidths->push_back(width);
  556. string tmp = string_builder.str();
  557. lines.push_back(tmp.substr(0,tmp.size()-1));
  558. // Indicate that this line was not automatically wrapped.
  559. if (wrappedlines)
  560. wrappedlines->push_back(false);
  561. }
  562. }
  563. void Font::setLineHeight(float height)
  564. {
  565. lineHeight = height;
  566. }
  567. float Font::getLineHeight() const
  568. {
  569. return lineHeight;
  570. }
  571. void Font::setFilter(const Texture::Filter &f)
  572. {
  573. if (!Texture::validateFilter(f, false))
  574. throw love::Exception("Invalid texture filter.");
  575. filter = f;
  576. for (GLuint texture : textures)
  577. {
  578. gl.bindTexture(texture);
  579. gl.setTextureFilter(filter);
  580. }
  581. }
  582. const Texture::Filter &Font::getFilter()
  583. {
  584. return filter;
  585. }
  586. bool Font::loadVolatile()
  587. {
  588. createTexture();
  589. textureCacheID++;
  590. return true;
  591. }
  592. void Font::unloadVolatile()
  593. {
  594. // nuke everything from orbit
  595. glyphs.clear();
  596. for (GLuint texture : textures)
  597. gl.deleteTexture(texture);
  598. textures.clear();
  599. gl.updateTextureMemorySize(textureMemorySize, 0);
  600. textureMemorySize = 0;
  601. }
  602. int Font::getAscent() const
  603. {
  604. return rasterizer->getAscent();
  605. }
  606. int Font::getDescent() const
  607. {
  608. return rasterizer->getDescent();
  609. }
  610. float Font::getBaseline() const
  611. {
  612. // 1.25 is magic line height for true type fonts
  613. return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
  614. }
  615. bool Font::hasGlyph(uint32 glyph) const
  616. {
  617. return rasterizer->hasGlyph(glyph);
  618. }
  619. bool Font::hasGlyphs(const std::string &text) const
  620. {
  621. return rasterizer->hasGlyphs(text);
  622. }
  623. uint32 Font::getTextureCacheID() const
  624. {
  625. return textureCacheID;
  626. }
  627. bool Font::getConstant(const char *in, AlignMode &out)
  628. {
  629. return alignModes.find(in, out);
  630. }
  631. bool Font::getConstant(AlignMode in, const char *&out)
  632. {
  633. return alignModes.find(in, out);
  634. }
  635. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
  636. {
  637. { "left", Font::ALIGN_LEFT },
  638. { "right", Font::ALIGN_RIGHT },
  639. { "center", Font::ALIGN_CENTER },
  640. { "justify", Font::ALIGN_JUSTIFY },
  641. };
  642. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
  643. } // opengl
  644. } // graphics
  645. } // love