Sprite cache: Allow caching only required subset of sprite zoom levels
Enable for blitters based on 32bpp_optimized or SSE
This commit is contained in:
		@@ -256,13 +256,52 @@ uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_p
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, byte control_flags)
 | 
			
		||||
uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, uint16 control_flags, uint8 zoom_levels)
 | 
			
		||||
{
 | 
			
		||||
	static const ZoomLevel zoom_lvl_map[6] = {ZOOM_LVL_OUT_4X, ZOOM_LVL_NORMAL, ZOOM_LVL_OUT_2X, ZOOM_LVL_OUT_8X, ZOOM_LVL_OUT_16X, ZOOM_LVL_OUT_32X};
 | 
			
		||||
 | 
			
		||||
	/* Is the sprite not present/stripped in the GRF? */
 | 
			
		||||
	if (file_pos == SIZE_MAX) return 0;
 | 
			
		||||
 | 
			
		||||
	/* clamp to first 6 zoom levels, as in zoom_lvl_map */
 | 
			
		||||
	zoom_levels &= 0x3F;
 | 
			
		||||
 | 
			
		||||
	uint8 available_levels = GB(control_flags, load_32bpp ? SCC_32BPP_ZOOM_START : SCC_PAL_ZOOM_START, 6);
 | 
			
		||||
	uint8 skip_levels = 0;
 | 
			
		||||
	ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZOOM_LVL_NORMAL : _settings_client.gui.sprite_zoom_min;
 | 
			
		||||
 | 
			
		||||
	if (unlikely(sprite_type == SpriteType::MapGen)) {
 | 
			
		||||
		available_levels = UINT8_MAX;
 | 
			
		||||
		zoom_levels = 0x3F;
 | 
			
		||||
	} else if (available_levels != 0) {
 | 
			
		||||
		if (zoom_min >= ZOOM_LVL_OUT_2X && (HasBit(available_levels, ZOOM_LVL_OUT_2X) || HasBit(available_levels, ZOOM_LVL_OUT_4X))) {
 | 
			
		||||
			ClrBit(available_levels, ZOOM_LVL_NORMAL);
 | 
			
		||||
		}
 | 
			
		||||
		if (zoom_min >= ZOOM_LVL_OUT_4X && HasBit(available_levels, ZOOM_LVL_OUT_4X)) {
 | 
			
		||||
			ClrBit(available_levels, ZOOM_LVL_NORMAL);
 | 
			
		||||
			ClrBit(available_levels, ZOOM_LVL_OUT_2X);
 | 
			
		||||
		}
 | 
			
		||||
		if (zoom_levels == 0) {
 | 
			
		||||
			skip_levels = available_levels;
 | 
			
		||||
		} else if (zoom_levels != 0x3F) {
 | 
			
		||||
			uint8 keep_levels = 0;
 | 
			
		||||
			for (uint8 bit : SetBitIterator(zoom_levels)) {
 | 
			
		||||
				if (HasBit(available_levels, bit)) {
 | 
			
		||||
					SetBit(keep_levels, bit);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				uint8 below = ((1 << bit) - 1) & available_levels;
 | 
			
		||||
				if (below != 0) {
 | 
			
		||||
					SetBit(keep_levels, FindLastBit(below));
 | 
			
		||||
				} else {
 | 
			
		||||
					SetBit(keep_levels, FindFirstBit((~below) & available_levels));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			skip_levels = available_levels & (~keep_levels);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Open the right file and go to the correct position */
 | 
			
		||||
	file.SeekTo(file_pos, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
@@ -285,16 +324,7 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_p
 | 
			
		||||
 | 
			
		||||
		if (sprite_type != SpriteType::MapGen) {
 | 
			
		||||
			if (zoom < lengthof(zoom_lvl_map)) {
 | 
			
		||||
				is_wanted_zoom_lvl = true;
 | 
			
		||||
				ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZOOM_LVL_NORMAL : _settings_client.gui.sprite_zoom_min;
 | 
			
		||||
				if (zoom_min >= ZOOM_LVL_OUT_2X &&
 | 
			
		||||
						HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && zoom_lvl_map[zoom] < ZOOM_LVL_OUT_2X) {
 | 
			
		||||
					is_wanted_zoom_lvl = false;
 | 
			
		||||
				}
 | 
			
		||||
				if (zoom_min >= ZOOM_LVL_OUT_4X &&
 | 
			
		||||
						HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && zoom_lvl_map[zoom] < ZOOM_LVL_OUT_4X) {
 | 
			
		||||
					is_wanted_zoom_lvl = false;
 | 
			
		||||
				}
 | 
			
		||||
				is_wanted_zoom_lvl = HasBit(available_levels, zoom_lvl_map[zoom]);
 | 
			
		||||
			} else {
 | 
			
		||||
				is_wanted_zoom_lvl = false;
 | 
			
		||||
			}
 | 
			
		||||
@@ -316,12 +346,28 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_p
 | 
			
		||||
			sprite[zoom_lvl].width  = file.ReadWord();
 | 
			
		||||
			sprite[zoom_lvl].x_offs = file.ReadWord();
 | 
			
		||||
			sprite[zoom_lvl].y_offs = file.ReadWord();
 | 
			
		||||
			sprite[zoom_lvl].colours = (SpriteColourComponent)colour;
 | 
			
		||||
 | 
			
		||||
			if (sprite[zoom_lvl].width > INT16_MAX || sprite[zoom_lvl].height > INT16_MAX) {
 | 
			
		||||
				WarnCorruptSprite(file, file_pos, __LINE__);
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ClrBit(available_levels, zoom_lvl);
 | 
			
		||||
 | 
			
		||||
			if (HasBit(skip_levels, zoom_lvl)) {
 | 
			
		||||
				sprite[zoom_lvl].data = nullptr;
 | 
			
		||||
				SetBit(loaded_sprites, zoom_lvl);
 | 
			
		||||
 | 
			
		||||
				if (available_levels == 0) {
 | 
			
		||||
					/* nothing more to do */
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				file.SkipBytes(num - 2 - 8);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Mask out colour information. */
 | 
			
		||||
			type = type & ~SCC_MASK;
 | 
			
		||||
 | 
			
		||||
@@ -331,8 +377,6 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_p
 | 
			
		||||
			if (colour & SCC_ALPHA) bpp++;    // Has alpha data.
 | 
			
		||||
			if (colour & SCC_PAL)   bpp++;    // Has palette data.
 | 
			
		||||
 | 
			
		||||
			sprite[zoom_lvl].colours = (SpriteColourComponent)colour;
 | 
			
		||||
 | 
			
		||||
			/* For chunked encoding we store the decompressed size in the file,
 | 
			
		||||
			 * otherwise we can calculate it from the image dimensions. */
 | 
			
		||||
			uint decomp_size = (type & 0x08) ? file.ReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp;
 | 
			
		||||
@@ -345,6 +389,10 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_p
 | 
			
		||||
 | 
			
		||||
			if (valid) SetBit(loaded_sprites, zoom_lvl);
 | 
			
		||||
			if (--count == 0) break;
 | 
			
		||||
			if (available_levels == 0) {
 | 
			
		||||
				/* nothing more to do */
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if (--count == 0) break;
 | 
			
		||||
			/* Not the wanted zoom level or colour depth, continue searching. */
 | 
			
		||||
@@ -356,10 +404,10 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_p
 | 
			
		||||
	return loaded_sprites;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, byte control_flags)
 | 
			
		||||
uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, uint16 control_flags, uint8 zoom_levels)
 | 
			
		||||
{
 | 
			
		||||
	if (this->container_ver >= 2) {
 | 
			
		||||
		return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, count, control_flags);
 | 
			
		||||
		return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, count, control_flags, zoom_levels);
 | 
			
		||||
	} else {
 | 
			
		||||
		return LoadSpriteV1(sprite, file, file_pos, sprite_type, load_32bpp);
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,11 @@
 | 
			
		||||
#include "spriteloader.hpp"
 | 
			
		||||
 | 
			
		||||
/** Sprite loader for graphics coming from a (New)GRF. */
 | 
			
		||||
class SpriteLoaderGrf : public SpriteLoader {
 | 
			
		||||
class SpriteLoaderGrf FINAL : public SpriteLoader {
 | 
			
		||||
	byte container_ver;
 | 
			
		||||
public:
 | 
			
		||||
	SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {}
 | 
			
		||||
	uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, byte control_flags);
 | 
			
		||||
	uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, uint16 control_flags, uint8 zoom_levels) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* SPRITELOADER_GRF_HPP */
 | 
			
		||||
 
 | 
			
		||||
@@ -75,17 +75,30 @@ public:
 | 
			
		||||
	 * @param control_flags Control flags, see SpriteCacheCtrlFlags.
 | 
			
		||||
	 * @return Bit mask of the zoom levels successfully loaded or 0 if no sprite could be loaded.
 | 
			
		||||
	 */
 | 
			
		||||
	virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, byte control_flags) = 0;
 | 
			
		||||
	virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint count, uint16 control_flags, uint8 zoom_levels) = 0;
 | 
			
		||||
 | 
			
		||||
	virtual ~SpriteLoader() = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** Interface for something that can encode a sprite. */
 | 
			
		||||
class SpriteEncoder {
 | 
			
		||||
	bool supports_missing_zoom_levels = false;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	inline void SetSupportsMissingZoomLevels(bool supported)
 | 
			
		||||
	{
 | 
			
		||||
		this->supports_missing_zoom_levels = supported;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
	virtual ~SpriteEncoder() = default;
 | 
			
		||||
 | 
			
		||||
	inline bool SupportsMissingZoomLevels() const
 | 
			
		||||
	{
 | 
			
		||||
		return this->supports_missing_zoom_levels;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Can the sprite encoder make use of RGBA sprites?
 | 
			
		||||
	 */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user