OpenTTD Source 20260206-master-g4d4e37dbf1
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
35uint8_t _dirkeys;
36bool _fullscreen;
37uint8_t _support8bpp;
38CursorVars _cursor;
41uint16_t _game_speed = 100;
46DrawPixelInfo _screen;
48std::atomic<bool> _exit_game;
49GameMode _game_mode;
51PauseModes _pause_mode;
53
54static uint8_t _stringwidth_table[FS_END][224];
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: // FILLRECT_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
152 case FILLRECT_CHECKER: {
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: // FILLRECT_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;
287 case FILLRECT_CHECKER: {
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
476static void SetColourRemap(TextColour colour)
477{
478 if (colour == TC_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 & TC_NO_SHADE) != 0 || colour == TC_BLACK;
483 bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
485
486 _string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour].p;
487 _string_colourremap[2] = no_shade ? 0 : 1;
488 _colour_remap_ptr = _string_colourremap;
489}
490
507static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation, TextColour default_colour)
508{
509 if (line.CountRuns() == 0) return 0;
510
511 int w = line.GetWidth();
512 int h = line.GetLeading();
513
514 /*
515 * The following is needed for truncation.
516 * Depending on the text direction, we either remove bits at the rear
517 * or the front. For this we shift the entire area to draw so it fits
518 * within the left/right bounds and the side we do not truncate it on.
519 * Then we determine the truncation location, i.e. glyphs that fall
520 * outside of the range min_x - max_x will not be drawn; they are thus
521 * the truncated glyphs.
522 *
523 * At a later step we insert the dots.
524 */
525
526 int max_w = right - left + 1; // The maximum width.
527
528 int offset_x = 0; // The offset we need for positioning the glyphs
529 int min_x = left; // The minimum x position to draw normal glyphs on.
530 int max_x = right; // The maximum x position to draw normal glyphs on.
531
532 truncation &= max_w < w; // Whether we need to do truncation.
533 int truncation_width = 0; // Width of the ellipsis string.
534
535 std::optional<Layouter> truncation_layout;
536 if (truncation) {
537 /*
538 * Assumption may be made that all fonts of a run are of the same size.
539 * In any case, we'll use these dots for the abbreviation, so even if
540 * another size would be chosen it won't have truncated too little for
541 * the truncation dots.
542 */
543 truncation_layout.emplace(GetEllipsis(), INT32_MAX, line.GetVisualRun(0).GetFont()->fc->GetSize());
544 truncation_width = truncation_layout->GetBounds().width;
545
546 /* Is there enough space even for an ellipsis? */
547 if (max_w < truncation_width) return (_current_text_dir == TD_RTL) ? left : right;
548
549 if (_current_text_dir == TD_RTL) {
550 min_x += truncation_width;
551 offset_x = w - max_w;
552 } else {
553 max_x -= truncation_width;
554 }
555
556 w = max_w;
557 }
558
559 /* In case we have a RTL language we swap the alignment. */
560 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
561
562 /* right is the right most position to draw on. In this case we want to do
563 * calculations with the width of the string. In comparison right can be
564 * seen as lastof(todraw) and width as lengthof(todraw). They differ by 1.
565 * So most +1/-1 additions are to move from lengthof to 'indices'.
566 */
567 switch (align & SA_HOR_MASK) {
568 case SA_LEFT:
569 /* right + 1 = left + w */
570 right = left + w - 1;
571 break;
572
573 case SA_HOR_CENTER:
574 left = RoundDivSU(right + 1 + left - w, 2);
575 /* right + 1 = left + w */
576 right = left + w - 1;
577 break;
578
579 case SA_RIGHT:
580 left = right + 1 - w;
581 break;
582
583 default:
584 NOT_REACHED();
585 }
586
587 const uint shadow_offset = ScaleGUITrad(1);
588
589 auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour initial_colour) {
590 const DrawPixelInfo *dpi = _cur_dpi;
591 int dpi_left = dpi->left;
592 int dpi_right = dpi->left + dpi->width - 1;
593 TextColour last_colour = initial_colour;
594
595 for (int run_index = 0; run_index < line.CountRuns(); run_index++) {
596 const ParagraphLayouter::VisualRun &run = line.GetVisualRun(run_index);
597 const auto &glyphs = run.GetGlyphs();
598 const auto &positions = run.GetPositions();
599 const Font *f = run.GetFont();
600
601 FontCache *fc = f->fc;
602 TextColour colour = f->colour;
603 if (colour == TC_INVALID || HasFlag(initial_colour, TC_FORCED)) colour = initial_colour;
604 bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
605 /* Update the last colour for the truncation ellipsis. */
606 last_colour = colour;
607 if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
608 SetColourRemap(do_shadow ? TC_BLACK : colour);
609
610 for (int i = 0; i < run.GetGlyphCount(); i++) {
611 GlyphID glyph = glyphs[i];
612
613 /* Not a valid glyph (empty) */
614 if (glyph == 0xFFFF) continue;
615
616 int begin_x = positions[i].left + left;
617 int end_x = positions[i].right + left;
618 int top = positions[i].top + y;
619
620 /* Truncated away. */
621 if (truncation && (begin_x < min_x || end_x > max_x)) continue;
622
623 const Sprite *sprite = fc->GetGlyph(glyph);
624 /* Check clipping (the "+ 1" is for the shadow). */
625 if (begin_x + sprite->x_offs > dpi_right || begin_x + sprite->x_offs + sprite->width /* - 1 + 1 */ < dpi_left) continue;
626
627 if (do_shadow && (glyph & SPRITE_GLYPH) != 0) continue;
628
629 GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap);
630 }
631 }
632 return last_colour;
633 };
634
635 /* Draw shadow, then foreground */
636 for (bool do_shadow : {true, false}) {
637 TextColour colour = draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, default_colour);
638
639 if (truncation) {
640 int x = (_current_text_dir == TD_RTL) ? left : (right - truncation_width);
641 draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, colour);
642 }
643 }
644
645 if (underline) {
646 GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap[1]});
647 }
648
649 return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
650}
651
669int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
670{
671 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
673
674 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
675 int extra = max_height / 2;
676
677 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > top + max_height + extra ||
678 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
679 return 0;
680 }
681
682 Layouter layout(str, INT32_MAX, fontsize);
683 if (layout.empty()) return 0;
684
685 return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true, colour);
686}
687
705int DrawString(int left, int right, int top, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
706{
707 return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize);
708}
709
717int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
718{
719 assert(maxw > 0);
720 Layouter layout(str, maxw, fontsize);
721 return layout.GetBounds().height;
722}
723
730int GetStringHeight(StringID str, int maxw)
731{
732 return GetStringHeight(GetString(str), maxw);
733}
734
741int GetStringLineCount(std::string_view str, int maxw)
742{
743 Layouter layout(str, maxw);
744 return (uint)layout.size();
745}
746
754{
755 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width)};
756 return box;
757}
758
766Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize)
767{
768 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width, fontsize)};
769 return box;
770}
771
788int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
789{
790 int maxw = right - left + 1;
791 int maxh = bottom - top + 1;
792
793 /* It makes no sense to even try if it can't be drawn anyway, or
794 * do we really want to support fonts of 0 or less pixels high? */
795 if (maxh <= 0) return top;
796
797 Layouter layout(str, maxw, fontsize);
798 int total_height = layout.GetBounds().height;
799 int y;
800 switch (align & SA_VERT_MASK) {
801 case SA_TOP:
802 y = top;
803 break;
804
805 case SA_VERT_CENTER:
806 y = RoundDivSU(bottom + top - total_height, 2);
807 break;
808
809 case SA_BOTTOM:
810 y = bottom - total_height;
811 break;
812
813 default: NOT_REACHED();
814 }
815
816 int last_line = top;
817 int first_line = bottom;
818
819 for (const auto &line : layout) {
820
821 int line_height = line->GetLeading();
822 if (y >= top && y + line_height - 1 <= bottom) {
823 last_line = y + line_height;
824 if (first_line > y) first_line = y;
825
826 DrawLayoutLine(*line, y, left, right, align, underline, false, colour);
827 }
828 y += line_height;
829 }
830
831 return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line;
832}
833
850int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
851{
852 return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize);
853}
854
873bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
874{
875 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
877
878 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
879 int extra = max_height / 2;
880
881 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > bottom + extra ||
882 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
883 return false;
884 }
885
886 DrawStringMultiLine(left, right, top, bottom, str, colour, align, underline, fontsize);
887 return true;
888}
889
900Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
901{
902 Layouter layout(str, INT32_MAX, start_fontsize);
903 return layout.GetBounds();
904}
905
914{
915 return GetStringBoundingBox(GetString(strid), start_fontsize);
916}
917
924uint GetStringListWidth(std::span<const StringID> list, FontSize fontsize)
925{
926 uint width = 0;
927 for (auto str : list) {
928 width = std::max(width, GetStringBoundingBox(str, fontsize).width);
929 }
930 return width;
931}
932
939Dimension GetStringListBoundingBox(std::span<const StringID> list, FontSize fontsize)
940{
941 Dimension d{0, 0};
942 for (auto str : list) {
943 d = maxdim(d, GetStringBoundingBox(str, fontsize));
944 }
945 return d;
946}
947
955void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
956{
957 SetColourRemap(colour);
958 GfxMainBlitter(GetGlyph(FS_NORMAL, c),
959 CentreBounds(r.left, r.right, GetCharacterWidth(FS_NORMAL, c)),
960 CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
962}
963
973{
974 const Sprite *sprite = GetSprite(sprid, SpriteType::Normal);
975
976 if (offset != nullptr) {
977 offset->x = UnScaleByZoom(sprite->x_offs, zoom);
978 offset->y = UnScaleByZoom(sprite->y_offs, zoom);
979 }
980
981 Dimension d;
982 d.width = std::max<int>(0, UnScaleByZoom(sprite->x_offs + sprite->width, zoom));
983 d.height = std::max<int>(0, UnScaleByZoom(sprite->y_offs + sprite->height, zoom));
984 return d;
985}
986
993{
994 switch (pal) {
995 case PAL_NONE: return BlitterMode::Normal;
998 default: return BlitterMode::ColourRemap;
999 }
1000}
1001
1010void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
1011{
1012 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1014 pal = GB(pal, 0, PALETTE_WIDTH);
1015 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1016 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite);
1017 } else if (pal != PAL_NONE) {
1018 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1020 } else {
1021 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1022 }
1023 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite);
1024 } else {
1025 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite);
1026 }
1027}
1028
1038void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
1039{
1040 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1042 pal = GB(pal, 0, PALETTE_WIDTH);
1043 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1044 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite, zoom);
1045 } else if (pal != PAL_NONE) {
1046 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1048 } else {
1049 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1050 }
1051 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite, zoom);
1052 } else {
1053 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite, zoom);
1054 }
1055}
1056
1070template <int ZOOM_BASE, bool SCALED_XY>
1071static 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)
1072{
1073 const DrawPixelInfo *dpi = (dst != nullptr) ? dst : _cur_dpi;
1075
1076 if (SCALED_XY) {
1077 /* Scale it */
1078 x = ScaleByZoom(x, zoom);
1079 y = ScaleByZoom(y, zoom);
1080 }
1081
1082 /* Move to the correct offset */
1083 x += sprite->x_offs;
1084 y += sprite->y_offs;
1085
1086 if (sub == nullptr) {
1087 /* No clipping. */
1088 bp.skip_left = 0;
1089 bp.skip_top = 0;
1090 bp.width = UnScaleByZoom(sprite->width, zoom);
1091 bp.height = UnScaleByZoom(sprite->height, zoom);
1092 } else {
1093 /* Amount of pixels to clip from the source sprite */
1094 int clip_left = std::max(0, -sprite->x_offs + sub->left * ZOOM_BASE );
1095 int clip_top = std::max(0, -sprite->y_offs + sub->top * ZOOM_BASE );
1096 int clip_right = std::max(0, sprite->width - (-sprite->x_offs + (sub->right + 1) * ZOOM_BASE));
1097 int clip_bottom = std::max(0, sprite->height - (-sprite->y_offs + (sub->bottom + 1) * ZOOM_BASE));
1098
1099 if (clip_left + clip_right >= sprite->width) return;
1100 if (clip_top + clip_bottom >= sprite->height) return;
1101
1102 bp.skip_left = UnScaleByZoomLower(clip_left, zoom);
1103 bp.skip_top = UnScaleByZoomLower(clip_top, zoom);
1104 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, zoom);
1105 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, zoom);
1106
1107 x += ScaleByZoom(bp.skip_left, zoom);
1108 y += ScaleByZoom(bp.skip_top, zoom);
1109 }
1110
1111 /* Copy the main data directly from the sprite */
1112 bp.sprite = sprite->data;
1113 bp.sprite_width = sprite->width;
1114 bp.sprite_height = sprite->height;
1115 bp.top = 0;
1116 bp.left = 0;
1117
1118 bp.dst = dpi->dst_ptr;
1119 bp.pitch = dpi->pitch;
1120 bp.remap = _colour_remap_ptr;
1121
1122 assert(sprite->width > 0);
1123 assert(sprite->height > 0);
1124
1125 if (bp.width <= 0) return;
1126 if (bp.height <= 0) return;
1127
1128 y -= SCALED_XY ? ScaleByZoom(dpi->top, zoom) : dpi->top;
1129 int y_unscaled = UnScaleByZoom(y, zoom);
1130 /* Check for top overflow */
1131 if (y < 0) {
1132 bp.height -= -y_unscaled;
1133 if (bp.height <= 0) return;
1134 bp.skip_top += -y_unscaled;
1135 y = 0;
1136 } else {
1137 bp.top = y_unscaled;
1138 }
1139
1140 /* Check for bottom overflow */
1141 y += SCALED_XY ? ScaleByZoom(bp.height - dpi->height, zoom) : ScaleByZoom(bp.height, zoom) - dpi->height;
1142 if (y > 0) {
1143 bp.height -= UnScaleByZoom(y, zoom);
1144 if (bp.height <= 0) return;
1145 }
1146
1147 x -= SCALED_XY ? ScaleByZoom(dpi->left, zoom) : dpi->left;
1148 int x_unscaled = UnScaleByZoom(x, zoom);
1149 /* Check for left overflow */
1150 if (x < 0) {
1151 bp.width -= -x_unscaled;
1152 if (bp.width <= 0) return;
1153 bp.skip_left += -x_unscaled;
1154 x = 0;
1155 } else {
1156 bp.left = x_unscaled;
1157 }
1158
1159 /* Check for right overflow */
1160 x += SCALED_XY ? ScaleByZoom(bp.width - dpi->width, zoom) : ScaleByZoom(bp.width, zoom) - dpi->width;
1161 if (x > 0) {
1162 bp.width -= UnScaleByZoom(x, zoom);
1163 if (bp.width <= 0) return;
1164 }
1165
1166 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, zoom));
1167 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, zoom));
1168
1169 /* We do not want to catch the mouse. However we also use that spritenumber for unknown (text) sprites. */
1170 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && sprite_id != SPR_CURSOR_MOUSE) {
1172 void *topleft = blitter->MoveTo(bp.dst, bp.left, bp.top);
1173 void *bottomright = blitter->MoveTo(topleft, bp.width - 1, bp.height - 1);
1174
1175 void *clicked = _newgrf_debug_sprite_picker.clicked_pixel;
1176
1177 if (topleft <= clicked && clicked <= bottomright) {
1178 uint offset = (((size_t)clicked - (size_t)topleft) / (blitter->GetScreenDepth() / 8)) % bp.pitch;
1179 if (offset < (uint)bp.width) {
1180 _newgrf_debug_sprite_picker.sprites.insert(sprite_id);
1181 }
1182 }
1183 }
1184
1185 BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, zoom);
1186}
1187
1195std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom)
1196{
1197 /* Invalid zoom level requested? */
1198 if (zoom < _settings_client.gui.zoom_min || zoom > _settings_client.gui.zoom_max) return nullptr;
1199
1201 if (blitter->GetScreenDepth() != 8 && blitter->GetScreenDepth() != 32) return nullptr;
1202
1203 /* Gather information about the sprite to write, reserve memory */
1204 const SpriteID real_sprite = GB(spriteId, 0, SPRITE_WIDTH);
1205 const Sprite *sprite = GetSprite(real_sprite, SpriteType::Normal);
1206 Dimension dim = GetSpriteSize(real_sprite, nullptr, zoom);
1207 size_t dim_size = static_cast<size_t>(dim.width) * dim.height;
1208 std::unique_ptr<uint32_t[]> result = std::make_unique<uint32_t[]>(dim_size);
1209
1210 /* Prepare new DrawPixelInfo - Normally this would be the screen but we want to draw to another buffer here.
1211 * Normally, pitch would be scaled screen width, but in our case our "screen" is only the sprite width wide. */
1212 DrawPixelInfo dpi;
1213 dpi.dst_ptr = result.get();
1214 dpi.pitch = dim.width;
1215 dpi.left = 0;
1216 dpi.top = 0;
1217 dpi.width = dim.width;
1218 dpi.height = dim.height;
1219 dpi.zoom = zoom;
1220
1221 dim_size = static_cast<size_t>(dim.width) * dim.height;
1222
1223 /* If the current blitter is a paletted blitter, we have to render to an extra buffer and resolve the palette later. */
1224 std::unique_ptr<uint8_t[]> pal_buffer{};
1225 if (blitter->GetScreenDepth() == 8) {
1226 pal_buffer = std::make_unique<uint8_t[]>(dim_size);
1227 dpi.dst_ptr = pal_buffer.get();
1228 }
1229
1230 /* Temporarily disable screen animations while blitting - This prevents 40bpp_anim from writing to the animation buffer. */
1231 Backup<bool> disable_anim(_screen_disable_anim, true);
1232 GfxBlitter<1, true>(sprite, 0, 0, BlitterMode::Normal, nullptr, real_sprite, zoom, &dpi);
1233 disable_anim.Restore();
1234
1235 if (blitter->GetScreenDepth() == 8) {
1236 /* Resolve palette. */
1237 uint32_t *dst = result.get();
1238 const uint8_t *src = pal_buffer.get();
1239 for (size_t i = 0; i < dim_size; ++i) {
1240 *dst++ = _cur_palette.palette[*src++].data;
1241 }
1242 }
1243
1244 return result;
1245}
1246
1247static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id)
1248{
1249 GfxBlitter<ZOOM_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
1250}
1251
1252static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id, ZoomLevel zoom)
1253{
1254 GfxBlitter<1, true>(sprite, x, y, mode, sub, sprite_id, zoom);
1255}
1256
1261void LoadStringWidthTable(FontSizes fontsizes)
1262{
1263 FontCache::ClearFontCaches(fontsizes);
1264
1265 for (FontSize fs : fontsizes) {
1266 for (uint i = 0; i != 224; i++) {
1267 _stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
1268 }
1269 }
1270}
1271
1278uint8_t GetCharacterWidth(FontSize size, char32_t key)
1279{
1280 /* Use _stringwidth_table cache if possible */
1281 if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
1282
1283 return GetGlyphWidth(size, key);
1284}
1285
1292{
1293 uint8_t width = 0;
1294 for (char c = '0'; c <= '9'; c++) {
1295 width = std::max(GetCharacterWidth(size, c), width);
1296 }
1297 return width;
1298}
1299
1307std::pair<uint8_t, uint8_t> GetBroadestDigit(FontSize size)
1308{
1309 uint8_t front = 0;
1310 uint8_t next = 0;
1311 int width = -1;
1312 for (char c = '9'; c >= '0'; c--) {
1313 int w = GetCharacterWidth(size, c);
1314 if (w <= width) continue;
1315
1316 width = w;
1317 next = c - '0';
1318 if (c != '0') front = c - '0';
1319 }
1320 return {front, next};
1321}
1322
1323void ScreenSizeChanged()
1324{
1325 _dirty_blocks_per_row = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
1326 _dirty_blocks_per_column = CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT);
1327 _dirty_blocks.resize(_dirty_blocks_per_column * _dirty_blocks_per_row);
1328
1329 /* check the dirty rect */
1330 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
1331 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
1332
1333 /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
1334 _cursor.visible = false;
1335
1336 if (VideoDriver::GetInstance() != nullptr) {
1337 if (AdjustGUIZoom(true)) ReInitAllWindows(true);
1338 }
1339}
1340
1341void UndrawMouseCursor()
1342{
1343 /* Don't undraw mouse cursor if it is handled by the video driver. */
1344 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1345
1346 /* Don't undraw the mouse cursor if the screen is not ready */
1347 if (_screen.dst_ptr == nullptr) return;
1348
1349 if (_cursor.visible) {
1351 _cursor.visible = false;
1352 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);
1353 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1354 }
1355}
1356
1357void DrawMouseCursor()
1358{
1359 /* Don't draw mouse cursor if it is handled by the video driver. */
1360 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1361
1362 /* Don't draw the mouse cursor if the screen is not ready */
1363 if (_screen.dst_ptr == nullptr) return;
1364
1366
1367 /* Redraw mouse cursor but only when it's inside the window */
1368 if (!_cursor.in_window) return;
1369
1370 /* Don't draw the mouse cursor if it's already drawn */
1371 if (_cursor.visible) {
1372 if (!_cursor.dirty) return;
1373 UndrawMouseCursor();
1374 }
1375
1376 /* Determine visible area */
1377 int left = _cursor.pos.x + _cursor.total_offs.x;
1378 int width = _cursor.total_size.x;
1379 if (left < 0) {
1380 width += left;
1381 left = 0;
1382 }
1383 if (left + width > _screen.width) {
1384 width = _screen.width - left;
1385 }
1386 if (width <= 0) return;
1387
1388 int top = _cursor.pos.y + _cursor.total_offs.y;
1389 int height = _cursor.total_size.y;
1390 if (top < 0) {
1391 height += top;
1392 top = 0;
1393 }
1394 if (top + height > _screen.height) {
1395 height = _screen.height - top;
1396 }
1397 if (height <= 0) return;
1398
1399 _cursor.draw_pos.x = left;
1400 _cursor.draw_pos.y = top;
1401 _cursor.draw_size.x = width;
1402 _cursor.draw_size.y = height;
1403
1404 uint8_t *buffer = _cursor_backup.Allocate(blitter->BufferSize(_cursor.draw_size.x, _cursor.draw_size.y));
1405
1406 /* Make backup of stuff below cursor */
1407 blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), buffer, _cursor.draw_size.x, _cursor.draw_size.y);
1408
1409 /* Draw cursor on screen */
1410 _cur_dpi = &_screen;
1411 for (const auto &cs : _cursor.sprites) {
1412 DrawSprite(cs.image.sprite, cs.image.pal, _cursor.pos.x + cs.pos.x, _cursor.pos.y + cs.pos.y);
1413 }
1414
1415 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1416
1417 _cursor.visible = true;
1418 _cursor.dirty = false;
1419}
1420
1431void RedrawScreenRect(int left, int top, int right, int bottom)
1432{
1433 assert(right <= _screen.width && bottom <= _screen.height);
1434 if (_cursor.visible) {
1435 if (right > _cursor.draw_pos.x &&
1436 left < _cursor.draw_pos.x + _cursor.draw_size.x &&
1437 bottom > _cursor.draw_pos.y &&
1438 top < _cursor.draw_pos.y + _cursor.draw_size.y) {
1439 UndrawMouseCursor();
1440 }
1441 }
1442
1444
1445 DrawOverlappedWindowForAll(left, top, right, bottom);
1446
1447 VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
1448}
1449
1458{
1459 auto is_dirty = [](auto block) -> bool { return block != 0; };
1460 auto block = _dirty_blocks.begin();
1461
1462 for (size_t x = 0; x < _dirty_blocks_per_row; ++x) {
1463 auto last_of_column = block + _dirty_blocks_per_column;
1464 for (size_t y = 0; y < _dirty_blocks_per_column; ++y, ++block) {
1465 if (!is_dirty(*block)) continue;
1466
1467 /* First try coalescing downwards */
1468 size_t height = std::find_if_not(block + 1, last_of_column, is_dirty) - block;
1469 size_t width = 1;
1470
1471 /* Clear dirty state. */
1472 std::fill_n(block, height, 0);
1473
1474 /* Try coalescing to the right too. */
1475 auto block_right = block;
1476 for (size_t x_right = x + 1; x_right < _dirty_blocks_per_row; ++x_right, ++width) {
1477 block_right += _dirty_blocks_per_column;
1478 auto last_right = block_right + height;
1479
1480 if (std::find_if_not(block_right, last_right, is_dirty) != last_right) break;
1481
1482 /* Clear dirty state. */
1483 std::fill_n(block_right, height, 0);
1484 }
1485
1486 int left = static_cast<int>(x * DIRTY_BLOCK_WIDTH);
1487 int top = static_cast<int>(y * DIRTY_BLOCK_HEIGHT);
1488 int right = left + static_cast<int>(width * DIRTY_BLOCK_WIDTH);
1489 int bottom = top + static_cast<int>(height * DIRTY_BLOCK_HEIGHT);
1490
1491 left = std::max(_invalid_rect.left, left);
1492 top = std::max(_invalid_rect.top, top);
1493 right = std::min(_invalid_rect.right, right);
1494 bottom = std::min(_invalid_rect.bottom, bottom);
1495
1496 if (left < right && top < bottom) {
1497 RedrawScreenRect(left, top, right, bottom);
1498 }
1499 }
1500 }
1501
1502 ++_dirty_block_colour;
1503 _invalid_rect.left = _screen.width;
1504 _invalid_rect.top = _screen.height;
1505 _invalid_rect.right = 0;
1506 _invalid_rect.bottom = 0;
1507}
1508
1521void AddDirtyBlock(int left, int top, int right, int bottom)
1522{
1523 if (left < 0) left = 0;
1524 if (top < 0) top = 0;
1525 if (right > _screen.width) right = _screen.width;
1526 if (bottom > _screen.height) bottom = _screen.height;
1527
1528 if (left >= right || top >= bottom) return;
1529
1530 _invalid_rect.left = std::min(_invalid_rect.left, left);
1531 _invalid_rect.top = std::min(_invalid_rect.top, top);
1532 _invalid_rect.right = std::max(_invalid_rect.right, right);
1533 _invalid_rect.bottom = std::max(_invalid_rect.bottom, bottom);
1534
1535 left /= DIRTY_BLOCK_WIDTH;
1536 top /= DIRTY_BLOCK_HEIGHT;
1537 right = CeilDiv(right, DIRTY_BLOCK_WIDTH);
1538 int height = CeilDiv(bottom, DIRTY_BLOCK_HEIGHT) - top;
1539
1540 assert(left < right && height > 0);
1541
1542 for (; left < right; ++left) {
1543 size_t offset = _dirty_blocks_per_column * left + top;
1544 std::fill_n(_dirty_blocks.begin() + offset, height, 0xFF);
1545 }
1546}
1547
1555{
1556 AddDirtyBlock(0, 0, _screen.width, _screen.height);
1557}
1558
1573bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
1574{
1576 const DrawPixelInfo *o = _cur_dpi;
1577
1578 n->zoom = ZoomLevel::Min;
1579
1580 assert(width > 0);
1581 assert(height > 0);
1582
1583 if ((left -= o->left) < 0) {
1584 width += left;
1585 if (width <= 0) return false;
1586 n->left = -left;
1587 left = 0;
1588 } else {
1589 n->left = 0;
1590 }
1591
1592 if (width > o->width - left) {
1593 width = o->width - left;
1594 if (width <= 0) return false;
1595 }
1596 n->width = width;
1597
1598 if ((top -= o->top) < 0) {
1599 height += top;
1600 if (height <= 0) return false;
1601 n->top = -top;
1602 top = 0;
1603 } else {
1604 n->top = 0;
1605 }
1606
1607 n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
1608 n->pitch = o->pitch;
1609
1610 if (height > o->height - top) {
1611 height = o->height - top;
1612 if (height <= 0) return false;
1613 }
1614 n->height = height;
1615
1616 return true;
1617}
1618
1624{
1625 /* Ignore setting any cursor before the sprites are loaded. */
1626 if (GetMaxSpriteID() == 0) return;
1627
1628 bool first = true;
1629 for (const auto &cs : _cursor.sprites) {
1630 const Sprite *p = GetSprite(GB(cs.image.sprite, 0, SPRITE_WIDTH), SpriteType::Normal);
1631 Point offs, size;
1632 offs.x = UnScaleGUI(p->x_offs) + cs.pos.x;
1633 offs.y = UnScaleGUI(p->y_offs) + cs.pos.y;
1634 size.x = UnScaleGUI(p->width);
1635 size.y = UnScaleGUI(p->height);
1636
1637 if (first) {
1638 /* First sprite sets the total. */
1639 _cursor.total_offs = offs;
1640 _cursor.total_size = size;
1641 first = false;
1642 } else {
1643 /* Additional sprites expand the total. */
1644 int right = std::max(_cursor.total_offs.x + _cursor.total_size.x, offs.x + size.x);
1645 int bottom = std::max(_cursor.total_offs.y + _cursor.total_size.y, offs.y + size.y);
1646 if (offs.x < _cursor.total_offs.x) _cursor.total_offs.x = offs.x;
1647 if (offs.y < _cursor.total_offs.y) _cursor.total_offs.y = offs.y;
1648 _cursor.total_size.x = right - _cursor.total_offs.x;
1649 _cursor.total_size.y = bottom - _cursor.total_offs.y;
1650 }
1651 }
1652
1653 _cursor.dirty = true;
1654}
1655
1661static void SetCursorSprite(CursorID cursor, PaletteID pal)
1662{
1663 if (_cursor.sprites.size() == 1 && _cursor.sprites[0].image.sprite == cursor && _cursor.sprites[0].image.pal == pal) return;
1664
1665 _cursor.sprites.clear();
1666 _cursor.sprites.emplace_back(cursor, pal, 0, 0);
1667
1669}
1670
1671static void SwitchAnimatedCursor()
1672{
1673 if (_cursor.animate_cur == std::end(_cursor.animate_list)) {
1674 _cursor.animate_cur = std::begin(_cursor.animate_list);
1675 }
1676
1677 assert(!_cursor.sprites.empty());
1678 SetCursorSprite(_cursor.animate_cur->sprite, _cursor.sprites[0].image.pal);
1679
1680 _cursor.animate_timeout = _cursor.animate_cur->display_time;
1681 ++_cursor.animate_cur;
1682}
1683
1684void CursorTick()
1685{
1686 if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0) {
1687 SwitchAnimatedCursor();
1688 }
1689}
1690
1695void SetMouseCursorBusy(bool busy)
1696{
1697 assert(!_cursor.sprites.empty());
1698 if (busy) {
1699 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
1700 } else {
1701 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
1702 }
1703}
1704
1712{
1713 /* Turn off animation */
1714 _cursor.animate_timeout = 0;
1715 /* Set cursor */
1716 SetCursorSprite(sprite, pal);
1717}
1718
1724void SetAnimatedMouseCursor(std::span<const AnimCursor> table)
1725{
1726 assert(!_cursor.sprites.empty());
1727 _cursor.animate_list = table;
1728 _cursor.animate_cur = std::end(table);
1729 _cursor.sprites[0].image.pal = PAL_NONE;
1730 SwitchAnimatedCursor();
1731}
1732
1739{
1740 if ((icon & ANIMCURSOR_FLAG) != 0) {
1742 } else {
1743 SetMouseCursor(icon, pal);
1744 }
1745}
1746
1753void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y)
1754{
1755 assert(this->fix_at);
1756
1757 this->delta.x = delta_x;
1758 this->delta.y = delta_y;
1759}
1760
1768{
1769 this->delta.x = x - this->pos.x;
1770 this->delta.y = y - this->pos.y;
1771
1772 if (this->fix_at) {
1773 return this->delta.x != 0 || this->delta.y != 0;
1774 } else if (this->pos.x != x || this->pos.y != y) {
1775 this->dirty = true;
1776 this->pos.x = x;
1777 this->pos.y = y;
1778 }
1779
1780 return false;
1781}
1782
1783bool ChangeResInGame(int width, int height)
1784{
1785 return (_screen.width == width && _screen.height == height) || VideoDriver::GetInstance()->ChangeResolution(width, height);
1786}
1787
1788bool ToggleFullScreen(bool fs)
1789{
1790 bool result = VideoDriver::GetInstance()->ToggleFullscreen(fs);
1791 if (_fullscreen != fs && _resolutions.empty()) {
1792 Debug(driver, 0, "Could not find a suitable fullscreen resolution");
1793 }
1794 return result;
1795}
1796
1797void SortResolutions()
1798{
1799 std::sort(_resolutions.begin(), _resolutions.end());
1800
1801 /* Remove any duplicates from the list. */
1802 auto last = std::unique(_resolutions.begin(), _resolutions.end());
1803 _resolutions.erase(last, _resolutions.end());
1804}
1805
1810{
1811 /* Determine real GUI zoom to use. */
1812 if (_gui_scale_cfg == -1) {
1813 /* Minimum design size of the game is 640x480. */
1814 float xs = _screen.width / 640.f;
1815 float ys = _screen.height / 480.f;
1816 int scale = std::min(xs, ys) * 100;
1817 /* Round down scaling to 25% increments and clamp to limits. */
1818 _gui_scale = Clamp((scale / 25) * 25, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1819 } else {
1820 _gui_scale = Clamp(_gui_scale_cfg, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1821 }
1822
1824 /* Font glyphs should not be clamped to min/max zoom. */
1825 _font_zoom = new_zoom;
1826 /* Ensure the gui_zoom is clamped between min/max. */
1827 new_zoom = Clamp(new_zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
1828 _gui_zoom = new_zoom;
1829}
1830
1837bool AdjustGUIZoom(bool automatic)
1838{
1839 if (VideoDriver::GetInstance() == nullptr) return false;
1840
1841 ZoomLevel old_gui_zoom = _gui_zoom;
1842 ZoomLevel old_font_zoom = _font_zoom;
1843 int old_scale = _gui_scale;
1844 UpdateGUIZoom();
1845 if (old_scale == _gui_scale && old_gui_zoom == _gui_zoom) return false;
1846
1847 /* Update cursors if sprite zoom level has changed. */
1848 if (old_gui_zoom != _gui_zoom) {
1851 }
1852 if (old_font_zoom != _font_zoom) {
1854 }
1857
1860
1861 /* Adjust all window sizes to match the new zoom level, so that they don't appear
1862 to move around when the application is moved to a screen with different DPI. */
1863 auto zoom_shift = old_gui_zoom - _gui_zoom;
1864 for (Window *w : Window::Iterate()) {
1865 if (automatic) {
1866 w->left = (w->left * _gui_scale) / old_scale;
1867 w->top = (w->top * _gui_scale) / old_scale;
1868 }
1869 if (w->viewport != nullptr) {
1870 w->viewport->zoom = Clamp(w->viewport->zoom - zoom_shift, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
1871 }
1872 }
1873
1874 return true;
1875}
1876
1877void ChangeGameSpeed(bool enable_fast_forward)
1878{
1879 if (enable_fast_forward) {
1880 _game_speed = _settings_client.gui.fast_forward_speed_limit;
1881 } else {
1882 _game_speed = 100;
1883 }
1884}
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.
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.
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...
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
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:717
void SetMouseCursor(CursorID sprite, PaletteID pal)
Assign a single non-animated sprite to the cursor.
Definition gfx.cpp:1711
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:972
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1623
static void SetCursorSprite(CursorID cursor, PaletteID pal)
Switch cursor to different sprite.
Definition gfx.cpp:1661
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:1307
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:507
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:1071
int GetStringLineCount(std::string_view str, int maxw)
Calculates number of lines of string.
Definition gfx.cpp:741
void LoadStringWidthTable(FontSizes fontsizes)
Initialize _stringwidth_table cache for the specified font sizes.
Definition gfx.cpp:1261
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:900
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:939
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:669
bool _ctrl_pressed
Is Ctrl pressed?
Definition gfx.cpp:39
static void SetColourRemap(TextColour colour)
Set the colour remap to be for the given colour.
Definition gfx.cpp:476
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
Definition gfx.cpp:35
bool _screen_disable_anim
Disable palette animation (important for 32bpp-anim blitter during giant screenshot).
Definition gfx.cpp:47
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:924
uint16_t _game_speed
Current game-speed; 100 is 1x, 0 is infinite.
Definition gfx.cpp:41
static BlitterMode GetBlitterMode(PaletteID pal)
Helper function to get the blitter mode for different types of palettes.
Definition gfx.cpp:992
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:1695
void SetCursor(CursorID icon, PaletteID pal)
Assign an animation or a non-animated sprite to the cursor.
Definition gfx.cpp:1738
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:1809
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:1038
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:1724
uint8_t GetDigitWidth(FontSize size)
Return the maximum width of single digit.
Definition gfx.cpp:1291
int _gui_scale_cfg
GUI scale in config.
Definition gfx.cpp:65
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:873
uint8_t GetCharacterWidth(FontSize size, char32_t key)
Return width of character glyph.
Definition gfx.cpp:1278
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
Draw a sprite in a viewport.
Definition gfx.cpp:1010
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:753
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:788
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:1195
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 uint8_t _stringwidth_table[FS_END][224]
Cache containing width of often used characters.
Definition gfx.cpp:54
SwitchMode _switch_mode
The next mainloop command.
Definition gfx.cpp:50
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
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:1573
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:1837
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
Draw single character horizontally centered around (x,y).
Definition gfx.cpp:955
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:948
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.
Definition gfx_type.h:361
@ Normal
The most basic (normal) sprite.
Definition gfx_type.h:358
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: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:1521
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1457
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1554
void RedrawScreenRect(int left, int top, int right, int bottom)
Repaints a specific rectangle of the screen.
Definition gfx.cpp:1431
#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: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 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:1767
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:1753
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
Colour for pixel/line drawing.
Definition gfx_type.h:405
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:936
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:3427
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