Write cube exports from document snapshots
This commit is contained in:
@@ -281,9 +281,11 @@ powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.p
|
|||||||
snapshot smoke.
|
snapshot smoke.
|
||||||
- Live equirectangular, layer, animation-frame, and cube-face export adapters
|
- Live equirectangular, layer, animation-frame, and cube-face export adapters
|
||||||
now prepare and log the same payload-bearing canvas document snapshot plus
|
now prepare and log the same payload-bearing canvas document snapshot plus
|
||||||
shared renderer-neutral active-frame upload and face-PNG export reports before
|
shared renderer-neutral active-frame upload and face-PNG export reports.
|
||||||
delegating to retained `Canvas` export execution. Depth and video export
|
Cube-face export writes the pure document/renderer PNG bytes to the retained
|
||||||
remain on the older retained path.
|
face filename set and falls back to `Canvas::export_cube_faces` if snapshot
|
||||||
|
capture, PNG generation, or file writing fails. Equirectangular, layer,
|
||||||
|
animation-frame, depth, and video export remain on retained writer paths.
|
||||||
- `pano_cli save-document-project` writes that pure document export to a PPI
|
- `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`,
|
file and is covered by `pano_cli_save_document_project_roundtrip_smoke`,
|
||||||
which inspects and loads the generated file.
|
which inspects and loads the generated file.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ and validation command.
|
|||||||
| PNG/JPEG import | `Image`, `Canvas` import paths | `pp_assets`, `pp_document` | Fixture import, malformed file |
|
| PNG/JPEG import | `Image`, `Canvas` import paths | `pp_assets`, `pp_document` | Fixture import, malformed file |
|
||||||
| PNG/JPEG export | `Canvas`, `Image`, export dialogs | `pp_assets`, `pp_paint_renderer`, `pp_app_core` | Golden output tolerance, export start/target planning tests, live export-adapter document snapshot readiness |
|
| PNG/JPEG export | `Canvas`, `Image`, export dialogs | `pp_assets`, `pp_paint_renderer`, `pp_app_core` | Golden output tolerance, export start/target planning tests, live export-adapter document snapshot readiness |
|
||||||
| Equirectangular import/export | `Canvas`, shaders, RTT, export dialogs | `pp_paint_renderer`, `pp_app_core` | Tiny cube/equirect golden, app-core file target tests, live export-adapter renderer-upload readiness |
|
| Equirectangular import/export | `Canvas`, shaders, RTT, export dialogs | `pp_paint_renderer`, `pp_app_core` | Tiny cube/equirect golden, app-core file target tests, live export-adapter renderer-upload readiness |
|
||||||
| Cube face export | `Canvas` | `pp_paint_renderer` | Pure six-face document frame composite, renderer texture-upload bridge, pure face-PNG export helper, payload-complete canvas-snapshot renderer-upload and face-PNG automation, live export-adapter renderer/export readiness, OpenGL command-plan coverage, six-face golden set |
|
| Cube face export | `Canvas` fallback | `pp_paint_renderer` | Pure six-face document frame composite, renderer texture-upload bridge, pure face-PNG export helper, payload-complete canvas-snapshot renderer-upload and face-PNG automation, live document/renderer face-PNG writer with retained Canvas fallback, OpenGL command-plan coverage, six-face golden set |
|
||||||
| Depth export | `Canvas`, grid tools | `pp_paint_renderer` | Float/readback validation |
|
| Depth export | `Canvas`, grid tools | `pp_paint_renderer` | Float/readback validation |
|
||||||
|
|
||||||
## Brush And Painting
|
## Brush And Painting
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -687,9 +687,11 @@ renderer-owned readback remain under
|
|||||||
`DEBT-0010`/`DEBT-0013`/`DEBT-0036`.
|
`DEBT-0010`/`DEBT-0013`/`DEBT-0036`.
|
||||||
Live equirectangular, layer, animation-frame, and cube-face export adapters now
|
Live equirectangular, layer, animation-frame, and cube-face export adapters now
|
||||||
prepare the same payload-bearing document snapshot and renderer-neutral upload
|
prepare the same payload-bearing document snapshot and renderer-neutral upload
|
||||||
report helper plus pure face-PNG export report before delegating to retained
|
report helper plus pure face-PNG export report. Cube-face export writes those
|
||||||
`Canvas` export execution, so export workflows consume the shared renderer
|
document/renderer-owned PNG bytes to the legacy face filenames when available
|
||||||
boundary without changing file output yet.
|
and falls back to retained `Canvas::export_cube_faces` on snapshot/write failure;
|
||||||
|
the other export workflows still delegate to retained `Canvas` writers after
|
||||||
|
readiness reporting.
|
||||||
`pano_cli plan-image-import` exposes app-core planning for File > Import image
|
`pano_cli plan-image-import` exposes app-core planning for File > Import image
|
||||||
route decisions, including wide equirectangular images, legacy vertical cube
|
route decisions, including wide equirectangular images, legacy vertical cube
|
||||||
strips, regular transform-placement images, and invalid image dimensions; live
|
strips, regular transform-placement images, and invalid image dimensions; live
|
||||||
@@ -2528,9 +2530,11 @@ Results:
|
|||||||
live canvas export/save writer replacement.
|
live canvas export/save writer replacement.
|
||||||
- Live image/collection/cube export adapters now prepare and log the same
|
- Live image/collection/cube export adapters now prepare and log the same
|
||||||
document/canvas plus shared renderer-upload and face-PNG export readiness
|
document/canvas plus shared renderer-upload and face-PNG export readiness
|
||||||
reports before retained `Canvas` export calls. Depth and video export remain
|
reports. Cube-face export now writes the pure document/renderer PNG bytes to
|
||||||
on their prior retained paths; actual image/cube writer replacement remains
|
the legacy face filenames before falling back to retained `Canvas` execution
|
||||||
tracked under export debt.
|
on failure. Equirectangular, layer, animation-frame, depth, and video export
|
||||||
|
remain on their prior retained writer paths; actual broader writer replacement
|
||||||
|
remains tracked under export debt.
|
||||||
- Snapshot creation now rejects invalid embedded RGBA8 face payloads before
|
- Snapshot creation now rejects invalid embedded RGBA8 face payloads before
|
||||||
document export or history can persist malformed state.
|
document export or history can persist malformed state.
|
||||||
- Package-smoke wrappers validate the Windows CMake app executable/runtime
|
- Package-smoke wrappers validate the Windows CMake app executable/runtime
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
#include "legacy_document_canvas_services.h"
|
#include "legacy_document_canvas_services.h"
|
||||||
#include "paint_renderer/compositor.h"
|
#include "paint_renderer/compositor.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <fstream>
|
||||||
|
#include <limits>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -21,12 +25,41 @@ void show_export_success_dialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pp::foundation::Status prepare_legacy_document_export_snapshot(App& app, const char* context)
|
struct LegacyDocumentExportSnapshotReports {
|
||||||
|
pp::app::DocumentCanvasSnapshotResult snapshot;
|
||||||
|
pp::paint_renderer::DocumentFrameFacePngExportResult face_pngs;
|
||||||
|
};
|
||||||
|
|
||||||
|
pp::foundation::Status write_binary_file(std::string_view path, std::span<const std::byte> bytes)
|
||||||
|
{
|
||||||
|
if (path.empty()) {
|
||||||
|
return pp::foundation::Status::invalid_argument("export path must not be empty");
|
||||||
|
}
|
||||||
|
if (bytes.size() > static_cast<std::size_t>(std::numeric_limits<std::streamsize>::max())) {
|
||||||
|
return pp::foundation::Status::out_of_range("export payload is too large to write");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream stream(std::string(path), std::ios::binary);
|
||||||
|
if (!stream) {
|
||||||
|
return pp::foundation::Status::invalid_argument("export path could not be opened for writing");
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
||||||
|
if (!stream) {
|
||||||
|
return pp::foundation::Status::invalid_argument("export payload could not be written");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<LegacyDocumentExportSnapshotReports> prepare_legacy_document_export_snapshot(
|
||||||
|
App& app,
|
||||||
|
const char* context)
|
||||||
{
|
{
|
||||||
auto snapshot = capture_legacy_canvas_document_payload_snapshot(app);
|
auto snapshot = capture_legacy_canvas_document_payload_snapshot(app);
|
||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
LOG("%s document export snapshot failed: %s", context, snapshot.status().message);
|
LOG("%s document export snapshot failed: %s", context, snapshot.status().message);
|
||||||
return snapshot.status();
|
return pp::foundation::Result<LegacyDocumentExportSnapshotReports>::failure(snapshot.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto report = pp::app::make_document_canvas_save_snapshot_report(snapshot.value());
|
const auto report = pp::app::make_document_canvas_save_snapshot_report(snapshot.value());
|
||||||
@@ -41,19 +74,22 @@ pp::foundation::Status prepare_legacy_document_export_snapshot(App& app, const c
|
|||||||
report.pending_face_payloads,
|
report.pending_face_payloads,
|
||||||
report.can_export_ppi ? "true" : "false");
|
report.can_export_ppi ? "true" : "false");
|
||||||
|
|
||||||
|
LegacyDocumentExportSnapshotReports reports {
|
||||||
|
.snapshot = std::move(snapshot.value()),
|
||||||
|
};
|
||||||
if (!report.can_export_ppi) {
|
if (!report.can_export_ppi) {
|
||||||
return pp::foundation::Status::success();
|
return pp::foundation::Result<LegacyDocumentExportSnapshotReports>::success(std::move(reports));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto recorded_upload = pp::paint_renderer::record_document_frame_upload(
|
const auto recorded_upload = pp::paint_renderer::record_document_frame_upload(
|
||||||
pp::paint_renderer::DocumentFrameUploadRequest {
|
pp::paint_renderer::DocumentFrameUploadRequest {
|
||||||
.document = &snapshot.value().document,
|
.document = &reports.snapshot.document,
|
||||||
.frame_index = snapshot.value().document.active_frame_index(),
|
.frame_index = reports.snapshot.document.active_frame_index(),
|
||||||
.clear_color = {},
|
.clear_color = {},
|
||||||
});
|
});
|
||||||
if (!recorded_upload) {
|
if (!recorded_upload) {
|
||||||
LOG("%s document export renderer upload failed: %s", context, recorded_upload.status().message);
|
LOG("%s document export renderer upload failed: %s", context, recorded_upload.status().message);
|
||||||
return recorded_upload.status();
|
return pp::foundation::Result<LegacyDocumentExportSnapshotReports>::failure(recorded_upload.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& uploaded = recorded_upload.value().upload;
|
const auto& uploaded = recorded_upload.value().upload;
|
||||||
@@ -67,16 +103,15 @@ pp::foundation::Status prepare_legacy_document_export_snapshot(App& app, const c
|
|||||||
recorded_upload.value().command_count,
|
recorded_upload.value().command_count,
|
||||||
recorded_upload.value().upload_command_count,
|
recorded_upload.value().upload_command_count,
|
||||||
recorded_upload.value().transition_command_count);
|
recorded_upload.value().transition_command_count);
|
||||||
|
|
||||||
const auto face_pngs = pp::paint_renderer::export_document_frame_face_pngs(
|
const auto face_pngs = pp::paint_renderer::export_document_frame_face_pngs(
|
||||||
pp::paint_renderer::DocumentFrameCompositeRequest {
|
pp::paint_renderer::DocumentFrameCompositeRequest {
|
||||||
.document = &snapshot.value().document,
|
.document = &reports.snapshot.document,
|
||||||
.frame_index = snapshot.value().document.active_frame_index(),
|
.frame_index = reports.snapshot.document.active_frame_index(),
|
||||||
.clear_color = {},
|
.clear_color = {},
|
||||||
});
|
});
|
||||||
if (!face_pngs) {
|
if (!face_pngs) {
|
||||||
LOG("%s document export face PNG export failed: %s", context, face_pngs.status().message);
|
LOG("%s document export face PNG export failed: %s", context, face_pngs.status().message);
|
||||||
return face_pngs.status();
|
return pp::foundation::Result<LegacyDocumentExportSnapshotReports>::failure(face_pngs.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(
|
LOG(
|
||||||
@@ -86,17 +121,56 @@ pp::foundation::Status prepare_legacy_document_export_snapshot(App& app, const c
|
|||||||
static_cast<unsigned long long>(face_pngs.value().encoded_bytes),
|
static_cast<unsigned long long>(face_pngs.value().encoded_bytes),
|
||||||
face_pngs.value().composite.face_payload_count,
|
face_pngs.value().composite.face_payload_count,
|
||||||
face_pngs.value().composite.composited_layer_face_count);
|
face_pngs.value().composite.composited_layer_face_count);
|
||||||
return pp::foundation::Status::success();
|
reports.face_pngs = std::move(face_pngs.value());
|
||||||
|
|
||||||
|
return pp::foundation::Result<LegacyDocumentExportSnapshotReports>::success(std::move(reports));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare_legacy_document_export_snapshot_or_continue(App& app, const char* context)
|
void prepare_legacy_document_export_snapshot_or_continue(App& app, const char* context)
|
||||||
{
|
{
|
||||||
const auto status = prepare_legacy_document_export_snapshot(app, context);
|
const auto prepared = prepare_legacy_document_export_snapshot(app, context);
|
||||||
if (!status.ok()) {
|
if (!prepared) {
|
||||||
LOG("%s document export snapshot bridge retained legacy export after failure: %s", context, status.message);
|
LOG(
|
||||||
|
"%s document export snapshot bridge retained legacy export after failure: %s",
|
||||||
|
context,
|
||||||
|
prepared.status().message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status export_cube_faces_from_document_snapshot(
|
||||||
|
App& app,
|
||||||
|
std::string_view document_name,
|
||||||
|
const LegacyDocumentExportSnapshotReports& reports)
|
||||||
|
{
|
||||||
|
static constexpr std::array<std::string_view, pp::document::cube_face_count> plane_names {
|
||||||
|
"front",
|
||||||
|
"right",
|
||||||
|
"back",
|
||||||
|
"left",
|
||||||
|
"top",
|
||||||
|
"bottom",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (document_name.empty()) {
|
||||||
|
return pp::foundation::Status::invalid_argument("document name must not be empty");
|
||||||
|
}
|
||||||
|
if (reports.face_pngs.face_count != pp::document::cube_face_count) {
|
||||||
|
return pp::foundation::Status::invalid_argument("document snapshot did not produce all cube face PNGs");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t face_index = 0; face_index < plane_names.size(); ++face_index) {
|
||||||
|
const std::string path = app.work_path + "/" + std::string(document_name) + "-"
|
||||||
|
+ std::string(plane_names[face_index]) + ".png";
|
||||||
|
const auto status = write_binary_file(path, reports.face_pngs.face_pngs[face_index]);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
app.publish_exported_image(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
class LegacyDocumentExportServices final : public pp::app::DocumentExportServices {
|
class LegacyDocumentExportServices final : public pp::app::DocumentExportServices {
|
||||||
public:
|
public:
|
||||||
explicit LegacyDocumentExportServices(App& app) noexcept
|
explicit LegacyDocumentExportServices(App& app) noexcept
|
||||||
@@ -200,7 +274,28 @@ public:
|
|||||||
void export_cube_faces(std::string_view document_name) override
|
void export_cube_faces(std::string_view document_name) override
|
||||||
{
|
{
|
||||||
auto* app = &app_;
|
auto* app = &app_;
|
||||||
prepare_legacy_document_export_snapshot_or_continue(app_, "export-cube-faces");
|
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-cube-faces");
|
||||||
|
if (prepared) {
|
||||||
|
const auto exported = export_cube_faces_from_document_snapshot(app_, document_name, prepared.value());
|
||||||
|
if (exported.ok()) {
|
||||||
|
show_export_success_dialog(
|
||||||
|
app_,
|
||||||
|
pp::app::plan_document_export_success_dialog(
|
||||||
|
pp::app::DocumentExportSuccessKind::cube_faces,
|
||||||
|
pp::app::document_export_media_platform_destination(),
|
||||||
|
app_.work_path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(
|
||||||
|
"export-cube-faces document export writer retained legacy export after failure: %s",
|
||||||
|
exported.message);
|
||||||
|
} else {
|
||||||
|
LOG(
|
||||||
|
"export-cube-faces document export snapshot bridge retained legacy export after failure: %s",
|
||||||
|
prepared.status().message);
|
||||||
|
}
|
||||||
|
|
||||||
app_.canvas->m_canvas->export_cube_faces(std::string(document_name), [app] {
|
app_.canvas->m_canvas->export_cube_faces(std::string(document_name), [app] {
|
||||||
show_export_success_dialog(
|
show_export_success_dialog(
|
||||||
*app,
|
*app,
|
||||||
|
|||||||
Reference in New Issue
Block a user