Export captured canvas snapshots through document writer

This commit is contained in:
2026-06-05 18:24:58 +02:00
parent 9a75782891
commit ba5c3069e1
8 changed files with 115 additions and 13 deletions

View File

@@ -265,9 +265,12 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
state toward `pp_document::CanvasDocument`, including dimensions, active
layer/frame, layer visibility/opacity/alpha/blend metadata, frame durations,
captured RGBA8 face payloads, and remaining renderer payload-readback counts,
plus the save-readiness report now consumed before retained live saves, and
is covered by `pano_cli_plan_canvas_document_snapshot_smoke` plus the
payload-bearing snapshot smoke.
plus the save-readiness report now consumed before retained live saves. For
payload-complete or metadata-only snapshots, the same app-core boundary now
exports through the pure `pp_document` PPI writer and reports generated byte
counts plus decoded dirty-face counts in `ppiExport` JSON. It is covered by
`pano_cli_plan_canvas_document_snapshot_smoke` plus the payload-bearing
snapshot smoke.
- `pano_cli save-document-project` writes that pure document export to a PPI
file and is covered by `pano_cli_save_document_project_roundtrip_smoke`,
which inspects and loads the generated file.
@@ -1094,8 +1097,10 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
cleanup. Existing Save, Save As, Save Version, and save-before-workflow
prepare and log a payload-bearing canvas document snapshot report before
delegating to retained `Canvas::project_save`. Retained legacy UI/canvas
execution and actual save serialization remain tracked by `DEBT-0040`,
`DEBT-0041`, and `DEBT-0042`.
execution and actual live save serialization remain tracked by `DEBT-0040`,
`DEBT-0041`, and `DEBT-0042`; the pure snapshot-to-PPI export handoff is
already validated in `pp_app_core_document_canvas_tests` and
`pano_cli_plan_canvas_document_snapshot_payload_smoke`.
- `src/legacy_document_export_services.*` is the current app-shell bridge
between `pp_app_core` document export execution plans and live equirectangular,
layers, animation-frame, depth, and cube-face export calls. It preserves

View File

@@ -11,7 +11,7 @@ and validation command.
| Capability | Current Area | Target Owner | Required Tests |
| --- | --- | --- | --- |
| PPI open/save | `Canvas`, serializer, dialogs | `pp_document`, `pp_assets`, `pano_cli` | Round-trip tiny project, old-version fixture, corrupt/truncated fixture, live-canvas-to-`pp_document` snapshot projection with captured RGBA8 payloads, pending renderer-readback counts, and save-readiness reporting before retained live saves |
| PPI open/save | `Canvas`, serializer, dialogs | `pp_document`, `pp_assets`, `pano_cli` | Round-trip tiny project, old-version fixture, corrupt/truncated fixture, live-canvas-to-`pp_document` snapshot projection with captured RGBA8 payloads, pending renderer-readback counts, save-readiness reporting before retained live saves, and pure PPI export from payload-complete snapshots |
| Open-document routing | `App::open_document` | `pp_app_core`, `pano_cli`, `pp_panopainter_ui`, `pp_document`, `pp_assets` | Project/ABR/PPBR route tests, malformed path tests, open-action plan tests, CLI route/action smoke, app open smoke |
| Document session decisions | `App::open_document`, `App::request_close`, save hotkeys, file menu, dialogs | `pp_app_core`, `pano_cli`, `pp_panopainter_ui` | Clean/dirty/prompt-open/save/save-as/save-version/save-before-workflow/name/new-document resolution/overwrite/version-target decision tests, CLI session, new-document, document-file, and document-version smoke, app close/open/save/new/browse smoke |
| Version metadata | `scripts/pre-build.py`, `version.*` | build system, `pp_foundation` | Generated header smoke test, missing-tag behavior |

View File

