Font.cpp 15 KB

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