#include "pch.h" #include "legacy_document_canvas_services.h" #include "action.h" #include "app.h" #include "canvas.h" #include #include #include #include #include #include namespace pp::panopainter { namespace { class LegacyDocumentCanvasClearServices final : public pp::app::DocumentCanvasClearServices { public: explicit LegacyDocumentCanvasClearServices(App& app) noexcept : app_(app) { } void clear_current_canvas(float r, float g, float b, float a) override { if (!legacy_document_canvas_available(app_)) { return; } app_.canvas->m_canvas->clear({ r, g, b, a }); } private: App& app_; }; class LegacyDocumentResizeServices final : public pp::app::DocumentResizeServices { public: explicit LegacyDocumentResizeServices(App& app) noexcept : app_(app) { } void resize_document(int width, int height) override { if (!legacy_document_canvas_available(app_)) { return; } app_.canvas->m_canvas->resize(width, height); } void update_title() override { app_.title_update(); } void clear_history() override { ActionManager::clear(); } private: App& app_; }; [[nodiscard]] std::uint32_t legacy_u32_or_zero(int value) noexcept { return value <= 0 ? 0U : static_cast(value); } [[nodiscard]] std::size_t pending_face_payload_count(const Layer& layer) noexcept { const auto frame_count = layer.frames_count(); if (frame_count <= 0) { return 0U; } return static_cast(frame_count) * pp::document::cube_face_count; } [[nodiscard]] std::uint32_t legacy_nonnegative_u32(float value) noexcept { if (value <= 0.0F) { return 0U; } return static_cast(value); } struct LegacyLayerPayloadStorage { std::vector> bytes; std::vector payloads; }; void append_legacy_layer_payloads( Layer& layer, int frame_index, LegacyLayerPayloadStorage& storage) { auto snapshot = layer.snapshot(frame_index); for (std::uint32_t face_index = 0; face_index < pp::document::cube_face_count; ++face_index) { if (!snapshot.m_dirty_face[face_index] || snapshot.image[face_index] == nullptr) { continue; } const auto& box = snapshot.m_dirty_box[face_index]; const auto x = legacy_nonnegative_u32(box.x); const auto y = legacy_nonnegative_u32(box.y); const auto width = legacy_nonnegative_u32(box.z - box.x); const auto height = legacy_nonnegative_u32(box.w - box.y); if (width == 0U || height == 0U) { continue; } const auto byte_count = static_cast(width) * static_cast(height) * pp::document::rgba8_components; storage.bytes.push_back(std::vector(byte_count)); std::memcpy(storage.bytes.back().data(), snapshot.image[face_index].get(), byte_count); storage.payloads.push_back(pp::app::DocumentCanvasFacePayloadInput { .frame_index = static_cast(std::max(frame_index, 0)), .face_index = face_index, .x = x, .y = y, .width = width, .height = height, .rgba8 = std::span(storage.bytes.back().data(), storage.bytes.back().size()), }); } } } // namespace bool legacy_document_canvas_available(const App& app) noexcept { return app.canvas != nullptr && app.canvas->m_canvas != nullptr; } pp::foundation::Result capture_legacy_canvas_document_snapshot( const Canvas& canvas) { std::vector layer_names; std::vector> layer_frame_durations; std::vector layers; layer_names.reserve(canvas.m_layers.size()); layer_frame_durations.reserve(canvas.m_layers.size()); layers.reserve(canvas.m_layers.size()); for (const auto& legacy_layer : canvas.m_layers) { if (!legacy_layer) { continue; } layer_names.push_back(legacy_layer->m_name); auto& durations = layer_frame_durations.emplace_back(); const auto frame_count = legacy_layer->frames_count(); durations.reserve(static_cast(std::max(frame_count, 0))); for (int frame_index = 0; frame_index < frame_count; ++frame_index) { durations.push_back(legacy_u32_or_zero(legacy_layer->frame_duration(frame_index))); } layers.push_back(pp::app::DocumentCanvasLayerSnapshotInput { .name = layer_names.back(), .visible = legacy_layer->m_visible, .alpha_locked = legacy_layer->m_alpha_locked, .opacity = legacy_layer->m_opacity, .blend_mode = legacy_layer->m_blend_mode, .frame_durations_ms = std::span(durations), .pending_face_payloads = pending_face_payload_count(*legacy_layer), }); } return pp::app::plan_document_canvas_snapshot(pp::app::DocumentCanvasSnapshotInput { .has_canvas = true, .width = legacy_u32_or_zero(canvas.m_width), .height = legacy_u32_or_zero(canvas.m_height), .active_layer_index = static_cast(std::max(canvas.m_current_layer_idx, 0)), .active_frame_index = static_cast(std::max(canvas.m_anim_frame, 0)), .layers = std::span(layers), }); } pp::foundation::Result capture_legacy_canvas_document_snapshot( const App& app) { if (!legacy_document_canvas_available(app)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("legacy document canvas snapshot requires a canvas")); } return capture_legacy_canvas_document_snapshot(*app.canvas->m_canvas); } pp::foundation::Result capture_legacy_canvas_document_payload_snapshot( Canvas& canvas) { std::vector layer_names; std::vector> layer_frame_durations; std::vector layer_payload_storage; std::vector layers; layer_names.reserve(canvas.m_layers.size()); layer_frame_durations.reserve(canvas.m_layers.size()); layer_payload_storage.reserve(canvas.m_layers.size()); layers.reserve(canvas.m_layers.size()); for (const auto& legacy_layer : canvas.m_layers) { if (!legacy_layer) { continue; } layer_names.push_back(legacy_layer->m_name); auto& durations = layer_frame_durations.emplace_back(); auto& payload_storage = layer_payload_storage.emplace_back(); const auto frame_count = legacy_layer->frames_count(); durations.reserve(static_cast(std::max(frame_count, 0))); for (int frame_index = 0; frame_index < frame_count; ++frame_index) { durations.push_back(legacy_u32_or_zero(legacy_layer->frame_duration(frame_index))); append_legacy_layer_payloads(*legacy_layer, frame_index, payload_storage); } layers.push_back(pp::app::DocumentCanvasLayerSnapshotInput { .name = layer_names.back(), .visible = legacy_layer->m_visible, .alpha_locked = legacy_layer->m_alpha_locked, .opacity = legacy_layer->m_opacity, .blend_mode = legacy_layer->m_blend_mode, .frame_durations_ms = std::span(durations), .pending_face_payloads = payload_storage.payloads.size(), .captured_face_payloads = std::span( payload_storage.payloads), }); } return pp::app::plan_document_canvas_snapshot(pp::app::DocumentCanvasSnapshotInput { .has_canvas = true, .width = legacy_u32_or_zero(canvas.m_width), .height = legacy_u32_or_zero(canvas.m_height), .active_layer_index = static_cast(std::max(canvas.m_current_layer_idx, 0)), .active_frame_index = static_cast(std::max(canvas.m_anim_frame, 0)), .layers = std::span(layers), }); } pp::foundation::Result capture_legacy_canvas_document_payload_snapshot( App& app) { if (!legacy_document_canvas_available(app)) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("legacy document canvas payload snapshot requires a canvas")); } return capture_legacy_canvas_document_payload_snapshot(*app.canvas->m_canvas); } pp::foundation::Status execute_legacy_document_canvas_clear_plan( App& app, const pp::app::DocumentCanvasClearPlan& plan) { LegacyDocumentCanvasClearServices services(app); return pp::app::execute_document_canvas_clear_plan(plan, services); } pp::foundation::Status execute_legacy_document_resize_plan( App& app, const pp::app::DocumentResizePlan& plan) { LegacyDocumentResizeServices services(app); return pp::app::execute_document_resize_plan(plan, services); } } // namespace pp::panopainter