Move export snapshot target support to app core

This commit is contained in:
2026-06-06 11:10:26 +02:00
parent 7575f51c45
commit 41279c8743
8 changed files with 208 additions and 110 deletions

View File

@@ -424,6 +424,71 @@ public:
return "Document export failed";
}
[[nodiscard]] constexpr bool ascii_iequals(std::string_view left, std::string_view right) noexcept
{
if (left.size() != right.size()) {
return false;
}
for (std::size_t i = 0; i < left.size(); ++i) {
auto lhs = left[i];
if (lhs >= 'A' && lhs <= 'Z') {
lhs = static_cast<char>(lhs - 'A' + 'a');
}
auto rhs = right[i];
if (rhs >= 'A' && rhs <= 'Z') {
rhs = static_cast<char>(rhs - 'A' + 'a');
}
if (lhs != rhs) {
return false;
}
}
return true;
}
[[nodiscard]] constexpr bool document_export_path_has_extension(
std::string_view path,
std::string_view extension) noexcept
{
return path.size() >= extension.size()
&& ascii_iequals(path.substr(path.size() - extension.size()), extension);
}
[[nodiscard]] constexpr bool document_export_path_is_png_target(std::string_view path) noexcept
{
return document_export_path_has_extension(path, ".png");
}
[[nodiscard]] constexpr bool document_export_path_is_jpeg_target(std::string_view path) noexcept
{
return document_export_path_has_extension(path, ".jpg")
|| document_export_path_has_extension(path, ".jpeg");
}
[[nodiscard]] constexpr bool document_export_snapshot_target_supported(
DocumentExportExecutionKind kind,
std::string_view target_path = {}) noexcept
{
switch (kind) {
case DocumentExportExecutionKind::equirectangular_file:
return document_export_path_is_png_target(target_path)
|| document_export_path_is_jpeg_target(target_path);
case DocumentExportExecutionKind::layers_collection:
case DocumentExportExecutionKind::layers_stem:
case DocumentExportExecutionKind::animation_frames_collection:
case DocumentExportExecutionKind::animation_frames_stem:
case DocumentExportExecutionKind::cube_faces:
return true;
case DocumentExportExecutionKind::depth:
case DocumentExportExecutionKind::animation_mp4:
case DocumentExportExecutionKind::timelapse:
return false;
}
return false;
}
[[nodiscard]] constexpr DocumentExportSnapshotRoutePlan plan_document_export_snapshot_route(
DocumentExportExecutionKind kind,
DocumentCanvasSaveSnapshotReport report,
@@ -456,6 +521,19 @@ public:
return plan;
}
[[nodiscard]] constexpr DocumentExportSnapshotRoutePlan plan_document_export_snapshot_route_for_target(
DocumentExportExecutionKind kind,
DocumentCanvasSaveSnapshotReport report,
std::string_view target_path,
bool platform_supported) noexcept
{
return plan_document_export_snapshot_route(
kind,
report,
document_export_snapshot_target_supported(kind, target_path),
platform_supported);
}
[[nodiscard]] constexpr DocumentExportCollectionTargetPlan plan_document_export_collection_target(
DocumentExportCollectionKind kind,
bool use_work_directory_collection) noexcept

View File

