Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-05-14 18:41:13 +05:00
994 changed files with 38759 additions and 34518 deletions

View File

@@ -28,6 +28,7 @@
#include "transparency.h" // CM
#include "core/backup_type.hpp"
#include "core/container_func.hpp"
#include "core/geometry_func.hpp"
#include "viewport_func.h"
#include "table/string_colours.h"
@@ -38,9 +39,9 @@
#include "safeguards.h"
byte _dirkeys; ///< 1 = left, 2 = up, 4 = right, 8 = down
uint8_t _dirkeys; ///< 1 = left, 2 = up, 4 = right, 8 = down
bool _fullscreen;
byte _support8bpp;
uint8_t _support8bpp;
CursorVars _cursor;
bool _ctrl_pressed; ///< Is Ctrl pressed?
bool _alt_pressed; ///< Is Alt pressed?
@@ -59,15 +60,15 @@ PauseMode _pause_mode;
GameSessionStats _game_session_stats; ///< Statistics about the current session.
namespace citymania { uint32 _pause_countdown = 0; }
static byte _stringwidth_table[FS_END][224]; ///< Cache containing width of often used characters. @see GetCharacterWidth()
static uint8_t _stringwidth_table[FS_END][224]; ///< Cache containing width of often used characters. @see GetCharacterWidth()
DrawPixelInfo *_cur_dpi;
static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = nullptr, SpriteID sprite_id = SPR_CURSOR_MOUSE);
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = nullptr, SpriteID sprite_id = SPR_CURSOR_MOUSE, ZoomLevel zoom = ZOOM_LVL_NORMAL);
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = nullptr, SpriteID sprite_id = SPR_CURSOR_MOUSE, ZoomLevel zoom = ZOOM_LVL_MIN);
static ReusableBuffer<uint8_t> _cursor_backup;
ZoomLevel _gui_zoom = ZOOM_LVL_OUT_4X; ///< GUI Zoom level
ZoomLevel _gui_zoom = ZOOM_LVL_NORMAL; ///< GUI Zoom level
ZoomLevel _font_zoom = _gui_zoom; ///< Sprite font Zoom level (not clamped)
int _gui_scale = MIN_INTERFACE_SCALE; ///< GUI scale, 100 is 100%.
int _gui_scale_cfg; ///< GUI scale in config.
@@ -79,8 +80,8 @@ int _gui_scale_cfg; ///< GUI scale in config.
*
* @ingroup dirty
*/
static const byte *_colour_remap_ptr;
static byte _string_colourremap[3]; ///< Recoloursprite for stringdrawing. The grf loader ensures that #SpriteType::Font sprites only use colours 0 to 2.
static const uint8_t *_colour_remap_ptr;
static uint8_t _string_colourremap[3]; ///< Recoloursprite for stringdrawing. The grf loader ensures that #SpriteType::Font sprites only use colours 0 to 2.
extern uint _dirty_block_colour;
static bool _whole_screen_dirty = false;
@@ -114,7 +115,7 @@ void GfxScroll(int left, int top, int width, int height, int xo, int yo)
/**
* Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
*
* @pre dpi->zoom == ZOOM_LVL_NORMAL, right >= left, bottom >= top
* @pre dpi->zoom == ZOOM_LVL_MIN, right >= left, bottom >= top
* @param left Minimum X (inclusive)
* @param top Minimum Y (inclusive)
* @param right Maximum X (inclusive)
@@ -133,7 +134,7 @@ void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectM
const int otop = top;
const int oleft = left;
if (dpi->zoom != ZOOM_LVL_NORMAL) return;
if (dpi->zoom != ZOOM_LVL_MIN) return;
if (left > right || top > bottom) return;
if (right < dpi->left || left >= dpi->left + dpi->width) return;
if (bottom < dpi->top || top >= dpi->top + dpi->height) return;
@@ -162,7 +163,7 @@ void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectM
break;
case FILLRECT_CHECKER: {
byte bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
uint8_t bo = (oleft - left + dpi->left + otop - top + dpi->top) & 1;
do {
for (int i = (bo ^= 1); i < right; i += 2) blitter->SetPixel(dst, i, 0, (uint8_t)colour);
dst = blitter->MoveTo(dst, 0, 1);
@@ -213,7 +214,7 @@ static std::vector<LineSegment> MakePolygonSegments(const std::vector<Point> &sh
* The odd-even winding rule is used, i.e. self-intersecting polygons will have holes in them.
* Left and top edges are inclusive, right and bottom edges are exclusive.
* @note For rectangles the GfxFillRect function will be faster.
* @pre dpi->zoom == ZOOM_LVL_NORMAL
* @pre dpi->zoom == ZOOM_LVL_MIN
* @param shape List of points on the polygon.
* @param colour An 8 bit palette index (FILLRECT_OPAQUE and FILLRECT_CHECKER) or a recolour spritenumber (FILLRECT_RECOLOUR).
* @param mode
@@ -225,7 +226,7 @@ void GfxFillPolygon(const std::vector<Point> &shape, int colour, FillRectMode mo
{
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
const DrawPixelInfo *dpi = _cur_dpi;
if (dpi->zoom != ZOOM_LVL_NORMAL) return;
if (dpi->zoom != ZOOM_LVL_MIN) return;
std::vector<LineSegment> segments = MakePolygonSegments(shape, Point{ dpi->left, dpi->top });
@@ -446,7 +447,7 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
* ....V.
*/
static const byte colour = PC_WHITE;
static const uint8_t colour = PC_WHITE;
GfxDrawLineUnscaled(x, y, x + dx1, y + dy1, colour);
GfxDrawLineUnscaled(x, y, x + dx2, y + dy2, colour);
@@ -489,7 +490,7 @@ static void SetColourRemap(TextColour colour)
bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0;
colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR | TC_FORCED);
_string_colourremap[1] = raw_colour ? (byte)colour : _string_colourmap[colour];
_string_colourremap[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour];
_string_colourremap[2] = no_shade ? 0 : 1;
_colour_remap_ptr = _string_colourremap;
}
@@ -505,11 +506,12 @@ static void SetColourRemap(TextColour colour)
* will be drawn in the right direction.
* @param underline Whether to underline what has been drawn or not.
* @param truncation Whether to perform string truncation or not.
* @param default_colour Colour of text if not specified within string.
*
* @return In case of left or center alignment the right most pixel we have drawn to.
* In case of right alignment the left most pixel we have drawn to.
*/
static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation)
static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, int right, StringAlignment align, bool underline, bool truncation, TextColour default_colour)
{
if (line.CountRuns() == 0) return 0;
@@ -603,6 +605,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
FontCache *fc = f->fc;
TextColour colour = f->colour;
if (colour == TC_INVALID || HasFlag(default_colour, TC_FORCED)) colour = default_colour;
colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
SetColourRemap(do_shadow ? TC_BLACK : colour); // the last run also sets the colour for the truncation dots
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
@@ -617,9 +620,9 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
/* Not a valid glyph (empty) */
if (glyph == 0xFFFF) continue;
int begin_x = positions[i].x + left - offset_x;
int end_x = positions[i + 1].x + left - offset_x - 1;
int top = positions[i].y + y;
int begin_x = positions[i].left + left - offset_x;
int end_x = positions[i].right + left - offset_x;
int top = positions[i].top + y;
/* Truncated away. */
if (truncation && (begin_x < min_x || end_x > max_x)) continue;
@@ -679,10 +682,10 @@ int DrawString(int left, int right, int top, std::string_view str, TextColour co
return 0;
}
Layouter layout(str, INT32_MAX, colour, fontsize);
Layouter layout(str, INT32_MAX, fontsize);
if (layout.empty()) return 0;
return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true);
return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true, colour);
}
/**
@@ -716,7 +719,7 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour, St
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
{
assert(maxw > 0);
Layouter layout(str, maxw, TC_FROMSTRING, fontsize);
Layouter layout(str, maxw, fontsize);
return layout.GetBounds().height;
}
@@ -792,7 +795,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_vi
* do we really want to support fonts of 0 or less pixels high? */
if (maxh <= 0) return top;
Layouter layout(str, maxw, colour, fontsize);
Layouter layout(str, maxw, fontsize);
int total_height = layout.GetBounds().height;
int y;
switch (align & SA_VERT_MASK) {
@@ -821,7 +824,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_vi
last_line = y + line_height;
if (first_line > y) first_line = y;
DrawLayoutLine(*line, y, left, right, align, underline, false);
DrawLayoutLine(*line, y, left, right, align, underline, false, colour);
}
y += line_height;
}
@@ -862,7 +865,7 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str,
*/
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
{
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
Layouter layout(str, INT32_MAX, start_fontsize);
return layout.GetBounds();
}
@@ -879,50 +882,32 @@ Dimension GetStringBoundingBox(StringID strid, FontSize start_fontsize)
/**
* Get maximum width of a list of strings.
* @param list List of strings, terminated with INVALID_STRING_ID.
* @param list List of strings.
* @param fontsize Font size to use.
* @return Width of longest string within the list.
*/
uint GetStringListWidth(const StringID *list, FontSize fontsize)
uint GetStringListWidth(std::span<const StringID> list, FontSize fontsize)
{
uint width = 0;
for (const StringID *str = list; *str != INVALID_STRING_ID; str++) {
width = std::max(width, GetStringBoundingBox(*str, fontsize).width);
for (auto str : list) {
width = std::max(width, GetStringBoundingBox(str, fontsize).width);
}
return width;
}
/**
* Get the leading corner of a character in a single-line string relative
* to the start of the string.
* @param str String containing the character.
* @param ch Pointer to the character in the string.
* @param start_fontsize Font size to start the text with.
* @return Upper left corner of the glyph associated with the character.
* Get maximum dimension of a list of strings.
* @param list List of strings, terminated by INVALID_STRING_ID.
* @param fontsize Font size to use.
* @return Dimension of highest and longest string within the list.
*/
Point GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize)
Dimension GetStringListBoundingBox(std::span<const StringID> list, FontSize fontsize)
{
/* Ensure "ch" is inside "str" or at the exact end. */
assert(ch >= str.data() && (ch - str.data()) <= static_cast<ptrdiff_t>(str.size()));
auto it_ch = str.begin() + (ch - str.data());
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
return layout.GetCharPosition(it_ch);
}
/**
* Get the character from a string that is drawn at a specific position.
* @param str String to test.
* @param x Position relative to the start of the string.
* @param start_fontsize Font size to start the text with.
* @return Index of the character position or -1 if there is no character at the position.
*/
ptrdiff_t GetCharAtPosition(std::string_view str, int x, FontSize start_fontsize)
{
if (x < 0) return -1;
Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
return layout.GetCharAtPosition(x, 0);
Dimension d{0, 0};
for (auto str : list) {
d = maxdim(d, GetStringBoundingBox(str, fontsize));
}
return d;
}
/**
@@ -1209,9 +1194,7 @@ std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel
const Sprite *sprite = GetSprite(real_sprite, SpriteType::Normal);
Dimension dim = GetSpriteSize(real_sprite, nullptr, zoom);
size_t dim_size = static_cast<size_t>(dim.width) * dim.height;
std::unique_ptr<uint32_t[]> result(new uint32_t[dim_size]);
/* Set buffer to fully transparent. */
MemSetT(result.get(), 0, dim_size);
std::unique_ptr<uint32_t[]> result = std::make_unique<uint32_t[]>(dim_size);
/* Prepare new DrawPixelInfo - Normally this would be the screen but we want to draw to another buffer here.
* Normally, pitch would be scaled screen width, but in our case our "screen" is only the sprite width wide. */
@@ -1227,23 +1210,21 @@ std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel
dim_size = static_cast<size_t>(dim.width) * dim.height;
/* If the current blitter is a paletted blitter, we have to render to an extra buffer and resolve the palette later. */
std::unique_ptr<byte[]> pal_buffer{};
std::unique_ptr<uint8_t[]> pal_buffer{};
if (blitter->GetScreenDepth() == 8) {
pal_buffer.reset(new byte[dim_size]);
MemSetT(pal_buffer.get(), 0, dim_size);
pal_buffer = std::make_unique<uint8_t[]>(dim_size);
dpi.dst_ptr = pal_buffer.get();
}
/* Temporarily disable screen animations while blitting - This prevents 40bpp_anim from writing to the animation buffer. */
Backup<bool> disable_anim(_screen_disable_anim, true, FILE_LINE);
Backup<bool> disable_anim(_screen_disable_anim, true);
GfxBlitter<1, true>(sprite, 0, 0, BM_NORMAL, nullptr, real_sprite, zoom, &dpi);
disable_anim.Restore();
if (blitter->GetScreenDepth() == 8) {
/* Resolve palette. */
uint32_t *dst = result.get();
const byte *src = pal_buffer.get();
const uint8_t *src = pal_buffer.get();
for (size_t i = 0; i < dim_size; ++i) {
*dst++ = _cur_palette.palette[*src++].data;
}
@@ -1254,7 +1235,7 @@ std::unique_ptr<uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel
static void GfxMainBlitterViewport(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id)
{
GfxBlitter<ZOOM_LVL_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
GfxBlitter<ZOOM_BASE, false>(sprite, x, y, mode, sub, sprite_id, _cur_dpi->zoom);
}
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub, SpriteID sprite_id, ZoomLevel zoom)
@@ -1283,7 +1264,7 @@ void LoadStringWidthTable(bool monospace)
* @param key Character code glyph
* @return Width of the character glyph
*/
byte GetCharacterWidth(FontSize size, char32_t key)
uint8_t GetCharacterWidth(FontSize size, char32_t key)
{
/* Use _stringwidth_table cache if possible */
if (key >= 32 && key < 256) return _stringwidth_table[size][key - 32];
@@ -1296,9 +1277,9 @@ byte GetCharacterWidth(FontSize size, char32_t key)
* @param size Font of the digit
* @return Width of the digit.
*/
byte GetDigitWidth(FontSize size)
uint8_t GetDigitWidth(FontSize size)
{
byte width = 0;
uint8_t width = 0;
for (char c = '0'; c <= '9'; c++) {
width = std::max(GetCharacterWidth(size, c), width);
}
@@ -1327,6 +1308,12 @@ void GetBroadestDigit(uint *front, uint *next, FontSize size)
void ScreenSizeChanged()
{
MarkWholeScreenDirty(); // CM
// _dirty_bytes_per_line = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH);
// _dirty_blocks = ReallocT<uint8_t>(_dirty_blocks, static_cast<size_t>(_dirty_bytes_per_line) * CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT));
// /* check the dirty rect */
// if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width;
// if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height;
/* screen size changed and the old bitmap is invalid now, so we don't want to undraw it */
_cursor.visible = false;
@@ -1402,8 +1389,8 @@ void DrawMouseCursor()
/* Draw cursor on screen */
_cur_dpi = &_screen;
for (uint i = 0; i < _cursor.sprite_count; ++i) {
DrawSprite(_cursor.sprite_seq[i].sprite, _cursor.sprite_seq[i].pal, _cursor.pos.x + _cursor.sprite_pos[i].x, _cursor.pos.y + _cursor.sprite_pos[i].y);
for (const auto &cs : _cursor.sprites) {
DrawSprite(cs.image.sprite, cs.image.pal, _cursor.pos.x + cs.pos.x, _cursor.pos.y + cs.pos.y);
}
VideoDriver::GetInstance()->MakeDirty(_cursor.draw_pos.x, _cursor.draw_pos.y, _cursor.draw_size.x, _cursor.draw_size.y);
@@ -1538,7 +1525,7 @@ void DrawDirtyBlocks()
};
DrawPixelInfo bk;
Backup dpi_backup(_cur_dpi, &bk, FILE_LINE);
Backup dpi_backup(_cur_dpi, &bk);
for (Window *w : Window::IterateFromBack()) {
w->flags &= ~WF_DRAG_DIRTIED;
@@ -1883,7 +1870,7 @@ bool FillDrawPixelInfo(DrawPixelInfo *n, int left, int top, int width, int heigh
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
const DrawPixelInfo *o = _cur_dpi;
n->zoom = ZOOM_LVL_NORMAL;
n->zoom = ZOOM_LVL_MIN;
assert(width > 0);
assert(height > 0);
@@ -1933,20 +1920,22 @@ void UpdateCursorSize()
/* Ignore setting any cursor before the sprites are loaded. */
if (GetMaxSpriteID() == 0) return;
static_assert(lengthof(_cursor.sprite_seq) == lengthof(_cursor.sprite_pos));
assert(_cursor.sprite_count <= lengthof(_cursor.sprite_seq));
for (uint i = 0; i < _cursor.sprite_count; ++i) {
const Sprite *p = GetSprite(GB(_cursor.sprite_seq[i].sprite, 0, SPRITE_WIDTH), SpriteType::Normal);
bool first = true;
for (const auto &cs : _cursor.sprites) {
const Sprite *p = GetSprite(GB(cs.image.sprite, 0, SPRITE_WIDTH), SpriteType::Normal);
Point offs, size;
offs.x = UnScaleGUI(p->x_offs) + _cursor.sprite_pos[i].x;
offs.y = UnScaleGUI(p->y_offs) + _cursor.sprite_pos[i].y;
offs.x = UnScaleGUI(p->x_offs) + cs.pos.x;
offs.y = UnScaleGUI(p->y_offs) + cs.pos.y;
size.x = UnScaleGUI(p->width);
size.y = UnScaleGUI(p->height);
if (i == 0) {
if (first) {
/* First sprite sets the total. */
_cursor.total_offs = offs;
_cursor.total_size = size;
first = false;
} else {
/* Additional sprites expand the total. */
int right = std::max(_cursor.total_offs.x + _cursor.total_size.x, offs.x + size.x);
int bottom = std::max(_cursor.total_offs.y + _cursor.total_size.y, offs.y + size.y);
if (offs.x < _cursor.total_offs.x) _cursor.total_offs.x = offs.x;
@@ -1966,13 +1955,10 @@ void UpdateCursorSize()
*/
static void SetCursorSprite(CursorID cursor, PaletteID pal)
{
if (_cursor.sprite_count == 1 && _cursor.sprite_seq[0].sprite == cursor && _cursor.sprite_seq[0].pal == pal) return;
if (_cursor.sprites.size() == 1 && _cursor.sprites[0].image.sprite == cursor && _cursor.sprites[0].image.pal == pal) return;
_cursor.sprite_count = 1;
_cursor.sprite_seq[0].sprite = cursor;
_cursor.sprite_seq[0].pal = pal;
_cursor.sprite_pos[0].x = 0;
_cursor.sprite_pos[0].y = 0;
_cursor.sprites.clear();
_cursor.sprites.emplace_back(cursor, pal, 0, 0);
UpdateCursorSize();
}
@@ -1983,7 +1969,8 @@ static void SwitchAnimatedCursor()
if (cur == nullptr || cur->sprite == AnimCursor::LAST) cur = _cursor.animate_list;
SetCursorSprite(cur->sprite, _cursor.sprite_seq[0].pal);
assert(!_cursor.sprites.empty());
SetCursorSprite(cur->sprite, _cursor.sprites[0].image.pal);
_cursor.animate_timeout = cur->display_time;
_cursor.animate_cur = cur + 1;
@@ -2002,10 +1989,11 @@ void CursorTick()
*/
void SetMouseCursorBusy(bool busy)
{
assert(!_cursor.sprites.empty());
if (busy) {
if (_cursor.sprite_seq[0].sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
if (_cursor.sprites[0].image.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
} else {
if (_cursor.sprite_seq[0].sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
if (_cursor.sprites[0].image.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
}
}
@@ -2030,9 +2018,10 @@ void SetMouseCursor(CursorID sprite, PaletteID pal)
*/
void SetAnimatedMouseCursor(const AnimCursor *table)
{
assert(!_cursor.sprites.empty());
_cursor.animate_list = table;
_cursor.animate_cur = nullptr;
_cursor.sprite_seq[0].pal = PAL_NONE;
_cursor.sprites[0].image.pal = PAL_NONE;
SwitchAnimatedCursor();
}
@@ -2107,7 +2096,7 @@ void UpdateGUIZoom()
_gui_scale = Clamp(_gui_scale_cfg, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE);
}
int8_t new_zoom = ScaleGUITrad(1) <= 1 ? ZOOM_LVL_OUT_4X : ScaleGUITrad(1) >= 4 ? ZOOM_LVL_MIN : ZOOM_LVL_OUT_2X;
int8_t new_zoom = ScaleGUITrad(1) <= 1 ? ZOOM_LVL_NORMAL : ScaleGUITrad(1) >= 4 ? ZOOM_LVL_IN_4X : ZOOM_LVL_IN_2X;
/* Font glyphs should not be clamped to min/max zoom. */
_font_zoom = static_cast<ZoomLevel>(new_zoom);
/* Ensure the gui_zoom is clamped between min/max. */