Centralize legacy video export bridge
This commit is contained in:
@@ -563,7 +563,8 @@ Known local toolchain state:
|
|||||||
invalid export naming inputs, plus export-start license/canvas availability
|
invalid export naming inputs, plus export-start license/canvas availability
|
||||||
decisions, export menu executor dispatch, file/stem/collection export
|
decisions, export menu executor dispatch, file/stem/collection export
|
||||||
execution dispatch, failed directory creation preservation, named depth/cube
|
execution dispatch, failed directory creation preservation, named depth/cube
|
||||||
export dispatch, and malformed export target rejection.
|
export dispatch, malformed export target rejection, video export dispatch for
|
||||||
|
animation MP4/timelapse paths, and empty video-path rejection.
|
||||||
- `pp_app_core_document_recording_tests` covers recording start/stop, clear,
|
- `pp_app_core_document_recording_tests` covers recording start/stop, clear,
|
||||||
platform recorded-file cleanup, frame-count reset, export progress totals,
|
platform recorded-file cleanup, frame-count reset, export progress totals,
|
||||||
and oversized progress-total clamping.
|
and oversized progress-total clamping.
|
||||||
@@ -612,7 +613,11 @@ Known local toolchain state:
|
|||||||
layers, animation-frame, depth, and cube-face export calls. It preserves
|
layers, animation-frame, depth, and cube-face export calls. It preserves
|
||||||
platform-specific export messages, directory creation, picker-selected stem
|
platform-specific export messages, directory creation, picker-selected stem
|
||||||
exports, Web prepared-file handoff, and legacy `Canvas` export execution while
|
exports, Web prepared-file handoff, and legacy `Canvas` export execution while
|
||||||
retained renderer/document/platform ownership is tracked by `DEBT-0043`.
|
retained renderer/document/platform ownership is tracked by `DEBT-0043`. It
|
||||||
|
also bridges timelapse and animation MP4 export picker-selected paths while
|
||||||
|
preserving desktop worker-thread timelapse behavior, mobile/Web save
|
||||||
|
callbacks, `App::rec_export`, animation `Canvas::export_anim_mp4`, and
|
||||||
|
success messages; retained video/export ownership is tracked by `DEBT-0044`.
|
||||||
- `src/legacy_history_services.*` is the current app-shell bridge between
|
- `src/legacy_history_services.*` is the current app-shell bridge between
|
||||||
`pp_app_core` history plans and legacy `ActionManager`; toolbar and
|
`pp_app_core` history plans and legacy `ActionManager`; toolbar and
|
||||||
`NodeCanvas` hotkeys share it while document-history extraction remains
|
`NodeCanvas` hotkeys share it while document-history extraction remains
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| DEBT-0041 | Open | Modernization | Accepted new-document planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_newdoc`, `pano_cli plan-new-document`, `NewDocumentServices`, and `src/legacy_document_session_services.*`, but the bridge still mutates legacy app document fields, clears legacy layer UI, resizes legacy `Canvas`, clears legacy history, creates the default layer through legacy UI, mutates unsaved/new-document flags, updates the title, and handles keyboard/dialog cleanup directly | Preserve current New Document dialog behavior while document creation moves toward app/document/UI services | `pp_app_core_document_session_tests`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli simulate-app-session --save-intent save`; `ctest --preset desktop-fast --build-config Debug` | New document creation, overwrite confirmation, canvas/document allocation, default layer creation, history clearing, title updates, dirty/new-document state, and keyboard/dialog cleanup are owned by injected app/document/UI services with `App::dialog_newdoc` acting only as a UI adapter |
|
| DEBT-0041 | Open | Modernization | Accepted new-document planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_newdoc`, `pano_cli plan-new-document`, `NewDocumentServices`, and `src/legacy_document_session_services.*`, but the bridge still mutates legacy app document fields, clears legacy layer UI, resizes legacy `Canvas`, clears legacy history, creates the default layer through legacy UI, mutates unsaved/new-document flags, updates the title, and handles keyboard/dialog cleanup directly | Preserve current New Document dialog behavior while document creation moves toward app/document/UI services | `pp_app_core_document_session_tests`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli simulate-app-session --save-intent save`; `ctest --preset desktop-fast --build-config Debug` | New document creation, overwrite confirmation, canvas/document allocation, default layer creation, history clearing, title updates, dirty/new-document state, and keyboard/dialog cleanup are owned by injected app/document/UI services with `App::dialog_newdoc` acting only as a UI adapter |
|
||||||
| DEBT-0042 | Open | Modernization | Accepted Save As and Save Version planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_save`, `App::dialog_save_ver`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `DocumentFileSaveServices`, `DocumentVersionSaveServices`, and `src/legacy_document_session_services.*`, but the bridge still opens legacy overwrite prompts, calls legacy `Canvas::project_save`, mutates app document name/path/directory fields, marks version saves dirty before saving, updates the title, and handles keyboard/dialog cleanup directly | Preserve current Save As and Save Version behavior while document persistence moves toward app/document/storage/UI services | `pp_app_core_document_session_tests`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli simulate-app-session --save-intent save-as`; `pano_cli simulate-app-session --save-intent save-version`; `ctest --preset desktop-fast --build-config Debug` | Save As overwrite prompting, project-save execution, app document metadata updates, title updates, version-save dirty-state handling, and keyboard/dialog cleanup are owned by injected app/document/storage/UI services with `App::dialog_save` and `App::dialog_save_ver` acting only as UI adapters |
|
| DEBT-0042 | Open | Modernization | Accepted Save As and Save Version planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_save`, `App::dialog_save_ver`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `DocumentFileSaveServices`, `DocumentVersionSaveServices`, and `src/legacy_document_session_services.*`, but the bridge still opens legacy overwrite prompts, calls legacy `Canvas::project_save`, mutates app document name/path/directory fields, marks version saves dirty before saving, updates the title, and handles keyboard/dialog cleanup directly | Preserve current Save As and Save Version behavior while document persistence moves toward app/document/storage/UI services | `pp_app_core_document_session_tests`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli simulate-app-session --save-intent save-as`; `pano_cli simulate-app-session --save-intent save-version`; `ctest --preset desktop-fast --build-config Debug` | Save As overwrite prompting, project-save execution, app document metadata updates, title updates, version-save dirty-state handling, and keyboard/dialog cleanup are owned by injected app/document/storage/UI services with `App::dialog_save` and `App::dialog_save_ver` acting only as UI adapters |
|
||||||
| DEBT-0043 | Open | Modernization | Equirectangular, layer, animation-frame, depth, and cube-face export planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_export`, `App::dialog_export_layers`, `App::dialog_export_anim_frames`, `App::dialog_export_depth`, `App::dialog_export_cube_faces`, `pano_cli plan-export-*`, `DocumentExportServices`, and `src/legacy_document_export_services.*`, but the bridge still calls legacy `Canvas` export methods, owns platform-specific export success messages, creates export directories, handles picker-selected stems, and performs Web prepared-file handoff directly | Preserve current image/collection/depth/cube export behavior while export execution moves toward document/renderer/platform/storage services | `pp_app_core_document_export_tests`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-menu --kind layers`; `pano_cli plan-export-target --kind collection --work-dir D:/Paint --doc-name demo --suffix _layers`; `pano_cli simulate-document-export`; `ctest --preset desktop-fast --build-config Debug` | File, collection, stem, depth, and cube export execution, export-directory creation, platform success reporting, Web file handoff, and legacy canvas export calls are owned by injected document/renderer/platform/storage services with export dialogs acting only as UI adapters |
|
| DEBT-0043 | Open | Modernization | Equirectangular, layer, animation-frame, depth, and cube-face export planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_export`, `App::dialog_export_layers`, `App::dialog_export_anim_frames`, `App::dialog_export_depth`, `App::dialog_export_cube_faces`, `pano_cli plan-export-*`, `DocumentExportServices`, and `src/legacy_document_export_services.*`, but the bridge still calls legacy `Canvas` export methods, owns platform-specific export success messages, creates export directories, handles picker-selected stems, and performs Web prepared-file handoff directly | Preserve current image/collection/depth/cube export behavior while export execution moves toward document/renderer/platform/storage services | `pp_app_core_document_export_tests`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-menu --kind layers`; `pano_cli plan-export-target --kind collection --work-dir D:/Paint --doc-name demo --suffix _layers`; `pano_cli simulate-document-export`; `ctest --preset desktop-fast --build-config Debug` | File, collection, stem, depth, and cube export execution, export-directory creation, platform success reporting, Web file handoff, and legacy canvas export calls are owned by injected document/renderer/platform/storage services with export dialogs acting only as UI adapters |
|
||||||
|
| DEBT-0044 | Open | Modernization | Timelapse and animation MP4 export execution dispatch now consumes pure `pp_app_core` through `App::dialog_timelapse_export`, `App::dialog_export_mp4`, `pano_cli plan-export-menu`, `pano_cli plan-export-target --kind name`, `DocumentVideoExportServices`, and `src/legacy_document_export_services.*`, but the bridge still launches legacy desktop timelapse worker threads, calls `App::rec_export`, calls `Canvas::export_anim_mp4`, owns mobile/Web save callbacks, and emits success messages directly | Preserve current MP4/timelapse export behavior while video export moves toward app/document/renderer/video/platform/storage services | `pp_app_core_document_export_tests`; `pano_cli plan-export-menu --kind animation-mp4`; `pano_cli plan-export-menu --kind timelapse`; `pano_cli plan-export-target --kind name --doc-name demo --suffix -animation`; `pano_cli plan-export-target --kind name --doc-name demo --suffix -timelapse`; `ctest --preset desktop-fast --build-config Debug` | Timelapse and animation MP4 execution, desktop worker threading, frame readback/video encoding handoff, mobile/Web save callbacks, and success reporting are owned by injected app/document/renderer/video/platform/storage services with export dialogs acting only as UI adapters |
|
||||||
|
|
||||||
## Closed Debt
|
## Closed Debt
|
||||||
|
|
||||||
|
|||||||
@@ -758,6 +758,12 @@ named export work through app-core document export executors and
|
|||||||
directory creation, picker-selected stems, Web prepared-file handoff, and legacy
|
directory creation, picker-selected stems, Web prepared-file handoff, and legacy
|
||||||
`Canvas` export calls while retained execution remains tracked under
|
`Canvas` export calls while retained execution remains tracked under
|
||||||
`DEBT-0043`.
|
`DEBT-0043`.
|
||||||
|
`App::dialog_timelapse_export` and `App::dialog_export_mp4` now route
|
||||||
|
picker-selected MP4 export paths through the app-core document video export
|
||||||
|
executor and `src/legacy_document_export_services.*`, preserving mobile/Web
|
||||||
|
suggested-name save callbacks, desktop worker-thread timelapse export,
|
||||||
|
`App::rec_export`, animation `Canvas::export_anim_mp4` dispatch, and existing
|
||||||
|
success messages while retained execution remains tracked under `DEBT-0044`.
|
||||||
|
|
||||||
Implementation tasks:
|
Implementation tasks:
|
||||||
|
|
||||||
@@ -1330,6 +1336,15 @@ Results:
|
|||||||
- Focused export CTest coverage passed for `pp_app_core_document_export_tests`,
|
- Focused export CTest coverage passed for `pp_app_core_document_export_tests`,
|
||||||
`pano_cli_plan_export_start/menu/target_*`, and
|
`pano_cli_plan_export_start/menu/target_*`, and
|
||||||
`pano_cli_simulate_document_export_smoke` after the live bridge split.
|
`pano_cli_simulate_document_export_smoke` after the live bridge split.
|
||||||
|
- `PanoPainter`, `pp_app_core_document_export_tests`, and `pano_cli` built
|
||||||
|
after timelapse and animation MP4 export execution moved behind document
|
||||||
|
video export services. A clean rebuild was required once because MSVC
|
||||||
|
reported the known Debug PDB `LNK1103` corruption, after which the app,
|
||||||
|
export tests, and `pano_cli` targets built cleanly.
|
||||||
|
- Focused video export CTest coverage passed for
|
||||||
|
`pp_app_core_document_export_tests`, `pano_cli_plan_export_menu_*`,
|
||||||
|
`pano_cli_plan_export_target_name_smoke`, and
|
||||||
|
`pano_cli_simulate_document_export_smoke`.
|
||||||
- `pp_app_core_document_recording_tests` passed, covering recording start/stop,
|
- `pp_app_core_document_recording_tests` passed, covering recording start/stop,
|
||||||
clear, platform recorded-file cleanup, frame-count reset, export progress
|
clear, platform recorded-file cleanup, frame-count reset, export progress
|
||||||
totals, and oversized progress-total clamping.
|
totals, and oversized progress-total clamping.
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ enum class DocumentExportCollectionKind {
|
|||||||
animation_frames,
|
animation_frames,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DocumentVideoExportKind {
|
||||||
|
animation_mp4,
|
||||||
|
timelapse,
|
||||||
|
};
|
||||||
|
|
||||||
struct DocumentExportMenuPlan {
|
struct DocumentExportMenuPlan {
|
||||||
DocumentExportMenuKind kind = DocumentExportMenuKind::jpeg;
|
DocumentExportMenuKind kind = DocumentExportMenuKind::jpeg;
|
||||||
DocumentExportMenuAction action = DocumentExportMenuAction::show_jpeg_dialog;
|
DocumentExportMenuAction action = DocumentExportMenuAction::show_jpeg_dialog;
|
||||||
@@ -96,6 +101,16 @@ public:
|
|||||||
virtual void export_cube_faces(std::string_view document_name) = 0;
|
virtual void export_cube_faces(std::string_view document_name) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DocumentVideoExportServices {
|
||||||
|
public:
|
||||||
|
virtual ~DocumentVideoExportServices() = default;
|
||||||
|
|
||||||
|
virtual void export_animation_mp4(std::string_view path) = 0;
|
||||||
|
virtual void export_timelapse_mp4(std::string_view path) = 0;
|
||||||
|
virtual void show_animation_export_success(std::string_view path) = 0;
|
||||||
|
virtual void show_timelapse_export_success(std::string_view path) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] constexpr DocumentExportStartDecision plan_document_export_start(
|
[[nodiscard]] constexpr DocumentExportStartDecision plan_document_export_start(
|
||||||
bool requires_license,
|
bool requires_license,
|
||||||
bool license_valid,
|
bool license_valid,
|
||||||
@@ -342,6 +357,29 @@ public:
|
|||||||
return pp::foundation::Status::success();
|
return pp::foundation::Status::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline pp::foundation::Status execute_document_video_export(
|
||||||
|
DocumentVideoExportKind kind,
|
||||||
|
std::string_view path,
|
||||||
|
DocumentVideoExportServices& services)
|
||||||
|
{
|
||||||
|
if (path.empty()) {
|
||||||
|
return pp::foundation::Status::invalid_argument("video export path must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
case DocumentVideoExportKind::animation_mp4:
|
||||||
|
services.export_animation_mp4(path);
|
||||||
|
services.show_animation_export_success(path);
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
case DocumentVideoExportKind::timelapse:
|
||||||
|
services.export_timelapse_mp4(path);
|
||||||
|
services.show_timelapse_export_success(path);
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::invalid_argument("unknown document video export kind");
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_menu_plan(
|
[[nodiscard]] inline pp::foundation::Status execute_document_export_menu_plan(
|
||||||
const DocumentExportMenuPlan& plan,
|
const DocumentExportMenuPlan& plan,
|
||||||
DocumentExportMenuServices& services)
|
DocumentExportMenuServices& services)
|
||||||
|
|||||||
@@ -566,19 +566,28 @@ void App::dialog_timelapse_export()
|
|||||||
|
|
||||||
pick_file_save("mp4", target.value().name,
|
pick_file_save("mp4", target.value().name,
|
||||||
[this](std::string path) {
|
[this](std::string path) {
|
||||||
rec_export(path);
|
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
||||||
|
*this,
|
||||||
|
pp::app::DocumentVideoExportKind::timelapse,
|
||||||
|
path,
|
||||||
|
false);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("Document timelapse export failed: %s", status.message);
|
||||||
},
|
},
|
||||||
[this](const std::string& path, bool saved) {
|
[](const std::string& path, bool saved) {
|
||||||
message_box("Export Timelapse", "Timelapse exported successfully.");
|
(void)path;
|
||||||
|
(void)saved;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
pick_file_save({ "mp4" }, [this](std::string path) {
|
pick_file_save({ "mp4" }, [this](std::string path) {
|
||||||
std::thread([this, path] {
|
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
||||||
BT_SetTerminate();
|
*this,
|
||||||
rec_export(path);
|
pp::app::DocumentVideoExportKind::timelapse,
|
||||||
message_box("Export Timelapse", "Timelapse exported to: " + path);
|
path,
|
||||||
}).detach();
|
true);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("Document timelapse export failed: %s", status.message);
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -597,17 +606,28 @@ void App::dialog_export_mp4()
|
|||||||
|
|
||||||
pick_file_save("mp4", target.value().name,
|
pick_file_save("mp4", target.value().name,
|
||||||
[this](std::string path) {
|
[this](std::string path) {
|
||||||
export_anim_mp4(path);
|
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
||||||
|
*this,
|
||||||
|
pp::app::DocumentVideoExportKind::animation_mp4,
|
||||||
|
path,
|
||||||
|
false);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("Document animation export failed: %s", status.message);
|
||||||
},
|
},
|
||||||
[this](const std::string& path, bool saved) {
|
[](const std::string& path, bool saved) {
|
||||||
message_box("Export Animation", "Animation exported successfully.");
|
(void)path;
|
||||||
|
(void)saved;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
pick_file_save({ "mp4" }, [this](std::string path) {
|
pick_file_save({ "mp4" }, [this](std::string path) {
|
||||||
Canvas::I->export_anim_mp4(path, [this, path] {
|
const auto status = pp::panopainter::execute_legacy_document_video_export(
|
||||||
message_box("Export Animation", "Animation exported to: " + path);
|
*this,
|
||||||
});
|
pp::app::DocumentVideoExportKind::animation_mp4,
|
||||||
|
path,
|
||||||
|
true);
|
||||||
|
if (!status.ok())
|
||||||
|
LOG("Document animation export failed: %s", status.message);
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -105,6 +108,65 @@ private:
|
|||||||
App& app_;
|
App& app_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LegacyDocumentVideoExportServices final : public pp::app::DocumentVideoExportServices {
|
||||||
|
public:
|
||||||
|
LegacyDocumentVideoExportServices(App& app, bool asynchronous) noexcept
|
||||||
|
: app_(app)
|
||||||
|
, asynchronous_(asynchronous)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void export_animation_mp4(std::string_view path) override
|
||||||
|
{
|
||||||
|
auto* app = &app_;
|
||||||
|
auto path_string = std::string(path);
|
||||||
|
if (asynchronous_) {
|
||||||
|
Canvas::I->export_anim_mp4(path_string, [app, path_string] {
|
||||||
|
app->message_box("Export Animation", "Animation exported to: " + path_string);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas::I->export_anim_mp4(path_string, [app] {
|
||||||
|
app->message_box("Export Animation", "Animation exported successfully.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void export_timelapse_mp4(std::string_view path) override
|
||||||
|
{
|
||||||
|
auto* app = &app_;
|
||||||
|
auto path_string = std::string(path);
|
||||||
|
if (asynchronous_) {
|
||||||
|
std::thread([app, path_string] {
|
||||||
|
BT_SetTerminate();
|
||||||
|
app->rec_export(path_string);
|
||||||
|
app->message_box("Export Timelapse", "Timelapse exported to: " + path_string);
|
||||||
|
}).detach();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app->rec_export(path_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_animation_export_success(std::string_view path) override
|
||||||
|
{
|
||||||
|
(void)path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_timelapse_export_success(std::string_view path) override
|
||||||
|
{
|
||||||
|
if (asynchronous_) {
|
||||||
|
(void)path;
|
||||||
|
} else {
|
||||||
|
app_.message_box("Export Timelapse", "Timelapse exported successfully.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
App& app_;
|
||||||
|
bool asynchronous_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
pp::foundation::Status execute_legacy_document_export_file(
|
pp::foundation::Status execute_legacy_document_export_file(
|
||||||
@@ -149,4 +211,14 @@ pp::foundation::Status execute_legacy_document_export_cube_faces(
|
|||||||
return pp::app::execute_document_export_cube_faces(document_name, services);
|
return pp::app::execute_document_export_cube_faces(document_name, services);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status execute_legacy_document_video_export(
|
||||||
|
App& app,
|
||||||
|
pp::app::DocumentVideoExportKind kind,
|
||||||
|
std::string_view path,
|
||||||
|
bool asynchronous)
|
||||||
|
{
|
||||||
|
LegacyDocumentVideoExportServices services(app, asynchronous);
|
||||||
|
return pp::app::execute_document_video_export(kind, path, services);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -29,4 +29,10 @@ namespace pp::panopainter {
|
|||||||
App& app,
|
App& app,
|
||||||
std::string_view document_name);
|
std::string_view document_name);
|
||||||
|
|
||||||
|
[[nodiscard]] pp::foundation::Status execute_legacy_document_video_export(
|
||||||
|
App& app,
|
||||||
|
pp::app::DocumentVideoExportKind kind,
|
||||||
|
std::string_view path,
|
||||||
|
bool asynchronous);
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -110,6 +110,45 @@ public:
|
|||||||
std::string call_order;
|
std::string call_order;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FakeDocumentVideoExportServices final : public pp::app::DocumentVideoExportServices {
|
||||||
|
public:
|
||||||
|
void export_animation_mp4(std::string_view path) override
|
||||||
|
{
|
||||||
|
animation_exports += 1;
|
||||||
|
last_path = std::string(path);
|
||||||
|
call_order += "animation-export;";
|
||||||
|
}
|
||||||
|
|
||||||
|
void export_timelapse_mp4(std::string_view path) override
|
||||||
|
{
|
||||||
|
timelapse_exports += 1;
|
||||||
|
last_path = std::string(path);
|
||||||
|
call_order += "timelapse-export;";
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_animation_export_success(std::string_view path) override
|
||||||
|
{
|
||||||
|
animation_messages += 1;
|
||||||
|
last_message_path = std::string(path);
|
||||||
|
call_order += "animation-message;";
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_timelapse_export_success(std::string_view path) override
|
||||||
|
{
|
||||||
|
timelapse_messages += 1;
|
||||||
|
last_message_path = std::string(path);
|
||||||
|
call_order += "timelapse-message;";
|
||||||
|
}
|
||||||
|
|
||||||
|
int animation_exports = 0;
|
||||||
|
int timelapse_exports = 0;
|
||||||
|
int animation_messages = 0;
|
||||||
|
int timelapse_messages = 0;
|
||||||
|
std::string last_path;
|
||||||
|
std::string last_message_path;
|
||||||
|
std::string call_order;
|
||||||
|
};
|
||||||
|
|
||||||
void equirectangular_export_builds_file_target(pp::tests::Harness& harness)
|
void equirectangular_export_builds_file_target(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
const auto target = pp::app::make_document_export_file_target("D:/Paint", "demo", ".png");
|
const auto target = pp::app::make_document_export_file_target("D:/Paint", "demo", ".png");
|
||||||
@@ -431,6 +470,51 @@ void export_executor_rejects_malformed_targets(pp::tests::Harness& harness)
|
|||||||
PP_EXPECT(harness, services.call_order.empty());
|
PP_EXPECT(harness, services.call_order.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void video_export_executor_dispatches_animation_and_timelapse(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeDocumentVideoExportServices services;
|
||||||
|
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::execute_document_video_export(
|
||||||
|
pp::app::DocumentVideoExportKind::animation_mp4,
|
||||||
|
"D:/Paint/animation.mp4",
|
||||||
|
services)
|
||||||
|
.ok());
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::execute_document_video_export(
|
||||||
|
pp::app::DocumentVideoExportKind::timelapse,
|
||||||
|
"D:/Paint/timelapse.mp4",
|
||||||
|
services)
|
||||||
|
.ok());
|
||||||
|
|
||||||
|
PP_EXPECT(harness, services.animation_exports == 1);
|
||||||
|
PP_EXPECT(harness, services.timelapse_exports == 1);
|
||||||
|
PP_EXPECT(harness, services.animation_messages == 1);
|
||||||
|
PP_EXPECT(harness, services.timelapse_messages == 1);
|
||||||
|
PP_EXPECT(harness, services.last_path == "D:/Paint/timelapse.mp4");
|
||||||
|
PP_EXPECT(harness, services.last_message_path == "D:/Paint/timelapse.mp4");
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
services.call_order
|
||||||
|
== "animation-export;animation-message;timelapse-export;timelapse-message;");
|
||||||
|
}
|
||||||
|
|
||||||
|
void video_export_executor_rejects_empty_paths(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeDocumentVideoExportServices services;
|
||||||
|
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
!pp::app::execute_document_video_export(
|
||||||
|
pp::app::DocumentVideoExportKind::animation_mp4,
|
||||||
|
"",
|
||||||
|
services)
|
||||||
|
.ok());
|
||||||
|
PP_EXPECT(harness, services.call_order.empty());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -454,5 +538,7 @@ int main()
|
|||||||
export_executor_dispatches_file_stem_collection_and_named_exports);
|
export_executor_dispatches_file_stem_collection_and_named_exports);
|
||||||
harness.run("export executor preserves failed directory creation", export_executor_preserves_failed_directory_creation);
|
harness.run("export executor preserves failed directory creation", export_executor_preserves_failed_directory_creation);
|
||||||
harness.run("export executor rejects malformed targets", export_executor_rejects_malformed_targets);
|
harness.run("export executor rejects malformed targets", export_executor_rejects_malformed_targets);
|
||||||
|
harness.run("video export executor dispatches animation and timelapse", video_export_executor_dispatches_animation_and_timelapse);
|
||||||
|
harness.run("video export executor rejects empty paths", video_export_executor_rejects_empty_paths);
|
||||||
return harness.finish();
|
return harness.finish();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user