diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index b38572b..55c24e8 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -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(...)`; the retained path still owns the concrete history mutation and layer dirty 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 builds its retained callback table through `make_legacy_canvas_stroke_commit_callbacks(...)`; the legacy executor still diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 6f2c2cb..ce0b9ef 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -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 -Status: Ready +Status: Done Score: +1 renderer boundary and OpenGL parity Debt: `DEBT-0036` 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 builder only forwards concrete canvas state. +Closeout: `aa53a5f9` + Done Checks: - `make_canvas_stroke_commit_callbacks()` no longer owns the history/dirty diff --git a/src/canvas.cpp b/src/canvas.cpp index 761f563..ea5739c 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -606,48 +606,13 @@ static auto make_canvas_stroke_commit_callbacks( canvas.apply_canvas_clear_color(cc); set_active_texture_unit(0); }, - [&]() { - 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); - }, + [&]() { stamp_canvas_stroke_commit_action(canvas, action); }, [&]() { canvas.stroke_commit_timelapse(); }, - [&](int i) { - canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).bindFramebuffer(); - }, - [&](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( - static_cast(box_sz.x * box_sz.y * 4)); - canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).readPixelsRgba8( - static_cast(canvas.m_dirty_box[i].x), - static_cast(canvas.m_dirty_box[i].y), - static_cast(box_sz.x), - static_cast(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) { capture_canvas_stroke_commit_layer_state(canvas, action, i); }, + [&](int i) { apply_canvas_stroke_commit_dirty_mutation(canvas, i); }, + [&](int i) { copy_canvas_stroke_commit_layer_image(canvas, i); }, [&](int 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); } +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( + static_cast(box_sz.x * box_sz.y * 4)); + canvas.m_layers[canvas.m_current_layer_idx]->rtt(i).readPixelsRgba8( + static_cast(canvas.m_dirty_box[i].x), + static_cast(canvas.m_dirty_box[i].y), + static_cast(box_sz.x), + static_cast(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::stroke_draw_compute(Stroke& stroke) const { auto samples = stroke.compute_samples();