1/**************************************************************************/
2/* atlas_texture.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "atlas_texture.h"
32
33#include "core/core_string_names.h"
34
35int AtlasTexture::get_width() const {
36 if (region.size.width == 0) {
37 if (atlas.is_valid()) {
38 return atlas->get_width();
39 }
40 return 1;
41 } else {
42 return region.size.width + margin.size.width;
43 }
44}
45
46int AtlasTexture::get_height() const {
47 if (region.size.height == 0) {
48 if (atlas.is_valid()) {
49 return atlas->get_height();
50 }
51 return 1;
52 } else {
53 return region.size.height + margin.size.height;
54 }
55}
56
57RID AtlasTexture::get_rid() const {
58 if (atlas.is_valid()) {
59 return atlas->get_rid();
60 }
61
62 return RID();
63}
64
65bool AtlasTexture::has_alpha() const {
66 if (atlas.is_valid()) {
67 return atlas->has_alpha();
68 }
69
70 return false;
71}
72
73void AtlasTexture::set_atlas(const Ref<Texture2D> &p_atlas) {
74 ERR_FAIL_COND(p_atlas == this);
75 if (atlas == p_atlas) {
76 return;
77 }
78 // Support recursive AtlasTextures.
79 if (Ref<AtlasTexture>(atlas).is_valid()) {
80 atlas->disconnect_changed(callable_mp((Resource *)this, &AtlasTexture::emit_changed));
81 }
82 atlas = p_atlas;
83 if (Ref<AtlasTexture>(atlas).is_valid()) {
84 atlas->connect_changed(callable_mp((Resource *)this, &AtlasTexture::emit_changed));
85 }
86
87 emit_changed();
88}
89
90Ref<Texture2D> AtlasTexture::get_atlas() const {
91 return atlas;
92}
93
94void AtlasTexture::set_region(const Rect2 &p_region) {
95 if (region == p_region) {
96 return;
97 }
98 region = p_region;
99 emit_changed();
100}
101
102Rect2 AtlasTexture::get_region() const {
103 return region;
104}
105
106void AtlasTexture::set_margin(const Rect2 &p_margin) {
107 if (margin == p_margin) {
108 return;
109 }
110 margin = p_margin;
111 emit_changed();
112}
113
114Rect2 AtlasTexture::get_margin() const {
115 return margin;
116}
117
118void AtlasTexture::set_filter_clip(const bool p_enable) {
119 filter_clip = p_enable;
120 emit_changed();
121}
122
123bool AtlasTexture::has_filter_clip() const {
124 return filter_clip;
125}
126
127void AtlasTexture::_bind_methods() {
128 ClassDB::bind_method(D_METHOD("set_atlas", "atlas"), &AtlasTexture::set_atlas);
129 ClassDB::bind_method(D_METHOD("get_atlas"), &AtlasTexture::get_atlas);
130
131 ClassDB::bind_method(D_METHOD("set_region", "region"), &AtlasTexture::set_region);
132 ClassDB::bind_method(D_METHOD("get_region"), &AtlasTexture::get_region);
133
134 ClassDB::bind_method(D_METHOD("set_margin", "margin"), &AtlasTexture::set_margin);
135 ClassDB::bind_method(D_METHOD("get_margin"), &AtlasTexture::get_margin);
136
137 ClassDB::bind_method(D_METHOD("set_filter_clip", "enable"), &AtlasTexture::set_filter_clip);
138 ClassDB::bind_method(D_METHOD("has_filter_clip"), &AtlasTexture::has_filter_clip);
139
140 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_atlas", "get_atlas");
141 ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region", PROPERTY_HINT_NONE, "suffix:px"), "set_region", "get_region");
142 ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin", PROPERTY_HINT_NONE, "suffix:px"), "set_margin", "get_margin");
143 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_clip"), "set_filter_clip", "has_filter_clip");
144}
145
146void AtlasTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
147 if (!atlas.is_valid()) {
148 return;
149 }
150
151 Rect2 rc = region;
152
153 if (rc.size.width == 0) {
154 rc.size.width = atlas->get_width();
155 }
156
157 if (rc.size.height == 0) {
158 rc.size.height = atlas->get_height();
159 }
160
161 atlas->draw_rect_region(p_canvas_item, Rect2(p_pos + margin.position, rc.size), rc, p_modulate, p_transpose, filter_clip);
162}
163
164void AtlasTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
165 if (!atlas.is_valid()) {
166 return;
167 }
168
169 Rect2 rc = region;
170
171 if (rc.size.width == 0) {
172 rc.size.width = atlas->get_width();
173 }
174
175 if (rc.size.height == 0) {
176 rc.size.height = atlas->get_height();
177 }
178
179 Vector2 scale = p_rect.size / (region.size + margin.size);
180 Rect2 dr(p_rect.position + margin.position * scale, rc.size * scale);
181
182 atlas->draw_rect_region(p_canvas_item, dr, rc, p_modulate, p_transpose, filter_clip);
183}
184
185void AtlasTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
186 //this might not necessarily work well if using a rect, needs to be fixed properly
187 if (!atlas.is_valid()) {
188 return;
189 }
190
191 Rect2 dr;
192 Rect2 src_c;
193 get_rect_region(p_rect, p_src_rect, dr, src_c);
194
195 atlas->draw_rect_region(p_canvas_item, dr, src_c, p_modulate, p_transpose, filter_clip);
196}
197
198bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, Rect2 &r_rect, Rect2 &r_src_rect) const {
199 if (!atlas.is_valid()) {
200 return false;
201 }
202
203 Rect2 src = p_src_rect;
204 if (src.size == Size2()) {
205 src.size = region.size;
206 }
207 Vector2 scale = p_rect.size / src.size;
208
209 src.position += (region.position - margin.position);
210 Rect2 src_clipped = region.intersection(src);
211 if (src_clipped.size == Size2()) {
212 return false;
213 }
214
215 Vector2 ofs = (src_clipped.position - src.position);
216 if (scale.x < 0) {
217 ofs.x += (src_clipped.size.x - src.size.x);
218 }
219 if (scale.y < 0) {
220 ofs.y += (src_clipped.size.y - src.size.y);
221 }
222
223 r_rect = Rect2(p_rect.position + ofs * scale, src_clipped.size * scale);
224 r_src_rect = src_clipped;
225 return true;
226}
227
228bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
229 if (!atlas.is_valid()) {
230 return true;
231 }
232
233 int x = p_x + region.position.x - margin.position.x;
234 int y = p_y + region.position.y - margin.position.y;
235
236 // margin edge may outside of atlas
237 if (x < 0 || x >= atlas->get_width()) {
238 return false;
239 }
240 if (y < 0 || y >= atlas->get_height()) {
241 return false;
242 }
243
244 return atlas->is_pixel_opaque(x, y);
245}
246
247Ref<Image> AtlasTexture::get_image() const {
248 if (atlas.is_null() || region.size.x <= 0 || region.size.y <= 0) {
249 return Ref<Image>();
250 }
251
252 Ref<Image> atlas_image = atlas->get_image();
253 if (atlas_image.is_null()) {
254 return Ref<Image>();
255 }
256
257 return atlas_image->get_region(region);
258}
259
260AtlasTexture::AtlasTexture() {}
261