Add document face compositor bridge
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "paint_renderer/compositor.h"
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
namespace pp::paint_renderer {
|
||||
|
||||
@@ -93,6 +94,52 @@ namespace {
|
||||
return pp::foundation::Result<std::size_t>::success(static_cast<std::size_t>(count));
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::paint::Rgba rgba8_pixel(std::span<const std::uint8_t> bytes) noexcept
|
||||
{
|
||||
constexpr auto inv = 1.0F / 255.0F;
|
||||
return pp::paint::Rgba {
|
||||
.r = static_cast<float>(bytes[0]) * inv,
|
||||
.g = static_cast<float>(bytes[1]) * inv,
|
||||
.b = static_cast<float>(bytes[2]) * inv,
|
||||
.a = static_cast<float>(bytes[3]) * inv,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Status composite_face_payload(
|
||||
std::span<pp::paint::Rgba> destination,
|
||||
pp::renderer::Extent2D extent,
|
||||
const pp::document::LayerFacePixels& payload,
|
||||
const pp::document::Layer& layer) noexcept
|
||||
{
|
||||
if (payload.x > extent.width || payload.width > extent.width - payload.x
|
||||
|| payload.y > extent.height || payload.height > extent.height - payload.y) {
|
||||
return pp::foundation::Status::out_of_range("document face payload rectangle is outside the render extent");
|
||||
}
|
||||
|
||||
const auto payload_pixel_count = static_cast<std::uint64_t>(payload.width)
|
||||
* static_cast<std::uint64_t>(payload.height);
|
||||
if (payload_pixel_count > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max() / 4U)
|
||||
|| payload.rgba8.size() != static_cast<std::size_t>(payload_pixel_count) * 4U) {
|
||||
return pp::foundation::Status::invalid_argument("document face payload byte size does not match dimensions");
|
||||
}
|
||||
|
||||
for (std::uint32_t y = 0; y < payload.height; ++y) {
|
||||
for (std::uint32_t x = 0; x < payload.width; ++x) {
|
||||
const auto payload_index = (static_cast<std::size_t>(y) * payload.width + x) * 4U;
|
||||
const auto destination_index = static_cast<std::size_t>(payload.y + y) * extent.width
|
||||
+ static_cast<std::size_t>(payload.x + x);
|
||||
auto stroke = rgba8_pixel(std::span<const std::uint8_t>(&payload.rgba8[payload_index], 4U));
|
||||
stroke.a *= layer.opacity;
|
||||
destination[destination_index] = pp::paint::blend_pixels(
|
||||
destination[destination_index],
|
||||
stroke,
|
||||
layer.blend_mode);
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] StrokeCompositePath composite_path_from_feedback(pp::renderer::PaintFeedbackPath path) noexcept
|
||||
{
|
||||
switch (path) {
|
||||
@@ -176,6 +223,71 @@ pp::foundation::Status composite_layer(
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
pp::foundation::Result<DocumentFaceCompositeResult> composite_document_face(
|
||||
DocumentFaceCompositeRequest request)
|
||||
{
|
||||
if (request.document == nullptr) {
|
||||
return pp::foundation::Result<DocumentFaceCompositeResult>::failure(
|
||||
pp::foundation::Status::invalid_argument("document composite request requires a document"));
|
||||
}
|
||||
|
||||
if (request.face_index >= pp::document::cube_face_count) {
|
||||
return pp::foundation::Result<DocumentFaceCompositeResult>::failure(
|
||||
pp::foundation::Status::out_of_range("document composite face index is outside the cube"));
|
||||
}
|
||||
|
||||
if (request.frame_index >= request.document->frames().size()) {
|
||||
return pp::foundation::Result<DocumentFaceCompositeResult>::failure(
|
||||
pp::foundation::Status::out_of_range("document composite frame index is outside the document"));
|
||||
}
|
||||
|
||||
const pp::renderer::Extent2D extent {
|
||||
.width = request.document->width(),
|
||||
.height = request.document->height(),
|
||||
};
|
||||
const auto pixel_count = expected_pixel_count(extent);
|
||||
if (!pixel_count) {
|
||||
return pp::foundation::Result<DocumentFaceCompositeResult>::failure(pixel_count.status());
|
||||
}
|
||||
|
||||
DocumentFaceCompositeResult result;
|
||||
result.extent = extent;
|
||||
result.pixels.assign(pixel_count.value(), request.clear_color);
|
||||
result.visited_layer_count = request.document->layers().size();
|
||||
|
||||
for (const auto& layer : request.document->layers()) {
|
||||
if (!layer.visible || layer.opacity == 0.0F || request.frame_index >= layer.frames.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool composited_layer = false;
|
||||
const auto& frame = layer.frames[request.frame_index];
|
||||
for (const auto& payload : frame.face_pixels) {
|
||||
if (payload.face_index != request.face_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto status = composite_face_payload(
|
||||
result.pixels,
|
||||
extent,
|
||||
payload,
|
||||
layer);
|
||||
if (!status.ok()) {
|
||||
return pp::foundation::Result<DocumentFaceCompositeResult>::failure(status);
|
||||
}
|
||||
|
||||
composited_layer = true;
|
||||
++result.face_payload_count;
|
||||
}
|
||||
|
||||
if (composited_layer) {
|
||||
++result.composited_layer_count;
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Result<DocumentFaceCompositeResult>::success(std::move(result));
|
||||
}
|
||||
|
||||
bool stroke_composite_requires_feedback(
|
||||
pp::paint::BlendMode layer_blend_mode,
|
||||
pp::paint::StrokeBlendMode stroke_blend_mode,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "document/document.h"
|
||||
#include "foundation/result.h"
|
||||
#include "paint/blend.h"
|
||||
#include "renderer_api/renderer_api.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace pp::paint_renderer {
|
||||
|
||||
@@ -83,11 +86,29 @@ struct CanvasStrokeFeedbackPlan {
|
||||
bool compatibility_fallback = false;
|
||||
};
|
||||
|
||||
struct DocumentFaceCompositeRequest {
|
||||
const pp::document::CanvasDocument* document = nullptr;
|
||||
std::size_t frame_index = 0;
|
||||
std::uint32_t face_index = 0;
|
||||
pp::paint::Rgba clear_color {};
|
||||
};
|
||||
|
||||
struct DocumentFaceCompositeResult {
|
||||
pp::renderer::Extent2D extent {};
|
||||
std::vector<pp::paint::Rgba> pixels;
|
||||
std::size_t visited_layer_count = 0;
|
||||
std::size_t composited_layer_count = 0;
|
||||
std::size_t face_payload_count = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] pp::foundation::Status composite_layer(
|
||||
std::span<pp::paint::Rgba> destination,
|
||||
pp::renderer::Extent2D extent,
|
||||
LayerCompositeView layer) noexcept;
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<DocumentFaceCompositeResult> composite_document_face(
|
||||
DocumentFaceCompositeRequest request);
|
||||
|
||||
[[nodiscard]] bool stroke_composite_requires_feedback(
|
||||
pp::paint::BlendMode layer_blend_mode,
|
||||
pp::paint::StrokeBlendMode stroke_blend_mode,
|
||||
|
||||
Reference in New Issue
Block a user