Commit f8964190 by Juan Linietsky

-Added EXR supprot for HDR (no BC6 compression yet though)

-Improvements to texture importer -Proper detection of S3TC compression modes, and added all modes to Image -Fixes to non-power of 2 compressed textures, which should all be supported by GLES3
parent 41918f32
......@@ -66,7 +66,6 @@ public:
FORMAT_RG8,
FORMAT_RGB8,
FORMAT_RGBA8,
FORMAT_RGB565, //16 bit
FORMAT_RGBA4444,
FORMAT_RGBA5551,
FORMAT_RF, //float
......@@ -77,14 +76,17 @@ public:
FORMAT_RGH,
FORMAT_RGBH,
FORMAT_RGBAH,
FORMAT_RGBE9995,
FORMAT_DXT1, //s3tc bc1
FORMAT_DXT3, //bc2
FORMAT_DXT5, //bc3
FORMAT_ATI1, //bc4
FORMAT_ATI2, //bc5
FORMAT_BPTC_RGBA, //btpc bc6h
FORMAT_BPTC_RGBF, //float /
FORMAT_BPTC_RGBFU, //unsigned float
FORMAT_LATC_L,
FORMAT_LATC_LA,
FORMAT_RGTC_R,
FORMAT_RGTC_RG,
FORMAT_BPTC_RGBA, //btpc bc7
FORMAT_BPTC_RGBF, //float bc6h
FORMAT_BPTC_RGBFU, //unsigned float bc6hu
FORMAT_PVRTC2, //pvrtc
FORMAT_PVRTC2A,
FORMAT_PVRTC4,
......@@ -125,13 +127,13 @@ public:
static void (*_image_decompress_etc)(Image *);
static void (*_image_decompress_etc2)(Image *);
Error _decompress_bc();
static PoolVector<uint8_t> (*lossy_packer)(const Ref<Image> &p_image, float p_quality);
static Ref<Image> (*lossy_unpacker)(const PoolVector<uint8_t> &p_buffer);
static PoolVector<uint8_t> (*lossless_packer)(const Ref<Image> &p_image);
static Ref<Image> (*lossless_unpacker)(const PoolVector<uint8_t> &p_buffer);
PoolVector<uint8_t>::Write write_lock;
protected:
static void _bind_methods();
......@@ -253,18 +255,18 @@ public:
static int get_format_pixel_size(Format p_format);
static int get_format_pixel_rshift(Format p_format);
static int get_format_block_size(Format p_format);
static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h);
static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0);
static int get_image_required_mipmaps(int p_width, int p_height, Format p_format);
enum CompressMode {
COMPRESS_16BIT,
COMPRESS_S3TC,
COMPRESS_PVRTC2,
COMPRESS_PVRTC4,
COMPRESS_ETC,
COMPRESS_ETC2
COMPRESS_ETC2,
};
Error compress(CompressMode p_mode = COMPRESS_S3TC);
......@@ -289,6 +291,24 @@ public:
virtual Ref<Resource> duplicate(bool p_subresources = false) const;
void lock();
void unlock();
//this is used for compression
enum DetectChannels {
DETECTED_L,
DETECTED_LA,
DETECTED_R,
DETECTED_RG,
DETECTED_RGB,
DETECTED_RGBA,
};
DetectChannels get_detected_channels();
Color get_pixel(int p_x, int p_y);
void put_pixel(int p_x, int p_y, const Color &p_color);
void copy_internals_from(const Ref<Image> &p_image) {
ERR_FAIL_COND(p_image.is_null());
format = p_image->format;
......
......@@ -278,6 +278,10 @@ public:
return u.f32;
}
static _ALWAYS_INLINE_ float half_to_float(const uint16_t h) {
return halfptr_to_float(&h);
}
static _ALWAYS_INLINE_ uint16_t make_half_float(float f) {
union {
......
......@@ -164,18 +164,6 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_
srgb = true;
} break;
case Image::FORMAT_RGB565: {
#ifndef GLES_OVER_GL
r_gl_internal_format = GL_RGB565;
#else
//#warning TODO: Convert tod 555 if 565 is not supported (GLES3.3-)
r_gl_internal_format = GL_RGB5;
#endif
//r_gl_internal_format=GL_RGB565;
r_gl_format = GL_RGB;
r_gl_type = GL_UNSIGNED_SHORT_5_6_5;
} break;
case Image::FORMAT_RGBA4444: {
r_gl_internal_format = GL_RGBA4;
......@@ -241,6 +229,12 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_
r_gl_type = GL_HALF_FLOAT;
} break;
case Image::FORMAT_RGBE9995: {
r_gl_internal_format = GL_RGB9_E5;
r_gl_format = GL_RGB;
r_gl_type = GL_UNSIGNED_INT_5_9_9_9_REV;
} break;
case Image::FORMAT_DXT1: {
if (config.s3tc_supported) {
......@@ -289,7 +283,7 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_
}
} break;
case Image::FORMAT_ATI1: {
case Image::FORMAT_LATC_L: {
if (config.latc_supported) {
......@@ -305,7 +299,7 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_
}
} break;
case Image::FORMAT_ATI2: {
case Image::FORMAT_LATC_LA: {
if (config.latc_supported) {
......@@ -319,6 +313,36 @@ Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_
}
} break;
case Image::FORMAT_RGTC_R: {
if (config.rgtc_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RED_RGTC1_EXT;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
srgb = true;
} else {
need_decompress = true;
}
} break;
case Image::FORMAT_RGTC_RG: {
if (config.rgtc_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RED_GREEN_RGTC2_EXT;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
} break;
case Image::FORMAT_BPTC_RGBA: {
if (config.bptc_supported) {
......@@ -662,7 +686,7 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
if (texture->alloc_width == img->get_width() / 2 && texture->alloc_height == img->get_height() / 2) {
img->shrink_x2();
} else if (img->get_format() <= Image::FORMAT_RGB565) {
} else if (img->get_format() <= Image::FORMAT_RGBA8) {
img->resize(texture->alloc_width, texture->alloc_height, Image::INTERPOLATE_BILINEAR);
}
......@@ -768,6 +792,9 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
int h = img->get_height();
int tsize = 0;
int block = Image::get_format_block_size(img->get_format());
for (int i = 0; i < mipmaps; i++) {
int size, ofs;
......@@ -777,7 +804,16 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
if (texture->compressed) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glCompressedTexImage2D(blit_target, i, internal_format, w, h, 0, size, &read[ofs]);
//this is not needed, as compressed takes the regular size, even if blocks extend it
//int bw = (w % block != 0) ? w + (block - w % block) : w;
//int bh = (h % block != 0) ? h + (block - h % block) : h;
int bw = w;
int bh = h;
glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]);
print_line("format: " + Image::get_format_name(texture->format) + " size: " + Vector2(bw, bh) + " block: " + itos(block));
} else {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
......@@ -6358,6 +6394,7 @@ void RasterizerStorageGLES3::initialize() {
config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_dxt1") || config.extensions.has("GL_EXT_texture_compression_s3tc") || config.extensions.has("WEBGL_compressed_texture_s3tc");
config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture");
config.latc_supported = config.extensions.has("GL_EXT_texture_compression_latc");
config.rgtc_supported = config.extensions.has("GL_EXT_texture_compression_rgtc");
config.bptc_supported = config.extensions.has("GL_ARB_texture_compression_bptc");
#ifdef GLES_OVER_GL
config.hdr_supported = true;
......
......@@ -69,6 +69,7 @@ public:
bool s3tc_supported;
bool latc_supported;
bool rgtc_supported;
bool bptc_supported;
bool etc_supported;
bool etc2_supported;
......
......@@ -169,6 +169,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_mode", PROPERTY_HINT_ENUM, "Compress,Force RGBE"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), p_preset == PRESET_3D ? 1 : 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), p_preset == PRESET_2D_PIXEL ? false : true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/mipmaps"), p_preset == PRESET_3D ? true : false));
......@@ -181,7 +182,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT));
}
void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb) {
void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe) {
FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE);
f->store_8('G');
......@@ -204,6 +205,10 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
if (p_detect_srgb)
format |= StreamTexture::FORMAT_BIT_DETECT_SRGB;
if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) {
p_compress_mode == COMPRESS_UNCOMPRESSED; //these can't go as lossy
}
switch (p_compress_mode) {
case COMPRESS_LOSSLESS: {
......@@ -267,7 +272,12 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
Ref<Image> image = p_image->duplicate();
image->generate_mipmaps();
image->compress(p_vram_compression);
if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) {
image->convert(Image::FORMAT_RGBE9995);
} else {
image->compress(p_vram_compression);
}
format |= image->get_format();
......@@ -316,6 +326,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
bool premult_alpha = p_options["process/premult_alpha"];
bool stream = p_options["stream"];
int size_limit = p_options["size_limit"];
bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1;
Ref<Image> image;
image.instance();
......@@ -367,16 +378,16 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
if (compress_mode == COMPRESS_VIDEO_RAM) {
//must import in all formats
//Android, GLES 2.x
_save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb);
_save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe);
r_platform_variants->push_back("etc");
//_save_stex(image,p_save_path+".etc2.stex",compress_mode,lossy,Image::COMPRESS_ETC2,mipmaps,tex_flags,stream);
//r_platform_variants->push_back("etc2");
_save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb);
_save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe);
r_platform_variants->push_back("s3tc");
} else {
//import normally
_save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_16BIT /*this is ignored */, mipmaps, tex_flags, stream, detect_3d, detect_srgb);
_save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe);
}
return OK;
......
......@@ -80,7 +80,7 @@ public:
virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const;
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const;
void _save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb);
void _save_stex(const Ref<Image> &p_image, const String &p_to_path, int p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, int p_texture_flags, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe);
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL);
......
......@@ -71,6 +71,8 @@ void TextureEditor::_notification(int p_what) {
String format;
if (texture->cast_to<ImageTexture>()) {
format = Image::get_format_name(texture->cast_to<ImageTexture>()->get_format());
} else if (texture->cast_to<StreamTexture>()) {
format = Image::get_format_name(texture->cast_to<StreamTexture>()->get_format());
} else {
format = texture->get_class();
}
......
......@@ -76,8 +76,8 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "DXT1", true, false, 4, 8, Image::FORMAT_DXT1 },
{ "DXT3", true, false, 4, 16, Image::FORMAT_DXT3 },
{ "DXT5", true, false, 4, 16, Image::FORMAT_DXT5 },
{ "ATI1", true, false, 4, 8, Image::FORMAT_ATI1 },
{ "ATI2", true, false, 4, 16, Image::FORMAT_ATI2 },
{ "ATI1", true, false, 4, 8, Image::FORMAT_LATC_L },
{ "ATI2", true, false, 4, 16, Image::FORMAT_LATC_LA },
{ "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 },
{ "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 },
......
......@@ -59,9 +59,9 @@ void image_decompress_squish(Image *p_image) {
squish_flags = squish::kDxt3;
} else if (p_image->get_format() == Image::FORMAT_DXT5) {
squish_flags = squish::kDxt5;
} else if (p_image->get_format() == Image::FORMAT_ATI1) {
} else if (p_image->get_format() == Image::FORMAT_LATC_L || p_image->get_format() == Image::FORMAT_RGTC_R) {
squish_flags = squish::kBc4;
} else if (p_image->get_format() == Image::FORMAT_ATI2) {
} else if (p_image->get_format() == Image::FORMAT_LATC_LA || p_image->get_format() == Image::FORMAT_RGTC_RG) {
squish_flags = squish::kBc5;
} else {
ERR_FAIL_COND(true);
......@@ -81,61 +81,82 @@ void image_decompress_squish(Image *p_image) {
void image_compress_squish(Image *p_image) {
if (p_image->get_format() >= Image::FORMAT_DXT1)
return; //do not compress, already compressed
int w = p_image->get_width();
int h = p_image->get_height();
if (!p_image->has_mipmaps()) {
ERR_FAIL_COND(!w || w % 4 != 0);
ERR_FAIL_COND(!h || h % 4 != 0);
} else {
ERR_FAIL_COND(!w || w != nearest_power_of_2(w));
ERR_FAIL_COND(!h || h != nearest_power_of_2(h));
};
if (p_image->get_format() <= Image::FORMAT_RGBA8) {
if (p_image->get_format() >= Image::FORMAT_DXT1)
return; //do not compress, already compressed
int squish_comp = squish::kColourRangeFit;
Image::Format target_format;
int shift = 0;
int squish_comp = squish::kColourRangeFit; // TODO: use lossy quality setting to determine the quality
Image::Format target_format;
Image::DetectChannels dc = p_image->get_detected_channels();
if (p_image->get_format() == Image::FORMAT_LA8) {
//compressed normalmap
target_format = Image::FORMAT_DXT5;
squish_comp |= squish::kDxt5;
} else if (p_image->detect_alpha() != Image::ALPHA_NONE) {
p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
target_format = Image::FORMAT_DXT3;
squish_comp |= squish::kDxt3;
} else {
target_format = Image::FORMAT_DXT1;
shift = 1;
squish_comp |= squish::kDxt1;
}
switch (dc) {
case Image::DETECTED_L: {
p_image->convert(Image::FORMAT_RGBA8); //always expects rgba
target_format = Image::FORMAT_LATC_L;
squish_comp |= squish::kBc4;
} break;
case Image::DETECTED_LA: {
PoolVector<uint8_t> data;
int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
data.resize(target_size);
target_format = Image::FORMAT_LATC_LA;
squish_comp |= squish::kBc5;
} break;
case Image::DETECTED_R: {
PoolVector<uint8_t>::Read rb = p_image->get_data().read();
PoolVector<uint8_t>::Write wb = data.write();
target_format = Image::FORMAT_RGTC_R;
squish_comp |= squish::kBc4;
} break;
case Image::DETECTED_RG: {
int dst_ofs = 0;
target_format = Image::FORMAT_RGTC_RG;
squish_comp |= squish::kBc5;
} break;
case Image::DETECTED_RGB: {
for (int i = 0; i <= mm_count; i++) {
target_format = Image::FORMAT_DXT1;
squish_comp |= squish::kDxt1;
} break;
case Image::DETECTED_RGBA: {
int src_ofs = p_image->get_mipmap_offset(i);
squish::CompressImage(&rb[src_ofs], w, h, &wb[dst_ofs], squish_comp);
dst_ofs += (MAX(4, w) * MAX(4, h)) >> shift;
w >>= 1;
h >>= 1;
}
//TODO, should convert both, then measure which one does a better job
target_format = Image::FORMAT_DXT5;
squish_comp |= squish::kDxt5;
rb = PoolVector<uint8_t>::Read();
wb = PoolVector<uint8_t>::Write();
} break;
}
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
PoolVector<uint8_t> data;
int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps() ? -1 : 0);
int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
data.resize(target_size);
int shift = Image::get_format_pixel_rshift(target_format);
PoolVector<uint8_t>::Read rb = p_image->get_data().read();
PoolVector<uint8_t>::Write wb = data.write();
int dst_ofs = 0;
for (int i = 0; i <= mm_count; i++) {
int bw = w % 4 != 0 ? w + (4 - w % 4) : w;
int bh = h % 4 != 0 ? h + (4 - h % 4) : h;
int src_ofs = p_image->get_mipmap_offset(i);
squish::CompressImage(&rb[src_ofs], bw, bh, &wb[dst_ofs], squish_comp);
dst_ofs += (MAX(4, w) * MAX(4, h)) >> shift;
w >>= 1;
h >>= 1;
}
rb = PoolVector<uint8_t>::Read();
wb = PoolVector<uint8_t>::Write();
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
}
#!/usr/bin/env python
Import('env')
Import('env_modules')
env_tinyexr = env_modules.Clone()
# Thirdparty source files
# Not unbundled for now as they are not commonly available as shared library
thirdparty_dir = "#thirdparty/tinyexr/"
thirdparty_sources = [
"tinyexr.cc",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
env_tinyexr.add_source_files(env.modules_sources, thirdparty_sources)
env_tinyexr.Append(CPPPATH=[thirdparty_dir])
# Godot's own source files
env_tinyexr.add_source_files(env.modules_sources, "*.cpp")
def can_build(platform):
return True
def configure(env):
pass
/*************************************************************************/
/* image_loader_jpegd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "image_loader_tinyexr.h"
#include "os/os.h"
#include "print_string.h"
#include "thirdparty/tinyexr/tinyexr.h"
Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f) {
PoolVector<uint8_t> src_image;
int src_image_len = f->get_len();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
PoolVector<uint8_t>::Write w = src_image.write();
f->get_buffer(&w[0], src_image_len);
f->close();
EXRVersion exr_version;
EXRImage exr_image;
EXRHeader exr_header;
const char *err = NULL;
InitEXRHeader(&exr_header);
int ret = ParseEXRVersionFromMemory(&exr_version, w.ptr(), src_image_len);
if (ret != TINYEXR_SUCCESS) {
return ERR_FILE_CORRUPT;
}
ret = ParseEXRHeaderFromMemory(&exr_header, &exr_version, w.ptr(), src_image_len, &err);
if (ret != TINYEXR_SUCCESS) {
if (err) {
ERR_PRINTS(String(err));
}
return ERR_FILE_CORRUPT;
}
InitEXRImage(&exr_image);
ret = LoadEXRImageFromMemory(&exr_image, &exr_header, w.ptr(), src_image_len, &err);
if (ret != TINYEXR_SUCCESS) {
if (err) {
ERR_PRINTS(String(err));
}
return ERR_FILE_CORRUPT;
}
// RGBA
int idxR = -1;
int idxG = -1;
int idxB = -1;
int idxA = -1;
for (int c = 0; c < exr_header.num_channels; c++) {
if (strcmp(exr_header.channels[c].name, "R") == 0) {
idxR = c;
} else if (strcmp(exr_header.channels[c].name, "G") == 0) {
idxG = c;
} else if (strcmp(exr_header.channels[c].name, "B") == 0) {
idxB = c;
} else if (strcmp(exr_header.channels[c].name, "A") == 0) {
idxA = c;
}
}
if (idxR == -1) {
ERR_PRINT("R channel not found");
// @todo { free exr_image }
return ERR_FILE_CORRUPT;
}
if (idxG == -1) {
ERR_PRINT("G channel not found\n")
// @todo { free exr_image }
return ERR_FILE_CORRUPT;
}
if (idxB == -1) {
ERR_PRINT("B channel not found\n")
// @todo { free exr_image }
return ERR_FILE_CORRUPT;
}
PoolVector<uint8_t> imgdata;
Image::Format format;
if (idxA > 0) {
imgdata.resize(exr_image.width * exr_image.height * 8); //RGBA16
format = Image::FORMAT_RGBAH;
} else {
imgdata.resize(exr_image.width * exr_image.height * 6); //RGB16
format = Image::FORMAT_RGBH;
}
{
PoolVector<uint8_t>::Write wd = imgdata.write();
uint16_t *iw = (uint16_t *)wd.ptr();
// Assume `out_rgba` have enough memory allocated.
for (int i = 0; i < exr_image.width * exr_image.height; i++) {
*iw++ = Math::make_half_float(reinterpret_cast<float **>(exr_image.images)[idxR][i]);
*iw++ = Math::make_half_float(reinterpret_cast<float **>(exr_image.images)[idxG][i]);
*iw++ = Math::make_half_float(reinterpret_cast<float **>(exr_image.images)[idxB][i]);
if (idxA > 0) {
*iw++ = Math::make_half_float(reinterpret_cast<float **>(exr_image.images)[idxA][i]);
}
}
}
print_line("EXR w: " + itos(exr_image.width) + " h:" + itos(exr_image.height) + " format " + Image::get_format_name(format));
p_image->create(exr_image.width, exr_image.height, false, format, imgdata);
w = PoolVector<uint8_t>::Write();
return OK;
}
void ImageLoaderTinyEXR::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("exr");
}
ImageLoaderTinyEXR::ImageLoaderTinyEXR() {
}
/*************************************************************************/
/* image_loader_jpegd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef IMAGE_LOADER_TINYEXR_H
#define IMAGE_LOADER_TINYEXR_H
#include "io/image_loader.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
class ImageLoaderTinyEXR : public ImageFormatLoader {
public:
virtual Error load_image(Ref<Image> p_image, FileAccess *f);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
ImageLoaderTinyEXR();
};
#endif
/*************************************************************************/
/* register_types.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "register_types.h"
#include "image_loader_tinyexr.h"
static ImageLoaderTinyEXR *image_loader_tinyexr = NULL;
void register_tinyexr_types() {
image_loader_tinyexr = memnew(ImageLoaderTinyEXR);
ImageLoader::add_image_format_loader(image_loader_tinyexr);
}
void unregister_tinyexr_types() {
memdelete(image_loader_tinyexr);
}
/*************************************************************************/
/* register_types.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
void register_tinyexr_types();
void unregister_tinyexr_types();
#define TINYEXR_IMPLEMENTATION
#include "tinyexr.h"
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment