Font.cpp 13 KB

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