From 36d14669c5baa7fd17291856d0d77c9bcfc9deac Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 25 Jul 2025 08:56:46 +0100 Subject: [PATCH] Codechange: Use structure to hold recolour remap sprite. --- src/blitter/32bpp_anim.cpp | 2 +- src/blitter/32bpp_anim_sse4.cpp | 2 +- src/blitter/32bpp_optimized.cpp | 2 +- src/blitter/32bpp_simple.cpp | 7 ++-- src/blitter/32bpp_sse_func.hpp | 2 +- src/blitter/40bpp_anim.cpp | 8 +++-- src/blitter/8bpp_base.cpp | 2 +- src/blitter/8bpp_optimized.cpp | 4 +-- src/blitter/8bpp_simple.cpp | 5 +-- src/blitter/base.hpp | 2 +- src/gfx.cpp | 20 +++++------ src/main_gui.cpp | 7 ++-- src/spritecache.cpp | 56 ++++++++++++++++++++----------- src/spritecache.h | 5 ++- src/spritecache_type.h | 12 +++++++ src/spriteloader/spriteloader.hpp | 12 +++++++ src/video/opengl.cpp | 2 +- 17 files changed, 97 insertions(+), 53 deletions(-) diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 797b0db738..6a030fbf01 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -36,7 +36,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left; 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; // store so we don't have to access it via bp every time + const uint8_t *remap = bp->remap->GetPaletteRemap(); // store so we don't have to access it via bp every time for (int y = 0; y < bp->height; y++) { Colour *dst_ln = dst + bp->pitch; diff --git a/src/blitter/32bpp_anim_sse4.cpp b/src/blitter/32bpp_anim_sse4.cpp index 4ed29b33c1..720ba7daad 100644 --- a/src/blitter/32bpp_anim_sse4.cpp +++ b/src/blitter/32bpp_anim_sse4.cpp @@ -33,7 +33,7 @@ template remap; + const uint8_t * const remap = bp->remap->GetPaletteRemap(); Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left; uint16_t *anim_line = this->anim_buf + this->ScreenToAnimOffset((uint32_t *)bp->dst) + bp->top * this->anim_buf_pitch + bp->left; int effective_width = bp->width; diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp index 7667597636..fa2d1369c9 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -48,7 +48,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left; /* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */ - const uint8_t *remap = bp->remap; + const uint8_t *remap = bp->remap->GetPaletteRemap(); for (int y = 0; y < bp->height; y++) { /* next dst line begins here */ diff --git a/src/blitter/32bpp_simple.cpp b/src/blitter/32bpp_simple.cpp index d3fe2f98ca..04ac7b6a66 100644 --- a/src/blitter/32bpp_simple.cpp +++ b/src/blitter/32bpp_simple.cpp @@ -23,6 +23,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(); /* 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); @@ -42,7 +43,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo if (src->m == 0) { if (src->a != 0) *dst = ComposeColourRGBA(src->r, src->g, src->b, src->a, *dst); } else { - if (bp->remap[src->m] != 0) *dst = ComposeColourPA(AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst); + if (remap[src->m] != 0) *dst = ComposeColourPA(AdjustBrightness(this->LookupColourInPalette(remap[src->m]), src->v), src->a, *dst); } break; @@ -53,7 +54,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo *dst = ComposeColourRGBA(g, g, g, src->a, *dst); } } else { - if (bp->remap[src->m] != 0) *dst = ComposeColourPA(AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst); + if (remap[src->m] != 0) *dst = ComposeColourPA(AdjustBrightness(this->LookupColourInPalette(remap[src->m]), src->v), src->a, *dst); } break; @@ -73,7 +74,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ if (src->a != 0) { - *dst = this->LookupColourInPalette(bp->remap[GetNearestColourIndex(*dst)]); + *dst = this->LookupColourInPalette(remap[GetNearestColourIndex(*dst)]); } break; diff --git a/src/blitter/32bpp_sse_func.hpp b/src/blitter/32bpp_sse_func.hpp index bed92686cc..8afdcfdbcd 100644 --- a/src/blitter/32bpp_sse_func.hpp +++ b/src/blitter/32bpp_sse_func.hpp @@ -221,7 +221,7 @@ inline void Blitter_32bppSSSE3::Draw(const Blitter::BlitterParams *bp, ZoomLevel inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) #endif { - const uint8_t * const remap = bp->remap; + const uint8_t * const remap = bp->remap->GetPaletteRemap(); Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left; int effective_width = bp->width; diff --git a/src/blitter/40bpp_anim.cpp b/src/blitter/40bpp_anim.cpp index 6e6d1233fa..52c2ec7678 100644 --- a/src/blitter/40bpp_anim.cpp +++ b/src/blitter/40bpp_anim.cpp @@ -114,7 +114,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel uint8_t *anim = VideoDriver::GetInstance()->GetAnimBuffer() + ((uint32_t *)bp->dst - (uint32_t *)_screen.dst_ptr) + bp->top * bp->pitch + bp->left; /* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */ - const uint8_t *remap = bp->remap; + const uint8_t *remap = bp->remap->GetPaletteRemap(); for (int y = 0; y < bp->height; y++) { /* next dst line begins here */ @@ -374,7 +374,8 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height, anim = anim - width + _screen.pitch; } while (--height); } else if (pal == PALETTE_NEWSPAPER) { - const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1; + const RecolourSprite *rs = GetRecolourSprite(pal); + const uint8_t *remap = rs->GetPaletteRemap(); do { for (int i = 0; i != width; i++) { if (*anim == 0) { @@ -389,7 +390,8 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height, anim = anim - width + _screen.pitch; } while (--height); } else { - const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1; + const RecolourSprite *rs = GetRecolourSprite(pal); + const uint8_t *remap = rs->GetPaletteRemap(); do { for (int i = 0; i != width; i++) { if (*anim != 0) *anim = remap[*anim]; diff --git a/src/blitter/8bpp_base.cpp b/src/blitter/8bpp_base.cpp index ae7858a17f..7e48c86ebf 100644 --- a/src/blitter/8bpp_base.cpp +++ b/src/blitter/8bpp_base.cpp @@ -16,7 +16,7 @@ void Blitter_8bppBase::DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) { - const uint8_t *ctab = GetNonSprite(pal, SpriteType::Recolour) + 1; + const uint8_t *ctab = GetRecolourSprite(pal)->GetPaletteRemap(); do { for (int i = 0; i != width; i++) *((uint8_t *)dst + i) = ctab[((uint8_t *)dst)[i]]; diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 5cc16d975a..86cc487f13 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -85,7 +85,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z switch (mode) { case BlitterMode::ColourRemap: case BlitterMode::CrashRemap: { - const uint8_t *remap = bp->remap; + const uint8_t *remap = bp->remap->GetPaletteRemap(); do { uint m = remap[*src]; if (m != 0) *dst = m; @@ -101,7 +101,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z case BlitterMode::Transparent: case BlitterMode::TransparentRemap: { - const uint8_t *remap = bp->remap; + const uint8_t *remap = bp->remap->GetPaletteRemap(); src += pixels; do { *dst = remap[*dst]; diff --git a/src/blitter/8bpp_simple.cpp b/src/blitter/8bpp_simple.cpp index 54cb9ee404..b47699b139 100644 --- a/src/blitter/8bpp_simple.cpp +++ b/src/blitter/8bpp_simple.cpp @@ -20,6 +20,7 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom { const uint8_t *src, *src_line; uint8_t *dst, *dst_line; + const uint8_t *remap = bp->remap->GetPaletteRemap(); /* Find where to start reading in the source sprite */ src_line = (const uint8_t *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, zoom); @@ -38,12 +39,12 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom switch (mode) { case BlitterMode::ColourRemap: case BlitterMode::CrashRemap: - colour = bp->remap[*src]; + colour = remap[*src]; break; case BlitterMode::Transparent: case BlitterMode::TransparentRemap: - if (*src != 0) colour = bp->remap[*dst]; + if (*src != 0) colour = remap[*dst]; break; case BlitterMode::BlackRemap: diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index 361cee3642..1139dc50d6 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -31,7 +31,7 @@ public: /** Parameters related to blitting. */ struct BlitterParams { const void *sprite; ///< Pointer to the sprite how ever the encoder stored it - const uint8_t *remap; ///< XXX -- Temporary storage for remap array + const RecolourSprite *remap; ///< Pointer to the RecolourSprite to use with remapping. int skip_left; ///< How much pixels of the source to skip on the left (based on zoom of dst) int skip_top; ///< How much pixels of the source to skip on the top (based on zoom of dst) diff --git a/src/gfx.cpp b/src/gfx.cpp index cf1340686d..66a3c7bee4 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -72,8 +72,8 @@ int _gui_scale_cfg; ///< GUI scale in config. * @ingroup dirty */ static Rect _invalid_rect; -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. +static const RecolourSprite *_colour_remap_ptr; +static RecolourSprite _string_colourremap; ///< RecolourSprite for string drawing. static const uint DIRTY_BLOCK_HEIGHT = 8; static const uint DIRTY_BLOCK_WIDTH = 64; @@ -480,9 +480,9 @@ 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 ? (uint8_t)colour : _string_colourmap[colour].p; - _string_colourremap[2] = no_shade ? 0 : 1; - _colour_remap_ptr = _string_colourremap; + _string_colourremap.palette[1] = raw_colour ? (uint8_t)colour : _string_colourmap[colour].p; + _string_colourremap.palette[2] = no_shade ? 0 : 1; + _colour_remap_ptr = &_string_colourremap; } /** @@ -640,7 +640,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, } if (underline) { - GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap[1]}); + GfxFillRect(left, y + h, right, y + h + WidgetDimensions::scaled.bevel.top - 1, PixelColour{_string_colourremap.GetPaletteRemap()[1]}); } return (align & SA_HOR_MASK) == SA_RIGHT ? left : right; @@ -1006,13 +1006,13 @@ void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSpri SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH); if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) { pal = GB(pal, 0, PALETTE_WIDTH); - _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1; + _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) { if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) { SetColourRemap((TextColour)GB(pal, 0, PALETTE_WIDTH)); } else { - _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1; + _colour_remap_ptr = GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH)); } GfxMainBlitterViewport(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite); } else { @@ -1034,13 +1034,13 @@ void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, SpriteID real_sprite = GB(img, 0, SPRITE_WIDTH); if (HasBit(img, PALETTE_MODIFIER_TRANSPARENT)) { pal = GB(pal, 0, PALETTE_WIDTH); - _colour_remap_ptr = GetNonSprite(pal, SpriteType::Recolour) + 1; + _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) { if (HasBit(pal, PALETTE_TEXT_RECOLOUR)) { SetColourRemap((TextColour)GB(pal, 0, PALETTE_WIDTH)); } else { - _colour_remap_ptr = GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1; + _colour_remap_ptr = GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH)); } GfxMainBlitter(GetSprite(real_sprite, SpriteType::Normal), x, y, GetBlitterMode(pal), sub, real_sprite, zoom); } else { diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 57e991b3ad..5e37f04710 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -553,10 +553,11 @@ void ShowSelectGameWindow(); void SetupColoursAndInitialWindow() { for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) { - const uint8_t *b = GetNonSprite(GetColourPalette(i), SpriteType::Recolour) + 1; - assert(b != nullptr); + const RecolourSprite *rs = GetRecolourSprite(GetColourPalette(i)); + assert(rs != nullptr); + const uint8_t *remap = rs->GetPaletteRemap(); for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) { - SetColourGradient(i, j, PixelColour{b[0xC6 + j]}); + SetColourGradient(i, j, PixelColour{remap[0xC6 + j]}); } } diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 4387a79a93..738afaf02a 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -406,6 +406,27 @@ static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels spr return true; } +/** + * Read recolour sprite data. + * @param file SpriteFile to read from. + * @param entries Number of entries to read. + */ +void RecolourSprite::Read(SpriteFile &file, size_t entries) +{ + assert(entries <= RecolourSprite::PALETTE_SIZE); + if (file.NeedsPaletteRemap()) { + /* Remapping from "Windows" to "DOS" requires a temporary buffer as entries are overwritten. */ + std::array tmp{}; + file.ReadBlock(tmp.data(), entries); + + for (uint i = 0; i < entries; ++i) { + this->palette[i] = _palmap_w2d[tmp[_palmap_d2w[i]]]; + } + } else { + file.ReadBlock(this->palette.data(), entries); + } +} + /** * Load a recolour sprite into memory. * @param file GRF we're reading from. @@ -416,31 +437,22 @@ static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels spr */ static void *ReadRecolourSprite(SpriteFile &file, size_t file_pos, uint num, SpriteAllocator &allocator) { - /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small - * number of recolour sprites that are 17 bytes that only exist in DOS - * GRFs which are the same as 257 byte recolour sprites, but with the last - * 240 bytes zeroed. */ - static const uint RECOLOUR_SPRITE_SIZE = 257; - uint8_t *dest = allocator.Allocate(std::max(RECOLOUR_SPRITE_SIZE, num)); - file.SeekTo(file_pos, SEEK_SET); - if (file.NeedsPaletteRemap()) { - uint8_t *dest_tmp = new uint8_t[std::max(RECOLOUR_SPRITE_SIZE, num)]; - /* Only a few recolour sprites are less than 257 bytes */ - if (num < RECOLOUR_SPRITE_SIZE) std::fill_n(dest_tmp, RECOLOUR_SPRITE_SIZE, 0); - file.ReadBlock(dest_tmp, num); + /* The first byte of the recolour sprite records the number of entries, with + * the caveat that as this is a single byte 256 is recorded as 0. */ + uint entries = file.ReadByte(); + if (entries == 0) entries = 256; + --num; - /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */ - for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) { - dest[i] = _palmap_w2d[dest_tmp[_palmap_d2w[i - 1] + 1]]; - } - delete[] dest_tmp; - } else { - file.ReadBlock(dest, num); + if (entries > num) { + Debug(grf, 1, "ReadRecolourSprite: Expected recolour sprite with {} entries but only {} present", entries, num); + entries = num; } - return dest; + RecolourSprite *rs = allocator.New(); + rs->Read(file, entries); + return rs; } /** @@ -784,6 +796,10 @@ void IncreaseSpriteLRU() void SpriteCache::ClearSpriteData() { + if (this->ptr == nullptr) return; + + if (this->type == SpriteType::Recolour) reinterpret_cast(this->ptr.get())->~RecolourSprite(); + _spritecache_bytes_used -= this->length; this->ptr.reset(); } diff --git a/src/spritecache.h b/src/spritecache.h index 4106622c1c..e91fa81eac 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -41,10 +41,9 @@ inline const Sprite *GetSprite(SpriteID sprite, SpriteType type) return (Sprite*)GetRawSprite(sprite, type); } -inline const uint8_t *GetNonSprite(SpriteID sprite, SpriteType type) +inline const RecolourSprite *GetRecolourSprite(SpriteID sprite) { - assert(type == SpriteType::Recolour); - return (uint8_t*)GetRawSprite(sprite, type); + return static_cast(GetRawSprite(sprite, SpriteType::Recolour)); } void GfxInitSpriteMem(); diff --git a/src/spritecache_type.h b/src/spritecache_type.h index a6ffbbc6a1..e41c9848c2 100644 --- a/src/spritecache_type.h +++ b/src/spritecache_type.h @@ -11,6 +11,7 @@ #define SPRITECACHE_TYPE_H #include "core/enum_type.hpp" +#include "spriteloader/sprite_file_type.hpp" /** Data structure describing a sprite. */ struct Sprite { @@ -21,6 +22,17 @@ struct Sprite { std::byte data[]; ///< Sprite data. }; +/** Data structure describing a palette remap. */ +class RecolourSprite { +public: + static constexpr size_t PALETTE_SIZE = 256; ///< Number of entries in a recolour sprite. + + void Read(SpriteFile &file, size_t entries); + inline const uint8_t *GetPaletteRemap() const { return this->palette.data(); } + + std::array palette{}; ///< Palette index remap, mapping from one palette index to another. +}; + 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. diff --git a/src/spriteloader/spriteloader.hpp b/src/spriteloader/spriteloader.hpp index fb36cb0294..6b349b0c3f 100644 --- a/src/spriteloader/spriteloader.hpp +++ b/src/spriteloader/spriteloader.hpp @@ -117,6 +117,18 @@ public: return static_cast(this->AllocatePtr(size)); } + /** + * Allocate memory and construct an object in the sprite cache. + * @tparam T Type to construct. + * @tparam Targs The constructor parameter types. + * @param args Parameters for the constructor. + */ + template + T *New(Targs &&... args) + { + return new (this->Allocate(sizeof(T))) T(std::forward(args)...); + } + protected: /** * Allocate memory for a sprite. diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index 949c17eaea..70541197a9 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -1290,7 +1290,7 @@ void OpenGLBackend::RenderOglSprite(const OpenGLSprite *gl_sprite, PaletteID pal _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, OpenGLSprite::pal_pbo); _glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - _glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, 256, GetNonSprite(GB(pal, 0, PALETTE_WIDTH), SpriteType::Recolour) + 1); + _glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, 256, GetRecolourSprite(GB(pal, 0, PALETTE_WIDTH))->GetPaletteRemap()); _glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RED, GL_UNSIGNED_BYTE, nullptr); _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);