From f2addc0e4450fbf165fa4b2d81aa6baf4c24cbf0 Mon Sep 17 00:00:00 2001 From: "Arthur Brainville (Ybalrid)" Date: Sat, 2 Mar 2019 22:00:48 +0100 Subject: [PATCH] 16bit images are 16bit images: added Image::bits and Image::pixel_type --- examples/gltfutil/main.cc | 4 +-- examples/gltfutil/texture_dumper.cc | 30 ++++++++++++++++++--- tiny_gltf.h | 42 ++++++++++++++++++++++------- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/examples/gltfutil/main.cc b/examples/gltfutil/main.cc index 4b258d2..5d080fa 100644 --- a/examples/gltfutil/main.cc +++ b/examples/gltfutil/main.cc @@ -20,8 +20,8 @@ int usage(int ret = 0) { "[path to output directory])\n\n" //<< "\t\t -i: start in interactive mode\n" << "\t\t -d: dump enclosed content (image assets)\n" - << "\t\t -f: file format for image output" - << "\t\t -o: ouptput directory path" + << "\t\t -f: file format for image output\n" + << "\t\t -o: ouptput directory path\n" << "\t\t -h: print this help\n"; return ret; } diff --git a/examples/gltfutil/texture_dumper.cc b/examples/gltfutil/texture_dumper.cc index 0fcc6a4..71cfbaa 100644 --- a/examples/gltfutil/texture_dumper.cc +++ b/examples/gltfutil/texture_dumper.cc @@ -1,5 +1,5 @@ -#include #include +#include #include "stb_image_write.h" #include "texture_dumper.h" @@ -26,28 +26,50 @@ void texture_dumper::dump_to_folder(const std::string& path) { cout << "image name is: \"" << image.name << "\"\n"; cout << "image size is: " << image.width << 'x' << image.height << '\n'; cout << "pixel channel count :" << image.component << '\n'; + cout << "pixel bit depth :" << image.bits << '\n'; std::string name = image.name.empty() ? std::to_string(index) : image.name; + // TODO stb_image_write doesn't support any 16bit wirtes; + unsigned char* bytes_to_write = + const_cast(image.image.data()); + unsigned char* tmp_buffer = nullptr; + if (image.pixel_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + unsigned short max = 0xFFFF; + tmp_buffer = new unsigned char[image.width * image.height * + image.component * sizeof(unsigned char)]; + + unsigned short* buffer_as_shorts = (unsigned short*)bytes_to_write; + for (int i = 0; i < image.component * image.width * image.height; ++i) { + tmp_buffer[i] = + (unsigned char)(0xFF * (float(buffer_as_shorts[i]) / float(max))); + } + bytes_to_write = + tmp_buffer; // swap the pointer, but keep tmp_buffer around as we + // need to delete that memory later + } + switch (configured_format) { case texture_output_format::png: name = path + "/" + name + ".png"; std::cout << "Image will be written to " << name << '\n'; stbi_write_png(name.c_str(), image.width, image.height, image.component, - image.image.data(), 0); + bytes_to_write, 0); break; case texture_output_format::bmp: std::cout << "Image will be written to " << name << '\n'; name = path + "/" + name + ".bmp"; stbi_write_bmp(name.c_str(), image.width, image.height, image.component, - image.image.data()); + bytes_to_write); break; case texture_output_format::tga: std::cout << "Image will be written to " << name << '\n'; name = path + "/" + name + ".tga"; stbi_write_tga(name.c_str(), image.width, image.height, image.component, - image.image.data()); + bytes_to_write); break; } + + delete[] tmp_buffer; } } diff --git a/tiny_gltf.h b/tiny_gltf.h index c602e0d..d3188fa 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -492,6 +492,8 @@ struct Image { int width; int height; int component; + int bits; // bit depth per channel. 8(byte), 16 or 32. + int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually UBYTE(bits = 8) or USHORT(bits = 16) std::vector image; int bufferView; // (required if no uri) std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", @@ -1640,28 +1642,49 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, std::str int w, h, comp, req_comp; + unsigned char* data = nullptr; + // force 32-bit textures for common Vulkan compatibility. It appears that // some GPU drivers do not support 24-bit images for Vulkan req_comp = 4; + int bits = 8; + int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + + // It is possible that the image we want to load is a 16bit per channel image + // We are going to attempt to load it as 16bit per channel, and if it worked, + // set the image data accodingly. We are casting the returned pointer into + // unsigned char, because we are representing "bytes". But we are updating + // the Image metadata to signal that this image uses 2 bytes (16bits) per + // channel: + if(stbi_is_16_bit_from_memory(bytes, size)) + { + data = (unsigned char*)stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp); + bits = 16; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } + + // at this point, if data is still NULL, it means that the image wasn't + // 16bit per channel, we are going to load it as a normal 8bit per channel + // mage as we used to do: // if image cannot be decoded, ignore parsing and keep it by its path // don't break in this case // FIXME we should only enter this function if the image is embedded. If // image->uri references // an image file, it should be left as it is. Image loading should not be // mandatory (to support other formats) - unsigned char *data = + if(!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); if (!data) { // NOTE: you can use `warn` instead of `err` if (err) { - (*err) += "Unknown image format. STB cannot decode image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\". Proably 16bit PNG?\n"; + (*err) += "Unknown image format. STB cannot decode image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; } return false; } if (w < 1 || h < 1) { - free(data); + stbi_image_free(data); if (err) { (*err) += "Invalid image data for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; } @@ -1670,7 +1693,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, std::str if (req_width > 0) { if (req_width != w) { - free(data); + stbi_image_free(data); if (err) { (*err) += "Image width mismatch for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; } @@ -1680,7 +1703,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, std::str if (req_height > 0) { if (req_height != h) { - free(data); + stbi_image_free(data); if (err) { (*err) += "Image height mismatch. for image[" + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; } @@ -1691,10 +1714,11 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, std::str image->width = w; image->height = h; image->component = req_comp; - image->image.resize(static_cast(w * h * req_comp)); - std::copy(data, data + w * h * req_comp, image->image.begin()); - - free(data); + image->bits = bits; + image->pixel_type = pixel_type; + image->image.resize(static_cast(w * h * req_comp) * (bits/8)); + std::copy(data, data + w * h * req_comp * (bits/8), image->image.begin()); + stbi_image_free(data); return true; }