1
0
Fork 0

Codechange: Add support for RGBA recolour remaps.

pull/11634/head
Peter Nelson 2025-07-25 08:56:48 +01:00
parent 79a2af5d44
commit bec34c38f7
No known key found for this signature in database
GPG Key ID: 8EF8F0A467DF75ED
9 changed files with 174 additions and 8 deletions

View File

@ -37,6 +37,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
uint16_t *anim = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)bp->dst) + bp->top * this->anim_buf_pitch + bp->left;
const uint8_t *remap = bp->remap->GetPaletteRemap(); // store so we don't have to access it via bp every time
const Colour *remap_rgba = mode == BlitterMode::RGBAColourRemap ? bp->remap->GetRGBARemap() : nullptr;
for (int y = 0; y < bp->height; y++) {
Colour *dst_ln = dst + bp->pitch;
@ -135,6 +136,43 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
}
break;
case BlitterMode::RGBAColourRemap:
if (src_px->a == 255) {
do {
uint m = *src_n;
/* In case the m-channel is zero, do not remap this pixel in any way */
if (m == 0) {
*dst = src_px->data;
*anim = 0;
} else {
const Colour &c = remap_rgba[GB(m, 0, 8)];
*anim = 0;
*dst = AdjustBrightness(c, GB(m, 8, 8));
}
anim++;
dst++;
src_px++;
src_n++;
} while (--n != 0);
} else {
do {
uint m = *src_n;
if (m == 0) {
*dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
*anim = 0;
} else {
const Colour &c = remap_rgba[GB(m, 0, 8)];
*anim = 0;
*dst = ComposeColourPANoCheck(AdjustBrightness(c, GB(m, 8, 8)), c.a * src_px->a / 255, *dst);
}
anim++;
dst++;
src_px++;
src_n++;
} while (--n != 0);
}
break;
case BlitterMode::CrashRemap:
if (src_px->a == 255) {
do {
@ -273,6 +311,7 @@ void Blitter_32bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomL
default: NOT_REACHED();
case BlitterMode::Normal: Draw<BlitterMode::Normal>(bp, zoom); return;
case BlitterMode::ColourRemap: Draw<BlitterMode::ColourRemap>(bp, zoom); return;
case BlitterMode::RGBAColourRemap: Draw<BlitterMode::RGBAColourRemap>(bp, zoom); return;
case BlitterMode::Transparent: Draw<BlitterMode::Transparent>(bp, zoom); return;
case BlitterMode::TransparentRemap: Draw<BlitterMode::TransparentRemap>(bp, zoom); return;
case BlitterMode::CrashRemap: Draw<BlitterMode::CrashRemap>(bp, zoom); return;

View File

@ -49,6 +49,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL
/* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */
const uint8_t *remap = bp->remap->GetPaletteRemap();
const Colour *remap_rgba = mode == BlitterMode::RGBAColourRemap ? bp->remap->GetRGBARemap() : nullptr;
for (int y = 0; y < bp->height; y++) {
/* next dst line begins here */
@ -142,6 +143,37 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL
}
break;
case BlitterMode::RGBAColourRemap:
if (src_px->a == 255) {
do {
uint m = *src_n;
/* In case the m-channel is zero, do not remap this pixel in any way */
if (m == 0) {
*dst = src_px->data;
} else {
const Colour c = remap_rgba[GB(m, 0, 8)];
*dst = AdjustBrightness(c, GB(m, 8, 8));
}
dst++;
src_px++;
src_n++;
} while (--n != 0);
} else {
do {
uint m = *src_n;
if (m == 0) {
*dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
} else {
const Colour c = remap_rgba[GB(m, 0, 8)];
*dst = ComposeColourPANoCheck(AdjustBrightness(c, GB(m, 8, 8)), src_px->a, *dst);
}
dst++;
src_px++;
src_n++;
} while (--n != 0);
}
break;
case BlitterMode::CrashRemap:
if (src_px->a == 255) {
do {
@ -263,6 +295,7 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode,
default: NOT_REACHED();
case BlitterMode::Normal: Draw<BlitterMode::Normal, Tpal_to_rgb>(bp, zoom); return;
case BlitterMode::ColourRemap: Draw<BlitterMode::ColourRemap, Tpal_to_rgb>(bp, zoom); return;
case BlitterMode::RGBAColourRemap: Draw<BlitterMode::RGBAColourRemap, Tpal_to_rgb>(bp, zoom); return;
case BlitterMode::Transparent: Draw<BlitterMode::Transparent, Tpal_to_rgb>(bp, zoom); return;
case BlitterMode::TransparentRemap: Draw<BlitterMode::TransparentRemap, Tpal_to_rgb>(bp, zoom); return;
case BlitterMode::CrashRemap: Draw<BlitterMode::CrashRemap, Tpal_to_rgb>(bp, zoom); return;

View File

@ -24,6 +24,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo
const Blitter_32bppSimple::Pixel *src, *src_line;
Colour *dst, *dst_line;
const uint8_t *remap = bp->remap->GetPaletteRemap();
const Colour *remap_rgba = mode == BlitterMode::RGBAColourRemap ? bp->remap->GetRGBARemap() : nullptr;
/* Find where to start reading in the source sprite */
src_line = (const Blitter_32bppSimple::Pixel *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, zoom);
@ -47,6 +48,18 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo
}
break;
case BlitterMode::RGBAColourRemap:
/* In case the m-channel is zero, do not remap this pixel in any way */
if (src->m == 0) {
if (src->a != 0) *dst = ComposeColourRGBA(src->r, src->g, src->b, src->a, *dst);
} else {
const Colour &c = remap_rgba[src->m];
if (c.a != 0) {
*dst = ComposeColourPA(AdjustBrightness(c, src->v), src->a, *dst);
}
}
break;
case BlitterMode::CrashRemap:
if (src->m == 0) {
if (src->a != 0) {

View File

@ -127,6 +127,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
/* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */
const uint8_t *remap = bp->remap->GetPaletteRemap();
const Colour *remap_rgba = mode == BlitterMode::RGBAColourRemap ? bp->remap->GetRGBARemap() : nullptr;
for (int y = 0; y < bp->height; y++) {
/* next dst line begins here */
@ -237,6 +238,49 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
}
break;
case BlitterMode::RGBAColourRemap:
if (src_px->a == 255) {
do {
uint8_t m = GB(*src_n, 0, 8);
/* In case the m-channel is zero, only apply the crash remap by darkening the RGB colour. */
if (m == 0) {
*dst = *src_px;
*anim = 0;
} else {
const Colour &c = remap_rgba[GB(m, 0, 8)];
if (c.a != 0) {
*dst = ComposeColourRGBANoCheck(c.r, c.g, c.b, c.a, *dst);
*anim = 0;
}
}
anim++;
dst++;
src_px++;
src_n++;
} while (--n != 0);
} else {
do {
uint8_t m = GB(*src_n, 0, 8);
Colour b = this->RealizeBlendedColour(*anim, *dst);
if (m == 0) {
Colour c = *src_px;
*dst = this->ComposeColourRGBANoCheck(c.r, c.g, c.b, src_px->a / 255, b);
*anim = 0;
} else {
const Colour &c = remap_rgba[m];
if (c.a != 0) {
*dst = this->ComposeColourPANoCheck(c, c.a * src_px->a / 255, b);
*anim = 0; // Animation colours don't work with alpha-blending.
}
}
anim++;
dst++;
src_px++;
src_n++;
} while (--n != 0);
}
break;
case BlitterMode::BlackRemap:
do {
*anim++ = 0;
@ -353,6 +397,7 @@ void Blitter_40bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomL
default: NOT_REACHED();
case BlitterMode::Normal: Draw<BlitterMode::Normal>(bp, zoom); return;
case BlitterMode::ColourRemap: Draw<BlitterMode::ColourRemap>(bp, zoom); return;
case BlitterMode::RGBAColourRemap: Draw<BlitterMode::RGBAColourRemap>(bp, zoom); return;
case BlitterMode::Transparent: Draw<BlitterMode::Transparent>(bp, zoom); return;
case BlitterMode::TransparentRemap: Draw<BlitterMode::TransparentRemap>(bp, zoom); return;
case BlitterMode::CrashRemap: Draw<BlitterMode::CrashRemap>(bp, zoom); return;

View File

@ -17,6 +17,7 @@
enum class BlitterMode : uint8_t {
Normal, ///< Perform the simple blitting.
ColourRemap, ///< Perform a colour remapping.
RGBAColourRemap, ///< Perform a colour remapping.
Transparent, ///< Perform transparency darkening remapping.
TransparentRemap, ///< Perform transparency colour remapping.
CrashRemap, ///< Perform a crash remapping.

View File

@ -983,13 +983,13 @@ Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
* @param pal The palette to get the blitter mode for.
* @return The blitter mode associated with the palette.
*/
static BlitterMode GetBlitterMode(PaletteID pal)
static BlitterMode GetBlitterMode(PaletteID pal, const RecolourSprite &recolour)
{
switch (pal) {
case PAL_NONE: return BlitterMode::Normal;
case PALETTE_CRASH: return BlitterMode::CrashRemap;
case PALETTE_ALL_BLACK: return BlitterMode::BlackRemap;
default: return BlitterMode::ColourRemap;
default: return recolour.IsRGB() && BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32 ? BlitterMode::RGBAColourRemap : BlitterMode::ColourRemap;
}
}
@ -1014,7 +1014,7 @@ void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSpri
} else {
_colour_remap_ptr = GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH));
}
GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite);
GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal, *_colour_remap_ptr), sub, real_sprite);
} else {
GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite);
}
@ -1042,7 +1042,7 @@ void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub,
} else {
_colour_remap_ptr = GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH));
}
GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite, zoom);
GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal, *_colour_remap_ptr), sub, real_sprite, zoom);
} else {
GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, BlitterMode::Normal, sub, real_sprite, zoom);
}

