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 | |
35 | int 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 | |
46 | int 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 | |
57 | RID AtlasTexture::get_rid() const { |
58 | if (atlas.is_valid()) { |
59 | return atlas->get_rid(); |
60 | } |
61 | |
62 | return RID(); |
63 | } |
64 | |
65 | bool AtlasTexture::has_alpha() const { |
66 | if (atlas.is_valid()) { |
67 | return atlas->has_alpha(); |
68 | } |
69 | |
70 | return false; |
71 | } |
72 | |
73 | void 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 | |
90 | Ref<Texture2D> AtlasTexture::get_atlas() const { |
91 | return atlas; |
92 | } |
93 | |
94 | void AtlasTexture::set_region(const Rect2 &p_region) { |
95 | if (region == p_region) { |
96 | return; |
97 | } |
98 | region = p_region; |
99 | emit_changed(); |
100 | } |
101 | |
102 | Rect2 AtlasTexture::get_region() const { |
103 | return region; |
104 | } |
105 | |
106 | void AtlasTexture::set_margin(const Rect2 &p_margin) { |
107 | if (margin == p_margin) { |
108 | return; |
109 | } |
110 | margin = p_margin; |
111 | emit_changed(); |
112 | } |
113 | |
114 | Rect2 AtlasTexture::get_margin() const { |
115 | return margin; |
116 | } |
117 | |
118 | void AtlasTexture::set_filter_clip(const bool p_enable) { |
119 | filter_clip = p_enable; |
120 | emit_changed(); |
121 | } |
122 | |
123 | bool AtlasTexture::has_filter_clip() const { |
124 | return filter_clip; |
125 | } |
126 | |
127 | void 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 | |
146 | void 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 | |
164 | void 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 | |
185 | void 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 | |
198 | bool 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 | |
228 | bool 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 | |
247 | Ref<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 | |
260 | AtlasTexture::AtlasTexture() {} |
261 | |