Add document frame move coverage
This commit is contained in:
@@ -87,8 +87,8 @@ Known local toolchain state:
|
||||
`pp_paint`, `pp_document`, `pp_renderer_api`, `pp_paint_renderer`,
|
||||
`pp_ui_core`, `pano_cli`, and their current headless test binaries,
|
||||
including foundation event/logging/task queue coverage, PNG metadata, PPI
|
||||
header, settings document, paint brush/stroke coverage, UI color parsing, and
|
||||
layout XML parse coverage.
|
||||
header, settings document, document frame move/duration coverage, paint
|
||||
brush/stroke coverage, UI color parsing, and layout XML parse coverage.
|
||||
- `pano_cli inspect-image` reports PNG IHDR metadata as JSON and is covered by
|
||||
`pano_cli_inspect_png_metadata_smoke` with a tiny IHDR fixture.
|
||||
- `panopainter_validate_shaders` validates the current combined GLSL shader
|
||||
|
||||
@@ -315,8 +315,9 @@ key/value limit tests.
|
||||
`pp_paint` has started with pure brush parameter validation/stamp evaluation,
|
||||
CPU reference math for the five current shader blend modes, and deterministic
|
||||
stroke spacing/interpolation. `pp_document` has
|
||||
started with a pure canvas/layer/frame model, layer metadata operations, and
|
||||
layer/frame/undo-redo history invariant tests. `pp_renderer_api` has started with renderer-neutral
|
||||
started with a pure canvas/layer/frame model, layer metadata operations, frame
|
||||
move/duration queries, and layer/frame/undo-redo history invariant tests.
|
||||
`pp_renderer_api` has started with renderer-neutral
|
||||
texture/readback descriptors and validation tests. `pp_paint_renderer` has
|
||||
started with deterministic CPU layer compositing over renderer extents using
|
||||
the paint blend reference. `pp_ui_core` has started with XML-layout-facing
|
||||
@@ -560,7 +561,8 @@ Results:
|
||||
- `pp_paint_brush_tests` passed.
|
||||
- `pp_paint_blend_tests` passed.
|
||||
- `pp_paint_stroke_tests` passed.
|
||||
- `pp_document_tests` passed.
|
||||
- `pp_document_tests` passed, including frame move, duration, and history
|
||||
invariants.
|
||||
- `pp_renderer_api_tests` passed.
|
||||
- `pp_paint_renderer_compositor_tests` passed.
|
||||
- `pp_ui_core_color_tests` passed.
|
||||
|
||||
@@ -55,6 +55,15 @@ namespace {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status validate_frame_index(std::size_t index, std::size_t frame_count) noexcept
|
||||
{
|
||||
if (index >= frame_count) {
|
||||
return pp::foundation::Status::out_of_range("frame index is outside the document");
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pp::foundation::Result<CanvasDocument> CanvasDocument::create(DocumentConfig config)
|
||||
@@ -96,6 +105,15 @@ std::size_t CanvasDocument::active_frame_index() const noexcept
|
||||
return active_frame_index_;
|
||||
}
|
||||
|
||||
std::uint64_t CanvasDocument::animation_duration_ms() const noexcept
|
||||
{
|
||||
std::uint64_t duration = 0;
|
||||
for (const auto& frame : frames_) {
|
||||
duration += frame.duration_ms;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
std::span<const Layer> CanvasDocument::layers() const noexcept
|
||||
{
|
||||
return layers_;
|
||||
@@ -265,9 +283,10 @@ pp::foundation::Result<std::size_t> CanvasDocument::add_frame(std::uint32_t dura
|
||||
|
||||
pp::foundation::Result<std::size_t> CanvasDocument::duplicate_frame(std::size_t index)
|
||||
{
|
||||
if (index >= frames_.size()) {
|
||||
const auto index_status = validate_frame_index(index, frames_.size());
|
||||
if (!index_status.ok()) {
|
||||
return pp::foundation::Result<std::size_t>::failure(
|
||||
pp::foundation::Status::out_of_range("frame index is outside the document"));
|
||||
index_status);
|
||||
}
|
||||
|
||||
if (frames_.size() >= max_frame_count) {
|
||||
@@ -283,8 +302,9 @@ pp::foundation::Result<std::size_t> CanvasDocument::duplicate_frame(std::size_t
|
||||
|
||||
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");
|
||||
const auto index_status = validate_frame_index(index, frames_.size());
|
||||
if (!index_status.ok()) {
|
||||
return index_status;
|
||||
}
|
||||
|
||||
if (frames_.size() == 1U) {
|
||||
@@ -301,10 +321,36 @@ pp::foundation::Status CanvasDocument::remove_frame(std::size_t index)
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Status CanvasDocument::move_frame(std::size_t from, std::size_t to)
|
||||
{
|
||||
if (from >= frames_.size() || to >= frames_.size()) {
|
||||
return pp::foundation::Status::out_of_range("frame index is outside the document");
|
||||
}
|
||||
|
||||
if (from == to) {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (active_frame_index_ == from) {
|
||||
active_frame_index_ = to;
|
||||
} else if (from < active_frame_index_ && active_frame_index_ <= to) {
|
||||
--active_frame_index_;
|
||||
} else if (to <= active_frame_index_ && active_frame_index_ < from) {
|
||||
++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");
|
||||
const auto index_status = validate_frame_index(index, frames_.size());
|
||||
if (!index_status.ok()) {
|
||||
return index_status;
|
||||
}
|
||||
|
||||
if (duration_ms < min_frame_duration_ms) {
|
||||
@@ -317,8 +363,9 @@ pp::foundation::Status CanvasDocument::set_frame_duration(std::size_t index, std
|
||||
|
||||
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");
|
||||
const auto index_status = validate_frame_index(index, frames_.size());
|
||||
if (!index_status.ok()) {
|
||||
return index_status;
|
||||
}
|
||||
|
||||
active_frame_index_ = index;
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
[[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::uint64_t animation_duration_ms() const noexcept;
|
||||
[[nodiscard]] std::span<const Layer> layers() const noexcept;
|
||||
[[nodiscard]] std::span<const AnimationFrame> frames() const noexcept;
|
||||
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
[[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 move_frame(std::size_t from, std::size_t to);
|
||||
[[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;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ void creates_document_with_default_layers(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, document.value().active_layer_index() == 0U);
|
||||
PP_EXPECT(h, document.value().frames().size() == 1U);
|
||||
PP_EXPECT(h, document.value().frames()[0].duration_ms == 100U);
|
||||
PP_EXPECT(h, document.value().animation_duration_ms() == 100U);
|
||||
PP_EXPECT(h, document.value().active_frame_index() == 0U);
|
||||
}
|
||||
|
||||
@@ -173,10 +174,45 @@ void manages_animation_frames_and_duration(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, document.frames()[2].duration_ms == 250U);
|
||||
PP_EXPECT(h, document.set_frame_duration(2, 333).ok());
|
||||
PP_EXPECT(h, document.frames()[2].duration_ms == 333U);
|
||||
PP_EXPECT(h, document.animation_duration_ms() == 683U);
|
||||
|
||||
PP_EXPECT(h, document.remove_frame(1).ok());
|
||||
PP_EXPECT(h, document.frames().size() == 2U);
|
||||
PP_EXPECT(h, document.active_frame_index() == 1U);
|
||||
PP_EXPECT(h, document.animation_duration_ms() == 433U);
|
||||
}
|
||||
|
||||
void moves_frames_and_preserves_active_frame_identity(pp::tests::Harness& h)
|
||||
{
|
||||
auto document_result = CanvasDocument::create(
|
||||
DocumentConfig { .width = 64, .height = 64, .layer_count = 1 });
|
||||
PP_EXPECT(h, document_result.ok());
|
||||
auto document = document_result.value();
|
||||
|
||||
PP_EXPECT(h, document.set_frame_duration(0, 100).ok());
|
||||
PP_EXPECT(h, document.add_frame(200).ok());
|
||||
PP_EXPECT(h, document.add_frame(300).ok());
|
||||
PP_EXPECT(h, document.add_frame(400).ok());
|
||||
|
||||
PP_EXPECT(h, document.set_active_frame(2).ok());
|
||||
PP_EXPECT(h, document.move_frame(2, 0).ok());
|
||||
PP_EXPECT(h, document.active_frame_index() == 0U);
|
||||
PP_EXPECT(h, document.frames()[0].duration_ms == 300U);
|
||||
PP_EXPECT(h, document.frames()[1].duration_ms == 100U);
|
||||
PP_EXPECT(h, document.frames()[2].duration_ms == 200U);
|
||||
PP_EXPECT(h, document.frames()[3].duration_ms == 400U);
|
||||
|
||||
PP_EXPECT(h, document.move_frame(3, 1).ok());
|
||||
PP_EXPECT(h, document.active_frame_index() == 0U);
|
||||
PP_EXPECT(h, document.frames()[1].duration_ms == 400U);
|
||||
PP_EXPECT(h, document.animation_duration_ms() == 1000U);
|
||||
|
||||
const auto missing_from = document.move_frame(9, 0);
|
||||
const auto missing_to = document.move_frame(0, 9);
|
||||
PP_EXPECT(h, !missing_from.ok());
|
||||
PP_EXPECT(h, missing_from.code == StatusCode::out_of_range);
|
||||
PP_EXPECT(h, !missing_to.ok());
|
||||
PP_EXPECT(h, missing_to.code == StatusCode::out_of_range);
|
||||
}
|
||||
|
||||
void rejects_invalid_animation_frame_operations(pp::tests::Harness& h)
|
||||
@@ -331,6 +367,7 @@ int main()
|
||||
harness.run("updates_layer_metadata", updates_layer_metadata);
|
||||
harness.run("rejects_invalid_layer_metadata", rejects_invalid_layer_metadata);
|
||||
harness.run("manages_animation_frames_and_duration", manages_animation_frames_and_duration);
|
||||
harness.run("moves_frames_and_preserves_active_frame_identity", moves_frames_and_preserves_active_frame_identity);
|
||||
harness.run("rejects_invalid_animation_frame_operations", rejects_invalid_animation_frame_operations);
|
||||
harness.run("records_document_history_and_restores_snapshots", records_document_history_and_restores_snapshots);
|
||||
harness.run("applying_after_undo_discards_redo_branch", applying_after_undo_discards_redo_branch);
|
||||
|
||||
Reference in New Issue
Block a user