View File

@ -556,10 +556,18 @@ void SetupColoursAndInitialWindow()
const RecolourSprite *rs = GetRecolourSprite(GetColourPalette(i));
assert(rs != nullptr);
const uint8_t *remap = rs->GetPaletteRemap();
if (rs->IsRGB()) {
const Colour *rgba_remap = rs->GetRGBARemap();
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
const Colour &c = rgba_remap[j];
SetColourGradient(i, j, PixelColour{remap[0xC6 + j], c});
}
} else {
for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
SetColourGradient(i, j, PixelColour{remap[0xC6 + j]});
}
}
}
new MainWindow(_main_window_desc);

View File

@ -427,6 +427,19 @@ void RecolourSprite::Read(SpriteFile &file, size_t entries)
}
}
void RecolourSpriteRGBA::Read(SpriteFile &file, size_t entries)
{
this->RecolourSprite::Read(file, entries);
/* Colour is byte-arranged differently by platform, so read components individually. */
for (uint i = 0; i < entries; ++i) {
this->rgba[i].r = file.ReadByte();
this->rgba[i].g = file.ReadByte();
this->rgba[i].b = file.ReadByte();
this->rgba[i].a = file.ReadByte();
}
}
/**
* Load a recolour sprite into memory.
* @param file GRF we're reading from.
@ -450,7 +463,8 @@ static void *ReadRecolourSprite(SpriteFile &file, size_t file_pos, uint num, Spr
entries = num;
}
RecolourSprite *rs = allocator.New<RecolourSprite>();
num -= entries;
RecolourSprite *rs = (num == entries * 4) ? allocator.New<RecolourSpriteRGBA>() : allocator.New<RecolourSprite>();
rs->Read(file, entries);
return rs;
}

View File

@ -11,6 +11,7 @@
#define SPRITECACHE_TYPE_H
#include "core/enum_type.hpp"
#include "gfx_type.h"
#include "spriteloader/sprite_file_type.hpp"
/** Data structure describing a sprite. */
@ -27,12 +28,24 @@ class RecolourSprite {
public:
static constexpr size_t PALETTE_SIZE = 256; ///< Number of entries in a recolour sprite.
void Read(SpriteFile &file, size_t entries);
virtual ~RecolourSprite() {}
virtual void Read(SpriteFile &file, size_t entries);
inline const uint8_t *GetPaletteRemap() const { return this->palette.data(); }
virtual bool IsRGB() const { return false; }
virtual const Colour *GetRGBARemap() const { return nullptr; }
std::array<uint8_t, PALETTE_SIZE> palette{}; ///< Palette index remap, mapping from one palette index to another.
};
class RecolourSpriteRGBA : public RecolourSprite {
public:
void Read(SpriteFile &file, size_t entries) override;
bool IsRGB() const override { return true; }
const Colour *GetRGBARemap() const override { return this->rgba.data(); }
std::array<Colour, PALETTE_SIZE> rgba{}; ///< RGBA remap, mapping from palette index to colour.
};
enum class SpriteCacheCtrlFlag : uint8_t {
AllowZoomMin1xPal, ///< Allow use of sprite min zoom setting at 1x in palette mode.
AllowZoomMin1x32bpp, ///< Allow use of sprite min zoom setting at 1x in 32bpp mode.