From 51fd00684b42e85f58bcd92b8fb21e3840708698 Mon Sep 17 00:00:00 2001 From: frosch Date: Fri, 2 May 2025 14:24:10 +0200 Subject: [PATCH] Add: [BaseSet, NewGRF] Support alternative sprites for RTL languages. --- src/blitter/32bpp_anim_sse4.hpp | 4 +- src/blitter/32bpp_optimized.cpp | 25 +++++++---- src/blitter/32bpp_optimized.hpp | 4 +- src/blitter/32bpp_simple.cpp | 5 ++- src/blitter/32bpp_simple.hpp | 2 +- src/blitter/32bpp_sse2.cpp | 15 +++++-- src/blitter/32bpp_sse2.hpp | 6 +-- src/blitter/40bpp_anim.cpp | 4 +- src/blitter/40bpp_anim.hpp | 2 +- src/blitter/8bpp_optimized.cpp | 14 ++++-- src/blitter/8bpp_optimized.hpp | 2 +- src/blitter/8bpp_simple.cpp | 5 ++- src/blitter/8bpp_simple.hpp | 2 +- src/blitter/null.cpp | 5 ++- src/blitter/null.hpp | 2 +- src/fontcache/freetypefontcache.cpp | 6 +-- src/gfx.cpp | 2 +- src/os/macosx/font_osx.cpp | 6 +-- src/os/windows/font_win32.cpp | 6 +-- src/settings_gui.cpp | 1 + src/spritecache.cpp | 67 ++++++++++++++++------------- src/spritecache.h | 1 + src/spriteloader/grf.cpp | 11 +++-- src/spriteloader/makeindexed.cpp | 2 +- src/spriteloader/spriteloader.hpp | 53 +++++++++++++++-------- src/video/opengl.cpp | 11 ++--- src/video/opengl.h | 4 +- 27 files changed, 160 insertions(+), 107 deletions(-) diff --git a/src/blitter/32bpp_anim_sse4.hpp b/src/blitter/32bpp_anim_sse4.hpp index 8e12b32236..aef968d988 100644 --- a/src/blitter/32bpp_anim_sse4.hpp +++ b/src/blitter/32bpp_anim_sse4.hpp @@ -40,9 +40,9 @@ public: 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 9aaf38d465..c69cb706fd 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -286,7 +286,7 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, } 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,7 +316,7 @@ Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const Spr if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max; } - for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + 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; @@ -411,26 +411,33 @@ Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const Spr } uint len = 0; // total length of data - for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + 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 (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + 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[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]); @@ -439,10 +446,10 @@ Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const Spr 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 1772502c4c..9137e0a37d 100644 --- a/src/blitter/32bpp_optimized.hpp +++ b/src/blitter/32bpp_optimized.hpp @@ -22,7 +22,7 @@ public: }; 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; std::string_view GetName() override { return "32bpp-optimized"; } @@ -30,7 +30,7 @@ public: protected: template void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck); - template Sprite *EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); + 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 4d5053c444..97341600a1 100644 --- a/src/blitter/32bpp_simple.cpp +++ b/src/blitter/32bpp_simple.cpp @@ -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 1c8eb1baf0..3d049dc858 100644 --- a/src/blitter/32bpp_simple.hpp +++ b/src/blitter/32bpp_simple.hpp @@ -26,7 +26,7 @@ class Blitter_32bppSimple : public Blitter_32bppBase { public: 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 e22ee0254f..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,7 +37,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(SpriteType sprite_type, const SpriteLoader /* Calculate sizes and allocate. */ SpriteData sd{}; uint all_sprites_size = 0; - for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + 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; @@ -47,23 +47,30 @@ 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 (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + 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[sck]; diff --git a/src/blitter/32bpp_sse2.hpp b/src/blitter/32bpp_sse2.hpp index 175519b11f..5464b4a51c 100644 --- a/src/blitter/32bpp_sse2.hpp +++ b/src/blitter/32bpp_sse2.hpp @@ -77,7 +77,7 @@ 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). */ @@ -87,9 +87,9 @@ public: template 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/40bpp_anim.cpp b/src/blitter/40bpp_anim.cpp index 83476310fe..e5fcb2f491 100644 --- a/src/blitter/40bpp_anim.cpp +++ b/src/blitter/40bpp_anim.cpp @@ -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 5be055ba99..8196501f08 100644 --- a/src/blitter/40bpp_anim.hpp +++ b/src/blitter/40bpp_anim.hpp @@ -27,7 +27,7 @@ public: 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, 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; diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 25631e748d..caf628fcb2 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -119,7 +119,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, S } } -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,7 +136,7 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max; } - for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + for (auto sck : SpriteCollKeyRange(zoom_min, zoom_max, has_rtl)) { memory += sprite[sck].width * sprite[sck].height; } @@ -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 (auto sck : SpriteCollKeyRange(zoom_min, zoom_max)) { + 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[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 f910627bbc..babea7649c 100644 --- a/src/blitter/8bpp_optimized.hpp +++ b/src/blitter/8bpp_optimized.hpp @@ -23,7 +23,7 @@ public: }; 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; std::string_view GetName() override { return "8bpp-optimized"; } }; diff --git a/src/blitter/8bpp_simple.cpp b/src/blitter/8bpp_simple.cpp index f30064b3a4..3f520824c0 100644 --- a/src/blitter/8bpp_simple.cpp +++ b/src/blitter/8bpp_simple.cpp @@ -61,9 +61,9 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Spri } } -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 f2cc6d1e19..35957b8c55 100644 --- a/src/blitter/8bpp_simple.hpp +++ b/src/blitter/8bpp_simple.hpp @@ -17,7 +17,7 @@ class Blitter_8bppSimple final : public Blitter_8bppBase { public: 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; std::string_view GetName() override { return "8bpp-simple"; } }; 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 9718fdd19a..7f0c8f3335 100644 --- a/src/blitter/null.hpp +++ b/src/blitter/null.hpp @@ -18,7 +18,7 @@ public: uint8_t GetScreenDepth() override { return 0; } 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 390b77c130..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.Root(); - sprite.AllocateData(SpriteCollKey::Root(), 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 35676d75c6..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, SpriteCollKey{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 0b448971f4..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.Root(); - sprite.AllocateData(SpriteCollKey::Root(), 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 75c27674ea..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.Root(); - sprite.AllocateData(SpriteCollKey::Root(), 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 94a073d904..0b773a0fbf 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1420,6 +1420,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 2b5c0b3e50..cc19037f2f 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -249,8 +249,8 @@ static bool ResizeSpriteIn(SpriteLoader::SpriteCollection &sprite, SpriteCollKey static void ResizeSpriteOut(SpriteLoader::SpriteCollection &sprite, SpriteCollKey sck) { - const auto &root_sprite = sprite.Root(); - const auto &src_sprite = sprite[SpriteCollKey{sck.zoom - 1}]; + 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() */ @@ -337,7 +337,7 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sp /* Get minimum top left corner coordinates. */ int min_xoffs = INT32_MAX; int min_yoffs = INT32_MAX; - for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max)) { + 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)); @@ -347,7 +347,7 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sp /* Get maximum dimensions taking necessary padding at the top left into account. */ int max_width = INT32_MIN; int max_height = INT32_MIN; - for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max)) { + 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)); @@ -362,7 +362,7 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sp } /* Pad sprites where needed. */ - for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max)) { + 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, @@ -383,28 +383,34 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sp 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) { - SpriteCollKey src_sck{zoom}; - if (!sprite_avail.Test(src_sck)) continue; - first_avail = zoom; - if (zoom != ZoomLevel::Min) { - auto root_sck = SpriteCollKey::Root(); - if (!ResizeSpriteIn(sprite, src_sck, root_sck)) return false; - sprite_avail.Set(root_sck); + 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 (auto sck : SpriteCollKeyRange(ZoomLevel::Min + 1, ZoomLevel::Max)) { + 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.Root(); + [[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)); @@ -417,11 +423,14 @@ static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys } /* 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) { - const SpriteCollKey src_sck{zoom}; - const SpriteCollKey dest_sck{zoom - 1}; - ResizeSpriteIn(sprite, src_sck, dest_sck); + 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); + } } } @@ -498,16 +507,16 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty /* 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); @@ -523,7 +532,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); @@ -549,12 +558,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 */ - SpriteCollKey min_sck{ZoomLevel::Min}; - SpriteCollKey font_sck{_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 09fee98a92..ff497e4b5e 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 230cfbecd5..8e97ef0b45 100644 --- a/src/spriteloader/grf.cpp +++ b/src/spriteloader/grf.cpp @@ -230,7 +230,7 @@ static SpriteCollKeys LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, Sprit /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */ if (type == 0xFF) return {}; - const SpriteCollKey sck{(sprite_type != SpriteType::MapGen) ? ZoomLevel::Normal : ZoomLevel::Min}; + const SpriteCollKey sck{(sprite_type != SpriteType::MapGen) ? ZoomLevel::Normal : ZoomLevel::Min, false}; auto &dest_sprite = sprite[sck]; dest_sprite.height = file.ReadByte(); @@ -280,6 +280,7 @@ static SpriteCollKeys LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, Sprit SpriteComponents colour{type}; /* Mask out colour component information from type. */ type &= ~SpriteComponents::MASK; + bool rtl = type & 0x10; uint8_t zoom = file.ReadByte(); @@ -288,7 +289,7 @@ static SpriteCollKeys LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, Sprit if (sprite_type != SpriteType::MapGen) { if (zoom < lengthof(zoom_lvl_map)) { - const SpriteCollKey sck{zoom_lvl_map[zoom]}; + const SpriteCollKey sck{zoom_lvl_map[zoom], rtl}; if (colour == SpriteComponent::Palette) avail_8bpp.Set(sck); if (colour != SpriteComponent::Palette) avail_32bpp.Set(sck); @@ -309,8 +310,10 @@ static SpriteCollKeys LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, Sprit is_wanted_zoom_lvl = (zoom == 0); } - if (is_wanted_colour_depth && is_wanted_zoom_lvl) { - const SpriteCollKey sck{sprite_type != SpriteType::MapGen ? zoom_lvl_map[zoom] : ZoomLevel::Min}; + bool is_wanted_textdir = !rtl || sprite_type == SpriteType::Normal; + + 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. */ diff --git a/src/spriteloader/makeindexed.cpp b/src/spriteloader/makeindexed.cpp index c022c93c1e..7e8c267fab 100644 --- a/src/spriteloader/makeindexed.cpp +++ b/src/spriteloader/makeindexed.cpp @@ -52,7 +52,7 @@ SpriteCollKeys SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollectio { SpriteCollKeys avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp); - for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max)) { + for (auto sck : SpriteCollKeyRange(ZoomLevel::Min, ZoomLevel::Max, true)) { if (avail.Test(sck)) Convert32bppTo8bpp(sprite[sck]); } diff --git a/src/spriteloader/spriteloader.hpp b/src/spriteloader/spriteloader.hpp index 901cc94f2a..a4a77f6f30 100644 --- a/src/spriteloader/spriteloader.hpp +++ b/src/spriteloader/spriteloader.hpp @@ -31,27 +31,30 @@ using SpriteComponents = EnumBitSet(const SpriteCollKey &rhs) const = default; - static SpriteCollKey Root() { return SpriteCollKey(ZoomLevel::Min); } + static SpriteCollKey Root(bool rtl) { return SpriteCollKey(ZoomLevel::Min, rtl); } }; /** * Set of sprite collection keys. */ class SpriteCollKeys { - ZoomLevels keys; + ZoomLevels ltr, rtl; public: inline constexpr SpriteCollKeys() = default; - inline constexpr void Set(const SpriteCollKey &sck) { this->keys.Set(sck.zoom); } - inline constexpr bool Test(const SpriteCollKey &sck) const { return this->keys.Test(sck.zoom); } - inline constexpr bool Any() const { return this->keys.Any(); } - inline constexpr bool None() const { return this->keys.None(); } + 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(); } }; /** @@ -59,13 +62,13 @@ public: */ template class SpriteCollMap { - std::array data{}; + std::array ltr{}, rtl{}; public: - inline constexpr T &operator[](const SpriteCollKey &sck) { return this->data[to_underlying(sck.zoom)]; } - inline constexpr const T &operator[](const SpriteCollKey &sck) const { return this->data[to_underlying(sck.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)]; } }; /** @@ -74,6 +77,7 @@ public: class SpriteCollKeyRange { public: class Iterator { + ZoomLevel zoom_min, zoom_max; SpriteCollKey pos; public: using value_type = SpriteCollKey; @@ -82,14 +86,19 @@ public: using pointer = void; using reference = void; - Iterator(SpriteCollKey pos) : pos(pos) {} + 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++() { - ++this->pos.zoom; + 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; } @@ -102,7 +111,12 @@ public: Iterator& operator--() { - --this->pos.zoom; + if (this->pos.zoom == this->zoom_min) { + this->pos.zoom = this->zoom_max; + this->pos.rtl = false; + } else { + --this->pos.zoom; + } return *this; } @@ -114,12 +128,13 @@ public: } }; - SpriteCollKeyRange(ZoomLevel zoom_min, ZoomLevel zoom_max) : zoom_min(zoom_min), zoom_max(zoom_max) {} - Iterator begin() const { return Iterator{SpriteCollKey{this->zoom_min}}; } - Iterator end() const { return ++Iterator{SpriteCollKey{this->zoom_max}}; } + 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. */ @@ -221,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 f55f7f6416..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[SpriteCollKey{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;