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 SpriteLoader::CommonPixel
*src_data
= MallocT
<SpriteLoader::CommonPixel
>(sprite_size
);
294 MemCpyT(src_data
, sprite
->data
, sprite_size
);
295 sprite
->AllocateData(zoom
, static_cast<size_t>(width
) * height
);
297 /* Copy with padding to destination. */
298 SpriteLoader::CommonPixel
*src
= src_data
;
299 SpriteLoader::CommonPixel
*data
= sprite
->data
;
300 for (uint y
= 0; y
< height
; y
++) {
301 if (y
< pad_top
|| pad_bottom
+ y
>= height
) {
302 /* Top/bottom padding. */
303 MemSetT(data
, 0, width
);
308 MemSetT(data
, 0, pad_left
);
313 MemCpyT(data
, src
, sprite
->width
);
314 src
+= sprite
->width
;
315 data
+= sprite
->width
;
319 MemSetT(data
, 0, pad_right
);
326 /* Update sprite size. */
327 sprite
->width
= width
;
328 sprite
->height
= height
;
329 sprite
->x_offs
-= pad_left
;
330 sprite
->y_offs
-= pad_top
;
335 static bool PadSprites(SpriteLoader::SpriteCollection
&sprite
, uint8_t sprite_avail
, SpriteEncoder
*encoder
)
337 /* Get minimum top left corner coordinates. */
338 int min_xoffs
= INT32_MAX
;
339 int min_yoffs
= INT32_MAX
;
340 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
341 if (HasBit(sprite_avail
, zoom
)) {
342 min_xoffs
= std::min(min_xoffs
, ScaleByZoom(sprite
[zoom
].x_offs
, zoom
));
343 min_yoffs
= std::min(min_yoffs
, ScaleByZoom(sprite
[zoom
].y_offs
, zoom
));
347 /* Get maximum dimensions taking necessary padding at the top left into account. */
348 int max_width
= INT32_MIN
;
349 int max_height
= INT32_MIN
;
350 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
351 if (HasBit(sprite_avail
, zoom
)) {
352 max_width
= std::max(max_width
, ScaleByZoom(sprite
[zoom
].width
+ sprite
[zoom
].x_offs
- UnScaleByZoom(min_xoffs
, zoom
), zoom
));
353 max_height
= std::max(max_height
, ScaleByZoom(sprite
[zoom
].height
+ sprite
[zoom
].y_offs
- UnScaleByZoom(min_yoffs
, zoom
), zoom
));
357 /* Align height and width if required to match the needs of the sprite encoder. */
358 uint align
= encoder
->GetSpriteAlignment();
360 max_width
= Align(max_width
, align
);
361 max_height
= Align(max_height
, align
);
364 /* Pad sprites where needed. */
365 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
366 if (HasBit(sprite_avail
, zoom
)) {
367 /* Scaling the sprite dimensions in the blitter is done with rounding up,
368 * so a negative padding here is not an error. */
369 int pad_left
= std::max(0, sprite
[zoom
].x_offs
- UnScaleByZoom(min_xoffs
, zoom
));
370 int pad_top
= std::max(0, sprite
[zoom
].y_offs
- UnScaleByZoom(min_yoffs
, zoom
));
371 int pad_right
= std::max(0, UnScaleByZoom(max_width
, zoom
) - sprite
[zoom
].width
- pad_left
);
372 int pad_bottom
= std::max(0, UnScaleByZoom(max_height
, zoom
) - sprite
[zoom
].height
- pad_top
);
374 if (pad_left
> 0 || pad_right
> 0 || pad_top
> 0 || pad_bottom
> 0) {
375 if (!PadSingleSprite(&sprite
[zoom
], zoom
, pad_left
, pad_top
, pad_right
, pad_bottom
)) return false;
383 static bool ResizeSprites(SpriteLoader::SpriteCollection
&sprite
, uint8_t sprite_avail
, SpriteEncoder
*encoder
)
385 /* Create a fully zoomed image if it does not exist */
386 ZoomLevel first_avail
= static_cast<ZoomLevel
>(FindFirstBit(sprite_avail
));
387 if (first_avail
!= ZOOM_LVL_MIN
) {
388 if (!ResizeSpriteIn(sprite
, first_avail
, ZOOM_LVL_MIN
)) return false;
389 SetBit(sprite_avail
, ZOOM_LVL_MIN
);
392 /* Pad sprites to make sizes match. */
393 if (!PadSprites(sprite
, sprite_avail
, encoder
)) return false;
395 /* Create other missing zoom levels */
396 for (ZoomLevel zoom
= ZOOM_LVL_BEGIN
; zoom
!= ZOOM_LVL_END
; zoom
++) {
397 if (zoom
== ZOOM_LVL_MIN
) continue;
399 if (HasBit(sprite_avail
, zoom
)) {
400 /* Check that size and offsets match the fully zoomed image. */
401 assert(sprite
[zoom
].width
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].width
, zoom
));
402 assert(sprite
[zoom
].height
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].height
, zoom
));
403 assert(sprite
[zoom
].x_offs
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].x_offs
, zoom
));
404 assert(sprite
[zoom
].y_offs
== UnScaleByZoom(sprite
[ZOOM_LVL_MIN
].y_offs
, zoom
));
407 /* Zoom level is not available, or unusable, so create it */
408 if (!HasBit(sprite_avail
, zoom
)) ResizeSpriteOut(sprite
, zoom
);
411 /* Upscale to desired sprite_min_zoom if provided sprite only had zoomed in versions. */
412 if (first_avail
< _settings_client
.gui
.sprite_zoom_min
) {
413 if (_settings_client
.gui
.sprite_zoom_min
>= ZOOM_LVL_NORMAL
) ResizeSpriteIn(sprite
, ZOOM_LVL_NORMAL
, ZOOM_LVL_IN_2X
);
414 if (_settings_client
.gui
.sprite_zoom_min
>= ZOOM_LVL_IN_2X
) ResizeSpriteIn(sprite
, ZOOM_LVL_IN_2X
, ZOOM_LVL_IN_4X
);
421 * Load a recolour sprite into memory.
422 * @param file GRF we're reading from.
423 * @param num Size of the sprite in the GRF.
424 * @return Sprite data.
426 static void *ReadRecolourSprite(SpriteFile
&file
, uint num
, SpriteAllocator
&allocator
)
428 /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
429 * number of recolour sprites that are 17 bytes that only exist in DOS
430 * GRFs which are the same as 257 byte recolour sprites, but with the last
431 * 240 bytes zeroed. */
432 static const uint RECOLOUR_SPRITE_SIZE
= 257;
433 uint8_t *dest
= allocator
.Allocate
<uint8_t>(std::max(RECOLOUR_SPRITE_SIZE
, num
));
435 if (file
.NeedsPaletteRemap()) {
436 uint8_t *dest_tmp
= new uint8_t[std::max(RECOLOUR_SPRITE_SIZE
, num
)];
438 /* Only a few recolour sprites are less than 257 bytes */
439 if (num
< RECOLOUR_SPRITE_SIZE
) memset(dest_tmp
, 0, RECOLOUR_SPRITE_SIZE
);
440 file
.ReadBlock(dest_tmp
, num
);
442 /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
443 for (uint i
= 1; i
< RECOLOUR_SPRITE_SIZE
; i
++) {
444 dest
[i
] = _palmap_w2d
[dest_tmp
[_palmap_d2w
[i
- 1] + 1]];
448 file
.ReadBlock(dest
, num
);
455 * Read a sprite from disk.
456 * @param sc Location of sprite.
457 * @param id Sprite number.
458 * @param sprite_type Type of sprite.
459 * @param allocator Allocator function to use.
460 * @param encoder Sprite encoder to use.
461 * @return Read sprite data.
463 static void *ReadSprite(const SpriteCache
*sc
, SpriteID id
, SpriteType sprite_type
, SpriteAllocator
&allocator
, SpriteEncoder
*encoder
)
465 /* Use current blitter if no other sprite encoder is given. */
466 if (encoder
== nullptr) encoder
= BlitterFactory::GetCurrentBlitter();
468 SpriteFile
&file
= *sc
->file
;
469 size_t file_pos
= sc
->file_pos
;
471 assert(sprite_type
!= SpriteType::Recolour
);
472 assert(IsMapgenSpriteID(id
) == (sprite_type
== SpriteType::MapGen
));
473 assert(sc
->type
== sprite_type
);
475 Debug(sprite
, 9, "Load sprite {}", id
);
477 SpriteLoader::SpriteCollection sprite
;
478 uint8_t sprite_avail
= 0;
479 sprite
[ZOOM_LVL_MIN
].type
= sprite_type
;
481 SpriteLoaderGrf
sprite_loader(file
.GetContainerVersion());
482 if (sprite_type
!= SpriteType::MapGen
&& encoder
->Is32BppSupported()) {
483 /* Try for 32bpp sprites first. */
484 sprite_avail
= sprite_loader
.LoadSprite(sprite
, file
, file_pos
, sprite_type
, true, sc
->control_flags
);
486 if (sprite_avail
== 0) {
487 sprite_avail
= sprite_loader
.LoadSprite(sprite
, file
, file_pos
, sprite_type
, false, sc
->control_flags
);
490 if (sprite_avail
== 0) {
491 if (sprite_type
== SpriteType::MapGen
) return nullptr;
492 if (id
== SPR_IMG_QUERY
) UserError("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
493 return (void*)GetRawSprite(SPR_IMG_QUERY
, SpriteType::Normal
, &allocator
, encoder
);
496 if (sprite_type
== SpriteType::MapGen
) {
497 /* Ugly hack to work around the problem that the old landscape
498 * generator assumes that those sprites are stored uncompressed in
499 * the memory, and they are only read directly by the code, never
500 * send to the blitter. So do not send it to the blitter (which will
501 * result in a data array in the format the blitter likes most), but
502 * extract the data directly and store that as sprite.
503 * Ugly: yes. Other solution: no. Blame the original author or
504 * something ;) The image should really have been a data-stream
505 * (so type = 0xFF basically). */
506 uint num
= sprite
[ZOOM_LVL_MIN
].width
* sprite
[ZOOM_LVL_MIN
].height
;
508 Sprite
*s
= allocator
.Allocate
<Sprite
>(sizeof(*s
) + num
);
509 s
->width
= sprite
[ZOOM_LVL_MIN
].width
;
510 s
->height
= sprite
[ZOOM_LVL_MIN
].height
;
511 s
->x_offs
= sprite
[ZOOM_LVL_MIN
].x_offs
;
512 s
->y_offs
= sprite
[ZOOM_LVL_MIN
].y_offs
;
514 SpriteLoader::CommonPixel
*src
= sprite
[ZOOM_LVL_MIN
].data
;
515 uint8_t *dest
= s
->data
;
524 if (!ResizeSprites(sprite
, sprite_avail
, encoder
)) {
525 if (id
== SPR_IMG_QUERY
) UserError("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
526 return (void*)GetRawSprite(SPR_IMG_QUERY
, SpriteType::Normal
, &allocator
, encoder
);
529 if (sprite
[ZOOM_LVL_MIN
].type
== SpriteType::Font
&& _font_zoom
!= ZOOM_LVL_MIN
) {
530 /* Make ZOOM_LVL_MIN be ZOOM_LVL_GUI */
531 sprite
[ZOOM_LVL_MIN
].width
= sprite
[_font_zoom
].width
;
532 sprite
[ZOOM_LVL_MIN
].height
= sprite
[_font_zoom
].height
;
533 sprite
[ZOOM_LVL_MIN
].x_offs
= sprite
[_font_zoom
].x_offs
;
534 sprite
[ZOOM_LVL_MIN
].y_offs
= sprite
[_font_zoom
].y_offs
;
535 sprite
[ZOOM_LVL_MIN
].data
= sprite
[_font_zoom
].data
;
536 sprite
[ZOOM_LVL_MIN
].colours
= sprite
[_font_zoom
].colours
;
539 return encoder
->Encode(sprite
, allocator
);
542 struct GrfSpriteOffset
{
544 uint8_t control_flags
;
547 /** Map from sprite numbers to position in the GRF file. */
548 static std::map
<uint32_t, GrfSpriteOffset
> _grf_sprite_offsets
;
551 * Get the file offset for a specific sprite in the sprite section of a GRF.
552 * @param id ID of the sprite to look up.
553 * @return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present.
555 size_t GetGRFSpriteOffset(uint32_t id
)
557 return _grf_sprite_offsets
.find(id
) != _grf_sprite_offsets
.end() ? _grf_sprite_offsets
[id
].file_pos
: SIZE_MAX
;
561 * Parse the sprite section of GRFs.
562 * @param container_version Container version of the GRF we're currently processing.
564 void ReadGRFSpriteOffsets(SpriteFile
&file
)
566 _grf_sprite_offsets
.clear();
568 if (file
.GetContainerVersion() >= 2) {
569 /* Seek to sprite section of the GRF. */
570 size_t data_offset
= file
.ReadDword();
571 size_t old_pos
= file
.GetPos();
572 file
.SeekTo(data_offset
, SEEK_CUR
);
574 GrfSpriteOffset offset
= { 0, 0 };
576 /* Loop over all sprite section entries and store the file
577 * offset for each newly encountered ID. */
578 uint32_t id
, prev_id
= 0;
579 while ((id
= file
.ReadDword()) != 0) {
581 _grf_sprite_offsets
[prev_id
] = offset
;
582 offset
.file_pos
= file
.GetPos() - 4;
583 offset
.control_flags
= 0;
586 uint length
= file
.ReadDword();
588 uint8_t colour
= file
.ReadByte() & SCC_MASK
;
591 uint8_t zoom
= file
.ReadByte();
593 if (colour
!= 0 && zoom
== 0) { // ZOOM_LVL_NORMAL (normal zoom)
594 SetBit(offset
.control_flags
, (colour
!= SCC_PAL
) ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP
: SCCF_ALLOW_ZOOM_MIN_1X_PAL
);
595 SetBit(offset
.control_flags
, (colour
!= SCC_PAL
) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP
: SCCF_ALLOW_ZOOM_MIN_2X_PAL
);
597 if (colour
!= 0 && zoom
== 2) { // ZOOM_LVL_IN_2X (2x zoomed in)
598 SetBit(offset
.control_flags
, (colour
!= SCC_PAL
) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP
: SCCF_ALLOW_ZOOM_MIN_2X_PAL
);
602 file
.SkipBytes(length
);
604 if (prev_id
!= 0) _grf_sprite_offsets
[prev_id
] = offset
;
606 /* Continue processing the data section. */
607 file
.SeekTo(old_pos
, SEEK_SET
);
613 * Load a real or recolour sprite.
614 * @param load_index Global sprite index.
615 * @param file GRF to load from.
616 * @param file_sprite_id Sprite number in the GRF.
617 * @param container_version Container version of the GRF.
618 * @return True if a valid sprite was loaded, false on any error.
620 bool LoadNextSprite(int load_index
, SpriteFile
&file
, uint file_sprite_id
)
622 size_t file_pos
= file
.GetPos();
624 /* Read sprite header. */
625 uint32_t num
= file
.GetContainerVersion() >= 2 ? file
.ReadDword() : file
.ReadWord();
626 if (num
== 0) return false;
627 uint8_t grf_type
= file
.ReadByte();
630 void *data
= nullptr;
631 uint8_t control_flags
= 0;
632 if (grf_type
== 0xFF) {
633 /* Some NewGRF files have "empty" pseudo-sprites which are 1
634 * byte long. Catch these so the sprites won't be displayed. */
639 type
= SpriteType::Recolour
;
640 CacheSpriteAllocator allocator
;
641 data
= ReadRecolourSprite(file
, num
, allocator
);
642 } else if (file
.GetContainerVersion() >= 2 && grf_type
== 0xFD) {
644 /* Invalid sprite section include, ignore. */
648 /* It is not an error if no sprite with the provided ID is found in the sprite section. */
649 auto iter
= _grf_sprite_offsets
.find(file
.ReadDword());
650 if (iter
!= _grf_sprite_offsets
.end()) {
651 file_pos
= iter
->second
.file_pos
;
652 control_flags
= iter
->second
.control_flags
;
656 type
= SpriteType::Normal
;
659 type
= SkipSpriteData(file
, grf_type
, num
- 8) ? SpriteType::Normal
: SpriteType::Invalid
;
660 /* Inline sprites are not supported for container version >= 2. */
661 if (file
.GetContainerVersion() >= 2) return false;
664 if (type
== SpriteType::Invalid
) return false;
666 if (load_index
>= MAX_SPRITES
) {
667 UserError("Tried to load too many sprites (#{}; max {})", load_index
, MAX_SPRITES
);
670 bool is_mapgen
= IsMapgenSpriteID(load_index
);
673 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?");
674 type
= SpriteType::MapGen
;
677 SpriteCache
*sc
= AllocateSpriteCache(load_index
);
679 sc
->file_pos
= file_pos
;
682 sc
->id
= file_sprite_id
;
685 sc
->control_flags
= control_flags
;
691 void DupSprite(SpriteID old_spr
, SpriteID new_spr
)
693 SpriteCache
*scnew
= AllocateSpriteCache(new_spr
); // may reallocate: so put it first
694 SpriteCache
*scold
= GetSpriteCache(old_spr
);
696 scnew
->file
= scold
->file
;
697 scnew
->file_pos
= scold
->file_pos
;
698 scnew
->ptr
= nullptr;
699 scnew
->id
= scold
->id
;
700 scnew
->type
= scold
->type
;
701 scnew
->warned
= false;
705 * S_FREE_MASK is used to mask-out lower bits of MemBlock::size
706 * If they are non-zero, the block is free.
707 * S_FREE_MASK has to ensure MemBlock is correctly aligned -
708 * it means 8B (S_FREE_MASK == 7) on 64bit systems!
710 static const size_t S_FREE_MASK
= sizeof(size_t) - 1;
712 /* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
713 static_assert(sizeof(MemBlock
) == sizeof(size_t));
714 /* make sure it's a power of two */
715 static_assert((sizeof(size_t) & (sizeof(size_t) - 1)) == 0);
717 static inline MemBlock
*NextBlock(MemBlock
*block
)
719 return (MemBlock
*)((uint8_t*)block
+ (block
->size
& ~S_FREE_MASK
));
722 static size_t GetSpriteCacheUsage()
727 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
728 if (!(s
->size
& S_FREE_MASK
)) tot_size
+= s
->size
;
735 void IncreaseSpriteLRU()
737 /* Increase all LRU values */
738 if (_sprite_lru_counter
> 16384) {
741 Debug(sprite
, 5, "Fixing lru {}, inuse={}", _sprite_lru_counter
, GetSpriteCacheUsage());
743 for (i
= 0; i
!= _spritecache_items
; i
++) {
744 SpriteCache
*sc
= GetSpriteCache(i
);
745 if (sc
->ptr
!= nullptr) {
748 } else if (sc
->lru
!= -32768) {
753 _sprite_lru_counter
= 0;
756 /* Compact sprite cache every now and then. */
757 if (++_compact_cache_counter
>= 740) {
758 CompactSpriteCache();
759 _compact_cache_counter
= 0;
764 * Called when holes in the sprite cache should be removed.
765 * That is accomplished by moving the cached data.
767 static void CompactSpriteCache()
771 Debug(sprite
, 3, "Compacting sprite cache, inuse={}", GetSpriteCacheUsage());
773 for (s
= _spritecache_ptr
; s
->size
!= 0;) {
774 if (s
->size
& S_FREE_MASK
) {
775 MemBlock
*next
= NextBlock(s
);
779 /* Since free blocks are automatically coalesced, this should hold true. */
780 assert(!(next
->size
& S_FREE_MASK
));
782 /* If the next block is the sentinel block, we can safely return */
783 if (next
->size
== 0) break;
785 /* Locate the sprite belonging to the next pointer. */
786 for (i
= 0; GetSpriteCache(i
)->ptr
!= next
->data
; i
++) {
787 assert(i
!= _spritecache_items
);
790 GetSpriteCache(i
)->ptr
= s
->data
; // Adjust sprite array entry
791 /* Swap this and the next block */
793 memmove(s
, next
, next
->size
);
797 /* Coalesce free blocks */
798 while (NextBlock(s
)->size
& S_FREE_MASK
) {
799 s
->size
+= NextBlock(s
)->size
& ~S_FREE_MASK
;
808 * Delete a single entry from the sprite cache.
809 * @param item Entry to delete.
811 static void DeleteEntryFromSpriteCache(uint item
)
813 /* Mark the block as free (the block must be in use) */
814 MemBlock
*s
= (MemBlock
*)GetSpriteCache(item
)->ptr
- 1;
815 assert(!(s
->size
& S_FREE_MASK
));
816 s
->size
|= S_FREE_MASK
;
817 GetSpriteCache(item
)->ptr
= nullptr;
819 /* And coalesce adjacent free blocks */
820 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
821 if (s
->size
& S_FREE_MASK
) {
822 while (NextBlock(s
)->size
& S_FREE_MASK
) {
823 s
->size
+= NextBlock(s
)->size
& ~S_FREE_MASK
;
829 static void DeleteEntryFromSpriteCache()
831 uint best
= UINT_MAX
;
834 Debug(sprite
, 3, "DeleteEntryFromSpriteCache, inuse={}", GetSpriteCacheUsage());
837 for (SpriteID i
= 0; i
!= _spritecache_items
; i
++) {
838 SpriteCache
*sc
= GetSpriteCache(i
);
839 if (sc
->type
!= SpriteType::Recolour
&& sc
->ptr
!= nullptr && sc
->lru
< cur_lru
) {
845 /* Display an error message and die, in case we found no sprite at all.
846 * This shouldn't really happen, unless all sprites are locked. */
847 if (best
== UINT_MAX
) FatalError("Out of sprite memory");
849 DeleteEntryFromSpriteCache(best
);
852 void *CacheSpriteAllocator::AllocatePtr(size_t mem_req
)
854 mem_req
+= sizeof(MemBlock
);
856 /* Align this to correct boundary. This also makes sure at least one
857 * bit is not used, so we can use it for other things. */
858 mem_req
= Align(mem_req
, S_FREE_MASK
+ 1);
863 for (s
= _spritecache_ptr
; s
->size
!= 0; s
= NextBlock(s
)) {
864 if (s
->size
& S_FREE_MASK
) {
865 size_t cur_size
= s
->size
& ~S_FREE_MASK
;
867 /* Is the block exactly the size we need or
868 * big enough for an additional free block? */
869 if (cur_size
== mem_req
||
870 cur_size
>= mem_req
+ sizeof(MemBlock
)) {
871 /* Set size and in use */
874 /* Do we need to inject a free block too? */
875 if (cur_size
!= mem_req
) {
876 NextBlock(s
)->size
= (cur_size
- mem_req
) | S_FREE_MASK
;
884 /* Reached sentinel, but no block found yet. Delete some old entry. */
885 DeleteEntryFromSpriteCache();
890 * Sprite allocator simply using malloc.
892 void *SimpleSpriteAllocator::AllocatePtr(size_t size
)
894 return MallocT
<uint8_t>(size
);
897 void *UniquePtrSpriteAllocator::AllocatePtr(size_t size
)
899 this->data
= std::make_unique
<uint8_t[]>(size
);
900 return this->data
.get();
904 * Handles the case when a sprite of different type is requested than is present in the SpriteCache.
905 * For SpriteType::Font sprites, it is normal. In other cases, default sprite is loaded instead.
906 * @param sprite ID of loaded sprite
907 * @param requested requested sprite type
908 * @param sc the currently known sprite cache for the requested sprite
909 * @return fallback sprite
910 * @note this function will do UserError() in the case the fallback sprite isn't available
912 static void *HandleInvalidSpriteRequest(SpriteID sprite
, SpriteType requested
, SpriteCache
*sc
, SpriteAllocator
*allocator
)
914 static const char * const sprite_types
[] = {
915 "normal", // SpriteType::Normal
916 "map generator", // SpriteType::MapGen
917 "character", // SpriteType::Font
918 "recolour", // SpriteType::Recolour
921 SpriteType available
= sc
->type
;
922 if (requested
== SpriteType::Font
&& available
== SpriteType::Normal
) {
923 if (sc
->ptr
== nullptr) sc
->type
= SpriteType::Font
;
924 return GetRawSprite(sprite
, sc
->type
, allocator
);
927 uint8_t warning_level
= sc
->warned
? 6 : 0;
929 Debug(sprite
, warning_level
, "Tried to load {} sprite #{} as a {} sprite. Probable cause: NewGRF interference", sprite_types
[static_cast<uint8_t>(available
)], sprite
, sprite_types
[static_cast<uint8_t>(requested
)]);
932 case SpriteType::Normal
:
933 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?");
935 case SpriteType::Font
:
936 return GetRawSprite(SPR_IMG_QUERY
, SpriteType::Normal
, allocator
);
937 case SpriteType::Recolour
:
938 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?");
939 return GetRawSprite(PALETTE_TO_DARK_BLUE
, SpriteType::Recolour
, allocator
);
940 case SpriteType::MapGen
:
941 /* this shouldn't happen, overriding of SpriteType::MapGen sprites is checked in LoadNextSprite()
942 * (the only case the check fails is when these sprites weren't even loaded...) */
949 * Reads a sprite (from disk or sprite cache).
950 * If the sprite is not available or of wrong type, a fallback sprite is returned.
951 * @param sprite Sprite to read.
952 * @param type Expected sprite type.
953 * @param allocator Allocator function to use. Set to nullptr to use the usual sprite cache.
954 * @param encoder Sprite encoder to use. Set to nullptr to use the currently active blitter.
955 * @return Sprite raw data
957 void *GetRawSprite(SpriteID sprite
, SpriteType type
, SpriteAllocator
*allocator
, SpriteEncoder
*encoder
)
959 assert(type
!= SpriteType::MapGen
|| IsMapgenSpriteID(sprite
));
960 assert(type
< SpriteType::Invalid
);
962 if (!SpriteExists(sprite
)) {
963 Debug(sprite
, 1, "Tried to load non-existing sprite #{}. Probable cause: Wrong/missing NewGRFs", sprite
);
965 /* SPR_IMG_QUERY is a BIG FAT RED ? */
966 sprite
= SPR_IMG_QUERY
;
969 SpriteCache
*sc
= GetSpriteCache(sprite
);
971 if (sc
->type
!= type
) return HandleInvalidSpriteRequest(sprite
, type
, sc
, allocator
);
973 if (allocator
== nullptr && encoder
== nullptr) {
974 /* Load sprite into/from spritecache */
975 CacheSpriteAllocator cache_allocator
;
978 sc
->lru
= ++_sprite_lru_counter
;
980 /* Load the sprite, if it is not loaded, yet */
981 if (sc
->ptr
== nullptr) sc
->ptr
= ReadSprite(sc
, sprite
, type
, cache_allocator
, nullptr);
985 /* Do not use the spritecache, but a different allocator. */
986 return ReadSprite(sc
, sprite
, type
, *allocator
, encoder
);
991 static void GfxInitSpriteCache()
993 /* initialize sprite cache heap */
994 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
995 uint target_size
= (bpp
> 0 ? _sprite_cache_size
* bpp
/ 8 : 1) * 1024 * 1024;
997 /* Remember 'target_size' from the previous allocation attempt, so we do not try to reach the target_size multiple times in case of failure. */
998 static uint last_alloc_attempt
= 0;
1000 if (_spritecache_ptr
== nullptr || (_allocated_sprite_cache_size
!= target_size
&& target_size
!= last_alloc_attempt
)) {
1001 delete[] reinterpret_cast<uint8_t *>(_spritecache_ptr
);
1003 last_alloc_attempt
= target_size
;
1004 _allocated_sprite_cache_size
= target_size
;
1007 /* Try to allocate 50% more to make sure we do not allocate almost all available. */
1008 _spritecache_ptr
= reinterpret_cast<MemBlock
*>(new(std::nothrow
) uint8_t[_allocated_sprite_cache_size
+ _allocated_sprite_cache_size
/ 2]);
1010 if (_spritecache_ptr
!= nullptr) {
1011 /* Allocation succeeded, but we wanted less. */
1012 delete[] reinterpret_cast<uint8_t *>(_spritecache_ptr
);
1013 _spritecache_ptr
= reinterpret_cast<MemBlock
*>(new uint8_t[_allocated_sprite_cache_size
]);
1014 } else if (_allocated_sprite_cache_size
< 2 * 1024 * 1024) {
1015 UserError("Cannot allocate spritecache");
1017 /* Try again to allocate half. */
1018 _allocated_sprite_cache_size
>>= 1;
1020 } while (_spritecache_ptr
== nullptr);
1022 if (_allocated_sprite_cache_size
!= target_size
) {
1023 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);
1025 ErrorMessageData
msg(STR_CONFIG_ERROR_OUT_OF_MEMORY
, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG
);
1026 msg
.SetDParam(0, target_size
);
1027 msg
.SetDParam(1, _allocated_sprite_cache_size
);
1028 ScheduleErrorMessage(msg
);
1032 /* A big free block */
1033 _spritecache_ptr
->size
= (_allocated_sprite_cache_size
- sizeof(MemBlock
)) | S_FREE_MASK
;
1034 /* Sentinel block (identified by size == 0) */
1035 NextBlock(_spritecache_ptr
)->size
= 0;
1038 void GfxInitSpriteMem()
1040 GfxInitSpriteCache();
1042 /* Reset the spritecache 'pool' */
1044 _spritecache_items
= 0;
1045 _spritecache
= nullptr;
1047 _compact_cache_counter
= 0;
1048 _sprite_files
.clear();
1052 * Remove all encoded sprites from the sprite cache without
1053 * discarding sprite location information.
1055 void GfxClearSpriteCache()
1057 /* Clear sprite ptr for all cached items */
1058 for (uint i
= 0; i
!= _spritecache_items
; i
++) {
1059 SpriteCache
*sc
= GetSpriteCache(i
);
1060 if (sc
->type
!= SpriteType::Recolour
&& sc
->ptr
!= nullptr) DeleteEntryFromSpriteCache(i
);
1063 VideoDriver::GetInstance()->ClearSystemSprites();
1067 * Remove all encoded font sprites from the sprite cache without
1068 * discarding sprite location information.
1070 void GfxClearFontSpriteCache()
1072 /* Clear sprite ptr for all cached font items */
1073 for (uint i
= 0; i
!= _spritecache_items
; i
++) {
1074 SpriteCache
*sc
= GetSpriteCache(i
);
1075 if (sc
->type
== SpriteType::Font
&& sc
->ptr
!= nullptr) DeleteEntryFromSpriteCache(i
);
1079 /* static */ ReusableBuffer
<SpriteLoader::CommonPixel
> SpriteLoader::Sprite::buffer
[ZOOM_LVL_END
];