OpenTTD Source 20250924-master-gbec4e71d53
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 <http://www.gnu.org/licenses/>.
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"
27#include "viewport_func.h"
28
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;
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 /* Imagine that the line is infinitely long and it intersects with
346 * infinitely long left and right edges of the clipping rectangle.
347 * If both intersection points are outside the clipping rectangle
348 * and both on the same side of it, we don't need to draw anything. */
349 int left_isec_y = y + (clip.left - x) * grade_y / grade_x;
350 int right_isec_y = y + (clip.right - x) * grade_y / grade_x;
351 if ((left_isec_y > clip.bottom + margin && right_isec_y > clip.bottom + margin) ||
352 (left_isec_y < clip.top - margin && right_isec_y < clip.top - margin)) {
353 return;
354 }
355
356 /* It is possible to use the line equation to further reduce the amount of
357 * work the blitter has to do by shortening the effective line segment.
358 * However, in order to get that right and prevent the flickering effects
359 * of rounding errors so much additional code has to be run here that in
360 * the general case the effect is not noticeable. */
361
362 blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash);
363}
364
376static inline bool GfxPreprocessLine(DrawPixelInfo *dpi, int &x, int &y, int &x2, int &y2, int width)
377{
378 x -= dpi->left;
379 x2 -= dpi->left;
380 y -= dpi->top;
381 y2 -= dpi->top;
382
383 /* Check simple clipping */
384 if (x + width / 2 < 0 && x2 + width / 2 < 0 ) return false;
385 if (y + width / 2 < 0 && y2 + width / 2 < 0 ) return false;
386 if (x - width / 2 > dpi->width && x2 - width / 2 > dpi->width ) return false;
387 if (y - width / 2 > dpi->height && y2 - width / 2 > dpi->height) return false;
388 return true;
389}
390
391void GfxDrawLine(int x, int y, int x2, int y2, PixelColour colour, int width, int dash)
392{
393 DrawPixelInfo *dpi = _cur_dpi;
394 if (GfxPreprocessLine(dpi, x, y, x2, y2, width)) {
395 GfxDoDrawLine(dpi->dst_ptr, x, y, x2, y2, dpi->width, dpi->height, colour, width, dash);
396 }
397}
398
399void GfxDrawLineUnscaled(int x, int y, int x2, int y2, PixelColour colour)
400{
401 DrawPixelInfo *dpi = _cur_dpi;
402 if (GfxPreprocessLine(dpi, x, y, x2, y2, 1)) {
403 GfxDoDrawLine(dpi->dst_ptr,
404 UnScaleByZoom(x, dpi->zoom), UnScaleByZoom(y, dpi->zoom),
405 UnScaleByZoom(x2, dpi->zoom), UnScaleByZoom(y2, dpi->zoom),
406 UnScaleByZoom(dpi->width, dpi->zoom), UnScaleByZoom(dpi->height, dpi->zoom), colour, 1);
407 }
408}
409
423void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
424{
425 /* ....
426 * .. ....
427 * .. ....
428 * .. ^
429 * <--__(dx1,dy1) /(dx2,dy2)
430 * : --__ / :
431 * : --__ / :
432 * : *(x,y) :
433 * : | :
434 * : | ..
435 * .... |(dx3,dy3)
436 * .... | ..
437 * ....V.
438 */
439
440 static constexpr PixelColour colour = PC_WHITE;
441
442 GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
443 GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
444 GfxDrawLineUnscaled(x, y, x + dx3, y + dy3, colour);
445
446 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx2, y + dy1 + dy2, colour);
447 GfxDrawLineUnscaled(x + dx1, y + dy1, x + dx1 + dx3, y + dy1 + dy3, colour);
448 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx1, y + dy2 + dy1, colour);
449 GfxDrawLineUnscaled(x + dx2, y + dy2, x + dx2 + dx3, y + dy2 + dy3, colour);
450 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx1, y + dy3 + dy1, colour);
451 GfxDrawLineUnscaled(x + dx3, y + dy3, x + dx3 + dx2, y + dy3 + dy2, colour);
452}
453
461void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash)
462{
463 GfxDrawLine(r.left, r.top, r.right, r.top, colour, width, dash);
464 GfxDrawLine(r.left, r.top, r.left, r.bottom, colour, width, dash);
465 GfxDrawLine(r.right, r.top, r.right, r.bottom, colour, width, dash);
466 GfxDrawLine(r.left, r.bottom, r.right, r.bottom, colour, width, dash);
467}
468
473static void SetColourRemap(TextColour colour)
474{
475 if (colour == TC_INVALID) return;
476
477 /* Black strings have no shading ever; the shading is black, so it
478 * would be invisible at best, but it actually makes it illegible. */
479 bool no_shade = (colour & TC_NO_SHADE) != 0 || colour == TC_BLACK;
480 bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
482
483 _string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour].p;
484 _string_colourremap[2] = no_shade ? 0 : 1;
485 _colour_remap_ptr = _string_colourremap;
486}
487
504static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation, TextColour default_colour)
505{
506 if (line.CountRuns() == 0) return 0;
507
508 int w = line.GetWidth();
509 int h = line.GetLeading();
510
511 /*
512 * The following is needed for truncation.
513 * Depending on the text direction, we either remove bits at the rear
514 * or the front. For this we shift the entire area to draw so it fits
515 * within the left/right bounds and the side we do not truncate it on.
516 * Then we determine the truncation location, i.e. glyphs that fall
517 * outside of the range min_x - max_x will not be drawn; they are thus
518 * the truncated glyphs.
519 *
520 * At a later step we insert the dots.
521 */
522
523 int max_w = right - left + 1; // The maximum width.
524
525 int offset_x = 0; // The offset we need for positioning the glyphs
526 int min_x = left; // The minimum x position to draw normal glyphs on.
527 int max_x = right; // The maximum x position to draw normal glyphs on.
528
529 truncation &= max_w < w; // Whether we need to do truncation.
530 int truncation_width = 0; // Width of the ellipsis string.
531
532 std::optional<Layouter> truncation_layout;
533 if (truncation) {
534 /*
535 * Assumption may be made that all fonts of a run are of the same size.
536 * In any case, we'll use these dots for the abbreviation, so even if
537 * another size would be chosen it won't have truncated too little for
538 * the truncation dots.
539 */
540 truncation_layout.emplace(GetEllipsis(), INT32_MAX, line.GetVisualRun(0).GetFont()->fc->GetSize());
541 truncation_width = truncation_layout->GetBounds().width;
542
543 /* Is there enough space even for an ellipsis? */
544 if (max_w < truncation_width) return (_current_text_dir == TD_RTL) ? left : right;
545
546 if (_current_text_dir == TD_RTL) {
547 min_x += truncation_width;
548 offset_x = w - max_w;
549 } else {
550 max_x -= truncation_width;
551 }
552
553 w = max_w;
554 }
555
556 /* In case we have a RTL language we swap the alignment. */
557 if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
558
559 /* right is the right most position to draw on. In this case we want to do
560 * calculations with the width of the string. In comparison right can be
561 * seen as lastof(todraw) and width as lengthof(todraw). They differ by 1.
562 * So most +1/-1 additions are to move from lengthof to 'indices'.
563 */
564 switch (align & SA_HOR_MASK) {
565 case SA_LEFT:
566 /* right + 1 = left + w */
567 right = left + w - 1;
568 break;
569
570 case SA_HOR_CENTER:
571 left = RoundDivSU(right + 1 + left - w, 2);
572 /* right + 1 = left + w */
573 right = left + w - 1;
574 break;
575
576 case SA_RIGHT:
577 left = right + 1 - w;
578 break;
579
580 default:
581 NOT_REACHED();
582 }
583
584 const uint shadow_offset = ScaleGUITrad(1);
585
586 auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour initial_colour) {
587 const DrawPixelInfo *dpi = _cur_dpi;
588 int dpi_left = dpi->left;
589 int dpi_right = dpi->left + dpi->width - 1;
590 TextColour last_colour = initial_colour;
591
592 for (int run_index = 0; run_index < line.CountRuns(); run_index++) {
593 const ParagraphLayouter::VisualRun &run = line.GetVisualRun(run_index);
594 const auto &glyphs = run.GetGlyphs();
595 const auto &positions = run.GetPositions();
596 const Font *f = run.GetFont();
597
598 FontCache *fc = f->fc;
599 TextColour colour = f->colour;
600 if (colour == TC_INVALID || HasFlag(initial_colour, TC_FORCED)) colour = initial_colour;
601 bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
602 /* Update the last colour for the truncation ellipsis. */
603 last_colour = colour;
604 if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
605 SetColourRemap(do_shadow ? TC_BLACK : colour);
606
607 for (int i = 0; i < run.GetGlyphCount(); i++) {
608 GlyphID glyph = glyphs[i];
609
610 /* Not a valid glyph (empty) */
611 if (glyph == 0xFFFF) continue;
612
613 int begin_x = positions[i].left + left;
614 int end_x = positions[i].right + left;
615 int top = positions[i].top + y;
616
617 /* Truncated away. */
618 if (truncation && (begin_x < min_x || end_x > max_x)) continue;
619
620 const Sprite *sprite = fc->GetGlyph(glyph);
621 /* Check clipping (the "+ 1" is for the shadow). */
622 if (begin_x + sprite->x_offs > dpi_right || begin_x + sprite->x_offs + sprite->width /* - 1 + 1 */ < dpi_left) continue;
623
624 if (do_shadow && (glyph & SPRITE_GLYPH) != 0) continue;
625
626 GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap);
627 }
628 }
629 return last_colour;
630 };
631
632 /* Draw shadow, then foreground */
633 for (bool do_shadow : {true, false}) {
634 TextColour colour = draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, default_colour);
635
636 if (truncation) {
637 int x = (_current_text_dir == TD_RTL) ? left : (right - truncation_width);
638 draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, colour);
639 }
640 }
641
642 if (underline) {
643 GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap[1]});
644 }
645
646 return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
647}
648
666int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
667{
668 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
670
671 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
672 int extra = max_height / 2;
673
674 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > top + max_height + extra ||
675 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
676 return 0;
677 }
678
679 Layouter layout(str, INT32_MAX, fontsize);
680 if (layout.empty()) return 0;
681
682 return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true, colour);
683}
684
702int DrawString(int left, int right, int top, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
703{
704 return DrawString(left, right, top, GetString(str), colour, align, underline, fontsize);
705}
706
713int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
714{
715 assert(maxw > 0);
716 Layouter layout(str, maxw, fontsize);
717 return layout.GetBounds().height;
718}
719
726int GetStringHeight(StringID str, int maxw)
727{
728 return GetStringHeight(GetString(str), maxw);
729}
730
737int GetStringLineCount(std::string_view str, int maxw)
738{
739 Layouter layout(str, maxw);
740 return (uint)layout.size();
741}
742
750{
751 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width)};
752 return box;
753}
754
761Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize)
762{
763 Dimension box = {suggestion.width, (uint)GetStringHeight(str, suggestion.width, fontsize)};
764 return box;
765}
766
783int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
784{
785 int maxw = right - left + 1;
786 int maxh = bottom - top + 1;
787
788 /* It makes no sense to even try if it can't be drawn anyway, or
789 * do we really want to support fonts of 0 or less pixels high? */
790 if (maxh <= 0) return top;
791
792 Layouter layout(str, maxw, fontsize);
793 int total_height = layout.GetBounds().height;
794 int y;
795 switch (align & SA_VERT_MASK) {
796 case SA_TOP:
797 y = top;
798 break;
799
800 case SA_VERT_CENTER:
801 y = RoundDivSU(bottom + top - total_height, 2);
802 break;
803
804 case SA_BOTTOM:
805 y = bottom - total_height;
806 break;
807
808 default: NOT_REACHED();
809 }
810
811 int last_line = top;
812 int first_line = bottom;
813
814 for (const auto &line : layout) {
815
816 int line_height = line->GetLeading();
817 if (y >= top && y + line_height - 1 <= bottom) {
818 last_line = y + line_height;
819 if (first_line > y) first_line = y;
820
821 DrawLayoutLine(*line, y, left, right, align, underline, false, colour);
822 }
823 y += line_height;
824 }
825
826 return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line;
827}
828
845int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
846{
847 return DrawStringMultiLine(left, right, top, bottom, GetString(str), colour, align, underline, fontsize);
848}
849
868bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
869{
870 /* The string may contain control chars to change the font, just use the biggest font for clipping. */
872
873 /* Funny glyphs may extent outside the usual bounds, so relax the clipping somewhat. */
874 int extra = max_height / 2;
875
876 if (_cur_dpi->top + _cur_dpi->height + extra < top || _cur_dpi->top > bottom + extra ||
877 _cur_dpi->left + _cur_dpi->width + extra < left || _cur_dpi->left > right + extra) {
878 return false;
879 }
880
881 DrawStringMultiLine(left, right, top, bottom, str, colour, align, underline, fontsize);
882 return true;
883}
884
895Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
896{
897 Layouter layout(str, INT32_MAX, start_fontsize);
898 return layout.GetBounds();
899}
900
908{
909 return GetStringBoundingBox(GetString(strid), start_fontsize);
910}
911
918uint GetStringListWidth(std::span<const StringID> list, FontSize fontsize)
919{
920 uint width = 0;
921 for (auto str : list) {
922 width = std::max(width, GetStringBoundingBox(str, fontsize).width);
923 }
924 return width;
925}
926
933Dimension GetStringListBoundingBox(std::span<const StringID> list, FontSize fontsize)
934{
935 Dimension d{0, 0};
936 for (auto str : list) {
937 d = maxdim(d, GetStringBoundingBox(str, fontsize));
938 }
939 return d;
940}
941
949void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
950{
951 SetColourRemap(colour);
952 GfxMainBlitter(GetGlyph(FS_NORMAL, c),
953 CentreBounds(r.left, r.right, GetCharacterWidth(FS_NORMAL, c)),
954 CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)),
956}
957
967{
968 const Sprite *sprite = GetSprite(sprid, SpriteType::Normal);
969
970 if (offset != nullptr) {
971 offset->x = UnScaleByZoom(sprite->x_offs, zoom);
972 offset->y = UnScaleByZoom(sprite->y_offs, zoom);
973 }
974
975 Dimension d;
976 d.width = std::max<int>(0, UnScaleByZoom(sprite->x_offs + sprite->width, zoom));
977 d.height = std::max<int>(0, UnScaleByZoom(sprite->y_offs + sprite->height, zoom));
978 return d;
979}
980
987{
988 switch (pal) {
989 case PAL_NONE: return BlitterMode::Normal;
992 default: return BlitterMode::ColourRemap;
993 }
994}
995
1004void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
1005{
1006 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1008 pal = GB(pal, 0, PALETTE_WIDTH);
1009 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1010 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite);
1011 } else if (pal != PAL_NONE) {
1012 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1014 } else {
1015 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1016 }
1017 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite);
1018 } else {
1019 GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite);
1020 }
1021}
1022
1032void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
1033{
1034 SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH);
1036 pal = GB(pal, 0, PALETTE_WIDTH);
1037 _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1;
1038 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite, zoom);
1039 } else if (pal != PAL_NONE) {
1040 if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) {
1042 } else {
1043 _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1;
1044 }
1045 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite, zoom);
1046 } else {
1047 GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite, zoom);
1048 }
1049}
1050
1063template <int ZOOM_BASE, bool SCALED_XY>
1064static 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)
1065{
1066 const DrawPixelInfo *dpi = (dst != nullptr) ? dst : _cur_dpi;
1068
1069 if (SCALED_XY) {
1070 /* Scale it */
1071 x = ScaleByZoom(x, zoom);
1072 y = ScaleByZoom(y, zoom);
1073 }
1074
1075 /* Move to the correct offset */
1076 x += sprite->x_offs;
1077 y += sprite->y_offs;
1078
1079 if (sub == nullptr) {
1080 /* No clipping. */
1081 bp.skip_left = 0;
1082 bp.skip_top = 0;
1083 bp.width = UnScaleByZoom(sprite->width, zoom);
1084 bp.height = UnScaleByZoom(sprite->height, zoom);
1085 } else {
1086 /* Amount of pixels to clip from the source sprite */
1087 int clip_left = std::max(0, -sprite->x_offs + sub->left * ZOOM_BASE );
1088 int clip_top = std::max(0, -sprite->y_offs + sub->top * ZOOM_BASE );
1089 int clip_right = std::max(0, sprite->width - (-sprite->x_offs + (sub->right + 1) * ZOOM_BASE));
1090 int clip_bottom = std::max(0, sprite->height - (-sprite->y_offs + (sub->bottom + 1) * ZOOM_BASE));
1091
1092 if (clip_left + clip_right >= sprite->width) return;
1093 if (clip_top + clip_bottom >= sprite->height) return;
1094
1095 bp.skip_left = UnScaleByZoomLower(clip_left, zoom);
1096 bp.skip_top = UnScaleByZoomLower(clip_top, zoom);
1097 bp.width = UnScaleByZoom(sprite->width - clip_left - clip_right, zoom);
1098 bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, zoom);
1099
1100 x += ScaleByZoom(bp.skip_left, zoom);
1101 y += ScaleByZoom(bp.skip_top, zoom);
1102 }
1103
1104 /* Copy the main data directly from the sprite */
1105 bp.sprite = sprite->data;
1106 bp.sprite_width = sprite->width;
1107 bp.sprite_height = sprite->height;
1108 bp.top = 0;
1109 bp.left = 0;
1110
1111 bp.dst = dpi->dst_ptr;
1112 bp.pitch = dpi->pitch;
1113 bp.remap = _colour_remap_ptr;
1114
1115 assert(sprite->width > 0);
1116 assert(sprite->height > 0);
1117
1118 if (bp.width <= 0) return;
1119 if (bp.height <= 0) return;
1120
1121 y -= SCALED_XY ? ScaleByZoom(dpi->top, zoom) : dpi->top;
1122 int y_unscaled = UnScaleByZoom(y, zoom);
1123 /* Check for top overflow */
1124 if (y < 0) {
1125 bp.height -= -y_unscaled;
1126 if (bp.height <= 0) return;
1127 bp.skip_top += -y_unscaled;
1128 y = 0;
1129 } else {
1130 bp.top = y_unscaled;
1131 }
1132
1133 /* Check for bottom overflow */
1134 y += SCALED_XY ? ScaleByZoom(bp.height - dpi->height, zoom) : ScaleByZoom(bp.height, zoom) - dpi->height;
1135 if (y > 0) {
1136 bp.height -= UnScaleByZoom(y, zoom);
1137 if (bp.height <= 0) return;
1138 }
1139
1140 x -= SCALED_XY ? ScaleByZoom(dpi->left, zoom) : dpi->left;
1141 int x_unscaled = UnScaleByZoom(x, zoom);
1142 /* Check for left overflow */
1143 if (x < 0) {
1144 bp.width -= -x_unscaled;
1145 if (bp.width <= 0) return;
1146 bp.skip_left += -x_unscaled;
1147 x = 0;
1148 } else {
1149 bp.left = x_unscaled;
1150 }
1151
1152 /* Check for right overflow */
1153 x += SCALED_XY ? ScaleByZoom(bp.width - dpi->width, zoom) : ScaleByZoom(bp.width, zoom) - dpi->width;
1154 if (x > 0) {
1155 bp.width -= UnScaleByZoom(x, zoom);
1156 if (bp.width <= 0) return;
1157 }
1158
1159 assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, zoom));
1160 assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, zoom));
1161
1162 /* We do not want to catch the mouse. However we also use that spritenumber for unknown (text) sprites. */
1163 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && sprite_id != SPR_CURSOR_MOUSE) {
1165 void *topleft = blitter->MoveTo(bp.dst, bp.left, bp.top);
1166 void *bottomright = blitter->MoveTo(topleft, bp.width - 1, bp.height - 1);
1167
1169
1170 if (topleft <= clicked && clicked <= bottomright) {
1171 uint offset = (((size_t)clicked - (size_t)topleft) / (blitter->GetScreenDepth() / 8)) % bp.pitch;
1172 if (offset < (uint)bp.width) {
1174 }
1175 }
1176 }
1177
1178 BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, zoom);
1179}
1180
1188std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom)
1189{
1190 /* Invalid zoom level requested? */
1191 if (zoom < _settings_client.gui.zoom_min || zoom > _settings_client.gui.zoom_max) return nullptr;
1192
1194 if (blitter->GetScreenDepth() != 8 && blitter->GetScreenDepth() != 32) return nullptr;
1195
1196 /* Gather information about the sprite to write, reserve memory */
1197 const SpriteID real_sprite = GB(spriteId, 0, SPRITE_WIDTH);
1198 const Sprite *sprite = GetSprite(real_sprite, SpriteType::Normal);
1199 Dimension dim = GetSpriteSize(real_sprite, nullptr, zoom);
1200 size_t dim_size = static_cast<size_t>(dim.width) * dim.height;
1201 std::unique_ptr<uint32_t[]> result = std::make_unique<uint32_t[]>(dim_size);
1202
1203 /* Prepare new DrawPixelInfo - Normally this would be the screen but we want to draw to another buffer here.
1204 * Normally, pitch would be scaled screen width, but in our case our "screen" is only the sprite width wide. */
1205 DrawPixelInfo dpi;
1206 dpi.dst_ptr = result.get();
1207 dpi.pitch = dim.width;
1208 dpi.left = 0;
1209 dpi.top = 0;
1210 dpi.width = dim.width;
1211 dpi.height = dim.height;
1212 dpi.zoom = zoom;
1213
1214 dim_size = static_cast<size_t>(dim.width) * dim.height;
1215
1216 /* If the current blitter is a paletted blitter, we have to render to an extra buffer and resolve the palette later. */
1217 std::unique_ptr<uint8_t[]> pal_buffer{};
1218 if (blitter->GetScreenDepth() == 8) {
1219 pal_buffer = std::make_unique<uint8_t[]>(dim_size);
1220 dpi.dst_ptr = pal_buffer.get();
1221 }
1222
1223 /* Temporarily disable screen animations while blitting - This prevents 40bpp_anim from writing to the animation buffer. */
1224 Backup<bool> disable_anim(_screen_disable_anim, true);
1225 GfxBlitter<1, true>(sprite, 0, 0, BlitterMode::Normal, nullptr, real_sprite, zoom, &dpi);
1226 disable_anim.Restore();
1227
1228 if (blitter->GetScreenDepth() == 8) {
1229 /* Resolve palette. */
1230 uint32_t *dst = result.get();
1231 const uint8_t *src = pal_buffer.get();
1232 for (size_t i = 0; i < dim_size; ++i) {
1233 *dst++ = _cur_palette.palette[*src++].data;
1234 }
1235 }
1236
1237 return result;
1238}
1239
1240static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id)
1241{
1242 GfxBlitter<ZOOM_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
1243}
1244
1245static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id, ZoomLevel zoom)
1246{
1247 GfxBlitter<1, true>(sprite, x, y, mode, sub, sprite_id, zoom);
1248}
1249
1255{
1256 FontCache::ClearFontCaches(fontsizes);
1257
1258 for (FontSize fs : fontsizes) {
1259 for (uint i = 0; i != 224; i++) {
1260 _stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
1261 }
1262 }
1263}
1264
1271uint8_t GetCharacterWidth(FontSize size, char32_t key)
1272{
1273 /* Use _stringwidth_table cache if possible */
1274 if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
1275
1276 return GetGlyphWidth(size, key);
1277}
1278
1285{
1286 uint8_t width = 0;
1287 for (char c = '0'; c <= '9'; c++) {
1288 width = std::max(GetCharacterWidth(size, c), width);
1289 }
1290 return width;
1291}
1292
1300std::pair<uint8_t, uint8_t> GetBroadestDigit(FontSize size)
1301{
1302 uint8_t front = 0;
1303 uint8_t next = 0;
1304 int width = -1;
1305 for (char c = '9'; c >= '0'; c--) {
1306 int w = GetCharacterWidth(size, c);
1307 if (w <= width) continue;
1308
1309 width = w;
1310 next = c - '0';
1311 if (c != '0') front = c - '0';
1312 }
1313 return {front, next};
1314}
1315
1316void ScreenSizeChanged()
1317{
1318 _dirty_blocks_per_row = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
1319 _dirty_blocks_per_column = CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT);
1320 _dirty_blocks.resize(_dirty_blocks_per_column * _dirty_blocks_per_row);
1321
1322 /* check the dirty rect */
1323 if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
1324 if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
1325
1326 /* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
1327 _cursor.visible = false;
1328
1329 if (VideoDriver::GetInstance() != nullptr) {
1330 if (AdjustGUIZoom(true)) ReInitAllWindows(true);
1331 }
1332}
1333
1334void UndrawMouseCursor()
1335{
1336 /* Don't undraw mouse cursor if it is handled by the video driver. */
1337 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1338
1339 /* Don't undraw the mouse cursor if the screen is not ready */
1340 if (_screen.dst_ptr == nullptr) return;
1341
1342 if (_cursor.visible) {
1344 _cursor.visible = false;
1345 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);
1346 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1347 }
1348}
1349
1350void DrawMouseCursor()
1351{
1352 /* Don't draw mouse cursor if it is handled by the video driver. */
1353 if (VideoDriver::GetInstance()->UseSystemCursor()) return;
1354
1355 /* Don't draw the mouse cursor if the screen is not ready */
1356 if (_screen.dst_ptr == nullptr) return;
1357
1359
1360 /* Redraw mouse cursor but only when it's inside the window */
1361 if (!_cursor.in_window) return;
1362
1363 /* Don't draw the mouse cursor if it's already drawn */
1364 if (_cursor.visible) {
1365 if (!_cursor.dirty) return;
1366 UndrawMouseCursor();
1367 }
1368
1369 /* Determine visible area */
1370 int left = _cursor.pos.x + _cursor.total_offs.x;
1371 int width = _cursor.total_size.x;
1372 if (left < 0) {
1373 width += left;
1374 left = 0;
1375 }
1376 if (left + width > _screen.width) {
1377 width = _screen.width - left;
1378 }
1379 if (width <= 0) return;
1380
1381 int top = _cursor.pos.y + _cursor.total_offs.y;
1382 int height = _cursor.total_size.y;
1383 if (top < 0) {
1384 height += top;
1385 top = 0;
1386 }
1387 if (top + height > _screen.height) {
1388 height = _screen.height - top;
1389 }
1390 if (height <= 0) return;
1391
1392 _cursor.draw_pos.x = left;
1393 _cursor.draw_pos.y = top;
1394 _cursor.draw_size.x = width;
1395 _cursor.draw_size.y = height;
1396
1397 uint8_t *buffer = _cursor_backup.Allocate(blitter->BufferSize(_cursor.draw_size.x, _cursor.draw_size.y));
1398
1399 /* Make backup of stuff below cursor */
1400 blitter->CopyToBuffer(blitter->MoveTo(_screen.dst_ptr, _cursor.draw_pos.x, _cursor.draw_pos.y), buffer, _cursor.draw_size.x, _cursor.draw_size.y);
1401
1402 /* Draw cursor on screen */
1403 _cur_dpi = &_screen;
1404 for (const auto &cs : _cursor.sprites) {
1405 DrawSprite(cs.image.sprite, cs.image.pal, _cursor.pos.x + cs.pos.x, _cursor.pos.y + cs.pos.y);
1406 }
1407
1408 VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
1409
1410 _cursor.visible = true;
1411 _cursor.dirty = false;
1412}
1413
1424void RedrawScreenRect(int left, int top, int right, int bottom)
1425{
1426 assert(right <= _screen.width && bottom <= _screen.height);
1427 if (_cursor.visible) {
1428 if (right > _cursor.draw_pos.x &&
1429 left < _cursor.draw_pos.x + _cursor.draw_size.x &&
1430 bottom > _cursor.draw_pos.y &&
1431 top < _cursor.draw_pos.y + _cursor.draw_size.y) {
1432 UndrawMouseCursor();
1433 }
1434 }
1435
1437
1438 DrawOverlappedWindowForAll(left, top, right, bottom);
1439
1440 VideoDriver::GetInstance()->MakeDirty(left, top, right - left, bottom - top);
1441}
1442
1451{
1452 auto is_dirty = [](auto block) -> bool { return block != 0; };
1453 auto block = _dirty_blocks.begin();
1454
1455 for (size_t x = 0; x < _dirty_blocks_per_row; ++x) {
1456 auto last_of_column = block + _dirty_blocks_per_column;
1457 for (size_t y = 0; y < _dirty_blocks_per_column; ++y, ++block) {
1458 if (!is_dirty(*block)) continue;
1459
1460 /* First try coalescing downwards */
1461 size_t height = std::find_if_not(block + 1, last_of_column, is_dirty) - block;
1462 size_t width = 1;
1463
1464 /* Clear dirty state. */
1465 std::fill_n(block, height, 0);
1466
1467 /* Try coalescing to the right too. */
1468 auto block_right = block;
1469 for (size_t x_right = x + 1; x_right < _dirty_blocks_per_row; ++x_right, ++width) {
1470 block_right += _dirty_blocks_per_column;
1471 auto last_right = block_right + height;
1472
1473 if (std::find_if_not(block_right, last_right, is_dirty) != last_right) break;
1474
1475 /* Clear dirty state. */
1476 std::fill_n(block_right, height, 0);
1477 }
1478
1479 int left = static_cast<int>(x * DIRTY_BLOCK_WIDTH);
1480 int top = static_cast<int>(y * DIRTY_BLOCK_HEIGHT);
1481 int right = left + static_cast<int>(width * DIRTY_BLOCK_WIDTH);
1482 int bottom = top + static_cast<int>(height * DIRTY_BLOCK_HEIGHT);
1483
1484 left = std::max(_invalid_rect.left, left);
1485 top = std::max(_invalid_rect.top, top);
1486 right = std::min(_invalid_rect.right, right);
1487 bottom = std::min(_invalid_rect.bottom, bottom);
1488
1489 if (left < right && top < bottom) {
1490 RedrawScreenRect(left, top, right, bottom);
1491 }
1492 }
1493 }
1494
1495 ++_dirty_block_colour;
1496 _invalid_rect.left = _screen.width;
1497 _invalid_rect.top = _screen.height;
1498 _invalid_rect.right = 0;
1499 _invalid_rect.bottom = 0;
1500}
1501
1514void AddDirtyBlock(int left, int top, int right, int bottom)
1515{
1516 if (left < 0) left = 0;
1517 if (top < 0) top = 0;
1518 if (right > _screen.width) right = _screen.width;
1519 if (bottom > _screen.height) bottom = _screen.height;
1520
1521 if (left >= right || top >= bottom) return;
1522
1523 _invalid_rect.left = std::min(_invalid_rect.left, left);
1524 _invalid_rect.top = std::min(_invalid_rect.top, top);
1525 _invalid_rect.right = std::max(_invalid_rect.right, right);
1526 _invalid_rect.bottom = std::max(_invalid_rect.bottom, bottom);
1527
1528 left /= DIRTY_BLOCK_WIDTH;
1529 top /= DIRTY_BLOCK_HEIGHT;
1530 right = CeilDiv(right, DIRTY_BLOCK_WIDTH);
1531 int height = CeilDiv(bottom, DIRTY_BLOCK_HEIGHT) - top;
1532
1533 assert(left < right && height > 0);
1534
1535 for (; left < right; ++left) {
1536 size_t offset = _dirty_blocks_per_column * left + top;
1537 std::fill_n(_dirty_blocks.begin() + offset, height, 0xFF);
1538 }
1539}
1540
1548{
1549 AddDirtyBlock(0, 0, _screen.width, _screen.height);
1550}
1551
1566bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int height)
1567{
1569 const DrawPixelInfo *o = _cur_dpi;
1570
1571 n->zoom = ZoomLevel::Min;
1572
1573 assert(width > 0);
1574 assert(height > 0);
1575
1576 if ((left -= o->left) < 0) {
1577 width += left;
1578 if (width <= 0) return false;
1579 n->left = -left;
1580 left = 0;
1581 } else {
1582 n->left = 0;
1583 }
1584
1585 if (width > o->width - left) {
1586 width = o->width - left;
1587 if (width <= 0) return false;
1588 }
1589 n->width = width;
1590
1591 if ((top -= o->top) < 0) {
1592 height += top;
1593 if (height <= 0) return false;
1594 n->top = -top;
1595 top = 0;
1596 } else {
1597 n->top = 0;
1598 }
1599
1600 n->dst_ptr = blitter->MoveTo(o->dst_ptr, left, top);
1601 n->pitch = o->pitch;
1602
1603 if (height > o->height - top) {
1604 height = o->height - top;
1605 if (height <= 0) return false;
1606 }
1607 n->height = height;
1608
1609 return true;
1610}
1611
1617{
1618 /* Ignore setting any cursor before the sprites are loaded. */
1619 if (GetMaxSpriteID() == 0) return;
1620
1621 bool first = true;
1622 for (const auto &cs : _cursor.sprites) {
1623 const Sprite *p = GetSprite(GB(cs.image.sprite, 0, SPRITE_WIDTH), SpriteType::Normal);
1624 Point offs, size;
1625 offs.x = UnScaleGUI(p->x_offs) + cs.pos.x;
1626 offs.y = UnScaleGUI(p->y_offs) + cs.pos.y;
1627 size.x = UnScaleGUI(p->width);
1628 size.y = UnScaleGUI(p->height);
1629
1630 if (first) {
1631 /* First sprite sets the total. */
1632 _cursor.total_offs = offs;
1633 _cursor.total_size = size;
1634 first = false;
1635 } else {
1636 /* Additional sprites expand the total. */
1637 int right = std::max(_cursor.total_offs.x + _cursor.total_size.x, offs.x + size.x);
1638 int bottom = std::max(_cursor.total_offs.y + _cursor.total_size.y, offs.y + size.y);
1639 if (offs.x < _cursor.total_offs.x) _cursor.total_offs.x = offs.x;
1640 if (offs.y < _cursor.total_offs.y) _cursor.total_offs.y = offs.y;
1641 _cursor.total_size.x = right - _cursor.total_offs.x;
1642 _cursor.total_size.y = bottom - _cursor.total_offs.y;
1643 }
1644 }
1645
1646 _cursor.dirty = true;
1647}
1648
1654static void SetCursorSprite(CursorID cursor, PaletteID pal)
1655{
1656 if (_cursor.sprites.size() == 1 && _cursor.sprites[0].image.sprite == cursor && _cursor.sprites[0].image.pal == pal) return;
1657
1658 _cursor.sprites.clear();
1659 _cursor.sprites.emplace_back(cursor, pal, 0, 0);
1660
1662}
1663
1664static void SwitchAnimatedCursor()
1665{
1666 if (_cursor.animate_cur == std::end(_cursor.animate_list)) {
1667 _cursor.animate_cur = std::begin(_cursor.animate_list);
1668 }
1669
1670 assert(!_cursor.sprites.empty());
1671 SetCursorSprite(_cursor.animate_cur->sprite, _cursor.sprites[0].image.pal);
1672
1673 _cursor.animate_timeout = _cursor.animate_cur->display_time;
1674 ++_cursor.animate_cur;
1675}
1676
1677void CursorTick()
1678{
1679 if (_cursor.animate_timeout != 0 && --_cursor.animate_timeout == 0) {
1680 SwitchAnimatedCursor();
1681 }
1682}
1683
1688void SetMouseCursorBusy(bool busy)
1689{
1690 assert(!_cursor.sprites.empty());
1691 if (busy) {
1692 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
1693 } else {
1694 if (_cursor.sprites[0].image.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
1695 }
1696}
1697
1705{
1706 /* Turn off animation */
1707 _cursor.animate_timeout = 0;
1708 /* Set cursor */
1709 SetCursorSprite(sprite, pal);
1710}
1711
1717void SetAnimatedMouseCursor(std::span<const AnimCursor> table)
1718{
1719 assert(!_cursor.sprites.empty());
1720 _cursor.animate_list = table;
1721 _cursor.animate_cur = std::end(table);
1722 _cursor.sprites[0].image.pal = PAL_NONE;
1723 SwitchAnimatedCursor();
1724}
1725
1732void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y)
1733{
1734 assert(this->fix_at);
1735
1736 this->delta.x = delta_x;
1737 this->delta.y = delta_y;
1738}
1739
1747{
1748 this->delta.x = x - this->pos.x;
1749 this->delta.y = y - this->pos.y;
1750
1751 if (this->fix_at) {
1752 return this->delta.x != 0 || this->delta.y != 0;
1753 } else if (this->pos.x != x || this->pos.y != y) {
1754 this->dirty = true;
1755 this->pos.x = x;
1756 this->pos.y = y;
1757 }
1758
1759 return false;
1760}
1761
1762bool ChangeResInGame(int width, int height)
1763{
1764 return (_screen.width == width && _screen.height == height) || VideoDriver::GetInstance()->ChangeResolution(width, height);
1765}
1766
1767bool ToggleFullScreen(bool fs)
1768{
1769 bool result = VideoDriver::GetInstance()->ToggleFullscreen(fs);
1770 if (_fullscreen != fs && _resolutions.empty()) {
1771 Debug(driver, 0, "Could not find a suitable fullscreen resolution");
1772 }
1773 return result;
1774}
1775
1776void SortResolutions()
1777{
1778 std::sort(_resolutions.begin(), _resolutions.end());
1779
1780 /* Remove any duplicates from the list. */
1781 auto last = std::unique(_resolutions.begin(), _resolutions.end());
1782 _resolutions.erase(last, _resolutions.end());
1783}
1784
1789{
1790 /* Determine real GUI zoom to use. */
1791 if (_gui_scale_cfg == -1) {
1792 /* Minimum design size of the game is 640x480. */
1793 float xs = _screen.width / 640.f;
1794 float ys = _screen.height / 480.f;
1795 int scale = std::min(xs, ys) * 100;
1796 /* Round down scaling to 25% increments and clamp to limits. */
1797 _gui_scale = Clamp((scale / 25) * 25, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1798 } else {
1799 _gui_scale = Clamp(_gui_scale_cfg, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
1800 }
1801
1803 /* Font glyphs should not be clamped to min/max zoom. */
1804 _font_zoom = new_zoom;
1805 /* Ensure the gui_zoom is clamped between min/max. */
1807 _gui_zoom = new_zoom;
1808}
1809
1816bool AdjustGUIZoom(bool automatic)
1817{
1818 ZoomLevel old_gui_zoom = _gui_zoom;
1819 ZoomLevel old_font_zoom = _font_zoom;
1820 int old_scale = _gui_scale;
1821 UpdateGUIZoom();
1822 if (old_scale == _gui_scale && old_gui_zoom == _gui_zoom) return false;
1823
1824 /* Update cursors if sprite zoom level has changed. */
1825 if (old_gui_zoom != _gui_zoom) {
1828 }
1829 if (old_font_zoom != _font_zoom) {
1831 }
1834
1837
1838 /* Adjust all window sizes to match the new zoom level, so that they don't appear
1839 to move around when the application is moved to a screen with different DPI. */
1840 auto zoom_shift = old_gui_zoom - _gui_zoom;
1841 for (Window *w : Window::Iterate()) {
1842 if (automatic) {
1843 w->left = (w->left * _gui_scale) / old_scale;
1844 w->top = (w->top * _gui_scale) / old_scale;
1845 }
1846 if (w->viewport != nullptr) {
1847 w->viewport->zoom = Clamp(w->viewport->zoom - zoom_shift, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
1848 }
1849 }
1850
1851 return true;
1852}
1853
1854void ChangeGameSpeed(bool enable_fast_forward)
1855{
1856 if (enable_fast_forward) {
1858 } else {
1859 _game_speed = 100;
1860 }
1861}
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.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
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
Some simple functions to help with accessing containers.
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
debug_inline 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:713
void SetMouseCursor(CursorID sprite, PaletteID pal)
Assign a single non-animated sprite to the cursor.
Definition gfx.cpp:1704
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:966
void UpdateCursorSize()
Update cursor dimension.
Definition gfx.cpp:1616
static void SetCursorSprite(CursorID cursor, PaletteID pal)
Switch cursor to different sprite.
Definition gfx.cpp:1654
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:1300
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:504
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:1064
int GetStringLineCount(std::string_view str, int maxw)
Calculates number of lines of string.
Definition gfx.cpp:737
void LoadStringWidthTable(FontSizes fontsizes)
Initialize _stringwidth_table cache for the specified font sizes.
Definition gfx.cpp:1254
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:895
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:933
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:666
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:473
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:918
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:986
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:1688
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:461
void UpdateGUIZoom()
Resolve GUI zoom level, if auto-suggestion is requested.
Definition gfx.cpp:1788
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:1032
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:1717
uint8_t GetDigitWidth(FontSize size)
Return the maximum width of single digit.
Definition gfx.cpp:1284
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:868
uint8_t GetCharacterWidth(FontSize size, char32_t key)
Return width of character glyph.
Definition gfx.cpp:1271
void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub)
Draw a sprite in a viewport.
Definition gfx.cpp:1004
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:376
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion)
Calculate string bounding box for multi-line strings.
Definition gfx.cpp:749
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:783
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:1188
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:423
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:1566
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:1816
void DrawCharCentered(char32_t c, const Rect &r, TextColour colour)
Draw single character horizontally centered around (x,y)
Definition gfx.cpp:949
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:945
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: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:1514
void DrawDirtyBlocks()
Repaints the rectangle blocks which are marked as 'dirty'.
Definition gfx.cpp:1450
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1547
void RedrawScreenRect(int left, int top, int right, int bottom)
Repaints a specific rectangle of the screen.
Definition gfx.cpp:1424
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr int RoundDivSU(int a, uint b)
Computes round(a / b) for signed a and unsigned b.
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool _networking
are we in networking mode?
Definition network.cpp:67
Basic functions/variables used all over the place.
void NetworkUndrawChatMessage()
Hide the chatbox.
Network functions used by other parts of OpenTTD.
Functions/types related to NewGRF debugging.
NewGrfDebugSpritePicker _newgrf_debug_sprite_picker
The sprite picker.
GameMode
Mode which defines the state of the game.
Definition openttd.h:18
SwitchMode
Mode which defines what mode we're switching to.
Definition openttd.h:26
static constexpr PixelColour PC_WHITE
White palette colour.
Functions related to modal progress.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Types related to global configuration settings.
void GfxClearFontSpriteCache()
Remove all encoded font sprites from the sprite cache without discarding sprite location information.
SpriteID GetMaxSpriteID()
Get a reasonable (upper bound) estimate of the maximum SpriteID used in OpenTTD; there will be no spr...
This file contains all sprite-related enums and defines.
static const PaletteID PALETTE_ALL_BLACK
Exchange any color by black, needed for painting fictive tiles outside map.
Definition sprites.h:1617
static constexpr uint8_t PALETTE_WIDTH
number of bits of the sprite containing the recolour palette
Definition sprites.h:1540
static constexpr uint8_t PALETTE_MODIFIER_TRANSPARENT
when a sprite is to be displayed transparently, this bit needs to be set.
Definition sprites.h:1553
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1611
static const CursorID SPR_CURSOR_MOUSE
Cursor sprite numbers.
Definition sprites.h:1396
static constexpr uint8_t PALETTE_TEXT_RECOLOUR
Set if palette is actually a magic text recolour.
Definition sprites.h:1538
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1541
static const PaletteID PALETTE_TO_TRANSPARENT
This sets the sprite to transparent.
Definition sprites.h:1608
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:1746
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:1732
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:273
AllWindows< false > Iterate
Iterate all windows in whatever order is easiest.
Definition window_gui.h:932
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:3392
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:16
@ In2x
Zoomed 2 times in.
@ Min
Minimum zoom level.
@ Normal
The normal zoom level.
@ In4x
Zoomed 4 times in.