1
0
Fork 0

Add: [BaseSet, NewGRF] Support alternative sprites for RTL languages.

pull/14188/head
frosch 2025-05-02 14:24:10 +02:00
parent 1aab6c4a18
commit 51fd00684b
27 changed files with 160 additions and 107 deletions

View File

@ -40,9 +40,9 @@ public:
void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck);
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; 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"; } std::string_view GetName() override { return "32bpp-sse4-anim"; }
using Blitter_32bppSSE2_Anim::LookupColourInPalette; using Blitter_32bppSSE2_Anim::LookupColourInPalette;

View File

@ -286,7 +286,7 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode,
} }
template <bool Tpal_to_rgb> template <bool Tpal_to_rgb>
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) /* 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; 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]; const SpriteLoader::Sprite *src_orig = &sprite[sck];
uint size = src_orig->height * src_orig->width; 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 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]; len += lengths[0][sck] + lengths[1][sck];
} }
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + sizeof(SpriteData) + len); Sprite *dest_sprite = allocator.Allocate<Sprite>(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->height = root_sprite.height;
dest_sprite->width = root_sprite.width; dest_sprite->width = root_sprite.width;
dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_offs; dest_sprite->y_offs = root_sprite.y_offs;
dest_sprite->has_rtl = has_rtl;
SpriteData *dst = (SpriteData *)dest_sprite->data; SpriteData *dst = (SpriteData *)dest_sprite->data;
uint32_t offset = 0; 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; dst->offset[0][sck] = offset;
offset += lengths[0][sck]; offset += lengths[0][sck];
dst->offset[1][sck] = offset; dst->offset[1][sck] = offset;
offset += lengths[1][sck]; 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<uint8_t *>(dst_px_orig[sck].get()), lengths[0][sck], dst->data + dst->offset[0][sck]); std::copy_n(reinterpret_cast<uint8_t *>(dst_px_orig[sck].get()), lengths[0][sck], dst->data + dst->offset[0][sck]);
std::copy_n(reinterpret_cast<uint8_t *>(dst_n_orig[sck].get()), lengths[1][sck], dst->data + dst->offset[1][sck]); std::copy_n(reinterpret_cast<uint8_t *>(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; return dest_sprite;
} }
template Sprite *Blitter_32bppOptimized::EncodeInternal<true>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); template Sprite *Blitter_32bppOptimized::EncodeInternal<true>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator);
template Sprite *Blitter_32bppOptimized::EncodeInternal<false>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); template Sprite *Blitter_32bppOptimized::EncodeInternal<false>(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<true>(sprite_type, sprite, allocator); return this->EncodeInternal<true>(sprite_type, sprite, has_rtl, allocator);
} }

View File

@ -22,7 +22,7 @@ public:
}; };
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; 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"; } std::string_view GetName() override { return "32bpp-optimized"; }
@ -30,7 +30,7 @@ public:
protected: protected:
template <bool Tpal_to_rgb> void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck); template <bool Tpal_to_rgb> void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck);
template <bool Tpal_to_rgb> Sprite *EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); template <bool Tpal_to_rgb> Sprite *EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, bool has_rtl, SpriteAllocator &allocator);
}; };
/** Factory for the optimised 32 bpp blitter (without palette animation). */ /** Factory for the optimised 32 bpp blitter (without palette animation). */

View File

@ -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); 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; Blitter_32bppSimple::Pixel *dst;
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(root_sprite.height) * static_cast<size_t>(root_sprite.width) * sizeof(*dst)); Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(root_sprite.height) * static_cast<size_t>(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->width = root_sprite.width;
dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_offs; dest_sprite->y_offs = root_sprite.y_offs;
dest_sprite->has_rtl = false;
dst = reinterpret_cast<Blitter_32bppSimple::Pixel *>(dest_sprite->data); dst = reinterpret_cast<Blitter_32bppSimple::Pixel *>(dest_sprite->data);
SpriteLoader::CommonPixel *src = reinterpret_cast<SpriteLoader::CommonPixel *>(root_sprite.data); SpriteLoader::CommonPixel *src = reinterpret_cast<SpriteLoader::CommonPixel *>(root_sprite.data);

View File

@ -26,7 +26,7 @@ class Blitter_32bppSimple : public Blitter_32bppBase {
public: public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override;
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) 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"; } std::string_view GetName() override { return "32bpp-simple"; }
}; };

