#include /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include // for memcmp #include // for cerr #include #include #include #include // for ntohs #if defined(WIN32) # include # include #else # include #endif #include #include #include #include #include #include #include namespace image { class PNGDecoder : public ImageDecoder::Decoder { public: static PNGDecoder* create(std::istream& stream); static bool checkSignature(char const* buf); PNGDecoder(const PNGDecoder&) = delete; PNGDecoder& operator=(const PNGDecoder&) = delete; private: explicit PNGDecoder(std::istream& stream); ~PNGDecoder() override; void init(); // ImageDecoder::Decoder interface LinearImage decode() override; static void cb_error(png_structp, png_const_charp); static void cb_stream(png_structp png, png_bytep buffer, png_size_t size); void error(); void stream(void* buffer, size_t size); png_structp mPNG = nullptr; png_infop mInfo = nullptr; std::istream& mStream; std::streampos mStreamStartPos; }; // ----------------------------------------------------------------------------------------------- class PSDDecoder : public ImageDecoder::Decoder { public: static PSDDecoder* create(std::istream& stream); static bool checkSignature(char const* buf); PSDDecoder(const PSDDecoder&) = delete; PSDDecoder& operator=(const PSDDecoder&) = delete; private: explicit PSDDecoder(std::istream& stream); ~PSDDecoder() override; // ImageDecoder::Decoder interface LinearImage decode() override; static const char sig[]; std::istream& mStream; std::streampos mStreamStartPos; }; // ----------------------------------------------------------------------------------------------- class EXRDecoder : public ImageDecoder::Decoder { public: static EXRDecoder* create(std::istream& stream, const std::string& sourceName); static bool checkSignature(char const* buf); EXRDecoder(const EXRDecoder&) = delete; EXRDecoder& operator = (const EXRDecoder&) = delete; private: EXRDecoder(std::istream& stream, const std::string& sourceName); ~EXRDecoder() override; // ImageDecoder::Decoder interface LinearImage decode() override; static const char sig[]; std::istream& mStream; std::streampos mStreamStartPos; std::string mSourceName; }; // ----------------------------------------------------------------------------------------------- LinearImage ImageDecoder::decode(std::istream& stream, const std::string& sourceName, ColorSpace sourceSpace) { Format format = Format::NONE; std::streampos pos = stream.tellg(); char buf[16]; stream.read(buf, sizeof(buf)); if (PNGDecoder::checkSignature(buf)) { format = Format::PNG; } else if (HDRDecoder::checkSignature(buf)) { format = Format::HDR; } else if (PSDDecoder::checkSignature(buf)) { format = Format::PSD; } else if (EXRDecoder::checkSignature(buf)) { format = Format::EXR; } stream.seekg(pos); std::unique_ptr decoder; switch (format) { case Format::NONE: return LinearImage(); case Format::PNG: decoder.reset(PNGDecoder::create(stream)); decoder->setColorSpace(sourceSpace); break; case Format::HDR: decoder.reset(HDRDecoder::create(stream)); decoder->setColorSpace(ColorSpace::LINEAR); break; case Format::PSD: decoder.reset(PSDDecoder::create(stream)); decoder->setColorSpace(ColorSpace::LINEAR); break; case Format::EXR: decoder.reset(EXRDecoder::create(stream, sourceName)); decoder->setColorSpace(ColorSpace::LINEAR); break; } return decoder->decode(); } // ----------------------------------------------------------------------------------------------- static inline float read32(std::istream& istream) { uint32_t data; istream.read(reinterpret_cast(&data), sizeof(uint32_t)); data = ntohl(data); return *reinterpret_cast(&data); } static inline float read16(std::istream& istream) { uint16_t data; istream.read(reinterpret_cast(&data), sizeof(uint16_t)); return static_cast(ntohs(data)) / std::numeric_limits::max(); } // ----------------------------------------------------------------------------------------------- PNGDecoder* PNGDecoder::create(std::istream& stream) { PNGDecoder* decoder = new PNGDecoder(stream); decoder->init(); return decoder; } bool PNGDecoder::checkSignature(char const* buf) { return png_check_sig(png_const_bytep(buf), 8); } PNGDecoder::PNGDecoder(std::istream& stream) : mStream(stream), mStreamStartPos(stream.tellg()) { mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); } void PNGDecoder::init() { png_set_error_fn(mPNG, this, cb_error, nullptr); png_set_read_fn(mPNG, this, cb_stream); } PNGDecoder::~PNGDecoder() { png_destroy_read_struct(&mPNG, &mInfo, nullptr); } LinearImage PNGDecoder::decode() { std::unique_ptr imageData; try { mInfo = png_create_info_struct(mPNG); png_read_info(mPNG, mInfo); int colorType = png_get_color_type(mPNG, mInfo); int bitDepth = png_get_bit_depth(mPNG, mInfo); if (colorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(mPNG); } if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { if (bitDepth < 8) { png_set_expand_gray_1_2_4_to_8(mPNG); } png_set_gray_to_rgb(mPNG); } if (png_get_valid(mPNG, mInfo, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(mPNG); } if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) { double gamma = 1.0; png_get_gAMA(mPNG, mInfo, &gamma); if (gamma != 1.0) { png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); } } else { png_set_gamma_fixed(mPNG, PNG_FP_1, PNG_FP_1); png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_GAMMA_LINEAR); } if (bitDepth < 16) { png_set_expand_16(mPNG); } png_read_update_info(mPNG, mInfo); // Read updated color type since we may have asked for a conversion before colorType = png_get_color_type(mPNG, mInfo); uint32_t width = png_get_image_width(mPNG, mInfo); uint32_t height = png_get_image_height(mPNG, mInfo); size_t rowBytes = png_get_rowbytes(mPNG, mInfo); imageData = std::make_unique(height * rowBytes); std::unique_ptr rowPointers(new png_bytep[height]); for (size_t y = 0 ; y < height ; y++) { rowPointers[y] = &imageData[y * rowBytes]; } png_read_image(mPNG, rowPointers.get()); png_read_end(mPNG, mInfo); if (colorType == PNG_COLOR_TYPE_RGBA) { if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) { return toLinearWithAlpha(width, height, rowBytes, imageData, [](uint16_t v) -> uint16_t { return ntohs(v); }, sRGBToLinear); } else { return toLinearWithAlpha(width, height, rowBytes, imageData, [](uint16_t v) -> uint16_t { return ntohs(v); }, [](const filament::math::float4& color) -> filament::math::float4 { return color; }); } } else { // Convert to linear float (PNG 16 stores data in network order (big endian). if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) { return toLinear(width, height, rowBytes, imageData, [](uint16_t v) -> uint16_t { return ntohs(v); }, sRGBToLinear< filament::math::float3>); } else { return toLinear(width, height, rowBytes, imageData, [](uint16_t v) -> uint16_t { return ntohs(v); }, [](const filament::math::float3& color) -> filament::math::float3 { return color; }); } } } catch(std::runtime_error& e) { // reset the stream, like we found it std::cerr << "Runtime error while decoding PNG: " << e.what() << std::endl; mStream.seekg(mStreamStartPos); imageData.release(); } return LinearImage(); } void PNGDecoder::cb_stream(png_structp png, png_bytep buffer, png_size_t size) { PNGDecoder* that = static_cast(png_get_io_ptr(png)); that->stream(buffer, size); } void PNGDecoder::stream(void* buffer, size_t size) { mStream.read(static_cast(buffer), size); if (!mStream.good()) { throw std::runtime_error("Problem with the PNG stream."); } } void PNGDecoder::cb_error(png_structp png, png_const_charp) { PNGDecoder* that = static_cast(png_get_error_ptr(png)); that->error(); } void PNGDecoder::error() { throw std::runtime_error("Error while decoding PNG stream."); } // ----------------------------------------------------------------------------------------------- const char PSDDecoder::sig[] = { '8', 'B', 'P', 'S', 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; PSDDecoder* PSDDecoder::create(std::istream& stream) { PSDDecoder* decoder = new PSDDecoder(stream); return decoder; } bool PSDDecoder::checkSignature(char const* buf) { return !memcmp(buf, sig, sizeof(sig)); } PSDDecoder::PSDDecoder(std::istream& stream) : mStream(stream), mStreamStartPos(stream.tellg()) { } PSDDecoder::~PSDDecoder() = default; LinearImage PSDDecoder::decode() { #pragma pack(push, 1) // IMPORTANT NOTE: PSD files use big endian storage struct Header { char signature[4]; uint16_t version; char padding[6]; uint16_t channels; uint32_t height; uint32_t width; uint16_t depth; uint16_t mode; }; #pragma pack(pop) static const uint16_t kColorModeRGB = 3; static const uint16_t kCompressionRAW = 0; try { Header h = { }; mStream.read(reinterpret_cast(&h), sizeof(Header)); if (ntohs(h.channels) != 3) { throw std::runtime_error("the image must have 3 channels only"); } uint16_t depth = ntohs(h.depth); if (depth != 16 && depth != 32) { throw std::runtime_error("the image depth must be 16 or 32 bits per pixel"); } if (ntohs(h.mode) != kColorModeRGB) { throw std::runtime_error("the image must be RGB"); } uint32_t width = ntohl(h.width); uint32_t height = ntohl(h.height); uint32_t length; // color mode data section mStream.read(reinterpret_cast(&length), sizeof(uint32_t)); mStream.seekg(ntohl(length), std::istream::cur); // image resources mStream.read(reinterpret_cast(&length), sizeof(uint32_t)); mStream.seekg(ntohl(length), std::istream::cur); // layer and mask info section mStream.read(reinterpret_cast(&length), sizeof(uint32_t)); mStream.seekg(ntohl(length), std::istream::cur); // compression format uint16_t compression; mStream.read(reinterpret_cast(&compression), sizeof(uint16_t)); if (ntohs(compression) != kCompressionRAW) { throw std::runtime_error("compressed images are not supported"); } LinearImage image(width, height, 3); if (depth == 32) { for (size_t i = 0; i < 3; i++) { for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { filament::math::float3& pixel = *reinterpret_cast< filament::math::float3*>(image.getPixelRef(x, y)); pixel[i] = read32(mStream); } } } } else { for (size_t i = 0; i < 3; i++) { for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { filament::math::float3& pixel = *reinterpret_cast< filament::math::float3*>(image.getPixelRef(x, y)); pixel[i] = read16(mStream); } } } } return image; } catch(std::runtime_error& e) { // reset the stream, like we found it std::cerr << "Runtime error while decoding PSD: " << e.what() << std::endl; mStream.seekg(mStreamStartPos); } return LinearImage(); } // ----------------------------------------------------------------------------------------------- const char EXRDecoder::sig[] = { 0x76, 0x2f, 0x31, 0x01 }; EXRDecoder* EXRDecoder::create(std::istream& stream, const std::string& sourceName) { EXRDecoder* decoder = new EXRDecoder(stream, sourceName); return decoder; } bool EXRDecoder::checkSignature(char const* buf) { return !memcmp(buf, sig, sizeof(sig)); } EXRDecoder::EXRDecoder(std::istream& stream, const std::string& sourceName) : mStream(stream), mStreamStartPos(stream.tellg()), mSourceName(sourceName) { } EXRDecoder::~EXRDecoder() = default; LinearImage EXRDecoder::decode() { try { // copy the EXR data in memory std::vector src; unsigned char buffer[4096]; while (mStream.read(reinterpret_cast(buffer), sizeof(buffer))) { src.insert(src.end(), &buffer[0], &buffer[4096]); } src.insert(src.end(), &buffer[0], &buffer[mStream.gcount()]); int width; int height; float* rgba; const char* error; int ret = LoadEXRFromMemory(&rgba, &width, &height, src.data(), src.size(), &error); if (ret != TINYEXR_SUCCESS) { std::cerr << "Could not decode OpenEXR: " << error << std::endl; FreeEXRErrorMessage(error); mStream.seekg(mStreamStartPos); return LinearImage(); } src.clear(); src.shrink_to_fit(); LinearImage image((uint32_t) width, (uint32_t) height, 3); size_t i = 0; for (uint32_t y = 0; y < height; y++) { for (uint32_t x = 0; x < width; x++) { filament::math::float3& pixel = *reinterpret_cast< filament::math::float3*>(image.getPixelRef(x, y)); pixel.r = rgba[i++]; pixel.g = rgba[i++]; pixel.b = rgba[i++]; // skip alpha i++; } } free(rgba); return image; } catch(std::runtime_error& e) { // reset the stream, like we found it std::cerr << "Runtime error while decoding OpenEXR: " << e.what() << std::endl; mStream.seekg(mStreamStartPos); } return LinearImage(); } } // namespace image