Font.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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. Font::Font(love::font::Rasterizer * r, const Image::Filter& filter)
  37. : rasterizer(r), height(r->getHeight()), lineHeight(1), mSpacing(1), filter(filter)
  38. {
  39. r->retain();
  40. love::font::GlyphData * gd = r->getGlyphData(32);
  41. type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA ? FONT_TRUETYPE : FONT_IMAGE);
  42. delete gd;
  43. createTexture();
  44. }
  45. Font::~Font()
  46. {
  47. rasterizer->release();
  48. unloadVolatile();
  49. }
  50. void Font::createTexture()
  51. {
  52. texture_x = texture_y = rowHeight = TEXTURE_PADDING;
  53. GLuint t;
  54. glGenTextures(1, &t);
  55. textures.push_back(t);
  56. bindTexture(t);
  57. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
  58. (filter.mag == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST);
  59. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
  60. (filter.min == Image::FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST);
  61. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  62. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  63. GLint format = (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA);
  64. // Initialize the texture
  65. glTexImage2D(GL_TEXTURE_2D,
  66. 0,
  67. GL_RGBA,
  68. (GLsizei)TEXTURE_WIDTH,
  69. (GLsizei)TEXTURE_HEIGHT,
  70. 0,
  71. format,
  72. GL_UNSIGNED_BYTE,
  73. NULL);
  74. // Fill the texture with transparent black
  75. std::vector<GLubyte> emptyData(TEXTURE_WIDTH * TEXTURE_HEIGHT * (type == FONT_TRUETYPE ? 2 : 4), 0);
  76. glTexSubImage2D(GL_TEXTURE_2D,
  77. 0,
  78. 0,
  79. 0,
  80. (GLsizei)TEXTURE_WIDTH,
  81. (GLsizei)TEXTURE_HEIGHT,
  82. format,
  83. GL_UNSIGNED_BYTE,
  84. &emptyData[0]);
  85. }
  86. Font::Glyph * Font::addGlyph(int glyph)
  87. {
  88. Glyph * g = new Glyph;
  89. g->list = glGenLists(1);
  90. if (g->list == 0)
  91. { // opengl failed to generate the list
  92. delete g;
  93. return NULL;
  94. }
  95. love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
  96. g->spacing = gd->getAdvance();
  97. int w = gd->getWidth();
  98. int h = gd->getHeight();
  99. if (texture_x + w + TEXTURE_PADDING > TEXTURE_WIDTH)
  100. { // out of space - new row!
  101. texture_x = TEXTURE_PADDING;
  102. texture_y += rowHeight;
  103. rowHeight = TEXTURE_PADDING;
  104. }
  105. if (texture_y + h + TEXTURE_PADDING > TEXTURE_HEIGHT)
  106. { // totally out of space - new texture!
  107. createTexture();
  108. }
  109. GLuint t = textures.back();
  110. bindTexture(t);
  111. glTexSubImage2D(GL_TEXTURE_2D, 0, texture_x, texture_y, w, h, (type == FONT_TRUETYPE ? GL_LUMINANCE_ALPHA : GL_RGBA), GL_UNSIGNED_BYTE, gd->getData());
  112. g->texture = t;
  113. Quad::Viewport v;
  114. v.x = (float) texture_x;
  115. v.y = (float) texture_y;
  116. v.w = (float) w;
  117. v.h = (float) h;
  118. Quad * q = new Quad(v, (const float) TEXTURE_WIDTH, (const float) TEXTURE_HEIGHT);
  119. const vertex * verts = q->getVertices();
  120. glEnableClientState(GL_VERTEX_ARRAY);
  121. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  122. glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].x);
  123. glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid *)&verts[0].s);
  124. glNewList(g->list, GL_COMPILE);
  125. glPushMatrix();
  126. glTranslatef(static_cast<float>(gd->getBearingX()), static_cast<float>(-gd->getBearingY()), 0.0f);
  127. glDrawArrays(GL_QUADS, 0, 4);
  128. glPopMatrix();
  129. glEndList();
  130. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  131. glDisableClientState(GL_VERTEX_ARRAY);
  132. delete q;
  133. delete gd;
  134. texture_x += (w + TEXTURE_PADDING);
  135. rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
  136. glyphs[glyph] = g;
  137. return g;
  138. }
  139. float Font::getHeight() const
  140. {
  141. return static_cast<float>(height);
  142. }
  143. void Font::print(std::string text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
  144. {
  145. float dx = 0.0f; // spacing counter for newline handling
  146. glPushMatrix();
  147. Matrix t;
  148. t.setTransformation(ceil(x), ceil(y), angle, sx, sy, ox, oy, kx, ky);
  149. glMultMatrixf((const GLfloat*)t.getElements());
  150. try
  151. {
  152. utf8::iterator<std::string::iterator> i (text.begin(), text.begin(), text.end());
  153. utf8::iterator<std::string::iterator> end (text.end(), text.begin(), text.end());
  154. while (i != end)
  155. {
  156. int g = *i++;
  157. if (g == '\n')
  158. { // wrap newline, but do not print it
  159. glTranslatef(-dx, floor(getHeight() * getLineHeight() + 0.5f), 0);
  160. dx = 0.0f;
  161. continue;
  162. }
  163. Glyph * glyph = glyphs[g];
  164. if (!glyph) glyph = addGlyph(g);
  165. glPushMatrix();
  166. // 1.25 is magic line height for true type fonts
  167. if (type == FONT_TRUETYPE) glTranslatef(0, floor(getHeight() / 1.25f + 0.5f), 0);
  168. bindTexture(glyph->texture);
  169. glCallList(glyph->list);
  170. glPopMatrix();
  171. glTranslatef(static_cast<GLfloat>(glyph->spacing), 0, 0);
  172. dx += glyph->spacing;
  173. }
  174. }
  175. catch (utf8::exception & e)
  176. {
  177. glPopMatrix();
  178. throw love::Exception("%s", e.what());
  179. }
  180. glPopMatrix();
  181. }
  182. void Font::print(char character, float x, float y)
  183. {
  184. Glyph * glyph = glyphs[character];
  185. if (!glyph) glyph = addGlyph(character);
  186. glPushMatrix();
  187. glTranslatef(x, floor(y+getHeight() + 0.5f), 0.0f);
  188. bindTexture(glyph->texture);
  189. glCallList(glyph->list);
  190. glPopMatrix();
  191. }
  192. int Font::getWidth(const std::string & line)
  193. {
  194. if (line.size() == 0) return 0;
  195. int temp = 0;
  196. Glyph * g;
  197. try
  198. {
  199. utf8::iterator<std::string::const_iterator> i (line.begin(), line.begin(), line.end());
  200. utf8::iterator<std::string::const_iterator> end (line.end(), line.begin(), line.end());
  201. while (i != end)
  202. {
  203. int c = *i++;
  204. g = glyphs[c];
  205. if (!g) g = addGlyph(c);
  206. temp += static_cast<int>(g->spacing * mSpacing);
  207. }
  208. }
  209. catch (utf8::exception & e)
  210. {
  211. throw love::Exception("%s", e.what());
  212. }
  213. return temp;
  214. }
  215. int Font::getWidth(const char * line)
  216. {
  217. return this->getWidth(std::string(line));
  218. }
  219. int Font::getWidth(const char character)
  220. {
  221. Glyph * g = glyphs[character];
  222. if (!g) g = addGlyph(character);
  223. return g->spacing;
  224. }
  225. std::vector<std::string> Font::getWrap(const std::string text, float wrap, int * max_width)
  226. {
  227. using namespace std;
  228. const float width_space = static_cast<float>(getWidth(' '));
  229. vector<string> lines_to_draw;
  230. int maxw = 0;
  231. //split text at newlines
  232. istringstream iss( text );
  233. string line;
  234. while (getline(iss, line, '\n'))
  235. {
  236. // split line into words
  237. vector<string> words;
  238. istringstream word_iss(line);
  239. copy(istream_iterator<string>(word_iss), istream_iterator<string>(),
  240. back_inserter< vector<string> >(words));
  241. // put words back together until a wrap occurs
  242. float width = 0.0f;
  243. float oldwidth = 0.0f;
  244. ostringstream string_builder;
  245. vector<string>::const_iterator word_iter;
  246. for (word_iter = words.begin(); word_iter != words.end(); ++word_iter)
  247. {
  248. string word( *word_iter );
  249. width += getWidth( word );
  250. // on wordwrap, push line to line buffer and clear string builder
  251. if (width >= wrap && oldwidth > 0)
  252. {
  253. int realw = (int) width;
  254. lines_to_draw.push_back( string_builder.str() );
  255. string_builder.str( "" );
  256. width = static_cast<float>(getWidth( word ));
  257. realw -= (int) width;
  258. if (realw > maxw)
  259. maxw = realw;
  260. }
  261. string_builder << word << " ";
  262. width += width_space;
  263. oldwidth = width;
  264. }
  265. // push last line
  266. if (width > maxw)
  267. maxw = (int) width;
  268. lines_to_draw.push_back( string_builder.str() );
  269. }
  270. if (max_width)
  271. *max_width = maxw;
  272. return lines_to_draw;
  273. }
  274. void Font::setLineHeight(float height)
  275. {
  276. this->lineHeight = height;
  277. }
  278. float Font::getLineHeight() const
  279. {
  280. return lineHeight;
  281. }
  282. void Font::setSpacing(float amount)
  283. {
  284. mSpacing = amount;
  285. }
  286. float Font::getSpacing() const
  287. {
  288. return mSpacing;
  289. }
  290. bool Font::loadVolatile()
  291. {
  292. createTexture();
  293. return true;
  294. }
  295. void Font::unloadVolatile()
  296. {
  297. // nuke everything from orbit
  298. std::map<int, Glyph *>::iterator it = glyphs.begin();
  299. Glyph * g;
  300. while (it != glyphs.end())
  301. {
  302. g = it->second;
  303. glDeleteLists(g->list, 1);
  304. delete g;
  305. glyphs.erase(it++);
  306. }
  307. std::vector<GLuint>::iterator iter = textures.begin();
  308. while (iter != textures.end())
  309. {
  310. deleteTexture(*iter);
  311. iter++;
  312. }
  313. textures.clear();
  314. }
  315. } // opengl
  316. } // graphics
  317. } // love