Create animation documents from pano cli

This commit is contained in:
2026-06-01 09:10:59 +02:00
parent 4ec2d093e8
commit d0ef88be89
4 changed files with 57 additions and 15 deletions

View File

@@ -96,6 +96,8 @@ Known local toolchain state:
parsing, and layout XML parse coverage. parsing, and layout XML parse coverage.
- `pano_cli inspect-image` reports PNG IHDR metadata as JSON and is covered by - `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. `pano_cli_inspect_png_metadata_smoke` with a tiny IHDR fixture.
- `pano_cli create-document` supports `--frames` and `--frame-duration-ms` and
is covered by `pano_cli_create_animation_document_smoke`.
- `panopainter_validate_shaders` validates the current combined GLSL shader - `panopainter_validate_shaders` validates the current combined GLSL shader
files for one vertex stage marker, one fragment stage marker, valid marker files for one vertex stage marker, one fragment stage marker, valid marker
order, and existing relative includes. order, and existing relative includes.

View File

@@ -327,9 +327,10 @@ the paint blend reference. `pp_ui_core` has started with XML-layout-facing
length parsing, color parsing, tinyxml-backed layout XML parsing, and invalid length parsing, color parsing, tinyxml-backed layout XML parsing, and invalid
input tests. input tests.
`pano_cli inspect-image` exposes PNG IHDR metadata as JSON, and `pano_cli inspect-image` exposes PNG IHDR metadata as JSON, and
`pano_cli parse-layout` exercises the XML layout path. Continue expanding `pano_cli create-document` can create simple animation documents with explicit
document behavior toward legacy Canvas parity and then port OpenGL classes frame count/duration. `pano_cli parse-layout` exercises the XML layout path.
behind the renderer boundary. Continue expanding document behavior toward legacy Canvas parity and then port
OpenGL classes behind the renderer boundary.
Implementation tasks: Implementation tasks:
@@ -574,6 +575,8 @@ Results:
- `pp_ui_core_layout_value_tests` passed. - `pp_ui_core_layout_value_tests` passed.
- `pp_ui_core_layout_xml_tests` passed. - `pp_ui_core_layout_xml_tests` passed.
- `pano_cli_create_document_smoke` passed. - `pano_cli_create_document_smoke` passed.
- `pano_cli_create_animation_document_smoke` passed and reports animation
duration JSON.
- `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure - `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure
test. test.
- `pano_cli_inspect_png_metadata_smoke` passed and reports PNG metadata JSON - `pano_cli_inspect_png_metadata_smoke` passed and reports PNG metadata JSON

View File

@@ -202,6 +202,12 @@ if(TARGET pano_cli)
set_tests_properties(pano_cli_create_document_smoke PROPERTIES set_tests_properties(pano_cli_create_document_smoke PROPERTIES
LABELS "integration;desktop-fast") LABELS "integration;desktop-fast")
add_test(NAME pano_cli_create_animation_document_smoke
COMMAND pano_cli create-document --width 64 --height 32 --layers 2 --frames 3 --frame-duration-ms 250)
set_tests_properties(pano_cli_create_animation_document_smoke PROPERTIES
LABELS "document;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"frames\":3.*\"activeFrame\":2.*\"animationDurationMs\":750")
add_test(NAME pano_cli_inspect_image_rejects_unsupported add_test(NAME pano_cli_inspect_image_rejects_unsupported
COMMAND pano_cli inspect-image --path "${CMAKE_CURRENT_SOURCE_DIR}/data/images/unsupported-image.txt") COMMAND pano_cli inspect-image --path "${CMAKE_CURRENT_SOURCE_DIR}/data/images/unsupported-image.txt")
set_tests_properties(pano_cli_inspect_image_rejects_unsupported PROPERTIES set_tests_properties(pano_cli_inspect_image_rejects_unsupported PROPERTIES

View File

@@ -21,6 +21,8 @@ struct DocumentArgs {
std::uint32_t width = 0; std::uint32_t width = 0;
std::uint32_t height = 0; std::uint32_t height = 0;
std::uint32_t layers = 1; std::uint32_t layers = 1;
std::uint32_t frames = 1;
std::uint32_t frame_duration_ms = 100;
}; };
struct InspectImageArgs { struct InspectImageArgs {
@@ -45,7 +47,7 @@ void print_help()
{ {
std::cout std::cout
<< "pano_cli commands:\n" << "pano_cli commands:\n"
<< " create-document --width N --height N [--layers N]\n" << " create-document --width N --height N [--layers N] [--frames N] [--frame-duration-ms N]\n"
<< " inspect-image --path FILE\n" << " inspect-image --path FILE\n"
<< " inspect-project --path FILE\n" << " inspect-project --path FILE\n"
<< " parse-layout --path FILE\n" << " parse-layout --path FILE\n"
@@ -56,7 +58,8 @@ pp::foundation::Status parse_document_args(int argc, char** argv, DocumentArgs&
{ {
for (int i = 2; i < argc; ++i) { for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]); const std::string_view key(argv[i]);
if (key == "--width" || key == "--height" || key == "--layers") { if (key == "--width" || key == "--height" || key == "--layers" || key == "--frames"
|| key == "--frame-duration-ms") {
if (i + 1 >= argc) { if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option"); return pp::foundation::Status::invalid_argument("missing value for option");
} }
@@ -70,8 +73,12 @@ pp::foundation::Status parse_document_args(int argc, char** argv, DocumentArgs&
args.width = value.value(); args.width = value.value();
} else if (key == "--height") { } else if (key == "--height") {
args.height = value.value(); args.height = value.value();
} else { } else if (key == "--layers") {
args.layers = value.value(); args.layers = value.value();
} else if (key == "--frames") {
args.frames = value.value();
} else {
args.frame_duration_ms = value.value();
} }
} else { } else {
return pp::foundation::Status::invalid_argument("unknown option"); return pp::foundation::Status::invalid_argument("unknown option");
@@ -86,6 +93,14 @@ pp::foundation::Status parse_document_args(int argc, char** argv, DocumentArgs&
return pp::foundation::Status::invalid_argument("layer count must be greater than zero"); return pp::foundation::Status::invalid_argument("layer count must be greater than zero");
} }
if (args.frames == 0) {
return pp::foundation::Status::invalid_argument("frame count must be greater than zero");
}
if (args.frame_duration_ms == 0) {
return pp::foundation::Status::invalid_argument("frame duration must be greater than zero");
}
return pp::foundation::Status::success(); return pp::foundation::Status::success();
} }
@@ -98,24 +113,40 @@ int create_document(int argc, char** argv)
return 2; return 2;
} }
const auto document = pp::document::CanvasDocument::create( const auto document_result = pp::document::CanvasDocument::create(
pp::document::DocumentConfig { pp::document::DocumentConfig {
.width = args.width, .width = args.width,
.height = args.height, .height = args.height,
.layer_count = args.layers, .layer_count = args.layers,
}); });
if (!document) { if (!document_result) {
print_error("create-document", document.status().message); print_error("create-document", document_result.status().message);
return 2; return 2;
} }
auto document = document_result.value();
const auto duration_status = document.set_frame_duration(0, args.frame_duration_ms);
if (!duration_status.ok()) {
print_error("create-document", duration_status.message);
return 2;
}
for (std::uint32_t i = 1; i < args.frames; ++i) {
const auto added_frame = document.add_frame(args.frame_duration_ms);
if (!added_frame) {
print_error("create-document", added_frame.status().message);
return 2;
}
}
std::cout << "{\"ok\":true,\"command\":\"create-document\",\"document\":{" std::cout << "{\"ok\":true,\"command\":\"create-document\",\"document\":{"
<< "\"width\":" << document.value().width() << "\"width\":" << document.width()
<< ",\"height\":" << document.value().height() << ",\"height\":" << document.height()
<< ",\"layers\":" << document.value().layers().size() << ",\"layers\":" << document.layers().size()
<< ",\"activeLayer\":" << document.value().active_layer_index() << ",\"activeLayer\":" << document.active_layer_index()
<< ",\"frames\":" << document.value().frames().size() << ",\"frames\":" << document.frames().size()
<< ",\"activeFrame\":" << document.value().active_frame_index() << ",\"activeFrame\":" << document.active_frame_index()
<< ",\"animationDurationMs\":" << document.animation_duration_ms()
<< "}}\n"; << "}}\n";
return 0; return 0;
} }