add undo action for bucket fill
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
class Action
|
class Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class Direction { Undo, Redo } m_direction;
|
||||||
bool was_saved = false;
|
bool was_saved = false;
|
||||||
virtual void run() = 0;
|
virtual void run() = 0;
|
||||||
virtual void undo() = 0;
|
virtual void undo() = 0;
|
||||||
|
|||||||
@@ -1342,7 +1342,7 @@ void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, std::map<int, std::unique_ptr<bool[]>>& plane_mask,
|
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)
|
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& source_color)
|
||||||
{
|
{
|
||||||
struct adj_t
|
struct adj_t
|
||||||
@@ -1365,12 +1365,19 @@ void Canvas::flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, std::
|
|||||||
LOG("flood_fill plane %d", plane);
|
LOG("flood_fill plane %d", plane);
|
||||||
|
|
||||||
auto& rtt = m_layers[layer]->m_rtt[plane];
|
auto& rtt = m_layers[layer]->m_rtt[plane];
|
||||||
auto sz = glm::ivec2(rtt.getWidth(), rtt.getHeight());
|
auto sz = rtt.getSize();
|
||||||
auto rgb = reinterpret_cast<glm::u8vec4*>(m_layers[layer]->m_rtt[plane].readTextureData());
|
|
||||||
|
|
||||||
if (plane_mask.find(plane) == plane_mask.end())
|
if (!plane_data.mask[plane])
|
||||||
plane_mask.insert({ plane, std::make_unique<bool[]>((size_t)sz.x * sz.y) });
|
{
|
||||||
auto& mask = plane_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]->m_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)
|
if (!source_color)
|
||||||
source_color = std::make_unique<glm::vec4>(rgb[pos.back().y * sz.x + pos.back().x]);
|
source_color = std::make_unique<glm::vec4>(rgb[pos.back().y * sz.x + pos.back().x]);
|
||||||
@@ -1378,35 +1385,35 @@ void Canvas::flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, std::
|
|||||||
|
|
||||||
std::array<std::vector<glm::ivec2>, 4> edges;
|
std::array<std::vector<glm::ivec2>, 4> edges;
|
||||||
static const std::array<adj_t, 4> adj[6] = {
|
static const std::array<adj_t, 4> adj[6] = {
|
||||||
// front - ok
|
// front
|
||||||
{
|
{
|
||||||
adj_t(3, 1, 0, 0),
|
adj_t(3, 1, 0, 0),
|
||||||
adj_t(4, 1, 1, 0),
|
adj_t(4, 1, 1, 0),
|
||||||
adj_t(1, 0, 0, 0),
|
adj_t(1, 0, 0, 0),
|
||||||
adj_t(5, 1, 0, 0),
|
adj_t(5, 1, 0, 0),
|
||||||
},
|
},
|
||||||
// right - ok
|
// right
|
||||||
{
|
{
|
||||||
adj_t(0, 1, 0, 0),
|
adj_t(0, 1, 0, 0),
|
||||||
adj_t(4, 1, 0, 1),
|
adj_t(4, 1, 0, 1),
|
||||||
adj_t(2, 0, 0, 0),
|
adj_t(2, 0, 0, 0),
|
||||||
adj_t(5, 0, 0, 1),
|
adj_t(5, 0, 0, 1),
|
||||||
},
|
},
|
||||||
// back - ok
|
// back
|
||||||
{
|
{
|
||||||
adj_t(1, 1, 0, 0),
|
adj_t(1, 1, 0, 0),
|
||||||
adj_t(4, 0, 0, 0),
|
adj_t(4, 0, 0, 0),
|
||||||
adj_t(3, 0, 0, 0),
|
adj_t(3, 0, 0, 0),
|
||||||
adj_t(5, 0, 1, 0),
|
adj_t(5, 0, 1, 0),
|
||||||
},
|
},
|
||||||
// left - ok
|
// left
|
||||||
{
|
{
|
||||||
adj_t(2, 1, 0, 0),
|
adj_t(2, 1, 0, 0),
|
||||||
adj_t(4, 0, 1, 1),
|
adj_t(4, 0, 1, 1),
|
||||||
adj_t(0, 0, 0, 0),
|
adj_t(0, 0, 0, 0),
|
||||||
adj_t(5, 1, 1, 1),
|
adj_t(5, 1, 1, 1),
|
||||||
},
|
},
|
||||||
// top - ok
|
// top
|
||||||
{
|
{
|
||||||
adj_t(1, 1, 1, 1),
|
adj_t(1, 1, 1, 1),
|
||||||
adj_t(0, 1, 1, 0),
|
adj_t(0, 1, 1, 0),
|
||||||
@@ -1454,6 +1461,10 @@ void Canvas::flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, std::
|
|||||||
{
|
{
|
||||||
mask[i] = true;
|
mask[i] = true;
|
||||||
rgb[i] = dest_color * 255.f;
|
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, zw(plane_data.bb[plane]));
|
||||||
|
plane_data.bb[plane] = { bb_min, bb_max };
|
||||||
}
|
}
|
||||||
pos.push_back(p);
|
pos.push_back(p);
|
||||||
}
|
}
|
||||||
@@ -1478,20 +1489,30 @@ void Canvas::flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, std::
|
|||||||
// test(p + glm::ivec2(x, y), false);
|
// test(p + glm::ivec2(x, y), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
rtt.bindTexture();
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sz.x, sz.y, GL_RGBA, GL_UNSIGNED_BYTE, rgb);
|
|
||||||
rtt.unbindTexture();
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
if (!edges[i].empty())
|
if (!edges[i].empty())
|
||||||
{
|
{
|
||||||
flood_fill(layer, adj[plane][i].plane, edges[i], plane_mask, threshold, dest_color, source_color);
|
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);
|
//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->m_rtt[plane];
|
||||||
|
rtt.bindTexture();
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rtt.getWidth(), rtt.getHeight(),
|
||||||
|
GL_RGBA, GL_UNSIGNED_BYTE, rgb[plane].get());
|
||||||
|
rtt.unbindTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Canvas::resize(int width, int height)
|
void Canvas::resize(int width, int height)
|
||||||
{
|
{
|
||||||
m_width = width;
|
m_width = width;
|
||||||
|
|||||||
12
src/canvas.h
12
src/canvas.h
@@ -66,6 +66,16 @@ class Canvas
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct FloodData
|
||||||
|
{
|
||||||
|
std::shared_ptr<Layer> layer;
|
||||||
|
std::unique_ptr<bool[]> mask[6];
|
||||||
|
std::unique_ptr<glm::u8vec4[]> rgb[6] = SIXPLETTE(0);
|
||||||
|
bool dirty[6] = SIXPLETTE(false);
|
||||||
|
glm::vec4 bb[6];
|
||||||
|
void apply();
|
||||||
|
};
|
||||||
|
|
||||||
Plane m_plane;
|
Plane m_plane;
|
||||||
Plane m_plane_brush;
|
Plane m_plane_brush;
|
||||||
DynamicShape m_brush_shape;
|
DynamicShape m_brush_shape;
|
||||||
@@ -166,7 +176,7 @@ public:
|
|||||||
void layer_add(std::string name, std::shared_ptr<Layer> layer = nullptr, int index = 0);
|
void layer_add(std::string name, std::shared_ptr<Layer> layer = nullptr, int index = 0);
|
||||||
void layer_order(int idx, int pos);
|
void layer_order(int idx, int pos);
|
||||||
void layer_merge(int source_idx, int dest_idx);
|
void layer_merge(int source_idx, int dest_idx);
|
||||||
void flood_fill(int layer, int plane, std::vector<glm::ivec2> pos, std::map<int, std::unique_ptr<bool[]>>& plane_mask,
|
void 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);
|
float threshold, glm::vec4 dest_color, std::unique_ptr<glm::vec4>& source_color);
|
||||||
void stroke_start(glm::vec3 point, float pressure);
|
void stroke_start(glm::vec3 point, float pressure);
|
||||||
void stroke_update(glm::vec3 point, float pressure);
|
void stroke_update(glm::vec3 point, float pressure);
|
||||||
|
|||||||
@@ -1549,11 +1549,6 @@ void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasModeFloodFill::init()
|
|
||||||
{
|
|
||||||
TextureManager::load(m_cursor_path.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||||
@@ -1566,6 +1561,48 @@ void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
|||||||
if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch)
|
if (Canvas::I->m_touch_lock && me->m_source == kEventSource::Touch)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
struct ActionFloodFill : public Action
|
||||||
|
{
|
||||||
|
std::shared_ptr<Layer> m_layer;
|
||||||
|
std::shared_ptr<Layer::Snapshot> m_snap;
|
||||||
|
glm::ivec2 m_pos;
|
||||||
|
glm::vec4 m_color;
|
||||||
|
float m_threshold;
|
||||||
|
int m_layer_index;
|
||||||
|
int m_plane;
|
||||||
|
virtual void run() override { }
|
||||||
|
virtual size_t memory() override { return m_snap->memsize(); }
|
||||||
|
virtual Action* get_redo() override
|
||||||
|
{
|
||||||
|
auto a = new ActionFloodFill;
|
||||||
|
a->m_direction = (Direction)(1 - (int)m_direction);
|
||||||
|
a->m_layer = m_layer;
|
||||||
|
a->m_snap = m_snap;
|
||||||
|
a->m_pos = m_pos;
|
||||||
|
a->m_color = m_color;
|
||||||
|
a->m_layer_index = m_layer_index;
|
||||||
|
a->m_threshold = m_threshold;
|
||||||
|
a->m_plane = m_plane;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
virtual void undo() override
|
||||||
|
{
|
||||||
|
if (m_direction == Direction::Undo)
|
||||||
|
{
|
||||||
|
m_layer->restore(*m_snap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Canvas::FloodData plane_data;
|
||||||
|
std::unique_ptr<glm::vec4> color;
|
||||||
|
Canvas::I->flood_fill(m_layer_index, m_plane, { m_pos },
|
||||||
|
plane_data, m_threshold, m_color, color);
|
||||||
|
plane_data.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
switch (me->m_type)
|
switch (me->m_type)
|
||||||
{
|
{
|
||||||
case kEventType::MouseUpL:
|
case kEventType::MouseUpL:
|
||||||
@@ -1574,10 +1611,23 @@ void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
|||||||
int plane;
|
int plane;
|
||||||
if (Canvas::I->point_trace(loc, ro, rd, hit, pos, n, plane))
|
if (Canvas::I->point_trace(loc, ro, rd, hit, pos, n, plane))
|
||||||
{
|
{
|
||||||
std::map<int, std::unique_ptr<bool[]>> plane_mask;
|
Canvas::FloodData plane_data;
|
||||||
std::unique_ptr<glm::vec4> color;
|
std::unique_ptr<glm::vec4> color;
|
||||||
Canvas::I->flood_fill(Canvas::I->m_current_layer_idx, plane, { (glm::ivec2)pos },
|
Canvas::I->flood_fill(Canvas::I->m_current_layer_idx, plane, { (glm::ivec2)pos },
|
||||||
plane_mask, m_tool->get_threshold(), Canvas::I->m_current_brush->m_tip_color, color);
|
plane_data, m_tool->get_threshold(), Canvas::I->m_current_brush->m_tip_color, color);
|
||||||
|
|
||||||
|
auto a = new ActionFloodFill;
|
||||||
|
a->m_direction = Action::Direction::Undo;
|
||||||
|
a->m_layer = plane_data.layer;
|
||||||
|
a->m_snap = std::make_shared<Layer::Snapshot>(plane_data.layer->snapshot());
|
||||||
|
a->m_pos = (glm::ivec2)pos;
|
||||||
|
a->m_color = Canvas::I->m_current_brush->m_tip_color;
|
||||||
|
a->m_layer_index = Canvas::I->m_current_layer_idx;
|
||||||
|
a->m_threshold = m_tool->get_threshold();
|
||||||
|
a->m_plane = plane;
|
||||||
|
ActionManager::add(a);
|
||||||
|
|
||||||
|
plane_data.apply();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1585,6 +1635,11 @@ void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CanvasModeFloodFill::init()
|
||||||
|
{
|
||||||
|
TextureManager::load(m_cursor_path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
void CanvasModeFloodFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
void CanvasModeFloodFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||||
{
|
{
|
||||||
if (m_draw_tip)
|
if (m_draw_tip)
|
||||||
|
|||||||
@@ -136,7 +136,6 @@ public:
|
|||||||
|
|
||||||
struct ActionLayerMerge : public Action
|
struct ActionLayerMerge : public Action
|
||||||
{
|
{
|
||||||
enum class Direction { Undo, Redo } m_direction;
|
|
||||||
std::shared_ptr<Layer::Snapshot> m_snap;
|
std::shared_ptr<Layer::Snapshot> m_snap;
|
||||||
std::shared_ptr<Layer> m_layer;
|
std::shared_ptr<Layer> m_layer;
|
||||||
std::shared_ptr<NodeLayer> m_layer_node;
|
std::shared_ptr<NodeLayer> m_layer_node;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public:
|
|||||||
GLuint getTextureID() const { return texID; }
|
GLuint getTextureID() const { return texID; }
|
||||||
int getWidth() const { return w; }
|
int getWidth() const { return w; }
|
||||||
int getHeight() const { return h; }
|
int getHeight() const { return h; }
|
||||||
|
glm::ivec2 getSize() const { return { w, h }; }
|
||||||
int bytes() const { return w * h * 4; }
|
int bytes() const { return w * h * 4; }
|
||||||
int stride() const { return w * 4; }
|
int stride() const { return w * 4; }
|
||||||
GLuint getFBO() const { return fboID; }
|
GLuint getFBO() const { return fboID; }
|
||||||
|
|||||||
Reference in New Issue
Block a user