Project legacy canvas metadata into documents
This commit is contained in:
@@ -1,8 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "document/document.h"
|
||||
#include "foundation/result.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace pp::app {
|
||||
|
||||
@@ -17,6 +26,34 @@ struct DocumentCanvasClearPlan {
|
||||
bool no_op = true;
|
||||
};
|
||||
|
||||
struct DocumentCanvasLayerSnapshotInput {
|
||||
std::string_view name;
|
||||
bool visible = true;
|
||||
bool alpha_locked = false;
|
||||
float opacity = 1.0F;
|
||||
int blend_mode = 0;
|
||||
std::span<const std::uint32_t> frame_durations_ms;
|
||||
std::size_t pending_face_payloads = 0;
|
||||
};
|
||||
|
||||
struct DocumentCanvasSnapshotInput {
|
||||
bool has_canvas = true;
|
||||
std::uint32_t width = 0;
|
||||
std::uint32_t height = 0;
|
||||
std::size_t active_layer_index = 0;
|
||||
std::size_t active_frame_index = 0;
|
||||
std::span<const DocumentCanvasLayerSnapshotInput> layers;
|
||||
};
|
||||
|
||||
struct DocumentCanvasSnapshotResult {
|
||||
pp::document::CanvasDocument document;
|
||||
std::size_t layer_count = 0;
|
||||
std::size_t frame_count = 0;
|
||||
std::size_t pending_face_payloads = 0;
|
||||
bool metadata_only = true;
|
||||
bool requires_renderer_payload_readback = false;
|
||||
};
|
||||
|
||||
class DocumentCanvasClearServices {
|
||||
public:
|
||||
virtual ~DocumentCanvasClearServices() = default;
|
||||
@@ -35,6 +72,115 @@ public:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<DocumentCanvasSnapshotResult> plan_document_canvas_snapshot(
|
||||
DocumentCanvasSnapshotInput input)
|
||||
{
|
||||
if (!input.has_canvas) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(
|
||||
pp::foundation::Status::invalid_argument("document canvas snapshot requires a canvas"));
|
||||
}
|
||||
|
||||
if (input.layers.empty()) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(
|
||||
pp::foundation::Status::invalid_argument("document canvas snapshot requires at least one layer"));
|
||||
}
|
||||
|
||||
std::size_t frame_count = 1U;
|
||||
std::size_t pending_face_payloads = 0U;
|
||||
for (const auto& layer : input.layers) {
|
||||
frame_count = std::max(frame_count, layer.frame_durations_ms.size());
|
||||
pending_face_payloads += layer.pending_face_payloads;
|
||||
}
|
||||
|
||||
if (input.active_layer_index >= input.layers.size()) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(
|
||||
pp::foundation::Status::out_of_range("active canvas layer is outside the document snapshot"));
|
||||
}
|
||||
|
||||
if (input.active_frame_index >= frame_count) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(
|
||||
pp::foundation::Status::out_of_range("active canvas frame is outside the document snapshot"));
|
||||
}
|
||||
|
||||
std::vector<pp::document::AnimationFrame> root_frames;
|
||||
root_frames.reserve(frame_count);
|
||||
for (std::size_t frame_index = 0; frame_index < frame_count; ++frame_index) {
|
||||
std::uint32_t duration_ms = 100U;
|
||||
for (const auto& layer : input.layers) {
|
||||
if (frame_index < layer.frame_durations_ms.size()) {
|
||||
duration_ms = layer.frame_durations_ms[frame_index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
root_frames.push_back(pp::document::AnimationFrame { .duration_ms = duration_ms });
|
||||
}
|
||||
|
||||
std::vector<std::string> layer_names;
|
||||
std::vector<std::vector<pp::document::AnimationFrame>> layer_frames;
|
||||
std::vector<pp::document::DocumentLayerConfig> layer_configs;
|
||||
layer_names.reserve(input.layers.size());
|
||||
layer_frames.reserve(input.layers.size());
|
||||
layer_configs.reserve(input.layers.size());
|
||||
|
||||
for (std::size_t layer_index = 0; layer_index < input.layers.size(); ++layer_index) {
|
||||
const auto& layer = input.layers[layer_index];
|
||||
if (layer.name.empty()) {
|
||||
layer_names.push_back("Layer " + std::to_string(layer_index + 1U));
|
||||
} else {
|
||||
layer_names.push_back(std::string(layer.name));
|
||||
}
|
||||
|
||||
layer_frames.push_back({});
|
||||
auto& frames = layer_frames.back();
|
||||
frames.reserve(layer.frame_durations_ms.empty() ? root_frames.size() : layer.frame_durations_ms.size());
|
||||
if (layer.frame_durations_ms.empty()) {
|
||||
frames = root_frames;
|
||||
} else {
|
||||
for (const auto duration_ms : layer.frame_durations_ms) {
|
||||
frames.push_back(pp::document::AnimationFrame { .duration_ms = duration_ms });
|
||||
}
|
||||
}
|
||||
|
||||
layer_configs.push_back(pp::document::DocumentLayerConfig {
|
||||
.name = layer_names.back(),
|
||||
.visible = layer.visible,
|
||||
.alpha_locked = layer.alpha_locked,
|
||||
.opacity = layer.opacity,
|
||||
.blend_mode = static_cast<pp::paint::BlendMode>(layer.blend_mode),
|
||||
.frames = std::span<const pp::document::AnimationFrame>(frames),
|
||||
});
|
||||
}
|
||||
|
||||
auto document = pp::document::CanvasDocument::create_from_snapshot(pp::document::DocumentSnapshotConfig {
|
||||
.width = input.width,
|
||||
.height = input.height,
|
||||
.layers = std::span<const pp::document::DocumentLayerConfig>(layer_configs),
|
||||
.frames = std::span<const pp::document::AnimationFrame>(root_frames),
|
||||
});
|
||||
if (!document) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(document.status());
|
||||
}
|
||||
|
||||
auto active_status = document.value().set_active_layer(input.active_layer_index);
|
||||
if (!active_status.ok()) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(active_status);
|
||||
}
|
||||
|
||||
active_status = document.value().set_active_frame(input.active_frame_index);
|
||||
if (!active_status.ok()) {
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::failure(active_status);
|
||||
}
|
||||
|
||||
return pp::foundation::Result<DocumentCanvasSnapshotResult>::success(DocumentCanvasSnapshotResult {
|
||||
.document = std::move(document.value()),
|
||||
.layer_count = input.layers.size(),
|
||||
.frame_count = frame_count,
|
||||
.pending_face_payloads = pending_face_payloads,
|
||||
.metadata_only = true,
|
||||
.requires_renderer_payload_readback = pending_face_payloads > 0U,
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<DocumentCanvasClearPlan> plan_document_canvas_clear(
|
||||
bool has_canvas,
|
||||
float r = 0.0F,
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
#include "legacy_document_canvas_services.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "canvas.h"
|
||||
#include "legacy_history_services.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
@@ -58,6 +65,20 @@ private:
|
||||
App& app_;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::uint32_t legacy_u32_or_zero(int value) noexcept
|
||||
{
|
||||
return value <= 0 ? 0U : static_cast<std::uint32_t>(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<std::size_t>(frame_count) * pp::document::cube_face_count;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool legacy_document_canvas_available(const App& app) noexcept
|
||||
@@ -65,6 +86,61 @@ bool legacy_document_canvas_available(const App& app) noexcept
|
||||
return app.canvas != nullptr && app.canvas->m_canvas != nullptr;
|
||||
}
|
||||
|
||||
pp::foundation::Result<pp::app::DocumentCanvasSnapshotResult> capture_legacy_canvas_document_snapshot(
|
||||
const Canvas& canvas)
|
||||
{
|
||||
std::vector<std::string> layer_names;
|
||||
std::vector<std::vector<std::uint32_t>> layer_frame_durations;
|
||||
std::vector<pp::app::DocumentCanvasLayerSnapshotInput> 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::size_t>(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<const std::uint32_t>(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::size_t>(std::max(canvas.m_current_layer_idx, 0)),
|
||||
.active_frame_index = static_cast<std::size_t>(std::max(canvas.m_anim_frame, 0)),
|
||||
.layers = std::span<const pp::app::DocumentCanvasLayerSnapshotInput>(layers),
|
||||
});
|
||||
}
|
||||
|
||||
pp::foundation::Result<pp::app::DocumentCanvasSnapshotResult> capture_legacy_canvas_document_snapshot(
|
||||
const App& app)
|
||||
{
|
||||
if (!legacy_document_canvas_available(app)) {
|
||||
return pp::foundation::Result<pp::app::DocumentCanvasSnapshotResult>::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::Status execute_legacy_document_canvas_clear_plan(
|
||||
App& app,
|
||||
const pp::app::DocumentCanvasClearPlan& plan)
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
#include "foundation/result.h"
|
||||
|
||||
class App;
|
||||
class Canvas;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
[[nodiscard]] bool legacy_document_canvas_available(const App& app) noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<pp::app::DocumentCanvasSnapshotResult>
|
||||
capture_legacy_canvas_document_snapshot(const Canvas& canvas);
|
||||
[[nodiscard]] pp::foundation::Result<pp::app::DocumentCanvasSnapshotResult>
|
||||
capture_legacy_canvas_document_snapshot(const App& app);
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_canvas_clear_plan(
|
||||
App& app,
|
||||
const pp::app::DocumentCanvasClearPlan& plan);
|
||||
|
||||
Reference in New Issue
Block a user