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 ); }
void ImageViewer::RefreshFile() { FILE *f = NULL; for(int attempt = 0; attempt < 10 && f == NULL; attempt++) { f = FileIO::fopen(m_Filename.c_str(), "rb"); if(f) break; Threading::Sleep(40); } if(!f) { RDCERR("Couldn't open %s! Exclusive lock elsewhere?", m_Filename.c_str()); return; } FetchTexture texDetails; ResourceFormat rgba8_unorm; rgba8_unorm.compByteWidth = 1; rgba8_unorm.compCount = 4; rgba8_unorm.compType = eCompType_UNorm; rgba8_unorm.special = false; ResourceFormat rgba32_float = rgba8_unorm; rgba32_float.compByteWidth = 4; rgba32_float.compType = eCompType_Float; texDetails.creationFlags = eTextureCreate_SwapBuffer | eTextureCreate_RTV; texDetails.cubemap = false; texDetails.customName = true; texDetails.name = m_Filename; texDetails.ID = m_TextureID; texDetails.byteSize = 0; texDetails.msQual = 0; texDetails.msSamp = 1; texDetails.format = rgba8_unorm; // reasonable defaults texDetails.numSubresources = 1; texDetails.dimension = 2; texDetails.arraysize = 1; texDetails.width = 1; texDetails.height = 1; texDetails.depth = 1; texDetails.mips = 1; byte *data = NULL; size_t datasize = 0; bool dds = false; if(is_exr_file(f)) { texDetails.format = rgba32_float; FileIO::fseek64(f, 0, SEEK_END); uint64_t size = FileIO::ftell64(f); FileIO::fseek64(f, 0, SEEK_SET); std::vector<byte> buffer; buffer.resize((size_t)size); FileIO::fread(&buffer[0], 1, buffer.size(), f); FileIO::fclose(f); EXRImage exrImage; InitEXRImage(&exrImage); const char *err = NULL; int ret = ParseMultiChannelEXRHeaderFromMemory(&exrImage, &buffer[0], &err); if(ret != 0) { RDCERR( "EXR file detected, but couldn't load with ParseMultiChannelEXRHeaderFromMemory %d: '%s'", ret, err); return; } texDetails.width = exrImage.width; texDetails.height = exrImage.height; datasize = texDetails.width * texDetails.height * 4 * sizeof(float); data = (byte *)malloc(datasize); for(int i = 0; i < exrImage.num_channels; i++) exrImage.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; ret = LoadMultiChannelEXRFromMemory(&exrImage, &buffer[0], &err); int channels[4] = {-1, -1, -1, -1}; for(int i = 0; i < exrImage.num_channels; i++) { switch(exrImage.channel_names[i][0]) { case 'R': channels[0] = i; break; case 'G': channels[1] = i; break; case 'B': channels[2] = i; break; case 'A': channels[3] = i; break; } } float *rgba = (float *)data; float **src = (float **)exrImage.images; for(uint32_t i = 0; i < texDetails.width * texDetails.height; i++) { for(int c = 0; c < 4; c++) { if(channels[c] >= 0) rgba[i * 4 + c] = src[channels[c]][i]; else if(c < 3) // RGB channels default to 0 rgba[i * 4 + c] = 0.0f; else // alpha defaults to 1 rgba[i * 4 + c] = 1.0f; } } FreeEXRImage(&exrImage); // shouldn't get here but let's be safe if(ret != 0) { free(data); RDCERR("EXR file detected, but couldn't load with LoadEXRFromMemory %d: '%s'", ret, err); return; } } else if(stbi_is_hdr_from_file(f)) { texDetails.format = rgba32_float; FileIO::fseek64(f, 0, SEEK_SET); int ignore = 0; data = (byte *)stbi_loadf_from_file(f, (int *)&texDetails.width, (int *)&texDetails.height, &ignore, 4); datasize = texDetails.width * texDetails.height * 4 * sizeof(float); } else if(is_dds_file(f)) { dds = true; } else { int ignore = 0; int ret = stbi_info_from_file(f, (int *)&texDetails.width, (int *)&texDetails.height, &ignore); // just in case (we shouldn't have come in here if this weren't true), make sure // the format is supported if(ret == 0 || texDetails.width == 0 || texDetails.width == ~0U || texDetails.height == 0 || texDetails.height == ~0U) { FileIO::fclose(f); return; } texDetails.format = rgba8_unorm; data = stbi_load_from_file(f, (int *)&texDetails.width, (int *)&texDetails.height, &ignore, 4); datasize = texDetails.width * texDetails.height * 4 * sizeof(byte); } // if we don't have data at this point (and we're not a dds file) then the // file was corrupted and we failed to load it if(!dds && data == NULL) { FileIO::fclose(f); return; } m_FrameRecord.frameInfo.initDataSize = 0; m_FrameRecord.frameInfo.persistentSize = 0; m_FrameRecord.frameInfo.fileSize = datasize; dds_data read_data = {0}; if(dds) { FileIO::fseek64(f, 0, SEEK_SET); read_data = load_dds_from_file(f); if(read_data.subdata == NULL) { FileIO::fclose(f); return; } texDetails.cubemap = read_data.cubemap; texDetails.arraysize = read_data.slices; texDetails.width = read_data.width; texDetails.height = read_data.height; texDetails.depth = read_data.depth; texDetails.mips = read_data.mips; texDetails.numSubresources = texDetails.arraysize * texDetails.mips; texDetails.format = read_data.format; texDetails.dimension = 1; if(texDetails.width > 1) texDetails.dimension = 2; if(texDetails.depth > 1) texDetails.dimension = 3; m_FrameRecord.frameInfo.fileSize = 0; for(uint32_t i = 0; i < texDetails.numSubresources; i++) m_FrameRecord.frameInfo.fileSize += read_data.subsizes[i]; } // recreate proxy texture if necessary. // we rewrite the texture IDs so that the // outside world doesn't need to know about this // (we only ever have one texture in the image // viewer so we can just set all texture IDs // used to that). if(m_TextureID != ResourceId()) { if(m_TexDetails.width != texDetails.width || m_TexDetails.height != texDetails.height || m_TexDetails.depth != texDetails.depth || m_TexDetails.cubemap != texDetails.cubemap || m_TexDetails.mips != texDetails.mips || m_TexDetails.arraysize != texDetails.arraysize || m_TexDetails.width != texDetails.width || m_TexDetails.format != texDetails.format) { m_TextureID = ResourceId(); } } if(m_TextureID == ResourceId()) m_TextureID = m_Proxy->CreateProxyTexture(texDetails); if(!dds) { m_Proxy->SetProxyTextureData(m_TextureID, 0, 0, data, datasize); free(data); } else { for(uint32_t i = 0; i < texDetails.numSubresources; i++) { m_Proxy->SetProxyTextureData(m_TextureID, i / texDetails.mips, i % texDetails.mips, read_data.subdata[i], (size_t)read_data.subsizes[i]); delete[] read_data.subdata[i]; } delete[] read_data.subdata; delete[] read_data.subsizes; } FileIO::fclose(f); }
ReplayCreateStatus IMG_CreateReplayDevice(const char *logfile, IReplayDriver **driver) { FILE *f = FileIO::fopen(logfile, "rb"); if(!f) return eReplayCreate_FileIOFailed; // make sure the file is a type we recognise before going further if(is_exr_file(f)) { const char *err = NULL; FileIO::fseek64(f, 0, SEEK_END); uint64_t size = FileIO::ftell64(f); FileIO::fseek64(f, 0, SEEK_SET); std::vector<byte> buffer; buffer.resize((size_t)size); FileIO::fread(&buffer[0], 1, buffer.size(), f); EXRImage exrImage; InitEXRImage(&exrImage); int ret = ParseMultiChannelEXRHeaderFromMemory(&exrImage, &buffer[0], &err); FreeEXRImage(&exrImage); // could be an unsupported form of EXR, like deep image or other if(ret != 0) { FileIO::fclose(f); RDCERR( "EXR file detected, but couldn't load with ParseMultiChannelEXRHeaderFromMemory %d: '%s'", ret, err); return eReplayCreate_APIUnsupported; } } else if(stbi_is_hdr_from_file(f)) { FileIO::fseek64(f, 0, SEEK_SET); int ignore = 0; float *data = stbi_loadf_from_file(f, &ignore, &ignore, &ignore, 4); if(!data) { FileIO::fclose(f); RDCERR("HDR file recognised, but couldn't load with stbi_loadf_from_file"); return eReplayCreate_FileCorrupted; } free(data); } else if(is_dds_file(f)) { FileIO::fseek64(f, 0, SEEK_SET); dds_data read_data = load_dds_from_file(f); if(read_data.subdata == NULL) { FileIO::fclose(f); RDCERR("DDS file recognised, but couldn't load"); return eReplayCreate_FileCorrupted; } for(int i = 0; i < read_data.slices * read_data.mips; i++) delete[] read_data.subdata[i]; delete[] read_data.subdata; delete[] read_data.subsizes; } else { int width = 0, height = 0; int ignore = 0; int ret = stbi_info_from_file(f, &width, &height, &ignore); // just in case (we shouldn't have come in here if this weren't true), make sure // the format is supported if(ret == 0 || width == 0 || width == ~0U || height == 0 || height == ~0U) { FileIO::fclose(f); return eReplayCreate_APIUnsupported; } byte *data = stbi_load_from_file(f, &ignore, &ignore, &ignore, 4); if(!data) { FileIO::fclose(f); RDCERR("File recognised, but couldn't load with stbi_load_from_file"); return eReplayCreate_FileCorrupted; } free(data); } FileIO::fclose(f); IReplayDriver *proxy = NULL; auto status = RenderDoc::Inst().CreateReplayDriver(RDC_Unknown, NULL, &proxy); if(status != eReplayCreate_Success || !proxy) { if(proxy) proxy->Shutdown(); return status; } *driver = new ImageViewer(proxy, logfile); return eReplayCreate_Success; }
ImageSourceFileTinyExr::ImageSourceFileTinyExr( DataSourceRef dataSource, ImageSource::Options /*options*/ ) { mExrImage.reset( new EXRImage ); const char *error; InitEXRImage( mExrImage.get() ); int status = 0; if( dataSource->isFilePath() ) { status = ParseMultiChannelEXRHeaderFromFile( mExrImage.get(), dataSource->getFilePath().string().c_str(), &error ); if( status != 0 ) throw ImageIoExceptionFailedLoadTinyExr( string( "Failed to parse OpenEXR header; Error message: " ) + error ); status = LoadMultiChannelEXRFromFile( mExrImage.get(), dataSource->getFilePath().string().c_str(), &error ); if( status != 0 ) throw ImageIoExceptionFailedLoadTinyExr( string( "Failed to parse OpenEXR file; Error message: " ) + error ); } else { BufferRef buffer = dataSource->getBuffer(); status = ParseMultiChannelEXRHeaderFromMemory( mExrImage.get(), (const unsigned char*)buffer->getData(), &error ); if( status != 0 ) throw ImageIoExceptionFailedLoadTinyExr( string( "Failed to parse OpenEXR header; Error message: " ) + error ); status = LoadMultiChannelEXRFromMemory( mExrImage.get(), (const unsigned char*)buffer->getData(), &error ); if( status != 0 ) throw ImageIoExceptionFailedLoadTinyExr( string( "Failed to parse OpenEXR file; Error message: " ) + error ); } // verify that the channels are all the same size; currently we don't support variably sized channels int pixelType = mExrImage->pixel_types[0]; for( int c = 1; c < mExrImage->num_channels; ++c ) { if( pixelType != mExrImage->pixel_types[c] ) throw ImageIoExceptionFailedLoadTinyExr( "TinyExr: heterogneous channel data types not supported" ); } switch( pixelType ) { case TINYEXR_PIXELTYPE_HALF: setDataType( ImageIo::FLOAT16 ); break; case TINYEXR_PIXELTYPE_FLOAT: setDataType( ImageIo::FLOAT32 ); break; default: throw ImageIoExceptionFailedLoadTinyExr( "TinyExr: Unknown data type" ); break; } setSize( mExrImage->width, mExrImage->height ); switch( mExrImage->num_channels ) { case 3: setColorModel( ImageIo::CM_RGB ); setChannelOrder( ImageIo::ChannelOrder::RGB ); break; case 4: setColorModel( ImageIo::CM_RGB ); setChannelOrder( ImageIo::ChannelOrder::RGBA ); break; default: throw ImageIoExceptionFailedLoadTinyExr( "TinyExr: Unsupported number of channels (" + to_string( mExrImage->num_channels ) + ")" ); } }
/* The gateway function */ void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { /* check for proper number of arguments */ if(nrhs != 1) { mexErrMsgIdAndTxt("HDRToolbox:write_exr:nrhs", "One input is required."); } char *nameFile; mwSize buflen; int status; buflen = mxGetN(prhs[0])*sizeof(mxChar)+1; nameFile = (char*) mxMalloc(buflen); /* Copy the string data into buf. */ status = mxGetString(prhs[0], nameFile, buflen); /* call the computational routine */ EXRImage image; InitEXRImage(&image); const char* err; int ret = ParseMultiChannelEXRHeaderFromFile(&image, nameFile, &err); if (ret != 0) { printf("Parse EXR error: %s\n", err); return; } int width = image.width; int height = image.height; int channels = image.num_channels; //Allocate into memory mwSize dims[3]; dims[0] = height; dims[1] = width; dims[2] = channels; plhs[0] = mxCreateNumericArray(channels, dims, mxDOUBLE_CLASS, mxREAL); double *outMatrix = mxGetPr(plhs[0]); for (int i = 0; i < image.num_channels; i++) { if (image.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { image.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; } } ret = LoadMultiChannelEXRFromFile(&image, nameFile, &err); if (ret != 0) { printf("Load EXR error: %s\n", err); return; } float **images = (float**) image.images; int nPixels = width * height; int nPixels2 = nPixels * 2; if(channels == 1) { nPixels = 0; nPixels2 = 0; } if(channels == 2) { nPixels2 = 0; } for (int i = 0; i < width; i++){ for (int j = 0; j < height; j++){ int index = i * height + j; int indexOut = j * width + i; outMatrix[index ] = images[2][indexOut]; outMatrix[index + nPixels] = images[1][indexOut]; outMatrix[index + nPixels2] = images[0][indexOut]; } } FreeEXRImage(&image); }