diff --git a/core/io/image.cpp b/core/io/image.cpp index 9bb987b670c534..772240004965b7 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3929,22 +3929,76 @@ void Image::renormalize_rgbe9995(uint32_t *p_rgb) { // Never used } +Image::FileFormat Image::detect_file_format(const uint8_t *p_data, int p_len) { + size_t len = p_len < 0 ? 0 : unsigned(p_len); + + static const uint8_t png_file_header[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; // \211PNG\r\n\032\n + static const size_t png_file_header_size = sizeof(png_file_header); + if (len > png_file_header_size && memcmp(p_data, png_file_header, png_file_header_size) == 0) { + return FILE_FORMAT_PNG; + } + + static const size_t riff_four_cc_size = 4; + static const uint8_t riff_file_header[] = { 0x52, 0x49, 0x46, 0x46 }; // RIFF + static const uint8_t webp_content_header[] = { 0x57, 0x45, 0x42, 0x50 }; // WEBP + if (len > riff_four_cc_size * 3 && // RIFF SIZE WEBP + memcmp(p_data, riff_file_header, riff_four_cc_size) == 0 && // RIFF header. + memcmp(p_data + riff_four_cc_size * 2, webp_content_header, riff_four_cc_size) == 0) { // WEBP chunk header. + return FILE_FORMAT_RIFF_WEBP; + } + + static const uint8_t jpeg_soi_marker[] = { 0xFF, 0xD8 }; + static const uint8_t jpeg_application0_marker[] = { 0xFF, 0xE0 }; + static const uint8_t jpeg_jfif_data_identifier[] = { 0x4A, 0x46, 0x49, 0x46, 0x00 }; // JFIF\0 + static const uint8_t jpeg_application1_marker[] = { 0xFF, 0xE1 }; + static const uint8_t jpeg_exif_data_identifier[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; // Exif\0\0 + + static const size_t jpeg_marker_size = 2; + static const size_t jpeg_data_length_size = sizeof(uint16_t); + static const size_t jpeg_data_identifier_offset = jpeg_marker_size * 2 + jpeg_data_length_size; + static const size_t jpeg_jfif_data_identifier_size = sizeof(jpeg_jfif_data_identifier); + static const size_t jpeg_exif_data_identifier_size = sizeof(jpeg_exif_data_identifier); + + if (len > 12 && memcmp(p_data, jpeg_soi_marker, jpeg_marker_size) == 0) { + if (memcmp(p_data + jpeg_marker_size, jpeg_application0_marker, jpeg_marker_size) == 0 && + memcmp(p_data + jpeg_data_identifier_offset, + jpeg_jfif_data_identifier, jpeg_jfif_data_identifier_size) == 0) { + return FILE_FORMAT_JPEG_JFIF; + } else if (memcmp(p_data + jpeg_marker_size, jpeg_application1_marker, jpeg_marker_size) == 0 && + memcmp(p_data + jpeg_data_identifier_offset, + jpeg_exif_data_identifier, jpeg_exif_data_identifier_size) == 0) { + return FILE_FORMAT_JPEG_EXIF; + } + } + + return FILE_FORMAT_UNKNOWN; +} + Image::Image(const uint8_t *p_mem_png_jpg, int p_len) { width = 0; height = 0; mipmaps = false; format = FORMAT_L8; - if (_png_mem_loader_func) { - copy_internals_from(_png_mem_loader_func(p_mem_png_jpg, p_len)); - } - - if (is_empty() && _jpg_mem_loader_func) { - copy_internals_from(_jpg_mem_loader_func(p_mem_png_jpg, p_len)); - } - - if (is_empty() && _webp_mem_loader_func) { - copy_internals_from(_webp_mem_loader_func(p_mem_png_jpg, p_len)); + switch (detect_file_format(p_mem_png_jpg, p_len)) { + case FILE_FORMAT_PNG: + if (_png_mem_loader_func) { + copy_internals_from(_png_mem_loader_func(p_mem_png_jpg, p_len)); + } + break; + case FILE_FORMAT_JPEG_JFIF: + case FILE_FORMAT_JPEG_EXIF: + if (_jpg_mem_loader_func) { + copy_internals_from(_jpg_mem_loader_func(p_mem_png_jpg, p_len)); + } + break; + case FILE_FORMAT_RIFF_WEBP: + if (_webp_mem_loader_func) { + copy_internals_from(_webp_mem_loader_func(p_mem_png_jpg, p_len)); + } + break; + default: // FILE_FORMAT_UNKNOWN + break; } } diff --git a/core/io/image.h b/core/io/image.h index 8e353a8bb76485..0f7110e857dc99 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -143,6 +143,14 @@ class Image : public Resource { ASTC_FORMAT_8x8, }; + enum FileFormat { + FILE_FORMAT_UNKNOWN, + FILE_FORMAT_PNG, + FILE_FORMAT_JPEG_JFIF, + FILE_FORMAT_JPEG_EXIF, + FILE_FORMAT_RIFF_WEBP, + }; + static ImageMemLoadFunc _png_mem_loader_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; @@ -217,6 +225,8 @@ class Image : public Resource { static void renormalize_half(uint16_t *p_rgb); static void renormalize_rgbe9995(uint32_t *p_rgb); + static FileFormat detect_file_format(const uint8_t *p_mem_png_jpg, int p_len); + public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 7ad1b7697ed054..52d51776a70ea1 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -373,8 +373,8 @@ void PortableCompressedTexture2D::_set_data(const Vector &p_data) { data_size -= 4; // Skip possible Godot png tag "PNG " before pure PNG data. - const uint8_t godot_png_prefix[] = { 0x50, 0x4E, 0x47, 0x20 }; - const size_t godot_png_prefix_size = sizeof(godot_png_prefix); + static const uint8_t godot_png_prefix[] = { 0x50, 0x4E, 0x47, 0x20 }; + static const size_t godot_png_prefix_size = sizeof(godot_png_prefix); if (memcmp(data, godot_png_prefix, godot_png_prefix_size) == 0) { data += godot_png_prefix_size; data_size -= godot_png_prefix_size;