26 #include "table/strings.h"
32 uint _sprite_cache_size = 4;
35 static uint _spritecache_items = 0;
37 static std::vector<std::unique_ptr<SpriteFile>> _sprite_files;
39 static inline SpriteCache *GetSpriteCache(uint index)
41 return &_spritecache[index];
46 if (index >= _spritecache_items) {
48 uint items =
Align(index + 1, 1024);
50 Debug(sprite, 4,
"Increasing sprite cache to {} items ({} bytes)", items, items *
sizeof(*_spritecache));
52 _spritecache =
ReallocT(_spritecache, items);
55 memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) *
sizeof(*_spritecache));
56 _spritecache_items = items;
59 return GetSpriteCache(index);
69 for (
auto &f : _sprite_files) {
70 if (f->GetFilename() == filename) {
96 if (file ==
nullptr) {
97 file = _sprite_files.insert(std::end(_sprite_files), std::make_unique<SpriteFile>(filename, subdir, palette_remap))->get();
109 static uint _sprite_lru_counter;
111 static uint _allocated_sprite_cache_size = 0;
112 static int _compact_cache_counter;
130 int size = (i == 0) ? 0x80 : i;
131 if (size > num)
return false;
147 if (
id >= _spritecache_items)
return false;
150 if (
id == 0)
return true;
151 return !(GetSpriteCache(
id)->file_pos == 0 && GetSpriteCache(
id)->
file ==
nullptr);
162 return GetSpriteCache(sprite)->
type;
172 if (!SpriteExists(sprite))
return nullptr;
173 return GetSpriteCache(sprite)->
file;
183 if (!SpriteExists(sprite))
return 0;
184 return GetSpriteCache(sprite)->id;
197 if (file ==
nullptr)
return 0;
200 for (
SpriteID i = begin; i != end; i++) {
201 if (SpriteExists(i)) {
203 if (sc->
file == file) {
205 Debug(sprite, 4,
"Sprite: {}", i);
222 return _spritecache_items;
230 if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX)
return false;
232 sprite[tgt].width = sprite[src].width * scaled_1;
233 sprite[tgt].height = sprite[src].height * scaled_1;
234 sprite[tgt].x_offs = sprite[src].x_offs * scaled_1;
235 sprite[tgt].y_offs = sprite[src].y_offs * scaled_1;
236 sprite[tgt].colours = sprite[src].colours;
238 sprite[tgt].AllocateData(tgt,
static_cast<size_t>(sprite[tgt].width) * sprite[tgt].height);
241 for (
int y = 0; y < sprite[tgt].height; y++) {
243 for (
int x = 0; x < sprite[tgt].width; x++) {
244 *dst = src_ln[x / scaled_1];
261 sprite[zoom].AllocateData(zoom,
static_cast<size_t>(sprite[zoom].height) * sprite[zoom].width);
267 for (uint y = 0; y < sprite[zoom].height; y++) {
269 assert(src_ln <= src_end);
270 for (uint x = 0; x < sprite[zoom].width; x++) {
271 assert(src < src_ln);
272 if (src + 1 != src_ln && (src + 1)->a != 0) {
280 src = src_ln + sprite[zoom - 1].width;
286 uint width = sprite->
width + pad_left + pad_right;
287 uint height = sprite->
height + pad_top + pad_bottom;
289 if (width > UINT16_MAX || height > UINT16_MAX)
return false;
292 size_t sprite_size =
static_cast<size_t>(sprite->
width) * sprite->
height;
293 std::vector<SpriteLoader::CommonPixel> src_data(sprite->
data, sprite->
data + sprite_size);
294 sprite->
AllocateData(zoom,
static_cast<size_t>(width) * height);
299 for (uint y = 0; y < height; y++) {
300 if (y < pad_top || pad_bottom + y >= height) {
313 src += sprite->
width;
314 data += sprite->
width;
325 sprite->
width = width;
327 sprite->
x_offs -= pad_left;
328 sprite->
y_offs -= pad_top;
336 int min_xoffs = INT32_MAX;
337 int min_yoffs = INT32_MAX;
339 if (
HasBit(sprite_avail, zoom)) {
340 min_xoffs = std::min(min_xoffs,
ScaleByZoom(sprite[zoom].x_offs, zoom));
341 min_yoffs = std::min(min_yoffs,
ScaleByZoom(sprite[zoom].y_offs, zoom));
346 int max_width = INT32_MIN;
347 int max_height = INT32_MIN;
349 if (
HasBit(sprite_avail, zoom)) {
350 max_width = std::max(max_width,
ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs -
UnScaleByZoom(min_xoffs, zoom), zoom));
351 max_height = std::max(max_height,
ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs -
UnScaleByZoom(min_yoffs, zoom), zoom));
358 max_width =
Align(max_width, align);
359 max_height =
Align(max_height, align);
364 if (
HasBit(sprite_avail, zoom)) {
367 int pad_left = std::max(0, sprite[zoom].x_offs -
UnScaleByZoom(min_xoffs, zoom));
368 int pad_top = std::max(0, sprite[zoom].y_offs -
UnScaleByZoom(min_yoffs, zoom));
369 int pad_right = std::max(0,
UnScaleByZoom(max_width, zoom) - sprite[zoom].width - pad_left);
370 int pad_bottom = std::max(0,
UnScaleByZoom(max_height, zoom) - sprite[zoom].height - pad_top);
372 if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) {
373 if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom))
return false;
386 if (!ResizeSpriteIn(sprite, first_avail,
ZOOM_LVL_MIN))
return false;
391 if (!PadSprites(sprite, sprite_avail, encoder))
return false;
397 if (
HasBit(sprite_avail, zoom)) {
406 if (!
HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
430 static const uint RECOLOUR_SPRITE_SIZE = 257;
431 uint8_t *dest = allocator.
Allocate<uint8_t>(std::max(RECOLOUR_SPRITE_SIZE, num));
434 uint8_t *dest_tmp =
new uint8_t[std::max(RECOLOUR_SPRITE_SIZE, num)];
437 if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE);
441 for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
467 size_t file_pos = sc->file_pos;
471 assert(sc->
type == sprite_type);
473 Debug(sprite, 9,
"Load sprite {}",
id);
476 uint8_t sprite_avail = 0;
484 if (sprite_avail == 0) {
488 if (sprite_avail == 0) {
490 if (
id == SPR_IMG_QUERY) UserError(
"Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
513 uint8_t *dest = s->
data;
522 if (!ResizeSprites(sprite, sprite_avail, encoder)) {
523 if (
id == SPR_IMG_QUERY) UserError(
"Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
537 return encoder->
Encode(sprite, allocator);
542 uint8_t control_flags;
569 size_t old_pos = file.
GetPos();
570 file.
SeekTo(data_offset, SEEK_CUR);
580 offset.file_pos = file.
GetPos() - 4;
581 offset.control_flags = 0;
591 if (colour != 0 && zoom == 0) {
595 if (colour != 0 && zoom == 2) {
605 file.
SeekTo(old_pos, SEEK_SET);
620 size_t file_pos = file.
GetPos();
624 if (num == 0)
return false;
628 void *data =
nullptr;
629 uint8_t control_flags = 0;
630 if (grf_type == 0xFF) {
649 file_pos = iter->second.file_pos;
650 control_flags = iter->second.control_flags;
665 UserError(
"Tried to load too many sprites (#{}; max {})", load_index,
MAX_SPRITES);
668 bool is_mapgen = IsMapgenSpriteID(load_index);
671 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?");
677 sc->file_pos = file_pos;
680 sc->id = file_sprite_id;
695 scnew->file_pos = scold->file_pos;
696 scnew->ptr =
nullptr;
697 scnew->id = scold->id;
712 static_assert(
sizeof(
MemBlock) ==
sizeof(
size_t));
714 static_assert((
sizeof(
size_t) & (
sizeof(
size_t) - 1)) == 0);
721 static size_t GetSpriteCacheUsage()
726 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
734 void IncreaseSpriteLRU()
737 if (_sprite_lru_counter > 16384) {
740 Debug(sprite, 5,
"Fixing lru {}, inuse={}", _sprite_lru_counter, GetSpriteCacheUsage());
742 for (i = 0; i != _spritecache_items; i++) {
744 if (sc->ptr !=
nullptr) {
747 }
else if (sc->lru != -32768) {
752 _sprite_lru_counter = 0;
756 if (++_compact_cache_counter >= 740) {
758 _compact_cache_counter = 0;
770 Debug(sprite, 3,
"Compacting sprite cache, inuse={}", GetSpriteCacheUsage());
772 for (s = _spritecache_ptr; s->size != 0;) {
782 if (next->size == 0)
break;
785 for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
786 assert(i != _spritecache_items);
789 GetSpriteCache(i)->ptr = s->data;
792 memmove(s, next, next->size);
816 GetSpriteCache(item)->ptr =
nullptr;
819 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
830 uint best = UINT_MAX;
833 Debug(sprite, 3,
"DeleteEntryFromSpriteCache, inuse={}", GetSpriteCacheUsage());
836 for (
SpriteID i = 0; i != _spritecache_items; i++) {
846 if (best == UINT_MAX) FatalError(
"Out of sprite memory");
862 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
868 if (cur_size == mem_req ||
869 cur_size >= mem_req +
sizeof(
MemBlock)) {
874 if (cur_size != mem_req) {
875 NextBlock(s)->size = (cur_size - mem_req) |
S_FREE_MASK;
893 return MallocT<uint8_t>(size);
898 this->data = std::make_unique<uint8_t[]>(size);
899 return this->data.get();
913 static const char *
const sprite_types[] = {
926 uint8_t warning_level = sc->
warned ? 6 : 0;
928 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)]);
932 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?");
937 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?");
961 if (!SpriteExists(sprite)) {
962 Debug(sprite, 1,
"Tried to load non-existing sprite #{}. Probable cause: Wrong/missing NewGRFs", sprite);
965 sprite = SPR_IMG_QUERY;
972 if (allocator ==
nullptr && encoder ==
nullptr) {
977 sc->lru = ++_sprite_lru_counter;
980 if (sc->ptr ==
nullptr) sc->ptr =
ReadSprite(sc, sprite, type, cache_allocator,
nullptr);
985 return ReadSprite(sc, sprite, type, *allocator, encoder);
990 static void GfxInitSpriteCache()
994 uint target_size = (bpp > 0 ? _sprite_cache_size * bpp / 8 : 1) * 1024 * 1024;
997 static uint last_alloc_attempt = 0;
999 if (_spritecache_ptr ==
nullptr || (_allocated_sprite_cache_size != target_size && target_size != last_alloc_attempt)) {
1000 delete[]
reinterpret_cast<uint8_t *
>(_spritecache_ptr);
1002 last_alloc_attempt = target_size;
1003 _allocated_sprite_cache_size = target_size;
1007 _spritecache_ptr =
reinterpret_cast<MemBlock *
>(
new(std::nothrow) uint8_t[_allocated_sprite_cache_size + _allocated_sprite_cache_size / 2]);
1009 if (_spritecache_ptr !=
nullptr) {
1011 delete[]
reinterpret_cast<uint8_t *
>(_spritecache_ptr);
1012 _spritecache_ptr =
reinterpret_cast<MemBlock *
>(
new uint8_t[_allocated_sprite_cache_size]);
1013 }
else if (_allocated_sprite_cache_size < 2 * 1024 * 1024) {
1014 UserError(
"Cannot allocate spritecache");
1017 _allocated_sprite_cache_size >>= 1;
1019 }
while (_spritecache_ptr ==
nullptr);
1021 if (_allocated_sprite_cache_size != target_size) {
1022 Debug(misc, 0,
"Not enough memory to allocate {} MiB of spritecache. Spritecache was reduced to {} MiB.", target_size / 1024 / 1024, _allocated_sprite_cache_size / 1024 / 1024);
1024 ErrorMessageData msg(STR_CONFIG_ERROR_OUT_OF_MEMORY, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG);
1025 msg.SetDParam(0, target_size);
1026 msg.SetDParam(1, _allocated_sprite_cache_size);
1034 NextBlock(_spritecache_ptr)->size = 0;
1037 void GfxInitSpriteMem()
1039 GfxInitSpriteCache();
1043 _spritecache_items = 0;
1044 _spritecache =
nullptr;
1046 _compact_cache_counter = 0;
1047 _sprite_files.clear();
1057 for (uint i = 0; i != _spritecache_items; i++) {
1072 for (uint i = 0; i != _spritecache_items; i++) {
T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
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.
SpriteAllocator that allocates memory from the sprite cache.
void * AllocatePtr(size_t size) override
Allocate memory for a sprite.
The data of the error message.
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).
void * AllocatePtr(size_t size) override
Sprite allocator simply using malloc.
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(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.
uint8_t LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags) override
Load a sprite from the disk and return a sprite struct which is the same for all loaders.
std::array< Sprite, ZOOM_LVL_END > SpriteCollection
Type defining a collection of sprites, one for each zoom level.
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,...)
Ouptut a line of debugging information.
Functions related to errors.
void ScheduleErrorMessage(ErrorList &datas)
Schedule a list of errors.
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)
Functions related to the gfx engine.
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.
constexpr T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
Functions related to memory operations.
void MemCpyT(T *destination, const T *source, size_t num=1)
Type-safe version of memcpy().
void MemSetT(T *ptr, uint8_t value, size_t num=1)
Type-safe version of memset().
Translation tables from one GRF to another GRF.
const uint8_t _palmap_w2d[]
Converting from the Windows palette to the DOS palette.
static const uint8_t _palmap_d2w[]
Converting from the DOS palette to the Windows palette.
Class related to random access to files.
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 DeleteEntryFromSpriteCache(uint item)
Delete a single entry from the sprite cache.
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 void * ReadRecolourSprite(SpriteFile &file, uint num, SpriteAllocator &allocator)
Load a recolour sprite into memory.
uint GetSpriteCountForFile(const std::string &filename, SpriteID begin, SpriteID end)
Count the sprites which originate from a specific file in a range of SpriteIDs.
void GfxClearFontSpriteCache()
Remove all encoded font sprites from the sprite cache without discarding sprite location information.
SpriteFile & OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap)
Open/get the SpriteFile that is cached for use in the 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.
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.
SpriteFile * GetOriginFile(SpriteID sprite)
Get the SpriteFile of a given sprite.
uint GetMaxSpriteID()
Get a reasonable (upper bound) estimate of the maximum SpriteID used in OpenTTD; there will be no spr...
void * GetRawSprite(SpriteID sprite, SpriteType type, SpriteAllocator *allocator, SpriteEncoder *encoder)
Reads a sprite (from disk or sprite cache).
static void * ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_type, SpriteAllocator &allocator, SpriteEncoder *encoder)
Read a sprite from disk.
size_t GetGRFSpriteOffset(uint32_t id)
Get the file offset for a specific sprite in the sprite section of a GRF.
bool SkipSpriteData(SpriteFile &file, uint8_t type, uint16_t num)
Skip the given amount of sprite graphics data.
static SpriteFile * GetCachedSpriteFileByName(const std::string &filename)
Get the cached SpriteFile given the name of the file.
uint32_t GetSpriteLocalID(SpriteID sprite)
Get the GRF-local sprite id of a given sprite.
static void CompactSpriteCache()
Called when holes in the sprite cache should be removed.
static const size_t S_FREE_MASK
S_FREE_MASK is used to mask-out lower bits of MemBlock::size If they are non-zero,...
void ReadGRFSpriteOffsets(SpriteFile &file)
Parse the sprite section of GRFs.
Functions to cache sprites in memory.
@ SCCF_ALLOW_ZOOM_MIN_2X_32BPP
Allow use of sprite min zoom setting at 2x in 32bpp mode.
@ SCCF_ALLOW_ZOOM_MIN_1X_32BPP
Allow use of sprite min zoom setting at 1x in 32bpp mode.
@ SCCF_ALLOW_ZOOM_MIN_1X_PAL
Allow use of sprite min zoom setting at 1x in palette mode.
@ SCCF_ALLOW_ZOOM_MIN_2X_PAL
Allow use of sprite min zoom setting at 2x in palette mode.
Internal functions to cache sprites in memory.
@ SCC_MASK
Mask of valid colour bits.
@ SCC_PAL
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.
void free(const void *ptr)
Version of the standard free that accepts const pointers.
GUISettings gui
settings related to the GUI
ZoomLevel sprite_zoom_min
maximum zoom level at which higher-resolution alternative sprites will be used (if available) instead...
uint8_t control_flags
Control flags, see SpriteCacheCtrlFlags.
bool warned
True iff the user has been warned about incorrect use of this sprite.
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 ReusableBuffer< SpriteLoader::CommonPixel > buffer[ZOOM_LVL_END]
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.
uint8_t 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 > ZOOM_LVL_MIN) When shifting right,...
int UnScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZOOM_LVL_MIN) When shifting right,...
ZoomLevel
All zoom levels we know.
@ ZOOM_LVL_NORMAL
The normal zoom level.
@ ZOOM_LVL_BEGIN
Begin for iteration.
@ ZOOM_LVL_IN_2X
Zoomed 2 times in.
@ ZOOM_LVL_END
End for iteration.
@ ZOOM_LVL_MIN
Minimum zoom level.
@ ZOOM_LVL_IN_4X
Zoomed 4 times in.