Add document animation frame tests

This commit is contained in:
2026-06-01 00:16:34 +02:00
parent 4d715afd60
commit dfdb7a4468
6 changed files with 171 additions and 3 deletions

View File

@@ -48,6 +48,7 @@ pp::foundation::Result<CanvasDocument> CanvasDocument::create(DocumentConfig con
for (std::uint32_t i = 0; i < config.layer_count; ++i) {
document.layers_.push_back(Layer { .name = default_layer_name(i) });
}
document.frames_.push_back(AnimationFrame {});
return pp::foundation::Result<CanvasDocument>::success(document);
}
@@ -67,11 +68,21 @@ std::size_t CanvasDocument::active_layer_index() const noexcept
return active_layer_index_;
}
std::size_t CanvasDocument::active_frame_index() const noexcept
{
return active_frame_index_;
}
std::span<const Layer> CanvasDocument::layers() const noexcept
{
return layers_;
}
std::span<const AnimationFrame> CanvasDocument::frames() const noexcept
{
return frames_;
}
pp::foundation::Result<std::size_t> CanvasDocument::add_layer(std::string_view name)
{
if (layers_.size() >= max_layer_count) {
@@ -141,4 +152,83 @@ pp::foundation::Status CanvasDocument::set_active_layer(std::size_t index) noexc
return pp::foundation::Status::success();
}
pp::foundation::Result<std::size_t> CanvasDocument::add_frame(std::uint32_t duration_ms)
{
if (frames_.size() >= max_frame_count) {
return pp::foundation::Result<std::size_t>::failure(
pp::foundation::Status::out_of_range("document frame count exceeds the configured limit"));
}
if (duration_ms < min_frame_duration_ms) {
return pp::foundation::Result<std::size_t>::failure(
pp::foundation::Status::invalid_argument("frame duration must be greater than zero"));
}
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_);
}
pp::foundation::Result<std::size_t> CanvasDocument::duplicate_frame(std::size_t index)
{
if (index >= frames_.size()) {
return pp::foundation::Result<std::size_t>::failure(
pp::foundation::Status::out_of_range("frame index is outside the document"));
}
if (frames_.size() >= max_frame_count) {
return pp::foundation::Result<std::size_t>::failure(
pp::foundation::Status::out_of_range("document frame count exceeds the configured limit"));
}
const auto insert_at = index + 1U;
frames_.insert(frames_.begin() + static_cast<std::ptrdiff_t>(insert_at), frames_[index]);
active_frame_index_ = insert_at;
return pp::foundation::Result<std::size_t>::success(active_frame_index_);
}
pp::foundation::Status CanvasDocument::remove_frame(std::size_t index)
{
if (index >= frames_.size()) {
return pp::foundation::Status::out_of_range("frame index is outside the document");
}
if (frames_.size() == 1U) {
return pp::foundation::Status::invalid_argument("document must keep at least one frame");
}
frames_.erase(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) {
--active_frame_index_;
}
return pp::foundation::Status::success();
}
pp::foundation::Status CanvasDocument::set_frame_duration(std::size_t index, std::uint32_t duration_ms) noexcept
{
if (index >= frames_.size()) {
return pp::foundation::Status::out_of_range("frame index is outside the document");
}
if (duration_ms < min_frame_duration_ms) {
return pp::foundation::Status::invalid_argument("frame duration must be greater than zero");
}
frames_[index].duration_ms = duration_ms;
return pp::foundation::Status::success();
}
pp::foundation::Status CanvasDocument::set_active_frame(std::size_t index) noexcept
{
if (index >= frames_.size()) {
return pp::foundation::Status::out_of_range("frame index is outside the document");
}
active_frame_index_ = index;
return pp::foundation::Status::success();
}
}

View File

@@ -13,6 +13,8 @@ namespace pp::document {
constexpr std::uint32_t max_canvas_dimension = 131072;
constexpr std::uint32_t max_layer_count = 1024;
constexpr std::uint32_t max_frame_count = 100000;
constexpr std::uint32_t min_frame_duration_ms = 1;
struct DocumentConfig {
std::uint32_t width = 0;
@@ -27,6 +29,10 @@ struct Layer {
pp::paint::BlendMode blend_mode = pp::paint::BlendMode::normal;
};
struct AnimationFrame {
std::uint32_t duration_ms = 100;
};
class CanvasDocument {
public:
[[nodiscard]] static pp::foundation::Result<CanvasDocument> create(DocumentConfig config);
@@ -34,18 +40,28 @@ public:
[[nodiscard]] std::uint32_t width() const noexcept;
[[nodiscard]] std::uint32_t height() const noexcept;
[[nodiscard]] std::size_t active_layer_index() const noexcept;
[[nodiscard]] std::size_t active_frame_index() const noexcept;
[[nodiscard]] std::span<const Layer> layers() const noexcept;
[[nodiscard]] std::span<const AnimationFrame> frames() const noexcept;
[[nodiscard]] pp::foundation::Result<std::size_t> add_layer(std::string_view name);
[[nodiscard]] pp::foundation::Status remove_layer(std::size_t index);
[[nodiscard]] pp::foundation::Status move_layer(std::size_t from, std::size_t to);
[[nodiscard]] pp::foundation::Status set_active_layer(std::size_t index) noexcept;
[[nodiscard]] pp::foundation::Result<std::size_t> add_frame(std::uint32_t duration_ms);
[[nodiscard]] pp::foundation::Result<std::size_t> duplicate_frame(std::size_t index);
[[nodiscard]] pp::foundation::Status remove_frame(std::size_t index);
[[nodiscard]] pp::foundation::Status set_frame_duration(std::size_t index, std::uint32_t duration_ms) noexcept;
[[nodiscard]] pp::foundation::Status set_active_frame(std::size_t index) noexcept;
private:
std::uint32_t width_ = 0;
std::uint32_t height_ = 0;
std::size_t active_layer_index_ = 0;
std::size_t active_frame_index_ = 0;
std::vector<Layer> layers_;
std::vector<AnimationFrame> frames_;
};
}