Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/resources/gradient_texture.cpp
20843 views
1
/**************************************************************************/
2
/* gradient_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 "gradient_texture.h"
32
33
#include "core/math/geometry_2d.h"
34
#include "servers/rendering/rendering_server.h"
35
36
GradientTexture1D::GradientTexture1D() {
37
_queue_update();
38
}
39
40
GradientTexture1D::~GradientTexture1D() {
41
if (texture.is_valid()) {
42
ERR_FAIL_NULL(RenderingServer::get_singleton());
43
RS::get_singleton()->free_rid(texture);
44
}
45
}
46
47
void GradientTexture1D::_bind_methods() {
48
ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture1D::set_gradient);
49
ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture1D::get_gradient);
50
51
ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture1D::set_width);
52
// The `get_width()` method is already exposed by the parent class Texture2D.
53
54
ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture1D::set_use_hdr);
55
ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture1D::is_using_hdr);
56
57
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, Gradient::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
58
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,16384,suffix:px"), "set_width", "get_width");
59
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
60
}
61
62
void GradientTexture1D::set_gradient(Ref<Gradient> p_gradient) {
63
if (p_gradient == gradient) {
64
return;
65
}
66
if (gradient.is_valid()) {
67
gradient->disconnect_changed(callable_mp(this, &GradientTexture1D::_queue_update));
68
}
69
gradient = p_gradient;
70
if (gradient.is_valid()) {
71
gradient->connect_changed(callable_mp(this, &GradientTexture1D::_queue_update));
72
}
73
_queue_update();
74
emit_changed();
75
}
76
77
Ref<Gradient> GradientTexture1D::get_gradient() const {
78
return gradient;
79
}
80
81
void GradientTexture1D::_queue_update() {
82
if (update_pending) {
83
return;
84
}
85
update_pending = true;
86
callable_mp(this, &GradientTexture1D::update_now).call_deferred();
87
}
88
89
void GradientTexture1D::_update() const {
90
update_pending = false;
91
92
if (gradient.is_null()) {
93
return;
94
}
95
96
if (use_hdr) {
97
// High dynamic range.
98
Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBAF));
99
Gradient &g = **gradient;
100
// `create()` isn't available for non-uint8_t data, so fill in the data manually.
101
for (int i = 0; i < width; i++) {
102
float ofs = float(i) / (width - 1);
103
image->set_pixel(i, 0, g.get_color_at_offset(ofs));
104
}
105
106
if (texture.is_valid()) {
107
RID new_texture = RS::get_singleton()->texture_2d_create(image);
108
RS::get_singleton()->texture_replace(texture, new_texture);
109
} else {
110
texture = RS::get_singleton()->texture_2d_create(image);
111
}
112
} else {
113
// Low dynamic range. "Overbright" colors will be clamped.
114
Vector<uint8_t> data;
115
data.resize(width * 4);
116
{
117
uint8_t *wd8 = data.ptrw();
118
Gradient &g = **gradient;
119
120
for (int i = 0; i < width; i++) {
121
float ofs = float(i) / (width - 1);
122
Color color = g.get_color_at_offset(ofs);
123
124
wd8[i * 4 + 0] = uint8_t(color.get_r8());
125
wd8[i * 4 + 1] = uint8_t(color.get_g8());
126
wd8[i * 4 + 2] = uint8_t(color.get_b8());
127
wd8[i * 4 + 3] = uint8_t(color.get_a8());
128
}
129
}
130
131
Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data));
132
133
if (texture.is_valid()) {
134
RID new_texture = RS::get_singleton()->texture_2d_create(image);
135
RS::get_singleton()->texture_replace(texture, new_texture);
136
} else {
137
texture = RS::get_singleton()->texture_2d_create(image);
138
}
139
}
140
RS::get_singleton()->texture_set_path(texture, get_path());
141
}
142
143
void GradientTexture1D::set_width(int p_width) {
144
ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range.");
145
width = p_width;
146
_queue_update();
147
emit_changed();
148
}
149
150
int GradientTexture1D::get_width() const {
151
return width;
152
}
153
154
void GradientTexture1D::set_use_hdr(bool p_enabled) {
155
if (p_enabled == use_hdr) {
156
return;
157
}
158
159
use_hdr = p_enabled;
160
_queue_update();
161
emit_changed();
162
}
163
164
bool GradientTexture1D::is_using_hdr() const {
165
return use_hdr;
166
}
167
168
RID GradientTexture1D::get_rid() const {
169
if (!texture.is_valid()) {
170
texture = RS::get_singleton()->texture_2d_placeholder_create();
171
}
172
return texture;
173
}
174
175
Ref<Image> GradientTexture1D::get_image() const {
176
update_now();
177
if (!texture.is_valid()) {
178
return Ref<Image>();
179
}
180
return RenderingServer::get_singleton()->texture_2d_get(texture);
181
}
182
183
void GradientTexture1D::update_now() const {
184
if (update_pending) {
185
_update();
186
}
187
}
188
189
//////////////////
190
191
GradientTexture2D::GradientTexture2D() {
192
_queue_update();
193
}
194
195
GradientTexture2D::~GradientTexture2D() {
196
if (texture.is_valid()) {
197
ERR_FAIL_NULL(RenderingServer::get_singleton());
198
RS::get_singleton()->free_rid(texture);
199
}
200
}
201
202
void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) {
203
if (gradient == p_gradient) {
204
return;
205
}
206
if (gradient.is_valid()) {
207
gradient->disconnect_changed(callable_mp(this, &GradientTexture2D::_queue_update));
208
}
209
gradient = p_gradient;
210
if (gradient.is_valid()) {
211
gradient->connect_changed(callable_mp(this, &GradientTexture2D::_queue_update));
212
}
213
_queue_update();
214
emit_changed();
215
}
216
217
Ref<Gradient> GradientTexture2D::get_gradient() const {
218
return gradient;
219
}
220
221
void GradientTexture2D::_queue_update() {
222
if (update_pending) {
223
return;
224
}
225
update_pending = true;
226
callable_mp(this, &GradientTexture2D::update_now).call_deferred();
227
}
228
229
void GradientTexture2D::_update() const {
230
update_pending = false;
231
232
if (gradient.is_null()) {
233
return;
234
}
235
Ref<Image> image;
236
image.instantiate();
237
238
if (gradient->get_point_count() <= 1) { // No need to interpolate.
239
image->initialize_data(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8);
240
image->fill((gradient->get_point_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1));
241
} else {
242
if (use_hdr) {
243
image->initialize_data(width, height, false, Image::FORMAT_RGBAF);
244
Gradient &g = **gradient;
245
// `create()` isn't available for non-uint8_t data, so fill in the data manually.
246
for (int y = 0; y < height; y++) {
247
for (int x = 0; x < width; x++) {
248
float ofs = _get_gradient_offset_at(x, y);
249
image->set_pixel(x, y, g.get_color_at_offset(ofs));
250
}
251
}
252
} else {
253
Vector<uint8_t> data;
254
data.resize(width * height * 4);
255
{
256
uint8_t *wd8 = data.ptrw();
257
Gradient &g = **gradient;
258
for (int y = 0; y < height; y++) {
259
for (int x = 0; x < width; x++) {
260
float ofs = _get_gradient_offset_at(x, y);
261
const Color &c = g.get_color_at_offset(ofs);
262
263
wd8[(x + (y * width)) * 4 + 0] = uint8_t(c.get_r8());
264
wd8[(x + (y * width)) * 4 + 1] = uint8_t(c.get_g8());
265
wd8[(x + (y * width)) * 4 + 2] = uint8_t(c.get_b8());
266
wd8[(x + (y * width)) * 4 + 3] = uint8_t(c.get_a8());
267
}
268
}
269
}
270
image->set_data(width, height, false, Image::FORMAT_RGBA8, data);
271
}
272
}
273
274
if (texture.is_valid()) {
275
RID new_texture = RS::get_singleton()->texture_2d_create(image);
276
RS::get_singleton()->texture_replace(texture, new_texture);
277
} else {
278
texture = RS::get_singleton()->texture_2d_create(image);
279
}
280
RS::get_singleton()->texture_set_path(texture, get_path());
281
}
282
283
float GradientTexture2D::_get_gradient_offset_at(int x, int y) const {
284
if (fill_to == fill_from) {
285
return 0;
286
}
287
float ofs = 0;
288
Vector2 pos;
289
if (width > 1) {
290
pos.x = static_cast<float>(x) / (width - 1);
291
}
292
if (height > 1) {
293
pos.y = static_cast<float>(y) / (height - 1);
294
}
295
if (fill == Fill::FILL_LINEAR) {
296
const Vector2 closest = Geometry2D::get_closest_point_to_segment_uncapped(pos, fill_from, fill_to);
297
ofs = (closest - fill_from).length() / (fill_to - fill_from).length();
298
if ((closest - fill_from).dot(fill_to - fill_from) < 0) {
299
ofs *= -1;
300
}
301
} else if (fill == Fill::FILL_RADIAL) {
302
ofs = (pos - fill_from).length() / (fill_to - fill_from).length();
303
} else if (fill == Fill::FILL_SQUARE) {
304
ofs = MAX(Math::abs(pos.x - fill_from.x), Math::abs(pos.y - fill_from.y)) / MAX(Math::abs(fill_to.x - fill_from.x), Math::abs(fill_to.y - fill_from.y));
305
}
306
if (repeat == Repeat::REPEAT_NONE) {
307
ofs = CLAMP(ofs, 0.0, 1.0);
308
} else if (repeat == Repeat::REPEAT) {
309
ofs = Math::fmod(ofs, 1.0f);
310
if (ofs < 0) {
311
ofs = 1 + ofs;
312
}
313
} else if (repeat == Repeat::REPEAT_MIRROR) {
314
ofs = Math::abs(ofs);
315
ofs = Math::fmod(ofs, 2.0f);
316
if (ofs > 1.0) {
317
ofs = 2.0 - ofs;
318
}
319
}
320
return ofs;
321
}
322
323
void GradientTexture2D::set_width(int p_width) {
324
ERR_FAIL_COND_MSG(p_width <= 0 || p_width > 16384, "Texture dimensions have to be within 1 to 16384 range.");
325
width = p_width;
326
_queue_update();
327
emit_changed();
328
}
329
330
int GradientTexture2D::get_width() const {
331
return width;
332
}
333
334
void GradientTexture2D::set_height(int p_height) {
335
ERR_FAIL_COND_MSG(p_height <= 0 || p_height > 16384, "Texture dimensions have to be within 1 to 16384 range.");
336
height = p_height;
337
_queue_update();
338
emit_changed();
339
}
340
int GradientTexture2D::get_height() const {
341
return height;
342
}
343
344
void GradientTexture2D::set_use_hdr(bool p_enabled) {
345
if (p_enabled == use_hdr) {
346
return;
347
}
348
349
use_hdr = p_enabled;
350
_queue_update();
351
emit_changed();
352
}
353
354
bool GradientTexture2D::is_using_hdr() const {
355
return use_hdr;
356
}
357
358
void GradientTexture2D::set_fill_from(Vector2 p_fill_from) {
359
fill_from = p_fill_from;
360
_queue_update();
361
emit_changed();
362
}
363
364
Vector2 GradientTexture2D::get_fill_from() const {
365
return fill_from;
366
}
367
368
void GradientTexture2D::set_fill_to(Vector2 p_fill_to) {
369
fill_to = p_fill_to;
370
_queue_update();
371
emit_changed();
372
}
373
374
Vector2 GradientTexture2D::get_fill_to() const {
375
return fill_to;
376
}
377
378
void GradientTexture2D::set_fill(Fill p_fill) {
379
fill = p_fill;
380
_queue_update();
381
emit_changed();
382
}
383
384
GradientTexture2D::Fill GradientTexture2D::get_fill() const {
385
return fill;
386
}
387
388
void GradientTexture2D::set_repeat(Repeat p_repeat) {
389
repeat = p_repeat;
390
_queue_update();
391
emit_changed();
392
}
393
394
GradientTexture2D::Repeat GradientTexture2D::get_repeat() const {
395
return repeat;
396
}
397
398
RID GradientTexture2D::get_rid() const {
399
if (!texture.is_valid()) {
400
texture = RS::get_singleton()->texture_2d_placeholder_create();
401
}
402
return texture;
403
}
404
405
Ref<Image> GradientTexture2D::get_image() const {
406
update_now();
407
if (!texture.is_valid()) {
408
return Ref<Image>();
409
}
410
return RenderingServer::get_singleton()->texture_2d_get(texture);
411
}
412
413
void GradientTexture2D::update_now() const {
414
if (update_pending) {
415
_update();
416
}
417
}
418
419
void GradientTexture2D::_bind_methods() {
420
ClassDB::bind_method(D_METHOD("set_gradient", "gradient"), &GradientTexture2D::set_gradient);
421
ClassDB::bind_method(D_METHOD("get_gradient"), &GradientTexture2D::get_gradient);
422
423
ClassDB::bind_method(D_METHOD("set_width", "width"), &GradientTexture2D::set_width);
424
ClassDB::bind_method(D_METHOD("set_height", "height"), &GradientTexture2D::set_height);
425
426
ClassDB::bind_method(D_METHOD("set_use_hdr", "enabled"), &GradientTexture2D::set_use_hdr);
427
ClassDB::bind_method(D_METHOD("is_using_hdr"), &GradientTexture2D::is_using_hdr);
428
429
ClassDB::bind_method(D_METHOD("set_fill", "fill"), &GradientTexture2D::set_fill);
430
ClassDB::bind_method(D_METHOD("get_fill"), &GradientTexture2D::get_fill);
431
ClassDB::bind_method(D_METHOD("set_fill_from", "fill_from"), &GradientTexture2D::set_fill_from);
432
ClassDB::bind_method(D_METHOD("get_fill_from"), &GradientTexture2D::get_fill_from);
433
ClassDB::bind_method(D_METHOD("set_fill_to", "fill_to"), &GradientTexture2D::set_fill_to);
434
ClassDB::bind_method(D_METHOD("get_fill_to"), &GradientTexture2D::get_fill_to);
435
436
ClassDB::bind_method(D_METHOD("set_repeat", "repeat"), &GradientTexture2D::set_repeat);
437
ClassDB::bind_method(D_METHOD("get_repeat"), &GradientTexture2D::get_repeat);
438
439
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, Gradient::get_class_static(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_gradient", "get_gradient");
440
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_width", "get_width");
441
ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,or_greater,suffix:px"), "set_height", "get_height");
442
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr"), "set_use_hdr", "is_using_hdr");
443
444
ADD_GROUP("Fill", "fill_");
445
ADD_PROPERTY(PropertyInfo(Variant::INT, "fill", PROPERTY_HINT_ENUM, "Linear,Radial,Square"), "set_fill", "get_fill");
446
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_from"), "set_fill_from", "get_fill_from");
447
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fill_to"), "set_fill_to", "get_fill_to");
448
449
ADD_GROUP("Repeat", "repeat_");
450
ADD_PROPERTY(PropertyInfo(Variant::INT, "repeat", PROPERTY_HINT_ENUM, "No Repeat,Repeat,Mirror Repeat"), "set_repeat", "get_repeat");
451
452
BIND_ENUM_CONSTANT(FILL_LINEAR);
453
BIND_ENUM_CONSTANT(FILL_RADIAL);
454
BIND_ENUM_CONSTANT(FILL_SQUARE);
455
456
BIND_ENUM_CONSTANT(REPEAT_NONE);
457
BIND_ENUM_CONSTANT(REPEAT);
458
BIND_ENUM_CONSTANT(REPEAT_MIRROR);
459
}
460
461