OpenTTD Source 20260531-master-g0e951f3528
gfx.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#include "gfx_func.h"
12#include "gfx_layout.h"
13#include "progress.h"
14#include "zoom_func.h"
15#include "blitter/factory.hpp"
17#include "strings_func.h"
18#include "settings_type.h"
19#include "network/network.h"
21#include "window_gui.h"
22#include "window_func.h"
23#include "newgrf_debug.h"
24#include "core/backup_type.hpp"
26#include "viewport_func.h"
27
28#include "table/animcursors.h"
30#include "table/sprites.h"
31#include "table/control_codes.h"
32
33#include "safeguards.h"
34
36bool _fullscreen;
38CursorVars _cursor;
41uint16_t _game_speed = 100;
46DrawPixelInfo _screen;
48std::atomic<bool> _exit_game;
49GameMode _game_mode;
51PauseModes _pause_mode;
53
55DrawPixelInfo *_cur_dpi;
56
57static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = nullptr, SpriteID sprite_id = SPR_CURSOR_MOUSE);
58static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = nullptr, SpriteID sprite_id = SPR_CURSOR_MOUSE, ZoomLevel zoom = ZoomLevel::Min);
59
60static ReusableBuffer<uint8_t> _cursor_backup;
61
64int _gui_scale = MIN_INTERFACE_SCALE;
66
75static const uint8_t *_colour_remap_ptr;
76static uint8_t _string_colourremap[3];
77
78static const uint DIRTY_BLOCK_HEIGHT = 8;
79static const uint DIRTY_BLOCK_WIDTH = 64;
80
81static size_t _dirty_blocks_per_row = 0;
82static size_t _dirty_blocks_per_column = 0;
83static std::vector<uint8_t> _dirty_blocks;
84extern uint _dirty_block_colour;
85
86void GfxScroll(int left, int top, int width, int height, int xo, int yo)
87{
89
90 if (xo == 0 && yo == 0) return;
91
92 if (_cursor.visible) UndrawMouseCursor();
93
95
96 blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
97 /* This part of the screen is now dirty. */
98 VideoDriver::GetInstance()->MakeDirty(left, top, width, height);
99}
100
101
116void GfxFillRect(int left, int top, int right, int bottom, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
117{
119 const DrawPixelInfo *dpi = _cur_dpi;
120 void *dst;
121 const int otop = top;
122 const int oleft = left;
123
124 if (dpi->zoom != ZoomLevel::Min) return;
125 if (left > right || top > bottom) return;
126 if (right < dpi->left || left >= dpi->left + dpi->width) return;
127 if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
128
129 if ( (left -= dpi->left) < 0) left = 0;
130 right = right - dpi->left + 1;
131 if (right > dpi->width) right = dpi->width;
132 right -= left;
133 assert(right > 0);
134
135 if ( (top -= dpi->top) < 0) top = 0;
136 bottom = bottom - dpi->top + 1;
137 if (bottom > dpi->height) bottom = dpi->height;
138 bottom -= top;
139 assert(bottom > 0);
140
141 dst = blitter->MoveTo(dpi->dst_ptr, left, top);
142
143 switch (mode) {
144 default: // FillRectMode::Opaque
145 blitter->DrawRect(dst, right, bottom, std::get<PixelColour>(colour));
146 break;
147
149 blitter->DrawColourMappingRect(dst, right, bottom, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
150 break;
151
153 uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
154 PixelColour pc = std::get<PixelColour>(colour);
155 do {
156 for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, pc);
157 dst = blitter->MoveTo(dst, 0, 1);
158 } while (--bottom > 0);
159 break;
160 }
161 }
162}
163
164typedef std::pair<Point, Point> LineSegment;
165
174static std::vector<LineSegment> MakePolygonSegments(std::span<const Point> shape, Point offset)
175{
176 std::vector<LineSegment> segments;
177 if (shape.size() < 3) return segments; // fewer than 3 will always result in an empty polygon
178 segments.reserve(shape.size());
179
180 /* Connect first and last point by having initial previous point be the last */
181 Point prev = shape.back();
182 prev.x -= offset.x;
183 prev.y -= offset.y;
184 for (Point pt : shape) {
185 pt.x -= offset.x;
186 pt.y -= offset.y;
187 /* Create segments for all non-horizontal lines in the polygon.
188 * The segments always have lowest Y coordinate first. */
189 if (prev.y > pt.y) {
190 segments.emplace_back(pt, prev);
191 } else if (prev.y < pt.y) {
192 segments.emplace_back(prev, pt);
193 }
194 prev = pt;
195 }
196
197 return segments;
198}
199
213void GfxFillPolygon(std::span<const Point> shape, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
214{
216 const DrawPixelInfo *dpi = _cur_dpi;
217 if (dpi->zoom != ZoomLevel::Min) return;
218
219 std::vector<LineSegment> segments = MakePolygonSegments(shape, Point{ dpi->left, dpi->top });
220
221 /* Remove segments appearing entirely above or below the clipping area. */
222 segments.erase(std::remove_if(segments.begin(), segments.end(), [dpi](const LineSegment &s) { return s.second.y <= 0 || s.first.y >= dpi->height; }), segments.end());
223
224 /* Check that this wasn't an empty shape (all points on a horizontal line or outside clipping.) */
225 if (segments.empty()) return;
226
227 /* Sort the segments by first point Y coordinate. */
228 std::sort(segments.begin(), segments.end(), [](const LineSegment &a, const LineSegment &b) { return a.first.y < b.first.y; });
229
230 /* Segments intersecting current scanline. */
231 std::vector<LineSegment> active;
232 /* Intersection points with a scanline.
233 * Kept outside loop to avoid repeated re-allocations. */
234 std::vector<int> intersections;
235 /* Normal, reasonable polygons don't have many intersections per scanline. */
236 active.reserve(4);
237 intersections.reserve(4);
238
239 /* Scan through the segments and paint each scanline. */
240 int y = segments.front().first.y;
241 std::vector<LineSegment>::iterator nextseg = segments.begin();
242 while (!active.empty() || nextseg != segments.end()) {
243 /* Clean up segments that have ended. */
244 active.erase(std::remove_if(active.begin(), active.end(), [y](const LineSegment &s) { return s.second.y == y; }), active.end());
245
246 /* Activate all segments starting on this scanline. */
247 while (nextseg != segments.end() && nextseg->first.y == y) {
248 active.push_back(*nextseg);
249 ++nextseg;
250 }
251
252 /* Check clipping. */
253 if (y < 0) {
254 ++y;
255 continue;
256 }
257 if (y >= dpi->height) return;
258
259 /* Intersect scanline with all active segments. */
260 intersections.clear();
261 for (const LineSegment &s : active) {
262 const int sdx = s.second.x - s.first.x;
263 const int sdy = s.second.y - s.first.y;
264 const int ldy = y - s.first.y;
265 const int x = s.first.x + sdx * ldy / sdy;
266 intersections.push_back(x);
267 }
268
269 /* Fill between pairs of intersections. */
270 std::sort(intersections.begin(), intersections.end());
271 for (size_t i = 1; i < intersections.size(); i += 2) {
272 /* Check clipping. */
273 const int x1 = std::max(0, intersections[i - 1]);
274 const int x2 = std::min(intersections[i], dpi->width);
275 if (x2 < 0) continue;
276 if (x1 >= dpi->width) continue;
277
278 /* Fill line y from x1 to x2. */
279 void *dst = blitter->MoveTo(dpi->dst_ptr, x1, y);
280 switch (mode) {
281 default: // FillRectMode::Opaque
282 blitter->DrawRect(dst, x2 - x1, 1, std::get<PixelColour>(colour));
283 break;
285 blitter->DrawColourMappingRect(dst, x2 - x1, 1, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
286 break;
288 /* Fill every other pixel, offset such that the sum of filled pixels' X and Y coordinates is odd.
289 * This creates a checkerboard effect. */
290 PixelColour pc = std::get<PixelColour>(colour);
291 for (int x = (x1 + y) & 1; x < x2 - x1; x += 2) {
292 blitter->SetPixel(dst, x, 0, pc);
293 }
294 break;
295 }
296 }
297 }
298
299 /* Next line */
300 ++y;
301 }
302}
303
318static inline void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash = 0)
319{
321
322 assert(width > 0);
323
324 if (y2 == y || x2 == x) {
325 /* Special case: horizontal/vertical line. All checks already done in GfxPreprocessLine. */
326 blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
327 return;
328 }
329
330 int grade_y = y2 - y;
331 int grade_x = x2 - x;
332
333 /* Clipping rectangle. Slightly extended so we can ignore the width of the line. */
334 int extra = (int)CeilDiv(3 * width, 4); // not less then "width * sqrt(2) / 2"
335 Rect clip = { -extra, -extra, screen_width - 1 + extra, screen_height - 1 + extra };
336
337 /* prevent integer overflows. */
338 int margin = 1;
339 while (INT_MAX / abs(grade_y) < std::max(abs(clip.left - x), abs(clip.right - x))) {
340 grade_y /= 2;
341 grade_x /= 2;
342 margin *= 2; // account for rounding errors
343 }
344
345 /* Prevent division by zero. */
346 if (grade_x == 0) grade_x = 1;
347
348 /* Imagine that the line is infinitely long and it intersects with
349 * infinitely long left and right edges of the clipping rectangle.
350 * If both intersection points are outside the clipping rectangle
351 * and both on the same side of it, we don't need to draw anything. */
352 int left_isec_y = y + (clip.left - x) * grade_y / grade_x;
353 int right_isec_y = y + (clip.right - x) * grade_y / grade_x;
354 if ((left_isec_y > clip.bottom + margin && right_isec_y > clip.bottom + margin) ||
355 (left_isec_y < clip.top - margin && right_isec_y < clip.top - margin)) {
356 return;
357 }
358
359 /* It is possible to use the line equation to further reduce the amount of
360 * work the blitter has to do by shortening the effective line segment.
361 * However, in order to get that right and prevent the flickering effects
362 * of rounding errors so much additional code has to be run here that in
363 * the general case the effect is not noticeable. */
364
365 blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
366}
367
379static inline bool GfxPreprocessLine(DrawPixelInfo *dpi, int &x, int &y, int &x2, int &y2, int width)
380{
381 x -= dpi->left;
382 x2 -= dpi->left;
383 y -= dpi->top;
384 y2 -= dpi->top;
385
386 /* Check simple clipping */
387 if (x + width / 2 < 0 && x2 + width / 2 < 0 ) return false;
388 if (y + width / 2 < 0 && y2 + width / 2 < 0 ) return false;
389 if (x - width / 2 > dpi->width && x2 - width / 2 > dpi->width ) return false;
390 if (y - width / 2 > dpi->height && y2 - width / 2 > dpi->height) return false;
391 return true;
392}
393
394void GfxDrawLine(int x, int y, int x2, int y2, PixelColour colour, int width, int dash)
395{
396 DrawPixelInfo *dpi = _cur_dpi;
397 if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) {
398 GfxDoDrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour, width, dash);
399 }
400}
401
402void GfxDrawLineUnscaled(int x, int y, int x2, int y2, PixelColour colour)
403{
404 DrawPixelInfo *dpi = _cur_dpi;
405 if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) {
406 GfxDoDrawLine(dpi->dst_ptr,
407 UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
408 UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
409 UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour, 1);
410 }
411}
412
426void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
427{
428 /* ....
429 * .. ....
430 * .. ....
431 * .. ^
432 * <--__(dx1,dy1) /(dx2,dy2)
433 * : --__ / :
434 * : --__ / :
435 * : *(x,y) :
436 * : | :
437 * : | ..
438 * .... |(dx3,dy3)
439 * .... | ..
440 * ....V.
441 */
442
443 static constexpr PixelColour colour = PC_WHITE;
444
445 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
446 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
447 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
448
449 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
450 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
451 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
452 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
453 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
454 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
455}
456
464void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
465{
466 GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash);
467 GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash);
468 GfxDrawLine(r.right, r.top, r.right, r.bottom, colour, width, dash);
469 GfxDrawLine(r.left, r.bottom, r.right, r.bottom, colour, width, dash);
470}
471
477{
478 if (colour == TextColour::Invalid) return;
479
480 /* Black strings have no shading ever; the shading is black, so it
481 * would be invisible at best, but it actually makes it illegible. */
482 bool no_shade = colour.flags.Test(ExtendedTextColourFlag::NoShade) || colour.colour == TextColour::Black;
483 bool raw_colour = colour.flags.Test(ExtendedTextColourFlag::IsPaletteColour);
484
485 _string_colourremap[1] = raw_colour ? to_underlying(colour.colour) : _string_colourmap[to_underlying(colour.colour)].p;
486 _string_colourremap[2] = no_shade ? 0 : 1;
487 _colour_remap_ptr = _string_colourremap;
488}
489
506static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation, ExtendedTextColour default_colour)
507{
508 if (line.CountRuns() == 0) return 0;
509
510 int w = line.GetWidth();
511 int h = line.GetLeading();
512
513 /*
514 * The following is needed for truncation.
515 * Depending on the text direction, we either remove bits at the rear
516 * or the front. For this we shift the entire area to draw so it fits
517 * within the left/right bounds and the side we do not truncate it on.
518 * Then we determine the truncation location, i.e. glyphs that fall
519 * outside of the range min_x - max_x will not be drawn; they are thus
520 * the truncated glyphs.
521 *
522 * At a later step we insert the dots.
523 */
524
525 int max_w = right - left + 1; // The maximum width.
526
527 int offset_x = 0; // The offset we need for positioning the glyphs
528 int min_x = left; // The minimum x position to draw normal glyphs on.
529 int max_x = right; // The maximum x position to draw normal glyphs on.
530
531 truncation &= max_w < w; // Whether we need to do truncation.
532 int truncation_width = 0; // Width of the ellipsis string.
533
534 std::optional<Layouter> truncation_layout;
535 if (truncation) {
536 /*
537 * Assumption may be made that all fonts of a run are of the same size.
538 * In any case, we'll use these dots for the abbreviation, so even if
539 * another size would be chosen it won't have truncated too little for
540 * the truncation dots.
541 */
542 truncation_layout.emplace(GetEllipsis(), INT32_MAX, line.GetVisualRun(0).GetFont()->fc->GetSize());
543 truncation_width = truncation_layout->GetBounds().width;
544
545 /* Is there enough space even for an ellipsis? */
546 if (max_w < truncation_width) return (_current_text_dir == TD_RTL) ? left : right;
547
548 if (_current_text_dir == TD_RTL) {
549 min_x += truncation_width;
550 offset_x = w - max_w;
551 } else {
552 max_x -= truncation_width;
553 }
554
555 w = max_w;
556 }
557
558 /* In case we have a RTL language we swap the alignment. */
559 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
560
561 /* right is the right most position to draw on. In this case we want to do
562 * calculations with the width of the string. In comparison right can be
563 * seen as lastof(todraw) and width as lengthof(todraw). They differ by 1.
564 * So most +1/-1 additions are to move from lengthof to 'indices'.
565 */
566 switch (align & SA_HOR_MASK) {
567 case SA_LEFT:
568 /* right + 1 = left + w */
569 right = left + w - 1;
570 break;
571
572 case SA_HOR_CENTER:
573 left = RoundDivSU(right + 1 + left - w, 2);
574 /* right + 1 = left + w */
575 right = left + w - 1;
576 break;
577
578 case SA_RIGHT:
579 left = right + 1 - w;
580 break;
581
582 default:
583 NOT_REACHED();
584 }
585
586 const uint shadow_offset = ScaleGUITrad(1);
587
588 auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, ExtendedTextColour initial_colour) {
589 const DrawPixelInfo *dpi = _cur_dpi;
590 int dpi_left = dpi->left;
591 int dpi_right = dpi->left + dpi->width - 1;
592 ExtendedTextColour last_colour{initial_colour};
593
594 for (size_t run_index = 0; run_index < line.CountRuns(); run_index++) {
595 const ParagraphLayouter::VisualRun &run = line.GetVisualRun(run_index);
596 const auto &glyphs = run.GetGlyphs();
597 const auto &positions = run.GetPositions();
598 const Font *f = run.GetFont();
599
600 FontCache *fc = f->fc;
601 ExtendedTextColour colour{f->colour};
602 if (colour == TextColour::Invalid || initial_colour.flags.Test(ExtendedTextColourFlag::Forced)) colour = initial_colour;
603 bool colour_has_shadow = !colour.flags.Test(ExtendedTextColourFlag::NoShade) && colour != TextColour::Black;
604 /* Update the last colour for the truncation ellipsis. */
605 last_colour = colour;
606 if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
607 SetColourRemap(do_shadow ? TextColour::Black : colour);
608
609 for (size_t i = 0; i < run.GetGlyphCount(); i++) {
610 GlyphID glyph = glyphs[i];
611
612 /* Not a valid glyph (empty) */
613 if (glyph == 0xFFFF) continue;
614
615 int begin_x = positions[i].left + left;
616 int end_x = positions[i].right + left;
617 int top = positions[i].top + y;
618
619 /* Truncated away. */
620 if (truncation && (begin_x < min_x || end_x > max_x)) continue;
621
622 const Sprite *sprite = fc->GetGlyph(glyph);
623 /* Check clipping (the "+ 1" is for the shadow). */
624 if (begin_x + sprite->x_offs > dpi_right || begin_x + sprite->x_offs + sprite->width /* - 1 + 1 */ < dpi_left) continue;
625
626 if (do_shadow && (glyph & SPRITE_GLYPH) != 0) continue;
627
628 GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap);
629 }
630 }
631 return last_colour;
632 };
633
634 /* Draw shadow, then foreground */
635 for (bool do_shadow : {true, false}) {
636 ExtendedTextColour colour = draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, default_colour);
637
638 if (truncation) {
639 int x = (_current_text_dir == TD_RTL) ? left : (right - truncation_width);
640 draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, colour);
641 }
642 }
643
644 if (underline) {
645 GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap[1]});
646 }
647
648 return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
649}
650
668int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
669{
670 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
672
673 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
674 int extra = max_height / 2;
675
676 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > top + max_height + extra ||
677 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
678 return 0;
679 }
680
681 Layouter layout(str, INT32_MAX, fontsize);
682 if (layout.empty()) return 0;
683
684 return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true, colour);
685}
686
704int DrawString(int left, int right, int top, StringID str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
705{
706 return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize);
707}
708
716int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
717{
718 assert(maxw > 0);
719 Layouter layout(str, maxw, fontsize);
720 return layout.GetBounds().height;
721}
722
729int GetStringHeight(StringID str, int maxw)
730{
731 return GetStringHeight(GetString(str), maxw);
732}
733
740int GetStringLineCount(std::string_view str, int maxw)
741{
742 Layouter layout(str, maxw);
743 return (uint)layout.size();
744}
745
753{
754 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width)};
755 return box;
756}
757
765Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize)
766{
767 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width, fontsize)};
768 return box;
769}
770
787int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
788{
789 int maxw = right - left + 1;
790 int maxh = bottom - top + 1;
791
792 /* It makes no sense to even try if it can't be drawn anyway, or
793 * do we really want to support fonts of 0 or less pixels high? */
794 if (maxh <= 0) return top;
795
796 Layouter layout(str, maxw, fontsize);
797 int total_height = layout.GetBounds().height;
798 int y;
799 switch (align & SA_VERT_MASK) {
800 case SA_TOP:
801 y = top;
802 break;
803
804 case SA_VERT_CENTER:
805 y = RoundDivSU(bottom + top - total_height, 2);
806 break;
807
808 case SA_BOTTOM:
809 y = bottom - total_height;
810 break;
811
812 default: NOT_REACHED();
813 }
814
815 int last_line = top;
816 int first_line = bottom;
817
818 for (const auto &line : layout) {
819
820 int line_height = line->GetLeading();
821 if (y >= top && y + line_height - 1 <= bottom) {
822 last_line = y + line_height;
823 if (first_line > y) first_line = y;
824
825 DrawLayoutLine(*line, y, left, right, align, underline, false, colour);
826 }
827 y += line_height;
828 }
829
830 return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line;
831}
832
849int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
850{
851 return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize);
852}
853
872bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
873{
874 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
876
877 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
878 int extra = max_height / 2;
879
880 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > bottom + extra ||
881 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
882 return false;
883 }
884
885 DrawStringMultiLine(left, right, top, bottom, str, colour, align, underline, fontsize);
886 return true;
887}
888
899Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
900{
901 Layouter layout(str, INT32_MAX, start_fontsize);
902 return layout.GetBounds();
903}
904
913{
914 return GetStringBoundingBox(GetString(strid), start_fontsize);
915}
916
923uint GetStringListWidth(std::span<const StringID> list, FontSize fontsize)
924{
925 uint width = 0;
926 for (auto str : list) {
927 width = std::max(width, GetStringBoundingBox(str, fontsize).width);
928 }
929 return width;
930}
931
938Dimension GetStringListBoundingBox(std::span<const StringID> list, FontSize fontsize)
939{
940 Dimension d{0, 0};
941 for (auto str : list) {
942 d = maxdim(d, GetStringBoundingBox(str, fontsize));
943 }
944 return d;
945}
946
954void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
955{
956 SetColourRemap(colour);
957 GfxMainBlitter(GetGlyph(FontSize::Normal, c),
958 CentreBounds(r.left, r.right, GetCharacterWidth(FontSize::Normal, c)),
961}
962
972{
973 const Sprite *sprite = GetSprite(sprid, SpriteType::Normal);
974
975 if (offset != nullptr) {
976 offset->x = UnScaleByZoom(sprite->x_offs, zoom);
977 offset->y = UnScaleByZoom(sprite->y_offs, zoom);
978 }
979
980 Dimension d;
981 d.width = std::max<int>(0, UnScaleByZoom(sprite->x_offs + sprite->width, zoom));
982 d.height = std::max<int>(0, UnScaleByZoom(sprite->y_offs + sprite->height, zoom));
983 return d;
984}
985
992{
993 switch (pal) {
994 case PAL_NONE: return BlitterMode::Normal;
997 default: return BlitterMode::ColourRemap;
998 }
999}
1000
1009void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
1010{
1011 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1013 pal = GB(pal, 0, PALETTE_WIDTH);
1014 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1015 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite);
1016 } else if (pal != PAL_NONE) {
1017 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1019 } else {
1020 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1021 }
1022 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite);
1023 } else {
1024 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite);
1025 }
1026}
1027
1037void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
1038{
1039 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1041 pal = GB(pal, 0, PALETTE_WIDTH);
1042 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1043 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite, zoom);
1044 } else if (pal != PAL_NONE) {
1045 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1047 } else {
1048 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1049 }
1050 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite, zoom);
1051 } else {
1052 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite, zoom);
1053 }
1054}
1055
1069template <int ZOOM_BASE, bool SCALED_XY>
1070static void GfxBlitter(const Sprite * const sprite, int x, int y, BlitterMode mode, const SubSprite * const sub, SpriteID sprite_id, ZoomLevel zoom, const DrawPixelInfo *dst = nullptr)
1071{
1072 const DrawPixelInfo *dpi = (dst != nullptr) ? dst : _cur_dpi;
1074
1075 if (SCALED_XY) {
1076 /* Scale it */
1077 x = ScaleByZoom(x, zoom);
1078 y = ScaleByZoom(y, zoom);
1079 }
1080
1081 /* Move to the correct offset */
1082 x += sprite->x_offs;
1083 y += sprite->y_offs;
1084
1085 if (sub == nullptr) {
1086 /* No clipping. */
1087 bp.skip_left = 0;
1088 bp.skip_top = 0;
1089 bp.width = UnScaleByZoom(sprite->width, zoom);
1090 bp.height = UnScaleByZoom(sprite->height, zoom);
1091 } else {
1092 /* Amount of pixels to clip from the source sprite */
1093 int clip_left = std::max(0, -sprite->x_offs + sub->left * ZOOM_BASE );
1094 int clip_top = std::max(0, -sprite->y_offs + sub->top * ZOOM_BASE );
1095 int clip_right = std::max(0, sprite->width - (-sprite->x_offs + (sub->right + 1) * ZOOM_BASE));
1096 int clip_bottom = std::max(0, sprite->height - (-sprite->y_offs + (sub->bottom + 1) * ZOOM_BASE));
1097
1098 if (clip_left + clip_right >= sprite->width) return;
1099 if (clip_top + clip_bottom >= sprite->height) return;
1100
1101 bp.skip_left = UnScaleByZoomLower(clip_left, zoom);
1102 bp.skip_top = UnScaleByZoomLower(clip_top, zoom);
1103 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, zoom);
1104 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, zoom);
1105
1106 x += ScaleByZoom(bp.skip_left, zoom);
1107 y += ScaleByZoom(bp.skip_top, zoom);
1108 }
1109
1110 /* Copy the main data directly from the sprite */
1111 bp.sprite = sprite->data;
1112 bp.sprite_width = sprite->width;
1113 bp.sprite_height = sprite->height;
1114 bp.top = 0;
1115 bp.left = 0;
1116
1117 bp.dst = dpi->dst_ptr;
1118 bp.pitch = dpi->pitch;
1119 bp.remap = _colour_remap_ptr;
1120
1121 assert(sprite->width > 0);
1122 assert(sprite->height > 0);
1123
1124 if (bp.width <= 0) return;
1125 if (bp.height <= 0) return;
1126
1127 y -= SCALED_XY ? ScaleByZoom(dpi->top, zoom) : dpi->top;
1128 int y_unscaled = UnScaleByZoom(y, zoom);
1129 /* Check for top overflow */
1130 if (y < 0) {
1131 bp.height -= -y_unscaled;
1132 if (bp.height <= 0) return;
1133 bp.skip_top += -y_unscaled;
1134 y = 0;
1135 } else {
1136 bp.top = y_unscaled;
1137 }
1138
1139 /* Check for bottom overflow */
1140 y += SCALED_XY ? ScaleByZoom(bp.height - dpi->height, zoom) : ScaleByZoom(bp.height, zoom) - dpi->height;
1141 if (y > 0) {
1142 bp.height -= UnScaleByZoom(y, zoom);
1143 if (bp.height <= 0) return;
1144 }
1145
1146 x -= SCALED_XY ? ScaleByZoom(dpi->left, zoom) : dpi->left;
1147 int x_unscaled = UnScaleByZoom(x, zoom);
1148 /* Check for left overflow */
1149 if (x < 0) {
1150 bp.width -= -x_unscaled;
1151 if (bp.width <= 0) return;
1152 bp.skip_left += -x_unscaled;
1153 x = 0;
1154 } else {
1155 bp.left = x_unscaled;
1156 }
1157
1158 /* Check for right overflow */
1159 x += SCALED_XY ? ScaleByZoom(bp.width - dpi->width, zoom) : ScaleByZoom(bp.width, zoom) - dpi->width;
1160 if (x > 0) {
1161 bp.width -= UnScaleByZoom(x, zoom);
1162 if (bp.width <= 0) return;
1163 }
1164
1165 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, zoom));
1166 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, zoom));
1167
1168 /* We do not want to catch the mouse. However we also use that spritenumber for unknown (text) sprites. */
1169 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && sprite_id != SPR_CURSOR_MOUSE) {
1171 void *topleft = blitter->MoveTo(bp.dst, bp.left, bp.top);
1172 void *bottomright = blitter->MoveTo(topleft, bp.width - 1, bp.height - 1);
1173
1174 void *clicked = _newgrf_debug_sprite_picker.clicked_pixel;
1175
1176 if (topleft <= clicked && clicked <= bottomright) {
1177 uint offset = (((size_t)clicked - (size_t)topleft) / (blitter->GetScreenDepth() / 8)) % bp.pitch;
1178 if (offset < (uint)bp.width) {
1179 _newgrf_debug_sprite_picker.sprites.insert(sprite_id);
1180 }
1181 }
1182 }
1183
1184 BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, zoom);
1185}
1186
1194std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom)
1195{
1196 /* Invalid zoom level requested? */
1197 if (zoom < _settings_client.gui.zoom_min || zoom > _settings_client.gui.zoom_max) return nullptr;
1198
1200 if (blitter->GetScreenDepth() != 8 && blitter->GetScreenDepth() != 32) return nullptr;
1201
1202 /* Gather information about the sprite to write, reserve memory */
1203 const SpriteID real_sprite = GB(spriteId, 0, SPRITE_WIDTH);
1204 const Sprite *sprite = GetSprite(real_sprite, SpriteType::Normal);
1205 Dimension dim = GetSpriteSize(real_sprite, nullptr, zoom);
1206 size_t dim_size = static_cast<size_t>(dim.width) * dim.height;
1207 std::unique_ptr<uint32_t[]> result = std::make_unique<uint32_t[]>(dim_size);
1208
1209 /* Prepare new DrawPixelInfo - Normally this would be the screen but we want to draw to another buffer here.
1210 * Normally, pitch would be scaled screen width, but in our case our "screen" is only the sprite width wide. */
1211 DrawPixelInfo dpi;
1212 dpi.dst_ptr = result.get();
1213 dpi.pitch = dim.width;
1214 dpi.left = 0;
1215 dpi.top = 0;
1216 dpi.width = dim.width;
1217 dpi.height = dim.height;
1218 dpi.zoom = zoom;
1219
1220 dim_size = static_cast<size_t>(dim.width) * dim.height;
1221
1222 /* If the current blitter is a paletted blitter, we have to render to an extra buffer and resolve the palette later. */
1223 std::unique_ptr<uint8_t[]> pal_buffer{};
1224 if (blitter->GetScreenDepth() == 8) {
1225 pal_buffer = std::make_unique<uint8_t[]>(dim_size);
1226 dpi.dst_ptr = pal_buffer.get();
1227 }
1228
1229 /* Temporarily disable screen animations while blitting - This prevents 40bpp_anim from writing to the animation buffer. */
1230 Backup<bool> disable_anim(_screen_disable_anim, true);
1231 GfxBlitter<1, true>(sprite, 0, 0, BlitterMode::Normal, nullptr, real_sprite, zoom, &dpi);
1232 disable_anim.Restore();
1233
1234 if (blitter->GetScreenDepth() == 8) {
1235 /* Resolve palette. */
1236 uint32_t *dst = result.get();
1237 const uint8_t *src = pal_buffer.get();
1238 for (size_t i = 0; i < dim_size; ++i) {
1239 *dst++ = _cur_palette.palette[*src++].data;
1240 }
1241 }
1242
1243 return result;
1244}
1245
1246static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id)
1247{
1248 GfxBlitter<ZOOM_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
1249}
1250
1251static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id, ZoomLevel zoom)
1252{
1253 GfxBlitter<1, true>(sprite, x, y, mode, sub, sprite_id, zoom);
1254}
1255
1260void LoadStringWidthTable(FontSizes fontsizes)
1261{
1262 FontCache::ClearFontCaches(fontsizes);
1263
1264 for (FontSize fs : fontsizes) {
1265 for (uint i = 0; i != 224; i++) {
1266 _stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
1267 }
1268 }
1269}
1270
1277uint8_t GetCharacterWidth(FontSize size, char32_t key)
1278{
1279 /* Use _stringwidth_table cache if possible */
1280 if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
1281
1282 return GetGlyphWidth(size, key);
1283}
1284
1291{
1292 uint8_t width = 0;
1293 for (char c = '0'; c <= '9'; c++) {
1294 width = std::max(GetCharacterWidth(size, c), width);
1295 }
1296 return width;
1297}
1298
1306std::pair<uint8_t, uint8_t> GetBroadestDigit(FontSize size)
1307{
1308 uint8_t front = 0;
1309 uint8_t next = 0;
1310 int width = -1;
1311 for (char c = '9'; c >= '0'; c--) {
1312 int w = GetCharacterWidth(size, c);
1313 if (w <= width) continue;
1314
1315 width = w;
1316 next = c - '0';
1317 if (c != '0') front = c - '0';
1318 }
1319 return {front, next};
1320}
1321
1322void ScreenSizeChanged()
1323{
1324 _dirty_blocks_per_row = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
1325 _dirty_blocks_per_column = CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT);
1326 _dirty_blocks.resize(_dirty_blocks_per_column * _dirty_blocks_per_row);
1327
1328 /* check the dirty rect */
1329 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
1330 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
1331
1332 /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
1333 _cursor.visible = false;
1334
1335 if (VideoDriver::GetInstance() != nullptr) {
1336 if (AdjustGUIZoom(true)) ReInitAllWindows(true);
1337 }
1338}
1339
1340void UndrawMouseCursor()
1341{
1342 /* Don't undraw mouse cursor if it is handled by the video driver. */
1343 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1344
1345 /* Don't undraw the mouse cursor if the screen is not ready */
1346 if (_screen.dst_ptr == nullptr) return;
1347
1348 if (_cursor.visible) {
1350 _cursor.visible = false;
1351 blitter->CopyFromBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), _cursor_backup.GetBuffer(), _cursor.draw_size.x, _cursor.draw_size.y);
1352 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1353 }
1354}
1355
1356void DrawMouseCursor()
1357{
1358 /* Don't draw mouse cursor if it is handled by the video driver. */
1359 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1360
1361 /* Don't draw the mouse cursor if the screen is not ready */
1362 if (_screen.dst_ptr == nullptr) return;
1363
1365
1366 /* Redraw mouse cursor but only when it's inside the window */
1367 if (!_cursor.in_window) return;
1368
1369 /* Don't draw the mouse cursor if it's already drawn */
1370 if (_cursor.visible) {
1371 if (!_cursor.dirty) return;
1372 UndrawMouseCursor();
1373 }
1374
1375 /* Determine visible area */
1376 int left = _cursor.pos.x + _cursor.total_offs.x;
1377 int width = _cursor.total_size.x;
1378 if (left < 0) {
1379 width += left;
1380 left = 0;
1381 }
1382 if (left + width > _screen.width) {
1383 width = _screen.width - left;
1384 }
1385 if (width <= 0) return;
1386
1387 int top = _cursor.pos.y + _cursor.total_offs.y;
1388 int height = _cursor.total_size.y;
1389 if (top < 0) {
1390 height += top;
1391 top = 0;
1392 }
1393 if (top + height > _screen.height) {
1394 height = _screen.height - top;
1395 }
1396 if (height <= 0) return;
1397
1398 _cursor.draw_pos.x = left;
1399 _cursor.draw_pos.y = top;
1400 _cursor.draw_size.x = width;
1401 _cursor.draw_size.y = height;
1402
1403 uint8_t *buffer = _cursor_backup.Allocate(blitter->BufferSize(_cursor.draw_size.x, _cursor.draw_size.y));
1404
1405 /* Make backup of stuff below cursor */
1406 blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), buffer, _cursor.draw_size.x, _cursor.draw_size.y);
1407
1408 /* Draw cursor on screen */
1409 _cur_dpi = &_screen;
1410 for (const auto &cs : _cursor.sprites) {
1411 DrawSprite(cs.image.sprite, cs.image.pal, _cursor.pos.x + cs.pos.x, _cursor.pos.y + cs.pos.y);
1412 }
1413
1414 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1415
1416 _cursor.visible = true;
1417 _cursor.dirty = false;
1418}
1419
1430void RedrawScreenRect(int left, int top, int right, int bottom)
1431{
1432 assert(right <= _screen.width && bottom <= _screen.height);
1433 if (_cursor.visible) {
1434 if (right > _cursor.draw_pos.x &&
1435 left < _cursor.draw_pos.x + _cursor.draw_size.x &&
1436 bottom > _cursor.draw_pos.y &&
1437 top < _cursor.draw_pos.y + _cursor.draw_size.y) {
1438 UndrawMouseCursor();
1439 }
1440 }
1441
1443
1444 DrawOverlappedWindowForAll(left, top, right, bottom);
1445
1446 VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
1447}
1448
1457{
1458 auto is_dirty = [](auto block) -> bool { return block != 0; };
1459 auto block = _dirty_blocks.begin();
1460
1461 for (size_t x = 0; x < _dirty_blocks_per_row; ++x) {
1462 auto last_of_column = block + _dirty_blocks_per_column;
1463 for (size_t y = 0; y < _dirty_blocks_per_column; ++y, ++block) {
1464 if (!is_dirty(*block)) continue;
1465
1466 /* First try coalescing downwards */
1467 size_t height = std::find_if_not(block + 1, last_of_column, is_dirty) - block;
1468 size_t width = 1;
1469
1470 /* Clear dirty state. */
1471 std::fill_n(block, height, 0);
1472
1473 /* Try coalescing to the right too. */
1474 auto block_right = block;
1475 for (size_t x_right = x + 1; x_right < _dirty_blocks_per_row; ++x_right, ++width) {
1476 block_right += _dirty_blocks_per_column;
1477 auto last_right = block_right + height;
1478
1479 if (std::find_if_not(block_right, last_right, is_dirty) != last_right) break;
1480
1481 /* Clear dirty state. */
1482 std::fill_n(block_right, height, 0);
1483 }
1484
1485 int left = static_cast<int>(x * DIRTY_BLOCK_WIDTH);
1486 int top = static_cast<int>(y * DIRTY_BLOCK_HEIGHT);
1487 int right = left + static_cast<int>(width * DIRTY_BLOCK_WIDTH);
1488 int bottom = top + static_cast<int>(height * DIRTY_BLOCK_HEIGHT);
1489
1490 left = std::max(_invalid_rect.left, left);
1491 top = std::max(_invalid_rect.top, top);
1492 right = std::min(_invalid_rect.right, right);
1493 bottom = std::min(_invalid_rect.bottom, bottom);
1494
1495 if (left < right && top < bottom) {
1496 RedrawScreenRect(left, top, right, bottom);
1497 }
1498 }
1499 }
1500
1501 ++_dirty_block_colour;
1502 _invalid_rect.left = _screen.width;
1503 _invalid_rect.top = _screen.height;
1504 _invalid_rect.right = 0;
1505 _invalid_rect.bottom = 0;
1506}
1507
1520void AddDirtyBlock(int left, int top, int right, int bottom)
1521{
1522 if (left < 0) left = 0;
1523 if (top < 0) top = 0;
1524 if (right > _screen.width) right = _screen.width;
1525 if (bottom > _screen.height) bottom = _screen.height;
1526
1527 if (left >= right || top >= bottom) return;
1528
1529 _invalid_rect.left = std::min(_invalid_rect.left, left);
1530 _invalid_rect.top = std::min(_invalid_rect.top, top);
1531 _invalid_rect.right = std::max(_invalid_rect.right, right);
1532 _invalid_rect.bottom = std::max(_invalid_rect.bottom, bottom);
1533
1534 left /= DIRTY_BLOCK_WIDTH;
1535 top /= DIRTY_BLOCK_HEIGHT;
1536 right = CeilDiv(right, DIRTY_BLOCK_WIDTH);
1537 int height = CeilDiv(bottom, DIRTY_BLOCK_HEIGHT) - top;
1538
1539 assert(left < right && height > 0);
1540
1541 for (; left < right; ++left) {
1542 size_t offset = _dirty_blocks_per_column * left + top;
1543 std::fill_n(_dirty_blocks.begin() + offset, height, 0xFF);
1544 }
1545}
1546
1554{
1555 AddDirtyBlock(0, 0, _screen.width, _screen.height);
1556}
1557
1572bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
1573{
1575 const DrawPixelInfo *o = _cur_dpi;
1576
1577 n->zoom = ZoomLevel::Min;
1578
1579 assert(width > 0);
1580 assert(height > 0);
1581
1582 if ((left -= o->left) < 0) {
1583 width += left;
1584 if (width <= 0) return false;
1585 n->left = -left;
1586 left = 0;
1587 } else {
1588 n->left = 0;
1589 }
1590
1591 if (width > o->width - left) {
1592 width = o->width - left;
1593 if (width <= 0) return false;
1594 }
1595 n->width = width;
1596
1597 if ((top -= o->top) < 0) {
1598 height += top;
1599 if (height <= 0) return false;
1600 n->top = -top;
1601 top = 0;
1602 } else {
1603 n->top = 0;
1604 }
1605
1606 n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
1607 n->pitch = o->pitch;
1608
1609 if (height > o->height - top) {
1610 height = o->height - top;
1611 if (height <= 0) return false;
1612 }
1613 n->height = height;
1614
1615 return true;
1616}
1617
1623{
1624 /* Ignore setting any cursor before the sprites are loaded. */
1625 if (GetMaxSpriteID() == 0) return;
1626
1627 bool first = true;
1628 for (const auto &cs : _cursor.sprites) {
1629 const Sprite *p = GetSprite(GB(cs.image.sprite, 0, SPRITE_WIDTH), SpriteType::Normal);
1630 Point offs, size;
1631 offs.x = UnScaleGUI(p->x_offs) + cs.pos.x;
1632 offs.y = UnScaleGUI(p->y_offs) + cs.pos.y;
1633 size.x = UnScaleGUI(p->width);
1634 size.y = UnScaleGUI(p->height);
1635
1636 if (first) {
1637 /* First sprite sets the total. */
1638 _cursor.total_offs = offs;
1639 _cursor.total_size = size;
1640 first = false;
1641 } else {
1642 /* Additional sprites expand the total. */
1643 int right = std::max(_cursor.total_offs.x + _cursor.total_size.x, offs.x + size.x);
1644 int bottom = std::max(_cursor.total_offs.y + _cursor.total_size.y, offs.y + size.y);
1645 if (offs.x < _cursor.total_offs.x) _cursor.total_offs.x = offs.x;
1646 if (offs.y < _cursor.total_offs.y) _cursor.total_offs.y = offs.y;
1647 _cursor.total_size.x = right - _cursor.total_offs.x;
1648 _cursor.total_size.y = bottom - _cursor.total_offs.y;
1649 }
1650 }
1651
1652 _cursor.dirty = true;
1653}
1654
1660static void SetCursorSprite(CursorID cursor, PaletteID pal)
1661{
1662 if (_cursor.sprites.size() == 1 && _cursor.sprites[0].image.sprite == cursor && _cursor.sprites[0].image.pal == pal) return;
1663
1664 _cursor.sprites.clear();
1665 _cursor.sprites.emplace_back(cursor, pal, 0, 0);
1666
1668}
1669
1670static void SwitchAnimatedCursor()
1671{
1672 if (_cursor.animate_cur == std::end(_cursor.animate_list)) {
1673 _cursor.animate_cur = std::begin(_cursor.animate_list);
1674 }
1675
1676 assert(!_cursor.sprites.empty());
1677 SetCursorSprite(_cursor.animate_cur->sprite, _cursor.sprites[0].image.pal);
1678
1679 _cursor.animate_timeout = _cursor.animate_cur->display_time;
1680 ++_cursor.animate_cur;
1681}
1682
1683void CursorTick()
1684{
1685 if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0) {
1686 SwitchAnimatedCursor();
1687 }
1688}
1689
1694void SetMouseCursorBusy(bool busy)
1695{
1696 assert(!_cursor.sprites.empty());
1697 if (busy) {
1698 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
1699 } else {
1700 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
1701 }
1702}
1703
1711{
1712 /* Turn off animation */
1713 _cursor.animate_timeout = 0;
1714 /* Set cursor */
1715 SetCursorSprite(sprite, pal);
1716}
1717
1723void SetAnimatedMouseCursor(std::span<const AnimCursor> table)
1724{
1725 assert(!_cursor.sprites.empty());
1726 _cursor.animate_list = table;
1727 _cursor.animate_cur = std::end(table);
1728 _cursor.sprites[0].image.pal = PAL_NONE;
1729 SwitchAnimatedCursor();
1730}
1731
1738{
1739 if ((icon & ANIMCURSOR_FLAG) != 0) {
1741 } else {
1742 SetMouseCursor(icon, pal);
1743 }
1744}
1745
1752void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y)
1753{
1754 assert(this->fix_at);
1755
1756 this->delta.x = delta_x;
1757 this->delta.y = delta_y;
1758}
1759
1767{
1768 this->delta.x = x - this->pos.x;
1769 this->delta.y = y - this->pos.y;
1770
1771 if (this->fix_at) {
1772 return this->delta.x != 0 || this->delta.y != 0;
1773 } else if (this->pos.x != x || this->pos.y != y) {
1774 this->dirty = true;
1775 this->pos.x = x;
1776 this->pos.y = y;
1777 }
1778
1779 return false;
1780}
1781
1782bool ChangeResInGame(int width, int height)
1783{
1784 return (_screen.width == width && _screen.height == height) || VideoDriver::GetInstance()->ChangeResolution(width, height);
1785}
1786
1787bool ToggleFullScreen(bool fs)
1788{
1789 bool result = VideoDriver::GetInstance()->ToggleFullscreen(fs);
1790 if (_fullscreen != fs && _resolutions.empty()) {
1791 Debug(driver, 0, "Could not find a suitable fullscreen resolution");
1792 }
1793 return result;
1794}
1795
1796void SortResolutions()
1797{
1798 std::sort(_resolutions.begin(), _resolutions.end());
1799
1800 /* Remove any duplicates from the list. */
1801 auto last = std::unique(_resolutions.begin(), _resolutions.end());
1802 _resolutions.erase(last, _resolutions.end());
1803}
1804
1809{
1810 /* Determine real GUI zoom to use. */
1811 if (_gui_scale_cfg == -1) {
1812 /* Minimum design size of the game is 640x480. */
1813 float xs = _screen.width / 640.f;
1814 float ys = _screen.height / 480.f;
1815 int scale = std::min(xs, ys) * 100;
1816 /* Round down scaling to 25% increments and clamp to limits. */
1817 _gui_scale = Clamp((scale / 25) * 25, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1818 } else {
1819 _gui_scale = Clamp(_gui_scale_cfg, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1820 }
1821
1823 /* Font glyphs should not be clamped to min/max zoom. */
1824 _font_zoom = new_zoom;
1825 /* Ensure the gui_zoom is clamped between min/max. */
1826 new_zoom = Clamp(new_zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
1827 _gui_zoom = new_zoom;
1828}
1829
1836bool AdjustGUIZoom(bool automatic)
1837{
1838 if (VideoDriver::GetInstance() == nullptr) return false;
1839
1840 ZoomLevel old_gui_zoom = _gui_zoom;
1841 ZoomLevel old_font_zoom = _font_zoom;
1842 int old_scale = _gui_scale;
1843 UpdateGUIZoom();
1844 if (old_scale == _gui_scale && old_gui_zoom == _gui_zoom) return false;
1845
1846 /* Update cursors if sprite zoom level has changed. */
1847 if (old_gui_zoom != _gui_zoom) {
1850 }
1851 if (old_font_zoom != _font_zoom) {
1853 }
1856
1859
1860 /* Adjust all window sizes to match the new zoom level, so that they don't appear
1861 to move around when the application is moved to a screen with different DPI. */
1862 auto zoom_shift = old_gui_zoom - _gui_zoom;
1863 for (Window *w : Window::Iterate()) {
1864 if (automatic) {
1865 w->left = (w->left * _gui_scale) / old_scale;
1866 w->top = (w->top * _gui_scale) / old_scale;
1867 }
1868 if (w->viewport != nullptr) {
1869 w->viewport->zoom = Clamp(w->viewport->zoom - zoom_shift, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
1870 }
1871 }
1872
1873 return true;
1874}
1875
1876void ChangeGameSpeed(bool enable_fast_forward)
1877{
1878 if (enable_fast_forward) {
1879 _game_speed = _settings_client.gui.fast_forward_speed_limit;
1880 } else {
1881 _game_speed = 100;
1882 }
1883}
void UpdateAllVirtCoords()
Update the viewport coordinates of all signs.
This file defines all the the animated cursors.
static constexpr std::span< const AnimCursor > _animcursors[]
This is an array of pointers to all the animated cursor definitions we have above.
Definition animcursors.h:67
Class for backupping variables and making sure they are restored later.
BlitterMode
The modes of blitting we can do.
Definition base.hpp:17
@ Transparent
Perform transparency darkening remapping.
Definition base.hpp:20
@ CrashRemap
Perform a crash remapping.
Definition base.hpp:22
@ BlackRemap
Perform remapping to a completely blackened sprite.
Definition base.hpp:23
@ Normal
Perform the simple blitting.
Definition base.hpp:18
@ TransparentRemap
Perform transparency colour remapping.
Definition base.hpp:21
@ ColourRemap
Perform a colour remapping.
Definition base.hpp:19
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:139
How all blitters should look like.
Definition base.hpp:29
virtual void * MoveTo(void *video, int x, int y)=0
Move the destination pointer the requested amount x and y, keeping in mind any pitch and bpp of the r...
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
virtual size_t BufferSize(uint width, uint height)=0
Calculate how much memory there is needed for an image of this size in the video-buffer.
virtual void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash=0)=0
Draw a line with a given colour.
virtual void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal)=0
Draw a colourtable to the screen.
virtual void SetPixel(void *video, int x, int y, PixelColour colour)=0
Draw a pixel with a given colour on the video-buffer.
virtual void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)=0
Draw an image to the screen, given an amount of params defined above.
virtual void CopyToBuffer(const void *video, void *dst, int width, int height)=0
Copy from the screen to a buffer.
virtual void DrawRect(void *video, int width, int height, PixelColour colour)=0
Make a single horizontal line in a single colour on the video-buffer.
virtual void CopyFromBuffer(void *video, const void *src, int width, int height)=0
Copy from a buffer to the screen.
virtual void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y)=0
Scroll the videobuffer some 'x' and 'y' value.
Font cache for basic fonts.
Definition fontcache.h:23
virtual const Sprite * GetGlyph(GlyphID key)=0
Get the glyph (sprite) of the given key.
static void ClearFontCaches(FontSizes fontsizes)
Clear cached information for the specified font caches.
FontSize GetSize() const
Get the FontSize of the font.
Definition fontcache.h:59
virtual bool GetDrawGlyphShadow()=0
Do we need to draw a glyph shadow?
Container with information about a font.
Definition gfx_layout.h:118
ExtendedTextColour colour
The colour this font has to be.
Definition gfx_layout.h:121
FontCache * fc
The font we are using.
Definition gfx_layout.h:120
The layouter performs all the layout work.
Definition gfx_layout.h:254
Dimension GetBounds()
Get the boundaries of this paragraph.
A single line worth of VisualRuns.
Definition gfx_layout.h:197
virtual int GetLeading() const =0
Get the font leading, or distance between the baselines of consecutive lines.
virtual const VisualRun & GetVisualRun(size_t run) const =0
Get a reference to the given run.
virtual int GetWidth() const =0
Get the width of this line.
virtual size_t CountRuns() const =0
Get the number of runs in this line.
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:154
virtual std::span< const GlyphID > GetGlyphs() const =0
Get the glyphs to draw.
virtual std::span< const Position > GetPositions() const =0
Get the positions for each of the glyphs.
virtual const Font * GetFont() const =0
Get the font.
virtual size_t GetGlyphCount() const =0
Get the number of glyphs.
A reusable buffer that can be used for places that temporary allocate a bit of memory and do that ver...
virtual bool ToggleFullscreen(bool fullscreen)=0
Change the full screen setting.
virtual void ClearSystemSprites()
Clear all cached sprites.
virtual void MakeDirty(int left, int top, int width, int height)=0
Mark a particular area dirty.
virtual bool ChangeResolution(int w, int h)=0
Change the resolution of the window.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
Control codes that are embedded in the translation strings.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
std::vector< Dimension > _resolutions
List of resolutions.
Definition driver.cpp:28
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
EnumClassIndexContainer< std::array< T, to_underlying(N)>, Index > EnumIndexArray
A typedef for EnumClassIndexContainer using std::array as the backing container type.
Factory to 'query' all available blitters.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:88
const Sprite * GetGlyph(FontSize size, char32_t key)
Get the Sprite for a glyph.
Definition fontcache.h:166
uint GetGlyphWidth(FontSize size, char32_t key)
Get the width of a glyph.
Definition fontcache.h:178
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition gfx.cpp:716
void SetMouseCursor(CursorID sprite, PaletteID pal)
Assign a single non-animated sprite to the cursor.
Definition gfx.cpp:1710
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:971
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1622
static void SetCursorSprite(CursorID cursor, PaletteID pal)
Switch cursor to different sprite.
Definition gfx.cpp:1660
std::pair< uint8_t, uint8_t > GetBroadestDigit(FontSize size)
Determine the broadest digits for guessing the maximum width of a n-digit number.
Definition gfx.cpp:1306
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:40
static void GfxBlitter(const Sprite *const sprite, int x, int y, BlitterMode mode, const SubSprite *const sub, SpriteID sprite_id, ZoomLevel zoom, const DrawPixelInfo *dst=nullptr)
The code for setting up the blitter mode and sprite information before finally drawing the sprite.
Definition gfx.cpp:1070
int GetStringLineCount(std::string_view str, int maxw)
Calculates number of lines of string.
Definition gfx.cpp:740
void LoadStringWidthTable(FontSizes fontsizes)
Initialize _stringwidth_table cache for the specified font sizes.
Definition gfx.cpp:1260
bool _left_button_down
Is left mouse button pressed?
Definition gfx.cpp:42
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:899
static std::vector< LineSegment > MakePolygonSegments(std::span< const Point > shape, Point offset)
Make line segments from a polygon defined by points, translated by an offset.
Definition gfx.cpp:174
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:938
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
bool _screen_disable_anim
Disable palette animation (important for 32bpp-anim blitter during giant screenshot).
Definition gfx.cpp:47
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:787
ZoomLevel _font_zoom
Sprite font Zoom level (not clamped).
Definition gfx.cpp:63
bool _left_button_clicked
Is left mouse button clicked?
Definition gfx.cpp:43
uint GetStringListWidth(std::span< const StringID > list, FontSize fontsize)
Get maximum width of a list of strings.
Definition gfx.cpp:923
uint16_t _game_speed
Current game-speed; 100 is 1x, 0 is infinite.
Definition gfx.cpp:41
int DrawString(int left, int right, int top, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
static BlitterMode GetBlitterMode(PaletteID pal)
Helper function to get the blitter mode for different types of palettes.
Definition gfx.cpp:991
static void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash=0)
Check line clipping by using a linear equation and draw the visible part of the line given by x/y and...
Definition gfx.cpp:318
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1694
void SetCursor(CursorID icon, PaletteID pal)
Assign an animation or a non-animated sprite to the cursor.
Definition gfx.cpp:1737
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:52
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:464
void UpdateGUIZoom()
Resolve GUI zoom level, if auto-suggestion is requested.
Definition gfx.cpp:1808
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1037
bool _right_button_clicked
Is right mouse button clicked?
Definition gfx.cpp:45
void SetAnimatedMouseCursor(std::span< const AnimCursor > table)
Assign an animation to the cursor.
Definition gfx.cpp:1723
uint8_t GetDigitWidth(FontSize size)
Return the maximum width of single digit.
Definition gfx.cpp:1290
int _gui_scale_cfg
GUI scale in config.
Definition gfx.cpp:65
Support8bpp _support8bpp
State of the support for 8bpp graphics.
Definition gfx.cpp:37
uint8_t GetCharacterWidth(FontSize size, char32_t key)
Return width of character glyph.
Definition gfx.cpp:1277
DirectionKeys _dirkeys
Pressed direction keys.
Definition gfx.cpp:35
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
Draw a sprite in a viewport.
Definition gfx.cpp:1009
bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, ExtendedTextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw a multiline string, possibly over multiple lines, if the region is within the current display cl...
Definition gfx.cpp:872
PauseModes _pause_mode
The current pause mode.
Definition gfx.cpp:51
void GfxFillPolygon(std::span< const Point > shape, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Fill a polygon with colour.
Definition gfx.cpp:213
static bool GfxPreprocessLine(DrawPixelInfo *dpi, int &x, int &y, int &x2, int &y2, int width)
Align parameters of a line to the given DPI and check simple clipping.
Definition gfx.cpp:379
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion)
Calculate string bounding box for multi-line strings.
Definition gfx.cpp:752
std::unique_ptr< uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom)
Draws a sprite to a new RGBA buffer (see Colour union) instead of drawing to the screen.
Definition gfx.cpp:1194
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:116
static EnumIndexArray< std::array< uint8_t, 244 >, FontSize, FontSize::End > _stringwidth_table
Cache containing width of often used characters.
Definition gfx.cpp:54
SwitchMode _switch_mode
The next mainloop command.
Definition gfx.cpp:50
static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation, ExtendedTextColour default_colour)
Drawing routine for drawing a laid out line of text.
Definition gfx.cpp:506
void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
Draws the projection of a parallelepiped.
Definition gfx.cpp:426
ZoomLevel _gui_zoom
GUI Zoom level.
Definition gfx.cpp:62
static void SetColourRemap(ExtendedTextColour colour)
Set the colour remap to be for the given colour.
Definition gfx.cpp:476
bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
Set up a clipping area for only drawing into a certain area.
Definition gfx.cpp:1572
bool _right_button_down
Is right mouse button pressed?
Definition gfx.cpp:44
static uint8_t _string_colourremap[3]
Recoloursprite for stringdrawing. The grf loader ensures that SpriteType::Font sprites only use colou...
Definition gfx.cpp:76
bool AdjustGUIZoom(bool automatic)
Resolve GUI zoom level and adjust GUI to new zoom, if auto-suggestion is requested.
Definition gfx.cpp:1836
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
Draw single character horizontally centered around (x,y).
Definition gfx.cpp:954
int _gui_scale
GUI scale, 100 is 100%.
Definition gfx.cpp:64
Functions related to the gfx engine.
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
From a rectangle that needs redrawing, find the windows that intersect with the rectangle.
Definition window.cpp:959
Palette _cur_palette
Current palette.
Definition palette.cpp:24
Functions related to laying out the texts.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
Support8bpp
Modes for 8bpp support.
Definition gfx_type.h:426
@ Recolour
Recolour sprite.
Definition gfx_type.h:407
@ Normal
The most basic (normal) sprite.
Definition gfx_type.h:404
FontSize
Available font sizes.
Definition gfx_type.h:248
@ Small
Index of the small font in the font tables.
Definition gfx_type.h:250
@ Large
Index of the large font in the font tables.
Definition gfx_type.h:251
@ End
Marker for the end of the enumerations.
Definition gfx_type.h:254
@ Normal
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ Monospace
Index of the monospaced font in the font tables.
Definition gfx_type.h:252
uint32_t CursorID
The number of the cursor (sprite).
Definition gfx_type.h:19
StringAlignment
How to align the to-be drawn text.
Definition gfx_type.h:433
@ SA_TOP
Top align the text.
Definition gfx_type.h:439
@ SA_LEFT
Left align the text.
Definition gfx_type.h:434
@ SA_HOR_MASK
Mask for horizontal alignment.
Definition gfx_type.h:437
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:436
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:435
@ SA_VERT_MASK
Mask for vertical alignment.
Definition gfx_type.h:442
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:446
@ SA_BOTTOM
Bottom align the text.
Definition gfx_type.h:441
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:440
constexpr FontSizes FONTSIZES_ALL
Mask of all possible font sizes.
Definition gfx_type.h:262
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
EnumBitSet< DirectionKey, uint8_t > DirectionKeys
Bitset of the direction keys.
Definition gfx_type.h:457
@ NoShade
Do not add shading to this text colour.
Definition gfx_type.h:342
@ IsPaletteColour
Colour value is already a real palette colour index, not an index of a StringColour.
Definition gfx_type.h:341
@ Forced
Ignore colour changes from strings.
Definition gfx_type.h:343
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:315
@ Invalid
Invalid colour.
Definition gfx_type.h:336
@ Black
Black colour.
Definition gfx_type.h:334
FillRectMode
Define the operation GfxFillRect performs.
Definition gfx_type.h:390
@ Recolour
Apply a recolour sprite to the screen content.
Definition gfx_type.h:393
@ Checker
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:392
static Rect _invalid_rect
The rect for repaint.
Definition gfx.cpp:74
void AddDirtyBlock(int left, int top, int right, int bottom)
Extend the internal _invalid_rect rectangle to contain the rectangle defined by the given parameters.
Definition gfx.cpp:1520
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1456
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1553
void RedrawScreenRect(int left, int top, int right, int bottom)
Repaints a specific rectangle of the screen.
Definition gfx.cpp:1430
#define Rect
Macro that prevents name conflicts between included headers.
#define Point
Macro that prevents name conflicts between included headers.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _networking
are we in networking mode?
Definition network.cpp:67
Basic functions/variables used all over the place.
void NetworkUndrawChatMessage()
Hide the chatbox.
Network functions used by other parts of OpenTTD.
Functions/types related to NewGRF debugging.
NewGrfDebugSpritePicker _newgrf_debug_sprite_picker
The sprite picker.
GameMode
Mode which defines the state of the game.
Definition openttd.h:18
SwitchMode
Mode which defines what mode we're switching to.
Definition openttd.h:26
static constexpr PixelColour PC_WHITE
White palette colour.
Functions related to modal progress.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Types related to global configuration settings.
void GfxClearFontSpriteCache()
Remove all encoded font sprites from the sprite cache without discarding sprite location information.
SpriteID GetMaxSpriteID()
Get a reasonable (upper bound) estimate of the maximum SpriteID used in OpenTTD; there will be no spr...
This file contains all sprite-related enums and defines.
static const CursorID ANIMCURSOR_FLAG
Flag for saying a cursor sprite is an animated cursor.
Definition sprites.h:1521
static const PaletteID PALETTE_ALL_BLACK
Exchange any colour by black, needed for painting fictive tiles outside map.
Definition sprites.h:1625
static constexpr uint8_t PALETTE_WIDTH
number of bits of the sprite containing the recolour palette
Definition sprites.h:1548
static constexpr uint8_t PALETTE_MODIFIER_TRANSPARENT
when a sprite is to be displayed transparently, this bit needs to be set.
Definition sprites.h:1561
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1404
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
Definition sprites.h:1546
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1549
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1616
Definition of base types and functions in a cross-platform compatible way.
The colour translation of GRF's strings.
static constexpr PixelColour _string_colourmap[17]
Colour mapping for TextColour.
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
std::string_view GetEllipsis()
Get the ellipsis string for the current language.
Definition strings.cpp:308
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
@ TD_RTL
Text is written right-to-left by default.
Class to backup a specific variable and restore it later.
void Restore()
Restore the variable.
Parameters related to blitting.
Definition base.hpp:32
int skip_top
How much pixels of the source to skip on the top (based on zoom of dst).
Definition base.hpp:37
int sprite_height
Real height of the sprite.
Definition base.hpp:41
void * dst
Destination buffer.
Definition base.hpp:45
int left
The left offset in the 'dst' in pixels to start drawing.
Definition base.hpp:42
int pitch
The pitch of the destination buffer.
Definition base.hpp:46
int sprite_width
Real width of the sprite.
Definition base.hpp:40
int skip_left
How much pixels of the source to skip on the left (based on zoom of dst).
Definition base.hpp:36
int height
The height in pixels that needs to be drawn to dst.
Definition base.hpp:39
const uint8_t * remap
XXX – Temporary storage for remap array.
Definition base.hpp:34
int width
The width in pixels that needs to be drawn to dst.
Definition base.hpp:38
const void * sprite
Pointer to the sprite how ever the encoder stored it.
Definition base.hpp:33
int top
The top offset in the 'dst' in pixels to start drawing.
Definition base.hpp:43
T y
Y coordinate.
T x
X coordinate.
Collection of variables for cursor-display and -animation.
Definition gfx_type.h:123
bool UpdateCursorPosition(int x, int y)
Update cursor position on mouse movement.
Definition gfx.cpp:1766
bool fix_at
mouse is moving, but cursor is not (used for scrolling)
Definition gfx_type.h:128
Point pos
logical mouse position
Definition gfx_type.h:125
void UpdateCursorPositionRelative(int delta_x, int delta_y)
Update cursor position based on a relative change.
Definition gfx.cpp:1752
bool dirty
the rect occupied by the mouse is dirty (redraw)
Definition gfx_type.h:146
Point delta
relative mouse movement in this tick
Definition gfx_type.h:126
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
Container for the text colour and some text colour related flags for drawing.
Definition gfx_type.h:348
ExtendedTextColourFlags flags
The flags.
Definition gfx_type.h:363
TextColour colour
The colour.
Definition gfx_type.h:362
Colour for pixel/line drawing.
Definition gfx_type.h:307
Data structure describing a sprite.
uint16_t width
Width of the sprite.
uint16_t height
Height of the sprite.
int16_t y_offs
Number of pixels to shift the sprite downwards.
std::byte data[]
Sprite data.
int16_t x_offs
Number of pixels to shift the sprite to the right.
Used to only draw a part of the sprite.
Definition gfx_type.h:278
Data structure for an opened window.
Definition window_gui.h:274
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:940
Base of all video drivers.
Functions related to (drawing on) viewports.
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:98
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
Definition window.cpp:3439
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:22
int UnScaleByZoomLower(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZoomLevel::Min).
Definition zoom_func.h:67
int UnScaleGUI(int value)
Short-hand to apply GUI zoom level.
Definition zoom_func.h:77
int UnScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:34
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:20
@ In2x
Zoomed 2 times in.
Definition zoom_type.h:25
@ Min
Minimum zoom level.
Definition zoom_type.h:23
@ Normal
The normal zoom level.
Definition zoom_type.h:26
@ In4x
Zoomed 4 times in.
Definition zoom_type.h:24