diff --git a/src/gfx.cpp b/src/gfx.cpp index 319fe0d789..50f195f60f 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -73,7 +73,7 @@ int _gui_scale_cfg; ///< GUI scale in config. */ static Rect _invalid_rect; static const RecolourSprite *_colour_remap_ptr; -static RecolourSprite _string_colourremap; ///< RecolourSprite for string drawing. +static RecolourSpriteRGBA _string_colourremap; ///< RecolourSprite for string drawing. static const uint DIRTY_BLOCK_HEIGHT = 8; static const uint DIRTY_BLOCK_WIDTH = 64; @@ -470,19 +470,32 @@ void DrawRectOutline(const Rect &r, PixelColour colour, int width, int dash) * Set the colour remap to be for the given colour. * @param colour the new colour of the remap. */ -static void SetColourRemap(TextColour colour) +static BlitterMode SetColourRemap(TextColour colour) { - if (colour == TC_INVALID) return; + if (colour == TC_INVALID) return BlitterMode::ColourRemap; /* Black strings have no shading ever; the shading is black, so it * would be invisible at best, but it actually makes it illegible. */ bool no_shade = (colour & TC_NO_SHADE) != 0 || colour == TC_BLACK; + + if ((colour & TC_IS_RGB_COLOUR) && BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) { + /* Unpack RGB TextColour */ + TextColourPacker tcp(colour); + _string_colourremap.rgba[1] = tcp.ToColour(); + _string_colourremap.rgba[2] = _cur_palette.palette[no_shade ? 0 : PC_BLACK.p]; + _colour_remap_ptr = &_string_colourremap; + + return BlitterMode::RGBAColourRemap; + } + bool raw_colour = (colour & TC_IS_PALETTE_COLOUR) != 0; colour &= ~(TC_NO_SHADE | TC_IS_PALETTE_COLOUR | TC_FORCED); _string_colourremap.palette[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour].p; - _string_colourremap.palette[2] = no_shade ? 0 : 1; + _string_colourremap.palette[2] = no_shade ? 0 : PC_BLACK.p; _colour_remap_ptr = &_string_colourremap; + + return BlitterMode::ColourRemap; } /** @@ -602,7 +615,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, /* Update the last colour for the truncation ellipsis. */ last_colour = colour; if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue; - SetColourRemap(do_shadow ? TC_BLACK : colour); + BlitterMode bm = SetColourRemap(do_shadow ? TC_BLACK : colour); // the last run also sets the colour for the truncation dots for (int i = 0; i < run.GetGlyphCount(); i++) { GlyphID glyph = glyphs[i]; @@ -623,7 +636,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, if (do_shadow && (glyph & SPRITE_GLYPH) != 0) continue; - GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap); + GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), bm); } } return last_colour; @@ -948,11 +961,11 @@ Dimension GetStringListBoundingBox(std::span list, FontSize font */ void DrawCharCentered(char32_t c, const Rect &r, TextColour colour) { - SetColourRemap(colour); + BlitterMode bm = SetColourRemap(colour); GfxMainBlitter(GetGlyph(FS_NORMAL, c), CentreBounds(r.left, r.right, GetCharacterWidth(FS_NORMAL, c)), CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), - BlitterMode::ColourRemap); + bm); } /** @@ -1009,12 +1022,14 @@ void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSpri _colour_remap_ptr = GetRecolourSprite(pal); GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite); } else if (pal != PAL_NONE) { + BlitterMode bm; if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) { - SetColourRemap((TextColour)GB(pal, 0, PALETTE_WIDTH)); + bm = SetColourRemap((TextColour)GB(pal, 0, PALETTE_WIDTH)); } else { _colour_remap_ptr = GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH)); + bm = GetBlitterMode(pal, *_colour_remap_ptr); } - GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal, *_colour_remap_ptr), sub, real_sprite); + GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, bm, sub, real_sprite); } else { GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite); } @@ -1037,12 +1052,14 @@ void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, _colour_remap_ptr = GetRecolourSprite(pal); GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, pal == PALETTE_TO_TRANSPARENT ? BlitterMode::Transparent : BlitterMode::TransparentRemap, sub, real_sprite, zoom); } else if (pal != PAL_NONE) { + BlitterMode bm; if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) { - SetColourRemap((TextColour)GB(pal, 0, PALETTE_WIDTH)); + bm = SetColourRemap((TextColour)GB(pal, 0, PALETTE_WIDTH)); } else { _colour_remap_ptr = GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH)); + bm = GetBlitterMode(pal, *_colour_remap_ptr); } - GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal, *_colour_remap_ptr), sub, real_sprite, zoom); + GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, bm, sub, real_sprite, zoom); } else { GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite, zoom); } diff --git a/src/gfx_type.h b/src/gfx_type.h index a45d68c5ba..a5f2255787 100644 --- a/src/gfx_type.h +++ b/src/gfx_type.h @@ -305,7 +305,7 @@ DECLARE_INCREMENT_DECREMENT_OPERATORS(Colours) DECLARE_ENUM_AS_ADDABLE(Colours) /** Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palette.png */ -enum TextColour : uint16_t { +enum TextColour : uint32_t { TC_BEGIN = 0x00, TC_FROMSTRING = 0x00, TC_BLUE = 0x00, @@ -331,9 +331,10 @@ enum TextColour : uint16_t { TC_IS_PALETTE_COLOUR = 0x100, ///< Colour value is already a real palette colour index, not an index of a StringColour. TC_NO_SHADE = 0x200, ///< Do not add shading to this text colour. TC_FORCED = 0x400, ///< Ignore colour changes from strings. + TC_IS_RGB_COLOUR = 0x800, ///< Colour includes RGB component. TC_COLOUR_MASK = 0xFF, ///< Mask to test if TextColour (without flags) is within limits. - TC_FLAGS_MASK = 0x700, ///< Mask to test if TextColour (with flags) is within limits. + TC_FLAGS_MASK = 0xF00, ///< Mask to test if TextColour (with flags) is within limits. }; DECLARE_ENUM_AS_BIT_SET(TextColour) @@ -416,7 +417,7 @@ struct PixelColour { constexpr inline bool HasRGB() const { return (this->r | this->g | this->b) != 0; } constexpr inline Colour ToColour() const { return {this->r, this->g, this->b}; } - constexpr inline TextColour ToTextColour() const { return static_cast(this->p) | TC_IS_PALETTE_COLOUR; } + TextColour ToTextColour() const; }; #endif /* GFX_TYPE_H */ diff --git a/src/palette.cpp b/src/palette.cpp index 44a2ae8329..d0853b5385 100644 --- a/src/palette.cpp +++ b/src/palette.cpp @@ -407,3 +407,16 @@ PixelColour::PixelColour(Colour colour) : r(colour.r), g(colour.g), b(colour.b) { this->p = GetNearestColourIndex(colour); } + +TextColour PixelColour::ToTextColour() const +{ + TextColour tc = static_cast(this->p) | TC_IS_PALETTE_COLOUR; + if (this->HasRGB()) { + tc |= TC_IS_RGB_COLOUR; + TextColourPacker tcp(tc); + tcp.SetR(this->r); + tcp.SetG(this->g); + tcp.SetB(this->b); + } + return tc; +} diff --git a/src/palette_func.h b/src/palette_func.h index a5e7dda44c..33ab88801b 100644 --- a/src/palette_func.h +++ b/src/palette_func.h @@ -124,4 +124,43 @@ static constexpr PixelColour PC_FIELDS {0x25}; ///< Light static constexpr PixelColour PC_TREES {0x57}; ///< Green palette colour for trees. static constexpr PixelColour PC_WATER {0xC9}; ///< Dark blue palette colour for water. +/** + * Stretch TNumBits to fill 8 bits. + * The most-significant digits are repeated as least-significant digits so that the full 8 bit range is used, e.g.: + * 000000 -> 00000000, 111100 -> 11110011, 111111 -> 11111111 + * @param v Value of TNumBits to stretch. + * @returns 8 bit stretched value. + */ +template +inline constexpr uint8_t StretchBits(uint8_t v) +{ + return (v << (8 - TNumBits)) | (v >> (8 - (8 - TNumBits) * 2)); +} + +struct TextColourPacker +{ + TextColour &tc; + + constexpr TextColourPacker(TextColour &tc) : tc(tc) { } + + static constexpr uint8_t R_START = 12; ///< Packed start of red component + static constexpr uint8_t R_SIZE = 6; ///< Packed size of red component + + static constexpr uint8_t G_START = 18; ///< Packed start of green component + static constexpr uint8_t G_SIZE = 6; ///< Packed size of green component + + static constexpr uint8_t B_START = 24; ///< Packed start of blue component + static constexpr uint8_t B_SIZE = 6; ///< Packed size of blue component + + inline constexpr uint8_t GetR() const { return StretchBits(GB(this->tc, R_START, R_SIZE)); } + inline constexpr uint8_t GetG() const { return StretchBits(GB(this->tc, G_START, G_SIZE)); } + inline constexpr uint8_t GetB() const { return StretchBits(GB(this->tc, B_START, B_SIZE)); } + + inline constexpr void SetR(uint8_t v) { SB(this->tc, R_START, R_SIZE, v >> (8 - R_SIZE)); } + inline constexpr void SetG(uint8_t v) { SB(this->tc, G_START, G_SIZE, v >> (8 - G_SIZE)); } + inline constexpr void SetB(uint8_t v) { SB(this->tc, B_START, B_SIZE, v >> (8 - B_SIZE)); } + + inline constexpr Colour ToColour() const { return Colour(this->GetR(), this->GetG(), this->GetB()); } +}; + #endif /* PALETTE_FUNC_H */