diff --git a/build/common/test_list.txt b/build/common/test_list.txt index 419ebcb6de..80c50761a1 100644 --- a/build/common/test_list.txt +++ b/build/common/test_list.txt @@ -4,4 +4,4 @@ libs/math/test_math libs/image/test_image compare libs/image/tests/reference/ libs/utils/test_utils tools/matc/test_matc -tools/cmgen/test_cmgen compare \ No newline at end of file +tools/cmgen/test_cmgen compare diff --git a/libs/image/include/image/ColorTransform.h b/libs/image/include/image/ColorTransform.h index af066f5c33..6b3eab4f10 100644 --- a/libs/image/include/image/ColorTransform.h +++ b/libs/image/include/image/ColorTransform.h @@ -320,7 +320,6 @@ static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, return result; } - // Constructs a 4-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. @@ -330,6 +329,18 @@ static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, return toLinearWithAlpha(w, h, bpr, src.get(), proc, transform); } +// Constructs a 3-channel LinearImage from RGBM data. +inline LinearImage toLinearFromRGBM(math::float4 const* src, uint32_t w, uint32_t h) { + LinearImage result(w, h, 3); + math::float3* dst = reinterpret_cast(result.getPixelRef()); + for (uint32_t row = 0; row < h; ++row) { + for (uint32_t col = 0; col < w; ++col, ++src, ++dst) { + *dst = RGBMtoLinear(*src); + } + } + return result; +} + } #endif // IMAGE_COLORTRANSFORM_H_ diff --git a/libs/imageio/src/ImageDiffer.cpp b/libs/imageio/src/ImageDiffer.cpp index 21f3ba26f4..22e1a897f5 100644 --- a/libs/imageio/src/ImageDiffer.cpp +++ b/libs/imageio/src/ImageDiffer.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -35,12 +36,17 @@ 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); - auto format = ImageEncoder::Format::PNG_LINEAR; 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) { + format = ImageEncoder::Format::RGBM; + } + if (nchan != 1) { memcpy(data.get(), limgResult.getPixelRef(), nbytes); Image im(std::move(data), width, height, bpr, bpp, nchan); @@ -60,8 +66,18 @@ void updateOrCompare(LinearImage limgResult, const utils::Path& fnameGolden, 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(width, height, nchan); - memcpy(limgGolden.getPixelRef(), imgGolden.getData(), width * height * sizeof(float) * nchan); + + // Convert 4-channel RGBM into proper RGB. + LinearImage limgGolden; + if (fnameGolden.getExtension() == "rgbm" && nchan == 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); + } // Expand the result image from L to RGB. if (limgResult.getChannels() == 1) { diff --git a/samples/app/IBL.cpp b/samples/app/IBL.cpp index 51f13ec2cc..dd26fb1ec7 100644 --- a/samples/app/IBL.cpp +++ b/samples/app/IBL.cpp @@ -100,7 +100,7 @@ bool IBL::loadCubemapLevel(filament::Texture** texture, const utils::Path& path, } stbi_info(facePath.getAbsolutePath().c_str(), &w, &h, nullptr); if (w != h) { - std::cerr << "with != height" << std::endl; + std::cerr << "width != height" << std::endl; return false; } diff --git a/tools/cmgen/tests/Footballfield/Footballfield.png b/tools/cmgen/tests/Footballfield/Footballfield.png new file mode 100644 index 0000000000..a86fb05436 Binary files /dev/null and b/tools/cmgen/tests/Footballfield/Footballfield.png differ diff --git a/tools/cmgen/tests/Footballfield/Footballfield.txt b/tools/cmgen/tests/Footballfield/Footballfield.txt new file mode 100644 index 0000000000..d9bd514116 --- /dev/null +++ b/tools/cmgen/tests/Footballfield/Footballfield.txt @@ -0,0 +1,13 @@ +Author +====== + +This is the work of Emil Persson, aka Humus. +http://www.humus.name + + + +License +======= + +This work is licensed under a Creative Commons Attribution 3.0 Unported License. +http://creativecommons.org/licenses/by/3.0/ diff --git a/tools/cmgen/tests/Footballfield/m3_nx.rgbm b/tools/cmgen/tests/Footballfield/m3_nx.rgbm new file mode 100644 index 0000000000..2eeda9d6d9 Binary files /dev/null and b/tools/cmgen/tests/Footballfield/m3_nx.rgbm differ diff --git a/tools/cmgen/tests/test_cmgen.cpp b/tools/cmgen/tests/test_cmgen.cpp index b6ef136ad2..01984c7f17 100644 --- a/tools/cmgen/tests/test_cmgen.cpp +++ b/tools/cmgen/tests/test_cmgen.cpp @@ -43,30 +43,56 @@ class CmgenTest : public testing::Test {}; static ComparisonMode g_comparisonMode; -TEST_F(CmgenTest, WhiteFurnace) { // NOLINT +static void checkFileExistence(string path) { + std::ifstream s(path.c_str(), std::ios::binary); + if (!s) { + std::cerr << "ERROR file does not exist: " << path << std::endl; + exit(1); + } +} - Path comparisonPath = "../../samples/envs/white_furnace"; - Path inputPath = "../../assets/environments/white_furnace/white_furnace.exr"; +// This spawns cmgen, telling it to process the environment map located at "inputPath". It creates +// an output folder in the same location as the test executable, which lets us avoid polluting our +// local source tree with output files. The given "resultPath" points the specific newly-generated +// output image that we'd like to compare or update, and the "goldenPath" points to the golden image +// (which lives in our source tree). +static void spawnTool(string inputPath, string resultPath, string goldenPath) { + const string executableFolder = Path::getCurrentExecutable().getParent(); + inputPath = Path::getCurrentDirectory() + inputPath; + resultPath = Path::getCurrentExecutable().getParent() + resultPath; + goldenPath = Path::getCurrentDirectory() + goldenPath; - const string resultImageFilename = "white_furnace/nx.rgbm"; - const string goldenImageFilename = comparisonPath + "nx.rgbm"; - - std::cerr << "Running cmgen on " << inputPath << "...\n"; - string cmdline = string("tools/cmgen/cmgen -x . ") + inputPath.c_str(); + std::cout << "Running cmgen on " << inputPath << std::endl; + checkFileExistence(inputPath); + string cmdline = executableFolder + "cmgen -x " + executableFolder + " " + inputPath; ASSERT_EQ(std::system(cmdline.c_str()), 0); - std::cerr << "Reading result image from " << resultImageFilename << "...\n"; - std::ifstream resultStream(resultImageFilename.c_str(), std::ios::binary); - Image resultImage = ImageDecoder::decode(resultStream, resultImageFilename); + 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); ASSERT_EQ(resultImage.isValid(), true); ASSERT_EQ(resultImage.getChannelsCount(), 4); + LinearImage resultLImage = toLinearFromRGBM( + static_cast(resultImage.getData()), + resultImage.getWidth(), resultImage.getHeight()); - std::cerr << "Golden image is at " << goldenImageFilename << "\n"; - LinearImage resultLImage(resultImage.getWidth(), resultImage.getHeight(), 4); - memcpy(resultLImage.getPixelRef(), resultImage.getData(), - resultImage.getWidth() * resultImage.getHeight() * sizeof(float) * 4); + std::cout << "Golden image is at " << goldenPath << std::endl; + updateOrCompare(resultLImage, goldenPath, g_comparisonMode, 0.01f); +} - updateOrCompare(resultLImage, goldenImageFilename, g_comparisonMode, 0.01f); +TEST_F(CmgenTest, HdrLatLong) { // NOLINT + const string inputPath = "assets/environments/white_furnace/white_furnace.exr"; + const string resultPath = "white_furnace/nx.rgbm"; + const string goldenPath = "samples/envs/white_furnace/nx.rgbm"; + spawnTool(inputPath, resultPath, goldenPath); +} + +TEST_F(CmgenTest, LdrCrossCube) { // NOLINT + const string inputPath = "tools/cmgen/tests/Footballfield/Footballfield.png"; + const string resultPath = "Footballfield/m3_nx.rgbm"; + const string goldenPath = "tools/cmgen/tests/Footballfield/m3_nx.rgbm"; + spawnTool(inputPath, resultPath, goldenPath); } int main(int argc, char** argv) {