Preserve per-layer document timelines
This commit is contained in:
@@ -33,6 +33,15 @@ namespace {
|
||||
return "Layer " + std::to_string(index + 1U);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::uint64_t frame_duration_sum(std::span<const AnimationFrame> frames) noexcept
|
||||
{
|
||||
std::uint64_t duration = 0;
|
||||
for (const auto& frame : frames) {
|
||||
duration += frame.duration_ms;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status validate_layer_name(std::string_view name) noexcept
|
||||
{
|
||||
if (name.empty()) {
|
||||
@@ -108,11 +117,14 @@ pp::foundation::Result<CanvasDocument> CanvasDocument::create(DocumentConfig con
|
||||
CanvasDocument document;
|
||||
document.width_ = config.width;
|
||||
document.height_ = config.height;
|
||||
document.frames_.push_back(AnimationFrame {});
|
||||
document.layers_.reserve(config.layer_count);
|
||||
for (std::uint32_t i = 0; i < config.layer_count; ++i) {
|
||||
document.layers_.push_back(Layer { .name = default_layer_name(i) });
|
||||
document.layers_.push_back(Layer {
|
||||
.name = default_layer_name(i),
|
||||
.frames = document.frames_,
|
||||
});
|
||||
}
|
||||
document.frames_.push_back(AnimationFrame {});
|
||||
|
||||
return pp::foundation::Result<CanvasDocument>::success(document);
|
||||
}
|
||||
@@ -158,13 +170,33 @@ pp::foundation::Result<CanvasDocument> CanvasDocument::create_from_snapshot(Docu
|
||||
return pp::foundation::Result<CanvasDocument>::failure(blend_status);
|
||||
}
|
||||
|
||||
const auto layer_frames = layer_config.frames.empty() ? config.frames : layer_config.frames;
|
||||
if (layer_frames.empty()) {
|
||||
return pp::foundation::Result<CanvasDocument>::failure(
|
||||
pp::foundation::Status::invalid_argument("document layer must contain at least one frame"));
|
||||
}
|
||||
|
||||
if (layer_frames.size() > max_frame_count) {
|
||||
return pp::foundation::Result<CanvasDocument>::failure(
|
||||
pp::foundation::Status::out_of_range("document layer frame count exceeds the configured limit"));
|
||||
}
|
||||
|
||||
for (const auto& frame_config : layer_frames) {
|
||||
const auto duration_status = validate_frame_duration(frame_config.duration_ms);
|
||||
if (!duration_status.ok()) {
|
||||
return pp::foundation::Result<CanvasDocument>::failure(duration_status);
|
||||
}
|
||||
}
|
||||
|
||||
document.layers_.push_back(Layer {
|
||||
.name = std::string(layer_config.name),
|
||||
.visible = layer_config.visible,
|
||||
.alpha_locked = layer_config.alpha_locked,
|
||||
.opacity = layer_config.opacity,
|
||||
.blend_mode = layer_config.blend_mode,
|
||||
.frames = {},
|
||||
});
|
||||
document.layers_.back().frames.assign(layer_frames.begin(), layer_frames.end());
|
||||
}
|
||||
|
||||
document.frames_.reserve(config.frames.size());
|
||||
@@ -202,13 +234,23 @@ std::size_t CanvasDocument::active_frame_index() const noexcept
|
||||
|
||||
std::uint64_t CanvasDocument::animation_duration_ms() const noexcept
|
||||
{
|
||||
std::uint64_t duration = 0;
|
||||
for (const auto& frame : frames_) {
|
||||
duration += frame.duration_ms;
|
||||
std::uint64_t duration = frame_duration_sum(frames_);
|
||||
for (const auto& layer : layers_) {
|
||||
duration = std::max(duration, frame_duration_sum(layer.frames));
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::uint64_t> CanvasDocument::layer_animation_duration_ms(std::size_t index) const noexcept
|
||||
{
|
||||
const auto index_status = validate_layer_index(index, layers_.size());
|
||||
if (!index_status.ok()) {
|
||||
return pp::foundation::Result<std::uint64_t>::failure(index_status);
|
||||
}
|
||||
|
||||
return pp::foundation::Result<std::uint64_t>::success(frame_duration_sum(layers_[index].frames));
|
||||
}
|
||||
|
||||
std::span<const Layer> CanvasDocument::layers() const noexcept
|
||||
{
|
||||
return layers_;
|
||||
@@ -236,6 +278,7 @@ pp::foundation::Result<std::size_t> CanvasDocument::add_layer(std::string_view n
|
||||
}
|
||||
layer.name = std::string(name);
|
||||
}
|
||||
layer.frames = frames_;
|
||||
layers_.push_back(layer);
|
||||
active_layer_index_ = layers_.size() - 1U;
|
||||
return pp::foundation::Result<std::size_t>::success(active_layer_index_);
|
||||
@@ -381,6 +424,9 @@ pp::foundation::Result<std::size_t> CanvasDocument::add_frame(std::uint32_t dura
|
||||
}
|
||||
|
||||
frames_.push_back(AnimationFrame { .duration_ms = duration_ms });
|
||||
for (auto& layer : layers_) {
|
||||
layer.frames.push_back(AnimationFrame { .duration_ms = duration_ms });
|
||||
}
|
||||
active_frame_index_ = frames_.size() - 1U;
|
||||
return pp::foundation::Result<std::size_t>::success(active_frame_index_);
|
||||
}
|
||||
@@ -400,6 +446,13 @@ pp::foundation::Result<std::size_t> CanvasDocument::duplicate_frame(std::size_t
|
||||
|
||||
const auto insert_at = index + 1U;
|
||||
frames_.insert(frames_.begin() + static_cast<std::ptrdiff_t>(insert_at), frames_[index]);
|
||||
for (auto& layer : layers_) {
|
||||
if (index < layer.frames.size()) {
|
||||
layer.frames.insert(
|
||||
layer.frames.begin() + static_cast<std::ptrdiff_t>(insert_at),
|
||||
layer.frames[index]);
|
||||
}
|
||||
}
|
||||
active_frame_index_ = insert_at;
|
||||
return pp::foundation::Result<std::size_t>::success(active_frame_index_);
|
||||
}
|
||||
@@ -416,6 +469,11 @@ pp::foundation::Status CanvasDocument::remove_frame(std::size_t index)
|
||||
}
|
||||
|
||||
frames_.erase(frames_.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
for (auto& layer : layers_) {
|
||||
if (index < layer.frames.size() && layer.frames.size() > 1U) {
|
||||
layer.frames.erase(layer.frames.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
}
|
||||
}
|
||||
if (active_frame_index_ >= frames_.size()) {
|
||||
active_frame_index_ = frames_.size() - 1U;
|
||||
} else if (active_frame_index_ > index) {
|
||||
@@ -438,6 +496,13 @@ pp::foundation::Status CanvasDocument::move_frame(std::size_t from, std::size_t
|
||||
const auto frame = frames_[from];
|
||||
frames_.erase(frames_.begin() + static_cast<std::ptrdiff_t>(from));
|
||||
frames_.insert(frames_.begin() + static_cast<std::ptrdiff_t>(to), frame);
|
||||
for (auto& layer : layers_) {
|
||||
if (from < layer.frames.size() && to < layer.frames.size()) {
|
||||
const auto layer_frame = layer.frames[from];
|
||||
layer.frames.erase(layer.frames.begin() + static_cast<std::ptrdiff_t>(from));
|
||||
layer.frames.insert(layer.frames.begin() + static_cast<std::ptrdiff_t>(to), layer_frame);
|
||||
}
|
||||
}
|
||||
|
||||
if (active_frame_index_ == from) {
|
||||
active_frame_index_ = to;
|
||||
@@ -463,6 +528,11 @@ pp::foundation::Status CanvasDocument::set_frame_duration(std::size_t index, std
|
||||
}
|
||||
|
||||
frames_[index].duration_ms = duration_ms;
|
||||
for (auto& layer : layers_) {
|
||||
if (index < layer.frames.size()) {
|
||||
layer.frames[index].duration_ms = duration_ms;
|
||||
}
|
||||
}
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,16 +25,17 @@ struct DocumentConfig {
|
||||
std::uint32_t layer_count = 1;
|
||||
};
|
||||
|
||||
struct AnimationFrame {
|
||||
std::uint32_t duration_ms = 100;
|
||||
};
|
||||
|
||||
struct Layer {
|
||||
std::string name;
|
||||
bool visible = true;
|
||||
bool alpha_locked = false;
|
||||
float opacity = 1.0F;
|
||||
pp::paint::BlendMode blend_mode = pp::paint::BlendMode::normal;
|
||||
};
|
||||
|
||||
struct AnimationFrame {
|
||||
std::uint32_t duration_ms = 100;
|
||||
std::vector<AnimationFrame> frames;
|
||||
};
|
||||
|
||||
struct DocumentLayerConfig {
|
||||
@@ -43,6 +44,7 @@ struct DocumentLayerConfig {
|
||||
bool alpha_locked = false;
|
||||
float opacity = 1.0F;
|
||||
pp::paint::BlendMode blend_mode = pp::paint::BlendMode::normal;
|
||||
std::span<const AnimationFrame> frames;
|
||||
};
|
||||
|
||||
struct DocumentSnapshotConfig {
|
||||
@@ -62,6 +64,7 @@ public:
|
||||
[[nodiscard]] std::size_t active_layer_index() const noexcept;
|
||||
[[nodiscard]] std::size_t active_frame_index() const noexcept;
|
||||
[[nodiscard]] std::uint64_t animation_duration_ms() const noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<std::uint64_t> layer_animation_duration_ms(std::size_t index) const noexcept;
|
||||
[[nodiscard]] std::span<const Layer> layers() const noexcept;
|
||||
[[nodiscard]] std::span<const AnimationFrame> frames() const noexcept;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user