Skip to content

Commit 0445ccf

Browse files
Calinouakien-mga
authored andcommitted
Fix Image CowData crash when baking large lightmaps
This switches to 64-bit integers in select locations of the Image class, so that image resolutions of 16384×16384 (used by lightmap texture arrays) can be used properly. Values that are larger should also work. VRAM compression is also supported, although most VRAM-compressed formats are limited to individual slices of 16384×16384. WebP is limited to 16383×16383 due to format limitations.
1 parent 293c0f7 commit 0445ccf

File tree

7 files changed

+70
-76
lines changed

7 files changed

+70
-76
lines changed

core/io/image.cpp

Lines changed: 39 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,10 @@ int Image::get_format_block_size(Format p_format) {
300300
return 1;
301301
}
302302

303-
void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_width, int &r_height) const {
303+
void Image::_get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const {
304304
int w = width;
305305
int h = height;
306-
int ofs = 0;
306+
int64_t ofs = 0;
307307

308308
int pixel_size = get_format_pixel_size(format);
309309
int pixel_rshift = get_format_pixel_rshift(format);
@@ -315,7 +315,7 @@ void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_widt
315315
int bw = w % block != 0 ? w + (block - w % block) : w;
316316
int bh = h % block != 0 ? h + (block - h % block) : h;
317317

318-
int s = bw * bh;
318+
int64_t s = bw * bh;
319319

320320
s *= pixel_size;
321321
s >>= pixel_rshift;
@@ -329,37 +329,30 @@ void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_widt
329329
r_height = h;
330330
}
331331

332-
int Image::get_mipmap_offset(int p_mipmap) const {
332+
int64_t Image::get_mipmap_offset(int p_mipmap) const {
333333
ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1);
334334

335-
int ofs, w, h;
335+
int64_t ofs;
336+
int w, h;
336337
_get_mipmap_offset_and_size(p_mipmap, ofs, w, h);
337338
return ofs;
338339
}
339340

340-
int Image::get_mipmap_byte_size(int p_mipmap) const {
341-
ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1);
342-
343-
int ofs, w, h;
344-
_get_mipmap_offset_and_size(p_mipmap, ofs, w, h);
345-
int ofs2;
346-
_get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w, h);
347-
return ofs2 - ofs;
348-
}
349-
350-
void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const {
351-
int ofs, w, h;
341+
void Image::get_mipmap_offset_and_size(int p_mipmap, int64_t &r_ofs, int64_t &r_size) const {
342+
int64_t ofs;
343+
int w, h;
352344
_get_mipmap_offset_and_size(p_mipmap, ofs, w, h);
353-
int ofs2;
345+
int64_t ofs2;
354346
_get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w, h);
355347
r_ofs = ofs;
356348
r_size = ofs2 - ofs;
357349
}
358350

359-
void Image::get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const {
360-
int ofs;
351+
void Image::get_mipmap_offset_size_and_dimensions(int p_mipmap, int64_t &r_ofs, int64_t &r_size, int &w, int &h) const {
352+
int64_t ofs;
361353
_get_mipmap_offset_and_size(p_mipmap, ofs, w, h);
362-
int ofs2, w2, h2;
354+
int64_t ofs2;
355+
int w2, h2;
363356
_get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w2, h2);
364357
r_ofs = ofs;
365358
r_size = ofs2 - ofs;
@@ -538,8 +531,8 @@ void Image::convert(Format p_new_format) {
538531
}
539532
}
540533

541-
int mip_offset = 0;
542-
int mip_size = 0;
534+
int64_t mip_offset = 0;
535+
int64_t mip_size = 0;
543536
new_img.get_mipmap_offset_and_size(mip, mip_offset, mip_size);
544537