@@ -479,6 +479,11 @@ agent or engineer to remove them without reconstructing context from chat.
report before delegating to retained `Canvas::project_save`. The retained
writer still owns PPI serialization, progress/threading, and compatibility
quirks, so pure writer replacement remains open.
- 2026-06-05: DEBT-0010/DEBT-0013 were narrowed again. `pp_app_core` now
exports payload-complete or metadata-only canvas document snapshots through
the pure `pp_document` PPI writer and rejects snapshots that still require
renderer readback; `pano_cli plan-canvas-document-snapshot` reports
`ppiExport` readiness, byte counts, and decoded dirty-face counts.
## Open Debt
@@ -492,10 +497,10 @@ agent or engineer to remove them without reconstructing context from chat.
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, and root CMake now exposes a focused `panopainter_platform_build_vcpkg_ui_core` target for the vcpkg-backed `pp_ui_core`/tinyxml2 boundary, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_vcpkg_ui_core` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
| DEBT-0008 | Open | Modernization | `windows-msvc-default` and `windows-msvc-vcpkg-headless` explicitly select Visual Studio 18 2026 for local validation, but non-VS2026 CMake executables on PATH may not know that generator | The local machine has VS 2026, but using an older CMake can still default to Ninja or reject the VS 2026 generator | `cmake --preset windows-msvc-default`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug` | The repo automation invokes or locates a CMake executable that supports `Visual Studio 18 2026`, and VS 2026 generator validation is the normal Windows path without manual tool selection |
| DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, while retained standard/Quest/Focus package CMake paths now have a refreshed CMake 3.10/C++23 baseline outside root CMake; automation queries `sdkmanager`, installs newer or missing SDK Manager NDK/CMake packages, selects the resulting pair before configure, and reports update decisions; root CMake exposes non-default platform-build and retained native package validation targets | Platform app entrypoints still live in legacy Gradle/CMake projects and need Phase 6 alignment | `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64`; `cmake --build --preset android-x64`; `cmake --build --preset android-quest-arm64`; `cmake --build --preset android-focus-arm64`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_android_assets`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages quest,focus -ConfigureOnly`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke` | Android standard, Quest, and Focus/Wave package targets consume shared component targets and have package smoke commands |
| DEBT-0010 | Open | Modernization | `pp_document` is a pure layer/frame/document/undo-history model with alpha-lock metadata, snapshot construction, per-layer frame metadata, renderer-free RGBA8 face payload storage, snapshot-embedded face-payload validation, renderer-free alpha8 selection-mask storage, PPI import/export helpers, stroke-script-to-face-payload CLI automation, `pp_paint_renderer` document face/frame compositors, renderer-neutral six-face texture upload, OpenGL command-planner validation through CLI render automation, live Canvas snapshot projection through `pp_app_core`/`legacy_document_canvas_services`, captured RGBA8 payload attachment to `pp_document`, and live Save/Save As/Save Version/save-before-workflow snapshot-readiness reporting before retained save execution, but export/action-command adoption, pure save-writer replacement, and renderer-owned cube-face readback ownership are not yet wired | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2`; `pano_cli load-project --path tests\data\projects\minimal-project.ppi`; `pano_cli simulate-document-render --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer 1`; `pp_document_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pp_paint_renderer_compositor_tests`; `pp_app_core_document_canvas_tests`; `pano_cli_simulate_document_edits_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_simulate_document_render_smoke`; `pano_cli_plan_canvas_document_snapshot_smoke`; `pano_cli_plan_canvas_document_snapshot_payload_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Legacy document behavior is represented by `pp_document`/`pp_paint_renderer` tests and the app consumes it through a boundary/facade |
| DEBT-0010 | Open | Modernization | `pp_document` is a pure layer/frame/document/undo-history model with alpha-lock metadata, snapshot construction, per-layer frame metadata, renderer-free RGBA8 face payload storage, snapshot-embedded face-payload validation, renderer-free alpha8 selection-mask storage, PPI import/export helpers, stroke-script-to-face-payload CLI automation, `pp_paint_renderer` document face/frame compositors, renderer-neutral six-face texture upload, OpenGL command-planner validation through CLI render automation, live Canvas snapshot projection through `pp_app_core`/`legacy_document_canvas_services`, captured RGBA8 payload attachment to `pp_document`, live Save/Save As/Save Version/save-before-workflow snapshot-readiness reporting before retained save execution, and pure app-core PPI export for payload-complete canvas snapshots, but export/action-command adoption, live save-writer replacement, and renderer-owned cube-face readback ownership are not yet wired | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2`; `pano_cli load-project --path tests\data\projects\minimal-project.ppi`; `pano_cli simulate-document-render --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer 1`; `pp_document_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pp_paint_renderer_compositor_tests`; `pp_app_core_document_canvas_tests`; `pano_cli_simulate_document_edits_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_simulate_document_render_smoke`; `pano_cli_plan_canvas_document_snapshot_smoke`; `pano_cli_plan_canvas_document_snapshot_payload_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Legacy document behavior is represented by `pp_document`/`pp_paint_renderer` tests and the app consumes it through a boundary/facade |
| DEBT-0011 | Open | Modernization | `package-smoke` validates the Windows CMake app artifact and launch-folder DLL payload, and reports a structured package readiness matrix for Windows AppX, Android standard/Quest/Focus APKs, Apple bundles, Linux app output, and WebGL output; the Windows app smoke passes the configure-time CMake executable so VS 2026 generator validation does not depend on `cmake` from PATH, retained Android package native CMake paths, and retained Linux/WebGL CMake baseline metadata are reachable from package validation and root CMake package-readiness targets, but Windows AppX/APK/Linux/Apple/WebGL package outputs are still `blocked` because root CMake package targets do not exist yet | Platform package targets are not migrated to root CMake yet | `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_windows_app_package_smoke`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_linux_webgl_package_readiness`; `python scripts/dev/check_package_smoke_readiness.py`; `bash -n scripts/automation/package-smoke.sh` | Package-smoke builds and validates Windows AppX, Android APK variants, Linux app, Apple bundles, and WebGL output where local toolchains are present |
| DEBT-0012 | Open | Modernization | `pp_ui_core` uses vcpkg tinyxml2 on `windows-msvc-vcpkg-headless`, but retains `pp_vendor_tinyxml2` for default and unproven platform presets | Mobile/AppX/Apple triplets and app packaging still need validation before removing the vendored fallback | `ctest --preset desktop-fast-vcpkg --build-config Debug`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | All supported presets consume vcpkg tinyxml2 or document a permanent vendored exception |
| DEBT-0013 | Open | Modernization | `pp_assets`, `pp_document`, `pano_cli inspect-project`, `pano_cli load-project`, and `pano_cli save-project` validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to `pp_document`, and live save-path snapshot-readiness reporting, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted | Full PPI save parity requires staged extraction of legacy `Canvas` serialization and image/layer payload handling | `ctest --preset desktop-fast --build-config Debug`; `pp_assets_image_pixels_tests`; `pp_assets_ppi_header_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pano_cli_inspect_project_layout_smoke`; `pano_cli_load_project_metadata_smoke`; `pano_cli_save_project_roundtrip_smoke`; `pano_cli_save_project_payload_roundtrip_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility |
| DEBT-0013 | Open | Modernization | `pp_assets`, `pp_document`, `pano_cli inspect-project`, `pano_cli load-project`, and `pano_cli save-project` validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to `pp_document`, live save-path snapshot-readiness reporting, and app-core canvas-snapshot-to-PPI export automation, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted | Full PPI save parity requires staged extraction of legacy `Canvas` serialization and image/layer payload handling | `ctest --preset desktop-fast --build-config Debug`; `pp_assets_image_pixels_tests`; `pp_assets_ppi_header_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pano_cli_inspect_project_layout_smoke`; `pano_cli_load_project_metadata_smoke`; `pano_cli_save_project_roundtrip_smoke`; `pano_cli_save_project_payload_roundtrip_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility |
| DEBT-0014 | Open | Modernization | `windows-clangcl-asan` now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer | Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible | `cmake --fresh --preset windows-clangcl-asan`; `cmake --build --preset windows-clangcl-asan --target pp_foundation` | Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make `platform-build.ps1 -Presets windows-clangcl-asan` pass for the headless matrix |
| DEBT-0015 | Open | Modernization | Cursor visibility requests now consume pure `pp_app_core` planning through `pano_cli plan-cursor-visibility`, `App::show_cursor`/`App::hide_cursor` dispatch through `PlatformServices` without platform guards, and Windows live execution uses injected `WindowsPlatformServices`, but macOS cursor execution still reaches the retained fallback adapter | Keep canvas cursor behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-cursor-visibility --visible`; `ctest --preset desktop-fast --build-config Debug` | Cursor visibility execution is owned by injected `pp_platform_*` services for every supported platform |
| DEBT-0016 | Open | Modernization | Clipboard get/set requests now consume pure `pp_app_core` planning through `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write`, and Windows live execution uses injected `WindowsPlatformServices`, but Apple/Android clipboard execution still reaches retained fallback adapter branches from `App::clipboard_get_text` and `App::clipboard_set_text` | Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-clipboard-write --text #ff00aa`; `ctest --preset desktop-fast --build-config Debug` | Clipboard execution is owned by injected `pp_platform_*` services for every supported platform |

