Extract canvas layer flows, pen modes, and node attributes

This commit is contained in:
2026-06-16 20:09:25 +02:00
parent 9b2a0d9c30
commit d6a7512b94
10 changed files with 832 additions and 747 deletions

View File

@@ -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<glm::ivec2> pos, FloodData& plane_data,
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& 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<bool[]>((size_t)sz.x * sz.y);
plane_data.rgb[plane] = std::unique_ptr<glm::u8vec4[]>(
reinterpret_cast<glm::u8vec4*>(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<glm::vec4>(rgb[pos.back().y * sz.x + pos.back().x]);
const glm::vec4 c = *source_color;
std::array<std::vector<glm::ivec2>, 4> edges;
static const std::array<adj_t, 4> 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++)

View File

@@ -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<std::int32_t>(App::I->width);
std::int32_t fb_height = static_cast<std::int32_t>(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<std::int32_t>((pos.x / App::I->width) * fb_width),
static_cast<std::int32_t>(((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)
{

View File

@@ -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 <array>
#include <cstdint>
#include <vector>
namespace {
GLenum blend_state()
{
return static_cast<GLenum>(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<glm::ivec2> pos, FloodData& plane_data,
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& 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<bool[]>((size_t)sz.x * sz.y);
plane_data.rgb[plane] = std::unique_ptr<glm::u8vec4[]>(
reinterpret_cast<glm::u8vec4*>(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<glm::vec4>(rgb[pos.back().y * sz.x + pos.back().x]);
const glm::vec4 c = *source_color;
std::array<std::vector<glm::ivec2>, 4> edges;
static const std::array<adj_t, 4> 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;
}

View File

@@ -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<std::int32_t>(App::I->width);
std::int32_t fb_height = static_cast<std::int32_t>(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<std::int32_t>((pos.x / App::I->width) * fb_width),
static_cast<std::int32_t>(((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)
{
}

View File

@@ -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

View File

@@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
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

View File

@@ -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*/)