OpenTTD Source 20260218-master-g2123fca5ea
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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 ***/
39public:
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 size_t GetGlyphCount() const override { return 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 size_t CountRuns() const override;
64 const ParagraphLayouter::VisualRun &GetVisualRun(size_t 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 */ std::unique_ptr<ParagraphLayouter> FallbackParagraphLayoutFactory::GetParagraphLayout(char32_t *buff, char32_t *buff_end, FontMap &font_mapping)
86{
87 return std::make_unique<FallbackParagraphLayout>(buff, buff_end - buff, font_mapping);
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
112FallbackParagraphLayout::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
138{
139 int leading = 0;
140 for (const auto &run : *this) {
141 leading = std::max(leading, run.GetLeading());
142 }
143
144 return leading;
145}
146
148{
149 if (this->empty()) return 0;
150
151 /*
152 * The last X position of a run contains is the end of that run.
153 * Since there is no left-to-right support, taking this value of
154 * the last run gives us the end of the line and thus the width.
155 */
156 const auto &run = this->GetVisualRun(this->CountRuns() - 1);
157 const auto &positions = run.GetPositions();
158 if (positions.empty()) return 0;
159 return positions.back().right + 1;
160}
161
163{
164 return this->size();
165}
166
168{
169 return this->at(run);
170}
171
179{
180 assert(runs.rbegin()->first == length);
181}
182
184{
185 this->buffer = this->buffer_begin;
186}
187
188std::unique_ptr<const ParagraphLayouter::Line> FallbackParagraphLayout::NextLine(int max_width)
189{
190 /* Simple idea:
191 * - split a line at a newline character, or at a space where we can break a line.
192 * - split for a visual run whenever a new line happens, or the font changes.
193 */
194 if (this->buffer == nullptr) return nullptr;
195
196 std::unique_ptr<FallbackLine> l = std::make_unique<FallbackLine>();
197
198 if (*this->buffer == '\0') {
199 /* Only a newline. */
200 this->buffer = nullptr;
201 l->emplace_back(this->runs.begin()->second, this->buffer, 0, 0, 0);
202 return l;
203 }
204
205 int offset = this->buffer - this->buffer_begin;
206 FontMap::iterator iter = this->runs.begin();
207 while (iter->first <= offset) {
208 ++iter;
209 assert(iter != this->runs.end());
210 }
211
212 const FontCache *fc = iter->second->fc;
213 const char32_t *next_run = this->buffer_begin + iter->first;
214
215 const char32_t *begin = this->buffer;
216 const char32_t *last_space = nullptr;
217 const char32_t *last_char;
218 int width = 0;
219 for (;;) {
220 char32_t c = *this->buffer;
221 last_char = this->buffer;
222
223 if (c == '\0') {
224 this->buffer = nullptr;
225 break;
226 }
227
228 if (this->buffer == next_run) {
229 int w = l->GetWidth();
230 l->emplace_back(iter->second, begin, this->buffer - begin, begin - this->buffer_begin, w);
231 ++iter;
232 assert(iter != this->runs.end());
233
234 next_run = this->buffer_begin + iter->first;
235 begin = this->buffer;
236 /* Since a next run is started, there is already some text that
237 * will be shown for this line. However, we do not want to break
238 * this line at the previous space, so pretend we passed a space
239 * just before this next run. */
240 last_space = begin - 1;
241 }
242
243 if (IsWhitespace(c)) last_space = this->buffer;
244
245 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
246 int char_width = GetCharacterWidth(fc->GetSize(), c);
247 width += char_width;
248 if (width > max_width) {
249 /* The string is longer than maximum width so we need to decide
250 * what to do with it. */
251 if (width == char_width) {
252 /* The character is wider than allowed width; don't know
253 * what to do with this case... bail out! */
254 this->buffer = nullptr;
255 return l;
256 }
257
258 if (last_space == nullptr) {
259 /* No space has been found. Just terminate at our current
260 * location. This usually happens for languages that do not
261 * require spaces in strings, like Chinese, Japanese and
262 * Korean. For other languages terminating mid-word might
263 * not be the best, but terminating the whole string instead
264 * of continuing the word at the next line is worse. */
265 last_char = this->buffer;
266 } else {
267 /* A space is found; perfect place to terminate */
268 this->buffer = last_space + 1;
269 last_char = last_space;
270 }
271 break;
272 }
273 }
274
275 this->buffer++;
276 }
277
278 if (l->empty() || last_char - begin > 0) {
279 int w = l->GetWidth();
280 l->emplace_back(iter->second, begin, last_char - begin, begin - this->buffer_begin, w);
281 }
282 return l;
283}
static std::unique_ptr< ParagraphLayouter > GetParagraphLayout(char32_t *buff, char32_t *buff_end, FontMap &font_mapping)
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 GetInternalCharLength(char32_t) const override
Get the number of elements the given character occupies in the underlying text buffer of the Layouter...
const ParagraphLayouter::VisualRun & GetVisualRun(size_t run) const override
Get a reference to the given run.
int GetLeading() const override
Get the font leading, or distance between the baselines of consecutive lines.
size_t CountRuns() const override
Get the number of runs in this line.
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.
int GetLeading() const override
Get the font leading, or distance between the baselines of consecutive lines.
std::vector< GlyphID > glyphs
The glyphs we're drawing.
std::span< const GlyphID > GetGlyphs() const override
Get the glyphs to draw.
size_t GetGlyphCount() const override
Get the number of glyphs.
const Font * GetFont() const override
Get the font.
std::span< const Position > GetPositions() const override
Get the positions for each of the glyphs.
std::vector< Position > positions
The positions of the glyphs.
std::span< const int > GetGlyphToCharMap() const override
The offset for each of the glyphs to the character run that was passed to the Layouter.
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:22
int GetHeight() const
Get the height of the font.
Definition fontcache.h:59
FontSize GetSize() const
Get the FontSize of the font.
Definition fontcache.h:53
Container with information about a font.
Definition gfx_layout.h:97
FontCache * fc
The font we are using.
Definition gfx_layout.h:99
A single line worth of VisualRuns.
Definition gfx_layout.h:174
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:132
Interface to glue fallback and normal layouter into one.
Definition gfx_layout.h:111
Control codes that are embedded in the translation strings.
uint8_t GetCharacterWidth(FontSize size, char32_t key)
Return width of character glyph.
Definition gfx.cpp:1278
std::vector< std::pair< int, Font * > > FontMap
Mapping from index to font.
Definition gfx_layout.h:106
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.
bool IsTextDirectionChar(char32_t c)
Is the given character a text direction character.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107