1// Aseprite Document Library
2// Copyright (C) 2019-2022 Igara Studio S.A.
3// Copyright (C) 2001-2016 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "doc/handle_anidir.h"
13
14#include "doc/frame.h"
15#include "doc/sprite.h"
16#include "doc/tag.h"
17
18namespace doc {
19
20frame_t calculate_next_frame(
21 const Sprite* sprite,
22 frame_t frame,
23 frame_t frameDelta,
24 const Tag* tag,
25 bool& pingPongForward)
26{
27 if (frameDelta == 0)
28 return frame;
29
30 frame_t first = frame_t(0);
31 frame_t last = sprite->lastFrame();
32 AniDir aniDir = AniDir::FORWARD;
33
34 if (tag) {
35 frame_t loopFrom, loopTo;
36
37 loopFrom = tag->fromFrame();
38 loopTo = tag->toFrame();
39 loopFrom = std::clamp(loopFrom, first, last);
40 loopTo = std::clamp(loopTo, first, last);
41
42 first = loopFrom;
43 last = loopTo;
44 aniDir = tag->aniDir();
45 }
46
47 frame_t frameRange = (last - first + 1);
48
49 switch (aniDir) {
50
51 case AniDir::REVERSE:
52 frameDelta = -frameDelta;
53 [[fallthrough]];
54
55 case AniDir::FORWARD:
56 frame += frameDelta;
57 while (frame > last) frame -= frameRange;
58 while (frame < first) frame += frameRange;
59 break;
60
61 case AniDir::PING_PONG: {
62 bool invertPingPong;
63 if (frameDelta < 0) {
64 frameDelta = -frameDelta;
65 pingPongForward = !pingPongForward;
66 invertPingPong = true;
67 }
68 else
69 invertPingPong = false;
70
71 while (--frameDelta >= 0) {
72 if (pingPongForward) {
73 ++frame;
74 if (frame > last) {
75 frame = last-1;
76 if (frame < first)
77 frame = first;
78 pingPongForward = false;
79 }
80 }
81 else {
82 --frame;
83 if (frame < first) {
84 frame = first+1;
85 if (frame > last)
86 frame = last;
87 pingPongForward = true;
88 }
89 }
90 }
91
92 if (invertPingPong)
93 pingPongForward = !pingPongForward;
94 break;
95 }
96 }
97
98 return frame;
99}
100
101} // namespace doc
102