View File

@ -20,7 +20,7 @@
/** Instantiation of the SSE2 32bpp blitter factory. */ /** Instantiation of the SSE2 32bpp blitter factory. */
static FBlitter_32bppSSE2 iFBlitter_32bppSSE2; 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. /* 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. * 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. */ /* Calculate sizes and allocate. */
SpriteData sd{}; SpriteData sd{};
uint all_sprites_size = 0; 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]; const SpriteLoader::Sprite *src_sprite = &sprite[sck];
auto &info = sd.infos[sck]; auto &info = sd.infos[sck];
info.sprite_width = src_sprite->width; 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; const uint rgba_size = info.sprite_line_size * src_sprite->height;
info.mv_offset = all_sprites_size + rgba_size; 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; const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height;
all_sprites_size += rgba_size + mv_size; all_sprites_size += rgba_size + mv_size;
} }
Sprite *dst_sprite = allocator.Allocate<Sprite>(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size); Sprite *dst_sprite = allocator.Allocate<Sprite>(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->height = root_sprite.height;
dst_sprite->width = root_sprite.width; dst_sprite->width = root_sprite.width;
dst_sprite->x_offs = root_sprite.x_offs; dst_sprite->x_offs = root_sprite.x_offs;
dst_sprite->y_offs = root_sprite.y_offs; dst_sprite->y_offs = root_sprite.y_offs;
dst_sprite->has_rtl = has_rtl;
std::copy_n(reinterpret_cast<std::byte *>(&sd), sizeof(SpriteData), dst_sprite->data); std::copy_n(reinterpret_cast<std::byte *>(&sd), sizeof(SpriteData), dst_sprite->data);
/* Copy colours and determine flags. */ /* Copy colours and determine flags. */
bool has_remap = false; bool has_remap = false;
bool has_anim = false; bool has_anim = false;
bool has_translucency = 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::Sprite *src_sprite = &sprite[sck];
const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data; const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data;
const auto &info = sd.infos[sck]; const auto &info = sd.infos[sck];

View File

@ -77,7 +77,7 @@ public:
uint8_t data[]; ///< Data, all zoomlevels. 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). */ /** The SSE2 32 bpp blitter (without palette animation). */
@ -87,9 +87,9 @@ public:
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent> template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
void Draw(const Blitter::BlitterParams *bp, SpriteCollKey sck); 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"; } std::string_view GetName() override { return "32bpp-sse2"; }

View File

@ -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<false>(sprite_type, sprite, allocator); return this->EncodeInternal<false>(sprite_type, sprite, has_rtl, allocator);
} }

View File

@ -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 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 Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override;
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) 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; size_t BufferSize(uint width, uint height) override;
Blitter::PaletteAnimation UsePaletteAnimation() override; Blitter::PaletteAnimation UsePaletteAnimation() override;
bool NeedsAnimationBuffer() override; bool NeedsAnimationBuffer() override;

View File

@ -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 */ /* Make memory for all zoom-levels */
uint memory = sizeof(SpriteData); 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; 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; 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; uint8_t *dst = temp_dst->data;
/* Make the sprites per zoom-level */ /* 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]; const SpriteLoader::Sprite &src_orig = sprite[sck];
/* Store the index table */ /* Store the index table */
uint offset = dst - temp_dst->data; uint offset = dst - temp_dst->data;
temp_dst->offset[sck] = 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 */ /* cache values, because compiler can't cache it */
int scaled_height = src_orig.height; 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 */ /* Allocate the exact amount of memory we need */
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + size); Sprite *dest_sprite = allocator.Allocate<Sprite>(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->height = root_sprite.height;
dest_sprite->width = root_sprite.width; dest_sprite->width = root_sprite.width;
dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_offs; dest_sprite->y_offs = root_sprite.y_offs;
dest_sprite->has_rtl = has_rtl;
std::copy_n(reinterpret_cast<std::byte *>(temp_dst), size, dest_sprite->data); std::copy_n(reinterpret_cast<std::byte *>(temp_dst), size, dest_sprite->data);
return dest_sprite; return dest_sprite;

View File

@ -23,7 +23,7 @@ public:
}; };
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; 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"; } std::string_view GetName() override { return "8bpp-optimized"; }
}; };

