3
0

FontRenderer.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. //
  9. // Purpose:
  10. // - Render a glyph outline into a bitmap using FreeType 2
  11. #if !defined(USE_NULLFONT_ALWAYS)
  12. #include <AtomLyIntegration/AtomFont/FontRenderer.h>
  13. #include <freetype/ftoutln.h>
  14. #include <freetype/ftglyph.h>
  15. #include <freetype/ftimage.h>
  16. #include <AzCore/Casting/lossy_cast.h>
  17. #include <CryCommon/ISystem.h>
  18. // Sizes are defined in in 26.6 fixed float format (TT_F26Dot6), where
  19. // 1 unit is 1/64 of a pixel.
  20. constexpr int FractionalPixelUnits = 64;
  21. namespace
  22. {
  23. FT_Int32 GetLoadFlags(AZ::FFont::HintBehavior hintBehavior)
  24. {
  25. switch (hintBehavior)
  26. {
  27. case AZ::FFont::HintBehavior::NoHinting:
  28. {
  29. return FT_LOAD_NO_HINTING;
  30. break;
  31. }
  32. case AZ::FFont::HintBehavior::AutoHint:
  33. {
  34. return FT_LOAD_FORCE_AUTOHINT;
  35. break;
  36. }
  37. }
  38. return FT_LOAD_DEFAULT;
  39. }
  40. FT_Int32 GetLoadTarget(AZ::FFont::HintStyle hintStyle)
  41. {
  42. if (hintStyle == AZ::FFont::HintStyle::Light)
  43. {
  44. return FT_LOAD_TARGET_LIGHT;
  45. }
  46. return FT_LOAD_TARGET_NORMAL;
  47. }
  48. FT_Render_Mode GetRenderMode(AZ::FFont::HintStyle hintStyle)
  49. {
  50. // We use the hint style to drive the render mode also. These should
  51. // usually be correlated with each other for best results, even though
  52. // they could technically be different.
  53. if (hintStyle == AZ::FFont::HintStyle::Light)
  54. {
  55. return FT_RENDER_MODE_LIGHT;
  56. }
  57. return FT_RENDER_MODE_NORMAL;
  58. }
  59. }
  60. //-------------------------------------------------------------------------------------------------
  61. AZ::FontRenderer::FontRenderer()
  62. : m_library(0)
  63. , m_face(0)
  64. , m_glyph(0)
  65. , m_sizeRatio(IFFontConstants::defaultSizeRatio)
  66. , m_encoding(AZ_FONT_ENCODING_UNICODE)
  67. , m_glyphBitmapWidth(0)
  68. , m_glyphBitmapHeight(0)
  69. {
  70. }
  71. //-------------------------------------------------------------------------------------------------
  72. AZ::FontRenderer::~FontRenderer()
  73. {
  74. FT_Done_Face(m_face);
  75. ;
  76. FT_Done_FreeType(m_library);
  77. m_face = NULL;
  78. m_library = NULL;
  79. }
  80. //-------------------------------------------------------------------------------------------------
  81. int AZ::FontRenderer::LoadFromFile(const AZStd::string& fileName)
  82. {
  83. int iError = FT_Init_FreeType(&m_library);
  84. if (iError)
  85. {
  86. return 0;
  87. }
  88. if (m_face)
  89. {
  90. FT_Done_Face(m_face);
  91. m_face = 0;
  92. }
  93. iError = FT_New_Face(m_library, fileName.c_str(), 0, &m_face);
  94. if (iError)
  95. {
  96. return 0;
  97. }
  98. SetEncoding(AZ_FONT_ENCODING_UNICODE);
  99. return 1;
  100. }
  101. //-------------------------------------------------------------------------------------------------
  102. int AZ::FontRenderer::LoadFromMemory(unsigned char* buffer, int bufferSize)
  103. {
  104. int iError = FT_Init_FreeType(&m_library);
  105. if (iError)
  106. {
  107. return 0;
  108. }
  109. if (m_face)
  110. {
  111. FT_Done_Face(m_face);
  112. m_face = 0;
  113. }
  114. iError = FT_New_Memory_Face(m_library, buffer, bufferSize, 0, &m_face);
  115. if (iError)
  116. {
  117. return 0;
  118. }
  119. SetEncoding(AZ_FONT_ENCODING_UNICODE);
  120. return 1;
  121. }
  122. //-------------------------------------------------------------------------------------------------
  123. int AZ::FontRenderer::Release()
  124. {
  125. FT_Done_Face(m_face);
  126. ;
  127. FT_Done_FreeType(m_library);
  128. m_face = NULL;
  129. m_library = NULL;
  130. return 1;
  131. }
  132. //-------------------------------------------------------------------------------------------------
  133. int AZ::FontRenderer::SetGlyphBitmapSize(int width, int height, float sizeRatio)
  134. {
  135. m_glyphBitmapWidth = width;
  136. m_glyphBitmapHeight = height;
  137. // Assign the given scale for texture slots as long as its positive
  138. m_sizeRatio = sizeRatio > 0.0f ? sizeRatio : m_sizeRatio;
  139. FT_Set_Pixel_Sizes(m_face, (int)(m_glyphBitmapWidth * m_sizeRatio), (int)(m_glyphBitmapHeight * m_sizeRatio));
  140. return 1;
  141. }
  142. //-------------------------------------------------------------------------------------------------
  143. int AZ::FontRenderer::GetGlyphBitmapSize(int* width, int* height)
  144. {
  145. if (width)
  146. {
  147. *width = m_glyphBitmapWidth;
  148. }
  149. if (height)
  150. {
  151. *height = m_glyphBitmapHeight;
  152. }
  153. return 1;
  154. }
  155. //-------------------------------------------------------------------------------------------------
  156. int AZ::FontRenderer::SetEncoding(FT_Encoding encoding)
  157. {
  158. if (FT_Select_Charmap(m_face, encoding))
  159. {
  160. return 0;
  161. }
  162. return 1;
  163. }
  164. //-------------------------------------------------------------------------------------------------
  165. //-------------------------------------------------------------------------------------------------
  166. int AZ::FontRenderer::GetGlyph(GlyphBitmap* glyphBitmap, int* horizontalAdvance, uint8_t* glyphWidth, uint8_t* glyphHeight, int32_t& m_characterOffsetX, int32_t& m_characterOffsetY, int iX, int iY, int characterCode, const FFont::FontHintParams& fontHintParams)
  167. {
  168. FT_Int32 loadFlags = GetLoadFlags(fontHintParams.hintBehavior);
  169. loadFlags |= GetLoadTarget(fontHintParams.hintStyle);
  170. int iError = FT_Load_Char(m_face, characterCode, loadFlags);
  171. if (iError)
  172. {
  173. return 0;
  174. }
  175. FT_Render_Mode renderMode = GetRenderMode(fontHintParams.hintStyle);
  176. m_glyph = m_face->glyph;
  177. iError = FT_Render_Glyph(m_glyph, renderMode);
  178. if (iError)
  179. {
  180. return 0;
  181. }
  182. if (horizontalAdvance)
  183. {
  184. *horizontalAdvance = static_cast<int>(m_glyph->metrics.horiAdvance) / FractionalPixelUnits;
  185. }
  186. if (glyphWidth)
  187. {
  188. *glyphWidth = static_cast<uint8_t>(m_glyph->bitmap.width);
  189. }
  190. if (glyphHeight)
  191. {
  192. *glyphHeight = static_cast<uint8_t>(m_glyph->bitmap.rows);
  193. }
  194. unsigned char* buffer = glyphBitmap->GetBuffer();
  195. AZ_Assert(buffer, "GlyphBitmap: bad buffer");
  196. uint32_t dwGlyphWidth = glyphBitmap->GetWidth();
  197. m_characterOffsetX = m_glyph->bitmap_left;
  198. m_characterOffsetY = (static_cast<int32_t>(round(m_glyphBitmapHeight * m_sizeRatio)) - m_glyph->bitmap_top);
  199. const int textureSlotBufferWidth = glyphBitmap->GetWidth();
  200. const int textureSlotBufferHeight = glyphBitmap->GetHeight();
  201. // might happen if font characters are too big or cache dimenstions in font.xml is too small "<font path="VeraMono.ttf" w="320" h="368"/>"
  202. const bool charWidthFits = static_cast<int>(iX + m_glyph->bitmap.width) <= textureSlotBufferWidth;
  203. const bool charHeightFits = static_cast<int>(iY + m_glyph->bitmap.rows) <= textureSlotBufferHeight;
  204. [[maybe_unused]] const bool charFitsInSlot = charWidthFits && charHeightFits;
  205. AZ_Error("Font", charFitsInSlot, "Character code %d doesn't fit in font texture; check 'sizeRatio' attribute in font XML or adjust this character's sizing in the font.", characterCode);
  206. // Since we might be re-rendering/overwriting a glyph that already exists
  207. // in the font texture, clear the contents of this particular slot so no
  208. // artifacts of the previous glyph remain.
  209. glyphBitmap->Clear();
  210. // Restrict iteration to smallest of either the texture slot or glyph
  211. // bitmap buffer ranges
  212. const int bufferMaxIterWidth = AZStd::GetMin<int>(textureSlotBufferWidth, m_glyph->bitmap.width);
  213. const int bufferMaxIterHeight = AZStd::GetMin<int>(textureSlotBufferHeight, m_glyph->bitmap.rows);
  214. for (int i = 0; i < bufferMaxIterHeight; i++)
  215. {
  216. int iNewY = i + iY;
  217. for (int j = 0; j < bufferMaxIterWidth; j++)
  218. {
  219. unsigned char cColor = m_glyph->bitmap.buffer[(i * m_glyph->bitmap.width) + j];
  220. int iOffset = iNewY * dwGlyphWidth + iX + j;
  221. if (iOffset >= (int)dwGlyphWidth * m_glyphBitmapHeight)
  222. {
  223. continue;
  224. }
  225. buffer[iOffset] = cColor;
  226. // buffer[iOffset] = cColor/2+32; // debug - visualize character in a block
  227. }
  228. }
  229. return 1;
  230. }
  231. int AZ::FontRenderer::GetGlyphScaled([[maybe_unused]] GlyphBitmap* glyphBitmap, [[maybe_unused]] int* glyphWidth, [[maybe_unused]] int* glyphHeight, [[maybe_unused]] int iX, [[maybe_unused]] int iY, [[maybe_unused]] float scaleX, [[maybe_unused]] float scaleY, [[maybe_unused]] int characterCode)
  232. {
  233. return 1;
  234. }
  235. Vec2 AZ::FontRenderer::GetKerning(uint32_t leftGlyph, uint32_t rightGlyph)
  236. {
  237. FT_Vector kerningOffsets;
  238. kerningOffsets.x = kerningOffsets.y = 0;
  239. if (FT_HAS_KERNING(m_face))
  240. {
  241. const FT_UInt leftGlyphIndex = FT_Get_Char_Index(m_face, leftGlyph);
  242. const FT_UInt rightGlyphIndex = FT_Get_Char_Index(m_face, rightGlyph);
  243. [[maybe_unused]] FT_Error ftError = FT_Get_Kerning(m_face, leftGlyphIndex, rightGlyphIndex, FT_KERNING_DEFAULT, &kerningOffsets);
  244. #if !defined(_RELEASE)
  245. if (0 != ftError)
  246. {
  247. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "FT_Get_Kerning returned %d", ftError);
  248. }
  249. #endif
  250. }
  251. return Vec2(azlossy_cast<float>(kerningOffsets.x / FractionalPixelUnits), azlossy_cast<float>(kerningOffsets.y / FractionalPixelUnits));
  252. }
  253. float AZ::FontRenderer::GetAscenderToHeightRatio()
  254. {
  255. return (static_cast<float>(m_face->ascender) / static_cast<float>(m_face->height));
  256. }
  257. #endif // #if !defined(USE_NULLFONT_ALWAYS)