22 #include "table/strings.h" 28 uint _sprite_cache_size = 4;
42 static uint _spritecache_items = 0;
46 static inline SpriteCache *GetSpriteCache(uint index)
48 return &_spritecache[index];
51 static inline bool IsMapgenSpriteID(
SpriteID sprite)
58 if (index >= _spritecache_items) {
60 uint items =
Align(index + 1, 1024);
62 DEBUG(sprite, 4,
"Increasing sprite cache to %u items (" PRINTF_SIZE
" bytes)", items, items *
sizeof(*_spritecache));
64 _spritecache =
ReallocT(_spritecache, items);
67 memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) *
sizeof(*_spritecache));
68 _spritecache_items = items;
71 return GetSpriteCache(index);
80 static uint _sprite_lru_counter;
82 static uint _allocated_sprite_cache_size = 0;
83 static int _compact_cache_counter;
86 static void *AllocSprite(
size_t mem_req);
102 int size = (i == 0) ? 0x80 : i;
103 if (size > num)
return false;
119 if (
id >= _spritecache_items)
return false;
122 if (
id == 0)
return true;
123 return !(GetSpriteCache(
id)->file_pos == 0 && GetSpriteCache(
id)->file_slot == 0);
134 return GetSpriteCache(sprite)->
type;
144 if (!SpriteExists(sprite))
return 0;
145 return GetSpriteCache(sprite)->file_slot;
155 if (!SpriteExists(sprite))
return 0;
156 return GetSpriteCache(sprite)->id;
169 for (
SpriteID i = begin; i != end; i++) {
170 if (SpriteExists(i)) {
172 if (sc->file_slot == file_slot) count++;
188 return _spritecache_items;
196 if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX)
return false;
198 sprite[tgt].
width = sprite[src].
width * scaled_1;
203 sprite[tgt].
AllocateData(tgt, sprite[tgt].width * sprite[tgt].height);
206 for (
int y = 0; y < sprite[tgt].
height; y++) {
208 for (
int x = 0; x < sprite[tgt].width; x++) {
209 *dst = src_ln[x / scaled_1];
225 sprite[zoom].
AllocateData(zoom, sprite[zoom].height * sprite[zoom].width);
231 for (uint y = 0; y < sprite[zoom].
height; y++) {
233 assert(src_ln <= src_end);
234 for (uint x = 0; x < sprite[zoom].
width; x++) {
235 assert(src < src_ln);
236 if (src + 1 != src_ln && (src + 1)->a != 0) {
244 src = src_ln + sprite[zoom - 1].
width;
250 uint width = sprite->
width + pad_left + pad_right;
251 uint height = sprite->
height + pad_top + pad_bottom;
253 if (width > UINT16_MAX || height > UINT16_MAX)
return false;
263 for (uint y = 0; y < height; y++) {
264 if (y < pad_top || pad_bottom + y >= height) {
277 src += sprite->
width;
278 data += sprite->
width;
290 sprite->
width = width;
292 sprite->
x_offs -= pad_left;
293 sprite->
y_offs -= pad_top;
301 int min_xoffs = INT32_MAX;
302 int min_yoffs = INT32_MAX;
304 if (
HasBit(sprite_avail, zoom)) {
305 min_xoffs =
min(min_xoffs,
ScaleByZoom(sprite[zoom].x_offs, zoom));
306 min_yoffs =
min(min_yoffs,
ScaleByZoom(sprite[zoom].y_offs, zoom));
311 int max_width = INT32_MIN;
312 int max_height = INT32_MIN;
314 if (
HasBit(sprite_avail, zoom)) {
322 if (
HasBit(sprite_avail, zoom)) {
325 int pad_left =
max(0, sprite[zoom].x_offs -
UnScaleByZoom(min_xoffs, zoom));
327 int pad_right =
max(0,
UnScaleByZoom(max_width, zoom) - sprite[zoom].width - pad_left);
328 int pad_bottom =
max(0,
UnScaleByZoom(max_height, zoom) - sprite[zoom].height - pad_top);
330 if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) {
331 if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom))
return false;
339 static bool ResizeSprites(
SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos)
344 if (!ResizeSpriteIn(sprite, first_avail,
ZOOM_LVL_NORMAL))
return false;
349 if (!PadSprites(sprite, sprite_avail))
return false;
353 if (
HasBit(sprite_avail, zoom)) {
362 if (!
HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
380 static const uint RECOLOUR_SPRITE_SIZE = 257;
381 byte *dest = (byte *)AllocSprite(
max(RECOLOUR_SPRITE_SIZE, num));
384 byte *dest_tmp =
AllocaM(byte,
max(RECOLOUR_SPRITE_SIZE, num));
387 if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE);
391 for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
411 uint8 file_slot = sc->file_slot;
412 size_t file_pos = sc->file_pos;
415 assert(IsMapgenSpriteID(
id) == (sprite_type ==
ST_MAPGEN));
416 assert(sc->
type == sprite_type);
418 DEBUG(sprite, 9,
"Load sprite %d",
id);
421 uint8 sprite_avail = 0;
427 sprite_avail = sprite_loader.
LoadSprite(sprite, file_slot, file_pos, sprite_type,
true);
429 if (sprite_avail == 0) {
430 sprite_avail = sprite_loader.
LoadSprite(sprite, file_slot, file_pos, sprite_type,
false);
433 if (sprite_avail == 0) {
434 if (sprite_type ==
ST_MAPGEN)
return nullptr;
435 if (
id == SPR_IMG_QUERY)
usererror(
"Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
458 byte *dest = s->
data;
467 if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
468 if (
id == SPR_IMG_QUERY)
usererror(
"Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
506 if (container_version >= 2) {
514 uint32 id, prev_id = 0;
535 bool LoadNextSprite(
int load_index, byte file_slot, uint file_sprite_id, byte container_version)
541 if (num == 0)
return false;
545 void *data =
nullptr;
546 if (grf_type == 0xFF) {
555 }
else if (container_version >= 2 && grf_type == 0xFD) {
568 if (container_version >= 2)
return false;
577 bool is_mapgen = IsMapgenSpriteID(load_index);
580 if (type !=
ST_NORMAL)
usererror(
"Uhm, would you be so kind not to load a NewGRF that changes the type of the map generator sprites?");
585 sc->file_slot = file_slot;
586 sc->file_pos = file_pos;
589 sc->id = file_sprite_id;
603 scnew->file_slot = scold->file_slot;
604 scnew->file_pos = scold->file_pos;
605 scnew->ptr =
nullptr;
606 scnew->id = scold->id;
621 assert_compile(
sizeof(
MemBlock) ==
sizeof(
size_t));
623 assert_compile((
sizeof(
size_t) & (
sizeof(
size_t) - 1)) == 0);
630 static size_t GetSpriteCacheUsage()
635 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
643 void IncreaseSpriteLRU()
646 if (_sprite_lru_counter > 16384) {
649 DEBUG(sprite, 3,
"Fixing lru %u, inuse=" PRINTF_SIZE, _sprite_lru_counter, GetSpriteCacheUsage());
651 for (i = 0; i != _spritecache_items; i++) {
653 if (sc->ptr !=
nullptr) {
656 }
else if (sc->lru != -32768) {
661 _sprite_lru_counter = 0;
665 if (++_compact_cache_counter >= 740) {
667 _compact_cache_counter = 0;
679 DEBUG(sprite, 3,
"Compacting sprite cache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
681 for (s = _spritecache_ptr; s->size != 0;) {
691 if (next->size == 0)
break;
694 for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
695 assert(i != _spritecache_items);
698 GetSpriteCache(i)->ptr = s->data;
701 memmove(s, next, next->size);
707 s->size += NextBlock(s)->size & ~S_FREE_MASK;
725 GetSpriteCache(item)->ptr =
nullptr;
728 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
739 uint best = UINT_MAX;
742 DEBUG(sprite, 3,
"DeleteEntryFromSpriteCache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
745 for (
SpriteID i = 0; i != _spritecache_items; i++) {
747 if (sc->
type !=
ST_RECOLOUR && sc->ptr !=
nullptr && sc->lru < cur_lru) {
755 if (best == UINT_MAX)
error(
"Out of sprite memory");
760 static void *AllocSprite(
size_t mem_req)
771 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
777 if (cur_size == mem_req ||
778 cur_size >= mem_req +
sizeof(
MemBlock)) {
783 if (cur_size != mem_req) {
784 NextBlock(s)->size = (cur_size - mem_req) |
S_FREE_MASK;
808 static const char *
const sprite_types[] = {
821 byte warning_level = sc->
warned ? 6 : 0;
823 DEBUG(sprite, warning_level,
"Tried to load %s sprite #%d as a %s sprite. Probable cause: NewGRF interference", sprite_types[available], sprite, sprite_types[requested]);
827 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?");
832 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?");
852 assert(type !=
ST_MAPGEN || IsMapgenSpriteID(sprite));
855 if (!SpriteExists(sprite)) {
856 DEBUG(sprite, 1,
"Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs", sprite);
859 sprite = SPR_IMG_QUERY;
866 if (allocator ==
nullptr) {
870 sc->lru = ++_sprite_lru_counter;
873 if (sc->ptr ==
nullptr) sc->ptr =
ReadSprite(sc, sprite, type, AllocSprite);
878 return ReadSprite(sc, sprite, type, allocator);
883 static void GfxInitSpriteCache()
887 uint target_size = (bpp > 0 ? _sprite_cache_size * bpp / 8 : 1) * 1024 * 1024;
890 static uint last_alloc_attempt = 0;
892 if (_spritecache_ptr ==
nullptr || (_allocated_sprite_cache_size != target_size && target_size != last_alloc_attempt)) {
893 delete[]
reinterpret_cast<byte *
>(_spritecache_ptr);
895 last_alloc_attempt = target_size;
896 _allocated_sprite_cache_size = target_size;
901 _spritecache_ptr =
reinterpret_cast<MemBlock *
>(
new byte[_allocated_sprite_cache_size + _allocated_sprite_cache_size / 2]);
902 }
catch (std::bad_alloc &) {
903 _spritecache_ptr =
nullptr;
906 if (_spritecache_ptr !=
nullptr) {
908 delete[]
reinterpret_cast<byte *
>(_spritecache_ptr);
909 _spritecache_ptr =
reinterpret_cast<MemBlock *
>(
new byte[_allocated_sprite_cache_size]);
910 }
else if (_allocated_sprite_cache_size < 2 * 1024 * 1024) {
911 usererror(
"Cannot allocate spritecache");
914 _allocated_sprite_cache_size >>= 1;
916 }
while (_spritecache_ptr ==
nullptr);
918 if (_allocated_sprite_cache_size != target_size) {
919 DEBUG(misc, 0,
"Not enough memory to allocate %d MiB of spritecache. Spritecache was reduced to %d MiB.", target_size / 1024 / 1024, _allocated_sprite_cache_size / 1024 / 1024);
921 ErrorMessageData msg(STR_CONFIG_ERROR_OUT_OF_MEMORY, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG);
923 msg.
SetDParam(1, _allocated_sprite_cache_size);
931 NextBlock(_spritecache_ptr)->size = 0;
934 void GfxInitSpriteMem()
936 GfxInitSpriteCache();
940 _spritecache_items = 0;
941 _spritecache =
nullptr;
943 _compact_cache_counter = 0;
953 for (uint i = 0; i != _spritecache_items; i++) {
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, the block is free.
Pseudosprite or other unusable sprite, used only internally.
uint GetOriginFileSlot(SpriteID sprite)
Get the (FIOS) file slot of a given sprite.
static ReusableBuffer< SpriteLoader::CommonPixel > buffer[ZOOM_LVL_COUNT]
Allocated memory to pass sprite data around.
static void * HandleInvalidSpriteRequest(SpriteID sprite, SpriteType requested, SpriteCache *sc, AllocatorProc *allocator)
Handles the case when a sprite of different type is requested than is present in the SpriteCache...
static T SetBit(T &x, const uint8 y)
Set a bit in a variable.
static int UnScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift right (when zoom > ZOOM_LVL_NORMAL) When shifting right...
Data structure describing a sprite.
uint16 FioReadWord()
Read a word (16 bits) from the file (in low endian format).
uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
Load a sprite from the disk and return a sprite struct which is the same for all loaders.
void SetDParam(uint n, uint64 v)
Set a error string parameter.
static void DeleteEntryFromSpriteCache(uint item)
Delete a single entry from the sprite cache.
static int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZOOM_LVL_NORMAL) When shifting right...
void AllocateData(ZoomLevel zoom, size_t size)
Allocate the sprite data of this sprite.
Functions for Standard In/Out file operations.
Maximum number of sprites that can be loaded at a given time.
const byte _palmap_w2d[]
Converting from the Windows palette to the DOS palette.
virtual Sprite * Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator)=0
Convert a sprite from the loader to our own format.
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
static void * ReadRecolourSprite(uint16 file_slot, uint num)
Load a recolour sprite into memory.
static T max(const T a, const T b)
Returns the maximum of two values.
byte FioReadByte()
Read a byte from the file.
bool warned
True iff the user has been warned about incorrect use of this sprite.
SpriteType GetSpriteType(SpriteID sprite)
Get the sprite type of a given sprite.
size_t GetGRFSpriteOffset(uint32 id)
Get the file offset for a specific sprite in the sprite section of a GRF.
Definition of a common pixel in OpenTTD's realm.
SpriteType type
The sprite type.
Base for reading sprites from (New)GRFs.
static bool IsInsideMM(const T x, const size_t min, const size_t max)
Checks if a value is in an interval.
void ReadGRFSpriteOffsets(byte container_version)
Parse the sprite section of GRFs.
static void * ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_type, AllocatorProc *allocator)
Read a sprite from disk.
Special sprite for the map generator.
int16 y_offs
Number of pixels to shift the sprite downwards.
Functions related to errors.
static T Align(const T x, uint n)
Return the smallest multiple of n equal or greater than x.
#define FIND_FIRST_BIT(x)
Returns the first non-zero bit in a 6-bit value (from right).
Functions related to the gfx engine.
Types related to global configuration settings.
Definition of base types and functions in a cross-platform compatible way.
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
A number of safeguards to prevent using unsafe methods.
void FioSeekTo(size_t pos, int mode)
Seek in the current file.
int16 x_offs
The x-offset of where the sprite will be drawn.
static T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type...
void ScheduleErrorMessage(const ErrorMessageData &data)
Schedule an error.
SpriteLoader::CommonPixel * data
The sprite itself.
Structure for passing information from the sprite loader to the blitter.
void FioReadBlock(void *ptr, size_t size)
Read a block.
bool _palette_remap_grf[]
Whether the given NewGRFs must get a palette remap from windows to DOS or not.
void GfxClearSpriteCache()
Remove all encoded sprites from the sprite cache without discarding sprite location information...
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
static T min(const T a, const T b)
Returns the minimum of two values.
bool SkipSpriteData(byte type, uint16 num)
Skip the given amount of sprite graphics data.
SpriteType
Types of sprites that might be loaded.
uint16 height
Height of the sprite.
The most basic (normal) sprite.
static void MemCpyT(T *destination, const T *source, size_t num=1)
Type-safe version of memcpy().
#define DEBUG(name, level,...)
Output a line of debugging information.
byte container_ver
Container version of the GRF the sprite is from.
uint GetMaxSpriteID()
Get a reasonable (upper bound) estimate of the maximum SpriteID used in OpenTTD; there will be no spr...
static std::map< uint32, size_t > _grf_sprite_offsets
Map from sprite numbers to position in the GRF file.
uint32 FioReadDword()
Read a double word (32 bits) from the file (in low endian format).
uint16 width
Width of the sprite.
uint16 width
Width of the sprite.
static void CompactSpriteCache()
Called when holes in the sprite cache should be removed.
The data of the error message.
uint32 SpriteID
The number of a sprite, without mapping bits and colourtables.
void * GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator)
Reads a sprite (from disk or sprite cache).
Sprite loader for graphics coming from a (New)GRF.
void FioSkipBytes(int n)
Skip n bytes ahead in the file.
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
Functions related to zooming.
uint16 height
Height of the sprite.
bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version)
Load a real or recolour sprite.
int16 x_offs
Number of pixels to shift the sprite to the right.
uint GetSpriteCountForSlot(uint file_slot, SpriteID begin, SpriteID end)
Count the sprites which originate from a specific file slot in a range of SpriteIDs.
static const byte _palmap_d2w[]
Converting from the DOS palette to the Windows palette.
uint32 GetSpriteLocalID(SpriteID sprite)
Get the GRF-local sprite id of a given sprite.
int16 y_offs
The y-offset of where the sprite will be drawn.
virtual uint8 GetScreenDepth()=0
Get the screen depth this blitter works for.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
SpriteType type
In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour spr...
ZoomLevel
All zoom levels we know.
size_t FioGetPos()
Get position in the current file.
Functions related to memory operations.
This file contains all sprite-related enums and defines.
Factory to 'query' all available blitters.
Translation tables from one GRF to another GRF.
static void MemSetT(T *ptr, byte value, size_t num=1)
Type-safe version of memset().