diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 7502311a..3f8decbf 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -22,6 +22,7 @@ set(PP_LEGACY_PAINT_DOCUMENT_SOURCES src/canvas.cpp src/canvas_actions.cpp src/canvas_layer.cpp + src/legacy_canvas_layer_services.cpp src/legacy_canvas_stroke_commit_services.cpp src/legacy_canvas_stroke_live_services.cpp src/legacy_canvas_document_io_services.cpp @@ -42,6 +43,8 @@ set(PP_LEGACY_RENDERER_GL_SOURCES ) set(PP_LEGACY_UI_CORE_SOURCES + src/legacy_ui_node_attributes.cpp + src/legacy_ui_node_attributes.h src/legacy_ui_node_loader.cpp src/legacy_ui_node_loader.h src/legacy_ui_node_event.cpp @@ -71,6 +74,7 @@ set(PP_LEGACY_UI_CORE_SOURCES set(PP_LEGACY_APP_SOURCES src/canvas_modes.cpp + src/legacy_canvas_mode_pen_line.cpp src/legacy_canvas_mode_helpers.cpp src/legacy_canvas_mode_helpers.h src/legacy_canvas_mode_transform.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index d1410e5a..144f4f79 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -79,10 +79,10 @@ What is still carrying too much live ownership: Current hotspot files: -- `src/canvas.cpp`: 1490 lines +- `src/canvas.cpp`: 1271 lines - `src/app_layout.cpp`: 125 lines -- `src/canvas_modes.cpp`: 1014 lines -- `src/node.cpp`: 995 lines +- `src/canvas_modes.cpp`: 720 lines +- `src/node.cpp`: 803 lines - `src/main.cpp`: 271 lines - `src/node_panel_brush.cpp`: 435 lines - `src/node_stroke_preview.cpp`: 751 lines @@ -264,11 +264,18 @@ Current architecture mismatches that must be treated as real blockers: `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 + stroke-render pocket, while `Canvas::layer_merge(...)`, + `Canvas::flood_fill(...)`, and `Canvas::FloodData::apply()` now also route + through `src/legacy_canvas_layer_services.cpp` instead of staying inline in + `src/canvas.cpp`, which trims another coherent retained layer/fill workflow + 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 - and transform-mode execution pocket, + and transform-mode execution pocket, while the `CanvasModePen` and + `CanvasModeLine` interaction families now also route through + `src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in + `src/canvas_modes.cpp`, while `NodePanelBrush` save/restore/scan/reload/find/get-path ownership now routes through `src/legacy_brush_panel_services.*` instead of staying inline in `src/node_panel_brush.cpp`, which trims another retained brush-workflow @@ -306,7 +313,9 @@ Current architecture mismatches that must be treated as real blockers: about without yet moving ownership into `pp_ui_core`, while the generic per-frame node execution/traversal family for `restore_context`, `clear_context`, `update`, `update_internal`, and `tick` now also lives in - `src/legacy_ui_node_execution.cpp`, + `src/legacy_ui_node_execution.cpp`, while `Node::parse_attributes(...)` now + also routes through `src/legacy_ui_node_attributes.*` instead of staying + inline in `src/node.cpp`, while `Canvas` point-trace/unproject/project/camera push-pop-get-set and face-to-shape helpers now also route through `src/legacy_canvas_projection_services.*` instead of staying inline in diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 17e0d5b9..df1921b6 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 -1490 lines, with `src/canvas_modes.cpp` still large at about 1014 lines. +1271 lines, with `src/canvas_modes.cpp` still large at about 720 lines. Current slice: - Canvas state-management helpers for picking, clear/clear-all, layer @@ -130,6 +130,15 @@ Current slice: `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. +- `Canvas::layer_merge(...)`, `Canvas::flood_fill(...)`, and + `Canvas::FloodData::apply()` now also live in + `src/legacy_canvas_layer_services.cpp` instead of staying inline in + `src/canvas.cpp`, which trims another coherent retained layer/fill workflow + pocket from the live canvas shell. +- The `CanvasModePen` and `CanvasModeLine` interaction families now also live + in `src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in + `src/canvas_modes.cpp`, which materially thins another retained pen/line + interaction pocket from the broader canvas/render hotspot family. Write scope: - `src/canvas.cpp` @@ -728,6 +737,10 @@ Current slice: `src/legacy_ui_node_event.*` instead of staying inline in `src/node.cpp`, which materially thins the base scene-graph event shell without changing its public surface. +- `Node::parse_attributes(...)` now also routes through + `src/legacy_ui_node_attributes.*` instead of staying inline in `src/node.cpp`, + which trims another coherent XML/Yoga attribute decoding pocket from the base + scene-graph shell without changing its public surface. Write scope: - `src/node.cpp` diff --git a/src/canvas.cpp b/src/canvas.cpp index 227df769..4e5fb16e 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1238,243 +1238,6 @@ void Canvas::stroke_start(glm::vec3 point, float pressure) m_mixer.unbindFramebuffer(); m_show_tmp = true; } -void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index -{ - m_dirty = false; - - App::I->render_task([&] - { - // prepare common states - apply_canvas_viewport(0, 0, m_width, m_height); - apply_canvas_capability(blend_state(), false); - - for (int i = 0; i < 6; i++) - { - if (!m_layers[source_idx]->face(i)) - continue; // no stroke on this face, skip it - - m_layers[dest_idx]->rtt(i).bindFramebuffer(); - - auto& lbox = m_layers[dest_idx]->box(i); - lbox = glm::vec4( - glm::min(xy(m_layers[source_idx]->box(i)), xy(lbox)), - glm::max(zw(m_layers[source_idx]->box(i)), zw(lbox)) - ); - m_layers[dest_idx]->face(i) = true; - - // copy to tmp2 for layer blending - set_active_texture_unit(0); - m_tex2[i].bind(); - copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height); - m_tex2[i].unbind(); - - m_sampler.bind(0); - m_sampler_nearest.bind(1); - { - pp::panopainter::setup_legacy_stroke_composite_shader( - pp::panopainter::LegacyStrokeCompositeUniforms { - .resolution = m_size, - .mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f), - .layer_alpha = m_layers[source_idx]->m_opacity, - .alpha_lock = false, - .mask_enabled = false, - .use_fragcoord = false, - .blend_mode = m_layers[source_idx]->m_blend_mode, - .use_dual = false, - .dual_alpha = 0.0f, - .use_pattern = false, - }); - - set_active_texture_unit(0); - m_tex2[i].bind(); - set_active_texture_unit(1); - m_layers[source_idx]->rtt(i).bindTexture(); - m_plane.draw_fill(); - m_layers[source_idx]->rtt(i).unbindTexture(); - set_active_texture_unit(0); - m_tex2[i].unbind(); - } - - m_layers[dest_idx]->rtt(i).unbindFramebuffer(); - } - }); -} - -void Canvas::flood_fill(int layer, int plane, std::vector pos, FloodData& plane_data, - float threshold, glm::vec4 dest_color, std::unique_ptr& source_color) -{ - struct adj_t - { - int plane; - bool flipx; - bool flipy; - bool flipcoord; - adj_t(int plane, bool flipx, bool flipy, int flipcoord) : - plane(plane), flipx(flipx), flipy(flipy), flipcoord(flipcoord) { } - glm::ivec2 compute(glm::ivec2 p, glm::ivec2 sz) const - { - glm::ivec2 ret; - ret[flipcoord] = flipx ? sz.x - p.x : p.x; - ret[1 - flipcoord] = flipy ? sz.y - p.y : p.y; - return ret; - } - }; - - LOG("flood_fill plane %d", plane); - - auto& rtt = m_layers[layer]->rtt(plane); - auto sz = rtt.getSize(); - - if (!plane_data.mask[plane]) - { - plane_data.mask[plane] = std::make_unique((size_t)sz.x * sz.y); - plane_data.rgb[plane] = std::unique_ptr( - reinterpret_cast(m_layers[layer]->rtt(plane).readTextureData())); - plane_data.bb[plane] = { sz.x, sz.y, 0, 0 }; - plane_data.dirty[plane] = false; - plane_data.layer = m_layers[layer]; - } - auto& mask = plane_data.mask[plane]; - auto& rgb = plane_data.rgb[plane]; - - if (!source_color) - source_color = std::make_unique(rgb[pos.back().y * sz.x + pos.back().x]); - const glm::vec4 c = *source_color; - - std::array, 4> edges; - static const std::array adj[6] = { - // front - { - adj_t(3, 1, 0, 0), - adj_t(4, 1, 1, 0), - adj_t(1, 0, 0, 0), - adj_t(5, 1, 0, 0), - }, - // right - { - adj_t(0, 1, 0, 0), - adj_t(4, 1, 0, 1), - adj_t(2, 0, 0, 0), - adj_t(5, 0, 0, 1), - }, - // back - { - adj_t(1, 1, 0, 0), - adj_t(4, 0, 0, 0), - adj_t(3, 0, 0, 0), - adj_t(5, 0, 1, 0), - }, - // left - { - adj_t(2, 1, 0, 0), - adj_t(4, 0, 1, 1), - adj_t(0, 0, 0, 0), - adj_t(5, 1, 1, 1), - }, - // top - { - adj_t(1, 1, 1, 1), - adj_t(0, 1, 1, 0), - adj_t(3, 1, 0, 1), - adj_t(2, 0, 1, 0), - }, - // bottom - { - adj_t(1, 0, 0, 1), - adj_t(2, 0, 0, 0), - adj_t(3, 0, 1, 1), - adj_t(0, 1, 0, 0), - }, - }; - - auto test = [&](glm::ivec2 p, bool set_color) -> bool - { - int i = p.y * sz.x + p.x; - if (p.x < 0) - { - edges[0].push_back(adj[plane][0].compute({ -p.x, p.y }, sz)); - return false; - } - else if (p.x >= sz.x) - { - edges[2].push_back(adj[plane][2].compute({ sz.x - p.x + 1, p.y }, sz)); - return false; - } - else if (p.y < 0) - { - edges[3].push_back(adj[plane][3].compute({ p.x, -p.y }, sz)); - return false; - } - else if (p.y >= sz.y) - { - edges[1].push_back(adj[plane][1].compute({ p.x, sz.y - p.y + 1 }, sz)); - return false; - } - if (!mask[i]) - { - if (c.a == 0 && glm::abs(rgb[i].a - c.a) < threshold || - c.a > 0 && rgb[i].a > 0 && glm::distance(glm::vec3(c), glm::vec3(rgb[i])) < threshold) - { - if (set_color) - { - mask[i] = true; - rgb[i] = dest_color * 255.f; - plane_data.dirty[plane] = true; - glm::vec2 bb_min = glm::min((glm::vec2)p, xy(plane_data.bb[plane])); - // add 1 pixel to the end because 1 pixel has min(0) and max(1) - glm::vec2 bb_max = glm::max((glm::vec2)p + glm::vec2(1), zw(plane_data.bb[plane])); - plane_data.bb[plane] = { bb_min, bb_max }; - } - pos.push_back(p); - } - return true; - } - return false; - }; - - while (!pos.empty()) - { - auto p = pos.back(); - pos.pop_back(); - if(!test(p + glm::ivec2( 0, 0), true)) - continue; - test(p + glm::ivec2(-1, 0), false); - test(p + glm::ivec2(1, 0), false); - test(p + glm::ivec2(0, 1), false); - test(p + glm::ivec2(0, -1), false); - //for (int x = -1; x <= 1; x++) - // for (int y = -1; y <= 1; y++) - // if (x != 0 && y != 0) - // test(p + glm::ivec2(x, y), false); - } - - for (int i = 0; i < 4; i++) - { - if (!edges[i].empty()) - { - flood_fill(layer, adj[plane][i].plane, edges[i], plane_data, threshold, dest_color, source_color); - //LOG("continue to plane %d -> %d", plane, adj[plane][i].plane); - } - } -} - -void Canvas::FloodData::apply() -{ - for (int plane = 0; plane < 6; plane++) - { - if (!dirty[plane]) - continue; - auto& rtt = layer->rtt(plane); - App::I->render_task([&] - { - rtt.updateRgba8(0, 0, rtt.getWidth(), rtt.getHeight(), rgb[plane].get()); - }); - layer->face(plane) = true; - layer->box(plane) = box_union(layer->box(plane), bb[plane]); - } - Canvas::I->m_unsaved = true; -} - void Canvas::destroy() { for (int i = 0; i < 6; i++) diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index 1cda8926..c84e232c 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -10,7 +10,6 @@ #include "node_canvas.h" #include "legacy_canvas_draw_merge_services.h" #include "legacy_canvas_stroke_composite_services.h" -#include "legacy_canvas_stroke_preview_services.h" #include "legacy_canvas_mode_helpers.h" #include "legacy_ui_overlay_services.h" #include "legacy_ui_gl_dispatch.h" @@ -24,317 +23,9 @@ using pp::legacy_canvas_mode::apply_canvas_mode_capability; using pp::legacy_canvas_mode::apply_canvas_mode_viewport; using pp::legacy_canvas_mode::query_canvas_mode_capability; using pp::legacy_canvas_mode::query_canvas_mode_read_framebuffer; -using pp::legacy_canvas_mode::read_canvas_mode_pixel; -using pp::legacy_canvas_mode::set_canvas_mode_active_texture_unit; //////////////////////////////////////////////////////////////////// -void CanvasModePen::on_GestureEvent(GestureEvent* ge) -{ - m_draw_tip = false; -} - -void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) -{ - m_draw_tip = App::I->draws_canvas_tip_for_input(me->m_source, me->m_type); - m_draw_outline = true; - - if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) - return; - - me->m_pressure = App::I->adjust_canvas_input_pressure(me->m_pressure); - - switch (me->m_type) - { - case kEventType::MouseDownL: - if (!App::I->keys[(int)kKey::KeySpacebar]) - { - if (App::I->keys[(int)kKey::KeyAlt] || m_picking) - { - m_picking = true; - Canvas::I->pick_start(); - glm::vec4 pix = Canvas::I->pick_get(loc); - Canvas::I->m_current_brush->m_tip_color = pix; - App::I->brush_update(true, false); - } - else - { - App::I->render_task_async([loc, pr = me->m_pressure] - { - Canvas::I->stroke_start({ loc, 0 }, pr); - }); - m_drawing = true; - } - m_dragging = true; - node->mouse_capture(); - } - break; - case kEventType::MouseUpL: - if (m_dragging && !m_picking) - { - pp::panopainter::release_legacy_mouse_capture(*node); - App::I->render_task_async([] - { - Canvas::I->stroke_end(); - }); - } - if (m_dragging && m_picking) - { - pp::panopainter::release_legacy_mouse_capture(*node); - glm::vec4 pix = Canvas::I->pick_get(loc); - Canvas::I->m_current_brush->m_tip_color = pix; - App::I->brush_update(true, false); - Canvas::I->pick_end(); - } - m_drawing = false; - m_dragging = false; - m_picking = false; - break; - case kEventType::MouseDownR: - if (App::I->keys[(int)kKey::KeyAlt]) - { - m_resizing = true; - m_dragging = true; - m_size_pos_start = m_cur_pos; - auto curve = App::I->stroke->m_curves[App::I->stroke->m_tip_size]; - m_size_value_start = curve.to_slider(Canvas::I->m_current_brush->m_tip_size); - node->mouse_capture(); - } - break; - case kEventType::MouseUpR: - if (m_dragging && m_resizing) - { - pp::panopainter::release_legacy_mouse_capture(*node); - m_dragging = false; - m_resizing = false; - } - break; - case kEventType::MouseMove: - if (m_dragging && !m_picking && !m_resizing) - { - App::I->render_task_async([loc, pr=me->m_pressure] - { - Canvas::I->stroke_update({ loc, 0 }, pr); - }); - } - if (m_dragging && m_picking) - { - glm::vec4 pix = Canvas::I->pick_get(loc); - Canvas::I->m_current_brush->m_tip_color = pix; - App::I->brush_update(true, false); - } - if (m_dragging && m_resizing) - { - auto diff = m_cur_pos - m_size_pos_start; - auto curve = App::I->stroke->m_curves[App::I->stroke->m_tip_size]; - Canvas::I->m_current_brush->m_tip_size = glm::max(curve.to_value(m_size_value_start + diff.x * 0.001f), 0.001f); - App::I->brush_update(true, true); - } - m_cur_pos = loc; - break; - case kEventType::MouseCancel: - if (m_dragging) - { - App::I->render_task_async([] - { - Canvas::I->stroke_cancel(); - }); - m_dragging = false; - pp::panopainter::release_legacy_mouse_capture(*node); - } - if (m_picking) - m_picking = false; - if (m_resizing) - m_resizing = false; - break; - default: - break; - } -} - -void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) -{ - if (m_draw_tip) - { - const auto& brush = Canvas::I->m_current_brush; - auto pos = m_resizing ? m_size_pos_start : m_cur_pos; - //if (App::I->keys[(int)kKey::KeyAlt] && !m_resizing) - // pos.x = pos.x - brush->m_tip_size * .5f; - float tip_scale_fix = 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f)); - float tip_angle = brush->m_tip_angle * (float)(M_PI * 2.0); - glm::vec2 tip_scale = App::I->zoom * brush->m_tip_scale * - glm::vec2(brush->m_tip_size * tip_scale_fix) * - glm::vec2(brush->m_tip_flipx ? -1 : 1, brush->m_tip_flipy ? -1.f : 1.f) * - glm::vec2((brush->m_tip_aspect <= 0.5 ? brush->m_tip_aspect * 2.f : 1.f), - (brush->m_tip_aspect > 0.5 ? 1.f - (brush->m_tip_aspect - .5f) * 2.f : 1.f)); - glm::vec2 tip_offset = glm::vec2(0); - auto tip_color = glm::vec4(glm::vec3(brush->m_tip_color), 1.f); - if (Canvas::I->m_current_stroke) - { - const auto& s = Canvas::I->m_current_stroke->m_prev_sample; - if (s.size > 0.f) - { - tip_scale = App::I->zoom * (brush->m_tip_size * tip_scale_fix) * s.scale; - tip_angle = s.angle; - tip_offset = s.pos - s.origin; - tip_color = glm::vec4(s.col, s.flow); - } - } - glm::u8vec4 pixel; - std::int32_t fb_width = static_cast(App::I->width); - std::int32_t fb_height = static_cast(App::I->height); - if (node->m_density != 1.f) - { - fb_width = node->m_rtt.getWidth(); - fb_height = node->m_rtt.getHeight(); - } - read_canvas_mode_pixel( - static_cast((pos.x / App::I->width) * fb_width), - static_cast(((App::I->height - pos.y - 1) / App::I->height) * fb_height), - pixel); - bool outline = glm::min(tip_scale.x, tip_scale.y) < 20 || m_resizing ? false : m_draw_outline; - pp::panopainter::setup_legacy_vr_stroke_preview_shader({ - .texture_slot = 0, - .alpha = brush->m_tip_flow * brush->m_tip_opacity, - .draw_outline = outline, - .color = outline ? glm::vec4(1.f - glm::vec3(pixel) / 255.f, 1.f) : tip_color, - .mvp = glm::scale(glm::vec3(1, -1, 1)) * - ortho * - glm::translate(glm::vec3(pos + tip_offset, 0)) * - glm::eulerAngleZ(tip_angle) * - glm::scale(glm::vec3(tip_scale, 1)), - }); - const bool blend = query_canvas_mode_capability(pp::renderer::gl::blend_state()); - apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true); - set_canvas_mode_active_texture_unit(0); - auto& tex = *brush->m_tip_texture; - tex.bind(); - Canvas::I->m_sampler_brush.bind(0); - Canvas::I->m_plane.draw_fill(); - tex.unbind(); - if (!blend) apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false); - } -} - -void CanvasModePen::leave(kCanvasMode next) -{ -} - -void CanvasModePen::enter(kCanvasMode prev) -{ - m_cur_pos = Canvas::I->m_cur_pos; -} - -//////////////////////////////////////////////////////////////////// - -void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc) -{ - m_draw_tip = App::I->draws_canvas_tip_for_input(me->m_source, me->m_type); - if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) - return; - switch (me->m_type) - { - case kEventType::MouseDownL: - node->mouse_capture(); - m_dragging = true; - m_drag_start = loc; - m_drag_pos = loc; - break; - case kEventType::MouseUpL: - pp::panopainter::release_legacy_mouse_capture(*node); - if (m_dragging) - { - App::I->render_task_async([=] - { - Canvas::I->stroke_start({ m_drag_start, 0 }, 1.f); - Canvas::I->stroke_update({ m_drag_pos, 0 }, 1.f); - Canvas::I->stroke_draw(); - Canvas::I->stroke_end(); - }); - } - m_dragging = false; - break; - case kEventType::MouseMove: - if (m_dragging) - m_drag_pos = loc; - m_cur_pos = loc; - break; - case kEventType::MouseCancel: - pp::panopainter::release_legacy_mouse_capture(*node); - m_dragging = false; - break; - default: - break; - } -} - -void CanvasModeLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) -{ - if (m_dragging) - { - pp::panopainter::setup_legacy_vr_color_shader({ - .color = Canvas::I->m_current_brush->m_tip_color, - .mvp = ortho, - }); - static glm::vec4 AB[2]; - AB[0] = { m_drag_start, 0, 1 }; - AB[1] = { m_drag_pos, 0, 1 }; - AB[0].y = Canvas::I->m_box.w - AB[0].y - 1; // invert Y - AB[1].y = Canvas::I->m_box.w - AB[1].y - 1; // invert Y - m_line.update_vertices(AB); - m_line.draw_stroke(); - } - else if (m_draw_tip) - { - const auto& brush = Canvas::I->m_current_brush; - float tip_scale_fix = 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f)); - float tip_angle = brush->m_tip_angle * (float)(M_PI * 2.0); - glm::vec2 tip_scale = App::I->zoom * brush->m_tip_scale * - glm::vec2(brush->m_tip_size * tip_scale_fix) * - glm::vec2(brush->m_tip_flipx ? -1 : 1, brush->m_tip_flipy ? -1.f : 1.f) * - glm::vec2((brush->m_tip_aspect <= 0.5 ? brush->m_tip_aspect * 2.f : 1.f), - (brush->m_tip_aspect > 0.5 ? 1.f - (brush->m_tip_aspect - .5f) * 2.f : 1.f)); - auto tip_color = glm::vec4(glm::vec3(brush->m_tip_color), 1.f); - pp::panopainter::setup_legacy_vr_stroke_preview_shader({ - .texture_slot = 0, - .alpha = brush->m_tip_flow * brush->m_tip_opacity, - .draw_outline = false, - .color = tip_color, - .mvp = glm::scale(glm::vec3(1, -1, 1)) * - ortho * - glm::translate(glm::vec3(m_cur_pos, 0)) * - glm::eulerAngleZ(tip_angle) * - glm::scale(glm::vec3(tip_scale, 1)), - }); - const bool blend = query_canvas_mode_capability(pp::renderer::gl::blend_state()); - apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true); - set_canvas_mode_active_texture_unit(0); - auto& tex = *brush->m_tip_texture; - tex.bind(); - Canvas::I->m_sampler_brush.bind(0); - Canvas::I->m_plane.draw_fill(); - tex.unbind(); - if (!blend) apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false); - } -} - -void CanvasModeLine::init() -{ - m_line.create(); -} - -void CanvasModeLine::enter(kCanvasMode prev) -{ - m_cur_pos = Canvas::I->m_cur_pos; -} - -void CanvasModeLine::leave(kCanvasMode next) -{ -} - -//////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////// void CanvasModeGrid::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { diff --git a/src/legacy_canvas_layer_services.cpp b/src/legacy_canvas_layer_services.cpp new file mode 100644 index 00000000..80a5f073 --- /dev/null +++ b/src/legacy_canvas_layer_services.cpp @@ -0,0 +1,258 @@ +#include "pch.h" +#include "canvas.h" +#include "app.h" +#include "legacy_canvas_stroke_composite_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "renderer_gl/opengl_capabilities.h" +#include "util.h" + +#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 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"); +} + +void apply_canvas_capability(std::uint32_t state, bool enabled) +{ + pp::legacy::ui_gl::set_capability(state, enabled, "Canvas"); +} + +} // namespace + +void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index +{ + m_dirty = false; + + App::I->render_task([&] + { + apply_canvas_viewport(0, 0, m_width, m_height); + apply_canvas_capability(blend_state(), false); + + for (int i = 0; i < 6; i++) + { + if (!m_layers[source_idx]->face(i)) + continue; + + m_layers[dest_idx]->rtt(i).bindFramebuffer(); + + auto& lbox = m_layers[dest_idx]->box(i); + lbox = glm::vec4( + glm::min(xy(m_layers[source_idx]->box(i)), xy(lbox)), + glm::max(zw(m_layers[source_idx]->box(i)), zw(lbox)) + ); + m_layers[dest_idx]->face(i) = true; + + set_active_texture_unit(0); + m_tex2[i].bind(); + copy_framebuffer_to_texture_2d(0, 0, 0, 0, m_width, m_height); + m_tex2[i].unbind(); + + m_sampler.bind(0); + m_sampler_nearest.bind(1); + { + pp::panopainter::setup_legacy_stroke_composite_shader( + pp::panopainter::LegacyStrokeCompositeUniforms { + .resolution = m_size, + .mvp = glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f), + .layer_alpha = m_layers[source_idx]->m_opacity, + .alpha_lock = false, + .mask_enabled = false, + .use_fragcoord = false, + .blend_mode = m_layers[source_idx]->m_blend_mode, + .use_dual = false, + .dual_alpha = 0.0f, + .use_pattern = false, + }); + + set_active_texture_unit(0); + m_tex2[i].bind(); + set_active_texture_unit(1); + m_layers[source_idx]->rtt(i).bindTexture(); + m_plane.draw_fill(); + m_layers[source_idx]->rtt(i).unbindTexture(); + set_active_texture_unit(0); + m_tex2[i].unbind(); + } + + m_layers[dest_idx]->rtt(i).unbindFramebuffer(); + } + }); +} + +void Canvas::flood_fill(int layer, int plane, std::vector pos, FloodData& plane_data, + float threshold, glm::vec4 dest_color, std::unique_ptr& source_color) +{ + struct adj_t + { + int plane; + bool flipx; + bool flipy; + bool flipcoord; + adj_t(int plane, bool flipx, bool flipy, int flipcoord) : + plane(plane), flipx(flipx), flipy(flipy), flipcoord(flipcoord) { } + glm::ivec2 compute(glm::ivec2 p, glm::ivec2 sz) const + { + glm::ivec2 ret; + ret[flipcoord] = flipx ? sz.x - p.x : p.x; + ret[1 - flipcoord] = flipy ? sz.y - p.y : p.y; + return ret; + } + }; + + LOG("flood_fill plane %d", plane); + + auto& rtt = m_layers[layer]->rtt(plane); + auto sz = rtt.getSize(); + + if (!plane_data.mask[plane]) + { + plane_data.mask[plane] = std::make_unique((size_t)sz.x * sz.y); + plane_data.rgb[plane] = std::unique_ptr( + reinterpret_cast(m_layers[layer]->rtt(plane).readTextureData())); + plane_data.bb[plane] = { sz.x, sz.y, 0, 0 }; + plane_data.dirty[plane] = false; + plane_data.layer = m_layers[layer]; + } + auto& mask = plane_data.mask[plane]; + auto& rgb = plane_data.rgb[plane]; + + if (!source_color) + source_color = std::make_unique(rgb[pos.back().y * sz.x + pos.back().x]); + const glm::vec4 c = *source_color; + + std::array, 4> edges; + static const std::array adj[6] = { + { + adj_t(3, 1, 0, 0), + adj_t(4, 1, 1, 0), + adj_t(1, 0, 0, 0), + adj_t(5, 1, 0, 0), + }, + { + adj_t(0, 1, 0, 0), + adj_t(4, 1, 0, 1), + adj_t(2, 0, 0, 0), + adj_t(5, 0, 0, 1), + }, + { + adj_t(1, 1, 0, 0), + adj_t(4, 0, 0, 0), + adj_t(3, 0, 0, 0), + adj_t(5, 0, 1, 0), + }, + { + adj_t(2, 1, 0, 0), + adj_t(4, 0, 1, 1), + adj_t(0, 0, 0, 0), + adj_t(5, 1, 1, 1), + }, + { + adj_t(1, 1, 1, 1), + adj_t(0, 1, 1, 0), + adj_t(3, 1, 0, 1), + adj_t(2, 0, 1, 0), + }, + { + adj_t(1, 0, 0, 1), + adj_t(2, 0, 0, 0), + adj_t(3, 0, 1, 1), + adj_t(0, 1, 0, 0), + }, + }; + + auto test = [&](glm::ivec2 p, bool set_color) -> bool + { + int i = p.y * sz.x + p.x; + if (p.x < 0) + { + edges[0].push_back(adj[plane][0].compute({ -p.x, p.y }, sz)); + return false; + } + else if (p.x >= sz.x) + { + edges[2].push_back(adj[plane][2].compute({ sz.x - p.x + 1, p.y }, sz)); + return false; + } + else if (p.y < 0) + { + edges[3].push_back(adj[plane][3].compute({ p.x, -p.y }, sz)); + return false; + } + else if (p.y >= sz.y) + { + edges[1].push_back(adj[plane][1].compute({ p.x, sz.y - p.y + 1 }, sz)); + return false; + } + if (!mask[i]) + { + if (c.a == 0 && glm::abs(rgb[i].a - c.a) < threshold || + c.a > 0 && rgb[i].a > 0 && glm::distance(glm::vec3(c), glm::vec3(rgb[i])) < threshold) + { + if (set_color) + { + mask[i] = true; + rgb[i] = dest_color * 255.f; + plane_data.dirty[plane] = true; + glm::vec2 bb_min = glm::min((glm::vec2)p, xy(plane_data.bb[plane])); + glm::vec2 bb_max = glm::max((glm::vec2)p + glm::vec2(1), zw(plane_data.bb[plane])); + plane_data.bb[plane] = { bb_min, bb_max }; + } + pos.push_back(p); + } + return true; + } + return false; + }; + + while (!pos.empty()) + { + auto p = pos.back(); + pos.pop_back(); + if (!test(p + glm::ivec2(0, 0), true)) + continue; + test(p + glm::ivec2(-1, 0), false); + test(p + glm::ivec2(1, 0), false); + test(p + glm::ivec2(0, 1), false); + test(p + glm::ivec2(0, -1), false); + } + + for (int i = 0; i < 4; i++) + { + if (!edges[i].empty()) + { + flood_fill(layer, adj[plane][i].plane, edges[i], plane_data, threshold, dest_color, source_color); + } + } +} + +void Canvas::FloodData::apply() +{ + for (int plane = 0; plane < 6; plane++) + { + if (!dirty[plane]) + continue; + auto& rtt = layer->rtt(plane); + App::I->render_task([&] + { + rtt.updateRgba8(0, 0, rtt.getWidth(), rtt.getHeight(), rgb[plane].get()); + }); + layer->face(plane) = true; + layer->box(plane) = box_union(layer->box(plane), bb[plane]); + } + Canvas::I->m_unsaved = true; +} diff --git a/src/legacy_canvas_mode_pen_line.cpp b/src/legacy_canvas_mode_pen_line.cpp new file mode 100644 index 00000000..3fd00790 --- /dev/null +++ b/src/legacy_canvas_mode_pen_line.cpp @@ -0,0 +1,315 @@ +#include "pch.h" + +#include "canvas_modes.h" + +#include "app.h" +#include "canvas.h" +#include "legacy_canvas_mode_helpers.h" +#include "legacy_canvas_stroke_preview_services.h" +#include "legacy_ui_overlay_services.h" +#include "legacy_ui_gl_dispatch.h" +#include "renderer_gl/opengl_capabilities.h" + +using pp::legacy_canvas_mode::apply_canvas_mode_capability; +using pp::legacy_canvas_mode::query_canvas_mode_capability; +using pp::legacy_canvas_mode::read_canvas_mode_pixel; +using pp::legacy_canvas_mode::set_canvas_mode_active_texture_unit; + +void CanvasModePen::on_GestureEvent(GestureEvent* ge) +{ + m_draw_tip = false; +} + +void CanvasModePen::on_MouseEvent(MouseEvent* me, glm::vec2& loc) +{ + m_draw_tip = App::I->draws_canvas_tip_for_input(me->m_source, me->m_type); + m_draw_outline = true; + + if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) + return; + + me->m_pressure = App::I->adjust_canvas_input_pressure(me->m_pressure); + + switch (me->m_type) + { + case kEventType::MouseDownL: + if (!App::I->keys[(int)kKey::KeySpacebar]) + { + if (App::I->keys[(int)kKey::KeyAlt] || m_picking) + { + m_picking = true; + Canvas::I->pick_start(); + glm::vec4 pix = Canvas::I->pick_get(loc); + Canvas::I->m_current_brush->m_tip_color = pix; + App::I->brush_update(true, false); + } + else + { + App::I->render_task_async([loc, pr = me->m_pressure] + { + Canvas::I->stroke_start({ loc, 0 }, pr); + }); + m_drawing = true; + } + m_dragging = true; + node->mouse_capture(); + } + break; + case kEventType::MouseUpL: + if (m_dragging && !m_picking) + { + pp::panopainter::release_legacy_mouse_capture(*node); + App::I->render_task_async([] { + Canvas::I->stroke_end(); + }); + } + if (m_dragging && m_picking) + { + pp::panopainter::release_legacy_mouse_capture(*node); + glm::vec4 pix = Canvas::I->pick_get(loc); + Canvas::I->m_current_brush->m_tip_color = pix; + App::I->brush_update(true, false); + Canvas::I->pick_end(); + } + m_drawing = false; + m_dragging = false; + m_picking = false; + break; + case kEventType::MouseDownR: + if (App::I->keys[(int)kKey::KeyAlt]) + { + m_resizing = true; + m_dragging = true; + m_size_pos_start = m_cur_pos; + auto curve = App::I->stroke->m_curves[App::I->stroke->m_tip_size]; + m_size_value_start = curve.to_slider(Canvas::I->m_current_brush->m_tip_size); + node->mouse_capture(); + } + break; + case kEventType::MouseUpR: + if (m_dragging && m_resizing) + { + pp::panopainter::release_legacy_mouse_capture(*node); + m_dragging = false; + m_resizing = false; + } + break; + case kEventType::MouseMove: + if (m_dragging && !m_picking && !m_resizing) + { + App::I->render_task_async([loc, pr=me->m_pressure] + { + Canvas::I->stroke_update({ loc, 0 }, pr); + }); + } + if (m_dragging && m_picking) + { + glm::vec4 pix = Canvas::I->pick_get(loc); + Canvas::I->m_current_brush->m_tip_color = pix; + App::I->brush_update(true, false); + } + if (m_dragging && m_resizing) + { + auto diff = m_cur_pos - m_size_pos_start; + auto curve = App::I->stroke->m_curves[App::I->stroke->m_tip_size]; + Canvas::I->m_current_brush->m_tip_size = glm::max(curve.to_value(m_size_value_start + diff.x * 0.001f), 0.001f); + App::I->brush_update(true, true); + } + m_cur_pos = loc; + break; + case kEventType::MouseCancel: + if (m_dragging) + { + App::I->render_task_async([] { + Canvas::I->stroke_cancel(); + }); + m_dragging = false; + pp::panopainter::release_legacy_mouse_capture(*node); + } + if (m_picking) + m_picking = false; + if (m_resizing) + m_resizing = false; + break; + default: + break; + } +} + +void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) +{ + if (m_draw_tip) + { + const auto& brush = Canvas::I->m_current_brush; + auto pos = m_resizing ? m_size_pos_start : m_cur_pos; + //if (App::I->keys[(int)kKey::KeyAlt] && !m_resizing) + // pos.x = pos.x - brush->m_tip_size * .5f; + float tip_scale_fix = 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f)); + float tip_angle = brush->m_tip_angle * (float)(M_PI * 2.0); + glm::vec2 tip_scale = App::I->zoom * brush->m_tip_scale * + glm::vec2(brush->m_tip_size * tip_scale_fix) * + glm::vec2(brush->m_tip_flipx ? -1 : 1, brush->m_tip_flipy ? -1.f : 1.f) * + glm::vec2((brush->m_tip_aspect <= 0.5 ? brush->m_tip_aspect * 2.f : 1.f), + (brush->m_tip_aspect > 0.5 ? 1.f - (brush->m_tip_aspect - .5f) * 2.f : 1.f)); + glm::vec2 tip_offset = glm::vec2(0); + auto tip_color = glm::vec4(glm::vec3(brush->m_tip_color), 1.f); + if (Canvas::I->m_current_stroke) + { + const auto& s = Canvas::I->m_current_stroke->m_prev_sample; + if (s.size > 0.f) + { + tip_scale = App::I->zoom * (brush->m_tip_size * tip_scale_fix) * s.scale; + tip_angle = s.angle; + tip_offset = s.pos - s.origin; + tip_color = glm::vec4(s.col, s.flow); + } + } + glm::u8vec4 pixel; + std::int32_t fb_width = static_cast(App::I->width); + std::int32_t fb_height = static_cast(App::I->height); + if (node->m_density != 1.f) + { + fb_width = node->m_rtt.getWidth(); + fb_height = node->m_rtt.getHeight(); + } + read_canvas_mode_pixel( + static_cast((pos.x / App::I->width) * fb_width), + static_cast(((App::I->height - pos.y - 1) / App::I->height) * fb_height), + pixel); + bool outline = glm::min(tip_scale.x, tip_scale.y) < 20 || m_resizing ? false : m_draw_outline; + pp::panopainter::setup_legacy_vr_stroke_preview_shader({ + .texture_slot = 0, + .alpha = brush->m_tip_flow * brush->m_tip_opacity, + .draw_outline = outline, + .color = outline ? glm::vec4(1.f - glm::vec3(pixel) / 255.f, 1.f) : tip_color, + .mvp = glm::scale(glm::vec3(1, -1, 1)) * + ortho * + glm::translate(glm::vec3(pos + tip_offset, 0)) * + glm::eulerAngleZ(tip_angle) * + glm::scale(glm::vec3(tip_scale, 1)), + }); + const bool blend = query_canvas_mode_capability(pp::renderer::gl::blend_state()); + apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true); + set_canvas_mode_active_texture_unit(0); + auto& tex = *brush->m_tip_texture; + tex.bind(); + Canvas::I->m_sampler_brush.bind(0); + Canvas::I->m_plane.draw_fill(); + tex.unbind(); + if (!blend) apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false); + } +} + +void CanvasModePen::leave(kCanvasMode next) +{ +} + +void CanvasModePen::enter(kCanvasMode prev) +{ + m_cur_pos = Canvas::I->m_cur_pos; +} + +void CanvasModeLine::on_MouseEvent(MouseEvent* me, glm::vec2& loc) +{ + m_draw_tip = App::I->draws_canvas_tip_for_input(me->m_source, me->m_type); + if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch) + return; + switch (me->m_type) + { + case kEventType::MouseDownL: + node->mouse_capture(); + m_dragging = true; + m_drag_start = loc; + m_drag_pos = loc; + break; + case kEventType::MouseUpL: + pp::panopainter::release_legacy_mouse_capture(*node); + if (m_dragging) + { + App::I->render_task_async([=] + { + Canvas::I->stroke_start({ m_drag_start, 0 }, 1.f); + Canvas::I->stroke_update({ m_drag_pos, 0 }, 1.f); + Canvas::I->stroke_draw(); + Canvas::I->stroke_end(); + }); + } + m_dragging = false; + break; + case kEventType::MouseMove: + if (m_dragging) + m_drag_pos = loc; + m_cur_pos = loc; + break; + case kEventType::MouseCancel: + pp::panopainter::release_legacy_mouse_capture(*node); + m_dragging = false; + break; + default: + break; + } +} + +void CanvasModeLine::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) +{ + if (m_dragging) + { + pp::panopainter::setup_legacy_vr_color_shader({ + .color = Canvas::I->m_current_brush->m_tip_color, + .mvp = ortho, + }); + static glm::vec4 AB[2]; + AB[0] = { m_drag_start, 0, 1 }; + AB[1] = { m_drag_pos, 0, 1 }; + AB[0].y = Canvas::I->m_box.w - AB[0].y - 1; // invert Y + AB[1].y = Canvas::I->m_box.w - AB[1].y - 1; // invert Y + m_line.update_vertices(AB); + m_line.draw_stroke(); + } + else if (m_draw_tip) + { + const auto& brush = Canvas::I->m_current_brush; + float tip_scale_fix = 1.f / glm::tan(glm::radians(Canvas::I->m_cam_fov * 0.5f)); + float tip_angle = brush->m_tip_angle * (float)(M_PI * 2.0); + glm::vec2 tip_scale = App::I->zoom * brush->m_tip_scale * + glm::vec2(brush->m_tip_size * tip_scale_fix) * + glm::vec2(brush->m_tip_flipx ? -1 : 1, brush->m_tip_flipy ? -1.f : 1.f) * + glm::vec2((brush->m_tip_aspect <= 0.5 ? brush->m_tip_aspect * 2.f : 1.f), + (brush->m_tip_aspect > 0.5 ? 1.f - (brush->m_tip_aspect - .5f) * 2.f : 1.f)); + auto tip_color = glm::vec4(glm::vec3(brush->m_tip_color), 1.f); + pp::panopainter::setup_legacy_vr_stroke_preview_shader({ + .texture_slot = 0, + .alpha = brush->m_tip_flow * brush->m_tip_opacity, + .draw_outline = false, + .color = tip_color, + .mvp = glm::scale(glm::vec3(1, -1, 1)) * + ortho * + glm::translate(glm::vec3(m_cur_pos, 0)) * + glm::eulerAngleZ(tip_angle) * + glm::scale(glm::vec3(tip_scale, 1)), + }); + const bool blend = query_canvas_mode_capability(pp::renderer::gl::blend_state()); + apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true); + set_canvas_mode_active_texture_unit(0); + auto& tex = *brush->m_tip_texture; + tex.bind(); + Canvas::I->m_sampler_brush.bind(0); + Canvas::I->m_plane.draw_fill(); + tex.unbind(); + if (!blend) apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false); + } +} + +void CanvasModeLine::init() +{ + m_line.create(); +} + +void CanvasModeLine::enter(kCanvasMode prev) +{ + m_cur_pos = Canvas::I->m_cur_pos; +} + +void CanvasModeLine::leave(kCanvasMode next) +{ +} diff --git a/src/legacy_ui_node_attributes.cpp b/src/legacy_ui_node_attributes.cpp new file mode 100644 index 00000000..2440bc13 --- /dev/null +++ b/src/legacy_ui_node_attributes.cpp @@ -0,0 +1,207 @@ +#include "pch.h" +#include "legacy_ui_node_attributes.h" + +#include "log.h" +#include "node.h" + +namespace pp::panopainter { + +void parse_legacy_ui_node_attribute(Node& node, kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + switch (ka) + { + case kAttribute::id: + node.m_nodeID_s = attr->Value(); + node.m_nodeID = const_hash(attr->Value()); + break; + case kAttribute::Width: + if (strcmp(attr->Value(), "auto") == 0) + { + YGNodeStyleSetWidth(node.y_node, YGUndefined); + YGNodeStyleSetWidthPercent(node.y_node, YGUndefined); + node.auto_width = true; + } + else + { + if (strchr(attr->Value(), '%')) + YGNodeStyleSetWidthPercent(node.y_node, attr->FloatValue()); + else + YGNodeStyleSetWidth(node.y_node, attr->FloatValue()); + node.auto_width = false; + } + break; + case kAttribute::MinWidth: + YGNodeStyleSetMinWidth(node.y_node, attr->FloatValue()); + break; + case kAttribute::MaxWidth: + YGNodeStyleSetMaxWidth(node.y_node, attr->FloatValue()); + break; + case kAttribute::Height: + if (strcmp(attr->Value(), "auto") == 0) + { + YGNodeStyleSetHeight(node.y_node, YGUndefined); + YGNodeStyleSetHeightPercent(node.y_node, YGUndefined); + node.auto_height = true; + } + else + { + if (strchr(attr->Value(), '%')) + YGNodeStyleSetHeightPercent(node.y_node, attr->FloatValue()); + else + YGNodeStyleSetHeight(node.y_node, attr->FloatValue()); + node.auto_height = false; + } + break; + case kAttribute::MinHeight: + YGNodeStyleSetMinHeight(node.y_node, attr->FloatValue()); + break; + case kAttribute::MaxHeight: + YGNodeStyleSetMaxHeight(node.y_node, attr->FloatValue()); + break; + case kAttribute::Grow: + YGNodeStyleSetFlexGrow(node.y_node, attr->FloatValue()); + break; + case kAttribute::Shrink: + YGNodeStyleSetFlexShrink(node.y_node, attr->FloatValue()); + break; + case kAttribute::FlexDir: + { + YGFlexDirection dir = YGFlexDirectionRow; + if (strcmp("col", attr->Value()) == 0) + dir = YGFlexDirectionColumn; + else if (strcmp("col-reverse", attr->Value()) == 0) + dir = YGFlexDirectionColumnReverse; + else if (strcmp("row", attr->Value()) == 0) + dir = YGFlexDirectionRow; + else if (strcmp("row-reverse", attr->Value()) == 0) + dir = YGFlexDirectionRowReverse; + YGNodeStyleSetFlexDirection(node.y_node, dir); + break; + } + case kAttribute::FlexWrap: + YGNodeStyleSetFlexWrap(node.y_node, attr->IntValue() ? YGWrapWrap : YGWrapNoWrap); + break; + case kAttribute::Justify: + { + YGJustify v = YGJustifyFlexStart; + if (strcmp("center", attr->Value()) == 0) + v = YGJustifyCenter; + else if (strcmp("flex-start", attr->Value()) == 0) + v = YGJustifyFlexStart; + else if (strcmp("flex-end", attr->Value()) == 0) + v = YGJustifyFlexEnd; + else if (strcmp("space-around", attr->Value()) == 0) + v = YGJustifySpaceAround; + else if (strcmp("space-between", attr->Value()) == 0) + v = YGJustifySpaceBetween; + YGNodeStyleSetJustifyContent(node.y_node, v); + break; + } + case kAttribute::Align: + { + YGAlign v = YGAlignStretch; + if (strcmp("stretch", attr->Value()) == 0) + v = YGAlignStretch; + else if (strcmp("flex-start", attr->Value()) == 0) + v = YGAlignFlexStart; + else if (strcmp("flex-end", attr->Value()) == 0) + v = YGAlignFlexEnd; + else if (strcmp("center", attr->Value()) == 0) + v = YGAlignCenter; + YGNodeStyleSetAlignItems(node.y_node, v); + break; + } + case kAttribute::Positioning: + { + YGPositionType v = YGPositionTypeRelative; + if (strcmp("relative", attr->Value()) == 0) + v = YGPositionTypeRelative; + else if (strcmp("absolute", attr->Value()) == 0) + v = YGPositionTypeAbsolute; + YGNodeStyleSetPositionType(node.y_node, v); + break; + } + case kAttribute::Position: + { + glm::vec4 v; + int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w); + if (n == 2) + { + YGNodeStyleSetPosition(node.y_node, YGEdgeLeft, v.x); + YGNodeStyleSetPosition(node.y_node, YGEdgeTop, v.y); + } + else + { + YGNodeStyleSetPadding(node.y_node, YGEdgeLeft, v.x); + YGNodeStyleSetPadding(node.y_node, YGEdgeTop, v.y); + YGNodeStyleSetPadding(node.y_node, YGEdgeRight, v.z); + YGNodeStyleSetPadding(node.y_node, YGEdgeBottom, v.w); + } + break; + } + case kAttribute::Padding: + { + glm::vec4 pad; + int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); + if (n == 1) + { + YGNodeStyleSetPadding(node.y_node, YGEdgeTop, pad.x); + YGNodeStyleSetPadding(node.y_node, YGEdgeRight, pad.x); + YGNodeStyleSetPadding(node.y_node, YGEdgeBottom, pad.x); + YGNodeStyleSetPadding(node.y_node, YGEdgeLeft, pad.x); + } + else + { + YGNodeStyleSetPadding(node.y_node, YGEdgeTop, pad.x); + YGNodeStyleSetPadding(node.y_node, YGEdgeRight, pad.y); + YGNodeStyleSetPadding(node.y_node, YGEdgeBottom, pad.z); + YGNodeStyleSetPadding(node.y_node, YGEdgeLeft, pad.w); + } + break; + } + case kAttribute::Margin: + { + glm::vec4 pad; + int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); + if (n == 1) + { + YGNodeStyleSetMargin(node.y_node, YGEdgeTop, pad.x); + YGNodeStyleSetMargin(node.y_node, YGEdgeRight, pad.x); + YGNodeStyleSetMargin(node.y_node, YGEdgeBottom, pad.x); + YGNodeStyleSetMargin(node.y_node, YGEdgeLeft, pad.x); + } + else + { + YGNodeStyleSetMargin(node.y_node, YGEdgeTop, pad.x); + YGNodeStyleSetMargin(node.y_node, YGEdgeRight, pad.y); + YGNodeStyleSetMargin(node.y_node, YGEdgeBottom, pad.z); + YGNodeStyleSetMargin(node.y_node, YGEdgeLeft, pad.w); + } + break; + } + case kAttribute::FloodEvents: + node.m_flood_events = attr->BoolValue(); + break; + case kAttribute::MouseCapture: + node.m_force_mouse_capture = attr->BoolValue(); + break; + case kAttribute::AspectRatio: + YGNodeStyleSetAspectRatio(node.y_node, attr->FloatValue()); + break; + case kAttribute::RTL: + if (strcmp("rtl", attr->Value()) == 0) + node.SetRTL(YGDirectionRTL); + else if (strcmp("ltr", attr->Value()) == 0) + node.SetRTL(YGDirectionLTR); + else if (strcmp("inherit", attr->Value()) == 0) + node.SetRTL(YGDirectionInherit); + else + { + LOG("Attribute %s for RTL unrecognized", attr->Value()); + } + default: + break; + } +} + +} // namespace pp::panopainter diff --git a/src/legacy_ui_node_attributes.h b/src/legacy_ui_node_attributes.h new file mode 100644 index 00000000..bc1b3de5 --- /dev/null +++ b/src/legacy_ui_node_attributes.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace tinyxml2 { +class XMLAttribute; +} + +class Node; + +enum class kAttribute : uint16_t; + +namespace pp::panopainter { + +void parse_legacy_ui_node_attribute(Node& node, kAttribute ka, const tinyxml2::XMLAttribute* attr); + +} // namespace pp::panopainter diff --git a/src/node.cpp b/src/node.cpp index 971f79ea..2d3df0c6 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "app.h" #include "log.h" +#include "legacy_ui_node_attributes.h" #include "legacy_ui_node_event.h" #include "legacy_ui_node_loader.h" #include "node.h" @@ -832,200 +833,7 @@ YGDirection Node::GetRTL() void Node::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) { - switch (ka) - { - case kAttribute::id: - m_nodeID_s = attr->Value(); - m_nodeID = const_hash(attr->Value()); - break; - case kAttribute::Width: - if (strcmp(attr->Value(), "auto") == 0) - { - YGNodeStyleSetWidth(y_node, YGUndefined); - YGNodeStyleSetWidthPercent(y_node, YGUndefined); - auto_width = true; - } - else - { - if (strchr(attr->Value(), '%')) - YGNodeStyleSetWidthPercent(y_node, attr->FloatValue()); - else - YGNodeStyleSetWidth(y_node, attr->FloatValue()); - auto_width = false; - } - break; - case kAttribute::MinWidth: - YGNodeStyleSetMinWidth(y_node, attr->FloatValue()); - break; - case kAttribute::MaxWidth: - YGNodeStyleSetMaxWidth(y_node, attr->FloatValue()); - break; - case kAttribute::Height: - if (strcmp(attr->Value(), "auto") == 0) - { - YGNodeStyleSetHeight(y_node, YGUndefined); - YGNodeStyleSetHeightPercent(y_node, YGUndefined); - auto_height = true; - } - else - { - if (strchr(attr->Value(), '%')) - YGNodeStyleSetHeightPercent(y_node, attr->FloatValue()); - else - YGNodeStyleSetHeight(y_node, attr->FloatValue()); - auto_height = false; - } - break; - case kAttribute::MinHeight: - YGNodeStyleSetMinHeight(y_node, attr->FloatValue()); - break; - case kAttribute::MaxHeight: - YGNodeStyleSetMaxHeight(y_node, attr->FloatValue()); - break; - case kAttribute::Grow: - YGNodeStyleSetFlexGrow(y_node, attr->FloatValue()); - break; - case kAttribute::Shrink: - YGNodeStyleSetFlexShrink(y_node, attr->FloatValue()); - break; - case kAttribute::FlexDir: - { - YGFlexDirection dir = YGFlexDirectionRow; - if (strcmp("col", attr->Value()) == 0) - dir = YGFlexDirectionColumn; - else if (strcmp("col-reverse", attr->Value()) == 0) - dir = YGFlexDirectionColumnReverse; - else if (strcmp("row", attr->Value()) == 0) - dir = YGFlexDirectionRow; - else if (strcmp("row-reverse", attr->Value()) == 0) - dir = YGFlexDirectionRowReverse; - YGNodeStyleSetFlexDirection(y_node, dir); - break; - } - case kAttribute::FlexWrap: - YGNodeStyleSetFlexWrap(y_node, attr->IntValue() ? YGWrapWrap : YGWrapNoWrap); - break; - case kAttribute::Justify: - { - YGJustify v = YGJustifyFlexStart; - if (strcmp("center", attr->Value()) == 0) - v = YGJustifyCenter; - else if (strcmp("flex-start", attr->Value()) == 0) - v = YGJustifyFlexStart; - else if (strcmp("flex-end", attr->Value()) == 0) - v = YGJustifyFlexEnd; - else if (strcmp("space-around", attr->Value()) == 0) - v = YGJustifySpaceAround; - else if (strcmp("space-between", attr->Value()) == 0) - v = YGJustifySpaceBetween; - YGNodeStyleSetJustifyContent(y_node, v); - break; - } - case kAttribute::Align: - { - YGAlign v = YGAlignStretch; - if (strcmp("stretch", attr->Value()) == 0) - v = YGAlignStretch; - else if (strcmp("flex-start", attr->Value()) == 0) - v = YGAlignFlexStart; - else if (strcmp("flex-end", attr->Value()) == 0) - v = YGAlignFlexEnd; - else if (strcmp("center", attr->Value()) == 0) - v = YGAlignCenter; - YGNodeStyleSetAlignItems(y_node, v); - break; - } - case kAttribute::Positioning: - { - YGPositionType v = YGPositionTypeRelative; - if (strcmp("relative", attr->Value()) == 0) - v = YGPositionTypeRelative; - else if (strcmp("absolute", attr->Value()) == 0) - v = YGPositionTypeAbsolute; - YGNodeStyleSetPositionType(y_node, v); - break; - } - case kAttribute::Position: - { - glm::vec4 v; - int n = sscanf(attr->Value(), "%f %f %f %f", &v.x, &v.y, &v.z, &v.w); - if (n == 2) - { - YGNodeStyleSetPosition(y_node, YGEdgeLeft, v.x); - YGNodeStyleSetPosition(y_node, YGEdgeTop, v.y); - } - else - { - YGNodeStyleSetPadding(y_node, YGEdgeLeft, v.x); - YGNodeStyleSetPadding(y_node, YGEdgeTop, v.y); - YGNodeStyleSetPadding(y_node, YGEdgeRight, v.z); - YGNodeStyleSetPadding(y_node, YGEdgeBottom, v.w); - } - break; - } - case kAttribute::Padding: - { - glm::vec4 pad; - int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); - if (n == 1) - { - YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x); - YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.x); - YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.x); - YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.x); - } - else - { - YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x); - YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.y); - YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.z); - YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.w); - } - break; - } - case kAttribute::Margin: - { - glm::vec4 pad; - int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); - if (n == 1) - { - YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x); - YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.x); - YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.x); - YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.x); - } - else - { - YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x); - YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.y); - YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.z); - YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.w); - } - break; - } - case kAttribute::FloodEvents: - m_flood_events = attr->BoolValue(); - break; - case kAttribute::MouseCapture: - m_force_mouse_capture = attr->BoolValue(); - break; - case kAttribute::AspectRatio: - YGNodeStyleSetAspectRatio(y_node, attr->FloatValue()); - break; - case kAttribute::RTL: - if (strcmp("rtl", attr->Value()) == 0) - SetRTL(YGDirectionRTL); - else if (strcmp("ltr", attr->Value()) == 0) - SetRTL(YGDirectionLTR); - else if (strcmp("inherit", attr->Value()) == 0) - SetRTL(YGDirectionInherit); - else - { - LOG("Attribute %s for RTL unrecognized", attr->Value()); - } - default: - break; - } + pp::panopainter::parse_legacy_ui_node_attribute(*this, ka, attr); } void Node::load_internal(const tinyxml2::XMLElement* x_node, bool skip_children /*= false*/)