diff --git a/libs/image/include/image/ColorTransform.h b/libs/image/include/image/ColorTransform.h index 9ff88266fa..15e2658f02 100644 --- a/libs/image/include/image/ColorTransform.h +++ b/libs/image/include/image/ColorTransform.h @@ -159,8 +159,8 @@ inline filament::math::float3 linearToSRGB(const filament::math::float3& color) } // Creates a n-channel sRGB image from a linear floating-point image. -// The source image can have more than N channels, but only the first N are honored. -template +// The source image can have more than N channels, but only the first N are converted to sRGB. +template std::unique_ptr fromLinearTosRGB(const LinearImage& image) { const size_t w = image.getWidth(); const size_t h = image.getHeight(); @@ -182,10 +182,8 @@ std::unique_ptr fromLinearTosRGB(const LinearImage& image) { } // Creates a N-channel RGB u8 image from a f32 image. -// The source image can have three or more channels, but only the first N are honored. -template +template std::unique_ptr fromLinearToRGB(const LinearImage& image) { - using filament::math::float3; size_t w = image.getWidth(); size_t h = image.getHeight(); size_t channels = image.getChannels(); diff --git a/libs/image/src/ImageSampler.cpp b/libs/image/src/ImageSampler.cpp index c5bac66009..fbc7a0ea02 100644 --- a/libs/image/src/ImageSampler.cpp +++ b/libs/image/src/ImageSampler.cpp @@ -342,9 +342,9 @@ void generateMipmaps(const LinearImage& source, Filter filter, LinearImage* resu uint32_t width = source.getWidth(); uint32_t height = source.getHeight(); for (uint32_t n = 0; n < mips; ++n) { - width = std::max(width >> 1u, 1u); - height = std::max(height >> 1u, 1u); - result[n] = resampleImage(source, width, height, filter); + width = std::max(width >> 1u, 1u); + height = std::max(height >> 1u, 1u); + result[n] = resampleImage(source, width, height, filter); } } diff --git a/libs/imageio/src/ImageDecoder.cpp b/libs/imageio/src/ImageDecoder.cpp index 54b01c01dc..ad6e3d3758 100644 --- a/libs/imageio/src/ImageDecoder.cpp +++ b/libs/imageio/src/ImageDecoder.cpp @@ -244,9 +244,15 @@ LinearImage PNGDecoder::decode() { if (colorType == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(mPNG); } - if (colorType == PNG_COLOR_TYPE_GRAY) { + 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) { png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); } else { @@ -257,6 +263,10 @@ LinearImage PNGDecoder::decode() { } 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); @@ -272,23 +282,23 @@ LinearImage PNGDecoder::decode() { 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< filament::math::float4>); + [](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; }); + [](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); }, + [](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; }); + [](uint16_t v) -> uint16_t { return ntohs(v); }, + [](const filament::math::float3& color) -> filament::math::float3 { return color; }); } } } catch(std::runtime_error& e) { diff --git a/libs/imageio/src/ImageEncoder.cpp b/libs/imageio/src/ImageEncoder.cpp index 4e7f3b5749..9f83c6c624 100644 --- a/libs/imageio/src/ImageEncoder.cpp +++ b/libs/imageio/src/ImageEncoder.cpp @@ -71,7 +71,7 @@ private: bool encode(const LinearImage& image) override; int chooseColorType(const LinearImage& image) const; - uint32_t getChannelsCount() const; + uint32_t getChannelsCount(int colorType) const; static void cb_error(png_structp png, png_const_charp error); static void cb_stream(png_structp png, png_bytep buffer, png_size_t size); @@ -298,8 +298,6 @@ int PNGEncoder::chooseColorType(const LinearImage& image) const { switch (channels) { case 1: return PNG_COLOR_TYPE_GRAY; - default: - std::cerr << "Warning: strange number of channels in PNG" << std::endl; case 3: switch (mFormat) { case PixelFormat::RGBM: @@ -308,15 +306,25 @@ int PNGEncoder::chooseColorType(const LinearImage& image) const { default: return PNG_COLOR_TYPE_RGB; } + case 4: + return PNG_COLOR_TYPE_RGBA; + default: + std::cerr << "Warning: strange number of channels in PNG" << std::endl; + return PNG_COLOR_TYPE_RGB; } } -uint32_t PNGEncoder::getChannelsCount() const { +uint32_t PNGEncoder::getChannelsCount(int colorType) const { switch (mFormat) { case PixelFormat::RGBM: case PixelFormat::RGB_10_11_11_REV: return 4; default: + switch (colorType) { + case PNG_COLOR_TYPE_GRAY: return 1; + case PNG_COLOR_TYPE_RGB: return 3; + case PNG_COLOR_TYPE_RGBA: return 4; + } return 3; } } @@ -333,7 +341,7 @@ bool PNGEncoder::encode(const LinearImage& image) { } break; default: - if (srcChannels != 1 && srcChannels != 3) { + if (srcChannels != 1 && srcChannels != 3 && srcChannels != 4) { std::cerr << "Cannot encode PNG: " << srcChannels << " channels." << std::endl; return false; } @@ -346,9 +354,11 @@ bool PNGEncoder::encode(const LinearImage& image) { // Write header (8 bit colour depth) size_t width = image.getWidth(); size_t height = image.getHeight(); + int colorType = chooseColorType(image); + png_set_IHDR(mPNG, mInfo, width, height, - 8, chooseColorType(image), PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + 8, colorType, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (mFormat == PixelFormat::LINEAR_RGB || mFormat == PixelFormat::RGB_10_11_11_REV) { png_set_gAMA(mPNG, mInfo, 1.0); @@ -366,26 +376,34 @@ bool PNGEncoder::encode(const LinearImage& image) { dstChannels = 1; data = fromLinearToGrayscale(image); } else { - dstChannels = getChannelsCount(); + dstChannels = getChannelsCount(colorType); switch (mFormat) { case PixelFormat::RGBM: data = fromLinearToRGBM(image); break; - case PixelFormat::sRGB: - data = fromLinearTosRGB(image); - break; - case PixelFormat::LINEAR_RGB: - data = fromLinearToRGB(image); - break; case PixelFormat::RGB_10_11_11_REV: data = fromLinearToRGB_10_11_11_REV(image); break; + case PixelFormat::sRGB: + if (dstChannels == 4) { + data = fromLinearTosRGB(image); + } else { + data = fromLinearTosRGB(image); + } + break; + case PixelFormat::LINEAR_RGB: + if (dstChannels == 4) { + data = fromLinearToRGB(image); + } else { + data = fromLinearToRGB(image); + } + break; } } for (size_t y = 0; y < height; y++) { - row_pointers[y] = reinterpret_cast(&data[y * width * dstChannels * - sizeof(uint8_t)]); + row_pointers[y] = reinterpret_cast + (&data[y * width * dstChannels * sizeof(uint8_t)]); } png_write_image(mPNG, row_pointers.get()); diff --git a/tools/mipgen/src/main.cpp b/tools/mipgen/src/main.cpp index 2396e1c571..8140853eb4 100644 --- a/tools/mipgen/src/main.cpp +++ b/tools/mipgen/src/main.cpp @@ -36,7 +36,7 @@ using namespace image; using namespace std; using namespace utils; -static ImageEncoder::Format g_format = ImageEncoder::Format::PNG_LINEAR; +static ImageEncoder::Format g_format = ImageEncoder::Format::PNG; static bool g_formatSpecified = false; static bool g_createGallery = false; static std::string g_compression = ""; @@ -252,7 +252,7 @@ int main(int argc, char* argv[]) { g_ktxContainer = true; g_formatSpecified = true; } else if (!g_formatSpecified) { - g_format = ImageEncoder::chooseFormat(outputPattern, !g_linearized); + g_format = ImageEncoder::chooseFormat(outputPattern, g_linearized); } puts("Reading image...");