Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/resources/bit_map.cpp
9903 views
1
/**************************************************************************/
2
/* bit_map.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 "bit_map.h"
32
33
#include "core/variant/typed_array.h"
34
35
void BitMap::create(const Size2i &p_size) {
36
ERR_FAIL_COND(p_size.width < 1);
37
ERR_FAIL_COND(p_size.height < 1);
38
39
ERR_FAIL_COND(static_cast<int64_t>(p_size.width) * static_cast<int64_t>(p_size.height) > INT32_MAX);
40
41
Error err = bitmask.resize(Math::division_round_up(p_size.width * p_size.height, 8));
42
ERR_FAIL_COND(err != OK);
43
44
width = p_size.width;
45
height = p_size.height;
46
47
memset(bitmask.ptrw(), 0, bitmask.size());
48
}
49
50
void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshold) {
51
ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
52
Ref<Image> img = p_image->duplicate();
53
img->convert(Image::FORMAT_LA8);
54
ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8);
55
56
create(Size2i(img->get_width(), img->get_height()));
57
58
const uint8_t *r = img->get_data().ptr();
59
uint8_t *w = bitmask.ptrw();
60
61
for (int i = 0; i < width * height; i++) {
62
int bbyte = i / 8;
63
int bbit = i % 8;
64
if (r[i * 2 + 1] / 255.0 > p_threshold) {
65
w[bbyte] |= (1 << bbit);
66
}
67
}
68
}
69
70
void BitMap::set_bit_rect(const Rect2i &p_rect, bool p_value) {
71
Rect2i current = Rect2i(0, 0, width, height).intersection(p_rect);
72
uint8_t *data = bitmask.ptrw();
73
74
for (int i = current.position.x; i < current.position.x + current.size.x; i++) {
75
for (int j = current.position.y; j < current.position.y + current.size.y; j++) {
76
int ofs = width * j + i;
77
int bbyte = ofs / 8;
78
int bbit = ofs % 8;
79
80
uint8_t b = data[bbyte];
81
82
if (p_value) {
83
b |= (1 << bbit);
84
} else {
85
b &= ~(1 << bbit);
86
}
87
88
data[bbyte] = b;
89
}
90
}
91
}
92
93
int BitMap::get_true_bit_count() const {
94
int ds = bitmask.size();
95
const uint8_t *d = bitmask.ptr();
96
int c = 0;
97
98
// Fast, almost branchless version.
99
100
for (int i = 0; i < ds; i++) {
101
c += (d[i] & (1 << 7)) >> 7;
102
c += (d[i] & (1 << 6)) >> 6;
103
c += (d[i] & (1 << 5)) >> 5;
104
c += (d[i] & (1 << 4)) >> 4;
105
c += (d[i] & (1 << 3)) >> 3;
106
c += (d[i] & (1 << 2)) >> 2;
107
c += (d[i] & (1 << 1)) >> 1;
108
c += d[i] & 1;
109
}
110
111
return c;
112
}
113
114
void BitMap::set_bitv(const Point2i &p_pos, bool p_value) {
115
set_bit(p_pos.x, p_pos.y, p_value);
116
}
117
118
void BitMap::set_bit(int p_x, int p_y, bool p_value) {
119
ERR_FAIL_INDEX(p_x, width);
120
ERR_FAIL_INDEX(p_y, height);
121
122
int ofs = width * p_y + p_x;
123
int bbyte = ofs / 8;
124
int bbit = ofs % 8;
125
126
uint8_t b = bitmask[bbyte];
127
128
if (p_value) {
129
b |= (1 << bbit);
130
} else {
131
b &= ~(1 << bbit);
132
}
133
134
bitmask.write[bbyte] = b;
135
}
136
137
bool BitMap::get_bitv(const Point2i &p_pos) const {
138
return get_bit(p_pos.x, p_pos.y);
139
}
140
141
bool BitMap::get_bit(int p_x, int p_y) const {
142
ERR_FAIL_INDEX_V(p_x, width, false);
143
ERR_FAIL_INDEX_V(p_y, height, false);
144
145
int ofs = width * p_y + p_x;
146
int bbyte = ofs / 8;
147
int bbit = ofs % 8;
148
149
return (bitmask[bbyte] & (1 << bbit)) != 0;
150
}
151
152
Size2i BitMap::get_size() const {
153
return Size2i(width, height);
154
}
155
156
void BitMap::_set_data(const Dictionary &p_d) {
157
ERR_FAIL_COND(!p_d.has("size"));
158
ERR_FAIL_COND(!p_d.has("data"));
159
160
create(p_d["size"]);
161
bitmask = p_d["data"];
162
}
163
164
Dictionary BitMap::_get_data() const {
165
Dictionary d;
166
d["size"] = get_size();
167
d["data"] = bitmask;
168
return d;
169
}
170
171
Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const {
172
int stepx = 0;
173
int stepy = 0;
174
int prevx = 0;
175
int prevy = 0;
176
int startx = p_start.x;
177
int starty = p_start.y;
178
int curx = startx;
179
int cury = starty;
180
unsigned int count = 0;
181
182
HashMap<Point2i, int> cross_map;
183
184
Vector<Vector2> _points;
185
int points_size = 0;
186
187
Vector<Vector<Vector2>> ret;
188
189
// Add starting entry at start of return.
190
ret.resize(1);
191
192
do {
193
int sv = 0;
194
{ // Square value
195
196
/*
197
checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent
198
+---+---+
199
| 1 | 2 |
200
+---+---+
201
| 4 | 8 | <- current pixel (curx,cury)
202
+---+---+
203
*/
204
Point2i tl = Point2i(curx - 1, cury - 1);
205
sv += (p_rect.has_point(tl) && get_bitv(tl)) ? 1 : 0;
206
Point2i tr = Point2i(curx, cury - 1);
207
sv += (p_rect.has_point(tr) && get_bitv(tr)) ? 2 : 0;
208
Point2i bl = Point2i(curx - 1, cury);
209
sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0;
210
Point2i br = Point2i(curx, cury);
211
sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0;
212
ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector<Vector2>>());
213
}
214
215
switch (sv) {
216
case 1:
217
case 5:
218
case 13:
219
/* going UP with these cases:
220
1 5 13
221
+---+---+ +---+---+ +---+---+
222
| 1 | | | 1 | | | 1 | |
223
+---+---+ +---+---+ +---+---+
224
| | | | 4 | | | 4 | 8 |
225
+---+---+ +---+---+ +---+---+
226
*/
227
stepx = 0;
228
stepy = -1;
229
break;
230
231
case 8:
232
case 10:
233
case 11:
234
/* going DOWN with these cases:
235
8 10 11
236
+---+---+ +---+---+ +---+---+
237
| | | | | 2 | | 1 | 2 |
238
+---+---+ +---+---+ +---+---+
239
| | 8 | | | 8 | | | 8 |
240
+---+---+ +---+---+ +---+---+
241
*/
242
stepx = 0;
243
stepy = 1;
244
break;
245
246
case 4:
247
case 12:
248
case 14:
249
/* going LEFT with these cases:
250
4 12 14
251
+---+---+ +---+---+ +---+---+
252
| | | | | | | | 2 |
253
+---+---+ +---+---+ +---+---+
254
| 4 | | | 4 | 8 | | 4 | 8 |
255
+---+---+ +---+---+ +---+---+
256
*/
257
stepx = -1;
258
stepy = 0;
259
break;
260
261
case 2:
262
case 3:
263
case 7:
264
/* going RIGHT with these cases:
265
2 3 7
266
+---+---+ +---+---+ +---+---+
267
| | 2 | | 1 | 2 | | 1 | 2 |
268
+---+---+ +---+---+ +---+---+
269
| | | | | | | 4 | |
270
+---+---+ +---+---+ +---+---+
271
*/
272
stepx = 1;
273
stepy = 0;
274
break;
275
case 9:
276
/* Going DOWN if coming from the LEFT, otherwise go UP.
277
9
278
+---+---+
279
| 1 | |
280
+---+---+
281
| | 8 |
282
+---+---+
283
*/
284
285
if (prevx == 1) {
286
stepx = 0;
287
stepy = 1;
288
} else {
289
stepx = 0;
290
stepy = -1;
291
}
292
break;
293
case 6:
294
/* Going RIGHT if coming from BELOW, otherwise go LEFT.
295
6
296
+---+---+
297
| | 2 |
298
+---+---+
299
| 4 | |
300
+---+---+
301
*/
302
303
if (prevy == -1) {
304
stepx = 1;
305
stepy = 0;
306
} else {
307
stepx = -1;
308
stepy = 0;
309
}
310
break;
311
default:
312
ERR_PRINT("this shouldn't happen.");
313
}
314
315
// Handle crossing points.
316
if (sv == 6 || sv == 9) {
317
const Point2i cur_pos(curx, cury);
318
319
// Find if this point has occurred before.
320
if (HashMap<Point2i, int>::Iterator found = cross_map.find(cur_pos)) {
321
// Add points after the previous crossing to the result.
322
ret.push_back(_points.slice(found->value + 1, points_size));
323
324
// Remove points after crossing point.
325
points_size = found->value + 1;
326
327
// Erase trailing map elements.
328
while (cross_map.last() != found) {
329
cross_map.remove(cross_map.last());
330
}
331
332
cross_map.erase(cur_pos);
333
} else {
334
// Add crossing point to map.
335
cross_map.insert(cur_pos, points_size - 1);
336
}
337
}
338
339
// Small optimization:
340
// If the previous direction is same as the current direction,
341
// then we should modify the last vector to current.
342
curx += stepx;
343
cury += stepy;
344
if (stepx == prevx && stepy == prevy) {
345
_points.set(points_size - 1, Vector2(curx, cury) - p_rect.position);
346
} else {
347
_points.resize(MAX(points_size + 1, _points.size()));
348
_points.set(points_size, Vector2(curx, cury) - p_rect.position);
349
points_size++;
350
}
351
352
count++;
353
prevx = stepx;
354
prevy = stepy;
355
356
ERR_FAIL_COND_V((int)count > 2 * (width * height + 1), Vector<Vector<Vector2>>());
357
} while (curx != startx || cury != starty);
358
359
// Add remaining points to result.
360
_points.resize(points_size);
361
362
ret.set(0, _points);
363
364
return ret;
365
}
366
367
static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) {
368
float res;
369
float slope;
370
float intercept;
371
372
if (start.x == end.x) {
373
res = Math::abs(i.x - end.x);
374
} else if (start.y == end.y) {
375
res = Math::abs(i.y - end.y);
376
} else {
377
slope = (end.y - start.y) / (end.x - start.x);
378
intercept = start.y - (slope * start.x);
379
res = Math::abs(slope * i.x - i.y + intercept) / Math::sqrt(Math::pow(slope, 2.0f) + 1.0);
380
}
381
return res;
382
}
383
384
static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) {
385
if (v.size() < 3) {
386
return v;
387
}
388
389
int index = -1;
390
float dist = 0.0;
391
// Not looping first and last point.
392
for (size_t i = 1, size = v.size(); i < size - 1; ++i) {
393
float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]);
394
if (cdist > dist) {
395
dist = cdist;
396
index = static_cast<int>(i);
397
}
398
}
399
if (dist > optimization) {
400
Vector<Vector2> left, right;
401
left.resize(index);
402
for (int i = 0; i < index; i++) {
403
left.write[i] = v[i];
404
}
405
right.resize(v.size() - index);
406
for (int i = 0; i < right.size(); i++) {
407
right.write[i] = v[index + i];
408
}
409
Vector<Vector2> r1 = rdp(left, optimization);
410
Vector<Vector2> r2 = rdp(right, optimization);
411
412
int middle = r1.size();
413
r1.resize(r1.size() + r2.size());
414
for (int i = 0; i < r2.size(); i++) {
415
r1.write[middle + i] = r2[i];
416
}
417
return r1;
418
} else {
419
Vector<Vector2> ret;
420
ret.push_back(v[0]);
421
ret.push_back(v[v.size() - 1]);
422
return ret;
423
}
424
}
425
426
static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect, float epsilon) {
427
int size = points.size();
428
// If there are less than 3 points, then we have nothing.
429
ERR_FAIL_COND_V(size < 3, Vector<Vector2>());
430
// If there are less than 9 points (but more than 3), then we don't need to reduce it.
431
if (size < 9) {
432
return points;
433
}
434
435
float maxEp = MIN(rect.size.width, rect.size.height);
436
float ep = CLAMP(epsilon, 0.0, maxEp / 2);
437
Vector<Vector2> result = rdp(points, ep);
438
439
Vector2 last = result[result.size() - 1];
440
441
if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) {
442
result.write[0].y = last.y;
443
result.resize(result.size() - 1);
444
}
445
return result;
446
}
447
448
struct FillBitsStackEntry {
449
Point2i pos;
450
int i = 0;
451
int j = 0;
452
};
453
454
static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) {
455
// Using a custom stack to work iteratively to avoid stack overflow on big bitmaps.
456
Vector<FillBitsStackEntry> stack;
457
// Tracking size since we won't be shrinking the stack vector.
458
int stack_size = 0;
459
460
Point2i pos = p_pos;
461
int next_i = 0;
462
int next_j = 0;
463
464
bool reenter = true;
465
bool popped = false;
466
do {
467
if (reenter) {
468
next_i = pos.x - 1;
469
next_j = pos.y - 1;
470
reenter = false;
471
}
472
473
for (int i = next_i; i <= pos.x + 1; i++) {
474
for (int j = next_j; j <= pos.y + 1; j++) {
475
if (popped) {
476
// The next loop over j must start normally.
477
next_j = pos.y - 1;
478
popped = false;
479
// Skip because an iteration was already executed with current counter values.
480
continue;
481
}
482
483
if (i < rect.position.x || i >= rect.position.x + rect.size.x) {
484
continue;
485
}
486
if (j < rect.position.y || j >= rect.position.y + rect.size.y) {
487
continue;
488
}
489
490
if (p_map->get_bit(i, j)) {
491
continue;
492
493
} else if (p_src->get_bit(i, j)) {
494
p_map->set_bit(i, j, true);
495
496
FillBitsStackEntry se = { pos, i, j };
497
stack.resize(MAX(stack_size + 1, stack.size()));
498
stack.set(stack_size, se);
499
stack_size++;
500
501
pos = Point2i(i, j);
502
reenter = true;
503
break;
504
}
505
}
506
if (reenter) {
507
break;
508
}
509
}
510
if (!reenter) {
511
if (stack_size) {
512
FillBitsStackEntry se = stack.get(stack_size - 1);
513
stack_size--;
514
pos = se.pos;
515
next_i = se.i;
516
next_j = se.j;
517
popped = true;
518
}
519
}
520
} while (reenter || popped);
521
}
522
523
Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const {
524
Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
525
526
Ref<BitMap> fill;
527
fill.instantiate();
528
fill->create(get_size());
529
530
Vector<Vector<Vector2>> polygons;
531
for (int i = r.position.y; i < r.position.y + r.size.height; i++) {
532
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
533
if (!fill->get_bit(j, i) && get_bit(j, i)) {
534
fill_bits(this, fill, Point2i(j, i), r);
535
536
for (Vector<Vector2> polygon : _march_square(r, Point2i(j, i))) {
537
polygon = reduce(polygon, r, p_epsilon);
538
539
if (polygon.size() < 3) {
540
print_verbose("Invalid polygon, skipped");
541
continue;
542
}
543
544
polygons.push_back(polygon);
545
}
546
}
547
}
548
}
549
550
return polygons;
551
}
552
553
void BitMap::grow_mask(int p_pixels, const Rect2i &p_rect) {
554
if (p_pixels == 0) {
555
return;
556
}
557
558
bool bit_value = p_pixels > 0;
559
p_pixels = Math::abs(p_pixels);
560
const int pixels2 = p_pixels * p_pixels;
561
562
Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect);
563
564
Ref<BitMap> copy;
565
copy.instantiate();
566
copy->create(get_size());
567
copy->bitmask = bitmask;
568
569
for (int i = r.position.y; i < r.position.y + r.size.height; i++) {
570
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
571
if (bit_value == get_bit(j, i)) {
572
continue;
573
}
574
575
bool found = false;
576
577
for (int y = i - p_pixels; y <= i + p_pixels; y++) {
578
for (int x = j - p_pixels; x <= j + p_pixels; x++) {
579
bool outside = false;
580
581
if ((x < p_rect.position.x) || (x >= p_rect.position.x + p_rect.size.x) || (y < p_rect.position.y) || (y >= p_rect.position.y + p_rect.size.y)) {
582
// Outside of rectangle counts as bit not set.
583
if (!bit_value) {
584
outside = true;
585
} else {
586
continue;
587
}
588
}
589
590
float d = Point2(j, i).distance_squared_to(Point2(x, y)) - CMP_EPSILON2;
591
if (d > pixels2) {
592
continue;
593
}
594
595
if (outside || (bit_value == copy->get_bit(x, y))) {
596
found = true;
597
break;
598
}
599
}
600
if (found) {
601
break;
602
}
603
}
604
605
if (found) {
606
set_bit(j, i, bit_value);
607
}
608
}
609
}
610
}
611
612
void BitMap::shrink_mask(int p_pixels, const Rect2i &p_rect) {
613
grow_mask(-p_pixels, p_rect);
614
}
615
616
TypedArray<PackedVector2Array> BitMap::_opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const {
617
Vector<Vector<Vector2>> result = clip_opaque_to_polygons(p_rect, p_epsilon);
618
619
// Convert result to bindable types.
620
621
TypedArray<PackedVector2Array> result_array;
622
result_array.resize(result.size());
623
for (int i = 0; i < result.size(); i++) {
624
const Vector<Vector2> &polygon = result[i];
625
626
PackedVector2Array polygon_array;
627
polygon_array.resize(polygon.size());
628
629
{
630
Vector2 *w = polygon_array.ptrw();
631
for (int j = 0; j < polygon.size(); j++) {
632
w[j] = polygon[j];
633
}
634
}
635
636
result_array[i] = polygon_array;
637
}
638
639
return result_array;
640
}
641
642
void BitMap::resize(const Size2i &p_new_size) {
643
ERR_FAIL_COND(p_new_size.width < 0 || p_new_size.height < 0);
644
if (p_new_size == get_size()) {
645
return;
646
}
647
648
Ref<BitMap> new_bitmap;
649
new_bitmap.instantiate();
650
new_bitmap->create(p_new_size);
651
// also allow for upscaling
652
int lw = (width == 0) ? 0 : p_new_size.width;
653
int lh = (height == 0) ? 0 : p_new_size.height;
654
655
float scale_x = ((float)width / p_new_size.width);
656
float scale_y = ((float)height / p_new_size.height);
657
for (int x = 0; x < lw; x++) {
658
for (int y = 0; y < lh; y++) {
659
bool new_bit = get_bit(x * scale_x, y * scale_y);
660
new_bitmap->set_bit(x, y, new_bit);
661
}
662
}
663
664
width = new_bitmap->width;
665
height = new_bitmap->height;
666
bitmask = new_bitmap->bitmask;
667
}
668
669
Ref<Image> BitMap::convert_to_image() const {
670
Ref<Image> image = Image::create_empty(width, height, false, Image::FORMAT_L8);
671
672
for (int i = 0; i < width; i++) {
673
for (int j = 0; j < height; j++) {
674
image->set_pixel(i, j, get_bit(i, j) ? Color(1, 1, 1) : Color(0, 0, 0));
675
}
676
}
677
678
return image;
679
}
680
681
void BitMap::blit(const Vector2i &p_pos, const Ref<BitMap> &p_bitmap) {
682
ERR_FAIL_COND_MSG(p_bitmap.is_null(), "It's not a reference to a valid BitMap object.");
683
684
int x = p_pos.x;
685
int y = p_pos.y;
686
int w = p_bitmap->get_size().width;
687
int h = p_bitmap->get_size().height;
688
689
for (int i = 0; i < w; i++) {
690
for (int j = 0; j < h; j++) {
691
int px = x + i;
692
int py = y + j;
693
if (px < 0 || px >= width) {
694
continue;
695
}
696
if (py < 0 || py >= height) {
697
continue;
698
}
699
if (p_bitmap->get_bit(i, j)) {
700
set_bit(px, py, true);
701
}
702
}
703
}
704
}
705
706
void BitMap::_bind_methods() {
707
ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create);
708
ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image", "threshold"), &BitMap::create_from_image_alpha, DEFVAL(0.1));
709
710
ClassDB::bind_method(D_METHOD("set_bitv", "position", "bit"), &BitMap::set_bitv);
711
ClassDB::bind_method(D_METHOD("set_bit", "x", "y", "bit"), &BitMap::set_bit);
712
ClassDB::bind_method(D_METHOD("get_bitv", "position"), &BitMap::get_bitv);
713
ClassDB::bind_method(D_METHOD("get_bit", "x", "y"), &BitMap::get_bit);
714
715
ClassDB::bind_method(D_METHOD("set_bit_rect", "rect", "bit"), &BitMap::set_bit_rect);
716
ClassDB::bind_method(D_METHOD("get_true_bit_count"), &BitMap::get_true_bit_count);
717
718
ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size);
719
ClassDB::bind_method(D_METHOD("resize", "new_size"), &BitMap::resize);
720
721
ClassDB::bind_method(D_METHOD("_set_data", "data"), &BitMap::_set_data);
722
ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data);
723
724
ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask);
725
ClassDB::bind_method(D_METHOD("convert_to_image"), &BitMap::convert_to_image);
726
ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0));
727
728
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
729
}
730
731