Integrate dialog export and Apple service teams
This commit is contained in:
@@ -10,6 +10,12 @@
|
||||
|
||||
namespace pp::paint_renderer {
|
||||
|
||||
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);
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool is_valid_blend_mode(pp::paint::BlendMode mode) noexcept
|
||||
@@ -122,6 +128,8 @@ struct CubeFaceSample {
|
||||
float t = 0.0F;
|
||||
};
|
||||
|
||||
constexpr float document_depth_export_default_fov_degrees = 85.0F;
|
||||
|
||||
[[nodiscard]] CubeFaceSample panopainter_cube_face_sample(float x, float y, float z) noexcept
|
||||
{
|
||||
const auto ax = std::fabs(x);
|
||||
@@ -279,6 +287,56 @@ struct EquirectangularProjectionResult {
|
||||
std::size_t composited_layer_face_count = 0;
|
||||
};
|
||||
|
||||
struct PerspectiveProjectionMap {
|
||||
pp::renderer::Extent2D output_extent {};
|
||||
std::vector<CubeFaceSample> samples;
|
||||
};
|
||||
|
||||
pp::foundation::Result<PerspectiveProjectionMap> make_perspective_projection_map(
|
||||
pp::renderer::Extent2D output_extent,
|
||||
float vertical_fov_degrees)
|
||||
{
|
||||
if (!std::isfinite(vertical_fov_degrees)) {
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::failure(
|
||||
pp::foundation::Status::invalid_argument("document depth export field of view must be finite"));
|
||||
}
|
||||
if (vertical_fov_degrees <= 0.0F || vertical_fov_degrees >= 180.0F) {
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::failure(
|
||||
pp::foundation::Status::out_of_range("document depth export field of view must be between 0 and 180"));
|
||||
}
|
||||
|
||||
const auto output_pixel_count = expected_pixel_count(output_extent);
|
||||
if (!output_pixel_count) {
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::failure(output_pixel_count.status());
|
||||
}
|
||||
|
||||
PerspectiveProjectionMap map;
|
||||
map.output_extent = output_extent;
|
||||
map.samples.reserve(output_pixel_count.value());
|
||||
|
||||
constexpr auto pi = 3.14159265358979323846F;
|
||||
const auto tan_half_fov = std::tan(vertical_fov_degrees * pi / 360.0F);
|
||||
const auto aspect = static_cast<float>(output_extent.width) / static_cast<float>(output_extent.height);
|
||||
for (std::uint32_t y = 0; y < output_extent.height; ++y) {
|
||||
const auto ny = 1.0F
|
||||
- ((static_cast<float>(y) + 0.5F) / static_cast<float>(output_extent.height)) * 2.0F;
|
||||
for (std::uint32_t x = 0; x < output_extent.width; ++x) {
|
||||
const auto nx = ((static_cast<float>(x) + 0.5F) / static_cast<float>(output_extent.width)) * 2.0F
|
||||
- 1.0F;
|
||||
const auto dir_x = nx * aspect * tan_half_fov;
|
||||
const auto dir_y = ny * tan_half_fov;
|
||||
constexpr auto dir_z = -1.0F;
|
||||
const auto inv_length = 1.0F / std::sqrt(dir_x * dir_x + dir_y * dir_y + dir_z * dir_z);
|
||||
map.samples.push_back(panopainter_cube_face_sample(
|
||||
dir_x * inv_length,
|
||||
dir_y * inv_length,
|
||||
dir_z * inv_length));
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::success(std::move(map));
|
||||
}
|
||||
|
||||
pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_equirectangular(
|
||||
const DocumentFrameCompositeResult& composite)
|
||||
{
|
||||
@@ -340,6 +398,87 @@ pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_e
|
||||
return pp::foundation::Result<EquirectangularProjectionResult>::success(std::move(result));
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::vector<pp::paint::Rgba>> project_document_frame_perspective(
|
||||
const DocumentFrameCompositeResult& composite,
|
||||
const PerspectiveProjectionMap& projection)
|
||||
{
|
||||
const auto face_pixel_count = expected_pixel_count(composite.extent);
|
||||
if (!face_pixel_count) {
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(face_pixel_count.status());
|
||||
}
|
||||
|
||||
for (const auto& face : composite.faces) {
|
||||
if (face.extent.width != composite.extent.width || face.extent.height != composite.extent.height
|
||||
|| face.pixels.size() != face_pixel_count.value()) {
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(
|
||||
pp::foundation::Status::invalid_argument("document depth export requires complete cube faces"));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<pp::paint::Rgba> pixels;
|
||||
pixels.reserve(projection.samples.size());
|
||||
for (const auto& sample : projection.samples) {
|
||||
pixels.push_back(sample_face_nearest(composite.faces[sample.face_index], sample.s, sample.t));
|
||||
}
|
||||
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::success(std::move(pixels));
|
||||
}
|
||||
|
||||
float sample_face_alpha_nearest(
|
||||
const DocumentFaceCompositeResult& face,
|
||||
float s,
|
||||
float t) noexcept
|
||||
{
|
||||
return sample_face_nearest(face, s, t).a;
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::vector<pp::paint::Rgba>> project_document_depth_perspective(
|
||||
const pp::document::CanvasDocument& document,
|
||||
std::size_t frame_index,
|
||||
const PerspectiveProjectionMap& projection)
|
||||
{
|
||||
std::vector<pp::paint::Rgba> pixels(
|
||||
projection.samples.size(),
|
||||
pp::paint::Rgba {
|
||||
.r = 0.0F,
|
||||
.g = 0.0F,
|
||||
.b = 0.0F,
|
||||
.a = 1.0F,
|
||||
});
|
||||
const auto layer_count = document.layers().size();
|
||||
for (std::size_t layer_index = 0; layer_index < layer_count; ++layer_index) {
|
||||
const auto& layer = document.layers()[layer_index];
|
||||
if (!layer.visible || layer.opacity == 0.0F || frame_index >= layer.frames.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto composite = composite_document_layer_frame(document, layer_index, frame_index, {});
|
||||
if (!composite) {
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(composite.status());
|
||||
}
|
||||
|
||||
const auto gray = static_cast<float>(layer_index + 1U) / static_cast<float>(layer_count + 1U);
|
||||
const pp::paint::Rgba layer_color {
|
||||
.r = gray,
|
||||
.g = gray,
|
||||
.b = gray,
|
||||
.a = 1.0F,
|
||||
};
|
||||
for (std::size_t pixel_index = 0; pixel_index < projection.samples.size(); ++pixel_index) {
|
||||
const auto& sample = projection.samples[pixel_index];
|
||||
const auto alpha = sample_face_alpha_nearest(
|
||||
composite.value().faces[sample.face_index],
|
||||
sample.s,
|
||||
sample.t);
|
||||
if (alpha > 0.01F) {
|
||||
pixels[pixel_index] = layer_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::success(std::move(pixels));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pp::foundation::Status composite_layer(
|
||||
@@ -806,6 +945,79 @@ pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export
|
||||
return pp::foundation::Result<DocumentDepthExportRenderPlan>::success(plan);
|
||||
}
|
||||
|
||||
pp::foundation::Result<DocumentDepthPngExportResult> export_document_depth_pngs(
|
||||
DocumentDepthExportRenderPlanRequest request)
|
||||
{
|
||||
auto plan = plan_document_depth_export_render(request);
|
||||
if (!plan) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(plan.status());
|
||||
}
|
||||
|
||||
auto projection = make_perspective_projection_map(
|
||||
plan.value().output_extent,
|
||||
document_depth_export_default_fov_degrees);
|
||||
if (!projection) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(projection.status());
|
||||
}
|
||||
|
||||
auto image_composite = composite_document_frame(DocumentFrameCompositeRequest {
|
||||
.document = request.document,
|
||||
.frame_index = request.frame_index,
|
||||
.clear_color = pp::paint::Rgba {
|
||||
.r = 0.0F,
|
||||
.g = 0.0F,
|
||||
.b = 0.0F,
|
||||
.a = 1.0F,
|
||||
},
|
||||
});
|
||||
if (!image_composite) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_composite.status());
|
||||
}
|
||||
|
||||
auto image_pixels = project_document_frame_perspective(image_composite.value(), projection.value());
|
||||
if (!image_pixels) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_pixels.status());
|
||||
}
|
||||
|
||||
auto depth_pixels = project_document_depth_perspective(*request.document, request.frame_index, projection.value());
|
||||
if (!depth_pixels) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(depth_pixels.status());
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> rgba8;
|
||||
append_rgba8_bytes(rgba8, image_pixels.value());
|
||||
auto image_png = pp::assets::encode_png_rgba8(
|
||||
plan.value().output_extent.width,
|
||||
plan.value().output_extent.height,
|
||||
rgba8);
|
||||
if (!image_png) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_png.status());
|
||||
}
|
||||
|
||||
append_rgba8_bytes(rgba8, depth_pixels.value());
|
||||
auto depth_png = pp::assets::encode_png_rgba8(
|
||||
plan.value().output_extent.width,
|
||||
plan.value().output_extent.height,
|
||||
rgba8);
|
||||
if (!depth_png) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(depth_png.status());
|
||||
}
|
||||
|
||||
DocumentDepthPngExportResult result;
|
||||
result.output_extent = plan.value().output_extent;
|
||||
result.image_encoded_bytes = static_cast<std::uint64_t>(image_png.value().size());
|
||||
result.depth_encoded_bytes = static_cast<std::uint64_t>(depth_png.value().size());
|
||||
result.merged_face_draw_count = plan.value().merged_face_draw_count;
|
||||
result.layer_depth_draw_count = plan.value().layer_depth_draw_count;
|
||||
result.visited_layer_count = plan.value().visited_layer_count;
|
||||
result.visible_layer_count = plan.value().visible_layer_count;
|
||||
result.face_payload_count = plan.value().face_payload_count;
|
||||
result.uses_perspective_camera = plan.value().uses_perspective_camera;
|
||||
result.image_png = std::move(image_png.value());
|
||||
result.depth_png = std::move(depth_png.value());
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::success(std::move(result));
|
||||
}
|
||||
|
||||
pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
||||
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request)
|
||||
{
|
||||
|
||||
@@ -189,6 +189,23 @@ struct DocumentDepthExportRenderPlan {
|
||||
bool requires_renderer_readback = true;
|
||||
};
|
||||
|
||||
struct DocumentDepthPngExportResult {
|
||||
pp::renderer::Extent2D output_extent {
|
||||
.width = 1024,
|
||||
.height = 1024,
|
||||
};
|
||||
std::vector<std::byte> image_png;
|
||||
std::vector<std::byte> depth_png;
|
||||
std::uint64_t image_encoded_bytes = 0;
|
||||
std::uint64_t depth_encoded_bytes = 0;
|
||||
std::size_t merged_face_draw_count = 0;
|
||||
std::size_t layer_depth_draw_count = 0;
|
||||
std::size_t visited_layer_count = 0;
|
||||
std::size_t visible_layer_count = 0;
|
||||
std::size_t face_payload_count = 0;
|
||||
bool uses_perspective_camera = true;
|
||||
};
|
||||
|
||||
struct DocumentLayerEquirectangularPngExportRequest {
|
||||
const pp::document::CanvasDocument* document = nullptr;
|
||||
std::size_t frame_index = 0;
|
||||
@@ -278,6 +295,9 @@ export_document_frame_equirectangular_jpeg(
|
||||
[[nodiscard]] pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export_render(
|
||||
DocumentDepthExportRenderPlanRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<DocumentDepthPngExportResult> export_document_depth_pngs(
|
||||
DocumentDepthExportRenderPlanRequest request);
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
||||
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user