1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 /**
21  * \file graphics/engine/text.h
22  * \brief Text rendering - CText class
23  */
24 
25 #pragma once
26 
27 
28 #include "graphics/core/color.h"
29 
30 #include "math/intpoint.h"
31 #include "math/point.h"
32 
33 #include <map>
34 #include <memory>
35 #include <vector>
36 
37 
38 // Graphics module namespace
39 namespace Gfx
40 {
41 
42 class CEngine;
43 class CDevice;
44 
45 //! Standard small font size
46 const float FONT_SIZE_SMALL = 12.0f;
47 //! Standard big font size
48 const float FONT_SIZE_BIG = 18.0f;
49 
50 /**
51  * \enum TextAlign
52  * \brief Type of text alignment
53  */
54 enum TextAlign
55 {
56     TEXT_ALIGN_RIGHT,
57     TEXT_ALIGN_LEFT,
58     TEXT_ALIGN_CENTER
59 };
60 
61 /* Font meta char constants */
62 
63 //! Type used for font character metainfo
64 typedef short FontMetaChar;
65 
66 /**
67  * \enum FontType
68  * \brief Type of font
69  *
70  * Bitmask in lower 4 bits (mask 0x00f)
71  */
72 enum FontType
73 {
74     //! Flag for bold font subtype
75     FONT_BOLD       = 0x04,
76     //! Flag for italic font subtype
77     FONT_ITALIC     = 0x08,
78 
79     //! Default colobot font used for interface
80     FONT_COMMON    = 0x00,
81     //! Alias for bold colobot font
82     FONT_COMMON_BOLD = FONT_COMMON | FONT_BOLD,
83     //! Alias for italic colobot font
84     FONT_COMMON_ITALIC = FONT_COMMON | FONT_ITALIC,
85 
86     //! Studio font used mainly in code editor
87     FONT_STUDIO    = 0x01,
88     //! Alias for bold studio font
89     FONT_STUDIO_BOLD = FONT_STUDIO | FONT_BOLD,
90     //! Alias for italic studio font (at this point not used anywhere)
91     FONT_STUDIO_ITALIC = FONT_STUDIO | FONT_ITALIC,
92 
93     //! SatCom font used for interface (currently bold and italic wariants aren't used anywhere)
94     FONT_SATCOM = 0x02,
95     //! Alias for bold satcom font
96     FONT_SATCOM_BOLD = FONT_SATCOM | FONT_BOLD,
97     //! Alias for italic satcom font
98     FONT_SATCOM_ITALIC = FONT_SATCOM | FONT_ITALIC,
99 
100     //! Pseudo-font loaded from textures for buttons, icons, etc.
101     FONT_BUTTON     = 0x03,
102 };
103 
104 /**
105  * \enum FontTitle
106  * \brief Size of font title
107  *
108  * Used internally by CEdit
109  *
110  * Bitmask in 2 bits left shifted 4 (mask 0x030)
111  */
112 enum FontTitle
113 {
114     FONT_TITLE_BIG       = 0x01 << 4,
115     FONT_TITLE_NORM      = 0x02 << 4,
116     FONT_TITLE_LITTLE    = 0x03 << 4,
117 };
118 
119 /**
120  * \enum FontHighlight
121  * \brief Type of color highlight for text
122  *
123  * Bitmask in 4 bits left shifted 6 (mask 0x3c0)
124  */
125 enum FontHighlight
126 {
127     FONT_HIGHLIGHT_NONE      = 0x00 << 6,
128     FONT_HIGHLIGHT_TABLE     = 0x02 << 6, //!< code background in SatCom
129     FONT_HIGHLIGHT_KEY       = 0x03 << 6, //!< background for keys in documentation in SatCom
130     FONT_HIGHLIGHT_TOKEN     = 0x04 << 6, //!< keywords in CBot scripts
131     FONT_HIGHLIGHT_TYPE      = 0x05 << 6, //!< types in CBot scripts
132     FONT_HIGHLIGHT_CONST     = 0x06 << 6, //!< constants in CBot scripts
133     FONT_HIGHLIGHT_THIS      = 0x07 << 6, //!< "this" keyword in CBot scripts
134     FONT_HIGHLIGHT_COMMENT   = 0x08 << 6, //!< comments in CBot scripts
135     FONT_HIGHLIGHT_KEYWORD   = 0x09 << 6, //!< builtin keywords in CBot scripts
136     FONT_HIGHLIGHT_STRING    = 0x0A << 6, //!< string literals in CBot scripts
137 };
138 
139 /**
140  * \enum FontMask
141  * \brief Masks in FontMetaChar for different attributes
142  */
143 enum FontMask
144 {
145     //! Mask for FontType
146     FONT_MASK_FONT  = 0x00f,
147     //! Mask for FontTitle
148     FONT_MASK_TITLE = 0x030,
149     //! Mask for FontHighlight
150     FONT_MASK_HIGHLIGHT = 0x3c0,
151     //! Mask for links
152     FONT_MASK_LINK = 0x400,
153     //! Mask for image bit (TODO: not used?)
154     FONT_MASK_IMAGE = 0x800
155 };
156 
157 
158 /**
159  * \struct UTF8Char
160  * \brief UTF-8 character in font cache
161  *
162  * Only 3-byte chars are supported
163  */
164 struct UTF8Char
165 {
166     char c1, c2, c3;
167     // Padding for 4-byte alignment
168     // It also seems to fix some problems reported by valgrind
169     char pad;
170 
171     explicit UTF8Char(char ch1 = '\0', char ch2 = '\0', char ch3 = '\0')
c1UTF8Char172         : c1(ch1), c2(ch2), c3(ch3), pad('\0') {}
173 
174     inline bool operator<(const UTF8Char &other) const
175     {
176         if (c1 < other.c1)
177             return true;
178         else if (c1 > other.c1)
179             return false;
180 
181         if (c2 < other.c2)
182             return true;
183         else if (c2 > other.c2)
184             return false;
185 
186         return c3 < other.c3;
187     }
188 
189     inline bool operator==(const UTF8Char &other) const
190     {
191         return c1 == other.c1 && c2 == other.c2 && c3 == other.c3;
192     }
193 };
194 
195 /**
196  * \struct CharTexture
197  * \brief Texture of font character
198  */
199 struct CharTexture
200 {
201     unsigned int id = 0;
202     Math::IntPoint charPos;
203     Math::IntPoint charSize;
204 };
205 
206 // Definition is private - in text.cpp
207 struct CachedFont;
208 struct MultisizeFont;
209 struct FontTexture;
210 
211 /**
212  * \enum SpecialChar
213  * \brief Special codes for certain characters
214  */
215 enum SpecialChar
216 {
217     CHAR_TAB        = '\t', //! Tab character - :
218     CHAR_NEWLINE    = '\n', //! Newline character - arrow pointing down and left
219     CHAR_DOT        = 1,    //! Single dot in the middle
220     CHAR_SQUARE     = 2,    //! Square
221     CHAR_SKIP_RIGHT = 5,    //! Filled triangle pointing right
222     CHAR_SKIP_LEFT  = 6     //! Filled triangle pointing left
223 };
224 
225 /**
226  * \class CText
227  * \brief Text rendering engine
228  *
229  * CText is responsible for drawing text in 2D interface. Font rendering is done using
230  * textures generated by SDL_ttf from TTF font files.
231  *
232  * All functions rendering text are divided into two types:
233  * - single font - function takes a single FontType argument that (along with size)
234  *   determines the font to be used for all characters,
235  * - multi-font - function takes the text as one argument and a std::vector of FontMetaChar
236  *   with per-character formatting information (font, highlights and some other info used by CEdit)
237  *
238  * All font rendering is done in UTF-8.
239  */
240 class CText
241 {
242 public:
243     CText(CEngine* engine);
244     virtual ~CText();
245 
246     //! Sets the device to be used
247     void        SetDevice(CDevice *device);
248 
249     //! Returns the last encountered error
250     std::string GetError();
251 
252     //! Initializes the font engine; must be called after SetDevice()
253     bool        Create();
254     //! Frees resources before exit
255     void        Destroy();
256 
257     //! Flushes cached textures
258     void        FlushCache();
259     //! Try to load new font files
260     bool        ReloadFonts();
261 
262     //@{
263     //! Tab size management
264     void        SetTabSize(int tabSize);
265     int         GetTabSize();
266     //@}
267 
268     //! Draws text (multi-format)
269     void        DrawText(const std::string &text, std::vector<FontMetaChar>::iterator format,
270                          std::vector<FontMetaChar>::iterator end,
271                          float size, Math::Point pos, float width, TextAlign align,
272                          int eol, Color color = Color(0.0f, 0.0f, 0.0f, 1.0f));
273     //! Draws text (one font)
274     void        DrawText(const std::string &text, FontType font,
275                          float size, Math::Point pos, float width, TextAlign align,
276                          int eol, Color color = Color(0.0f, 0.0f, 0.0f, 1.0f));
277 
278     //! Calculates dimensions for text (multi-format)
279     void        SizeText(const std::string &text, std::vector<FontMetaChar>::iterator format,
280                          std::vector<FontMetaChar>::iterator endFormat,
281                          float size, Math::Point pos, TextAlign align,
282                          Math::Point &start, Math::Point &end);
283     //! Calculates dimensions for text (one font)
284     void        SizeText(const std::string &text, FontType font,
285                          float size, Math::Point pos, TextAlign align,
286                          Math::Point &start, Math::Point &end);
287 
288     //! Returns the ascent font metric
289     float       GetAscent(FontType font, float size);
290     //! Returns the descent font metric
291     float       GetDescent(FontType font, float size);
292     //! Returns the height font metric
293     float       GetHeight(FontType font, float size);
294     int GetHeightInt(FontType font, float size);
295 
296     //! Returns width of string (multi-format)
297     TEST_VIRTUAL float GetStringWidth(const std::string& text,
298                                       std::vector<FontMetaChar>::iterator format,
299                                       std::vector<FontMetaChar>::iterator end, float size);
300     //! Returns width of string (single font)
301     TEST_VIRTUAL float GetStringWidth(std::string text, FontType font, float size);
302     //! Returns width of single character
303     TEST_VIRTUAL float GetCharWidth(UTF8Char ch, FontType font, float size, float offset);
304     int GetCharWidthInt(UTF8Char ch, FontType font, float size, float offset);
305 
306     //! Justifies a line of text (multi-format)
307     int         Justify(const std::string &text, std::vector<FontMetaChar>::iterator format,
308                         std::vector<FontMetaChar>::iterator end,
309                         float size, float width);
310     //! Justifies a line of text (one font)
311     int         Justify(const std::string &text, FontType font, float size, float width);
312 
313     //! Returns the most suitable position to a given offset (multi-format)
314     int         Detect(const std::string &text, std::vector<FontMetaChar>::iterator format,
315                        std::vector<FontMetaChar>::iterator end,
316                        float size, float offset);
317     //! Returns the most suitable position to a given offset (one font)
318     int         Detect(const std::string &text, FontType font, float size, float offset);
319 
320     UTF8Char    TranslateSpecialChar(int specialChar);
321 
322     CharTexture GetCharTexture(UTF8Char ch, FontType font, float size);
323     Math::IntPoint GetFontTextureSize();
324 
325 protected:
326     CachedFont* GetOrOpenFont(FontType font, float size);
327     CharTexture CreateCharTexture(UTF8Char ch, CachedFont* font);
328     FontTexture* GetOrCreateFontTexture(Math::IntPoint tileSize);
329     FontTexture CreateFontTexture(Math::IntPoint tileSize);
330     Math::IntPoint GetNextTilePos(const FontTexture& fontTexture);
331 
332     void        DrawString(const std::string &text, std::vector<FontMetaChar>::iterator format,
333                            std::vector<FontMetaChar>::iterator end,
334                            float size, Math::IntPoint pos, int width, int eol, Color color);
335     void        DrawString(const std::string &text, FontType font,
336                            float size, Math::IntPoint pos, int width, int eol, Color color);
337     void        DrawHighlight(FontMetaChar hl, Math::IntPoint pos, Math::IntPoint size);
338     void        DrawCharAndAdjustPos(UTF8Char ch, FontType font, float size, Math::IntPoint &pos, Color color);
339     void        StringToUTFCharList(const std::string &text, std::vector<UTF8Char> &chars);
340     void        StringToUTFCharList(const std::string &text, std::vector<UTF8Char> &chars, std::vector<FontMetaChar>::iterator format, std::vector<FontMetaChar>::iterator end);
341 
342     int GetCharSizeAt(Gfx::FontType font, const std::string& text, unsigned int index) const;
343 
344 protected:
345     CEngine*       m_engine;
346     CDevice*       m_device;
347 
348     std::string  m_error;
349     float        m_defaultSize;
350     int          m_tabSize;
351 
352     std::map<FontType, std::unique_ptr<MultisizeFont>> m_fonts;
353     std::vector<FontTexture> m_fontTextures;
354 
355     FontType     m_lastFontType;
356     int          m_lastFontSize;
357     CachedFont*  m_lastCachedFont;
358 
359     class CQuadBatch;
360     std::unique_ptr<CQuadBatch> m_quadBatch;
361 };
362 
363 
364 } // namespace Gfx
365