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
20namespace doc {
21
22using namespace base::serialization;
23using namespace base::serialization::little_endian;
24
25std::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
33void SelectedFrames::clear()
34{
35 m_ranges.clear();
36}
37
38// TODO this works only for forward ranges
39void 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
77void 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
88SelectedFrames 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
117bool 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
129void 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
144SelectedFrames 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
154SelectedFrames 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
180bool 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
189bool 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