Text.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /**
  2. * Copyright (c) 2006-2016 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 "Text.h"
  21. #include "common/Matrix.h"
  22. #include <algorithm>
  23. namespace love
  24. {
  25. namespace graphics
  26. {
  27. namespace opengl
  28. {
  29. love::Type Text::type("Text", &Drawable::type);
  30. Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
  31. : font(font)
  32. , vbo(nullptr)
  33. , vert_offset(0)
  34. , texture_cache_id((uint32) -1)
  35. {
  36. set(text);
  37. }
  38. Text::~Text()
  39. {
  40. delete vbo;
  41. }
  42. void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
  43. {
  44. size_t offset = vertoffset * sizeof(Font::GlyphVertex);
  45. size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
  46. uint8 *vbodata = nullptr;
  47. // If we haven't created a VBO or the vertices are too big, make a new one.
  48. if (datasize > 0 && (!vbo || (offset + datasize) > vbo->getSize()))
  49. {
  50. // Make it bigger than necessary to reduce potential future allocations.
  51. size_t newsize = size_t((offset + datasize) * 1.5);
  52. if (vbo != nullptr)
  53. newsize = std::max(size_t(vbo->getSize() * 1.5), newsize);
  54. GLBuffer *new_vbo = new GLBuffer(newsize, nullptr, BUFFER_VERTEX, GL_DYNAMIC_DRAW);
  55. if (vbo != nullptr)
  56. {
  57. try
  58. {
  59. vbodata = (uint8 *) vbo->map();
  60. }
  61. catch (love::Exception &)
  62. {
  63. delete new_vbo;
  64. throw;
  65. }
  66. new_vbo->fill(0, vbo->getSize(), vbodata);
  67. }
  68. delete vbo;
  69. vbo = new_vbo;
  70. }
  71. if (vbo != nullptr && datasize > 0)
  72. {
  73. vbodata = (uint8 *) vbo->map();
  74. memcpy(vbodata + offset, &vertices[0], datasize);
  75. // We unmap when we draw, to avoid unnecessary full map()/unmap() calls.
  76. }
  77. }
  78. void Text::regenerateVertices()
  79. {
  80. // If the font's texture cache was invalidated then we need to recreate the
  81. // text's vertices, since glyph texcoords might have changed.
  82. if (font->getTextureCacheID() != texture_cache_id)
  83. {
  84. std::vector<TextData> textdata = text_data;
  85. clear();
  86. for (const TextData &t : textdata)
  87. addTextData(t);
  88. texture_cache_id = font->getTextureCacheID();
  89. }
  90. }
  91. void Text::addTextData(const TextData &t)
  92. {
  93. std::vector<Font::GlyphVertex> vertices;
  94. std::vector<Font::DrawCommand> new_commands;
  95. Font::TextInfo text_info;
  96. // We only have formatted text if the align mode is valid.
  97. if (t.align == Font::ALIGN_MAX_ENUM)
  98. new_commands = font->generateVertices(t.codepoints, vertices, 0.0f, Vector(0.0f, 0.0f), &text_info);
  99. else
  100. new_commands = font->generateVerticesFormatted(t.codepoints, t.wrap, t.align, vertices, &text_info);
  101. if (t.use_matrix)
  102. t.matrix.transform(&vertices[0], &vertices[0], (int) vertices.size());
  103. size_t voffset = vert_offset;
  104. if (!t.append_vertices)
  105. {
  106. voffset = 0;
  107. draw_commands.clear();
  108. text_data.clear();
  109. }
  110. uploadVertices(vertices, voffset);
  111. if (!new_commands.empty())
  112. {
  113. // The start vertex should be adjusted to account for the vertex offset.
  114. for (Font::DrawCommand &cmd : new_commands)
  115. cmd.startvertex += (int) voffset;
  116. auto firstcmd = new_commands.begin();
  117. // If the first draw command in the new list has the same texture as the
  118. // last one in the existing list we're building and its vertices are
  119. // in-order, we can combine them (saving a draw call.)
  120. if (!draw_commands.empty())
  121. {
  122. auto prevcmd = draw_commands.back();
  123. if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  124. {
  125. draw_commands.back().vertexcount += firstcmd->vertexcount;
  126. ++firstcmd;
  127. }
  128. }
  129. // Append the new draw commands to the list we're building.
  130. draw_commands.insert(draw_commands.end(), firstcmd, new_commands.end());
  131. }
  132. vert_offset = voffset + vertices.size();
  133. text_data.push_back(t);
  134. text_data.back().text_info = text_info;
  135. // Font::generateVertices can invalidate the font's texture cache.
  136. if (font->getTextureCacheID() != texture_cache_id)
  137. regenerateVertices();
  138. }
  139. void Text::set(const std::vector<Font::ColoredString> &text)
  140. {
  141. return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
  142. }
  143. void Text::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
  144. {
  145. if (text.empty() || (text.size() == 1 && text[0].str.empty()))
  146. return clear();
  147. Font::ColoredCodepoints codepoints;
  148. Font::getCodepointsFromString(text, codepoints);
  149. addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
  150. }
  151. int Text::add(const std::vector<Font::ColoredString> &text, const Matrix4 &m)
  152. {
  153. return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
  154. }
  155. int Text::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
  156. {
  157. Font::ColoredCodepoints codepoints;
  158. Font::getCodepointsFromString(text, codepoints);
  159. addTextData({codepoints, wrap, align, {}, true, true, m});
  160. return (int) text_data.size() - 1;
  161. }
  162. void Text::clear()
  163. {
  164. text_data.clear();
  165. draw_commands.clear();
  166. texture_cache_id = font->getTextureCacheID();
  167. vert_offset = 0;
  168. }
  169. void Text::draw(const Matrix4 &m)
  170. {
  171. if (vbo == nullptr || draw_commands.empty())
  172. return;
  173. OpenGL::TempDebugGroup debuggroup("Text object draw");
  174. // Re-generate the text if the Font's texture cache was invalidated.
  175. if (font->getTextureCacheID() != texture_cache_id)
  176. regenerateVertices();
  177. const size_t pos_offset = offsetof(Font::GlyphVertex, x);
  178. const size_t tex_offset = offsetof(Font::GlyphVertex, s);
  179. const size_t color_offset = offsetof(Font::GlyphVertex, color.r);
  180. const size_t stride = sizeof(Font::GlyphVertex);
  181. OpenGL::TempTransform transform(gl);
  182. transform.get() *= m;
  183. {
  184. vbo->bind();
  185. vbo->unmap(); // Make sure all pending data is flushed to the GPU.
  186. // Font::drawVertices expects AttribPointer calls to be done already.
  187. glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
  188. glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset));
  189. glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset));
  190. }
  191. gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
  192. font->drawVertices(draw_commands, true);
  193. }
  194. void Text::setFont(Font *f)
  195. {
  196. font.set(f);
  197. // Invalidate the texture cache ID since the font is different. We also have
  198. // to re-upload all the vertices based on the new font's textures.
  199. texture_cache_id = (uint32) -1;
  200. regenerateVertices();
  201. }
  202. Font *Text::getFont() const
  203. {
  204. return font.get();
  205. }
  206. int Text::getWidth(int index) const
  207. {
  208. if (index < 0)
  209. index = std::max((int) text_data.size() - 1, 0);
  210. if (index >= (int) text_data.size())
  211. return 0;
  212. return text_data[index].text_info.width;
  213. }
  214. int Text::getHeight(int index) const
  215. {
  216. if (index < 0)
  217. index = std::max((int) text_data.size() - 1, 0);
  218. if (index >= (int) text_data.size())
  219. return 0;
  220. return text_data[index].text_info.height;
  221. }
  222. } // opengl
  223. } // graphics
  224. } // love