summaryrefslogtreecommitdiffstats
path: root/core/io/image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/io/image.cpp')
-rw-r--r--core/io/image.cpp303
1 files changed, 176 insertions, 127 deletions
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 9bb987b670..ce08b417a8 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -517,21 +517,31 @@ void Image::convert(Format p_new_format) {
return;
}
+ // Includes the main image.
+ const int mipmap_count = get_mipmap_count() + 1;
+
if (format > FORMAT_RGBE9995 || p_new_format > FORMAT_RGBE9995) {
ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead.");
} else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) {
//use put/set pixel which is slower but works with non byte formats
- Image new_img(width, height, false, p_new_format);
+ Image new_img(width, height, mipmaps, p_new_format);
- for (int i = 0; i < width; i++) {
- for (int j = 0; j < height; j++) {
- new_img.set_pixel(i, j, get_pixel(i, j));
+ for (int mip = 0; mip < mipmap_count; mip++) {
+ Ref<Image> src_mip = get_image_from_mipmap(mip);
+ Ref<Image> new_mip = new_img.get_image_from_mipmap(mip);
+
+ for (int y = 0; y < src_mip->height; y++) {
+ for (int x = 0; x < src_mip->width; x++) {
+ new_mip->set_pixel(x, y, src_mip->get_pixel(x, y));
+ }
}
- }
- if (has_mipmaps()) {
- new_img.generate_mipmaps();
+ int mip_offset = 0;
+ int mip_size = 0;
+ new_img.get_mipmap_offset_and_size(mip, mip_offset, mip_size);
+
+ memcpy(new_img.data.ptrw() + mip_offset, new_mip->data.ptr(), mip_size);
}
_copy_internals_from(new_img);
@@ -539,113 +549,115 @@ void Image::convert(Format p_new_format) {
return;
}
- Image new_img(width, height, false, p_new_format);
-
- const uint8_t *rptr = data.ptr();
- uint8_t *wptr = new_img.data.ptrw();
+ Image new_img(width, height, mipmaps, p_new_format);
int conversion_type = format | p_new_format << 8;
- switch (conversion_type) {
- case FORMAT_L8 | (FORMAT_LA8 << 8):
- _convert<1, false, 1, true, true, true>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_R8 << 8):
- _convert<1, false, 1, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_RG8 << 8):
- _convert<1, false, 2, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_RGB8 << 8):
- _convert<1, false, 3, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_L8 | (FORMAT_RGBA8 << 8):
- _convert<1, false, 3, true, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_L8 << 8):
- _convert<1, true, 1, false, true, true>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_R8 << 8):
- _convert<1, true, 1, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_RG8 << 8):
- _convert<1, true, 2, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_RGB8 << 8):
- _convert<1, true, 3, false, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_LA8 | (FORMAT_RGBA8 << 8):
- _convert<1, true, 3, true, true, false>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_L8 << 8):
- _convert<1, false, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_LA8 << 8):
- _convert<1, false, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_RG8 << 8):
- _convert<1, false, 2, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_RGB8 << 8):
- _convert<1, false, 3, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_R8 | (FORMAT_RGBA8 << 8):
- _convert<1, false, 3, true, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_L8 << 8):
- _convert<2, false, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_LA8 << 8):
- _convert<2, false, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_R8 << 8):
- _convert<2, false, 1, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_RGB8 << 8):
- _convert<2, false, 3, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RG8 | (FORMAT_RGBA8 << 8):
- _convert<2, false, 3, true, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_L8 << 8):
- _convert<3, false, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_LA8 << 8):
- _convert<3, false, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_R8 << 8):
- _convert<3, false, 1, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_RG8 << 8):
- _convert<3, false, 2, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGB8 | (FORMAT_RGBA8 << 8):
- _convert<3, false, 3, true, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_L8 << 8):
- _convert<3, true, 1, false, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_LA8 << 8):
- _convert<3, true, 1, true, false, true>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_R8 << 8):
- _convert<3, true, 1, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_RG8 << 8):
- _convert<3, true, 2, false, false, false>(width, height, rptr, wptr);
- break;
- case FORMAT_RGBA8 | (FORMAT_RGB8 << 8):
- _convert<3, true, 3, false, false, false>(width, height, rptr, wptr);
- break;
- }
-
- bool gen_mipmaps = mipmaps;
+ for (int mip = 0; mip < mipmap_count; mip++) {
+ int mip_offset = 0;
+ int mip_size = 0;
+ int mip_width = 0;
+ int mip_height = 0;
+ get_mipmap_offset_size_and_dimensions(mip, mip_offset, mip_size, mip_width, mip_height);
- _copy_internals_from(new_img);
+ const uint8_t *rptr = data.ptr() + mip_offset;
+ uint8_t *wptr = new_img.data.ptrw() + new_img.get_mipmap_offset(mip);
- if (gen_mipmaps) {
- generate_mipmaps();
+ switch (conversion_type) {
+ case FORMAT_L8 | (FORMAT_LA8 << 8):
+ _convert<1, false, 1, true, true, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_R8 << 8):
+ _convert<1, false, 1, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_RG8 << 8):
+ _convert<1, false, 2, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_RGB8 << 8):
+ _convert<1, false, 3, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_L8 | (FORMAT_RGBA8 << 8):
+ _convert<1, false, 3, true, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_L8 << 8):
+ _convert<1, true, 1, false, true, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_R8 << 8):
+ _convert<1, true, 1, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_RG8 << 8):
+ _convert<1, true, 2, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_RGB8 << 8):
+ _convert<1, true, 3, false, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_LA8 | (FORMAT_RGBA8 << 8):
+ _convert<1, true, 3, true, true, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_L8 << 8):
+ _convert<1, false, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_LA8 << 8):
+ _convert<1, false, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_RG8 << 8):
+ _convert<1, false, 2, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_RGB8 << 8):
+ _convert<1, false, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_R8 | (FORMAT_RGBA8 << 8):
+ _convert<1, false, 3, true, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_L8 << 8):
+ _convert<2, false, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_LA8 << 8):
+ _convert<2, false, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_R8 << 8):
+ _convert<2, false, 1, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_RGB8 << 8):
+ _convert<2, false, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RG8 | (FORMAT_RGBA8 << 8):
+ _convert<2, false, 3, true, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_L8 << 8):
+ _convert<3, false, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_LA8 << 8):
+ _convert<3, false, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_R8 << 8):
+ _convert<3, false, 1, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_RG8 << 8):
+ _convert<3, false, 2, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGB8 | (FORMAT_RGBA8 << 8):
+ _convert<3, false, 3, true, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_L8 << 8):
+ _convert<3, true, 1, false, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_LA8 << 8):
+ _convert<3, true, 1, true, false, true>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_R8 << 8):
+ _convert<3, true, 1, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_RG8 << 8):
+ _convert<3, true, 2, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ case FORMAT_RGBA8 | (FORMAT_RGB8 << 8):
+ _convert<3, true, 3, false, false, false>(mip_width, mip_height, rptr, wptr);
+ break;
+ }
}
+
+ _copy_internals_from(new_img);
}
Image::Format Image::get_format() const {
@@ -1918,8 +1930,7 @@ Error Image::generate_mipmaps(bool p_renormalize) {
}
Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) {
- Vector<double> normal_sat_vec; //summed area table
- double *normal_sat = nullptr; //summed area table for normal map
+ LocalVector<double> normal_sat_vec; //summed area table
int normal_w = 0, normal_h = 0;
ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->is_empty(), ERR_INVALID_PARAMETER, "Must provide a valid normal map for roughness mipmaps");
@@ -1933,8 +1944,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
normal_h = nm->get_height();
normal_sat_vec.resize(normal_w * normal_h * 3);
-
- normal_sat = normal_sat_vec.ptrw();
+ double *normal_sat = normal_sat_vec.ptr();
//create summed area table
@@ -2009,24 +2019,26 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
avg[2] += normal_sat[tofs + 2];
}
- if (from_y > 0) {
+ if (from_y > 0 && to_x > 0) {
uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3;
avg[0] -= normal_sat[tofs + 0];
avg[1] -= normal_sat[tofs + 1];
avg[2] -= normal_sat[tofs + 2];
}
- if (from_x > 0) {
+ if (from_x > 0 && to_y > 0) {
uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3;
avg[0] -= normal_sat[tofs + 0];
avg[1] -= normal_sat[tofs + 1];
avg[2] -= normal_sat[tofs + 2];
}
- uint32_t tofs = (to_y * normal_w + to_x) * 3;
- avg[0] += normal_sat[tofs + 0];
- avg[1] += normal_sat[tofs + 1];
- avg[2] += normal_sat[tofs + 2];
+ if (to_y > 0 && to_x > 0) {
+ uint32_t tofs = (to_y * normal_w + to_x) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+ }
double div = double(size_x * size_y);
Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div);
@@ -2344,7 +2356,7 @@ void Image::initialize_data(const char **p_xpm) {
}
Color *colorptr = colormap.getptr(pixelstr);
- ERR_FAIL_COND(!colorptr);
+ ERR_FAIL_NULL(colorptr);
uint8_t pixel[4];
for (uint32_t i = 0; i < pixel_size; i++) {
pixel[i] = CLAMP((*colorptr)[i] * 255, 0, 255);
@@ -2634,23 +2646,23 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
switch (p_mode) {
case COMPRESS_S3TC: {
- ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_bc_func, ERR_UNAVAILABLE);
_image_compress_bc_func(this, p_channels);
} break;
case COMPRESS_ETC: {
- ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_etc1_func, ERR_UNAVAILABLE);
_image_compress_etc1_func(this);
} break;
case COMPRESS_ETC2: {
- ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_etc2_func, ERR_UNAVAILABLE);
_image_compress_etc2_func(this, p_channels);
} break;
case COMPRESS_BPTC: {
- ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_bptc_func, ERR_UNAVAILABLE);
_image_compress_bptc_func(this, p_channels);
} break;
case COMPRESS_ASTC: {
- ERR_FAIL_COND_V(!_image_compress_astc_func, ERR_UNAVAILABLE);
+ ERR_FAIL_NULL_V(_image_compress_astc_func, ERR_UNAVAILABLE);
_image_compress_astc_func(this, p_astc_format);
} break;
case COMPRESS_MAX: {
@@ -3004,6 +3016,8 @@ ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
+ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
@@ -3398,6 +3412,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("convert", "format"), &Image::convert);
+ ClassDB::bind_method(D_METHOD("get_mipmap_count"), &Image::get_mipmap_count);
ClassDB::bind_method(D_METHOD("get_mipmap_offset", "mipmap"), &Image::get_mipmap_offset);
ClassDB::bind_method(D_METHOD("resize_to_po2", "square", "interpolation"), &Image::resize_to_po2, DEFVAL(false), DEFVAL(INTERPOLATE_BILINEAR));
@@ -3475,6 +3490,10 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_webp_from_buffer", "buffer"), &Image::load_webp_from_buffer);
ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
+ ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer);
+
+ ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data");
@@ -3637,7 +3656,7 @@ void Image::bump_map_to_normal_map(float bump_scale) {
const uint8_t *rp = data.ptr();
uint8_t *wp = result_image.ptrw();
- ERR_FAIL_COND(!rp);
+ ERR_FAIL_NULL(rp);
unsigned char *write_ptr = wp;
float *read_ptr = (float *)rp;
@@ -3754,7 +3773,7 @@ void Image::fix_alpha_edges() {
}
int closest_dist = max_dist;
- uint8_t closest_color[3];
+ uint8_t closest_color[3] = { 0 };
int from_x = MAX(0, j - max_radius);
int to_x = MIN(width - 1, j + max_radius);
@@ -3825,6 +3844,36 @@ Error Image::load_bmp_from_buffer(const Vector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _bmp_mem_loader_func);
}
+Error Image::load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale) {
+ ERR_FAIL_NULL_V_MSG(
+ _svg_scalable_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The SVG module isn't enabled. Recompile the Godot editor or export template binary with the `module_svg_enabled=yes` SCons option.");
+
+ int buffer_size = p_array.size();
+
+ ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
+
+ Ref<Image> image = _svg_scalable_mem_loader_func(p_array.ptr(), buffer_size, scale);
+ ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR);
+
+ copy_internals_from(image);
+
+ return OK;
+}
+
+Error Image::load_svg_from_string(const String &p_svg_str, float scale) {
+ return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale);
+}
+
+Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) {
+ ERR_FAIL_NULL_V_MSG(
+ _ktx_mem_loader_func,
+ ERR_UNAVAILABLE,
+ "The KTX module isn't enabled. Recompile the Godot editor or export template binary with the `module_ktx_enabled=yes` SCons option.");
+ return _load_from_buffer(p_array, _ktx_mem_loader_func);
+}
+
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
ERR_FAIL_COND(!data.size());
@@ -3868,7 +3917,7 @@ Error Image::_load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc
int buffer_size = p_array.size();
ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!p_loader, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_loader, ERR_INVALID_PARAMETER);
const uint8_t *r = p_array.ptr();