#include "paint_node.h" #include "../paint_utilities.h" #include "core/math/geometry.h" #include "paint_project.h" Vector2i PaintNode::get_size() { return _size; } void PaintNode::set_size(const Vector2i &size) { if (size == _size) { return; } _size = size; emit_signal("size_changed"); _propagate_notification_resized(); update(); } bool PaintNode::get_draw_outline() { return _draw_outline; } void PaintNode::set_draw_outline(const bool val) { _draw_outline = val; update(); } Ref PaintNode::render_image() { _propagate_notification_project_pre_render(); Ref img = call("_render_image"); _propagate_notification_project_post_render(); return img; } Ref PaintNode::_render_image() { Ref image; image.instance(); Vector2i size = get_size(); if (size.x <= 0 || size.y <= 0) { return image; } image->create(size.x, size.y, false, Image::FORMAT_RGBA8); for (int i = 0; i < get_child_count(); ++i) { PaintNode *pn = Object::cast_to(get_child(i)); if (pn && pn->is_visible()) { //dont apply own transform render_evaluate_paint_node(pn, Transform2D(), image); } } render_paint_node(this, Transform2D(), image); return image; } Ref PaintNode::get_rendered_image() { return call("_get_rendered_image"); } Ref PaintNode::_get_rendered_image() { return Ref(); } bool PaintNode::render_should_evaluate_children() { return call("_render_should_evaluate_children"); } bool PaintNode::_render_should_evaluate_children() { return true; } void PaintNode::render_evaluate_paint_node(PaintNode *node, Transform2D transform, Ref image) { ERR_FAIL_COND(!node); ERR_FAIL_COND(!image.is_valid()); Transform2D currtf = transform * node->get_transform(); if (node->render_should_evaluate_children()) { for (int i = 0; i < node->get_child_count(); ++i) { PaintNode *pn = Object::cast_to(node->get_child(i)); if (pn && pn->is_visible()) { render_evaluate_paint_node(pn, currtf, image); } } } render_paint_node(node, currtf, image); } void PaintNode::render_paint_node(PaintNode *node, Transform2D transform, Ref image) { ERR_FAIL_COND(!node); ERR_FAIL_COND(!image.is_valid()); Ref save_image = node->get_rendered_image(); if (!save_image.is_valid() || save_image->empty()) { return; } Vector2i save_image_size = save_image->get_size(); Vector2i image_size = image->get_size(); Vector2 p0 = transform.xform(Vector2(0, 0)); Vector2 p1 = transform.xform(Vector2(0, save_image_size.y)); Vector2 p2 = transform.xform(Vector2(save_image_size.x, 0)); Vector2 p3 = transform.xform(Vector2(save_image_size.x, save_image_size.y)); Rect2i bound(p0, Vector2()); bound.expand_to(p1); bound.expand_to(p2); bound.expand_to(p3); bound.grow(1); Transform2D inverse = transform.affine_inverse(); save_image->lock(); image->lock(); for (int x = bound.position.x; x < bound.position.x + bound.size.x; ++x) { for (int y = bound.position.y; y < bound.position.y + bound.size.y; ++y) { Point2i p = Point2i(x, y); if (p.x < 0 || p.y < 0 || p.x >= image_size.x || p.y >= image_size.y) { continue; } Vector2 npos = inverse.xform(Vector2(p.x, p.y)); if (npos.x < 0 || npos.y < 0 || npos.x >= save_image_size.x || npos.y >= save_image_size.y) { continue; } Color sic = save_image->get_pixel(npos.x, npos.y); Color oic = image->get_pixel(p.x, p.y); image->set_pixel(p.x, p.y, oic.blend(sic)); } } image->unlock(); save_image->unlock(); } PoolVector2iArray PaintNode::util_get_pixels_in_line(const Vector2i &from, const Vector2i &to) { return PaintUtilities::get_pixels_in_line(from, to); } int PaintNode::util_to_1d_v(const Vector2i &p, int w) { return PaintUtilities::to_1D_v(p, w); } int PaintNode::util_to_1d(int x, int y, int w) { return PaintUtilities::to_1D(x, y, w); } Vector2i PaintNode::util_to_2d(int idx, int w) { return PaintUtilities::to_2D(idx, w); } Color PaintNode::util_color_from_array(const PoolRealArray &color_array) { return PaintUtilities::color_from_array(color_array); } Color PaintNode::util_random_color() { return PaintUtilities::random_color(); } Color PaintNode::util_random_color_alt() { return PaintUtilities::random_color_alt(); } bool PaintNode::forward_canvas_gui_input(const Ref &p_event) { return call("_forward_canvas_gui_input", p_event); } bool PaintNode::_forward_canvas_gui_input(const Ref &p_event) { return false; } PaintProject *PaintNode::get_paint_project() { PaintNode *p = this; while (p) { PaintProject *pp = Object::cast_to(p); if (pp) { return pp; } p = Object::cast_to(p->get_parent()); } return NULL; } PaintNode *PaintNode::get_parent_paint_node() { return Object::cast_to(get_parent()); } PaintNode *PaintNode::find_parent_paint_node() { Node *p = get_parent(); while (p) { PaintNode *pn = Object::cast_to(p); if (pn) { return pn; } p = Object::cast_to(p->get_parent()); } return NULL; } String PaintNode::get_configuration_warning() const { const PaintNode *p = this; while (p) { const PaintProject *pp = Object::cast_to(p); if (pp) { return ""; } p = Object::cast_to(p->get_parent()); } return "This Node should be a child of a PaintProject!"; } void PaintNode::_propagate_notification_resized() { //Only send it to children //So if they don't resize themselves, the notification stops for (int i = 0; i < get_child_count(); ++i) { PaintNode *pn = Object::cast_to(get_child(i)); if (pn) { pn->notification(NOTIFICATION_PARENT_PAINT_NODE_RESIZED); } } } void PaintNode::_propagate_notification_project_pre_render() { notification(NOTIFICATION_PAINT_PROJECT_PRE_RENDER); for (int i = 0; i < get_child_count(); ++i) { PaintNode *pn = Object::cast_to(get_child(i)); if (pn) { pn->_propagate_notification_project_pre_render(); } } } void PaintNode::_propagate_notification_project_post_render() { notification(NOTIFICATION_PAINT_PROJECT_POST_RENDER); for (int i = 0; i < get_child_count(); ++i) { PaintNode *pn = Object::cast_to(get_child(i)); if (pn) { pn->_propagate_notification_project_post_render(); } } } PaintNode::PaintNode() { _draw_outline = true; } PaintNode::~PaintNode() { } void PaintNode::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POST_ENTER_TREE: { if (get_size() == Vector2i(0, 0)) { PaintNode *pn = get_parent_paint_node(); if (pn) { set_size(pn->get_size()); } } } break; case NOTIFICATION_DRAW: { if (_draw_outline) { draw_line(Point2(0, 0), Point2(_size.x, 0), Color(0, 0, 0, 1)); draw_line(Point2(0, _size.y), Point2(_size.x, _size.y), Color(0, 0, 0, 1)); draw_line(Point2(0, 0), Point2(0, _size.y), Color(0, 0, 0, 1)); draw_line(Point2(_size.x, 0), Point2(_size.x, _size.y), Color(0, 0, 0, 1)); } } break; default: break; } } void PaintNode::_bind_methods() { ADD_SIGNAL(MethodInfo("size_changed")); ClassDB::bind_method(D_METHOD("get_size"), &PaintNode::get_size); ClassDB::bind_method(D_METHOD("set_size", "size"), &PaintNode::set_size); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); ClassDB::bind_method(D_METHOD("get_draw_outline"), &PaintNode::get_draw_outline); ClassDB::bind_method(D_METHOD("set_draw_outline", "val"), &PaintNode::set_draw_outline); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_outline"), "set_draw_outline", "get_draw_outline"); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "r", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "_render_image")); ClassDB::bind_method(D_METHOD("render_image"), &PaintNode::render_image); ClassDB::bind_method(D_METHOD("_render_image"), &PaintNode::_render_image); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::OBJECT, "r", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "_get_rendered_image")); ClassDB::bind_method(D_METHOD("get_rendered_image"), &PaintNode::get_rendered_image); ClassDB::bind_method(D_METHOD("_get_rendered_image"), &PaintNode::_get_rendered_image); BIND_VMETHOD(MethodInfo(PropertyInfo(Variant::BOOL, "r"), "_render_should_evaluate_children")); ClassDB::bind_method(D_METHOD("render_should_evaluate_children"), &PaintNode::render_should_evaluate_children); ClassDB::bind_method(D_METHOD("_render_should_evaluate_children"), &PaintNode::_render_should_evaluate_children); ClassDB::bind_method(D_METHOD("util_get_pixels_in_line", "from", "to"), &PaintNode::util_get_pixels_in_line); ClassDB::bind_method(D_METHOD("util_to_1d_v", "p", "w"), &PaintNode::util_to_1d_v); ClassDB::bind_method(D_METHOD("util_to_1d", "x", "y", "w"), &PaintNode::util_to_1d); ClassDB::bind_method(D_METHOD("util_to_2d", "idx", "w"), &PaintNode::util_to_2d); ClassDB::bind_method(D_METHOD("util_color_from_array", "color_array"), &PaintNode::util_color_from_array); ClassDB::bind_method(D_METHOD("util_random_color"), &PaintNode::util_random_color); ClassDB::bind_method(D_METHOD("util_random_color_alt"), &PaintNode::util_random_color_alt); BIND_VMETHOD(MethodInfo(Variant::BOOL, "_forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ClassDB::bind_method(D_METHOD("forward_canvas_gui_input", "event"), &PaintNode::forward_canvas_gui_input); ClassDB::bind_method(D_METHOD("_forward_canvas_gui_input", "event"), &PaintNode::_forward_canvas_gui_input); ClassDB::bind_method(D_METHOD("get_paint_project"), &PaintNode::get_paint_project); ClassDB::bind_method(D_METHOD("get_parent_paint_node"), &PaintNode::get_parent_paint_node); ClassDB::bind_method(D_METHOD("find_parent_paint_node"), &PaintNode::find_parent_paint_node); BIND_CONSTANT(NOTIFICATION_PARENT_PAINT_NODE_RESIZED); BIND_CONSTANT(NOTIFICATION_PAINT_PROJECT_PRE_RENDER); BIND_CONSTANT(NOTIFICATION_PAINT_PROJECT_POST_RENDER); }