diff options
author | Juan Linietsky <reduzio@gmail.com> | 2019-09-26 23:16:44 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2020-02-11 12:02:36 +0100 |
commit | 4aea9f74e650743fe6585d5230dd6e5f6c94c478 (patch) | |
tree | 5ee02ffbe4ec128317e3c7cdca4861f6a39fb382 /scene | |
parent | 263bebe0237b85b1343ba17b117c8c43287ecc57 (diff) |
Rewritten StreamTexture for better code reuse, added basis universal support
Diffstat (limited to 'scene')
-rw-r--r-- | scene/resources/texture.cpp | 326 | ||||
-rw-r--r-- | scene/resources/texture.h | 12 |
2 files changed, 171 insertions, 167 deletions
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index c0311aca3a..20c3654c2f 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -352,130 +352,50 @@ ImageTexture::~ImageTexture() { ////////////////////////////////////////// -void StreamTexture::set_path(const String &p_path, bool p_take_over) { - - if (texture.is_valid()) { - VisualServer::get_singleton()->texture_set_path(texture, p_path); - } - - Resource::set_path(p_path, p_take_over); -} - -void StreamTexture::_requested_3d(void *p_ud) { - - StreamTexture *st = (StreamTexture *)p_ud; - Ref<StreamTexture> stex(st); - ERR_FAIL_COND(!request_3d_callback); - request_3d_callback(stex); -} - -void StreamTexture::_requested_roughness(void *p_ud, const String &p_normal_path, VS::TextureDetectRoughnessChannel p_roughness_channel) { - - StreamTexture *st = (StreamTexture *)p_ud; - Ref<StreamTexture> stex(st); - ERR_FAIL_COND(!request_roughness_callback); - request_roughness_callback(stex, p_normal_path, p_roughness_channel); -} - -void StreamTexture::_requested_normal(void *p_ud) { - - StreamTexture *st = (StreamTexture *)p_ud; - Ref<StreamTexture> stex(st); - ERR_FAIL_COND(!request_normal_callback); - request_normal_callback(stex); -} - -StreamTexture::TextureFormatRequestCallback StreamTexture::request_3d_callback = NULL; -StreamTexture::TextureFormatRoughnessRequestCallback StreamTexture::request_roughness_callback = NULL; -StreamTexture::TextureFormatRequestCallback StreamTexture::request_normal_callback = NULL; - -Image::Format StreamTexture::get_format() const { - - return format; -} - -Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int p_size_limit) { - - alpha_cache.unref(); - - ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER); - - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); - - uint8_t header[4]; - f->get_buffer(header, 4); - if (header[0] != 'G' || header[1] != 'D' || header[2] != 'S' || header[3] != 'T') { - memdelete(f); - ERR_FAIL_COND_V(header[0] != 'G' || header[1] != 'D' || header[2] != 'S' || header[3] != 'T', ERR_FILE_CORRUPT); - } - - tw = f->get_16(); - tw_custom = f->get_16(); - th = f->get_16(); - th_custom = f->get_16(); - - f->get_32(); //texture flags (ignore, no longer supported) - uint32_t df = f->get_32(); //data format - -#ifdef TOOLS_ENABLED +Ref<Image> StreamTexture::load_image_from_file(FileAccess *f, int p_size_limit) { - r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D; - r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS; - r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL; - -#else - - r_request_3d = false; - r_request_roughness = false; - r_request_normal = false; - -#endif - if (!(df & FORMAT_BIT_STREAM)) { - p_size_limit = 0; - } + uint32_t data_format = f->get_32(); + uint32_t w = f->get_16(); + uint32_t h = f->get_16(); + uint32_t mipmaps = f->get_32(); + Image::Format format = Image::Format(f->get_32()); - if (df & FORMAT_BIT_LOSSLESS || df & FORMAT_BIT_LOSSY) { + print_line("format: " + itos(data_format) + " size " + Size2i(w, h) + " mipmaps: " + itos(mipmaps)); + if (data_format == DATA_FORMAT_LOSSLESS || data_format == DATA_FORMAT_LOSSY || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { //look for a PNG or WEBP file inside - int sw = tw; - int sh = th; - - uint32_t mipmaps = f->get_32(); - uint32_t size = f->get_32(); - - //print_line("mipmaps: " + itos(mipmaps)); - - while (mipmaps > 1 && p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) { - - f->seek(f->get_position() + size); - mipmaps = f->get_32(); - size = f->get_32(); - - sw = MAX(sw >> 1, 1); - sh = MAX(sh >> 1, 1); - mipmaps--; - } + int sw = w; + int sh = h; //mipmaps need to be read independently, they will be later combined Vector<Ref<Image> > mipmap_images; int total_size = 0; - for (uint32_t i = 0; i < mipmaps; i++) { + bool first = true; + + for (uint32_t i = 0; i < mipmaps + 1; i++) { - if (i) { - size = f->get_32(); + uint32_t size = f->get_32(); + + if (p_size_limit > 0 && i < (mipmaps - 1) && (sw > p_size_limit || sh > p_size_limit)) { + //can't load this due to size limit + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + f->seek(f->get_position() + size); + continue; } PoolVector<uint8_t> pv; pv.resize(size); { - PoolVector<uint8_t>::Write w = pv.write(); - f->get_buffer(w.ptr(), size); + PoolVector<uint8_t>::Write wr = pv.write(); + f->get_buffer(wr.ptr(), size); } Ref<Image> img; - if (df & FORMAT_BIT_LOSSLESS) { + if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + img = Image::basis_universal_unpacker(pv); + } else if (data_format == DATA_FORMAT_LOSSLESS) { img = Image::lossless_unpacker(pv); } else { img = Image::lossy_unpacker(pv); @@ -483,29 +403,43 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_ if (img.is_null() || img->empty()) { memdelete(f); - ERR_FAIL_COND_V(img.is_null() || img->empty(), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(img.is_null() || img->empty(), Ref<Image>()); + } + + if (first) { + //format will actually be the format of the first image, + //as it may have changed on compression + format = img->get_format(); + first = false; + } else if (img->get_format() != format) { + img->convert(format); //all needs to be the same format } total_size += img->get_data().size(); mipmap_images.push_back(img); + + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); } //print_line("mipmap read total: " + itos(mipmap_images.size())); - memdelete(f); //no longer needed + Ref<Image> image; + image.instance(); if (mipmap_images.size() == 1) { - + //only one image (which will most likely be the case anyway for this format) image = mipmap_images[0]; - return OK; + return image; } else { + //rarer use case, but needs to be supported PoolVector<uint8_t> img_data; img_data.resize(total_size); { - PoolVector<uint8_t>::Write w = img_data.write(); + PoolVector<uint8_t>::Write wr = img_data.write(); int ofs = 0; for (int i = 0; i < mipmap_images.size(); i++) { @@ -513,88 +447,149 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_ PoolVector<uint8_t> id = mipmap_images[i]->get_data(); int len = id.size(); PoolVector<uint8_t>::Read r = id.read(); - copymem(&w[ofs], r.ptr(), len); + copymem(&wr[ofs], r.ptr(), len); ofs += len; } } - image->create(sw, sh, true, mipmap_images[0]->get_format(), img_data); - return OK; + image->create(w, h, true, mipmap_images[0]->get_format(), img_data); + return image; } - } else { + } else if (data_format == DATA_FORMAT_IMAGE) { - //look for regular format - Image::Format format = (Image::Format)(df & FORMAT_MASK_IMAGE_FORMAT); - bool mipmaps = df & FORMAT_BIT_HAS_MIPMAPS; + int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false); - if (!mipmaps) { - int size = Image::get_image_data_size(tw, th, format, false); + for (uint32_t i = 0; i < mipmaps + 1; i++) { + int tw, th; + int ofs = Image::get_image_mipmap_offset_and_dimensions(w, h, format, i, tw, th); - PoolVector<uint8_t> img_data; - img_data.resize(size); + if (p_size_limit > 0 && i < mipmaps && (p_size_limit > tw || p_size_limit > th)) { + if (ofs) { + f->seek(f->get_position() + ofs); + } + continue; //oops, size limit enforced, go to next + } + + PoolVector<uint8_t> data; + data.resize(size - ofs); { - PoolVector<uint8_t>::Write w = img_data.write(); - f->get_buffer(w.ptr(), size); + PoolVector<uint8_t>::Write wr = data.write(); + f->get_buffer(wr.ptr(), data.size()); } - memdelete(f); + Ref<Image> image; + image.instance(); - image->create(tw, th, false, format, img_data); - return OK; - } else { + image->create(tw, th, mipmaps - i ? true : false, format, data); - int sw = tw; - int sh = th; + return image; + } + } - int mipmaps2 = Image::get_image_required_mipmaps(tw, th, format); - int total_size = Image::get_image_data_size(tw, th, format, true); - int idx = 0; + return Ref<Image>(); +} - while (mipmaps2 > 1 && p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) { +void StreamTexture::set_path(const String &p_path, bool p_take_over) { - sw = MAX(sw >> 1, 1); - sh = MAX(sh >> 1, 1); - mipmaps2--; - idx++; - } + if (texture.is_valid()) { + VisualServer::get_singleton()->texture_set_path(texture, p_path); + } - int ofs = Image::get_image_mipmap_offset(tw, th, format, idx); + Resource::set_path(p_path, p_take_over); +} - if (total_size - ofs <= 0) { - memdelete(f); - ERR_FAIL_V(ERR_FILE_CORRUPT); - } +void StreamTexture::_requested_3d(void *p_ud) { - f->seek(f->get_position() + ofs); + StreamTexture *st = (StreamTexture *)p_ud; + Ref<StreamTexture> stex(st); + ERR_FAIL_COND(!request_3d_callback); + request_3d_callback(stex); +} - PoolVector<uint8_t> img_data; - img_data.resize(total_size - ofs); +void StreamTexture::_requested_roughness(void *p_ud, const String &p_normal_path, VS::TextureDetectRoughnessChannel p_roughness_channel) { - { - PoolVector<uint8_t>::Write w = img_data.write(); - int bytes = f->get_buffer(w.ptr(), total_size - ofs); - //print_line("requested read: " + itos(total_size - ofs) + " but got: " + itos(bytes)); + StreamTexture *st = (StreamTexture *)p_ud; + Ref<StreamTexture> stex(st); + ERR_FAIL_COND(!request_roughness_callback); + request_roughness_callback(stex, p_normal_path, p_roughness_channel); +} - memdelete(f); +void StreamTexture::_requested_normal(void *p_ud) { - int expected = total_size - ofs; - if (bytes < expected) { - //this is a compatibility workaround for older format, which saved less mipmaps2. It is still recommended the image is reimported. - zeromem(w.ptr() + bytes, (expected - bytes)); - } else if (bytes != expected) { - ERR_FAIL_V(ERR_FILE_CORRUPT); - } - } + StreamTexture *st = (StreamTexture *)p_ud; + Ref<StreamTexture> stex(st); + ERR_FAIL_COND(!request_normal_callback); + request_normal_callback(stex); +} - image->create(sw, sh, true, format, img_data); +StreamTexture::TextureFormatRequestCallback StreamTexture::request_3d_callback = NULL; +StreamTexture::TextureFormatRoughnessRequestCallback StreamTexture::request_roughness_callback = NULL; +StreamTexture::TextureFormatRequestCallback StreamTexture::request_normal_callback = NULL; - return OK; - } +Image::Format StreamTexture::get_format() const { + + return format; +} + +Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) { + + alpha_cache.unref(); + + ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER); + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + + uint8_t header[4]; + f->get_buffer(header, 4); + if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') { + memdelete(f); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is corrupt (Bad header)."); } - ERR_FAIL_V(ERR_BUG); //unreachable + uint32_t version = f->get_32(); + + if (version > FORMAT_VERSION) { + memdelete(f); + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new."); + } + tw_custom = f->get_32(); + th_custom = f->get_32(); + uint32_t df = f->get_32(); //data format + + //skip reserved + mipmap_limit = int(f->get_32()); + //reserved + f->get_32(); + f->get_32(); + f->get_32(); + +#ifdef TOOLS_ENABLED + + r_request_3d = request_3d_callback && df & FORMAT_BIT_DETECT_3D; + r_request_roughness = request_roughness_callback && df & FORMAT_BIT_DETECT_ROUGNESS; + r_request_normal = request_normal_callback && df & FORMAT_BIT_DETECT_NORMAL; + +#else + + r_request_3d = false; + r_request_roughness = false; + r_request_normal = false; + +#endif + if (!(df & FORMAT_BIT_STREAM)) { + p_size_limit = 0; + } + + image = load_image_from_file(f, p_size_limit); + + if (image.is_null() || image->empty()) { + return ERR_CANT_OPEN; + } + + return OK; } Error StreamTexture::load(const String &p_path) { @@ -606,8 +601,9 @@ Error StreamTexture::load(const String &p_path) { bool request_3d; bool request_normal; bool request_roughness; + int mipmap_limit; - Error err = _load_data(p_path, lw, lh, lwc, lhc, image, request_3d, request_normal, request_roughness); + Error err = _load_data(p_path, lw, lh, lwc, lhc, image, request_3d, request_normal, request_roughness, mipmap_limit); if (err) return err; diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 173556da72..b42b770903 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -33,6 +33,7 @@ #include "core/io/resource_loader.h" #include "core/math/rect2.h" +#include "core/os/file_access.h" #include "core/os/mutex.h" #include "core/os/rw_lock.h" #include "core/os/thread_safe.h" @@ -139,7 +140,12 @@ public: enum DataFormat { DATA_FORMAT_IMAGE, DATA_FORMAT_LOSSLESS, - DATA_FORMAT_LOSSY + DATA_FORMAT_LOSSY, + DATA_FORMAT_BASIS_UNIVERSAL, + }; + + enum { + FORMAT_VERSION = 1 }; enum FormatBits { @@ -155,7 +161,7 @@ public: }; private: - Error _load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int p_size_limit = 0); + Error _load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0); String path_to_file; mutable RID texture; Image::Format format; @@ -173,6 +179,8 @@ protected: void _validate_property(PropertyInfo &property) const; public: + static Ref<Image> load_image_from_file(FileAccess *p_file, int p_size_limit); + typedef void (*TextureFormatRequestCallback)(const Ref<StreamTexture> &); typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<StreamTexture> &, const String &p_normal_path, VS::TextureDetectRoughnessChannel p_roughness_channel); |