View File

@@ -671,8 +671,11 @@ now builds the same metadata snapshot from live `Canvas` state and has an
opt-in dirty-face payload snapshot path backed by retained `Layer::snapshot()`
readback. Live Save, Save As, Save Version, and save-before-workflow paths now
prepare and log a payload-completeness report from that snapshot before
delegating to retained `Canvas::project_save`; pure PPI writer replacement,
export adoption, and renderer-owned readback remain under
delegating to retained `Canvas::project_save`; the app-core snapshot boundary
also has a tested pure PPI export helper, and
`pano_cli plan-canvas-document-snapshot` runs that helper for payload-complete
snapshots and reports generated byte/dirty-face summaries. Live save writer
replacement, export adoption, and renderer-owned readback remain under
`DEBT-0010`/`DEBT-0013`/`DEBT-0036`.
`pano_cli plan-image-import` exposes app-core planning for File > Import image
route decisions, including wide equirectangular images, legacy vertical cube
@@ -2213,7 +2216,9 @@ Results:
retained `Canvas::project_save` writer, keeping behavior stable while moving
the app path onto the document/canvas boundary.
- `pano_cli plan-canvas-document-snapshot` now emits the same save-readiness
report (`payloadComplete` and `canExportPpi`) used by the live save bridge.
report (`payloadComplete` and `canExportPpi`) used by the live save bridge,
and payload-complete snapshots now run the pure `pp_document` PPI exporter
and decoded-project summary before emitting `ppiExport` JSON.
- `pp_app_core_document_import_tests` passed, covering wide equirectangular,
legacy vertical cube strip, regular transform-placement, and invalid-dimension
import route decisions, equirectangular service dispatch, transform import
@@ -2485,6 +2490,13 @@ Results:
Version, and save-before-workflow now log payload completeness and PPI
readiness from `pp_app_core` while legacy `Canvas::project_save` still owns
the actual file write, progress/threading behavior, and compatibility quirks.
- `pp_app_core` now exposes
`export_document_canvas_save_snapshot_to_ppi`, which refuses snapshots that
still need renderer payload readback and exports payload-complete or
metadata-only snapshots through the pure `pp_document` PPI writer. The
document-canvas tests decode the generated bytes, and
`pano_cli plan-canvas-document-snapshot` reports `ppiExport` readiness,
byte count, and dirty-face count for agent automation.
- Snapshot creation now rejects invalid embedded RGBA8 face payloads before
document export or history can persist malformed state.
- Package-smoke wrappers validate the Windows CMake app executable/runtime

