TextBatch.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * Copyright (c) 2006-2022 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 "TextBatch.h"
  21. #include "Graphics.h"
  22. #include <algorithm>
  23. namespace love
  24. {
  25. namespace graphics
  26. {
  27. love::Type TextBatch::type("TextBatch", &Drawable::type);
  28. TextBatch::TextBatch(Font *font, const std::vector<Font::ColoredString> &text)
  29. : font(font)
  30. , vertexAttributes(Font::vertexFormat, 0)
  31. , vertexData(nullptr)
  32. , modifiedVertices()
  33. , vertOffset(0)
  34. , textureCacheID((uint32) -1)
  35. {
  36. set(text);
  37. }
  38. TextBatch::~TextBatch()
  39. {
  40. if (vertexData != nullptr)
  41. free(vertexData);
  42. }
  43. void TextBatch::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
  44. {
  45. size_t offset = vertoffset * sizeof(Font::GlyphVertex);
  46. size_t datasize = vertices.size() * sizeof(Font::GlyphVertex);
  47. // If we haven't created a VBO or the vertices are too big, make a new one.
  48. if (datasize > 0 && (!vertexBuffer || (offset + datasize) > vertexBuffer->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 (vertexBuffer != nullptr)
  53. newsize = std::max(size_t(vertexBuffer->getSize() * 1.5), newsize);
  54. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  55. Buffer::Settings settings(BUFFERUSAGEFLAG_VERTEX, BUFFERDATAUSAGE_DYNAMIC);
  56. auto decl = Buffer::getCommonFormatDeclaration(Font::vertexFormat);
  57. Buffer *newbuffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0);
  58. void *newdata = nullptr;
  59. if (vertexData != nullptr)
  60. newdata = realloc(vertexData, newsize);
  61. else
  62. newdata = malloc(newsize);
  63. if (newdata == nullptr)
  64. throw love::Exception("Out of memory.");
  65. else
  66. vertexData = (uint8 *) newdata;
  67. vertexBuffer = newbuffer;
  68. vertexBuffers.set(0, vertexBuffer, 0);
  69. }
  70. if (vertexData != nullptr && datasize > 0)
  71. {
  72. memcpy(vertexData + offset, &vertices[0], datasize);
  73. modifiedVertices.encapsulate(offset, datasize);
  74. }
  75. }
  76. void TextBatch::regenerateVertices()
  77. {
  78. // If the font's texture cache was invalidated then we need to recreate the
  79. // text's vertices, since glyph texcoords might have changed.
  80. if (font->getTextureCacheID() != textureCacheID)
  81. {
  82. std::vector<TextData> textdata = textData;
  83. clear();
  84. for (const TextData &t : textdata)
  85. addTextData(t);
  86. textureCacheID = font->getTextureCacheID();
  87. }
  88. }
  89. void TextBatch::addTextData(const TextData &t)
  90. {
  91. std::vector<Font::GlyphVertex> vertices;
  92. std::vector<Font::DrawCommand> newcommands;
  93. Font::TextInfo textinfo;
  94. Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
  95. // We only have formatted text if the align mode is valid.
  96. if (t.align == Font::ALIGN_MAX_ENUM)
  97. newcommands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
  98. else
  99. newcommands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &textinfo);
  100. size_t voffset = vertOffset;
  101. if (!t.appendVertices)
  102. {
  103. voffset = 0;
  104. vertOffset = 0;
  105. drawCommands.clear();
  106. textData.clear();
  107. }
  108. if (t.useMatrix && !vertices.empty())
  109. t.matrix.transformXY(vertices.data(), vertices.data(), (int) vertices.size());
  110. uploadVertices(vertices, voffset);
  111. if (!newcommands.empty())
  112. {
  113. // The start vertex should be adjusted to account for the vertex offset.
  114. for (Font::DrawCommand &cmd : newcommands)
  115. cmd.startvertex += (int) voffset;
  116. auto firstcmd = newcommands.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 (!drawCommands.empty())
  121. {
  122. auto prevcmd = drawCommands.back();
  123. if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  124. {
  125. drawCommands.back().vertexcount += firstcmd->vertexcount;
  126. ++firstcmd;
  127. }
  128. }
  129. // Append the new draw commands to the list we're building.
  130. drawCommands.insert(drawCommands.end(), firstcmd, newcommands.end());
  131. }
  132. vertOffset = voffset + vertices.size();
  133. textData.push_back(t);
  134. textData.back().textInfo = textinfo;
  135. // Font::generateVertices can invalidate the font's texture cache.
  136. if (font->getTextureCacheID() != textureCacheID)
  137. regenerateVertices();
  138. }
  139. void TextBatch::set(const std::vector<Font::ColoredString> &text)
  140. {
  141. return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
  142. }
  143. void TextBatch::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 TextBatch::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 TextBatch::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) textData.size() - 1;
  161. }
  162. void TextBatch::clear()
  163. {
  164. textData.clear();
  165. drawCommands.clear();
  166. textureCacheID = font->getTextureCacheID();
  167. vertOffset = 0;
  168. }
  169. void TextBatch::setFont(Font *f)
  170. {
  171. font.set(f);
  172. // Invalidate the texture cache ID since the font is different. We also have
  173. // to re-upload all the vertices based on the new font's textures.
  174. textureCacheID = (uint32) -1;
  175. regenerateVertices();
  176. }
  177. Font *TextBatch::getFont() const
  178. {
  179. return font.get();
  180. }
  181. int TextBatch::getWidth(int index) const
  182. {
  183. if (index < 0)
  184. index = std::max((int) textData.size() - 1, 0);
  185. if (index >= (int) textData.size())
  186. return 0;
  187. return textData[index].textInfo.width;
  188. }
  189. int TextBatch::getHeight(int index) const
  190. {
  191. if (index < 0)
  192. index = std::max((int) textData.size() - 1, 0);
  193. if (index >= (int) textData.size())
  194. return 0;
  195. return textData[index].textInfo.height;
  196. }
  197. void TextBatch::draw(Graphics *gfx, const Matrix4 &m)
  198. {
  199. if (vertexBuffer == nullptr || vertexData == nullptr || drawCommands.empty())
  200. return;
  201. gfx->flushBatchedDraws();
  202. if (Shader::isDefaultActive())
  203. Shader::attachDefault(Shader::STANDARD_DEFAULT);
  204. Texture *firsttex = nullptr;
  205. if (!drawCommands.empty())
  206. firsttex = drawCommands[0].texture;
  207. if (Shader::current)
  208. Shader::current->validateDrawState(PRIMITIVE_TRIANGLES, firsttex);
  209. // Re-generate the text if the Font's texture cache was invalidated.
  210. if (font->getTextureCacheID() != textureCacheID)
  211. regenerateVertices();
  212. int totalverts = 0;
  213. for (const Font::DrawCommand &cmd : drawCommands)
  214. totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
  215. // Make sure all pending data is uploaded to the GPU.
  216. if (modifiedVertices.isValid())
  217. {
  218. size_t offset = modifiedVertices.getOffset();
  219. size_t size = modifiedVertices.getSize();
  220. if (vertexBuffer->getDataUsage() == BUFFERDATAUSAGE_STREAM)
  221. vertexBuffer->fill(0, vertexBuffer->getSize(), vertexData);
  222. else
  223. vertexBuffer->fill(offset, size, vertexData + offset);
  224. modifiedVertices.invalidate();
  225. }
  226. Graphics::TempTransform transform(gfx, m);
  227. for (const Font::DrawCommand &cmd : drawCommands)
  228. gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
  229. }
  230. } // graphics
  231. } // love