Codechange: Replace viewport StringID specialisation with flags. (#13237)

When drawing viewport strings, the StringID is used to determine how to draw the sign. Instead, allow the behaviour to be set by the caller with flags. This means that some of the viewport-specific strings are no longer necessary.

ViewportAddString() now returns a pointer to a string as it may not actually add the string, in which case preparing the string parameters in advance is a waste of time.
This commit is contained in:
Peter Nelson
2025-01-02 20:47:12 +00:00
committed by GitHub
parent fd7a883cbd
commit 810dc23215
8 changed files with 154 additions and 92 deletions

View File

@@ -113,11 +113,11 @@ static const int MAX_TILE_EXTENT_BOTTOM = ZOOM_BASE * (TILE_PIXELS + 2 * TILE_HE
struct StringSpriteToDraw {
std::string string;
StringID string_id;
uint16_t width;
Colours colour;
ViewportStringFlags flags;
int32_t x;
int32_t y;
uint16_t width;
};
struct TileSpriteToDraw {
@@ -861,16 +861,26 @@ void AddChildSpriteScreen(SpriteID image, PaletteID pal, int x, int y, bool tran
_vd.last_child = child_id;
}
static void AddStringToDraw(int x, int y, StringID string, Colours colour, uint16_t width)
/**
* Add a string to draw to a viewport.
* @param x Left position of string.
* @param y Top position of string.
* @param colour Colour of string.
* @param flags ViewportStringFlags to control the string's appearance.
* @param width Width of the string.
* @returns Reference to raw string which should be filled in by the caller.
*/
static std::string &AddStringToDraw(int x, int y, Colours colour, ViewportStringFlags flags, uint16_t width)
{
assert(width != 0);
StringSpriteToDraw &ss = _vd.string_sprites_to_draw.emplace_back();
ss.string = GetString(string);
ss.string_id = string;
ss.colour = colour;
ss.flags = flags;
ss.x = x;
ss.y = y;
ss.width = width;
ss.colour = colour;
return ss.string;
}
@@ -1299,24 +1309,22 @@ static void ViewportAddLandscape()
}
/**
* Add a string to draw in the viewport
* Add a string to draw in the current viewport.
* @param dpi current viewport area
* @param small_from Zoomlevel from when the small font should be used
* @param sign sign position and dimension
* @param string_normal String for normal and 2x zoom level
* @param string_small String for 4x and 8x zoom level
* @param string_small_shadow Shadow string for 4x and 8x zoom level; or #STR_NULL if no shadow
* @param flags ViewportStringFlags to control the string's appearance.
* @param colour colour of the sign background; or INVALID_COLOUR if transparent
* @returns Pointer to std::string to filled with sign, or nullptr if string would be outside the viewport bounds.
*/
void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, Colours colour)
std::string *ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sign, ViewportStringFlags flags, Colours colour)
{
bool small = dpi->zoom >= small_from;
int left = dpi->left;
int top = dpi->top;
int right = left + dpi->width;
int bottom = top + dpi->height;
bool small = HasFlag(flags, ViewportStringFlags::Small);
int sign_height = ScaleByZoom(WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom, dpi->zoom);
int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
@@ -1324,19 +1332,10 @@ void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const Vie
top > sign->top + sign_height ||
right < sign->center - sign_half_width ||
left > sign->center + sign_half_width) {
return;
return nullptr;
}
if (!small) {
AddStringToDraw(sign->center - sign_half_width, sign->top, string_normal, colour, sign->width_normal);
} else {
int shadow_offset = 0;
if (string_small_shadow != STR_NULL) {
shadow_offset = 4;
AddStringToDraw(sign->center - sign_half_width + shadow_offset, sign->top, string_small_shadow, INVALID_COLOUR, sign->width_small | 0x8000);
}
AddStringToDraw(sign->center - sign_half_width, sign->top - shadow_offset, string_small, colour, sign->width_small | 0x8000);
}
return &AddStringToDraw(sign->center - sign_half_width, sign->top, colour, flags, small ? sign->width_small : sign->width_normal);
}
static Rect ExpandRectWithViewportSignMargins(Rect r, ZoomLevel zoom)
@@ -1354,6 +1353,80 @@ static Rect ExpandRectWithViewportSignMargins(Rect r, ZoomLevel zoom)
return r;
}
/**
* Add town strings to a viewport.
* @param dpi Current viewport area.
* @param towns List of towns to add.
* @param small Add small versions of strings.
*/
static void ViewportAddTownStrings(DrawPixelInfo *dpi, const std::vector<const Town *> &towns, bool small)
{
ViewportStringFlags flags{};
if (small) flags = ViewportStringFlags::Small | ViewportStringFlags::Shadow;
StringID stringid = !small && _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_TOWN_NAME;
for (const Town *t : towns) {
std::string *str = ViewportAddString(dpi, &t->cache.sign, flags, INVALID_COLOUR);
if (str == nullptr) continue;
SetDParam(0, t->index);
SetDParam(1, t->cache.population);
*str = GetString(stringid);
}
}
/**
* Add sign strings to a viewport.
* @param dpi Current viewport area.
* @param towns List of signs to add.
* @param small Add small versions of strings.
*/
static void ViewportAddSignStrings(DrawPixelInfo *dpi, const std::vector<const Sign *> &signs, bool small)
{
ViewportStringFlags flags{};
if (small) flags = ViewportStringFlags::Small;
/* Signs placed by a game script don't have a frame. */
ViewportStringFlags deity_flags{flags};
flags |= IsTransparencySet(TO_SIGNS) ? ViewportStringFlags::TransparentRect : ViewportStringFlags::ColourRect;
for (const Sign *si : signs) {
std::string *str = ViewportAddString(dpi, &si->sign, (si->owner == OWNER_DEITY) ? deity_flags : flags,
(si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
if (str == nullptr) continue;
SetDParam(0, si->index);
*str = GetString(STR_SIGN_NAME);
}
}
/**
* Add station strings to a viewport.
* @param dpi Current viewport area.
* @param towns List of stations to add.
* @param small Add small versions of strings.
*/
static void ViewportAddStationStrings(DrawPixelInfo *dpi, const std::vector<const BaseStation *> &stations, bool small)
{
/* Transparent station signs have colour text instead of a colour panel. */
ViewportStringFlags flags{IsTransparencySet(TO_SIGNS) ? ViewportStringFlags::TextColour : ViewportStringFlags::ColourRect};
if (small) flags = ViewportStringFlags::Small;
for (const BaseStation *st : stations) {
std::string *str = ViewportAddString(dpi, &st->sign, flags, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
if (str == nullptr) continue;
if (Station::IsExpected(st)) { /* Station */
SetDParam(0, st->index);
SetDParam(1, st->facilities);
*str = GetString(small ? STR_STATION_NAME : STR_VIEWPORT_STATION);
} else { /* Waypoint */
SetDParam(0, st->index);
*str = GetString(STR_WAYPOINT_NAME);
}
}
}
static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
{
Rect search_rect{ dpi->left, dpi->top, dpi->left + dpi->width, dpi->top + dpi->height };
@@ -1417,42 +1490,17 @@ static void ViewportAddKdtreeSigns(DrawPixelInfo *dpi)
}
});
/* Layering order (bottom to top): Town names, signs, stations */
/* Small versions of signs are used zoom level 4X and higher. */
bool small = dpi->zoom >= ZOOM_LVL_OUT_4X;
for (const auto *t : towns) {
SetDParam(0, t->index);
SetDParam(1, t->cache.population);
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &t->cache.sign,
_settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK);
}
/* Layering order (bottom to top): Town names, signs, stations */
ViewportAddTownStrings(dpi, towns, small);
/* Do not draw signs nor station names if they are set invisible */
if (IsInvisibilitySet(TO_SIGNS)) return;
for (const auto *si : signs) {
SetDParam(0, si->index);
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &si->sign,
STR_WHITE_SIGN,
(IsTransparencySet(TO_SIGNS) || si->owner == OWNER_DEITY) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK, STR_NULL,
(si->owner == OWNER_NONE) ? COLOUR_GREY : (si->owner == OWNER_DEITY ? INVALID_COLOUR : _company_colours[si->owner]));
}
for (const auto *st : stations) {
SetDParam(0, st->index);
SetDParam(1, st->facilities);
if (Station::IsExpected(st)) {
/* Station */
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
STR_VIEWPORT_STATION, STR_VIEWPORT_STATION_TINY, STR_NULL,
(st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
} else {
/* Waypoint */
ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
STR_VIEWPORT_WAYPOINT, STR_VIEWPORT_WAYPOINT_TINY, STR_NULL,
(st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
}
}
ViewportAddSignStrings(dpi, signs, small);
ViewportAddStationStrings(dpi, stations, small);
}
@@ -1710,30 +1758,36 @@ static void ViewportDrawDirtyBlocks()
static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector *sstdv)
{
for (const StringSpriteToDraw &ss : *sstdv) {
TextColour colour = TC_BLACK;
bool small = HasBit(ss.width, 15);
int w = GB(ss.width, 0, 15);
bool small = HasFlag(ss.flags, ViewportStringFlags::Small);
int w = ss.width;
int x = UnScaleByZoom(ss.x, zoom);
int y = UnScaleByZoom(ss.y, zoom);
int h = WidgetDimensions::scaled.fullbevel.top + GetCharacterHeight(small ? FS_SMALL : FS_NORMAL) + WidgetDimensions::scaled.fullbevel.bottom;
if (ss.colour != INVALID_COLOUR) {
if (IsTransparencySet(TO_SIGNS) && ss.string_id != STR_WHITE_SIGN) {
/* Don't draw the rectangle.
* Real colours need the TC_IS_PALETTE_COLOUR flag.
* Otherwise colours from _string_colourmap are assumed. */
colour = (TextColour)GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR;
} else {
/* Draw the rectangle if 'transparent station signs' is off,
* or if we are drawing a general text sign (STR_WHITE_SIGN). */
DrawFrameRect(
x, y, x + w - 1, y + h - 1, ss.colour,
IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
);
}
TextColour colour = TC_WHITE;
if (HasFlag(ss.flags, ViewportStringFlags::ColourRect)) {
if (ss.colour != INVALID_COLOUR) DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, FR_NONE);
colour = TC_BLACK;
} else if (HasFlag(ss.flags, ViewportStringFlags::TransparentRect)) {
DrawFrameRect(x, y, x + w - 1, y + h - 1, ss.colour, FR_TRANSPARENT);
}
DrawString(x + WidgetDimensions::scaled.fullbevel.left, x + w - 1 - WidgetDimensions::scaled.fullbevel.right, y + WidgetDimensions::scaled.fullbevel.top, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
if (HasFlag(ss.flags, ViewportStringFlags::TextColour)) {
if (ss.colour != INVALID_COLOUR) colour = static_cast<TextColour>(GetColourGradient(ss.colour, SHADE_LIGHTER) | TC_IS_PALETTE_COLOUR);
}
int left = x + WidgetDimensions::scaled.fullbevel.left;
int right = x + w - 1 - WidgetDimensions::scaled.fullbevel.right;
int top = y + WidgetDimensions::scaled.fullbevel.top;
int shadow_offset = 0;
if (small && HasFlag(ss.flags, ViewportStringFlags::Shadow)) {
/* Shadow needs to be shifted 1 pixel. */
shadow_offset = WidgetDimensions::scaled.fullbevel.top;
DrawString(left + shadow_offset, right + shadow_offset, top, ss.string, TC_BLACK, SA_HOR_CENTER, false, FS_SMALL);
}
DrawString(left, right, top - shadow_offset, ss.string, colour, SA_HOR_CENTER, false, small ? FS_SMALL : FS_NORMAL);
}
}