OpenTTD Source  20241121-master-g67a0fccfad
gfx_layout_fallback.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 
12 #include "gfx_layout_fallback.h"
13 #include "string_func.h"
14 #include "zoom_func.h"
15 
16 #include "table/control_codes.h"
17 
18 #include "safeguards.h"
19 
20 /*** Paragraph layout ***/
39 public:
42  std::vector<GlyphID> glyphs;
43  std::vector<Position> positions;
44  std::vector<int> glyph_to_char;
45 
47 
48  public:
49  FallbackVisualRun(Font *font, const char32_t *chars, int glyph_count, int char_offset, int x);
50  const Font *GetFont() const override { return this->font; }
51  int GetGlyphCount() const override { return static_cast<int>(this->glyphs.size()); }
52  std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
53  std::span<const Position> GetPositions() const override { return this->positions; }
54  int GetLeading() const override { return this->GetFont()->fc->GetHeight(); }
55  std::span<const int> GetGlyphToCharMap() const override { return this->glyph_to_char; }
56  };
57 
59  class FallbackLine : public std::vector<FallbackVisualRun>, public ParagraphLayouter::Line {
60  public:
61  int GetLeading() const override;
62  int GetWidth() const override;
63  int CountRuns() const override;
64  const ParagraphLayouter::VisualRun &GetVisualRun(int run) const override;
65 
66  int GetInternalCharLength(char32_t) const override { return 1; }
67  };
68 
69  const char32_t *buffer_begin;
70  const char32_t *buffer;
72 
73  FallbackParagraphLayout(char32_t *buffer, int length, FontMap &runs);
74  void Reflow() override;
75  std::unique_ptr<const Line> NextLine(int max_width) override;
76 };
77 
85 /* static */ ParagraphLayouter *FallbackParagraphLayoutFactory::GetParagraphLayout(char32_t *buff, char32_t *buff_end, FontMap &fontMapping)
86 {
87  return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping);
88 }
89 
97 /* static */ size_t FallbackParagraphLayoutFactory::AppendToBuffer(char32_t *buff, [[maybe_unused]] const char32_t *buffer_last, char32_t c)
98 {
99  assert(buff < buffer_last);
100  *buff = c;
101  return 1;
102 }
103 
112 FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const char32_t *chars, int char_count, int char_offset, int x) :
113  font(font)
114 {
115  const bool isbuiltin = font->fc->IsBuiltInFont();
116 
117  this->glyphs.reserve(char_count);
118  this->glyph_to_char.reserve(char_count);
119  this->positions.reserve(char_count);
120 
121  int advance = x;
122  for (int i = 0; i < char_count; i++) {
123  const GlyphID &glyph_id = this->glyphs.emplace_back(font->fc->MapCharToGlyph(chars[i]));
124  int x_advance = font->fc->GetGlyphWidth(glyph_id);
125  if (isbuiltin) {
126  this->positions.emplace_back(advance, advance + x_advance - 1, font->fc->GetAscender()); // Apply sprite font's ascender.
127  } else if (chars[i] >= SCC_SPRITE_START && chars[i] <= SCC_SPRITE_END) {
128  this->positions.emplace_back(advance, advance + x_advance - 1, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre
129  } else {
130  this->positions.emplace_back(advance, advance + x_advance - 1, 0); // No ascender adjustment.
131  }
132  advance += x_advance;
133  this->glyph_to_char.push_back(char_offset + i);
134  }
135 }
136 
142 {
143  int leading = 0;
144  for (const auto &run : *this) {
145  leading = std::max(leading, run.GetLeading());
146  }
147 
148  return leading;
149 }
150 
156 {
157  if (this->empty()) return 0;
158 
159  /*
160  * The last X position of a run contains is the end of that run.
161  * Since there is no left-to-right support, taking this value of
162  * the last run gives us the end of the line and thus the width.
163  */
164  const auto &run = this->GetVisualRun(this->CountRuns() - 1);
165  const auto &positions = run.GetPositions();
166  if (positions.empty()) return 0;
167  return positions.back().right + 1;
168 }
169 
175 {
176  return (uint)this->size();
177 }
178 
184 {
185  return this->at(run);
186 }
187 
195 {
196  assert(runs.rbegin()->first == length);
197 }
198 
203 {
204  this->buffer = this->buffer_begin;
205 }
206 
212 std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine(int max_width)
213 {
214  /* Simple idea:
215  * - split a line at a newline character, or at a space where we can break a line.
216  * - split for a visual run whenever a new line happens, or the font changes.
217  */
218  if (this->buffer == nullptr) return nullptr;
219 
220  std::unique_ptr<FallbackLine> l = std::make_unique<FallbackLine>();
221 
222  if (*this->buffer == '\0') {
223  /* Only a newline. */
224  this->buffer = nullptr;
225  l->emplace_back(this->runs.begin()->second, this->buffer, 0, 0, 0);
226  return l;
227  }
228 
229  int offset = this->buffer - this->buffer_begin;
230  FontMap::iterator iter = this->runs.begin();
231  while (iter->first <= offset) {
232  ++iter;
233  assert(iter != this->runs.end());
234  }
235 
236  const FontCache *fc = iter->second->fc;
237  const char32_t *next_run = this->buffer_begin + iter->first;
238 
239  const char32_t *begin = this->buffer;
240  const char32_t *last_space = nullptr;
241  const char32_t *last_char;
242  int width = 0;
243  for (;;) {
244  char32_t c = *this->buffer;
245  last_char = this->buffer;
246 
247  if (c == '\0') {
248  this->buffer = nullptr;
249  break;
250  }
251 
252  if (this->buffer == next_run) {
253  int w = l->GetWidth();
254  l->emplace_back(iter->second, begin, this->buffer - begin, begin - this->buffer_begin, w);
255  ++iter;
256  assert(iter != this->runs.end());
257 
258  next_run = this->buffer_begin + iter->first;
259  begin = this->buffer;
260  /* Since a next run is started, there is already some text that
261  * will be shown for this line. However, we do not want to break
262  * this line at the previous space, so pretend we passed a space
263  * just before this next run. */
264  last_space = begin - 1;
265  }
266 
267  if (IsWhitespace(c)) last_space = this->buffer;
268 
269  if (IsPrintable(c) && !IsTextDirectionChar(c)) {
270  int char_width = GetCharacterWidth(fc->GetSize(), c);
271  width += char_width;
272  if (width > max_width) {
273  /* The string is longer than maximum width so we need to decide
274  * what to do with it. */
275  if (width == char_width) {
276  /* The character is wider than allowed width; don't know
277  * what to do with this case... bail out! */
278  this->buffer = nullptr;
279  return l;
280  }
281 
282  if (last_space == nullptr) {
283  /* No space has been found. Just terminate at our current
284  * location. This usually happens for languages that do not
285  * require spaces in strings, like Chinese, Japanese and
286  * Korean. For other languages terminating mid-word might
287  * not be the best, but terminating the whole string instead
288  * of continuing the word at the next line is worse. */
289  last_char = this->buffer;
290  } else {
291  /* A space is found; perfect place to terminate */
292  this->buffer = last_space + 1;
293  last_char = last_space;
294  }
295  break;
296  }
297  }
298 
299  this->buffer++;
300  }
301 
302  if (l->empty() || last_char - begin > 0) {
303  int w = l->GetWidth();
304  l->emplace_back(iter->second, begin, last_char - begin, begin - this->buffer_begin, w);
305  }
306  return l;
307 }
static ParagraphLayouter * GetParagraphLayout(char32_t *buff, char32_t *buff_end, FontMap &fontMapping)
Get the actual ParagraphLayout for the given buffer.
static size_t AppendToBuffer(char32_t *buff, const char32_t *buffer_last, char32_t c)
Append a wide character to the internal buffer.
A single line worth of VisualRuns.
int GetWidth() const override
Get the width of this line.
int GetLeading() const override
Get the height of the line.
const ParagraphLayouter::VisualRun & GetVisualRun(int run) const override
Get a specific visual run.
int CountRuns() const override
Get the number of runs in this line.
Visual run contains data about the bit of text with the same font.
std::vector< int > glyph_to_char
The char index of the glyphs.
Font * font
The font used to layout these.
FallbackVisualRun(Font *font, const char32_t *chars, int glyph_count, int char_offset, int x)
Create the visual run.
std::vector< GlyphID > glyphs
The glyphs we're drawing.
std::vector< Position > positions
The positions of the glyphs.
Class handling the splitting of a paragraph of text into lines and visual runs.
const char32_t * buffer_begin
Begin of the buffer.
std::unique_ptr< const Line > NextLine(int max_width) override
Construct a new line with a maximum width.
const char32_t * buffer
The current location in the buffer.
FontMap & runs
The fonts we have to use for this paragraph.
FallbackParagraphLayout(char32_t *buffer, int length, FontMap &runs)
Create a new paragraph layouter.
void Reflow() override
Reset the position to the start of the paragraph.
Font cache for basic fonts.
Definition: fontcache.h:21
int GetHeight() const
Get the height of the font.
Definition: fontcache.h:48
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
virtual bool IsBuiltInFont()=0
Is this a built-in sprite font?
virtual uint GetGlyphWidth(GlyphID key)=0
Get the width of the glyph with the given key.
int GetAscender() const
Get the ascender value of the font.
Definition: fontcache.h:54
FontSize GetSize() const
Get the FontSize of the font.
Definition: fontcache.h:42
Container with information about a font.
Definition: gfx_layout.h:75
FontCache * fc
The font we are using.
Definition: gfx_layout.h:77
A single line worth of VisualRuns.
Definition: gfx_layout.h:119
Visual run contains data about the bit of text with the same font.
Definition: gfx_layout.h:107
Interface to glue fallback and normal layouter into one.
Definition: gfx_layout.h:89
Control codes that are embedded in the translation strings.
uint32_t GlyphID
Glyphs are characters from a font.
Definition: fontcache.h:17
uint8_t GetCharacterWidth(FontSize size, char32_t key)
Return width of character glyph.
Definition: gfx.cpp:1227
std::map< int, Font * > FontMap
Mapping from index to font.
Definition: gfx_layout.h:84
Functions related to laying out the texts as fallback.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
bool IsWhitespace(char32_t c)
Check whether UNICODE character is whitespace or not, i.e.
Definition: string_func.h:249
bool IsTextDirectionChar(char32_t c)
Is the given character a text direction character.
Definition: string_func.h:217
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition: zoom_func.h:107