View File

@@ -1,6 +1,7 @@
#pragma once
#include "document/document.h"
#include "document/ppi_export.h"
#include "foundation/result.h"
#include <algorithm>
@@ -77,6 +78,11 @@ struct DocumentCanvasSaveSnapshotReport {
bool can_export_ppi = false;
};
struct DocumentCanvasPpiExportResult {
DocumentCanvasSaveSnapshotReport report;
std::vector<std::byte> bytes;
};
class DocumentCanvasClearServices {
public:
virtual ~DocumentCanvasClearServices() = default;
@@ -242,6 +248,27 @@ public:
};
}
[[nodiscard]] inline pp::foundation::Result<DocumentCanvasPpiExportResult>
export_document_canvas_save_snapshot_to_ppi(const DocumentCanvasSnapshotResult& snapshot)
{
const auto report = make_document_canvas_save_snapshot_report(snapshot);
if (!report.can_export_ppi) {
return pp::foundation::Result<DocumentCanvasPpiExportResult>::failure(
pp::foundation::Status::invalid_argument(
"canvas document snapshot still requires renderer payload readback"));
}
auto bytes = pp::document::export_ppi_project_document(snapshot.document);
if (!bytes) {
return pp::foundation::Result<DocumentCanvasPpiExportResult>::failure(bytes.status());
}
return pp::foundation::Result<DocumentCanvasPpiExportResult>::success(DocumentCanvasPpiExportResult {
.report = report,
.bytes = std::move(bytes.value()),
});
}
[[nodiscard]] inline pp::foundation::Result<DocumentCanvasClearPlan> plan_document_canvas_clear(
bool has_canvas,
float r = 0.0F,

View File

@@ -1474,13 +1474,13 @@ if(TARGET pano_cli)
COMMAND pano_cli plan-canvas-document-snapshot --width 128 --height 64 --layers 3 --frames 2 --current-layer 2 --current-frame 1 --hidden-layer 0 --alpha-locked-layer 2 --opacity 0.5 --blend-mode 4 --pending-face-payloads-per-layer 6)
set_tests_properties(pano_cli_plan_canvas_document_snapshot_smoke PROPERTIES
LABELS "app;document;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-document-snapshot\".*\"width\":128.*\"height\":64.*\"layers\":3.*\"frames\":2.*\"activeLayer\":2.*\"activeFrame\":1.*\"activeLayerName\":\"Layer 3\".*\"activeLayerOpacity\":0.5.*\"activeLayerBlend\":\"overlay\".*\"activeLayerAlphaLocked\":true.*\"pendingFacePayloads\":18.*\"metadataOnly\":true.*\"requiresRendererPayloadReadback\":true.*\"documentFacePayloads\":0.*\"saveReport\":\\{\"payloadComplete\":false,\"canExportPpi\":false\\}")
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-document-snapshot\".*\"width\":128.*\"height\":64.*\"layers\":3.*\"frames\":2.*\"activeLayer\":2.*\"activeFrame\":1.*\"activeLayerName\":\"Layer 3\".*\"activeLayerOpacity\":0.5.*\"activeLayerBlend\":\"overlay\".*\"activeLayerAlphaLocked\":true.*\"pendingFacePayloads\":18.*\"metadataOnly\":true.*\"requiresRendererPayloadReadback\":true.*\"documentFacePayloads\":0.*\"saveReport\":\\{\"payloadComplete\":false,\"canExportPpi\":false\\}.*\"ppiExport\":\\{\"ready\":false,\"bytes\":0,\"dirtyFaces\":0\\}")
add_test(NAME pano_cli_plan_canvas_document_snapshot_payload_smoke
COMMAND pano_cli plan-canvas-document-snapshot --width 128 --height 64 --layers 2 --frames 2 --current-layer 1 --current-frame 1 --pending-face-payloads-per-layer 2 --captured-face-payloads-per-layer 2)
set_tests_properties(pano_cli_plan_canvas_document_snapshot_payload_smoke PROPERTIES
LABELS "app;document;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-document-snapshot\".*\"layers\":2.*\"frames\":2.*\"activeLayer\":1.*\"activeFrame\":1.*\"pendingFacePayloads\":4.*\"capturedFacePayloads\":4.*\"metadataOnly\":false.*\"requiresRendererPayloadReadback\":false.*\"documentFacePayloads\":4.*\"saveReport\":\\{\"payloadComplete\":true,\"canExportPpi\":true\\}")
PASS_REGULAR_EXPRESSION "\"command\":\"plan-canvas-document-snapshot\".*\"layers\":2.*\"frames\":2.*\"activeLayer\":1.*\"activeFrame\":1.*\"pendingFacePayloads\":4.*\"capturedFacePayloads\":4.*\"metadataOnly\":false.*\"requiresRendererPayloadReadback\":false.*\"documentFacePayloads\":4.*\"saveReport\":\\{\"payloadComplete\":true,\"canExportPpi\":true\\}.*\"ppiExport\":\\{\"ready\":true,\"bytes\":[1-9][0-9]*,\"dirtyFaces\":4\\}")
add_test(NAME pano_cli_plan_canvas_document_snapshot_no_canvas
COMMAND pano_cli plan-canvas-document-snapshot --no-canvas)

