diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index b4b49d9..a897171 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -523,6 +523,11 @@ Progress Notes: `Canvas`. Next slice should target another narrow draw-merge execution seam without reopening landed temporary-composite, layer-blend, final-plane, or texture-alpha helpers. +- 2026-06-13: `pp_paint_renderer_stroke_execution_tests` now also covers + retained frame-plan assembly for previous-sample projection mode and zoom + scaling. Next slice should target the remaining preview/Canvas stroke + execution seam or another narrow renderer boundary without reopening the + landed stroke-frame planner coverage. - 2026-06-13: `NodeStrokePreview::draw_stroke_immediate()` now routes retained preview feedback/material/composite planning plus stroke-shader uniform assembly through `plan_legacy_node_stroke_preview_pass_orchestration(...)`; diff --git a/tests/paint_renderer/stroke_execution_tests.cpp b/tests/paint_renderer/stroke_execution_tests.cpp index ffef910..39f841b 100644 --- a/tests/paint_renderer/stroke_execution_tests.cpp +++ b/tests/paint_renderer/stroke_execution_tests.cpp @@ -607,6 +607,14 @@ void retained_stroke_frame_samples_with_dirty_tracking_updates_after_finish(pp:: void retained_stroke_frame_planner_uses_previous_sample_and_projection_mode(pp::tests::Harness& h) { + struct FramePlan { + glm::vec4 col; + float flow; + float opacity; + std::array shapes; + glm::vec4 m_mixer_rect; + }; + const auto frames = pp::panopainter::plan_legacy_canvas_stroke_frames( pp::panopainter::LegacyCanvasStrokeComputeRequest { .previous_sample = StrokeSample { @@ -655,7 +663,7 @@ void retained_stroke_frame_planner_uses_previous_sample_and_projection_mode(pp:: return std::array { brush_quad }; }, [](glm::vec4 mixer_rect, glm::vec4 color, float flow, float opacity, auto&& shapes) { - return StrokeFrame { + return FramePlan { .col = color, .flow = flow, .opacity = opacity, @@ -681,6 +689,67 @@ void retained_stroke_frame_planner_uses_previous_sample_and_projection_mode(pp:: PP_EXPECT(h, nearly_equal(frames[1].opacity, 0.9F)); } +void retained_stroke_frame_planner_scales_mixer_bounds_with_zoom(pp::tests::Harness& h) +{ + struct FramePlan { + glm::vec4 col; + float flow; + float opacity; + std::array shapes; + glm::vec4 m_mixer_rect; + }; + + const auto frames = pp::panopainter::plan_legacy_canvas_stroke_frames( + pp::panopainter::LegacyCanvasStrokeComputeRequest { + .previous_sample = StrokeSample { + .col = { 0.0F, 0.0F, 1.0F }, + .pos = { 5.0F, 6.0F, 0.0F }, + .origin = { 0.0F, 0.0F, 0.0F }, + .scale = { 1.0F, 1.0F }, + .size = 3.0F, + .flow = 1.0F, + .opacity = 1.0F, + .angle = 0.0F, + }, + .samples = std::array { + StrokeSample { + .col = { 1.0F, 1.0F, 0.0F }, + .pos = { 9.0F, 10.0F, 0.0F }, + .origin = { 0.0F, 0.0F, 0.0F }, + .scale = { 1.0F, 1.0F }, + .size = 4.0F, + .flow = 0.25F, + .opacity = 0.5F, + .angle = 0.0F, + }, + }, + .zoom = 2.0F, + .mixer_size = glm::vec2(64.0F, 64.0F), + .model_view = glm::mat4(1.0F), + }, + [&](std::array& brush_quad, bool, glm::mat4) { + return std::array { brush_quad }; + }, + [](glm::vec4 mixer_rect, glm::vec4 color, float flow, float opacity, auto&& shapes) { + return FramePlan { + .col = color, + .flow = flow, + .opacity = opacity, + .shapes = std::move(shapes), + .m_mixer_rect = mixer_rect, + }; + }); + + PP_EXPECT(h, frames.size() == 1U); + PP_EXPECT(h, nearly_equal(frames[0].m_mixer_rect.x, 2.0F)); + PP_EXPECT(h, nearly_equal(frames[0].m_mixer_rect.y, 3.0F)); + PP_EXPECT(h, nearly_equal(frames[0].m_mixer_rect.z, 8.0F)); + PP_EXPECT(h, nearly_equal(frames[0].m_mixer_rect.w, 8.0F)); + PP_EXPECT(h, nearly_equal(frames[0].col.b, 0.0F)); + PP_EXPECT(h, nearly_equal(frames[0].flow, 0.25F)); + PP_EXPECT(h, nearly_equal(frames[0].opacity, 0.5F)); +} + void retained_stroke_live_pass_with_face_framebuffers_preserves_order_and_dirty_tracking(pp::tests::Harness& h) { StrokeFrame frame; @@ -1040,6 +1109,9 @@ int main() harness.run( "retained_stroke_frame_planner_uses_previous_sample_and_projection_mode", retained_stroke_frame_planner_uses_previous_sample_and_projection_mode); + harness.run( + "retained_stroke_frame_planner_scales_mixer_bounds_with_zoom", + retained_stroke_frame_planner_scales_mixer_bounds_with_zoom); harness.run( "retained_stroke_live_pass_with_face_framebuffers_preserves_order_and_dirty_tracking", retained_stroke_live_pass_with_face_framebuffers_preserves_order_and_dirty_tracking);