Images.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. * Images.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 "Images.h"
  12. #include "MiscWidgets.h"
  13. #include "../gui/CGuiHandler.h"
  14. #include "../renderSDL/SDL_Extensions.h"
  15. #include "../render/IImage.h"
  16. #include "../render/CAnimation.h"
  17. #include "../battle/BattleInterface.h"
  18. #include "../battle/BattleInterfaceClasses.h"
  19. #include "../CGameInfo.h"
  20. #include "../CPlayerInterface.h"
  21. #include "../CMusicHandler.h"
  22. #include "../../CCallback.h"
  23. #include "../../lib/CConfigHandler.h"
  24. #include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
  25. #include "../../lib/CRandomGenerator.h"
  26. CPicture::CPicture(std::shared_ptr<IImage> image, const Point & position)
  27. : bg(image)
  28. , visible(true)
  29. , needRefresh(false)
  30. {
  31. pos += position;
  32. pos.w = bg->width();
  33. pos.h = bg->height();
  34. }
  35. CPicture::CPicture( const std::string &bmpname, int x, int y )
  36. : CPicture(bmpname, Point(x,y))
  37. {}
  38. CPicture::CPicture( const std::string &bmpname )
  39. : CPicture(bmpname, Point(0,0))
  40. {}
  41. CPicture::CPicture( const std::string &bmpname, const Point & position )
  42. : bg(IImage::createFromFile(bmpname))
  43. , visible(true)
  44. , needRefresh(false)
  45. {
  46. pos.x += position.x;
  47. pos.y += position.y;
  48. assert(bg);
  49. if(bg)
  50. {
  51. pos.w = bg->width();
  52. pos.h = bg->height();
  53. }
  54. else
  55. {
  56. pos.w = pos.h = 0;
  57. }
  58. }
  59. CPicture::CPicture(std::shared_ptr<IImage> image, const Rect &SrcRect, int x, int y)
  60. : CPicture(image, Point(x,y))
  61. {
  62. srcRect = SrcRect;
  63. pos.w = srcRect->w;
  64. pos.h = srcRect->h;
  65. }
  66. void CPicture::show(SDL_Surface * to)
  67. {
  68. if (visible && needRefresh)
  69. showAll(to);
  70. }
  71. void CPicture::showAll(SDL_Surface * to)
  72. {
  73. if(bg && visible)
  74. bg->draw(to, pos.x, pos.y, srcRect.has_value() ? &srcRect.value() : nullptr);
  75. }
  76. void CPicture::setAlpha(int value)
  77. {
  78. bg->setAlpha(value);
  79. }
  80. void CPicture::scaleTo(Point size)
  81. {
  82. bg = bg->scaleFast(size);
  83. pos.w = bg->width();
  84. pos.h = bg->height();
  85. }
  86. void CPicture::colorize(PlayerColor player)
  87. {
  88. bg->playerColored(player);
  89. }
  90. CFilledTexture::CFilledTexture(std::string imageName, Rect position):
  91. CIntObject(0, position.topLeft()),
  92. texture(IImage::createFromFile(imageName))
  93. {
  94. pos.w = position.w;
  95. pos.h = position.h;
  96. imageArea = Rect(Point(), texture->dimensions());
  97. }
  98. CFilledTexture::CFilledTexture(std::shared_ptr<IImage> image, Rect position, Rect imageArea)
  99. : CIntObject(0, position.topLeft())
  100. , texture(image)
  101. , imageArea(imageArea)
  102. {
  103. pos.w = position.w;
  104. pos.h = position.h;
  105. }
  106. void CFilledTexture::showAll(SDL_Surface *to)
  107. {
  108. CSDL_Ext::CClipRectGuard guard(to, pos);
  109. for (int y=pos.top(); y < pos.bottom(); y+= texture->height())
  110. {
  111. for (int x=pos.left(); x < pos.right(); x+=texture->width())
  112. texture->draw(to, x, y, &imageArea);
  113. }
  114. }
  115. CAnimImage::CAnimImage(const std::string & name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
  116. frame(Frame),
  117. group(Group),
  118. player(-1),
  119. flags(Flags)
  120. {
  121. pos.x += x;
  122. pos.y += y;
  123. anim = std::make_shared<CAnimation>(name);
  124. init();
  125. }
  126. CAnimImage::CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags):
  127. anim(Anim),
  128. frame(Frame),
  129. group(Group),
  130. player(-1),
  131. flags(Flags)
  132. {
  133. pos.x += x;
  134. pos.y += y;
  135. init();
  136. }
  137. CAnimImage::CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, Rect targetPos, size_t Group, ui8 Flags):
  138. anim(Anim),
  139. frame(Frame),
  140. group(Group),
  141. player(-1),
  142. flags(Flags),
  143. scaledSize(targetPos.w, targetPos.h)
  144. {
  145. pos.x += targetPos.x;
  146. pos.y += targetPos.y;
  147. init();
  148. }
  149. size_t CAnimImage::size()
  150. {
  151. return anim->size(group);
  152. }
  153. bool CAnimImage::isScaled() const
  154. {
  155. return (scaledSize.x != 0);
  156. }
  157. void CAnimImage::setSizeFromImage(const IImage &img)
  158. {
  159. if (isScaled())
  160. {
  161. // At the time of writing this, IImage had no method to scale to different aspect ratio
  162. // Therefore, have to ignore the target height and preserve original aspect ratio
  163. pos.w = scaledSize.x;
  164. pos.h = roundf(float(scaledSize.x) * img.height() / img.width());
  165. }
  166. else
  167. {
  168. pos.w = img.width();
  169. pos.h = img.height();
  170. }
  171. }
  172. void CAnimImage::init()
  173. {
  174. visible = true;
  175. anim->load(frame, group);
  176. if (flags & CShowableAnim::BASE)
  177. anim->load(0,group);
  178. auto img = anim->getImage(frame, group);
  179. if (img)
  180. setSizeFromImage(*img);
  181. }
  182. CAnimImage::~CAnimImage()
  183. {
  184. }
  185. void CAnimImage::showAll(SDL_Surface * to)
  186. {
  187. if(!visible)
  188. return;
  189. std::vector<size_t> frames = {frame};
  190. if((flags & CShowableAnim::BASE) && frame != 0)
  191. {
  192. frames.insert(frames.begin(), 0);
  193. }
  194. for(auto targetFrame : frames)
  195. {
  196. if(auto img = anim->getImage(targetFrame, group))
  197. {
  198. if(isScaled())
  199. {
  200. auto scaled = img->scaleFast(scaledSize);
  201. scaled->draw(to, pos.x, pos.y);
  202. }
  203. else
  204. img->draw(to, pos.x, pos.y);
  205. }
  206. }
  207. }
  208. void CAnimImage::setFrame(size_t Frame, size_t Group)
  209. {
  210. if (frame == Frame && group==Group)
  211. return;
  212. if (anim->size(Group) > Frame)
  213. {
  214. anim->load(Frame, Group);
  215. frame = Frame;
  216. group = Group;
  217. if(auto img = anim->getImage(frame, group))
  218. {
  219. if (flags & CShowableAnim::PLAYER_COLORED)
  220. img->playerColored(player);
  221. setSizeFromImage(*img);
  222. }
  223. }
  224. else
  225. logGlobal->error("Error: accessing unavailable frame %d:%d in CAnimation!", Group, Frame);
  226. }
  227. void CAnimImage::playerColored(PlayerColor currPlayer)
  228. {
  229. player = currPlayer;
  230. flags |= CShowableAnim::PLAYER_COLORED;
  231. anim->getImage(frame, group)->playerColored(player);
  232. if (flags & CShowableAnim::BASE)
  233. anim->getImage(0, group)->playerColored(player);
  234. }
  235. CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
  236. anim(std::make_shared<CAnimation>(name)),
  237. group(Group),
  238. frame(0),
  239. first(0),
  240. frameTimeTotal(frameTime),
  241. frameTimePassed(0),
  242. flags(Flags),
  243. xOffset(0),
  244. yOffset(0),
  245. alpha(alpha)
  246. {
  247. anim->loadGroup(group);
  248. last = anim->size(group);
  249. pos.w = anim->getImage(0, group)->width();
  250. pos.h = anim->getImage(0, group)->height();
  251. pos.x+= x;
  252. pos.y+= y;
  253. }
  254. CShowableAnim::~CShowableAnim()
  255. {
  256. anim->unloadGroup(group);
  257. }
  258. void CShowableAnim::setAlpha(ui32 alphaValue)
  259. {
  260. alpha = std::min<ui32>(alphaValue, 255);
  261. }
  262. bool CShowableAnim::set(size_t Group, size_t from, size_t to)
  263. {
  264. size_t max = anim->size(Group);
  265. if (to < max)
  266. max = to;
  267. if (max < from || max == 0)
  268. return false;
  269. anim->unloadGroup(group);
  270. anim->loadGroup(Group);
  271. group = Group;
  272. frame = first = from;
  273. last = max;
  274. frameTimePassed = 0;
  275. return true;
  276. }
  277. bool CShowableAnim::set(size_t Group)
  278. {
  279. if (anim->size(Group)== 0)
  280. return false;
  281. if (group != Group)
  282. {
  283. anim->unloadGroup(group);
  284. anim->loadGroup(Group);
  285. first = 0;
  286. group = Group;
  287. last = anim->size(Group);
  288. }
  289. frame = 0;
  290. frameTimePassed = 0;
  291. return true;
  292. }
  293. void CShowableAnim::reset()
  294. {
  295. frame = first;
  296. if (callback)
  297. callback();
  298. }
  299. void CShowableAnim::clipRect(int posX, int posY, int width, int height)
  300. {
  301. xOffset = posX;
  302. yOffset = posY;
  303. pos.w = width;
  304. pos.h = height;
  305. }
  306. void CShowableAnim::show(SDL_Surface * to)
  307. {
  308. if ( flags & BASE )// && frame != first) // FIXME: results in graphical glytch in Fortress, upgraded hydra's dwelling
  309. blitImage(first, group, to);
  310. blitImage(frame, group, to);
  311. if ((flags & PLAY_ONCE) && frame + 1 == last)
  312. return;
  313. frameTimePassed += GH.mainFPSmng->getElapsedMilliseconds();
  314. if(frameTimePassed >= frameTimeTotal)
  315. {
  316. frameTimePassed -= frameTimeTotal;
  317. if ( ++frame >= last)
  318. reset();
  319. }
  320. }
  321. void CShowableAnim::showAll(SDL_Surface * to)
  322. {
  323. if ( flags & BASE )// && frame != first)
  324. blitImage(first, group, to);
  325. blitImage(frame, group, to);
  326. }
  327. void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to)
  328. {
  329. assert(to);
  330. Rect src( xOffset, yOffset, pos.w, pos.h);
  331. auto img = anim->getImage(frame, group);
  332. if(img)
  333. {
  334. img->setAlpha(alpha);
  335. img->draw(to, pos.x, pos.y, &src);
  336. }
  337. }
  338. void CShowableAnim::rotate(bool on, bool vertical)
  339. {
  340. ui8 flag = vertical? VERTICAL_FLIP:HORIZONTAL_FLIP;
  341. if (on)
  342. flags |= flag;
  343. else
  344. flags &= ~flag;
  345. }
  346. void CShowableAnim::setDuration(int durationMs)
  347. {
  348. frameTimeTotal = durationMs/(last - first);
  349. }
  350. CCreatureAnim::CCreatureAnim(int x, int y, std::string name, ui8 flags, ECreatureAnimType type):
  351. CShowableAnim(x, y, name, flags, 100, size_t(type)) // H3 uses 100 ms per frame, irregardless of battle speed settings
  352. {
  353. xOffset = 0;
  354. yOffset = 0;
  355. }
  356. void CCreatureAnim::loopPreview(bool warMachine)
  357. {
  358. std::vector<ECreatureAnimType> available;
  359. static const ECreatureAnimType creaPreviewList[] = {
  360. ECreatureAnimType::HOLDING,
  361. ECreatureAnimType::HITTED,
  362. ECreatureAnimType::DEFENCE,
  363. ECreatureAnimType::ATTACK_FRONT,
  364. ECreatureAnimType::SPECIAL_FRONT
  365. };
  366. static const ECreatureAnimType machPreviewList[] = {
  367. ECreatureAnimType::HOLDING,
  368. ECreatureAnimType::MOVING,
  369. ECreatureAnimType::SHOOT_UP,
  370. ECreatureAnimType::SHOOT_FRONT,
  371. ECreatureAnimType::SHOOT_DOWN
  372. };
  373. auto & previewList = warMachine ? machPreviewList : creaPreviewList;
  374. for (auto & elem : previewList)
  375. if (anim->size(size_t(elem)))
  376. available.push_back(elem);
  377. size_t rnd = CRandomGenerator::getDefault().nextInt((int)available.size() * 2 - 1);
  378. if (rnd >= available.size())
  379. {
  380. ECreatureAnimType type;
  381. if ( anim->size(size_t(ECreatureAnimType::MOVING)) == 0 )//no moving animation present
  382. type = ECreatureAnimType::HOLDING;
  383. else
  384. type = ECreatureAnimType::MOVING;
  385. //display this anim for ~1 second (time is random, but it looks good)
  386. for (size_t i=0; i< 12/anim->size(size_t(type)) + 1; i++)
  387. addLast(type);
  388. }
  389. else
  390. addLast(available[rnd]);
  391. }
  392. void CCreatureAnim::addLast(ECreatureAnimType newType)
  393. {
  394. auto currType = ECreatureAnimType(group);
  395. if (currType != ECreatureAnimType::MOVING && newType == ECreatureAnimType::MOVING)//starting moving - play init sequence
  396. {
  397. queue.push( ECreatureAnimType::MOVE_START );
  398. }
  399. else if (currType == ECreatureAnimType::MOVING && newType != ECreatureAnimType::MOVING )//previous anim was moving - finish it
  400. {
  401. queue.push( ECreatureAnimType::MOVE_END );
  402. }
  403. if (newType == ECreatureAnimType::TURN_L || newType == ECreatureAnimType::TURN_R)
  404. queue.push(newType);
  405. queue.push(newType);
  406. }
  407. void CCreatureAnim::reset()
  408. {
  409. //if we are in the middle of rotation - set flag
  410. if (group == size_t(ECreatureAnimType::TURN_L) && !queue.empty() && queue.front() == ECreatureAnimType::TURN_L)
  411. rotate(true);
  412. if (group == size_t(ECreatureAnimType::TURN_R) && !queue.empty() && queue.front() == ECreatureAnimType::TURN_R)
  413. rotate(false);
  414. while (!queue.empty())
  415. {
  416. ECreatureAnimType at = queue.front();
  417. queue.pop();
  418. if (set(size_t(at)))
  419. return;
  420. }
  421. if (callback)
  422. callback();
  423. while (!queue.empty())
  424. {
  425. ECreatureAnimType at = queue.front();
  426. queue.pop();
  427. if (set(size_t(at)))
  428. return;
  429. }
  430. set(size_t(ECreatureAnimType::HOLDING));
  431. }
  432. void CCreatureAnim::startPreview(bool warMachine)
  433. {
  434. callback = std::bind(&CCreatureAnim::loopPreview, this, warMachine);
  435. }
  436. void CCreatureAnim::clearAndSet(ECreatureAnimType type)
  437. {
  438. while (!queue.empty())
  439. queue.pop();
  440. set(size_t(type));
  441. }