Font.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /**
  2. * Copyright (c) 2006-2013 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. namespace love
  30. {
  31. namespace graphics
  32. {
  33. namespace opengl
  34. {
  35. const int Font::TEXTURE_WIDTHS[] = {128, 256, 256, 512, 512, 1024, 1024};
  36. const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512, 1024};
  37. Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
  38. : rasterizer(r)
  39. , height(r->getHeight())
  40. , lineHeight(1)
  41. , mSpacing(1)
  42. , filter(filter)
  43. {
  44. this->filter.mipmap = Texture::FILTER_NONE;
  45. // Try to find the best texture size match for the font size. default to the
  46. // largest texture size if no rough match is found.
  47. textureSizeIndex = NUM_TEXTURE_SIZES - 1;
  48. for (int i = 0; i < NUM_TEXTURE_SIZES; i++)
  49. {
  50. // Make a rough estimate of the total used texture size, based on glyph
  51. // height. The estimated size is likely larger than the actual total
  52. // size, which is good because texture switching is expensive.
  53. if ((height * 0.8) * height * 95 <= TEXTURE_WIDTHS[i] * TEXTURE_HEIGHTS[i])
  54. {
  55. textureSizeIndex = i;
  56. break;
  57. }
  58. }
  59. textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
  60. textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
  61. love::font::GlyphData *gd = 0;
  62. try
  63. {
  64. gd = r->getGlyphData(32);
  65. type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
  66. loadVolatile();
  67. }
  68. catch (love::Exception &)
  69. {
  70. delete gd;
  71. throw;
  72. }
  73. delete gd;
  74. rasterizer->retain();
  75. }
  76. Font::~Font()
  77. {
  78. rasterizer->release();
  79. unloadVolatile();
  80. }
  81. bool Font::initializeTexture(GLenum format)
  82. {
  83. GLint internalformat = (format == GL_LUMINANCE_ALPHA) ? GL_LUMINANCE8_ALPHA8 : GL_RGBA8;
  84. // clear errors before initializing
  85. while (glGetError() != GL_NO_ERROR);
  86. glTexImage2D(GL_TEXTURE_2D,
  87. 0,
  88. internalformat,
  89. (GLsizei)textureWidth,
  90. (GLsizei)textureHeight,
  91. 0,
  92. format,
  93. GL_UNSIGNED_BYTE,
  94. NULL);
  95. return glGetError() == GL_NO_ERROR;
  96. }
  97. void Font::createTexture()
  98. {
  99. textureX = textureY = rowHeight = TEXTURE_PADDING;
  100. GLuint t;
  101. glGenTextures(1, &t);
  102. textures.push_back(t);
  103. gl.bindTexture(t);
  104. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  105. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  106. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  107. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  108. GLenum format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
  109. // Initialize the texture, attempting smaller sizes if initialization fails.
  110. bool initialized = false;
  111. while (textureSizeIndex >= 0)
  112. {
  113. textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
  114. textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
  115. initialized = initializeTexture(format);
  116. if (initialized || textureSizeIndex <= 0)
  117. break;
  118. --textureSizeIndex;
  119. }
  120. if (!initialized)
  121. {
  122. // Clean up before throwing.
  123. gl.deleteTexture(t);
  124. gl.bindTexture(0);
  125. textures.pop_back();
  126. throw love::Exception("Could not create font texture!");
  127. }
  128. // Fill the texture with transparent black.
  129. std::vector<GLubyte> emptyData(textureWidth * textureHeight * (type == FONT_TRUETYPE ? 2 : 4), 0);
  130. glTexSubImage2D(GL_TEXTURE_2D,
  131. 0,
  132. 0, 0,
  133. (GLsizei)textureWidth,
  134. (GLsizei)textureHeight,
  135. format,
  136. GL_UNSIGNED_BYTE,
  137. &emptyData[0]);
  138. setFilter(filter);
  139. }
  140. Font::Glyph *Font::addGlyph(uint32 glyph)
  141. {
  142. love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
  143. int w = gd->getWidth();
  144. int h = gd->getHeight();
  145. if (textureX + w + TEXTURE_PADDING > textureWidth)
  146. {
  147. // out of space - new row!
  148. textureX = TEXTURE_PADDING;
  149. textureY += rowHeight;
  150. rowHeight = TEXTURE_PADDING;
  151. }
  152. if (textureY + h + TEXTURE_PADDING > textureHeight)
  153. {
  154. // totally out of space - new texture!
  155. createTexture();
  156. }
  157. Glyph *g = new Glyph;
  158. g->texture = 0;
  159. g->spacing = gd->getAdvance();
  160. memset(g->vertices, 0, sizeof(GlyphVertex) * 4);
  161. // don't waste space for empty glyphs. also fixes a division by zero bug with ati drivers
  162. if (w > 0 && h > 0)
  163. {
  164. const GLuint t = textures.back();
  165. gl.bindTexture(t);
  166. glTexSubImage2D(GL_TEXTURE_2D,
  167. 0,
  168. textureX,
  169. textureY,
  170. w, h,
  171. (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA),
  172. GL_UNSIGNED_BYTE,
  173. gd->getData());
  174. g->texture = t;
  175. const GlyphVertex verts[4] = {
  176. { 0.0f, 0.0f, float(textureX)/float(textureWidth), float(textureY)/float(textureHeight)},
  177. { 0.0f, float(h), float(textureX)/float(textureWidth), float(textureY+h)/float(textureHeight)},
  178. {float(w), float(h), float(textureX+w)/float(textureWidth), float(textureY+h)/float(textureHeight)},
  179. {float(w), 0.0f, float(textureX+w)/float(textureWidth), float(textureY)/float(textureHeight)},
  180. };
  181. // copy vertex data to the glyph and set proper bearing
  182. for (int i = 0; i < 4; i++)
  183. {
  184. g->vertices[i] = verts[i];
  185. g->vertices[i].x += gd->getBearingX();
  186. g->vertices[i].y -= gd->getBearingY();
  187. }
  188. }
  189. if (w > 0)
  190. textureX += (w + TEXTURE_PADDING);
  191. if (h > 0)
  192. rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
  193. delete gd;
  194. glyphs[glyph] = g;
  195. return g;
  196. }
  197. Font::Glyph *Font::findGlyph(uint32 glyph)
  198. {
  199. auto it = glyphs.find(glyph);
  200. if (it != glyphs.end())
  201. return it->second;
  202. else
  203. return addGlyph(glyph);
  204. }
  205. float Font::getHeight() const
  206. {
  207. return static_cast<float>(height);
  208. }
  209. void Font::print(const std::string &text, float x, float y, float extra_spacing, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  210. {
  211. // Spacing counter and newline handling.
  212. float dx = 0.0f;
  213. float dy = 0.0f;
  214. float lineheight = getBaseline();
  215. // Keeps track of when we need to switch textures in our vertex array.
  216. std::vector<GlyphArrayDrawInfo> glyphinfolist;
  217. // Pre-allocate space for the maximum possible number of vertices.
  218. std::vector<GlyphVertex> glyphverts;
  219. glyphverts.reserve(text.length() * 4);
  220. int vertexcount = 0;
  221. try
  222. {
  223. utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
  224. utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
  225. while (i != end)
  226. {
  227. uint32 g = *i++;
  228. if (g == '\n')
  229. {
  230. // Wrap newline, but do not print it.
  231. dy += floorf(getHeight() * getLineHeight() + 0.5f);
  232. dx = 0.0f;
  233. continue;
  234. }
  235. Glyph *glyph = findGlyph(g);
  236. if (glyph->texture != 0)
  237. {
  238. // Copy the vertices and set their proper relative positions.
  239. for (int j = 0; j < 4; j++)
  240. {
  241. glyphverts.push_back(glyph->vertices[j]);
  242. glyphverts.back().x += dx;
  243. glyphverts.back().y += dy + lineheight;
  244. }
  245. // Check if glyph texture has changed since the last iteration.
  246. if (glyphinfolist.size() == 0 || glyphinfolist.back().texture != glyph->texture)
  247. {
  248. // keep track of each sub-section of the string whose glyphs use different textures than the previous section
  249. GlyphArrayDrawInfo gdrawinfo;
  250. gdrawinfo.startvertex = vertexcount;
  251. gdrawinfo.vertexcount = 0;
  252. gdrawinfo.texture = glyph->texture;
  253. glyphinfolist.push_back(gdrawinfo);
  254. }
  255. vertexcount += 4;
  256. glyphinfolist.back().vertexcount += 4;
  257. }
  258. // Advance the x position for the next glyph.
  259. dx += glyph->spacing;
  260. // Account for extra spacing given to space characters.
  261. if (g == ' ' && extra_spacing != 0.0f)
  262. dx = floorf(dx + extra_spacing);
  263. }
  264. }
  265. catch (utf8::exception &e)
  266. {
  267. throw love::Exception("Decoding error: %s", e.what());
  268. }
  269. if (vertexcount <= 0 || glyphinfolist.size() == 0)
  270. return;
  271. // Sort glyph draw info list by texture first, and quad position in memory
  272. // second (using the struct's < operator).
  273. std::sort(glyphinfolist.begin(), glyphinfolist.end());
  274. glPushMatrix();
  275. Matrix t;
  276. t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
  277. glMultMatrixf((const GLfloat *)t.getElements());
  278. glEnableClientState(GL_VERTEX_ARRAY);
  279. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  280. glVertexPointer(2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[0].x);
  281. glTexCoordPointer(2, GL_FLOAT, sizeof(GlyphVertex), (GLvoid *)&glyphverts[0].s);
  282. gl.prepareDraw();
  283. // We need to draw a new vertex array for every section of the string which
  284. // uses a different texture than the previous section.
  285. std::vector<GlyphArrayDrawInfo>::const_iterator it;
  286. for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
  287. {
  288. gl.bindTexture(it->texture);
  289. glDrawArrays(GL_QUADS, it->startvertex, it->vertexcount);
  290. }
  291. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  292. glDisableClientState(GL_VERTEX_ARRAY);
  293. glPopMatrix();
  294. }
  295. int Font::getWidth(const std::string &str)
  296. {
  297. if (str.size() == 0) return 0;
  298. std::istringstream iss(str);
  299. std::string line;
  300. Glyph *g;
  301. int max_width = 0;
  302. while (getline(iss, line, '\n'))
  303. {
  304. int width = 0;
  305. try
  306. {
  307. utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
  308. utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
  309. while (i != end)
  310. {
  311. uint32 c = *i++;
  312. g = findGlyph(c);
  313. width += static_cast<int>(g->spacing * mSpacing);
  314. }
  315. }
  316. catch(utf8::exception &e)
  317. {
  318. throw love::Exception("Decoding error: %s", e.what());
  319. }
  320. if (width > max_width)
  321. max_width = width;
  322. }
  323. return max_width;
  324. }
  325. int Font::getWidth(char character)
  326. {
  327. Glyph *g = findGlyph(character);
  328. return g->spacing;
  329. }
  330. std::vector<std::string> Font::getWrap(const std::string &text, float wrap, int *max_width, std::vector<bool> *wrappedlines)
  331. {
  332. using namespace std;
  333. const float width_space = static_cast<float>(getWidth(' '));
  334. vector<string> lines_to_draw;
  335. int maxw = 0;
  336. //split text at newlines
  337. istringstream iss(text);
  338. string line;
  339. ostringstream string_builder;
  340. while (getline(iss, line, '\n'))
  341. {
  342. // split line into words
  343. vector<string> words;
  344. istringstream word_iss(line);
  345. copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
  346. back_inserter< vector<string> >(words));
  347. // put words back together until a wrap occurs
  348. float width = 0.0f;
  349. float oldwidth = 0.0f;
  350. string_builder.str("");
  351. vector<string>::const_iterator word_iter, wend = words.end();
  352. for (word_iter = words.begin(); word_iter != wend; ++word_iter)
  353. {
  354. const string &word = *word_iter;
  355. width += getWidth(word);
  356. // on wordwrap, push line to line buffer and clear string builder
  357. if (width > wrap && oldwidth > 0)
  358. {
  359. int realw = (int) width;
  360. // remove trailing space
  361. string tmp = string_builder.str();
  362. lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
  363. string_builder.str("");
  364. width = static_cast<float>(getWidth(word));
  365. realw -= (int) width;
  366. if (realw > maxw)
  367. maxw = realw;
  368. // Indicate that this line was automatically wrapped.
  369. if (wrappedlines)
  370. wrappedlines->push_back(true);
  371. }
  372. string_builder << word << " ";
  373. width += width_space;
  374. oldwidth = width;
  375. }
  376. // push last line
  377. if (width > maxw)
  378. maxw = (int) width;
  379. string tmp = string_builder.str();
  380. lines_to_draw.push_back(tmp.substr(0,tmp.size()-1));
  381. // Indicate that this line was not automatically wrapped.
  382. if (wrappedlines)
  383. wrappedlines->push_back(false);
  384. }
  385. if (max_width)
  386. *max_width = maxw;
  387. return lines_to_draw;
  388. }
  389. void Font::setLineHeight(float height)
  390. {
  391. this->lineHeight = height;
  392. }
  393. float Font::getLineHeight() const
  394. {
  395. return lineHeight;
  396. }
  397. void Font::setSpacing(float amount)
  398. {
  399. mSpacing = amount;
  400. }
  401. float Font::getSpacing() const
  402. {
  403. return mSpacing;
  404. }
  405. void Font::setFilter(const Texture::Filter &f)
  406. {
  407. filter = f;
  408. for (auto it = textures.begin(); it != textures.end(); ++it)
  409. {
  410. gl.bindTexture(*it);
  411. gl.setTextureFilter(filter);
  412. }
  413. }
  414. const Texture::Filter &Font::getFilter()
  415. {
  416. return filter;
  417. }
  418. bool Font::loadVolatile()
  419. {
  420. createTexture();
  421. return true;
  422. }
  423. void Font::unloadVolatile()
  424. {
  425. // nuke everything from orbit
  426. std::map<uint32, Glyph *>::iterator it = glyphs.begin();
  427. Glyph *g;
  428. while (it != glyphs.end())
  429. {
  430. g = it->second;
  431. delete g;
  432. glyphs.erase(it++);
  433. }
  434. std::vector<GLuint>::iterator iter = textures.begin();
  435. while (iter != textures.end())
  436. {
  437. gl.deleteTexture(*iter);
  438. iter++;
  439. }
  440. textures.clear();
  441. }
  442. int Font::getAscent() const
  443. {
  444. return rasterizer->getAscent();
  445. }
  446. int Font::getDescent() const
  447. {
  448. return rasterizer->getDescent();
  449. }
  450. float Font::getBaseline() const
  451. {
  452. // 1.25 is magic line height for true type fonts
  453. return (type == FONT_TRUETYPE) ? floorf(getHeight() / 1.25f + 0.5f) : 0.0f;
  454. }
  455. bool Font::hasGlyph(uint32 glyph) const
  456. {
  457. return rasterizer->hasGlyph(glyph);
  458. }
  459. bool Font::hasGlyphs(const std::string &text) const
  460. {
  461. return rasterizer->hasGlyphs(text);
  462. }
  463. } // opengl
  464. } // graphics
  465. } // love