View File

@ -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; Sprite *dest_sprite;
dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(root_sprite.height) * static_cast<size_t>(root_sprite.width)); dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(root_sprite.height) * static_cast<size_t>(root_sprite.width));
@ -71,6 +71,7 @@ Sprite *Blitter_8bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollect
dest_sprite->width = root_sprite.width; dest_sprite->width = root_sprite.width;
dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_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 */ /* Copy over only the 'remap' channel, as that is what we care about in 8bpp */
uint8_t *dst = reinterpret_cast<uint8_t *>(dest_sprite->data); uint8_t *dst = reinterpret_cast<uint8_t *>(dest_sprite->data);

View File

@ -17,7 +17,7 @@
class Blitter_8bppSimple final : public Blitter_8bppBase { class Blitter_8bppSimple final : public Blitter_8bppBase {
public: public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, SpriteCollKey sck) override; 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"; } std::string_view GetName() override { return "8bpp-simple"; }
}; };

View File

@ -15,16 +15,17 @@
/** Instantiation of the null blitter factory. */ /** Instantiation of the null blitter factory. */
static FBlitter_Null iFBlitter_Null; 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; Sprite *dest_sprite;
dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite)); dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite));
const auto &root_sprite = sprite.Root(); const auto &root_sprite = sprite.Root(false);
dest_sprite->height = root_sprite.height; dest_sprite->height = root_sprite.height;
dest_sprite->width = root_sprite.width; dest_sprite->width = root_sprite.width;
dest_sprite->x_offs = root_sprite.x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_offs; dest_sprite->y_offs = root_sprite.y_offs;
dest_sprite->has_rtl = false;
return dest_sprite; return dest_sprite;
} }

View File

@ -18,7 +18,7 @@ public:
uint8_t GetScreenDepth() override { return 0; } uint8_t GetScreenDepth() override { return 0; }
void Draw(Blitter::BlitterParams *, BlitterMode, SpriteCollKey) override {}; void Draw(Blitter::BlitterParams *, BlitterMode, SpriteCollKey) override {};
void DrawColourMappingRect(void *, int, int, PaletteID) 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 *MoveTo(void *, int, int) override { return nullptr; };
void SetPixel(void *, int, int, uint8_t) override {}; void SetPixel(void *, int, int, uint8_t) override {};
void DrawRect(void *, int, int, uint8_t) override {}; void DrawRect(void *, int, int, uint8_t) override {};

View File

@ -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 */ /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */
SpriteLoader::SpriteCollection spritecollection; SpriteLoader::SpriteCollection spritecollection;
SpriteLoader::Sprite &sprite = spritecollection.Root(); SpriteLoader::Sprite &sprite = spritecollection.Root(false);
sprite.AllocateData(SpriteCollKey::Root(), static_cast<size_t>(width) * height); sprite.AllocateData(SpriteCollKey::Root(false), static_cast<size_t>(width) * height);
sprite.colours = SpriteComponent::Palette; sprite.colours = SpriteComponent::Palette;
if (aa) sprite.colours.Set(SpriteComponent::Alpha); if (aa) sprite.colours.Set(SpriteComponent::Alpha);
sprite.width = width; sprite.width = width;
@ -273,7 +273,7 @@ const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa)
} }
UniquePtrSpriteAllocator allocator; UniquePtrSpriteAllocator allocator;
BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator); BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, false, allocator);
GlyphEntry new_glyph; GlyphEntry new_glyph;
new_glyph.data = std::move(allocator.data); new_glyph.data = std::move(allocator.data);

View File

