diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 4eb118056b..7034392daa 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -333,13 +333,13 @@ static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint p return true; } -static bool PadSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite_avail, SpriteEncoder *encoder) +static bool PadSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels sprite_avail, SpriteEncoder *encoder) { /* Get minimum top left corner coordinates. */ int min_xoffs = INT32_MAX; int min_yoffs = INT32_MAX; for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) { - if (HasBit(sprite_avail, zoom)) { + if (sprite_avail.Test(zoom)) { min_xoffs = std::min(min_xoffs, ScaleByZoom(sprite[zoom].x_offs, zoom)); min_yoffs = std::min(min_yoffs, ScaleByZoom(sprite[zoom].y_offs, zoom)); } @@ -349,7 +349,7 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite_av int max_width = INT32_MIN; int max_height = INT32_MIN; for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) { - if (HasBit(sprite_avail, zoom)) { + if (sprite_avail.Test(zoom)) { max_width = std::max(max_width, ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom), zoom)); max_height = std::max(max_height, ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom), zoom)); } @@ -364,7 +364,7 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite_av /* Pad sprites where needed. */ for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) { - if (HasBit(sprite_avail, zoom)) { + if (sprite_avail.Test(zoom)) { auto &cur_sprite = sprite[zoom]; /* Scaling the sprite dimensions in the blitter is done with rounding up, * so a negative padding here is not an error. */ @@ -382,13 +382,18 @@ static bool PadSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite_av return true; } -static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite_avail, SpriteEncoder *encoder) +static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, ZoomLevels sprite_avail, SpriteEncoder *encoder) { /* Create a fully zoomed image if it does not exist */ - ZoomLevel first_avail = static_cast(FindFirstBit(sprite_avail)); - if (first_avail != ZOOM_LVL_MIN) { - if (!ResizeSpriteIn(sprite, first_avail, ZOOM_LVL_MIN)) return false; - SetBit(sprite_avail, ZOOM_LVL_MIN); + ZoomLevel first_avail; + for (ZoomLevel zoom = ZOOM_LVL_MIN; zoom <= ZOOM_LVL_MAX; ++zoom) { + if (!sprite_avail.Test(zoom)) continue; + first_avail = zoom; + if (zoom != ZOOM_LVL_MIN) { + if (!ResizeSpriteIn(sprite, zoom, ZOOM_LVL_MIN)) return false; + sprite_avail.Set(ZOOM_LVL_MIN); + } + break; } /* Pad sprites to make sizes match. */ @@ -398,7 +403,7 @@ static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) { if (zoom == ZOOM_LVL_MIN) continue; - if (HasBit(sprite_avail, zoom)) { + if (sprite_avail.Test(zoom)) { /* Check that size and offsets match the fully zoomed image. */ [[maybe_unused]] const auto &root_sprite = sprite[ZOOM_LVL_MIN]; [[maybe_unused]] const auto &dest_sprite = sprite[zoom]; @@ -406,10 +411,10 @@ static bool ResizeSprites(SpriteLoader::SpriteCollection &sprite, uint8_t sprite assert(dest_sprite.height == UnScaleByZoom(root_sprite.height, zoom)); assert(dest_sprite.x_offs == UnScaleByZoom(root_sprite.x_offs, zoom)); assert(dest_sprite.y_offs == UnScaleByZoom(root_sprite.y_offs, zoom)); + } else { + /* Zoom level is not available, or unusable, so create it */ + ResizeSpriteOut(sprite, zoom); } - - /* Zoom level is not available, or unusable, so create it */ - if (!HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom); } /* Upscale to desired sprite_min_zoom if provided sprite only had zoomed in versions. */ @@ -482,25 +487,25 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty Debug(sprite, 9, "Load sprite {}", id); SpriteLoader::SpriteCollection sprite; - uint8_t sprite_avail = 0; - uint8_t avail_8bpp = 0; - uint8_t avail_32bpp = 0; + ZoomLevels sprite_avail; + ZoomLevels avail_8bpp; + ZoomLevels avail_32bpp; SpriteLoaderGrf sprite_loader(file.GetContainerVersion()); if (sprite_type != SpriteType::MapGen && encoder->Is32BppSupported()) { /* Try for 32bpp sprites first. */ sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, true, sc->control_flags, avail_8bpp, avail_32bpp); } - if (sprite_avail == 0) { + if (sprite_avail.None()) { 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 != 0 && !encoder->Is32BppSupported() && sprite_avail == 0) { + if (sprite_type == SpriteType::Normal && avail_32bpp.Any() && !encoder->Is32BppSupported() && sprite_avail.None()) { /* 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 == 0) { + if (sprite_avail.None()) { 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); diff --git a/src/spriteloader/grf.cpp b/src/spriteloader/grf.cpp index e14d96892c..db9ee7299c 100644 --- a/src/spriteloader/grf.cpp +++ b/src/spriteloader/grf.cpp @@ -215,10 +215,10 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t f return true; } -uint8_t LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t &avail_8bpp) +static ZoomLevels LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, ZoomLevels &avail_8bpp) { /* Check the requested colour depth. */ - if (load_32bpp) return 0; + if (load_32bpp) return {}; /* Open the right file and go to the correct position */ file.SeekTo(file_pos, SEEK_SET); @@ -228,7 +228,7 @@ uint8_t LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s uint8_t type = file.ReadByte(); /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */ - if (type == 0xFF) return 0; + if (type == 0xFF) return {}; ZoomLevel zoom_lvl = (sprite_type != SpriteType::MapGen) ? ZOOM_LVL_NORMAL : ZOOM_LVL_MIN; auto &dest_sprite = sprite[zoom_lvl]; @@ -241,7 +241,7 @@ uint8_t LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s if (dest_sprite.width > INT16_MAX) { WarnCorruptSprite(file, file_pos, __LINE__); - return 0; + return {}; } /* 0x02 indicates it is a compressed sprite, so we can't rely on 'num' to be valid. @@ -249,33 +249,33 @@ uint8_t LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s num = (type & 0x02) ? dest_sprite.width * dest_sprite.height : num - 8; if (DecodeSingleSprite(&dest_sprite, file, file_pos, sprite_type, num, type, zoom_lvl, SpriteComponent::Palette, 1)) { - SetBit(avail_8bpp, zoom_lvl); + avail_8bpp.Set(zoom_lvl); return avail_8bpp; } - return 0; + return {}; } -uint8_t LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) +static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) { static const ZoomLevel zoom_lvl_map[6] = {ZOOM_LVL_NORMAL, ZOOM_LVL_IN_4X, ZOOM_LVL_IN_2X, ZOOM_LVL_OUT_2X, ZOOM_LVL_OUT_4X, ZOOM_LVL_OUT_8X}; /* Is the sprite not present/stripped in the GRF? */ - if (file_pos == SIZE_MAX) return 0; + if (file_pos == SIZE_MAX) return {}; /* Open the right file and go to the correct position */ file.SeekTo(file_pos, SEEK_SET); uint32_t id = file.ReadDword(); - uint8_t loaded_sprites = 0; + ZoomLevels loaded_sprites; do { int64_t num = file.ReadDword(); size_t start_pos = file.GetPos(); uint8_t type = file.ReadByte(); /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here. */ - if (type == 0xFF) return 0; + if (type == 0xFF) return {}; SpriteComponents colour{type}; /* Mask out colour component information from type. */ @@ -288,17 +288,18 @@ uint8_t LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s if (sprite_type != SpriteType::MapGen) { if (zoom < lengthof(zoom_lvl_map)) { - if (colour == SpriteComponent::Palette) SetBit(avail_8bpp, zoom_lvl_map[zoom]); - if (colour != SpriteComponent::Palette) SetBit(avail_32bpp, zoom_lvl_map[zoom]); + ZoomLevel zoom_lvl = zoom_lvl_map[zoom]; + if (colour == SpriteComponent::Palette) avail_8bpp.Set(zoom_lvl); + if (colour != SpriteComponent::Palette) avail_32bpp.Set(zoom_lvl); is_wanted_zoom_lvl = true; ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZOOM_LVL_MIN : _settings_client.gui.sprite_zoom_min; if (zoom_min >= ZOOM_LVL_IN_2X && - HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && zoom_lvl_map[zoom] < ZOOM_LVL_IN_2X) { + HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && zoom_lvl < ZOOM_LVL_IN_2X) { is_wanted_zoom_lvl = false; } if (zoom_min >= ZOOM_LVL_NORMAL && - HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && zoom_lvl_map[zoom] < ZOOM_LVL_NORMAL) { + HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && zoom_lvl < ZOOM_LVL_NORMAL) { is_wanted_zoom_lvl = false; } } else { @@ -311,7 +312,7 @@ uint8_t LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s if (is_wanted_colour_depth && is_wanted_zoom_lvl) { ZoomLevel zoom_lvl = (sprite_type != SpriteType::MapGen) ? zoom_lvl_map[zoom] : ZOOM_LVL_MIN; - if (HasBit(loaded_sprites, zoom_lvl)) { + if (loaded_sprites.Test(zoom_lvl)) { /* We already have this zoom level, skip sprite. */ Debug(sprite, 1, "Ignoring duplicate zoom level sprite {} from {}", id, file.GetSimplifiedFilename()); file.SkipBytes(num - 2); @@ -326,7 +327,7 @@ uint8_t LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s if (dest_sprite.width > INT16_MAX || dest_sprite.height > INT16_MAX) { WarnCorruptSprite(file, file_pos, __LINE__); - return 0; + return {}; } /* Convert colour components to pixel size. */ @@ -344,10 +345,10 @@ uint8_t LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s bool valid = DecodeSingleSprite(&dest_sprite, file, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2); if (file.GetPos() != start_pos + num) { WarnCorruptSprite(file, file_pos, __LINE__); - return 0; + return {}; } - if (valid) SetBit(loaded_sprites, zoom_lvl); + if (valid) loaded_sprites.Set(zoom_lvl); } else { /* Not the wanted zoom level or colour depth, continue searching. */ file.SkipBytes(num - 2); @@ -358,7 +359,7 @@ uint8_t LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, s return loaded_sprites; } -uint8_t SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) +ZoomLevels SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) { if (this->container_ver >= 2) { return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, control_flags, avail_8bpp, avail_32bpp); diff --git a/src/spriteloader/grf.hpp b/src/spriteloader/grf.hpp index 14e89abd03..2050a909eb 100644 --- a/src/spriteloader/grf.hpp +++ b/src/spriteloader/grf.hpp @@ -17,7 +17,7 @@ class SpriteLoaderGrf : public SpriteLoader { uint8_t container_ver; public: SpriteLoaderGrf(uint8_t container_ver) : container_ver(container_ver) {} - uint8_t LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) override; + ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override; }; #endif /* SPRITELOADER_GRF_HPP */ diff --git a/src/spriteloader/makeindexed.cpp b/src/spriteloader/makeindexed.cpp index 8218e12d59..89a0bade2f 100644 --- a/src/spriteloader/makeindexed.cpp +++ b/src/spriteloader/makeindexed.cpp @@ -48,12 +48,12 @@ static void Convert32bppTo8bpp(SpriteLoader::Sprite &sprite) } } -uint8_t SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) +ZoomLevels SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) { - uint8_t avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp); + ZoomLevels avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp); for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) { - if (HasBit(avail, zoom)) Convert32bppTo8bpp(sprite[zoom]); + if (avail.Test(zoom)) Convert32bppTo8bpp(sprite[zoom]); } return avail; diff --git a/src/spriteloader/makeindexed.h b/src/spriteloader/makeindexed.h index 7b0f690663..23bdd39d7a 100644 --- a/src/spriteloader/makeindexed.h +++ b/src/spriteloader/makeindexed.h @@ -17,7 +17,7 @@ class SpriteLoaderMakeIndexed : public SpriteLoader { SpriteLoader &baseloader; public: SpriteLoaderMakeIndexed(SpriteLoader &baseloader) : baseloader(baseloader) {} - uint8_t LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) override; + ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override; }; #endif /* SPRITELOADER_MAKEINDEXED_H */ diff --git a/src/spriteloader/spriteloader.hpp b/src/spriteloader/spriteloader.hpp index a1614b8ee4..b57910538f 100644 --- a/src/spriteloader/spriteloader.hpp +++ b/src/spriteloader/spriteloader.hpp @@ -71,14 +71,16 @@ public: /** * Load a sprite from the disk and return a sprite struct which is the same for all loaders. * @param[out] sprite The sprites to fill with data. - * @param file_slot The file "descriptor" of the file we read from. + * @param file The file "descriptor" of the file we read from. * @param file_pos The position within the file the image begins. * @param sprite_type The type of sprite we're trying to load. * @param load_32bpp True if 32bpp sprites should be loaded, false for a 8bpp sprite. * @param control_flags Control flags, see SpriteCacheCtrlFlags. - * @return Bit mask of the zoom levels successfully loaded or 0 if no sprite could be loaded. + * @param[out] avail_8bpp Available 8bpp sprites. + * @param[out] avail_32bpp Available 32bpp sprites. + * @return Available sprites matching \a load_32bpp. */ - virtual uint8_t LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, uint8_t &avail_8bpp, uint8_t &avail_32bpp) = 0; + virtual ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) = 0; virtual ~SpriteLoader() = default; }; diff --git a/src/zoom_type.h b/src/zoom_type.h index 2308ac6dd0..304d13e874 100644 --- a/src/zoom_type.h +++ b/src/zoom_type.h @@ -43,6 +43,7 @@ enum ZoomLevel : uint8_t { }; DECLARE_INCREMENT_DECREMENT_OPERATORS(ZoomLevel) DECLARE_ENUM_AS_ADDABLE(ZoomLevel) +using ZoomLevels = EnumBitSet; static uint const ZOOM_BASE_SHIFT = static_cast(ZOOM_LVL_NORMAL); static uint const ZOOM_BASE = 1U << ZOOM_BASE_SHIFT;