2009-08-21 20:21:05 +00:00
/*
* This file is part of OpenTTD .
* 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.
* 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 .
* 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/>.
*/
2008-05-06 15:11:33 +00:00
/** @file spritecache.cpp Caching of sprites. */
2007-04-04 01:35:16 +00:00
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
2008-08-31 10:50:05 +00:00
# include "fileio_func.h"
2007-06-11 11:50:49 +00:00
# include "spriteloader/grf.hpp"
2008-09-02 18:45:15 +00:00
# include "gfx_func.h"
2013-03-03 13:00:06 +00:00
# include "error.h"
2012-02-04 13:29:13 +00:00
# include "zoom_func.h"
# include "settings_type.h"
2007-06-17 20:30:28 +00:00
# include "blitter/factory.hpp"
2018-08-19 19:52:01 +00:00
# include "core/alloc_func.hpp"
2009-01-11 22:28:30 +00:00
# include "core/math_func.hpp"
2012-02-04 13:29:13 +00:00
# include "core/mem_func.hpp"
2019-01-14 18:51:28 +00:00
# include "scope_info.h"
2004-08-09 17:04:08 +00:00
2008-01-13 01:21:35 +00:00
# include "table/sprites.h"
2013-03-03 13:00:06 +00:00
# include "table/strings.h"
2011-05-04 17:12:37 +00:00
# include "table/palette_convert.h"
2008-01-13 01:21:35 +00:00
2017-02-08 00:18:09 +00:00
# include "3rdparty/cpp-btree/btree_map.h"
2018-08-19 01:48:46 +00:00
# include <vector>
2018-08-19 19:52:01 +00:00
# include <algorithm>
2018-08-19 01:48:46 +00:00
2014-04-23 20:13:33 +00:00
# include "safeguards.h"
2007-06-11 11:50:49 +00:00
/* Default of 4MB spritecache */
uint _sprite_cache_size = 4 ;
2004-08-09 17:04:08 +00:00
2018-08-19 19:52:01 +00:00
static size_t _spritecache_bytes_used = 0 ;
PACK_N ( class SpriteDataBuffer {
void * ptr = nullptr ;
uint32 size = 0 ;
public :
void * GetPtr ( ) { return this - > ptr ; }
uint32 GetSize ( ) { return this - > size ; }
void Allocate ( uint32 size )
{
_spritecache_bytes_used - = this - > size ;
free ( this - > ptr ) ;
this - > ptr = MallocT < byte > ( size ) ;
this - > size = size ;
_spritecache_bytes_used + = this - > size ;
}
void Clear ( )
{
_spritecache_bytes_used - = this - > size ;
free ( this - > ptr ) ;
this - > ptr = nullptr ;
this - > size = 0 ;
}
SpriteDataBuffer ( ) { }
SpriteDataBuffer ( uint32 size ) { this - > Allocate ( size ) ; }
SpriteDataBuffer ( SpriteDataBuffer & & other ) noexcept
{
* this = std : : move ( other ) ;
}
~ SpriteDataBuffer ( )
{
this - > Clear ( ) ;
}
SpriteDataBuffer & operator = ( SpriteDataBuffer & & other ) noexcept
{
this - > Clear ( ) ;
this - > ptr = other . ptr ;
this - > size = other . size ;
other . ptr = nullptr ;
other . size = 0 ;
return * this ;
}
} , 4 ) ;
PACK_N ( struct SpriteCache {
2008-05-27 21:41:00 +00:00
size_t file_pos ;
2018-08-19 19:52:01 +00:00
SpriteDataBuffer buffer ;
2008-07-04 14:45:51 +00:00
uint32 id ;
2018-08-19 19:52:01 +00:00
uint32 lru ;
2008-01-21 23:55:57 +00:00
uint16 file_slot ;
2019-01-23 17:32:54 +00:00
/**
* Bits 6 - 0 : SpriteType 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 .
* Bit 7 : bool warned True iff the user has been warned about incorrect use of this sprite .
*/
byte type_field ;
2018-08-19 19:52:01 +00:00
byte container_ver ; ///< Container version of the GRF the sprite is from.
2007-01-03 14:42:08 +00:00
2018-08-19 19:52:01 +00:00
void * GetPtr ( ) { return this - > buffer . GetPtr ( ) ; }
2019-01-23 17:32:54 +00:00
SpriteType GetType ( ) const { return ( SpriteType ) GB ( this - > type_field , 0 , 7 ) ; }
void SetType ( SpriteType type ) { SB ( this - > type_field , 0 , 7 , type ) ; }
bool GetWarned ( ) const { return GB ( this - > type_field , 7 , 1 ) ; }
void SetWarned ( bool warned ) { SB ( this - > type_field , 7 , 1 , warned ? 1 : 0 ) ; }
2018-08-19 19:52:01 +00:00
} , 4 ) ;
assert_compile ( sizeof ( SpriteCache ) < = 32 ) ;
2007-01-03 14:42:08 +00:00
2018-08-19 19:52:01 +00:00
static std : : vector < SpriteCache > _spritecache ;
static SpriteDataBuffer _last_sprite_allocation ;
2007-01-03 14:42:08 +00:00
static inline SpriteCache * GetSpriteCache ( uint index )
{
return & _spritecache [ index ] ;
}
2009-01-11 22:28:30 +00:00
static inline bool IsMapgenSpriteID ( SpriteID sprite )
{
return IsInsideMM ( sprite , 4845 , 4882 ) ;
}
2007-01-03 14:42:08 +00:00
static SpriteCache * AllocateSpriteCache ( uint index )
{
2018-08-19 01:48:46 +00:00
if ( index > = _spritecache . size ( ) ) {
_spritecache . resize ( index + 1 ) ;
2007-01-03 14:42:08 +00:00
}
return GetSpriteCache ( index ) ;
}
2018-08-19 19:52:01 +00:00
static uint32 _sprite_lru_counter ;
2004-08-09 17:04:08 +00:00
2012-02-04 13:28:52 +00:00
static void * AllocSprite ( size_t mem_req ) ;
2004-08-09 17:04:08 +00:00
2008-08-30 09:46:52 +00:00
/**
* Skip the given amount of sprite graphics data .
* @ param type the type of sprite ( compressed etc )
* @ param num the amount of sprites to skip
2009-03-19 17:58:25 +00:00
* @ return true if the data could be correctly skipped .
2008-08-30 09:46:52 +00:00
*/
2009-03-19 17:58:25 +00:00
bool SkipSpriteData ( byte type , uint16 num )
2004-08-09 17:04:08 +00:00
{
if ( type & 2 ) {
FioSkipBytes ( num ) ;
2005-02-11 14:33:43 +00:00
} else {
while ( num > 0 ) {
int8 i = FioReadByte ( ) ;
if ( i > = 0 ) {
2008-08-30 12:14:56 +00:00
int size = ( i = = 0 ) ? 0x80 : i ;
2009-03-19 17:58:25 +00:00
if ( size > num ) return false ;
2008-08-30 12:14:56 +00:00
num - = size ;
FioSkipBytes ( size ) ;
2005-02-11 14:33:43 +00:00
} else {
i = - ( i > > 3 ) ;
num - = i ;
FioReadByte ( ) ;
}
2004-08-09 17:04:08 +00:00
}
}
2009-03-19 17:58:25 +00:00
return true ;
2008-08-30 09:46:52 +00:00
}
2006-04-16 11:26:23 +00:00
/* Check if the given Sprite ID exists */
bool SpriteExists ( SpriteID id )
{
2018-08-19 01:48:46 +00:00
if ( id > = _spritecache . size ( ) ) return false ;
2013-10-06 19:40:40 +00:00
2006-04-16 11:26:23 +00:00
/* Special case for Sprite ID zero -- its position is also 0... */
2007-01-03 14:42:08 +00:00
if ( id = = 0 ) return true ;
2007-09-13 22:48:11 +00:00
return ! ( GetSpriteCache ( id ) - > file_pos = = 0 & & GetSpriteCache ( id ) - > file_slot = = 0 ) ;
2006-04-16 11:26:23 +00:00
}
2010-04-25 16:27:30 +00:00
/**
* Get the sprite type of a given sprite .
* @ param sprite The sprite to look at .
* @ return the type of sprite .
*/
SpriteType GetSpriteType ( SpriteID sprite )
{
if ( ! SpriteExists ( sprite ) ) return ST_INVALID ;
2019-01-23 17:32:54 +00:00
return GetSpriteCache ( sprite ) - > GetType ( ) ;
2010-04-25 16:27:30 +00:00
}
/**
* Get the ( FIOS ) file slot of a given sprite .
* @ param sprite The sprite to look at .
* @ return the FIOS file slot
*/
uint GetOriginFileSlot ( SpriteID sprite )
{
if ( ! SpriteExists ( sprite ) ) return 0 ;
return GetSpriteCache ( sprite ) - > file_slot ;
}
2020-01-26 12:45:51 +00:00
/**
* Get the GRF - local sprite id of a given sprite .
* @ param sprite The sprite to look at .
* @ return The GRF - local sprite id .
*/
uint32 GetSpriteLocalID ( SpriteID sprite )
{
if ( ! SpriteExists ( sprite ) ) return 0 ;
return GetSpriteCache ( sprite ) - > id ;
}
2017-01-14 18:30:26 +00:00
/**
* Count the sprites which originate from a specific file slot in a range of SpriteIDs .
* @ param file_slot FIOS file slot .
* @ param begin First sprite in range .
* @ param end First sprite not in range .
* @ return Number of sprites .
*/
uint GetSpriteCountForSlot ( uint file_slot , SpriteID begin , SpriteID end )
{
uint count = 0 ;
for ( SpriteID i = begin ; i ! = end ; i + + ) {
if ( SpriteExists ( i ) ) {
SpriteCache * sc = GetSpriteCache ( i ) ;
if ( sc - > file_slot = = file_slot ) count + + ;
}
}
return count ;
}
2010-04-25 16:27:30 +00:00
/**
* Get a reasonable ( upper bound ) estimate of the maximum
* SpriteID used in OpenTTD ; there will be no sprites with
2018-08-19 01:48:46 +00:00
* a higher SpriteID .
2010-04-25 16:27:30 +00:00
* @ note It ' s actually the number of spritecache items .
* @ return maximum SpriteID
*/
uint GetMaxSpriteID ( )
{
2018-08-19 01:48:46 +00:00
return _spritecache . size ( ) ;
2010-04-25 16:27:30 +00:00
}
2012-02-04 13:29:13 +00:00
static bool ResizeSpriteIn ( SpriteLoader : : Sprite * sprite , ZoomLevel src , ZoomLevel tgt )
{
2014-01-02 11:04:37 +00:00
uint8 scaled_1 = ScaleByZoom ( 1 , ( ZoomLevel ) ( src - tgt ) ) ;
2012-02-04 13:29:13 +00:00
/* Check for possible memory overflow. */
if ( sprite [ src ] . width * scaled_1 > UINT16_MAX | | sprite [ src ] . height * scaled_1 > UINT16_MAX ) return false ;
sprite [ tgt ] . width = sprite [ src ] . width * scaled_1 ;
sprite [ tgt ] . height = sprite [ src ] . height * scaled_1 ;
sprite [ tgt ] . x_offs = sprite [ src ] . x_offs * scaled_1 ;
sprite [ tgt ] . y_offs = sprite [ src ] . y_offs * scaled_1 ;
sprite [ tgt ] . AllocateData ( tgt , sprite [ tgt ] . width * sprite [ tgt ] . height ) ;
SpriteLoader : : CommonPixel * dst = sprite [ tgt ] . data ;
for ( int y = 0 ; y < sprite [ tgt ] . height ; y + + ) {
const SpriteLoader : : CommonPixel * src_ln = & sprite [ src ] . data [ y / scaled_1 * sprite [ src ] . width ] ;
for ( int x = 0 ; x < sprite [ tgt ] . width ; x + + ) {
* dst = src_ln [ x / scaled_1 ] ;
dst + + ;
}
}
return true ;
}
static void ResizeSpriteOut ( SpriteLoader : : Sprite * sprite , ZoomLevel zoom )
{
/* Algorithm based on 32bpp_Optimized::ResizeSprite() */
sprite [ zoom ] . width = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . width , zoom ) ;
sprite [ zoom ] . height = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . height , zoom ) ;
sprite [ zoom ] . x_offs = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . x_offs , zoom ) ;
sprite [ zoom ] . y_offs = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . y_offs , zoom ) ;
sprite [ zoom ] . AllocateData ( zoom , sprite [ zoom ] . height * sprite [ zoom ] . width ) ;
SpriteLoader : : CommonPixel * dst = sprite [ zoom ] . data ;
const SpriteLoader : : CommonPixel * src = sprite [ zoom - 1 ] . data ;
const SpriteLoader : : CommonPixel * src_end = src + sprite [ zoom - 1 ] . height * sprite [ zoom - 1 ] . width ;
for ( uint y = 0 ; y < sprite [ zoom ] . height ; y + + ) {
2012-02-25 17:18:17 +00:00
const SpriteLoader : : CommonPixel * src_ln = src + sprite [ zoom - 1 ] . width ;
assert ( src_ln < = src_end ) ;
2012-02-04 13:29:13 +00:00
for ( uint x = 0 ; x < sprite [ zoom ] . width ; x + + ) {
2012-02-25 17:18:17 +00:00
assert ( src < src_ln ) ;
if ( src + 1 ! = src_ln & & ( src + 1 ) - > a ! = 0 ) {
* dst = * ( src + 1 ) ;
} else {
* dst = * src ;
}
2012-02-04 13:29:13 +00:00
dst + + ;
src + = 2 ;
}
2012-02-25 17:18:17 +00:00
src = src_ln + sprite [ zoom - 1 ] . width ;
2012-02-04 13:29:13 +00:00
}
}
static bool PadSingleSprite ( SpriteLoader : : Sprite * sprite , ZoomLevel zoom , uint pad_left , uint pad_top , uint pad_right , uint pad_bottom )
{
uint width = sprite - > width + pad_left + pad_right ;
uint height = sprite - > height + pad_top + pad_bottom ;
if ( width > UINT16_MAX | | height > UINT16_MAX ) return false ;
/* Copy source data and reallocate sprite memory. */
SpriteLoader : : CommonPixel * src_data = MallocT < SpriteLoader : : CommonPixel > ( sprite - > width * sprite - > height ) ;
MemCpyT ( src_data , sprite - > data , sprite - > width * sprite - > height ) ;
sprite - > AllocateData ( zoom , width * height ) ;
/* Copy with padding to destination. */
SpriteLoader : : CommonPixel * src = src_data ;
SpriteLoader : : CommonPixel * data = sprite - > data ;
for ( uint y = 0 ; y < height ; y + + ) {
if ( y < pad_top | | pad_bottom + y > = height ) {
/* Top/bottom padding. */
MemSetT ( data , 0 , width ) ;
data + = width ;
} else {
if ( pad_left > 0 ) {
/* Pad left. */
MemSetT ( data , 0 , pad_left ) ;
data + = pad_left ;
}
/* Copy pixels. */
MemCpyT ( data , src , sprite - > width ) ;
src + = sprite - > width ;
data + = sprite - > width ;
if ( pad_right > 0 ) {
/* Pad right. */
MemSetT ( data , 0 , pad_right ) ;
data + = pad_right ;
}
}
}
free ( src_data ) ;
/* Update sprite size. */
sprite - > width = width ;
sprite - > height = height ;
sprite - > x_offs - = pad_left ;
sprite - > y_offs - = pad_top ;
return true ;
}
2015-11-22 23:31:51 +00:00
static bool PadSprites ( SpriteLoader : : Sprite * sprite , unsigned int sprite_avail )
2012-02-04 13:29:13 +00:00
{
2012-02-23 12:13:30 +00:00
/* Get minimum top left corner coordinates. */
int min_xoffs = INT32_MAX ;
int min_yoffs = INT32_MAX ;
for ( ZoomLevel zoom = ZOOM_LVL_BEGIN ; zoom ! = ZOOM_LVL_END ; zoom + + ) {
2012-02-04 13:29:13 +00:00
if ( HasBit ( sprite_avail , zoom ) ) {
2012-02-23 12:13:30 +00:00
min_xoffs = min ( min_xoffs , ScaleByZoom ( sprite [ zoom ] . x_offs , zoom ) ) ;
min_yoffs = min ( min_yoffs , ScaleByZoom ( sprite [ zoom ] . y_offs , zoom ) ) ;
2012-02-04 13:29:13 +00:00
}
}
2012-02-23 12:13:30 +00:00
/* Get maximum dimensions taking necessary padding at the top left into account. */
int max_width = INT32_MIN ;
int max_height = INT32_MIN ;
2012-02-04 13:29:13 +00:00
for ( ZoomLevel zoom = ZOOM_LVL_BEGIN ; zoom ! = ZOOM_LVL_END ; zoom + + ) {
if ( HasBit ( sprite_avail , zoom ) ) {
2012-02-23 12:13:30 +00:00
max_width = max ( max_width , ScaleByZoom ( sprite [ zoom ] . width + sprite [ zoom ] . x_offs - UnScaleByZoom ( min_xoffs , zoom ) , zoom ) ) ;
max_height = max ( max_height , ScaleByZoom ( sprite [ zoom ] . height + sprite [ zoom ] . y_offs - UnScaleByZoom ( min_yoffs , zoom ) , zoom ) ) ;
}
}
2012-02-04 13:29:13 +00:00
2012-02-23 12:13:30 +00:00
/* Pad sprites where needed. */
for ( ZoomLevel zoom = ZOOM_LVL_BEGIN ; zoom ! = ZOOM_LVL_END ; zoom + + ) {
if ( HasBit ( sprite_avail , zoom ) ) {
/* Scaling the sprite dimensions in the blitter is done with rounding up,
* so a negative padding here is not an error . */
int pad_left = max ( 0 , sprite [ zoom ] . x_offs - UnScaleByZoom ( min_xoffs , zoom ) ) ;
int pad_top = max ( 0 , sprite [ zoom ] . y_offs - UnScaleByZoom ( min_yoffs , zoom ) ) ;
int pad_right = max ( 0 , UnScaleByZoom ( max_width , zoom ) - sprite [ zoom ] . width - pad_left ) ;
int pad_bottom = max ( 0 , UnScaleByZoom ( max_height , zoom ) - sprite [ zoom ] . height - pad_top ) ;
if ( pad_left > 0 | | pad_right > 0 | | pad_top > 0 | | pad_bottom > 0 ) {
2012-02-04 13:29:13 +00:00
if ( ! PadSingleSprite ( & sprite [ zoom ] , zoom , pad_left , pad_top , pad_right , pad_bottom ) ) return false ;
}
}
}
return true ;
}
2015-11-22 23:31:51 +00:00
static bool ResizeSprites ( SpriteLoader : : Sprite * sprite , unsigned int sprite_avail , uint32 file_slot , uint32 file_pos )
2012-02-04 13:29:13 +00:00
{
/* Create a fully zoomed image if it does not exist */
ZoomLevel first_avail = static_cast < ZoomLevel > ( FIND_FIRST_BIT ( sprite_avail ) ) ;
if ( first_avail ! = ZOOM_LVL_NORMAL ) {
if ( ! ResizeSpriteIn ( sprite , first_avail , ZOOM_LVL_NORMAL ) ) return false ;
2012-02-23 12:13:30 +00:00
SetBit ( sprite_avail , ZOOM_LVL_NORMAL ) ;
2012-02-04 13:29:13 +00:00
}
/* Pad sprites to make sizes match. */
if ( ! PadSprites ( sprite , sprite_avail ) ) return false ;
/* Create other missing zoom levels */
for ( ZoomLevel zoom = ZOOM_LVL_OUT_2X ; zoom ! = ZOOM_LVL_END ; zoom + + ) {
if ( HasBit ( sprite_avail , zoom ) ) {
/* Check that size and offsets match the fully zoomed image. */
assert ( sprite [ zoom ] . width = = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . width , zoom ) ) ;
assert ( sprite [ zoom ] . height = = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . height , zoom ) ) ;
assert ( sprite [ zoom ] . x_offs = = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . x_offs , zoom ) ) ;
assert ( sprite [ zoom ] . y_offs = = UnScaleByZoom ( sprite [ ZOOM_LVL_NORMAL ] . y_offs , zoom ) ) ;
}
/* Zoom level is not available, or unusable, so create it */
if ( ! HasBit ( sprite_avail , zoom ) ) ResizeSpriteOut ( sprite , zoom ) ;
}
return true ;
}
2012-02-04 13:28:52 +00:00
/**
* Load a recolour sprite into memory .
* @ param file_slot GRF we ' re reading from .
* @ param num Size of the sprite in the GRF .
* @ return Sprite data .
*/
static void * ReadRecolourSprite ( uint16 file_slot , uint num )
{
/* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
* number of recolour sprites that are 17 bytes that only exist in DOS
* GRFs which are the same as 257 byte recolour sprites , but with the last
* 240 bytes zeroed . */
static const uint RECOLOUR_SPRITE_SIZE = 257 ;
byte * dest = ( byte * ) AllocSprite ( max ( RECOLOUR_SPRITE_SIZE , num ) ) ;
if ( _palette_remap_grf [ file_slot ] ) {
byte * dest_tmp = AllocaM ( byte , max ( RECOLOUR_SPRITE_SIZE , num ) ) ;
/* Only a few recolour sprites are less than 257 bytes */
if ( num < RECOLOUR_SPRITE_SIZE ) memset ( dest_tmp , 0 , RECOLOUR_SPRITE_SIZE ) ;
FioReadBlock ( dest_tmp , num ) ;
/* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
for ( uint i = 1 ; i < RECOLOUR_SPRITE_SIZE ; i + + ) {
dest [ i ] = _palmap_w2d [ dest_tmp [ _palmap_d2w [ i - 1 ] + 1 ] ] ;
}
} else {
FioReadBlock ( dest , num ) ;
}
return dest ;
}
2011-01-14 16:28:23 +00:00
/**
* Read a sprite from disk .
* @ param sc Location of sprite .
* @ param id Sprite number .
* @ param sprite_type Type of sprite .
* @ param allocator Allocator function to use .
* @ return Read sprite data .
*/
static void * ReadSprite ( const SpriteCache * sc , SpriteID id , SpriteType sprite_type , AllocatorProc * allocator )
2004-08-09 17:04:08 +00:00
{
2018-11-26 01:01:50 +00:00
uint file_slot = sc - > file_slot ;
2008-05-27 21:41:00 +00:00
size_t file_pos = sc - > file_pos ;
2005-07-05 19:54:35 +00:00
2019-01-14 18:51:28 +00:00
SCOPE_INFO_FMT ( [ & ] , " ReadSprite: pos: " PRINTF_SIZE " , id: %u, slot: %u (%s), type: %u " , file_pos , id , file_slot , FioGetFilename ( file_slot ) , sprite_type ) ;
2012-02-04 13:28:52 +00:00
assert ( sprite_type ! = ST_RECOLOUR ) ;
2009-01-11 22:28:30 +00:00
assert ( IsMapgenSpriteID ( id ) = = ( sprite_type = = ST_MAPGEN ) ) ;
2019-01-23 17:32:54 +00:00
assert ( sc - > GetType ( ) = = sprite_type ) ;
2007-02-08 14:02:12 +00:00
2009-01-11 22:28:30 +00:00
DEBUG ( sprite , 9 , " Load sprite %d " , id ) ;
2005-02-17 15:53:47 +00:00
2012-02-04 13:29:13 +00:00
SpriteLoader : : Sprite sprite [ ZOOM_LVL_COUNT ] ;
uint8 sprite_avail = 0 ;
sprite [ ZOOM_LVL_NORMAL ] . type = sprite_type ;
2012-02-04 13:29:04 +00:00
SpriteLoaderGrf sprite_loader ( sc - > container_ver ) ;
2014-01-02 22:41:58 +00:00
if ( sprite_type ! = ST_MAPGEN & & BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) = = 32 ) {
2012-02-04 22:18:57 +00:00
/* Try for 32bpp sprites first. */
sprite_avail = sprite_loader . LoadSprite ( sprite , file_slot , file_pos , sprite_type , true ) ;
}
if ( sprite_avail = = 0 ) {
sprite_avail = sprite_loader . LoadSprite ( sprite , file_slot , file_pos , sprite_type , false ) ;
}
2004-09-08 18:05:49 +00:00
2012-02-04 13:29:13 +00:00
if ( sprite_avail = = 0 ) {
2019-04-10 21:07:06 +00:00
if ( sprite_type = = ST_MAPGEN ) return nullptr ;
2008-11-23 13:42:05 +00:00
if ( id = = SPR_IMG_QUERY ) usererror ( " Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do? " ) ;
2011-01-14 16:36:34 +00:00
return ( void * ) GetRawSprite ( SPR_IMG_QUERY , ST_NORMAL , allocator ) ;
2008-11-23 13:42:05 +00:00
}
2012-02-04 13:28:56 +00:00
if ( sprite_type = = ST_MAPGEN ) {
/* Ugly hack to work around the problem that the old landscape
* generator assumes that those sprites are stored uncompressed in
* the memory , and they are only read directly by the code , never
* send to the blitter . So do not send it to the blitter ( which will
* result in a data array in the format the blitter likes most ) , but
* extract the data directly and store that as sprite .
* Ugly : yes . Other solution : no . Blame the original author or
* something ; ) The image should really have been a data - stream
2013-01-08 22:46:42 +00:00
* ( so type = 0xFF basically ) . */
2012-02-04 13:29:13 +00:00
uint num = sprite [ ZOOM_LVL_NORMAL ] . width * sprite [ ZOOM_LVL_NORMAL ] . height ;
2012-02-04 13:28:56 +00:00
Sprite * s = ( Sprite * ) allocator ( sizeof ( * s ) + num ) ;
2012-02-04 13:29:13 +00:00
s - > width = sprite [ ZOOM_LVL_NORMAL ] . width ;
s - > height = sprite [ ZOOM_LVL_NORMAL ] . height ;
s - > x_offs = sprite [ ZOOM_LVL_NORMAL ] . x_offs ;
s - > y_offs = sprite [ ZOOM_LVL_NORMAL ] . y_offs ;
2012-02-04 13:28:56 +00:00
2012-02-04 13:29:13 +00:00
SpriteLoader : : CommonPixel * src = sprite [ ZOOM_LVL_NORMAL ] . data ;
2012-02-04 13:28:56 +00:00
byte * dest = s - > data ;
while ( num - - > 0 ) {
* dest + + = src - > m ;
src + + ;
}
return s ;
}
2014-10-12 20:43:25 +00:00
if ( ! ResizeSprites ( sprite , sprite_avail , file_slot , sc - > id ) ) {
if ( id = = SPR_IMG_QUERY ) usererror ( " Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do? " ) ;
return ( void * ) GetRawSprite ( SPR_IMG_QUERY , ST_NORMAL , allocator ) ;
2012-02-04 13:29:13 +00:00
}
2014-10-12 20:43:25 +00:00
2019-02-23 08:27:46 +00:00
if ( sprite - > type = = ST_FONT & & ZOOM_LVL_FONT ! = ZOOM_LVL_NORMAL ) {
/* Make ZOOM_LVL_NORMAL be ZOOM_LVL_FONT */
sprite [ ZOOM_LVL_NORMAL ] . width = sprite [ ZOOM_LVL_FONT ] . width ;
sprite [ ZOOM_LVL_NORMAL ] . height = sprite [ ZOOM_LVL_FONT ] . height ;
sprite [ ZOOM_LVL_NORMAL ] . x_offs = sprite [ ZOOM_LVL_FONT ] . x_offs ;
sprite [ ZOOM_LVL_NORMAL ] . y_offs = sprite [ ZOOM_LVL_FONT ] . y_offs ;
sprite [ ZOOM_LVL_NORMAL ] . data = sprite [ ZOOM_LVL_FONT ] . data ;
2014-10-12 20:43:25 +00:00
}
2014-01-02 22:41:58 +00:00
return BlitterFactory : : GetCurrentBlitter ( ) - > Encode ( sprite , allocator ) ;
2004-08-09 17:04:08 +00:00
}
2013-06-27 21:18:56 +00:00
/** Map from sprite numbers to position in the GRF file. */
2017-02-08 00:18:09 +00:00
static btree : : btree_map < uint32 , size_t > _grf_sprite_offsets ;
2012-02-04 13:29:04 +00:00
/**
* Get the file offset for a specific sprite in the sprite section of a GRF .
* @ param id ID of the sprite to look up .
* @ return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present .
*/
size_t GetGRFSpriteOffset ( uint32 id )
{
2017-02-08 00:18:09 +00:00
auto iter = _grf_sprite_offsets . find ( id ) ;
return iter ! = _grf_sprite_offsets . end ( ) ? iter - > second : SIZE_MAX ;
2012-02-04 13:29:04 +00:00
}
/**
* Parse the sprite section of GRFs .
* @ param container_version Container version of the GRF we ' re currently processing .
*/
void ReadGRFSpriteOffsets ( byte container_version )
{
_grf_sprite_offsets . clear ( ) ;
if ( container_version > = 2 ) {
/* Seek to sprite section of the GRF. */
size_t data_offset = FioReadDword ( ) ;
size_t old_pos = FioGetPos ( ) ;
FioSeekTo ( data_offset , SEEK_CUR ) ;
/* Loop over all sprite section entries and store the file
* offset for each newly encountered ID . */
uint32 id , prev_id = 0 ;
while ( ( id = FioReadDword ( ) ) ! = 0 ) {
if ( id ! = prev_id ) _grf_sprite_offsets [ id ] = FioGetPos ( ) - 4 ;
prev_id = id ;
FioSkipBytes ( FioReadDword ( ) ) ;
}
/* Continue processing the data section. */
FioSeekTo ( old_pos , SEEK_SET ) ;
}
}
/**
* Load a real or recolour sprite .
* @ param load_index Global sprite index .
* @ param file_slot GRF to load from .
* @ param file_sprite_id Sprite number in the GRF .
* @ param container_version Container version of the GRF .
* @ return True if a valid sprite was loaded , false on any error .
*/
2019-01-14 18:45:57 +00:00
bool LoadNextSprite ( int load_index , uint file_slot , uint file_sprite_id , byte container_version )
2004-08-09 17:04:08 +00:00
{
2008-05-27 21:41:00 +00:00
size_t file_pos = FioGetPos ( ) ;
2004-08-09 17:04:08 +00:00
2019-01-14 18:51:28 +00:00
SCOPE_INFO_FMT ( [ & ] , " LoadNextSprite: pos: " PRINTF_SIZE " , slot: %u (%s), load_index: %d, file_sprite_id: %u, container_ver: %u " , file_pos , file_slot , FioGetFilename ( file_slot ) , load_index , file_sprite_id , container_version ) ;
2012-02-04 13:28:52 +00:00
/* Read sprite header. */
2012-02-04 13:29:04 +00:00
uint32 num = container_version > = 2 ? FioReadDword ( ) : FioReadWord ( ) ;
2012-02-04 13:28:52 +00:00
if ( num = = 0 ) return false ;
byte grf_type = FioReadByte ( ) ;
SpriteType type ;
2019-04-10 21:07:06 +00:00
void * data = nullptr ;
2012-02-04 13:28:52 +00:00
if ( grf_type = = 0xFF ) {
/* Some NewGRF files have "empty" pseudo-sprites which are 1
* byte long . Catch these so the sprites won ' t be displayed . */
if ( num = = 1 ) {
FioReadByte ( ) ;
return false ;
}
type = ST_RECOLOUR ;
data = ReadRecolourSprite ( file_slot , num ) ;
2012-02-04 13:29:04 +00:00
} else if ( container_version > = 2 & & grf_type = = 0xFD ) {
if ( num ! = 4 ) {
/* Invalid sprite section include, ignore. */
FioSkipBytes ( num ) ;
return false ;
}
/* It is not an error if no sprite with the provided ID is found in the sprite section. */
file_pos = GetGRFSpriteOffset ( FioReadDword ( ) ) ;
type = ST_NORMAL ;
2012-02-04 13:28:52 +00:00
} else {
FioSkipBytes ( 7 ) ;
type = SkipSpriteData ( grf_type , num - 8 ) ? ST_NORMAL : ST_INVALID ;
2012-02-04 13:29:04 +00:00
/* Inline sprites are not supported for container version >= 2. */
if ( container_version > = 2 ) return false ;
2012-02-04 13:28:52 +00:00
}
2009-01-11 22:28:30 +00:00
if ( type = = ST_INVALID ) return false ;
2004-11-12 18:47:19 +00:00
2006-04-20 05:57:47 +00:00
if ( load_index > = MAX_SPRITES ) {
2008-06-05 20:54:52 +00:00
usererror ( " Tried to load too many sprites (#%d; max %d) " , load_index , MAX_SPRITES ) ;
2006-04-20 05:57:47 +00:00
}
2009-01-11 22:28:30 +00:00
bool is_mapgen = IsMapgenSpriteID ( load_index ) ;
if ( is_mapgen ) {
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? " ) ;
type = ST_MAPGEN ;
}
SpriteCache * sc = AllocateSpriteCache ( load_index ) ;
2007-09-13 18:22:34 +00:00
sc - > file_slot = file_slot ;
2007-01-03 14:42:08 +00:00
sc - > file_pos = file_pos ;
2018-08-19 19:52:01 +00:00
if ( data ! = nullptr ) {
assert ( data = = _last_sprite_allocation . GetPtr ( ) ) ;
sc - > buffer = std : : move ( _last_sprite_allocation ) ;
}
2007-01-03 14:42:08 +00:00
sc - > lru = 0 ;
2007-06-14 14:31:48 +00:00
sc - > id = file_sprite_id ;
2019-01-23 17:32:54 +00:00
sc - > SetType ( type ) ;
sc - > SetWarned ( false ) ;
2012-02-04 13:29:04 +00:00
sc - > container_ver = container_version ;
2007-06-13 16:21:11 +00:00
2004-08-09 17:04:08 +00:00
return true ;
}
2005-09-10 08:17:30 +00:00
2007-01-10 18:56:51 +00:00
void DupSprite ( SpriteID old_spr , SpriteID new_spr )
2005-09-10 08:17:30 +00:00
{
2008-01-29 00:29:28 +00:00
SpriteCache * scnew = AllocateSpriteCache ( new_spr ) ; // may reallocate: so put it first
2007-01-10 18:56:51 +00:00
SpriteCache * scold = GetSpriteCache ( old_spr ) ;
2007-01-03 14:42:08 +00:00
2007-09-13 18:22:34 +00:00
scnew - > file_slot = scold - > file_slot ;
2007-01-03 14:42:08 +00:00
scnew - > file_pos = scold - > file_pos ;
2007-06-13 16:21:11 +00:00
scnew - > id = scold - > id ;
2019-01-23 17:32:54 +00:00
scnew - > SetType ( scold - > GetType ( ) ) ;
scnew - > SetWarned ( false ) ;
2012-02-04 13:29:04 +00:00
scnew - > container_ver = scold - > container_ver ;
2005-09-10 08:17:30 +00:00
}
2008-05-27 21:41:00 +00:00
static size_t GetSpriteCacheUsage ( )
2004-08-09 17:04:08 +00:00
{
2018-08-19 19:52:01 +00:00
return _spritecache_bytes_used ;
2004-08-09 17:04:08 +00:00
}
2010-08-01 19:22:34 +00:00
/**
2018-08-19 19:52:01 +00:00
* Delete a single entry from the sprite cache .
* @ param item Entry to delete .
2010-08-01 19:44:49 +00:00
*/
2018-08-19 19:52:01 +00:00
static void DeleteEntryFromSpriteCache ( uint item )
2004-08-09 17:04:08 +00:00
{
2018-08-19 19:52:01 +00:00
GetSpriteCache ( item ) - > buffer . Clear ( ) ;
}
2004-08-09 17:04:08 +00:00
2018-08-19 19:52:01 +00:00
static void DeleteEntriesFromSpriteCache ( size_t target )
{
const size_t initial_in_use = GetSpriteCacheUsage ( ) ;
2004-09-08 18:05:49 +00:00
2018-08-19 19:52:01 +00:00
struct SpriteInfo {
uint32 lru ;
SpriteID id ;
uint32 size ;
2004-09-08 18:05:49 +00:00
2018-09-12 07:46:29 +00:00
bool operator < ( const SpriteInfo & other ) const
2018-08-19 19:52:01 +00:00
{
return this - > lru < other . lru ;
}
} ;
std : : vector < SpriteInfo > candidates ;
size_t candidate_bytes = 0 ;
2004-08-09 17:04:08 +00:00
2018-08-19 19:52:01 +00:00
auto push = [ & ] ( SpriteInfo info ) {
candidates . push_back ( info ) ;
std : : push_heap ( candidates . begin ( ) , candidates . end ( ) ) ;
candidate_bytes + = info . size ;
} ;
2004-09-08 18:05:49 +00:00
2018-08-19 19:52:01 +00:00
auto pop = [ & ] ( ) {
candidate_bytes - = candidates . front ( ) . size ;
std : : pop_heap ( candidates . begin ( ) , candidates . end ( ) ) ;
candidates . pop_back ( ) ;
} ;
2005-02-11 13:35:27 +00:00
2018-08-19 19:52:01 +00:00
SpriteID i = 0 ;
for ( ; i ! = _spritecache . size ( ) & & candidate_bytes < target ; i + + ) {
SpriteCache * sc = GetSpriteCache ( i ) ;
2019-04-11 17:14:13 +00:00
if ( sc - > GetType ( ) ! = ST_RECOLOUR & & sc - > GetPtr ( ) ! = nullptr ) {
2018-08-19 19:52:01 +00:00
push ( { sc - > lru , i , sc - > buffer . GetSize ( ) } ) ;
if ( candidate_bytes > = target ) break ;
2004-08-09 17:04:08 +00:00
}
}
2018-08-19 19:52:01 +00:00
for ( ; i ! = _spritecache . size ( ) ; i + + ) {
SpriteCache * sc = GetSpriteCache ( i ) ;
2019-04-11 17:14:13 +00:00
if ( sc - > GetType ( ) ! = ST_RECOLOUR & & sc - > GetPtr ( ) ! = nullptr & & sc - > lru < = candidates . front ( ) . lru ) {
2018-08-19 19:52:01 +00:00
push ( { sc - > lru , i , sc - > buffer . GetSize ( ) } ) ;
while ( ! candidates . empty ( ) & & candidate_bytes - candidates . front ( ) . size > = target ) {
pop ( ) ;
2012-02-04 13:28:52 +00:00
}
}
}
2004-08-09 17:04:08 +00:00
2018-08-19 19:52:01 +00:00
for ( auto & it : candidates ) {
DeleteEntryFromSpriteCache ( it . id ) ;
2004-08-09 17:04:08 +00:00
}
2018-08-19 19:52:01 +00:00
DEBUG ( sprite , 3 , " DeleteEntriesFromSpriteCache, deleted: " PRINTF_SIZE " , freed: " PRINTF_SIZE " , in use: " PRINTF_SIZE " --> " PRINTF_SIZE " , delta: " PRINTF_SIZE " , requested: " PRINTF_SIZE ,
candidates . size ( ) , candidate_bytes , initial_in_use , GetSpriteCacheUsage ( ) , initial_in_use - GetSpriteCacheUsage ( ) , target ) ;
2004-08-09 17:04:08 +00:00
}
2018-08-19 19:52:01 +00:00
void IncreaseSpriteLRU ( )
2004-08-09 17:04:08 +00:00
{
2018-08-19 19:52:01 +00:00
int bpp = BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) ;
uint target_size = ( bpp > 0 ? _sprite_cache_size * bpp / 8 : 1 ) * 1024 * 1024 ;
if ( _spritecache_bytes_used > target_size ) {
DeleteEntriesFromSpriteCache ( _spritecache_bytes_used - target_size + 512 * 1024 ) ;
}
2004-08-09 17:04:08 +00:00
2018-08-19 19:52:01 +00:00
/* Increase all LRU values */
if ( _sprite_lru_counter > = 0xC0000000 ) {
SpriteID i ;
2004-09-08 18:05:49 +00:00
2018-08-19 19:52:01 +00:00
DEBUG ( sprite , 3 , " Fixing lru %u, inuse= " PRINTF_SIZE , _sprite_lru_counter , GetSpriteCacheUsage ( ) ) ;
2004-08-09 17:04:08 +00:00
2018-08-19 19:52:01 +00:00
for ( i = 0 ; i ! = _spritecache . size ( ) ; i + + ) {
SpriteCache * sc = GetSpriteCache ( i ) ;
2019-04-11 17:14:13 +00:00
if ( sc - > GetPtr ( ) ! = nullptr ) {
2018-08-19 19:52:01 +00:00
if ( sc - > lru > 0x80000000 ) {
sc - > lru - = 0x80000000 ;
} else {
sc - > lru = 0 ;
2005-02-11 13:35:27 +00:00
}
}
2004-08-09 17:04:08 +00:00
}
2018-08-19 19:52:01 +00:00
_sprite_lru_counter - = 0x80000000 ;
2004-08-09 17:04:08 +00:00
}
}
2018-08-19 19:52:01 +00:00
static void * AllocSprite ( size_t mem_req )
{
assert ( _last_sprite_allocation . GetPtr ( ) = = nullptr ) ;
_last_sprite_allocation . Allocate ( mem_req ) ;
return _last_sprite_allocation . GetPtr ( ) ;
}
2010-08-01 19:22:34 +00:00
/**
* Handles the case when a sprite of different type is requested than is present in the SpriteCache .
2009-01-11 22:28:30 +00:00
* For ST_FONT sprites , it is normal . In other cases , default sprite is loaded instead .
* @ param sprite ID of loaded sprite
* @ param requested requested sprite type
2009-09-19 09:51:14 +00:00
* @ param sc the currently known sprite cache for the requested sprite
2009-01-11 22:28:30 +00:00
* @ return fallback sprite
2010-08-01 19:44:49 +00:00
* @ note this function will do usererror ( ) in the case the fallback sprite isn ' t available
*/
2011-01-14 16:36:34 +00:00
static void * HandleInvalidSpriteRequest ( SpriteID sprite , SpriteType requested , SpriteCache * sc , AllocatorProc * allocator )
2009-01-11 22:28:30 +00:00
{
2009-09-20 23:11:01 +00:00
static const char * const sprite_types [ ] = {
2009-01-11 22:28:30 +00:00
" normal " , // ST_NORMAL
" map generator " , // ST_MAPGEN
" character " , // ST_FONT
" recolour " , // ST_RECOLOUR
} ;
2019-01-23 17:32:54 +00:00
SpriteType available = sc - > GetType ( ) ;
2009-02-07 02:29:08 +00:00
if ( requested = = ST_FONT & & available = = ST_NORMAL ) {
2019-04-11 17:14:13 +00:00
if ( sc - > GetPtr ( ) = = nullptr ) sc - > SetType ( ST_FONT ) ;
2019-01-23 17:32:54 +00:00
return GetRawSprite ( sprite , sc - > GetType ( ) , allocator ) ;
2009-02-07 02:29:08 +00:00
}
2009-01-11 22:28:30 +00:00
2019-01-23 17:32:54 +00:00
byte warning_level = sc - > GetWarned ( ) ? 6 : 0 ;
sc - > SetWarned ( true ) ;
2009-01-11 22:28:30 +00:00
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 ] ) ;
switch ( requested ) {
case ST_NORMAL :
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? " ) ;
2017-08-13 18:38:42 +00:00
FALLTHROUGH ;
2009-01-11 22:28:30 +00:00
case ST_FONT :
2011-01-14 16:36:34 +00:00
return GetRawSprite ( SPR_IMG_QUERY , ST_NORMAL , allocator ) ;
2009-01-11 22:28:30 +00:00
case ST_RECOLOUR :
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? " ) ;
2011-01-14 16:36:34 +00:00
return GetRawSprite ( PALETTE_TO_DARK_BLUE , ST_RECOLOUR , allocator ) ;
2009-01-11 22:28:30 +00:00
case ST_MAPGEN :
/* this shouldn't happen, overriding of ST_MAPGEN sprites is checked in LoadNextSprite()
* ( the only case the check fails is when these sprites weren ' t even loaded . . . ) */
default :
NOT_REACHED ( ) ;
}
}
2004-08-09 17:04:08 +00:00
2011-01-14 16:36:34 +00:00
/**
* Reads a sprite ( from disk or sprite cache ) .
* If the sprite is not available or of wrong type , a fallback sprite is returned .
* @ param sprite Sprite to read .
* @ param type Expected sprite type .
2019-04-10 21:07:06 +00:00
* @ param allocator Allocator function to use . Set to nullptr to use the usual sprite cache .
2011-01-14 16:36:34 +00:00
* @ return Sprite raw data
*/
void * GetRawSprite ( SpriteID sprite , SpriteType type , AllocatorProc * allocator )
2004-08-09 17:04:08 +00:00
{
2012-12-26 15:14:37 +00:00
assert ( type ! = ST_MAPGEN | | IsMapgenSpriteID ( sprite ) ) ;
2009-01-11 22:28:30 +00:00
assert ( type < ST_INVALID ) ;
if ( ! SpriteExists ( sprite ) ) {
DEBUG ( sprite , 1 , " Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs " , sprite ) ;
/* SPR_IMG_QUERY is a BIG FAT RED ? */
sprite = SPR_IMG_QUERY ;
}
2004-08-09 17:04:08 +00:00
2009-01-11 22:28:30 +00:00
SpriteCache * sc = GetSpriteCache ( sprite ) ;
2004-08-09 17:04:08 +00:00
2019-01-23 17:32:54 +00:00
if ( sc - > GetType ( ) ! = type ) return HandleInvalidSpriteRequest ( sprite , type , sc , allocator ) ;
2011-01-14 16:36:34 +00:00
2019-04-10 21:07:06 +00:00
if ( allocator = = nullptr ) {
2011-01-14 16:36:34 +00:00
/* Load sprite into/from spritecache */
2007-01-03 14:42:08 +00:00
2011-01-14 16:36:34 +00:00
/* Update LRU */
sc - > lru = + + _sprite_lru_counter ;
2007-01-03 14:42:08 +00:00
2011-01-14 16:36:34 +00:00
/* Load the sprite, if it is not loaded, yet */
2019-04-11 17:14:13 +00:00
if ( sc - > GetPtr ( ) = = nullptr ) {
2018-08-19 19:52:01 +00:00
void * ptr = ReadSprite ( sc , sprite , type , AllocSprite ) ;
assert ( ptr = = _last_sprite_allocation . GetPtr ( ) ) ;
sc - > buffer = std : : move ( _last_sprite_allocation ) ;
}
2008-02-14 15:13:36 +00:00
2018-08-19 19:52:01 +00:00
return sc - > GetPtr ( ) ;
2011-01-14 16:36:34 +00:00
} else {
/* Do not use the spritecache, but a different allocator. */
return ReadSprite ( sc , sprite , type , allocator ) ;
}
2004-08-09 17:04:08 +00:00
}
2015-08-02 18:37:42 +00:00
/**
* Reads a sprite and finds its most representative colour .
* @ param sprite Sprite to read .
* @ param palette_id Palette for remapping colours .
* @ return if blitter supports 32 bpp , average Colour . data else a palette index .
*/
uint32 GetSpriteMainColour ( SpriteID sprite_id , PaletteID palette_id )
{
if ( ! SpriteExists ( sprite_id ) ) return 0 ;
SpriteCache * sc = GetSpriteCache ( sprite_id ) ;
2019-01-23 17:32:54 +00:00
if ( sc - > GetType ( ) ! = ST_NORMAL ) return 0 ;
2015-08-02 18:37:42 +00:00
2019-04-11 17:14:13 +00:00
const byte * const remap = ( palette_id = = PAL_NONE ? nullptr : GetNonSprite ( GB ( palette_id , 0 , PALETTE_WIDTH ) , ST_RECOLOUR ) + 1 ) ;
2015-08-02 18:37:42 +00:00
2018-11-26 01:01:50 +00:00
uint file_slot = sc - > file_slot ;
2015-08-02 18:37:42 +00:00
size_t file_pos = sc - > file_pos ;
SpriteLoader : : Sprite sprites [ ZOOM_LVL_COUNT ] ;
sprites [ ZOOM_LVL_NORMAL ] . type = ST_NORMAL ;
SpriteLoaderGrf sprite_loader ( sc - > container_ver ) ;
uint8 sprite_avail ;
const uint8 screen_depth = BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) ;
/* Try to read the 32bpp sprite first. */
if ( screen_depth = = 32 ) {
sprite_avail = sprite_loader . LoadSprite ( sprites , file_slot , file_pos , ST_NORMAL , true ) ;
2020-12-27 03:03:05 +00:00
if ( sprite_avail ! = 0 ) {
SpriteLoader : : Sprite * sprite = & sprites [ FindFirstBit ( sprite_avail ) ] ;
2015-08-02 18:37:42 +00:00
/* Return the average colour. */
uint32 r = 0 , g = 0 , b = 0 , cnt = 0 ;
SpriteLoader : : CommonPixel * pixel = sprite - > data ;
for ( uint x = sprite - > width * sprite - > height ; x ! = 0 ; x - - ) {
if ( pixel - > a ) {
if ( remap & & pixel - > m ) {
const Colour c = _cur_palette . palette [ remap [ pixel - > m ] ] ;
if ( c . a ) {
r + = c . r ;
g + = c . g ;
b + = c . b ;
cnt + + ;
}
} else {
r + = pixel - > r ;
g + = pixel - > g ;
b + = pixel - > b ;
cnt + + ;
}
}
pixel + + ;
}
return cnt ? Colour ( r / cnt , g / cnt , b / cnt ) . data : 0 ;
}
}
/* No 32bpp, try 8bpp. */
sprite_avail = sprite_loader . LoadSprite ( sprites , file_slot , file_pos , ST_NORMAL , false ) ;
2020-12-27 03:03:05 +00:00
if ( sprite_avail ! = 0 ) {
SpriteLoader : : Sprite * sprite = & sprites [ FindFirstBit ( sprite_avail ) ] ;
2015-08-02 18:37:42 +00:00
SpriteLoader : : CommonPixel * pixel = sprite - > data ;
if ( screen_depth = = 32 ) {
/* Return the average colour. */
uint32 r = 0 , g = 0 , b = 0 , cnt = 0 ;
for ( uint x = sprite - > width * sprite - > height ; x ! = 0 ; x - - ) {
if ( pixel - > a ) {
const uint col_index = remap ? remap [ pixel - > m ] : pixel - > m ;
const Colour c = _cur_palette . palette [ col_index ] ;
r + = c . r ;
g + = c . g ;
b + = c . b ;
cnt + + ;
}
pixel + + ;
}
return cnt ? Colour ( r / cnt , g / cnt , b / cnt ) . data : 0 ;
} else {
/* Return the most used indexed colour. */
int cnt [ 256 ] ;
memset ( cnt , 0 , sizeof ( cnt ) ) ;
for ( uint x = sprite - > width * sprite - > height ; x ! = 0 ; x - - ) {
cnt [ remap ? remap [ pixel - > m ] : pixel - > m ] + + ;
pixel + + ;
}
int cnt_max = - 1 ;
uint32 rk = 0 ;
for ( uint x = 1 ; x < lengthof ( cnt ) ; x + + ) {
if ( cnt [ x ] > cnt_max ) {
rk = x ;
cnt_max = cnt [ x ] ;
}
}
return rk ;
}
}
return 0 ;
}
2004-08-09 17:04:08 +00:00
2011-11-24 12:26:44 +00:00
void GfxInitSpriteMem ( )
{
2007-01-03 14:42:08 +00:00
/* Reset the spritecache 'pool' */
2018-08-19 01:48:46 +00:00
_spritecache . clear ( ) ;
2018-08-19 19:52:01 +00:00
assert ( _spritecache_bytes_used = = 0 ) ;
2004-08-09 17:04:08 +00:00
}
2009-02-23 10:50:25 +00:00
2011-11-24 12:26:44 +00:00
/**
* Remove all encoded sprites from the sprite cache without
* discarding sprite location information .
*/
void GfxClearSpriteCache ( )
{
/* Clear sprite ptr for all cached items */
2018-08-19 01:48:46 +00:00
for ( uint i = 0 ; i ! = _spritecache . size ( ) ; i + + ) {
2011-11-24 12:26:44 +00:00
SpriteCache * sc = GetSpriteCache ( i ) ;
2019-04-11 17:14:13 +00:00
if ( sc - > GetType ( ) ! = ST_RECOLOUR & & sc - > GetPtr ( ) ! = nullptr ) DeleteEntryFromSpriteCache ( i ) ;
2011-11-24 12:26:44 +00:00
}
}
2012-02-04 13:29:13 +00:00
/* static */ ReusableBuffer < SpriteLoader : : CommonPixel > SpriteLoader : : Sprite : : buffer [ ZOOM_LVL_COUNT ] ;