diff options
Diffstat (limited to 'core/image.cpp')
-rw-r--r-- | core/image.cpp | 301 |
1 files changed, 246 insertions, 55 deletions
diff --git a/core/image.cpp b/core/image.cpp index 2640c6be2a..6ab8bb6d46 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -81,21 +81,21 @@ const char *Image::format_names[Image::FORMAT_MAX] = { SavePNGFunc Image::save_png_func = NULL; -void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_dst, const uint8_t *p_src) { +void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) { uint32_t ofs = (p_y * width + p_x) * p_pixelsize; for (uint32_t i = 0; i < p_pixelsize; i++) { - p_dst[ofs + i] = p_src[i]; + p_data[ofs + i] = p_pixel[i]; } } -void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixelsize, const uint8_t *p_src, uint8_t *p_dst) { +void Image::_get_pixelb(int p_x, int p_y, uint32_t p_pixelsize, const uint8_t *p_data, uint8_t *p_pixel) { uint32_t ofs = (p_y * width + p_x) * p_pixelsize; for (uint32_t i = 0; i < p_pixelsize; i++) { - p_dst[i] = p_src[ofs + i]; + p_pixel[i] = p_data[ofs + i]; } } @@ -423,7 +423,7 @@ void Image::convert(Format p_new_format) { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { - new_img.put_pixel(i, j, get_pixel(i, j)); + new_img.set_pixel(i, j, get_pixel(i, j)); } } @@ -818,7 +818,7 @@ void Image::flip_y() { uint8_t down[16]; uint32_t pixel_size = get_format_pixel_size(format); - for (int y = 0; y < height; y++) { + for (int y = 0; y < height / 2; y++) { for (int x = 0; x < width; x++) { @@ -854,7 +854,7 @@ void Image::flip_x() { for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { + for (int x = 0; x < width / 2; x++) { _get_pixelb(x, y, pixel_size, w.ptr(), up); _get_pixelb(width - x - 1, y, pixel_size, w.ptr(), down); @@ -1325,19 +1325,19 @@ void Image::create(const char **p_xpm) { line++; } } -#define DETECT_ALPHA_MAX_TRESHOLD 254 -#define DETECT_ALPHA_MIN_TRESHOLD 2 - -#define DETECT_ALPHA(m_value) \ - { \ - uint8_t value = m_value; \ - if (value < DETECT_ALPHA_MIN_TRESHOLD) \ - bit = true; \ - else if (value < DETECT_ALPHA_MAX_TRESHOLD) { \ - \ - detected = true; \ - break; \ - } \ +#define DETECT_ALPHA_MAX_THRESHOLD 254 +#define DETECT_ALPHA_MIN_THRESHOLD 2 + +#define DETECT_ALPHA(m_value) \ + { \ + uint8_t value = m_value; \ + if (value < DETECT_ALPHA_MIN_THRESHOLD) \ + bit = true; \ + else if (value < DETECT_ALPHA_MAX_THRESHOLD) { \ + \ + detected = true; \ + break; \ + } \ } #define DETECT_NON_ALPHA(m_value) \ @@ -1457,7 +1457,7 @@ Error Image::save_png(const String &p_path) const { if (save_png_func == NULL) return ERR_UNAVAILABLE; - return save_png_func(p_path, Ref<Image>(this)); + return save_png_func(p_path, Ref<Image>((Image *)this)); } int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { @@ -1483,23 +1483,23 @@ Error Image::decompress() { _image_decompress_bc(this); else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc) _image_decompress_pvrtc(this); - else if (format == FORMAT_ETC && _image_decompress_etc) - _image_decompress_etc(this); - else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc) + else if (format == FORMAT_ETC && _image_decompress_etc1) + _image_decompress_etc1(this); + else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc1) _image_decompress_etc2(this); else return ERR_UNAVAILABLE; return OK; } -Error Image::compress(CompressMode p_mode, bool p_for_srgb) { +Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) { switch (p_mode) { case COMPRESS_S3TC: { ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); - _image_compress_bc_func(this, p_for_srgb); + _image_compress_bc_func(this, p_source); } break; case COMPRESS_PVRTC2: { @@ -1513,13 +1513,13 @@ Error Image::compress(CompressMode p_mode, bool p_for_srgb) { } break; case COMPRESS_ETC: { - ERR_FAIL_COND_V(!_image_compress_etc_func, ERR_UNAVAILABLE); - _image_compress_etc_func(this); + ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE); + _image_compress_etc1_func(this, p_lossy_quality); } break; case COMPRESS_ETC2: { - ERR_FAIL_COND_V(!_image_compress_etc_func, ERR_UNAVAILABLE); - _image_compress_etc_func(this); + ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); + _image_compress_etc2_func(this, p_lossy_quality, p_source); } break; } @@ -1599,7 +1599,7 @@ Rect2 Image::get_used_rect() const { Ref<Image> Image::get_rect(const Rect2 &p_area) const { Ref<Image> img = memnew(Image(p_area.size.x, p_area.size.y, mipmaps, format)); - img->blit_rect(Ref<Image>(this), p_area, Point2(0, 0)); + img->blit_rect(Ref<Image>((Image *)this), p_area, Point2(0, 0)); return img; } @@ -1612,11 +1612,11 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po ERR_FAIL_COND(srcdsize == 0); ERR_FAIL_COND(format != p_src->format); - Rect2i local_src_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest + p_src_rect.pos, p_src_rect.size)); - - if (local_src_rect.size.x <= 0 || local_src_rect.size.y <= 0) + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; - Rect2i src_rect(p_src_rect.pos + (local_src_rect.pos - p_dest), local_src_rect.size); + + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); PoolVector<uint8_t>::Write wp = data.write(); uint8_t *dst_data_ptr = wp.ptr(); @@ -1626,15 +1626,15 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po int pixel_size = get_format_pixel_size(format); - for (int i = 0; i < src_rect.size.y; i++) { + for (int i = 0; i < dest_rect.size.y; i++) { - for (int j = 0; j < src_rect.size.x; j++) { + for (int j = 0; j < dest_rect.size.x; j++) { - int src_x = src_rect.pos.x + j; - int src_y = src_rect.pos.y + i; + int src_x = clipped_src_rect.position.x + j; + int src_y = clipped_src_rect.position.y + i; - int dst_x = local_src_rect.pos.x + j; - int dst_y = local_src_rect.pos.y + i; + int dst_x = dest_rect.position.x + j; + int dst_y = dest_rect.position.y + i; const uint8_t *src = &src_data_ptr[(src_y * p_src->width + src_x) * pixel_size]; uint8_t *dst = &dst_data_ptr[(dst_y * width + dst_x) * pixel_size]; @@ -1646,17 +1646,200 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po } } +void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { + + ERR_FAIL_COND(p_src.is_null()); + ERR_FAIL_COND(p_mask.is_null()); + int dsize = data.size(); + int srcdsize = p_src->data.size(); + int maskdsize = p_mask->data.size(); + ERR_FAIL_COND(dsize == 0); + ERR_FAIL_COND(srcdsize == 0); + ERR_FAIL_COND(maskdsize == 0); + ERR_FAIL_COND(p_src->width != p_mask->width); + ERR_FAIL_COND(p_src->height != p_mask->height); + ERR_FAIL_COND(format != p_src->format); + + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + return; + + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + + PoolVector<uint8_t>::Write wp = data.write(); + uint8_t *dst_data_ptr = wp.ptr(); + + PoolVector<uint8_t>::Read rp = p_src->data.read(); + const uint8_t *src_data_ptr = rp.ptr(); + + int pixel_size = get_format_pixel_size(format); + + Ref<Image> msk = p_mask; + msk->lock(); + + for (int i = 0; i < dest_rect.size.y; i++) { + + for (int j = 0; j < dest_rect.size.x; j++) { + + int src_x = clipped_src_rect.position.x + j; + int src_y = clipped_src_rect.position.y + i; + + if (msk->get_pixel(src_x, src_y).a != 0) { + + int dst_x = dest_rect.position.x + j; + int dst_y = dest_rect.position.y + i; + + const uint8_t *src = &src_data_ptr[(src_y * p_src->width + src_x) * pixel_size]; + uint8_t *dst = &dst_data_ptr[(dst_y * width + dst_x) * pixel_size]; + + for (int k = 0; k < pixel_size; k++) { + dst[k] = src[k]; + } + } + } + } + + msk->unlock(); +} + +void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) { + + ERR_FAIL_COND(p_src.is_null()); + int dsize = data.size(); + int srcdsize = p_src->data.size(); + ERR_FAIL_COND(dsize == 0); + ERR_FAIL_COND(srcdsize == 0); + ERR_FAIL_COND(format != p_src->format); + + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + return; + + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + + lock(); + Ref<Image> img = p_src; + img->lock(); + + for (int i = 0; i < dest_rect.size.y; i++) { + + for (int j = 0; j < dest_rect.size.x; j++) { + + int src_x = clipped_src_rect.position.x + j; + int src_y = clipped_src_rect.position.y + i; + + int dst_x = dest_rect.position.x + j; + int dst_y = dest_rect.position.y + i; + + Color sc = img->get_pixel(src_x, src_y); + Color dc = get_pixel(dst_x, dst_y); + dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); + dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); + dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); + dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); + set_pixel(dst_x, dst_y, dc); + } + } + + img->unlock(); + unlock(); +} + +void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) { + + ERR_FAIL_COND(p_src.is_null()); + ERR_FAIL_COND(p_mask.is_null()); + int dsize = data.size(); + int srcdsize = p_src->data.size(); + int maskdsize = p_mask->data.size(); + ERR_FAIL_COND(dsize == 0); + ERR_FAIL_COND(srcdsize == 0); + ERR_FAIL_COND(maskdsize == 0); + ERR_FAIL_COND(p_src->width != p_mask->width); + ERR_FAIL_COND(p_src->height != p_mask->height); + ERR_FAIL_COND(format != p_src->format); + + Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect); + if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) + return; + + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + + lock(); + Ref<Image> img = p_src; + Ref<Image> msk = p_mask; + img->lock(); + msk->lock(); + + for (int i = 0; i < dest_rect.size.y; i++) { + + for (int j = 0; j < dest_rect.size.x; j++) { + + int src_x = clipped_src_rect.position.x + j; + int src_y = clipped_src_rect.position.y + i; + + // If the mask's pixel is transparent then we skip it + //Color c = msk->get_pixel(src_x, src_y); + //if (c.a == 0) continue; + if (msk->get_pixel(src_x, src_y).a != 0) { + + int dst_x = dest_rect.position.x + j; + int dst_y = dest_rect.position.y + i; + + Color sc = img->get_pixel(src_x, src_y); + Color dc = get_pixel(dst_x, dst_y); + dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); + dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); + dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); + dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); + set_pixel(dst_x, dst_y, dc); + } + } + } + + msk->unlock(); + img->unlock(); + unlock(); +} + +void Image::fill(const Color &c) { + + lock(); + + PoolVector<uint8_t>::Write wp = data.write(); + uint8_t *dst_data_ptr = wp.ptr(); + + int pixel_size = get_format_pixel_size(format); + + // put first pixel with the format-aware API + set_pixel(0, 0, c); + + for (int y = 0; y < height; y++) { + + for (int x = 0; x < width; x++) { + + uint8_t *dst = &dst_data_ptr[(y * width + x) * pixel_size]; + + for (int k = 0; k < pixel_size; k++) { + dst[k] = dst_data_ptr[k]; + } + } + } + + unlock(); +} + Ref<Image> (*Image::_png_mem_loader_func)(const uint8_t *, int) = NULL; Ref<Image> (*Image::_jpg_mem_loader_func)(const uint8_t *, int) = NULL; -void (*Image::_image_compress_bc_func)(Image *, bool) = NULL; +void (*Image::_image_compress_bc_func)(Image *, Image::CompressSource) = NULL; void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL; void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL; -void (*Image::_image_compress_etc_func)(Image *) = NULL; -void (*Image::_image_compress_etc2_func)(Image *) = NULL; +void (*Image::_image_compress_etc1_func)(Image *, float) = NULL; +void (*Image::_image_compress_etc2_func)(Image *, float, Image::CompressSource) = NULL; void (*Image::_image_decompress_pvrtc)(Image *) = NULL; void (*Image::_image_decompress_bc)(Image *) = NULL; -void (*Image::_image_decompress_etc)(Image *) = NULL; +void (*Image::_image_decompress_etc1)(Image *) = NULL; void (*Image::_image_decompress_etc2)(Image *) = NULL; PoolVector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = NULL; @@ -1712,7 +1895,7 @@ void Image::unlock() { write_lock = PoolVector<uint8_t>::Write(); } -Color Image::get_pixel(int p_x, int p_y) { +Color Image::get_pixel(int p_x, int p_y) const { uint8_t *ptr = write_lock.ptr(); #ifdef DEBUG_ENABLED @@ -1858,12 +2041,12 @@ Color Image::get_pixel(int p_x, int p_y) { return Color(); } -void Image::put_pixel(int p_x, int p_y, const Color &p_color) { +void Image::set_pixel(int p_x, int p_y, const Color &p_color) { uint8_t *ptr = write_lock.ptr(); #ifdef DEBUG_ENABLED if (!ptr) { - ERR_EXPLAIN("Image must be locked with 'lock()' before using put_pixel()"); + ERR_EXPLAIN("Image must be locked with 'lock()' before using set_pixel()"); ERR_FAIL_COND(!ptr); } @@ -1977,7 +2160,7 @@ void Image::put_pixel(int p_x, int p_y, const Color &p_color) { } break; default: { - ERR_EXPLAIN("Can't put_pixel() on compressed image, sorry."); + ERR_EXPLAIN("Can't set_pixel() on compressed image, sorry."); ERR_FAIL(); } } @@ -2040,7 +2223,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mipmap_offset", "mipmap"), &Image::get_mipmap_offset); - ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL("false")); + ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL(false)); ClassDB::bind_method(D_METHOD("resize", "width", "height", "interpolation"), &Image::resize, DEFVAL(INTERPOLATE_BILINEAR)); ClassDB::bind_method(D_METHOD("shrink_x2"), &Image::shrink_x2); ClassDB::bind_method(D_METHOD("expand_x2_hq2x"), &Image::expand_x2_hq2x); @@ -2072,6 +2255,10 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy); ClassDB::bind_method(D_METHOD("blit_rect", "src:Image", "src_rect", "dst"), &Image::blit_rect); + ClassDB::bind_method(D_METHOD("blit_rect_mask", "src:Image", "mask:Image", "src_rect", "dst"), &Image::blit_rect_mask); + ClassDB::bind_method(D_METHOD("blend_rect", "src:Image", "src_rect", "dst"), &Image::blend_rect); + ClassDB::bind_method(D_METHOD("blend_rect_mask", "src:Image", "mask:Image", "src_rect", "dst"), &Image::blend_rect_mask); + ClassDB::bind_method(D_METHOD("fill", "color"), &Image::fill); ClassDB::bind_method(D_METHOD("get_used_rect"), &Image::get_used_rect); ClassDB::bind_method(D_METHOD("get_rect:Image", "rect"), &Image::get_rect); @@ -2083,7 +2270,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("lock"), &Image::lock); ClassDB::bind_method(D_METHOD("unlock"), &Image::unlock); - ClassDB::bind_method(D_METHOD("put_pixel", "x", "y", "color"), &Image::put_pixel); + ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel); ClassDB::bind_method(D_METHOD("get_pixel", "x", "y"), &Image::get_pixel); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data"); @@ -2140,9 +2327,13 @@ void Image::_bind_methods() { BIND_CONSTANT(COMPRESS_PVRTC4); BIND_CONSTANT(COMPRESS_ETC); BIND_CONSTANT(COMPRESS_ETC2); + + BIND_CONSTANT(COMPRESS_SOURCE_GENERIC); + BIND_CONSTANT(COMPRESS_SOURCE_SRGB); + BIND_CONSTANT(COMPRESS_SOURCE_NORMAL); } -void Image::set_compress_bc_func(void (*p_compress_func)(Image *, bool)) { +void Image::set_compress_bc_func(void (*p_compress_func)(Image *, CompressSource)) { _image_compress_bc_func = p_compress_func; } @@ -2243,7 +2434,7 @@ void Image::fix_alpha_edges() { unsigned char *data_ptr = wp.ptr(); const int max_radius = 4; - const int alpha_treshold = 20; + const int alpha_threshold = 20; const int max_dist = 0x7FFFFFFF; for (int i = 0; i < height; i++) { @@ -2252,7 +2443,7 @@ void Image::fix_alpha_edges() { const uint8_t *rptr = &srcptr[(i * width + j) * 4]; uint8_t *wptr = &data_ptr[(i * width + j) * 4]; - if (rptr[3] >= alpha_treshold) + if (rptr[3] >= alpha_threshold) continue; int closest_dist = max_dist; @@ -2274,7 +2465,7 @@ void Image::fix_alpha_edges() { const uint8_t *rp = &srcptr[(k * width + l) << 2]; - if (rp[3] < alpha_treshold) + if (rp[3] < alpha_threshold) continue; closest_color[0] = rp[0]; |