29uint _sprite_cache_size = 4;
32static std::vector<SpriteCache> _spritecache;
33static size_t _spritecache_bytes_used = 0;
34static uint32_t _sprite_lru_counter;
35static std::vector<std::unique_ptr<SpriteFile>> _sprite_files;
37static inline SpriteCache *GetSpriteCache(uint index)
39 return &_spritecache[index];
44 if (index >= _spritecache.size()) {
46 uint items =
Align(index + 1, 1024);
48 Debug(sprite, 4,
"Increasing sprite cache to {} items ({} bytes)", items, items *
sizeof(
SpriteCache));
50 _spritecache.resize(items);
53 return GetSpriteCache(index);
63 for (
auto &f : _sprite_files) {
64 if (f->GetFilename() == filename) {
90 if (file ==
nullptr) {
91 file = _sprite_files.insert(std::end(_sprite_files), std::make_unique<SpriteFile>(filename, subdir, palette_remap))->get();
113 int size = (i == 0) ? 0x80 : i;
114 if (size > num)
return false;
130 if (
id >= _spritecache.size())
return false;
133 if (
id == 0)
return true;
134 return !(GetSpriteCache(
id)->file_pos == 0 && GetSpriteCache(
id)->file ==
nullptr);
145 return GetSpriteCache(sprite)->type;
155 if (!SpriteExists(sprite))
return nullptr;
156 return GetSpriteCache(sprite)->file;
166 if (!SpriteExists(sprite))
return 0;
167 return GetSpriteCache(sprite)->id;
180 if (file ==
nullptr)
return 0;
183 for (
SpriteID i = begin; i != end; i++) {
184 if (SpriteExists(i)) {
186 if (sc->
file == file) {
188 Debug(sprite, 4,
"Sprite: {}", i);
205 return static_cast<SpriteID>(_spritecache.size());
211 const auto &src_sprite = sprite[src];
212 auto &dest_sprite = sprite[tgt];
215 if (src_sprite.width * scaled_1 > UINT16_MAX || src_sprite.height * scaled_1 > UINT16_MAX)
return false;
217 dest_sprite.width = src_sprite.width * scaled_1;
218 dest_sprite.height = src_sprite.height * scaled_1;
219 dest_sprite.x_offs = src_sprite.x_offs * scaled_1;
220 dest_sprite.y_offs = src_sprite.y_offs * scaled_1;
221 dest_sprite.colours = src_sprite.colours;
223 dest_sprite.AllocateData(tgt,
static_cast<size_t>(dest_sprite.width) * dest_sprite.height);
226 for (
int y = 0; y < dest_sprite.height; y++) {
228 for (
int x = 0; x < dest_sprite.width; x++) {
229 *dst = src_ln[x / scaled_1];
239 const auto &root_sprite = sprite.Root();
240 const auto &src_sprite = sprite[zoom - 1];
241 auto &dest_sprite = sprite[zoom];
245 dest_sprite.height =
UnScaleByZoom(root_sprite.height, zoom);
246 dest_sprite.x_offs =
UnScaleByZoom(root_sprite.x_offs, zoom);
247 dest_sprite.y_offs =
UnScaleByZoom(root_sprite.y_offs, zoom);
248 dest_sprite.colours = root_sprite.colours;
250 dest_sprite.AllocateData(zoom,
static_cast<size_t>(dest_sprite.height) * dest_sprite.width);
256 for (uint y = 0; y < dest_sprite.height; y++) {
258 assert(src_ln <= src_end);
259 for (uint x = 0; x < dest_sprite.width; x++) {
260 assert(src < src_ln);
261 if (src + 1 != src_ln && (src + 1)->a != 0) {
269 src = src_ln + src_sprite.width;
275 uint width = sprite->
width + pad_left + pad_right;
276 uint height = sprite->
height + pad_top + pad_bottom;
278 if (width > UINT16_MAX || height > UINT16_MAX)
return false;
281 size_t sprite_size =
static_cast<size_t>(sprite->
width) * sprite->
height;
282 std::vector<SpriteLoader::CommonPixel> src_data(sprite->
data, sprite->
data + sprite_size);
283 sprite->
AllocateData(zoom,
static_cast<size_t>(width) * height);
288 for (uint y = 0; y < height; y++) {
289 if (y < pad_top || pad_bottom + y >= height) {
301 std::copy_n(src, sprite->
width, data);
302 src += sprite->
width;
303 data += sprite->
width;
314 sprite->
width = width;
316 sprite->
x_offs -= pad_left;
317 sprite->
y_offs -= pad_top;
325 int min_xoffs = INT32_MAX;
326 int min_yoffs = INT32_MAX;
328 min_xoffs = std::min(min_xoffs,
ScaleByZoom(sprite[zoom].x_offs, zoom));
329 min_yoffs = std::min(min_yoffs,
ScaleByZoom(sprite[zoom].y_offs, zoom));
333 int max_width = INT32_MIN;
334 int max_height = INT32_MIN;
336 max_width = std::max(max_width,
ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs -
UnScaleByZoom(min_xoffs, zoom), zoom));
337 max_height = std::max(max_height,
ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs -
UnScaleByZoom(min_yoffs, zoom), zoom));
343 max_width =
Align(max_width, align);
344 max_height =
Align(max_height, align);
349 auto &cur_sprite = sprite[zoom];
352 int pad_left = std::max(0, cur_sprite.x_offs -
UnScaleByZoom(min_xoffs, zoom));
353 int pad_top = std::max(0, cur_sprite.y_offs -
UnScaleByZoom(min_yoffs, zoom));
354 int pad_right = std::max(0,
UnScaleByZoom(max_width, zoom) - cur_sprite.width - pad_left);
355 int pad_bottom = std::max(0,
UnScaleByZoom(max_height, zoom) - cur_sprite.height - pad_top);
357 if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) {
358 if (!PadSingleSprite(&cur_sprite, zoom, pad_left, pad_top, pad_right, pad_bottom))
return false;
370 if (!sprite_avail.
Test(zoom))
continue;
380 if (!PadSprites(sprite, sprite_avail, encoder))
return false;
386 if (sprite_avail.
Test(zoom)) {
388 [[maybe_unused]]
const auto &root_sprite = sprite[
ZoomLevel::Min];
389 [[maybe_unused]]
const auto &dest_sprite = sprite[zoom];
390 assert(dest_sprite.width ==
UnScaleByZoom(root_sprite.width, zoom));
391 assert(dest_sprite.height ==
UnScaleByZoom(root_sprite.height, zoom));
392 assert(dest_sprite.x_offs ==
UnScaleByZoom(root_sprite.x_offs, zoom));
393 assert(dest_sprite.y_offs ==
UnScaleByZoom(root_sprite.y_offs, zoom));
396 ResizeSpriteOut(sprite, zoom);
403 ResizeSpriteIn(sprite, zoom, zoom - 1);
424 static const uint RECOLOUR_SPRITE_SIZE = 257;
425 uint8_t *dest = allocator.
Allocate<uint8_t>(std::max(RECOLOUR_SPRITE_SIZE, num));
427 file.
SeekTo(file_pos, SEEK_SET);
429 uint8_t *dest_tmp =
new uint8_t[std::max(RECOLOUR_SPRITE_SIZE, num)];
432 if (num < RECOLOUR_SPRITE_SIZE) std::fill_n(dest_tmp, RECOLOUR_SPRITE_SIZE, 0);
436 for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
437 dest[i] = _palmap_w2d[dest_tmp[
_palmap_d2w[i - 1] + 1]];
462 size_t file_pos = sc->file_pos;
466 assert(sc->
type == sprite_type);
468 Debug(sprite, 9,
"Load sprite {}",
id);
471 ZoomLevels sprite_avail;
472 ZoomLevels avail_8bpp;
473 ZoomLevels avail_32bpp;
478 sprite_avail = sprite_loader.
LoadSprite(sprite, file, file_pos, sprite_type,
true, sc->
control_flags, avail_8bpp, avail_32bpp);
480 if (sprite_avail.
None()) {
481 sprite_avail = sprite_loader.
LoadSprite(sprite, file, file_pos, sprite_type,
false, sc->
control_flags, avail_8bpp, avail_32bpp);
485 sprite_avail = make_indexed.
LoadSprite(sprite, file, file_pos, sprite_type,
true, sc->
control_flags, sprite_avail, avail_32bpp);
489 if (sprite_avail.
None()) {
491 if (
id == SPR_IMG_QUERY) UserError(
"Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
505 const auto &root_sprite = sprite.Root();
506 uint num = root_sprite.
width * root_sprite.height;
509 s->
width = root_sprite.width;
510 s->
height = root_sprite.height;
511 s->
x_offs = root_sprite.x_offs;
512 s->
y_offs = root_sprite.y_offs;
515 uint8_t *dest =
reinterpret_cast<uint8_t *
>(s->
data);
524 if (!ResizeSprites(sprite, sprite_avail, encoder)) {
525 if (
id == SPR_IMG_QUERY) UserError(
"Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
534 return encoder->
Encode(sprite_type, sprite, allocator);
539 SpriteCacheCtrlFlags control_flags{};
566 size_t old_pos = file.
GetPos();
567 file.
SeekTo(data_offset, SEEK_CUR);
577 offset.file_pos = file.
GetPos() - 4;
578 offset.control_flags.
Reset();
583 SpriteComponents colour{file.
ReadByte()};
588 if (colour.
Any() && zoom == 0) {
592 if (colour.
Any() && zoom == 2) {
602 file.
SeekTo(old_pos, SEEK_SET);
616 size_t file_pos = file.
GetPos();
620 if (num == 0)
return false;
624 SpriteCacheCtrlFlags control_flags;
625 if (grf_type == 0xFF) {
644 file_pos = iter->second.file_pos;
645 control_flags = iter->second.control_flags;
660 UserError(
"Tried to load too many sprites (#{}; max {})", load_index,
MAX_SPRITES);
663 bool is_mapgen = IsMapgenSpriteID(load_index);
666 if (type !=
SpriteType::Normal) UserError(
"Uhm, would you be so kind not to load a NewGRF that changes the type of the map generator sprites?");
672 sc->file_pos = file_pos;
675 sc->id = file_sprite_id;
690 scnew->file_pos = scold->file_pos;
691 scnew->ClearSpriteData();
692 scnew->id = scold->id;
706 const size_t initial_in_use = _spritecache_bytes_used;
713 bool operator<(
const SpriteInfo &other)
const
715 return this->lru < other.lru;
719 std::vector<SpriteInfo> candidates;
720 size_t candidate_bytes = 0;
722 auto push = [&](SpriteInfo info) {
723 candidates.push_back(info);
724 std::push_heap(candidates.begin(), candidates.end());
725 candidate_bytes += info.size;
729 candidate_bytes -= candidates.front().size;
730 std::pop_heap(candidates.begin(), candidates.end());
731 candidates.pop_back();
735 for (; i !=
static_cast<SpriteID>(_spritecache.size()) && candidate_bytes < to_remove; i++) {
737 if (sc->ptr !=
nullptr) {
738 push({ sc->lru, i, sc->
length });
739 if (candidate_bytes >= to_remove)
break;
744 for (; i !=
static_cast<SpriteID>(_spritecache.size()); i++) {
746 if (sc->ptr !=
nullptr && sc->lru <= candidates.front().lru) {
747 push({ sc->lru, i, sc->
length });
748 while (!candidates.empty() && candidate_bytes - candidates.front().size >= to_remove) {
754 for (
const auto &it : candidates) {
755 GetSpriteCache(it.id)->ClearSpriteData();
758 Debug(sprite, 3,
"DeleteEntriesFromSpriteCache, deleted: {}, freed: {}, in use: {} --> {}, requested: {}",
759 candidates.size(), candidate_bytes, initial_in_use, _spritecache_bytes_used, to_remove);
762void IncreaseSpriteLRU()
765 uint target_size = (bpp > 0 ? _sprite_cache_size * bpp / 8 : 1) * 1024 * 1024;
766 if (_spritecache_bytes_used > target_size) {
770 if (_sprite_lru_counter >= 0xC0000000) {
771 Debug(sprite, 3,
"Fixing lru {}, inuse={}", _sprite_lru_counter, _spritecache_bytes_used);
774 if (sc.ptr !=
nullptr) {
775 if (sc.lru > 0x80000000) {
776 sc.lru -= 0x80000000;
782 _sprite_lru_counter -= 0x80000000;
786void SpriteCache::ClearSpriteData()
788 _spritecache_bytes_used -= this->
length;
794 this->data = std::make_unique<std::byte[]>(size);
796 return this->data.get();
811 static const std::string_view sprite_types[] = {
824 uint8_t warning_level = sc->
warned ? 6 : 0;
826 Debug(sprite, warning_level,
"Tried to load {} sprite #{} as a {} sprite. Probable cause: NewGRF interference", sprite_types[
static_cast<uint8_t
>(available)], sprite, sprite_types[
static_cast<uint8_t
>(requested)]);
830 if (sprite == SPR_IMG_QUERY) UserError(
"Uhm, would you be so kind not to load a NewGRF that makes the 'query' sprite a non-normal sprite?");
835 if (sprite == PALETTE_TO_DARK_BLUE) UserError(
"Uhm, would you be so kind not to load a NewGRF that makes the 'PALETTE_TO_DARK_BLUE' sprite a non-remap sprite?");
859 if (!SpriteExists(sprite)) {
860 Debug(sprite, 1,
"Tried to load non-existing sprite #{}. Probable cause: Wrong/missing NewGRFs", sprite);
863 sprite = SPR_IMG_QUERY;
870 if (allocator ==
nullptr && encoder ==
nullptr) {
874 sc->lru = ++_sprite_lru_counter;
877 if (sc->ptr ==
nullptr) {
882 ReadSprite(sc, sprite, type, cache_allocator,
nullptr);
884 sc->ptr = std::move(cache_allocator.data);
885 sc->
length =
static_cast<uint32_t
>(cache_allocator.size);
886 _spritecache_bytes_used += sc->
length;
889 return static_cast<void *
>(sc->ptr.get());
892 return ReadSprite(sc, sprite, type, *allocator, encoder);
896void GfxInitSpriteMem()
899 _spritecache.clear();
900 _spritecache.shrink_to_fit();
902 _sprite_files.clear();
903 _spritecache_bytes_used = 0;
914 if (sc.ptr !=
nullptr) sc.ClearSpriteData();
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
void ReadBlock(void *ptr, size_t size)
Read a block.
size_t GetPos() const
Get position in the file.
void SeekTo(size_t pos, int mode)
Seek in the current file.
uint8_t ReadByte()
Read a byte from the file.
uint32_t ReadDword()
Read a double word (32 bits) from the file (in low endian format).
void SkipBytes(size_t n)
Skip n bytes ahead in the file.
uint16_t ReadWord()
Read a word (16 bits) from the file (in low endian format).
Interface for something that can allocate memory for a sprite.
T * Allocate(size_t size)
Allocate memory for a sprite.
Interface for something that can encode a sprite.
virtual bool Is32BppSupported()=0
Can the sprite encoder make use of RGBA sprites?
virtual uint GetSpriteAlignment()
Get the value which the height and width on a sprite have to be aligned by.
virtual Sprite * Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)=0
Convert a sprite from the loader to our own format.
RandomAccessFile with some extra information specific for sprite files.
bool NeedsPaletteRemap() const
Whether a palette remap is needed when loading sprites from this file.
uint8_t GetContainerVersion() const
Get the version number of container type used by the file.
void SeekToBegin()
Seek to the begin of the content, i.e.
Sprite loader for graphics coming from a (New)GRF.
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override
Load a sprite from the disk and return a sprite struct which is the same for all loaders.
Sprite loader for converting graphics coming from another source.
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override
Load a sprite from the disk and return a sprite struct which is the same for all loaders.
SpriteCollMap< Sprite > SpriteCollection
Type defining a collection of sprites, one for each zoom level.
SpriteAllocator that allocates memory via a unique_ptr array.
void * AllocatePtr(size_t size) override
Allocate memory for a sprite.
virtual void ClearSystemSprites()
Clear all cached sprites.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Error reporting related functions.
Factory to 'query' all available blitters.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
ZoomLevel _font_zoom
Sprite font Zoom level (not clamped).
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
SpriteType
Types of sprites that might be loaded.
@ Recolour
Recolour sprite.
@ Font
A sprite used for fonts.
@ MapGen
Special sprite for the map generator.
@ Invalid
Pseudosprite or other unusable sprite, used only internally.
@ Normal
The most basic (normal) sprite.
Base for reading sprites from (New)GRFs.
Base for converting sprites from another source from 32bpp RGBA to indexed 8bpp.
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Translation tables from one GRF to another GRF.
static const uint8_t _palmap_d2w[]
Converting from the DOS palette to the Windows palette.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Types related to global configuration settings.
static void DeleteEntriesFromSpriteCache(size_t to_remove)
Delete entries from the sprite cache to remove the requested number of bytes.
static std::map< uint32_t, GrfSpriteOffset > _grf_sprite_offsets
Map from sprite numbers to position in the GRF file.
SpriteType GetSpriteType(SpriteID sprite)
Get the sprite type of a given sprite.
void GfxClearSpriteCache()
Remove all encoded sprites from the sprite cache without discarding sprite location information.
static SpriteFile * GetCachedSpriteFileByName(const std::string &filename)
Get the cached SpriteFile given the name of the file.
uint GetSpriteCountForFile(const std::string &filename, SpriteID begin, SpriteID end)
Count the sprites which originate from a specific file in a range of SpriteIDs.
static void * ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_type, SpriteAllocator &allocator, SpriteEncoder *encoder)
Read a sprite from disk.
void GfxClearFontSpriteCache()
Remove all encoded font sprites from the sprite cache without discarding sprite location information.
SpriteID GetMaxSpriteID()
Get a reasonable (upper bound) estimate of the maximum SpriteID used in OpenTTD; there will be no spr...
SpriteFile & OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap)
Open/get the SpriteFile that is cached for use in the sprite cache.
static void * HandleInvalidSpriteRequest(SpriteID sprite, SpriteType requested, SpriteCache *sc, SpriteAllocator *allocator)
Handles the case when a sprite of different type is requested than is present in the SpriteCache.
void * GetRawSprite(SpriteID sprite, SpriteType type, SpriteAllocator *allocator, SpriteEncoder *encoder)
Reads a sprite (from disk or sprite cache).
std::span< const std::unique_ptr< SpriteFile > > GetCachedSpriteFiles()
Get the list of cached SpriteFiles.
bool LoadNextSprite(SpriteID load_index, SpriteFile &file, uint file_sprite_id)
Load a real or recolour sprite.
SpriteFile * GetOriginFile(SpriteID sprite)
Get the SpriteFile of a given sprite.
size_t GetGRFSpriteOffset(uint32_t id)
Get the file offset for a specific sprite in the sprite section of a GRF.
static void * ReadRecolourSprite(SpriteFile &file, size_t file_pos, uint num, SpriteAllocator &allocator)
Load a recolour sprite into memory.
bool SkipSpriteData(SpriteFile &file, uint8_t type, uint16_t num)
Skip the given amount of sprite graphics data.
uint32_t GetSpriteLocalID(SpriteID sprite)
Get the GRF-local sprite id of a given sprite.
void ReadGRFSpriteOffsets(SpriteFile &file)
Parse the sprite section of GRFs.
Functions to cache sprites in memory.
Internal functions to cache sprites in memory.
@ AllowZoomMin1x32bpp
Allow use of sprite min zoom setting at 1x in 32bpp mode.
@ AllowZoomMin2x32bpp
Allow use of sprite min zoom setting at 2x in 32bpp mode.
@ AllowZoomMin1xPal
Allow use of sprite min zoom setting at 1x in palette mode.
@ AllowZoomMin2xPal
Allow use of sprite min zoom setting at 2x in palette mode.
@ Palette
Sprite has palette data.
This file contains all sprite-related enums and defines.
static constexpr uint32_t MAX_SPRITES
Masks needed for sprite operations.
Definition of base types and functions in a cross-platform compatible way.
Functions related to OTTD's strings.
uint32_t length
Length of sprite data.
bool warned
True iff the user has been warned about incorrect use of this sprite.
SpriteCacheCtrlFlags control_flags
Control flags, see SpriteCacheCtrlFlags.
SpriteType type
In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour spr...
SpriteFile * file
The file the sprite in this entry can be found in.
Definition of a common pixel in OpenTTD's realm.
Structure for passing information from the sprite loader to the blitter.
static SpriteCollMap< ReusableBuffer< SpriteLoader::CommonPixel > > buffer
Allocated memory to pass sprite data around.
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
uint16_t width
Width of the sprite.
int16_t x_offs
The x-offset of where the sprite will be drawn.
SpriteLoader::CommonPixel * data
The sprite itself.
uint16_t height
Height of the sprite.
int16_t y_offs
The y-offset of where the sprite will be drawn.
Data structure describing a sprite.
uint16_t width
Width of the sprite.
uint16_t height
Height of the sprite.
int16_t y_offs
Number of pixels to shift the sprite downwards.
std::byte data[]
Sprite data.
int16_t x_offs
Number of pixels to shift the sprite to the right.
Base of all video drivers.
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
int AdjustByZoom(int value, int zoom)
Adjust by zoom level; zoom < 0 shifts right, zoom >= 0 shifts left.
int UnScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZoomLevel::Min) When shifting right,...
ZoomLevel
All zoom levels we know.
@ Begin
Begin for iteration.
@ Normal
The normal zoom level.