summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/resources/texture.cpp326
-rw-r--r--scene/resources/texture.h12
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);