/*************************************************************************/ /* texture_layer_merger.cpp */ /*************************************************************************/ /* This file is part of: */ /* PANDEMONIUM ENGINE */ /* https://github.com/Relintai/pandemonium_engine */ /*************************************************************************/ /* Copyright (c) 2022-present Péter Magyar. */ /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ /* */ /* 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 "texture_layer_merger.h" int TextureLayerMerger::get_width() const { return _width; } void TextureLayerMerger::set_width(const int p_value) { _width = p_value; } int TextureLayerMerger::get_height() const { return _height; } void TextureLayerMerger::set_height(const int p_value) { _height = p_value; } uint32_t TextureLayerMerger::get_texture_flags() const { return _texture_flags; } void TextureLayerMerger::set_texture_flags(uint32_t p_flags) { _texture_flags = p_flags; } Color TextureLayerMerger::get_base_color() const { return _base_color; } void TextureLayerMerger::set_base_color(const Color &p_color) { _base_color = p_color; } Ref TextureLayerMerger::get_data() const { return _image; } void TextureLayerMerger::set_data(const Ref &p_image) { ERR_FAIL_COND(p_image.is_null()); _image = p_image; } Ref TextureLayerMerger::get_result_as_texture() const { ERR_FAIL_COND_V(!_image.is_valid(), Ref()); Ref tex; tex.instance(); tex->create_from_image(_image); return tex; } void TextureLayerMerger::add_texture(const Ref &p_texture, const Color &p_color, const Vector2 &p_position, Rect2 p_rect) { ERR_FAIL_COND(!p_texture.is_valid()); LayerMergerEntry entry; entry.texture = p_texture; entry.color = p_color; entry.position = p_position; if (p_rect.size.x <= 0) p_rect.size.x = p_texture->get_width(); if (p_rect.size.y <= 0) p_rect.size.y = p_texture->get_height(); entry.rect = p_rect; if (_width == 0 || _height == 0) { Ref at = p_texture; int w = 0; int h = 0; if (at.is_valid()) { w = at->get_region().size.x; h = at->get_region().size.y; } else { w = p_texture->get_width(); h = p_texture->get_height(); } if (_width == 0) _width = w; if (_height == 0) _height = h; } _entries.push_back(entry); } Ref TextureLayerMerger::get_texture(const int p_index) { ERR_FAIL_INDEX_V(p_index, _entries.size(), Ref()); return _entries.get(p_index).texture; } void TextureLayerMerger::set_texture(const int p_index, const Ref &p_texture) { ERR_FAIL_INDEX(p_index, _entries.size()); _entries.get(p_index).texture = p_texture; } Color TextureLayerMerger::get_color(const int p_index) { ERR_FAIL_INDEX_V(p_index, _entries.size(), Color()); return _entries.get(p_index).color; } void TextureLayerMerger::set_color(const int p_index, const Color &p_color) { ERR_FAIL_INDEX(p_index, _entries.size()); _entries.get(p_index).color = p_color; } Vector2 TextureLayerMerger::get_position(const int p_index) { ERR_FAIL_INDEX_V(p_index, _entries.size(), Vector2()); return _entries.get(p_index).position; } void TextureLayerMerger::set_position(const int p_index, const Vector2 &p_position) { ERR_FAIL_INDEX(p_index, _entries.size()); _entries.get(p_index).position = p_position; } Rect2 TextureLayerMerger::get_rect(const int p_index) { ERR_FAIL_INDEX_V(p_index, _entries.size(), Rect2()); return _entries.get(p_index).rect; } void TextureLayerMerger::set_rect(const int p_index, const Rect2 &p_rect) { ERR_FAIL_INDEX(p_index, _entries.size()); _entries.get(p_index).rect = p_rect; } void TextureLayerMerger::remove_texture(const int p_index) { ERR_FAIL_INDEX(p_index, _entries.size()); return _entries.remove(p_index); } int TextureLayerMerger::get_texture_count() { return _entries.size(); } void TextureLayerMerger::clear() { _entries.clear(); } void TextureLayerMerger::merge() { ERR_FAIL_COND(_width <= 0 || _height <= 0); if (!_image.is_valid()) { _image.instance(); } PoolVector data; data.resize(_width * _height * 4); write_base_color_to_array(data); for (int i = 0; i < _entries.size(); ++i) { const LayerMergerEntry &e = _entries[i]; ERR_CONTINUE(!e.texture.is_valid()); int rx = e.rect.position.x + 0.1; int ry = e.rect.position.y + 0.1; int rw = e.rect.size.x + 0.1; int rh = e.rect.size.y + 0.1; int posx = e.position.x + 0.1; int posy = e.position.y + 0.1; int atlas_x = 0; int atlas_y = 0; if (posx > _width || posy > _height) continue; Ref altas_texture = e.texture; Ref input_image; if (altas_texture.is_valid()) { Ref atlas = altas_texture->get_atlas(); ERR_CONTINUE(!atlas.is_valid()); input_image = atlas->get_data(); Rect2 region = altas_texture->get_region(); atlas_x = region.position.x + 0.1; atlas_y = region.position.y + 0.1; int atlas_w = region.size.x + 0.1; int atlas_h = region.size.y + 0.1; if (rw > atlas_w) rw = atlas_w; if (rh > atlas_h) rh = atlas_h; } else { input_image = e.texture->get_data(); } ERR_CONTINUE(!input_image.is_valid()); int iiw = input_image->get_width(); int iih = input_image->get_height(); PoolVector input_image_data = input_image->get_data(); const Color &blendcolor = e.color; float blend_arr[] = { blendcolor.r, blendcolor.g, blendcolor.b, blendcolor.a }; ERR_CONTINUE(iiw == 0 || iih == 0); ERR_CONTINUE(rx > iiw || ry > iih); //clamp width, and height if bigger (for ease of use) if (atlas_x + rx + rw >= iiw) rw -= (atlas_x + rx + rw) - iiw; if (atlas_y + ry + rh >= iih) rh -= (atlas_y + ry + rh) - iih; //Let's take position into account if (rx + rw + posx >= _width) rw -= (rx + rw + posx) - _width; if (ry + rh + posy >= _height) rh -= (ry + rh + posy) - _height; if (rw <= 0 || rh <= 0) continue; int elen = 0; if (input_image->get_format() == Image::FORMAT_RGBA8) { elen = 4; } else if (input_image->get_format() == Image::FORMAT_RGB8) { elen = 3; } ERR_CONTINUE_MSG(elen == 0, "Unsupported image format! Format: " + String::num(input_image->get_format())); for (int y = 0; y < rh; ++y) { int img_gen_index = (posx + ((y + posy) * _width)) * 4; int img_input_index = (rx + atlas_x + ((y + atlas_y + ry) * iiw)) * elen; for (int x = 0; x < rw; ++x) { float blend_alpha = blendcolor.a; if (elen == 4) { float orig_alpha = input_image_data.get(img_input_index + 3) / 255.0; blend_alpha -= 1.0 - orig_alpha; if (blend_alpha < 0) blend_alpha = 0; blend_alpha = orig_alpha; } for (int sp = 0; sp < elen; ++sp) { int main_index = img_gen_index + sp; int main_val = data.get(main_index); int input_val = input_image_data.get(img_input_index + sp); main_val = (input_val * blend_arr[sp]) * blend_alpha + (main_val * (1 - blend_alpha)); if (main_val > 255) main_val = 255; data.set(main_index, main_val); } img_gen_index += 4; img_input_index += elen; } } } _image->create(_width, _height, (_texture_flags & Texture::FLAG_MIPMAPS) != 0, Image::FORMAT_RGBA8, data); } void TextureLayerMerger::write_base_color_to_array(PoolVector &data) { int cr = _base_color.r * 255; int cg = _base_color.g * 255; int cb = _base_color.b * 255; int ca = _base_color.a * 255; for (int i = 0; i < data.size(); i += 4) { data.set(i, cr); data.set(i + 1, cg); data.set(i + 2, cb); data.set(i + 3, ca); } } void TextureLayerMerger::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &TextureLayerMerger::get_width); ClassDB::bind_method(D_METHOD("set_width", "image"), &TextureLayerMerger::set_width); ADD_PROPERTY(PropertyInfo(Variant::INT, "width"), "set_width", "get_width"); ClassDB::bind_method(D_METHOD("get_height"), &TextureLayerMerger::get_height); ClassDB::bind_method(D_METHOD("set_height", "image"), &TextureLayerMerger::set_height); ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); ClassDB::bind_method(D_METHOD("get_texture_flags"), &TextureLayerMerger::get_texture_flags); ClassDB::bind_method(D_METHOD("set_texture_flags", "image"), &TextureLayerMerger::set_texture_flags); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_texture_flags", "get_texture_flags"); ClassDB::bind_method(D_METHOD("get_base_color"), &TextureLayerMerger::get_base_color); ClassDB::bind_method(D_METHOD("set_base_color", "color"), &TextureLayerMerger::set_base_color); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "base_color"), "set_base_color", "get_base_color"); ClassDB::bind_method(D_METHOD("get_data"), &TextureLayerMerger::get_data); ClassDB::bind_method(D_METHOD("set_data", "image"), &TextureLayerMerger::set_data); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "Image"), "set_data", "get_data"); ClassDB::bind_method(D_METHOD("get_result_as_texture"), &TextureLayerMerger::get_result_as_texture); ClassDB::bind_method(D_METHOD("add_texture", "texture", "color", "position", "rect"), &TextureLayerMerger::add_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Vector2()), DEFVAL(Rect2())); ClassDB::bind_method(D_METHOD("get_texture", "index"), &TextureLayerMerger::get_texture); ClassDB::bind_method(D_METHOD("set_texture", "index", "texture"), &TextureLayerMerger::set_texture); ClassDB::bind_method(D_METHOD("get_color", "index"), &TextureLayerMerger::get_color); ClassDB::bind_method(D_METHOD("set_color", "index", "color"), &TextureLayerMerger::set_color); ClassDB::bind_method(D_METHOD("get_position", "index"), &TextureLayerMerger::get_position); ClassDB::bind_method(D_METHOD("set_position", "index", "position"), &TextureLayerMerger::set_position); ClassDB::bind_method(D_METHOD("get_rect", "index"), &TextureLayerMerger::get_rect); ClassDB::bind_method(D_METHOD("set_rect", "index", "rect"), &TextureLayerMerger::set_rect); ClassDB::bind_method(D_METHOD("remove_texture", "index"), &TextureLayerMerger::remove_texture); ClassDB::bind_method(D_METHOD("get_texture_count"), &TextureLayerMerger::get_texture_count); ClassDB::bind_method(D_METHOD("clear"), &TextureLayerMerger::clear); ClassDB::bind_method(D_METHOD("merge"), &TextureLayerMerger::merge); } TextureLayerMerger::TextureLayerMerger() { _width = 0; _height = 0; _texture_flags = 0; } TextureLayerMerger::~TextureLayerMerger() { _image.unref(); _entries.clear(); }