Extract stroke dirty bounds planning
This commit is contained in:
@@ -82,6 +82,34 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] CanvasStrokeBox box_union(CanvasStrokeBox lhs, CanvasStrokeBox rhs) noexcept
|
||||
{
|
||||
return CanvasStrokeBox {
|
||||
.min_x = std::min(lhs.min_x, rhs.min_x),
|
||||
.min_y = std::min(lhs.min_y, rhs.min_y),
|
||||
.max_x = std::max(lhs.max_x, rhs.max_x),
|
||||
.max_y = std::max(lhs.max_y, rhs.max_y),
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] CanvasStrokeBox clamp_box_to_legacy_dirty_extent(
|
||||
CanvasStrokeBox box,
|
||||
pp::renderer::Extent2D extent) noexcept
|
||||
{
|
||||
const auto max_value = static_cast<float>(extent.width);
|
||||
return CanvasStrokeBox {
|
||||
.min_x = std::clamp(box.min_x, 0.0F, max_value),
|
||||
.min_y = std::clamp(box.min_y, 0.0F, max_value),
|
||||
.max_x = std::clamp(box.max_x, 0.0F, max_value),
|
||||
.max_y = std::clamp(box.max_y, 0.0F, max_value),
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] bool has_positive_area(CanvasStrokeBox box) noexcept
|
||||
{
|
||||
return box.max_x > box.min_x && box.max_y > box.min_y;
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<std::size_t> expected_pixel_count(pp::renderer::Extent2D extent) noexcept
|
||||
{
|
||||
const auto extent_status = pp::renderer::validate_extent(extent);
|
||||
@@ -1371,6 +1399,117 @@ pp::foundation::Result<CanvasStrokeRasterizationPlan> plan_canvas_stroke_rasteri
|
||||
return pp::foundation::Result<CanvasStrokeRasterizationPlan>::success(plan);
|
||||
}
|
||||
|
||||
CanvasStrokeSampleBoundsPlan plan_canvas_stroke_sample_bounds(
|
||||
CanvasStrokeSampleBoundsRequest request) noexcept
|
||||
{
|
||||
CanvasStrokeSampleBoundsPlan plan;
|
||||
if (request.extent.width == 0U || request.extent.height == 0U || request.vertices.empty()) {
|
||||
return plan;
|
||||
}
|
||||
|
||||
const auto target_width = static_cast<float>(request.extent.width);
|
||||
const auto target_height = static_cast<float>(request.extent.height);
|
||||
auto min_x = target_width;
|
||||
auto min_y = target_height;
|
||||
auto max_x = 0.0F;
|
||||
auto max_y = 0.0F;
|
||||
for (const auto& vertex : request.vertices) {
|
||||
min_x = std::max(0.0F, std::min(min_x, vertex.x));
|
||||
min_y = std::max(0.0F, std::min(min_y, vertex.y));
|
||||
max_x = std::min(target_width, std::max(max_x, vertex.x));
|
||||
max_y = std::min(target_height, std::max(max_y, vertex.y));
|
||||
}
|
||||
|
||||
const auto pad = std::max(0.0F, request.sample_padding_pixels);
|
||||
const auto copy_x = static_cast<int>(std::clamp(std::floor(min_x) - pad, 0.0F, target_width));
|
||||
const auto copy_y = static_cast<int>(std::clamp(std::floor(min_y) - pad, 0.0F, target_height));
|
||||
const auto max_width = static_cast<int>(request.extent.width) - copy_x;
|
||||
const auto max_height = static_cast<int>(request.extent.height) - copy_y;
|
||||
const auto copy_width = static_cast<int>(std::clamp(
|
||||
std::ceil(max_x - min_x) + pad * 2.0F,
|
||||
0.0F,
|
||||
static_cast<float>(max_width)));
|
||||
const auto copy_height = static_cast<int>(std::clamp(
|
||||
std::ceil(max_y - min_y) + pad * 2.0F,
|
||||
0.0F,
|
||||
static_cast<float>(max_height)));
|
||||
|
||||
plan.copy_region = CanvasStrokeCopyRegion {
|
||||
.x = copy_x,
|
||||
.y = copy_y,
|
||||
.width = copy_width,
|
||||
.height = copy_height,
|
||||
};
|
||||
plan.dirty_bounds = CanvasStrokeBox {
|
||||
.min_x = static_cast<float>(copy_x),
|
||||
.min_y = static_cast<float>(copy_y),
|
||||
.max_x = static_cast<float>(copy_x + copy_width),
|
||||
.max_y = static_cast<float>(copy_y + copy_height),
|
||||
};
|
||||
plan.has_pixels = copy_width > 0 && copy_height > 0;
|
||||
return plan;
|
||||
}
|
||||
|
||||
CanvasStrokeFaceDirtyUpdatePlan plan_canvas_stroke_face_dirty_update(
|
||||
CanvasStrokeFaceDirtyUpdateRequest request) noexcept
|
||||
{
|
||||
CanvasStrokeFaceDirtyUpdatePlan plan;
|
||||
plan.accumulated_dirty_box = request.previous_accumulated_dirty_box;
|
||||
plan.pass_dirty_box = box_union(request.previous_pass_dirty_box, request.sample_dirty_box);
|
||||
plan.has_dirty_pixels = has_positive_area(request.sample_dirty_box);
|
||||
plan.pass_dirty = plan.has_dirty_pixels;
|
||||
if (request.include_in_committed_dirty_box && plan.has_dirty_pixels) {
|
||||
plan.accumulated_dirty_box = clamp_box_to_legacy_dirty_extent(
|
||||
box_union(request.previous_accumulated_dirty_box, request.sample_dirty_box),
|
||||
request.extent);
|
||||
plan.committed_dirty = true;
|
||||
}
|
||||
return plan;
|
||||
}
|
||||
|
||||
CanvasStrokePadRegionPlan plan_canvas_stroke_pad_region(
|
||||
CanvasStrokePadRegionRequest request) noexcept
|
||||
{
|
||||
CanvasStrokePadRegionPlan plan;
|
||||
if (request.extent.width == 0U || request.extent.height == 0U) {
|
||||
return plan;
|
||||
}
|
||||
|
||||
const auto pad = std::max(0.0F, request.pad_pixels);
|
||||
const auto width = static_cast<float>(request.extent.width);
|
||||
const auto height = static_cast<float>(request.extent.height);
|
||||
const auto origin_x = std::max(0.0F, request.pass_dirty_box.min_x - pad);
|
||||
const auto origin_y = std::max(0.0F, request.pass_dirty_box.min_y - pad);
|
||||
const auto max_x = std::min(width, request.pass_dirty_box.max_x + pad);
|
||||
const auto max_y = std::min(height, request.pass_dirty_box.max_y + pad);
|
||||
const auto copy_width = max_x - origin_x;
|
||||
const auto copy_height = max_y - origin_y;
|
||||
if (copy_width <= 0.0F || copy_height <= 0.0F) {
|
||||
return plan;
|
||||
}
|
||||
|
||||
const auto left = origin_x * 2.0F / width - 1.0F;
|
||||
const auto bottom = origin_y * 2.0F / height - 1.0F;
|
||||
const auto right = max_x * 2.0F / width - 1.0F;
|
||||
const auto top = max_y * 2.0F / height - 1.0F;
|
||||
plan.copy_region = CanvasStrokeCopyRegion {
|
||||
.x = static_cast<int>(origin_x),
|
||||
.y = static_cast<int>(origin_y),
|
||||
.width = static_cast<int>(copy_width),
|
||||
.height = static_cast<int>(copy_height),
|
||||
};
|
||||
plan.ndc_quad = {
|
||||
CanvasStrokePoint { .x = left, .y = bottom },
|
||||
CanvasStrokePoint { .x = left, .y = top },
|
||||
CanvasStrokePoint { .x = right, .y = top },
|
||||
CanvasStrokePoint { .x = left, .y = bottom },
|
||||
CanvasStrokePoint { .x = right, .y = top },
|
||||
CanvasStrokePoint { .x = right, .y = bottom },
|
||||
};
|
||||
plan.has_pixels = true;
|
||||
return plan;
|
||||
}
|
||||
|
||||
const char* stroke_composite_path_name(StrokeCompositePath path) noexcept
|
||||
{
|
||||
switch (path) {
|
||||
|
||||
@@ -149,6 +149,65 @@ struct CanvasStrokeRasterizationPlan {
|
||||
bool compatibility_fallback = false;
|
||||
};
|
||||
|
||||
struct CanvasStrokePoint {
|
||||
float x = 0.0F;
|
||||
float y = 0.0F;
|
||||
};
|
||||
|
||||
struct CanvasStrokeBox {
|
||||
float min_x = 0.0F;
|
||||
float min_y = 0.0F;
|
||||
float max_x = 0.0F;
|
||||
float max_y = 0.0F;
|
||||
};
|
||||
|
||||
struct CanvasStrokeCopyRegion {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
struct CanvasStrokeSampleBoundsRequest {
|
||||
pp::renderer::Extent2D extent {};
|
||||
std::span<const CanvasStrokePoint> vertices;
|
||||
float sample_padding_pixels = 1.0F;
|
||||
};
|
||||
|
||||
struct CanvasStrokeSampleBoundsPlan {
|
||||
CanvasStrokeCopyRegion copy_region {};
|
||||
CanvasStrokeBox dirty_bounds {};
|
||||
bool has_pixels = false;
|
||||
};
|
||||
|
||||
struct CanvasStrokeFaceDirtyUpdateRequest {
|
||||
pp::renderer::Extent2D extent {};
|
||||
CanvasStrokeBox previous_accumulated_dirty_box {};
|
||||
CanvasStrokeBox previous_pass_dirty_box {};
|
||||
CanvasStrokeBox sample_dirty_box {};
|
||||
bool include_in_committed_dirty_box = true;
|
||||
};
|
||||
|
||||
struct CanvasStrokeFaceDirtyUpdatePlan {
|
||||
CanvasStrokeBox accumulated_dirty_box {};
|
||||
CanvasStrokeBox pass_dirty_box {};
|
||||
bool has_dirty_pixels = false;
|
||||
bool committed_dirty = false;
|
||||
bool pass_dirty = false;
|
||||
};
|
||||
|
||||
struct CanvasStrokePadRegionRequest {
|
||||
pp::renderer::Extent2D extent {};
|
||||
CanvasStrokeBox pass_dirty_box {};
|
||||
float pad_pixels = 20.0F;
|
||||
};
|
||||
|
||||
struct CanvasStrokePadRegionPlan {
|
||||
CanvasStrokeCopyRegion copy_region {};
|
||||
std::array<CanvasStrokePoint, 6> ndc_quad {};
|
||||
bool has_pixels = false;
|
||||
};
|
||||
|
||||
struct DocumentFaceCompositeRequest {
|
||||
const pp::document::CanvasDocument* document = nullptr;
|
||||
std::size_t frame_index = 0;
|
||||
@@ -393,6 +452,15 @@ export_document_animation_frames_equirectangular_pngs(
|
||||
pp::renderer::RenderDeviceFeatures features,
|
||||
pp::renderer::Extent2D extent) noexcept;
|
||||
|
||||
[[nodiscard]] CanvasStrokeSampleBoundsPlan plan_canvas_stroke_sample_bounds(
|
||||
CanvasStrokeSampleBoundsRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] CanvasStrokeFaceDirtyUpdatePlan plan_canvas_stroke_face_dirty_update(
|
||||
CanvasStrokeFaceDirtyUpdateRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] CanvasStrokePadRegionPlan plan_canvas_stroke_pad_region(
|
||||
CanvasStrokePadRegionRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] const char* stroke_composite_path_name(StrokeCompositePath path) noexcept;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user