Extract canvas layer flows, pen modes, and node attributes
This commit is contained in:
258
src/legacy_canvas_layer_services.cpp
Normal file
258
src/legacy_canvas_layer_services.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user