pandemonium_engine/modules/paint/deprecated/paint_canvas.cpp

656 lines
16 KiB
C++

/*
Copyright (c) 2019 Flairieve
Copyright (c) 2020-2022 cobrapitz
Copyright (c) 2022 Péter Magyar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "paint_canvas.h"
#include "../ui/paint_canvas_background.h"
#include "../ui/paint_canvas_outline.h"
#include "../paint_utilities.h"
#include "../ui/paint_visual_grid.h"
#include "scene/gui/control.h"
#include "scene/gui/texture_rect.h"
#include "paint_canvas_layer.h"
#include "../paint_icons/paint_icons.h"
static float scale = 1;
template <class T>
static Ref<Texture> make_icon(T p_src) {
Ref<ImageTexture> texture(memnew(ImageTexture));
Ref<Image> img = memnew(Image(p_src));
if (scale > 1) {
Size2 orig_size = Size2(img->get_width(), img->get_height());
img->convert(Image::FORMAT_RGBA8);
img->expand_x2_hq2x();
if (scale != 2.0) {
img->resize(orig_size.x * scale, orig_size.y * scale);
}
} else if (scale < 1) {
Size2 orig_size = Size2(img->get_width(), img->get_height());
img->convert(Image::FORMAT_RGBA8);
img->resize(orig_size.x * scale, orig_size.y * scale);
}
texture->create_from_image(img, ImageTexture::FLAG_FILTER);
return texture;
}
void PaintCanvasOld::_process(float delta) {
if (!is_visible_in_tree()) {
return;
}
Vector2 mouse_position = get_local_mouse_position();
Rect2 rect = Rect2(Vector2(0, 0), get_size());
mouse_in_region = rect.has_point(mouse_position);
}
void PaintCanvasOld::_draw() {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
layer->update_texture();
}
preview_layer->update_texture();
tool_layer->update_texture();
}
int PaintCanvasOld::get_pixel_size() const {
return _pixel_size;
}
void PaintCanvasOld::set_pixel_size(const int size) {
_pixel_size = size;
set_grid_size(_grid_size);
set_big_grid_size(_big_grid_size);
set_canvas_width(_canvas_width);
set_canvas_height(_canvas_height);
}
int PaintCanvasOld::get_grid_size() const {
return _grid_size;
}
void PaintCanvasOld::set_grid_size(const int size) {
_grid_size = size;
if (grid) {
int s = size * _pixel_size;
grid->set_grid_size(s);
}
}
int PaintCanvasOld::get_big_grid_size() const {
return _big_grid_size;
}
void PaintCanvasOld::set_big_grid_size(const int size) {
_big_grid_size = size;
if (big_grid) {
int s = size * _pixel_size;
big_grid->set_size(Size2(s, s));
}
}
int PaintCanvasOld::get_canvas_width() const {
return _canvas_width;
}
void PaintCanvasOld::set_canvas_width(const int val) {
_canvas_width = val;
Size2 s = get_size();
s.x = _canvas_width * _pixel_size;
set_size(s);
}
int PaintCanvasOld::get_canvas_height() const {
return _canvas_height;
}
void PaintCanvasOld::set_canvas_height(const int val) {
_canvas_height = val;
Size2 s = get_size();
s.y = _canvas_height * _pixel_size;
set_size(s);
}
void PaintCanvasOld::toggle_alpha_locked(const String &layer_name) {
Ref<PaintCanvasLayer> layer = find_layer_by_name(layer_name);
if (layer.is_valid()) {
layer->toggle_alpha_locked();
}
}
bool PaintCanvasOld::is_alpha_locked() {
if (!active_layer.is_valid()) {
return false;
}
return active_layer->alpha_locked;
}
Rect2 PaintCanvasOld::get_content_margin() {
Rect2 rect = Rect2(999999, 999999, -999999, -999999);
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> l = layers[i];
ERR_CONTINUE(!l.is_valid());
Rect2 r = l->image->get_used_rect();
if (r.position.x < rect.position.x) {
rect.position.x = r.position.x;
}
if (r.position.y < rect.position.y) {
rect.position.y = r.position.y;
}
if (r.size.x > rect.size.x) {
rect.size.x = r.size.x;
}
if (r.size.y > rect.size.y) {
rect.size.y = r.size.y;
}
}
return rect;
}
void PaintCanvasOld::crop_to_content() {
/*
var rect = get_content_margin()
#print(rect)
for layer in layers:
layer.image
# set_canvas_width(rect.size.x)
# set_canvas_height(rect.size.x)
# preview_layer.resize(width, height)
# tool_layer.resize(width, height)
# for layer in layers:
# layer.resize(width, height)
*/
}
Ref<PaintCanvasLayer> PaintCanvasOld::get_active_layer() {
return active_layer;
}
Ref<PaintCanvasLayer> PaintCanvasOld::get_preview_layer() {
return preview_layer;
}
void PaintCanvasOld::clear_active_layer() {
if (active_layer.is_valid()) {
active_layer->clear();
}
}
void PaintCanvasOld::clear_preview_layer() {
preview_layer->clear();
}
void PaintCanvasOld::clear_layer(const String &layer_name) {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> l = layers[i];
ERR_CONTINUE(!l.is_valid());
if (l->name == layer_name) {
l->clear();
return;
}
}
}
Ref<PaintCanvasLayer> PaintCanvasOld::remove_layer(const String &layer_name) {
// change current layer if the active layer is removed
Ref<PaintCanvasLayer> del_layer = find_layer_by_name(layer_name);
del_layer->clear();
if (del_layer == active_layer) {
if (layers.size() > 1) {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
if (layer == active_layer) {
continue;
}
active_layer = layer;
break;
}
} else {
active_layer.unref();
}
}
layers.erase(del_layer);
return active_layer;
}
Ref<PaintCanvasLayer> PaintCanvasOld::add_new_layer(const String &layer_name) {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
if (layer->name == layer_name) {
return layer;
}
}
Ref<PaintCanvasLayer> layer;
layer.instance();
layer->name = layer_name;
TextureRect *texture_rect = memnew(TextureRect);
texture_rect->set_name(layer_name);
canvas_layers->add_child(texture_rect, true);
texture_rect->set_expand(true);
texture_rect->set_anchors_and_margins_preset(Control::PRESET_WIDE);
//texture_rect->set_margin(Margin::MARGIN_RIGHT, 0);
//texture_rect->set_margin(Margin::MARGIN_BOTTOM, 0);
texture_rect->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
layer->create(texture_rect, _canvas_width, _canvas_height);
layers.push_back(layer);
if (!active_layer.is_valid()) {
active_layer = layer;
}
return layer;
}
Ref<PaintCanvasLayer> PaintCanvasOld::duplicate_layer(const String &layer_name, const String &new_layer_name) {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
if (layer->name == new_layer_name) {
return layer;
}
}
Ref<PaintCanvasLayer> dup_layer = find_layer_by_name(layer_name);
Ref<PaintCanvasLayer> layer = add_new_layer(new_layer_name);
layer->image->copy_internals_from(dup_layer->image);
return layer;
}
void PaintCanvasOld::toggle_layer_visibility(const String &layer_name) {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
if (layer->name == layer_name) {
layer->set_visible(!layer->get_visible());
return;
}
}
}
Ref<PaintCanvasLayer> PaintCanvasOld::find_layer_by_name(const String &layer_name) {
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
if (layer->name == layer_name) {
return layer;
}
}
return Ref<PaintCanvasLayer>();
}
void PaintCanvasOld::toggle_lock_layer(const String &layer_name) {
find_layer_by_name(layer_name)->toggle_lock();
}
bool PaintCanvasOld::is_active_layer_locked() {
return active_layer->locked;
}
void PaintCanvasOld::move_layer_forward(const String &layer_name) {
TextureRect *layer = find_layer_by_name(layer_name)->texture_rect_ref;
int new_idx = MAX(layer->get_index() - 1, 0);
canvas_layers->move_child(layer, new_idx);
}
void PaintCanvasOld::move_layer_back(const String &layer_name) {
TextureRect *layer = find_layer_by_name(layer_name)->texture_rect_ref;
canvas_layers->move_child(layer, layer->get_index() + 1);
}
void PaintCanvasOld::select_layer(const String &layer_name) {
active_layer = find_layer_by_name(layer_name);
}
void PaintCanvasOld::_on_mouse_entered() {
mouse_on_top = true;
}
void PaintCanvasOld::_on_mouse_exited() {
mouse_on_top = false;
}
bool PaintCanvasOld::is_inside_canvas(const int x, const int y) {
if (x < 0 || y < 0) {
return false;
}
if (x >= _canvas_width || y >= _canvas_height) {
return false;
}
return true;
}
//Note: Arrays are always passed by reference. To get a copy of an array which
// can be modified independently of the original array, use duplicate.
// (https://docs.godotengine.org/en/stable/classes/class_array.html)
void PaintCanvasOld::set_pixel_arr(const PoolVector2iArray &pixels, const Color &color) {
PoolVector2iArray::Read r = pixels.read();
for (int i = 0; i < pixels.size(); ++i) {
const Vector2i &pixel = r[i];
_set_pixel(active_layer, pixel.x, pixel.y, color);
}
}
void PaintCanvasOld::set_pixel_v(const Vector2i &pos, const Color &color) {
set_pixel(pos.x, pos.y, color);
}
void PaintCanvasOld::set_pixel(const int x, const int y, const Color &color) {
_set_pixel(active_layer, x, y, color);
}
void PaintCanvasOld::_set_pixel_v(Ref<PaintCanvasLayer> layer, const Vector2i &v, const Color &color) {
_set_pixel(layer, v.x, v.y, color);
}
void PaintCanvasOld::_set_pixel(Ref<PaintCanvasLayer> layer, const int x, const int y, const Color &color) {
if (!is_inside_canvas(x, y)) {
return;
}
layer->set_pixel(x, y, color);
}
Color PaintCanvasOld::get_pixel_v(const Vector2i &pos) {
return get_pixel(pos.x, pos.y);
}
Color PaintCanvasOld::get_pixel(const int x, const int y) {
if (active_layer.is_valid()) {
return active_layer->get_pixel(x, y);
}
return Color();
}
void PaintCanvasOld::set_preview_pixel_v(const Vector2i &pos, const Color &color) {
set_preview_pixel(pos.x, pos.y, color);
}
void PaintCanvasOld::set_preview_pixel(const int x, const int y, const Color &color) {
if (!is_inside_canvas(x, y)) {
return;
}
preview_layer->set_pixel(x, y, color);
}
Color PaintCanvasOld::get_preview_pixel_v(const Vector2i &pos) {
return get_preview_pixel(pos.x, pos.y);
}
Color PaintCanvasOld::get_preview_pixel(const int x, const int y) {
return preview_layer->get_pixel(x, y);
}
bool PaintCanvasOld::validate_pixel_v(const Vector2i &pos) const {
if (active_layer.is_valid()) {
return active_layer->validate_pixel_v(pos);
}
return false;
}
void PaintCanvasOld::toggle_grid() {
grid->set_visible(!grid->is_visible());
}
void PaintCanvasOld::show_grid() {
grid->show();
}
void PaintCanvasOld::hide_grid() {
grid->hide();
}
PoolVector2iArray PaintCanvasOld::select_color(const int p_x, const int p_y) {
PoolVector2iArray same_color_pixels;
Color color = get_pixel(p_x, p_y);
for (int x = 0; x < active_layer->layer_width; ++x) {
for (int y = 0; y < active_layer->layer_height; ++y) {
Color pixel_color = active_layer->get_pixel(x, y);
if (pixel_color == color) {
same_color_pixels.append(Vector2i(x, y));
}
}
}
return same_color_pixels;
}
PoolVector2iArray PaintCanvasOld::select_same_color(const int p_x, const int p_y) {
return get_neighbouring_pixels(p_x, p_y);
}
// returns array of Vector2
// yoinked from
// https://www.geeksforgeeks.org/flood-fill-algorithm-implement-fill-paint/
PoolVector2iArray PaintCanvasOld::get_neighbouring_pixels(const int pos_x, const int pos_y) {
PoolVector2iArray pixels;
PoolIntArray to_check_queue;
PoolIntArray checked_queue;
to_check_queue.append(PaintUtilities::to_1D(pos_x, pos_y, _canvas_width));
Color color = get_pixel(pos_x, pos_y);
while (!to_check_queue.empty()) {
int idx = to_check_queue[0];
to_check_queue.remove(0);
Vector2i p = PaintUtilities::to_2D(idx, _canvas_width);
if (checked_queue.contains(idx)) {
continue;
}
checked_queue.append(idx);
if (get_pixel(p.x, p.y) != color) {
continue;
}
// add to result
pixels.append(p);
// check neighbours
int x = p.x - 1;
int y = p.y;
if (is_inside_canvas(x, y)) {
idx = PaintUtilities::to_1D(x, y, _canvas_width);
to_check_queue.append(idx);
}
x = p.x + 1;
if (is_inside_canvas(x, y)) {
idx = PaintUtilities::to_1D(x, y, _canvas_width);
to_check_queue.append(idx);
}
x = p.x;
y = p.y - 1;
if (is_inside_canvas(x, y)) {
idx = PaintUtilities::to_1D(x, y, _canvas_width);
to_check_queue.append(idx);
}
y = p.y + 1;
if (is_inside_canvas(x, y)) {
idx = PaintUtilities::to_1D(x, y, _canvas_width);
to_check_queue.append(idx);
}
}
return pixels;
}
void PaintCanvasOld::resize(int width, int height) {
//if (get_canvas_width() == width && get_canvas_height() == height) {
// return;
// }
if (width < 0) {
width = 1;
}
if (height < 0) {
height = 1;
}
_canvas_width = width;
_canvas_height = height;
Size2 s;
s.x = _canvas_width * _pixel_size;
s.y = _canvas_height * _pixel_size;
set_size(s);
preview_layer->resize(width, height);
tool_layer->resize(width, height);
for (int i = 0; i < layers.size(); ++i) {
Ref<PaintCanvasLayer> layer = layers[i];
ERR_CONTINUE(!layer.is_valid());
layer->resize(width, height);
}
update();
}
void PaintCanvasOld::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_POSTINITIALIZE: {
connect("mouse_entered", this, "_on_mouse_entered");
connect("mouse_exited", this, "_on_mouse_exited");
//canvas_size = Vector2(int(rect_size.x / grid_size), int(rect_size.y / grid_size));
//pixel_size = canvas_size;
active_layer = add_new_layer("Layer1");
////hack
//_canvas_width = 0;
//_canvas_height = 0;
set_process(true);
} break;
case NOTIFICATION_ENTER_TREE: {
resize(64, 64);
} break;
case NOTIFICATION_PROCESS: {
_process(get_process_delta_time());
} break;
case NOTIFICATION_DRAW: {
_draw();
} break;
}
}
PaintCanvasOld::PaintCanvasOld() {
big_grid = nullptr;
_pixel_size = 16;
_canvas_width = 64;
_canvas_height = 64;
_grid_size = 16;
_big_grid_size = 10;
_can_draw = true;
symmetry_x = false;
symmetry_y = false;
mouse_in_region = false;
mouse_on_top = false;
canvas_background = memnew(PaintCanvasBackground);
canvas_background->set_texture(make_icon(grid_png));
canvas_background->set_expand(true);
canvas_background->set_stretch_mode(TextureRect::STRETCH_TILE);
//canvas_background->set_draw_behind_parent(true);
add_child(canvas_background);
canvas_layers = memnew(Control);
canvas_layers->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
canvas_layers->set_anchors_and_margins_preset(Control::PRESET_WIDE);
add_child(canvas_layers);
preview_layer_rect = memnew(TextureRect);
preview_layer_rect->set_expand(true);
preview_layer_rect->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
preview_layer_rect->set_anchors_and_margins_preset(Control::PRESET_WIDE);
add_child(preview_layer_rect);
tool_preview_layer_rect = memnew(TextureRect);
tool_preview_layer_rect->set_expand(true);
tool_preview_layer_rect->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
tool_preview_layer_rect->set_anchors_and_margins_preset(Control::PRESET_WIDE);
add_child(tool_preview_layer_rect);
grid = memnew(PaintVisualGrid);
grid->size = 4;
grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
add_child(grid);
canvas_outline = memnew(PaintCanvasOutline);
canvas_outline->color = Color(0, 1, 0, 1);
canvas_outline->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
add_child(canvas_outline);
preview_layer.instance();
tool_layer.instance();
preview_layer->name = "Preview";
tool_layer->name = "Tool";
preview_layer->create(preview_layer_rect, _canvas_width, _canvas_height);
tool_layer->create(tool_preview_layer_rect, _canvas_width, _canvas_height);
}
PaintCanvasOld::~PaintCanvasOld() {
}
void PaintCanvasOld::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_mouse_entered"), &PaintCanvasOld::_on_mouse_entered);
ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &PaintCanvasOld::_on_mouse_exited);
}