@@ -25,53 +25,6 @@ void show_export_success_dialog(
}
}
bool is_png_export_target(std::string_view path) noexcept
{
if (path.size() < 4U) {
return false;
}
const auto extension = path.substr(path.size() - 4U);
return extension[0] == '.'
&& (extension[1] == 'p' || extension[1] == 'P')
&& (extension[2] == 'n' || extension[2] == 'N')
&& (extension[3] == 'g' || extension[3] == 'G');
}
bool ascii_iequals(std::string_view left, std::string_view right) noexcept
{
if (left.size() != right.size()) {
return false;
}
for (std::size_t i = 0; i < left.size(); ++i) {
auto lhs = left[i];
if (lhs >= 'A' && lhs <= 'Z') {
lhs = static_cast<char>(lhs - 'A' + 'a');
}
auto rhs = right[i];
if (rhs >= 'A' && rhs <= 'Z') {
rhs = static_cast<char>(rhs - 'A' + 'a');
}
if (lhs != rhs) {
return false;
}
}
return true;
}
bool has_extension(std::string_view path, std::string_view extension) noexcept
{
return path.size() >= extension.size()
&& ascii_iequals(path.substr(path.size() - extension.size()), extension);
}
bool is_jpeg_export_target(std::string_view path) noexcept
{
return has_extension(path, ".jpg") || has_extension(path, ".jpeg");
}
struct LegacyDocumentExportSnapshotReports {
pp::app::DocumentCanvasSnapshotResult snapshot;
pp::paint_renderer::DocumentFrameFacePngExportResult face_pngs;
@@ -202,18 +155,18 @@ void prepare_legacy_document_export_snapshot_or_continue(App& app, const char* c
}
}
bool use_legacy_document_snapshot_writer(
bool should_use_document_snapshot_writer(
const char* context,
pp::app::DocumentExportExecutionKind kind,
const LegacyDocumentExportSnapshotReports& reports,
bool target_supported,
std::string_view target_path,
bool platform_supported)
{
const auto report = pp::app::make_document_canvas_save_snapshot_report(reports.snapshot);
const auto route = pp::app::plan_document_export_snapshot_route(
const auto route = pp::app::plan_document_export_snapshot_route_for_target(
kind,
report,
target_supported,
target_path,
platform_supported);
if (!route.uses_document_snapshot_writer) {
LOG(
@@ -344,7 +297,7 @@ pp::foundation::Status export_equirectangular_from_document_snapshot(
pp::paint_renderer::DocumentFrameEquirectangularPngExportResult png_export;
pp::paint_renderer::DocumentFrameEquirectangularJpegExportResult jpeg_export;
if (is_png_export_target(target.path)) {
if (pp::app::document_export_path_is_png_target(target.path)) {
auto exported = pp::paint_renderer::export_document_frame_equirectangular_png(reports.face_pngs.composite);
if (!exported) {
return exported.status();
@@ -359,7 +312,7 @@ pp::foundation::Status export_equirectangular_from_document_snapshot(
png_export.face_payload_count,
png_export.composited_layer_face_count);
bytes = std::span<const std::byte>(png_export.png.data(), png_export.png.size());
} else if (is_jpeg_export_target(target.path)) {
} else if (pp::app::document_export_path_is_jpeg_target(target.path)) {
auto exported = pp::paint_renderer::export_document_frame_equirectangular_jpeg(reports.face_pngs.composite);
if (!exported) {
return exported.status();
@@ -405,40 +358,37 @@ public:
{
auto* app = &app_;
#if !__WEB__
if (is_png_export_target(target.path) || is_jpeg_export_target(target.path)) {
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-equirectangular");
if (prepared) {
if (use_legacy_document_snapshot_writer(
"export-equirectangular",
pp::app::DocumentExportExecutionKind::equirectangular_file,
prepared.value(),
true,
true)) {
const auto exported = export_equirectangular_from_document_snapshot(app_, target, prepared.value());
if (exported.ok()) {
show_export_success_dialog(
app_,
pp::app::plan_document_export_success_dialog(
pp::app::DocumentExportSuccessKind::equirectangular,
pp::app::document_export_equirectangular_platform_destination(),
app_.work_path));
return;
}
LOG(
"export-equirectangular document export writer retained legacy export after failure: %s",
exported.message);
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-equirectangular");
if (prepared) {
if (should_use_document_snapshot_writer(
"export-equirectangular",
pp::app::DocumentExportExecutionKind::equirectangular_file,
prepared.value(),
target.path,
true)) {
const auto exported = export_equirectangular_from_document_snapshot(app_, target, prepared.value());
if (exported.ok()) {
show_export_success_dialog(
app_,
pp::app::plan_document_export_success_dialog(
pp::app::DocumentExportSuccessKind::equirectangular,
pp::app::document_export_equirectangular_platform_destination(),
app_.work_path));
return;
}
} else {
LOG(
"export-equirectangular document export snapshot bridge retained legacy export after failure: %s",
prepared.status().message);
"export-equirectangular document export writer retained legacy export after failure: %s",
exported.message);
}
} else
#endif
{
prepare_legacy_document_export_snapshot_or_continue(app_, "export-equirectangular");
} else {
LOG(
"export-equirectangular document export snapshot bridge retained legacy export after failure: %s",
prepared.status().message);
}
#else
prepare_legacy_document_export_snapshot_or_continue(app_, "export-equirectangular");
#endif
app_.canvas->m_canvas->export_equirectangular(target.path, [app, target] {
#if __WEB__
app->ui_task([app, target] {
@@ -465,11 +415,11 @@ public:
const auto collection_target = pp::app::DocumentExportCollectionTarget {
.stem_path = target.stem_path,
};
if (use_legacy_document_snapshot_writer(
if (should_use_document_snapshot_writer(
"export-layers",
pp::app::DocumentExportExecutionKind::layers_stem,
prepared.value(),
true,
{},
true)) {
const auto exported = export_layers_from_document_snapshot(app_, collection_target, prepared.value());
if (exported.ok()) {
@@ -508,11 +458,11 @@ public:
#if !__WEB__
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-layers");
if (prepared) {
if (use_legacy_document_snapshot_writer(
if (should_use_document_snapshot_writer(
"export-layers",
pp::app::DocumentExportExecutionKind::layers_collection,
prepared.value(),
true,
{},
true)) {
const auto exported = export_layers_from_document_snapshot(app_, target, prepared.value());
if (exported.ok()) {
@@ -552,11 +502,11 @@ public:
const auto collection_target = pp::app::DocumentExportCollectionTarget {
.stem_path = target.stem_path,
};
if (use_legacy_document_snapshot_writer(
if (should_use_document_snapshot_writer(
"export-animation-frames",
pp::app::DocumentExportExecutionKind::animation_frames_stem,
prepared.value(),
true,
{},
true)) {
const auto exported = export_animation_frames_from_document_snapshot(
app_,
@@ -600,11 +550,11 @@ public:
#if !__WEB__
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-animation-frames");
if (prepared) {
if (use_legacy_document_snapshot_writer(
if (should_use_document_snapshot_writer(
"export-animation-frames",
pp::app::DocumentExportExecutionKind::animation_frames_collection,
prepared.value(),
true,
{},
true)) {
const auto exported = export_animation_frames_from_document_snapshot(app_, target, prepared.value());
if (exported.ok()) {
@@ -697,11 +647,11 @@ public:
auto* app = &app_;
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-cube-faces");
if (prepared) {
if (use_legacy_document_snapshot_writer(
if (should_use_document_snapshot_writer(
"export-cube-faces",
pp::app::DocumentExportExecutionKind::cube_faces,
prepared.value(),
true,
{},
true)) {
const auto exported = export_cube_faces_from_document_snapshot(app_, document_name, prepared.value());
if (exported.ok()) {