2017-07-13 10:26:03 +02:00
/*
* CAnimation . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2011-12-14 00:23:17 +03:00
# include "StdInc.h"
2014-07-13 20:53:37 +03:00
# include "CAnimation.h"
2022-09-18 14:47:49 +02:00
# include "SDL_Extensions.h"
# include "SDL_Pixels.h"
2022-12-15 23:24:03 +02:00
# include "ColorFilter.h"
2022-09-18 14:47:49 +02:00
2014-07-13 20:53:37 +03:00
# include "../CBitmapHandler.h"
# include "../Graphics.h"
2013-07-28 17:49:50 +03:00
# include "../lib/filesystem/Filesystem.h"
2013-04-07 13:48:07 +03:00
# include "../lib/filesystem/ISimpleResourceLoader.h"
2011-07-21 21:29:22 +03:00
# include "../lib/JsonNode.h"
2014-04-10 20:11:09 +03:00
# include "../lib/CRandomGenerator.h"
2011-10-08 04:23:46 +03:00
2016-11-07 23:19:53 +02:00
class SDLImageLoader ;
2011-07-21 21:29:22 +03:00
typedef std : : map < size_t , std : : vector < JsonNode > > source_map ;
2011-02-06 19:26:27 +02:00
typedef std : : map < size_t , IImage * > image_map ;
typedef std : : map < size_t , image_map > group_map ;
2010-10-18 18:08:59 +03:00
2017-09-05 15:44:27 +02:00
/// Class for def loading
2016-11-07 23:19:53 +02:00
/// After loading will store general info (palette and frame offsets) and pointer to file itself
class CDefFile
{
private :
2022-12-29 12:06:34 +02:00
PACKED_STRUCT_BEGIN
2016-11-07 23:19:53 +02:00
struct SSpriteDef
{
ui32 size ;
ui32 format ; /// format in which pixel data is stored
ui32 fullWidth ; /// full width and height of frame, including borders
ui32 fullHeight ;
ui32 width ; /// width and height of pixel data, borders excluded
ui32 height ;
si32 leftMargin ;
si32 topMargin ;
2022-12-29 12:06:34 +02:00
} PACKED_STRUCT_END ;
2016-11-07 23:19:53 +02:00
//offset[group][frame] - offset of frame data in file
std : : map < size_t , std : : vector < size_t > > offset ;
std : : unique_ptr < ui8 [ ] > data ;
std : : unique_ptr < SDL_Color [ ] > palette ;
public :
CDefFile ( std : : string Name ) ;
~ CDefFile ( ) ;
//load frame as SDL_Surface
template < class ImageLoader >
void loadFrame ( size_t frame , size_t group , ImageLoader & loader ) const ;
const std : : map < size_t , size_t > getEntries ( ) const ;
} ;
/*
* Wrapper around SDL_Surface
*/
class SDLImage : public IImage
{
public :
2020-01-26 10:01:48 +02:00
const static int DEFAULT_PALETTE_COLORS = 256 ;
2016-11-07 23:19:53 +02:00
//Surface without empty borders
SDL_Surface * surf ;
//size of left and top borders
Point margins ;
//total size including borders
Point fullSize ;
public :
//Load image from def file
2018-07-25 00:36:48 +02:00
SDLImage ( CDefFile * data , size_t frame , size_t group = 0 ) ;
2016-11-07 23:19:53 +02:00
//Load from bitmap file
2018-07-25 00:36:48 +02:00
SDLImage ( std : : string filename ) ;
2017-09-08 13:25:12 +02:00
SDLImage ( const JsonNode & conf ) ;
2016-11-07 23:19:53 +02:00
//Create using existing surface, extraRef will increase refcount on SDL_Surface
SDLImage ( SDL_Surface * from , bool extraRef ) ;
~ SDLImage ( ) ;
2020-01-25 11:21:26 +02:00
// Keep the original palette, in order to do color switching operation
void savePalette ( ) ;
2023-01-11 20:57:42 +02:00
void draw ( SDL_Surface * where , int posX = 0 , int posY = 0 , const Rect * src = nullptr ) const override ;
2023-01-17 22:01:35 +02:00
void draw ( SDL_Surface * where , const Rect * dest , const Rect * src ) const override ;
2018-04-07 13:34:11 +02:00
std : : shared_ptr < IImage > scaleFast ( float scale ) const override ;
2016-11-25 14:23:28 +02:00
void exportBitmap ( const boost : : filesystem : : path & path ) const override ;
2016-11-07 23:19:53 +02:00
void playerColored ( PlayerColor player ) override ;
void setFlagColor ( PlayerColor player ) override ;
2022-11-25 11:46:47 +02:00
bool isTransparent ( const Point & coords ) const override ;
Point dimensions ( ) const override ;
2016-11-07 23:19:53 +02:00
void horizontalFlip ( ) override ;
void verticalFlip ( ) override ;
void shiftPalette ( int from , int howMany ) override ;
2022-12-15 23:24:03 +02:00
void adjustPalette ( const ColorFilter & shifter ) override ;
void resetPalette ( int colorID ) override ;
2020-01-25 11:21:26 +02:00
void resetPalette ( ) override ;
2016-11-07 23:19:53 +02:00
2022-12-14 12:04:37 +02:00
void setSpecialPallete ( const SpecialPalette & SpecialPalette ) override ;
2017-09-05 17:59:26 +02:00
2016-11-07 23:19:53 +02:00
friend class SDLImageLoader ;
2020-01-25 11:21:26 +02:00
private :
2020-01-26 10:01:48 +02:00
SDL_Palette * originalPalette ;
2016-11-07 23:19:53 +02:00
} ;
2011-02-06 19:26:27 +02:00
class SDLImageLoader
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
SDLImage * image ;
ui8 * lineStart ;
ui8 * position ;
public :
//load size raw pixels from data
inline void Load ( size_t size , const ui8 * data ) ;
//set size pixels to color
inline void Load ( size_t size , ui8 color = 0 ) ;
inline void EndLine ( ) ;
//init image with these sizes and palette
2011-12-22 16:05:19 +03:00
inline void init ( Point SpriteSize , Point Margins , Point FullSize , SDL_Color * pal ) ;
2011-02-06 19:26:27 +02:00
SDLImageLoader ( SDLImage * Img ) ;
~ SDLImageLoader ( ) ;
} ;
2010-10-18 18:08:59 +03:00
2022-11-25 00:26:14 +02:00
std : : shared_ptr < IImage > IImage : : createFromFile ( const std : : string & path )
{
return std : : shared_ptr < IImage > ( new SDLImage ( path ) ) ;
}
2012-12-14 22:47:38 +03:00
// Extremely simple file cache. TODO: smarter, more general solution
class CFileCache
{
static const int cacheSize = 50 ; //Max number of cached files
struct FileData
{
2016-01-29 17:08:44 +02:00
ResourceID name ;
size_t size ;
std : : unique_ptr < ui8 [ ] > data ;
2012-12-14 22:47:38 +03:00
2016-01-29 16:16:14 +02:00
std : : unique_ptr < ui8 [ ] > getCopy ( )
2012-12-14 22:47:38 +03:00
{
2016-01-29 16:16:14 +02:00
auto ret = std : : unique_ptr < ui8 [ ] > ( new ui8 [ size ] ) ;
std : : copy ( data . get ( ) , data . get ( ) + size , ret . get ( ) ) ;
2012-12-14 22:47:38 +03:00
return ret ;
}
2016-01-29 16:16:14 +02:00
FileData ( ResourceID name_ , size_t size_ , std : : unique_ptr < ui8 [ ] > data_ ) :
name { std : : move ( name_ ) } ,
size { size_ } ,
data { std : : move ( data_ ) }
2012-12-15 16:40:22 +03:00
{ }
2012-12-14 22:47:38 +03:00
} ;
2016-01-29 16:16:14 +02:00
std : : deque < FileData > cache ;
2012-12-14 22:47:38 +03:00
public :
2016-01-29 16:16:14 +02:00
std : : unique_ptr < ui8 [ ] > getCachedFile ( ResourceID rid )
2012-12-14 22:47:38 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto & file : cache )
2012-12-14 22:47:38 +03:00
{
if ( file . name = = rid )
return file . getCopy ( ) ;
}
// Still here? Cache miss
if ( cache . size ( ) > cacheSize )
cache . pop_front ( ) ;
2013-07-28 17:49:50 +03:00
auto data = CResourceHandler : : get ( ) - > load ( rid ) - > readAll ( ) ;
2016-01-29 16:16:14 +02:00
cache . emplace_back ( std : : move ( rid ) , data . second , std : : move ( data . first ) ) ;
2012-12-14 22:47:38 +03:00
return cache . back ( ) . getCopy ( ) ;
}
} ;
2017-09-04 16:52:36 +02:00
enum class DefType : uint32_t
{
SPELL = 0x40 ,
2017-09-05 13:32:52 +02:00
SPRITE = 0x41 ,
2017-09-04 16:52:36 +02:00
CREATURE = 0x42 ,
MAP = 0x43 ,
MAP_HERO = 0x44 ,
TERRAIN = 0x45 ,
CURSOR = 0x46 ,
INTERFACE = 0x47 ,
2017-09-05 13:32:52 +02:00
SPRITE_FRAME = 0x48 ,
2017-09-04 16:52:36 +02:00
BATTLE_HERO = 0x49
} ;
2012-12-14 22:47:38 +03:00
static CFileCache animationCache ;
2011-02-06 19:26:27 +02:00
/*************************************************************************
* DefFile , class used for def loading *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-10-18 18:08:59 +03:00
2017-09-03 22:38:15 +02:00
bool operator = = ( const SDL_Color & lhs , const SDL_Color & rhs )
{
return ( lhs . a = = rhs . a ) & & ( lhs . b = = rhs . b ) & & ( lhs . g = = rhs . g ) & & ( lhs . r = = rhs . r ) ;
}
2011-02-06 19:26:27 +02:00
CDefFile : : CDefFile ( std : : string Name ) :
2013-06-26 14:18:27 +03:00
data ( nullptr ) ,
palette ( nullptr )
2011-02-06 19:26:27 +02:00
{
//First 8 colors in def palette used for transparency
static SDL_Color H3Palette [ 8 ] =
{
2022-12-14 12:04:37 +02:00
{ 0 , 0 , 0 , 0 } , // transparency ( used in most images )
{ 0 , 0 , 0 , 64 } , // shadow border ( used in battle, adventure map def's )
{ 0 , 0 , 0 , 64 } , // shadow border ( used in fog-of-war def's )
{ 0 , 0 , 0 , 128 } , // shadow body ( used in fog-of-war def's )
{ 0 , 0 , 0 , 128 } , // shadow body ( used in battle, adventure map def's )
{ 0 , 0 , 0 , 0 } , // selection ( used in battle def's )
{ 0 , 0 , 0 , 128 } , // shadow body below selection ( used in battle def's )
{ 0 , 0 , 0 , 64 } // shadow border below selection ( used in battle def's )
2011-02-06 19:26:27 +02:00
} ;
2012-12-14 22:47:38 +03:00
data = animationCache . getCachedFile ( ResourceID ( std : : string ( " SPRITES/ " ) + Name , EResType : : ANIMATION ) ) ;
2011-02-06 19:26:27 +02:00
2016-01-29 16:16:14 +02:00
palette = std : : unique_ptr < SDL_Color [ ] > ( new SDL_Color [ 256 ] ) ;
2011-02-06 19:26:27 +02:00
int it = 0 ;
2010-10-18 18:08:59 +03:00
2016-01-29 16:16:14 +02:00
ui32 type = read_le_u32 ( data . get ( ) + it ) ;
2011-02-06 19:26:27 +02:00
it + = 4 ;
2011-10-08 04:23:46 +03:00
//int width = read_le_u32(data + it); it+=4;//not used
//int height = read_le_u32(data + it); it+=4;
2011-02-06 19:26:27 +02:00
it + = 8 ;
2016-01-29 16:16:14 +02:00
ui32 totalBlocks = read_le_u32 ( data . get ( ) + it ) ;
2011-02-06 19:26:27 +02:00
it + = 4 ;
2010-10-18 18:08:59 +03:00
2011-12-14 00:23:17 +03:00
for ( ui32 i = 0 ; i < 256 ; i + + )
2011-02-06 19:26:27 +02:00
{
palette [ i ] . r = data [ it + + ] ;
palette [ i ] . g = data [ it + + ] ;
palette [ i ] . b = data [ it + + ] ;
2015-09-02 17:49:29 +02:00
palette [ i ] . a = SDL_ALPHA_OPAQUE ;
2011-02-06 19:26:27 +02:00
}
2017-09-03 22:38:15 +02:00
2017-09-04 16:52:36 +02:00
switch ( static_cast < DefType > ( type ) )
{
case DefType : : SPELL :
palette [ 0 ] = H3Palette [ 0 ] ;
break ;
2017-09-05 13:32:52 +02:00
case DefType : : SPRITE :
case DefType : : SPRITE_FRAME :
for ( ui32 i = 0 ; i < 8 ; i + + )
palette [ i ] = H3Palette [ i ] ;
break ;
2017-09-04 16:52:36 +02:00
case DefType : : CREATURE :
palette [ 0 ] = H3Palette [ 0 ] ;
palette [ 1 ] = H3Palette [ 1 ] ;
palette [ 4 ] = H3Palette [ 4 ] ;
palette [ 5 ] = H3Palette [ 5 ] ;
palette [ 6 ] = H3Palette [ 6 ] ;
palette [ 7 ] = H3Palette [ 7 ] ;
break ;
case DefType : : MAP :
case DefType : : MAP_HERO :
palette [ 0 ] = H3Palette [ 0 ] ;
palette [ 1 ] = H3Palette [ 1 ] ;
palette [ 4 ] = H3Palette [ 4 ] ;
//5 = owner flag, handled separately
break ;
case DefType : : TERRAIN :
palette [ 0 ] = H3Palette [ 0 ] ;
palette [ 1 ] = H3Palette [ 1 ] ;
palette [ 2 ] = H3Palette [ 2 ] ;
palette [ 3 ] = H3Palette [ 3 ] ;
palette [ 4 ] = H3Palette [ 4 ] ;
break ;
case DefType : : CURSOR :
palette [ 0 ] = H3Palette [ 0 ] ;
break ;
case DefType : : INTERFACE :
palette [ 0 ] = H3Palette [ 0 ] ;
palette [ 1 ] = H3Palette [ 1 ] ;
palette [ 4 ] = H3Palette [ 4 ] ;
2017-09-05 13:32:52 +02:00
//player colors handled separately
//TODO: disallow colorizing other def types
2017-09-04 16:52:36 +02:00
break ;
case DefType : : BATTLE_HERO :
2017-09-05 13:32:52 +02:00
palette [ 0 ] = H3Palette [ 0 ] ;
palette [ 1 ] = H3Palette [ 1 ] ;
palette [ 4 ] = H3Palette [ 4 ] ;
2017-09-04 16:52:36 +02:00
break ;
default :
logAnim - > error ( " Unknown def type %d in %s " , type , Name ) ;
break ;
2017-09-03 22:38:15 +02:00
}
2010-10-18 18:08:59 +03:00
2011-12-14 00:23:17 +03:00
for ( ui32 i = 0 ; i < totalBlocks ; i + + )
2011-02-06 19:26:27 +02:00
{
2016-01-29 16:16:14 +02:00
size_t blockID = read_le_u32 ( data . get ( ) + it ) ;
2011-02-06 19:26:27 +02:00
it + = 4 ;
2016-01-29 16:16:14 +02:00
size_t totalEntries = read_le_u32 ( data . get ( ) + it ) ;
2011-02-06 19:26:27 +02:00
it + = 12 ;
//8 unknown bytes - skipping
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
//13 bytes for name of every frame in this block - not used, skipping
2020-10-01 10:38:06 +02:00
it + = 13 * ( int ) totalEntries ;
2010-10-18 18:08:59 +03:00
2011-12-14 00:23:17 +03:00
for ( ui32 j = 0 ; j < totalEntries ; j + + )
2011-02-06 19:26:27 +02:00
{
2016-01-29 16:16:14 +02:00
size_t currOffset = read_le_u32 ( data . get ( ) + it ) ;
2011-02-06 19:26:27 +02:00
offset [ blockID ] . push_back ( currOffset ) ;
it + = 4 ;
}
}
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
template < class ImageLoader >
void CDefFile : : loadFrame ( size_t frame , size_t group , ImageLoader & loader ) const
{
std : : map < size_t , std : : vector < size_t > > : : const_iterator it ;
it = offset . find ( group ) ;
assert ( it ! = offset . end ( ) ) ;
2010-10-18 18:08:59 +03:00
2016-01-29 16:16:14 +02:00
const ui8 * FDef = data . get ( ) + it - > second [ frame ] ;
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
const SSpriteDef sd = * reinterpret_cast < const SSpriteDef * > ( FDef ) ;
SSpriteDef sprite ;
2013-02-05 22:56:28 +03:00
sprite . format = read_le_u32 ( & sd . format ) ;
sprite . fullWidth = read_le_u32 ( & sd . fullWidth ) ;
sprite . fullHeight = read_le_u32 ( & sd . fullHeight ) ;
sprite . width = read_le_u32 ( & sd . width ) ;
sprite . height = read_le_u32 ( & sd . height ) ;
sprite . leftMargin = read_le_u32 ( & sd . leftMargin ) ;
sprite . topMargin = read_le_u32 ( & sd . topMargin ) ;
2010-10-18 18:08:59 +03:00
2011-12-14 00:23:17 +03:00
ui32 currentOffset = sizeof ( SSpriteDef ) ;
2018-03-22 03:33:15 +02:00
//special case for some "old" format defs (SGTWMTA.DEF and SGTWMTB.DEF)
if ( sprite . format = = 1 & & sprite . width > sprite . fullWidth & & sprite . height > sprite . fullHeight )
{
sprite . leftMargin = 0 ;
sprite . topMargin = 0 ;
sprite . width = sprite . fullWidth ;
sprite . height = sprite . fullHeight ;
currentOffset - = 16 ;
}
const ui32 BaseOffset = currentOffset ;
2010-11-18 15:34:21 +02:00
2011-12-22 16:05:19 +03:00
loader . init ( Point ( sprite . width , sprite . height ) ,
2020-10-06 01:27:04 +02:00
Point ( sprite . leftMargin , sprite . topMargin ) ,
Point ( sprite . fullWidth , sprite . fullHeight ) , palette . get ( ) ) ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
switch ( sprite . format )
2010-10-18 18:08:59 +03:00
{
case 0 :
{
2011-02-06 19:26:27 +02:00
//pixel data is not compressed, copy data to surface
2018-03-22 03:33:15 +02:00
for ( ui32 i = 0 ; i < sprite . height ; i + + )
2010-10-18 18:08:59 +03:00
{
2016-11-07 23:19:53 +02:00
loader . Load ( sprite . width , FDef + currentOffset ) ;
2011-02-06 19:26:27 +02:00
currentOffset + = sprite . width ;
loader . EndLine ( ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
break ;
2010-10-18 18:08:59 +03:00
}
case 1 :
{
2010-11-18 15:34:21 +02:00
//for each line we have offset of pixel data
2011-02-09 14:33:58 +02:00
const ui32 * RWEntriesLoc = reinterpret_cast < const ui32 * > ( FDef + currentOffset ) ;
2011-02-06 19:26:27 +02:00
currentOffset + = sizeof ( ui32 ) * sprite . height ;
2010-11-18 15:34:21 +02:00
2018-03-22 03:33:15 +02:00
for ( ui32 i = 0 ; i < sprite . height ; i + + )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
//get position of the line
2011-10-08 04:23:46 +03:00
currentOffset = BaseOffset + read_le_u32 ( RWEntriesLoc + i ) ;
2011-12-14 00:23:17 +03:00
ui32 TotalRowLength = 0 ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
while ( TotalRowLength < sprite . width )
2010-10-18 18:08:59 +03:00
{
2018-03-22 03:33:15 +02:00
ui8 segmentType = FDef [ currentOffset + + ] ;
ui32 length = FDef [ currentOffset + + ] + 1 ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
if ( segmentType = = 0xFF ) //Raw data
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
loader . Load ( length , FDef + currentOffset ) ;
currentOffset + = length ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
else // RLE
2010-10-18 18:08:59 +03:00
{
2018-03-22 03:33:15 +02:00
loader . Load ( length , segmentType ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
TotalRowLength + = length ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
loader . EndLine ( ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
break ;
2010-10-18 18:08:59 +03:00
}
case 2 :
{
2011-10-08 04:23:46 +03:00
currentOffset = BaseOffset + read_le_u16 ( FDef + BaseOffset ) ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
for ( ui32 i = 0 ; i < sprite . height ; i + + )
2010-10-18 18:08:59 +03:00
{
2011-12-14 00:23:17 +03:00
ui32 TotalRowLength = 0 ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
while ( TotalRowLength < sprite . width )
2010-10-18 18:08:59 +03:00
{
2018-03-22 03:33:15 +02:00
ui8 segment = FDef [ currentOffset + + ] ;
ui8 code = segment / 32 ;
ui8 length = ( segment & 31 ) + 1 ;
2011-02-06 19:26:27 +02:00
2018-03-22 03:33:15 +02:00
if ( code = = 7 ) //Raw data
2010-10-18 18:08:59 +03:00
{
2016-10-27 14:07:12 +02:00
loader . Load ( length , FDef + currentOffset ) ;
2011-02-06 19:26:27 +02:00
currentOffset + = length ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
else //RLE
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
loader . Load ( length , code ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
TotalRowLength + = length ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
loader . EndLine ( ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
break ;
2010-10-18 18:08:59 +03:00
}
case 3 :
{
2018-03-22 03:33:15 +02:00
for ( ui32 i = 0 ; i < sprite . height ; i + + )
2010-10-18 18:08:59 +03:00
{
2011-10-08 04:23:46 +03:00
currentOffset = BaseOffset + read_le_u16 ( FDef + BaseOffset + i * 2 * ( sprite . width / 32 ) ) ;
2011-12-14 00:23:17 +03:00
ui32 TotalRowLength = 0 ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
while ( TotalRowLength < sprite . width )
2010-10-18 18:08:59 +03:00
{
2011-12-14 00:23:17 +03:00
ui8 segment = FDef [ currentOffset + + ] ;
ui8 code = segment / 32 ;
ui8 length = ( segment & 31 ) + 1 ;
2010-10-18 18:08:59 +03:00
2018-03-22 03:33:15 +02:00
if ( code = = 7 ) //Raw data
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
loader . Load ( length , FDef + currentOffset ) ;
currentOffset + = length ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
else //RLE
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
loader . Load ( length , code ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
TotalRowLength + = length ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
loader . EndLine ( ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
break ;
2010-10-18 18:08:59 +03:00
}
default :
2017-08-11 13:38:10 +02:00
logGlobal - > error ( " Error: unsupported format of def file: %d " , sprite . format ) ;
2010-10-18 18:08:59 +03:00
break ;
}
2017-07-12 21:01:10 +02:00
}
2010-10-18 18:08:59 +03:00
2016-01-29 16:16:14 +02:00
CDefFile : : ~ CDefFile ( ) = default ;
2010-10-18 18:08:59 +03:00
2011-02-20 11:24:53 +02:00
const std : : map < size_t , size_t > CDefFile : : getEntries ( ) const
2010-10-18 18:08:59 +03:00
{
2011-02-20 11:24:53 +02:00
std : : map < size_t , size_t > ret ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : offset )
ret [ elem . first ] = elem . second . size ( ) ;
2011-02-20 11:24:53 +02:00
return ret ;
2011-02-06 19:26:27 +02:00
}
/*************************************************************************
* Classes for image loaders - helpers for loading from def files *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
SDLImageLoader : : SDLImageLoader ( SDLImage * Img ) :
image ( Img ) ,
2013-06-26 14:18:27 +03:00
lineStart ( nullptr ) ,
position ( nullptr )
2011-02-06 19:26:27 +02:00
{
}
2011-12-22 16:05:19 +03:00
void SDLImageLoader : : init ( Point SpriteSize , Point Margins , Point FullSize , SDL_Color * pal )
2011-02-06 19:26:27 +02:00
{
//Init image
2018-07-25 00:36:48 +02:00
image - > surf = SDL_CreateRGBSurface ( 0 , SpriteSize . x , SpriteSize . y , 8 , 0 , 0 , 0 , 0 ) ;
2011-02-06 19:26:27 +02:00
image - > margins = Margins ;
image - > fullSize = FullSize ;
//Prepare surface
2020-01-26 10:01:48 +02:00
SDL_Palette * p = SDL_AllocPalette ( SDLImage : : DEFAULT_PALETTE_COLORS ) ;
SDL_SetPaletteColors ( p , pal , 0 , SDLImage : : DEFAULT_PALETTE_COLORS ) ;
2015-09-05 14:03:37 +02:00
SDL_SetSurfacePalette ( image - > surf , p ) ;
2016-01-29 16:16:14 +02:00
SDL_FreePalette ( p ) ;
2015-09-05 14:03:37 +02:00
2011-02-06 19:26:27 +02:00
SDL_LockSurface ( image - > surf ) ;
lineStart = position = ( ui8 * ) image - > surf - > pixels ;
}
inline void SDLImageLoader : : Load ( size_t size , const ui8 * data )
{
if ( size )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
memcpy ( ( void * ) position , data , size ) ;
position + = size ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
inline void SDLImageLoader : : Load ( size_t size , ui8 color )
{
if ( size )
{
memset ( ( void * ) position , color , size ) ;
position + = size ;
}
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
inline void SDLImageLoader : : EndLine ( )
{
lineStart + = image - > surf - > pitch ;
position = lineStart ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
SDLImageLoader : : ~ SDLImageLoader ( )
{
SDL_UnlockSurface ( image - > surf ) ;
2015-06-21 00:38:05 +02:00
SDL_SetColorKey ( image - > surf , SDL_TRUE , 0 ) ;
2011-02-06 19:26:27 +02:00
//TODO: RLE if compressed and bpp>1
}
/*************************************************************************
* Classes for images , support loading from file and drawing on surface *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-04-07 13:34:11 +02:00
IImage : : IImage ( ) = default ;
IImage : : ~ IImage ( ) = default ;
2011-02-06 19:26:27 +02:00
2022-12-11 22:09:57 +02:00
int IImage : : width ( ) const
{
return dimensions ( ) . x ;
}
int IImage : : height ( ) const
{
return dimensions ( ) . y ;
}
2011-02-06 19:26:27 +02:00
2018-07-25 00:36:48 +02:00
SDLImage : : SDLImage ( CDefFile * data , size_t frame , size_t group )
2017-09-08 13:25:12 +02:00
: surf ( nullptr ) ,
margins ( 0 , 0 ) ,
2020-01-26 10:01:48 +02:00
fullSize ( 0 , 0 ) ,
originalPalette ( nullptr )
2011-02-06 19:26:27 +02:00
{
SDLImageLoader loader ( this ) ;
data - > loadFrame ( frame , group , loader ) ;
2020-01-25 11:21:26 +02:00
savePalette ( ) ;
2011-02-06 19:26:27 +02:00
}
2017-09-08 13:25:12 +02:00
SDLImage : : SDLImage ( SDL_Surface * from , bool extraRef )
: surf ( nullptr ) ,
margins ( 0 , 0 ) ,
2020-01-26 10:01:48 +02:00
fullSize ( 0 , 0 ) ,
originalPalette ( nullptr )
2011-02-06 19:26:27 +02:00
{
surf = from ;
2020-01-25 11:21:26 +02:00
if ( surf = = nullptr )
return ;
savePalette ( ) ;
2011-02-06 19:26:27 +02:00
if ( extraRef )
surf - > refcount + + ;
fullSize . x = surf - > w ;
fullSize . y = surf - > h ;
}
2017-09-08 13:25:12 +02:00
SDLImage : : SDLImage ( const JsonNode & conf )
: surf ( nullptr ) ,
margins ( 0 , 0 ) ,
2020-01-26 10:01:48 +02:00
fullSize ( 0 , 0 ) ,
originalPalette ( nullptr )
2017-09-08 13:25:12 +02:00
{
std : : string filename = conf [ " file " ] . String ( ) ;
surf = BitmapHandler : : loadBitmap ( filename ) ;
if ( surf = = nullptr )
return ;
2020-01-25 11:21:26 +02:00
savePalette ( ) ;
2017-09-08 13:25:12 +02:00
const JsonNode & jsonMargins = conf [ " margins " ] ;
2020-10-01 10:38:06 +02:00
margins . x = static_cast < int > ( jsonMargins [ " left " ] . Integer ( ) ) ;
margins . y = static_cast < int > ( jsonMargins [ " top " ] . Integer ( ) ) ;
2017-09-08 13:25:12 +02:00
2020-10-01 10:38:06 +02:00
fullSize . x = static_cast < int > ( conf [ " width " ] . Integer ( ) ) ;
fullSize . y = static_cast < int > ( conf [ " height " ] . Integer ( ) ) ;
2017-09-08 13:25:12 +02:00
if ( fullSize . x = = 0 )
{
2020-10-01 10:38:06 +02:00
fullSize . x = margins . x + surf - > w + ( int ) jsonMargins [ " right " ] . Integer ( ) ;
2017-09-08 13:25:12 +02:00
}
if ( fullSize . y = = 0 )
{
2020-10-01 10:38:06 +02:00
fullSize . y = margins . y + surf - > h + ( int ) jsonMargins [ " bottom " ] . Integer ( ) ;
2017-09-08 13:25:12 +02:00
}
}
2018-07-25 00:36:48 +02:00
SDLImage : : SDLImage ( std : : string filename )
2017-09-08 13:25:12 +02:00
: surf ( nullptr ) ,
margins ( 0 , 0 ) ,
2020-01-26 10:01:48 +02:00
fullSize ( 0 , 0 ) ,
originalPalette ( nullptr )
2011-02-06 19:26:27 +02:00
{
2011-07-18 18:21:16 +03:00
surf = BitmapHandler : : loadBitmap ( filename ) ;
2011-05-28 13:05:25 +03:00
2018-07-25 00:36:48 +02:00
if ( surf = = nullptr )
2011-05-28 13:05:25 +03:00
{
2017-08-11 13:38:10 +02:00
logGlobal - > error ( " Error: failed to load image %s " , filename ) ;
2013-11-24 14:36:51 +03:00
return ;
2011-05-28 13:05:25 +03:00
}
else
{
2020-01-25 11:21:26 +02:00
savePalette ( ) ;
2011-05-28 13:05:25 +03:00
fullSize . x = surf - > w ;
fullSize . y = surf - > h ;
}
2011-02-06 19:26:27 +02:00
}
2023-01-11 20:57:42 +02:00
void SDLImage : : draw ( SDL_Surface * where , int posX , int posY , const Rect * src ) const
2011-03-15 19:25:51 +02:00
{
2017-09-08 13:25:12 +02:00
if ( ! surf )
2011-02-20 11:24:53 +02:00
return ;
2016-10-18 09:31:31 +02:00
2016-11-07 23:19:53 +02:00
Rect destRect ( posX , posY , surf - > w , surf - > h ) ;
2023-01-11 20:57:42 +02:00
draw ( where , & destRect , src ) ;
2016-11-07 23:19:53 +02:00
}
2023-01-17 22:01:35 +02:00
void SDLImage : : draw ( SDL_Surface * where , const Rect * dest , const Rect * src ) const
2016-11-07 23:19:53 +02:00
{
if ( ! surf )
return ;
Rect sourceRect ( 0 , 0 , surf - > w , surf - > h ) ;
2016-11-08 21:14:38 +02:00
Point destShift ( 0 , 0 ) ;
2016-11-07 23:19:53 +02:00
if ( src )
2016-11-04 16:05:56 +02:00
{
2016-11-07 23:19:53 +02:00
if ( src - > x < margins . x )
destShift . x + = margins . x - src - > x ;
if ( src - > y < margins . y )
destShift . y + = margins . y - src - > y ;
2023-01-17 22:01:35 +02:00
sourceRect = Rect ( * src ) . intersect ( Rect ( margins . x , margins . y , surf - > w , surf - > h ) ) ;
2016-11-07 23:19:53 +02:00
sourceRect - = margins ;
2010-10-18 18:08:59 +03:00
}
2016-11-07 23:19:53 +02:00
else
destShift = margins ;
2016-10-27 14:07:12 +02:00
2016-11-07 23:19:53 +02:00
if ( dest )
2023-01-17 22:01:35 +02:00
destShift + = dest - > topLeft ( ) ;
2016-11-07 23:19:53 +02:00
if ( surf - > format - > BitsPerPixel = = 8 )
2016-10-27 14:07:12 +02:00
{
2023-01-17 22:01:35 +02:00
CSDL_Ext : : blit8bppAlphaTo24bpp ( surf , sourceRect , where , destShift ) ;
2016-10-27 14:07:12 +02:00
}
2016-11-04 16:05:56 +02:00
else
2016-11-07 23:15:46 +02:00
{
2023-01-17 22:01:35 +02:00
CSDL_Ext : : blitSurface ( surf , sourceRect , where , destShift ) ;
2016-11-07 23:15:46 +02:00
}
2016-11-04 16:05:56 +02:00
}
2018-04-07 13:34:11 +02:00
std : : shared_ptr < IImage > SDLImage : : scaleFast ( float scale ) const
2016-11-07 23:19:53 +02:00
{
2020-10-01 10:38:06 +02:00
auto scaled = CSDL_Ext : : scaleSurfaceFast ( surf , ( int ) ( surf - > w * scale ) , ( int ) ( surf - > h * scale ) ) ;
2016-11-07 23:19:53 +02:00
if ( scaled - > format & & scaled - > format - > palette ) // fix color keying, because SDL loses it at this point
CSDL_Ext : : setColorKey ( scaled , scaled - > format - > palette - > colors [ 0 ] ) ;
else if ( scaled - > format & & scaled - > format - > Amask )
SDL_SetSurfaceBlendMode ( scaled , SDL_BLENDMODE_BLEND ) ; //just in case
else
CSDL_Ext : : setDefaultColorKey ( scaled ) ; //just in case
2016-11-10 13:38:33 +02:00
SDLImage * ret = new SDLImage ( scaled , false ) ;
2016-11-08 21:14:38 +02:00
ret - > fullSize . x = ( int ) round ( ( float ) fullSize . x * scale ) ;
ret - > fullSize . y = ( int ) round ( ( float ) fullSize . y * scale ) ;
ret - > margins . x = ( int ) round ( ( float ) margins . x * scale ) ;
ret - > margins . y = ( int ) round ( ( float ) margins . y * scale ) ;
2018-04-07 13:34:11 +02:00
return std : : shared_ptr < IImage > ( ret ) ;
2016-11-07 23:19:53 +02:00
}
2016-11-25 14:23:28 +02:00
void SDLImage : : exportBitmap ( const boost : : filesystem : : path & path ) const
{
SDL_SaveBMP ( surf , path . string ( ) . c_str ( ) ) ;
}
2013-03-03 20:06:03 +03:00
void SDLImage : : playerColored ( PlayerColor player )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
graphics - > blueToPlayersAdv ( surf , player ) ;
2010-10-18 18:08:59 +03:00
}
2016-11-07 23:19:53 +02:00
void SDLImage : : setFlagColor ( PlayerColor player )
{
if ( player < PlayerColor : : PLAYER_LIMIT | | player = = PlayerColor : : NEUTRAL )
CSDL_Ext : : setPlayerColor ( surf , player ) ;
}
2022-11-25 11:46:47 +02:00
bool SDLImage : : isTransparent ( const Point & coords ) const
{
return CSDL_Ext : : isTransparent ( surf , coords . x , coords . y ) ;
}
Point SDLImage : : dimensions ( ) const
{
return fullSize ;
}
2016-11-07 23:19:53 +02:00
void SDLImage : : horizontalFlip ( )
{
2016-11-08 21:14:38 +02:00
margins . y = fullSize . y - surf - > h - margins . y ;
2016-11-07 23:19:53 +02:00
2016-11-08 21:14:38 +02:00
//todo: modify in-place
SDL_Surface * flipped = CSDL_Ext : : horizontalFlip ( surf ) ;
2016-11-07 23:19:53 +02:00
SDL_FreeSurface ( surf ) ;
surf = flipped ;
}
void SDLImage : : verticalFlip ( )
{
2016-11-08 21:14:38 +02:00
margins . x = fullSize . x - surf - > w - margins . x ;
2016-11-07 23:19:53 +02:00
2016-11-08 21:14:38 +02:00
//todo: modify in-place
SDL_Surface * flipped = CSDL_Ext : : verticalFlip ( surf ) ;
2016-11-07 23:19:53 +02:00
SDL_FreeSurface ( surf ) ;
surf = flipped ;
}
2020-01-25 11:21:26 +02:00
// Keep the original palette, in order to do color switching operation
void SDLImage : : savePalette ( )
{
2020-01-26 04:49:53 +02:00
// For some images that don't have palette, skip this
2020-01-26 10:01:48 +02:00
if ( surf - > format - > palette = = nullptr )
2020-01-26 04:49:53 +02:00
return ;
2020-01-26 10:01:48 +02:00
if ( originalPalette = = nullptr )
originalPalette = SDL_AllocPalette ( DEFAULT_PALETTE_COLORS ) ;
SDL_SetPaletteColors ( originalPalette , surf - > format - > palette - > colors , 0 , DEFAULT_PALETTE_COLORS ) ;
2020-01-25 11:21:26 +02:00
}
2016-11-07 23:19:53 +02:00
void SDLImage : : shiftPalette ( int from , int howMany )
{
//works with at most 16 colors, if needed more -> increase values
assert ( howMany < 16 ) ;
if ( surf - > format - > palette )
{
SDL_Color palette [ 16 ] ;
for ( int i = 0 ; i < howMany ; + + i )
{
palette [ ( i + 1 ) % howMany ] = surf - > format - > palette - > colors [ from + i ] ;
}
2023-01-17 22:01:35 +02:00
CSDL_Ext : : setColors ( surf , palette , from , howMany ) ;
2016-11-07 23:19:53 +02:00
}
}
2022-12-15 23:24:03 +02:00
void SDLImage : : adjustPalette ( const ColorFilter & shifter )
2020-01-25 11:21:26 +02:00
{
2020-01-26 10:01:48 +02:00
if ( originalPalette = = nullptr )
return ;
2020-01-25 11:21:26 +02:00
SDL_Palette * palette = surf - > format - > palette ;
2020-01-26 10:01:48 +02:00
// Note: here we skip the first 8 colors in the palette that predefined in H3Palette
for ( int i = 8 ; i < palette - > ncolors ; i + + )
2020-01-25 11:21:26 +02:00
{
2022-12-15 23:24:03 +02:00
palette - > colors [ i ] = shifter . shiftColor ( originalPalette - > colors [ i ] ) ;
2020-01-25 11:21:26 +02:00
}
}
void SDLImage : : resetPalette ( )
{
2020-01-26 10:01:48 +02:00
if ( originalPalette = = nullptr )
return ;
2020-01-25 11:21:26 +02:00
// Always keept the original palette not changed, copy a new palette to assign to surface
2020-01-26 10:01:48 +02:00
SDL_SetPaletteColors ( surf - > format - > palette , originalPalette - > colors , 0 , originalPalette - > ncolors ) ;
2020-01-25 11:21:26 +02:00
}
2022-12-15 23:24:03 +02:00
void SDLImage : : resetPalette ( int colorID )
{
if ( originalPalette = = nullptr )
return ;
// Always keept the original palette not changed, copy a new palette to assign to surface
SDL_SetPaletteColors ( surf - > format - > palette , originalPalette - > colors + colorID , colorID , 1 ) ;
}
2022-12-14 12:04:37 +02:00
void SDLImage : : setSpecialPallete ( const IImage : : SpecialPalette & SpecialPalette )
2017-09-05 17:59:26 +02:00
{
if ( surf - > format - > palette )
{
2023-01-17 22:01:35 +02:00
CSDL_Ext : : setColors ( surf , const_cast < SDL_Color * > ( SpecialPalette . data ( ) ) , 1 , 7 ) ;
2017-09-05 17:59:26 +02:00
}
}
2011-02-06 19:26:27 +02:00
SDLImage : : ~ SDLImage ( )
{
SDL_FreeSurface ( surf ) ;
2020-01-26 10:01:48 +02:00
if ( originalPalette ! = nullptr )
{
SDL_FreePalette ( originalPalette ) ;
originalPalette = nullptr ;
}
2011-02-06 19:26:27 +02:00
}
2010-10-18 18:08:59 +03:00
2018-04-07 13:34:11 +02:00
std : : shared_ptr < IImage > CAnimation : : getFromExtraDef ( std : : string filename )
2011-02-20 11:24:53 +02:00
{
size_t pos = filename . find ( ' : ' ) ;
if ( pos = = - 1 )
2013-06-26 14:18:27 +03:00
return nullptr ;
2011-02-20 11:24:53 +02:00
CAnimation anim ( filename . substr ( 0 , pos ) ) ;
pos + + ;
size_t frame = atoi ( filename . c_str ( ) + pos ) ;
size_t group = 0 ;
pos = filename . find ( ' : ' , pos ) ;
if ( pos ! = - 1 )
{
2016-11-07 23:19:53 +02:00
pos + + ;
2011-02-20 11:24:53 +02:00
group = frame ;
frame = atoi ( filename . c_str ( ) + pos ) ;
}
anim . load ( frame , group ) ;
2018-03-30 13:02:04 +02:00
auto ret = anim . images [ group ] [ frame ] ;
2011-02-20 11:24:53 +02:00
anim . images . clear ( ) ;
return ret ;
}
2017-09-08 13:25:12 +02:00
bool CAnimation : : loadFrame ( size_t frame , size_t group )
2011-02-06 19:26:27 +02:00
{
2017-09-08 13:25:12 +02:00
if ( size ( group ) < = frame )
2011-02-06 19:26:27 +02:00
{
printError ( frame , group , " LoadFrame " ) ;
return false ;
}
2010-10-18 18:08:59 +03:00
2018-03-30 13:02:04 +02:00
auto image = getImage ( frame , group , false ) ;
2017-09-08 13:25:12 +02:00
if ( image )
2011-02-06 19:26:27 +02:00
{
return true ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
//try to get image from def
2017-11-26 23:18:18 +02:00
if ( source [ group ] [ frame ] . getType ( ) = = JsonNode : : JsonType : : DATA_NULL )
2011-02-06 19:26:27 +02:00
{
2017-09-08 13:25:12 +02:00
if ( defFile )
2013-04-22 22:51:22 +03:00
{
2017-09-08 13:25:12 +02:00
auto frameList = defFile - > getEntries ( ) ;
2013-04-23 12:16:20 +03:00
2017-09-08 13:25:12 +02:00
if ( vstd : : contains ( frameList , group ) & & frameList . at ( group ) > frame ) // frame is present
2013-04-23 12:16:20 +03:00
{
2018-07-25 00:36:48 +02:00
images [ group ] [ frame ] = std : : make_shared < SDLImage > ( defFile . get ( ) , frame , group ) ;
2013-04-23 12:16:20 +03:00
return true ;
}
2013-04-22 22:51:22 +03:00
}
2013-04-23 12:16:20 +03:00
// still here? image is missing
2013-04-22 22:51:22 +03:00
2013-04-23 12:16:20 +03:00
printError ( frame , group , " LoadFrame " ) ;
2018-07-25 00:36:48 +02:00
images [ group ] [ frame ] = std : : make_shared < SDLImage > ( " DEFAULT " ) ;
2011-02-06 19:26:27 +02:00
}
else //load from separate file
{
2018-03-30 13:02:04 +02:00
auto img = getFromExtraDef ( source [ group ] [ frame ] [ " file " ] . String ( ) ) ;
2017-09-08 13:25:12 +02:00
if ( ! img )
2018-04-07 13:34:11 +02:00
img = std : : make_shared < SDLImage > ( source [ group ] [ frame ] ) ;
2011-07-21 21:29:22 +03:00
2011-02-20 11:24:53 +02:00
images [ group ] [ frame ] = img ;
2011-02-06 19:26:27 +02:00
return true ;
}
return false ;
}
bool CAnimation : : unloadFrame ( size_t frame , size_t group )
{
2018-03-30 13:02:04 +02:00
auto image = getImage ( frame , group , false ) ;
2018-04-07 13:34:11 +02:00
if ( image )
2011-02-06 19:26:27 +02:00
{
2018-04-07 13:34:11 +02:00
images [ group ] . erase ( frame ) ;
if ( images [ group ] . empty ( ) )
2011-02-06 19:26:27 +02:00
images . erase ( group ) ;
return true ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
return false ;
2010-10-18 18:08:59 +03:00
}
2013-04-22 22:51:22 +03:00
void CAnimation : : initFromJson ( const JsonNode & config )
{
std : : string basepath ;
basepath = config [ " basepath " ] . String ( ) ;
2017-11-26 23:18:18 +02:00
JsonNode base ( JsonNode : : JsonType : : DATA_STRUCT ) ;
2017-09-08 13:25:12 +02:00
base [ " margins " ] = config [ " margins " ] ;
base [ " width " ] = config [ " width " ] ;
base [ " height " ] = config [ " height " ] ;
for ( const JsonNode & group : config [ " sequences " ] . Vector ( ) )
2013-04-22 22:51:22 +03:00
{
2017-09-08 13:25:12 +02:00
size_t groupID = group [ " group " ] . Integer ( ) ; //TODO: string-to-value conversion("moving" -> MOVING)
2013-04-22 22:51:22 +03:00
source [ groupID ] . clear ( ) ;
2017-09-08 13:25:12 +02:00
for ( const JsonNode & frame : group [ " frames " ] . Vector ( ) )
2013-04-22 22:51:22 +03:00
{
2017-11-26 23:18:18 +02:00
JsonNode toAdd ( JsonNode : : JsonType : : DATA_STRUCT ) ;
2017-09-08 13:25:12 +02:00
JsonUtils : : inherit ( toAdd , base ) ;
2017-11-03 17:39:43 +02:00
toAdd [ " file " ] . String ( ) = basepath + frame . String ( ) ;
2017-09-08 13:25:12 +02:00
source [ groupID ] . push_back ( toAdd ) ;
2013-04-22 22:51:22 +03:00
}
}
2017-09-08 13:25:12 +02:00
for ( const JsonNode & node : config [ " images " ] . Vector ( ) )
2013-04-22 22:51:22 +03:00
{
2017-09-08 13:25:12 +02:00
size_t group = node [ " group " ] . Integer ( ) ;
size_t frame = node [ " frame " ] . Integer ( ) ;
2013-04-22 22:51:22 +03:00
if ( source [ group ] . size ( ) < = frame )
source [ group ] . resize ( frame + 1 ) ;
2017-11-26 23:18:18 +02:00
JsonNode toAdd ( JsonNode : : JsonType : : DATA_STRUCT ) ;
2017-09-08 13:25:12 +02:00
JsonUtils : : inherit ( toAdd , base ) ;
toAdd [ " file " ] . String ( ) = basepath + node [ " file " ] . String ( ) ;
source [ group ] [ frame ] = toAdd ;
2013-04-22 22:51:22 +03:00
}
}
2016-11-25 14:23:28 +02:00
void CAnimation : : exportBitmaps ( const boost : : filesystem : : path & path ) const
{
if ( images . empty ( ) )
{
logGlobal - > error ( " Nothing to export, animation is empty " ) ;
return ;
}
boost : : filesystem : : path actualPath = path / " SPRITES " / name ;
boost : : filesystem : : create_directories ( actualPath ) ;
size_t counter = 0 ;
for ( const auto & groupPair : images )
{
size_t group = groupPair . first ;
for ( const auto & imagePair : groupPair . second )
{
size_t frame = imagePair . first ;
2018-03-30 13:02:04 +02:00
const auto img = imagePair . second ;
2016-11-25 14:23:28 +02:00
boost : : format fmt ( " %d_%d.bmp " ) ;
fmt % group % frame ;
img - > exportBitmap ( actualPath / fmt . str ( ) ) ;
counter + + ;
}
}
logGlobal - > info ( " Exported %d frames to %s " , counter , actualPath . string ( ) ) ;
}
2017-09-08 13:25:12 +02:00
void CAnimation : : init ( )
2010-10-18 18:08:59 +03:00
{
2017-09-08 13:25:12 +02:00
if ( defFile )
2010-10-18 18:08:59 +03:00
{
2017-09-08 13:25:12 +02:00
const std : : map < size_t , size_t > defEntries = defFile - > getEntries ( ) ;
2011-02-06 19:26:27 +02:00
2013-07-28 17:49:50 +03:00
for ( auto & defEntry : defEntries )
source [ defEntry . first ] . resize ( defEntry . second ) ;
2010-10-18 18:08:59 +03:00
}
2011-07-21 21:29:22 +03:00
2013-04-22 22:51:22 +03:00
ResourceID resID ( std : : string ( " SPRITES/ " ) + name , EResType : : TEXT ) ;
if ( vstd : : contains ( graphics - > imageLists , resID . getName ( ) ) )
initFromJson ( graphics - > imageLists [ resID . getName ( ) ] ) ;
2013-07-28 17:49:50 +03:00
auto configList = CResourceHandler : : get ( ) - > getResourcesWithName ( resID ) ;
2012-08-06 10:34:37 +03:00
2013-07-28 17:49:50 +03:00
for ( auto & loader : configList )
2011-07-21 21:29:22 +03:00
{
2013-07-28 17:49:50 +03:00
auto stream = loader - > load ( resID ) ;
2012-08-06 10:34:37 +03:00
std : : unique_ptr < ui8 [ ] > textData ( new ui8 [ stream - > getSize ( ) ] ) ;
stream - > read ( textData . get ( ) , stream - > getSize ( ) ) ;
const JsonNode config ( ( char * ) textData . get ( ) , stream - > getSize ( ) ) ;
2011-07-22 19:22:22 +03:00
2013-04-22 22:51:22 +03:00
initFromJson ( config ) ;
2011-07-21 21:29:22 +03:00
}
2011-02-06 19:26:27 +02:00
}
2010-10-18 18:08:59 +03:00
void CAnimation : : printError ( size_t frame , size_t group , std : : string type ) const
{
2017-08-11 13:38:10 +02:00
logGlobal - > error ( " %s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d " , type , name , group , frame ) ;
2010-10-18 18:08:59 +03:00
}
2018-07-25 00:36:48 +02:00
CAnimation : : CAnimation ( std : : string Name ) :
2010-10-18 18:08:59 +03:00
name ( Name ) ,
2017-09-08 13:25:12 +02:00
preloaded ( false ) ,
2018-07-25 00:36:48 +02:00
defFile ( )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
size_t dotPos = name . find_last_of ( ' . ' ) ;
if ( dotPos ! = - 1 )
2010-10-18 18:08:59 +03:00
name . erase ( dotPos ) ;
2011-02-06 19:26:27 +02:00
std : : transform ( name . begin ( ) , name . end ( ) , name . begin ( ) , toupper ) ;
2017-09-08 13:25:12 +02:00
ResourceID resource ( std : : string ( " SPRITES/ " ) + name , EResType : : ANIMATION ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( resource ) )
2018-07-25 00:36:48 +02:00
defFile = std : : make_shared < CDefFile > ( name ) ;
2017-09-08 13:25:12 +02:00
init ( ) ;
2017-09-04 17:41:22 +02:00
if ( source . empty ( ) )
logAnim - > error ( " Animation %s failed to load " , Name ) ;
2010-10-18 18:08:59 +03:00
}
CAnimation : : CAnimation ( ) :
name ( " " ) ,
2017-09-08 13:25:12 +02:00
preloaded ( false ) ,
2018-07-25 00:36:48 +02:00
defFile ( )
2010-10-18 18:08:59 +03:00
{
2017-09-08 13:25:12 +02:00
init ( ) ;
2010-10-18 18:08:59 +03:00
}
2018-07-25 00:36:48 +02:00
CAnimation : : ~ CAnimation ( ) = default ;
2010-10-18 18:08:59 +03:00
2016-11-07 23:19:53 +02:00
void CAnimation : : duplicateImage ( const size_t sourceGroup , const size_t sourceFrame , const size_t targetGroup )
{
2018-07-25 00:36:48 +02:00
if ( ! source . count ( sourceGroup ) )
{
logAnim - > error ( " Group %d missing in %s " , sourceGroup , name ) ;
return ;
}
if ( source [ sourceGroup ] . size ( ) < = sourceFrame )
{
logAnim - > error ( " Frame [%d %d] missing in %s " , sourceGroup , sourceFrame , name ) ;
return ;
}
2016-11-07 23:19:53 +02:00
//todo: clone actual loaded Image object
JsonNode clone ( source [ sourceGroup ] [ sourceFrame ] ) ;
2017-11-26 23:18:18 +02:00
if ( clone . getType ( ) = = JsonNode : : JsonType : : DATA_NULL )
2016-11-07 23:19:53 +02:00
{
std : : string temp = name + " : " + boost : : lexical_cast < std : : string > ( sourceGroup ) + " : " + boost : : lexical_cast < std : : string > ( sourceFrame ) ;
2020-10-06 01:27:04 +02:00
clone [ " file " ] . String ( ) = temp ;
2016-11-07 23:19:53 +02:00
}
source [ targetGroup ] . push_back ( clone ) ;
size_t index = source [ targetGroup ] . size ( ) - 1 ;
if ( preloaded )
load ( index , targetGroup ) ;
}
2011-02-20 11:24:53 +02:00
void CAnimation : : setCustom ( std : : string filename , size_t frame , size_t group )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
if ( source [ group ] . size ( ) < = frame )
source [ group ] . resize ( frame + 1 ) ;
2011-07-22 19:22:22 +03:00
source [ group ] [ frame ] [ " file " ] . String ( ) = filename ;
2011-02-20 11:24:53 +02:00
//FIXME: update image if already loaded
2010-10-18 18:08:59 +03:00
}
2018-04-07 13:34:11 +02:00
std : : shared_ptr < IImage > CAnimation : : getImage ( size_t frame , size_t group , bool verbose ) const
2010-10-18 18:08:59 +03:00
{
2013-06-29 16:05:48 +03:00
auto groupIter = images . find ( group ) ;
2011-02-06 19:26:27 +02:00
if ( groupIter ! = images . end ( ) )
2010-10-18 18:08:59 +03:00
{
2013-06-29 16:05:48 +03:00
auto imageIter = groupIter - > second . find ( frame ) ;
2011-02-06 19:26:27 +02:00
if ( imageIter ! = groupIter - > second . end ( ) )
return imageIter - > second ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
if ( verbose )
printError ( frame , group , " GetImage " ) ;
2013-06-26 14:18:27 +03:00
return nullptr ;
2010-10-18 18:08:59 +03:00
}
void CAnimation : : load ( )
{
2013-06-29 16:05:48 +03:00
for ( auto & elem : source )
for ( size_t image = 0 ; image < elem . second . size ( ) ; image + + )
2017-09-08 13:25:12 +02:00
loadFrame ( image , elem . first ) ;
2010-10-18 18:08:59 +03:00
}
void CAnimation : : unload ( )
{
2013-06-29 16:05:48 +03:00
for ( auto & elem : source )
for ( size_t image = 0 ; image < elem . second . size ( ) ; image + + )
unloadFrame ( image , elem . first ) ;
2011-02-06 19:26:27 +02:00
2010-10-18 18:08:59 +03:00
}
2016-10-18 09:31:31 +02:00
void CAnimation : : preload ( )
{
2016-11-07 23:19:53 +02:00
if ( ! preloaded )
{
preloaded = true ;
load ( ) ;
}
2016-10-18 09:31:31 +02:00
}
2010-10-18 18:08:59 +03:00
void CAnimation : : loadGroup ( size_t group )
{
2011-02-06 19:26:27 +02:00
if ( vstd : : contains ( source , group ) )
for ( size_t image = 0 ; image < source [ group ] . size ( ) ; image + + )
2017-09-08 13:25:12 +02:00
loadFrame ( image , group ) ;
2010-10-18 18:08:59 +03:00
}
void CAnimation : : unloadGroup ( size_t group )
{
2011-02-06 19:26:27 +02:00
if ( vstd : : contains ( source , group ) )
for ( size_t image = 0 ; image < source [ group ] . size ( ) ; image + + )
unloadFrame ( image , group ) ;
2010-10-18 18:08:59 +03:00
}
void CAnimation : : load ( size_t frame , size_t group )
{
2017-09-08 13:25:12 +02:00
loadFrame ( frame , group ) ;
2010-10-18 18:08:59 +03:00
}
void CAnimation : : unload ( size_t frame , size_t group )
{
unloadFrame ( frame , group ) ;
}
2011-02-06 19:26:27 +02:00
size_t CAnimation : : size ( size_t group ) const
2010-10-18 18:08:59 +03:00
{
2013-06-29 16:05:48 +03:00
auto iter = source . find ( group ) ;
2011-02-06 19:26:27 +02:00
if ( iter ! = source . end ( ) )
return iter - > second . size ( ) ;
2010-10-18 18:08:59 +03:00
return 0 ;
}
2017-09-05 15:44:27 +02:00
void CAnimation : : horizontalFlip ( )
{
for ( auto & group : images )
for ( auto & image : group . second )
image . second - > horizontalFlip ( ) ;
}
void CAnimation : : verticalFlip ( )
{
for ( auto & group : images )
for ( auto & image : group . second )
image . second - > verticalFlip ( ) ;
}
void CAnimation : : playerColored ( PlayerColor player )
{
for ( auto & group : images )
for ( auto & image : group . second )
image . second - > playerColored ( player ) ;
}
2017-09-05 19:04:17 +02:00
void CAnimation : : createFlippedGroup ( const size_t sourceGroup , const size_t targetGroup )
{
for ( size_t frame = 0 ; frame < size ( sourceGroup ) ; + + frame )
{
duplicateImage ( sourceGroup , frame , targetGroup ) ;
2018-03-30 13:02:04 +02:00
auto image = getImage ( frame , targetGroup ) ;
2017-09-05 19:04:17 +02:00
image - > verticalFlip ( ) ;
}
}
2015-01-31 00:37:28 +02:00
float CFadeAnimation : : initialCounter ( ) const
{
if ( fadingMode = = EMode : : OUT )
return 1.0f ;
return 0.0f ;
}
void CFadeAnimation : : update ( )
{
if ( ! fading )
return ;
2016-01-29 16:16:14 +02:00
2015-01-31 00:37:28 +02:00
if ( fadingMode = = EMode : : OUT )
fadingCounter - = delta ;
else
fadingCounter + = delta ;
2016-01-29 16:16:14 +02:00
2015-01-31 00:37:28 +02:00
if ( isFinished ( ) )
{
fading = false ;
if ( shouldFreeSurface )
{
SDL_FreeSurface ( fadingSurface ) ;
fadingSurface = nullptr ;
}
}
}
bool CFadeAnimation : : isFinished ( ) const
{
if ( fadingMode = = EMode : : OUT )
return fadingCounter < = 0.0f ;
return fadingCounter > = 1.0f ;
}
CFadeAnimation : : CFadeAnimation ( )
2016-11-27 16:48:18 +02:00
: delta ( 0 ) , fadingSurface ( nullptr ) , fading ( false ) , fadingCounter ( 0 ) , shouldFreeSurface ( false ) ,
2015-01-31 00:37:28 +02:00
fadingMode ( EMode : : NONE )
{
}
CFadeAnimation : : ~ CFadeAnimation ( )
{
if ( fadingSurface & & shouldFreeSurface )
2016-01-29 16:16:14 +02:00
SDL_FreeSurface ( fadingSurface ) ;
2015-01-31 00:37:28 +02:00
}
2017-07-15 13:08:20 +02:00
void CFadeAnimation : : init ( EMode mode , SDL_Surface * sourceSurface , bool freeSurfaceAtEnd , float animDelta )
2015-01-31 00:37:28 +02:00
{
if ( fading )
{
2015-02-02 18:42:42 +02:00
// in that case, immediately finish the previous fade
// (alternatively, we could just return here to ignore the new fade request until this one finished (but we'd need to free the passed bitmap to avoid leaks))
2017-08-10 18:39:27 +02:00
logGlobal - > warn ( " Tried to init fading animation that is already running. " ) ;
2015-02-02 18:42:42 +02:00
if ( fadingSurface & & shouldFreeSurface )
2016-01-29 16:16:14 +02:00
SDL_FreeSurface ( fadingSurface ) ;
}
2015-01-31 00:37:28 +02:00
if ( animDelta < = 0.0f )
{
2017-08-11 13:38:10 +02:00
logGlobal - > warn ( " Fade anim: delta should be positive; %f given. " , animDelta ) ;
2015-01-31 00:37:28 +02:00
animDelta = DEFAULT_DELTA ;
}
2016-01-29 16:16:14 +02:00
2015-01-31 00:37:28 +02:00
if ( sourceSurface )
fadingSurface = sourceSurface ;
2016-01-29 16:16:14 +02:00
2015-01-31 00:37:28 +02:00
delta = animDelta ;
fadingMode = mode ;
fadingCounter = initialCounter ( ) ;
fading = true ;
shouldFreeSurface = freeSurfaceAtEnd ;
}
2023-01-17 22:01:35 +02:00
void CFadeAnimation : : draw ( SDL_Surface * targetSurface , const Point & targetPoint )
2016-01-29 16:16:14 +02:00
{
2015-01-31 00:37:28 +02:00
if ( ! fading | | ! fadingSurface | | fadingMode = = EMode : : NONE )
{
fading = false ;
return ;
}
2016-01-29 16:16:14 +02:00
2020-10-01 10:38:06 +02:00
CSDL_Ext : : setAlpha ( fadingSurface , ( int ) ( fadingCounter * 255 ) ) ;
2023-01-17 22:01:35 +02:00
CSDL_Ext : : blitSurface ( fadingSurface , targetSurface , targetPoint ) ; //FIXME
2015-02-18 16:31:55 +02:00
CSDL_Ext : : setAlpha ( fadingSurface , 255 ) ;
2015-01-31 00:37:28 +02:00
}