Font.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. /**
  2. * Copyright (c) 2006-2024 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 "common/math.h"
  24. #include "common/Matrix.h"
  25. #include "Graphics.h"
  26. #include <math.h>
  27. #include <sstream>
  28. #include <algorithm> // for max
  29. #include <limits>
  30. namespace love
  31. {
  32. namespace graphics
  33. {
  34. static inline uint16 normToUint16(double n)
  35. {
  36. return (uint16) (n * LOVE_UINT16_MAX);
  37. }
  38. static inline uint64 packGlyphIndex(love::font::TextShaper::GlyphIndex glyphindex)
  39. {
  40. return ((uint64)glyphindex.rasterizerIndex << 32) | (uint64)glyphindex.index;
  41. }
  42. static inline love::font::TextShaper::GlyphIndex unpackGlyphIndex(uint64 packedindex)
  43. {
  44. return {(int) (packedindex & 0xFFFFFFFF), (int) (packedindex >> 32)};
  45. }
  46. love::Type Font::type("Font", &Object::type);
  47. int Font::fontCount = 0;
  48. const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub;
  49. Font::Font(love::font::Rasterizer *r, const SamplerState &s)
  50. : shaper(r->newTextShaper(), Acquire::NORETAIN)
  51. , textureWidth(128)
  52. , textureHeight(128)
  53. , samplerState()
  54. , dpiScale(r->getDPIScale())
  55. , textureCacheID(0)
  56. {
  57. samplerState.minFilter = s.minFilter;
  58. samplerState.magFilter = s.magFilter;
  59. samplerState.maxAnisotropy = s.maxAnisotropy;
  60. // Try to find the best texture size match for the font size. default to the
  61. // largest texture size if no rough match is found.
  62. while (true)
  63. {
  64. float dpiscale = r->getDPIScale();
  65. if ((shaper->getHeight() * 0.8 * dpiscale) * shaper->getHeight() * 30 * dpiscale <= textureWidth * textureHeight)
  66. break;
  67. TextureSize nextsize = getNextTextureSize();
  68. if (nextsize.width <= textureWidth && nextsize.height <= textureHeight)
  69. break;
  70. textureWidth = nextsize.width;
  71. textureHeight = nextsize.height;
  72. }
  73. love::font::GlyphData *gd = r->getGlyphData(32); // Space character.
  74. pixelFormat = gd->getFormat();
  75. gd->release();
  76. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  77. if (pixelFormat == PIXELFORMAT_LA8_UNORM && !gfx->isPixelFormatSupported(pixelFormat, PIXELFORMATUSAGEFLAGS_SAMPLE))
  78. pixelFormat = PIXELFORMAT_RGBA8_UNORM;
  79. loadVolatile();
  80. ++fontCount;
  81. }
  82. Font::~Font()
  83. {
  84. --fontCount;
  85. }
  86. Font::TextureSize Font::getNextTextureSize() const
  87. {
  88. TextureSize size = {textureWidth, textureHeight};
  89. int maxsize = 2048;
  90. auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
  91. if (gfx != nullptr)
  92. {
  93. const auto &caps = gfx->getCapabilities();
  94. maxsize = (int) caps.limits[Graphics::LIMIT_TEXTURE_SIZE];
  95. }
  96. int maxwidth = std::min(8192, maxsize);
  97. int maxheight = std::min(4096, maxsize);
  98. if (size.width * 2 <= maxwidth || size.height * 2 <= maxheight)
  99. {
  100. // {128, 128} -> {256, 128} -> {256, 256} -> {512, 256} -> etc.
  101. if (size.width == size.height)
  102. size.width *= 2;
  103. else
  104. size.height *= 2;
  105. }
  106. return size;
  107. }
  108. bool Font::loadVolatile()
  109. {
  110. textureCacheID++;
  111. glyphs.clear();
  112. textures.clear();
  113. createTexture();
  114. return true;
  115. }
  116. void Font::createTexture()
  117. {
  118. auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
  119. gfx->flushBatchedDraws();
  120. Texture *texture = nullptr;
  121. TextureSize size = {textureWidth, textureHeight};
  122. TextureSize nextsize = getNextTextureSize();
  123. bool recreatetexture = false;
  124. // If we have an existing texture already, we'll try replacing it with a
  125. // larger-sized one rather than creating a second one. Having a single
  126. // texture reduces texture switches and draw calls when rendering.
  127. if ((nextsize.width > size.width || nextsize.height > size.height) && !textures.empty())
  128. {
  129. recreatetexture = true;
  130. size = nextsize;
  131. textures.pop_back();
  132. }
  133. Texture::Settings settings;
  134. settings.format = pixelFormat;
  135. settings.width = size.width;
  136. settings.height = size.height;
  137. texture = gfx->newTexture(settings, nullptr);
  138. texture->setSamplerState(samplerState);
  139. {
  140. size_t datasize = getPixelFormatSliceSize(pixelFormat, size.width, size.height);
  141. size_t pixelcount = size.width * size.height;
  142. // Initialize the texture with transparent white for truetype fonts
  143. // (since we keep luminance constant and vary alpha in those glyphs),
  144. // and transparent black otherwise.
  145. std::vector<uint8> emptydata(datasize, 0);
  146. if (shaper->getRasterizers()[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
  147. {
  148. if (pixelFormat == PIXELFORMAT_LA8_UNORM)
  149. {
  150. for (size_t i = 0; i < pixelcount; i++)
  151. emptydata[i * 2 + 0] = 255;
  152. }
  153. else if (pixelFormat == PIXELFORMAT_RGBA8_UNORM)
  154. {
  155. for (size_t i = 0; i < pixelcount; i++)
  156. {
  157. emptydata[i * 4 + 0] = 255;
  158. emptydata[i * 4 + 1] = 255;
  159. emptydata[i * 4 + 2] = 255;
  160. }
  161. }
  162. }
  163. Rect rect = {0, 0, size.width, size.height};
  164. texture->replacePixels(emptydata.data(), emptydata.size(), 0, 0, rect, false);
  165. }
  166. textures.emplace_back(texture, Acquire::NORETAIN);
  167. textureWidth = size.width;
  168. textureHeight = size.height;
  169. rowHeight = textureX = textureY = TEXTURE_PADDING;
  170. // Re-add the old glyphs if we re-created the existing texture object.
  171. if (recreatetexture)
  172. {
  173. textureCacheID++;
  174. std::vector<love::font::TextShaper::GlyphIndex> glyphstoadd;
  175. for (const auto &glyphpair : glyphs)
  176. glyphstoadd.push_back(unpackGlyphIndex(glyphpair.first));
  177. glyphs.clear();
  178. for (auto glyphindex : glyphstoadd)
  179. addGlyph(glyphindex);
  180. }
  181. }
  182. void Font::unloadVolatile()
  183. {
  184. glyphs.clear();
  185. textures.clear();
  186. }
  187. love::font::GlyphData *Font::getRasterizerGlyphData(love::font::TextShaper::GlyphIndex glyphindex, float &dpiscale)
  188. {
  189. const auto &r = shaper->getRasterizers()[glyphindex.rasterizerIndex];
  190. dpiscale = r->getDPIScale();
  191. return r->getGlyphDataForIndex(glyphindex.index);
  192. }
  193. const Font::Glyph &Font::addGlyph(love::font::TextShaper::GlyphIndex glyphindex)
  194. {
  195. float glyphdpiscale = getDPIScale();
  196. StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyphindex, glyphdpiscale), Acquire::NORETAIN);
  197. int w = gd->getWidth();
  198. int h = gd->getHeight();
  199. if (w + TEXTURE_PADDING * 2 < textureWidth && h + TEXTURE_PADDING * 2 < textureHeight)
  200. {
  201. if (textureX + w + TEXTURE_PADDING > textureWidth)
  202. {
  203. // Out of space - new row!
  204. textureX = TEXTURE_PADDING;
  205. textureY += rowHeight;
  206. rowHeight = TEXTURE_PADDING;
  207. }
  208. if (textureY + h + TEXTURE_PADDING > textureHeight)
  209. {
  210. // Totally out of space - new texture!
  211. createTexture();
  212. // Makes sure the above code for checking if the glyph can fit at
  213. // the current position in the texture is run again for this glyph.
  214. return addGlyph(glyphindex);
  215. }
  216. }
  217. Glyph g;
  218. g.texture = nullptr;
  219. memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
  220. // Don't waste space for empty glyphs.
  221. if (w > 0 && h > 0)
  222. {
  223. Texture *texture = textures.back();
  224. g.texture = texture;
  225. Rect rect = {textureX, textureY, gd->getWidth(), gd->getHeight()};
  226. if (pixelFormat != gd->getFormat())
  227. {
  228. if (!(pixelFormat == PIXELFORMAT_RGBA8_UNORM && gd->getFormat() == PIXELFORMAT_LA8_UNORM))
  229. throw love::Exception("Cannot upload font glyphs to texture atlas: unexpected format conversion.");
  230. const uint8 *src = (const uint8 *) gd->getData();
  231. size_t dstsize = getPixelFormatSliceSize(pixelFormat, w, h);
  232. std::vector<uint8> dst(dstsize, 0);
  233. uint8 *dstdata = dst.data();
  234. for (int pixel = 0; pixel < w * h; pixel++)
  235. {
  236. dstdata[pixel * 4 + 0] = src[pixel * 2 + 0];
  237. dstdata[pixel * 4 + 1] = src[pixel * 2 + 0];
  238. dstdata[pixel * 4 + 2] = src[pixel * 2 + 0];
  239. dstdata[pixel * 4 + 3] = src[pixel * 2 + 1];
  240. }
  241. texture->replacePixels(dstdata, dstsize, 0, 0, rect, false);
  242. }
  243. else
  244. {
  245. texture->replacePixels(gd->getData(), gd->getSize(), 0, 0, rect, false);
  246. }
  247. double tX = (double) textureX, tY = (double) textureY;
  248. double tWidth = (double) textureWidth, tHeight = (double) textureHeight;
  249. Color32 c(255, 255, 255, 255);
  250. // Extrude the quad borders by 1 pixel. We have an extra pixel of
  251. // transparent padding in the texture atlas, so the quad extrusion will
  252. // add some antialiasing at the edges of the quad.
  253. int o = 1;
  254. // 0---2
  255. // | / |
  256. // 1---3
  257. const GlyphVertex verts[4] =
  258. {
  259. { -o, -o, normToUint16((tX-o)/tWidth), normToUint16((tY-o)/tHeight), c},
  260. { -o, h+o, normToUint16((tX-o)/tWidth), normToUint16((tY+h+o)/tHeight), c},
  261. {w+o, -o, normToUint16((tX+w+o)/tWidth), normToUint16((tY-o)/tHeight), c},
  262. {w+o, h+o, normToUint16((tX+w+o)/tWidth), normToUint16((tY+h+o)/tHeight), c}
  263. };
  264. // Copy vertex data to the glyph and set proper bearing.
  265. for (int i = 0; i < 4; i++)
  266. {
  267. g.vertices[i] = verts[i];
  268. g.vertices[i].x += gd->getBearingX();
  269. g.vertices[i].y -= gd->getBearingY();
  270. g.vertices[i].x /= glyphdpiscale;
  271. g.vertices[i].y /= glyphdpiscale;
  272. }
  273. textureX += w + TEXTURE_PADDING;
  274. rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
  275. }
  276. uint64 packedindex = packGlyphIndex(glyphindex);
  277. glyphs[packedindex] = g;
  278. return glyphs[packedindex];
  279. }
  280. const Font::Glyph &Font::findGlyph(love::font::TextShaper::GlyphIndex glyphindex)
  281. {
  282. uint64 packedindex = packGlyphIndex(glyphindex);
  283. const auto it = glyphs.find(packedindex);
  284. if (it != glyphs.end())
  285. return it->second;
  286. return addGlyph(glyphindex);
  287. }
  288. float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
  289. {
  290. return shaper->getKerning(leftglyph, rightglyph);
  291. }
  292. float Font::getKerning(const std::string &leftchar, const std::string &rightchar)
  293. {
  294. return shaper->getKerning(leftchar, rightchar);
  295. }
  296. float Font::getHeight() const
  297. {
  298. return shaper->getHeight();
  299. }
  300. std::vector<Font::DrawCommand> Font::generateVertices(const love::font::ColoredCodepoints &codepoints, Range range, const Colorf &constantcolor, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector2 offset, love::font::TextShaper::TextInfo *info)
  301. {
  302. std::vector<love::font::TextShaper::GlyphPosition> glyphpositions;
  303. std::vector<love::font::IndexedColor> colors;
  304. shaper->computeGlyphPositions(codepoints, range, offset, extra_spacing, &glyphpositions, &colors, info);
  305. size_t vertstartsize = vertices.size();
  306. vertices.reserve(vertstartsize + glyphpositions.size() * 4);
  307. Colorf linearconstantcolor = gammaCorrectColor(constantcolor);
  308. Color32 curcolor = toColor32(constantcolor);
  309. int curcolori = 0;
  310. int ncolors = (int)colors.size();
  311. // Keeps track of when we need to switch textures in our vertex array.
  312. std::vector<DrawCommand> commands;
  313. for (int i = 0; i < (int) glyphpositions.size(); i++)
  314. {
  315. const auto &info = glyphpositions[i];
  316. uint32 cacheid = textureCacheID;
  317. const Glyph &glyph = findGlyph(info.glyphIndex);
  318. // If findGlyph invalidates the texture cache, restart the loop.
  319. if (cacheid != textureCacheID)
  320. {
  321. i = -1; // The next iteration will increment this to 0.
  322. commands.clear();
  323. vertices.resize(vertstartsize);
  324. curcolori = 0;
  325. curcolor = toColor32(constantcolor);
  326. continue;
  327. }
  328. if (curcolori < ncolors && colors[curcolori].index == i)
  329. {
  330. Colorf c = colors[curcolori].color;
  331. c.r = std::min(std::max(c.r, 0.0f), 1.0f);
  332. c.g = std::min(std::max(c.g, 0.0f), 1.0f);
  333. c.b = std::min(std::max(c.b, 0.0f), 1.0f);
  334. c.a = std::min(std::max(c.a, 0.0f), 1.0f);
  335. gammaCorrectColor(c);
  336. c *= linearconstantcolor;
  337. unGammaCorrectColor(c);
  338. curcolor = toColor32(c);
  339. curcolori++;
  340. }
  341. if (glyph.texture != nullptr)
  342. {
  343. // Copy the vertices and set their colors and relative positions.
  344. for (int j = 0; j < 4; j++)
  345. {
  346. vertices.push_back(glyph.vertices[j]);
  347. vertices.back().x += info.position.x;
  348. vertices.back().y += info.position.y;
  349. vertices.back().color = curcolor;
  350. }
  351. // Check if glyph texture has changed since the last iteration.
  352. if (commands.empty() || commands.back().texture != glyph.texture)
  353. {
  354. // Add a new draw command if the texture has changed.
  355. DrawCommand cmd;
  356. cmd.startvertex = (int)vertices.size() - 4;
  357. cmd.vertexcount = 0;
  358. cmd.texture = glyph.texture;
  359. commands.push_back(cmd);
  360. }
  361. commands.back().vertexcount += 4;
  362. }
  363. }
  364. const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
  365. {
  366. // Texture binds are expensive, so we should sort by that first.
  367. if (a.texture != b.texture)
  368. return a.texture < b.texture;
  369. else
  370. return a.startvertex < b.startvertex;
  371. };
  372. std::sort(commands.begin(), commands.end(), drawsort);
  373. return commands;
  374. }
  375. std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const love::font::ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, love::font::TextShaper::TextInfo *info)
  376. {
  377. wrap = std::max(wrap, 0.0f);
  378. uint32 cacheid = textureCacheID;
  379. std::vector<DrawCommand> drawcommands;
  380. vertices.reserve(text.cps.size() * 4);
  381. std::vector<Range> ranges;
  382. std::vector<int> widths;
  383. shaper->getWrap(text, wrap, ranges, &widths);
  384. float y = 0.0f;
  385. float maxwidth = 0.0f;
  386. for (int i = 0; i < (int)ranges.size(); i++)
  387. {
  388. const auto& range = ranges[i];
  389. if (!range.isValid())
  390. {
  391. y += getHeight() * getLineHeight();
  392. continue;
  393. }
  394. float width = (float) widths[i];
  395. love::Vector2 offset(0.0f, floorf(y));
  396. float extraspacing = 0.0f;
  397. maxwidth = std::max(width, maxwidth);
  398. switch (align)
  399. {
  400. case ALIGN_RIGHT:
  401. offset.x = floorf(wrap - width);
  402. break;
  403. case ALIGN_CENTER:
  404. offset.x = floorf((wrap - width) / 2.0f);
  405. break;
  406. case ALIGN_JUSTIFY:
  407. {
  408. auto start = text.cps.begin() + range.getOffset();
  409. auto end = start + range.getSize();
  410. float numspaces = std::count(start, end, ' ');
  411. if (width < wrap && numspaces >= 1)
  412. extraspacing = (wrap - width) / numspaces;
  413. else
  414. extraspacing = 0.0f;
  415. break;
  416. }
  417. case ALIGN_LEFT:
  418. default:
  419. break;
  420. }
  421. std::vector<DrawCommand> newcommands = generateVertices(text, range, constantcolor, vertices, extraspacing, offset);
  422. if (!newcommands.empty())
  423. {
  424. auto firstcmd = newcommands.begin();
  425. // If the first draw command in the new list has the same texture
  426. // as the last one in the existing list we're building and its
  427. // vertices are in-order, we can combine them (saving a draw call.)
  428. if (!drawcommands.empty())
  429. {
  430. auto prevcmd = drawcommands.back();
  431. if (prevcmd.texture == firstcmd->texture && (prevcmd.startvertex + prevcmd.vertexcount) == firstcmd->startvertex)
  432. {
  433. drawcommands.back().vertexcount += firstcmd->vertexcount;
  434. ++firstcmd;
  435. }
  436. }
  437. // Append the new draw commands to the list we're building.
  438. drawcommands.insert(drawcommands.end(), firstcmd, newcommands.end());
  439. }
  440. y += getHeight() * getLineHeight();
  441. }
  442. if (info != nullptr)
  443. {
  444. info->width = (int) maxwidth;
  445. info->height = (int) y;
  446. }
  447. if (cacheid != textureCacheID)
  448. {
  449. vertices.clear();
  450. drawcommands = generateVerticesFormatted(text, constantcolor, wrap, align, vertices);
  451. }
  452. return drawcommands;
  453. }
  454. void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices)
  455. {
  456. if (vertices.empty() || drawcommands.empty())
  457. return;
  458. Matrix4 m(gfx->getTransform(), t);
  459. for (const DrawCommand &cmd : drawcommands)
  460. {
  461. Graphics::BatchedDrawCommand streamcmd;
  462. streamcmd.formats[0] = vertexFormat;
  463. streamcmd.indexMode = TRIANGLEINDEX_QUADS;
  464. streamcmd.vertexCount = cmd.vertexcount;
  465. streamcmd.texture = cmd.texture;
  466. Graphics::BatchedVertexData data = gfx->requestBatchedDraw(streamcmd);
  467. GlyphVertex *vertexdata = (GlyphVertex *) data.stream[0];
  468. memcpy(vertexdata, &vertices[cmd.startvertex], sizeof(GlyphVertex) * cmd.vertexcount);
  469. m.transformXY(vertexdata, &vertices[cmd.startvertex], cmd.vertexcount);
  470. }
  471. }
  472. void Font::print(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, const Matrix4 &m, const Colorf &constantcolor)
  473. {
  474. love::font::ColoredCodepoints codepoints;
  475. love::font::getCodepointsFromString(text, codepoints);
  476. std::vector<GlyphVertex> vertices;
  477. std::vector<DrawCommand> drawcommands = generateVertices(codepoints, Range(), constantcolor, vertices);
  478. printv(gfx, m, drawcommands, vertices);
  479. }
  480. void Font::printf(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor)
  481. {
  482. love::font::ColoredCodepoints codepoints;
  483. love::font::getCodepointsFromString(text, codepoints);
  484. std::vector<GlyphVertex> vertices;
  485. std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, constantcolor, wrap, align, vertices);
  486. printv(gfx, m, drawcommands, vertices);
  487. }
  488. int Font::getWidth(const std::string &str)
  489. {
  490. return shaper->getWidth(str);
  491. }
  492. int Font::getWidth(uint32 glyph)
  493. {
  494. return shaper->getGlyphAdvance(glyph);
  495. }
  496. void Font::getWrap(const love::font::ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &ranges, std::vector<int> *linewidths)
  497. {
  498. shaper->getWrap(codepoints, wraplimit, ranges, linewidths);
  499. }
  500. void Font::getWrap(const std::vector<love::font::ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
  501. {
  502. shaper->getWrap(text, wraplimit, lines, linewidths);
  503. }
  504. void Font::setLineHeight(float height)
  505. {
  506. shaper->setLineHeight(height);
  507. }
  508. float Font::getLineHeight() const
  509. {
  510. return shaper->getLineHeight();
  511. }
  512. void Font::setSamplerState(const SamplerState &s)
  513. {
  514. samplerState.minFilter = s.minFilter;
  515. samplerState.magFilter = s.magFilter;
  516. samplerState.maxAnisotropy = s.maxAnisotropy;
  517. for (const auto &texture : textures)
  518. texture->setSamplerState(samplerState);
  519. }
  520. const SamplerState &Font::getSamplerState() const
  521. {
  522. return samplerState;
  523. }
  524. int Font::getAscent() const
  525. {
  526. return shaper->getAscent();
  527. }
  528. int Font::getDescent() const
  529. {
  530. return shaper->getDescent();
  531. }
  532. float Font::getBaseline() const
  533. {
  534. return shaper->getBaseline();
  535. }
  536. bool Font::hasGlyph(uint32 glyph) const
  537. {
  538. return shaper->hasGlyph(glyph);
  539. }
  540. bool Font::hasGlyphs(const std::string &text) const
  541. {
  542. return shaper->hasGlyphs(text);
  543. }
  544. void Font::setFallbacks(const std::vector<Font *> &fallbacks)
  545. {
  546. std::vector<love::font::Rasterizer*> rasterizerfallbacks;
  547. for (const Font* f : fallbacks)
  548. rasterizerfallbacks.push_back(f->shaper->getRasterizers()[0]);
  549. shaper->setFallbacks(rasterizerfallbacks);
  550. // Invalidate existing textures.
  551. textureCacheID++;
  552. glyphs.clear();
  553. while (textures.size() > 1)
  554. textures.pop_back();
  555. rowHeight = textureX = textureY = TEXTURE_PADDING;
  556. }
  557. float Font::getDPIScale() const
  558. {
  559. return dpiScale;
  560. }
  561. uint32 Font::getTextureCacheID() const
  562. {
  563. return textureCacheID;
  564. }
  565. bool Font::getConstant(const char *in, AlignMode &out)
  566. {
  567. return alignModes.find(in, out);
  568. }
  569. bool Font::getConstant(AlignMode in, const char *&out)
  570. {
  571. return alignModes.find(in, out);
  572. }
  573. std::vector<std::string> Font::getConstants(AlignMode)
  574. {
  575. return alignModes.getNames();
  576. }
  577. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM>::Entry Font::alignModeEntries[] =
  578. {
  579. { "left", ALIGN_LEFT },
  580. { "right", ALIGN_RIGHT },
  581. { "center", ALIGN_CENTER },
  582. { "justify", ALIGN_JUSTIFY },
  583. };
  584. StringMap<Font::AlignMode, Font::ALIGN_MAX_ENUM> Font::alignModes(Font::alignModeEntries, sizeof(Font::alignModeEntries));
  585. } // graphics
  586. } // love