From 044387308a5b7c87c8adbc61b33bfe24559c5cad Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Fri, 17 Aug 2018 11:22:05 -0700 Subject: [PATCH] Migrate imageio to LinearImage. (#111) --- libs/image/include/image/ColorTransform.h | 19 ++++ libs/image/include/image/ImageOps.h | 3 + libs/image/include/image/LinearImage.h | 1 + libs/image/src/ImageOps.cpp | 13 +++ libs/imageio/include/imageio/ImageDecoder.h | 7 +- libs/imageio/include/imageio/ImageEncoder.h | 6 +- libs/imageio/src/ImageDecoder.cpp | 63 ++++++------ libs/imageio/src/ImageDiffer.cpp | 36 ++----- libs/imageio/src/ImageEncoder.cpp | 108 +++++++++++--------- samples/frame_generator.cpp | 7 +- tools/cmgen/src/cmgen.cpp | 84 ++++++++------- tools/cmgen/tests/test_cmgen.cpp | 8 +- tools/normal-blending/src/main.cpp | 18 ++-- tools/roughness-prefilter/src/main.cpp | 73 ++++++------- tools/skygen/src/main.cpp | 12 +-- 15 files changed, 243 insertions(+), 215 deletions(-) diff --git a/libs/image/include/image/ColorTransform.h b/libs/image/include/image/ColorTransform.h index 6b3eab4f10..5807bd7d18 100644 --- a/libs/image/include/image/ColorTransform.h +++ b/libs/image/include/image/ColorTransform.h @@ -273,6 +273,25 @@ std::unique_ptr fromLinearToRGBM(const LinearImage& image) { return dst; } +// Creates a packed single-channel integer-based image from a floating-point image. +// For example if T is uint8_t, then this performs a transformation from [0,1] to [0,255]. +template +std::unique_ptr fromLinearToGrayscale(const LinearImage& image) { + const size_t w = image.getWidth(); + const size_t h = image.getHeight(); + assert(image.getChannels() == 1); + std::unique_ptr dst(new uint8_t[w * h * sizeof(T)]); + T* d = reinterpret_cast(dst.get()); + for (size_t y = 0; y < h; ++y) { + float const* p = image.getPixelRef(0, y); + for (size_t x = 0; x < w; ++x, ++p, ++d) { + const float gray = math::saturate(*p) * std::numeric_limits::max(); + d[0] = T(gray); + } + } + return dst; +} + // Constructs a 3-channel LinearImage from an untyped data blob. // The "proc" lambda converts a single color component into a float. // The "transform" lambda performs an arbitrary float-to-float transformation. diff --git a/libs/image/include/image/ImageOps.h b/libs/image/include/image/ImageOps.h index 2684e08cd0..8ff69d0298 100644 --- a/libs/image/include/image/ImageOps.h +++ b/libs/image/include/image/ImageOps.h @@ -38,6 +38,9 @@ LinearImage verticalFlip(const LinearImage& image); // Transforms normals (components live in [-1,+1]) into colors (components live in [0,+1]). LinearImage vectorsToColors(const LinearImage& image); +// Creates a single-channel image by extracting the selected channel. +LinearImage extractChannel(const LinearImage& image, uint32_t channel); + // Constructs a multi-channel image by copying data from a sequence of single-channel images. LinearImage combineChannels(std::initializer_list images); LinearImage combineChannels(LinearImage const* img, size_t count); diff --git a/libs/image/include/image/LinearImage.h b/libs/image/include/image/LinearImage.h index 39ec00c2f7..fccda01b91 100644 --- a/libs/image/include/image/LinearImage.h +++ b/libs/image/include/image/LinearImage.h @@ -88,6 +88,7 @@ public: uint32_t getHeight() const { return mHeight; } uint32_t getChannels() const { return mChannels; } void reset() { *this = LinearImage(); } + bool isValid() const { return mData; } private: diff --git a/libs/image/src/ImageOps.cpp b/libs/image/src/ImageOps.cpp index 2e81037e95..ab8e039d40 100644 --- a/libs/image/src/ImageOps.cpp +++ b/libs/image/src/ImageOps.cpp @@ -128,6 +128,19 @@ LinearImage vectorsToColors(const LinearImage& image) { return result; } +LinearImage extractChannel(const LinearImage& source, uint32_t channel) { + const uint32_t width = source.getWidth(), height = source.getHeight(); + const uint32_t nchan = source.getChannels(); + ASSERT_PRECONDITION(channel < nchan, "Channel is out of range."); + LinearImage result(width, height, 1); + auto src = source.getPixelRef(); + auto dst = result.getPixelRef(); + for (uint32_t n = 0, npixels = width * height; n < npixels; ++n, ++dst, src += nchan) { + dst[0] = src[channel]; + } + return result; +} + LinearImage combineChannels(std::initializer_list images) { size_t count = images.end() - images.begin(); return combineChannels(images.begin(), count); diff --git a/libs/imageio/include/imageio/ImageDecoder.h b/libs/imageio/include/imageio/ImageDecoder.h index 4932e2e960..ad96106a53 100644 --- a/libs/imageio/include/imageio/ImageDecoder.h +++ b/libs/imageio/include/imageio/ImageDecoder.h @@ -17,9 +17,10 @@ #ifndef IMAGE_IMAGEDECODER_H_ #define IMAGE_IMAGEDECODER_H_ +#include #include -#include +#include namespace image { @@ -30,12 +31,12 @@ public: SRGB }; - static Image decode(std::istream& stream, const std::string& sourceName, + static LinearImage decode(std::istream& stream, const std::string& sourceName, ColorSpace sourceSpace = ColorSpace::SRGB); class Decoder { public: - virtual Image decode() = 0; + virtual LinearImage decode() = 0; virtual ~Decoder() = default; ColorSpace getColorSpace() const noexcept { diff --git a/libs/imageio/include/imageio/ImageEncoder.h b/libs/imageio/include/imageio/ImageEncoder.h index 10a9c5ce33..c8c285f643 100644 --- a/libs/imageio/include/imageio/ImageEncoder.h +++ b/libs/imageio/include/imageio/ImageEncoder.h @@ -20,7 +20,7 @@ #include #include -#include +#include namespace image { @@ -43,7 +43,7 @@ public: }; // the encode function only expects images that store pixels as floats - static void encode(std::ostream& stream, Format format, const Image& image, + static void encode(std::ostream& stream, Format format, const LinearImage& image, const std::string& compression, const std::string& destName); static Format chooseFormat(const std::string& name, bool forceLinear = false); @@ -51,7 +51,7 @@ public: class Encoder { public: - virtual void encode(const Image& image) = 0; + virtual void encode(const LinearImage& image) = 0; virtual ~Encoder() = default; }; }; diff --git a/libs/imageio/src/ImageDecoder.cpp b/libs/imageio/src/ImageDecoder.cpp index 0bd0e22e61..b2a11bf043 100644 --- a/libs/imageio/src/ImageDecoder.cpp +++ b/libs/imageio/src/ImageDecoder.cpp @@ -42,6 +42,7 @@ #include #include +#include namespace image { @@ -60,7 +61,7 @@ private: void init(); // ImageDecoder::Decoder interface - virtual Image decode() override; + virtual 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); @@ -89,7 +90,7 @@ private: HDRDecoder& operator=(const HDRDecoder&) = delete; // ImageDecoder::Decoder interface - virtual Image decode() override; + virtual LinearImage decode() override; static const char sigRadiance[]; static const char sigRGBE[]; @@ -112,7 +113,7 @@ private: PSDDecoder& operator = (const PSDDecoder&) = delete; // ImageDecoder::Decoder interface - virtual Image decode() override; + virtual LinearImage decode() override; static const char sig[]; std::istream& mStream; @@ -134,7 +135,7 @@ private: EXRDecoder& operator = (const EXRDecoder&) = delete; // ImageDecoder::Decoder interface - virtual Image decode() override; + virtual LinearImage decode() override; static const char sig[]; std::istream& mStream; @@ -144,7 +145,7 @@ private: // ----------------------------------------------------------------------------------------------- -Image ImageDecoder::decode(std::istream& stream, const std::string& sourceName, +LinearImage ImageDecoder::decode(std::istream& stream, const std::string& sourceName, ColorSpace sourceSpace) { Format format = Format::NONE; @@ -168,7 +169,7 @@ Image ImageDecoder::decode(std::istream& stream, const std::string& sourceName, std::unique_ptr decoder; switch (format) { case Format::NONE: - return Image(); + return LinearImage(); case Format::PNG: decoder.reset(PNGDecoder::create(stream)); decoder->setColorSpace(sourceSpace); @@ -265,7 +266,7 @@ static Image toLinearWithAlphaDeprecated(size_t w, size_t h, size_t bpr, return Image(std::move(dst), w, h, w * sizeof(math::float4), sizeof(math::float4), 4); } -Image PNGDecoder::decode() { +LinearImage PNGDecoder::decode() { std::unique_ptr imageData; try { mInfo = png_create_info_struct(mPNG); @@ -304,22 +305,22 @@ Image PNGDecoder::decode() { if (colorType == PNG_COLOR_TYPE_RGBA) { if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) { - return toLinearWithAlphaDeprecated(width, height, rowBytes, imageData, + return toLinearWithAlpha(width, height, rowBytes, imageData, [ ](uint16_t v) -> uint16_t { return ntohs(v); }, sRGBToLinear); } else { - return toLinearWithAlphaDeprecated(width, height, rowBytes, imageData, + return toLinearWithAlpha(width, height, rowBytes, imageData, [ ](uint16_t v) -> uint16_t { return ntohs(v); }, [ ](const math::float4& color) -> math::float4 { return color; }); } } else { // Convert to linear float (PNG 16 stores data in network order (big endian). if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) { - return toLinearDeprecated(width, height, rowBytes, imageData, + return toLinear(width, height, rowBytes, imageData, [ ](uint16_t v) -> uint16_t { return ntohs(v); }, sRGBToLinear); } else { - return toLinearDeprecated(width, height, rowBytes, imageData, + return toLinear(width, height, rowBytes, imageData, [ ](uint16_t v) -> uint16_t { return ntohs(v); }, [ ](const math::float3& color) -> math::float3 { return color; }); } @@ -330,7 +331,7 @@ Image PNGDecoder::decode() { mStream.seekg(mStreamStartPos); imageData.release(); } - return Image(); + return LinearImage(); } void PNGDecoder::cb_stream(png_structp png, png_bytep buffer, png_size_t size) { @@ -375,7 +376,7 @@ HDRDecoder::HDRDecoder(std::istream& stream) HDRDecoder::~HDRDecoder() = default; -Image HDRDecoder::decode() { +LinearImage HDRDecoder::decode() { try { float gamma; float exposure; @@ -398,13 +399,11 @@ Image HDRDecoder::decode() { } while (true); } - std::unique_ptr data(new uint8_t[width * height * sizeof(math::float3)]); - Image image(std::move(data), width, height, width*sizeof(math::float3), sizeof(math::float3)); + LinearImage image(width, height, 3); uint32_t flags = 0; - if (sx == '-') flags |= Image::FLIP_X; - if (sy == '+') flags |= Image::FLIP_Y; - image.flip(flags); + if (sx == '-') image = horizontalFlip(image); + if (sy == '+') image = verticalFlip(image); uint16_t w; uint16_t magic; @@ -447,7 +446,7 @@ Image HDRDecoder::decode() { uint8_t const* g = &rgbe[width]; uint8_t const* b = &rgbe[2*width]; uint8_t const* e = &rgbe[3*width]; - math::float3* i = static_cast(image.getPixelRef(0, y)); + math::float3* i = reinterpret_cast(image.getPixelRef(0, y)); // (rgb/256) * 2^(e-128) for (size_t x=0 ; x data(new uint8_t[width * height * sizeof(math::float3)]); - Image image(std::move(data), width, height, - width * sizeof(math::float3), sizeof(math::float3)); + LinearImage image(width, height, 3); if (depth == 32) { for (size_t i = 0; i < 3; i++) { for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { - math::float3& pixel = *static_cast(image.getPixelRef(x, y)); + math::float3& pixel = *reinterpret_cast(image.getPixelRef(x, y)); pixel[i] = read32(mStream); } } @@ -559,7 +556,7 @@ Image PSDDecoder::decode() { for (size_t i = 0; i < 3; i++) { for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { - math::float3& pixel = *static_cast(image.getPixelRef(x, y)); + math::float3& pixel = *reinterpret_cast(image.getPixelRef(x, y)); pixel[i] = read16(mStream); } } @@ -573,7 +570,7 @@ Image PSDDecoder::decode() { mStream.seekg(mStreamStartPos); } - return Image(); + return LinearImage(); } // ----------------------------------------------------------------------------------------------- @@ -595,7 +592,7 @@ EXRDecoder::EXRDecoder(std::istream& stream, const std::string& sourceName) EXRDecoder::~EXRDecoder() = default; -Image EXRDecoder::decode() { +LinearImage EXRDecoder::decode() { try { // copy the EXR data in memory std::vector src; @@ -614,19 +611,17 @@ Image EXRDecoder::decode() { if (ret != TINYEXR_SUCCESS) { std::cerr << "Could not decode OpenEXR: " << error << std::endl; mStream.seekg(mStreamStartPos); - return Image(); + return LinearImage(); } src.resize(0); - std::unique_ptr data(new uint8_t[width * height * sizeof(math::float3)]); - Image image(std::move(data), static_cast(width), static_cast(height), - width * sizeof(math::float3), sizeof(math::float3)); + LinearImage image(width, height, 3); size_t i = 0; for (size_t y = 0; y < height; y++) { for (size_t x = 0; x < width; x++) { - math::float3& pixel = *static_cast(image.getPixelRef(x, y)); + math::float3& pixel = *reinterpret_cast(image.getPixelRef(x, y)); pixel.r = rgba[i++]; pixel.g = rgba[i++]; pixel.b = rgba[i++]; @@ -642,7 +637,7 @@ Image EXRDecoder::decode() { mStream.seekg(mStreamStartPos); } - return Image(); + return LinearImage(); } } // namespace image diff --git a/libs/imageio/src/ImageDiffer.cpp b/libs/imageio/src/ImageDiffer.cpp index 22e1a897f5..80c8c40829 100644 --- a/libs/imageio/src/ImageDiffer.cpp +++ b/libs/imageio/src/ImageDiffer.cpp @@ -26,7 +26,7 @@ namespace image { -// TODO: (1) Remove usage of the old Image class. (2) Remove special treatment of 1-channel data. +// TODO: Remove special treatment of 1-channel data. void updateOrCompare(LinearImage limgResult, const utils::Path& fnameGolden, ComparisonMode mode, float epsilon) { if (mode == ComparisonMode::SKIP) { @@ -36,26 +36,15 @@ void updateOrCompare(LinearImage limgResult, const utils::Path& fnameGolden, // Regenerate the PNG file at the given path. if (mode == ComparisonMode::UPDATE) { std::ofstream out(fnameGolden, std::ios::binary | std::ios::trunc); - const size_t width = limgResult.getWidth(); - const size_t height = limgResult.getHeight(); - const size_t nchan = limgResult.getChannels(); - const size_t bpp = nchan * sizeof(float), bpr = width * bpp, nbytes = bpr * height; - std::unique_ptr data(new uint8_t[nbytes]); - auto format = ImageEncoder::Format::PNG_LINEAR; - if (fnameGolden.getExtension() == "rgbm" && nchan == 3) { + if (fnameGolden.getExtension() == "rgbm") { format = ImageEncoder::Format::RGBM; } - - if (nchan != 1) { - memcpy(data.get(), limgResult.getPixelRef(), nbytes); - Image im(std::move(data), width, height, bpr, bpp, nchan); - ImageEncoder::encode(out, format, im, "", fnameGolden); + if (limgResult.getChannels() != 1) { + ImageEncoder::encode(out, format, limgResult, "", fnameGolden); } else { auto limg2 = combineChannels({limgResult, limgResult, limgResult}); - memcpy(data.get(), limg2.getPixelRef(), nbytes); - Image im(std::move(data), width, height, bpr, bpp, nchan); - ImageEncoder::encode(out, format, im, "", fnameGolden); + ImageEncoder::encode(out, format, limg2, "", fnameGolden); } return; } @@ -63,20 +52,13 @@ void updateOrCompare(LinearImage limgResult, const utils::Path& fnameGolden, // Load the PNG file at the given path. std::ifstream in(fnameGolden, std::ios::binary); ASSERT_PRECONDITION(in, "Unable to open: %s", fnameGolden.c_str()); - Image imgGolden = ImageDecoder::decode(in, fnameGolden, ImageDecoder::ColorSpace::LINEAR); - const size_t width = imgGolden.getWidth(), height = imgGolden.getHeight(); - const size_t nchan = imgGolden.getChannelsCount(); + LinearImage limgGolden = ImageDecoder::decode(in, fnameGolden, ImageDecoder::ColorSpace::LINEAR); // Convert 4-channel RGBM into proper RGB. - LinearImage limgGolden; - if (fnameGolden.getExtension() == "rgbm" && nchan == 4) { + if (fnameGolden.getExtension() == "rgbm" && limgGolden.getChannels() == 4) { limgGolden = toLinearFromRGBM( - static_cast(imgGolden.getData()), - imgGolden.getWidth(), imgGolden.getHeight()); - } else { - limgGolden = LinearImage(width, height, nchan); - memcpy(limgGolden.getPixelRef(), imgGolden.getData(), - width * height * sizeof(float) * nchan); + reinterpret_cast(limgGolden.getPixelRef()), + limgGolden.getWidth(), limgGolden.getHeight()); } // Expand the result image from L to RGB. diff --git a/libs/imageio/src/ImageEncoder.cpp b/libs/imageio/src/ImageEncoder.cpp index 0937dff6bb..aff7a3517c 100644 --- a/libs/imageio/src/ImageEncoder.cpp +++ b/libs/imageio/src/ImageEncoder.cpp @@ -65,9 +65,9 @@ private: void init(); // ImageEncoder::Encoder interface - virtual void encode(const Image& image) override; + virtual void encode(const LinearImage& image) override; - int chooseColorType(const Image& image) const; + int chooseColorType(const LinearImage& image) const; uint32_t getChannelsCount() const; static void cb_error(png_structp png, png_const_charp error); @@ -98,7 +98,7 @@ private: HDREncoder& operator = (const HDREncoder&) = delete; // ImageEncoder::Encoder interface - virtual void encode(const Image& image) override; + virtual void encode(const LinearImage& image) override; static void float2rgbe(uint8_t rgbe[4], const math::float3& color); static size_t countRepeats(uint8_t const* data, size_t length); @@ -123,7 +123,7 @@ private: PSDEncoder& operator = (const PSDEncoder&) = delete; // ImageEncoder::Encoder interface - virtual void encode(const Image& image) override; + virtual void encode(const LinearImage& image) override; std::ostream& mStream; std::streampos mStreamStartPos; @@ -147,7 +147,7 @@ private: EXREncoder& operator = (const EXREncoder&) = delete; // ImageEncoder::Encoder interface - virtual void encode(const Image& image) override; + virtual void encode(const LinearImage& image) override; std::ostream& mStream; std::streampos mStreamStartPos; @@ -175,7 +175,7 @@ private: DDSEncoder& operator = (const DDSEncoder&) = delete; // ImageEncoder::Encoder interface - virtual void encode(const Image& image) override; + virtual void encode(const LinearImage& image) override; std::ostream& mStream; std::streampos mStreamStartPos; @@ -185,7 +185,7 @@ private: // ------------------------------------------------------------------------------------------------ -void ImageEncoder::encode(std::ostream& stream, Format format, const Image& image, +void ImageEncoder::encode(std::ostream& stream, Format format, const LinearImage& image, const std::string& compression, const std::string& destName) { std::unique_ptr encoder; switch(format) { @@ -283,13 +283,14 @@ void PNGEncoder::init() { png_set_write_fn(mPNG, this, cb_stream, NULL); } -int PNGEncoder::chooseColorType(const Image& image) const { - size_t channels = image.getChannelsCount(); +int PNGEncoder::chooseColorType(const LinearImage& image) const { + size_t channels = image.getChannels(); switch (channels) { case 1: return PNG_COLOR_TYPE_GRAY; - case 3: default: + std::cerr << "Warning: strange number of channels in PNG" << std::endl; + case 3: switch (mFormat) { case PixelFormat::RGBM: return PNG_COLOR_TYPE_RGBA; @@ -308,10 +309,11 @@ uint32_t PNGEncoder::getChannelsCount() const { } } -void PNGEncoder::encode(const Image& image) { - size_t srcChannels = image.getChannelsCount(); +void PNGEncoder::encode(const LinearImage& image) { + size_t srcChannels = image.getChannels(); if ((mFormat == PixelFormat::RGBM && srcChannels != 3) || (srcChannels != 1 && srcChannels != 3)) { + std::cerr << "Cannot encode PNG: " << srcChannels << " channels." << std::endl; return; } @@ -333,22 +335,28 @@ void PNGEncoder::encode(const Image& image) { png_write_info(mPNG, mInfo); - uint32_t channels = (srcChannels == 1 ? 1 : getChannelsCount()); - std::unique_ptr row_pointers(new png_bytep[height]); std::unique_ptr data; - switch (mFormat) { - case PixelFormat::RGBM: - data = fromLinearToRGBM(image); - break; - case PixelFormat::sRGB: - case PixelFormat::LINEAR_RGB: - data = fromLinearToRGB(image); - break; + + uint32_t dstChannels; + if (srcChannels == 1) { + dstChannels = 1; + data = fromLinearToGrayscale(image); + } else { + dstChannels = getChannelsCount(); + switch (mFormat) { + case PixelFormat::RGBM: + data = fromLinearToRGBM(image); + break; + case PixelFormat::sRGB: + case PixelFormat::LINEAR_RGB: + data = fromLinearToRGB(image); + break; + } } for (size_t y = 0; y < height; y++) { - row_pointers[y] = reinterpret_cast(&data[y * width * channels * + row_pointers[y] = reinterpret_cast(&data[y * width * dstChannels * sizeof(uint8_t)]); } @@ -465,8 +473,8 @@ void HDREncoder::rle(std::ostream& out, uint8_t const* data, size_t length) { } } -void HDREncoder::encode(const Image& image) { - if (image.getChannelsCount() != 3) { +void HDREncoder::encode(const LinearImage& image) { + if (image.getChannels() != 3) { return; } @@ -495,7 +503,7 @@ void HDREncoder::encode(const Image& image) { for (size_t y=0 ; y(image.getPixelRef(0, y)); + float3 const* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x=0 ; x(&v), sizeof(uint8_t)); } -void PSDEncoder::encode(const Image& image) { +void PSDEncoder::encode(const LinearImage& image) { static const uint16_t kColorModeRGB = 3; static const uint16_t kCompressionRAW = 0; // preview mode: 0 = highlight compression, 1 = exposure & gamma static const uint32_t kToningPreviewExposureGamma = 1; - if (image.getChannelsCount() != 3) { + if (image.getChannels() != 3) { return; } @@ -652,7 +660,7 @@ void PSDEncoder::encode(const Image& image) { if (depth == 32) { for (size_t channel = 0; channel < 3; channel++) { for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { write32(mStream, (*data)[channel]); data++; @@ -662,7 +670,7 @@ void PSDEncoder::encode(const Image& image) { } else { for (size_t channel = 0; channel < 3; channel++) { for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { write16(mStream, linearTosRGB((*data)[channel])); data++; @@ -713,8 +721,8 @@ static int toEXRCompression(const std::string& c) { throw std::runtime_error("unknown compression scheme " + c); } -void EXREncoder::encode(const Image& image) { - if (image.getChannelsCount() != 3) { +void EXREncoder::encode(const LinearImage& image) { + if (image.getChannels() != 3) { return; } @@ -738,7 +746,7 @@ void EXREncoder::encode(const Image& image) { size_t i = 0; for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++, data++) { r[i] = data->r; g[i] = data->g; @@ -875,7 +883,7 @@ DDSEncoder::DDSEncoder(std::ostream& stream, const std::string& compression, Pix DDSEncoder::~DDSEncoder() { } -static uint32_t chooseBpp(const Image& image, const std::string& compression) { +static uint32_t chooseBpp(const LinearImage& image, const std::string& compression) { size_t depth = 16; if (compression == "8") depth = 8; if (compression == "32") depth = 32; @@ -888,10 +896,10 @@ static uint32_t chooseBpp(const Image& image, const std::string& compression) { { 4, 8, 16 }, }; - return formats[image.getChannelsCount() - 1][index]; + return formats[image.getChannels() - 1][index]; } -static uint32_t chooseDXGIFormat(const Image& image, const std::string& compression) { +static uint32_t chooseDXGIFormat(const LinearImage& image, const std::string& compression) { size_t depth = 16; if (compression == "8") depth = 8; if (compression == "32") depth = 32; @@ -904,10 +912,10 @@ static uint32_t chooseDXGIFormat(const Image& image, const std::string& compress { DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R32G32B32A32_FLOAT }, }; - return formats[image.getChannelsCount() - 1][index]; + return formats[image.getChannels() - 1][index]; } -void DDSEncoder::encode(const Image& image) { +void DDSEncoder::encode(const LinearImage& image) { try { size_t width = image.getWidth(); size_t height = image.getHeight(); @@ -946,7 +954,7 @@ void DDSEncoder::encode(const Image& image) { switch (mFormat) { case PixelFormat::sRGB: for (size_t y = 0; y < height; y++) { - const float* data = static_cast(image.getPixelRef(0, y)); + const float* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { uint8_t b = (uint8_t) (linearTosRGB(saturate(*data)) * 255); mStream.write((const char*) &b, 1); @@ -956,7 +964,7 @@ void DDSEncoder::encode(const Image& image) { break; case PixelFormat::LINEAR_RGB: for (size_t y = 0; y < height; y++) { - const float* data = static_cast(image.getPixelRef(0, y)); + const float* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { uint8_t b = (uint8_t) (saturate(*data) * 255); mStream.write((const char*) &b, 1); @@ -969,7 +977,7 @@ void DDSEncoder::encode(const Image& image) { } case DXGI_FORMAT_R16_FLOAT: { for (size_t y = 0; y < height; y++) { - const float* data = static_cast(image.getPixelRef(0, y)); + const float* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { math::half p = math::half(*data); mStream.write((const char*) &p, 2); @@ -980,7 +988,7 @@ void DDSEncoder::encode(const Image& image) { } case DXGI_FORMAT_R32_FLOAT: { for (size_t y = 0; y < height; y++) { - const float* data = static_cast(image.getPixelRef(0, y)); + const float* data = reinterpret_cast(image.getPixelRef(0, y)); mStream.write((const char*) data, width * sizeof(float)); } break; @@ -989,7 +997,7 @@ void DDSEncoder::encode(const Image& image) { switch (mFormat) { case PixelFormat::sRGB: for (size_t y = 0; y < height; y++) { - const float2* data = static_cast(image.getPixelRef(0, y)); + const float2* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { uint8_t b; b = (uint8_t) (linearTosRGB(saturate(data->g)) * 255); @@ -1002,7 +1010,7 @@ void DDSEncoder::encode(const Image& image) { break; case PixelFormat::LINEAR_RGB: for (size_t y = 0; y < height; y++) { - const float2* data = static_cast(image.getPixelRef(0, y)); + const float2* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { uint8_t b; b = (uint8_t) (saturate(data->g) * 255); @@ -1018,7 +1026,7 @@ void DDSEncoder::encode(const Image& image) { } case DXGI_FORMAT_R16G16_FLOAT: { for (size_t y = 0; y < height; y++) { - const float2* data = static_cast(image.getPixelRef(0, y)); + const float2* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { half2 p = half2(*data); mStream.write((const char*) &p, sizeof(p)); @@ -1029,7 +1037,7 @@ void DDSEncoder::encode(const Image& image) { } case DXGI_FORMAT_R32G32_FLOAT: { for (size_t y = 0; y < height; y++) { - const float2* data = static_cast(image.getPixelRef(0, y)); + const float2* data = reinterpret_cast(image.getPixelRef(0, y)); mStream.write((const char*) data, width * sizeof(float2)); } break; @@ -1038,7 +1046,7 @@ void DDSEncoder::encode(const Image& image) { switch (mFormat) { case PixelFormat::sRGB: for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { uint8_t r = (uint8_t) (linearTosRGB(saturate(data->r)) * 255); uint8_t g = (uint8_t) (linearTosRGB(saturate(data->g)) * 255); @@ -1052,7 +1060,7 @@ void DDSEncoder::encode(const Image& image) { break; case PixelFormat::LINEAR_RGB: for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { uint8_t r = (uint8_t) (saturate(data->r) * 255); uint8_t g = (uint8_t) (saturate(data->g) * 255); @@ -1068,7 +1076,7 @@ void DDSEncoder::encode(const Image& image) { } case DXGI_FORMAT_R16G16B16A16_FLOAT: { for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { half4 p = half4(half3(*data), 1); mStream.write((const char*) &p, sizeof(ushort4)); @@ -1079,7 +1087,7 @@ void DDSEncoder::encode(const Image& image) { } case DXGI_FORMAT_R32G32B32A32_FLOAT: { for (size_t y = 0; y < height; y++) { - const float3* data = static_cast(image.getPixelRef(0, y)); + const float3* data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < width; x++) { float4 p = float4(3.0f, 3.0f, 3.0f, 1.0f); mStream.write((const char*) &p, sizeof(float4)); diff --git a/samples/frame_generator.cpp b/samples/frame_generator.cpp index dc8b81b5f1..520b008bc0 100644 --- a/samples/frame_generator.cpp +++ b/samples/frame_generator.cpp @@ -336,9 +336,8 @@ static void setup(Engine* engine, View* view, Scene* scene) { } template -static Image toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) { - std::unique_ptr buffer(new uint8_t[w * h * 3 * sizeof(float3)]); - Image result(std::move(buffer), w, h, w * sizeof(float3), sizeof(float3)); +static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) { + LinearImage result(w, h, 3); math::float3* d = reinterpret_cast(result.getPixelRef(0, 0)); for (size_t y = 0; y < h; ++y) { T const* p = reinterpret_cast(src + y * bpr); @@ -382,7 +381,7 @@ static void postRender(Engine*, View* view, Scene*, Renderer* renderer) { CaptureState* state = static_cast(user); const Viewport& v = state->view->getViewport(); - Image image(toLinear(v.width, v.height, v.width * 3, + LinearImage image(toLinear(v.width, v.height, v.width * 3, static_cast(buffer))); int digits = (int) log10 ((double) g_materialVariantCount) + 1; diff --git a/tools/cmgen/src/cmgen.cpp b/tools/cmgen/src/cmgen.cpp index e11cd6ea59..0495a4c395 100644 --- a/tools/cmgen/src/cmgen.cpp +++ b/tools/cmgen/src/cmgen.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -90,6 +91,8 @@ static void extractCubemapFaces(const utils::Path& iname, const Cubemap& cm, con static void outputSh(std::ostream& out, const std::unique_ptr& sh, size_t numBands); static void outputSpectrum(std::ostream& out, const std::unique_ptr& sh, size_t numBands); +static void saveImage(const std::string& path, ImageEncoder::Format format, const Image& image, + const std::string& compression); // ----------------------------------------------------------------------------------------------- @@ -393,22 +396,27 @@ int main(int argc, char* argv[]) { std::cout << "Decoding image..." << std::endl; } std::ifstream input_stream(iname.getPath(), std::ios::binary); - Image inputImage = ImageDecoder::decode(input_stream, iname.getPath()); - if (!inputImage.isValid()) { - std::cerr << "Unsupported image format!" << std::endl; - exit(0); + LinearImage linputImage = ImageDecoder::decode(input_stream, iname.getPath()); + if (!linputImage.isValid()) { + std::cerr << "Unable to open image: " << iname.getPath() << std::endl; + exit(1); } - if (inputImage.getChannelsCount() != 3) { + if (linputImage.getChannels() != 3) { std::cerr << "Input image must be RGB (3 channels)! This image has " - << inputImage.getChannelsCount() << " channels." << std::endl; - exit(0); + << linputImage.getChannels() << " channels." << std::endl; + exit(1); } + // Convert from LinearImage to the deprecated Image object which is used throughout cmgen. + std::unique_ptr buf(new uint8_t[ + linputImage.getWidth() * linputImage.getHeight() * sizeof(float3)]); + const size_t width = linputImage.getWidth(), height = linputImage.getHeight(); + const size_t bpp = sizeof(float) * 3, bpr = bpp * width; + memcpy(buf.get(), linputImage.getPixelRef(), height * bpr); + Image inputImage(std::move(buf), width, height, bpr, bpp); + CubemapUtils::clamp(inputImage); - size_t width = inputImage.getWidth(); - size_t height = inputImage.getHeight(); - if ((isPOT(width) && (width * 3 == height * 4)) || (isPOT(height) && (height * 3 == width * 4))) { // This is cross cubemap @@ -588,10 +596,8 @@ void sphericalHarmonics(const utils::Path& iname, const Cubemap& inputCubemap) { } if (g_sh_file == ShFile::SH_CROSS) { - std::ofstream outputStream(g_sh_filename, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, - ImageEncoder::chooseFormat(g_sh_filename.getName()), - image, g_compression, g_sh_filename); + saveImage(g_sh_filename, ImageEncoder::chooseFormat(g_sh_filename.getName()), + image, g_compression); } if (g_sh_file == ShFile::SH_TEXT) { std::ofstream outputStream(g_sh_filename, std::ios::trunc); @@ -609,9 +615,7 @@ void sphericalHarmonics(const utils::Path& iname, const Cubemap& inputCubemap) { std::string basename = iname.getNameWithoutExtension(); utils::Path filePath = outputDir + (basename + "_sh" + (g_sh_irradiance ? "_i" : "_r") + ".png"); - std::ofstream outputStream(filePath, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "", - filePath.getPath()); + saveImage(filePath, ImageEncoder::Format::PNG, image, ""); } { // save a file with the "other one" (irradiance or radiance) @@ -620,9 +624,7 @@ void sphericalHarmonics(const utils::Path& iname, const Cubemap& inputCubemap) { std::string basename = iname.getNameWithoutExtension(); utils::Path filePath = outputDir + (basename + "_sh" + (!g_sh_irradiance ? "_i" : "_r") + ".png"); - std::ofstream outputStream(filePath, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "", - filePath.getPath()); + saveImage(filePath, ImageEncoder::Format::PNG, image, ""); } } } @@ -677,10 +679,7 @@ void iblMipmapPrefilter(const utils::Path& iname, std::string ext = ImageEncoder::chooseExtension(debug_format); std::string basename = iname.getNameWithoutExtension(); utils::Path filePath = outputDir + (basename + "_is_m" + (std::to_string(level) + ext)); - - std::ofstream outputStream(filePath, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, debug_format, img, - g_compression, filePath.getPath()); + saveImage(filePath, debug_format, img, g_compression); } std::string ext = ImageEncoder::chooseExtension(g_format); @@ -688,9 +687,7 @@ void iblMipmapPrefilter(const utils::Path& iname, Cubemap::Face face = (Cubemap::Face)i; std::string filename = outputDir + ("is_m" + std::to_string(level) + "_" + CubemapUtils::getFaceName(face) + ext); - std::ofstream outputStream(filename, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, g_format, dst.getImageForFace(face), - g_compression, filename); + saveImage(filename, g_format, dst.getImageForFace(face), g_compression); } } } @@ -737,10 +734,7 @@ void iblRoughnessPrefilter(const utils::Path& iname, std::string ext = ImageEncoder::chooseExtension(debug_format); std::string basename = iname.getNameWithoutExtension(); utils::Path filePath = outputDir + (basename + "_roughness_m" + (std::to_string(level) + ext)); - - std::ofstream outputStream(filePath, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, debug_format, image, - g_compression, filePath.getPath()); + saveImage(filePath, debug_format, image, g_compression); } std::string ext = ImageEncoder::chooseExtension(g_format); @@ -748,9 +742,7 @@ void iblRoughnessPrefilter(const utils::Path& iname, Cubemap::Face face = (Cubemap::Face) j; std::string filename = outputDir + ("m" + std::to_string(level) + "_" + CubemapUtils::getFaceName(face) + ext); - std::ofstream outputStream(filename, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, g_format, dst.getImageForFace(face), - g_compression, filename); + saveImage(filename, g_format, dst.getImageForFace(face), g_compression); } } } @@ -804,9 +796,8 @@ void iblLutDfg(const utils::Path& filename, size_t size, bool multiscatter) { outputStream.flush(); outputStream.close(); } else { - std::ofstream outputStream(filename, std::ios::binary | std::ios::trunc); ImageEncoder::Format format = ImageEncoder::chooseFormat(filename.getName(), true); - ImageEncoder::encode(outputStream, format, image, g_compression, filename.getPath()); + saveImage(filename, format, image, g_compression); } } @@ -819,8 +810,23 @@ void extractCubemapFaces(const utils::Path& iname, const Cubemap& cm, const util for (size_t i=0 ; i<6 ; i++) { Cubemap::Face face = (Cubemap::Face)i; std::string filename(outputDir + (CubemapUtils::getFaceName(face) + ext)); - std::ofstream outputStream(filename, std::ios::binary | std::ios::trunc); - ImageEncoder::encode(outputStream, g_format, cm.getImageForFace(face), - g_compression, filename); + saveImage(filename, g_format, cm.getImageForFace(face), g_compression); } } + +static void saveImage(const std::string& path, ImageEncoder::Format format, const Image& image, + const std::string& compression) { + std::ofstream outputStream(path, std::ios::binary | std::ios::trunc); + LinearImage linearImage(image.getWidth(), image.getHeight(), 3); + + // Copy row by row since the image has padding. + assert(image.getBytesPerPixel() == 12); + const size_t w = image.getWidth(), h = image.getHeight(); + for (size_t row = 0; row < h; ++row) { + float* dst = linearImage.getPixelRef(0, row); + float const* src = static_cast(image.getPixelRef(0, row)); + memcpy(dst, src, w * 12); + } + + ImageEncoder::encode(outputStream, format, linearImage, compression, path); +} diff --git a/tools/cmgen/tests/test_cmgen.cpp b/tools/cmgen/tests/test_cmgen.cpp index 0afb9533a8..edbd8e1782 100644 --- a/tools/cmgen/tests/test_cmgen.cpp +++ b/tools/cmgen/tests/test_cmgen.cpp @@ -100,12 +100,12 @@ static void processEnvMap(string inputPath, string resultPath, string goldenPath std::cout << "Reading result image from " << resultPath << std::endl; checkFileExistence(resultPath); std::ifstream resultStream(resultPath.c_str(), std::ios::binary); - Image resultImage = ImageDecoder::decode(resultStream, resultPath); + LinearImage resultImage = ImageDecoder::decode(resultStream, resultPath); ASSERT_EQ(resultImage.isValid(), true); - ASSERT_EQ(resultImage.getChannelsCount(), 4); + ASSERT_EQ(resultImage.getChannels(), 4); LinearImage resultLImage = toLinearFromRGBM( - static_cast(resultImage.getData()), - (uint32_t) resultImage.getWidth(), (uint32_t) resultImage.getHeight()); + reinterpret_cast(resultImage.getPixelRef()), + resultImage.getWidth(), resultImage.getHeight()); std::cout << "Golden image is at " << goldenPath << std::endl; updateOrCompare(resultLImage, goldenPath, g_comparisonMode, 0.01f); diff --git a/tools/normal-blending/src/main.cpp b/tools/normal-blending/src/main.cpp index 99704a66f0..2df8416a52 100644 --- a/tools/normal-blending/src/main.cpp +++ b/tools/normal-blending/src/main.cpp @@ -34,7 +34,7 @@ static image::ImageEncoder::Format g_format = image::ImageEncoder::Format::PNG; static bool g_formatSpecified = false; static std::string g_compression = ""; -static void blend(const Image& normal, const Image& detail, Image& output); +static void blend(const LinearImage& normal, const LinearImage& detail, LinearImage output); static void printUsage(const char* name) { std::string execName(utils::Path(name).getName()); @@ -159,7 +159,7 @@ int main(int argc, char* argv[]) { // make sure we load the normal maps as linear data std::ifstream inputStream(normalMap, std::ios::binary); - Image normalImage = ImageDecoder::decode(inputStream, normalMap, + LinearImage normalImage = ImageDecoder::decode(inputStream, normalMap, ImageDecoder::ColorSpace::LINEAR); if (!normalImage.isValid()) { std::cerr << "The input normal map is invalid: " << normalMap << std::endl; @@ -168,7 +168,7 @@ int main(int argc, char* argv[]) { inputStream.close(); inputStream.open(detailMap, std::ios::binary); - Image detailImage = ImageDecoder::decode(inputStream, detailMap, + LinearImage detailImage = ImageDecoder::decode(inputStream, detailMap, ImageDecoder::ColorSpace::LINEAR); if (!detailImage.isValid()) { std::cerr << "The detail normal map is invalid: " << detailMap << std::endl; @@ -189,9 +189,7 @@ int main(int argc, char* argv[]) { size_t width = normalImage.getWidth(); size_t height = normalImage.getHeight(); - - std::unique_ptr buffer(new uint8_t[width * height * sizeof(float3)]); - Image image(std::move(buffer), width, height, width * sizeof(float3), sizeof(float3)); + LinearImage image(width, height, 3); blend(normalImage, detailImage, image); @@ -214,14 +212,14 @@ int main(int argc, char* argv[]) { } } -void blend(const Image& normal, const Image& detail, Image& output) { +void blend(const LinearImage& normal, const LinearImage& detail, LinearImage output) { const size_t width = output.getWidth(); const size_t height = output.getHeight(); for (size_t y = 0; y < height; y++) { - float3* normalRow = static_cast(normal.getPixelRef(0, y)); - float3* detailRow = static_cast(detail.getPixelRef(0, y)); - float3* outputRow = static_cast(output.getPixelRef(0, y)); + float3 const* normalRow = reinterpret_cast(normal.getPixelRef(0, y)); + float3 const* detailRow = reinterpret_cast(detail.getPixelRef(0, y)); + float3* outputRow = reinterpret_cast(output.getPixelRef(0, y)); for (size_t x = 0; x < width; x++, normalRow++, detailRow++, outputRow++) { // Reoriented Normal Mapping diff --git a/tools/roughness-prefilter/src/main.cpp b/tools/roughness-prefilter/src/main.cpp index 3bd3af53a1..9b1f5adc7d 100644 --- a/tools/roughness-prefilter/src/main.cpp +++ b/tools/roughness-prefilter/src/main.cpp @@ -22,6 +22,8 @@ #include +#include + #include #include @@ -176,7 +178,7 @@ inline bool isPOT(size_t x) { } float solveVMF(const float2& pos, const size_t sampleCount, const float roughness, - const Image& normal) { + const LinearImage& normal) { float3 averageNormal(0.0f); float2 topLeft(-float(sampleCount) / 2.0f + 0.5f); @@ -185,7 +187,7 @@ float solveVMF(const float2& pos, const size_t sampleCount, const float roughnes for (size_t x = 0; x < sampleCount; x++) { float2 offset(topLeft + float2(x, y)); float2 samplePos(floor(pos + offset) + 0.5f); - float3 sampleNormal = *static_cast( + float3 sampleNormal = *reinterpret_cast( normal.getPixelRef(size_t(samplePos.x), size_t(samplePos.y))); sampleNormal = sampleNormal * 2.0f - 1.0f; @@ -209,13 +211,13 @@ float solveVMF(const float2& pos, const size_t sampleCount, const float roughnes return std::sqrt(roughness * roughness + (2.0f / kappa)); } -void prefilter(const Image& normal, const size_t mipLevel, Image& output) { +void prefilter(const LinearImage& normal, const size_t mipLevel, LinearImage& output) { const size_t width = output.getWidth(); const size_t height = output.getHeight(); const size_t sampleCount = 1u << mipLevel; for (size_t y = 0; y < height; y++) { - auto* outputRow = static_cast(output.getPixelRef(0, y)); + auto* outputRow = reinterpret_cast(output.getPixelRef(0, y)); for (size_t x = 0; x < width; x++, outputRow++) { const float2 uv = (float2(x, y) + 0.5f) / float2(width, height); const float2 pos = uv * normal.getWidth(); @@ -226,15 +228,16 @@ void prefilter(const Image& normal, const size_t mipLevel, Image& output) { } template -void prefilter(const Image& normal, const Image& roughness, const size_t mipLevel, Image& output) { +void prefilter(const LinearImage& normal, const LinearImage& roughness, const size_t mipLevel, + LinearImage& output) { const size_t width = output.getWidth(); const size_t height = output.getHeight(); const size_t sampleCount = 1u << mipLevel; for (size_t y = 0; y < height; y++) { - auto* outputRow = static_cast(output.getPixelRef(0, y)); + auto* outputRow = reinterpret_cast(output.getPixelRef(0, y)); for (size_t x = 0; x < width; x++, outputRow++) { - const float3* data = static_cast(roughness.getPixelRef(x, y)); + const float3* data = reinterpret_cast(roughness.getPixelRef(x, y)); if (FIRST_MIP) { *outputRow = *data; } else { @@ -265,7 +268,7 @@ int main(int argc, char* argv[]) { // make sure we load the normal maps as linear data std::ifstream inputStream(normalMap, std::ios::binary); - Image normalImage = ImageDecoder::decode(inputStream, normalMap, + LinearImage normalImage = ImageDecoder::decode(inputStream, normalMap, ImageDecoder::ColorSpace::LINEAR); inputStream.close(); @@ -281,8 +284,8 @@ int main(int argc, char* argv[]) { exit(1); } - Image roughnessImage; - std::vector mipImages; + LinearImage roughnessImage; + std::vector mipImages; bool hasRoughnessMap = false; if (!g_roughnessMap.isEmpty()) { @@ -330,13 +333,13 @@ int main(int argc, char* argv[]) { const size_t height = hasRoughnessMap ? roughnessImage.getHeight() : normalImage.getHeight(); const size_t mipLevels = size_t(std::log2f(width)) + 1; - size_t channels = 3; + bool exportGrayscale = false; switch (g_format) { case ImageEncoder::Format::DDS: case ImageEncoder::Format::DDS_LINEAR: case ImageEncoder::Format::PNG: case ImageEncoder::Format::PNG_LINEAR: - channels = 1; + exportGrayscale = true; break; default: break; @@ -344,23 +347,21 @@ int main(int argc, char* argv[]) { if (hasRoughnessMap) { mipImages.push_back(std::move(roughnessImage)); - Image* prevMip = &mipImages.at(0); + LinearImage* prevMip = &mipImages.at(0); for (size_t i = 1; i <= mipLevels; i++) { const size_t w = width >> i; const size_t h = height >> i; - const size_t size = w * h * sizeof(float3); - std::unique_ptr buffer(new uint8_t[size]); - Image image(std::move(buffer), w, h, w * sizeof(float3), sizeof(float3), channels); + LinearImage image(w, h, 3); for (size_t y = 0; y < h; y++) { - auto* dst = static_cast(image.getPixelRef(0, y)); + auto* dst = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < w; x++, dst++) { - float3 aa = *static_cast(prevMip->getPixelRef(x * 2, y * 2)); - float3 ba = *static_cast(prevMip->getPixelRef(x * 2 + 1, y * 2)); - float3 ab = *static_cast(prevMip->getPixelRef(x * 2, y * 2 + 1)); - float3 bb = *static_cast(prevMip->getPixelRef(x * 2 + 1, y * 2 + 1)); + float3 aa = *reinterpret_cast(prevMip->getPixelRef(x * 2, y * 2)); + float3 ba = *reinterpret_cast(prevMip->getPixelRef(x * 2 + 1, y * 2)); + float3 ab = *reinterpret_cast(prevMip->getPixelRef(x * 2, y * 2 + 1)); + float3 bb = *reinterpret_cast(prevMip->getPixelRef(x * 2 + 1, y * 2 + 1)); *dst = (aa + ba + ab + bb) / 4.0f; } } @@ -376,23 +377,22 @@ int main(int argc, char* argv[]) { JobSystem::Job* parent = js.createJob(); for (size_t i = 0; i < mipLevels; i++) { JobSystem::Job* mip = jobs::createJob(js, parent, - [&normalImage, &mipImages, outputMap, i, width, height, channels, hasRoughnessMap]() { + [&normalImage, &mipImages, outputMap, i, width, height, exportGrayscale, hasRoughnessMap]() { const size_t w = width >> i; const size_t h = height >> i; - const size_t size = w * h * sizeof(float3); - std::unique_ptr buffer(new uint8_t[size]); - Image image(std::move(buffer), w, h, w * sizeof(float3), sizeof(float3), channels); + LinearImage image(w, h, 3); if (i == 0) { if (hasRoughnessMap) { - if (mipImages.at(0).getBytesPerRow() == image.getBytesPerRow()) { - memcpy(image.getData(), mipImages.at(0).getData(), size); + if (mipImages.at(0).getWidth() == image.getWidth()) { + const size_t size = image.getWidth() * image.getHeight() * 12; + memcpy(image.getPixelRef(), mipImages.at(0).getPixelRef(), size); } else { prefilter(normalImage, mipImages.at(0), 0, image); } } else { - std::fill_n(static_cast(image.getData()), w * h, float3(g_roughness)); + std::fill_n(reinterpret_cast(image.getPixelRef()), w * h, float3(g_roughness)); } } else { if (hasRoughnessMap) { @@ -410,13 +410,16 @@ int main(int argc, char* argv[]) { std::ofstream outputStream(out, std::ios::binary | std::ios::trunc); if (!outputStream.good()) { std::cerr << "The output file cannot be opened: " << out << std::endl; - } else { - ImageEncoder::encode(outputStream, g_format, image, g_compression, out.getPath()); - outputStream.close(); - if (!outputStream.good()) { - std::cerr << "An error occurred while writing the output file: " << out << - std::endl; - } + return; + } + if (exportGrayscale) { + image = extractChannel(image, 0); + } + ImageEncoder::encode(outputStream, g_format, image, g_compression, out.getPath()); + outputStream.close(); + if (!outputStream.good()) { + std::cerr << "An error occurred while writing the output file: " << out << + std::endl; } }); js.run(mip); diff --git a/tools/skygen/src/main.cpp b/tools/skygen/src/main.cpp index 5326a42a7d..c13c39170b 100644 --- a/tools/skygen/src/main.cpp +++ b/tools/skygen/src/main.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -83,7 +84,7 @@ static float angleBetween(float thetav, float phiv, float theta, float phi) { return acosf(cosGamma); } -static void generateSky(const Image& image) { +static void generateSky(LinearImage image) { printf("Sky parameters\n"); printf(" Elevation: %.2f°\n", g_elevation * 180.0 * M_1_PI); printf(" Azimuth: %.2f°\n", g_azimuth * 180.0 * M_1_PI); @@ -121,7 +122,7 @@ static void generateSky(const Image& image) { size_t y0 = size_t(d); for (size_t y = y0; y < y0 + c; y++) { - float3* UTILS_RESTRICT data = static_cast(image.getPixelRef(0, y)); + float3* UTILS_RESTRICT data = reinterpret_cast(image.getPixelRef(0, y)); float v = (y + 0.5f) / h; float theta = float(M_PI * v); @@ -193,7 +194,7 @@ static void generateSky(const Image& image) { const size_t h = image.getHeight(); for (size_t y = 0; y < h; y++) { - float3* UTILS_RESTRICT data = static_cast(image.getPixelRef(0, y)); + float3* UTILS_RESTRICT data = reinterpret_cast(image.getPixelRef(0, y)); for (size_t x = 0; x < w; x++, data++) { *data *= hdrScale; if (g_tonemap) { @@ -389,10 +390,9 @@ int main(int argc, char* argv[]) { const uint32_t height = std::max(1u, g_outputWidth >> 1); // allocate map - const size_t size = width * height * sizeof(float3); - std::unique_ptr buffer(new uint8_t[size]); - Image image(std::move(buffer), width, height, width * sizeof(float3), sizeof(float3), 3); + LinearImage image(width, height, 3); + // render the sky and sun disk generateSky(image); // write the environment map to disk