2011-12-14 00:23:17 +03:00
# include "StdInc.h"
2014-07-13 20:53:37 +03:00
# include "CAnimation.h"
2011-02-20 11:24:53 +02:00
# include <SDL_image.h>
2010-10-18 18:08:59 +03:00
2014-07-13 20:53:37 +03:00
# include "../CBitmapHandler.h"
# include "../Graphics.h"
# include "../gui/SDL_Extensions.h"
# include "../gui/SDL_Pixels.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
2010-10-18 18:08:59 +03: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-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
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
2011-02-06 19:26:27 +02:00
class CompImageLoader
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
CompImage * image ;
ui8 * position ;
ui8 * entry ;
2011-12-14 00:23:17 +03:00
ui32 currentLine ;
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
inline ui8 typeOf ( ui8 color ) ;
inline void NewEntry ( ui8 color , size_t size ) ;
inline void NewEntry ( const ui8 * & data , size_t size ) ;
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
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
CompImageLoader ( CompImage * Img ) ;
~ CompImageLoader ( ) ;
} ;
2010-10-18 18:08:59 +03:00
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 16:16:14 +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 ( ) ;
}
} ;
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
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 ] =
{
{ 0 , 0 , 0 , 0 } , // 100% - transparency
2013-02-05 22:56:28 +03:00
{ 0 , 0 , 0 , 64 } , // 75% - shadow border,
2011-02-06 19:26:27 +02:00
{ 0 , 0 , 0 , 128 } , // TODO: find exact value
{ 0 , 0 , 0 , 128 } , // TODO: for transparency
{ 0 , 0 , 0 , 128 } , // 50% - shadow body
{ 0 , 0 , 0 , 0 } , // 100% - selection highlight
{ 0 , 0 , 0 , 128 } , // 50% - shadow body below selection
2013-02-05 22:56:28 +03:00
{ 0 , 0 , 0 , 64 } // 75% - shadow border below selection
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
}
2012-12-14 22:47:38 +03:00
if ( type = = 71 | | type = = 64 ) //Buttons/buildings don't have shadows\semi-transparency
2016-01-29 16:16:14 +02:00
memset ( palette . get ( ) , 0 , sizeof ( SDL_Color ) * 2 ) ;
2011-07-27 12:35:27 +03:00
else
2016-01-29 16:16:14 +02:00
memcpy ( palette . get ( ) , H3Palette , sizeof ( SDL_Color ) * 8 ) ; //initialize shadow\selection colors
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
it + = 13 * 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 ) ;
ui32 BaseOffset = sizeof ( SSpriteDef ) ;
2010-11-18 15:34:21 +02:00
2011-12-22 16:05:19 +03:00
loader . init ( Point ( sprite . width , sprite . height ) ,
Point ( sprite . leftMargin , sprite . topMargin ) ,
2016-01-29 16:16:14 +02:00
Point ( sprite . fullWidth , sprite . fullHeight ) , palette . get ( ) ) ;
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +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
2011-12-14 00:23:17 +03: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
loader . Load ( sprite . width , FDef [ currentOffset ] ) ;
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
2011-12-14 00:23:17 +03: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
2011-02-06 19:26:27 +02:00
while ( TotalRowLength < sprite . width )
2010-10-18 18:08:59 +03:00
{
2011-12-14 00:23:17 +03:00
ui8 type = FDef [ currentOffset + + ] ;
ui32 length = FDef [ currentOffset + + ] + 1 ;
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
if ( type = = 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
{
2011-02-06 19:26:27 +02:00
loader . Load ( length , type ) ;
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
2011-12-14 00:23:17 +03: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
2011-02-06 19:26:27 +02:00
while ( TotalRowLength < sprite . width )
2010-10-18 18:08:59 +03:00
{
2011-12-14 00:23:17 +03:00
ui8 SegmentType = FDef [ currentOffset + + ] ;
ui8 code = SegmentType / 32 ;
ui8 length = ( SegmentType & 31 ) + 1 ;
2011-02-06 19:26:27 +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
}
case 3 :
{
2011-12-14 00:23:17 +03: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
2011-02-06 19:26:27 +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
2011-02-06 19:26:27 +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 :
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Error: unsupported format of def file: " < < sprite . format ;
2010-10-18 18:08:59 +03:00
break ;
}
} ;
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
image - > surf = SDL_CreateRGBSurface ( SDL_SWSURFACE , SpriteSize . x , SpriteSize . y , 8 , 0 , 0 , 0 , 0 ) ;
image - > margins = Margins ;
image - > fullSize = FullSize ;
//Prepare surface
2016-01-29 16:16:14 +02:00
SDL_Palette * p = SDL_AllocPalette ( 256 ) ;
2015-09-05 14:03:37 +02:00
SDL_SetPaletteColors ( p , pal , 0 , 256 ) ;
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
}
////////////////////////////////////////////////////////////////////////////////
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
CompImageLoader : : CompImageLoader ( CompImage * Img ) :
image ( Img ) ,
2013-06-26 14:18:27 +03:00
position ( nullptr ) ,
entry ( nullptr ) ,
2011-02-06 19:26:27 +02:00
currentLine ( 0 )
{
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
}
2011-12-22 16:05:19 +03:00
void CompImageLoader : : init ( Point SpriteSize , Point Margins , Point FullSize , SDL_Color * pal )
2011-02-06 19:26:27 +02:00
{
2011-12-22 16:05:19 +03:00
image - > sprite = Rect ( Margins , SpriteSize ) ;
2011-02-06 19:26:27 +02:00
image - > fullSize = FullSize ;
if ( SpriteSize . x & & SpriteSize . y )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
image - > palette = new SDL_Color [ 256 ] ;
memcpy ( ( void * ) image - > palette , ( void * ) pal , 256 * sizeof ( SDL_Color ) ) ;
//Allocate enought space for worst possible case, c-style malloc used due to resizing after load
image - > surf = ( ui8 * ) malloc ( SpriteSize . x * SpriteSize . y * 3 ) ;
2011-12-14 00:23:17 +03:00
image - > line = new ui32 [ SpriteSize . y + 1 ] ;
2011-02-06 19:26:27 +02:00
image - > line [ 0 ] = 0 ;
position = image - > surf ;
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 CompImageLoader : : NewEntry ( ui8 color , size_t size )
{
assert ( color ! = 0xff ) ;
assert ( size & & size < 256 ) ;
entry = position ;
entry [ 0 ] = color ;
entry [ 1 ] = size ;
position + = 2 ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
inline void CompImageLoader : : NewEntry ( const ui8 * & data , size_t size )
{
assert ( size & & size < 256 ) ;
entry = position ;
entry [ 0 ] = 0xff ;
entry [ 1 ] = size ;
position + = 2 ;
memcpy ( position , data , size ) ;
position + = size ;
data + = size ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
inline ui8 CompImageLoader : : typeOf ( ui8 color )
{
if ( color = = 0 )
return 0 ;
2015-06-21 00:13:45 +02:00
2014-05-21 19:04:34 +03:00
if ( image - > palette [ color ] . a ! = 255 )
return 1 ;
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
return 2 ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
inline void CompImageLoader : : Load ( size_t size , const ui8 * data )
{
while ( size )
{
//Try to compress data
while ( true )
{
ui8 color = data [ 0 ] ;
if ( color ! = 0xff )
{
size_t runLength = 1 ;
while ( runLength < size & & color = = data [ runLength ] )
runLength + + ;
2010-11-15 17:15:00 +02:00
2012-12-14 22:47:38 +03:00
if ( runLength > 1 & & runLength < 255 ) //Row of one color found - use RLE
2011-02-06 19:26:27 +02:00
{
Load ( runLength , color ) ;
data + = runLength ;
size - = runLength ;
if ( ! size )
return ;
}
else
break ;
}
else
break ;
}
//Select length for new raw entry
size_t runLength = 1 ;
ui8 color = data [ 0 ] ;
ui8 type = typeOf ( color ) ;
ui8 color2 ;
ui8 type2 ;
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
if ( size > 1 )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
do
{
color2 = data [ runLength ] ;
type2 = typeOf ( color2 ) ;
runLength + + ;
}
//While we have data of this type and different colors
while ( ( runLength < size ) & & ( type = = type2 ) & & ( ( color2 ! = 0xff ) | | ( color2 ! = color ) ) ) ;
}
size - = runLength ;
//add data to last entry
if ( entry & & entry [ 0 ] = = 0xff & & type = = typeOf ( entry [ 2 ] ) )
{
size_t toCopy = std : : min < size_t > ( runLength , 255 - entry [ 1 ] ) ;
runLength - = toCopy ;
entry [ 1 ] + = toCopy ;
memcpy ( position , data , toCopy ) ;
data + = toCopy ;
position + = toCopy ;
}
//Create new entries
while ( runLength > 255 )
{
NewEntry ( data , 255 ) ;
runLength - = 255 ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
if ( runLength )
NewEntry ( data , runLength ) ;
2010-10-18 18:08:59 +03:00
}
}
2011-02-06 19:26:27 +02:00
inline void CompImageLoader : : Load ( size_t size , ui8 color )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
if ( ! size )
return ;
if ( color = = 0xff )
2010-10-18 18:08:59 +03:00
{
2013-06-29 16:05:48 +03:00
auto tmpbuf = new ui8 [ size ] ;
2011-02-06 19:26:27 +02:00
memset ( ( void * ) tmpbuf , color , size ) ;
Load ( size , tmpbuf ) ;
delete [ ] tmpbuf ;
return ;
}
//Current entry is RLE with same color as new block
if ( entry & & entry [ 0 ] = = color )
{
size_t toCopy = std : : min < size_t > ( size , 255 - entry [ 1 ] ) ;
size - = toCopy ;
entry [ 1 ] + = toCopy ;
}
//Create new entries
while ( size > 255 )
{
NewEntry ( color , 255 ) ;
size - = 255 ;
}
if ( size )
NewEntry ( color , size ) ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
void CompImageLoader : : EndLine ( )
{
currentLine + + ;
image - > line [ currentLine ] = position - image - > surf ;
2013-06-26 14:18:27 +03:00
entry = nullptr ;
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
CompImageLoader : : ~ CompImageLoader ( )
{
if ( ! image - > surf )
return ;
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
ui8 * newPtr = ( ui8 * ) realloc ( ( void * ) image - > surf , position - image - > surf ) ;
if ( newPtr )
image - > surf = newPtr ;
}
/*************************************************************************
* Classes for images , support loading from file and drawing on surface *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
IImage : : IImage ( ) :
refCount ( 1 )
{
}
bool IImage : : decreaseRef ( )
{
refCount - - ;
return refCount < = 0 ;
}
void IImage : : increaseRef ( )
{
refCount + + ;
}
SDLImage : : SDLImage ( CDefFile * data , size_t frame , size_t group , bool compressed ) :
2013-06-26 14:18:27 +03:00
surf ( nullptr )
2011-02-06 19:26:27 +02:00
{
SDLImageLoader loader ( this ) ;
data - > loadFrame ( frame , group , loader ) ;
}
SDLImage : : SDLImage ( SDL_Surface * from , bool extraRef ) :
margins ( 0 , 0 )
{
surf = from ;
if ( extraRef )
surf - > refcount + + ;
fullSize . x = surf - > w ;
fullSize . y = surf - > h ;
}
SDLImage : : SDLImage ( std : : string filename , bool compressed ) :
margins ( 0 , 0 )
{
2011-07-18 18:21:16 +03:00
surf = BitmapHandler : : loadBitmap ( filename ) ;
2011-05-28 13:05:25 +03:00
2013-06-26 14:18:27 +03:00
if ( surf = = nullptr )
2011-05-28 13:05:25 +03:00
{
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Error: failed to load image " < < filename ;
2013-11-24 14:36:51 +03:00
return ;
2011-05-28 13:05:25 +03:00
}
else
{
fullSize . x = surf - > w ;
fullSize . y = surf - > h ;
}
2011-07-21 21:29:22 +03:00
if ( compressed )
{
SDL_Surface * temp = surf ;
// add RLE flag
2014-05-21 21:43:44 +03:00
if ( surf - > format - > palette )
{
2014-07-02 21:20:54 +03:00
CSDL_Ext : : setColorKey ( temp , temp - > format - > palette - > colors [ 0 ] ) ;
2014-05-21 21:43:44 +03:00
}
2016-01-29 16:16:14 +02:00
SDL_SetSurfaceRLE ( temp , SDL_RLEACCEL ) ;
2011-07-21 21:29:22 +03:00
// convert surface to enable RLE
surf = SDL_ConvertSurface ( temp , temp - > format , temp - > flags ) ;
SDL_FreeSurface ( temp ) ;
}
2011-02-06 19:26:27 +02:00
}
2011-12-22 16:05:19 +03:00
void SDLImage : : draw ( SDL_Surface * where , int posX , int posY , Rect * src , ui8 rotation ) const
2011-03-15 19:25:51 +02:00
{
if ( ! surf )
2011-02-20 11:24:53 +02:00
return ;
2011-12-22 16:05:19 +03:00
Rect sourceRect ( margins . x , margins . y , surf - > w , surf - > h ) ;
2011-02-06 19:26:27 +02:00
//TODO: rotation and scaling
if ( src )
{
sourceRect = sourceRect & * src ;
2010-10-18 18:08:59 +03:00
}
2011-12-22 16:05:19 +03:00
Rect destRect ( posX , posY , surf - > w , surf - > h ) ;
2011-02-06 19:26:27 +02:00
destRect + = sourceRect . topLeft ( ) ;
sourceRect - = margins ;
CSDL_Ext : : blitSurface ( surf , & sourceRect , where , & destRect ) ;
2010-10-18 18:08:59 +03:00
}
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
}
2011-02-06 19:26:27 +02:00
int SDLImage : : width ( ) const
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
return fullSize . x ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
int SDLImage : : height ( ) const
{
return fullSize . y ;
}
SDLImage : : ~ SDLImage ( )
{
SDL_FreeSurface ( surf ) ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
CompImage : : CompImage ( const CDefFile * data , size_t frame , size_t group ) :
2013-06-26 14:18:27 +03:00
surf ( nullptr ) ,
line ( nullptr ) ,
palette ( nullptr )
2016-01-29 16:16:14 +02:00
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
CompImageLoader loader ( this ) ;
data - > loadFrame ( frame , group , loader ) ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
CompImage : : CompImage ( SDL_Surface * surf )
{
//TODO
assert ( 0 ) ;
2010-10-18 18:08:59 +03:00
}
2011-12-22 16:05:19 +03:00
void CompImage : : draw ( SDL_Surface * where , int posX , int posY , Rect * src , ui8 alpha ) const
2010-10-18 18:08:59 +03:00
{
2011-03-22 15:19:07 +02:00
int rotation = 0 ; //TODO
2011-02-20 11:24:53 +02:00
//rotation & 2 = horizontal rotation
//rotation & 4 = vertical rotation
2011-02-06 19:26:27 +02:00
if ( ! surf )
return ;
2011-12-22 16:05:19 +03:00
Rect sourceRect ( sprite ) ;
2011-02-06 19:26:27 +02:00
//TODO: rotation and scaling
if ( src )
sourceRect = sourceRect & * src ;
//Limit source rect to sizes of surface
2011-12-22 16:05:19 +03:00
sourceRect = sourceRect & Rect ( 0 , 0 , where - > w , where - > h ) ;
2011-02-06 19:26:27 +02:00
//Starting point on SDL surface
2011-12-22 16:05:19 +03:00
Point dest ( posX + sourceRect . x , posY + sourceRect . y ) ;
2011-02-20 11:24:53 +02:00
if ( rotation & 2 )
dest . y + = sourceRect . h ;
if ( rotation & 4 )
dest . x + = sourceRect . w ;
2011-02-06 19:26:27 +02:00
sourceRect - = sprite . topLeft ( ) ;
for ( int currY = 0 ; currY < sourceRect . h ; currY + + )
2010-11-15 17:15:00 +02:00
{
2011-02-06 19:26:27 +02:00
ui8 * data = surf + line [ currY + sourceRect . y ] ;
ui8 type = * ( data + + ) ;
ui8 size = * ( data + + ) ;
int currX = sourceRect . x ;
//Skip blocks until starting position reached
while ( currX > size )
{
currX - = size ;
if ( type = = 0xff )
data + = size ;
type = * ( data + + ) ;
size = * ( data + + ) ;
}
//This block will be shown partially - calculate size\position
size - = currX ;
if ( type = = 0xff )
data + = currX ;
currX = 0 ;
ui8 bpp = where - > format - > BytesPerPixel ;
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
//Calculate position for blitting: pixels + Y + X
ui8 * blitPos = ( ui8 * ) where - > pixels ;
2011-02-20 11:24:53 +02:00
if ( rotation & 4 )
blitPos + = ( dest . y - currY ) * where - > pitch ;
else
blitPos + = ( dest . y + currY ) * where - > pitch ;
2011-02-06 19:26:27 +02:00
blitPos + = dest . x * bpp ;
2011-02-20 11:24:53 +02:00
//Blit blocks that must be fully visible
2011-02-06 19:26:27 +02:00
while ( currX + size < sourceRect . w )
{
2011-02-20 11:24:53 +02:00
//blit block, pointers will be modified if needed
2011-03-22 15:19:07 +02:00
BlitBlockWithBpp ( bpp , type , size , data , blitPos , alpha , rotation & 2 ) ;
2011-02-06 19:26:27 +02:00
currX + = size ;
type = * ( data + + ) ;
size = * ( data + + ) ;
}
//Blit last, semi-visible block
size = sourceRect . w - currX ;
2011-03-22 15:19:07 +02:00
BlitBlockWithBpp ( bpp , type , size , data , blitPos , alpha , rotation & 2 ) ;
2010-11-15 17:15:00 +02:00
}
2011-02-06 19:26:27 +02:00
}
2010-10-18 18:08:59 +03:00
2011-03-22 15:19:07 +02:00
# define CASEBPP(x,y) case x: BlitBlock<x,y>(type, size, data, dest, alpha); break
2011-02-06 19:26:27 +02:00
2011-02-20 11:24:53 +02:00
//FIXME: better way to get blitter
2011-03-22 15:19:07 +02:00
void CompImage : : BlitBlockWithBpp ( ui8 bpp , ui8 type , ui8 size , ui8 * & data , ui8 * & dest , ui8 alpha , bool rotated ) const
2011-02-20 11:24:53 +02:00
{
assert ( bpp > 1 & & bpp < 5 ) ;
2016-01-29 16:16:14 +02:00
2011-02-20 11:24:53 +02:00
if ( rotated )
switch ( bpp )
{
CASEBPP ( 2 , 1 ) ;
CASEBPP ( 3 , 1 ) ;
CASEBPP ( 4 , 1 ) ;
}
else
switch ( bpp )
{
CASEBPP ( 2 , 1 ) ;
CASEBPP ( 3 , 1 ) ;
CASEBPP ( 4 , 1 ) ;
}
2011-02-06 19:26:27 +02:00
}
2011-02-20 11:24:53 +02:00
# undef CASEBPP
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
//Blit one block from RLE-d surface
2011-02-20 11:24:53 +02:00
template < int bpp , int dir >
2011-03-22 15:19:07 +02:00
void CompImage : : BlitBlock ( ui8 type , ui8 size , ui8 * & data , ui8 * & dest , ui8 alpha ) const
2011-02-06 19:26:27 +02:00
{
//Raw data
if ( type = = 0xff )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
ui8 color = * data ;
2011-03-22 15:19:07 +02:00
if ( alpha ! = 255 ) //Per-surface alpha is set
{
for ( size_t i = 0 ; i < size ; i + + )
{
SDL_Color col = palette [ * ( data + + ) ] ;
2014-05-21 19:04:34 +03:00
col . a = ( ui32 ) col . a * alpha / 255 ;
2011-03-22 15:19:07 +02:00
ColorPutter < bpp , 1 > : : PutColorAlpha ( dest , col ) ;
}
return ;
}
2015-06-21 00:13:45 +02:00
2014-05-21 19:04:34 +03:00
if ( palette [ color ] . a = = 255 )
2011-02-06 19:26:27 +02:00
{
//Put row of RGB data
2011-03-22 15:19:07 +02:00
for ( size_t i = 0 ; i < size ; i + + )
2011-02-06 19:26:27 +02:00
ColorPutter < bpp , 1 > : : PutColor ( dest , palette [ * ( data + + ) ] ) ;
}
2010-10-18 18:08:59 +03:00
else
{
2011-02-06 19:26:27 +02:00
//Put row of RGBA data
2011-03-22 15:19:07 +02:00
for ( size_t i = 0 ; i < size ; i + + )
2011-02-06 19:26:27 +02:00
ColorPutter < bpp , 1 > : : PutColorAlpha ( dest , palette [ * ( data + + ) ] ) ;
2016-01-29 16:16:14 +02:00
2011-02-06 19:26:27 +02:00
}
}
//RLE-d sequence
else
{
2014-05-21 19:04:34 +03:00
if ( alpha ! = 255 & & palette [ type ] . a ! = 0 ) //Per-surface alpha is set
{
SDL_Color col = palette [ type ] ;
col . a = ( int ) col . a * ( 255 - alpha ) / 255 ;
for ( size_t i = 0 ; i < size ; i + + )
ColorPutter < bpp , 1 > : : PutColorAlpha ( dest , col ) ;
return ;
}
2016-01-29 16:16:14 +02:00
2014-05-21 19:04:34 +03:00
switch ( palette [ type ] . a )
2011-02-06 19:26:27 +02:00
{
case 0 :
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
//Skip row
dest + = size * bpp ;
break ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
case 255 :
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
//Put RGB row
2011-02-20 11:24:53 +02:00
ColorPutter < bpp , 1 > : : PutColorRow ( dest , palette [ type ] , size ) ;
2011-02-06 19:26:27 +02:00
break ;
}
default :
{
//Put RGBA row
2011-03-22 15:19:07 +02:00
for ( size_t i = 0 ; i < size ; i + + )
2011-02-06 19:26:27 +02:00
ColorPutter < bpp , 1 > : : PutColorAlpha ( dest , palette [ type ] ) ;
break ;
2010-10-18 18:08:59 +03:00
}
}
}
2011-02-06 19:26:27 +02:00
}
2013-03-03 20:06:03 +03:00
void CompImage : : playerColored ( PlayerColor player )
2011-02-06 19:26:27 +02:00
{
2013-06-26 14:18:27 +03:00
SDL_Color * pal = nullptr ;
2013-03-03 20:06:03 +03:00
if ( player < PlayerColor : : PLAYER_LIMIT )
2010-10-18 18:08:59 +03:00
{
2013-03-03 20:06:03 +03:00
pal = graphics - > playerColorPalette + 32 * player . getNum ( ) ;
2010-10-18 18:08:59 +03:00
}
2013-03-03 20:06:03 +03:00
else if ( player = = PlayerColor : : NEUTRAL )
2011-02-06 19:26:27 +02:00
{
pal = graphics - > neutralColorPalette ;
}
else
assert ( 0 ) ;
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
for ( int i = 0 ; i < 32 ; + + i )
{
2014-05-23 13:51:38 +03:00
CSDL_Ext : : colorAssign ( palette [ 224 + i ] , pal [ i ] ) ;
2011-02-06 19:26:27 +02:00
}
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
int CompImage : : width ( ) const
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
return fullSize . x ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
int CompImage : : height ( ) const
{
return fullSize . y ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
CompImage : : ~ CompImage ( )
2010-10-18 18:08:59 +03:00
{
2011-02-06 19:26:27 +02:00
free ( surf ) ;
delete [ ] line ;
delete [ ] palette ;
}
/*************************************************************************
* CAnimation for animations handling , can load part of file if needed *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-10-18 18:08:59 +03:00
2011-02-20 11:24:53 +02:00
IImage * CAnimation : : getFromExtraDef ( std : : string filename )
{
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 )
{
group = frame ;
frame = atoi ( filename . c_str ( ) + pos ) ;
}
anim . load ( frame , group ) ;
IImage * ret = anim . images [ group ] [ frame ] ;
anim . images . clear ( ) ;
return ret ;
}
2011-02-06 19:26:27 +02:00
bool CAnimation : : loadFrame ( CDefFile * file , size_t frame , size_t group )
{
if ( size ( group ) < = frame )
{
printError ( frame , group , " LoadFrame " ) ;
return false ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
IImage * image = getImage ( frame , group , false ) ;
if ( image )
{
image - > increaseRef ( ) ;
return true ;
}
2010-10-18 18:08:59 +03:00
2011-02-06 19:26:27 +02:00
//try to get image from def
2011-07-21 21:29:22 +03:00
if ( source [ group ] [ frame ] . getType ( ) = = JsonNode : : DATA_NULL )
2011-02-06 19:26:27 +02:00
{
2013-04-23 12:16:20 +03:00
if ( file )
2013-04-22 22:51:22 +03:00
{
2013-04-23 12:16:20 +03:00
auto frameList = file - > getEntries ( ) ;
if ( vstd : : contains ( frameList , group ) & & frameList . at ( group ) > frame ) // frame is present
{
if ( compressed )
images [ group ] [ frame ] = new CompImage ( file , frame , group ) ;
else
images [ group ] [ frame ] = new SDLImage ( file , frame , group ) ;
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 " ) ;
images [ group ] [ frame ] = new SDLImage ( " DEFAULT " , compressed ) ;
2011-02-06 19:26:27 +02:00
}
else //load from separate file
{
2011-07-21 21:29:22 +03:00
std : : string filename = source [ group ] [ frame ] . Struct ( ) . find ( " file " ) - > second . String ( ) ;
2016-01-29 16:16:14 +02:00
2011-07-21 21:29:22 +03:00
IImage * img = getFromExtraDef ( filename ) ;
if ( ! img )
img = new SDLImage ( filename , compressed ) ;
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 )
{
IImage * image = getImage ( frame , group , false ) ;
if ( image )
{
//decrease ref count for image and delete if needed
if ( image - > decreaseRef ( ) )
2010-11-15 17:15:00 +02:00
{
2011-02-06 19:26:27 +02:00
delete image ;
images [ group ] . erase ( frame ) ;
2010-10-18 18:08:59 +03:00
}
2011-02-06 19:26:27 +02:00
if ( images [ group ] . empty ( ) )
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 ( ) ;
2013-06-29 16:05:48 +03:00
for ( const JsonNode & group : config [ " sequences " ] . Vector ( ) )
2013-04-22 22:51:22 +03:00
{
size_t groupID = group [ " group " ] . Float ( ) ; //TODO: string-to-value conversion("moving" -> MOVING)
source [ groupID ] . clear ( ) ;
2013-06-29 16:05:48 +03:00
for ( const JsonNode & frame : group [ " frames " ] . Vector ( ) )
2013-04-22 22:51:22 +03:00
{
source [ groupID ] . push_back ( JsonNode ( ) ) ;
std : : string filename = frame . String ( ) ;
source [ groupID ] . back ( ) [ " file " ] . String ( ) = basepath + filename ;
}
}
2013-06-29 16:05:48 +03:00
for ( const JsonNode & node : config [ " images " ] . Vector ( ) )
2013-04-22 22:51:22 +03:00
{
size_t group = node [ " group " ] . Float ( ) ;
size_t frame = node [ " frame " ] . Float ( ) ;
if ( source [ group ] . size ( ) < = frame )
source [ group ] . resize ( frame + 1 ) ;
source [ group ] [ frame ] = node ;
std : : string filename = node [ " file " ] . String ( ) ;
source [ group ] [ frame ] [ " file " ] . String ( ) = basepath + filename ;
}
}
2011-02-06 19:26:27 +02:00
void CAnimation : : init ( CDefFile * file )
2010-10-18 18:08:59 +03:00
{
2011-07-21 21:29:22 +03:00
if ( file )
2010-10-18 18:08:59 +03:00
{
2011-02-20 11:24:53 +02:00
const std : : map < size_t , size_t > defEntries = file - > 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
}
CDefFile * CAnimation : : getFile ( ) const
{
2012-08-01 15:02:54 +03:00
ResourceID identifier ( std : : string ( " SPRITES/ " ) + name , EResType : : ANIMATION ) ;
if ( CResourceHandler : : get ( ) - > existsResource ( identifier ) )
2011-02-06 19:26:27 +02:00
return new CDefFile ( name ) ;
2013-06-26 14:18:27 +03:00
return nullptr ;
2010-10-18 18:08:59 +03:00
}
void CAnimation : : printError ( size_t frame , size_t group , std : : string type ) const
{
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < type < < " error: Request for frame not present in CAnimation! "
< < " \t File name: " < < name < < " Group: " < < group < < " Frame: " < < frame ;
2010-10-18 18:08:59 +03:00
}
CAnimation : : CAnimation ( std : : string Name , bool Compressed ) :
name ( Name ) ,
2011-02-06 19:26:27 +02:00
compressed ( Compressed )
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 ) ;
2010-10-18 18:08:59 +03:00
CDefFile * file = getFile ( ) ;
init ( file ) ;
delete file ;
2011-03-22 15:19:07 +02:00
loadedAnims . insert ( this ) ;
2010-10-18 18:08:59 +03:00
}
CAnimation : : CAnimation ( ) :
name ( " " ) ,
2011-02-06 19:26:27 +02:00
compressed ( false )
2010-10-18 18:08:59 +03:00
{
2013-06-26 14:18:27 +03:00
init ( nullptr ) ;
2011-03-22 15:19:07 +02:00
loadedAnims . insert ( this ) ;
2010-10-18 18:08:59 +03:00
}
CAnimation : : ~ CAnimation ( )
{
2011-02-20 11:24:53 +02:00
if ( ! images . empty ( ) )
{
2013-04-09 17:31:36 +03:00
logGlobal - > warnStream ( ) < < " Warning: not all frames were unloaded from " < < name ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : images )
for ( auto & _image : elem . second )
delete _image . second ;
2011-02-20 11:24:53 +02:00
}
2011-03-22 15:19:07 +02:00
loadedAnims . erase ( this ) ;
2010-10-18 18:08:59 +03:00
}
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
}
2011-02-06 19:26:27 +02:00
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 ( )
{
CDefFile * file = getFile ( ) ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : source )
for ( size_t image = 0 ; image < elem . second . size ( ) ; image + + )
loadFrame ( file , image , elem . first ) ;
2010-10-18 18:08:59 +03:00
delete file ;
}
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
}
void CAnimation : : loadGroup ( size_t group )
{
CDefFile * file = getFile ( ) ;
2011-02-06 19:26:27 +02:00
if ( vstd : : contains ( source , group ) )
for ( size_t image = 0 ; image < source [ group ] . size ( ) ; image + + )
loadFrame ( file , image , group ) ;
2010-11-15 17:15:00 +02:00
delete file ;
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 )
{
CDefFile * file = getFile ( ) ;
loadFrame ( file , frame , group ) ;
delete file ;
}
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 ;
}
2011-03-22 15:19:07 +02:00
std : : set < CAnimation * > CAnimation : : loadedAnims ;
2011-03-27 20:24:30 +03:00
void CAnimation : : getAnimInfo ( )
2011-03-22 15:19:07 +02:00
{
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Animation stats: Loaded " < < loadedAnims . size ( ) < < " total " ;
2013-06-29 16:05:48 +03:00
for ( auto anim : loadedAnims )
2011-03-22 15:19:07 +02:00
{
2016-01-29 16:16:14 +02:00
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " Name: " < < anim - > name < < " Groups: " < < anim - > images . size ( ) ;
2011-03-22 15:19:07 +02:00
if ( ! anim - > images . empty ( ) )
2013-04-09 17:31:36 +03:00
logGlobal - > errorStream ( ) < < " , " < < anim - > images . begin ( ) - > second . size ( ) < < " image loaded in group " < < anim - > images . begin ( ) - > first ;
2011-03-27 20:24:30 +03:00
}
}
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 ( )
: fadingSurface ( nullptr ) ,
fading ( false ) ,
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
}
2015-02-02 18:42:42 +02:00
void CFadeAnimation : : init ( EMode mode , SDL_Surface * sourceSurface , bool freeSurfaceAtEnd /* = false */ , float animDelta /* = DEFAULT_DELTA */ )
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))
2015-01-31 00:37:28 +02:00
logGlobal - > warnStream ( ) < < " 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 )
{
logGlobal - > warnStream ( ) < < " Fade anim: delta should be positive; " < < animDelta < < " given. " ;
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 ;
}
void CFadeAnimation : : draw ( SDL_Surface * targetSurface , const SDL_Rect * sourceRect , SDL_Rect * destRect )
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
2015-02-18 16:31:55 +02:00
CSDL_Ext : : setAlpha ( fadingSurface , fadingCounter * 255 ) ;
SDL_BlitSurface ( fadingSurface , const_cast < SDL_Rect * > ( sourceRect ) , targetSurface , destRect ) ; //FIXME
CSDL_Ext : : setAlpha ( fadingSurface , 255 ) ;
2015-01-31 00:37:28 +02:00
}