2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 /** @file spritecache.cpp Caching of sprites. */
11 #include "random_access_file_type.h"
12 #include "spriteloader/grf.hpp"
15 #include "error_func.h"
16 #include "zoom_func.h"
17 #include "settings_type.h"
18 #include "blitter/factory.hpp"
19 #include "core/math_func.hpp"
20 #include "core/mem_func.hpp"
21 #include "video/video_driver.hpp"
22 #include "spritecache.h"
23 #include "spritecache_internal.h"
25 #include "table/sprites.h"
26 #include "table/strings.h"
27 #include "table/palette_convert.h"
29 #include "safeguards.h"
31 /* Default of 4MB spritecache */
32 uint _sprite_cache_size
= 4;
35 static uint _spritecache_items
= 0;
36 static SpriteCache
*_spritecache
= nullptr;
37 static std::vector
<std::unique_ptr
<SpriteFile
>> _sprite_files
;
39 static inline SpriteCache
*GetSpriteCache(uint index
)
41 return &_spritecache
[index
];
44 SpriteCache
*AllocateSpriteCache(uint index
)
46 if (index
>= _spritecache_items
) {
47 /* Add another 1024 items to the 'pool' */
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
);
54 /* Reset the new items and update the count */
55 memset(_spritecache
+ _spritecache_items
, 0, (items
- _spritecache_items
) * sizeof(*_spritecache
));
56 _spritecache_items
= items
;
59 return GetSpriteCache(index
);
63 * Get the cached SpriteFile given the name of the file.
64 * @param filename The name of the file at the disk.
65 * @return The SpriteFile or \c null.
67 static SpriteFile
*GetCachedSpriteFileByName(const std::string
&filename
)
69 for (auto &f
: _sprite_files
) {
70 if (f
->GetFilename() == filename
) {
78 * Get the list of cached SpriteFiles.
79 * @return Read-only list of cache SpriteFiles.
81 std::span
<const std::unique_ptr
<SpriteFile
>> GetCachedSpriteFiles()
87 * Open/get the SpriteFile that is cached for use in the sprite cache.
88 * @param filename Name of the file at the disk.
89 * @param subdir The sub directory to search this file in.
90 * @param palette_remap Whether a palette remap needs to be performed for this file.
91 * @return The reference to the SpriteCache.
93 SpriteFile
&OpenCachedSpriteFile(const std::string
&filename
, Subdirectory subdir
, bool palette_remap
)
95 SpriteFile
*file
= GetCachedSpriteFileByName(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
;
110 static MemBlock
*_spritecache_ptr
;
111 static uint _allocated_sprite_cache_size
= 0;
112 static int _compact_cache_counter
;
114 static void CompactSpriteCache();
117 * Skip the given amount of sprite graphics data.
118 * @param type the type of sprite (compressed etc)
119 * @param num the amount of sprites to skip
120 * @return true if the data could be correctly skipped.
122 bool SkipSpriteData(SpriteFile
&file
, uint8_t type
, uint16_t num
)
128 int8_t i
= file
.ReadByte();
130 int size
= (i
== 0) ? 0x80 : i
;
131 if (size
> num
) return false;
133 file
.SkipBytes(size
);
144 /* Check if the given Sprite ID exists */
145 bool SpriteExists(SpriteID id
)
147 if (id
>= _spritecache_items
) return false;
149 /* Special case for Sprite ID zero -- its position is also 0... */
150 if (id
== 0) return true;
151 return !(GetSpriteCache(id
)->file_pos
== 0 && GetSpriteCache(id
)->file
== nullptr);
155 * Get the sprite type of a given sprite.
156 * @param sprite The sprite to look at.
157 * @return the type of sprite.
159 SpriteType
GetSpriteType(SpriteID sprite
)
161 if (!SpriteExists(sprite
)) return SpriteType::Invalid
;
162 return GetSpriteCache(sprite
)->type
;
166 * Get the SpriteFile of a given sprite.
167 * @param sprite The sprite to look at.
168 * @return The SpriteFile.
170 SpriteFile
*GetOriginFile(SpriteID sprite
)
172 if (!SpriteExists(sprite
)) return nullptr;
173 return GetSpriteCache(sprite
)->file
;
177 * Get the GRF-local sprite id of a given sprite.
178 * @param sprite The sprite to look at.
179 * @return The GRF-local sprite id.
181 uint32_t GetSpriteLocalID(SpriteID sprite
)
183 if (!SpriteExists(sprite
)) return 0;
184 return GetSpriteCache(sprite
)->id
;
188 * Count the sprites which originate from a specific file in a range of SpriteIDs.
189 * @param file The loaded SpriteFile.
190 * @param begin First sprite in range.
191 * @param end First sprite not in range.
192 * @return Number of sprites.
194 uint
GetSpriteCountForFile(const std::string
&filename
, SpriteID begin
, SpriteID end
)
196 SpriteFile
*file
= GetCachedSpriteFileByName(filename
);
197 if (file
== nullptr) return 0;
200 for (SpriteID i
= begin
; i
!= end
; i
++) {
201 if (SpriteExists(i
)) {
202 SpriteCache
*sc
= GetSpriteCache(i
);
203 if (sc
->file
== file
) {
205 Debug(sprite
, 4, "Sprite: {}", i
);
213 * Get a reasonable (upper bound) estimate of the maximum
214 * SpriteID used in OpenTTD; there will be no sprites with
215 * a higher SpriteID, although there might be up to roughly
216 * a thousand unused SpriteIDs below this number.
217 * @note It's actually the number of spritecache items.
218 * @return maximum SpriteID
220 uint
GetMaxSpriteID()
222 return _spritecache_items
;
225 static bool ResizeSpriteIn(SpriteLoader::SpriteCollection
&sprite
, ZoomLevel src
, ZoomLevel tgt
)
227 uint8_t scaled_1
= ScaleByZoom(1, (ZoomLevel
)(src
- tgt
));
229 /* Check for possible memory overflow. */
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
);
240 SpriteLoader::CommonPixel
*dst
= sprite
[tgt
].data
;
241 for (int y
= 0; y
< sprite
[tgt
].height
; y
++) {
242 const SpriteLoader::CommonPixel
*src_ln
= &sprite
[src
].data
[y
/ scaled_1
* sprite
[src
].width
];
243 for (int x
= 0; x
< sprite
[tgt
].width
; x
++) {
244 *dst
= src_ln
[x
/ scaled_1
];
252 static void ResizeSpriteOut(SpriteLoader::SpriteCollection
&sprite
, ZoomLevel zoom
)
254 /* Algorithm based on 32bpp_Optimized::ResizeSprite() */
255 sprite
[zoom
].width
= UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].width
, zoom
);
256 sprite
[zoom
].height
= UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].height
, zoom
);
257 sprite
[zoom
].x_offs
= UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].x_offs
, zoom
);
258 sprite
[zoom
].y_offs
= UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].y_offs
, zoom
);
259 sprite
[zoom
].colours
= sprite
[ZOOM_LVL_MIN
].colours
;
261 sprite
[zoom
].AllocateData(zoom
, static_cast<size_t>(sprite
[zoom
].height
) * sprite
[zoom
].width
);
263 SpriteLoader::CommonPixel
*dst
= sprite
[zoom
].data
;
264 const SpriteLoader::CommonPixel
*src
= sprite
[zoom
- 1].data
;
265 [[maybe_unused
]] const SpriteLoader::CommonPixel
*src_end
= src
+ sprite
[zoom
- 1].height
* sprite
[zoom
- 1].width
;
267 for (uint y
= 0; y
< sprite
[zoom
].height
; y
++) {
268 const SpriteLoader::CommonPixel
*src_ln
= src
+ sprite
[zoom
- 1].width
;
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
;
284 static bool PadSingleSprite(SpriteLoader::Sprite
*sprite
, ZoomLevel zoom
, uint pad_left
, uint pad_top
, uint pad_right
, uint pad_bottom
)
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;
291 /* Copy source data and reallocate sprite memory. */
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
);
296 /* Copy with padding to destination. */
297 SpriteLoader::CommonPixel
*src
= src_data
.data();
298 SpriteLoader::CommonPixel
*data
= sprite
->data
;
299 for (uint y
= 0; y
< height
; y
++) {
300 if (y
< pad_top
|| pad_bottom
+ y
>= height
) {
301 /* Top/bottom padding. */
302 MemSetT(data
, 0, width
);
307 MemSetT(data
, 0, pad_left
);
312 MemCpyT(data
, src
, sprite
->width
);
313 src
+= sprite
->width
;
314 data
+= sprite
->width
;
318 MemSetT(data
, 0, pad_right
);
324 /* Update sprite size. */
325 sprite
->width
= width
;
326 sprite
->height
= height
;
327 sprite
->x_offs
-= pad_left
;
328 sprite
->y_offs
-= pad_top
;
333 static bool PadSprites(SpriteLoader::SpriteCollection
&sprite
, uint8_t sprite_avail
, SpriteEncoder
*encoder
)
335 /* Get minimum top left corner coordinates. */
336 int min_xoffs
= INT32_MAX
;
337 int min_yoffs
= INT32_MAX
;
338 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
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
));
345 /* Get maximum dimensions taking necessary padding at the top left into account. */
346 int max_width
= INT32_MIN
;
347 int max_height
= INT32_MIN
;
348 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
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
));
355 /* Align height and width if required to match the needs of the sprite encoder. */
356 uint align
= encoder
->GetSpriteAlignment();
358 max_width
= Align(max_width
, align
);
359 max_height
= Align(max_height
, align
);
362 /* Pad sprites where needed. */
363 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
364 if (HasBit(sprite_avail
, zoom
)) {
365 /* Scaling the sprite dimensions in the blitter is done with rounding up,
366 * so a negative padding here is not an error. */
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;
381 static bool ResizeSprites(SpriteLoader::SpriteCollection
&sprite
, uint8_t sprite_avail
, SpriteEncoder
*encoder
)
383 /* Create a fully zoomed image if it does not exist */
384 ZoomLevel first_avail
= static_cast<ZoomLevel
>(FindFirstBit(sprite_avail
));
385 if (first_avail
!= ZOOM_LVL_MIN
) {
386 if (!ResizeSpriteIn(sprite
, first_avail
, ZOOM_LVL_MIN
)) return false;
387 SetBit(sprite_avail
, ZOOM_LVL_MIN
);
390 /* Pad sprites to make sizes match. */
391 if (!PadSprites(sprite
, sprite_avail
, encoder
)) return false;
393 /* Create other missing zoom levels */
394 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
395 if (zoom
== ZOOM_LVL_MIN
) continue;
397 if (HasBit(sprite_avail
, zoom
)) {
398 /* Check that size and offsets match the fully zoomed image. */
399 assert(sprite
[zoom
].width
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].width
, zoom
));
400 assert(sprite
[zoom
].height
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].height
, zoom
));
401 assert(sprite
[zoom
].x_offs
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].x_offs
, zoom
));
402 assert(sprite
[zoom
].y_offs
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].y_offs
, zoom
));
405 /* Zoom level is not available, or unusable, so create it */
406 if (!HasBit(sprite_avail
, zoom
)) ResizeSpriteOut(sprite
, zoom
);
409 /* Upscale to desired sprite_min_zoom if provided sprite only had zoomed in versions. */
410 if (first_avail
< _settings_client
.gui
.sprite_zoom_min
) {
411 if (_settings_client
.gui
.sprite_zoom_min
>= ZOOM_LVL_NORMAL
) ResizeSpriteIn(sprite
, ZOOM_LVL_NORMAL
, ZOOM_LVL_IN_2X
);
412 if (_settings_client
.gui
.sprite_zoom_min
>= ZOOM_LVL_IN_2X
) ResizeSpriteIn(sprite
, ZOOM_LVL_IN_2X
, ZOOM_LVL_IN_4X
);
419 * Load a recolour sprite into memory.
420 * @param file GRF we're reading from.
421 * @param num Size of the sprite in the GRF.
422 * @return Sprite data.
424 static void *ReadRecolourSprite(SpriteFile
&file
, uint num
, SpriteAllocator
&allocator
)
426 /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
427 * number of recolour sprites that are 17 bytes that only exist in DOS
428 * GRFs which are the same as 257 byte recolour sprites, but with the last
429 * 240 bytes zeroed. */
430 static const uint RECOLOUR_SPRITE_SIZE
= 257;
431 uint8_t *dest
= allocator
.Allocate
<uint8_t>(std::max(RECOLOUR_SPRITE_SIZE
, num
));
433 if (file
.NeedsPaletteRemap()) {
434 uint8_t *dest_tmp
= new uint8_t[std::max(RECOLOUR_SPRITE_SIZE
, num
)];
436 /* Only a few recolour sprites are less than 257 bytes */
437 if (num
< RECOLOUR_SPRITE_SIZE
) memset(dest_tmp
, 0, RECOLOUR_SPRITE_SIZE
);
438 file
.ReadBlock(dest_tmp
, num
);
440 /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
441 for (uint i
= 1; i
< RECOLOUR_SPRITE_SIZE
; i
++) {
442 dest
[i
] = _palmap_w2d
[dest_tmp
[_palmap_d2w
[i
- 1] + 1]];
446 file
.ReadBlock(dest
, num
);
453 * Read a sprite from disk.
454 * @param sc Location of sprite.
455 * @param id Sprite number.
456 * @param sprite_type Type of sprite.
457 * @param allocator Allocator function to use.
458 * @param encoder Sprite encoder to use.
459 * @return Read sprite data.
461 static void *ReadSprite(const SpriteCache
*sc
, SpriteID id
, SpriteType sprite_type
, SpriteAllocator
&allocator
, SpriteEncoder
*encoder
)
463 /* Use current blitter if no other sprite encoder is given. */
464 if (encoder
== nullptr) encoder
= BlitterFactory::GetCurrentBlitter();
466 SpriteFile
&file
= *sc
->file
;
467 size_t file_pos
= sc
->file_pos
;
469 assert(sprite_type
!= SpriteType::Recolour
);
470 assert(IsMapgenSpriteID(id
) == (sprite_type
== SpriteType::MapGen
));
471 assert(sc
->type
== sprite_type
);
473 Debug(sprite
, 9, "Load sprite {}", id
);
475 SpriteLoader::SpriteCollection sprite
;
476 uint8_t sprite_avail
= 0;
477 sprite
[ZOOM_LVL_MIN
].type
= sprite_type
;
479 SpriteLoaderGrf
sprite_loader(file
.GetContainerVersion());
480 if (sprite_type
!= SpriteType::MapGen
&& encoder
->Is32BppSupported()) {
481 /* Try for 32bpp sprites first. */
482 sprite_avail
= sprite_loader
.LoadSprite(sprite
, file
, file_pos
, sprite_type
, true, sc
->control_flags
);
484 if (sprite_avail
== 0) {
485 sprite_avail
= sprite_loader
.LoadSprite(sprite
, file
, file_pos
, sprite_type
, false, sc
->control_flags
);
488 if (sprite_avail
== 0) {
489 if (sprite_type
== SpriteType::MapGen
) return nullptr;
490 if (id
== SPR_IMG_QUERY
) UserError("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
491 return (void*)GetRawSprite(SPR_IMG_QUERY
, SpriteType::Normal
, &allocator
, encoder
);
494 if (sprite_type
== SpriteType::MapGen
) {
495 /* Ugly hack to work around the problem that the old landscape
496 * generator assumes that those sprites are stored uncompressed in
497 * the memory, and they are only read directly by the code, never
498 * send to the blitter. So do not send it to the blitter (which will
499 * result in a data array in the format the blitter likes most), but
500 * extract the data directly and store that as sprite.
501 * Ugly: yes. Other solution: no. Blame the original author or
502 * something ;) The image should really have been a data-stream
503 * (so type = 0xFF basically). */
504 uint num
= sprite
[ZOOM_LVL_MIN
].width
* sprite
[ZOOM_LVL_MIN
].height
;
506 Sprite
*s
= allocator
.Allocate
<Sprite
>(sizeof(*s
) + num
);
507 s
->width
= sprite
[ZOOM_LVL_MIN
].width
;
508 s
->height
= sprite
[ZOOM_LVL_MIN
].height
;
509 s
->x_offs
= sprite
[ZOOM_LVL_MIN
].x_offs
;
510 s
->y_offs
= sprite
[ZOOM_LVL_MIN
].y_offs
;
512 SpriteLoader::CommonPixel
*src
= sprite
[ZOOM_LVL_MIN
].data
;
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?");
524 return (void*)GetRawSprite(SPR_IMG_QUERY
, SpriteType::Normal
, &allocator
, encoder
);
527 if (sprite
[ZOOM_LVL_MIN
].type
== SpriteType::Font
&& _font_zoom
!= ZOOM_LVL_MIN
) {
528 /* Make ZOOM_LVL_MIN be ZOOM_LVL_GUI */
529 sprite
[ZOOM_LVL_MIN
].width
= sprite
[_font_zoom
].width
;
530 sprite
[ZOOM_LVL_MIN
].height
= sprite
[_font_zoom
].height
;
531 sprite
[ZOOM_LVL_MIN
].x_offs
= sprite
[_font_zoom
].x_offs
;
532 sprite
[ZOOM_LVL_MIN
].y_offs
= sprite
[_font_zoom
].y_offs
;
533 sprite
[ZOOM_LVL_MIN
].data
= sprite
[_font_zoom
].data
;
534 sprite
[ZOOM_LVL_MIN
].colours
= sprite
[_font_zoom
].colours
;
537 return encoder
->Encode(sprite
, allocator
);
540 struct GrfSpriteOffset
{
542 uint8_t control_flags
;
545 /** Map from sprite numbers to position in the GRF file. */
546 static std::map
<uint32_t, GrfSpriteOffset
> _grf_sprite_offsets
;
549 * Get the file offset for a specific sprite in the sprite section of a GRF.
550 * @param id ID of the sprite to look up.
551 * @return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present.
553 size_t GetGRFSpriteOffset(uint32_t id
)
555 return _grf_sprite_offsets
.find(id
) != _grf_sprite_offsets
.end() ? _grf_sprite_offsets
[id
].file_pos
: SIZE_MAX
;
559 * Parse the sprite section of GRFs.
560 * @param container_version Container version of the GRF we're currently processing.
562 void ReadGRFSpriteOffsets(SpriteFile
&file
)
564 _grf_sprite_offsets
.clear();
566 if (file
.GetContainerVersion() >= 2) {
567 /* Seek to sprite section of the GRF. */
568 size_t data_offset
= file
.ReadDword();
569 size_t old_pos
= file
.GetPos();
570 file
.SeekTo(data_offset
, SEEK_CUR
);
572 GrfSpriteOffset offset
= { 0, 0 };
574 /* Loop over all sprite section entries and store the file
575 * offset for each newly encountered ID. */
576 SpriteID id
, prev_id
= 0;
577 while ((id
= file
.ReadDword()) != 0) {
579 _grf_sprite_offsets
[prev_id
] = offset
;
580 offset
.file_pos
= file
.GetPos() - 4;
581 offset
.control_flags
= 0;
584 uint length
= file
.ReadDword();
586 uint8_t colour
= file
.ReadByte() & SCC_MASK
;
589 uint8_t zoom
= file
.ReadByte();
591 if (colour
!= 0 && zoom
== 0) { // ZOOM_LVL_NORMAL (normal zoom)
592 SetBit(offset
.control_flags
, (colour
!= SCC_PAL
) ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP
: SCCF_ALLOW_ZOOM_MIN_1X_PAL
);
593 SetBit(offset
.control_flags
, (colour
!= SCC_PAL
) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP
: SCCF_ALLOW_ZOOM_MIN_2X_PAL
);
595 if (colour
!= 0 && zoom
== 2) { // ZOOM_LVL_IN_2X (2x zoomed in)
596 SetBit(offset
.control_flags
, (colour
!= SCC_PAL
) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP
: SCCF_ALLOW_ZOOM_MIN_2X_PAL
);
600 file
.SkipBytes(length
);
602 if (prev_id
!= 0) _grf_sprite_offsets
[prev_id
] = offset
;
604 /* Continue processing the data section. */
605 file
.SeekTo(old_pos
, SEEK_SET
);
611 * Load a real or recolour sprite.
612 * @param load_index Global sprite index.
613 * @param file GRF to load from.
614 * @param file_sprite_id Sprite number in the GRF.
615 * @param container_version Container version of the GRF.
616 * @return True if a valid sprite was loaded, false on any error.
618 bool LoadNextSprite(SpriteID load_index
, SpriteFile
&file
, uint file_sprite_id
)
620 size_t file_pos
= file
.GetPos();
622 /* Read sprite header. */
623 uint32_t num
= file
.GetContainerVersion() >= 2 ? file
.ReadDword() : file
.ReadWord();
624 if (num
== 0) return false;
625 uint8_t grf_type
= file
.ReadByte();
628 void *data
= nullptr;
629 uint8_t control_flags
= 0;
630 if (grf_type
== 0xFF) {
631 /* Some NewGRF files have "empty" pseudo-sprites which are 1
632 * byte long. Catch these so the sprites won't be displayed. */
637 type
= SpriteType::Recolour
;
638 CacheSpriteAllocator allocator
;
639 data
= ReadRecolourSprite(file
, num
, allocator
);
640 } else if (file
.GetContainerVersion() >= 2 && grf_type
== 0xFD) {
642 /* Invalid sprite section include, ignore. */
646 /* It is not an error if no sprite with the provided ID is found in the sprite section. */
647 auto iter
= _grf_sprite_offsets
.find(file
.ReadDword());
648 if (iter
!= _grf_sprite_offsets
.end()) {
649 file_pos
= iter
->second
.file_pos
;
650 control_flags
= iter
->second
.control_flags
;
654 type
= SpriteType::Normal
;
657 type
= SkipSpriteData(file
, grf_type
, num
- 8) ? SpriteType::Normal
: SpriteType::Invalid
;
658 /* Inline sprites are not supported for container version >= 2. */
659 if (file
.GetContainerVersion() >= 2) return false;
662 if (type
== SpriteType::Invalid
) return false;
664 if (load_index
>= MAX_SPRITES
) {
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?");
672 type
= SpriteType::MapGen
;
675 SpriteCache
*sc
= AllocateSpriteCache(load_index
);
677 sc
->file_pos
= file_pos
;
680 sc
->id
= file_sprite_id
;
683 sc
->control_flags
= control_flags
;
689 void DupSprite(SpriteID old_spr
, SpriteID new_spr
)
691 SpriteCache
*scnew
= AllocateSpriteCache(new_spr
); // may reallocate: so put it first
692 SpriteCache
*scold
= GetSpriteCache(old_spr
);
694 scnew
->file
= scold
->file
;
695 scnew
->file_pos
= scold
->file_pos
;
696 scnew
->ptr
= nullptr;
697 scnew
->id
= scold
->id
;
698 scnew
->type
= scold
->type
;
699 scnew
->warned
= false;
700 scnew
->control_flags
= scold
->control_flags
;
704 * S_FREE_MASK is used to mask-out lower bits of MemBlock::size
705 * If they are non-zero, the block is free.
706 * S_FREE_MASK has to ensure MemBlock is correctly aligned -
707 * it means 8B (S_FREE_MASK == 7) on 64bit systems!
709 static const size_t S_FREE_MASK
= sizeof(size_t) - 1;
711 /* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
712 static_assert(sizeof(MemBlock
) == sizeof(size_t));
713 /* make sure it's a power of two */
714 static_assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
716 static inline MemBlock
*NextBlock(MemBlock
*block
)
718 return (MemBlock
*)((uint8_t*)block
+ (block
->size
& ~S_FREE_MASK
));
721 static size_t GetSpriteCacheUsage()
726 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
727 if (!(s
->size
& S_FREE_MASK
)) tot_size
+= s
->size
;
734 void IncreaseSpriteLRU()
736 /* Increase all LRU values */
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
++) {
743 SpriteCache
*sc
= GetSpriteCache(i
);
744 if (sc
->ptr
!= nullptr) {
747 } else if (sc
->lru
!= -32768) {
752 _sprite_lru_counter
= 0;
755 /* Compact sprite cache every now and then. */
756 if (++_compact_cache_counter
>= 740) {
757 CompactSpriteCache();
758 _compact_cache_counter
= 0;
763 * Called when holes in the sprite cache should be removed.
764 * That is accomplished by moving the cached data.
766 static void CompactSpriteCache()
770 Debug(sprite
, 3, "Compacting sprite cache, inuse={}", GetSpriteCacheUsage());
772 for (s
= _spritecache_ptr
; s
->size
!= 0;) {
773 if (s
->size
& S_FREE_MASK
) {
774 MemBlock
*next
= NextBlock(s
);
778 /* Since free blocks are automatically coalesced, this should hold true. */
779 assert(!(next
->size
& S_FREE_MASK
));
781 /* If the next block is the sentinel block, we can safely return */
782 if (next
->size
== 0) break;
784 /* Locate the sprite belonging to the next pointer. */
785 for (i
= 0; GetSpriteCache(i
)->ptr
!= next
->data
; i
++) {
786 assert(i
!= _spritecache_items
);
789 GetSpriteCache(i
)->ptr
= s
->data
; // Adjust sprite array entry
790 /* Swap this and the next block */
792 memmove(s
, next
, next
->size
);
796 /* Coalesce free blocks */
797 while (NextBlock(s
)->size
& S_FREE_MASK
) {
798 s
->size
+= NextBlock(s
)->size
& ~S_FREE_MASK
;
807 * Delete a single entry from the sprite cache.
808 * @param item Entry to delete.
810 static void DeleteEntryFromSpriteCache(uint item
)
812 /* Mark the block as free (the block must be in use) */
813 MemBlock
*s
= (MemBlock
*)GetSpriteCache(item
)->ptr
- 1;
814 assert(!(s
->size
& S_FREE_MASK
));
815 s
->size
|= S_FREE_MASK
;
816 GetSpriteCache(item
)->ptr
= nullptr;
818 /* And coalesce adjacent free blocks */
819 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
820 if (s
->size
& S_FREE_MASK
) {
821 while (NextBlock(s
)->size
& S_FREE_MASK
) {
822 s
->size
+= NextBlock(s
)->size
& ~S_FREE_MASK
;
828 static void DeleteEntryFromSpriteCache()
830 uint best
= UINT_MAX
;
833 Debug(sprite
, 3, "DeleteEntryFromSpriteCache, inuse={}", GetSpriteCacheUsage());
836 for (SpriteID i
= 0; i
!= _spritecache_items
; i
++) {
837 SpriteCache
*sc
= GetSpriteCache(i
);
838 if (sc
->type
!= SpriteType::Recolour
&& sc
->ptr
!= nullptr && sc
->lru
< cur_lru
) {
844 /* Display an error message and die, in case we found no sprite at all.
845 * This shouldn't really happen, unless all sprites are locked. */
846 if (best
== UINT_MAX
) FatalError("Out of sprite memory");
848 DeleteEntryFromSpriteCache(best
);
851 void *CacheSpriteAllocator::AllocatePtr(size_t mem_req
)
853 mem_req
+= sizeof(MemBlock
);
855 /* Align this to correct boundary. This also makes sure at least one
856 * bit is not used, so we can use it for other things. */
857 mem_req
= Align(mem_req
, S_FREE_MASK
+ 1);
862 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
863 if (s
->size
& S_FREE_MASK
) {
864 size_t cur_size
= s
->size
& ~S_FREE_MASK
;
866 /* Is the block exactly the size we need or
867 * big enough for an additional free block? */
868 if (cur_size
== mem_req
||
869 cur_size
>= mem_req
+ sizeof(MemBlock
)) {
870 /* Set size and in use */
873 /* Do we need to inject a free block too? */
874 if (cur_size
!= mem_req
) {
875 NextBlock(s
)->size
= (cur_size
- mem_req
) | S_FREE_MASK
;
883 /* Reached sentinel, but no block found yet. Delete some old entry. */
884 DeleteEntryFromSpriteCache();
889 * Sprite allocator simply using malloc.
891 void *SimpleSpriteAllocator::AllocatePtr(size_t size
)
893 return MallocT
<uint8_t>(size
);
896 void *UniquePtrSpriteAllocator::AllocatePtr(size_t size
)
898 this->data
= std::make_unique
<uint8_t[]>(size
);
899 return this->data
.get();
903 * Handles the case when a sprite of different type is requested than is present in the SpriteCache.
904 * For SpriteType::Font sprites, it is normal. In other cases, default sprite is loaded instead.
905 * @param sprite ID of loaded sprite
906 * @param requested requested sprite type
907 * @param sc the currently known sprite cache for the requested sprite
908 * @return fallback sprite
909 * @note this function will do UserError() in the case the fallback sprite isn't available
911 static void *HandleInvalidSpriteRequest(SpriteID sprite
, SpriteType requested
, SpriteCache
*sc
, SpriteAllocator
*allocator
)
913 static const char * const sprite_types
[] = {
914 "normal", // SpriteType::Normal
915 "map generator", // SpriteType::MapGen
916 "character", // SpriteType::Font
917 "recolour", // SpriteType::Recolour
920 SpriteType available
= sc
->type
;
921 if (requested
== SpriteType::Font
&& available
== SpriteType::Normal
) {
922 if (sc
->ptr
== nullptr) sc
->type
= SpriteType::Font
;
923 return GetRawSprite(sprite
, sc
->type
, allocator
);
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
)]);
931 case SpriteType::Normal
:
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?");
934 case SpriteType::Font
:
935 return GetRawSprite(SPR_IMG_QUERY
, SpriteType::Normal
, allocator
);
936 case SpriteType::Recolour
:
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?");
938 return GetRawSprite(PALETTE_TO_DARK_BLUE
, SpriteType::Recolour
, allocator
);
939 case SpriteType::MapGen
:
940 /* this shouldn't happen, overriding of SpriteType::MapGen sprites is checked in LoadNextSprite()
941 * (the only case the check fails is when these sprites weren't even loaded...) */
948 * Reads a sprite (from disk or sprite cache).
949 * If the sprite is not available or of wrong type, a fallback sprite is returned.
950 * @param sprite Sprite to read.
951 * @param type Expected sprite type.
952 * @param allocator Allocator function to use. Set to nullptr to use the usual sprite cache.
953 * @param encoder Sprite encoder to use. Set to nullptr to use the currently active blitter.
954 * @return Sprite raw data
956 void *GetRawSprite(SpriteID sprite
, SpriteType type
, SpriteAllocator
*allocator
, SpriteEncoder
*encoder
)
958 assert(type
!= SpriteType::MapGen
|| IsMapgenSpriteID(sprite
));
959 assert(type
< SpriteType::Invalid
);
961 if (!SpriteExists(sprite
)) {
962 Debug(sprite
, 1, "Tried to load non-existing sprite #{}. Probable cause: Wrong/missing NewGRFs", sprite
);
964 /* SPR_IMG_QUERY is a BIG FAT RED ? */
965 sprite
= SPR_IMG_QUERY
;
968 SpriteCache
*sc
= GetSpriteCache(sprite
);
970 if (sc
->type
!= type
) return HandleInvalidSpriteRequest(sprite
, type
, sc
, allocator
);
972 if (allocator
== nullptr && encoder
== nullptr) {
973 /* Load sprite into/from spritecache */
974 CacheSpriteAllocator cache_allocator
;
977 sc
->lru
= ++_sprite_lru_counter
;
979 /* Load the sprite, if it is not loaded, yet */
980 if (sc
->ptr
== nullptr) sc
->ptr
= ReadSprite(sc
, sprite
, type
, cache_allocator
, nullptr);
984 /* Do not use the spritecache, but a different allocator. */
985 return ReadSprite(sc
, sprite
, type
, *allocator
, encoder
);
990 static void GfxInitSpriteCache()
992 /* initialize sprite cache heap */
993 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
994 uint target_size
= (bpp
> 0 ? _sprite_cache_size
* bpp
/ 8 : 1) * 1024 * 1024;
996 /* Remember 'target_size' from the previous allocation attempt, so we do not try to reach the target_size multiple times in case of failure. */
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
;
1006 /* Try to allocate 50% more to make sure we do not allocate almost all available. */
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) {
1010 /* Allocation succeeded, but we wanted less. */
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");
1016 /* Try again to allocate half. */
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
);
1027 ScheduleErrorMessage(msg
);
1031 /* A big free block */
1032 _spritecache_ptr
->size
= (_allocated_sprite_cache_size
- sizeof(MemBlock
)) | S_FREE_MASK
;
1033 /* Sentinel block (identified by size == 0) */
1034 NextBlock(_spritecache_ptr
)->size
= 0;
1037 void GfxInitSpriteMem()
1039 GfxInitSpriteCache();
1041 /* Reset the spritecache 'pool' */
1043 _spritecache_items
= 0;
1044 _spritecache
= nullptr;
1046 _compact_cache_counter
= 0;
1047 _sprite_files
.clear();
1051 * Remove all encoded sprites from the sprite cache without
1052 * discarding sprite location information.
1054 void GfxClearSpriteCache()
1056 /* Clear sprite ptr for all cached items */
1057 for (uint i
= 0; i
!= _spritecache_items
; i
++) {
1058 SpriteCache
*sc
= GetSpriteCache(i
);
1059 if (sc
->type
!= SpriteType::Recolour
&& sc
->ptr
!= nullptr) DeleteEntryFromSpriteCache(i
);
1062 VideoDriver::GetInstance()->ClearSystemSprites();
1066 * Remove all encoded font sprites from the sprite cache without
1067 * discarding sprite location information.
1069 void GfxClearFontSpriteCache()
1071 /* Clear sprite ptr for all cached font items */
1072 for (uint i
= 0; i
!= _spritecache_items
; i
++) {
1073 SpriteCache
*sc
= GetSpriteCache(i
);
1074 if (sc
->type
== SpriteType::Font
&& sc
->ptr
!= nullptr) DeleteEntryFromSpriteCache(i
);
1078 /* static */ ReusableBuffer
<SpriteLoader::CommonPixel
> SpriteLoader::Sprite::buffer
[ZOOM_LVL_END
];