545538
memcpy(new_img.data.ptrw() + mip_offset, new_mip->data.ptr(), mip_size);
@@ -555,8 +548,8 @@ void Image::convert(Format p_new_format) {
555548
int conversion_type = format | p_new_format << 8;
556549

557550
for (int mip = 0; mip < mipmap_count; mip++) {
558-
int mip_offset = 0;
559-
int mip_size = 0;
551+
int64_t mip_offset = 0;
552+
int64_t mip_size = 0;
560553
int mip_width = 0;
561554
int mip_height = 0;
562555
get_mipmap_offset_size_and_dimensions(mip, mip_offset, mip_size, mip_width, mip_height);
@@ -1151,15 +1144,15 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
11511144
if (i == 0) {
11521145
// Read from the first mipmap that will be interpolated
11531146
// (if both levels are the same, we will not interpolate, but at least we'll sample from the right level)
1154-
int offs;
1147+
int64_t offs;
11551148
_get_mipmap_offset_and_size(mip1, offs, src_width, src_height);
11561149
src_ptr = r_ptr + offs;
11571150
} else if (!interpolate_mipmaps) {
11581151
// No need generate a second image
11591152
break;
11601153
} else {
11611154
// Switch to read from the second mipmap that will be interpolated
1162-
int offs;
1155+
int64_t offs;
11631156
_get_mipmap_offset_and_size(mip2, offs, src_width, src_height);
11641157
src_ptr = r_ptr + offs;
11651158
// Switch to write to the second destination image
@@ -1599,9 +1592,9 @@ void Image::flip_x() {
15991592
}
16001593

16011594
/// Get mipmap size and offset.
1602-
int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) {
1595+
int64_t Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) {
16031596
// Data offset in mipmaps (including the original texture).
1604-
int size = 0;
1597+
int64_t size = 0;
16051598

16061599
int w = p_width;
16071600
int h = p_height;
@@ -1623,7 +1616,7 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &
16231616
int bw = w % block != 0 ? w + (block - w % block) : w;
16241617
int bh = h % block != 0 ? h + (block - h % block) : h;
16251618

1626-
int s = bw * bh;
1619+
int64_t s = bw * bh;
16271620

16281621
s *= pixsize;
16291622
s >>= pixshift;
@@ -1837,7 +1830,8 @@ Error Image::generate_mipmaps(bool p_renormalize) {
18371830
int prev_w = width;
18381831

18391832
for (int i = 1; i <= mmcount; i++) {
1840-
int ofs, w, h;
1833+
int64_t ofs;
1834+
int w, h;
18411835
_get_mipmap_offset_and_size(i, ofs, w, h);
18421836

18431837
switch (format) {
@@ -1993,7 +1987,8 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
19931987
uint8_t *base_ptr = data.ptrw();
19941988

19951989
for (int i = 1; i <= mmcount; i++) {
1996-
int ofs, w, h;
1990+
int64_t ofs;
1991+
int w, h;
19971992
_get_mipmap_offset_and_size(i, ofs, w, h);
19981993
uint8_t *ptr = &base_ptr[ofs];
19991994

@@ -2102,21 +2097,6 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
21022097
_set_color_at_ofs(ptr, pixel_ofs, c);
21032098
}
21042099
}
2105-
#if 0
2106-
{
2107-
int size = get_mipmap_byte_size(i);
2108-
print_line("size for mimpap " + itos(i) + ": " + itos(size));
2109-
Vector<uint8_t> imgdata;
2110-
imgdata.resize(size);
2111-
2112-
2113-
uint8_t* wr = imgdata.ptrw();
2114-
memcpy(wr.ptr(), ptr, size);
2115-
wr = uint8_t*();
2116-
Ref<Image> im = Image::create_from_data(w, h, false, format, imgdata);
2117-
im->save_png("res://mipmap_" + itos(i) + ".png");
2118-
}
2119-
#endif
21202100
}
21212101

21222102
return OK;
@@ -2131,7 +2111,8 @@ void Image::clear_mipmaps() {
21312111
return;
21322112
}
21332113

2134-
int ofs, w, h;
2114+
int64_t ofs;
2115+
int w, h;
21352116
_get_mipmap_offset_and_size(1, ofs, w, h);
21362117
data.resize(ofs);
21372118

@@ -2176,7 +2157,7 @@ void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Forma
21762157
ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "The Image format specified (" + itos(p_format) + ") is out of range. See Image's Format enum.");
21772158

21782159
int mm = 0;
2179-
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
2160+
int64_t size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
21802161
data.resize(size);
21812162

21822163
{
@@ -2202,7 +2183,7 @@ void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Forma
22022183
ERR_FAIL_INDEX_MSG(p_format, FORMAT_MAX, "The Image format specified (" + itos(p_format) + ") is out of range. See Image's Format enum.");
22032184

22042185
int mm;
2205-
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
2186+
int64_t size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
22062187

22072188
if (unlikely(p_data.size() != size)) {
22082189
String description_mipmaps = get_format_name(p_format) + " ";
@@ -2405,7 +2386,7 @@ bool Image::is_invisible() const {
24052386
return false;
24062387
}
24072388

2408-
int len = data.size();
2389+
int64_t len = data.size();
24092390

24102391
if (len == 0) {
24112392
return true;
@@ -2445,7 +2426,7 @@ bool Image::is_invisible() const {
24452426
}
24462427

24472428
Image::AlphaMode Image::detect_alpha() const {
2448-
int len = data.size();
2429+
int64_t len = data.size();
24492430

24502431
if (len == 0) {
24512432
return ALPHA_NONE;
@@ -2597,15 +2578,15 @@ Size2i Image::get_image_mipmap_size(int p_width, int p_height, Format p_format,
25972578
return ret;
25982579
}
25992580

2600-
int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) {
2581+
int64_t Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) {
26012582
if (p_mipmap <= 0) {
26022583
return 0;
26032584
}
26042585
int mm;
26052586
return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1);
26062587
}
26072588

2608-
int Image::get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h) {
2589+
int64_t Image::get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h) {
26092590
if (p_mipmap <= 0) {
26102591
r_w = p_width;
26112592
r_h = p_height;
@@ -3642,9 +3623,10 @@ Ref<Image> Image::rgbe_to_srgb() {
36423623
return new_image;
36433624
}
36443625

3645-
Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
3646-
int ofs, size, w, h;
3647-
get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h);
3626+
Ref<Image> Image::get_image_from_mipmap(int p_mipmap) const {
3627+
int64_t ofs, size;
3628+
int w, h;
3629+
get_mipmap_offset_size_and_dimensions(p_mipmap, ofs, size, w, h);
36483630

36493631
Vector<uint8_t> new_data;
36503632
new_data.resize(size);

core/io/image.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ class Image : public Resource {
195195
data = p_image.data;
196196
}
197197

198-
_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
198+
_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
199199

200-
static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = nullptr, int *r_mm_height = nullptr);
200+
static int64_t _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = nullptr, int *r_mm_height = nullptr);
201201
bool _can_modify(Format p_format) const;
202202

203203
_FORCE_INLINE_ void _get_clipped_src_and_dest_rects(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest, Rect2i &r_clipped_src_rect, Rect2i &r_clipped_dest_rect) const;
@@ -238,10 +238,12 @@ class Image : public Resource {
238238
*/
239239
Format get_format() const;
240240

241-
int get_mipmap_byte_size(int p_mipmap) const; //get where the mipmap begins in data
242-
int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data
243-
void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data
244-
void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data
241+
/**
242+
* Get where the mipmap begins in data.
243+
*/
244+
int64_t get_mipmap_offset(int p_mipmap) const;
245+
void get_mipmap_offset_and_size(int p_mipmap, int64_t &r_ofs, int64_t &r_size) const;
246+
void get_mipmap_offset_size_and_dimensions(int p_mipmap, int64_t &r_ofs, int64_t &r_size, int &w, int &h) const;
245247

246248
enum Image3DValidateError {
247249
VALIDATE_3D_OK,
@@ -354,8 +356,8 @@ class Image : public Resource {
354356
static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false);
355357
static int get_image_required_mipmaps(int p_width, int p_height, Format p_format);
356358
static Size2i get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap);
357-
static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
358-
static int get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
359+
static int64_t get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
360+
static int64_t get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
359361

360362
enum CompressMode {
361363
COMPRESS_S3TC,
@@ -383,7 +385,7 @@ class Image : public Resource {
383385
void srgb_to_linear();
384386
void normal_map_to_xy();
385387
Ref<Image> rgbe_to_srgb();
386-
Ref<Image> get_image_from_mipmap(int p_mipamp) const;
388+
Ref<Image> get_image_from_mipmap(int p_mipmap) const;
387389
void bump_map_to_normal_map(float bump_scale = 1.0);
388390

389391
void blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);

drivers/gles3/storage/texture_storage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,7 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image,
14911491
int tsize = 0;
14921492

14931493
for (int i = 0; i < mipmaps; i++) {
1494-
int size, ofs;
1494+
int64_t size, ofs;
14951495
img->get_mipmap_offset_and_size(i, ofs, size);
14961496
if (compressed) {
14971497
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

misc/extension_api_validation/4.2-stable.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,11 @@ GH-93982
372372
Validate extension JSON: Error: Field 'classes/Sprite3D/properties/frame_coords': type changed value in new API, from "Vector2" to "Vector2i".
373373

374374
The type was wrong to begin with and has been corrected. Vector2 and Vector2i are convertible, so it should be compatible.
375+
376+
377+
GH-94243
378+
--------
379+
Validate extension JSON: Error: Field 'classes/Image/methods/get_mipmap_offset/return_value': meta changed value in new API, from "int32" to "int64".
380+
381+
Type changed to int64_t to support baking large lightmaps.
382+
No compatibility method needed, both GDExtension and C# generate it as int64_t anyway.

modules/basis_universal/image_compress_basisu.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
120120
Vector<uint32_t> mip_data_padded;
121121

122122
for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
123-
int ofs, size, width, height;
123+
int64_t ofs, size;
124+
int width, height;
124125
image->get_mipmap_offset_size_and_dimensions(i, ofs, size, width, height);
125126

126127
const uint8_t *image_mip_data = image_data.ptr() + ofs;

modules/squish/image_decompress_squish.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ void image_decompress_squish(Image *p_image) {
7777
}
7878

7979
for (int i = 0; i <= mm_count; i++) {
80-
int src_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0;
80+
int64_t src_ofs = 0, mipmap_size = 0;
81+
int mipmap_w = 0, mipmap_h = 0;
8182
p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h);
8283

83-
int dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i);
84+
int64_t dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i);
8485
squish::DecompressImage(&wb[dst_ofs], w, h, &rb[src_ofs], squish_flags);
8586

8687
w >>= 1;

tests/core/io/test_image.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,8 @@ TEST_CASE("[Image] Custom mipmaps") {
355355
uint8_t *data_ptr = data.ptrw();
356356

357357
for (int mip = 0; mip < mipmaps; mip++) {
358-
int mip_offset = 0;
359-
int mip_size = 0;
358+
int64_t mip_offset = 0;
359+
int64_t mip_size = 0;
360360
image->get_mipmap_offset_and_size(mip, mip_offset, mip_size);
361361

362362
for (int i = 0; i < mip_size; i++) {
@@ -378,8 +378,8 @@ TEST_CASE("[Image] Custom mipmaps") {
378378
const uint8_t *data_ptr = data.ptr();
379379

380380
for (int mip = 0; mip < mipmaps; mip++) {
381-
int mip_offset = 0;
382-
int mip_size = 0;
381+
int64_t mip_offset = 0;
382+
int64_t mip_size = 0;
383383
image_bytes->get_mipmap_offset_and_size(mip, mip_offset, mip_size);
384384

385385
for (int i = 0; i < mip_size; i++) {
@@ -402,8 +402,8 @@ TEST_CASE("[Image] Custom mipmaps") {
402402
const uint8_t *data_ptr = data.ptr();
403403

404404
for (int mip = 0; mip < mipmaps; mip++) {
405-
int mip_offset = 0;
406-
int mip_size = 0;
405+
int64_t mip_offset = 0;
406+
int64_t mip_size = 0;
407407
image_rgbaf->get_mipmap_offset_and_size(mip, mip_offset, mip_size);
408408

409409
for (int i = 0; i < mip_size; i += 4) {

0 commit comments

Comments
 (0)