Animation.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. /*
  2. * Animation.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. //code is copied from vcmiclient/CAnimation.cpp with minimal changes
  11. #include "StdInc.h"
  12. #include "Animation.h"
  13. #include "BitmapHandler.h"
  14. #include "../lib/filesystem/Filesystem.h"
  15. #include "../lib/filesystem/ISimpleResourceLoader.h"
  16. #include "../lib/JsonNode.h"
  17. #include "../lib/CRandomGenerator.h"
  18. #include "../lib/VCMIDirs.h"
  19. typedef std::map<size_t, std::vector<JsonNode>> source_map;
  20. /// Class for def loading
  21. /// After loading will store general info (palette and frame offsets) and pointer to file itself
  22. class DefFile
  23. {
  24. private:
  25. struct SSpriteDef
  26. {
  27. ui32 size;
  28. ui32 format; /// format in which pixel data is stored
  29. ui32 fullWidth; /// full width and height of frame, including borders
  30. ui32 fullHeight;
  31. ui32 width; /// width and height of pixel data, borders excluded
  32. ui32 height;
  33. si32 leftMargin;
  34. si32 topMargin;
  35. };
  36. //offset[group][frame] - offset of frame data in file
  37. std::map<size_t, std::vector <size_t> > offset;
  38. std::unique_ptr<ui8[]> data;
  39. std::unique_ptr<QVector<QRgb>> palette;
  40. public:
  41. DefFile(std::string Name);
  42. ~DefFile();
  43. std::shared_ptr<QImage> loadFrame(size_t frame, size_t group) const;
  44. const std::map<size_t, size_t> getEntries() const;
  45. };
  46. class ImageLoader
  47. {
  48. QImage * image;
  49. ui8 * lineStart;
  50. ui8 * position;
  51. QPoint spriteSize, margins, fullSize;
  52. public:
  53. //load size raw pixels from data
  54. inline void Load(size_t size, const ui8 * data);
  55. //set size pixels to color
  56. inline void Load(size_t size, ui8 color=0);
  57. inline void EndLine();
  58. //init image with these sizes and palette
  59. inline void init(QPoint SpriteSize, QPoint Margins, QPoint FullSize);
  60. ImageLoader(QImage * Img);
  61. ~ImageLoader();
  62. };
  63. // Extremely simple file cache. TODO: smarter, more general solution
  64. class FileCache
  65. {
  66. static const int cacheSize = 50; //Max number of cached files
  67. struct FileData
  68. {
  69. ResourceID name;
  70. size_t size;
  71. std::unique_ptr<ui8[]> data;
  72. std::unique_ptr<ui8[]> getCopy()
  73. {
  74. auto ret = std::unique_ptr<ui8[]>(new ui8[size]);
  75. std::copy(data.get(), data.get() + size, ret.get());
  76. return ret;
  77. }
  78. FileData(ResourceID name_, size_t size_, std::unique_ptr<ui8[]> data_):
  79. name{std::move(name_)},
  80. size{size_},
  81. data{std::move(data_)}
  82. {}
  83. };
  84. std::deque<FileData> cache;
  85. public:
  86. std::unique_ptr<ui8[]> getCachedFile(ResourceID rid)
  87. {
  88. for(auto & file : cache)
  89. {
  90. if(file.name == rid)
  91. return file.getCopy();
  92. }
  93. // Still here? Cache miss
  94. if(cache.size() > cacheSize)
  95. cache.pop_front();
  96. auto data = CResourceHandler::get()->load(rid)->readAll();
  97. cache.emplace_back(std::move(rid), data.second, std::move(data.first));
  98. return cache.back().getCopy();
  99. }
  100. };
  101. enum class DefType : uint32_t
  102. {
  103. SPELL = 0x40,
  104. SPRITE = 0x41,
  105. CREATURE = 0x42,
  106. MAP = 0x43,
  107. MAP_HERO = 0x44,
  108. TERRAIN = 0x45,
  109. CURSOR = 0x46,
  110. INTERFACE = 0x47,
  111. SPRITE_FRAME = 0x48,
  112. BATTLE_HERO = 0x49
  113. };
  114. static FileCache animationCache;
  115. /*************************************************************************
  116. * DefFile, class used for def loading *
  117. *************************************************************************/
  118. DefFile::DefFile(std::string Name):
  119. data(nullptr)
  120. {
  121. #if 0
  122. static QRgba H3_ORIG_PALETTE[8] =
  123. {
  124. { 0, 255, 255, SDL_ALPHA_OPAQUE},
  125. {255, 150, 255, SDL_ALPHA_OPAQUE},
  126. {255, 100, 255, SDL_ALPHA_OPAQUE},
  127. {255, 50, 255, SDL_ALPHA_OPAQUE},
  128. {255, 0, 255, SDL_ALPHA_OPAQUE},
  129. {255, 255, 0, SDL_ALPHA_OPAQUE},
  130. {180, 0, 255, SDL_ALPHA_OPAQUE},
  131. { 0, 255, 0, SDL_ALPHA_OPAQUE}
  132. };
  133. #endif // 0
  134. //First 8 colors in def palette used for transparency
  135. static QRgb H3Palette[8] =
  136. {
  137. qRgba(0, 0, 0, 0), // 100% - transparency
  138. qRgba(0, 0, 0, 32), // 75% - shadow border,
  139. qRgba(0, 0, 0, 64), // TODO: find exact value
  140. qRgba(0, 0, 0, 128), // TODO: for transparency
  141. qRgba(0, 0, 0, 128), // 50% - shadow body
  142. qRgba(0, 0, 0, 0), // 100% - selection highlight
  143. qRgba(0, 0, 0, 128), // 50% - shadow body below selection
  144. qRgba(0, 0, 0, 64) // 75% - shadow border below selection
  145. };
  146. data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
  147. palette = std::make_unique<QVector<QRgb>>(256);
  148. int it = 0;
  149. ui32 type = read_le_u32(data.get() + it);
  150. it += 4;
  151. //int width = read_le_u32(data + it); it+=4;//not used
  152. //int height = read_le_u32(data + it); it+=4;
  153. it += 8;
  154. ui32 totalBlocks = read_le_u32(data.get() + it);
  155. it += 4;
  156. for (ui32 i= 0; i<256; i++)
  157. {
  158. ui8 c[3];
  159. c[0] = data[it++];
  160. c[1] = data[it++];
  161. c[2] = data[it++];
  162. (*palette)[i] = qRgba(c[0], c[1], c[2], 255);
  163. }
  164. switch(static_cast<DefType>(type))
  165. {
  166. case DefType::SPELL:
  167. (*palette)[0] = H3Palette[0];
  168. break;
  169. case DefType::SPRITE:
  170. case DefType::SPRITE_FRAME:
  171. for(ui32 i= 0; i<8; i++)
  172. (*palette)[i] = H3Palette[i];
  173. break;
  174. case DefType::CREATURE:
  175. (*palette)[0] = H3Palette[0];
  176. (*palette)[1] = H3Palette[1];
  177. (*palette)[4] = H3Palette[4];
  178. (*palette)[5] = H3Palette[5];
  179. (*palette)[6] = H3Palette[6];
  180. (*palette)[7] = H3Palette[7];
  181. break;
  182. case DefType::MAP:
  183. case DefType::MAP_HERO:
  184. (*palette)[0] = H3Palette[0];
  185. (*palette)[1] = H3Palette[1];
  186. (*palette)[4] = H3Palette[4];
  187. //5 = owner flag, handled separately
  188. break;
  189. case DefType::TERRAIN:
  190. (*palette)[0] = H3Palette[0];
  191. (*palette)[1] = H3Palette[1];
  192. (*palette)[2] = H3Palette[2];
  193. (*palette)[3] = H3Palette[3];
  194. (*palette)[4] = H3Palette[4];
  195. break;
  196. case DefType::CURSOR:
  197. (*palette)[0] = H3Palette[0];
  198. break;
  199. case DefType::INTERFACE:
  200. (*palette)[0] = H3Palette[0];
  201. (*palette)[1] = H3Palette[1];
  202. (*palette)[4] = H3Palette[4];
  203. //player colors handled separately
  204. //TODO: disallow colorizing other def types
  205. break;
  206. case DefType::BATTLE_HERO:
  207. (*palette)[0] = H3Palette[0];
  208. (*palette)[1] = H3Palette[1];
  209. (*palette)[4] = H3Palette[4];
  210. break;
  211. default:
  212. logAnim->error("Unknown def type %d in %s", type, Name);
  213. break;
  214. }
  215. for (ui32 i=0; i<totalBlocks; i++)
  216. {
  217. size_t blockID = read_le_u32(data.get() + it);
  218. it+=4;
  219. size_t totalEntries = read_le_u32(data.get() + it);
  220. it+=12;
  221. //8 unknown bytes - skipping
  222. //13 bytes for name of every frame in this block - not used, skipping
  223. it+= 13 * (int)totalEntries;
  224. for (ui32 j=0; j<totalEntries; j++)
  225. {
  226. size_t currOffset = read_le_u32(data.get() + it);
  227. offset[blockID].push_back(currOffset);
  228. it += 4;
  229. }
  230. }
  231. }
  232. std::shared_ptr<QImage> DefFile::loadFrame(size_t frame, size_t group) const
  233. {
  234. std::map<size_t, std::vector <size_t> >::const_iterator it;
  235. it = offset.find(group);
  236. assert (it != offset.end());
  237. const ui8 * FDef = data.get()+it->second[frame];
  238. const SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef);
  239. SSpriteDef sprite;
  240. sprite.format = read_le_u32(&sd.format);
  241. sprite.fullWidth = read_le_u32(&sd.fullWidth);
  242. sprite.fullHeight = read_le_u32(&sd.fullHeight);
  243. sprite.width = read_le_u32(&sd.width);
  244. sprite.height = read_le_u32(&sd.height);
  245. sprite.leftMargin = read_le_u32(&sd.leftMargin);
  246. sprite.topMargin = read_le_u32(&sd.topMargin);
  247. ui32 currentOffset = sizeof(SSpriteDef);
  248. //special case for some "old" format defs (SGTWMTA.DEF and SGTWMTB.DEF)
  249. if(sprite.format == 1 && sprite.width > sprite.fullWidth && sprite.height > sprite.fullHeight)
  250. {
  251. sprite.leftMargin = 0;
  252. sprite.topMargin = 0;
  253. sprite.width = sprite.fullWidth;
  254. sprite.height = sprite.fullHeight;
  255. currentOffset -= 16;
  256. }
  257. const ui32 BaseOffset = currentOffset;
  258. std::shared_ptr<QImage> img = std::make_shared<QImage>(sprite.fullWidth, sprite.fullHeight, QImage::Format_Indexed8);
  259. if(!img)
  260. throw std::runtime_error("Image memory cannot be allocated");
  261. ImageLoader loader(img.get());
  262. loader.init(QPoint(sprite.width, sprite.height),
  263. QPoint(sprite.leftMargin, sprite.topMargin),
  264. QPoint(sprite.fullWidth, sprite.fullHeight));
  265. switch(sprite.format)
  266. {
  267. case 0:
  268. {
  269. //pixel data is not compressed, copy data to surface
  270. for(ui32 i=0; i<sprite.height; i++)
  271. {
  272. loader.Load(sprite.width, FDef + currentOffset);
  273. currentOffset += sprite.width;
  274. loader.EndLine();
  275. }
  276. break;
  277. }
  278. case 1:
  279. {
  280. //for each line we have offset of pixel data
  281. const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef+currentOffset);
  282. currentOffset += sizeof(ui32) * sprite.height;
  283. for(ui32 i=0; i<sprite.height; i++)
  284. {
  285. //get position of the line
  286. currentOffset=BaseOffset + read_le_u32(RWEntriesLoc + i);
  287. ui32 TotalRowLength = 0;
  288. while(TotalRowLength<sprite.width)
  289. {
  290. ui8 segmentType = FDef[currentOffset++];
  291. ui32 length = FDef[currentOffset++] + 1;
  292. if(segmentType==0xFF)//Raw data
  293. {
  294. loader.Load(length, FDef + currentOffset);
  295. currentOffset+=length;
  296. }
  297. else// RLE
  298. {
  299. loader.Load(length, segmentType);
  300. }
  301. TotalRowLength += length;
  302. }
  303. loader.EndLine();
  304. }
  305. break;
  306. }
  307. case 2:
  308. {
  309. currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset);
  310. for(ui32 i=0; i<sprite.height; i++)
  311. {
  312. ui32 TotalRowLength=0;
  313. while(TotalRowLength<sprite.width)
  314. {
  315. ui8 segment=FDef[currentOffset++];
  316. ui8 code = segment / 32;
  317. ui8 length = (segment & 31) + 1;
  318. if(code==7)//Raw data
  319. {
  320. loader.Load(length, FDef + currentOffset);
  321. currentOffset += length;
  322. }
  323. else//RLE
  324. {
  325. loader.Load(length, code);
  326. }
  327. TotalRowLength+=length;
  328. }
  329. loader.EndLine();
  330. }
  331. break;
  332. }
  333. case 3:
  334. {
  335. for(ui32 i=0; i<sprite.height; i++)
  336. {
  337. currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset+i*2*(sprite.width/32));
  338. ui32 TotalRowLength=0;
  339. while(TotalRowLength<sprite.width)
  340. {
  341. ui8 segment = FDef[currentOffset++];
  342. ui8 code = segment / 32;
  343. ui8 length = (segment & 31) + 1;
  344. if(code==7)//Raw data
  345. {
  346. loader.Load(length, FDef + currentOffset);
  347. currentOffset += length;
  348. }
  349. else//RLE
  350. {
  351. loader.Load(length, code);
  352. }
  353. TotalRowLength += length;
  354. }
  355. loader.EndLine();
  356. }
  357. break;
  358. }
  359. default:
  360. logGlobal->error("Error: unsupported format of def file: %d", sprite.format);
  361. break;
  362. }
  363. img->setColorTable(*palette);
  364. return img;
  365. }
  366. DefFile::~DefFile() = default;
  367. const std::map<size_t, size_t > DefFile::getEntries() const
  368. {
  369. std::map<size_t, size_t > ret;
  370. for (auto & elem : offset)
  371. ret[elem.first] = elem.second.size();
  372. return ret;
  373. }
  374. /*************************************************************************
  375. * Classes for image loaders - helpers for loading from def files *
  376. *************************************************************************/
  377. ImageLoader::ImageLoader(QImage * Img):
  378. image(Img),
  379. lineStart(Img->bits()),
  380. position(Img->bits())
  381. {
  382. }
  383. void ImageLoader::init(QPoint SpriteSize, QPoint Margins, QPoint FullSize)
  384. {
  385. spriteSize = SpriteSize;
  386. margins = Margins;
  387. fullSize = FullSize;
  388. memset((void *)image->bits(), 0, fullSize.y() * image->bytesPerLine());
  389. lineStart = image->bits();
  390. lineStart += margins.y() * image->bytesPerLine() + margins.x();
  391. position = lineStart;
  392. }
  393. inline void ImageLoader::Load(size_t size, const ui8 * data)
  394. {
  395. if(size)
  396. {
  397. memcpy((void *)position, data, size);
  398. position += size;
  399. }
  400. }
  401. inline void ImageLoader::Load(size_t size, ui8 color)
  402. {
  403. if(size)
  404. {
  405. memset((void *)position, color, size);
  406. position += size;
  407. }
  408. }
  409. inline void ImageLoader::EndLine()
  410. {
  411. lineStart += image->bytesPerLine();
  412. position = lineStart;
  413. }
  414. ImageLoader::~ImageLoader()
  415. {
  416. }
  417. /*************************************************************************
  418. * Classes for images, support loading from file and drawing on surface *
  419. *************************************************************************/
  420. std::shared_ptr<QImage> Animation::getFromExtraDef(std::string filename)
  421. {
  422. size_t pos = filename.find(':');
  423. if(pos == -1)
  424. return nullptr;
  425. Animation anim(filename.substr(0, pos));
  426. pos++;
  427. size_t frame = atoi(filename.c_str()+pos);
  428. size_t group = 0;
  429. pos = filename.find(':', pos);
  430. if(pos != -1)
  431. {
  432. pos++;
  433. group = frame;
  434. frame = atoi(filename.c_str()+pos);
  435. }
  436. anim.load(frame ,group);
  437. auto ret = anim.images[group][frame];
  438. anim.images.clear();
  439. return ret;
  440. }
  441. bool Animation::loadFrame(size_t frame, size_t group)
  442. {
  443. if(size(group) <= frame)
  444. {
  445. printError(frame, group, "LoadFrame");
  446. return false;
  447. }
  448. auto image = getImage(frame, group, false);
  449. if(image)
  450. {
  451. return true;
  452. }
  453. //try to get image from def
  454. if(source[group][frame].getType() == JsonNode::JsonType::DATA_NULL)
  455. {
  456. if(defFile)
  457. {
  458. auto frameList = defFile->getEntries();
  459. if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present
  460. {
  461. images[group][frame] = defFile->loadFrame(frame, group);
  462. return true;
  463. }
  464. }
  465. return false;
  466. // still here? image is missing
  467. printError(frame, group, "LoadFrame");
  468. images[group][frame] = std::make_shared<QImage>("DEFAULT");
  469. }
  470. else //load from separate file
  471. {
  472. images[group][frame] = getFromExtraDef(source[group][frame]["file"].String());;
  473. return true;
  474. }
  475. return false;
  476. }
  477. bool Animation::unloadFrame(size_t frame, size_t group)
  478. {
  479. auto image = getImage(frame, group, false);
  480. if(image)
  481. {
  482. images[group].erase(frame);
  483. if(images[group].empty())
  484. images.erase(group);
  485. return true;
  486. }
  487. return false;
  488. }
  489. void Animation::init()
  490. {
  491. if(defFile)
  492. {
  493. const std::map<size_t, size_t> defEntries = defFile->getEntries();
  494. for (auto & defEntry : defEntries)
  495. source[defEntry.first].resize(defEntry.second);
  496. }
  497. #if 0 //this code is not used but maybe requred if there will be configurable sprites
  498. ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
  499. //if(vstd::contains(graphics->imageLists, resID.getName()))
  500. //initFromJson(graphics->imageLists[resID.getName()]);
  501. auto configList = CResourceHandler::get()->getResourcesWithName(resID);
  502. for(auto & loader : configList)
  503. {
  504. auto stream = loader->load(resID);
  505. std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
  506. stream->read(textData.get(), stream->getSize());
  507. const JsonNode config((char*)textData.get(), stream->getSize());
  508. //initFromJson(config);
  509. }
  510. #endif
  511. }
  512. void Animation::printError(size_t frame, size_t group, std::string type) const
  513. {
  514. logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name, group, frame);
  515. }
  516. Animation::Animation(std::string Name):
  517. name(Name),
  518. preloaded(false),
  519. defFile()
  520. {
  521. size_t dotPos = name.find_last_of('.');
  522. if( dotPos!=-1 )
  523. name.erase(dotPos);
  524. std::transform(name.begin(), name.end(), name.begin(), toupper);
  525. ResourceID resource(std::string("SPRITES/") + name, EResType::ANIMATION);
  526. if(CResourceHandler::get()->existsResource(resource))
  527. defFile = std::make_shared<DefFile>(name);
  528. init();
  529. if(source.empty())
  530. logAnim->error("Animation %s failed to load", Name);
  531. }
  532. Animation::Animation():
  533. name(""),
  534. preloaded(false),
  535. defFile()
  536. {
  537. init();
  538. }
  539. Animation::~Animation() = default;
  540. void Animation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup)
  541. {
  542. if(!source.count(sourceGroup))
  543. {
  544. logAnim->error("Group %d missing in %s", sourceGroup, name);
  545. return;
  546. }
  547. if(source[sourceGroup].size() <= sourceFrame)
  548. {
  549. logAnim->error("Frame [%d %d] missing in %s", sourceGroup, sourceFrame, name);
  550. return;
  551. }
  552. //todo: clone actual loaded Image object
  553. JsonNode clone(source[sourceGroup][sourceFrame]);
  554. if(clone.getType() == JsonNode::JsonType::DATA_NULL)
  555. {
  556. std::string temp = name+":"+boost::lexical_cast<std::string>(sourceGroup)+":"+boost::lexical_cast<std::string>(sourceFrame);
  557. clone["file"].String() = temp;
  558. }
  559. source[targetGroup].push_back(clone);
  560. size_t index = source[targetGroup].size() - 1;
  561. if(preloaded)
  562. load(index, targetGroup);
  563. }
  564. void Animation::setCustom(std::string filename, size_t frame, size_t group)
  565. {
  566. if(source[group].size() <= frame)
  567. source[group].resize(frame+1);
  568. source[group][frame]["file"].String() = filename;
  569. //FIXME: update image if already loaded
  570. }
  571. std::shared_ptr<QImage> Animation::getImage(size_t frame, size_t group, bool verbose) const
  572. {
  573. auto groupIter = images.find(group);
  574. if(groupIter != images.end())
  575. {
  576. auto imageIter = groupIter->second.find(frame);
  577. if(imageIter != groupIter->second.end())
  578. return imageIter->second;
  579. }
  580. if(verbose)
  581. printError(frame, group, "GetImage");
  582. return nullptr;
  583. }
  584. void Animation::exportBitmaps(const QDir & path) const
  585. {
  586. if(images.empty())
  587. {
  588. logGlobal->error("Nothing to export, animation is empty");
  589. return;
  590. }
  591. QString actualPath = path.absolutePath() + "/SPRITES/" + QString::fromStdString(name);
  592. QDir().mkdir(actualPath);
  593. size_t counter = 0;
  594. for(const auto& groupPair : images)
  595. {
  596. size_t group = groupPair.first;
  597. for(const auto& imagePair : groupPair.second)
  598. {
  599. size_t frame = imagePair.first;
  600. const auto img = imagePair.second;
  601. QString filename = QString("%1_%2_%3.png").arg(QString::fromStdString(name)).arg(group).arg(frame);
  602. QString filePath = actualPath + "/" + filename;
  603. img->save(filePath, "PNG");
  604. counter++;
  605. }
  606. }
  607. logGlobal->info("Exported %d frames to %s", counter, actualPath.toStdString());
  608. }
  609. void Animation::load()
  610. {
  611. for (auto & elem : source)
  612. for (size_t image=0; image < elem.second.size(); image++)
  613. loadFrame(image, elem.first);
  614. }
  615. void Animation::unload()
  616. {
  617. for (auto & elem : source)
  618. for (size_t image=0; image < elem.second.size(); image++)
  619. unloadFrame(image, elem.first);
  620. }
  621. void Animation::preload()
  622. {
  623. if(!preloaded)
  624. {
  625. preloaded = true;
  626. load();
  627. }
  628. }
  629. void Animation::loadGroup(size_t group)
  630. {
  631. if(vstd::contains(source, group))
  632. for (size_t image=0; image < source[group].size(); image++)
  633. loadFrame(image, group);
  634. }
  635. void Animation::unloadGroup(size_t group)
  636. {
  637. if(vstd::contains(source, group))
  638. for (size_t image=0; image < source[group].size(); image++)
  639. unloadFrame(image, group);
  640. }
  641. void Animation::load(size_t frame, size_t group)
  642. {
  643. loadFrame(frame, group);
  644. }
  645. void Animation::unload(size_t frame, size_t group)
  646. {
  647. unloadFrame(frame, group);
  648. }
  649. size_t Animation::size(size_t group) const
  650. {
  651. auto iter = source.find(group);
  652. if(iter != source.end())
  653. return iter->second.size();
  654. return 0;
  655. }
  656. void Animation::horizontalFlip()
  657. {
  658. for(auto & group : images)
  659. for(auto & image : group.second)
  660. *image.second = image.second->transformed(QTransform::fromScale(-1, 1));
  661. }
  662. void Animation::verticalFlip()
  663. {
  664. for(auto & group : images)
  665. for(auto & image : group.second)
  666. *image.second = image.second->transformed(QTransform::fromScale(1, -1));
  667. }
  668. void Animation::playerColored(PlayerColor player)
  669. {
  670. #if 0 //can be required in image preview?
  671. for(auto & group : images)
  672. for(auto & image : group.second)
  673. image.second->playerColored(player);
  674. #endif
  675. }
  676. void Animation::createFlippedGroup(const size_t sourceGroup, const size_t targetGroup)
  677. {
  678. for(size_t frame = 0; frame < size(sourceGroup); ++frame)
  679. {
  680. duplicateImage(sourceGroup, frame, targetGroup);
  681. auto image = getImage(frame, targetGroup);
  682. *image = image->transformed(QTransform::fromScale(1, -1));
  683. }
  684. }