aseprite/src/doc/frames_sequence.cpp

208 lines
4.8 KiB
C++

// Aseprite Document Library
// Copyright (c) 2023 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/frames_sequence.h"
#include "base/base.h"
#include "base/debug.h"
#include "base/serialization.h"
#include <algorithm>
#include <iostream>
namespace doc {
using namespace base::serialization;
using namespace base::serialization::little_endian;
FramesSequence::FramesSequence(const SelectedFrames& selectedFrames)
{
// TODO: This might be optimized by copying each range directly, but
// for this I would need that SelectedFrames make this class its friend.
for(auto frame : selectedFrames) {
this->insert(frame);
}
}
std::size_t FramesSequence::size() const
{
std::size_t size = 0;
for (auto& range : m_ranges)
size += ABS(range.toFrame - range.fromFrame) + 1;
return size;
}
void FramesSequence::clear()
{
m_ranges.clear();
}
void FramesSequence::insert(frame_t frame)
{
ASSERT(frame >= 0);
if (m_ranges.empty()) {
m_ranges.push_back(FrameRange(frame));
return;
}
auto& lastRange = m_ranges.back();
int rangeStep = (lastRange.fromFrame < lastRange.toFrame) -
(lastRange.fromFrame > lastRange.toFrame);
int step = (lastRange.toFrame < frame) - (lastRange.toFrame > frame);
if (step &&
(rangeStep == step || !rangeStep) &&
frame-lastRange.toFrame == step)
lastRange.toFrame = frame;
else
m_ranges.push_back(FrameRange(frame));
}
void FramesSequence::insert(frame_t fromFrame, frame_t toFrame)
{
int step = (fromFrame <= toFrame ? 1 : -1);
for (frame_t frame = fromFrame; frame != toFrame; frame+=step) {
insert(frame);
}
insert(toFrame);
}
FramesSequence FramesSequence::filter(frame_t fromFrame, frame_t toFrame) const
{
FramesSequence f;
if (fromFrame > toFrame)
std::swap(fromFrame, toFrame);
for (const auto& range : m_ranges) {
FrameRange r(range);
const bool isForward = (r.fromFrame <= r.toFrame);
if (isForward) {
if (r.fromFrame < fromFrame) r.fromFrame = fromFrame;
if (r.toFrame > toFrame) r.toFrame = toFrame;
}
else {
if (r.fromFrame > toFrame) r.fromFrame = toFrame;
if (r.toFrame < fromFrame) r.toFrame = fromFrame;
}
if (( isForward && r.fromFrame <= r.toFrame) ||
(!isForward && r.fromFrame >= r.toFrame))
f.m_ranges.push_back(r);
}
return f;
}
bool FramesSequence::contains(frame_t frame) const
{
return std::find_if(
m_ranges.begin(),
m_ranges.end(),
[frame](const FrameRange& r) -> bool {
return (r.fromFrame <= frame && frame <= r.toFrame) ||
(r.fromFrame >= frame && frame >= r.toFrame);
}) != m_ranges.end();
}
frame_t FramesSequence::lowestFrame() const
{
frame_t lof = this->firstFrame();
for (auto range : m_ranges) {
lof = std::min<frame_t>(range.fromFrame, std::min<frame_t>(range.toFrame, lof));
}
return lof;
}
void FramesSequence::displace(frame_t frameDelta)
{
// Avoid setting negative numbers in frame ranges
auto lof = lowestFrame();
if (lof+frameDelta < 0)
frameDelta = -lof;
for (auto& range : m_ranges) {
range.fromFrame += frameDelta;
range.toFrame += frameDelta;
ASSERT(range.fromFrame >= 0);
ASSERT(range.toFrame >= 0);
}
}
FramesSequence FramesSequence::makeReverse() const
{
FramesSequence newFrames;
for (const FrameRange& range : m_ranges)
newFrames.m_ranges.insert(
newFrames.m_ranges.begin(),
FrameRange(range.toFrame, range.fromFrame));
return newFrames;
}
FramesSequence FramesSequence::makePingPong() const
{
FramesSequence newFrames = *this;
const int n = m_ranges.size();
int i = 0;
int j = m_ranges.size()-1;
for (const FrameRange& range : m_ranges) {
// Discard first or last range if it contains just one frame.
if (range.toFrame == range.fromFrame && (i == 0 || j == 0)) {
++i;
--j;
continue;
}
FrameRange reversedRange(range.toFrame,
range.fromFrame);
int step = (range.fromFrame < range.toFrame) -
(range.fromFrame > range.toFrame);
if (i == 0) reversedRange.toFrame+=step;
if (j == 0) reversedRange.fromFrame-=step;
if (reversedRange.fromFrame != reversedRange.toFrame)
newFrames.m_ranges.insert(
newFrames.m_ranges.begin() + n,
reversedRange);
++i;
--j;
}
return newFrames;
}
bool FramesSequence::write(std::ostream& os) const
{
write32(os, size());
for (const frame_t frame : *this) {
write32(os, frame);
}
return os.good();
}
bool FramesSequence::read(std::istream& is)
{
clear();
int nframes = read32(is);
for (int i=0; i<nframes && is; ++i) {
frame_t frame = read32(is);
insert(frame);
}
return is.good();
}
} // namespace doc