Export layer collections through paint renderer

This commit is contained in:
2026-06-05 20:48:16 +02:00
parent 77268a28fb
commit 3c36be4b43
10 changed files with 815 additions and 21 deletions

View File

@@ -408,6 +408,77 @@ pp::foundation::Result<DocumentFrameCompositeResult> composite_document_frame(
return pp::foundation::Result<DocumentFrameCompositeResult>::success(std::move(result));
}
pp::foundation::Result<DocumentFrameCompositeResult> composite_document_layer_frame(
const pp::document::CanvasDocument& document,
std::size_t layer_index,
std::size_t frame_index,
pp::paint::Rgba clear_color)
{
if (layer_index >= document.layers().size()) {
return pp::foundation::Result<DocumentFrameCompositeResult>::failure(
pp::foundation::Status::out_of_range("document layer export index is outside the document"));
}
if (frame_index >= document.frames().size()) {
return pp::foundation::Result<DocumentFrameCompositeResult>::failure(
pp::foundation::Status::out_of_range("document layer export frame index is outside the document"));
}
const pp::renderer::Extent2D extent {
.width = document.width(),
.height = document.height(),
};
const auto pixel_count = expected_pixel_count(extent);
if (!pixel_count) {
return pp::foundation::Result<DocumentFrameCompositeResult>::failure(pixel_count.status());
}
const auto& source_layer = document.layers()[layer_index];
auto export_layer = source_layer;
export_layer.visible = true;
export_layer.opacity = 1.0F;
export_layer.blend_mode = pp::paint::BlendMode::normal;
DocumentFrameCompositeResult result;
result.extent = extent;
result.visited_layer_count = 1U;
for (std::uint32_t face_index = 0; face_index < pp::document::cube_face_count; ++face_index) {
DocumentFaceCompositeResult face;
face.extent = extent;
face.pixels.assign(pixel_count.value(), clear_color);
face.visited_layer_count = 1U;
if (frame_index < source_layer.frames.size()) {
bool composited_face = false;
const auto& frame = source_layer.frames[frame_index];
for (const auto& payload : frame.face_pixels) {
if (payload.face_index != face_index) {
continue;
}
const auto status = composite_face_payload(face.pixels, extent, payload, export_layer);
if (!status.ok()) {
return pp::foundation::Result<DocumentFrameCompositeResult>::failure(status);
}
composited_face = true;
++face.face_payload_count;
++result.face_payload_count;
}
if (composited_face) {
face.composited_layer_count = 1U;
++result.composited_layer_face_count;
}
}
result.faces[face_index] = std::move(face);
}
return pp::foundation::Result<DocumentFrameCompositeResult>::success(std::move(result));
}
pp::foundation::Result<DocumentFrameUploadResult> upload_document_frame_faces(
pp::renderer::IRenderDevice& device,
DocumentFrameUploadRequest request)
@@ -612,6 +683,91 @@ export_document_frame_equirectangular_png(DocumentFrameCompositeRequest request)
return export_document_frame_equirectangular_png(composite.value());
}
pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request)
{
if (request.document == nullptr) {
return pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>::failure(
pp::foundation::Status::invalid_argument("document layer export request requires a document"));
}
if (request.frame_index >= request.document->frames().size()) {
return pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>::failure(
pp::foundation::Status::out_of_range("document layer export frame index is outside the document"));
}
DocumentLayerEquirectangularPngExportResult result;
result.layers.reserve(request.document->layers().size());
for (std::size_t layer_index = 0; layer_index < request.document->layers().size(); ++layer_index) {
auto composite = composite_document_layer_frame(
*request.document,
layer_index,
request.frame_index,
request.clear_color);
if (!composite) {
return pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>::failure(composite.status());
}
auto exported = export_document_frame_equirectangular_png(composite.value());
if (!exported) {
return pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>::failure(exported.status());
}
DocumentLayerEquirectangularPng layer;
layer.layer_index = layer_index;
layer.layer_name = request.document->layers()[layer_index].name;
layer.face_extent = exported.value().face_extent;
layer.equirectangular_extent = exported.value().equirectangular_extent;
layer.encoded_bytes = exported.value().encoded_bytes;
layer.face_payload_count = exported.value().face_payload_count;
layer.composited_layer_face_count = exported.value().composited_layer_face_count;
layer.png = std::move(exported.value().png);
result.encoded_bytes += layer.encoded_bytes;
result.layers.push_back(std::move(layer));
}
result.layer_count = result.layers.size();
return pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>::success(std::move(result));
}
pp::foundation::Result<DocumentAnimationFrameEquirectangularPngExportResult>
export_document_animation_frames_equirectangular_pngs(
DocumentAnimationFrameEquirectangularPngExportRequest request)
{
if (request.document == nullptr) {
return pp::foundation::Result<DocumentAnimationFrameEquirectangularPngExportResult>::failure(
pp::foundation::Status::invalid_argument("document animation-frame export request requires a document"));
}
DocumentAnimationFrameEquirectangularPngExportResult result;
result.frames.reserve(request.document->frames().size());
for (std::size_t frame_index = 0; frame_index < request.document->frames().size(); ++frame_index) {
auto exported = export_document_frame_equirectangular_png(DocumentFrameCompositeRequest {
.document = request.document,
.frame_index = frame_index,
.clear_color = request.clear_color,
});
if (!exported) {
return pp::foundation::Result<DocumentAnimationFrameEquirectangularPngExportResult>::failure(
exported.status());
}
DocumentAnimationFrameEquirectangularPng frame;
frame.frame_index = frame_index;
frame.face_extent = exported.value().face_extent;
frame.equirectangular_extent = exported.value().equirectangular_extent;
frame.encoded_bytes = exported.value().encoded_bytes;
frame.face_payload_count = exported.value().face_payload_count;
frame.composited_layer_face_count = exported.value().composited_layer_face_count;
frame.png = std::move(exported.value().png);
result.encoded_bytes += frame.encoded_bytes;
result.frames.push_back(std::move(frame));
}
result.frame_count = result.frames.size();
return pp::foundation::Result<DocumentAnimationFrameEquirectangularPngExportResult>::success(std::move(result));
}
pp::foundation::Result<DocumentFrameExportReadinessResult> prepare_document_frame_export_readiness(
DocumentFrameCompositeRequest request)
{

View File

@@ -10,6 +10,7 @@
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <vector>
namespace pp::paint_renderer {
@@ -155,6 +156,50 @@ struct DocumentFrameEquirectangularPngExportResult {
std::size_t composited_layer_face_count = 0;
};
struct DocumentLayerEquirectangularPngExportRequest {
const pp::document::CanvasDocument* document = nullptr;
std::size_t frame_index = 0;
pp::paint::Rgba clear_color {};
};
struct DocumentLayerEquirectangularPng {
std::size_t layer_index = 0;
std::string layer_name;
pp::renderer::Extent2D face_extent {};
pp::renderer::Extent2D equirectangular_extent {};
std::vector<std::byte> png;
std::uint64_t encoded_bytes = 0;
std::size_t face_payload_count = 0;
std::size_t composited_layer_face_count = 0;
};
struct DocumentLayerEquirectangularPngExportResult {
std::vector<DocumentLayerEquirectangularPng> layers;
std::uint64_t encoded_bytes = 0;
std::size_t layer_count = 0;
};
struct DocumentAnimationFrameEquirectangularPngExportRequest {
const pp::document::CanvasDocument* document = nullptr;
pp::paint::Rgba clear_color {};
};
struct DocumentAnimationFrameEquirectangularPng {
std::size_t frame_index = 0;
pp::renderer::Extent2D face_extent {};
pp::renderer::Extent2D equirectangular_extent {};
std::vector<std::byte> png;
std::uint64_t encoded_bytes = 0;
std::size_t face_payload_count = 0;
std::size_t composited_layer_face_count = 0;
};
struct DocumentAnimationFrameEquirectangularPngExportResult {
std::vector<DocumentAnimationFrameEquirectangularPng> frames;
std::uint64_t encoded_bytes = 0;
std::size_t frame_count = 0;
};
struct DocumentFrameExportReadinessResult {
RecordedDocumentFrameUploadResult recorded_upload {};
DocumentFrameFacePngExportResult face_pngs {};
@@ -187,6 +232,13 @@ export_document_frame_equirectangular_png(const DocumentFrameCompositeResult& co
[[nodiscard]] pp::foundation::Result<DocumentFrameEquirectangularPngExportResult>
export_document_frame_equirectangular_png(DocumentFrameCompositeRequest request);
[[nodiscard]] pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request);
[[nodiscard]] pp::foundation::Result<DocumentAnimationFrameEquirectangularPngExportResult>
export_document_animation_frames_equirectangular_pngs(
DocumentAnimationFrameEquirectangularPngExportRequest request);
[[nodiscard]] pp::foundation::Result<DocumentFrameExportReadinessResult> prepare_document_frame_export_readiness(
DocumentFrameCompositeRequest request);