(svn r27724) -Cleanup: Remove pointless usage of IsOpenTTDBaseGRF. System GRFs are...
[openttd.git] / src / spritecache.cpp
blob908e7599ab9136895049321b51312ccc9fbc829e
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file spritecache.cpp Caching of sprites. */
12 #include "stdafx.h"
13 #include "fileio_func.h"
14 #include "spriteloader/grf.hpp"
15 #include "gfx_func.h"
16 #include "error.h"
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 "safeguards.h"
29 /* Default of 4MB spritecache */
30 uint _sprite_cache_size = 4;
32 typedef SimpleTinyEnumT<SpriteType, byte> SpriteTypeByte;
34 struct SpriteCache {
35 void *ptr;
36 size_t file_pos;
37 uint32 id;
38 uint16 file_slot;
39 int16 lru;
40 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.
41 bool warned; ///< True iff the user has been warned about incorrect use of this sprite
42 byte container_ver; ///< Container version of the GRF the sprite is from.
46 static uint _spritecache_items = 0;
47 static SpriteCache *_spritecache = NULL;
50 static inline SpriteCache *GetSpriteCache(uint index)
52 return &_spritecache[index];
55 static inline bool IsMapgenSpriteID(SpriteID sprite)
57 return IsInsideMM(sprite, 4845, 4882);
60 static SpriteCache *AllocateSpriteCache(uint index)
62 if (index >= _spritecache_items) {
63 /* Add another 1024 items to the 'pool' */
64 uint items = Align(index + 1, 1024);
66 DEBUG(sprite, 4, "Increasing sprite cache to %u items (" PRINTF_SIZE " bytes)", items, items * sizeof(*_spritecache));
68 _spritecache = ReallocT(_spritecache, items);
70 /* Reset the new items and update the count */
71 memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) * sizeof(*_spritecache));
72 _spritecache_items = items;
75 return GetSpriteCache(index);
79 struct MemBlock {
80 size_t size;
81 byte data[];
84 static uint _sprite_lru_counter;
85 static MemBlock *_spritecache_ptr;
86 static uint _allocated_sprite_cache_size = 0;
87 static int _compact_cache_counter;
89 static void CompactSpriteCache();
90 static void *AllocSprite(size_t mem_req);
92 /**
93 * Skip the given amount of sprite graphics data.
94 * @param type the type of sprite (compressed etc)
95 * @param num the amount of sprites to skip
96 * @return true if the data could be correctly skipped.
98 bool SkipSpriteData(byte type, uint16 num)
100 if (type & 2) {
101 FioSkipBytes(num);
102 } else {
103 while (num > 0) {
104 int8 i = FioReadByte();
105 if (i >= 0) {
106 int size = (i == 0) ? 0x80 : i;
107 if (size > num) return false;
108 num -= size;
109 FioSkipBytes(size);
110 } else {
111 i = -(i >> 3);
112 num -= i;
113 FioReadByte();
117 return true;
120 /* Check if the given Sprite ID exists */
121 bool SpriteExists(SpriteID id)
123 if (id >= _spritecache_items) return false;
125 /* Special case for Sprite ID zero -- its position is also 0... */
126 if (id == 0) return true;
127 return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0);
131 * Get the sprite type of a given sprite.
132 * @param sprite The sprite to look at.
133 * @return the type of sprite.
135 SpriteType GetSpriteType(SpriteID sprite)
137 if (!SpriteExists(sprite)) return ST_INVALID;
138 return GetSpriteCache(sprite)->type;
142 * Get the (FIOS) file slot of a given sprite.
143 * @param sprite The sprite to look at.
144 * @return the FIOS file slot
146 uint GetOriginFileSlot(SpriteID sprite)
148 if (!SpriteExists(sprite)) return 0;
149 return GetSpriteCache(sprite)->file_slot;
153 * Get a reasonable (upper bound) estimate of the maximum
154 * SpriteID used in OpenTTD; there will be no sprites with
155 * a higher SpriteID, although there might be up to roughly
156 * a thousand unused SpriteIDs below this number.
157 * @note It's actually the number of spritecache items.
158 * @return maximum SpriteID
160 uint GetMaxSpriteID()
162 return _spritecache_items;
165 static bool ResizeSpriteIn(SpriteLoader::Sprite *sprite, ZoomLevel src, ZoomLevel tgt)
167 uint8 scaled_1 = ScaleByZoom(1, (ZoomLevel)(src - tgt));
169 /* Check for possible memory overflow. */
170 if (sprite[src].width * scaled_1 > UINT16_MAX || sprite[src].height * scaled_1 > UINT16_MAX) return false;
172 sprite[tgt].width = sprite[src].width * scaled_1;
173 sprite[tgt].height = sprite[src].height * scaled_1;
174 sprite[tgt].x_offs = sprite[src].x_offs * scaled_1;
175 sprite[tgt].y_offs = sprite[src].y_offs * scaled_1;
177 sprite[tgt].AllocateData(tgt, sprite[tgt].width * sprite[tgt].height);
179 SpriteLoader::CommonPixel *dst = sprite[tgt].data;
180 for (int y = 0; y < sprite[tgt].height; y++) {
181 const SpriteLoader::CommonPixel *src_ln = &sprite[src].data[y / scaled_1 * sprite[src].width];
182 for (int x = 0; x < sprite[tgt].width; x++) {
183 *dst = src_ln[x / scaled_1];
184 dst++;
188 return true;
191 static void ResizeSpriteOut(SpriteLoader::Sprite *sprite, ZoomLevel zoom)
193 /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
194 sprite[zoom].width = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom);
195 sprite[zoom].height = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom);
196 sprite[zoom].x_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom);
197 sprite[zoom].y_offs = UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom);
199 sprite[zoom].AllocateData(zoom, sprite[zoom].height * sprite[zoom].width);
201 SpriteLoader::CommonPixel *dst = sprite[zoom].data;
202 const SpriteLoader::CommonPixel *src = sprite[zoom - 1].data;
203 const SpriteLoader::CommonPixel *src_end = src + sprite[zoom - 1].height * sprite[zoom - 1].width;
205 for (uint y = 0; y < sprite[zoom].height; y++) {
206 const SpriteLoader::CommonPixel *src_ln = src + sprite[zoom - 1].width;
207 assert(src_ln <= src_end);
208 for (uint x = 0; x < sprite[zoom].width; x++) {
209 assert(src < src_ln);
210 if (src + 1 != src_ln && (src + 1)->a != 0) {
211 *dst = *(src + 1);
212 } else {
213 *dst = *src;
215 dst++;
216 src += 2;
218 src = src_ln + sprite[zoom - 1].width;
222 static bool PadSingleSprite(SpriteLoader::Sprite *sprite, ZoomLevel zoom, uint pad_left, uint pad_top, uint pad_right, uint pad_bottom)
224 uint width = sprite->width + pad_left + pad_right;
225 uint height = sprite->height + pad_top + pad_bottom;
227 if (width > UINT16_MAX || height > UINT16_MAX) return false;
229 /* Copy source data and reallocate sprite memory. */
230 SpriteLoader::CommonPixel *src_data = MallocT<SpriteLoader::CommonPixel>(sprite->width * sprite->height);
231 MemCpyT(src_data, sprite->data, sprite->width * sprite->height);
232 sprite->AllocateData(zoom, width * height);
234 /* Copy with padding to destination. */
235 SpriteLoader::CommonPixel *src = src_data;
236 SpriteLoader::CommonPixel *data = sprite->data;
237 for (uint y = 0; y < height; y++) {
238 if (y < pad_top || pad_bottom + y >= height) {
239 /* Top/bottom padding. */
240 MemSetT(data, 0, width);
241 data += width;
242 } else {
243 if (pad_left > 0) {
244 /* Pad left. */
245 MemSetT(data, 0, pad_left);
246 data += pad_left;
249 /* Copy pixels. */
250 MemCpyT(data, src, sprite->width);
251 src += sprite->width;
252 data += sprite->width;
254 if (pad_right > 0) {
255 /* Pad right. */
256 MemSetT(data, 0, pad_right);
257 data += pad_right;
261 free(src_data);
263 /* Update sprite size. */
264 sprite->width = width;
265 sprite->height = height;
266 sprite->x_offs -= pad_left;
267 sprite->y_offs -= pad_top;
269 return true;
272 static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail)
274 /* Get minimum top left corner coordinates. */
275 int min_xoffs = INT32_MAX;
276 int min_yoffs = INT32_MAX;
277 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
278 if (HasBit(sprite_avail, zoom)) {
279 min_xoffs = min(min_xoffs, ScaleByZoom(sprite[zoom].x_offs, zoom));
280 min_yoffs = min(min_yoffs, ScaleByZoom(sprite[zoom].y_offs, zoom));
284 /* Get maximum dimensions taking necessary padding at the top left into account. */
285 int max_width = INT32_MIN;
286 int max_height = INT32_MIN;
287 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
288 if (HasBit(sprite_avail, zoom)) {
289 max_width = max(max_width, ScaleByZoom(sprite[zoom].width + sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom), zoom));
290 max_height = max(max_height, ScaleByZoom(sprite[zoom].height + sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom), zoom));
294 /* Pad sprites where needed. */
295 for (ZoomLevel zoom = ZOOM_LVL_BEGIN; zoom != ZOOM_LVL_END; zoom++) {
296 if (HasBit(sprite_avail, zoom)) {
297 /* Scaling the sprite dimensions in the blitter is done with rounding up,
298 * so a negative padding here is not an error. */
299 int pad_left = max(0, sprite[zoom].x_offs - UnScaleByZoom(min_xoffs, zoom));
300 int pad_top = max(0, sprite[zoom].y_offs - UnScaleByZoom(min_yoffs, zoom));
301 int pad_right = max(0, UnScaleByZoom(max_width, zoom) - sprite[zoom].width - pad_left);
302 int pad_bottom = max(0, UnScaleByZoom(max_height, zoom) - sprite[zoom].height - pad_top);
304 if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0) {
305 if (!PadSingleSprite(&sprite[zoom], zoom, pad_left, pad_top, pad_right, pad_bottom)) return false;
310 return true;
313 static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos)
315 /* Create a fully zoomed image if it does not exist */
316 ZoomLevel first_avail = static_cast<ZoomLevel>(FIND_FIRST_BIT(sprite_avail));
317 if (first_avail != ZOOM_LVL_NORMAL) {
318 if (!ResizeSpriteIn(sprite, first_avail, ZOOM_LVL_NORMAL)) return false;
319 SetBit(sprite_avail, ZOOM_LVL_NORMAL);
322 /* Pad sprites to make sizes match. */
323 if (!PadSprites(sprite, sprite_avail)) return false;
325 /* Create other missing zoom levels */
326 for (ZoomLevel zoom = ZOOM_LVL_OUT_2X; zoom != ZOOM_LVL_END; zoom++) {
327 if (HasBit(sprite_avail, zoom)) {
328 /* Check that size and offsets match the fully zoomed image. */
329 assert(sprite[zoom].width == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].width, zoom));
330 assert(sprite[zoom].height == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].height, zoom));
331 assert(sprite[zoom].x_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].x_offs, zoom));
332 assert(sprite[zoom].y_offs == UnScaleByZoom(sprite[ZOOM_LVL_NORMAL].y_offs, zoom));
335 /* Zoom level is not available, or unusable, so create it */
336 if (!HasBit(sprite_avail, zoom)) ResizeSpriteOut(sprite, zoom);
339 return true;
343 * Load a recolour sprite into memory.
344 * @param file_slot GRF we're reading from.
345 * @param num Size of the sprite in the GRF.
346 * @return Sprite data.
348 static void *ReadRecolourSprite(uint16 file_slot, uint num)
350 /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
351 * number of recolour sprites that are 17 bytes that only exist in DOS
352 * GRFs which are the same as 257 byte recolour sprites, but with the last
353 * 240 bytes zeroed. */
354 static const uint RECOLOUR_SPRITE_SIZE = 257;
355 byte *dest = (byte *)AllocSprite(max(RECOLOUR_SPRITE_SIZE, num));
357 if (_palette_remap_grf[file_slot]) {
358 byte *dest_tmp = AllocaM(byte, max(RECOLOUR_SPRITE_SIZE, num));
360 /* Only a few recolour sprites are less than 257 bytes */
361 if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE);
362 FioReadBlock(dest_tmp, num);
364 /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
365 for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
366 dest[i] = _palmap_w2d[dest_tmp[_palmap_d2w[i - 1] + 1]];
368 } else {
369 FioReadBlock(dest, num);
372 return dest;
376 * Read a sprite from disk.
377 * @param sc Location of sprite.
378 * @param id Sprite number.
379 * @param sprite_type Type of sprite.
380 * @param allocator Allocator function to use.
381 * @return Read sprite data.
383 static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_type, AllocatorProc *allocator)
385 uint8 file_slot = sc->file_slot;
386 size_t file_pos = sc->file_pos;
388 assert(sprite_type != ST_RECOLOUR);
389 assert(IsMapgenSpriteID(id) == (sprite_type == ST_MAPGEN));
390 assert(sc->type == sprite_type);
392 DEBUG(sprite, 9, "Load sprite %d", id);
394 SpriteLoader::Sprite sprite[ZOOM_LVL_COUNT];
395 uint8 sprite_avail = 0;
396 sprite[ZOOM_LVL_NORMAL].type = sprite_type;
398 SpriteLoaderGrf sprite_loader(sc->container_ver);
399 if (sprite_type != ST_MAPGEN && BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) {
400 /* Try for 32bpp sprites first. */
401 sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, true);
403 if (sprite_avail == 0) {
404 sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, false);
407 if (sprite_avail == 0) {
408 if (sprite_type == ST_MAPGEN) return NULL;
409 if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
410 return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
413 if (sprite_type == ST_MAPGEN) {
414 /* Ugly hack to work around the problem that the old landscape
415 * generator assumes that those sprites are stored uncompressed in
416 * the memory, and they are only read directly by the code, never
417 * send to the blitter. So do not send it to the blitter (which will
418 * result in a data array in the format the blitter likes most), but
419 * extract the data directly and store that as sprite.
420 * Ugly: yes. Other solution: no. Blame the original author or
421 * something ;) The image should really have been a data-stream
422 * (so type = 0xFF basically). */
423 uint num = sprite[ZOOM_LVL_NORMAL].width * sprite[ZOOM_LVL_NORMAL].height;
425 Sprite *s = (Sprite *)allocator(sizeof(*s) + num);
426 s->width = sprite[ZOOM_LVL_NORMAL].width;
427 s->height = sprite[ZOOM_LVL_NORMAL].height;
428 s->x_offs = sprite[ZOOM_LVL_NORMAL].x_offs;
429 s->y_offs = sprite[ZOOM_LVL_NORMAL].y_offs;
431 SpriteLoader::CommonPixel *src = sprite[ZOOM_LVL_NORMAL].data;
432 byte *dest = s->data;
433 while (num-- > 0) {
434 *dest++ = src->m;
435 src++;
438 return s;
441 if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id)) {
442 if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
443 return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
446 if (sprite->type == ST_FONT && ZOOM_LVL_GUI != ZOOM_LVL_NORMAL) {
447 /* Make ZOOM_LVL_GUI be ZOOM_LVL_NORMAL */
448 sprite[ZOOM_LVL_NORMAL].width = sprite[ZOOM_LVL_GUI].width;
449 sprite[ZOOM_LVL_NORMAL].height = sprite[ZOOM_LVL_GUI].height;
450 sprite[ZOOM_LVL_NORMAL].x_offs = sprite[ZOOM_LVL_GUI].x_offs;
451 sprite[ZOOM_LVL_NORMAL].y_offs = sprite[ZOOM_LVL_GUI].y_offs;
452 sprite[ZOOM_LVL_NORMAL].data = sprite[ZOOM_LVL_GUI].data;
455 return BlitterFactory::GetCurrentBlitter()->Encode(sprite, allocator);
459 /** Map from sprite numbers to position in the GRF file. */
460 static std::map<uint32, size_t> _grf_sprite_offsets;
463 * Get the file offset for a specific sprite in the sprite section of a GRF.
464 * @param id ID of the sprite to look up.
465 * @return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present.
467 size_t GetGRFSpriteOffset(uint32 id)
469 return _grf_sprite_offsets.find(id) != _grf_sprite_offsets.end() ? _grf_sprite_offsets[id] : SIZE_MAX;
473 * Parse the sprite section of GRFs.
474 * @param container_version Container version of the GRF we're currently processing.
476 void ReadGRFSpriteOffsets(byte container_version)
478 _grf_sprite_offsets.clear();
480 if (container_version >= 2) {
481 /* Seek to sprite section of the GRF. */
482 size_t data_offset = FioReadDword();
483 size_t old_pos = FioGetPos();
484 FioSeekTo(data_offset, SEEK_CUR);
486 /* Loop over all sprite section entries and store the file
487 * offset for each newly encountered ID. */
488 uint32 id, prev_id = 0;
489 while ((id = FioReadDword()) != 0) {
490 if (id != prev_id) _grf_sprite_offsets[id] = FioGetPos() - 4;
491 prev_id = id;
492 FioSkipBytes(FioReadDword());
495 /* Continue processing the data section. */
496 FioSeekTo(old_pos, SEEK_SET);
502 * Load a real or recolour sprite.
503 * @param load_index Global sprite index.
504 * @param file_slot GRF to load from.
505 * @param file_sprite_id Sprite number in the GRF.
506 * @param container_version Container version of the GRF.
507 * @return True if a valid sprite was loaded, false on any error.
509 bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version)
511 size_t file_pos = FioGetPos();
513 /* Read sprite header. */
514 uint32 num = container_version >= 2 ? FioReadDword() : FioReadWord();
515 if (num == 0) return false;
516 byte grf_type = FioReadByte();
518 SpriteType type;
519 void *data = NULL;
520 if (grf_type == 0xFF) {
521 /* Some NewGRF files have "empty" pseudo-sprites which are 1
522 * byte long. Catch these so the sprites won't be displayed. */
523 if (num == 1) {
524 FioReadByte();
525 return false;
527 type = ST_RECOLOUR;
528 data = ReadRecolourSprite(file_slot, num);
529 } else if (container_version >= 2 && grf_type == 0xFD) {
530 if (num != 4) {
531 /* Invalid sprite section include, ignore. */
532 FioSkipBytes(num);
533 return false;
535 /* It is not an error if no sprite with the provided ID is found in the sprite section. */
536 file_pos = GetGRFSpriteOffset(FioReadDword());
537 type = ST_NORMAL;
538 } else {
539 FioSkipBytes(7);
540 type = SkipSpriteData(grf_type, num - 8) ? ST_NORMAL : ST_INVALID;
541 /* Inline sprites are not supported for container version >= 2. */
542 if (container_version >= 2) return false;
545 if (type == ST_INVALID) return false;
547 if (load_index >= MAX_SPRITES) {
548 usererror("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
551 bool is_mapgen = IsMapgenSpriteID(load_index);
553 if (is_mapgen) {
554 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?");
555 type = ST_MAPGEN;
558 SpriteCache *sc = AllocateSpriteCache(load_index);
559 sc->file_slot = file_slot;
560 sc->file_pos = file_pos;
561 sc->ptr = data;
562 sc->lru = 0;
563 sc->id = file_sprite_id;
564 sc->type = type;
565 sc->warned = false;
566 sc->container_ver = container_version;
568 return true;
572 void DupSprite(SpriteID old_spr, SpriteID new_spr)
574 SpriteCache *scnew = AllocateSpriteCache(new_spr); // may reallocate: so put it first
575 SpriteCache *scold = GetSpriteCache(old_spr);
577 scnew->file_slot = scold->file_slot;
578 scnew->file_pos = scold->file_pos;
579 scnew->ptr = NULL;
580 scnew->id = scold->id;
581 scnew->type = scold->type;
582 scnew->warned = false;
583 scnew->container_ver = scold->container_ver;
587 * S_FREE_MASK is used to mask-out lower bits of MemBlock::size
588 * If they are non-zero, the block is free.
589 * S_FREE_MASK has to ensure MemBlock is correctly aligned -
590 * it means 8B (S_FREE_MASK == 7) on 64bit systems!
592 static const size_t S_FREE_MASK = sizeof(size_t) - 1;
594 /* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
595 assert_compile(sizeof(MemBlock) == sizeof(size_t));
596 /* make sure it's a power of two */
597 assert_compile((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
599 static inline MemBlock *NextBlock(MemBlock *block)
601 return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
604 static size_t GetSpriteCacheUsage()
606 size_t tot_size = 0;
607 MemBlock *s;
609 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
610 if (!(s->size & S_FREE_MASK)) tot_size += s->size;
613 return tot_size;
617 void IncreaseSpriteLRU()
619 /* Increase all LRU values */
620 if (_sprite_lru_counter > 16384) {
621 SpriteID i;
623 DEBUG(sprite, 3, "Fixing lru %u, inuse=" PRINTF_SIZE, _sprite_lru_counter, GetSpriteCacheUsage());
625 for (i = 0; i != _spritecache_items; i++) {
626 SpriteCache *sc = GetSpriteCache(i);
627 if (sc->ptr != NULL) {
628 if (sc->lru >= 0) {
629 sc->lru = -1;
630 } else if (sc->lru != -32768) {
631 sc->lru--;
635 _sprite_lru_counter = 0;
638 /* Compact sprite cache every now and then. */
639 if (++_compact_cache_counter >= 740) {
640 CompactSpriteCache();
641 _compact_cache_counter = 0;
646 * Called when holes in the sprite cache should be removed.
647 * That is accomplished by moving the cached data.
649 static void CompactSpriteCache()
651 MemBlock *s;
653 DEBUG(sprite, 3, "Compacting sprite cache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
655 for (s = _spritecache_ptr; s->size != 0;) {
656 if (s->size & S_FREE_MASK) {
657 MemBlock *next = NextBlock(s);
658 MemBlock temp;
659 SpriteID i;
661 /* Since free blocks are automatically coalesced, this should hold true. */
662 assert(!(next->size & S_FREE_MASK));
664 /* If the next block is the sentinel block, we can safely return */
665 if (next->size == 0) break;
667 /* Locate the sprite belonging to the next pointer. */
668 for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
669 assert(i != _spritecache_items);
672 GetSpriteCache(i)->ptr = s->data; // Adjust sprite array entry
673 /* Swap this and the next block */
674 temp = *s;
675 memmove(s, next, next->size);
676 s = NextBlock(s);
677 *s = temp;
679 /* Coalesce free blocks */
680 while (NextBlock(s)->size & S_FREE_MASK) {
681 s->size += NextBlock(s)->size & ~S_FREE_MASK;
683 } else {
684 s = NextBlock(s);
690 * Delete a single entry from the sprite cache.
691 * @param item Entry to delete.
693 static void DeleteEntryFromSpriteCache(uint item)
695 /* Mark the block as free (the block must be in use) */
696 MemBlock *s = (MemBlock*)GetSpriteCache(item)->ptr - 1;
697 assert(!(s->size & S_FREE_MASK));
698 s->size |= S_FREE_MASK;
699 GetSpriteCache(item)->ptr = NULL;
701 /* And coalesce adjacent free blocks */
702 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
703 if (s->size & S_FREE_MASK) {
704 while (NextBlock(s)->size & S_FREE_MASK) {
705 s->size += NextBlock(s)->size & ~S_FREE_MASK;
711 static void DeleteEntryFromSpriteCache()
713 uint best = UINT_MAX;
714 int cur_lru;
716 DEBUG(sprite, 3, "DeleteEntryFromSpriteCache, inuse=" PRINTF_SIZE, GetSpriteCacheUsage());
718 cur_lru = 0xffff;
719 for (SpriteID i = 0; i != _spritecache_items; i++) {
720 SpriteCache *sc = GetSpriteCache(i);
721 if (sc->type != ST_RECOLOUR && sc->ptr != NULL && sc->lru < cur_lru) {
722 cur_lru = sc->lru;
723 best = i;
727 /* Display an error message and die, in case we found no sprite at all.
728 * This shouldn't really happen, unless all sprites are locked. */
729 if (best == UINT_MAX) error("Out of sprite memory");
731 DeleteEntryFromSpriteCache(best);
734 static void *AllocSprite(size_t mem_req)
736 mem_req += sizeof(MemBlock);
738 /* Align this to correct boundary. This also makes sure at least one
739 * bit is not used, so we can use it for other things. */
740 mem_req = Align(mem_req, S_FREE_MASK + 1);
742 for (;;) {
743 MemBlock *s;
745 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
746 if (s->size & S_FREE_MASK) {
747 size_t cur_size = s->size & ~S_FREE_MASK;
749 /* Is the block exactly the size we need or
750 * big enough for an additional free block? */
751 if (cur_size == mem_req ||
752 cur_size >= mem_req + sizeof(MemBlock)) {
753 /* Set size and in use */
754 s->size = mem_req;
756 /* Do we need to inject a free block too? */
757 if (cur_size != mem_req) {
758 NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
761 return s->data;
766 /* Reached sentinel, but no block found yet. Delete some old entry. */
767 DeleteEntryFromSpriteCache();
772 * Handles the case when a sprite of different type is requested than is present in the SpriteCache.
773 * For ST_FONT sprites, it is normal. In other cases, default sprite is loaded instead.
774 * @param sprite ID of loaded sprite
775 * @param requested requested sprite type
776 * @param sc the currently known sprite cache for the requested sprite
777 * @return fallback sprite
778 * @note this function will do usererror() in the case the fallback sprite isn't available
780 static void *HandleInvalidSpriteRequest(SpriteID sprite, SpriteType requested, SpriteCache *sc, AllocatorProc *allocator)
782 static const char * const sprite_types[] = {
783 "normal", // ST_NORMAL
784 "map generator", // ST_MAPGEN
785 "character", // ST_FONT
786 "recolour", // ST_RECOLOUR
789 SpriteType available = sc->type;
790 if (requested == ST_FONT && available == ST_NORMAL) {
791 if (sc->ptr == NULL) sc->type = ST_FONT;
792 return GetRawSprite(sprite, sc->type, allocator);
795 byte warning_level = sc->warned ? 6 : 0;
796 sc->warned = true;
797 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]);
799 switch (requested) {
800 case ST_NORMAL:
801 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?");
802 /* FALL THROUGH */
803 case ST_FONT:
804 return GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
805 case ST_RECOLOUR:
806 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?");
807 return GetRawSprite(PALETTE_TO_DARK_BLUE, ST_RECOLOUR, allocator);
808 case ST_MAPGEN:
809 /* this shouldn't happen, overriding of ST_MAPGEN sprites is checked in LoadNextSprite()
810 * (the only case the check fails is when these sprites weren't even loaded...) */
811 default:
812 NOT_REACHED();
817 * Reads a sprite (from disk or sprite cache).
818 * If the sprite is not available or of wrong type, a fallback sprite is returned.
819 * @param sprite Sprite to read.
820 * @param type Expected sprite type.
821 * @param allocator Allocator function to use. Set to NULL to use the usual sprite cache.
822 * @return Sprite raw data
824 void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator)
826 assert(type != ST_MAPGEN || IsMapgenSpriteID(sprite));
827 assert(type < ST_INVALID);
829 if (!SpriteExists(sprite)) {
830 DEBUG(sprite, 1, "Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs", sprite);
832 /* SPR_IMG_QUERY is a BIG FAT RED ? */
833 sprite = SPR_IMG_QUERY;
836 SpriteCache *sc = GetSpriteCache(sprite);
838 if (sc->type != type) return HandleInvalidSpriteRequest(sprite, type, sc, allocator);
840 if (allocator == NULL) {
841 /* Load sprite into/from spritecache */
843 /* Update LRU */
844 sc->lru = ++_sprite_lru_counter;
846 /* Load the sprite, if it is not loaded, yet */
847 if (sc->ptr == NULL) sc->ptr = ReadSprite(sc, sprite, type, AllocSprite);
849 return sc->ptr;
850 } else {
851 /* Do not use the spritecache, but a different allocator. */
852 return ReadSprite(sc, sprite, type, allocator);
857 static void GfxInitSpriteCache()
859 /* initialize sprite cache heap */
860 int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
861 uint target_size = (bpp > 0 ? _sprite_cache_size * bpp / 8 : 1) * 1024 * 1024;
863 /* Remember 'target_size' from the previous allocation attempt, so we do not try to reach the target_size multiple times in case of failure. */
864 static uint last_alloc_attempt = 0;
866 if (_spritecache_ptr == NULL || (_allocated_sprite_cache_size != target_size && target_size != last_alloc_attempt)) {
867 delete[] reinterpret_cast<byte *>(_spritecache_ptr);
869 last_alloc_attempt = target_size;
870 _allocated_sprite_cache_size = target_size;
872 do {
873 try {
874 /* Try to allocate 50% more to make sure we do not allocate almost all available. */
875 _spritecache_ptr = reinterpret_cast<MemBlock *>(new byte[_allocated_sprite_cache_size + _allocated_sprite_cache_size / 2]);
876 } catch (std::bad_alloc &) {
877 _spritecache_ptr = NULL;
880 if (_spritecache_ptr != NULL) {
881 /* Allocation succeeded, but we wanted less. */
882 delete[] reinterpret_cast<byte *>(_spritecache_ptr);
883 _spritecache_ptr = reinterpret_cast<MemBlock *>(new byte[_allocated_sprite_cache_size]);
884 } else if (_allocated_sprite_cache_size < 2 * 1024 * 1024) {
885 usererror("Cannot allocate spritecache");
886 } else {
887 /* Try again to allocate half. */
888 _allocated_sprite_cache_size >>= 1;
890 } while (_spritecache_ptr == NULL);
892 if (_allocated_sprite_cache_size != target_size) {
893 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);
895 ErrorMessageData msg(STR_CONFIG_ERROR_OUT_OF_MEMORY, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG);
896 msg.SetDParam(0, target_size);
897 msg.SetDParam(1, _allocated_sprite_cache_size);
898 ScheduleErrorMessage(msg);
902 /* A big free block */
903 _spritecache_ptr->size = (_allocated_sprite_cache_size - sizeof(MemBlock)) | S_FREE_MASK;
904 /* Sentinel block (identified by size == 0) */
905 NextBlock(_spritecache_ptr)->size = 0;
908 void GfxInitSpriteMem()
910 GfxInitSpriteCache();
912 /* Reset the spritecache 'pool' */
913 free(_spritecache);
914 _spritecache_items = 0;
915 _spritecache = NULL;
917 _compact_cache_counter = 0;
921 * Remove all encoded sprites from the sprite cache without
922 * discarding sprite location information.
924 void GfxClearSpriteCache()
926 /* Clear sprite ptr for all cached items */
927 for (uint i = 0; i != _spritecache_items; i++) {
928 SpriteCache *sc = GetSpriteCache(i);
929 if (sc->type != ST_RECOLOUR && sc->ptr != NULL) DeleteEntryFromSpriteCache(i);
933 /* static */ ReusableBuffer<SpriteLoader::CommonPixel> SpriteLoader::Sprite::buffer[ZOOM_LVL_COUNT];