CAnimation.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  1. /*
  2. * CAnimation.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "CAnimation.h"
  12. #include <SDL_image.h>
  13. #include <SDL.h>
  14. #include "../CBitmapHandler.h"
  15. #include "../Graphics.h"
  16. #include "../gui/SDL_Extensions.h"
  17. #include "../gui/SDL_Pixels.h"
  18. #include "../lib/filesystem/Filesystem.h"
  19. #include "../lib/filesystem/ISimpleResourceLoader.h"
  20. #include "../lib/JsonNode.h"
  21. #include "../lib/CRandomGenerator.h"
  22. class SDLImageLoader;
  23. typedef std::map <size_t, std::vector <JsonNode> > source_map;
  24. typedef std::map<size_t, IImage* > image_map;
  25. typedef std::map<size_t, image_map > group_map;
  26. /// Class for def loading
  27. /// After loading will store general info (palette and frame offsets) and pointer to file itself
  28. class CDefFile
  29. {
  30. private:
  31. struct SSpriteDef
  32. {
  33. ui32 size;
  34. ui32 format; /// format in which pixel data is stored
  35. ui32 fullWidth; /// full width and height of frame, including borders
  36. ui32 fullHeight;
  37. ui32 width; /// width and height of pixel data, borders excluded
  38. ui32 height;
  39. si32 leftMargin;
  40. si32 topMargin;
  41. } PACKED_STRUCT;
  42. //offset[group][frame] - offset of frame data in file
  43. std::map<size_t, std::vector <size_t> > offset;
  44. std::unique_ptr<ui8[]> data;
  45. std::unique_ptr<SDL_Color[]> palette;
  46. public:
  47. CDefFile(std::string Name);
  48. ~CDefFile();
  49. //load frame as SDL_Surface
  50. template<class ImageLoader>
  51. void loadFrame(size_t frame, size_t group, ImageLoader &loader) const;
  52. const std::map<size_t, size_t> getEntries() const;
  53. };
  54. /*
  55. * Wrapper around SDL_Surface
  56. */
  57. class SDLImage : public IImage
  58. {
  59. public:
  60. const static int DEFAULT_PALETTE_COLORS = 256;
  61. //Surface without empty borders
  62. SDL_Surface * surf;
  63. //size of left and top borders
  64. Point margins;
  65. //total size including borders
  66. Point fullSize;
  67. public:
  68. //Load image from def file
  69. SDLImage(CDefFile *data, size_t frame, size_t group=0);
  70. //Load from bitmap file
  71. SDLImage(std::string filename);
  72. SDLImage(const JsonNode & conf);
  73. //Create using existing surface, extraRef will increase refcount on SDL_Surface
  74. SDLImage(SDL_Surface * from, bool extraRef);
  75. ~SDLImage();
  76. // Keep the original palette, in order to do color switching operation
  77. void savePalette();
  78. void draw(SDL_Surface * where, int posX=0, int posY=0, Rect *src=nullptr, ui8 alpha=255) const override;
  79. void draw(SDL_Surface * where, SDL_Rect * dest, SDL_Rect * src, ui8 alpha=255) const override;
  80. std::shared_ptr<IImage> scaleFast(float scale) const override;
  81. void exportBitmap(const boost::filesystem::path & path) const override;
  82. void playerColored(PlayerColor player) override;
  83. void setFlagColor(PlayerColor player) override;
  84. int width() const override;
  85. int height() const override;
  86. void horizontalFlip() override;
  87. void verticalFlip() override;
  88. void shiftPalette(int from, int howMany) override;
  89. void adjustPalette(const ColorShifter * shifter) override;
  90. void resetPalette() override;
  91. void setBorderPallete(const BorderPallete & borderPallete) override;
  92. friend class SDLImageLoader;
  93. private:
  94. SDL_Palette * originalPalette;
  95. };
  96. class SDLImageLoader
  97. {
  98. SDLImage * image;
  99. ui8 * lineStart;
  100. ui8 * position;
  101. public:
  102. //load size raw pixels from data
  103. inline void Load(size_t size, const ui8 * data);
  104. //set size pixels to color
  105. inline void Load(size_t size, ui8 color=0);
  106. inline void EndLine();
  107. //init image with these sizes and palette
  108. inline void init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal);
  109. SDLImageLoader(SDLImage * Img);
  110. ~SDLImageLoader();
  111. };
  112. // Extremely simple file cache. TODO: smarter, more general solution
  113. class CFileCache
  114. {
  115. static const int cacheSize = 50; //Max number of cached files
  116. struct FileData
  117. {
  118. ResourceID name;
  119. size_t size;
  120. std::unique_ptr<ui8[]> data;
  121. std::unique_ptr<ui8[]> getCopy()
  122. {
  123. auto ret = std::unique_ptr<ui8[]>(new ui8[size]);
  124. std::copy(data.get(), data.get() + size, ret.get());
  125. return ret;
  126. }
  127. FileData(ResourceID name_, size_t size_, std::unique_ptr<ui8[]> data_):
  128. name{std::move(name_)},
  129. size{size_},
  130. data{std::move(data_)}
  131. {}
  132. };
  133. std::deque<FileData> cache;
  134. public:
  135. std::unique_ptr<ui8[]> getCachedFile(ResourceID rid)
  136. {
  137. for(auto & file : cache)
  138. {
  139. if (file.name == rid)
  140. return file.getCopy();
  141. }
  142. // Still here? Cache miss
  143. if (cache.size() > cacheSize)
  144. cache.pop_front();
  145. auto data = CResourceHandler::get()->load(rid)->readAll();
  146. cache.emplace_back(std::move(rid), data.second, std::move(data.first));
  147. return cache.back().getCopy();
  148. }
  149. };
  150. enum class DefType : uint32_t
  151. {
  152. SPELL = 0x40,
  153. SPRITE = 0x41,
  154. CREATURE = 0x42,
  155. MAP = 0x43,
  156. MAP_HERO = 0x44,
  157. TERRAIN = 0x45,
  158. CURSOR = 0x46,
  159. INTERFACE = 0x47,
  160. SPRITE_FRAME = 0x48,
  161. BATTLE_HERO = 0x49
  162. };
  163. static CFileCache animationCache;
  164. /*************************************************************************
  165. * DefFile, class used for def loading *
  166. *************************************************************************/
  167. bool operator== (const SDL_Color & lhs, const SDL_Color & rhs)
  168. {
  169. return (lhs.a == rhs.a) && (lhs.b == rhs.b) &&(lhs.g == rhs.g) &&(lhs.r == rhs.r);
  170. }
  171. CDefFile::CDefFile(std::string Name):
  172. data(nullptr),
  173. palette(nullptr)
  174. {
  175. #if 0
  176. static SDL_Color H3_ORIG_PALETTE[8] =
  177. {
  178. { 0, 255, 255, SDL_ALPHA_OPAQUE},
  179. {255, 150, 255, SDL_ALPHA_OPAQUE},
  180. {255, 100, 255, SDL_ALPHA_OPAQUE},
  181. {255, 50, 255, SDL_ALPHA_OPAQUE},
  182. {255, 0, 255, SDL_ALPHA_OPAQUE},
  183. {255, 255, 0, SDL_ALPHA_OPAQUE},
  184. {180, 0, 255, SDL_ALPHA_OPAQUE},
  185. { 0, 255, 0, SDL_ALPHA_OPAQUE}
  186. };
  187. #endif // 0
  188. //First 8 colors in def palette used for transparency
  189. static SDL_Color H3Palette[8] =
  190. {
  191. { 0, 0, 0, 0},// 100% - transparency
  192. { 0, 0, 0, 32},// 75% - shadow border,
  193. { 0, 0, 0, 64},// TODO: find exact value
  194. { 0, 0, 0, 128},// TODO: for transparency
  195. { 0, 0, 0, 128},// 50% - shadow body
  196. { 0, 0, 0, 0},// 100% - selection highlight
  197. { 0, 0, 0, 128},// 50% - shadow body below selection
  198. { 0, 0, 0, 64} // 75% - shadow border below selection
  199. };
  200. data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
  201. palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]);
  202. int it = 0;
  203. ui32 type = read_le_u32(data.get() + it);
  204. it+=4;
  205. //int width = read_le_u32(data + it); it+=4;//not used
  206. //int height = read_le_u32(data + it); it+=4;
  207. it+=8;
  208. ui32 totalBlocks = read_le_u32(data.get() + it);
  209. it+=4;
  210. for (ui32 i= 0; i<256; i++)
  211. {
  212. palette[i].r = data[it++];
  213. palette[i].g = data[it++];
  214. palette[i].b = data[it++];
  215. palette[i].a = SDL_ALPHA_OPAQUE;
  216. }
  217. switch(static_cast<DefType>(type))
  218. {
  219. case DefType::SPELL:
  220. palette[0] = H3Palette[0];
  221. break;
  222. case DefType::SPRITE:
  223. case DefType::SPRITE_FRAME:
  224. for(ui32 i= 0; i<8; i++)
  225. palette[i] = H3Palette[i];
  226. break;
  227. case DefType::CREATURE:
  228. palette[0] = H3Palette[0];
  229. palette[1] = H3Palette[1];
  230. palette[4] = H3Palette[4];
  231. palette[5] = H3Palette[5];
  232. palette[6] = H3Palette[6];
  233. palette[7] = H3Palette[7];
  234. break;
  235. case DefType::MAP:
  236. case DefType::MAP_HERO:
  237. palette[0] = H3Palette[0];
  238. palette[1] = H3Palette[1];
  239. palette[4] = H3Palette[4];
  240. //5 = owner flag, handled separately
  241. break;
  242. case DefType::TERRAIN:
  243. palette[0] = H3Palette[0];
  244. palette[1] = H3Palette[1];
  245. palette[2] = H3Palette[2];
  246. palette[3] = H3Palette[3];
  247. palette[4] = H3Palette[4];
  248. break;
  249. case DefType::CURSOR:
  250. palette[0] = H3Palette[0];
  251. break;
  252. case DefType::INTERFACE:
  253. palette[0] = H3Palette[0];
  254. palette[1] = H3Palette[1];
  255. palette[4] = H3Palette[4];
  256. //player colors handled separately
  257. //TODO: disallow colorizing other def types
  258. break;
  259. case DefType::BATTLE_HERO:
  260. palette[0] = H3Palette[0];
  261. palette[1] = H3Palette[1];
  262. palette[4] = H3Palette[4];
  263. break;
  264. default:
  265. logAnim->error("Unknown def type %d in %s", type, Name);
  266. break;
  267. }
  268. for (ui32 i=0; i<totalBlocks; i++)
  269. {
  270. size_t blockID = read_le_u32(data.get() + it);
  271. it+=4;
  272. size_t totalEntries = read_le_u32(data.get() + it);
  273. it+=12;
  274. //8 unknown bytes - skipping
  275. //13 bytes for name of every frame in this block - not used, skipping
  276. it+= 13 * totalEntries;
  277. for (ui32 j=0; j<totalEntries; j++)
  278. {
  279. size_t currOffset = read_le_u32(data.get() + it);
  280. offset[blockID].push_back(currOffset);
  281. it += 4;
  282. }
  283. }
  284. }
  285. template<class ImageLoader>
  286. void CDefFile::loadFrame(size_t frame, size_t group, ImageLoader &loader) const
  287. {
  288. std::map<size_t, std::vector <size_t> >::const_iterator it;
  289. it = offset.find(group);
  290. assert (it != offset.end());
  291. const ui8 * FDef = data.get()+it->second[frame];
  292. const SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef);
  293. SSpriteDef sprite;
  294. sprite.format = read_le_u32(&sd.format);
  295. sprite.fullWidth = read_le_u32(&sd.fullWidth);
  296. sprite.fullHeight = read_le_u32(&sd.fullHeight);
  297. sprite.width = read_le_u32(&sd.width);
  298. sprite.height = read_le_u32(&sd.height);
  299. sprite.leftMargin = read_le_u32(&sd.leftMargin);
  300. sprite.topMargin = read_le_u32(&sd.topMargin);
  301. ui32 currentOffset = sizeof(SSpriteDef);
  302. //special case for some "old" format defs (SGTWMTA.DEF and SGTWMTB.DEF)
  303. if(sprite.format == 1 && sprite.width > sprite.fullWidth && sprite.height > sprite.fullHeight)
  304. {
  305. sprite.leftMargin = 0;
  306. sprite.topMargin = 0;
  307. sprite.width = sprite.fullWidth;
  308. sprite.height = sprite.fullHeight;
  309. currentOffset -= 16;
  310. }
  311. const ui32 BaseOffset = currentOffset;
  312. loader.init(Point(sprite.width, sprite.height),
  313. Point(sprite.leftMargin, sprite.topMargin),
  314. Point(sprite.fullWidth, sprite.fullHeight), palette.get());
  315. switch(sprite.format)
  316. {
  317. case 0:
  318. {
  319. //pixel data is not compressed, copy data to surface
  320. for(ui32 i=0; i<sprite.height; i++)
  321. {
  322. loader.Load(sprite.width, FDef + currentOffset);
  323. currentOffset += sprite.width;
  324. loader.EndLine();
  325. }
  326. break;
  327. }
  328. case 1:
  329. {
  330. //for each line we have offset of pixel data
  331. const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef+currentOffset);
  332. currentOffset += sizeof(ui32) * sprite.height;
  333. for(ui32 i=0; i<sprite.height; i++)
  334. {
  335. //get position of the line
  336. currentOffset=BaseOffset + read_le_u32(RWEntriesLoc + i);
  337. ui32 TotalRowLength = 0;
  338. while(TotalRowLength<sprite.width)
  339. {
  340. ui8 segmentType = FDef[currentOffset++];
  341. ui32 length = FDef[currentOffset++] + 1;
  342. if(segmentType==0xFF)//Raw data
  343. {
  344. loader.Load(length, FDef + currentOffset);
  345. currentOffset+=length;
  346. }
  347. else// RLE
  348. {
  349. loader.Load(length, segmentType);
  350. }
  351. TotalRowLength += length;
  352. }
  353. loader.EndLine();
  354. }
  355. break;
  356. }
  357. case 2:
  358. {
  359. currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset);
  360. for(ui32 i=0; i<sprite.height; i++)
  361. {
  362. ui32 TotalRowLength=0;
  363. while(TotalRowLength<sprite.width)
  364. {
  365. ui8 segment=FDef[currentOffset++];
  366. ui8 code = segment / 32;
  367. ui8 length = (segment & 31) + 1;
  368. if(code==7)//Raw data
  369. {
  370. loader.Load(length, FDef + currentOffset);
  371. currentOffset += length;
  372. }
  373. else//RLE
  374. {
  375. loader.Load(length, code);
  376. }
  377. TotalRowLength+=length;
  378. }
  379. loader.EndLine();
  380. }
  381. break;
  382. }
  383. case 3:
  384. {
  385. for(ui32 i=0; i<sprite.height; i++)
  386. {
  387. currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset+i*2*(sprite.width/32));
  388. ui32 TotalRowLength=0;
  389. while(TotalRowLength<sprite.width)
  390. {
  391. ui8 segment = FDef[currentOffset++];
  392. ui8 code = segment / 32;
  393. ui8 length = (segment & 31) + 1;
  394. if(code==7)//Raw data
  395. {
  396. loader.Load(length, FDef + currentOffset);
  397. currentOffset += length;
  398. }
  399. else//RLE
  400. {
  401. loader.Load(length, code);
  402. }
  403. TotalRowLength += length;
  404. }
  405. loader.EndLine();
  406. }
  407. break;
  408. }
  409. default:
  410. logGlobal->error("Error: unsupported format of def file: %d", sprite.format);
  411. break;
  412. }
  413. }
  414. CDefFile::~CDefFile() = default;
  415. const std::map<size_t, size_t > CDefFile::getEntries() const
  416. {
  417. std::map<size_t, size_t > ret;
  418. for (auto & elem : offset)
  419. ret[elem.first] = elem.second.size();
  420. return ret;
  421. }
  422. /*************************************************************************
  423. * Classes for image loaders - helpers for loading from def files *
  424. *************************************************************************/
  425. SDLImageLoader::SDLImageLoader(SDLImage * Img):
  426. image(Img),
  427. lineStart(nullptr),
  428. position(nullptr)
  429. {
  430. }
  431. void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal)
  432. {
  433. //Init image
  434. image->surf = SDL_CreateRGBSurface(0, SpriteSize.x, SpriteSize.y, 8, 0, 0, 0, 0);
  435. image->margins = Margins;
  436. image->fullSize = FullSize;
  437. //Prepare surface
  438. SDL_Palette * p = SDL_AllocPalette(SDLImage::DEFAULT_PALETTE_COLORS);
  439. SDL_SetPaletteColors(p, pal, 0, SDLImage::DEFAULT_PALETTE_COLORS);
  440. SDL_SetSurfacePalette(image->surf, p);
  441. SDL_FreePalette(p);
  442. SDL_LockSurface(image->surf);
  443. lineStart = position = (ui8*)image->surf->pixels;
  444. }
  445. inline void SDLImageLoader::Load(size_t size, const ui8 * data)
  446. {
  447. if (size)
  448. {
  449. memcpy((void *)position, data, size);
  450. position += size;
  451. }
  452. }
  453. inline void SDLImageLoader::Load(size_t size, ui8 color)
  454. {
  455. if (size)
  456. {
  457. memset((void *)position, color, size);
  458. position += size;
  459. }
  460. }
  461. inline void SDLImageLoader::EndLine()
  462. {
  463. lineStart += image->surf->pitch;
  464. position = lineStart;
  465. }
  466. SDLImageLoader::~SDLImageLoader()
  467. {
  468. SDL_UnlockSurface(image->surf);
  469. SDL_SetColorKey(image->surf, SDL_TRUE, 0);
  470. //TODO: RLE if compressed and bpp>1
  471. }
  472. /*************************************************************************
  473. * Classes for images, support loading from file and drawing on surface *
  474. *************************************************************************/
  475. IImage::IImage() = default;
  476. IImage::~IImage() = default;
  477. SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group)
  478. : surf(nullptr),
  479. margins(0, 0),
  480. fullSize(0, 0),
  481. originalPalette(nullptr)
  482. {
  483. SDLImageLoader loader(this);
  484. data->loadFrame(frame, group, loader);
  485. savePalette();
  486. }
  487. SDLImage::SDLImage(SDL_Surface * from, bool extraRef)
  488. : surf(nullptr),
  489. margins(0, 0),
  490. fullSize(0, 0),
  491. originalPalette(nullptr)
  492. {
  493. surf = from;
  494. if (surf == nullptr)
  495. return;
  496. savePalette();
  497. if (extraRef)
  498. surf->refcount++;
  499. fullSize.x = surf->w;
  500. fullSize.y = surf->h;
  501. }
  502. SDLImage::SDLImage(const JsonNode & conf)
  503. : surf(nullptr),
  504. margins(0, 0),
  505. fullSize(0, 0),
  506. originalPalette(nullptr)
  507. {
  508. std::string filename = conf["file"].String();
  509. surf = BitmapHandler::loadBitmap(filename);
  510. if(surf == nullptr)
  511. return;
  512. savePalette();
  513. const JsonNode & jsonMargins = conf["margins"];
  514. margins.x = jsonMargins["left"].Integer();
  515. margins.y = jsonMargins["top"].Integer();
  516. fullSize.x = conf["width"].Integer();
  517. fullSize.y = conf["height"].Integer();
  518. if(fullSize.x == 0)
  519. {
  520. fullSize.x = margins.x + surf->w + jsonMargins["right"].Integer();
  521. }
  522. if(fullSize.y == 0)
  523. {
  524. fullSize.y = margins.y + surf->h + jsonMargins["bottom"].Integer();
  525. }
  526. }
  527. SDLImage::SDLImage(std::string filename)
  528. : surf(nullptr),
  529. margins(0, 0),
  530. fullSize(0, 0),
  531. originalPalette(nullptr)
  532. {
  533. surf = BitmapHandler::loadBitmap(filename);
  534. if(surf == nullptr)
  535. {
  536. logGlobal->error("Error: failed to load image %s", filename);
  537. return;
  538. }
  539. else
  540. {
  541. savePalette();
  542. fullSize.x = surf->w;
  543. fullSize.y = surf->h;
  544. }
  545. }
  546. void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alpha) const
  547. {
  548. if(!surf)
  549. return;
  550. Rect destRect(posX, posY, surf->w, surf->h);
  551. draw(where, &destRect, src);
  552. }
  553. void SDLImage::draw(SDL_Surface* where, SDL_Rect* dest, SDL_Rect* src, ui8 alpha) const
  554. {
  555. if (!surf)
  556. return;
  557. Rect sourceRect(0, 0, surf->w, surf->h);
  558. Point destShift(0, 0);
  559. if(src)
  560. {
  561. if(src->x < margins.x)
  562. destShift.x += margins.x - src->x;
  563. if(src->y < margins.y)
  564. destShift.y += margins.y - src->y;
  565. sourceRect = Rect(*src) & Rect(margins.x, margins.y, surf->w, surf->h);
  566. sourceRect -= margins;
  567. }
  568. else
  569. destShift = margins;
  570. Rect destRect(destShift.x, destShift.y, surf->w, surf->h);
  571. if(dest)
  572. {
  573. destRect.x += dest->x;
  574. destRect.y += dest->y;
  575. }
  576. if(surf->format->BitsPerPixel == 8)
  577. {
  578. CSDL_Ext::blit8bppAlphaTo24bpp(surf, &sourceRect, where, &destRect);
  579. }
  580. else
  581. {
  582. SDL_UpperBlit(surf, &sourceRect, where, &destRect);
  583. }
  584. }
  585. std::shared_ptr<IImage> SDLImage::scaleFast(float scale) const
  586. {
  587. auto scaled = CSDL_Ext::scaleSurfaceFast(surf, surf->w * scale, surf->h * scale);
  588. if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point
  589. CSDL_Ext::setColorKey(scaled, scaled->format->palette->colors[0]);
  590. else if(scaled->format && scaled->format->Amask)
  591. SDL_SetSurfaceBlendMode(scaled, SDL_BLENDMODE_BLEND);//just in case
  592. else
  593. CSDL_Ext::setDefaultColorKey(scaled);//just in case
  594. SDLImage * ret = new SDLImage(scaled, false);
  595. ret->fullSize.x = (int) round((float)fullSize.x * scale);
  596. ret->fullSize.y = (int) round((float)fullSize.y * scale);
  597. ret->margins.x = (int) round((float)margins.x * scale);
  598. ret->margins.y = (int) round((float)margins.y * scale);
  599. return std::shared_ptr<IImage>(ret);
  600. }
  601. void SDLImage::exportBitmap(const boost::filesystem::path& path) const
  602. {
  603. SDL_SaveBMP(surf, path.string().c_str());
  604. }
  605. void SDLImage::playerColored(PlayerColor player)
  606. {
  607. graphics->blueToPlayersAdv(surf, player);
  608. }
  609. void SDLImage::setFlagColor(PlayerColor player)
  610. {
  611. if(player < PlayerColor::PLAYER_LIMIT || player==PlayerColor::NEUTRAL)
  612. CSDL_Ext::setPlayerColor(surf, player);
  613. }
  614. int SDLImage::width() const
  615. {
  616. return fullSize.x;
  617. }
  618. int SDLImage::height() const
  619. {
  620. return fullSize.y;
  621. }
  622. void SDLImage::horizontalFlip()
  623. {
  624. margins.y = fullSize.y - surf->h - margins.y;
  625. //todo: modify in-place
  626. SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf);
  627. SDL_FreeSurface(surf);
  628. surf = flipped;
  629. }
  630. void SDLImage::verticalFlip()
  631. {
  632. margins.x = fullSize.x - surf->w - margins.x;
  633. //todo: modify in-place
  634. SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf);
  635. SDL_FreeSurface(surf);
  636. surf = flipped;
  637. }
  638. // Keep the original palette, in order to do color switching operation
  639. void SDLImage::savePalette()
  640. {
  641. // For some images that don't have palette, skip this
  642. if(surf->format->palette == nullptr)
  643. return;
  644. if(originalPalette == nullptr)
  645. originalPalette = SDL_AllocPalette(DEFAULT_PALETTE_COLORS);
  646. SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, DEFAULT_PALETTE_COLORS);
  647. }
  648. void SDLImage::shiftPalette(int from, int howMany)
  649. {
  650. //works with at most 16 colors, if needed more -> increase values
  651. assert(howMany < 16);
  652. if(surf->format->palette)
  653. {
  654. SDL_Color palette[16];
  655. for(int i=0; i<howMany; ++i)
  656. {
  657. palette[(i+1)%howMany] = surf->format->palette->colors[from + i];
  658. }
  659. SDL_SetColors(surf, palette, from, howMany);
  660. }
  661. }
  662. void SDLImage::adjustPalette(const ColorShifter * shifter)
  663. {
  664. if(originalPalette == nullptr)
  665. return;
  666. SDL_Palette* palette = surf->format->palette;
  667. // Note: here we skip the first 8 colors in the palette that predefined in H3Palette
  668. for(int i = 8; i < palette->ncolors; i++)
  669. {
  670. palette->colors[i] = shifter->shiftColor(originalPalette->colors[i]);
  671. }
  672. }
  673. void SDLImage::resetPalette()
  674. {
  675. if(originalPalette == nullptr)
  676. return;
  677. // Always keept the original palette not changed, copy a new palette to assign to surface
  678. SDL_SetPaletteColors(surf->format->palette, originalPalette->colors, 0, originalPalette->ncolors);
  679. }
  680. void SDLImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
  681. {
  682. if(surf->format->palette)
  683. {
  684. SDL_SetColors(surf, const_cast<SDL_Color *>(borderPallete.data()), 5, 3);
  685. }
  686. }
  687. SDLImage::~SDLImage()
  688. {
  689. SDL_FreeSurface(surf);
  690. if(originalPalette != nullptr)
  691. {
  692. SDL_FreePalette(originalPalette);
  693. originalPalette = nullptr;
  694. }
  695. }
  696. std::shared_ptr<IImage> CAnimation::getFromExtraDef(std::string filename)
  697. {
  698. size_t pos = filename.find(':');
  699. if (pos == -1)
  700. return nullptr;
  701. CAnimation anim(filename.substr(0, pos));
  702. pos++;
  703. size_t frame = atoi(filename.c_str()+pos);
  704. size_t group = 0;
  705. pos = filename.find(':', pos);
  706. if (pos != -1)
  707. {
  708. pos++;
  709. group = frame;
  710. frame = atoi(filename.c_str()+pos);
  711. }
  712. anim.load(frame ,group);
  713. auto ret = anim.images[group][frame];
  714. anim.images.clear();
  715. return ret;
  716. }
  717. bool CAnimation::loadFrame(size_t frame, size_t group)
  718. {
  719. if(size(group) <= frame)
  720. {
  721. printError(frame, group, "LoadFrame");
  722. return false;
  723. }
  724. auto image = getImage(frame, group, false);
  725. if(image)
  726. {
  727. return true;
  728. }
  729. //try to get image from def
  730. if(source[group][frame].getType() == JsonNode::JsonType::DATA_NULL)
  731. {
  732. if(defFile)
  733. {
  734. auto frameList = defFile->getEntries();
  735. if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present
  736. {
  737. images[group][frame] = std::make_shared<SDLImage>(defFile.get(), frame, group);
  738. return true;
  739. }
  740. }
  741. // still here? image is missing
  742. printError(frame, group, "LoadFrame");
  743. images[group][frame] = std::make_shared<SDLImage>("DEFAULT");
  744. }
  745. else //load from separate file
  746. {
  747. auto img = getFromExtraDef(source[group][frame]["file"].String());
  748. if(!img)
  749. img = std::make_shared<SDLImage>(source[group][frame]);
  750. images[group][frame] = img;
  751. return true;
  752. }
  753. return false;
  754. }
  755. bool CAnimation::unloadFrame(size_t frame, size_t group)
  756. {
  757. auto image = getImage(frame, group, false);
  758. if(image)
  759. {
  760. images[group].erase(frame);
  761. if(images[group].empty())
  762. images.erase(group);
  763. return true;
  764. }
  765. return false;
  766. }
  767. void CAnimation::initFromJson(const JsonNode & config)
  768. {
  769. std::string basepath;
  770. basepath = config["basepath"].String();
  771. JsonNode base(JsonNode::JsonType::DATA_STRUCT);
  772. base["margins"] = config["margins"];
  773. base["width"] = config["width"];
  774. base["height"] = config["height"];
  775. for(const JsonNode & group : config["sequences"].Vector())
  776. {
  777. size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING)
  778. source[groupID].clear();
  779. for(const JsonNode & frame : group["frames"].Vector())
  780. {
  781. JsonNode toAdd(JsonNode::JsonType::DATA_STRUCT);
  782. JsonUtils::inherit(toAdd, base);
  783. toAdd["file"].String() = basepath + frame.String();
  784. source[groupID].push_back(toAdd);
  785. }
  786. }
  787. for(const JsonNode & node : config["images"].Vector())
  788. {
  789. size_t group = node["group"].Integer();
  790. size_t frame = node["frame"].Integer();
  791. if (source[group].size() <= frame)
  792. source[group].resize(frame+1);
  793. JsonNode toAdd(JsonNode::JsonType::DATA_STRUCT);
  794. JsonUtils::inherit(toAdd, base);
  795. toAdd["file"].String() = basepath + node["file"].String();
  796. source[group][frame] = toAdd;
  797. }
  798. }
  799. void CAnimation::exportBitmaps(const boost::filesystem::path& path) const
  800. {
  801. if(images.empty())
  802. {
  803. logGlobal->error("Nothing to export, animation is empty");
  804. return;
  805. }
  806. boost::filesystem::path actualPath = path / "SPRITES" / name;
  807. boost::filesystem::create_directories(actualPath);
  808. size_t counter = 0;
  809. for(const auto & groupPair : images)
  810. {
  811. size_t group = groupPair.first;
  812. for(const auto & imagePair : groupPair.second)
  813. {
  814. size_t frame = imagePair.first;
  815. const auto img = imagePair.second;
  816. boost::format fmt("%d_%d.bmp");
  817. fmt % group % frame;
  818. img->exportBitmap(actualPath / fmt.str());
  819. counter++;
  820. }
  821. }
  822. logGlobal->info("Exported %d frames to %s", counter, actualPath.string());
  823. }
  824. void CAnimation::init()
  825. {
  826. if(defFile)
  827. {
  828. const std::map<size_t, size_t> defEntries = defFile->getEntries();
  829. for (auto & defEntry : defEntries)
  830. source[defEntry.first].resize(defEntry.second);
  831. }
  832. ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
  833. if (vstd::contains(graphics->imageLists, resID.getName()))
  834. initFromJson(graphics->imageLists[resID.getName()]);
  835. auto configList = CResourceHandler::get()->getResourcesWithName(resID);
  836. for(auto & loader : configList)
  837. {
  838. auto stream = loader->load(resID);
  839. std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
  840. stream->read(textData.get(), stream->getSize());
  841. const JsonNode config((char*)textData.get(), stream->getSize());
  842. initFromJson(config);
  843. }
  844. }
  845. void CAnimation::printError(size_t frame, size_t group, std::string type) const
  846. {
  847. logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name, group, frame);
  848. }
  849. CAnimation::CAnimation(std::string Name):
  850. name(Name),
  851. preloaded(false),
  852. defFile()
  853. {
  854. size_t dotPos = name.find_last_of('.');
  855. if ( dotPos!=-1 )
  856. name.erase(dotPos);
  857. std::transform(name.begin(), name.end(), name.begin(), toupper);
  858. ResourceID resource(std::string("SPRITES/") + name, EResType::ANIMATION);
  859. if(CResourceHandler::get()->existsResource(resource))
  860. defFile = std::make_shared<CDefFile>(name);
  861. init();
  862. if(source.empty())
  863. logAnim->error("Animation %s failed to load", Name);
  864. }
  865. CAnimation::CAnimation():
  866. name(""),
  867. preloaded(false),
  868. defFile()
  869. {
  870. init();
  871. }
  872. CAnimation::~CAnimation() = default;
  873. void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup)
  874. {
  875. if(!source.count(sourceGroup))
  876. {
  877. logAnim->error("Group %d missing in %s", sourceGroup, name);
  878. return;
  879. }
  880. if(source[sourceGroup].size() <= sourceFrame)
  881. {
  882. logAnim->error("Frame [%d %d] missing in %s", sourceGroup, sourceFrame, name);
  883. return;
  884. }
  885. //todo: clone actual loaded Image object
  886. JsonNode clone(source[sourceGroup][sourceFrame]);
  887. if(clone.getType() == JsonNode::JsonType::DATA_NULL)
  888. {
  889. std::string temp = name+":"+boost::lexical_cast<std::string>(sourceGroup)+":"+boost::lexical_cast<std::string>(sourceFrame);
  890. clone["file"].String() = temp;
  891. }
  892. source[targetGroup].push_back(clone);
  893. size_t index = source[targetGroup].size() - 1;
  894. if(preloaded)
  895. load(index, targetGroup);
  896. }
  897. void CAnimation::shiftColor(const ColorShifter * shifter)
  898. {
  899. for(auto groupIter = images.begin(); groupIter != images.end(); groupIter++)
  900. {
  901. for(auto frameIter = groupIter->second.begin(); frameIter != groupIter->second.end(); frameIter++)
  902. {
  903. std::shared_ptr<IImage> image = frameIter->second;
  904. image->adjustPalette(shifter);
  905. }
  906. }
  907. }
  908. void CAnimation::setCustom(std::string filename, size_t frame, size_t group)
  909. {
  910. if (source[group].size() <= frame)
  911. source[group].resize(frame+1);
  912. source[group][frame]["file"].String() = filename;
  913. //FIXME: update image if already loaded
  914. }
  915. std::shared_ptr<IImage> CAnimation::getImage(size_t frame, size_t group, bool verbose) const
  916. {
  917. auto groupIter = images.find(group);
  918. if (groupIter != images.end())
  919. {
  920. auto imageIter = groupIter->second.find(frame);
  921. if (imageIter != groupIter->second.end())
  922. return imageIter->second;
  923. }
  924. if (verbose)
  925. printError(frame, group, "GetImage");
  926. return nullptr;
  927. }
  928. void CAnimation::load()
  929. {
  930. for (auto & elem : source)
  931. for (size_t image=0; image < elem.second.size(); image++)
  932. loadFrame(image, elem.first);
  933. }
  934. void CAnimation::unload()
  935. {
  936. for (auto & elem : source)
  937. for (size_t image=0; image < elem.second.size(); image++)
  938. unloadFrame(image, elem.first);
  939. }
  940. void CAnimation::preload()
  941. {
  942. if(!preloaded)
  943. {
  944. preloaded = true;
  945. load();
  946. }
  947. }
  948. void CAnimation::loadGroup(size_t group)
  949. {
  950. if (vstd::contains(source, group))
  951. for (size_t image=0; image < source[group].size(); image++)
  952. loadFrame(image, group);
  953. }
  954. void CAnimation::unloadGroup(size_t group)
  955. {
  956. if (vstd::contains(source, group))
  957. for (size_t image=0; image < source[group].size(); image++)
  958. unloadFrame(image, group);
  959. }
  960. void CAnimation::load(size_t frame, size_t group)
  961. {
  962. loadFrame(frame, group);
  963. }
  964. void CAnimation::unload(size_t frame, size_t group)
  965. {
  966. unloadFrame(frame, group);
  967. }
  968. size_t CAnimation::size(size_t group) const
  969. {
  970. auto iter = source.find(group);
  971. if (iter != source.end())
  972. return iter->second.size();
  973. return 0;
  974. }
  975. void CAnimation::horizontalFlip()
  976. {
  977. for(auto & group : images)
  978. for(auto & image : group.second)
  979. image.second->horizontalFlip();
  980. }
  981. void CAnimation::verticalFlip()
  982. {
  983. for(auto & group : images)
  984. for(auto & image : group.second)
  985. image.second->verticalFlip();
  986. }
  987. void CAnimation::playerColored(PlayerColor player)
  988. {
  989. for(auto & group : images)
  990. for(auto & image : group.second)
  991. image.second->playerColored(player);
  992. }
  993. void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targetGroup)
  994. {
  995. for(size_t frame = 0; frame < size(sourceGroup); ++frame)
  996. {
  997. duplicateImage(sourceGroup, frame, targetGroup);
  998. auto image = getImage(frame, targetGroup);
  999. image->verticalFlip();
  1000. }
  1001. }
  1002. float CFadeAnimation::initialCounter() const
  1003. {
  1004. if (fadingMode == EMode::OUT)
  1005. return 1.0f;
  1006. return 0.0f;
  1007. }
  1008. void CFadeAnimation::update()
  1009. {
  1010. if (!fading)
  1011. return;
  1012. if (fadingMode == EMode::OUT)
  1013. fadingCounter -= delta;
  1014. else
  1015. fadingCounter += delta;
  1016. if (isFinished())
  1017. {
  1018. fading = false;
  1019. if (shouldFreeSurface)
  1020. {
  1021. SDL_FreeSurface(fadingSurface);
  1022. fadingSurface = nullptr;
  1023. }
  1024. }
  1025. }
  1026. bool CFadeAnimation::isFinished() const
  1027. {
  1028. if (fadingMode == EMode::OUT)
  1029. return fadingCounter <= 0.0f;
  1030. return fadingCounter >= 1.0f;
  1031. }
  1032. CFadeAnimation::CFadeAnimation()
  1033. : delta(0), fadingSurface(nullptr), fading(false), fadingCounter(0), shouldFreeSurface(false),
  1034. fadingMode(EMode::NONE)
  1035. {
  1036. }
  1037. CFadeAnimation::~CFadeAnimation()
  1038. {
  1039. if (fadingSurface && shouldFreeSurface)
  1040. SDL_FreeSurface(fadingSurface);
  1041. }
  1042. void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd, float animDelta)
  1043. {
  1044. if (fading)
  1045. {
  1046. // in that case, immediately finish the previous fade
  1047. // (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))
  1048. logGlobal->warn("Tried to init fading animation that is already running.");
  1049. if (fadingSurface && shouldFreeSurface)
  1050. SDL_FreeSurface(fadingSurface);
  1051. }
  1052. if (animDelta <= 0.0f)
  1053. {
  1054. logGlobal->warn("Fade anim: delta should be positive; %f given.", animDelta);
  1055. animDelta = DEFAULT_DELTA;
  1056. }
  1057. if (sourceSurface)
  1058. fadingSurface = sourceSurface;
  1059. delta = animDelta;
  1060. fadingMode = mode;
  1061. fadingCounter = initialCounter();
  1062. fading = true;
  1063. shouldFreeSurface = freeSurfaceAtEnd;
  1064. }
  1065. void CFadeAnimation::draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect)
  1066. {
  1067. if (!fading || !fadingSurface || fadingMode == EMode::NONE)
  1068. {
  1069. fading = false;
  1070. return;
  1071. }
  1072. CSDL_Ext::setAlpha(fadingSurface, fadingCounter * 255);
  1073. SDL_BlitSurface(fadingSurface, const_cast<SDL_Rect *>(sourceRect), targetSurface, destRect); //FIXME
  1074. CSDL_Ext::setAlpha(fadingSurface, 255);
  1075. }