Add document PPI export boundary

This commit is contained in:
2026-06-02 11:11:01 +02:00
parent b3710498f3
commit bad2670f87
10 changed files with 333 additions and 7 deletions

107
src/document/ppi_export.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include "document/ppi_export.h"
#include "assets/image_pixels.h"
#include "assets/ppi_header.h"
#include <cstdint>
#include <span>
#include <utility>
namespace pp::document {
namespace {
[[nodiscard]] pp::foundation::Result<std::uint32_t> ppi_blend_mode(
pp::paint::BlendMode blend_mode) noexcept
{
switch (blend_mode) {
case pp::paint::BlendMode::normal:
return pp::foundation::Result<std::uint32_t>::success(0);
case pp::paint::BlendMode::multiply:
return pp::foundation::Result<std::uint32_t>::success(1);
case pp::paint::BlendMode::screen:
return pp::foundation::Result<std::uint32_t>::success(2);
case pp::paint::BlendMode::color_dodge:
return pp::foundation::Result<std::uint32_t>::success(3);
case pp::paint::BlendMode::overlay:
return pp::foundation::Result<std::uint32_t>::success(4);
}
return pp::foundation::Result<std::uint32_t>::failure(
pp::foundation::Status::invalid_argument("document layer blend mode cannot be exported to PPI"));
}
}
pp::foundation::Result<std::vector<std::byte>> export_ppi_project_document(
const CanvasDocument& document)
{
std::vector<std::vector<pp::assets::PpiFrameConfig>> frame_configs;
frame_configs.reserve(document.layers().size());
std::vector<pp::assets::PpiLayerConfig> layer_configs;
layer_configs.reserve(document.layers().size());
std::vector<std::vector<std::byte>> payloads;
payloads.reserve(document.face_pixel_payload_count());
std::vector<pp::assets::PpiDirtyFacePayloadConfig> dirty_faces;
dirty_faces.reserve(document.face_pixel_payload_count());
for (std::size_t layer_index = 0; layer_index < document.layers().size(); ++layer_index) {
const auto& layer = document.layers()[layer_index];
const auto blend_mode = ppi_blend_mode(layer.blend_mode);
if (!blend_mode) {
return pp::foundation::Result<std::vector<std::byte>>::failure(blend_mode.status());
}
auto& frames = frame_configs.emplace_back();
frames.reserve(layer.frames.size());
for (std::size_t frame_index = 0; frame_index < layer.frames.size(); ++frame_index) {
const auto& frame = layer.frames[frame_index];
frames.push_back(pp::assets::PpiFrameConfig {
.duration_ms = frame.duration_ms,
});
for (const auto& face : frame.face_pixels) {
const auto encoded = pp::assets::encode_png_rgba8(
face.width,
face.height,
face.rgba8);
if (!encoded) {
return pp::foundation::Result<std::vector<std::byte>>::failure(encoded.status());
}
payloads.push_back(encoded.value());
const auto& payload = payloads.back();
dirty_faces.push_back(pp::assets::PpiDirtyFacePayloadConfig {
.layer_index = static_cast<std::uint32_t>(layer_index),
.frame_index = static_cast<std::uint32_t>(frame_index),
.face_index = face.face_index,
.x = face.x,
.y = face.y,
.width = face.width,
.height = face.height,
.png_rgba8 = std::span<const std::byte>(payload.data(), payload.size()),
});
}
}
layer_configs.push_back(pp::assets::PpiLayerConfig {
.name = layer.name,
.metadata = pp::assets::PpiLayerMetadataConfig {
.opacity = layer.opacity,
.blend_mode = blend_mode.value(),
.alpha_locked = layer.alpha_locked,
.visible = layer.visible,
},
.frames = std::span<const pp::assets::PpiFrameConfig>(frames.data(), frames.size()),
});
}
return pp::assets::create_ppi_project(pp::assets::PpiProjectConfig {
.width = document.width(),
.height = document.height(),
.layers = std::span<const pp::assets::PpiLayerConfig>(layer_configs.data(), layer_configs.size()),
.dirty_faces = std::span<const pp::assets::PpiDirtyFacePayloadConfig>(dirty_faces.data(), dirty_faces.size()),
});
}
}

14
src/document/ppi_export.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include "document/document.h"
#include "foundation/result.h"
#include <cstddef>
#include <vector>
namespace pp::document {
[[nodiscard]] pp::foundation::Result<std::vector<std::byte>> export_ppi_project_document(
const CanvasDocument& document);
}