TEST(EXR, Randoms) { int width = 1024; int height = 1024; RNG rng; float *buf = new float[4 * width * height]; for (int i = 0; i < 4 * width * height; ++i) { buf[i] = -20 + 20. * rng.UniformFloat(); } EXRImage image; image.num_channels = 4; const char *channels[] = { "B", "G", "R", "A" }; image.channel_names = channels; unsigned char *images[] = { (unsigned char *)buf, (unsigned char *)(buf + width * height), (unsigned char *)(buf + 2 * width * height), (unsigned char *)(buf + 3 * width * height) }; image.images = images; int pixel_types[] = { TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF }; image.pixel_types = pixel_types; image.width = width; image.height = height; const char *err = nullptr; EXPECT_EQ(0, SaveMultiChannelEXRToFile(&image, "test.exr", &err)) << err; EXRImage readImage; EXPECT_EQ(0, LoadMultiChannelEXRFromFile(&readImage, "test.exr", &err)) << err; CompareImages(image, readImage, true); }
static void WriteImageEXR(const std::string &name, const Float *pixels, int xRes, int yRes, int totalXRes, int totalYRes, int xOffset, int yOffset) { EXRImage image; image.num_channels = 3; const char *channel_names[] = {"B", "G", "R"}; image.channel_names = channel_names; int pixel_types[3] = {TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF}; image.pixel_types = pixel_types; image.width = xRes; image.height = yRes; image.images = new unsigned char *[3]; float *bgr = new float[3 * xRes * yRes]; image.images[0] = (unsigned char *)bgr; image.images[1] = (unsigned char *)(bgr + 1 * xRes * yRes); image.images[2] = (unsigned char *)(bgr + 2 * xRes * yRes); int offset = 0; for (int y = 0; y < yRes; ++y) { for (int x = 0; x < xRes; ++x, ++offset) { ((float *)image.images[0])[offset] = pixels[3 * offset + 2]; // B ((float *)image.images[1])[offset] = pixels[3 * offset + 1]; // G ((float *)image.images[2])[offset] = pixels[3 * offset + 0]; // R } } const char *err; if (SaveMultiChannelEXRToFile(&image, name.c_str(), &err)) { Error("Error writing \"%s\": %s", name.c_str(), err); } delete[] bgr; delete[] image.images; }
TEST(EXR, BasicRoundTrip) { int width = 289; int height = 1 + 65536 / width; float *buf = new float[width * height]; for (int i = 0; i < 65536; ++i) { FP16 half; half.u = i; buf[i] = half_to_float_full(half).f; } for (int i = 65536; i < width * height; ++i) buf[i] = 0; EXRImage image; image.num_channels = 1; const char *channels[] = { "R" }; image.channel_names = channels; unsigned char *images[] = { (unsigned char *)buf }; image.images = images; int pixel_types[] = { TINYEXR_PIXELTYPE_HALF }; image.pixel_types = pixel_types; image.width = width; image.height = height; const char *err = nullptr; EXPECT_EQ(0, SaveMultiChannelEXRToFile(&image, "test.exr", &err)) << err; EXRImage readImage; EXPECT_EQ(0, LoadMultiChannelEXRFromFile(&readImage, "test.exr", &err)) << err; CompareImages(image, readImage, false); }
static void WriteImageEXR(const std::string &name, const Float *pixels, int xRes, int yRes, int totalXRes, int totalYRes, int xOffset, int yOffset) { EXRImage image; InitEXRImage(&image); image.num_channels = 3; const char *channel_names[] = {"B", "G", "R"}; image.channel_names = channel_names; int pixel_types[3] = {TINYEXR_PIXELTYPE_FLOAT, TINYEXR_PIXELTYPE_FLOAT, TINYEXR_PIXELTYPE_FLOAT}; image.pixel_types = pixel_types; int requestedPixelTypes[] = {TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF, TINYEXR_PIXELTYPE_HALF}; image.requested_pixel_types = requestedPixelTypes; image.width = xRes; image.height = yRes; image.images = new unsigned char *[3]; float *bgr = new float[3 * xRes * yRes]; image.images[0] = (unsigned char *)bgr; image.images[1] = (unsigned char *)(bgr + 1 * xRes * yRes); image.images[2] = (unsigned char *)(bgr + 2 * xRes * yRes); image.pixel_aspect_ratio = 1.; // Work around bug in OpenEXR 1.1. // See https://github.com/mmp/pbrt-v3/issues/43. image.compression = (xRes < 64 || yRes < 64) ? TINYEXR_COMPRESSIONTYPE_NONE : TINYEXR_COMPRESSIONTYPE_ZIP; int offset = 0; for (int y = 0; y < yRes; ++y) { for (int x = 0; x < xRes; ++x, ++offset) { ((float *)image.images[0])[offset] = pixels[3 * offset + 2]; // B ((float *)image.images[1])[offset] = pixels[3 * offset + 1]; // G ((float *)image.images[2])[offset] = pixels[3 * offset + 0]; // R } } const char *err; if (SaveMultiChannelEXRToFile(&image, name.c_str(), &err)) { Error("Error writing \"%s\": %s", name.c_str(), err); } delete[] bgr; delete[] image.images; }
void ImageTargetFileTinyExr::finalize() { unique_ptr<EXRImage> exrImage( new EXRImage ); InitEXRImage( exrImage.get() ); // we intentionally do not call FreeEXRImage on this const char *channelNames[4]; // turn interleaved data into a series of planar channels std::vector<Channel32f> channels; float *srcData = mData.data(); for( int c = 0; c < mNumComponents; ++c ) { channels.emplace_back( getWidth(), getHeight() ); Channel32f srcChannel( getWidth(), getHeight(), mNumComponents * getWidth() * sizeof(float), mNumComponents, srcData + c ); channels.back().copyFrom( srcChannel, srcChannel.getBounds() ); } exrImage->num_channels = mNumComponents; exrImage->width = mWidth; exrImage->height = mHeight; float* imagePtr[4]; int pixelTypes[4], requested_pixel_types[4]; for( int i = 0; i < exrImage->num_channels; i++ ) { pixelTypes[i] = TINYEXR_PIXELTYPE_FLOAT; requested_pixel_types[i] = pixelTypes[i]; channelNames[i] = mChannelNames[i].c_str(); imagePtr[i] = channels[i].getData(); } exrImage->channel_names = channelNames; exrImage->images = (unsigned char **)imagePtr; exrImage->pixel_types = pixelTypes; exrImage->requested_pixel_types = requested_pixel_types; const char *error; int status = SaveMultiChannelEXRToFile( exrImage.get(), mFilePath.string().c_str(), &error ); if( status != 0 ) throw ImageIoExceptionFailedWriteTinyExr( string( "TinyExr: failed to write. Error: " ) + error ); }