@ -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});
} }
/** /**

View File

@ -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"); if (width > MAX_GLYPH_DIM || height > MAX_GLYPH_DIM) UserError("Font glyph is too large");
SpriteLoader::SpriteCollection spritecollection; SpriteLoader::SpriteCollection spritecollection;
SpriteLoader::Sprite &sprite = spritecollection.Root(); SpriteLoader::Sprite &sprite = spritecollection.Root(false);
sprite.AllocateData(SpriteCollKey::Root(), width * height); sprite.AllocateData(SpriteCollKey::Root(false), width * height);
sprite.colours = SpriteComponent::Palette; sprite.colours = SpriteComponent::Palette;
if (use_aa) sprite.colours.Set(SpriteComponent::Alpha); if (use_aa) sprite.colours.Set(SpriteComponent::Alpha);
sprite.width = width; sprite.width = width;
@ -278,7 +278,7 @@ const Sprite *CoreTextFontCache::InternalGetGlyph(GlyphID key, bool use_aa)
} }
UniquePtrSpriteAllocator allocator; UniquePtrSpriteAllocator allocator;
BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator); BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, false, allocator);
GlyphEntry new_glyph; GlyphEntry new_glyph;
new_glyph.data = std::move(allocator.data); new_glyph.data = std::move(allocator.data);

View File

@ -224,8 +224,8 @@ void Win32FontCache::ClearFontCache()
/* GDI has rendered the glyph, now we allocate a sprite and copy the image into it. */ /* GDI has rendered the glyph, now we allocate a sprite and copy the image into it. */
SpriteLoader::SpriteCollection spritecollection; SpriteLoader::SpriteCollection spritecollection;
SpriteLoader::Sprite &sprite = spritecollection.Root(); SpriteLoader::Sprite &sprite = spritecollection.Root(false);
sprite.AllocateData(SpriteCollKey::Root(), width * height); sprite.AllocateData(SpriteCollKey::Root(false), width * height);
sprite.colours = SpriteComponent::Palette; sprite.colours = SpriteComponent::Palette;
if (aa) sprite.colours.Set(SpriteComponent::Alpha); if (aa) sprite.colours.Set(SpriteComponent::Alpha);
sprite.width = width; sprite.width = width;
@ -264,7 +264,7 @@ void Win32FontCache::ClearFontCache()
} }
UniquePtrSpriteAllocator allocator; UniquePtrSpriteAllocator allocator;
BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, allocator); BlitterFactory::GetCurrentBlitter()->Encode(SpriteType::Font, spritecollection, false, allocator);
GlyphEntry new_glyph; GlyphEntry new_glyph;
new_glyph.data = std::move(allocator.data); new_glyph.data = std::move(allocator.data);

View File

