272 lines
9.4 KiB
C++
272 lines
9.4 KiB
C++
#include "pch.h"
|
|
|
|
#include "legacy_document_canvas_services.h"
|
|
|
|
#include "app.h"
|
|
#include "canvas.h"
|
|
#include "legacy_history_services.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <cstdint>
|
|
#include <span>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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
|
|
{
|
|
clear_legacy_history();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
[[nodiscard]] std::uint32_t legacy_nonnegative_u32(float value) noexcept
|
|
{
|
|
if (value <= 0.0F) {
|
|
return 0U;
|
|
}
|
|
return static_cast<std::uint32_t>(value);
|
|
}
|
|
|
|
struct LegacyLayerPayloadStorage {
|
|
std::vector<std::vector<std::uint8_t>> bytes;
|
|
std::vector<pp::app::DocumentCanvasFacePayloadInput> 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<std::size_t>(width) * static_cast<std::size_t>(height)
|
|
* pp::document::rgba8_components;
|
|
storage.bytes.push_back(std::vector<std::uint8_t>(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::uint32_t>(std::max(frame_index, 0)),
|
|
.face_index = face_index,
|
|
.x = x,
|
|
.y = y,
|
|
.width = width,
|
|
.height = height,
|
|
.rgba8 = std::span<const std::uint8_t>(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<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::Result<pp::app::DocumentCanvasSnapshotResult> capture_legacy_canvas_document_payload_snapshot(
|
|
Canvas& canvas)
|
|
{
|
|
std::vector<std::string> layer_names;
|
|
std::vector<std::vector<std::uint32_t>> layer_frame_durations;
|
|
std::vector<LegacyLayerPayloadStorage> layer_payload_storage;
|
|
std::vector<pp::app::DocumentCanvasLayerSnapshotInput> 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::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)));
|
|
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<const std::uint32_t>(durations),
|
|
.pending_face_payloads = payload_storage.payloads.size(),
|
|
.captured_face_payloads = std::span<const pp::app::DocumentCanvasFacePayloadInput>(
|
|
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::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_payload_snapshot(
|
|
App& app)
|
|
{
|
|
if (!legacy_document_canvas_available(app)) {
|
|
return pp::foundation::Result<pp::app::DocumentCanvasSnapshotResult>::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
|