OpenTTD Source 20260108-master-g8ba1860eaa
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
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
29#include "table/sprites.h"
30#include "table/control_codes.h"
31
32#include "safeguards.h"
33
34uint8_t _dirkeys;
35bool _fullscreen;
36uint8_t _support8bpp;
37CursorVars _cursor;
40uint16_t _game_speed = 100;
45DrawPixelInfo _screen;
47std::atomic<bool> _exit_game;
48GameMode _game_mode;
52
53static uint8_t _stringwidth_table[FS_END][224];
54DrawPixelInfo *_cur_dpi;
55
56static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = nullptr, SpriteID sprite_id = SPR_CURSOR_MOUSE);
57static 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);
58
59static ReusableBuffer<uint8_t> _cursor_backup;
60
63int _gui_scale = MIN_INTERFACE_SCALE;
65
74static const uint8_t *_colour_remap_ptr;
75static uint8_t _string_colourremap[3];
76
77static const uint DIRTY_BLOCK_HEIGHT = 8;
78static const uint DIRTY_BLOCK_WIDTH = 64;
79
80static size_t _dirty_blocks_per_row = 0;
81static size_t _dirty_blocks_per_column = 0;
82static std::vector<uint8_t> _dirty_blocks;
83extern uint _dirty_block_colour;
84
85void GfxScroll(int left, int top, int width, int height, int xo, int yo)
86{
88
89 if (xo == 0 && yo == 0) return;
90
91 if (_cursor.visible) UndrawMouseCursor();
92
94
95 blitter->ScrollBuffer(_screen.dst_ptr, left, top, width, height, xo, yo);
96 /* This part of the screen is now dirty. */
97 VideoDriver::GetInstance()->MakeDirty(left, top, width, height);
98}
99
100
115void GfxFillRect(int left, int top, int right, int bottom, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
116{
118 const DrawPixelInfo *dpi = _cur_dpi;
119 void *dst;
120 const int otop = top;
121 const int oleft = left;
122
123 if (dpi->zoom != ZoomLevel::Min) return;
124 if (left > right || top > bottom) return;
125 if (right < dpi->left || left >= dpi->left + dpi->width) return;
126 if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
127
128 if ( (left -= dpi->left) < 0) left = 0;
129 right = right - dpi->left + 1;
130 if (right > dpi->width) right = dpi->width;
131 right -= left;
132 assert(right > 0);
133
134 if ( (top -= dpi->top) < 0) top = 0;
135 bottom = bottom - dpi->top + 1;
136 if (bottom > dpi->height) bottom = dpi->height;
137 bottom -= top;
138 assert(bottom > 0);
139
140 dst = blitter->MoveTo(dpi->dst_ptr, left, top);
141
142 switch (mode) {
143 default: // FILLRECT_OPAQUE
144 blitter->DrawRect(dst, right, bottom, std::get<PixelColour>(colour));
145 break;
146
148 blitter->DrawColourMappingRect(dst, right, bottom, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
149 break;
150
151 case FILLRECT_CHECKER: {
152 uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
153 PixelColour pc = std::get<PixelColour>(colour);
154 do {
155 for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, pc);
156 dst = blitter->MoveTo(dst, 0, 1);
157 } while (--bottom > 0);
158 break;
159 }
160 }
161}
162
163typedef std::pair<Point, Point> LineSegment;
164
173static std::vector<LineSegment> MakePolygonSegments(std::span<const Point> shape, Point offset)
174{
175 std::vector<LineSegment> segments;
176 if (shape.size() < 3) return segments; // fewer than 3 will always result in an empty polygon
177 segments.reserve(shape.size());
178
179 /* Connect first and last point by having initial previous point be the last */
180 Point prev = shape.back();
181 prev.x -= offset.x;
182 prev.y -= offset.y;
183 for (Point pt : shape) {
184 pt.x -= offset.x;
185 pt.y -= offset.y;
186 /* Create segments for all non-horizontal lines in the polygon.
187 * The segments always have lowest Y coordinate first. */
188 if (prev.y > pt.y) {
189 segments.emplace_back(pt, prev);
190 } else if (prev.y < pt.y) {
191 segments.emplace_back(prev, pt);
192 }
193 prev = pt;
194 }
195
196 return segments;
197}
198
212void GfxFillPolygon(std::span<const Point> shape, const std::variant<PixelColour, PaletteID> &colour, FillRectMode mode)
213{
215 const DrawPixelInfo *dpi = _cur_dpi;
216 if (dpi->zoom != ZoomLevel::Min) return;
217
218 std::vector<LineSegment> segments = MakePolygonSegments(shape, Point{ dpi->left, dpi->top });
219
220 /* Remove segments appearing entirely above or below the clipping area. */
221 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());
222
223 /* Check that this wasn't an empty shape (all points on a horizontal line or outside clipping.) */
224 if (segments.empty()) return;
225
226 /* Sort the segments by first point Y coordinate. */
227 std::sort(segments.begin(), segments.end(), [](const LineSegment &a, const LineSegment &b) { return a.first.y < b.first.y; });
228
229 /* Segments intersecting current scanline. */
230 std::vector<LineSegment> active;
231 /* Intersection points with a scanline.
232 * Kept outside loop to avoid repeated re-allocations. */
233 std::vector<int> intersections;
234 /* Normal, reasonable polygons don't have many intersections per scanline. */
235 active.reserve(4);
236 intersections.reserve(4);
237
238 /* Scan through the segments and paint each scanline. */
239 int y = segments.front().first.y;
240 std::vector<LineSegment>::iterator nextseg = segments.begin();
241 while (!active.empty() || nextseg != segments.end()) {
242 /* Clean up segments that have ended. */
243 active.erase(std::remove_if(active.begin(), active.end(), [y](const LineSegment &s) { return s.second.y == y; }), active.end());
244
245 /* Activate all segments starting on this scanline. */
246 while (nextseg != segments.end() && nextseg->first.y == y) {
247 active.push_back(*nextseg);
248 ++nextseg;
249 }
250
251 /* Check clipping. */
252 if (y < 0) {
253 ++y;
254 continue;
255 }
256 if (y >= dpi->height) return;
257
258 /* Intersect scanline with all active segments. */
259 intersections.clear();
260 for (const LineSegment &s : active) {
261 const int sdx = s.second.x - s.first.x;
262 const int sdy = s.second.y - s.first.y;
263 const int ldy = y - s.first.y;
264 const int x = s.first.x + sdx * ldy / sdy;
265 intersections.push_back(x);
266 }
267
268 /* Fill between pairs of intersections. */
269 std::sort(intersections.begin(), intersections.end());
270 for (size_t i = 1; i < intersections.size(); i += 2) {
271 /* Check clipping. */
272 const int x1 = std::max(0, intersections[i - 1]);
273 const int x2 = std::min(intersections[i], dpi->width);
274 if (x2 < 0) continue;
275 if (x1 >= dpi->width) continue;
276
277 /* Fill line y from x1 to x2. */
278 void *dst = blitter->MoveTo(dpi->dst_ptr, x1, y);
279 switch (mode) {
280 default: // FILLRECT_OPAQUE
281 blitter->DrawRect(dst, x2 - x1, 1, std::get<PixelColour>(colour));
282 break;
284 blitter->DrawColourMappingRect(dst, x2 - x1, 1, GB(std::get<PaletteID>(colour), 0, PALETTE_WIDTH));
285 break;
286 case FILLRECT_CHECKER: {
287 /* Fill every other pixel, offset such that the sum of filled pixels' X and Y coordinates is odd.
288 * This creates a checkerboard effect. */
289 PixelColour pc = std::get<PixelColour>(colour);
290 for (int x = (x1 + y) & 1; x < x2 - x1; x += 2) {
291 blitter->SetPixel(dst, x, 0, pc);
292 }
293 break;
294 }
295 }
296 }
297
298 /* Next line */
299 ++y;
300 }
301}
302
317static 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)
318{
320
321 assert(width > 0);
322
323 if (y2 == y || x2 == x) {
324 /* Special case: horizontal/vertical line. All checks already done in GfxPreprocessLine. */
325 blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
326 return;
327 }
328
329 int grade_y = y2 - y;
330 int grade_x = x2 - x;
331
332 /* Clipping rectangle. Slightly extended so we can ignore the width of the line. */
333 int extra = (int)CeilDiv(3 * width, 4); // not less then "width * sqrt(2) / 2"
334 Rect clip = { -extra, -extra, screen_width - 1 + extra, screen_height - 1 + extra };
335
336 /* prevent integer overflows. */
337 int margin = 1;
338 while (INT_MAX / abs(grade_y) < std::max(abs(clip.left - x), abs(clip.right - x))) {
339 grade_y /= 2;
340 grade_x /= 2;
341 margin *= 2; // account for rounding errors
342 }
343
344 /* Prevent division by zero. */
345 if (grade_x == 0) grade_x = 1;
346
347 /* Imagine that the line is infinitely long and it intersects with
348 * infinitely long left and right edges of the clipping rectangle.
349 * If both intersection points are outside the clipping rectangle
350 * and both on the same side of it, we don't need to draw anything. */
351 int left_isec_y = y + (clip.left - x) * grade_y / grade_x;
352 int right_isec_y = y + (clip.right - x) * grade_y / grade_x;
353 if ((left_isec_y > clip.bottom + margin && right_isec_y > clip.bottom + margin) ||
354 (left_isec_y < clip.top - margin && right_isec_y < clip.top - margin)) {
355 return;
356 }
357
358 /* It is possible to use the line equation to further reduce the amount of
359 * work the blitter has to do by shortening the effective line segment.
360 * However, in order to get that right and prevent the flickering effects
361 * of rounding errors so much additional code has to be run here that in
362 * the general case the effect is not noticeable. */
363
364 blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
365}
366
378static inline bool GfxPreprocessLine(DrawPixelInfo *dpi, int &x, int &y, int &x2, int &y2, int width)
379{
380 x -= dpi->left;
381 x2 -= dpi->left;
382 y -= dpi->top;
383 y2 -= dpi->top;
384
385 /* Check simple clipping */
386 if (x + width / 2 < 0 && x2 + width / 2 < 0 ) return false;
387 if (y + width / 2 < 0 && y2 + width / 2 < 0 ) return false;
388 if (x - width / 2 > dpi->width && x2 - width / 2 > dpi->width ) return false;
389 if (y - width / 2 > dpi->height && y2 - width / 2 > dpi->height) return false;
390 return true;
391}
392
393void GfxDrawLine(int x, int y, int x2, int y2, PixelColour colour, int width, int dash)
394{
395 DrawPixelInfo *dpi = _cur_dpi;
396 if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) {
397 GfxDoDrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour, width, dash);
398 }
399}
400
401void GfxDrawLineUnscaled(int x, int y, int x2, int y2, PixelColour colour)
402{
403 DrawPixelInfo *dpi = _cur_dpi;
404 if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) {
405 GfxDoDrawLine(dpi->dst_ptr,
406 UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
407 UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
408 UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour, 1);
409 }
410}
411
425void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
426{
427 /* ....
428 * .. ....
429 * .. ....
430 * .. ^
431 * <--__(dx1,dy1) /(dx2,dy2)
432 * : --__ / :
433 * : --__ / :
434 * : *(x,y) :
435 * : | :
436 * : | ..
437 * .... |(dx3,dy3)
438 * .... | ..
439 * ....V.
440 */
441
442 static constexpr PixelColour colour = PC_WHITE;
443
444 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
445 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
446 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
447
448 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
449 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
450 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
451 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
452 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
453 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
454}
455
463void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
464{
465 GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash);
466 GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash);
467 GfxDrawLine(r.right, r.top, r.right, r.bottom, colour, width, dash);
468 GfxDrawLine(r.left, r.bottom, r.right, r.bottom, colour, width, dash);
469}
470
475static void SetColourRemap(TextColour colour)
476{
477 if (colour == TC_INVALID) return;
478
479 /* Black strings have no shading ever; the shading is black, so it
480 * would be invisible at best, but it actually makes it illegible. */
481 bool no_shade = (colour & TC_NO_SHADE) != 0 || colour == TC_BLACK;
482 bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
484
485 _string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[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, TextColour 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, TextColour initial_colour) {
589 const DrawPixelInfo *dpi = _cur_dpi;
590 int dpi_left = dpi->left;
591 int dpi_right = dpi->left + dpi->width - 1;
592 TextColour last_colour = initial_colour;
593
594 for (int 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 TextColour colour = f->colour;
602 if (colour == TC_INVALID || HasFlag(initial_colour, TC_FORCED)) colour = initial_colour;
603 bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_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 ? TC_BLACK : colour);
608
609 for (int 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 TextColour 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, TextColour 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, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
705{
706 return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize);
707}
708
715int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
716{
717 assert(maxw > 0);
718 Layouter layout(str, maxw, fontsize);
719 return layout.GetBounds().height;
720}
721
728int GetStringHeight(StringID str, int maxw)
729{
730 return GetStringHeight(GetString(str), maxw);
731}
732
739int GetStringLineCount(std::string_view str, int maxw)
740{
741 Layouter layout(str, maxw);
742 return (uint)layout.size();
743}
744
752{
753 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width)};
754 return box;
755}
756
763Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize)
764{
765 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width, fontsize)};
766 return box;
767}
768
785int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
786{
787 int maxw = right - left + 1;
788 int maxh = bottom - top + 1;
789
790 /* It makes no sense to even try if it can't be drawn anyway, or
791 * do we really want to support fonts of 0 or less pixels high? */
792 if (maxh <= 0) return top;
793
794 Layouter layout(str, maxw, fontsize);
795 int total_height = layout.GetBounds().height;
796 int y;
797 switch (align & SA_VERT_MASK) {
798 case SA_TOP:
799 y = top;
800 break;
801
802 case SA_VERT_CENTER:
803 y = RoundDivSU(bottom + top - total_height, 2);
804 break;
805
806 case SA_BOTTOM:
807 y = bottom - total_height;
808 break;
809
810 default: NOT_REACHED();
811 }
812
813 int last_line = top;
814 int first_line = bottom;
815
816 for (const auto &line : layout) {
817
818 int line_height = line->GetLeading();
819 if (y >= top && y + line_height - 1 <= bottom) {
820 last_line = y + line_height;
821 if (first_line > y) first_line = y;
822
823 DrawLayoutLine(*line, y, left, right, align, underline, false, colour);
824 }
825 y += line_height;
826 }
827
828 return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line;
829}
830
847int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
848{
849 return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize);
850}
851
870bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
871{
872 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
874
875 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
876 int extra = max_height / 2;
877
878 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > bottom + extra ||
879 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
880 return false;
881 }
882
883 DrawStringMultiLine(left, right, top, bottom, str, colour, align, underline, fontsize);
884 return true;
885}
886
897Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
898{
899 Layouter layout(str, INT32_MAX, start_fontsize);
900 return layout.GetBounds();
901}
902
910{
911 return GetStringBoundingBox(GetString(strid), start_fontsize);
912}
913
920uint GetStringListWidth(std::span<const StringID> list, FontSize fontsize)
921{
922 uint width = 0;
923 for (auto str : list) {
924 width = std::max(width, GetStringBoundingBox(str, fontsize).width);
925 }
926 return width;
927}
928
935Dimension GetStringListBoundingBox(std::span<const StringID> list, FontSize fontsize)
936{
937 Dimension d{0, 0};
938 for (auto str : list) {
939 d = maxdim(d, GetStringBoundingBox(str, fontsize));
940 }
941 return d;
942}
943
951void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
952{
953 SetColourRemap(colour);
954 GfxMainBlitter(GetGlyph(FS_NORMAL, c),
955 CentreBounds(r.left, r.right, GetCharacterWidth(FS_NORMAL, c)),
956 CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
958}
959
969{
970 const Sprite *sprite = GetSprite(sprid, SpriteType::Normal);
971
972 if (offset != nullptr) {
973 offset->x = UnScaleByZoom(sprite->x_offs, zoom);
974 offset->y = UnScaleByZoom(sprite->y_offs, zoom);
975 }
976
977 Dimension d;
978 d.width = std::max<int>(0, UnScaleByZoom(sprite->x_offs + sprite->width, zoom));
979 d.height = std::max<int>(0, UnScaleByZoom(sprite->y_offs + sprite->height, zoom));
980 return d;
981}
982
989{
990 switch (pal) {
991 case PAL_NONE: return BlitterMode::Normal;
994 default: return BlitterMode::ColourRemap;
995 }
996}
997
1006void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
1007{
1008 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1010 pal = GB(pal, 0, PALETTE_WIDTH);
1011 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1012 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite);
1013 } else if (pal != PAL_NONE) {
1014 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1016 } else {
1017 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1018 }
1019 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite);
1020 } else {
1021 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite);
1022 }
1023}
1024
1034void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
1035{
1036 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1038 pal = GB(pal, 0, PALETTE_WIDTH);
1039 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1040 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite, zoom);
1041 } else if (pal != PAL_NONE) {
1042 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1044 } else {
1045 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1046 }
1047 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite, zoom);
1048 } else {
1049 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite, zoom);
1050 }
1051}
1052
1065template <int ZOOM_BASE, bool SCALED_XY>
1066static 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)
1067{
1068 const DrawPixelInfo *dpi = (dst != nullptr) ? dst : _cur_dpi;
1070
1071 if (SCALED_XY) {
1072 /* Scale it */
1073 x = ScaleByZoom(x, zoom);
1074 y = ScaleByZoom(y, zoom);
1075 }
1076
1077 /* Move to the correct offset */
1078 x += sprite->x_offs;
1079 y += sprite->y_offs;
1080
1081 if (sub == nullptr) {
1082 /* No clipping. */
1083 bp.skip_left = 0;
1084 bp.skip_top = 0;
1085 bp.width = UnScaleByZoom(sprite->width, zoom);
1086 bp.height = UnScaleByZoom(sprite->height, zoom);
1087 } else {
1088 /* Amount of pixels to clip from the source sprite */
1089 int clip_left = std::max(0, -sprite->x_offs + sub->left * ZOOM_BASE );
1090 int clip_top = std::max(0, -sprite->y_offs + sub->top * ZOOM_BASE );
1091 int clip_right = std::max(0, sprite->width - (-sprite->x_offs + (sub->right + 1) * ZOOM_BASE));
1092 int clip_bottom = std::max(0, sprite->height - (-sprite->y_offs + (sub->bottom + 1) * ZOOM_BASE));
1093
1094 if (clip_left + clip_right >= sprite->width) return;
1095 if (clip_top + clip_bottom >= sprite->height) return;
1096
1097 bp.skip_left = UnScaleByZoomLower(clip_left, zoom);
1098 bp.skip_top = UnScaleByZoomLower(clip_top, zoom);
1099 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, zoom);
1100 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, zoom);
1101
1102 x += ScaleByZoom(bp.skip_left, zoom);
1103 y += ScaleByZoom(bp.skip_top, zoom);
1104 }
1105
1106 /* Copy the main data directly from the sprite */
1107 bp.sprite = sprite->data;
1108 bp.sprite_width = sprite->width;
1109 bp.sprite_height = sprite->height;
1110 bp.top = 0;
1111 bp.left = 0;
1112
1113 bp.dst = dpi->dst_ptr;
1114 bp.pitch = dpi->pitch;
1115 bp.remap = _colour_remap_ptr;
1116
1117 assert(sprite->width > 0);
1118 assert(sprite->height > 0);
1119
1120 if (bp.width <= 0) return;
1121 if (bp.height <= 0) return;
1122
1123 y -= SCALED_XY ? ScaleByZoom(dpi->top, zoom) : dpi->top;
1124 int y_unscaled = UnScaleByZoom(y, zoom);
1125 /* Check for top overflow */
1126 if (y < 0) {
1127 bp.height -= -y_unscaled;
1128 if (bp.height <= 0) return;
1129 bp.skip_top += -y_unscaled;
1130 y = 0;
1131 } else {
1132 bp.top = y_unscaled;
1133 }
1134
1135 /* Check for bottom overflow */
1136 y += SCALED_XY ? ScaleByZoom(bp.height - dpi->height, zoom) : ScaleByZoom(bp.height, zoom) - dpi->height;
1137 if (y > 0) {
1138 bp.height -= UnScaleByZoom(y, zoom);
1139 if (bp.height <= 0) return;
1140 }
1141
1142 x -= SCALED_XY ? ScaleByZoom(dpi->left, zoom) : dpi->left;
1143 int x_unscaled = UnScaleByZoom(x, zoom);
1144 /* Check for left overflow */
1145 if (x < 0) {
1146 bp.width -= -x_unscaled;
1147 if (bp.width <= 0) return;
1148 bp.skip_left += -x_unscaled;
1149 x = 0;
1150 } else {
1151 bp.left = x_unscaled;
1152 }
1153
1154 /* Check for right overflow */
1155 x += SCALED_XY ? ScaleByZoom(bp.width - dpi->width, zoom) : ScaleByZoom(bp.width, zoom) - dpi->width;
1156 if (x > 0) {
1157 bp.width -= UnScaleByZoom(x, zoom);
1158 if (bp.width <= 0) return;
1159 }
1160
1161 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, zoom));
1162 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, zoom));
1163
1164 /* We do not want to catch the mouse. However we also use that spritenumber for unknown (text) sprites. */
1165 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && sprite_id != SPR_CURSOR_MOUSE) {
1167 void *topleft = blitter->MoveTo(bp.dst, bp.left, bp.top);
1168 void *bottomright = blitter->MoveTo(topleft, bp.width - 1, bp.height - 1);
1169
1171
1172 if (topleft <= clicked && clicked <= bottomright) {
1173 uint offset = (((size_t)clicked - (size_t)topleft) / (blitter->GetScreenDepth() / 8)) % bp.pitch;
1174 if (offset < (uint)bp.width) {
1176 }
1177 }
1178 }
1179
1180 BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, zoom);
1181}
1182
1190std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom)
1191{
1192 /* Invalid zoom level requested? */
1193 if (zoom < _settings_client.gui.zoom_min || zoom > _settings_client.gui.zoom_max) return nullptr;
1194
1196 if (blitter->GetScreenDepth() != 8 && blitter->GetScreenDepth() != 32) return nullptr;
1197
1198 /* Gather information about the sprite to write, reserve memory */
1199 const SpriteID real_sprite = GB(spriteId, 0, SPRITE_WIDTH);
1200 const Sprite *sprite = GetSprite(real_sprite, SpriteType::Normal);
1201 Dimension dim = GetSpriteSize(real_sprite, nullptr, zoom);
1202 size_t dim_size = static_cast<size_t>(dim.width) * dim.height;
1203 std::unique_ptr<uint32_t[]> result = std::make_unique<uint32_t[]>(dim_size);
1204
1205 /* Prepare new DrawPixelInfo - Normally this would be the screen but we want to draw to another buffer here.
1206 * Normally, pitch would be scaled screen width, but in our case our "screen" is only the sprite width wide. */
1207 DrawPixelInfo dpi;
1208 dpi.dst_ptr = result.get();
1209 dpi.pitch = dim.width;
1210 dpi.left = 0;
1211 dpi.top = 0;
1212 dpi.width = dim.width;
1213 dpi.height = dim.height;
1214 dpi.zoom = zoom;
1215
1216 dim_size = static_cast<size_t>(dim.width) * dim.height;
1217
1218 /* If the current blitter is a paletted blitter, we have to render to an extra buffer and resolve the palette later. */
1219 std::unique_ptr<uint8_t[]> pal_buffer{};
1220 if (blitter->GetScreenDepth() == 8) {
1221 pal_buffer = std::make_unique<uint8_t[]>(dim_size);
1222 dpi.dst_ptr = pal_buffer.get();
1223 }
1224
1225 /* Temporarily disable screen animations while blitting - This prevents 40bpp_anim from writing to the animation buffer. */
1226 Backup<bool> disable_anim(_screen_disable_anim, true);
1227 GfxBlitter<1, true>(sprite, 0, 0, BlitterMode::Normal, nullptr, real_sprite, zoom, &dpi);
1228 disable_anim.Restore();
1229
1230 if (blitter->GetScreenDepth() == 8) {
1231 /* Resolve palette. */
1232 uint32_t *dst = result.get();
1233 const uint8_t *src = pal_buffer.get();
1234 for (size_t i = 0; i < dim_size; ++i) {
1235 *dst++ = _cur_palette.palette[*src++].data;
1236 }
1237 }
1238
1239 return result;
1240}
1241
1242static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id)
1243{
1244 GfxBlitter<ZOOM_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
1245}
1246
1247static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id, ZoomLevel zoom)
1248{
1249 GfxBlitter<1, true>(sprite, x, y, mode, sub, sprite_id, zoom);
1250}
1251
1257{
1258 FontCache::ClearFontCaches(fontsizes);
1259
1260 for (FontSize fs : fontsizes) {
1261 for (uint i = 0; i != 224; i++) {
1262 _stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
1263 }
1264 }
1265}
1266
1273uint8_t GetCharacterWidth(FontSize size, char32_t key)
1274{
1275 /* Use _stringwidth_table cache if possible */
1276 if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
1277
1278 return GetGlyphWidth(size, key);
1279}
1280
1287{
1288 uint8_t width = 0;
1289 for (char c = '0'; c <= '9'; c++) {
1290 width = std::max(GetCharacterWidth(size, c), width);
1291 }
1292 return width;
1293}
1294
1302std::pair<uint8_t, uint8_t> GetBroadestDigit(FontSize size)
1303{
1304 uint8_t front = 0;
1305 uint8_t next = 0;
1306 int width = -1;
1307 for (char c = '9'; c >= '0'; c--) {
1308 int w = GetCharacterWidth(size, c);
1309 if (w <= width) continue;
1310
1311 width = w;
1312 next = c - '0';
1313 if (c != '0') front = c - '0';
1314 }
1315 return {front, next};
1316}
1317
1318void ScreenSizeChanged()
1319{
1320 _dirty_blocks_per_row = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
1321 _dirty_blocks_per_column = CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT);
1322 _dirty_blocks.resize(_dirty_blocks_per_column * _dirty_blocks_per_row);
1323
1324 /* check the dirty rect */
1325 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
1326 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
1327
1328 /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
1329 _cursor.visible = false;
1330
1331 if (VideoDriver::GetInstance() != nullptr) {
1332 if (AdjustGUIZoom(true)) ReInitAllWindows(true);
1333 }
1334}
1335
1336void UndrawMouseCursor()
1337{
1338 /* Don't undraw mouse cursor if it is handled by the video driver. */
1339 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1340
1341 /* Don't undraw the mouse cursor if the screen is not ready */
1342 if (_screen.dst_ptr == nullptr) return;
1343
1344 if (_cursor.visible) {
1346 _cursor.visible = false;
1347 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);
1348 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1349 }
1350}
1351
1352void DrawMouseCursor()
1353{
1354 /* Don't draw mouse cursor if it is handled by the video driver. */
1355 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1356
1357 /* Don't draw the mouse cursor if the screen is not ready */
1358 if (_screen.dst_ptr == nullptr) return;
1359
1361
1362 /* Redraw mouse cursor but only when it's inside the window */
1363 if (!_cursor.in_window) return;
1364
1365 /* Don't draw the mouse cursor if it's already drawn */
1366 if (_cursor.visible) {
1367 if (!_cursor.dirty) return;
1368 UndrawMouseCursor();
1369 }
1370
1371 /* Determine visible area */
1372 int left = _cursor.pos.x + _cursor.total_offs.x;
1373 int width = _cursor.total_size.x;
1374 if (left < 0) {
1375 width += left;
1376 left = 0;
1377 }
1378 if (left + width > _screen.width) {
1379 width = _screen.width - left;
1380 }
1381 if (width <= 0) return;
1382
1383 int top = _cursor.pos.y + _cursor.total_offs.y;
1384 int height = _cursor.total_size.y;
1385 if (top < 0) {
1386 height += top;
1387 top = 0;
1388 }
1389 if (top + height > _screen.height) {
1390 height = _screen.height - top;
1391 }
1392 if (height <= 0) return;
1393
1394 _cursor.draw_pos.x = left;
1395 _cursor.draw_pos.y = top;
1396 _cursor.draw_size.x = width;
1397 _cursor.draw_size.y = height;
1398
1399 uint8_t *buffer = _cursor_backup.Allocate(blitter->BufferSize(_cursor.draw_size.x, _cursor.draw_size.y));
1400
1401 /* Make backup of stuff below cursor */
1402 blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), buffer, _cursor.draw_size.x, _cursor.draw_size.y);
1403
1404 /* Draw cursor on screen */
1405 _cur_dpi = &_screen;
1406 for (const auto &cs : _cursor.sprites) {
1407 DrawSprite(cs.image.sprite, cs.image.pal, _cursor.pos.x + cs.pos.x, _cursor.pos.y + cs.pos.y);
1408 }
1409
1410 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1411
1412 _cursor.visible = true;
1413 _cursor.dirty = false;
1414}
1415
1426void RedrawScreenRect(int left, int top, int right, int bottom)
1427{
1428 assert(right <= _screen.width && bottom <= _screen.height);
1429 if (_cursor.visible) {
1430 if (right > _cursor.draw_pos.x &&
1431 left < _cursor.draw_pos.x + _cursor.draw_size.x &&
1432 bottom > _cursor.draw_pos.y &&
1433 top < _cursor.draw_pos.y + _cursor.draw_size.y) {
1434 UndrawMouseCursor();
1435 }
1436 }
1437
1439
1440 DrawOverlappedWindowForAll(left, top, right, bottom);
1441
1442 VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
1443}
1444
1453{
1454 auto is_dirty = [](auto block) -> bool { return block != 0; };
1455 auto block = _dirty_blocks.begin();
1456
1457 for (size_t x = 0; x < _dirty_blocks_per_row; ++x) {
1458 auto last_of_column = block + _dirty_blocks_per_column;
1459 for (size_t y = 0; y < _dirty_blocks_per_column; ++y, ++block) {
1460 if (!is_dirty(*block)) continue;
1461
1462 /* First try coalescing downwards */
1463 size_t height = std::find_if_not(block + 1, last_of_column, is_dirty) - block;
1464 size_t width = 1;
1465
1466 /* Clear dirty state. */
1467 std::fill_n(block, height, 0);
1468
1469 /* Try coalescing to the right too. */
1470 auto block_right = block;
1471 for (size_t x_right = x + 1; x_right < _dirty_blocks_per_row; ++x_right, ++width) {
1472 block_right += _dirty_blocks_per_column;
1473 auto last_right = block_right + height;
1474
1475 if (std::find_if_not(block_right, last_right, is_dirty) != last_right) break;
1476
1477 /* Clear dirty state. */
1478 std::fill_n(block_right, height, 0);
1479 }
1480
1481 int left = static_cast<int>(x * DIRTY_BLOCK_WIDTH);
1482 int top = static_cast<int>(y * DIRTY_BLOCK_HEIGHT);
1483 int right = left + static_cast<int>(width * DIRTY_BLOCK_WIDTH);
1484 int bottom = top + static_cast<int>(height * DIRTY_BLOCK_HEIGHT);
1485
1486 left = std::max(_invalid_rect.left, left);
1487 top = std::max(_invalid_rect.top, top);
1488 right = std::min(_invalid_rect.right, right);
1489 bottom = std::min(_invalid_rect.bottom, bottom);
1490
1491 if (left < right && top < bottom) {
1492 RedrawScreenRect(left, top, right, bottom);
1493 }
1494 }
1495 }
1496
1497 ++_dirty_block_colour;
1498 _invalid_rect.left = _screen.width;
1499 _invalid_rect.top = _screen.height;
1500 _invalid_rect.right = 0;
1501 _invalid_rect.bottom = 0;
1502}
1503
1516void AddDirtyBlock(int left, int top, int right, int bottom)
1517{
1518 if (left < 0) left = 0;
1519 if (top < 0) top = 0;
1520 if (right > _screen.width) right = _screen.width;
1521 if (bottom > _screen.height) bottom = _screen.height;
1522
1523 if (left >= right || top >= bottom) return;
1524
1525 _invalid_rect.left = std::min(_invalid_rect.left, left);
1526 _invalid_rect.top = std::min(_invalid_rect.top, top);
1527 _invalid_rect.right = std::max(_invalid_rect.right, right);
1528 _invalid_rect.bottom = std::max(_invalid_rect.bottom, bottom);
1529
1530 left /= DIRTY_BLOCK_WIDTH;
1531 top /= DIRTY_BLOCK_HEIGHT;
1532 right = CeilDiv(right, DIRTY_BLOCK_WIDTH);
1533 int height = CeilDiv(bottom, DIRTY_BLOCK_HEIGHT) - top;
1534
1535 assert(left < right && height > 0);
1536
1537 for (; left < right; ++left) {
1538 size_t offset = _dirty_blocks_per_column * left + top;
1539 std::fill_n(_dirty_blocks.begin() + offset, height, 0xFF);
1540 }
1541}
1542
1550{
1551 AddDirtyBlock(0, 0, _screen.width, _screen.height);
1552}
1553
1568bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
1569{
1571 const DrawPixelInfo *o = _cur_dpi;
1572
1573 n->zoom = ZoomLevel::Min;
1574
1575 assert(width > 0);
1576 assert(height > 0);
1577
1578 if ((left -= o->left) < 0) {
1579 width += left;
1580 if (width <= 0) return false;
1581 n->left = -left;
1582 left = 0;
1583 } else {
1584 n->left = 0;
1585 }
1586
1587 if (width > o->width - left) {
1588 width = o->width - left;
1589 if (width <= 0) return false;
1590 }
1591 n->width = width;
1592
1593 if ((top -= o->top) < 0) {
1594 height += top;
1595 if (height <= 0) return false;
1596 n->top = -top;
1597 top = 0;
1598 } else {
1599 n->top = 0;
1600 }
1601
1602 n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
1603 n->pitch = o->pitch;
1604
1605 if (height > o->height - top) {
1606 height = o->height - top;
1607 if (height <= 0) return false;
1608 }
1609 n->height = height;
1610
1611 return true;
1612}
1613
1619{
1620 /* Ignore setting any cursor before the sprites are loaded. */
1621 if (GetMaxSpriteID() == 0) return;
1622
1623 bool first = true;
1624 for (const auto &cs : _cursor.sprites) {
1625 const Sprite *p = GetSprite(GB(cs.image.sprite, 0, SPRITE_WIDTH), SpriteType::Normal);
1626 Point offs, size;
1627 offs.x = UnScaleGUI(p->x_offs) + cs.pos.x;
1628 offs.y = UnScaleGUI(p->y_offs) + cs.pos.y;
1629 size.x = UnScaleGUI(p->width);
1630 size.y = UnScaleGUI(p->height);
1631
1632 if (first) {
1633 /* First sprite sets the total. */
1634 _cursor.total_offs = offs;
1635 _cursor.total_size = size;
1636 first = false;
1637 } else {
1638 /* Additional sprites expand the total. */
1639 int right = std::max(_cursor.total_offs.x + _cursor.total_size.x, offs.x + size.x);
1640 int bottom = std::max(_cursor.total_offs.y + _cursor.total_size.y, offs.y + size.y);
1641 if (offs.x < _cursor.total_offs.x) _cursor.total_offs.x = offs.x;
1642 if (offs.y < _cursor.total_offs.y) _cursor.total_offs.y = offs.y;
1643 _cursor.total_size.x = right - _cursor.total_offs.x;
1644 _cursor.total_size.y = bottom - _cursor.total_offs.y;
1645 }
1646 }
1647
1648 _cursor.dirty = true;
1649}
1650
1656static void SetCursorSprite(CursorID cursor, PaletteID pal)
1657{
1658 if (_cursor.sprites.size() == 1 && _cursor.sprites[0].image.sprite == cursor && _cursor.sprites[0].image.pal == pal) return;
1659
1660 _cursor.sprites.clear();
1661 _cursor.sprites.emplace_back(cursor, pal, 0, 0);
1662
1664}
1665
1666static void SwitchAnimatedCursor()
1667{
1668 if (_cursor.animate_cur == std::end(_cursor.animate_list)) {
1669 _cursor.animate_cur = std::begin(_cursor.animate_list);
1670 }
1671
1672 assert(!_cursor.sprites.empty());
1673 SetCursorSprite(_cursor.animate_cur->sprite, _cursor.sprites[0].image.pal);
1674
1675 _cursor.animate_timeout = _cursor.animate_cur->display_time;
1676 ++_cursor.animate_cur;
1677}
1678
1679void CursorTick()
1680{
1681 if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0) {
1682 SwitchAnimatedCursor();
1683 }
1684}
1685
1690void SetMouseCursorBusy(bool busy)
1691{
1692 assert(!_cursor.sprites.empty());
1693 if (busy) {
1694 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
1695 } else {
1696 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
1697 }
1698}
1699
1707{
1708 /* Turn off animation */
1709 _cursor.animate_timeout = 0;
1710 /* Set cursor */
1711 SetCursorSprite(sprite, pal);
1712}
1713
1719void SetAnimatedMouseCursor(std::span<const AnimCursor> table)
1720{
1721 assert(!_cursor.sprites.empty());
1722 _cursor.animate_list = table;
1723 _cursor.animate_cur = std::end(table);
1724 _cursor.sprites[0].image.pal = PAL_NONE;
1725 SwitchAnimatedCursor();
1726}
1727
1734void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y)
1735{
1736 assert(this->fix_at);
1737
1738 this->delta.x = delta_x;
1739 this->delta.y = delta_y;
1740}
1741
1749{
1750 this->delta.x = x - this->pos.x;
1751 this->delta.y = y - this->pos.y;
1752
1753 if (this->fix_at) {
1754 return this->delta.x != 0 || this->delta.y != 0;
1755 } else if (this->pos.x != x || this->pos.y != y) {
1756 this->dirty = true;
1757 this->pos.x = x;
1758 this->pos.y = y;
1759 }
1760
1761 return false;
1762}
1763
1764bool ChangeResInGame(int width, int height)
1765{
1766 return (_screen.width == width && _screen.height == height) || VideoDriver::GetInstance()->ChangeResolution(width, height);
1767}
1768
1769bool ToggleFullScreen(bool fs)
1770{
1771 bool result = VideoDriver::GetInstance()->ToggleFullscreen(fs);
1772 if (_fullscreen != fs && _resolutions.empty()) {
1773 Debug(driver, 0, "Could not find a suitable fullscreen resolution");
1774 }
1775 return result;
1776}
1777
1778void SortResolutions()
1779{
1780 std::sort(_resolutions.begin(), _resolutions.end());
1781
1782 /* Remove any duplicates from the list. */
1783 auto last = std::unique(_resolutions.begin(), _resolutions.end());
1784 _resolutions.erase(last, _resolutions.end());
1785}
1786
1791{
1792 /* Determine real GUI zoom to use. */
1793 if (_gui_scale_cfg == -1) {
1794 /* Minimum design size of the game is 640x480. */
1795 float xs = _screen.width / 640.f;
1796 float ys = _screen.height / 480.f;
1797 int scale = std::min(xs, ys) * 100;
1798 /* Round down scaling to 25% increments and clamp to limits. */
1799 _gui_scale = Clamp((scale / 25) * 25, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1800 } else {
1801 _gui_scale = Clamp(_gui_scale_cfg, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1802 }
1803
1805 /* Font glyphs should not be clamped to min/max zoom. */
1806 _font_zoom = new_zoom;
1807 /* Ensure the gui_zoom is clamped between min/max. */
1809 _gui_zoom = new_zoom;
1810}
1811
1818bool AdjustGUIZoom(bool automatic)
1819{
1820 ZoomLevel old_gui_zoom = _gui_zoom;
1821 ZoomLevel old_font_zoom = _font_zoom;
1822 int old_scale = _gui_scale;
1823 UpdateGUIZoom();
1824 if (old_scale == _gui_scale && old_gui_zoom == _gui_zoom) return false;
1825
1826 /* Update cursors if sprite zoom level has changed. */
1827 if (old_gui_zoom != _gui_zoom) {
1830 }
1831 if (old_font_zoom != _font_zoom) {
1833 }
1836
1839
1840 /* Adjust all window sizes to match the new zoom level, so that they don't appear
1841 to move around when the application is moved to a screen with different DPI. */
1842 auto zoom_shift = old_gui_zoom - _gui_zoom;
1843 for (Window *w : Window::Iterate()) {
1844 if (automatic) {
1845 w->left = (w->left * _gui_scale) / old_scale;
1846 w->top = (w->top * _gui_scale) / old_scale;
1847 }
1848 if (w->viewport != nullptr) {
1849 w->viewport->zoom = Clamp(w->viewport->zoom - zoom_shift, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
1850 }
1851 }
1852
1853 return true;
1854}
1855
1856void ChangeGameSpeed(bool enable_fast_forward)
1857{
1858 if (enable_fast_forward) {
1860 } else {
1861 _game_speed = 100;
1862 }
1863}
void UpdateAllVirtCoords()
Update the viewport coordinates of all signs.
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.
@ CrashRemap
Perform a crash remapping.
@ BlackRemap
Perform remapping to a completely blackened sprite.
@ Normal
Perform the simple blitting.
@ TransparentRemap
Perform transparency colour remapping.
@ ColourRemap
Perform a colour remapping.
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.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:136
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.
Enum-as-bit-set wrapper.
std::pair< const_iterator, bool > insert(const Tkey &key)
Insert a key into the set, if it does not already exist.
Font cache for basic fonts.
Definition fontcache.h:22
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:53
virtual bool GetDrawGlyphShadow()=0
Do we need to draw a glyph shadow?
Container with information about a font.
Definition gfx_layout.h:97
FontCache * fc
The font we are using.
Definition gfx_layout.h:99
TextColour colour
The colour this font has to be.
Definition gfx_layout.h:100
The layouter performs all the layout work.
Definition gfx_layout.h:160
Dimension GetBounds()
Get the boundaries of this paragraph.
A single line worth of VisualRuns.
Definition gfx_layout.h:141
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:129
A reusable buffer that can be used for places that temporary allocate a bit of memory and do that ver...
const T * GetBuffer() const
Get the currently allocated buffer.
T * Allocate(size_t count)
Get buffer of at least count times T.
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 bool HasFlag(const T x, const T y)
Checks if a value in a bitset enum is set.
Factory to 'query' all available blitters.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
const Sprite * GetGlyph(FontSize size, char32_t key)
Get the Sprite for a glyph.
Definition fontcache.h:153
uint GetGlyphWidth(FontSize size, char32_t key)
Get the width of a glyph.
Definition fontcache.h:160
uint32_t GlyphID
Glyphs are characters from a font.
Definition fontcache.h:18
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:715
void SetMouseCursor(CursorID sprite, PaletteID pal)
Assign a single non-animated sprite to the cursor.
Definition gfx.cpp:1706
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:968
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1618
static void SetCursorSprite(CursorID cursor, PaletteID pal)
Switch cursor to different sprite.
Definition gfx.cpp:1656
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:1302
static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation, TextColour default_colour)
Drawing routine for drawing a laid out line of text.
Definition gfx.cpp:506
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:39
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:1066
int GetStringLineCount(std::string_view str, int maxw)
Calculates number of lines of string.
Definition gfx.cpp:739
void LoadStringWidthTable(FontSizes fontsizes)
Initialize _stringwidth_table cache for the specified font sizes.
Definition gfx.cpp:1256
bool _left_button_down
Is left mouse button pressed?
Definition gfx.cpp:41
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:897
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:173
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition gfx.cpp:935
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:668
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:38
static void SetColourRemap(TextColour colour)
Set the colour remap to be for the given colour.
Definition gfx.cpp:475
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
Definition gfx.cpp:34
bool _screen_disable_anim
Disable palette animation (important for 32bpp-anim blitter during giant screenshot)
Definition gfx.cpp:46
ZoomLevel _font_zoom
Sprite font Zoom level (not clamped)
Definition gfx.cpp:62
bool _left_button_clicked
Is left mouse button clicked?
Definition gfx.cpp:42
uint GetStringListWidth(std::span< const StringID > list, FontSize fontsize)
Get maximum width of a list of strings.
Definition gfx.cpp:920
uint16_t _game_speed
Current game-speed; 100 is 1x, 0 is infinite.
Definition gfx.cpp:40
static BlitterMode GetBlitterMode(PaletteID pal)
Helper function to get the blitter mode for different types of palettes.
Definition gfx.cpp:988
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:317
void SetMouseCursorBusy(bool busy)
Set or unset the ZZZ cursor.
Definition gfx.cpp:1690
GameSessionStats _game_session_stats
Statistics about the current session.
Definition gfx.cpp:51
void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
Draw the outline of a Rect.
Definition gfx.cpp:463
void UpdateGUIZoom()
Resolve GUI zoom level, if auto-suggestion is requested.
Definition gfx.cpp:1790
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:1034
bool _right_button_clicked
Is right mouse button clicked?
Definition gfx.cpp:44
void SetAnimatedMouseCursor(std::span< const AnimCursor > table)
Assign an animation to the cursor.
Definition gfx.cpp:1719
uint8_t GetDigitWidth(FontSize size)
Return the maximum width of single digit.
Definition gfx.cpp:1286
int _gui_scale_cfg
GUI scale in config.
Definition gfx.cpp:64
bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour 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:870
uint8_t GetCharacterWidth(FontSize size, char32_t key)
Return width of character glyph.
Definition gfx.cpp:1273
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
Draw a sprite in a viewport.
Definition gfx.cpp:1006
PauseModes _pause_mode
The current pause mode.
Definition gfx.cpp:50
void GfxFillPolygon(std::span< const Point > shape, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Fill a polygon with colour.
Definition gfx.cpp:212
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:378
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion)
Calculate string bounding box for multi-line strings.
Definition gfx.cpp:751
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition gfx.cpp:785
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:1190
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:115
static uint8_t _stringwidth_table[FS_END][224]
Cache containing width of often used characters.
Definition gfx.cpp:53
SwitchMode _switch_mode
The next mainloop command.
Definition gfx.cpp:49
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:425
ZoomLevel _gui_zoom
GUI Zoom level.
Definition gfx.cpp:61
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:1568
bool _right_button_down
Is right mouse button pressed?
Definition gfx.cpp:43
static uint8_t _string_colourremap[3]
Recoloursprite for stringdrawing. The grf loader ensures that SpriteType::Font sprites only use colou...
Definition gfx.cpp:75
bool AdjustGUIZoom(bool automatic)
Resolve GUI zoom level and adjust GUI to new zoom, if auto-suggestion is requested.
Definition gfx.cpp:1818
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
Draw single character horizontally centered around (x,y)
Definition gfx.cpp:951
int _gui_scale
GUI scale, 100 is 100%.
Definition gfx.cpp:63
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:947
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
@ Recolour
Recolour sprite.
@ Normal
The most basic (normal) sprite.
FontSize
Available font sizes.
Definition gfx_type.h:248
@ FS_MONO
Index of the monospaced font in the font tables.
Definition gfx_type.h:252
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
@ FS_LARGE
Index of the large font in the font tables.
Definition gfx_type.h:251
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:387
@ SA_TOP
Top align the text.
Definition gfx_type.h:393
@ SA_LEFT
Left align the text.
Definition gfx_type.h:388
@ SA_HOR_MASK
Mask for horizontal alignment.
Definition gfx_type.h:391
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
@ SA_VERT_MASK
Mask for vertical alignment.
Definition gfx_type.h:396
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:400
@ SA_BOTTOM
Bottom align the text.
Definition gfx_type.h:395
@ SA_VERT_CENTER
Vertically center the text.
Definition gfx_type.h:394
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
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:332
@ TC_NO_SHADE
Do not add shading to this text colour.
Definition gfx_type.h:331
@ TC_IS_PALETTE_COLOUR
Colour value is already a real palette colour index, not an index of a StringColour.
Definition gfx_type.h:330
FillRectMode
Define the operation GfxFillRect performs.
Definition gfx_type.h:344
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:346
@ FILLRECT_RECOLOUR
Apply a recolour sprite to the screen content.
Definition gfx_type.h:347
static Rect _invalid_rect
The rect for repaint.
Definition gfx.cpp:73
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:1516
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1452
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1549
void RedrawScreenRect(int left, int top, int right, int bottom)
Repaints a specific rectangle of the screen.
Definition gfx.cpp:1426
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:66
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 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
GUISettings gui
settings related to the GUI
T y
Y coordinate.
T x
X coordinate.
Collection of variables for cursor-display and -animation.
Definition gfx_type.h:123
bool visible
cursor is visible
Definition gfx_type.h:145
bool UpdateCursorPosition(int x, int y)
Update cursor position on mouse movement.
Definition gfx.cpp:1748
uint animate_timeout
in case of animated cursor, number of ticks to show the current cursor
Definition gfx_type.h:143
std::vector< CursorSprite > sprites
Sprites comprising cursor.
Definition gfx_type.h:136
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:1734
std::span< constAnimCursor >::iterator animate_cur
in case of animated cursor, current frame
Definition gfx_type.h:142
bool in_window
mouse inside this window, determines drawing logic
Definition gfx_type.h:147
Point total_size
union of sprite properties
Definition gfx_type.h:137
bool dirty
the rect occupied by the mouse is dirty (redraw)
Definition gfx_type.h:146
Point draw_size
position and size bounding-box for drawing
Definition gfx_type.h:139
Point delta
relative mouse movement in this tick
Definition gfx_type.h:126
std::span< const AnimCursor > animate_list
in case of animated cursor, list of frames
Definition gfx_type.h:141
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
uint16_t fast_forward_speed_limit
Game speed to use when fast-forward is enabled.
ZoomLevel zoom_min
minimum zoom out level
ZoomLevel zoom_max
maximum zoom out level
NewGrfDebugSpritePickerMode mode
Current state.
void * clicked_pixel
Clicked pixel (pointer to blitter buffer)
FlatSet< SpriteID > sprites
Sprites found.
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Definition gfx_type.h:374
Colour for pixel/line drawing.
Definition gfx_type.h:405
uint8_t p
Palette index.
Definition gfx_type.h:406
Specification of a rectangle with absolute coordinates of all edges.
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:933
Base of all video drivers.
Functions related to (drawing on) viewports.
void SetupWidgetDimensions()
Set up pre-scaled versions of Widget Dimensions.
Definition widget.cpp:80
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:3417
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.
@ Min
Minimum zoom level.
@ Normal
The normal zoom level.
@ In4x
Zoomed 4 times in.