summaryrefslogtreecommitdiff
path: root/core/io/image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/io/image.cpp')
-rw-r--r--core/io/image.cpp154
1 files changed, 142 insertions, 12 deletions
diff --git a/core/io/image.cpp b/core/io/image.cpp
index a945d3e6cd..0f20aabd7e 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -85,6 +85,7 @@ SaveJPGFunc Image::save_jpg_func = nullptr;
SaveEXRFunc Image::save_exr_func = nullptr;
SavePNGBufferFunc Image::save_png_buffer_func = nullptr;
+SaveEXRBufferFunc Image::save_exr_buffer_func = nullptr;
SaveJPGBufferFunc Image::save_jpg_buffer_func = nullptr;
SaveWebPFunc Image::save_webp_func = nullptr;
@@ -1338,6 +1339,108 @@ void Image::crop(int p_width, int p_height) {
crop_from_point(0, 0, p_width, p_height);
}
+void Image::rotate_90(ClockDirection p_direction) {
+ ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
+ ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
+ ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
+
+ int saved_width = height;
+ int saved_height = width;
+
+ if (width != height) {
+ int n = MAX(width, height);
+ resize(n, n, INTERPOLATE_NEAREST);
+ }
+
+ bool used_mipmaps = has_mipmaps();
+ if (used_mipmaps) {
+ clear_mipmaps();
+ }
+
+ {
+ uint8_t *w = data.ptrw();
+ uint8_t src[16];
+ uint8_t dst[16];
+ uint32_t pixel_size = get_format_pixel_size(format);
+
+ // Flip.
+
+ if (p_direction == CLOCKWISE) {
+ for (int y = 0; y < height / 2; y++) {
+ for (int x = 0; x < width; x++) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(x, height - y - 1, pixel_size, w, dst);
+
+ _put_pixelb(x, height - y - 1, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ } else {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width / 2; x++) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(width - x - 1, y, pixel_size, w, dst);
+
+ _put_pixelb(width - x - 1, y, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ }
+
+ // Transpose.
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (x < y) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(y, x, pixel_size, w, dst);
+
+ _put_pixelb(y, x, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ }
+ }
+
+ if (saved_width != saved_height) {
+ resize(saved_width, saved_height, INTERPOLATE_NEAREST);
+ } else if (used_mipmaps) {
+ generate_mipmaps();
+ }
+}
+
+void Image::rotate_180() {
+ ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
+ ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
+ ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
+
+ bool used_mipmaps = has_mipmaps();
+ if (used_mipmaps) {
+ clear_mipmaps();
+ }
+
+ {
+ uint8_t *w = data.ptrw();
+ uint8_t src[16];
+ uint8_t dst[16];
+ uint32_t pixel_size = get_format_pixel_size(format);
+
+ for (int y = 0; y < height / 2; y++) {
+ for (int x = 0; x < width; x++) {
+ _get_pixelb(x, y, pixel_size, w, src);
+ _get_pixelb(width - x - 1, height - y - 1, pixel_size, w, dst);
+
+ _put_pixelb(width - x - 1, height - y - 1, pixel_size, w, src);
+ _put_pixelb(x, y, pixel_size, w, dst);
+ }
+ }
+ }
+
+ if (used_mipmaps) {
+ generate_mipmaps();
+ }
+}
+
void Image::flip_y() {
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_y in compressed or custom image formats.");
@@ -2283,6 +2386,21 @@ Error Image::load(const String &p_path) {
return ImageLoader::load_image(p_path, this);
}
+Ref<Image> Image::load_from_file(const String &p_path) {
+#ifdef DEBUG_ENABLED
+ if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) {
+ WARN_PRINT("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource.");
+ }
+#endif
+ Ref<Image> image;
+ image.instantiate();
+ Error err = ImageLoader::load_image(p_path, image);
+ if (err != OK) {
+ ERR_FAIL_V_MSG(Ref<Image>(), vformat("Failed to load image. Error %d", err));
+ }
+ return image;
+}
+
Error Image::save_png(const String &p_path) const {
if (save_png_func == nullptr) {
return ERR_UNAVAILABLE;
@@ -2323,6 +2441,13 @@ Error Image::save_exr(const String &p_path, bool p_grayscale) const {
return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale);
}
+Vector<uint8_t> Image::save_exr_to_buffer(bool p_grayscale) const {
+ if (save_exr_buffer_func == nullptr) {
+ return Vector<uint8_t>();
+ }
+ return save_exr_buffer_func(Ref<Image>((Image *)this), p_grayscale);
+}
+
Error Image::save_webp(const String &p_path, const bool p_lossy, const float p_quality) const {
if (save_webp_func == nullptr) {
return ERR_UNAVAILABLE;
@@ -2455,15 +2580,15 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const V
create(p_width, p_height, p_mipmaps, p_format, p_data);
}
-Rect2 Image::get_used_rect() const {
+Rect2i Image::get_used_rect() const {
if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGB565) {
- return Rect2(Point2(), Size2(width, height));
+ return Rect2i(0, 0, width, height);
}
int len = data.size();
if (len == 0) {
- return Rect2();
+ return Rect2i();
}
int minx = 0xFFFFFF, miny = 0xFFFFFFF;
@@ -2489,15 +2614,15 @@ Rect2 Image::get_used_rect() const {
}
if (maxx == -1) {
- return Rect2();
+ return Rect2i();
} else {
- return Rect2(minx, miny, maxx - minx + 1, maxy - miny + 1);
+ return Rect2i(minx, miny, maxx - minx + 1, maxy - miny + 1);
}
}
-Ref<Image> Image::get_rect(const Rect2 &p_area) const {
+Ref<Image> Image::get_rect(const Rect2i &p_area) const {
Ref<Image> img = memnew(Image(p_area.size.x, p_area.size.y, mipmaps, format));
- img->blit_rect(Ref<Image>((Image *)this), p_area, Point2(0, 0));
+ img->blit_rect(Ref<Image>((Image *)this), p_area, Point2i(0, 0));
return img;
}
@@ -2534,7 +2659,7 @@ void Image::_get_clipped_src_and_dest_rects(const Ref<Image> &p_src, const Rect2
r_clipped_dest_rect.size.y = r_clipped_src_rect.size.y;
}
-void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) {
+void Image::blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest) {
ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
int dsize = data.size();
int srcdsize = p_src->data.size();
@@ -2576,7 +2701,7 @@ 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) {
+void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest) {
ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object.");
int dsize = data.size();
@@ -2626,7 +2751,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
}
}
-void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) {
+void Image::blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest) {
ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
int dsize = data.size();
int srcdsize = p_src->data.size();
@@ -2661,7 +2786,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P
}
}
-void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) {
+void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest) {
ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object.");
int dsize = data.size();
@@ -2733,7 +2858,7 @@ void Image::fill(const Color &p_color) {
_repeat_pixel_over_subsequent_memory(dst_data_ptr, pixel_size, width * height);
}
-void Image::fill_rect(const Rect2 &p_rect, const Color &p_color) {
+void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) {
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill rect in compressed or custom image formats.");
Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect.abs());
@@ -3175,11 +3300,13 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_empty"), &Image::is_empty);
ClassDB::bind_method(D_METHOD("load", "path"), &Image::load);
+ ClassDB::bind_static_method("Image", D_METHOD("load_from_file", "path"), &Image::load_from_file);
ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png);
ClassDB::bind_method(D_METHOD("save_png_to_buffer"), &Image::save_png_to_buffer);
ClassDB::bind_method(D_METHOD("save_jpg", "path", "quality"), &Image::save_jpg, DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("save_jpg_to_buffer", "quality"), &Image::save_jpg_to_buffer, DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("save_exr_to_buffer", "grayscale"), &Image::save_exr_to_buffer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("save_webp", "path", "lossy", "quality"), &Image::save_webp, DEFVAL(false), DEFVAL(0.75f));
ClassDB::bind_method(D_METHOD("save_webp_to_buffer", "lossy", "quality"), &Image::save_webp_to_buffer, DEFVAL(false), DEFVAL(0.75f));
@@ -3192,6 +3319,9 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress);
ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed);
+ ClassDB::bind_method(D_METHOD("rotate_90", "direction"), &Image::rotate_90);
+ ClassDB::bind_method(D_METHOD("rotate_180"), &Image::rotate_180);
+
ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges);
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);