108 lines
4.2 KiB
C++
108 lines
4.2 KiB
C++
#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()),
|
|
});
|
|
}
|
|
|
|
}
|