diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 5006305789..8055f8b10f 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -21,12 +21,12 @@ static FBlitter_32bppAnim iFBlitter_32bppAnim; template -inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck) { const SpriteData *src = (const SpriteData *)bp->sprite; - const Colour *src_px = reinterpret_cast(src->data + src->offset[0][zoom]); - const uint16_t *src_n = reinterpret_cast(src->data + src->offset[1][zoom]); + const Colour *src_px = reinterpret_cast(src->data + src->offset[0][sck]); + const uint16_t *src_n = reinterpret_cast(src->data + src->offset[1][sck]); for (uint i = bp->skip_top; i != 0; i--) { src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); @@ -261,22 +261,22 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } } -void Blitter_32bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { if (_screen_disable_anim) { /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */ - Blitter_32bppOptimized::Draw(bp, mode, zoom); + Blitter_32bppOptimized::Draw(bp, mode, sck); return; } switch (mode) { default: NOT_REACHED(); - case BlitterMode::Normal: Draw(bp, zoom); return; - case BlitterMode::ColourRemap: Draw(bp, zoom); return; - case BlitterMode::Transparent: Draw(bp, zoom); return; - case BlitterMode::TransparentRemap: Draw(bp, zoom); return; - case BlitterMode::CrashRemap: Draw(bp, zoom); return; - case BlitterMode::BlackRemap: Draw(bp, zoom); return; + case BlitterMode::Normal: Draw(bp, sck); return; + case BlitterMode::ColourRemap: Draw(bp, sck); return; + case BlitterMode::Transparent: Draw(bp, sck); return; + case BlitterMode::TransparentRemap: Draw(bp, sck); return; + case BlitterMode::CrashRemap: Draw(bp, sck); return; + case BlitterMode::BlackRemap: Draw(bp, sck); return; } } diff --git a/src/blitter/32bpp_anim.hpp b/src/blitter/32bpp_anim.hpp index acc911ffe0..eafdabbf56 100644 --- a/src/blitter/32bpp_anim.hpp +++ b/src/blitter/32bpp_anim.hpp @@ -32,7 +32,7 @@ public: this->palette = _cur_palette; } - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; void SetPixel(void *video, int x, int y, uint8_t colour) override; void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8_t colour, int width, int dash) override; @@ -64,7 +64,7 @@ public: return across + (lines * this->anim_buf_pitch); } - template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); + template void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); }; /** Factory for the 32bpp blitter with animation. */ diff --git a/src/blitter/32bpp_anim_sse4.cpp b/src/blitter/32bpp_anim_sse4.cpp index 4ed29b33c1..aeac9bcb8f 100644 --- a/src/blitter/32bpp_anim_sse4.cpp +++ b/src/blitter/32bpp_anim_sse4.cpp @@ -26,12 +26,12 @@ static FBlitter_32bppSSE4_Anim iFBlitter_32bppSSE4_Anim; * * @tparam mode blitter mode * @param bp further blitting parameters - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ IGNORE_UNINITIALIZED_WARNING_START template GNU_TARGET("sse4.1") -inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, SpriteCollKey sck) { const uint8_t * const remap = bp->remap; Colour *dst_line = (Colour *) bp->dst + bp->top * bp->pitch + bp->left; @@ -40,7 +40,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, ZoomLevel zoom /* Find where to start reading in the source sprite. */ const Blitter_32bppSSE_Base::SpriteData * const sd = (const Blitter_32bppSSE_Base::SpriteData *) bp->sprite; - const SpriteInfo * const si = &sd->infos[zoom]; + const SpriteInfo *const si = &sd->infos[sck]; const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width; const Colour *src_rgba_line = (const Colour *) ((const uint8_t *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size); @@ -380,13 +380,13 @@ IGNORE_UNINITIALIZED_WARNING_STOP * * @param bp further blitting parameters * @param mode blitter mode - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ -void Blitter_32bppSSE4_Anim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppSSE4_Anim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { if (_screen_disable_anim) { /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */ - Blitter_32bppSSE4::Draw(bp, mode, zoom); + Blitter_32bppSSE4::Draw(bp, mode, sck); return; } @@ -397,24 +397,24 @@ bm_normal: if (bp->skip_left != 0 || bp->width <= MARGIN_NORMAL_THRESHOLD) { const BlockType bt_last = (BlockType) (bp->width & 1); if (bt_last == BT_EVEN) { - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); } else { - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); } } else { #ifdef POINTER_IS_64BIT if (sprite_flags.Test(SpriteFlag::Translucent)) { - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); } else { - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); } #else - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); #endif } break; @@ -422,17 +422,17 @@ bm_normal: case BlitterMode::ColourRemap: if (sprite_flags.Test(SpriteFlag::NoRemap)) goto bm_normal; if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) { - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); } else { - if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, sck); + else Draw(bp, sck); } break; - case BlitterMode::Transparent: Draw(bp, zoom); return; - case BlitterMode::TransparentRemap: Draw(bp, zoom); return; - case BlitterMode::CrashRemap: Draw(bp, zoom); return; - case BlitterMode::BlackRemap: Draw(bp, zoom); return; + case BlitterMode::Transparent: Draw(bp, sck); return; + case BlitterMode::TransparentRemap: Draw(bp, sck); return; + case BlitterMode::CrashRemap: Draw(bp, sck); return; + case BlitterMode::BlackRemap: Draw(bp, sck); return; } } diff --git a/src/blitter/32bpp_anim_sse4.hpp b/src/blitter/32bpp_anim_sse4.hpp index c325fb806c..aef968d988 100644 --- a/src/blitter/32bpp_anim_sse4.hpp +++ b/src/blitter/32bpp_anim_sse4.hpp @@ -37,12 +37,12 @@ private: public: template - void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override { - return Blitter_32bppSSE_Base::Encode(sprite_type, sprite, allocator); + return Blitter_32bppSSE_Base::Encode(sprite_type, sprite, has_rtl, allocator); } std::string_view GetName() override { return "32bpp-sse4-anim"; } using Blitter_32bppSSE2_Anim::LookupColourInPalette; diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp index 7667597636..c69cb706fd 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -23,20 +23,20 @@ static FBlitter_32bppOptimized iFBlitter_32bppOptimized; * * @tparam mode blitter mode * @param bp further blitting parameters - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ template -inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck) { const SpriteData *src = (const SpriteData *)bp->sprite; /* src_px : each line begins with uint32_t n = 'number of bytes in this line', * then n times is the Colour struct for this line */ - const Colour *src_px = reinterpret_cast(src->data + src->offset[0][zoom]); + const Colour *src_px = reinterpret_cast(src->data + src->offset[0][sck]); /* src_n : each line begins with uint32_t n = 'number of bytes in this line', * then interleaved stream of 'm' and 'n' channels. 'm' is remap, * 'n' is number of bytes with the same alpha channel class */ - const uint16_t *src_n = reinterpret_cast(src->data + src->offset[1][zoom]); + const uint16_t *src_n = reinterpret_cast(src->data + src->offset[1][sck]); /* skip upper lines in src_px and src_n */ for (uint i = bp->skip_top; i != 0; i--) { @@ -257,36 +257,36 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } template -void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { switch (mode) { default: NOT_REACHED(); - case BlitterMode::Normal: Draw(bp, zoom); return; - case BlitterMode::ColourRemap: Draw(bp, zoom); return; - case BlitterMode::Transparent: Draw(bp, zoom); return; - case BlitterMode::TransparentRemap: Draw(bp, zoom); return; - case BlitterMode::CrashRemap: Draw(bp, zoom); return; - case BlitterMode::BlackRemap: Draw(bp, zoom); return; + case BlitterMode::Normal: Draw(bp, sck); return; + case BlitterMode::ColourRemap: Draw(bp, sck); return; + case BlitterMode::Transparent: Draw(bp, sck); return; + case BlitterMode::TransparentRemap: Draw(bp, sck); return; + case BlitterMode::CrashRemap: Draw(bp, sck); return; + case BlitterMode::BlackRemap: Draw(bp, sck); return; } } -template void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); -template void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); +template void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck); +template void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck); /** * Draws a sprite to a (screen) buffer. Calls adequate templated function. * * @param bp further blitting parameters * @param mode blitter mode - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ -void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { - this->Draw(bp, mode, zoom); + this->Draw(bp, mode, sck); } template -Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) { /* streams of pixels (a, r, g, b channels) * @@ -316,16 +316,16 @@ Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const Spr if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max; } - for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { - const SpriteLoader::Sprite *src_orig = &sprite[z]; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + const SpriteLoader::Sprite *src_orig = &sprite[sck]; uint size = src_orig->height * src_orig->width; - dst_px_orig[z] = std::make_unique(size + src_orig->height * 2); - dst_n_orig[z] = std::make_unique(size * 2 + src_orig->height * 4 * 2); + dst_px_orig[sck] = std::make_unique(size + src_orig->height * 2); + dst_n_orig[sck] = std::make_unique(size * 2 + src_orig->height * 4 * 2); - uint32_t *dst_px_ln = reinterpret_cast(dst_px_orig[z].get()); - uint32_t *dst_n_ln = reinterpret_cast(dst_n_orig[z].get()); + uint32_t *dst_px_ln = reinterpret_cast(dst_px_orig[sck].get()); + uint32_t *dst_n_ln = reinterpret_cast(dst_n_orig[sck].get()); const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data; @@ -406,43 +406,50 @@ Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const Spr dst_n_ln = (uint32_t *)dst_n; } - lengths[0][z] = reinterpret_cast(dst_px_ln) - reinterpret_cast(dst_px_orig[z].get()); // all are aligned to 4B boundary - lengths[1][z] = reinterpret_cast(dst_n_ln) - reinterpret_cast(dst_n_orig[z].get()); + lengths[0][sck] = reinterpret_cast(dst_px_ln) - reinterpret_cast(dst_px_orig[sck].get()); // all are aligned to 4B boundary + lengths[1][sck] = reinterpret_cast(dst_n_ln) - reinterpret_cast(dst_n_orig[sck].get()); } uint len = 0; // total length of data - for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { - len += lengths[0][z] + lengths[1][z]; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + len += lengths[0][sck] + lengths[1][sck]; } Sprite *dest_sprite = allocator.Allocate(sizeof(*dest_sprite) + sizeof(SpriteData) + len); - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); dest_sprite->height = root_sprite.height; dest_sprite->width = root_sprite.width; dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->y_offs = root_sprite.y_offs; + dest_sprite->has_rtl = has_rtl; SpriteData *dst = (SpriteData *)dest_sprite->data; uint32_t offset = 0; - for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { - dst->offset[0][z] = offset; - offset += lengths[0][z]; - dst->offset[1][z] = offset; - offset += lengths[1][z]; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + dst->offset[0][sck] = offset; + offset += lengths[0][sck]; + dst->offset[1][sck] = offset; + offset += lengths[1][sck]; + if (!has_rtl) { + /* Duplicate the sprite for RTL */ + SpriteCollKey rtl{sck.zoom, true}; + dst->offset[0][rtl] = dst->offset[0][sck]; + dst->offset[1][rtl] = dst->offset[1][sck]; + } - std::copy_n(reinterpret_cast(dst_px_orig[z].get()), lengths[0][z], dst->data + dst->offset[0][z]); - std::copy_n(reinterpret_cast(dst_n_orig[z].get()), lengths[1][z], dst->data + dst->offset[1][z]); + std::copy_n(reinterpret_cast(dst_px_orig[sck].get()), lengths[0][sck], dst->data + dst->offset[0][sck]); + std::copy_n(reinterpret_cast(dst_n_orig[sck].get()), lengths[1][sck], dst->data + dst->offset[1][sck]); } return dest_sprite; } -template Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); -template Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); +template Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator); +template Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator); -Sprite *Blitter_32bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_32bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) { - return this->EncodeInternal(sprite_type, sprite, allocator); + return this->EncodeInternal(sprite_type, sprite, has_rtl, allocator); } diff --git a/src/blitter/32bpp_optimized.hpp b/src/blitter/32bpp_optimized.hpp index c1a8a37cd2..9137e0a37d 100644 --- a/src/blitter/32bpp_optimized.hpp +++ b/src/blitter/32bpp_optimized.hpp @@ -21,16 +21,16 @@ public: uint8_t data[]; ///< Data, all zoomlevels. }; - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; std::string_view GetName() override { return "32bpp-optimized"; } - template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); + template void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); protected: - template void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); - template Sprite *EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); + template void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck); + template Sprite *EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator); }; /** Factory for the optimised 32 bpp blitter (without palette animation). */ diff --git a/src/blitter/32bpp_simple.cpp b/src/blitter/32bpp_simple.cpp index d3fe2f98ca..97341600a1 100644 --- a/src/blitter/32bpp_simple.cpp +++ b/src/blitter/32bpp_simple.cpp @@ -19,13 +19,13 @@ /** Instantiation of the simple 32bpp blitter factory. */ static FBlitter_32bppSimple iFBlitter_32bppSimple; -void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { const Blitter_32bppSimple::Pixel *src, *src_line; Colour *dst, *dst_line; /* 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); + src_line = (const Blitter_32bppSimple::Pixel *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, sck.zoom); dst_line = (Colour *)bp->dst + bp->top * bp->pitch + bp->left; for (int y = 0; y < bp->height; y++) { @@ -33,7 +33,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo dst_line += bp->pitch; src = src_line; - src_line += bp->sprite_width * ScaleByZoom(1, zoom); + src_line += bp->sprite_width * ScaleByZoom(1, sck.zoom); for (int x = 0; x < bp->width; x++) { switch (mode) { @@ -82,7 +82,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo break; } dst++; - src += ScaleByZoom(1, zoom); + src += ScaleByZoom(1, sck.zoom); } } } @@ -115,9 +115,9 @@ void Blitter_32bppSimple::DrawColourMappingRect(void *dst, int width, int height Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal); } -Sprite *Blitter_32bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_32bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, bool, SpriteAllocator &allocator) { - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); Blitter_32bppSimple::Pixel *dst; Sprite *dest_sprite = allocator.Allocate(sizeof(*dest_sprite) + static_cast(root_sprite.height) * static_cast(root_sprite.width) * sizeof(*dst)); @@ -125,6 +125,7 @@ Sprite *Blitter_32bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollec dest_sprite->width = root_sprite.width; dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->y_offs = root_sprite.y_offs; + dest_sprite->has_rtl = false; dst = reinterpret_cast(dest_sprite->data); SpriteLoader::CommonPixel *src = reinterpret_cast(root_sprite.data); diff --git a/src/blitter/32bpp_simple.hpp b/src/blitter/32bpp_simple.hpp index 4bacba345f..3d049dc858 100644 --- a/src/blitter/32bpp_simple.hpp +++ b/src/blitter/32bpp_simple.hpp @@ -24,9 +24,9 @@ class Blitter_32bppSimple : public Blitter_32bppBase { uint8_t v; ///< Brightness-channel }; public: - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; std::string_view GetName() override { return "32bpp-simple"; } }; diff --git a/src/blitter/32bpp_sse2.cpp b/src/blitter/32bpp_sse2.cpp index 6fc08ca6cd..26fd962b6c 100644 --- a/src/blitter/32bpp_sse2.cpp +++ b/src/blitter/32bpp_sse2.cpp @@ -20,7 +20,7 @@ /** Instantiation of the SSE2 32bpp blitter factory. */ static FBlitter_32bppSSE2 iFBlitter_32bppSSE2; -Sprite *Blitter_32bppSSE_Base::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_32bppSSE_Base::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) { /* First uint32_t of a line = the number of transparent pixels from the left. * Second uint32_t of a line = the number of transparent pixels from the right. @@ -37,9 +37,9 @@ Sprite *Blitter_32bppSSE_Base::Encode(SpriteType sprite_type, const SpriteLoader /* Calculate sizes and allocate. */ SpriteData sd{}; uint all_sprites_size = 0; - for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { - const SpriteLoader::Sprite *src_sprite = &sprite[z]; - auto &info = sd.infos[z]; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + const SpriteLoader::Sprite *src_sprite = &sprite[sck]; + auto &info = sd.infos[sck]; info.sprite_width = src_sprite->width; info.sprite_offset = all_sprites_size; info.sprite_line_size = sizeof(Colour) * src_sprite->width + sizeof(uint32_t) * META_LENGTH; @@ -47,26 +47,33 @@ Sprite *Blitter_32bppSSE_Base::Encode(SpriteType sprite_type, const SpriteLoader const uint rgba_size = info.sprite_line_size * src_sprite->height; info.mv_offset = all_sprites_size + rgba_size; + if (!has_rtl) { + /* Duplicate the sprite for RTL */ + SpriteCollKey rtl{sck.zoom, true}; + sd.infos[rtl] = info; + } + const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height; all_sprites_size += rgba_size + mv_size; } Sprite *dst_sprite = allocator.Allocate(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size); - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); dst_sprite->height = root_sprite.height; dst_sprite->width = root_sprite.width; dst_sprite->x_offs = root_sprite.x_offs; dst_sprite->y_offs = root_sprite.y_offs; + dst_sprite->has_rtl = has_rtl; std::copy_n(reinterpret_cast(&sd), sizeof(SpriteData), dst_sprite->data); /* Copy colours and determine flags. */ bool has_remap = false; bool has_anim = false; bool has_translucency = false; - for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { - const SpriteLoader::Sprite *src_sprite = &sprite[z]; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + const SpriteLoader::Sprite *src_sprite = &sprite[sck]; const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data; - const auto &info = sd.infos[z]; + const auto &info = sd.infos[sck]; Colour *dst_rgba_line = reinterpret_cast(&dst_sprite->data[sizeof(SpriteData) + info.sprite_offset]); MapValue *dst_mv = reinterpret_cast(&dst_sprite->data[sizeof(SpriteData) + info.mv_offset]); for (uint y = src_sprite->height; y != 0; y--) { diff --git a/src/blitter/32bpp_sse2.hpp b/src/blitter/32bpp_sse2.hpp index 383ef41c39..5464b4a51c 100644 --- a/src/blitter/32bpp_sse2.hpp +++ b/src/blitter/32bpp_sse2.hpp @@ -77,19 +77,19 @@ public: uint8_t data[]; ///< Data, all zoomlevels. }; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator); }; /** The SSE2 32 bpp blitter (without palette animation). */ class Blitter_32bppSSE2 : public Blitter_32bppSimple, public Blitter_32bppSSE_Base { public: - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; template - void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); + void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override { - return Blitter_32bppSSE_Base::Encode(sprite_type, sprite, allocator); + return Blitter_32bppSSE_Base::Encode(sprite_type, sprite, has_rtl, allocator); } std::string_view GetName() override { return "32bpp-sse2"; } diff --git a/src/blitter/32bpp_sse4.hpp b/src/blitter/32bpp_sse4.hpp index a46e624b02..b049ed7d84 100644 --- a/src/blitter/32bpp_sse4.hpp +++ b/src/blitter/32bpp_sse4.hpp @@ -29,9 +29,9 @@ /** The SSE4 32 bpp blitter (without palette animation). */ class Blitter_32bppSSE4 : public Blitter_32bppSSSE3 { public: - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; template - void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); + void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); std::string_view GetName() override { return "32bpp-sse4"; } }; diff --git a/src/blitter/32bpp_sse_func.hpp b/src/blitter/32bpp_sse_func.hpp index bed92686cc..d1e22b861f 100644 --- a/src/blitter/32bpp_sse_func.hpp +++ b/src/blitter/32bpp_sse_func.hpp @@ -208,17 +208,17 @@ INTERNAL_LINKAGE inline __m128i AdjustBrightnessOfTwoPixels([[maybe_unused]] __m * * @tparam mode blitter mode * @param bp further blitting parameters - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ IGNORE_UNINITIALIZED_WARNING_START template GNU_TARGET(SSE_TARGET) #if (SSE_VERSION == 2) -inline void Blitter_32bppSSE2::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_32bppSSE2::Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck) #elif (SSE_VERSION == 3) -inline void Blitter_32bppSSSE3::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_32bppSSSE3::Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck) #elif (SSE_VERSION == 4) -inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck) #endif { const uint8_t * const remap = bp->remap; @@ -227,7 +227,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel /* Find where to start reading in the source sprite. */ const SpriteData * const sd = (const SpriteData *) bp->sprite; - const SpriteInfo * const si = &sd->infos[zoom]; + const SpriteInfo *const si = &sd->infos[sck]; const MapValue *src_mv_line = (const MapValue *) &sd->data[si->mv_offset] + bp->skip_top * si->sprite_width; const Colour *src_rgba_line = (const Colour *) ((const uint8_t *) &sd->data[si->sprite_offset] + bp->skip_top * si->sprite_line_size); @@ -453,14 +453,14 @@ IGNORE_UNINITIALIZED_WARNING_STOP * * @param bp further blitting parameters * @param mode blitter mode - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ #if (SSE_VERSION == 2) -void Blitter_32bppSSE2::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppSSE2::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) #elif (SSE_VERSION == 3) -void Blitter_32bppSSSE3::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppSSSE3::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) #elif (SSE_VERSION == 4) -void Blitter_32bppSSE4::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_32bppSSE4::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) #endif { switch (mode) { @@ -469,14 +469,14 @@ void Blitter_32bppSSE4::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomL bm_normal: const BlockType bt_last = (BlockType) (bp->width & 1); switch (bt_last) { - default: Draw(bp, zoom); return; - case BT_ODD: Draw(bp, zoom); return; + default: Draw(bp, sck); return; + case BT_ODD: Draw(bp, sck); return; } } else { if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags.Test(SpriteFlag::Translucent)) { - Draw(bp, zoom); + Draw(bp, sck); } else { - Draw(bp, zoom); + Draw(bp, sck); } return; } @@ -485,14 +485,14 @@ bm_normal: case BlitterMode::ColourRemap: if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags.Test(SpriteFlag::NoRemap)) goto bm_normal; if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) { - Draw(bp, zoom); return; + Draw(bp, sck); return; } else { - Draw(bp, zoom); return; + Draw(bp, sck); return; } - case BlitterMode::Transparent: Draw(bp, zoom); return; - case BlitterMode::TransparentRemap: Draw(bp, zoom); return; - case BlitterMode::CrashRemap: Draw(bp, zoom); return; - case BlitterMode::BlackRemap: Draw(bp, zoom); return; + case BlitterMode::Transparent: Draw(bp, sck); return; + case BlitterMode::TransparentRemap: Draw(bp, sck); return; + case BlitterMode::CrashRemap: Draw(bp, sck); return; + case BlitterMode::BlackRemap: Draw(bp, sck); return; } } #endif /* FULL_ANIMATION */ diff --git a/src/blitter/32bpp_sse_type.h b/src/blitter/32bpp_sse_type.h index 78d05e67e3..05dc512fe4 100644 --- a/src/blitter/32bpp_sse_type.h +++ b/src/blitter/32bpp_sse_type.h @@ -28,7 +28,7 @@ #endif #define META_LENGTH 2 ///< Number of uint32_t inserted before each line of pixels in a sprite. -#define MARGIN_NORMAL_THRESHOLD (zoom == ZoomLevel::Out8x ? 8 : 4) ///< Minimum width to use margins with BlitterMode::Normal. +#define MARGIN_NORMAL_THRESHOLD (sck.zoom == ZoomLevel::Out8x ? 8 : 4) ///< Minimum width to use margins with BlitterMode::Normal. #define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BlitterMode::ColourRemap. typedef union alignas(16) um128i { diff --git a/src/blitter/32bpp_ssse3.hpp b/src/blitter/32bpp_ssse3.hpp index 323b77d825..e9dbb34f91 100644 --- a/src/blitter/32bpp_ssse3.hpp +++ b/src/blitter/32bpp_ssse3.hpp @@ -29,9 +29,9 @@ /** The SSSE3 32 bpp blitter (without palette animation). */ class Blitter_32bppSSSE3 : public Blitter_32bppSSE2 { public: - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; template - void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); + void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); std::string_view GetName() override { return "32bpp-ssse3"; } }; diff --git a/src/blitter/40bpp_anim.cpp b/src/blitter/40bpp_anim.cpp index 7ef8cd4ba8..e99b0345ad 100644 --- a/src/blitter/40bpp_anim.cpp +++ b/src/blitter/40bpp_anim.cpp @@ -87,20 +87,20 @@ void Blitter_40bppAnim::DrawLine(void *video, int x, int y, int x2, int y2, int * * @tparam mode blitter mode * @param bp further blitting parameters - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ template -inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) +inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck) { const SpriteData *src = (const SpriteData *)bp->sprite; /* src_px : each line begins with uint32_t n = 'number of bytes in this line', * then n times is the Colour struct for this line */ - const Colour *src_px = reinterpret_cast(src->data + src->offset[0][zoom]); + const Colour *src_px = reinterpret_cast(src->data + src->offset[0][sck]); /* src_n : each line begins with uint32_t n = 'number of bytes in this line', * then interleaved stream of 'm' and 'n' channels. 'm' is remap, * 'n' is number of bytes with the same alpha channel class */ - const uint16_t *src_n = reinterpret_cast(src->data + src->offset[1][zoom]); + const uint16_t *src_n = reinterpret_cast(src->data + src->offset[1][sck]); /* skip upper lines in src_px and src_n */ for (uint i = bp->skip_top; i != 0; i--) { @@ -325,26 +325,26 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel * * @param bp further blitting parameters * @param mode blitter mode - * @param zoom zoom level at which we are drawing + * @param sck sprite collection key to draw */ -void Blitter_40bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_40bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { assert(_screen.dst_ptr != nullptr); if (_screen_disable_anim || VideoDriver::GetInstance()->GetAnimBuffer() == nullptr) { /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */ - Blitter_32bppOptimized::Draw(bp, mode, zoom); + Blitter_32bppOptimized::Draw(bp, mode, sck); return; } switch (mode) { default: NOT_REACHED(); - case BlitterMode::Normal: Draw(bp, zoom); return; - case BlitterMode::ColourRemap: Draw(bp, zoom); return; - case BlitterMode::Transparent: Draw(bp, zoom); return; - case BlitterMode::TransparentRemap: Draw(bp, zoom); return; - case BlitterMode::CrashRemap: Draw(bp, zoom); return; - case BlitterMode::BlackRemap: Draw(bp, zoom); return; + case BlitterMode::Normal: Draw(bp, sck); return; + case BlitterMode::ColourRemap: Draw(bp, sck); return; + case BlitterMode::Transparent: Draw(bp, sck); return; + case BlitterMode::TransparentRemap: Draw(bp, sck); return; + case BlitterMode::CrashRemap: Draw(bp, sck); return; + case BlitterMode::BlackRemap: Draw(bp, sck); return; } } @@ -400,9 +400,9 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height, } } -Sprite *Blitter_40bppAnim::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_40bppAnim::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) { - return this->EncodeInternal(sprite_type, sprite, allocator); + return this->EncodeInternal(sprite_type, sprite, has_rtl, allocator); } diff --git a/src/blitter/40bpp_anim.hpp b/src/blitter/40bpp_anim.hpp index 66990bae4e..8196501f08 100644 --- a/src/blitter/40bpp_anim.hpp +++ b/src/blitter/40bpp_anim.hpp @@ -25,16 +25,16 @@ public: void CopyToBuffer(const void *video, void *dst, int width, int height) override; void CopyImageToBuffer(const void *video, void *dst, int width, int height, int dst_pitch) override; void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; size_t BufferSize(uint width, uint height) override; Blitter::PaletteAnimation UsePaletteAnimation() override; bool NeedsAnimationBuffer() override; std::string_view GetName() override { return "40bpp-anim"; } - template void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); + template void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); protected: static inline Colour RealizeBlendedColour(uint8_t anim, Colour c) diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 5cc16d975a..caf628fcb2 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -18,11 +18,11 @@ /** Instantiation of the 8bpp optimised blitter factory. */ static FBlitter_8bppOptimized iFBlitter_8bppOptimized; -void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { /* Find the offset of this zoom-level */ const SpriteData *sprite_src = (const SpriteData *)bp->sprite; - uint offset = sprite_src->offset[zoom]; + uint offset = sprite_src->offset[sck]; /* Find where to start reading in the source sprite */ const uint8_t *src = sprite_src->data + offset; @@ -119,7 +119,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z } } -Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) { /* Make memory for all zoom-levels */ uint memory = sizeof(SpriteData); @@ -136,8 +136,8 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max; } - for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { - memory += sprite[i].width * sprite[i].height; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + memory += sprite[sck].width * sprite[sck].height; } /* We have no idea how much memory we really need, so just guess something */ @@ -151,11 +151,16 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader uint8_t *dst = temp_dst->data; /* Make the sprites per zoom-level */ - for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { - const SpriteLoader::Sprite &src_orig = sprite[i]; + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { + const SpriteLoader::Sprite &src_orig = sprite[sck]; /* Store the index table */ uint offset = dst - temp_dst->data; - temp_dst->offset[i] = offset; + temp_dst->offset[sck] = offset; + if (!has_rtl) { + /* Duplicate the sprite for RTL */ + SpriteCollKey rtl{sck.zoom, true}; + temp_dst->offset[rtl] = offset; + } /* cache values, because compiler can't cache it */ int scaled_height = src_orig.height; @@ -220,11 +225,12 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader /* Allocate the exact amount of memory we need */ Sprite *dest_sprite = allocator.Allocate(sizeof(*dest_sprite) + size); - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); dest_sprite->height = root_sprite.height; dest_sprite->width = root_sprite.width; dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->y_offs = root_sprite.y_offs; + dest_sprite->has_rtl = has_rtl; std::copy_n(reinterpret_cast(temp_dst), size, dest_sprite->data); return dest_sprite; diff --git a/src/blitter/8bpp_optimized.hpp b/src/blitter/8bpp_optimized.hpp index 240b6dbdd9..babea7649c 100644 --- a/src/blitter/8bpp_optimized.hpp +++ b/src/blitter/8bpp_optimized.hpp @@ -22,8 +22,8 @@ public: uint8_t data[]; ///< Data, all zoomlevels. }; - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; std::string_view GetName() override { return "8bpp-optimized"; } }; diff --git a/src/blitter/8bpp_simple.cpp b/src/blitter/8bpp_simple.cpp index 54cb9ee404..3f520824c0 100644 --- a/src/blitter/8bpp_simple.cpp +++ b/src/blitter/8bpp_simple.cpp @@ -16,13 +16,13 @@ /** Instantiation of the simple 8bpp blitter factory. */ static FBlitter_8bppSimple iFBlitter_8bppSimple; -void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) +void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) { const uint8_t *src, *src_line; uint8_t *dst, *dst_line; /* 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); + src_line = (const uint8_t *)bp->sprite + (bp->skip_top * bp->sprite_width + bp->skip_left) * ScaleByZoom(1, sck.zoom); dst_line = (uint8_t *)bp->dst + bp->top * bp->pitch + bp->left; for (int y = 0; y < bp->height; y++) { @@ -30,7 +30,7 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom dst_line += bp->pitch; src = src_line; - src_line += bp->sprite_width * ScaleByZoom(1, zoom); + src_line += bp->sprite_width * ScaleByZoom(1, sck.zoom); for (int x = 0; x < bp->width; x++) { uint colour = 0; @@ -56,14 +56,14 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom } if (colour != 0) *dst = colour; dst++; - src += ScaleByZoom(1, zoom); + src += ScaleByZoom(1, sck.zoom); } } } -Sprite *Blitter_8bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_8bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, bool, SpriteAllocator &allocator) { - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); Sprite *dest_sprite; dest_sprite = allocator.Allocate(sizeof(*dest_sprite) + static_cast(root_sprite.height) * static_cast(root_sprite.width)); @@ -71,6 +71,7 @@ Sprite *Blitter_8bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollect dest_sprite->width = root_sprite.width; dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->y_offs = root_sprite.y_offs; + dest_sprite->has_rtl = false; /* Copy over only the 'remap' channel, as that is what we care about in 8bpp */ uint8_t *dst = reinterpret_cast(dest_sprite->data); diff --git a/src/blitter/8bpp_simple.hpp b/src/blitter/8bpp_simple.hpp index 9398456737..35957b8c55 100644 --- a/src/blitter/8bpp_simple.hpp +++ b/src/blitter/8bpp_simple.hpp @@ -16,8 +16,8 @@ /** Most trivial 8bpp blitter. */ class Blitter_8bppSimple final : public Blitter_8bppBase { public: - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; std::string_view GetName() override { return "8bpp-simple"; } }; diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index 2a31d1d0b0..4a56a0e752 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -67,7 +67,7 @@ public: /** * Draw an image to the screen, given an amount of params defined above. */ - virtual void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) = 0; + virtual void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) = 0; /** * Draw a colourtable to the screen. This is: the colour of the screen is read diff --git a/src/blitter/null.cpp b/src/blitter/null.cpp index 41b8a719b1..ef9f1f78ab 100644 --- a/src/blitter/null.cpp +++ b/src/blitter/null.cpp @@ -15,16 +15,17 @@ /** Instantiation of the null blitter factory. */ static FBlitter_Null iFBlitter_Null; -Sprite *Blitter_Null::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +Sprite *Blitter_Null::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, bool, SpriteAllocator &allocator) { Sprite *dest_sprite; dest_sprite = allocator.Allocate(sizeof(*dest_sprite)); - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); dest_sprite->height = root_sprite.height; dest_sprite->width = root_sprite.width; dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->y_offs = root_sprite.y_offs; + dest_sprite->has_rtl = false; return dest_sprite; } diff --git a/src/blitter/null.hpp b/src/blitter/null.hpp index 8e90ca9af8..7f0c8f3335 100644 --- a/src/blitter/null.hpp +++ b/src/blitter/null.hpp @@ -16,9 +16,9 @@ class Blitter_Null : public Blitter { public: uint8_t GetScreenDepth() override { return 0; } - void Draw(Blitter::BlitterParams *, BlitterMode, ZoomLevel) override {}; + void Draw(Blitter::BlitterParams *, BlitterMode, SpriteCollKey) override {}; void DrawColourMappingRect(void *, int, int, PaletteID) override {}; - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; void *MoveTo(void *, int, int) override { return nullptr; }; void SetPixel(void *, int, int, uint8_t) override {}; void DrawRect(void *, int, int, uint8_t) override {}; diff --git a/src/fontcache/freetypefontcache.cpp b/src/fontcache/freetypefontcache.cpp index 80bb2cda6d..19d2e90020 100644 --- a/src/fontcache/freetypefontcache.cpp +++ b/src/fontcache/freetypefontcache.cpp @@ -242,8 +242,8 @@ const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa) /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */ SpriteLoader::SpriteCollection spritecollection; - SpriteLoader::Sprite &sprite = spritecollection[ZoomLevel::Min]; - sprite.AllocateData(ZoomLevel::Min, static_cast(width) * height); + SpriteLoader::Sprite &sprite = spritecollection.Root(false); + sprite.AllocateData(SpriteCollKey::Root(false), static_cast(width) * height); sprite.colours = SpriteComponent::Palette; if (aa) sprite.colours.Set(SpriteComponent::Alpha); sprite.width = width; @@ -273,7 +273,7 @@ const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa) } UniquePtrSpriteAllocator allocator; - BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator); + BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, false, allocator); GlyphEntry new_glyph; new_glyph.data = std::move(allocator.data); diff --git a/src/gfx.cpp b/src/gfx.cpp index 6e12254374..b790def13f 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1167,7 +1167,7 @@ static void GfxBlitter(const Sprite * const sprite, int x, int y, BlitterMode mo } } - BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, zoom); + BlitterFactory::GetCurrentBlitter()->Draw(&bp, mode, SpriteCollKey{zoom, _current_text_dir == TD_RTL}); } /** diff --git a/src/os/macosx/font_osx.cpp b/src/os/macosx/font_osx.cpp index 6588eb7507..4516cb17b0 100644 --- a/src/os/macosx/font_osx.cpp +++ b/src/os/macosx/font_osx.cpp @@ -229,8 +229,8 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa) if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) UserError("Font glyph is too large"); SpriteLoader::SpriteCollection spritecollection; - SpriteLoader::Sprite &sprite = spritecollection[ZoomLevel::Min]; - sprite.AllocateData(ZoomLevel::Min, width * height); + SpriteLoader::Sprite &sprite = spritecollection.Root(false); + sprite.AllocateData(SpriteCollKey::Root(false), width * height); sprite.colours = SpriteComponent::Palette; if (use_aa) sprite.colours.Set(SpriteComponent::Alpha); sprite.width = width; @@ -278,7 +278,7 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa) } UniquePtrSpriteAllocator allocator; - BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator); + BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, false, allocator); GlyphEntry new_glyph; new_glyph.data = std::move(allocator.data); diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 6358ca4d3d..b7e14648c9 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -224,8 +224,8 @@ void Win32FontCache::ClearFontCache() /* GDI has rendered the glyph, now we allocate a sprite and copy the image into it. */ SpriteLoader::SpriteCollection spritecollection; - SpriteLoader::Sprite &sprite = spritecollection[ZoomLevel::Min]; - sprite.AllocateData(ZoomLevel::Min, width * height); + SpriteLoader::Sprite &sprite = spritecollection.Root(false); + sprite.AllocateData(SpriteCollKey::Root(false), width * height); sprite.colours = SpriteComponent::Palette; if (aa) sprite.colours.Set(SpriteComponent::Alpha); sprite.width = width; @@ -264,7 +264,7 @@ void Win32FontCache::ClearFontCache() } UniquePtrSpriteAllocator allocator; - BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator); + BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, false, allocator); GlyphEntry new_glyph; new_glyph.data = std::move(allocator.data); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index d5f8d50e19..02058cd191 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1414,6 +1414,7 @@ struct GameOptionsWindow : Window { CheckForMissingGlyphs(); ClearAllCachedNames(); UpdateAllVirtCoords(); + VideoDriver::GetInstance()->ClearSystemSprites(); // relevant if _current_text_dir changes CheckBlitter(); ReInitAllWindows(false); break; diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 5cf38cfb13..66f8afa1a0 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -204,9 +204,9 @@ SpriteID GetMaxSpriteID() return static_cast(_spritecache.size()); } -static bool ResizeSpriteIn(SpriteLoader::SpriteCollection &sprite, ZoomLevel src, ZoomLevel tgt) +static bool ResizeSpriteIn(SpriteLoader::SpriteCollection &sprite, SpriteCollKey src, SpriteCollKey tgt) { - uint8_t scaled_1 = AdjustByZoom(1, src - tgt); + uint8_t scaled_1 = AdjustByZoom(1, src.zoom - tgt.zoom); const auto &src_sprite = sprite[src]; auto &dest_sprite = sprite[tgt]; @@ -233,20 +233,20 @@ static bool ResizeSpriteIn(SpriteLoader::SpriteCollection &sprite, ZoomLevel src return true; } -static void ResizeSpriteOut(SpriteLoader::SpriteCollection &sprite, ZoomLevel zoom) +static void ResizeSpriteOut(SpriteLoader::SpriteCollection &sprite, SpriteCollKey sck) { - const auto &root_sprite = sprite.Root(); - const auto &src_sprite = sprite[zoom - 1]; - auto &dest_sprite = sprite[zoom]; + const auto &root_sprite = sprite.Root(sck.rtl); + const auto &src_sprite = sprite[SpriteCollKey{sck.zoom - 1, sck.rtl}]; + auto &dest_sprite = sprite[sck]; /* Algorithm based on 32bpp_Optimized::ResizeSprite() */ - dest_sprite.width = UnScaleByZoom(root_sprite.width, zoom); - dest_sprite.height = UnScaleByZoom(root_sprite.height, zoom); - dest_sprite.x_offs = UnScaleByZoom(root_sprite.x_offs, zoom); - dest_sprite.y_offs = UnScaleByZoom(root_sprite.y_offs, zoom); + dest_sprite.width = UnScaleByZoom(root_sprite.width, sck.zoom); + dest_sprite.height = UnScaleByZoom(root_sprite.height, sck.zoom); + dest_sprite.x_offs = UnScaleByZoom(root_sprite.x_offs, sck.zoom); + dest_sprite.y_offs = UnScaleByZoom(root_sprite.y_offs, sck.zoom); dest_sprite.colours = root_sprite.colours; - dest_sprite.AllocateData(zoom, static_cast(dest_sprite.height) * dest_sprite.width); + dest_sprite.AllocateData(sck, static_cast(dest_sprite.height) * dest_sprite.width); SpriteLoader::CommonPixel *dst = dest_sprite.data; const SpriteLoader::CommonPixel *src = src_sprite.data; @@ -269,7 +269,7 @@ static void ResizeSpriteOut(SpriteLoader::SpriteCollection &sprite, ZoomLevel zo } } -static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom) +static bool PadSingleSprite(SpriteLoader::Sprite *sprite, SpriteCollKey sck, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom) { uint width = sprite->width + pad_left + pad_right; uint height = sprite->height + pad_top + pad_bottom; @@ -279,7 +279,7 @@ static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint p /* Copy source data and reallocate sprite memory. */ size_t sprite_size = static_cast(sprite->width) * sprite->height; std::vector src_data(sprite->data, sprite->data + sprite_size); - sprite->AllocateData(zoom, static_cast(width) * height); + sprite->AllocateData(sck, static_cast(width) * height); /* Copy with padding to destination. */ SpriteLoader::CommonPixel *src = src_data.data(); @@ -318,25 +318,25 @@ static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint p return true; } -static bool PadSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels sprite_avail, SpriteEncoder *encoder) +static bool PadSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sprite_avail, SpriteEncoder *encoder) { /* Get minimum top left corner coordinates. */ int min_xoffs = INT32_MAX; int min_yoffs = INT32_MAX; - for (ZoomLevel zoom = ZoomLevel::Begin; zoom != ZoomLevel::End; zoom++) { - if (sprite_avail.Test(zoom)) { - min_xoffs = std::min(min_xoffs, ScaleByZoom(sprite[zoom].x_offs, zoom)); - min_yoffs = std::min(min_yoffs, ScaleByZoom(sprite[zoom].y_offs, zoom)); + for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max, true)) { + if (sprite_avail.Test(sck)) { + min_xoffs = std::min(min_xoffs, ScaleByZoom(sprite[sck].x_offs, sck.zoom)); + min_yoffs = std::min(min_yoffs, ScaleByZoom(sprite[sck].y_offs, sck.zoom)); } } /* Get maximum dimensions taking necessary padding at the top left into account. */ int max_width = INT32_MIN; int max_height = INT32_MIN; - for (ZoomLevel zoom = ZoomLevel::Begin; zoom != ZoomLevel::End; zoom++) { - if (sprite_avail.Test(zoom)) { - max_width = std::max(max_width, ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom), zoom)); - max_height = std::max(max_height, ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom), zoom)); + for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max, true)) { + if (sprite_avail.Test(sck)) { + max_width = std::max(max_width, ScaleByZoom(sprite[sck].width + sprite[sck].x_offs - UnScaleByZoom(min_xoffs, sck.zoom), sck.zoom)); + max_height = std::max(max_height, ScaleByZoom(sprite[sck].height + sprite[sck].y_offs - UnScaleByZoom(min_yoffs, sck.zoom), sck.zoom)); } } @@ -348,18 +348,18 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels sprite } /* Pad sprites where needed. */ - for (ZoomLevel zoom = ZoomLevel::Begin; zoom != ZoomLevel::End; zoom++) { - if (sprite_avail.Test(zoom)) { - auto &cur_sprite = sprite[zoom]; + for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max, true)) { + if (sprite_avail.Test(sck)) { + auto &cur_sprite = sprite[sck]; /* Scaling the sprite dimensions in the blitter is done with rounding up, * so a negative padding here is not an error. */ - int pad_left = std::max(0, cur_sprite.x_offs - UnScaleByZoom(min_xoffs, zoom)); - int pad_top = std::max(0, cur_sprite.y_offs - UnScaleByZoom(min_yoffs, zoom)); - int pad_right = std::max(0, UnScaleByZoom(max_width, zoom) - cur_sprite.width - pad_left); - int pad_bottom = std::max(0, UnScaleByZoom(max_height, zoom) - cur_sprite.height - pad_top); + int pad_left = std::max(0, cur_sprite.x_offs - UnScaleByZoom(min_xoffs, sck.zoom)); + int pad_top = std::max(0, cur_sprite.y_offs - UnScaleByZoom(min_yoffs, sck.zoom)); + int pad_right = std::max(0, UnScaleByZoom(max_width, sck.zoom) - cur_sprite.width - pad_left); + int pad_bottom = std::max(0, UnScaleByZoom(max_height, sck.zoom) - cur_sprite.height - pad_top); if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) { - if (!PadSingleSprite(&cur_sprite, zoom, pad_left, pad_top, pad_right, pad_bottom)) return false; + if (!PadSingleSprite(&cur_sprite, sck, pad_left, pad_top, pad_right, pad_bottom)) return false; } } } @@ -367,45 +367,56 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels sprite return true; } -static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels sprite_avail, SpriteEncoder *encoder) +static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sprite_avail, SpriteEncoder *encoder) { + assert(sprite_avail.AnyLtr()); + bool has_rtl = sprite_avail.AnyRtl(); + /* Create a fully zoomed image if it does not exist */ - ZoomLevel first_avail; - for (ZoomLevel zoom = ZoomLevel::Min; zoom <= ZoomLevel::Max; ++zoom) { - if (!sprite_avail.Test(zoom)) continue; - first_avail = zoom; - if (zoom != ZoomLevel::Min) { - if (!ResizeSpriteIn(sprite, zoom, ZoomLevel::Min)) return false; - sprite_avail.Set(ZoomLevel::Min); + ZoomLevel first_avail[2]; + for (bool rtl : {false, true}) { + if (rtl && !has_rtl) continue; + for (ZoomLevel zoom = ZoomLevel::Min; zoom <= ZoomLevel::Max; ++zoom) { + SpriteCollKey src_sck{zoom, rtl}; + if (!sprite_avail.Test(src_sck)) continue; + first_avail[rtl ? 1 : 0] = zoom; + if (zoom != ZoomLevel::Min) { + auto root_sck = SpriteCollKey::Root(rtl); + if (!ResizeSpriteIn(sprite, src_sck, root_sck)) return false; + sprite_avail.Set(root_sck); + } + break; } - break; } /* Pad sprites to make sizes match. */ if (!PadSprites(sprite, sprite_avail, encoder)) return false; /* Create other missing zoom levels */ - for (ZoomLevel zoom = ZoomLevel::Begin; zoom != ZoomLevel::End; zoom++) { - if (zoom == ZoomLevel::Min) continue; - - if (sprite_avail.Test(zoom)) { + for (auto sck : SpriteCollKeyRange(ZoomLevel::Min + 1, ZoomLevel::Max, has_rtl)) { + if (sprite_avail.Test(sck)) { /* Check that size and offsets match the fully zoomed image. */ - [[maybe_unused]] const auto &root_sprite = sprite[ZoomLevel::Min]; - [[maybe_unused]] const auto &dest_sprite = sprite[zoom]; - assert(dest_sprite.width == UnScaleByZoom(root_sprite.width, zoom)); - assert(dest_sprite.height == UnScaleByZoom(root_sprite.height, zoom)); - assert(dest_sprite.x_offs == UnScaleByZoom(root_sprite.x_offs, zoom)); - assert(dest_sprite.y_offs == UnScaleByZoom(root_sprite.y_offs, zoom)); + [[maybe_unused]] const auto &root_sprite = sprite.Root(sck.rtl); + [[maybe_unused]] const auto &dest_sprite = sprite[sck]; + assert(dest_sprite.width == UnScaleByZoom(root_sprite.width, sck.zoom)); + assert(dest_sprite.height == UnScaleByZoom(root_sprite.height, sck.zoom)); + assert(dest_sprite.x_offs == UnScaleByZoom(root_sprite.x_offs, sck.zoom)); + assert(dest_sprite.y_offs == UnScaleByZoom(root_sprite.y_offs, sck.zoom)); } else { /* Zoom level is not available, or unusable, so create it */ - ResizeSpriteOut(sprite, zoom); + ResizeSpriteOut(sprite, sck); } } /* Replace sprites with higher resolution than the desired maximum source resolution with scaled up sprites, if not already done. */ - if (first_avail < _settings_client.gui.sprite_zoom_min) { - for (ZoomLevel zoom = std::min(ZoomLevel::Normal, _settings_client.gui.sprite_zoom_min); zoom > ZoomLevel::Min; --zoom) { - ResizeSpriteIn(sprite, zoom, zoom - 1); + for (bool rtl : {false, true}) { + if (rtl && !has_rtl) continue; + if (first_avail[rtl ? 1 : 0] < _settings_client.gui.sprite_zoom_min) { + for (ZoomLevel zoom = std::min(ZoomLevel::Normal, _settings_client.gui.sprite_zoom_min); zoom > ZoomLevel::Min; --zoom) { + const SpriteCollKey src_sck{zoom, rtl}; + const SpriteCollKey dest_sck{zoom - 1, rtl}; + ResizeSpriteIn(sprite, src_sck, dest_sck); + } } } @@ -473,25 +484,25 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty Debug(sprite, 9, "Load sprite {}", id); SpriteLoader::SpriteCollection sprite; - ZoomLevels sprite_avail; - ZoomLevels avail_8bpp; - ZoomLevels avail_32bpp; + SpriteCollKeys sprite_avail; + SpriteCollKeys avail_8bpp; + SpriteCollKeys avail_32bpp; SpriteLoaderGrf sprite_loader(file.GetContainerVersion()); if (sprite_type != SpriteType::MapGen && encoder->Is32BppSupported()) { /* Try for 32bpp sprites first. */ sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, true, sc->control_flags, avail_8bpp, avail_32bpp); } - if (sprite_avail.None()) { + if (sprite_avail.NoLtr()) { sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, false, sc->control_flags, avail_8bpp, avail_32bpp); - if (sprite_type == SpriteType::Normal && avail_32bpp.Any() && !encoder->Is32BppSupported() && sprite_avail.None()) { + if (sprite_type == SpriteType::Normal && avail_32bpp.AnyLtr() && !encoder->Is32BppSupported() && sprite_avail.NoLtr()) { /* No 8bpp available, try converting from 32bpp. */ SpriteLoaderMakeIndexed make_indexed(sprite_loader); sprite_avail = make_indexed.LoadSprite(sprite, file, file_pos, sprite_type, true, sc->control_flags, sprite_avail, avail_32bpp); } } - if (sprite_avail.None()) { + if (sprite_avail.NoLtr()) { if (sprite_type == SpriteType::MapGen) return nullptr; if (id == SPR_IMG_QUERY) UserError("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?"); return (void*)GetRawSprite(SPR_IMG_QUERY, SpriteType::Normal, &allocator, encoder); @@ -507,7 +518,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty * Ugly: yes. Other solution: no. Blame the original author or * something ;) The image should really have been a data-stream * (so type = 0xFF basically). */ - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(false); uint num = root_sprite.width * root_sprite.height; Sprite *s = allocator.Allocate(sizeof(*s) + num); @@ -533,10 +544,12 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty if (sprite_type == SpriteType::Font && _font_zoom != ZoomLevel::Min) { /* Make ZoomLevel::Min be ZOOM_LVL_GUI */ - sprite[ZoomLevel::Min] = sprite[_font_zoom]; + SpriteCollKey min_sck{ZoomLevel::Min, false}; + SpriteCollKey font_sck{_font_zoom, false}; + sprite[min_sck] = sprite[font_sck]; } - return encoder->Encode(sprite_type, sprite, allocator); + return encoder->Encode(sprite_type, sprite, sprite_avail.AnyRtl(), allocator); } struct GrfSpriteOffset { diff --git a/src/spritecache.h b/src/spritecache.h index 388e2a64c2..ea973ef35b 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -19,6 +19,7 @@ struct Sprite { uint16_t width; ///< Width of the sprite. int16_t x_offs; ///< Number of pixels to shift the sprite to the right. int16_t y_offs; ///< Number of pixels to shift the sprite downwards. + bool has_rtl; ///< Whether the sprite is textdir aware. std::byte data[]; ///< Sprite data. }; diff --git a/src/spriteloader/grf.cpp b/src/spriteloader/grf.cpp index 57cb86a1a3..ffca888996 100644 --- a/src/spriteloader/grf.cpp +++ b/src/spriteloader/grf.cpp @@ -52,12 +52,12 @@ static bool WarnCorruptSprite(const SpriteFile &file, size_t file_pos, int line) * @param sprite_type Type of the sprite we're decoding. * @param num Size of the decompressed sprite. * @param type Type of the encoded sprite. - * @param zoom_lvl Requested zoom level. + * @param sck Key for allocator. * @param colour_fmt Colour format of the sprite. * @param container_format Container format of the GRF this sprite is in. * @return True if the sprite was successfully loaded. */ -bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, int64_t num, uint8_t type, ZoomLevel zoom_lvl, SpriteComponents colour_fmt, uint8_t container_format) +bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, int64_t num, uint8_t type, SpriteCollKey sck, SpriteComponents colour_fmt, uint8_t container_format) { /* * Original sprite height was max 255 pixels, with 4x extra zoom => 1020 pixels. @@ -101,7 +101,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t f if (num != 0) return WarnCorruptSprite(file, file_pos, __LINE__); - sprite->AllocateData(zoom_lvl, static_cast(sprite->width) * sprite->height); + sprite->AllocateData(sck, static_cast(sprite->width) * sprite->height); /* Convert colour depth to pixel size. */ int bpp = 0; @@ -215,7 +215,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t f return true; } -static ZoomLevels LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, ZoomLevels &avail_8bpp) +static SpriteCollKeys LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCollKeys &avail_8bpp) { /* Check the requested colour depth. */ if (load_32bpp) return {}; @@ -230,8 +230,8 @@ static ZoomLevels LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFil /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */ if (type == 0xFF) return {}; - ZoomLevel zoom_lvl = (sprite_type != SpriteType::MapGen) ? ZoomLevel::Normal : ZoomLevel::Min; - auto &dest_sprite = sprite[zoom_lvl]; + const SpriteCollKey sck{(sprite_type != SpriteType::MapGen) ? ZoomLevel::Normal : ZoomLevel::Min, false}; + auto &dest_sprite = sprite[sck]; dest_sprite.height = file.ReadByte(); dest_sprite.width = file.ReadWord(); @@ -248,15 +248,15 @@ static ZoomLevels LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFil * In case it is uncompressed, the size is 'num' - 8 (header-size). */ num = (type & 0x02) ? dest_sprite.width * dest_sprite.height : num - 8; - if (DecodeSingleSprite(&dest_sprite, file, file_pos, sprite_type, num, type, zoom_lvl, SpriteComponent::Palette, 1)) { - avail_8bpp.Set(zoom_lvl); + if (DecodeSingleSprite(&dest_sprite, file, file_pos, sprite_type, num, type, sck, SpriteComponent::Palette, 1)) { + avail_8bpp.Set(sck); return avail_8bpp; } return {}; } -static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) +static SpriteCollKeys LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, SpriteCollKeys &avail_8bpp, SpriteCollKeys &avail_32bpp) { static const ZoomLevel zoom_lvl_map[6] = {ZoomLevel::Normal, ZoomLevel::In4x, ZoomLevel::In2x, ZoomLevel::Out2x, ZoomLevel::Out4x, ZoomLevel::Out8x}; @@ -268,7 +268,7 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil uint32_t id = file.ReadDword(); - ZoomLevels loaded_sprites; + SpriteCollKeys loaded_sprites; do { int64_t num = file.ReadDword(); size_t start_pos = file.GetPos(); @@ -280,6 +280,7 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil SpriteComponents colour{type}; /* Mask out colour component information from type. */ type &= ~SpriteComponents::MASK; + bool rtl = type & 0x10; uint8_t zoom = file.ReadByte(); @@ -288,18 +289,18 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil if (sprite_type != SpriteType::MapGen) { if (zoom < lengthof(zoom_lvl_map)) { - ZoomLevel zoom_lvl = zoom_lvl_map[zoom]; - if (colour == SpriteComponent::Palette) avail_8bpp.Set(zoom_lvl); - if (colour != SpriteComponent::Palette) avail_32bpp.Set(zoom_lvl); + const SpriteCollKey sck{zoom_lvl_map[zoom], rtl}; + if (colour == SpriteComponent::Palette) avail_8bpp.Set(sck); + if (colour != SpriteComponent::Palette) avail_32bpp.Set(sck); is_wanted_zoom_lvl = true; ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZoomLevel::Min : _settings_client.gui.sprite_zoom_min; if (zoom_min >= ZoomLevel::In2x && - HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && zoom_lvl < ZoomLevel::In2x) { + HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && sck.zoom < ZoomLevel::In2x) { is_wanted_zoom_lvl = false; } if (zoom_min >= ZoomLevel::Normal && - HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && zoom_lvl < ZoomLevel::Normal) { + HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && sck.zoom < ZoomLevel::Normal) { is_wanted_zoom_lvl = false; } } else { @@ -309,17 +310,19 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil is_wanted_zoom_lvl = (zoom == 0); } - if (is_wanted_colour_depth && is_wanted_zoom_lvl) { - ZoomLevel zoom_lvl = (sprite_type != SpriteType::MapGen) ? zoom_lvl_map[zoom] : ZoomLevel::Min; + bool is_wanted_textdir = !rtl || sprite_type == SpriteType::Normal; - if (loaded_sprites.Test(zoom_lvl)) { + if (is_wanted_colour_depth && is_wanted_zoom_lvl && is_wanted_textdir) { + const SpriteCollKey sck{sprite_type != SpriteType::MapGen ? zoom_lvl_map[zoom] : ZoomLevel::Min, rtl}; + + if (loaded_sprites.Test(sck)) { /* We already have this zoom level, skip sprite. */ Debug(sprite, 1, "Ignoring duplicate zoom level sprite {} from {}", id, file.GetSimplifiedFilename()); file.SkipBytes(num - 2); continue; } - auto &dest_sprite = sprite[zoom_lvl]; + auto &dest_sprite = sprite[sck]; dest_sprite.height = file.ReadWord(); dest_sprite.width = file.ReadWord(); dest_sprite.x_offs = file.ReadWord(); @@ -342,13 +345,13 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil * otherwise we can calculate it from the image dimensions. */ uint decomp_size = (type & 0x08) ? file.ReadDword() : dest_sprite.width * dest_sprite.height * bpp; - bool valid = DecodeSingleSprite(&dest_sprite, file, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2); + bool valid = DecodeSingleSprite(&dest_sprite, file, file_pos, sprite_type, decomp_size, type, sck, colour, 2); if (file.GetPos() != start_pos + num) { WarnCorruptSprite(file, file_pos, __LINE__); return {}; } - if (valid) loaded_sprites.Set(zoom_lvl); + if (valid) loaded_sprites.Set(sck); } else { /* Not the wanted zoom level or colour depth, continue searching. */ file.SkipBytes(num - 2); @@ -359,7 +362,7 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil return loaded_sprites; } -ZoomLevels SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) +SpriteCollKeys SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, SpriteCollKeys &avail_8bpp, SpriteCollKeys &avail_32bpp) { if (this->container_ver >= 2) { return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, control_flags, avail_8bpp, avail_32bpp); diff --git a/src/spriteloader/grf.hpp b/src/spriteloader/grf.hpp index 2050a909eb..f97ed989f1 100644 --- a/src/spriteloader/grf.hpp +++ b/src/spriteloader/grf.hpp @@ -17,7 +17,7 @@ class SpriteLoaderGrf : public SpriteLoader { uint8_t container_ver; public: SpriteLoaderGrf(uint8_t container_ver) : container_ver(container_ver) {} - ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override; + SpriteCollKeys LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, SpriteCollKeys &avail_8bpp, SpriteCollKeys &avail_32bpp) override; }; #endif /* SPRITELOADER_GRF_HPP */ diff --git a/src/spriteloader/makeindexed.cpp b/src/spriteloader/makeindexed.cpp index fd5cb2c631..7e8c267fab 100644 --- a/src/spriteloader/makeindexed.cpp +++ b/src/spriteloader/makeindexed.cpp @@ -48,12 +48,12 @@ static void Convert32bppTo8bpp(SpriteLoader::Sprite &sprite) } } -ZoomLevels SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) +SpriteCollKeys SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, SpriteCollKeys &avail_8bpp, SpriteCollKeys &avail_32bpp) { - ZoomLevels avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp); + SpriteCollKeys avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp); - for (ZoomLevel zoom = ZoomLevel::Begin; zoom != ZoomLevel::End; zoom++) { - if (avail.Test(zoom)) Convert32bppTo8bpp(sprite[zoom]); + for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max, true)) { + if (avail.Test(sck)) Convert32bppTo8bpp(sprite[sck]); } return avail; diff --git a/src/spriteloader/makeindexed.h b/src/spriteloader/makeindexed.h index 23bdd39d7a..8b179df0e0 100644 --- a/src/spriteloader/makeindexed.h +++ b/src/spriteloader/makeindexed.h @@ -17,7 +17,7 @@ class SpriteLoaderMakeIndexed : public SpriteLoader { SpriteLoader &baseloader; public: SpriteLoaderMakeIndexed(SpriteLoader &baseloader) : baseloader(baseloader) {} - ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override; + SpriteCollKeys LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, SpriteCollKeys &avail_8bpp, SpriteCollKeys &avail_32bpp) override; }; #endif /* SPRITELOADER_MAKEINDEXED_H */ diff --git a/src/spriteloader/spriteloader.hpp b/src/spriteloader/spriteloader.hpp index cf4673b760..a4a77f6f30 100644 --- a/src/spriteloader/spriteloader.hpp +++ b/src/spriteloader/spriteloader.hpp @@ -27,17 +27,114 @@ enum class SpriteComponent : uint8_t { using SpriteComponents = EnumBitSet; /** - * Map zoom level to data. + * Key into sprite collections. + */ +class SpriteCollKey { +public: + bool rtl; + ZoomLevel zoom; + + inline constexpr SpriteCollKey(ZoomLevel zoom, bool rtl) : rtl(rtl), zoom(zoom) {} + + inline constexpr bool operator==(const SpriteCollKey &rhs) const = default; + inline constexpr std::strong_ordering operator<=>(const SpriteCollKey &rhs) const = default; + + static SpriteCollKey Root(bool rtl) { return SpriteCollKey(ZoomLevel::Min, rtl); } +}; + +/** + * Set of sprite collection keys. + */ +class SpriteCollKeys { + ZoomLevels ltr, rtl; +public: + inline constexpr SpriteCollKeys() = default; + inline constexpr void Set(const SpriteCollKey &sck) { (sck.rtl ? this->rtl : this->ltr).Set(sck.zoom); } + inline constexpr bool Test(const SpriteCollKey &sck) const { return (sck.rtl ? this->rtl : this->ltr).Test(sck.zoom); } + inline constexpr bool AnyLtr() const { return this->ltr.Any(); } + inline constexpr bool AnyRtl() const { return this->rtl.Any(); } + inline constexpr bool NoLtr() const { return this->ltr.None(); } + inline constexpr bool NoRtl() const { return this->rtl.None(); } +}; + +/** + * Map sprite collection keys to data. */ template class SpriteCollMap { - std::array data{}; + std::array ltr{}, rtl{}; public: - inline constexpr T &operator[](const ZoomLevel &zoom) { return this->data[to_underlying(zoom)]; } - inline constexpr const T &operator[](const ZoomLevel &zoom) const { return this->data[to_underlying(zoom)]; } + inline constexpr T &operator[](const SpriteCollKey &sck) { return (sck.rtl ? this->rtl : this->ltr)[to_underlying(sck.zoom)]; } + inline constexpr const T &operator[](const SpriteCollKey &sck) const { return (sck.rtl ? this->rtl : this->ltr)[to_underlying(sck.zoom)]; } - T &Root() { return this->data[to_underlying(ZoomLevel::Min)]; } - const T &Root() const { return this->data[to_underlying(ZoomLevel::Min)]; } + T &Root(bool rtl) { return (rtl ? this->rtl : this->ltr)[to_underlying(ZoomLevel::Min)]; } + const T &Root(bool rtl) const { return (rtl ? this->rtl : this->ltr)[to_underlying(ZoomLevel::Min)]; } +}; + +/** + * Range of sprite collection keys. + */ +class SpriteCollKeyRange { +public: + class Iterator { + ZoomLevel zoom_min, zoom_max; + SpriteCollKey pos; + public: + using value_type = SpriteCollKey; + using difference_type = std::ptrdiff_t; + using iterator_category = std::bidirectional_iterator_tag; + using pointer = void; + using reference = void; + + Iterator(ZoomLevel zoom_min, ZoomLevel zoom_max, SpriteCollKey pos) : zoom_min(zoom_min), zoom_max(zoom_max), pos(pos) {} + bool operator==(const Iterator &rhs) const { return this->pos == rhs.pos; } + std::strong_ordering operator<=>(const Iterator &rhs) const { return this->pos <=> rhs.pos; } + const SpriteCollKey &operator*() const { return this->pos; } + + Iterator& operator++() + { + if (!this->pos.rtl && this->pos.zoom == this->zoom_max) { + this->pos.zoom = this->zoom_min; + this->pos.rtl = true; + } else { + ++this->pos.zoom; + } + return *this; + } + + Iterator operator++(int) + { + Iterator result = *this; + ++*this; + return result; + } + + Iterator& operator--() + { + if (this->pos.zoom == this->zoom_min) { + this->pos.zoom = this->zoom_max; + this->pos.rtl = false; + } else { + --this->pos.zoom; + } + return *this; + } + + Iterator operator--(int) + { + Iterator result = *this; + --*this; + return result; + } + }; + + SpriteCollKeyRange(ZoomLevel zoom_min, ZoomLevel zoom_max, bool has_rtl) : zoom_min(zoom_min), zoom_max(zoom_max), has_rtl(has_rtl) {} + Iterator begin() const { return Iterator{this->zoom_min, this->zoom_max, SpriteCollKey{this->zoom_min, false}}; } + Iterator end() const { return ++Iterator{this->zoom_min, this->zoom_max, SpriteCollKey{this->zoom_max, this->has_rtl}}; } + +private: + ZoomLevel zoom_min, zoom_max; + bool has_rtl; }; /** Interface for the loader of our sprites. */ @@ -68,17 +165,17 @@ public: /** * Allocate the sprite data of this sprite. - * @param zoom Zoom level to allocate the data for. + * @param sck Key to allocate the data for. * @param size the minimum size of the data field. */ - void AllocateData(ZoomLevel zoom, size_t size) { this->data = Sprite::buffer[zoom].ZeroAllocate(size); } + void AllocateData(SpriteCollKey sck, size_t size) { this->data = Sprite::buffer[sck].ZeroAllocate(size); } private: /** Allocated memory to pass sprite data around */ static SpriteCollMap> buffer; }; /** - * Type defining a collection of sprites, one for each zoom level. + * Type defining a collection of sprites, one for each key. */ using SpriteCollection = SpriteCollMap; @@ -94,7 +191,7 @@ public: * @param[out] avail_32bpp Available 32bpp sprites. * @return Available sprites matching \a load_32bpp. */ - virtual ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) = 0; + virtual SpriteCollKeys LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, SpriteCollKeys &avail_8bpp, SpriteCollKeys &avail_32bpp) = 0; virtual ~SpriteLoader() = default; }; @@ -139,7 +236,7 @@ public: /** * Convert a sprite from the loader to our own format. */ - virtual Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) = 0; + virtual Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) = 0; /** * Get the value which the height and width on a sprite have to be aligned by. diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index 008d3f998d..d4b4ff18f9 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -34,6 +34,7 @@ #include "../debug.h" #include "../blitter/factory.hpp" #include "../zoom_func.h" +#include "../strings_func.h" #include "../core/string_consumer.hpp" #include "../table/opengl_shader.h" @@ -1258,11 +1259,11 @@ void OpenGLBackend::ReleaseAnimBuffer(const Rect &update_rect) } } -/* virtual */ Sprite *OpenGLBackend::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) +/* virtual */ Sprite *OpenGLBackend::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) { /* This encoding is only called for mouse cursors. We don't need real sprites but OpenGLSprites to show as cursor. These need to be put in the LRU cache. */ OpenGLSpriteAllocator &gl_allocator = static_cast(allocator); - gl_allocator.lru.Insert(gl_allocator.sprite, std::make_unique(sprite_type, sprite)); + gl_allocator.lru.Insert(gl_allocator.sprite, std::make_unique(sprite_type, sprite, has_rtl && _current_text_dir == TD_RTL)); return nullptr; } @@ -1397,9 +1398,9 @@ void OpenGLBackend::RenderOglSprite(const OpenGLSprite *gl_sprite, PaletteID pal * Create an OpenGL sprite with a palette remap part. * @param sprite The sprite to create the OpenGL sprite for */ -OpenGLSprite::OpenGLSprite(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite) +OpenGLSprite::OpenGLSprite(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool rtl) { - const auto &root_sprite = sprite.Root(); + const auto &root_sprite = sprite.Root(rtl); this->dim.width = root_sprite.width; this->dim.height = root_sprite.height; this->x_offs = root_sprite.x_offs; @@ -1441,7 +1442,7 @@ OpenGLSprite::OpenGLSprite(SpriteType sprite_type, const SpriteLoader::SpriteCol /* Upload texture data. */ for (ZoomLevel zoom = ZoomLevel::Min; zoom <= (sprite_type == SpriteType::Font ? ZoomLevel::Min : ZoomLevel::Max); ++zoom) { - const auto &src_sprite = sprite[zoom]; + const auto &src_sprite = sprite[SpriteCollKey{zoom, rtl}]; this->Update(src_sprite.width, src_sprite.height, to_underlying(zoom), src_sprite.data); } diff --git a/src/video/opengl.h b/src/video/opengl.h index 75515f7210..009c26b07f 100644 --- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -108,7 +108,7 @@ public: bool Is32BppSupported() override { return true; } uint GetSpriteAlignment() override { return 1u << to_underlying(ZoomLevel::Max); } - Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; + Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator) override; }; @@ -139,7 +139,7 @@ private: bool BindTextures() const; public: - OpenGLSprite(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite); + OpenGLSprite(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool rtl); /* No support for moving/copying the textures is implemented. */ OpenGLSprite(const OpenGLSprite&) = delete;