@ -1420,6 +1420,7 @@ struct GameOptionsWindow : Window {
CheckForMissingGlyphs(); CheckForMissingGlyphs();
ClearAllCachedNames(); ClearAllCachedNames();
UpdateAllVirtCoords(); UpdateAllVirtCoords();
VideoDriver::GetInstance()->ClearSystemSprites(); // relevant if _current_text_dir changes
CheckBlitter(); CheckBlitter();
ReInitAllWindows(false); ReInitAllWindows(false);
break; break;

View File

@ -249,8 +249,8 @@ static bool ResizeSpriteIn(SpriteLoader::SpriteCollection &sprite, SpriteCollKey
static void ResizeSpriteOut(SpriteLoader::SpriteCollection &sprite, SpriteCollKey sck) static void ResizeSpriteOut(SpriteLoader::SpriteCollection &sprite, SpriteCollKey sck)
{ {
const auto &root_sprite = sprite.Root(); const auto &root_sprite = sprite.Root(sck.rtl);
const auto &src_sprite = sprite[SpriteCollKey{sck.zoom - 1}]; const auto &src_sprite = sprite[SpriteCollKey{sck.zoom - 1, sck.rtl}];
auto &dest_sprite = sprite[sck]; auto &dest_sprite = sprite[sck];
/* Algorithm based on 32bpp_Optimized::ResizeSprite() */ /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
@ -337,7 +337,7 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, SpriteCollKeys sp
/* Get minimum top left corner coordinates. */ /* Get minimum top left corner coordinates. */
int min_xoffs = INT32_MAX; int min_xoffs = INT32_MAX;
int min_yoffs = 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)) { if (sprite_avail.Test(sck)) {
min_xoffs = std::min(min_xoffs, ScaleByZoom(sprite[sck].x_offs, sck.zoom)); 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)); 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. */ /* Get maximum dimensions taking necessary padding at the top left into account. */
int max_width = INT32_MIN; int max_width = INT32_MIN;
int max_height = 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)) { 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_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)); 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. */ /* 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)) { if (sprite_avail.Test(sck)) {
auto &cur_sprite = sprite[sck]; auto &cur_sprite = sprite[sck];
/* Scaling the sprite dimensions in the blitter is done with rounding up, /* 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) 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 */ /* Create a fully zoomed image if it does not exist */
ZoomLevel first_avail; ZoomLevel first_avail[2];
for (ZoomLevel zoom = ZoomLevel::Min; zoom <= ZoomLevel::Max; ++zoom) { for (bool rtl : {false, true}) {
SpriteCollKey src_sck{zoom}; if (rtl && !has_rtl) continue;
if (!sprite_avail.Test(src_sck)) continue; for (ZoomLevel zoom = ZoomLevel::Min; zoom <= ZoomLevel::Max; ++zoom) {
first_avail = zoom; SpriteCollKey src_sck{zoom, rtl};
if (zoom != ZoomLevel::Min) { if (!sprite_avail.Test(src_sck)) continue;
auto root_sck = SpriteCollKey::Root(); first_avail[rtl ? 1 : 0] = zoom;
if (!ResizeSpriteIn(sprite, src_sck, root_sck)) return false; if (zoom != ZoomLevel::Min) {
sprite_avail.Set(root_sck); 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. */ /* Pad sprites to make sizes match. */
if (!PadSprites(sprite, sprite_avail, encoder)) return false; if (!PadSprites(sprite, sprite_avail, encoder)) return false;
/* Create other missing zoom levels */ /* 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)) { if (sprite_avail.Test(sck)) {
/* Check that size and offsets match the fully zoomed image. */ /* 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]; [[maybe_unused]] const auto &dest_sprite = sprite[sck];
assert(dest_sprite.width == UnScaleByZoom(root_sprite.width, sck.zoom)); assert(dest_sprite.width == UnScaleByZoom(root_sprite.width, sck.zoom));
assert(dest_sprite.height == UnScaleByZoom(root_sprite.height, 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. */ /* 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 (bool rtl : {false, true}) {
for (ZoomLevel zoom = std::min(ZoomLevel::Normal, _settings_client.gui.sprite_zoom_min); zoom > ZoomLevel::Min; --zoom) { if (rtl && !has_rtl) continue;
const SpriteCollKey src_sck{zoom}; if (first_avail[rtl ? 1 : 0] < _settings_client.gui.sprite_zoom_min) {
const SpriteCollKey dest_sck{zoom - 1}; for (ZoomLevel zoom = std::min(ZoomLevel::Normal, _settings_client.gui.sprite_zoom_min); zoom > ZoomLevel::Min; --zoom) {
ResizeSpriteIn(sprite, src_sck, dest_sck); 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. */ /* Try for 32bpp sprites first. */
sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, true, sc->control_flags, avail_8bpp, avail_32bpp); 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); 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. */ /* No 8bpp available, try converting from 32bpp. */
SpriteLoaderMakeIndexed make_indexed(sprite_loader); SpriteLoaderMakeIndexed make_indexed(sprite_loader);
sprite_avail = make_indexed.LoadSprite(sprite, file, file_pos, sprite_type, true, sc->control_flags, sprite_avail, avail_32bpp); 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 (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?"); 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); 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 * Ugly: yes. Other solution: no. Blame the original author or
* something ;) The image should really have been a data-stream * something ;) The image should really have been a data-stream
* (so type = 0xFF basically). */ * (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; uint num = root_sprite.width * root_sprite.height;
Sprite *s = allocator.Allocate<Sprite>(sizeof(*s) + num); Sprite *s = allocator.Allocate<Sprite>(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) { if (sprite_type == SpriteType::Font && _font_zoom != ZoomLevel::Min) {
/* Make ZoomLevel::Min be ZOOM_LVL_GUI */ /* Make ZoomLevel::Min be ZOOM_LVL_GUI */
SpriteCollKey min_sck{ZoomLevel::Min}; SpriteCollKey min_sck{ZoomLevel::Min, false};
SpriteCollKey font_sck{_font_zoom}; SpriteCollKey font_sck{_font_zoom, false};
sprite[min_sck] = sprite[font_sck]; 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 { struct GrfSpriteOffset {

View File

@ -19,6 +19,7 @@ struct Sprite {
uint16_t width; ///< Width of the sprite. uint16_t width; ///< Width of the sprite.
int16_t x_offs; ///< Number of pixels to shift the sprite to the right. 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. 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. std::byte data[]; ///< Sprite data.
}; };

View File

@ -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 */ /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */
if (type == 0xFF) return {}; 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]; auto &dest_sprite = sprite[sck];
dest_sprite.height = file.ReadByte(); dest_sprite.height = file.ReadByte();
@ -280,6 +280,7 @@ static SpriteCollKeys LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, Sprit
SpriteComponents colour{type}; SpriteComponents colour{type};
/* Mask out colour component information from type. */ /* Mask out colour component information from type. */
type &= ~SpriteComponents::MASK; type &= ~SpriteComponents::MASK;
bool rtl = type & 0x10;
uint8_t zoom = file.ReadByte(); uint8_t zoom = file.ReadByte();
@ -288,7 +289,7 @@ static SpriteCollKeys LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, Sprit
if (sprite_type != SpriteType::MapGen) { if (sprite_type != SpriteType::MapGen) {
if (zoom < lengthof(zoom_lvl_map)) { 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_8bpp.Set(sck);
if (colour != SpriteComponent::Palette) avail_32bpp.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); is_wanted_zoom_lvl = (zoom == 0);
} }
if (is_wanted_colour_depth && is_wanted_zoom_lvl) { bool is_wanted_textdir = !rtl || sprite_type == SpriteType::Normal;
const SpriteCollKey sck{sprite_type != SpriteType::MapGen ? zoom_lvl_map[zoom] : ZoomLevel::Min};
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)) { if (loaded_sprites.Test(sck)) {
/* We already have this zoom level, skip sprite. */ /* We already have this zoom level, skip sprite. */

View File

@ -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); 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]); if (avail.Test(sck)) Convert32bppTo8bpp(sprite[sck]);
} }

View File

@ -31,27 +31,30 @@ using SpriteComponents = EnumBitSet<SpriteComponent, uint8_t, SpriteComponent::E
*/ */
class SpriteCollKey { class SpriteCollKey {
public: public:
bool rtl;
ZoomLevel zoom; ZoomLevel zoom;
inline constexpr explicit SpriteCollKey(ZoomLevel zoom) : zoom(zoom) {} inline constexpr SpriteCollKey(ZoomLevel zoom, bool rtl) : rtl(rtl), zoom(zoom) {}
inline constexpr bool operator==(const SpriteCollKey &rhs) const = default; inline constexpr bool operator==(const SpriteCollKey &rhs) const = default;
inline constexpr std::strong_ordering operator<=>(const SpriteCollKey &rhs) const = default; inline constexpr std::strong_ordering operator<=>(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. * Set of sprite collection keys.
*/ */
class SpriteCollKeys { class SpriteCollKeys {
ZoomLevels keys; ZoomLevels ltr, rtl;
public: public:
inline constexpr SpriteCollKeys() = default; inline constexpr SpriteCollKeys() = default;
inline constexpr void Set(const SpriteCollKey &sck) { this->keys.Set(sck.zoom); } 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 this->keys.Test(sck.zoom); } inline constexpr bool Test(const SpriteCollKey &sck) const { return (sck.rtl ? this->rtl : this->ltr).Test(sck.zoom); }
inline constexpr bool Any() const { return this->keys.Any(); } inline constexpr bool AnyLtr() const { return this->ltr.Any(); }
inline constexpr bool None() const { return this->keys.None(); } 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 T> template <class T>
class SpriteCollMap { class SpriteCollMap {
std::array<T, to_underlying(ZoomLevel::End)> data{}; std::array<T, to_underlying(ZoomLevel::End)> ltr{}, rtl{};
public: public:
inline constexpr T &operator[](const SpriteCollKey &sck) { 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 this->data[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)]; } T &Root(bool rtl) { return (rtl ? this->rtl : this->ltr)[to_underlying(ZoomLevel::Min)]; }
const T &Root() const { return this->data[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 { class SpriteCollKeyRange {
public: public:
class Iterator { class Iterator {
ZoomLevel zoom_min, zoom_max;
SpriteCollKey pos; SpriteCollKey pos;
public: public:
using value_type = SpriteCollKey; using value_type = SpriteCollKey;
@ -82,14 +86,19 @@ public:
using pointer = void; using pointer = void;
using reference = 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; } bool operator==(const Iterator &rhs) const { return this->pos == rhs.pos; }
std::strong_ordering 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; } const SpriteCollKey &operator*() const { return this->pos; }
Iterator& operator++() 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; return *this;
} }
@ -102,7 +111,12 @@ public:
Iterator& operator--() 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; return *this;
} }
@ -114,12 +128,13 @@ public:
} }
}; };
SpriteCollKeyRange(ZoomLevel zoom_min, ZoomLevel zoom_max) : zoom_min(zoom_min), zoom_max(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{SpriteCollKey{this->zoom_min}}; } Iterator begin() const { return Iterator{this->zoom_min, this->zoom_max, SpriteCollKey{this->zoom_min, false}}; }
Iterator end() const { return ++Iterator{SpriteCollKey{this->zoom_max}}; } Iterator end() const { return ++Iterator{this->zoom_min, this->zoom_max, SpriteCollKey{this->zoom_max, this->has_rtl}}; }
private: private:
ZoomLevel zoom_min, zoom_max; ZoomLevel zoom_min, zoom_max;
bool has_rtl;
}; };
/** Interface for the loader of our sprites. */ /** Interface for the loader of our sprites. */
@ -221,7 +236,7 @@ public:
/** /**
* Convert a sprite from the loader to our own format. * 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. * Get the value which the height and width on a sprite have to be aligned by.

View File

@ -34,6 +34,7 @@
#include "../debug.h" #include "../debug.h"
#include "../blitter/factory.hpp" #include "../blitter/factory.hpp"
#include "../zoom_func.h" #include "../zoom_func.h"
#include "../strings_func.h"
#include "../core/string_consumer.hpp" #include "../core/string_consumer.hpp"
#include "../table/opengl_shader.h" #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. */ /* 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<OpenGLSpriteAllocator&>(allocator); OpenGLSpriteAllocator &gl_allocator = static_cast<OpenGLSpriteAllocator&>(allocator);
gl_allocator.lru.Insert(gl_allocator.sprite, std::make_unique<OpenGLSprite>(sprite_type, sprite)); gl_allocator.lru.Insert(gl_allocator.sprite, std::make_unique<OpenGLSprite>(sprite_type, sprite, has_rtl && _current_text_dir == TD_RTL));
return nullptr; return nullptr;
} }
@ -1397,9 +1398,9 @@ void OpenGLBackend::RenderOglSprite(const OpenGLSprite *gl_sprite, PaletteID pal
* Create an OpenGL sprite with a palette remap part. * Create an OpenGL sprite with a palette remap part.
* @param sprite The sprite to create the OpenGL sprite for * @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.width = root_sprite.width;
this->dim.height = root_sprite.height; this->dim.height = root_sprite.height;
this->x_offs = root_sprite.x_offs; this->x_offs = root_sprite.x_offs;
@ -1441,7 +1442,7 @@ OpenGLSprite::OpenGLSprite(SpriteType sprite_type, const SpriteLoader::SpriteCol
/* Upload texture data. */ /* Upload texture data. */
for (ZoomLevel zoom = ZoomLevel::Min; zoom <= (sprite_type == SpriteType::Font ? ZoomLevel::Min : ZoomLevel::Max); ++zoom) { 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); this->Update(src_sprite.width, src_sprite.height, to_underlying(zoom), src_sprite.data);
} }

View File

@ -108,7 +108,7 @@ public:
bool Is32BppSupported() override { return true; } bool Is32BppSupported() override { return true; }
uint GetSpriteAlignment() override { return 1u << to_underlying(ZoomLevel::Max); } 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; bool BindTextures() const;
public: 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. */ /* No support for moving/copying the textures is implemented. */
OpenGLSprite(const OpenGLSprite&) = delete; OpenGLSprite(const OpenGLSprite&) = delete;