1 | // Aseprite Document Library |
2 | // Copyright (c) 2016-2018 David Capello |
3 | // |
4 | // This file is released under the terms of the MIT license. |
5 | // Read LICENSE.txt for more information. |
6 | |
7 | #ifdef HAVE_CONFIG_H |
8 | #include "config.h" |
9 | #endif |
10 | |
11 | #include "doc/selected_frames.h" |
12 | |
13 | #include "base/base.h" |
14 | #include "base/debug.h" |
15 | #include "base/serialization.h" |
16 | |
17 | #include <algorithm> |
18 | #include <iostream> |
19 | |
20 | namespace doc { |
21 | |
22 | using namespace base::serialization; |
23 | using namespace base::serialization::little_endian; |
24 | |
25 | std::size_t SelectedFrames::size() const |
26 | { |
27 | std::size_t size = 0; |
28 | for (auto& range : m_ranges) |
29 | size += ABS(range.toFrame - range.fromFrame) + 1; |
30 | return size; |
31 | } |
32 | |
33 | void SelectedFrames::clear() |
34 | { |
35 | m_ranges.clear(); |
36 | } |
37 | |
38 | // TODO this works only for forward ranges |
39 | void SelectedFrames::insert(frame_t frame) |
40 | { |
41 | ASSERT(frame >= 0); |
42 | |
43 | if (m_ranges.empty()) { |
44 | m_ranges.push_back(FrameRange(frame)); |
45 | return; |
46 | } |
47 | |
48 | auto it = m_ranges.begin(); |
49 | auto end = m_ranges.end(); |
50 | auto next = it; |
51 | |
52 | for (; it!=end; it=next) { |
53 | if (frame >= it->fromFrame && |
54 | frame <= it->toFrame) |
55 | break; |
56 | |
57 | if (frame < it->fromFrame) { |
58 | if (frame == it->fromFrame-1) |
59 | --it->fromFrame; |
60 | else |
61 | m_ranges.insert(it, FrameRange(frame)); |
62 | break; |
63 | } |
64 | |
65 | ++next; |
66 | |
67 | if (frame > it->toFrame && (next == end || frame < next->fromFrame-1)) { |
68 | if (frame == it->toFrame+1) |
69 | ++it->toFrame; |
70 | else |
71 | m_ranges.insert(next, FrameRange(frame)); |
72 | break; |
73 | } |
74 | } |
75 | } |
76 | |
77 | void SelectedFrames::insert(frame_t fromFrame, frame_t toFrame) |
78 | { |
79 | if (fromFrame > toFrame) |
80 | std::swap(fromFrame, toFrame); |
81 | |
82 | // TODO improve this |
83 | for (frame_t frame = fromFrame; frame <= toFrame; ++frame) { |
84 | insert(frame); |
85 | } |
86 | } |
87 | |
88 | SelectedFrames SelectedFrames::filter(frame_t fromFrame, frame_t toFrame) const |
89 | { |
90 | SelectedFrames f; |
91 | |
92 | if (fromFrame > toFrame) |
93 | std::swap(fromFrame, toFrame); |
94 | |
95 | for (const auto& range : m_ranges) { |
96 | FrameRange r(range); |
97 | const bool isForward = (r.fromFrame <= r.toFrame); |
98 | |
99 | if (isForward) { |
100 | if (r.fromFrame < fromFrame) r.fromFrame = fromFrame; |
101 | if (r.toFrame > toFrame) r.toFrame = toFrame; |
102 | } |
103 | else { |
104 | if (r.fromFrame > toFrame) r.fromFrame = toFrame; |
105 | if (r.toFrame < fromFrame) r.toFrame = fromFrame; |
106 | } |
107 | |
108 | if (( isForward && r.fromFrame <= r.toFrame) || |
109 | (!isForward && r.fromFrame >= r.toFrame)) |
110 | f.m_ranges.push_back(r); |
111 | } |
112 | |
113 | return f; |
114 | } |
115 | |
116 | // TODO this works only for forward ranges |
117 | bool SelectedFrames::contains(frame_t frame) const |
118 | { |
119 | return std::binary_search( |
120 | m_ranges.begin(), |
121 | m_ranges.end(), |
122 | FrameRange(frame), |
123 | [](const FrameRange& a, |
124 | const FrameRange& b) -> bool { |
125 | return (a.toFrame < b.fromFrame); |
126 | }); |
127 | } |
128 | |
129 | void SelectedFrames::displace(frame_t frameDelta) |
130 | { |
131 | // Avoid setting negative numbers in frame ranges |
132 | if (firstFrame()+frameDelta < 0) |
133 | frameDelta = -firstFrame(); |
134 | |
135 | for (auto& range : m_ranges) { |
136 | range.fromFrame += frameDelta; |
137 | range.toFrame += frameDelta; |
138 | |
139 | ASSERT(range.fromFrame >= 0); |
140 | ASSERT(range.toFrame >= 0); |
141 | } |
142 | } |
143 | |
144 | SelectedFrames SelectedFrames::makeReverse() const |
145 | { |
146 | SelectedFrames newFrames; |
147 | for (const FrameRange& range : m_ranges) |
148 | newFrames.m_ranges.insert( |
149 | newFrames.m_ranges.begin(), |
150 | FrameRange(range.toFrame, range.fromFrame)); |
151 | return newFrames; |
152 | } |
153 | |
154 | SelectedFrames SelectedFrames::makePingPong() const |
155 | { |
156 | SelectedFrames newFrames = *this; |
157 | const int n = m_ranges.size(); |
158 | int i = 0; |
159 | int j = m_ranges.size()-1; |
160 | |
161 | for (const FrameRange& range : m_ranges) { |
162 | FrameRange reversedRange(range.toFrame, |
163 | range.fromFrame); |
164 | |
165 | if (i == 0) reversedRange.toFrame++; |
166 | if (j == 0) reversedRange.fromFrame--; |
167 | |
168 | if (reversedRange.fromFrame >= reversedRange.toFrame) |
169 | newFrames.m_ranges.insert( |
170 | newFrames.m_ranges.begin() + n, |
171 | reversedRange); |
172 | |
173 | ++i; |
174 | --j; |
175 | } |
176 | |
177 | return newFrames; |
178 | } |
179 | |
180 | bool SelectedFrames::write(std::ostream& os) const |
181 | { |
182 | write32(os, size()); |
183 | for (const frame_t frame : *this) { |
184 | write32(os, frame); |
185 | } |
186 | return os.good(); |
187 | } |
188 | |
189 | bool SelectedFrames::read(std::istream& is) |
190 | { |
191 | clear(); |
192 | |
193 | int nframes = read32(is); |
194 | for (int i=0; i<nframes && is; ++i) { |
195 | frame_t frame = read32(is); |
196 | insert(frame); |
197 | } |
198 | return is.good(); |
199 | } |
200 | |
201 | } // namespace doc |
202 | |