Extract stroke commit history mutation helpers

This commit is contained in:
2026-06-13 23:58:59 +02:00
parent aa53a5f9ac
commit 4484880e32
3 changed files with 66 additions and 40 deletions

View File

@@ -182,6 +182,10 @@ agent or engineer to remove them without reconstructing context from chat.
routes the remaining service wiring through `make_canvas_stroke_commit_request(...)`; routes the remaining service wiring through `make_canvas_stroke_commit_request(...)`;
the retained path still owns the concrete history mutation and layer dirty the retained path still owns the concrete history mutation and layer dirty
box updates. box updates.
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_commit()` now
routes action bookkeeping and dirty-box capture/mutation through helper
functions; the retained callback builder now only forwards concrete canvas
state.
- 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_commit()` now - 2026-06-13: DEBT-0036 was narrowed again. `Canvas::stroke_commit()` now
builds its retained callback table through builds its retained callback table through
`make_legacy_canvas_stroke_commit_callbacks(...)`; the legacy executor still `make_legacy_canvas_stroke_commit_callbacks(...)`; the legacy executor still

View File

@@ -1839,7 +1839,7 @@ ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_stroke_ex
### STR-040 - Extract Stroke Commit Local History And Dirty Mutation ### STR-040 - Extract Stroke Commit Local History And Dirty Mutation
Status: Ready Status: Done
Score: +1 renderer boundary and OpenGL parity Score: +1 renderer boundary and OpenGL parity
Debt: `DEBT-0036` Debt: `DEBT-0036`
Scope: `src/canvas.cpp`, `tests/paint_renderer/stroke_execution_tests.cpp` Scope: `src/canvas.cpp`, `tests/paint_renderer/stroke_execution_tests.cpp`
@@ -1850,6 +1850,8 @@ Move the remaining local history and dirty-mutation work inside
`make_canvas_stroke_commit_callbacks()` into a retained helper so the callback `make_canvas_stroke_commit_callbacks()` into a retained helper so the callback
builder only forwards concrete canvas state. builder only forwards concrete canvas state.
Closeout: `aa53a5f9`
Done Checks: Done Checks:
- `make_canvas_stroke_commit_callbacks()` no longer owns the history/dirty - `make_canvas_stroke_commit_callbacks()` no longer owns the history/dirty

View File

@@ -606,48 +606,13 @@ static auto make_canvas_stroke_commit_callbacks(
canvas.apply_canvas_clear_color(cc); canvas.apply_canvas_clear_color(cc);
set_active_texture_unit(0); set_active_texture_unit(0);
}, },
[&]() { [&]() { stamp_canvas_stroke_commit_action(canvas, action); },
action->m_layer_idx = canvas.m_current_layer_idx;
action->m_frame_idx = canvas.layer().m_frame_index;
action->m_canvas = &canvas;
ActionManager::add(action);
},
[&]() { [&]() {
canvas.stroke_commit_timelapse(); canvas.stroke_commit_timelapse();
}, },
[&](int i) { [&](int i) { capture_canvas_stroke_commit_layer_state(canvas, action, i); },
canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).bindFramebuffer(); [&](int i) { apply_canvas_stroke_commit_dirty_mutation(canvas, i); },
}, [&](int i) { copy_canvas_stroke_commit_layer_image(canvas, i); },
[&](int i) {
glm::vec2 box_sz = zw(canvas.m_dirty_box[i]) - xy(canvas.m_dirty_box[i]);
action->m_image[i] = std::make_unique<uint8_t[]>(
static_cast<std::size_t>(box_sz.x * box_sz.y * 4));
canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).readPixelsRgba8(
static_cast<int>(canvas.m_dirty_box[i].x),
static_cast<int>(canvas.m_dirty_box[i].y),
static_cast<int>(box_sz.x),
static_cast<int>(box_sz.y),
action->m_image[i].get());
action->m_box[i] = canvas.m_dirty_box[i];
action->m_old_box[i] = canvas.m_layers[canvas.m_current_layer_idx]->box(i);
action->m_old_dirty[i] = canvas.m_layers[canvas.m_current_layer_idx]->face(i);
},
[&](int i) {
if (!canvas.m_layers[canvas.m_current_layer_idx]->m_alpha_locked) {
auto& lbox = canvas.m_layers[canvas.m_current_layer_idx]->box(i);
lbox = glm::vec4(
glm::min(xy(canvas.m_dirty_box[i]), xy(lbox)),
glm::max(zw(canvas.m_dirty_box[i]), zw(lbox)));
}
canvas.m_layers[canvas.m_current_layer_idx]->face(i) = true;
},
[&](int i) {
set_active_texture_unit(0);
canvas.m_tex2[i].bind();
copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height);
canvas.m_tex2[i].unbind();
},
[&](int i) { [&](int i) {
bind_commit_inputs(i); bind_commit_inputs(i);
}, },
@@ -848,6 +813,61 @@ static auto make_canvas_stroke_commit_request(
return pp::panopainter::make_legacy_canvas_stroke_commit_request(faces, sequence, commit_callbacks); return pp::panopainter::make_legacy_canvas_stroke_commit_request(faces, sequence, commit_callbacks);
} }
static void stamp_canvas_stroke_commit_action(
Canvas& canvas,
ActionStroke* action)
{
action->m_layer_idx = canvas.m_current_layer_idx;
action->m_frame_idx = canvas.layer().m_frame_index;
action->m_canvas = &canvas;
ActionManager::add(action);
}
static void capture_canvas_stroke_commit_layer_state(
Canvas& canvas,
ActionStroke* action,
int i)
{
canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).bindFramebuffer();
glm::vec2 box_sz = zw(canvas.m_dirty_box[i]) - xy(canvas.m_dirty_box[i]);
action->m_image[i] = std::make_unique<uint8_t[]>(
static_cast<std::size_t>(box_sz.x * box_sz.y * 4));
canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).readPixelsRgba8(
static_cast<int>(canvas.m_dirty_box[i].x),
static_cast<int>(canvas.m_dirty_box[i].y),
static_cast<int>(box_sz.x),
static_cast<int>(box_sz.y),
action->m_image[i].get());
action->m_box[i] = canvas.m_dirty_box[i];
action->m_old_box[i] = canvas.m_layers[canvas.m_current_layer_idx]->box(i);
action->m_old_dirty[i] = canvas.m_layers[canvas.m_current_layer_idx]->face(i);
}
static void apply_canvas_stroke_commit_dirty_mutation(
Canvas& canvas,
int i)
{
if (!canvas.m_layers[canvas.m_current_layer_idx]->m_alpha_locked) {
auto& lbox = canvas.m_layers[canvas.m_current_layer_idx]->box(i);
lbox = glm::vec4(
glm::min(xy(canvas.m_dirty_box[i]), xy(lbox)),
glm::max(zw(canvas.m_dirty_box[i]), zw(lbox)));
}
canvas.m_layers[canvas.m_current_layer_idx]->face(i) = true;
}
static void copy_canvas_stroke_commit_layer_image(
Canvas& canvas,
int i)
{
set_active_texture_unit(0);
canvas.m_tex2[i].bind();
copy_framebuffer_to_texture_2d(0, 0, 0, 0, canvas.m_width, canvas.m_height);
canvas.m_tex2[i].unbind();
}
std::vector<Canvas::StrokeFrame> Canvas::stroke_draw_compute(Stroke& stroke) const std::vector<Canvas::StrokeFrame> Canvas::stroke_draw_compute(Stroke& stroke) const
{ {
auto samples = stroke.compute_samples(); auto samples = stroke.compute_samples();