View File

@@ -1,4 +1,5 @@
#include "app_core/document_canvas.h"
#include "assets/ppi_header.h"
#include "test_harness.h"
#include <cstdint>
@@ -89,6 +90,10 @@ void snapshot_plan_projects_canvas_metadata(pp::tests::Harness& harness)
PP_EXPECT(harness, value.document.layers()[1].blend_mode == pp::paint::BlendMode::overlay);
PP_EXPECT(harness, value.document.layers()[1].frames.size() == 1U);
PP_EXPECT(harness, value.document.face_pixel_payload_count() == 0U);
const auto export_result = pp::app::export_document_canvas_save_snapshot_to_ppi(value);
PP_EXPECT(harness, !export_result);
PP_EXPECT(harness, export_result.status().code == pp::foundation::StatusCode::invalid_argument);
}
void snapshot_plan_defaults_empty_names_and_frames(pp::tests::Harness& harness)
@@ -117,6 +122,16 @@ void snapshot_plan_defaults_empty_names_and_frames(pp::tests::Harness& harness)
PP_EXPECT(harness, !result.value().requires_renderer_payload_readback);
PP_EXPECT(harness, result.value().document.layers()[0].name == "Layer 1");
PP_EXPECT(harness, result.value().document.frames()[0].duration_ms == 100U);
const auto export_result = pp::app::export_document_canvas_save_snapshot_to_ppi(result.value());
PP_EXPECT(harness, export_result);
if (export_result) {
const auto decoded = pp::assets::decode_ppi_project_images(export_result.value().bytes);
PP_EXPECT(harness, decoded);
if (decoded) {
PP_EXPECT(harness, decoded.value().project.body.summary.dirty_face_count == 0U);
}
}
}
void snapshot_plan_attaches_captured_face_payloads(pp::tests::Harness& harness)
@@ -177,6 +192,20 @@ void snapshot_plan_attaches_captured_face_payloads(pp::tests::Harness& harness)
PP_EXPECT(harness, report.captured_face_payloads == 1U);
PP_EXPECT(harness, report.payload_complete);
PP_EXPECT(harness, report.can_export_ppi);
const auto export_result = pp::app::export_document_canvas_save_snapshot_to_ppi(result.value());
PP_EXPECT(harness, export_result);
if (export_result) {
PP_EXPECT(harness, export_result.value().report.can_export_ppi);
PP_EXPECT(harness, export_result.value().bytes.size() > 0U);
const auto decoded = pp::assets::decode_ppi_project_images(export_result.value().bytes);
PP_EXPECT(harness, decoded);
if (decoded) {
PP_EXPECT(harness, decoded.value().project.body.summary.dirty_face_count == 1U);
PP_EXPECT(harness, decoded.value().project.body.summary.rgba_face_payload_count == 1U);
}
}
}
void snapshot_plan_rejects_invalid_canvas_state(pp::tests::Harness& harness)

