From 9b2a0d9c301495d5a1c8ef887e69d6ae6d26b879 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 16 Jun 2026 19:53:50 +0200 Subject: [PATCH] Extract canvas live stroke, node canvas state, and preset panel UI --- cmake/PanoPainterSources.cmake | 5 + docs/modernization/roadmap.md | 25 +- docs/modernization/tasks.md | 17 +- src/canvas.cpp | 514 +---------------- src/canvas.h | 1 + src/legacy_brush_preset_panel_ui.cpp | 213 +++++++ src/legacy_brush_preset_panel_ui.h | 19 + src/legacy_canvas_stroke_live_services.cpp | 614 +++++++++++++++++++++ src/legacy_node_canvas_state_services.cpp | 182 ++++++ src/legacy_node_canvas_state_services.h | 18 + src/node_canvas.cpp | 138 +---- src/node_panel_brush.cpp | 166 +----- src/node_panel_brush.h | 2 + 13 files changed, 1107 insertions(+), 807 deletions(-) create mode 100644 src/legacy_brush_preset_panel_ui.cpp create mode 100644 src/legacy_brush_preset_panel_ui.h create mode 100644 src/legacy_canvas_stroke_live_services.cpp create mode 100644 src/legacy_node_canvas_state_services.cpp create mode 100644 src/legacy_node_canvas_state_services.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 38cbfa01..7502311a 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -23,6 +23,7 @@ set(PP_LEGACY_PAINT_DOCUMENT_SOURCES src/canvas_actions.cpp src/canvas_layer.cpp src/legacy_canvas_stroke_commit_services.cpp + src/legacy_canvas_stroke_live_services.cpp src/legacy_canvas_document_io_services.cpp src/legacy_canvas_object_draw_services.cpp src/legacy_canvas_object_draw_services.h @@ -155,6 +156,10 @@ set(PP_PANOPAINTER_UI_SOURCES src/legacy_brush_panel_services.h src/legacy_document_animation_services.cpp src/legacy_document_animation_services.h + src/legacy_node_canvas_state_services.cpp + src/legacy_node_canvas_state_services.h + src/legacy_brush_preset_panel_ui.cpp + src/legacy_brush_preset_panel_ui.h src/legacy_grid_ui_services.cpp src/legacy_grid_ui_services.h src/legacy_quick_ui_services.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index f3c3448d..d1410e5a 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -79,14 +79,14 @@ What is still carrying too much live ownership: Current hotspot files: -- `src/canvas.cpp`: 2122 lines +- `src/canvas.cpp`: 1490 lines - `src/app_layout.cpp`: 125 lines - `src/canvas_modes.cpp`: 1014 lines - `src/node.cpp`: 995 lines - `src/main.cpp`: 271 lines -- `src/node_panel_brush.cpp`: 652 lines +- `src/node_panel_brush.cpp`: 435 lines - `src/node_stroke_preview.cpp`: 751 lines -- `src/node_canvas.cpp`: 877 lines +- `src/node_canvas.cpp`: 702 lines - `src/app.cpp`: 575 lines - `src/app_dialogs.cpp`: 168 lines @@ -260,7 +260,11 @@ Current architecture mismatches that must be treated as real blockers: larger stroke commit/sample execution family now also route through `src/legacy_canvas_stroke_commit_services.*` instead of staying inline in `src/canvas.cpp`, which trims another large retained stroke-render and - viewport-state execution family from the live canvas shell, while the + viewport-state execution family from the live canvas shell, while the live + `Canvas::stroke_draw()` orchestration now also routes through + `src/legacy_canvas_stroke_live_services.cpp` instead of staying inline in + `src/canvas.cpp`, which materially thins another large retained live + stroke-render pocket, while the `CanvasModeTransform` interaction family now also routes through `src/legacy_canvas_mode_transform.cpp` instead of staying inline in `src/canvas_modes.cpp`, which materially thins another retained canvas-view @@ -273,10 +277,19 @@ Current architecture mismatches that must be treated as real blockers: import/export/import-ABR routing now also lives in `src/legacy_brush_preset_services.*` instead of staying inline in `src/node_panel_brush.cpp`, which trims another large preset-workflow pocket - from the live UI node, while `NodeCanvas::handle_event()` now also routes + from the live UI node, while `NodePanelBrushPreset` init/menu wiring, click + handling, item construction, and added-state update now also route through + `src/legacy_brush_preset_panel_ui.*` instead of staying inline in + `src/node_panel_brush.cpp`, which materially thins another retained preset + panel UI pocket, while `NodeCanvas::handle_event()` now also routes through `execute_node_canvas_handle_event(...)`, which trims another coherent input-routing block from `src/node_canvas.cpp` even though the file is still - a live canvas/controller shell, while shared canvas-mode GL wrappers plus the + a live canvas/controller shell, while `NodeCanvas` restore/clear context, + resize handling, camera reset, buffer creation, cursor visibility/update, + tick, and destroy ownership now also route through + `src/legacy_node_canvas_state_services.*` instead of staying inline in + `src/node_canvas.cpp`, which materially thins another retained state/control + pocket, while shared canvas-mode GL wrappers plus the `CanvasModeBasicCamera` and `CanvasModeCamera` input handlers now also route through `src/legacy_canvas_mode_helpers.*` instead of staying inline in `src/canvas_modes.cpp`, while diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 56e98e81..17e0d5b9 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -91,7 +91,7 @@ Status: In Progress Why now: `src/canvas.cpp` is still the biggest single architectural blocker at about -2122 lines, with `src/canvas_modes.cpp` still large at about 1014 lines. +1490 lines, with `src/canvas_modes.cpp` still large at about 1014 lines. Current slice: - Canvas state-management helpers for picking, clear/clear-all, layer @@ -126,6 +126,10 @@ Current slice: `src/legacy_canvas_mode_transform.cpp` instead of staying inline in `src/canvas_modes.cpp`, which materially thins another retained transform-mode pocket from the broader canvas/render hotspot family. +- The live `Canvas::stroke_draw()` orchestration now also lives in + `src/legacy_canvas_stroke_live_services.cpp` instead of staying inline in + `src/canvas.cpp`, which materially thins another large retained live + stroke-render pocket from the canvas shell. Write scope: - `src/canvas.cpp` @@ -289,6 +293,11 @@ Current slice: `execute_node_canvas_handle_event(...)` helper, which trims another coherent input-routing block from `src/node_canvas.cpp` even though the node still owns broader canvas/controller behavior. +- `NodeCanvas` restore/clear context, resize handling, camera reset, buffer + creation, cursor visibility/update, tick, and destroy ownership now also + live in `src/legacy_node_canvas_state_services.*` instead of staying inline + in `src/node_canvas.cpp`, which materially thins another retained + state/control pocket without reopening the draw path. Write scope: - `src/node_stroke_preview.cpp` @@ -1011,6 +1020,12 @@ Current slice: in `src/node_panel_brush.cpp`, which trims another large preset-workflow pocket from the live UI node even though the broader cloud/package worker split still remains follow-up work. +- `NodePanelBrushPreset` init/menu wiring, click handling, item construction, + and added-state update now also live in + `src/legacy_brush_preset_panel_ui.*` instead of staying inline in + `src/node_panel_brush.cpp`, which materially thins another retained preset + panel UI pocket even though cloud/package worker ownership remains the + follow-up. Write scope: - `src/legacy_cloud_services.*` diff --git a/src/canvas.cpp b/src/canvas.cpp index 8deea45c..227df769 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -932,141 +932,6 @@ pp::panopainter::LegacyCanvasStrokeMixPassShell make_canvas_stroke_mix_pass_shel }); } -std::vector Canvas::stroke_draw_compute(Stroke& stroke) const -{ - auto samples = stroke.compute_samples(); - return pp::panopainter::plan_legacy_canvas_stroke_frames( - pp::panopainter::LegacyCanvasStrokeComputeRequest { - .previous_sample = stroke.m_prev_sample, - .samples = samples, - .zoom = App::I->zoom, - .mixer_size = glm::vec2(App::I->width, App::I->height), - .model_view = m_mv, - }, - [&](std::array& brush_quad, bool project_3d, glm::mat4 model_view) { - return stroke_draw_project(brush_quad, project_3d, model_view); - }, - []( - glm::vec4 mixer_rect, - glm::vec4 color, - float flow, - float opacity, - std::array, 6>&& shapes) { - return StrokeFrame { - .col = color, - .flow = flow, - .opacity = opacity, - .shapes = std::move(shapes), - .m_mixer_rect = mixer_rect, - }; - }); -} - -void Canvas::stroke_draw_pad_pass( - const std::array& pad_faces, - bool copy_stroke_destination, - const std::array& pad_destination_texture_binding, - const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch& pad_destination_texture_dispatch, - const pp::renderer::Extent2D& stroke_extent, - const glm::vec4& pad_color) -{ - pp::panopainter::setup_legacy_stroke_pad_shader( - pp::panopainter::LegacyStrokePadUniforms { - .color = pad_color, - .uses_destination_feedback = copy_stroke_destination, - }); - [[maybe_unused]] const auto pad_result = pp::panopainter::execute_legacy_canvas_stroke_pad_face_callbacks( - pad_faces, - stroke_extent, - copy_stroke_destination, - [&](std::span pad_quad) { - m_brush_shape.update_vertices( - const_cast(pad_quad.data()), - static_cast(pad_quad.size())); - }, - [&](int face_index) { - m_tmp[face_index].bindFramebuffer(); - }, - [&](int) { - pp::panopainter::bind_legacy_canvas_stroke_texture_inputs( - pad_destination_texture_binding, - pad_destination_texture_dispatch); - }, - [&](const pp::paint_renderer::CanvasStrokeCopyRegion& copy_region) { - pp::panopainter::execute_legacy_canvas_stroke_pad_copy_region( - copy_region, - [](int src_x, int src_y, int dst_x, int dst_y, int width, int height) { - copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height); - }); - }, - [&](int) { - pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs( - pad_destination_texture_binding, - pad_destination_texture_dispatch); - }, - [&] { - m_brush_shape.draw_fill(); - }, - [&](int face_index) { - m_tmp[face_index].unbindFramebuffer(); - }); -} - -void Canvas::stroke_draw_pad_face_orchestration( - const std::array& box_dirty, - const std::array& box_face, - bool copy_stroke_destination, - const pp::renderer::Extent2D& stroke_extent, - const glm::vec4& pad_color) -{ - stroke_draw_pad_face_callback_body( - box_dirty, - box_face, - copy_stroke_destination, - stroke_extent, - pad_color); -} - -void Canvas::stroke_draw_pad_face_callback_body( - const std::array& box_dirty, - const std::array& box_face, - bool copy_stroke_destination, - const pp::renderer::Extent2D& stroke_extent, - const glm::vec4& pad_color) -{ - pp::panopainter::setup_legacy_stroke_pad_shader( - pp::panopainter::LegacyStrokePadUniforms { - .color = pad_color, - .uses_destination_feedback = copy_stroke_destination, - }); - const auto pad_faces = pp::panopainter::make_legacy_canvas_stroke_pad_faces(box_dirty, box_face); - constexpr std::array pad_destination_texture_binding { - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, - .slot = 1, - }, - }; - const auto pad_destination_texture_dispatch = - pp::panopainter::make_legacy_canvas_stroke_pad_destination_texture_dispatch( - [&](int texture_slot) { - set_active_texture_unit(texture_slot); - }, - [&](int dst_face_index) { - m_tex[dst_face_index].bind(); - }, - [&](int dst_face_index) { - m_tex[dst_face_index].unbind(); - }, - 0); - stroke_draw_pad_pass( - pad_faces, - copy_stroke_destination, - pad_destination_texture_binding, - pad_destination_texture_dispatch, - stroke_extent, - pad_color); -} - static pp::panopainter::LegacyCanvasDrawMergeTemporaryCompositeExecution make_canvas_draw_merge_temporary_paint_request( Canvas& canvas, @@ -1204,386 +1069,9 @@ void Canvas::draw_merge_final_plane_composite( make_canvas_draw_merge_final_plane_composite_execution(*this)); } -void Canvas::stroke_draw_dual_pass( - const std::vector& frames_dual, - const std::array& dual_pass_texture_bindings, - const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch& dual_pass_brush_tip_dispatch, - const pp::renderer::Extent2D& stroke_extent, - const std::array& include_dual_dirty, - bool uses_pattern, - bool copy_stroke_destination) -{ - [[maybe_unused]] const auto dual_result = - pp::panopainter::execute_legacy_canvas_stroke_dual_pass( - make_stroke_draw_dual_pass_request( - frames_dual, - dual_pass_texture_bindings, - dual_pass_brush_tip_dispatch, - stroke_extent, - include_dual_dirty, - uses_pattern, - copy_stroke_destination)); -} - -pp::panopainter::LegacyCanvasStrokeMainPassExecutionRequest Canvas::make_stroke_draw_main_pass_request( - const Brush& brush, - std::function bind_samplers, - std::function execute_frame_pass, - std::function unbind_samplers) -{ - constexpr std::array main_pass_texture_bindings { - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, - .slot = 0, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, - .slot = 1, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::pattern, - .slot = 2, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, - .slot = 3, - }, - }; - constexpr std::array main_pass_texture_unbindings { - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, - .slot = 3, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, - .slot = 1, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, - .slot = 0, - }, - }; - const auto main_pass_texture_dispatch = pp::panopainter::make_legacy_canvas_stroke_main_pass_texture_dispatch( - [&](int texture_slot) { - set_active_texture_unit(texture_slot); - }, - [&] { - brush.m_tip_texture->bind(); - }, - [&] { - brush.m_tip_texture->unbind(); - }, - [&] { - brush.m_pattern_texture ? - brush.m_pattern_texture->bind() : - unbind_texture_2d(); - }, - [&] { - m_mixer.bindTexture(); - }, - [&] { - m_mixer.unbindTexture(); - }); - - return pp::panopainter::make_legacy_canvas_stroke_main_pass_execution_request( - "Canvas::stroke_draw", - std::move(bind_samplers), - [&] { - pp::panopainter::bind_legacy_canvas_stroke_texture_inputs( - main_pass_texture_bindings, - main_pass_texture_dispatch); - }, - std::move(execute_frame_pass), - [&] { - pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs( - main_pass_texture_unbindings, - main_pass_texture_dispatch); - }, - std::move(unbind_samplers)); -} - -pp::panopainter::LegacyCanvasStrokeDualPassRequest Canvas::make_stroke_draw_dual_pass_request( - const std::vector& frames_dual, - const std::array& dual_pass_texture_bindings, - const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch& dual_pass_brush_tip_dispatch, - const pp::renderer::Extent2D& stroke_extent, - const std::array& include_dual_dirty, - bool uses_pattern, - bool copy_stroke_destination) -{ - return pp::panopainter::LegacyCanvasStrokeDualPassRequest { - .context = "Canvas::stroke_draw", - .bind_brush_tip = [&] { - pp::panopainter::bind_legacy_canvas_stroke_texture_inputs( - dual_pass_texture_bindings, - dual_pass_brush_tip_dispatch); - }, - .unbind_brush_tip = [&] { - pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs( - dual_pass_texture_bindings, - dual_pass_brush_tip_dispatch); - }, - .setup_dual_shader = [&] { - pp::panopainter::setup_legacy_canvas_stroke_dual_shader( - uses_pattern); - }, - .execute_frame_pass = [&] { - stroke_draw_dual_pass_frame_pass( - frames_dual, - stroke_extent, - include_dual_dirty, - copy_stroke_destination); - }, - }; -} - -void Canvas::stroke_draw_dual_pass_frame_pass( - const std::vector& frames_dual, - const pp::renderer::Extent2D& stroke_extent, - const std::array& include_dual_dirty, - bool copy_stroke_destination) -{ - pp::panopainter::execute_legacy_canvas_stroke_dual_pass_frame_callbacks( - frames_dual, - stroke_extent, - std::span(m_dirty_box), - std::span(), - std::span(include_dual_dirty), - [&](auto& f) { - pp::panopainter::apply_legacy_stroke_sample_uniforms( - pp::panopainter::LegacyStrokeSampleUniforms { - .color = f.col, - .alpha = f.flow, - .opacity = f.opacity, - }); - }, - [](auto&, int, auto&) {}, - [&](auto&, int i, auto&& P) { - auto polygon = std::move(P); - return stroke_draw_samples(i, polygon, copy_stroke_destination); - }, - m_tmp_dual, - true); -} - void Canvas::stroke_draw() { - if (!(m_current_stroke && m_current_stroke->has_sample())) - { - stroke_commit_timelapse(); - //stroke_draw_mix({ 0,0 }, { m_mixer.getWidth(), m_mixer.getHeight() }); - return; - } - - m_dirty = true; - - const auto vp = query_canvas_viewport(); - const auto cc = query_canvas_clear_color(); - - const auto& brush = m_current_stroke->m_brush; - auto ortho_proj = glm::ortho(0.f, (float)m_width, 0.f, (float)m_height, -1.f, 1.f); - - apply_canvas_viewport(0, 0, m_width, m_height); - - glm::vec2 patt_scale = glm::vec2(brush->m_pattern_scale); - if (brush->m_pattern_flipx) patt_scale.x *= -1.f; - if (brush->m_pattern_flipy) patt_scale.y *= -1.f; - - const auto stroke_rasterization = canvas_stroke_rasterization_plan(m_width, m_height); - const bool copy_stroke_destination = stroke_rasterization.copy_stroke_destination; - const auto stroke_material = canvas_stroke_material_plan(*brush, copy_stroke_destination); - const auto stroke_extent = canvas_stroke_extent(m_width, m_height); - - apply_canvas_capability(blend_state(), false); - pp::panopainter::setup_legacy_stroke_shader( - pp::panopainter::LegacyStrokeShaderSetupUniforms { - .resolution = glm::vec2(static_cast(m_width), static_cast(m_height)), - .pattern = { - .scale = patt_scale, - .invert = static_cast(brush->m_pattern_invert), - .brightness = brush->m_pattern_brightness, - .contrast = brush->m_pattern_contrast, - .depth = brush->m_pattern_depth, - .blend_mode = brush->m_pattern_blend_mode, - .offset = m_pattern_offset, - }, - .mvp = ortho_proj, - .uses_destination_feedback = stroke_material.stroke_pass.uses_destination_feedback, - .uses_pattern = stroke_material.stroke_pass.uses_pattern, - .mix_alpha = brush->m_tip_mix, - .wet = brush->m_tip_wet, - .noise = brush->m_tip_noise, - .set_opacity = true, - .opacity = brush->m_tip_opacity, - }); - - // DRAW MAIN BRUSH - constexpr std::array live_pass_sampler_bindings { - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, - .slot = 0, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, - .slot = 1, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::pattern, - .slot = 2, - }, - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, - .slot = 3, - }, - }; - const pp::panopainter::LegacyCanvasStrokeSamplerDispatch live_pass_sampler_dispatch = - pp::panopainter::make_legacy_canvas_stroke_live_pass_sampler_dispatch( - [&](int slot) { - m_sampler_brush.bind(slot); - }, - [&] { - m_sampler_brush.unbind(); - }, - [&](int slot) { - m_sampler_nearest.bind(slot); - }, - [&] { - m_sampler_nearest.unbind(); - }, - [&](int slot) { - m_sampler_stencil.bind(slot); - }, - [&] { - m_sampler_stencil.unbind(); - }, - [&](int slot) { - m_sampler.bind(slot); - }, - [&] { - m_sampler.unbind(); - }); - auto frames = stroke_draw_compute(*m_current_stroke); - - std::array box_face = SIXPLETTE(glm::vec4(m_size, 0, 0)); - std::array box_dirty = SIXPLETTE(false); - const std::array include_main_dirty = SIXPLETTE(true); - glm::vec4 pad_color; - [[maybe_unused]] const auto main_pass_result = - pp::panopainter::execute_legacy_canvas_stroke_main_pass( - make_stroke_draw_main_pass_request( - *brush, - [&] { - pp::panopainter::bind_legacy_canvas_stroke_sampler_inputs( - live_pass_sampler_bindings, - live_pass_sampler_dispatch); - }, - [&] { - pp::panopainter::execute_legacy_canvas_stroke_main_pass_frame_callbacks( - frames, - stroke_extent, - std::span(m_dirty_box), - std::span(box_face), - std::span(include_main_dirty), - [&](auto& f) { - if (brush->m_tip_mix > 0.f) - { - stroke_draw_mix(xy(f.m_mixer_rect), zw(f.m_mixer_rect)); - } - pad_color = f.col; - }, - [&](auto&, int i, auto&) { - m_dirty_face[i] = true; - box_dirty[i] = true; - }, - [&](auto& f, int i, auto& P) { - pp::panopainter::use_legacy_stroke_shader(); - pp::panopainter::apply_legacy_stroke_sample_uniforms( - pp::panopainter::LegacyStrokeSampleUniforms { - .color = f.col, - .alpha = f.flow, - .opacity = f.opacity, - }); - return stroke_draw_samples(i, P, copy_stroke_destination); - }, - m_tmp); - }, - [&] { - pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs( - live_pass_sampler_bindings, - live_pass_sampler_dispatch); - } - )); - - // pad stroke - // In order to mitigate color bleeding at the edge of shapes in transparent layers - // we need to fill the area around the stroke with the same color because by default - // the transparent area may have black or other undefined color. - // This step is only useful for previewing the stroke because on commit the dilate - // algorithm fixes this issue. - // NOTE: at the moment this works on the whole canvas, but it can be optimized - // to only affect the current dirty box. In this case it may be necessary to do this - // work on documents that doesn't have the padding, so on document loading. - stroke_draw_pad_face_orchestration( - box_dirty, - box_face, - copy_stroke_destination, - stroke_extent, - pad_color); - - // DRAW DUAL BRUSH - - if (stroke_material.dual_pass.enabled && m_dual_stroke) - { - const auto& dual_brush = m_dual_stroke->m_brush; - constexpr std::array dual_pass_texture_bindings { - pp::panopainter::LegacyCanvasStrokeTextureBinding { - .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, - .slot = 0, - }, - }; - const auto dual_pass_brush_tip_dispatch = - pp::panopainter::make_legacy_canvas_stroke_brush_tip_texture_dispatch( - [&](int texture_slot) { - set_active_texture_unit(texture_slot); - }, - [&](int) { - dual_brush->m_tip_texture ? - dual_brush->m_tip_texture->bind() : - unbind_texture_2d(); - }, - [&](int) { - dual_brush->m_tip_texture ? - dual_brush->m_tip_texture->unbind() : - unbind_texture_2d(); - }, - 0); - auto frames_dual = stroke_draw_compute(*m_dual_stroke); - const std::array include_dual_dirty = - SIXPLETTE(stroke_material.composite_pass.dual_blend_mode == 0); - stroke_draw_dual_pass( - frames_dual, - dual_pass_texture_bindings, - dual_pass_brush_tip_dispatch, - stroke_extent, - include_dual_dirty, - stroke_material.dual_pass.uses_pattern, - copy_stroke_destination); - } - - pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs( - live_pass_sampler_bindings, - live_pass_sampler_dispatch); - - apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height); - apply_canvas_clear_color(cc); - - if (m_commit_delayed) - { - m_show_tmp = false; - m_commit_delayed = false; - stroke_commit(); - m_current_stroke = nullptr; - } + stroke_draw_live(); } bool Canvas::point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id) diff --git a/src/canvas.h b/src/canvas.h index eea4e844..62f4042a 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -202,6 +202,7 @@ public: // return rect {origin, size} glm::vec4 stroke_draw_samples(int i, std::vector& P, bool copy_stroke_destination); std::vector stroke_draw_compute(Stroke& stroke) const; + void stroke_draw_live(); void stroke_draw(); void stroke_end(); void stroke_cancel(); diff --git a/src/legacy_brush_preset_panel_ui.cpp b/src/legacy_brush_preset_panel_ui.cpp new file mode 100644 index 00000000..bb5a8ba4 --- /dev/null +++ b/src/legacy_brush_preset_panel_ui.cpp @@ -0,0 +1,213 @@ +#include "pch.h" + +#include "legacy_brush_preset_panel_ui.h" + +#include "app.h" +#include "app_core/brush_ui.h" +#include "asset.h" +#include "canvas.h" +#include "legacy_ui_overlay_services.h" +#include "node_message_box.h" +#include "node_popup_menu.h" + +namespace pp::panopainter { + +void LegacyBrushPresetPanelUi::init(NodePanelBrushPreset& owner) +{ + owner.init_template_file("data/dialogs/panel-brushes.xml", "tpl-panel-brush-preset"); + owner.m_container = owner.find("brushes"); + + owner.m_btn_add = owner.find("btn-add"); + owner.m_btn_add->on_click = [&owner](Node*) { + const auto plan = pp::app::plan_brush_preset_list_add( + static_cast(owner.m_container->m_children.size()), + Canvas::I && Canvas::I->m_current_brush); + if (plan) { + owner.execute_preset_list_plan(plan.value()); + } + }; + + owner.m_btn_up = owner.find("btn-up"); + owner.m_btn_up->on_click = [&owner](Node*) { + if (owner.m_current) + { + const int idx = owner.m_container->get_child_index(owner.m_current); + const auto plan = pp::app::plan_brush_preset_list_move( + static_cast(owner.m_container->m_children.size()), + idx, + -1); + if (plan) { + owner.execute_preset_list_plan(plan.value()); + } + } + }; + + owner.m_btn_down = owner.find("btn-down"); + owner.m_btn_down->on_click = [&owner](Node*) { + if (owner.m_current) + { + const int idx = owner.m_container->get_child_index(owner.m_current); + const auto plan = pp::app::plan_brush_preset_list_move( + static_cast(owner.m_container->m_children.size()), + idx, + 1); + if (plan) { + owner.execute_preset_list_plan(plan.value()); + } + } + }; + + /* + owner.m_btn_save = owner.find("btn-save"); + owner.m_btn_save->on_click = [this](Node*) { + if (m_current) + { + *m_current->m_brush = *Canvas::I->m_current_brush; + m_current->m_preview->draw_stroke(); + m_current->m_thumb->set_image(m_current->m_brush->m_brush_thumb_path); + save(); + } + }; + */ + + owner.m_btn_delete = owner.find("btn-remove"); + owner.m_btn_delete->on_click = [&owner](Node*) { + if (!owner.m_current) + return; + + const int index = owner.m_container->get_child_index(owner.m_current); + const auto plan = pp::app::plan_brush_preset_list_remove( + static_cast(owner.m_container->m_children.size()), + index); + if (plan) { + owner.execute_preset_list_plan(plan.value()); + } + }; + + owner.m_btn_menu = owner.find("btn-menu"); + owner.m_btn_menu->on_click = [&owner](Node* b) { + open_menu(owner, b); + }; + + owner.m_btn_import = owner.find("import"); + owner.m_btn_import->on_click = [&owner](Node*) { + App::I->pick_file({ "abr", "ppbr" }, [presets = std::static_pointer_cast(owner.shared_from_this())](std::string path) { + presets->import_brush(path); + }); + }; + + owner.m_btn_download = owner.find("download"); + owner.m_btn_download->on_click = [](Node*) { + App::I->dialog_preset_download(); + }; + + owner.m_notification = owner.find("notification"); + + if (Asset::exist(App::I->data_path + "/settings/presets.bin") && !owner.restore()) + { + auto mb = App::I->message_box("Presets", "Could not read brush presets file, it will be deleted.", true); + mb->btn_ok->on_click = [mb](Node*) { + Asset::delete_file(App::I->data_path + "/settings/presets.bin"); + pp::panopainter::close_legacy_dialog_node(*mb); + }; + mb->btn_cancel->on_click = [mb](Node*) { + pp::panopainter::close_legacy_dialog_node(*mb); + }; + } + + owner.m_notification->SetVisibility(owner.m_container->m_children.size() == 0); +} + +void LegacyBrushPresetPanelUi::handle_click(NodePanelBrushPreset& owner, Node* target) +{ + const int idx = owner.m_container->get_child_index(target); + const auto plan = pp::app::plan_brush_preset_list_select( + static_cast(owner.m_container->m_children.size()), + idx); + if (plan) { + owner.execute_preset_list_plan(plan.value()); + } +} + +void LegacyBrushPresetPanelUi::add_brush(NodePanelBrushPreset& owner, std::shared_ptr brush) +{ + NodeBrushPresetItem* b = new NodeBrushPresetItem; + owner.m_container->add_child(b); + b->init(); + b->create(); + b->loaded(); + b->thumb_path = brush->m_brush_thumb_path; + b->high_path = brush->m_brush_path; + b->m_brush = brush; + b->m_preview->m_brush = brush; + b->m_preview->draw_stroke(); + b->m_thumb->m_use_mipmaps = true; + b->m_thumb->set_image(brush->m_brush_thumb_path); + b->m_caption_size->set_text_format("%d", (int)brush->m_tip_size); + b->on_click = std::bind(&NodePanelBrushPreset::handle_click, &owner, std::placeholders::_1); + owner.m_notification->SetVisibility(owner.m_container->m_children.size() == 0); +} + +void LegacyBrushPresetPanelUi::added(NodePanelBrushPreset& owner) +{ + owner.m_interacted = false; + owner.m_notification->SetVisibility(owner.m_container->m_children.size() == 0); +} + +void LegacyBrushPresetPanelUi::open_menu(NodePanelBrushPreset& owner, Node* b) +{ + auto popup = std::dynamic_pointer_cast( + owner.load_template("data/dialogs/panel-brushes.xml", "tpl-brush-popup")); + if (!popup) + return; + + popup->SetPosition(b->m_pos.x + b->m_size.x, b->m_pos.y); + const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(owner, popup); + if (!popup_overlay) { + return; + } + + const auto popup_handle = popup_overlay.value(); + owner.root()->update(); + auto bounds = owner.root()->GetSize() - zw(popup->get_children_rect()); + popup->SetPosition(glm::clamp(popup->m_pos, { 0, 0 }, bounds)); + popup->on_select = [&owner, popup_handle](Node* target, int index) { + switch (index) + { + case 0: // import file + App::I->pick_file({"abr", "ppbr"}, [presets = std::static_pointer_cast(owner.shared_from_this())](std::string path) { + presets->import_brush(path); + }); + break; + case 1: // export file + App::I->dialog_ppbr_export(); + break; + case 2: // download + break; + case 3: // upload + break; + case 4: // clear presets + show_clear_presets_confirmation(owner); + break; + } + (void)target; + (void)pp::panopainter::close_legacy_overlay_node(owner, popup_handle); + }; +} + +void LegacyBrushPresetPanelUi::show_clear_presets_confirmation(NodePanelBrushPreset& owner) +{ + auto mb = App::I->message_box("Clear Presets", "Do you want to remove all the Brush Presets?", true); + mb->btn_ok->m_text->set_text("Yes"); + mb->btn_cancel->m_text->set_text("No"); + mb->btn_ok->on_click = mb->on_submit = [&owner, mb](Node*) { + const auto plan = pp::app::plan_brush_preset_list_clear( + static_cast(owner.m_container->m_children.size())); + if (plan) { + owner.execute_preset_list_plan(plan.value()); + } + pp::panopainter::close_legacy_dialog_node(*mb); + }; +} + +} // namespace pp::panopainter diff --git a/src/legacy_brush_preset_panel_ui.h b/src/legacy_brush_preset_panel_ui.h new file mode 100644 index 00000000..c3257d61 --- /dev/null +++ b/src/legacy_brush_preset_panel_ui.h @@ -0,0 +1,19 @@ +#pragma once + +#include "node_panel_brush.h" + +namespace pp::panopainter { + +class LegacyBrushPresetPanelUi final { +public: + static void init(NodePanelBrushPreset& owner); + static void handle_click(NodePanelBrushPreset& owner, Node* target); + static void add_brush(NodePanelBrushPreset& owner, std::shared_ptr brush); + static void added(NodePanelBrushPreset& owner); + +private: + static void open_menu(NodePanelBrushPreset& owner, Node* button); + static void show_clear_presets_confirmation(NodePanelBrushPreset& owner); +}; + +} // namespace pp::panopainter diff --git a/src/legacy_canvas_stroke_live_services.cpp b/src/legacy_canvas_stroke_live_services.cpp new file mode 100644 index 00000000..bcc9d66b --- /dev/null +++ b/src/legacy_canvas_stroke_live_services.cpp @@ -0,0 +1,614 @@ +#include "pch.h" +#include "canvas.h" +#include "app.h" +#include "legacy_canvas_stroke_services.h" +#include "legacy_canvas_stroke_composite_services.h" +#include "legacy_canvas_stroke_edge_services.h" +#include "legacy_canvas_stroke_execution_services.h" +#include "legacy_canvas_stroke_shader_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "renderer_gl/opengl_capabilities.h" +#include "util.h" + +#include +#include +#include +#include + +namespace { + +GLenum blend_state() +{ + return static_cast(pp::renderer::gl::blend_state()); +} + +void set_active_texture_unit(std::uint32_t unit_index) +{ + pp::legacy::ui_gl::activate_texture_unit(unit_index, "Canvas"); +} + +void unbind_texture_2d() +{ + pp::legacy::ui_gl::unbind_texture_2d("Canvas"); +} + +void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) +{ + pp::legacy::ui_gl::apply_viewport(x, y, width, height, "Canvas"); +} + +pp::renderer::gl::OpenGlViewportRect query_canvas_viewport() +{ + return pp::legacy::ui_gl::query_viewport_rect("Canvas"); +} + +std::array query_canvas_clear_color() +{ + return pp::legacy::ui_gl::query_clear_color("Canvas"); +} + +void apply_canvas_clear_color(std::array color) +{ + pp::legacy::ui_gl::set_clear_color(color, "Canvas"); +} + +void apply_canvas_capability(std::uint32_t capability, bool enabled) +{ + pp::legacy::ui_gl::set_capability(capability, enabled, "Canvas"); +} + +pp::paint_renderer::CanvasStrokeRasterizationPlan canvas_stroke_rasterization_plan( + int width, + int height) noexcept +{ + return pp::panopainter::plan_legacy_canvas_stroke_rasterization( + ShaderManager::render_device_features(), + width, + height); +} + +pp::paint_renderer::CanvasStrokeMaterialPlan canvas_stroke_material_plan( + const Brush& brush, + bool destination_feedback_needed) noexcept +{ + return pp::panopainter::plan_legacy_canvas_stroke_material( + pp::paint_renderer::CanvasStrokeMaterialRequest { + .destination_feedback_needed = destination_feedback_needed, + .pattern_enabled = brush.m_pattern_enabled, + .pattern_eachsample = brush.m_pattern_eachsample, + .wet_blend = brush.m_tip_wet > 0.F, + .mix_blend = brush.m_tip_mix > 0.F, + .noise_enabled = brush.m_tip_noise > 0.F, + .dual_brush_enabled = brush.m_dual_enabled, + .dual_blend_mode = brush.m_dual_blend_mode, + .pattern_blend_mode = brush.m_pattern_blend_mode, + .dual_alpha = brush.m_dual_opacity, + }); +} + +pp::renderer::Extent2D canvas_stroke_extent(int width, int height) noexcept +{ + return pp::renderer::Extent2D { + .width = static_cast(std::max(width, 0)), + .height = static_cast(std::max(height, 0)), + }; +} + +} // namespace + +std::vector Canvas::stroke_draw_compute(Stroke& stroke) const +{ + auto samples = stroke.compute_samples(); + return pp::panopainter::plan_legacy_canvas_stroke_frames( + pp::panopainter::LegacyCanvasStrokeComputeRequest { + .previous_sample = stroke.m_prev_sample, + .samples = samples, + .zoom = App::I->zoom, + .mixer_size = glm::vec2(App::I->width, App::I->height), + .model_view = m_mv, + }, + [&](std::array& brush_quad, bool project_3d, glm::mat4 model_view) { + return stroke_draw_project(brush_quad, project_3d, model_view); + }, + []( + glm::vec4 mixer_rect, + glm::vec4 color, + float flow, + float opacity, + std::array, 6>&& shapes) { + return StrokeFrame { + .col = color, + .flow = flow, + .opacity = opacity, + .shapes = std::move(shapes), + .m_mixer_rect = mixer_rect, + }; + }); +} + +void Canvas::stroke_draw_pad_pass( + const std::array& pad_faces, + bool copy_stroke_destination, + const std::array& pad_destination_texture_binding, + const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch& pad_destination_texture_dispatch, + const pp::renderer::Extent2D& stroke_extent, + const glm::vec4& pad_color) +{ + pp::panopainter::setup_legacy_stroke_pad_shader( + pp::panopainter::LegacyStrokePadUniforms { + .color = pad_color, + .uses_destination_feedback = copy_stroke_destination, + }); + [[maybe_unused]] const auto pad_result = pp::panopainter::execute_legacy_canvas_stroke_pad_face_callbacks( + pad_faces, + stroke_extent, + copy_stroke_destination, + [&](std::span pad_quad) { + m_brush_shape.update_vertices( + const_cast(pad_quad.data()), + static_cast(pad_quad.size())); + }, + [&](int face_index) { + m_tmp[face_index].bindFramebuffer(); + }, + [&](int) { + pp::panopainter::bind_legacy_canvas_stroke_texture_inputs( + pad_destination_texture_binding, + pad_destination_texture_dispatch); + }, + [&](const pp::paint_renderer::CanvasStrokeCopyRegion& copy_region) { + pp::panopainter::execute_legacy_canvas_stroke_pad_copy_region( + copy_region, + [](int src_x, int src_y, int dst_x, int dst_y, int width, int height) { + copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height); + }); + }, + [&](int) { + pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs( + pad_destination_texture_binding, + pad_destination_texture_dispatch); + }, + [&] { + m_brush_shape.draw_fill(); + }, + [&](int face_index) { + m_tmp[face_index].unbindFramebuffer(); + }); +} + +void Canvas::stroke_draw_pad_face_orchestration( + const std::array& box_dirty, + const std::array& box_face, + bool copy_stroke_destination, + const pp::renderer::Extent2D& stroke_extent, + const glm::vec4& pad_color) +{ + stroke_draw_pad_face_callback_body( + box_dirty, + box_face, + copy_stroke_destination, + stroke_extent, + pad_color); +} + +void Canvas::stroke_draw_pad_face_callback_body( + const std::array& box_dirty, + const std::array& box_face, + bool copy_stroke_destination, + const pp::renderer::Extent2D& stroke_extent, + const glm::vec4& pad_color) +{ + pp::panopainter::setup_legacy_stroke_pad_shader( + pp::panopainter::LegacyStrokePadUniforms { + .color = pad_color, + .uses_destination_feedback = copy_stroke_destination, + }); + const auto pad_faces = pp::panopainter::make_legacy_canvas_stroke_pad_faces(box_dirty, box_face); + constexpr std::array pad_destination_texture_binding { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, + .slot = 1, + }, + }; + const auto pad_destination_texture_dispatch = + pp::panopainter::make_legacy_canvas_stroke_pad_destination_texture_dispatch( + [&](int texture_slot) { + set_active_texture_unit(texture_slot); + }, + [&](int dst_face_index) { + m_tex[dst_face_index].bind(); + }, + [&](int dst_face_index) { + m_tex[dst_face_index].unbind(); + }, + 0); + stroke_draw_pad_pass( + pad_faces, + copy_stroke_destination, + pad_destination_texture_binding, + pad_destination_texture_dispatch, + stroke_extent, + pad_color); +} + +void Canvas::stroke_draw_dual_pass( + const std::vector& frames_dual, + const std::array& dual_pass_texture_bindings, + const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch& dual_pass_brush_tip_dispatch, + const pp::renderer::Extent2D& stroke_extent, + const std::array& include_dual_dirty, + bool uses_pattern, + bool copy_stroke_destination) +{ + [[maybe_unused]] const auto dual_result = + pp::panopainter::execute_legacy_canvas_stroke_dual_pass( + make_stroke_draw_dual_pass_request( + frames_dual, + dual_pass_texture_bindings, + dual_pass_brush_tip_dispatch, + stroke_extent, + include_dual_dirty, + uses_pattern, + copy_stroke_destination)); +} + +pp::panopainter::LegacyCanvasStrokeMainPassExecutionRequest Canvas::make_stroke_draw_main_pass_request( + const Brush& brush, + std::function bind_samplers, + std::function execute_frame_pass, + std::function unbind_samplers) +{ + constexpr std::array main_pass_texture_bindings { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, + .slot = 0, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, + .slot = 1, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::pattern, + .slot = 2, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, + .slot = 3, + }, + }; + constexpr std::array main_pass_texture_unbindings { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, + .slot = 3, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, + .slot = 1, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, + .slot = 0, + }, + }; + const auto main_pass_texture_dispatch = pp::panopainter::make_legacy_canvas_stroke_main_pass_texture_dispatch( + [&](int texture_slot) { + set_active_texture_unit(texture_slot); + }, + [&] { + brush.m_tip_texture->bind(); + }, + [&] { + brush.m_tip_texture->unbind(); + }, + [&] { + brush.m_pattern_texture ? + brush.m_pattern_texture->bind() : + unbind_texture_2d(); + }, + [&] { + m_mixer.bindTexture(); + }, + [&] { + m_mixer.unbindTexture(); + }); + + return pp::panopainter::make_legacy_canvas_stroke_main_pass_execution_request( + "Canvas::stroke_draw", + std::move(bind_samplers), + [&] { + pp::panopainter::bind_legacy_canvas_stroke_texture_inputs( + main_pass_texture_bindings, + main_pass_texture_dispatch); + }, + std::move(execute_frame_pass), + [&] { + pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs( + main_pass_texture_unbindings, + main_pass_texture_dispatch); + }, + std::move(unbind_samplers)); +} + +pp::panopainter::LegacyCanvasStrokeDualPassRequest Canvas::make_stroke_draw_dual_pass_request( + const std::vector& frames_dual, + const std::array& dual_pass_texture_bindings, + const pp::panopainter::LegacyCanvasStrokeTextureInputDispatch& dual_pass_brush_tip_dispatch, + const pp::renderer::Extent2D& stroke_extent, + const std::array& include_dual_dirty, + bool uses_pattern, + bool copy_stroke_destination) +{ + return pp::panopainter::LegacyCanvasStrokeDualPassRequest { + .context = "Canvas::stroke_draw", + .bind_brush_tip = [&] { + pp::panopainter::bind_legacy_canvas_stroke_texture_inputs( + dual_pass_texture_bindings, + dual_pass_brush_tip_dispatch); + }, + .unbind_brush_tip = [&] { + pp::panopainter::unbind_legacy_canvas_stroke_texture_inputs( + dual_pass_texture_bindings, + dual_pass_brush_tip_dispatch); + }, + .setup_dual_shader = [&] { + pp::panopainter::setup_legacy_canvas_stroke_dual_shader( + uses_pattern); + }, + .execute_frame_pass = [&] { + stroke_draw_dual_pass_frame_pass( + frames_dual, + stroke_extent, + include_dual_dirty, + copy_stroke_destination); + }, + }; +} + +void Canvas::stroke_draw_dual_pass_frame_pass( + const std::vector& frames_dual, + const pp::renderer::Extent2D& stroke_extent, + const std::array& include_dual_dirty, + bool copy_stroke_destination) +{ + pp::panopainter::execute_legacy_canvas_stroke_dual_pass_frame_callbacks( + frames_dual, + stroke_extent, + std::span(m_dirty_box), + std::span(), + std::span(include_dual_dirty), + [&](auto& f) { + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = f.col, + .alpha = f.flow, + .opacity = f.opacity, + }); + }, + [](auto&, int, auto&) {}, + [&](auto&, int i, auto&& P) { + auto polygon = std::move(P); + return stroke_draw_samples(i, polygon, copy_stroke_destination); + }, + m_tmp_dual, + true); +} + +void Canvas::stroke_draw_live() +{ + if (!(m_current_stroke && m_current_stroke->has_sample())) + { + stroke_commit_timelapse(); + //stroke_draw_mix({ 0,0 }, { m_mixer.getWidth(), m_mixer.getHeight() }); + return; + } + + m_dirty = true; + + const auto vp = query_canvas_viewport(); + const auto cc = query_canvas_clear_color(); + + const auto& brush = m_current_stroke->m_brush; + auto ortho_proj = glm::ortho(0.f, (float)m_width, 0.f, (float)m_height, -1.f, 1.f); + + apply_canvas_viewport(0, 0, m_width, m_height); + + glm::vec2 patt_scale = glm::vec2(brush->m_pattern_scale); + if (brush->m_pattern_flipx) patt_scale.x *= -1.f; + if (brush->m_pattern_flipy) patt_scale.y *= -1.f; + + const auto stroke_rasterization = canvas_stroke_rasterization_plan(m_width, m_height); + const bool copy_stroke_destination = stroke_rasterization.copy_stroke_destination; + const auto stroke_material = canvas_stroke_material_plan(*brush, copy_stroke_destination); + const auto stroke_extent = canvas_stroke_extent(m_width, m_height); + + apply_canvas_capability(blend_state(), false); + pp::panopainter::setup_legacy_stroke_shader( + pp::panopainter::LegacyStrokeShaderSetupUniforms { + .resolution = glm::vec2(static_cast(m_width), static_cast(m_height)), + .pattern = { + .scale = patt_scale, + .invert = static_cast(brush->m_pattern_invert), + .brightness = brush->m_pattern_brightness, + .contrast = brush->m_pattern_contrast, + .depth = brush->m_pattern_depth, + .blend_mode = brush->m_pattern_blend_mode, + .offset = m_pattern_offset, + }, + .mvp = ortho_proj, + .uses_destination_feedback = stroke_material.stroke_pass.uses_destination_feedback, + .uses_pattern = stroke_material.stroke_pass.uses_pattern, + .mix_alpha = brush->m_tip_mix, + .wet = brush->m_tip_wet, + .noise = brush->m_tip_noise, + .set_opacity = true, + .opacity = brush->m_tip_opacity, + }); + + // DRAW MAIN BRUSH + constexpr std::array live_pass_sampler_bindings { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, + .slot = 0, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::stroke_destination, + .slot = 1, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::pattern, + .slot = 2, + }, + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::mixer, + .slot = 3, + }, + }; + const pp::panopainter::LegacyCanvasStrokeSamplerDispatch live_pass_sampler_dispatch = + pp::panopainter::make_legacy_canvas_stroke_live_pass_sampler_dispatch( + [&](int slot) { + m_sampler_brush.bind(slot); + }, + [&] { + m_sampler_brush.unbind(); + }, + [&](int slot) { + m_sampler_nearest.bind(slot); + }, + [&] { + m_sampler_nearest.unbind(); + }, + [&](int slot) { + m_sampler_stencil.bind(slot); + }, + [&] { + m_sampler_stencil.unbind(); + }, + [&](int slot) { + m_sampler.bind(slot); + }, + [&] { + m_sampler.unbind(); + }); + auto frames = stroke_draw_compute(*m_current_stroke); + + std::array box_face = SIXPLETTE(glm::vec4(m_size, 0, 0)); + std::array box_dirty = SIXPLETTE(false); + const std::array include_main_dirty = SIXPLETTE(true); + glm::vec4 pad_color; + [[maybe_unused]] const auto main_pass_result = + pp::panopainter::execute_legacy_canvas_stroke_main_pass( + make_stroke_draw_main_pass_request( + *brush, + [&] { + pp::panopainter::bind_legacy_canvas_stroke_sampler_inputs( + live_pass_sampler_bindings, + live_pass_sampler_dispatch); + }, + [&] { + pp::panopainter::execute_legacy_canvas_stroke_main_pass_frame_callbacks( + frames, + stroke_extent, + std::span(m_dirty_box), + std::span(box_face), + std::span(include_main_dirty), + [&](auto& f) { + if (brush->m_tip_mix > 0.f) + { + stroke_draw_mix(xy(f.m_mixer_rect), zw(f.m_mixer_rect)); + } + pad_color = f.col; + }, + [&](auto&, int i, auto&) { + m_dirty_face[i] = true; + box_dirty[i] = true; + }, + [&](auto& f, int i, auto& P) { + pp::panopainter::use_legacy_stroke_shader(); + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = f.col, + .alpha = f.flow, + .opacity = f.opacity, + }); + return stroke_draw_samples(i, P, copy_stroke_destination); + }, + m_tmp); + }, + [&] { + pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs( + live_pass_sampler_bindings, + live_pass_sampler_dispatch); + } + )); + + // pad stroke + // In order to mitigate color bleeding at the edge of shapes in transparent layers + // we need to fill the area around the stroke with the same color because by default + // the transparent area may have black or other undefined color. + // This step is only useful for previewing the stroke because on commit the dilate + // algorithm fixes this issue. + // NOTE: at the moment this works on the whole canvas, but it can be optimized + // to only affect the current dirty box. In this case it may be necessary to do this + // work on documents that doesn't have the padding, so on document loading. + stroke_draw_pad_face_orchestration( + box_dirty, + box_face, + copy_stroke_destination, + stroke_extent, + pad_color); + + // DRAW DUAL BRUSH + + if (stroke_material.dual_pass.enabled && m_dual_stroke) + { + const auto& dual_brush = m_dual_stroke->m_brush; + constexpr std::array dual_pass_texture_bindings { + pp::panopainter::LegacyCanvasStrokeTextureBinding { + .input = pp::panopainter::LegacyCanvasStrokeTextureInput::brush_tip, + .slot = 0, + }, + }; + const auto dual_pass_brush_tip_dispatch = + pp::panopainter::make_legacy_canvas_stroke_brush_tip_texture_dispatch( + [&](int texture_slot) { + set_active_texture_unit(texture_slot); + }, + [&](int) { + dual_brush->m_tip_texture ? + dual_brush->m_tip_texture->bind() : + unbind_texture_2d(); + }, + [&](int) { + dual_brush->m_tip_texture ? + dual_brush->m_tip_texture->unbind() : + unbind_texture_2d(); + }, + 0); + auto frames_dual = stroke_draw_compute(*m_dual_stroke); + const std::array include_dual_dirty = + SIXPLETTE(stroke_material.composite_pass.dual_blend_mode == 0); + stroke_draw_dual_pass( + frames_dual, + dual_pass_texture_bindings, + dual_pass_brush_tip_dispatch, + stroke_extent, + include_dual_dirty, + stroke_material.dual_pass.uses_pattern, + copy_stroke_destination); + } + + pp::panopainter::unbind_legacy_canvas_stroke_sampler_inputs( + live_pass_sampler_bindings, + live_pass_sampler_dispatch); + + apply_canvas_viewport(vp.x, vp.y, vp.width, vp.height); + apply_canvas_clear_color(cc); + + if (m_commit_delayed) + { + m_show_tmp = false; + m_commit_delayed = false; + stroke_commit(); + m_current_stroke = nullptr; + } +} diff --git a/src/legacy_node_canvas_state_services.cpp b/src/legacy_node_canvas_state_services.cpp new file mode 100644 index 00000000..ccb0322b --- /dev/null +++ b/src/legacy_node_canvas_state_services.cpp @@ -0,0 +1,182 @@ +#include "pch.h" + +#include "legacy_node_canvas_state_services.h" + +#include "app.h" +#include "app_core/canvas_tool_ui.h" +#include "app_core/canvas_view.h" +#include "log.h" +#include "renderer_gl/opengl_capabilities.h" + +namespace pp::panopainter { +namespace { + +pp::app::CanvasToolMode canvas_tool_mode(kCanvasMode mode) noexcept +{ + switch (mode) { + case kCanvasMode::Draw: + return pp::app::CanvasToolMode::draw; + case kCanvasMode::Erase: + return pp::app::CanvasToolMode::erase; + case kCanvasMode::Line: + return pp::app::CanvasToolMode::line; + case kCanvasMode::Camera: + return pp::app::CanvasToolMode::camera; + case kCanvasMode::Grid: + return pp::app::CanvasToolMode::grid; + case kCanvasMode::Copy: + return pp::app::CanvasToolMode::copy; + case kCanvasMode::Cut: + return pp::app::CanvasToolMode::cut; + case kCanvasMode::Fill: + return pp::app::CanvasToolMode::fill; + case kCanvasMode::MaskFree: + return pp::app::CanvasToolMode::mask_free; + case kCanvasMode::MaskLine: + return pp::app::CanvasToolMode::mask_line; + case kCanvasMode::FloodFill: + return pp::app::CanvasToolMode::flood_fill; + case kCanvasMode::COUNT: + break; + } + return pp::app::CanvasToolMode::draw; +} + +pp::app::CanvasCursorVisibilityMode canvas_cursor_visibility_mode(NodeCanvas::kCursorVisibility mode) noexcept +{ + switch (mode) { + case NodeCanvas::kCursorVisibility::Never: + return pp::app::CanvasCursorVisibilityMode::never; + case NodeCanvas::kCursorVisibility::SmallBrush: + return pp::app::CanvasCursorVisibilityMode::small_brush; + case NodeCanvas::kCursorVisibility::NotPainting: + return pp::app::CanvasCursorVisibilityMode::not_painting; + case NodeCanvas::kCursorVisibility::Always: + return pp::app::CanvasCursorVisibilityMode::always; + } + return pp::app::CanvasCursorVisibilityMode::never; +} + +} // namespace + +void restore_legacy_node_canvas_context(NodeCanvas& node_canvas) +{ + static_cast(node_canvas).restore_context(); + const int canvas_resolution = App::I->default_canvas_resolution(); + node_canvas.m_canvas->create(canvas_resolution, canvas_resolution); + + node_canvas.m_sampler.create(); + + node_canvas.m_sampler.set_filter( + pp::renderer::gl::linear_texture_filter(), + pp::renderer::gl::nearest_texture_filter()); + node_canvas.m_face_plane.create<1>(2, 2); + node_canvas.m_canvas->snapshot_restore(); + CanvasMode::node = &node_canvas; + for (int i = 0; i < (int)kCanvasMode::COUNT; i++) + for (auto m : Canvas::modes[i]) + m->init(); +} + +void clear_legacy_node_canvas_context(NodeCanvas& node_canvas) +{ + static_cast(node_canvas).clear_context(); + node_canvas.m_canvas->snapshot_save(); + node_canvas.m_canvas->clear_context(); + // TODO: clear CanvasMode objects +} + +void handle_legacy_node_canvas_resize(NodeCanvas& node_canvas, glm::vec2 old_size, glm::vec2 new_size, float zoom) +{ + (void)old_size; + (void)zoom; + if (new_size.x != node_canvas.m_canvas->m_width || new_size.y != node_canvas.m_canvas->m_height) + { + new_size = new_size * node_canvas.m_density; + create_legacy_node_canvas_buffers(node_canvas); + } +} + +void reset_legacy_node_canvas_camera(NodeCanvas& node_canvas) +{ + const auto state = pp::app::plan_canvas_camera_reset(); + node_canvas.m_canvas->m_cam_rot = glm::mat4( + state.rotation[0], state.rotation[1], state.rotation[2], state.rotation[3], + state.rotation[4], state.rotation[5], state.rotation[6], state.rotation[7], + state.rotation[8], state.rotation[9], state.rotation[10], state.rotation[11], + state.rotation[12], state.rotation[13], state.rotation[14], state.rotation[15]); + node_canvas.m_canvas->m_cam_pos = { + state.position[0], + state.position[1], + state.position[2], + }; + node_canvas.m_canvas->m_cam_fov = state.field_of_view_degrees; + node_canvas.m_canvas->m_pan = { + state.pan[0], + state.pan[1], + }; +} + +void create_legacy_node_canvas_buffers(NodeCanvas& node_canvas) +{ + auto new_size = node_canvas.GetSize() * node_canvas.m_density; + LOG("NodeCanvas::create_buffers size: %d x %d density %f", (int)new_size.x, (int)new_size.y, node_canvas.m_density); + node_canvas.m_canvas->m_mixer.create((int)new_size.x * node_canvas.m_canvas->m_mixer_scale, + (int)new_size.y * node_canvas.m_canvas->m_mixer_scale, -1, pp::renderer::gl::rgba8_internal_format()); + node_canvas.m_blender_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format()); + node_canvas.m_cache_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format()); + node_canvas.m_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format(), true); + node_canvas.m_blender_bg.create((int)new_size.x, (int)new_size.y, pp::renderer::gl::rgba8_internal_format()); +} + +void set_legacy_node_canvas_density(NodeCanvas& node_canvas, float density) +{ + node_canvas.m_density = density; + create_legacy_node_canvas_buffers(node_canvas); +} + +void set_legacy_node_canvas_cursor_visibility(NodeCanvas& node_canvas, NodeCanvas::kCursorVisibility mode) +{ + node_canvas.m_cursor_visibility = mode; +} + +void update_legacy_node_canvas_cursor(NodeCanvas& node_canvas) +{ + auto* pen_mode = node_canvas.m_canvas->get_mode(); + const auto plan = pp::app::plan_canvas_cursor_visibility(pp::app::CanvasCursorVisibilityInput { + .mode = canvas_tool_mode(node_canvas.m_canvas->m_current_mode), + .visibility_mode = canvas_cursor_visibility_mode(node_canvas.m_cursor_visibility), + .has_current_brush = node_canvas.m_canvas->m_current_brush != nullptr, + .brush_tip_size = node_canvas.m_canvas->m_current_brush ? node_canvas.m_canvas->m_current_brush->m_tip_size : 0.0F, + .pen_is_drawing = pen_mode && pen_mode->m_drawing, + .alt_down = App::I && App::I->keys[(int)kKey::KeyAlt], + .pen_is_resizing = pen_mode && pen_mode->m_resizing, + .pen_is_picking = pen_mode && pen_mode->m_picking, + }); + if (!plan) { + LOG("Canvas cursor visibility planning failed: %s", plan.status().message); + App::I->show_cursor(); + return; + } + + plan.value().visible ? App::I->show_cursor() : App::I->hide_cursor(); +} + +void tick_legacy_node_canvas_state(NodeCanvas& node_canvas, float dt) +{ + node_canvas.m_outline_pan = glm::fract(node_canvas.m_outline_pan + dt * 0.01f); +} + +void destroy_legacy_node_canvas_state(NodeCanvas& node_canvas) +{ + node_canvas.m_blender_rtt.destroy(); + node_canvas.m_cache_rtt.destroy(); + node_canvas.m_rtt.destroy(); + node_canvas.m_blender_bg.destroy(); + node_canvas.m_face_plane.destroy(); + node_canvas.m_line.destroy(); + node_canvas.m_grid.destroy(); + static_cast(node_canvas).destroy(); +} + +} // namespace pp::panopainter diff --git a/src/legacy_node_canvas_state_services.h b/src/legacy_node_canvas_state_services.h new file mode 100644 index 00000000..e602007a --- /dev/null +++ b/src/legacy_node_canvas_state_services.h @@ -0,0 +1,18 @@ +#pragma once + +#include "node_canvas.h" + +namespace pp::panopainter { + +void restore_legacy_node_canvas_context(NodeCanvas& node_canvas); +void clear_legacy_node_canvas_context(NodeCanvas& node_canvas); +void handle_legacy_node_canvas_resize(NodeCanvas& node_canvas, glm::vec2 old_size, glm::vec2 new_size, float zoom); +void reset_legacy_node_canvas_camera(NodeCanvas& node_canvas); +void create_legacy_node_canvas_buffers(NodeCanvas& node_canvas); +void set_legacy_node_canvas_density(NodeCanvas& node_canvas, float density); +void set_legacy_node_canvas_cursor_visibility(NodeCanvas& node_canvas, NodeCanvas::kCursorVisibility mode); +void update_legacy_node_canvas_cursor(NodeCanvas& node_canvas); +void tick_legacy_node_canvas_state(NodeCanvas& node_canvas, float dt); +void destroy_legacy_node_canvas_state(NodeCanvas& node_canvas); + +} // namespace pp::panopainter diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 9b3fdec0..1c569f17 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -8,13 +8,13 @@ #include "app_core/canvas_hotkey.h" #include "app_core/canvas_tool_ui.h" -#include "app_core/canvas_view.h" #include "app_core/document_animation.h" #include "app.h" #include "node_panel_grid.h" #include "legacy_canvas_draw_merge_services.h" #include "legacy_canvas_stroke_composite_services.h" #include "legacy_canvas_stroke_erase_services.h" +#include "legacy_node_canvas_state_services.h" #include "legacy_preference_storage.h" #include "legacy_ui_gl_dispatch.h" #include "legacy_ui_overlay_services.h" @@ -139,54 +139,6 @@ pp::app::CanvasHotkeyKey canvas_hotkey_key(kKey key) noexcept } } -pp::app::CanvasToolMode canvas_tool_mode(kCanvasMode mode) noexcept -{ - switch (mode) { - case kCanvasMode::Draw: - return pp::app::CanvasToolMode::draw; - case kCanvasMode::Erase: - return pp::app::CanvasToolMode::erase; - case kCanvasMode::Line: - return pp::app::CanvasToolMode::line; - case kCanvasMode::Camera: - return pp::app::CanvasToolMode::camera; - case kCanvasMode::Grid: - return pp::app::CanvasToolMode::grid; - case kCanvasMode::Copy: - return pp::app::CanvasToolMode::copy; - case kCanvasMode::Cut: - return pp::app::CanvasToolMode::cut; - case kCanvasMode::Fill: - return pp::app::CanvasToolMode::fill; - case kCanvasMode::MaskFree: - return pp::app::CanvasToolMode::mask_free; - case kCanvasMode::MaskLine: - return pp::app::CanvasToolMode::mask_line; - case kCanvasMode::FloodFill: - return pp::app::CanvasToolMode::flood_fill; - case kCanvasMode::COUNT: - return pp::app::CanvasToolMode::draw; - } - - return pp::app::CanvasToolMode::draw; -} - -pp::app::CanvasCursorVisibilityMode canvas_cursor_visibility_mode(NodeCanvas::kCursorVisibility mode) noexcept -{ - switch (mode) { - case NodeCanvas::kCursorVisibility::Never: - return pp::app::CanvasCursorVisibilityMode::never; - case NodeCanvas::kCursorVisibility::SmallBrush: - return pp::app::CanvasCursorVisibilityMode::small_brush; - case NodeCanvas::kCursorVisibility::NotPainting: - return pp::app::CanvasCursorVisibilityMode::not_painting; - case NodeCanvas::kCursorVisibility::Always: - return pp::app::CanvasCursorVisibilityMode::always; - } - - return pp::app::CanvasCursorVisibilityMode::never; -} - pp::app::CanvasHotkeyState canvas_hotkey_state(bool mouse_focused, int touch_finger_count = 0) noexcept { pp::app::CanvasHotkeyState state; @@ -684,29 +636,12 @@ void NodeCanvas::init() void NodeCanvas::restore_context() { - Node::restore_context(); - const int canvas_resolution = App::I->default_canvas_resolution(); - m_canvas->create(canvas_resolution, canvas_resolution); - - m_sampler.create(); - - m_sampler.set_filter( - pp::renderer::gl::linear_texture_filter(), - pp::renderer::gl::nearest_texture_filter()); - m_face_plane.create<1>(2, 2); - m_canvas->snapshot_restore(); - CanvasMode::node = this; - for (int i = 0; i < (int)kCanvasMode::COUNT; i++) - for (auto m : Canvas::modes[i]) - m->init(); + pp::panopainter::restore_legacy_node_canvas_context(*this); } void NodeCanvas::clear_context() { - Node::clear_context(); - m_canvas->snapshot_save(); - m_canvas->clear_context(); - // TODO: clear CanvasMode objects + pp::panopainter::clear_legacy_node_canvas_context(*this); } void NodeCanvas::draw() @@ -781,11 +716,7 @@ void NodeCanvas::draw() void NodeCanvas::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) { - if (new_size.x != m_canvas->m_width || new_size.y != m_canvas->m_height) - { - new_size = new_size * m_density; - create_buffers(); - } + pp::panopainter::handle_legacy_node_canvas_resize(*this, old_size, new_size, zoom); } kEventResult NodeCanvas::handle_event(Event* e) @@ -796,82 +727,35 @@ kEventResult NodeCanvas::handle_event(Event* e) void NodeCanvas::reset_camera() { - const auto state = pp::app::plan_canvas_camera_reset(); - m_canvas->m_cam_rot = glm::mat4( - state.rotation[0], state.rotation[1], state.rotation[2], state.rotation[3], - state.rotation[4], state.rotation[5], state.rotation[6], state.rotation[7], - state.rotation[8], state.rotation[9], state.rotation[10], state.rotation[11], - state.rotation[12], state.rotation[13], state.rotation[14], state.rotation[15]); - m_canvas->m_cam_pos = { - state.position[0], - state.position[1], - state.position[2], - }; - m_canvas->m_cam_fov = state.field_of_view_degrees; - m_canvas->m_pan = { - state.pan[0], - state.pan[1], - }; + pp::panopainter::reset_legacy_node_canvas_camera(*this); } void NodeCanvas::create_buffers() { - auto new_size = GetSize() * m_density; - LOG("NodeCanvas::create_buffers size: %d x %d density %f", (int)new_size.x, (int)new_size.y, m_density); - m_canvas->m_mixer.create((int)new_size.x * m_canvas->m_mixer_scale, - (int)new_size.y * m_canvas->m_mixer_scale, -1, pp::renderer::gl::rgba8_internal_format()); - m_blender_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format()); - m_cache_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format()); - m_rtt.create((int)new_size.x, (int)new_size.y, -1, pp::renderer::gl::rgba8_internal_format(), true); - m_blender_bg.create((int)new_size.x, (int)new_size.y, pp::renderer::gl::rgba8_internal_format()); + pp::panopainter::create_legacy_node_canvas_buffers(*this); } void NodeCanvas::set_density(float d) { - m_density = d; - create_buffers(); + pp::panopainter::set_legacy_node_canvas_density(*this, d); } void NodeCanvas::set_cursor_visibility(kCursorVisibility mode) { - m_cursor_visibility = mode; + pp::panopainter::set_legacy_node_canvas_cursor_visibility(*this, mode); } void NodeCanvas::update_cursor() { - auto* pen_mode = m_canvas->get_mode(); - const auto plan = pp::app::plan_canvas_cursor_visibility(pp::app::CanvasCursorVisibilityInput { - .mode = canvas_tool_mode(m_canvas->m_current_mode), - .visibility_mode = canvas_cursor_visibility_mode(m_cursor_visibility), - .has_current_brush = m_canvas->m_current_brush != nullptr, - .brush_tip_size = m_canvas->m_current_brush ? m_canvas->m_current_brush->m_tip_size : 0.0F, - .pen_is_drawing = pen_mode && pen_mode->m_drawing, - .alt_down = App::I && App::I->keys[(int)kKey::KeyAlt], - .pen_is_resizing = pen_mode && pen_mode->m_resizing, - .pen_is_picking = pen_mode && pen_mode->m_picking, - }); - if (!plan) { - LOG("Canvas cursor visibility planning failed: %s", plan.status().message); - App::I->show_cursor(); - return; - } - - plan.value().visible ? App::I->show_cursor() : App::I->hide_cursor(); + pp::panopainter::update_legacy_node_canvas_cursor(*this); } void NodeCanvas::on_tick(float dt) { - m_outline_pan = glm::fract(m_outline_pan + dt * 0.01f); + pp::panopainter::tick_legacy_node_canvas_state(*this, dt); } void NodeCanvas::destroy() { - m_blender_rtt.destroy(); - m_cache_rtt.destroy(); - m_rtt.destroy(); - m_blender_bg.destroy(); - m_face_plane.destroy(); - m_line.destroy(); - m_grid.destroy(); - Node::destroy(); + pp::panopainter::destroy_legacy_node_canvas_state(*this); } diff --git a/src/node_panel_brush.cpp b/src/node_panel_brush.cpp index 970078e0..3dbdf7cb 100644 --- a/src/node_panel_brush.cpp +++ b/src/node_panel_brush.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "log.h" #include "node_panel_brush.h" +#include "legacy_brush_preset_panel_ui.h" #include "legacy_brush_panel_services.h" #include "app_core/brush_ui.h" #include "legacy_brush_ui_services.h" @@ -405,142 +406,7 @@ void NodePanelBrushPreset::execute_preset_list_plan(const pp::app::BrushPresetLi void NodePanelBrushPreset::init() { - init_template_file("data/dialogs/panel-brushes.xml", "tpl-panel-brush-preset"); - m_container = find("brushes"); - m_btn_add = find("btn-add"); - m_btn_add->on_click = [this] (Node*) { - const auto plan = pp::app::plan_brush_preset_list_add( - static_cast(m_container->m_children.size()), - Canvas::I && Canvas::I->m_current_brush); - if (plan) { - execute_preset_list_plan(plan.value()); - } - }; - m_btn_up = find("btn-up"); - m_btn_up->on_click = [this](Node*) { - if (m_current) - { - int idx = m_container->get_child_index(m_current); - const auto plan = pp::app::plan_brush_preset_list_move( - static_cast(m_container->m_children.size()), - idx, - -1); - if (plan) { - execute_preset_list_plan(plan.value()); - } - } - }; - m_btn_down = find("btn-down"); - m_btn_down->on_click = [this](Node*) { - if (m_current) - { - int idx = m_container->get_child_index(m_current); - const auto plan = pp::app::plan_brush_preset_list_move( - static_cast(m_container->m_children.size()), - idx, - 1); - if (plan) { - execute_preset_list_plan(plan.value()); - } - } - }; -/* - m_btn_save = find("btn-save"); - m_btn_save->on_click = [this](Node*) { - if (m_current) - { - *m_current->m_brush = *Canvas::I->m_current_brush; - m_current->m_preview->draw_stroke(); - m_current->m_thumb->set_image(m_current->m_brush->m_brush_thumb_path); - save(); - } - }; -*/ - m_btn_delete = find("btn-remove"); - m_btn_delete->on_click = [this](Node*) { - if (!m_current) - return; - int index = m_container->get_child_index(m_current); - const auto plan = pp::app::plan_brush_preset_list_remove( - static_cast(m_container->m_children.size()), - index); - if (plan) { - execute_preset_list_plan(plan.value()); - } - }; - m_btn_menu = find("btn-menu"); - m_btn_menu->on_click = [this](Node* b) { - auto popup = std::dynamic_pointer_cast( - load_template("data/dialogs/panel-brushes.xml", "tpl-brush-popup")); - if (!popup) - return; - popup->SetPosition(b->m_pos.x + b->m_size.x, b->m_pos.y); - const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*this, popup); - if (!popup_overlay) { - return; - } - const auto popup_handle = popup_overlay.value(); - root()->update(); - auto bounds = root()->GetSize() - zw(popup->get_children_rect()); - popup->SetPosition(glm::clamp(popup->m_pos, { 0, 0 }, bounds)); - popup->on_select = [this, popup_handle](Node* target, int index) { - switch (index) - { - case 0: // import file - App::I->pick_file({"abr", "ppbr"}, [presets = std::static_pointer_cast(shared_from_this())] (std::string path) { - presets->import_brush(path); - }); - break; - case 1: // export file - App::I->dialog_ppbr_export(); - break; - case 2: // download - break; - case 3: // upload - break; - case 4: // clear presets - { - auto mb = App::I->message_box("Clear Presets", "Do you want to remove all the Brush Presets?", true); - mb->btn_ok->m_text->set_text("Yes"); - mb->btn_cancel->m_text->set_text("No"); - mb->btn_ok->on_click = mb->on_submit = [this, mb](Node*) { - const auto plan = pp::app::plan_brush_preset_list_clear( - static_cast(m_container->m_children.size())); - if (plan) { - execute_preset_list_plan(plan.value()); - } - pp::panopainter::close_legacy_dialog_node(*mb); - }; - break; - } - } - (void)pp::panopainter::close_legacy_overlay_node(*this, popup_handle); - }; - }; - m_btn_import = find("import"); - m_btn_import->on_click = [this] (Node*) { - App::I->pick_file({ "abr", "ppbr" }, [presets = std::static_pointer_cast(shared_from_this())](std::string path) { - presets->import_brush(path); - }); - }; - m_btn_download = find("download"); - m_btn_download->on_click = [this] (Node*) { - App::I->dialog_preset_download(); - }; - m_notification = find("notification"); - - if (Asset::exist(App::I->data_path + "/settings/presets.bin") && !restore()) - { - auto mb = App::I->message_box("Presets", "Could not read brush presets file, it will be deleted.", true); - mb->btn_ok->on_click = [mb](Node*) { - Asset::delete_file(App::I->data_path + "/settings/presets.bin"); - pp::panopainter::close_legacy_dialog_node(*mb); - }; - mb->btn_cancel->on_click = [mb](Node*) { - pp::panopainter::close_legacy_dialog_node(*mb); - }; - } - m_notification->SetVisibility(m_container->m_children.size() == 0); + pp::panopainter::LegacyBrushPresetPanelUi::init(*this); } kEventResult NodePanelBrushPreset::handle_event(Event* e) @@ -574,13 +440,7 @@ kEventResult NodePanelBrushPreset::handle_event(Event* e) void NodePanelBrushPreset::handle_click(Node* target) { - int idx = m_container->get_child_index(target); - const auto plan = pp::app::plan_brush_preset_list_select( - static_cast(m_container->m_children.size()), - idx); - if (plan) { - execute_preset_list_plan(plan.value()); - } + pp::panopainter::LegacyBrushPresetPanelUi::handle_click(*this, target); } bool NodePanelBrushPreset::save() @@ -599,21 +459,7 @@ bool NodePanelBrushPreset::restore() void NodePanelBrushPreset::add_brush(std::shared_ptr brush) { - NodeBrushPresetItem* b = new NodeBrushPresetItem; - m_container->add_child(b); - b->init(); - b->create(); - b->loaded(); - b->thumb_path = brush->m_brush_thumb_path; - b->high_path = brush->m_brush_path; - b->m_brush = brush; - b->m_preview->m_brush = brush; - b->m_preview->draw_stroke(); - b->m_thumb->m_use_mipmaps = true; - b->m_thumb->set_image(brush->m_brush_thumb_path); - b->m_caption_size->set_text_format("%d", (int)brush->m_tip_size); - b->on_click = std::bind(&NodePanelBrushPreset::handle_click, this, std::placeholders::_1); - m_notification->SetVisibility(m_container->m_children.size() == 0); + pp::panopainter::LegacyBrushPresetPanelUi::add_brush(*this, std::move(brush)); } bool NodePanelBrushPreset::export_ppbr(const std::string& path_in, const PPBRInfo& info_data) @@ -647,6 +493,6 @@ void NodePanelBrushPreset::clear_brushes() void NodePanelBrushPreset::added(Node* parent) { - m_interacted = false; - m_notification->SetVisibility(m_container->m_children.size() == 0); + (void)parent; + pp::panopainter::LegacyBrushPresetPanelUi::added(*this); } diff --git a/src/node_panel_brush.h b/src/node_panel_brush.h index cb9e2c59..0f3225d1 100644 --- a/src/node_panel_brush.h +++ b/src/node_panel_brush.h @@ -17,6 +17,7 @@ namespace pp::panopainter { class LegacyBrushTextureListServices; class LegacyBrushPresetListServices; class LegacyBrushPresetServices; +class LegacyBrushPresetPanelUi; } class NodeButtonBrush : public NodeButtonCustom, public Serializer::Type @@ -90,6 +91,7 @@ public: class NodePanelBrushPreset : public Node { + friend class pp::panopainter::LegacyBrushPresetPanelUi; friend class pp::panopainter::LegacyBrushPresetListServices; friend class pp::panopainter::LegacyBrushPresetServices;