4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file spritecache.cpp Caching of sprites. */
13 #include "fileio_func.h"
14 #include "spriteloader/grf.hpp"
17 #include "zoom_func.h"
18 #include "settings_type.h"
19 #include "blitter/factory.hpp"
20 #include "core/math_func.hpp"
21 #include "core/mem_func.hpp"
23 #include "table/sprites.h"
24 #include "table/strings.h"
25 #include "table/palette_convert.h"
27 #include "3rdparty/cpp-btree/btree_map.h"
29 #include "safeguards.h"
31 /* Default of 4MB spritecache */
32 uint _sprite_cache_size
= 4;
34 typedef SimpleTinyEnumT
<SpriteType
, byte
> SpriteTypeByte
;
42 SpriteTypeByte type
; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
43 bool warned
; ///< True iff the user has been warned about incorrect use of this sprite
44 byte container_ver
; ///< Container version of the GRF the sprite is from.
48 static uint _spritecache_items
= 0;
49 static SpriteCache
*_spritecache
= nullptr;
52 static inline SpriteCache
*GetSpriteCache(uint index
)
54 return &_spritecache
[index
];
57 static inline bool IsMapgenSpriteID(SpriteID sprite
)
59 return IsInsideMM(sprite
, 4845, 4882);
62 static SpriteCache
*AllocateSpriteCache(uint index
)
64 if (index
>= _spritecache_items
) {
65 /* Add another 1024 items to the 'pool' */
66 uint items
= Align(index
+ 1, 1024);
68 DEBUG(sprite
, 4, "Increasing sprite cache to %u items (" PRINTF_SIZE
" bytes)", items
, items
* sizeof(*_spritecache
));
70 _spritecache
= ReallocT(_spritecache
, items
);
72 /* Reset the new items and update the count */
73 memset(_spritecache
+ _spritecache_items
, 0, (items
- _spritecache_items
) * sizeof(*_spritecache
));
74 _spritecache_items
= items
;
77 return GetSpriteCache(index
);
86 static uint _sprite_lru_counter
;
87 static MemBlock
*_spritecache_ptr
;
88 static uint _allocated_sprite_cache_size
= 0;
89 static int _compact_cache_counter
;
91 static void CompactSpriteCache();
92 static void *AllocSprite(size_t mem_req
);
95 * Skip the given amount of sprite graphics data.
96 * @param type the type of sprite (compressed etc)
97 * @param num the amount of sprites to skip
98 * @return true if the data could be correctly skipped.
100 bool SkipSpriteData(byte type
, uint16 num
)
106 int8 i
= FioReadByte();
108 int size
= (i
== 0) ? 0x80 : i
;
109 if (size
> num
) return false;
122 /* Check if the given Sprite ID exists */
123 bool SpriteExists(SpriteID id
)
125 if (id
>= _spritecache_items
) return false;
127 /* Special case for Sprite ID zero -- its position is also 0... */
128 if (id
== 0) return true;
129 return !(GetSpriteCache(id
)->file_pos
== 0 && GetSpriteCache(id
)->file_slot
== 0);
133 * Get the sprite type of a given sprite.
134 * @param sprite The sprite to look at.
135 * @return the type of sprite.
137 SpriteType
GetSpriteType(SpriteID sprite
)
139 if (!SpriteExists(sprite
)) return ST_INVALID
;
140 return GetSpriteCache(sprite
)->type
;
144 * Get the (FIOS) file slot of a given sprite.
145 * @param sprite The sprite to look at.
146 * @return the FIOS file slot
148 uint
GetOriginFileSlot(SpriteID sprite
)
150 if (!SpriteExists(sprite
)) return 0;
151 return GetSpriteCache(sprite
)->file_slot
;
155 * Count the sprites which originate from a specific file slot in a range of SpriteIDs.
156 * @param file_slot FIOS file slot.
157 * @param begin First sprite in range.
158 * @param end First sprite not in range.
159 * @return Number of sprites.
161 uint
GetSpriteCountForSlot(uint file_slot
, SpriteID begin
, SpriteID end
)
164 for (SpriteID i
= begin
; i
!= end
; i
++) {
165 if (SpriteExists(i
)) {
166 SpriteCache
*sc
= GetSpriteCache(i
);
167 if (sc
->file_slot
== file_slot
) count
++;
174 * Get a reasonable (upper bound) estimate of the maximum
175 * SpriteID used in OpenTTD; there will be no sprites with
176 * a higher SpriteID, although there might be up to roughly
177 * a thousand unused SpriteIDs below this number.
178 * @note It's actually the number of spritecache items.
179 * @return maximum SpriteID
181 uint
GetMaxSpriteID()
183 return _spritecache_items
;
186 static bool ResizeSpriteIn(SpriteLoader::Sprite
*sprite
, ZoomLevel src
, ZoomLevel tgt
)
188 uint8 scaled_1
= ScaleByZoom(1, (ZoomLevel
)(src
- tgt
));
190 /* Check for possible memory overflow. */
191 if (sprite
[src
].width
* scaled_1
> UINT16_MAX
|| sprite
[src
].height
* scaled_1
> UINT16_MAX
) return false;
193 sprite
[tgt
].width
= sprite
[src
].width
* scaled_1
;
194 sprite
[tgt
].height
= sprite
[src
].height
* scaled_1
;
195 sprite
[tgt
].x_offs
= sprite
[src
].x_offs
* scaled_1
;
196 sprite
[tgt
].y_offs
= sprite
[src
].y_offs
* scaled_1
;
198 sprite
[tgt
].AllocateData(tgt
, sprite
[tgt
].width
* sprite
[tgt
].height
);
200 SpriteLoader::CommonPixel
*dst
= sprite
[tgt
].data
;
201 for (int y
= 0; y
< sprite
[tgt
].height
; y
++) {
202 const SpriteLoader::CommonPixel
*src_ln
= &sprite
[src
].data
[y
/ scaled_1
* sprite
[src
].width
];
203 for (int x
= 0; x
< sprite
[tgt
].width
; x
++) {
204 *dst
= src_ln
[x
/ scaled_1
];
212 static void ResizeSpriteOut(SpriteLoader::Sprite
*sprite
, ZoomLevel zoom
)
214 /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
215 sprite
[zoom
].width
= UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].width
, zoom
);
216 sprite
[zoom
].height
= UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].height
, zoom
);
217 sprite
[zoom
].x_offs
= UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].x_offs
, zoom
);
218 sprite
[zoom
].y_offs
= UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].y_offs
, zoom
);
220 sprite
[zoom
].AllocateData(zoom
, sprite
[zoom
].height
* sprite
[zoom
].width
);
222 SpriteLoader::CommonPixel
*dst
= sprite
[zoom
].data
;
223 const SpriteLoader::CommonPixel
*src
= sprite
[zoom
- 1].data
;
224 const SpriteLoader::CommonPixel
*src_end
= src
+ sprite
[zoom
- 1].height
* sprite
[zoom
- 1].width
;
226 for (uint y
= 0; y
< sprite
[zoom
].height
; y
++) {
227 const SpriteLoader::CommonPixel
*src_ln
= src
+ sprite
[zoom
- 1].width
;
228 assert(src_ln
<= src_end
);
229 for (uint x
= 0; x
< sprite
[zoom
].width
; x
++) {
230 assert(src
< src_ln
);
231 if (src
+ 1 != src_ln
&& (src
+ 1)->a
!= 0) {
239 src
= src_ln
+ sprite
[zoom
- 1].width
;
243 static bool PadSingleSprite(SpriteLoader::Sprite
*sprite
, ZoomLevel zoom
, uint pad_left
, uint pad_top
, uint pad_right
, uint pad_bottom
)
245 uint width
= sprite
->width
+ pad_left
+ pad_right
;
246 uint height
= sprite
->height
+ pad_top
+ pad_bottom
;
248 if (width
> UINT16_MAX
|| height
> UINT16_MAX
) return false;
250 /* Copy source data and reallocate sprite memory. */
251 SpriteLoader::CommonPixel
*src_data
= MallocT
<SpriteLoader::CommonPixel
>(sprite
->width
* sprite
->height
);
252 MemCpyT(src_data
, sprite
->data
, sprite
->width
* sprite
->height
);
253 sprite
->AllocateData(zoom
, width
* height
);
255 /* Copy with padding to destination. */
256 SpriteLoader::CommonPixel
*src
= src_data
;
257 SpriteLoader::CommonPixel
*data
= sprite
->data
;
258 for (uint y
= 0; y
< height
; y
++) {
259 if (y
< pad_top
|| pad_bottom
+ y
>= height
) {
260 /* Top/bottom padding. */
261 MemSetT(data
, 0, width
);
266 MemSetT(data
, 0, pad_left
);
271 MemCpyT(data
, src
, sprite
->width
);
272 src
+= sprite
->width
;
273 data
+= sprite
->width
;
277 MemSetT(data
, 0, pad_right
);
284 /* Update sprite size. */
285 sprite
->width
= width
;
286 sprite
->height
= height
;
287 sprite
->x_offs
-= pad_left
;
288 sprite
->y_offs
-= pad_top
;
293 static bool PadSprites(SpriteLoader::Sprite
*sprite
, unsigned int sprite_avail
)
295 /* Get minimum top left corner coordinates. */
296 int min_xoffs
= INT32_MAX
;
297 int min_yoffs
= INT32_MAX
;
298 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
299 if (HasBit(sprite_avail
, zoom
)) {
300 min_xoffs
= min(min_xoffs
, ScaleByZoom(sprite
[zoom
].x_offs
, zoom
));
301 min_yoffs
= min(min_yoffs
, ScaleByZoom(sprite
[zoom
].y_offs
, zoom
));
305 /* Get maximum dimensions taking necessary padding at the top left into account. */
306 int max_width
= INT32_MIN
;
307 int max_height
= INT32_MIN
;
308 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
309 if (HasBit(sprite_avail
, zoom
)) {
310 max_width
= max(max_width
, ScaleByZoom(sprite
[zoom
].width
+ sprite
[zoom
].x_offs
- UnScaleByZoom(min_xoffs
, zoom
), zoom
));
311 max_height
= max(max_height
, ScaleByZoom(sprite
[zoom
].height
+ sprite
[zoom
].y_offs
- UnScaleByZoom(min_yoffs
, zoom
), zoom
));
315 /* Pad sprites where needed. */
316 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
317 if (HasBit(sprite_avail
, zoom
)) {
318 /* Scaling the sprite dimensions in the blitter is done with rounding up,
319 * so a negative padding here is not an error. */
320 int pad_left
= max(0, sprite
[zoom
].x_offs
- UnScaleByZoom(min_xoffs
, zoom
));
321 int pad_top
= max(0, sprite
[zoom
].y_offs
- UnScaleByZoom(min_yoffs
, zoom
));
322 int pad_right
= max(0, UnScaleByZoom(max_width
, zoom
) - sprite
[zoom
].width
- pad_left
);
323 int pad_bottom
= max(0, UnScaleByZoom(max_height
, zoom
) - sprite
[zoom
].height
- pad_top
);
325 if (pad_left
> 0 || pad_right
> 0 || pad_top
> 0 || pad_bottom
> 0) {
326 if (!PadSingleSprite(&sprite
[zoom
], zoom
, pad_left
, pad_top
, pad_right
, pad_bottom
)) return false;
334 static bool ResizeSprites(SpriteLoader::Sprite
*sprite
, unsigned int sprite_avail
, uint32 file_slot
, uint32 file_pos
)
336 /* Create a fully zoomed image if it does not exist */
337 ZoomLevel first_avail
= static_cast<ZoomLevel
>(FIND_FIRST_BIT(sprite_avail
));
338 if (first_avail
!= ZOOM_LVL_NORMAL
) {
339 if (!ResizeSpriteIn(sprite
, first_avail
, ZOOM_LVL_NORMAL
)) return false;
340 SetBit(sprite_avail
, ZOOM_LVL_NORMAL
);
343 /* Pad sprites to make sizes match. */
344 if (!PadSprites(sprite
, sprite_avail
)) return false;
346 /* Create other missing zoom levels */
347 for (ZoomLevel zoom
= ZOOM_LVL_OUT_2X
; zoom
!= ZOOM_LVL_END
; zoom
++) {
348 if (HasBit(sprite_avail
, zoom
)) {
349 /* Check that size and offsets match the fully zoomed image. */
350 assert(sprite
[zoom
].width
== UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].width
, zoom
));
351 assert(sprite
[zoom
].height
== UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].height
, zoom
));
352 assert(sprite
[zoom
].x_offs
== UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].x_offs
, zoom
));
353 assert(sprite
[zoom
].y_offs
== UnScaleByZoom(sprite
[ZOOM_LVL_NORMAL
].y_offs
, zoom
));
356 /* Zoom level is not available, or unusable, so create it */
357 if (!HasBit(sprite_avail
, zoom
)) ResizeSpriteOut(sprite
, zoom
);
364 * Load a recolour sprite into memory.
365 * @param file_slot GRF we're reading from.
366 * @param num Size of the sprite in the GRF.
367 * @return Sprite data.
369 static void *ReadRecolourSprite(uint16 file_slot
, uint num
)
371 /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
372 * number of recolour sprites that are 17 bytes that only exist in DOS
373 * GRFs which are the same as 257 byte recolour sprites, but with the last
374 * 240 bytes zeroed. */
375 static const uint RECOLOUR_SPRITE_SIZE
= 257;
376 byte
*dest
= (byte
*)AllocSprite(max(RECOLOUR_SPRITE_SIZE
, num
));
378 if (_palette_remap_grf
[file_slot
]) {
379 byte
*dest_tmp
= AllocaM(byte
, max(RECOLOUR_SPRITE_SIZE
, num
));
381 /* Only a few recolour sprites are less than 257 bytes */
382 if (num
< RECOLOUR_SPRITE_SIZE
) memset(dest_tmp
, 0, RECOLOUR_SPRITE_SIZE
);
383 FioReadBlock(dest_tmp
, num
);
385 /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
386 for (uint i
= 1; i
< RECOLOUR_SPRITE_SIZE
; i
++) {
387 dest
[i
] = _palmap_w2d
[dest_tmp
[_palmap_d2w
[i
- 1] + 1]];
390 FioReadBlock(dest
, num
);
397 * Read a sprite from disk.
398 * @param sc Location of sprite.
399 * @param id Sprite number.
400 * @param sprite_type Type of sprite.
401 * @param allocator Allocator function to use.
402 * @return Read sprite data.
404 static void *ReadSprite(const SpriteCache
*sc
, SpriteID id
, SpriteType sprite_type
, AllocatorProc
*allocator
)
406 uint8 file_slot
= sc
->file_slot
;
407 size_t file_pos
= sc
->file_pos
;
409 assert(sprite_type
!= ST_RECOLOUR
);
410 assert(IsMapgenSpriteID(id
) == (sprite_type
== ST_MAPGEN
));
411 assert(sc
->type
== sprite_type
);
413 DEBUG(sprite
, 9, "Load sprite %d", id
);
415 SpriteLoader::Sprite sprite
[ZOOM_LVL_COUNT
];
416 uint8 sprite_avail
= 0;
417 sprite
[ZOOM_LVL_NORMAL
].type
= sprite_type
;
419 SpriteLoaderGrf
sprite_loader(sc
->container_ver
);
420 if (sprite_type
!= ST_MAPGEN
&& BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) {
421 /* Try for 32bpp sprites first. */
422 sprite_avail
= sprite_loader
.LoadSprite(sprite
, file_slot
, file_pos
, sprite_type
, true);
424 if (sprite_avail
== 0) {
425 sprite_avail
= sprite_loader
.LoadSprite(sprite
, file_slot
, file_pos
, sprite_type
, false);
428 if (sprite_avail
== 0) {
429 if (sprite_type
== ST_MAPGEN
) return nullptr;
430 if (id
== SPR_IMG_QUERY
) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
431 return (void*)GetRawSprite(SPR_IMG_QUERY
, ST_NORMAL
, allocator
);
434 if (sprite_type
== ST_MAPGEN
) {
435 /* Ugly hack to work around the problem that the old landscape
436 * generator assumes that those sprites are stored uncompressed in
437 * the memory, and they are only read directly by the code, never
438 * send to the blitter. So do not send it to the blitter (which will
439 * result in a data array in the format the blitter likes most), but
440 * extract the data directly and store that as sprite.
441 * Ugly: yes. Other solution: no. Blame the original author or
442 * something ;) The image should really have been a data-stream
443 * (so type = 0xFF basically). */
444 uint num
= sprite
[ZOOM_LVL_NORMAL
].width
* sprite
[ZOOM_LVL_NORMAL
].height
;
446 Sprite
*s
= (Sprite
*)allocator(sizeof(*s
) + num
);
447 s
->width
= sprite
[ZOOM_LVL_NORMAL
].width
;
448 s
->height
= sprite
[ZOOM_LVL_NORMAL
].height
;
449 s
->x_offs
= sprite
[ZOOM_LVL_NORMAL
].x_offs
;
450 s
->y_offs
= sprite
[ZOOM_LVL_NORMAL
].y_offs
;
452 SpriteLoader::CommonPixel
*src
= sprite
[ZOOM_LVL_NORMAL
].data
;
453 byte
*dest
= s
->data
;
462 if (!ResizeSprites(sprite
, sprite_avail
, file_slot
, sc
->id
)) {
463 if (id
== SPR_IMG_QUERY
) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
464 return (void*)GetRawSprite(SPR_IMG_QUERY
, ST_NORMAL
, allocator
);
467 if (sprite
->type
== ST_FONT
&& ZOOM_LVL_GUI
!= ZOOM_LVL_NORMAL
) {
468 /* Make ZOOM_LVL_GUI be ZOOM_LVL_NORMAL */
469 sprite
[ZOOM_LVL_NORMAL
].width
= sprite
[ZOOM_LVL_GUI
].width
;
470 sprite
[ZOOM_LVL_NORMAL
].height
= sprite
[ZOOM_LVL_GUI
].height
;
471 sprite
[ZOOM_LVL_NORMAL
].x_offs
= sprite
[ZOOM_LVL_GUI
].x_offs
;
472 sprite
[ZOOM_LVL_NORMAL
].y_offs
= sprite
[ZOOM_LVL_GUI
].y_offs
;
473 sprite
[ZOOM_LVL_NORMAL
].data
= sprite
[ZOOM_LVL_GUI
].data
;
476 return BlitterFactory::GetCurrentBlitter()->Encode(sprite
, allocator
);
480 /** Map from sprite numbers to position in the GRF file. */
481 static btree::btree_map
<uint32
, size_t> _grf_sprite_offsets
;
484 * Get the file offset for a specific sprite in the sprite section of a GRF.
485 * @param id ID of the sprite to look up.
486 * @return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present.
488 size_t GetGRFSpriteOffset(uint32 id
)
490 auto iter
= _grf_sprite_offsets
.find(id
);
491 return iter
!= _grf_sprite_offsets
.end() ? iter
->second
: SIZE_MAX
;
495 * Parse the sprite section of GRFs.
496 * @param container_version Container version of the GRF we're currently processing.
498 void ReadGRFSpriteOffsets(byte container_version
)
500 _grf_sprite_offsets
.clear();
502 if (container_version
>= 2) {
503 /* Seek to sprite section of the GRF. */
504 size_t data_offset
= FioReadDword();
505 size_t old_pos
= FioGetPos();
506 FioSeekTo(data_offset
, SEEK_CUR
);
508 /* Loop over all sprite section entries and store the file
509 * offset for each newly encountered ID. */
510 uint32 id
, prev_id
= 0;
511 while ((id
= FioReadDword()) != 0) {
512 if (id
!= prev_id
) _grf_sprite_offsets
[id
] = FioGetPos() - 4;
514 FioSkipBytes(FioReadDword());
517 /* Continue processing the data section. */
518 FioSeekTo(old_pos
, SEEK_SET
);
524 * Load a real or recolour sprite.
525 * @param load_index Global sprite index.
526 * @param file_slot GRF to load from.
527 * @param file_sprite_id Sprite number in the GRF.
528 * @param container_version Container version of the GRF.
529 * @return True if a valid sprite was loaded, false on any error.
531 bool LoadNextSprite(int load_index
, byte file_slot
, uint file_sprite_id
, byte container_version
)
533 size_t file_pos
= FioGetPos();
535 /* Read sprite header. */
536 uint32 num
= container_version
>= 2 ? FioReadDword() : FioReadWord();
537 if (num
== 0) return false;
538 byte grf_type
= FioReadByte();
541 void *data
= nullptr;
542 if (grf_type
== 0xFF) {
543 /* Some NewGRF files have "empty" pseudo-sprites which are 1
544 * byte long. Catch these so the sprites won't be displayed. */
550 data
= ReadRecolourSprite(file_slot
, num
);
551 } else if (container_version
>= 2 && grf_type
== 0xFD) {
553 /* Invalid sprite section include, ignore. */
557 /* It is not an error if no sprite with the provided ID is found in the sprite section. */
558 file_pos
= GetGRFSpriteOffset(FioReadDword());
562 type
= SkipSpriteData(grf_type
, num
- 8) ? ST_NORMAL
: ST_INVALID
;
563 /* Inline sprites are not supported for container version >= 2. */
564 if (container_version
>= 2) return false;
567 if (type
== ST_INVALID
) return false;
569 if (load_index
>= MAX_SPRITES
) {
570 usererror("Tried to load too many sprites (#%d; max %d)", load_index
, MAX_SPRITES
);
573 bool is_mapgen
= IsMapgenSpriteID(load_index
);
576 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?");
580 SpriteCache
*sc
= AllocateSpriteCache(load_index
);
581 sc
->file_slot
= file_slot
;
582 sc
->file_pos
= file_pos
;
585 sc
->id
= file_sprite_id
;
588 sc
->container_ver
= container_version
;
594 void DupSprite(SpriteID old_spr
, SpriteID new_spr
)
596 SpriteCache
*scnew
= AllocateSpriteCache(new_spr
); // may reallocate: so put it first
597 SpriteCache
*scold
= GetSpriteCache(old_spr
);
599 scnew
->file_slot
= scold
->file_slot
;
600 scnew
->file_pos
= scold
->file_pos
;
601 scnew
->ptr
= nullptr;
602 scnew
->id
= scold
->id
;
603 scnew
->type
= scold
->type
;
604 scnew
->warned
= false;
605 scnew
->container_ver
= scold
->container_ver
;
609 * S_FREE_MASK is used to mask-out lower bits of MemBlock::size
610 * If they are non-zero, the block is free.
611 * S_FREE_MASK has to ensure MemBlock is correctly aligned -
612 * it means 8B (S_FREE_MASK == 7) on 64bit systems!
614 static const size_t S_FREE_MASK
= sizeof(size_t) - 1;
616 /* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
617 assert_compile(sizeof(MemBlock
) == sizeof(size_t));
618 /* make sure it's a power of two */
619 assert_compile((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
621 static inline MemBlock
*NextBlock(MemBlock
*block
)
623 return (MemBlock
*)((byte
*)block
+ (block
->size
& ~S_FREE_MASK
));
626 static size_t GetSpriteCacheUsage()
631 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
632 if (!(s
->size
& S_FREE_MASK
)) tot_size
+= s
->size
;
639 void IncreaseSpriteLRU()
641 /* Increase all LRU values */
642 if (_sprite_lru_counter
> 16384) {
645 DEBUG(sprite
, 3, "Fixing lru %u, inuse=" PRINTF_SIZE
, _sprite_lru_counter
, GetSpriteCacheUsage());
647 for (i
= 0; i
!= _spritecache_items
; i
++) {
648 SpriteCache
*sc
= GetSpriteCache(i
);
649 if (sc
->ptr
!= nullptr) {
652 } else if (sc
->lru
!= -32768) {
657 _sprite_lru_counter
= 0;
660 /* Compact sprite cache every now and then. */
661 if (++_compact_cache_counter
>= 740) {
662 CompactSpriteCache();
663 _compact_cache_counter
= 0;
668 * Called when holes in the sprite cache should be removed.
669 * That is accomplished by moving the cached data.
671 static void CompactSpriteCache()
675 DEBUG(sprite
, 3, "Compacting sprite cache, inuse=" PRINTF_SIZE
, GetSpriteCacheUsage());
677 for (s
= _spritecache_ptr
; s
->size
!= 0;) {
678 if (s
->size
& S_FREE_MASK
) {
679 MemBlock
*next
= NextBlock(s
);
683 /* Since free blocks are automatically coalesced, this should hold true. */
684 assert(!(next
->size
& S_FREE_MASK
));
686 /* If the next block is the sentinel block, we can safely return */
687 if (next
->size
== 0) break;
689 /* Locate the sprite belonging to the next pointer. */
690 for (i
= 0; GetSpriteCache(i
)->ptr
!= next
->data
; i
++) {
691 assert(i
!= _spritecache_items
);
694 GetSpriteCache(i
)->ptr
= s
->data
; // Adjust sprite array entry
695 /* Swap this and the next block */
697 memmove(s
, next
, next
->size
);
701 /* Coalesce free blocks */
702 while (NextBlock(s
)->size
& S_FREE_MASK
) {
703 s
->size
+= NextBlock(s
)->size
& ~S_FREE_MASK
;
712 * Delete a single entry from the sprite cache.
713 * @param item Entry to delete.
715 static void DeleteEntryFromSpriteCache(uint item
)
717 /* Mark the block as free (the block must be in use) */
718 MemBlock
*s
= (MemBlock
*)GetSpriteCache(item
)->ptr
- 1;
719 assert(!(s
->size
& S_FREE_MASK
));
720 s
->size
|= S_FREE_MASK
;
721 GetSpriteCache(item
)->ptr
= nullptr;
723 /* And coalesce adjacent free blocks */
724 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
725 if (s
->size
& S_FREE_MASK
) {
726 while (NextBlock(s
)->size
& S_FREE_MASK
) {
727 s
->size
+= NextBlock(s
)->size
& ~S_FREE_MASK
;
733 static void DeleteEntryFromSpriteCache()
735 uint best
= UINT_MAX
;
738 DEBUG(sprite
, 3, "DeleteEntryFromSpriteCache, inuse=" PRINTF_SIZE
, GetSpriteCacheUsage());
741 for (SpriteID i
= 0; i
!= _spritecache_items
; i
++) {
742 SpriteCache
*sc
= GetSpriteCache(i
);
743 if (sc
->type
!= ST_RECOLOUR
&& sc
->ptr
!= nullptr && sc
->lru
< cur_lru
) {
749 /* Display an error message and die, in case we found no sprite at all.
750 * This shouldn't really happen, unless all sprites are locked. */
751 if (best
== UINT_MAX
) error("Out of sprite memory");
753 DeleteEntryFromSpriteCache(best
);
756 static void *AllocSprite(size_t mem_req
)
758 mem_req
+= sizeof(MemBlock
);
760 /* Align this to correct boundary. This also makes sure at least one
761 * bit is not used, so we can use it for other things. */
762 mem_req
= Align(mem_req
, S_FREE_MASK
+ 1);
767 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
768 if (s
->size
& S_FREE_MASK
) {
769 size_t cur_size
= s
->size
& ~S_FREE_MASK
;
771 /* Is the block exactly the size we need or
772 * big enough for an additional free block? */
773 if (cur_size
== mem_req
||
774 cur_size
>= mem_req
+ sizeof(MemBlock
)) {
775 /* Set size and in use */
778 /* Do we need to inject a free block too? */
779 if (cur_size
!= mem_req
) {
780 NextBlock(s
)->size
= (cur_size
- mem_req
) | S_FREE_MASK
;
788 /* Reached sentinel, but no block found yet. Delete some old entry. */
789 DeleteEntryFromSpriteCache();
794 * Handles the case when a sprite of different type is requested than is present in the SpriteCache.
795 * For ST_FONT sprites, it is normal. In other cases, default sprite is loaded instead.
796 * @param sprite ID of loaded sprite
797 * @param requested requested sprite type
798 * @param sc the currently known sprite cache for the requested sprite
799 * @return fallback sprite
800 * @note this function will do usererror() in the case the fallback sprite isn't available
802 static void *HandleInvalidSpriteRequest(SpriteID sprite
, SpriteType requested
, SpriteCache
*sc
, AllocatorProc
*allocator
)
804 static const char * const sprite_types
[] = {
805 "normal", // ST_NORMAL
806 "map generator", // ST_MAPGEN
807 "character", // ST_FONT
808 "recolour", // ST_RECOLOUR
811 SpriteType available
= sc
->type
;
812 if (requested
== ST_FONT
&& available
== ST_NORMAL
) {
813 if (sc
->ptr
== nullptr) sc
->type
= ST_FONT
;
814 return GetRawSprite(sprite
, sc
->type
, allocator
);
817 byte warning_level
= sc
->warned
? 6 : 0;
819 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
]);
823 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?");
826 return GetRawSprite(SPR_IMG_QUERY
, ST_NORMAL
, allocator
);
828 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?");
829 return GetRawSprite(PALETTE_TO_DARK_BLUE
, ST_RECOLOUR
, allocator
);
831 /* this shouldn't happen, overriding of ST_MAPGEN sprites is checked in LoadNextSprite()
832 * (the only case the check fails is when these sprites weren't even loaded...) */
839 * Reads a sprite (from disk or sprite cache).
840 * If the sprite is not available or of wrong type, a fallback sprite is returned.
841 * @param sprite Sprite to read.
842 * @param type Expected sprite type.
843 * @param allocator Allocator function to use. Set to nullptr to use the usual sprite cache.
844 * @return Sprite raw data
846 void *GetRawSprite(SpriteID sprite
, SpriteType type
, AllocatorProc
*allocator
)
848 assert(type
!= ST_MAPGEN
|| IsMapgenSpriteID(sprite
));
849 assert(type
< ST_INVALID
);
851 if (!SpriteExists(sprite
)) {
852 DEBUG(sprite
, 1, "Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs", sprite
);
854 /* SPR_IMG_QUERY is a BIG FAT RED ? */
855 sprite
= SPR_IMG_QUERY
;
858 SpriteCache
*sc
= GetSpriteCache(sprite
);
860 if (sc
->type
!= type
) return HandleInvalidSpriteRequest(sprite
, type
, sc
, allocator
);
862 if (allocator
== nullptr) {
863 /* Load sprite into/from spritecache */
866 sc
->lru
= ++_sprite_lru_counter
;
868 /* Load the sprite, if it is not loaded, yet */
869 if (sc
->ptr
== nullptr) sc
->ptr
= ReadSprite(sc
, sprite
, type
, AllocSprite
);
873 /* Do not use the spritecache, but a different allocator. */
874 return ReadSprite(sc
, sprite
, type
, allocator
);
879 * Reads a sprite and finds its most representative colour.
880 * @param sprite Sprite to read.
881 * @param palette_id Palette for remapping colours.
882 * @return if blitter supports 32bpp, average Colour.data else a palette index.
884 uint32
GetSpriteMainColour(SpriteID sprite_id
, PaletteID palette_id
)
886 if (!SpriteExists(sprite_id
)) return 0;
888 SpriteCache
*sc
= GetSpriteCache(sprite_id
);
889 if (sc
->type
!= ST_NORMAL
) return 0;
891 const byte
* const remap
= (palette_id
== PAL_NONE
? nullptr : GetNonSprite(GB(palette_id
, 0, PALETTE_WIDTH
), ST_RECOLOUR
) + 1);
893 uint8 file_slot
= sc
->file_slot
;
894 size_t file_pos
= sc
->file_pos
;
896 SpriteLoader::Sprite sprites
[ZOOM_LVL_COUNT
];
897 SpriteLoader::Sprite
*sprite
= &sprites
[ZOOM_LVL_SHIFT
];
898 sprites
[ZOOM_LVL_NORMAL
].type
= ST_NORMAL
;
899 SpriteLoaderGrf
sprite_loader(sc
->container_ver
);
901 const uint8 screen_depth
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
903 /* Try to read the 32bpp sprite first. */
904 if (screen_depth
== 32) {
905 sprite_avail
= sprite_loader
.LoadSprite(sprites
, file_slot
, file_pos
, ST_NORMAL
, true);
906 if (sprite_avail
& ZOOM_LVL_BASE
) {
907 /* Return the average colour. */
908 uint32 r
= 0, g
= 0, b
= 0, cnt
= 0;
909 SpriteLoader::CommonPixel
*pixel
= sprite
->data
;
910 for (uint x
= sprite
->width
* sprite
->height
; x
!= 0; x
--) {
912 if (remap
&& pixel
->m
) {
913 const Colour c
= _cur_palette
.palette
[remap
[pixel
->m
]];
930 return cnt
? Colour(r
/ cnt
, g
/ cnt
, b
/ cnt
).data
: 0;
934 /* No 32bpp, try 8bpp. */
935 sprite_avail
= sprite_loader
.LoadSprite(sprites
, file_slot
, file_pos
, ST_NORMAL
, false);
936 if (sprite_avail
& ZOOM_LVL_BASE
) {
937 SpriteLoader::CommonPixel
*pixel
= sprite
->data
;
938 if (screen_depth
== 32) {
939 /* Return the average colour. */
940 uint32 r
= 0, g
= 0, b
= 0, cnt
= 0;
941 for (uint x
= sprite
->width
* sprite
->height
; x
!= 0; x
--) {
943 const uint col_index
= remap
? remap
[pixel
->m
] : pixel
->m
;
944 const Colour c
= _cur_palette
.palette
[col_index
];
952 return cnt
? Colour(r
/ cnt
, g
/ cnt
, b
/ cnt
).data
: 0;
955 /* Return the most used indexed colour. */
957 memset(cnt
, 0, sizeof(cnt
));
958 for (uint x
= sprite
->width
* sprite
->height
; x
!= 0; x
--) {
959 cnt
[remap
? remap
[pixel
->m
] : pixel
->m
]++;
964 for (uint x
= 1; x
< lengthof(cnt
); x
++) {
965 if (cnt
[x
] > cnt_max
) {
977 static void GfxInitSpriteCache()
979 /* initialize sprite cache heap */
980 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
981 uint target_size
= (bpp
> 0 ? _sprite_cache_size
* bpp
/ 8 : 1) * 1024 * 1024;
983 /* Remember 'target_size' from the previous allocation attempt, so we do not try to reach the target_size multiple times in case of failure. */
984 static uint last_alloc_attempt
= 0;
986 if (_spritecache_ptr
== nullptr || (_allocated_sprite_cache_size
!= target_size
&& target_size
!= last_alloc_attempt
)) {
987 delete[] reinterpret_cast<byte
*>(_spritecache_ptr
);
989 last_alloc_attempt
= target_size
;
990 _allocated_sprite_cache_size
= target_size
;
994 /* Try to allocate 50% more to make sure we do not allocate almost all available. */
995 _spritecache_ptr
= reinterpret_cast<MemBlock
*>(new byte
[_allocated_sprite_cache_size
+ _allocated_sprite_cache_size
/ 2]);
996 } catch (std::bad_alloc
&) {
997 _spritecache_ptr
= nullptr;
1000 if (_spritecache_ptr
!= nullptr) {
1001 /* Allocation succeeded, but we wanted less. */
1002 delete[] reinterpret_cast<byte
*>(_spritecache_ptr
);
1003 _spritecache_ptr
= reinterpret_cast<MemBlock
*>(new byte
[_allocated_sprite_cache_size
]);
1004 } else if (_allocated_sprite_cache_size
< 2 * 1024 * 1024) {
1005 usererror("Cannot allocate spritecache");
1007 /* Try again to allocate half. */
1008 _allocated_sprite_cache_size
>>= 1;
1010 } while (_spritecache_ptr
== nullptr);
1012 if (_allocated_sprite_cache_size
!= target_size
) {
1013 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);
1015 ErrorMessageData
msg(STR_CONFIG_ERROR_OUT_OF_MEMORY
, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG
);
1016 msg
.SetDParam(0, target_size
);
1017 msg
.SetDParam(1, _allocated_sprite_cache_size
);
1018 ScheduleErrorMessage(msg
);
1022 /* A big free block */
1023 _spritecache_ptr
->size
= (_allocated_sprite_cache_size
- sizeof(MemBlock
)) | S_FREE_MASK
;
1024 /* Sentinel block (identified by size == 0) */
1025 NextBlock(_spritecache_ptr
)->size
= 0;
1028 void GfxInitSpriteMem()
1030 GfxInitSpriteCache();
1032 /* Reset the spritecache 'pool' */
1034 _spritecache_items
= 0;
1035 _spritecache
= nullptr;
1037 _compact_cache_counter
= 0;
1041 * Remove all encoded sprites from the sprite cache without
1042 * discarding sprite location information.
1044 void GfxClearSpriteCache()
1046 /* Clear sprite ptr for all cached items */
1047 for (uint i
= 0; i
!= _spritecache_items
; i
++) {
1048 SpriteCache
*sc
= GetSpriteCache(i
);
1049 if (sc
->type
!= ST_RECOLOUR
&& sc
->ptr
!= nullptr) DeleteEntryFromSpriteCache(i
);
1053 /* static */ ReusableBuffer
<SpriteLoader::CommonPixel
> SpriteLoader::Sprite::buffer
[ZOOM_LVL_COUNT
];