View File

@@ -5995,6 +5995,27 @@ int plan_canvas_document_snapshot(int argc, char** argv)
const auto& document = value.document;
const auto& active_layer = document.layers()[document.active_layer_index()];
const auto save_report = pp::app::make_document_canvas_save_snapshot_report(value);
bool ppi_export_ready = false;
std::size_t ppi_export_bytes = 0;
std::uint32_t ppi_export_dirty_faces = 0;
if (save_report.can_export_ppi) {
const auto exported = pp::app::export_document_canvas_save_snapshot_to_ppi(value);
if (!exported) {
print_error("plan-canvas-document-snapshot", exported.status().message);
return 2;
}
const auto decoded = pp::assets::decode_ppi_project_images(exported.value().bytes);
if (!decoded) {
print_error("plan-canvas-document-snapshot", decoded.status().message);
return 2;
}
ppi_export_ready = true;
ppi_export_bytes = exported.value().bytes.size();
ppi_export_dirty_faces = decoded.value().project.body.summary.dirty_face_count;
}
std::cout << "{\"ok\":true,\"command\":\"plan-canvas-document-snapshot\""
<< ",\"state\":{\"hasCanvas\":" << json_bool(args.has_canvas)
<< ",\"width\":" << args.width
@@ -6021,6 +6042,9 @@ int plan_canvas_document_snapshot(int argc, char** argv)
<< ",\"documentFacePayloads\":" << document.face_pixel_payload_count()
<< ",\"saveReport\":{\"payloadComplete\":" << json_bool(save_report.payload_complete)
<< ",\"canExportPpi\":" << json_bool(save_report.can_export_ppi)
<< "},\"ppiExport\":{\"ready\":" << json_bool(ppi_export_ready)
<< ",\"bytes\":" << ppi_export_bytes
<< ",\"dirtyFaces\":" << ppi_export_dirty_faces
<< "}"
<< "}}\n";
return 0;