Animation.cpp 19 KB

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