void write_half_exr( const boost::filesystem::path& p, Imf::Header& header, const image::const_image_view_t& view, bool write_alpha) { boost::gil::rgba16f_image_t img( view.width(), view.height()); boost::gil::copy_and_convert_pixels( view, boost::gil::view( img)); header.channels().insert( "R", Imf::HALF); header.channels().insert( "G", Imf::HALF); header.channels().insert( "B", Imf::HALF); if( write_alpha) header.channels().insert( "A", Imf::HALF); Imf::FrameBuffer frameBuffer; char *ptr = (char *) boost::gil::interleaved_view_get_raw_data( boost::gil::view( img)); std::size_t xstride = 4 * sizeof(half); std::size_t ystride = xstride * img.width(); frameBuffer.insert( "R", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "G", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "B", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); if( write_alpha) frameBuffer.insert( "A", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); Imf::OutputFile out_file( p.external_file_string().c_str(), header); out_file.setFrameBuffer( frameBuffer); out_file.writePixels( img.height()); }
void Bitmap::save(const std::string &filename) { cout << "Writing a " << cols() << "x" << rows() << " OpenEXR file to \"" << filename << "\"" << endl; Imf::Header header((int) cols(), (int) rows()); header.insert("comments", Imf::StringAttribute("Generated by Nori")); Imf::ChannelList &channels = header.channels(); channels.insert("R", Imf::Channel(Imf::FLOAT)); channels.insert("G", Imf::Channel(Imf::FLOAT)); channels.insert("B", Imf::Channel(Imf::FLOAT)); Imf::FrameBuffer frameBuffer; size_t compStride = sizeof(float), pixelStride = 3 * compStride, rowStride = pixelStride * cols(); char *ptr = reinterpret_cast<char *>(data()); frameBuffer.insert("R", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; frameBuffer.insert("G", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; frameBuffer.insert("B", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); Imf::OutputFile file(filename.c_str(), header); file.setFrameBuffer(frameBuffer); file.writePixels((int) rows()); }
NORI_NAMESPACE_BEGIN Bitmap::Bitmap(const std::string &filename) { Imf::InputFile file(filename.c_str()); const Imf::Header &header = file.header(); const Imf::ChannelList &channels = header.channels(); Imath::Box2i dw = file.header().dataWindow(); resize(dw.max.y - dw.min.y + 1, dw.max.x - dw.min.x + 1); cout << "Reading a " << cols() << "x" << rows() << " OpenEXR file from \"" << filename << "\"" << endl; const char *ch_r = nullptr, *ch_g = nullptr, *ch_b = nullptr; for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) { std::string name = toLower(it.name()); if (it.channel().xSampling != 1 || it.channel().ySampling != 1) { /* Sub-sampled layers are not supported */ continue; } if (!ch_r && (name == "r" || name == "red" || endsWith(name, ".r") || endsWith(name, ".red"))) { ch_r = it.name(); } else if (!ch_g && (name == "g" || name == "green" || endsWith(name, ".g") || endsWith(name, ".green"))) { ch_g = it.name(); } else if (!ch_b && (name == "b" || name == "blue" || endsWith(name, ".b") || endsWith(name, ".blue"))) { ch_b = it.name(); } } if (!ch_r || !ch_g || !ch_b) throw NoriException("This is not a standard RGB OpenEXR file!"); size_t compStride = sizeof(float), pixelStride = 3 * compStride, rowStride = pixelStride * cols(); char *ptr = reinterpret_cast<char *>(data()); Imf::FrameBuffer frameBuffer; frameBuffer.insert(ch_r, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; frameBuffer.insert(ch_g, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride; frameBuffer.insert(ch_b, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); file.setFrameBuffer(frameBuffer); file.readPixels(dw.min.y, dw.max.y); m_totalLuminance = getTotalLuminace(); }
void SaveTexture( const std::string& _filename, float* _data, int _w, int _h, bool _verbose) { int xRes = _w; int yRes = _h; int xOffset = 0; int yOffset = 0; int nChannels = 4; Imf::Header header(xRes, yRes); Imath::Box2i dataWindow(Imath::V2i(xOffset, yOffset), Imath::V2i(xOffset + xRes - 1, yOffset + yRes - 1)); header.dataWindow() = dataWindow; header.channels().insert("R", Imf::Channel (Imf::HALF)); header.channels().insert("G", Imf::Channel (Imf::HALF)); header.channels().insert("B", Imf::Channel (Imf::HALF)); header.channels().insert("A", Imf::Channel (Imf::HALF)); ::half *hchannels = new ::half[nChannels * xRes * yRes]; for (int y = 0; y < yRes; ++y) for (int x = 0; x < xRes; ++x) for (int c = 0; c < nChannels; ++c) { int iSrc = c + x*nChannels + (yRes-1-y)*xRes*nChannels; int iDst = c + x*nChannels + y*xRes*nChannels; hchannels[iDst] = _data[iSrc]; } Imf::FrameBuffer fb; fb.insert("R", Imf::Slice(Imf::HALF, (char *)hchannels, nChannels*sizeof(::half), nChannels*xRes*sizeof(::half))); fb.insert("G", Imf::Slice(Imf::HALF, (char *)hchannels+1*sizeof(::half), nChannels*sizeof(::half), nChannels*xRes*sizeof(::half))); fb.insert("B", Imf::Slice(Imf::HALF, (char *)hchannels+2*sizeof(::half), nChannels*sizeof(::half), nChannels*xRes*sizeof(::half))); fb.insert("A", Imf::Slice(Imf::HALF, (char *)hchannels+3*sizeof(::half), nChannels*sizeof(::half), nChannels*xRes*sizeof(::half))); Imf::OutputFile file(_filename.c_str(), header); file.setFrameBuffer(fb); try { file.writePixels(yRes); } catch (const std::exception &e) { Error("Unable to write image file \"%s\": %s", _filename.c_str(), e.what()); assert(false); } delete[] hchannels; }
void ImgTools::write_rgba_layer(const char *filename, const Imf::Rgba* pixels, const Imath::Box2i &dispwin, const Imath::Box2i &datawin) { using namespace Imf; const int DISP_WIDTH = dispwin.max.x - dispwin.min.x + 1; const int DISP_HEIGHT = dispwin.max.y - dispwin.min.y + 1; const int DATA_WIDTH = datawin.max.x - datawin.min.x + 1; const int DATA_HEIGHT = datawin.max.y - datawin.min.y + 1; const Imf::Rgba* BASE = pixels - datawin.min.x - datawin.min.y * DATA_WIDTH; Imf::Header header(DISP_WIDTH, DISP_HEIGHT); header.dataWindow() = datawin; header.channels().insert("R", Imf::Channel(Imf::HALF)); header.channels().insert("G", Imf::Channel(Imf::HALF)); header.channels().insert("B", Imf::Channel(Imf::HALF)); header.channels().insert("A", Imf::Channel(Imf::HALF)); Imf::OutputFile exr(filename, header); Imf::FrameBuffer frameBuffer; frameBuffer.insert("R", // name Imf::Slice(Imf::HALF, // type (char *)(&BASE->r), // base sizeof(*BASE), // xStride sizeof(*BASE) * DATA_WIDTH)); // yStride frameBuffer.insert("G", // name Imf::Slice(Imf::HALF, // type (char *)(&BASE->g), // base sizeof(*BASE), // xStride sizeof(*BASE) * DATA_WIDTH)); // yStride frameBuffer.insert("B", // name Imf::Slice(Imf::HALF, // type (char *)(&BASE->b), // base sizeof(*BASE), // xStride sizeof(*BASE) * DATA_WIDTH)); // yStride frameBuffer.insert("A", // name Imf::Slice(Imf::HALF, // type (char *)(&BASE->a), // base sizeof(*BASE), // xStride sizeof(*BASE) * DATA_WIDTH)); // yStride exr.setFrameBuffer(frameBuffer); exr.writePixels(DATA_HEIGHT); }
bool ImageIO::writeImage(QVector<float> &pixels, const QString &filePath, const LayerDesc &desc, int width, int height) { try { Imf::Header header (width, height); Imf::FrameBuffer frameBuffer; for (int chan = 0; chan < desc.numChannels(); chan++) { QString chan_name = QString("%1.%2").arg(desc._layer_name).arg(desc._channels[chan]); header.channels().insert(qPrintable(chan_name), Imf::Channel(Imf::FLOAT)); frameBuffer.insert(qPrintable(chan_name), Imf::Slice(Imf::FLOAT, (char *) pixels.data() + chan*sizeof(float), sizeof(float)*desc.numChannels(), sizeof(float)*width*desc.numChannels())); } Imf::OutputFile file(qPrintable(remapFilePath(filePath)), header); file.setFrameBuffer(frameBuffer); file.writePixels(height); } catch (const std::exception &e) { qWarning() << e.what(); return false; } return true; }
void CqExrInputFile::readPixelsImpl(TqUint8* buffer, TqInt startLine, TqInt numScanlines) const { // correct the start line for OpenEXR conventions const Imath::Box2i& dataWindow = m_exrFile->header().dataWindow(); startLine += dataWindow.min.y; // Set up an OpenEXR framebuffer Imf::FrameBuffer frameBuffer; const CqChannelList& channels = m_header.channelList(); const TqChannelNameMap& nameMap = m_header.find<Attr::ExrChannelNameMap>(); const TqInt xStride = channels.bytesPerPixel(); const TqInt yStride = m_header.width()*xStride; // In OpenEXR, the buffer base pointer is assumed to point at the // coordinates of the (0,0) pixel. We need to correct our buffer pointer // by subtracting the offset to (0,0) from the start of the data. buffer -= dataWindow.min.x*xStride + dataWindow.min.y*yStride; for(TqInt i = 0; i < channels.numChannels(); ++i) { frameBuffer.insert(nameMap.find(channels[i].name)->second.c_str(), Imf::Slice( exrChannelType(channels[i].type), reinterpret_cast<char*>(buffer + channels.channelByteOffset(i)), xStride, yStride ) ); } m_exrFile->setFrameBuffer(frameBuffer); // Read in the pixels m_exrFile->readPixels(startLine, startLine + numScanlines - 1); }
void FExrImageWrapper::WriteFrameBufferChannel(Imf::FrameBuffer& ImfFrameBuffer, const char* ChannelName, const sourcetype* SrcData, TArray<uint8>& ChannelBuffer) { const int32 OutputPixelSize = ((OutputFormat == Imf::HALF) ? 2 : 4); ChannelBuffer.AddUninitialized(Width*Height*OutputPixelSize); uint32 SrcChannels = GetNumChannelsFromFormat(RawFormat); ExtractAndConvertChannel(SrcData, SrcChannels, Width, Height, (typename TExrImageOutputChannelType<OutputFormat>::Type*)&ChannelBuffer[0]); Imf::Slice FrameChannel = Imf::Slice(OutputFormat, (char*)&ChannelBuffer[0], OutputPixelSize, Width*OutputPixelSize); ImfFrameBuffer.insert(ChannelName, FrameChannel); }
bool OpenEXRInput::read_native_scanlines (int ybegin, int yend, int z, int chbegin, int chend, void *data) { chend = clamp (chend, chbegin+1, m_spec.nchannels); // std::cerr << "openexr rns " << ybegin << ' ' << yend << ", channels " // << chbegin << "-" << (chend-1) << "\n"; if (m_input_scanline == NULL && m_scanline_input_part == NULL) { error ("called OpenEXRInput::read_native_scanlines without an open file"); return false; } // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. const PartInfo &part (m_parts[m_subimage]); size_t pixelbytes = m_spec.pixel_bytes (chbegin, chend, true); size_t scanlinebytes = (size_t)m_spec.width * pixelbytes; char *buf = (char *)data - m_spec.x * pixelbytes - ybegin * scanlinebytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = chbegin; c < chend; ++c) { size_t chanbytes = m_spec.channelformat(c).size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (part.pixeltype[c], buf + chanoffset, pixelbytes, scanlinebytes)); chanoffset += chanbytes; } if (m_input_scanline) { m_input_scanline->setFrameBuffer (frameBuffer); m_input_scanline->readPixels (ybegin, yend-1); #ifdef USE_OPENEXR_VERSION2 } else if (m_scanline_input_part) { m_scanline_input_part->setFrameBuffer (frameBuffer); m_scanline_input_part->readPixels (ybegin, yend-1); #endif } else { error ("Attempted to read scanline from a non-scanline file."); return false; } } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } catch (...) { // catch-all for edge cases or compiler bugs error ("Failed OpenEXR read: unknown exception"); return false; } return true; }
void SaveExr(const Image<unsigned char>& image_in, const pangolin::PixelFormat& fmt, const std::string& filename, bool top_line_first) { PANGOLIN_UNUSED(image_in); PANGOLIN_UNUSED(fmt); PANGOLIN_UNUSED(filename); PANGOLIN_UNUSED(top_line_first); #ifdef HAVE_OPENEXR ManagedImage<unsigned char> flip_image; Image<unsigned char> image; if(top_line_first) { image = image_in; }else{ flip_image.Reinitialise(image_in.pitch,image_in.h); for(size_t y=0; y<image_in.h; ++y) { std::memcpy(flip_image.RowPtr(y), image_in.RowPtr(y), image_in.pitch); } image = flip_image; } Imf::Header header (image.w, image.h); SetOpenEXRChannels(header.channels(), fmt); Imf::OutputFile file (filename.c_str(), header); Imf::FrameBuffer frameBuffer; int ch=0; size_t ch_bits = 0; for(Imf::ChannelList::Iterator it = header.channels().begin(); it != header.channels().end(); ++it) { frameBuffer.insert( it.name(), Imf::Slice( it.channel().type, (char*)image.ptr + ch_bits/8, fmt.channel_bits[ch]/8, image.pitch ) ); ch_bits += fmt.channel_bits[ch++]; } file.setFrameBuffer(frameBuffer); file.writePixels(image.h); #else throw std::runtime_error("EXR Support not enabled. Please rebuild Pangolin."); #endif // HAVE_OPENEXR }
void write_half_rgb_exr( Imf::OStream& os, Imf::Header& header, const image::const_image_view_t& view) { boost::gil::rgba16f_image_t img( view.width(), view.height()); boost::gil::copy_and_convert_pixels( view, boost::gil::view( img)); header.channels().insert( "R", Imf::HALF); header.channels().insert( "G", Imf::HALF); header.channels().insert( "B", Imf::HALF); Imf::FrameBuffer frameBuffer; char *ptr = (char *) boost::gil::interleaved_view_get_raw_data( boost::gil::view( img)); std::size_t xstride = 4 * sizeof(half); std::size_t ystride = xstride * img.width(); frameBuffer.insert( "R", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "G", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); frameBuffer.insert( "B", Imf::Slice( Imf::HALF, ptr, xstride, ystride)); ptr += sizeof(half); Imf::OutputFile out_file( os, header); out_file.setFrameBuffer( frameBuffer); out_file.writePixels( img.height()); }
void SaveExr(const Image<unsigned char>& image_in, const pangolin::VideoPixelFormat& fmt, const std::string& filename, bool top_line_first) { #ifdef HAVE_OPENEXR Image<unsigned char> image; if(top_line_first) { image = image_in; }else{ image.Alloc(image_in.w,image_in.h,image_in.pitch); for(size_t y=0; y<image_in.h; ++y) { std::memcpy(image.ptr + y*image.pitch, image_in.ptr + (image_in.h-y-1)*image_in.pitch, image.pitch); } } Imf::Header header (image.w, image.h); SetOpenEXRChannels(header.channels(), fmt); Imf::OutputFile file (filename.c_str(), header); Imf::FrameBuffer frameBuffer; int ch=0; size_t ch_bits = 0; for(Imf::ChannelList::Iterator it = header.channels().begin(); it != header.channels().end(); ++it) { frameBuffer.insert( it.name(), Imf::Slice( it.channel().type, (char*)image.ptr + ch_bits/8, fmt.channel_bits[ch]/8, image.pitch ) ); ch_bits += fmt.channel_bits[ch++]; } file.setFrameBuffer(frameBuffer); file.writePixels(image.h); if(!top_line_first) { image.Dealloc(); } #else throw std::runtime_error("EXR Support not enabled. Please rebuild Pangolin."); #endif // HAVE_OPENEXR }
bool OpenEXROutput::write_tile (int x, int y, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { bool native = (format == TypeDesc::UNKNOWN); size_t pixel_bytes = m_spec.pixel_bytes (native); if (native && xstride == AutoStride) xstride = (stride_t) pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, spec().tile_width, spec().tile_height); data = to_native_tile (format, data, xstride, ystride, zstride, m_scratch); // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. char *buf = (char *)data - x * pixel_bytes - y * pixel_bytes * m_spec.tile_width; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformats.size() ? m_spec.channelformats[c].size() : m_spec.format.size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixel_bytes, pixel_bytes*m_spec.tile_width)); chanoffset += chanbytes; } m_output_tiled->setFrameBuffer (frameBuffer); m_output_tiled->writeTile ((x - m_spec.x) / m_spec.tile_width, (y - m_spec.y) / m_spec.tile_height, m_miplevel, m_miplevel); } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } return true; }
bool OpenEXROutput::write_scanline (int y, int z, TypeDesc format, const void *data, stride_t xstride) { bool native = (format == TypeDesc::UNKNOWN); size_t pixel_bytes = m_spec.pixel_bytes (true); // native if (native && xstride == AutoStride) xstride = (stride_t) pixel_bytes; m_spec.auto_stride (xstride, format, spec().nchannels); data = to_native_scanline (format, data, xstride, m_scratch); // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. imagesize_t scanlinebytes = m_spec.scanline_bytes (native); char *buf = (char *)data - m_spec.x * pixel_bytes - y * scanlinebytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformats.size() ? m_spec.channelformats[c].size() : m_spec.format.size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixel_bytes, scanlinebytes)); chanoffset += chanbytes; } m_output_scanline->setFrameBuffer (frameBuffer); m_output_scanline->writePixels (1); } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } // FIXME -- can we checkpoint the file? return true; }
bool OpenEXRInput::read_native_scanlines (int ybegin, int yend, int z, int firstchan, int nchans, void *data) { // std::cerr << "openexr rns " << ybegin << ' ' << yend << ", channels " // << firstchan << "-" << (firstchan+nchans-1) << "\n"; if (m_input_scanline == NULL) return false; // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. size_t pixelbytes = m_spec.pixel_bytes (firstchan, nchans, true); size_t scanlinebytes = (size_t)m_spec.width * pixelbytes; char *buf = (char *)data - m_spec.x * pixelbytes - ybegin * scanlinebytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < nchans; ++c) { size_t chanbytes = m_spec.channelformats.size() ? m_spec.channelformats[c+firstchan].size() : m_spec.format.size(); frameBuffer.insert (m_spec.channelnames[c+firstchan].c_str(), Imf::Slice (m_pixeltype[c+firstchan], buf + chanoffset, pixelbytes, scanlinebytes)); chanoffset += chanbytes; } m_input_scanline->setFrameBuffer (frameBuffer); m_input_scanline->readPixels (ybegin, yend-1); } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } return true; }
bool OpenEXRInput::read_native_tile (int x, int y, int z, void *data) { ASSERT (m_input_tiled != NULL); // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. size_t pixelbytes = m_spec.pixel_bytes (true); char *buf = (char *)data - x * pixelbytes - y * pixelbytes * m_spec.tile_width; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformats.size() ? m_spec.channelformats[c].size() : m_spec.format.size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixelbytes, pixelbytes*m_spec.tile_width)); chanoffset += chanbytes; } m_input_tiled->setFrameBuffer (frameBuffer); m_input_tiled->readTile ((x - m_spec.x) / m_spec.tile_width, (y - m_spec.y) / m_spec.tile_height, m_miplevel, m_miplevel); } catch (const std::exception &e) { error ("Filed OpenEXR read: %s", e.what()); return false; } return true; }
void OpenEXRImpl :: load_channels(Imf::InputFile &file, Matrix& mat, int numChannels, const char *channelNames) { Imath::Box2i dw = file.header().dataWindow(); int width = dw.max.x - dw.min.x + 1; int height = dw.max.y - dw.min.y + 1; mat.create(numChannels, Matrix::FLOAT32, width, height); Imf::FrameBuffer frameBuffer; for(int i=0; i < numChannels; i++) { char c[2]; c[0] = channelNames[i]; c[1] = '\0'; frameBuffer.insert(c, Imf::Slice(Imf::FLOAT, (char *)(mat.data.fl + i), sizeof(float)*numChannels, sizeof(float)*numChannels*width)); } file.setFrameBuffer(frameBuffer); file.readPixels(dw.min.y, dw.max.y); }
bool saveExr(const Path &path, const float *img, int w, int h, int channels) { if (channels <= 0 || channels > 4) return false; OutputStreamHandle outputStream = FileUtils::openOutputStream(path); if (!outputStream) return false; try { Imf::Header header(w, h, 1.0f, Imath::V2f(0, 0), 1.0f, Imf::INCREASING_Y, Imf::PIZ_COMPRESSION); Imf::FrameBuffer frameBuffer; std::unique_ptr<half[]> data(new half[w*h*channels]); for (int i = 0; i < w*h*channels; ++i) data[i] = half(img[i]); const char *channelNames[] = {"R", "G", "B", "A"}; for (int i = 0; i < channels; ++i) { const char *channelName = (channels == 1) ? "Y" : channelNames[i]; header.channels().insert(channelName, Imf::Channel(Imf::HALF)); frameBuffer.insert(channelName, Imf::Slice(Imf::HALF, reinterpret_cast<char *>(data.get() + i), sizeof(half)*channels, sizeof(half)*channels*w)); } ExrOStream out(std::move(outputStream)); Imf::OutputFile file(out, header); file.setFrameBuffer(frameBuffer); file.writePixels(h); return true; } catch(const std::exception &e) { std::cout << "OpenEXR writer failed: " << e.what() << std::endl; return false; } }
ImageError OpenEXRImpl :: save(const char *filename, AlloArray &mat) { ImageError err = IMAGE_ERROR_NONE; mImage.fromMatrix(&mat); Imf::Header header(mImage.header.dim[0], mImage.header.dim[1]); Imf::PixelType pt = pixelTypeForMatrixType(mImage.header.type); int planes = mImage.header.planes; int rowsize; mImage.getRowSize(rowsize); char channelNames[] = {0, 0, 0, 0}; switch(planes) { case 1: channelNames[0] = 'Y'; break; case 2: channelNames[0] = 'Y'; channelNames[1] = 'A'; break; case 3: channelNames[0] = 'R'; channelNames[1] = 'G'; channelNames[2] = 'B'; break; case 4: channelNames[0] = 'R'; channelNames[1] = 'G'; channelNames[2] = 'B'; channelNames[3] = 'A'; break; } for(int i=0; i < planes; i++) { char c[] = {channelNames[i], '\0'}; header.channels().insert(c, Imf::Channel(pt)); } Imf::OutputFile file(filename, header); Imf::FrameBuffer frameBuffer; for(int i=0; i < planes; i++) { char c[] = {channelNames[i], '\0'}; frameBuffer.insert (c, Imf::Slice(pt, (char *)(mImage.data.fl + i), sizeof(float)*planes, rowsize)); } file.setFrameBuffer(frameBuffer); file.writePixels(mImage.header.dim[1]); return err; }
int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit) { EXRUnit *exr_unit = (EXRUnit*)unit; int result = 0; VFrame *output_frame; data->set_compressed_size(0); int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT; int components = BC_CModels::components(native_cmodel); if(frame->get_color_model() != native_cmodel) { if(!exr_unit->temp_frame) exr_unit->temp_frame = new VFrame(0, asset->width, asset->height, native_cmodel); exr_unit->temp_frame->transfer_from(frame); output_frame = exr_unit->temp_frame; } else output_frame = frame; Imf::Header header(output_frame->get_w(), output_frame->get_h()); header.compression() = (Imf::Compression)compression_to_exr( asset->exr_compression); header.channels().insert("R", Imf::Channel(Imf::FLOAT)); header.channels().insert("G", Imf::Channel(Imf::FLOAT)); header.channels().insert("B", Imf::Channel(Imf::FLOAT)); if(asset->exr_use_alpha) header.channels().insert("A", Imf::Channel(Imf::FLOAT)); EXROStream exr_stream(data); Imf::OutputFile file(exr_stream, header); Imf::FrameBuffer framebuffer; float **rows = (float**)output_frame->get_rows(); framebuffer.insert("R", Imf::Slice(Imf::FLOAT, (char*)(rows[0]), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); framebuffer.insert("G", Imf::Slice(Imf::FLOAT, (char*)(rows[0] + 1), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); framebuffer.insert("B", Imf::Slice(Imf::FLOAT, (char*)(rows[0] + 2), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); if(asset->exr_use_alpha) framebuffer.insert("A", Imf::Slice(Imf::FLOAT, (char*)(rows[0] + 3), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); file.setFrameBuffer(framebuffer); file.writePixels(asset->height); return 0; }
int FileEXR::read_frame(VFrame *frame, VFrame *data) { EXRIStream exr_stream((char*)data->get_data(), data->get_compressed_size()); Imf::InputFile file(exr_stream); Imath::Box2i dw = file.header().dataWindow(); int dx = dw.min.x; int dy = dw.min.y; Imf::FrameBuffer framebuffer; float **rows = (float**)frame->get_rows(); int components = BC_CModels::components(frame->get_color_model()); if(is_yuv) { if(!temp_y) temp_y = new float[asset->width * asset->height]; if(!temp_u) temp_u = new float[asset->width * asset->height / 4]; if(!temp_v) temp_v = new float[asset->width * asset->height / 4]; framebuffer.insert("Y", Imf::Slice(Imf::FLOAT, (char*)(temp_y - dy * asset->width - dx), sizeof(float), sizeof(float) * frame->get_w())); framebuffer.insert("BY", Imf::Slice(Imf::FLOAT, (char*)(temp_u - dy * asset->width / 4 - dx / 2), sizeof(float), sizeof(float) * frame->get_w() / 2, 2, 2)); framebuffer.insert("RY", Imf::Slice(Imf::FLOAT, (char*)(temp_v - dy * asset->width / 4 - dx / 2), sizeof(float), sizeof(float) * frame->get_w() / 2, 2, 2)); } else { framebuffer.insert("R", Imf::Slice(Imf::FLOAT, (char*)(&rows[-dy][-dx * components]), sizeof(float) * components, sizeof(float) * components * frame->get_w())); framebuffer.insert("G", Imf::Slice(Imf::FLOAT, (char*)(&rows[-dy][-dx * components + 1]), sizeof(float) * components, sizeof(float) * components * frame->get_w())); framebuffer.insert("B", Imf::Slice(Imf::FLOAT, (char*)(&rows[-dy][-dx * components + 2]), sizeof(float) * components, sizeof(float) * components * frame->get_w())); } // Alpha always goes directly to the output frame if(components == 4) { framebuffer.insert("A", Imf::Slice(Imf::FLOAT, (char*)(&rows[-dy][-dx * components + 3]), sizeof(float) * components, sizeof(float) * components * frame->get_w())); } file.setFrameBuffer(framebuffer); file.readPixels (dw.min.y, dw.max.y); if(is_yuv) { // Convert to RGB using crazy ILM equations Imath::V3f yw; Imf::Chromaticities cr; yw = Imf::RgbaYca::computeYw(cr); for(int i = 0; i < asset->height - 1; i += 2) { float *y_row1 = temp_y + i * asset->width; float *y_row2 = temp_y + (i + 1) * asset->width; float *u_row = temp_u + (i * asset->width / 4); float *v_row = temp_v + (i * asset->width / 4); float *out_row1 = rows[i]; float *out_row2 = rows[i + 1]; for(int j = 0; j < asset->width - 1; j += 2) { float v = *u_row++; float u = *v_row++; float y; float r, g, b; y = *y_row1++; r = (u + 1) * y; b = (v + 1) * y; g = (y - r * yw.x - b * yw.z) / yw.y; *out_row1++ = r; *out_row1++ = g; *out_row1++ = b; if(components == 4) out_row1++; y = *y_row1++; r = (u + 1) * y; b = (v + 1) * y; g = (y - r * yw.x - b * yw.z) / yw.y; *out_row1++ = r; *out_row1++ = g; *out_row1++ = b; if(components == 4) out_row1++; y = *y_row2++; r = (u + 1) * y; b = (v + 1) * y; g = (y - r * yw.x - b * yw.z) / yw.y; *out_row2++ = r; *out_row2++ = g; *out_row2++ = b; if(components == 4) out_row1++; y = *y_row2++; r = (u + 1) * y; b = (v + 1) * y; g = (y - r * yw.x - b * yw.z) / yw.y; *out_row2++ = r; *out_row2++ = g; *out_row2++ = b; if(components == 4) out_row1++; } } } return 0; }
bool OpenEXROutput::write_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride) { // std::cerr << "exr::write_tiles " << xbegin << ' ' << xend // << ' ' << ybegin << ' ' << yend << "\n"; if (! m_output_tiled || ! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) return false; // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. bool native = (format == TypeDesc::UNKNOWN); size_t user_pixelbytes = m_spec.pixel_bytes (native); size_t pixelbytes = m_spec.pixel_bytes (true); if (native && xstride == AutoStride) xstride = (stride_t) user_pixelbytes; m_spec.auto_stride (xstride, ystride, zstride, format, spec().nchannels, (xend-xbegin), (yend-ybegin)); data = to_native_rectangle (xbegin, xend, ybegin, yend, zbegin, zend, format, data, xstride, ystride, zstride, m_scratch); // clamp to the image edge xend = std::min (xend, m_spec.x+m_spec.width); yend = std::min (yend, m_spec.y+m_spec.height); zend = std::min (zend, m_spec.z+m_spec.depth); int firstxtile = (xbegin-m_spec.x) / m_spec.tile_width; int firstytile = (ybegin-m_spec.y) / m_spec.tile_height; int nxtiles = (xend - xbegin + m_spec.tile_width - 1) / m_spec.tile_width; int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height; std::vector<char> padded; int width = nxtiles*m_spec.tile_width; int height = nytiles*m_spec.tile_height; stride_t widthbytes = width * pixelbytes; if (width != (xend-xbegin) || height != (yend-ybegin)) { // If the image region is not an even multiple of the tile size, // we need to copy and add padding. padded.resize (pixelbytes * width * height, 0); OIIO::copy_image (m_spec.nchannels, xend-xbegin, yend-ybegin, 1, data, pixelbytes, pixelbytes, (xend-xbegin)*pixelbytes, (xend-xbegin)*(yend-ybegin)*pixelbytes, &padded[0], pixelbytes, widthbytes, height*widthbytes); data = &padded[0]; } char *buf = (char *)data - xbegin * pixelbytes - ybegin * widthbytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformats.size() ? m_spec.channelformats[c].size() : m_spec.format.size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixelbytes, widthbytes)); chanoffset += chanbytes; } m_output_tiled->setFrameBuffer (frameBuffer); m_output_tiled->writeTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } return true; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { const char *channel_name[4] = { "R", "G", "B", "A" }; BOOL bIsFlipped = FALSE; half *halfData = NULL; if(!dib || !handle) return FALSE; try { // check for EXR_LC compression and verify that the format is RGB if((flags & EXR_LC) == EXR_LC) { FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) { THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images"); } if((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) { THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2"); } } // wrap the FreeImage IO stream C_OStream ostream(io, handle); // compression Imf::Compression compress; if((flags & EXR_NONE) == EXR_NONE) { // no compression compress = Imf::NO_COMPRESSION; } else if((flags & EXR_ZIP) == EXR_ZIP) { // zlib compression, in blocks of 16 scan lines compress = Imf::ZIP_COMPRESSION; } else if((flags & EXR_PIZ) == EXR_PIZ) { // piz-based wavelet compression compress = Imf::PIZ_COMPRESSION; } else if((flags & EXR_PXR24) == EXR_PXR24) { // lossy 24-bit float compression compress = Imf::PXR24_COMPRESSION; } else if((flags & EXR_B44) == EXR_B44) { // lossy 44% float compression compress = Imf::B44_COMPRESSION; } else { // default value compress = Imf::PIZ_COMPRESSION; } // create the header int width = FreeImage_GetWidth(dib); int height = FreeImage_GetHeight(dib); int dx = 0, dy = 0; Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1)); Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1)); Imf::Header header = Imf::Header(displayWindow, dataWindow, 1, Imath::V2f(0,0), 1, Imf::INCREASING_Y, compress); // handle thumbnail SetPreviewImage(dib, header); // check for EXR_LC compression if((flags & EXR_LC) == EXR_LC) { return SaveAsEXR_LC(ostream, dib, header, width, height); } // output pixel type Imf::PixelType pixelType; if((flags & EXR_FLOAT) == EXR_FLOAT) { pixelType = Imf::FLOAT; // save as float data type } else { // default value pixelType = Imf::HALF; // save as half data type } // check the data type and number of channels int components = 0; FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); switch(image_type) { case FIT_FLOAT: components = 1; // insert luminance channel header.channels().insert ("Y", Imf::Channel(pixelType)); break; case FIT_RGBF: components = 3; for(int c = 0; c < components; c++) { // insert R, G and B channels header.channels().insert (channel_name[c], Imf::Channel(pixelType)); } break; case FIT_RGBAF: components = 4; for(int c = 0; c < components; c++) { // insert R, G, B and A channels header.channels().insert (channel_name[c], Imf::Channel(pixelType)); } break; default: THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR."); } // build a frame buffer (i.e. what we have on input) Imf::FrameBuffer frameBuffer; BYTE *bits = NULL; // pointer to our pixel buffer size_t bytespp = 0; // size of our pixel in bytes size_t bytespc = 0; // size of our pixel component in bytes unsigned pitch = 0; // size of our yStride in bytes if(pixelType == Imf::HALF) { // convert from float to half halfData = new(std::nothrow) half[width * height * components]; if(!halfData) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); for(int y = 0; y < height; y++) { float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); half *dst_bits = halfData + y * width * components; for(int x = 0; x < width; x++) { for(int c = 0; c < components; c++) { dst_bits[c] = src_bits[c]; } src_bits += components; dst_bits += components; } } bits = (BYTE*)halfData; bytespc = sizeof(half); bytespp = sizeof(half) * components; pitch = sizeof(half) * width * components; } else if(pixelType == Imf::FLOAT) { // invert dib scanlines bIsFlipped = FreeImage_FlipVertical(dib); bits = FreeImage_GetBits(dib); bytespc = sizeof(float); bytespp = sizeof(float) * components; pitch = FreeImage_GetPitch(dib); } if(image_type == FIT_FLOAT) { frameBuffer.insert ("Y", // name Imf::Slice (pixelType, // type (char*)(bits), // base bytespp, // xStride pitch)); // yStride } else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) { for(int c = 0; c < components; c++) { char *channel_base = (char*)(bits) + c*bytespc; frameBuffer.insert (channel_name[c],// name Imf::Slice (pixelType, // type channel_base, // base bytespp, // xStride pitch)); // yStride } } // write the data Imf::OutputFile file (ostream, header); file.setFrameBuffer (frameBuffer); file.writePixels (height); if(halfData != NULL) delete[] halfData; if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } return TRUE; } catch(Iex::BaseExc & e) { if(halfData != NULL) delete[] halfData; if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } FreeImage_OutputMessageProc(s_format_id, e.what()); return FALSE; } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { bool bUseRgbaInterface = false; FIBITMAP *dib = NULL; if(!handle) { return NULL; } try { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; // save the stream starting point const long stream_start = io->tell_proc(handle); // wrap the FreeImage IO stream C_IStream istream(io, handle); // open the file Imf::InputFile file(istream); // get file info const Imath::Box2i &dataWindow = file.header().dataWindow(); int width = dataWindow.max.x - dataWindow.min.x + 1; int height = dataWindow.max.y - dataWindow.min.y + 1; //const Imf::Compression &compression = file.header().compression(); const Imf::ChannelList &channels = file.header().channels(); // check the number of components and check for a coherent format std::string exr_color_model; Imf::PixelType pixel_type = Imf::HALF; FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; int components = 0; bool bMixedComponents = false; for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { components++; if(components == 1) { exr_color_model += i.name(); pixel_type = i.channel().type; } else { exr_color_model += "/"; exr_color_model += i.name(); if (i.channel().type != pixel_type) { bMixedComponents = true; } } } if(bMixedComponents) { bool bHandled = false; // we may have a RGBZ or RGBAZ image ... if(components > 4) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B") && channels.findChannel("A")) { std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); bHandled = true; } } else if(components > 3) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); bHandled = true; } } if(!bHandled) { THROW (Iex::InputExc, "Unable to handle mixed component types (color model = " << exr_color_model << ")"); } } switch(pixel_type) { case Imf::UINT: THROW (Iex::InputExc, "Unsupported format: UINT"); break; case Imf::HALF: case Imf::FLOAT: default: break; } // check for supported image color models // -------------------------------------------------------------- if((components == 1) || (components == 2)) { // if the image is gray-alpha (YA), ignore the alpha channel if((components == 1) && channels.findChannel("Y")) { image_type = FIT_FLOAT; components = 1; } else { std::string msg = "Warning: loading color model " + exr_color_model + " as Y color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); image_type = FIT_FLOAT; // ignore the other channel components = 1; } } else if(components == 3) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { image_type = FIT_RGBF; } else if(channels.findChannel("BY") && channels.findChannel("RY") && channels.findChannel("Y")) { image_type = FIT_RGBF; bUseRgbaInterface = true; } } else if(components >= 4) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { if(channels.findChannel("A")) { if(components > 4) { std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); } image_type = FIT_RGBAF; // ignore other layers if there is more than one alpha layer components = 4; } else { std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); image_type = FIT_RGBF; // ignore other channels components = 3; } } } if(image_type == FIT_UNKNOWN) { THROW (Iex::InputExc, "Unsupported color model: " << exr_color_model); } // allocate a new dib dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, 0); if(!dib) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); // try to load the preview image // -------------------------------------------------------------- if(file.header().hasPreviewImage()) { const Imf::PreviewImage& preview = file.header().previewImage(); const unsigned thWidth = preview.width(); const unsigned thHeight = preview.height(); FIBITMAP* thumbnail = FreeImage_Allocate(thWidth, thHeight, 32); if(thumbnail) { const Imf::PreviewRgba *src_line = preview.pixels(); BYTE *dst_line = FreeImage_GetScanLine(thumbnail, thHeight - 1); const unsigned dstPitch = FreeImage_GetPitch(thumbnail); for (unsigned y = 0; y < thHeight; ++y) { const Imf::PreviewRgba *src_pixel = src_line; RGBQUAD* dst_pixel = (RGBQUAD*)dst_line; for(unsigned x = 0; x < thWidth; ++x) { dst_pixel->rgbRed = src_pixel->r; dst_pixel->rgbGreen = src_pixel->g; dst_pixel->rgbBlue = src_pixel->b; dst_pixel->rgbReserved = src_pixel->a; src_pixel++; dst_pixel++; } src_line += thWidth; dst_line -= dstPitch; } FreeImage_SetThumbnail(dib, thumbnail); FreeImage_Unload(thumbnail); } } if(header_only) { // header only mode return dib; } // load pixels // -------------------------------------------------------------- const BYTE *bits = FreeImage_GetBits(dib); // pointer to our pixel buffer const size_t bytespp = sizeof(float) * components; // size of our pixel in bytes const unsigned pitch = FreeImage_GetPitch(dib); // size of our yStride in bytes Imf::PixelType pixelType = Imf::FLOAT; // load as float data type; if(bUseRgbaInterface) { // use the RGBA interface (used when loading RY BY Y images ) const int chunk_size = 16; BYTE *scanline = (BYTE*)bits; // re-open using the RGBA interface io->seek_proc(handle, stream_start, SEEK_SET); Imf::RgbaInputFile rgbaFile(istream); // read the file in chunks Imath::Box2i dw = dataWindow; Imf::Array2D<Imf::Rgba> chunk(chunk_size, width); while (dw.min.y <= dw.max.y) { // read a chunk rgbaFile.setFrameBuffer (&chunk[0][0] - dw.min.x - dw.min.y * width, 1, width); rgbaFile.readPixels (dw.min.y, MIN(dw.min.y + chunk_size - 1, dw.max.y)); // fill the dib const int y_max = ((dw.max.y - dw.min.y) <= chunk_size) ? (dw.max.y - dw.min.y) : chunk_size; for(int y = 0; y < y_max; y++) { FIRGBF *pixel = (FIRGBF*)scanline; const Imf::Rgba *half_rgba = chunk[y]; for(int x = 0; x < width; x++) { // convert from half to float pixel[x].red = half_rgba[x].r; pixel[x].green = half_rgba[x].g; pixel[x].blue = half_rgba[x].b; } // next line scanline += pitch; } // next chunk dw.min.y += chunk_size; } } else { // use the low level interface // build a frame buffer (i.e. what we want on output) Imf::FrameBuffer frameBuffer; // allow dataWindow with minimal bounds different form zero size_t offset = - dataWindow.min.x * bytespp - dataWindow.min.y * pitch; if(components == 1) { frameBuffer.insert ("Y", // name Imf::Slice (pixelType, // type (char*)(bits + offset), // base bytespp, // xStride pitch, // yStride 1, 1, // x/y sampling 0.0)); // fillValue } else if((components == 3) || (components == 4)) { const char *channel_name[4] = { "R", "G", "B", "A" }; for(int c = 0; c < components; c++) { frameBuffer.insert ( channel_name[c], // name Imf::Slice (pixelType, // type (char*)(bits + c * sizeof(float) + offset), // base bytespp, // xStride pitch, // yStride 1, 1, // x/y sampling 0.0)); // fillValue } } // read the file file.setFrameBuffer(frameBuffer); file.readPixels(dataWindow.min.y, dataWindow.max.y); } // lastly, flip dib lines FreeImage_FlipVertical(dib); } catch(Iex::BaseExc & e) { if(dib != NULL) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, e.what()); return NULL; } return dib; }
bool OpenEXRInput::read_native_tiles (int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, void *data) { chend = clamp (chend, chbegin+1, m_spec.nchannels); #if 0 std::cerr << "openexr rnt " << xbegin << ' ' << xend << ' ' << ybegin << ' ' << yend << ", chans " << chbegin << "-" << (chend-1) << "\n"; #endif if (! (m_input_tiled || m_tiled_input_part) || ! m_spec.valid_tile_range (xbegin, xend, ybegin, yend, zbegin, zend)) { error ("called OpenEXRInput::read_native_tiles without an open file"); return false; } // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. const PartInfo &part (m_parts[m_subimage]); size_t pixelbytes = m_spec.pixel_bytes (chbegin, chend, true); int firstxtile = (xbegin-m_spec.x) / m_spec.tile_width; int firstytile = (ybegin-m_spec.y) / m_spec.tile_height; // clamp to the image edge xend = std::min (xend, m_spec.x+m_spec.width); yend = std::min (yend, m_spec.y+m_spec.height); zend = std::min (zend, m_spec.z+m_spec.depth); // figure out how many tiles we need int nxtiles = (xend - xbegin + m_spec.tile_width - 1) / m_spec.tile_width; int nytiles = (yend - ybegin + m_spec.tile_height - 1) / m_spec.tile_height; int whole_width = nxtiles * m_spec.tile_width; int whole_height = nytiles * m_spec.tile_height; boost::scoped_array<char> tmpbuf; void *origdata = data; if (whole_width != (xend-xbegin) || whole_height != (yend-ybegin)) { // Deal with the case of reading not a whole number of tiles -- // OpenEXR will happily overwrite user memory in this case. tmpbuf.reset (new char [nxtiles * nytiles * m_spec.tile_bytes(true)]); data = &tmpbuf[0]; } char *buf = (char *)data - xbegin * pixelbytes - ybegin * pixelbytes * m_spec.tile_width * nxtiles; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = chbegin; c < chend; ++c) { size_t chanbytes = m_spec.channelformat(c).size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (part.pixeltype[c], buf + chanoffset, pixelbytes, pixelbytes*m_spec.tile_width*nxtiles)); chanoffset += chanbytes; } if (m_input_tiled) { m_input_tiled->setFrameBuffer (frameBuffer); m_input_tiled->readTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); #ifdef USE_OPENEXR_VERSION2 } else if (m_tiled_input_part) { m_tiled_input_part->setFrameBuffer (frameBuffer); m_tiled_input_part->readTiles (firstxtile, firstxtile+nxtiles-1, firstytile, firstytile+nytiles-1, m_miplevel, m_miplevel); #endif } else { ASSERT (0); } if (data != origdata) { stride_t user_scanline_bytes = (xend-xbegin) * pixelbytes; stride_t scanline_stride = nxtiles*m_spec.tile_width*pixelbytes; for (int y = ybegin; y < yend; ++y) memcpy ((char *)origdata+(y-ybegin)*scanline_stride, (char *)data+(y-ybegin)*scanline_stride, user_scanline_bytes); } } catch (const std::exception &e) { error ("Failed OpenEXR read: %s", e.what()); return false; } return true; }
static std::unique_ptr<float[]> loadExr(const Path &path, TexelConversion request, int &w, int &h) { InputStreamHandle inputStream = FileUtils::openInputStream(path); if (!inputStream) return nullptr; try { ExrIStream in(std::move(inputStream)); Imf::InputFile file(in); Imath::Box2i dataWindow = file.header().dataWindow(); w = dataWindow.max.x - dataWindow.min.x + 1; h = dataWindow.max.y - dataWindow.min.y + 1; int dx = dataWindow.min.x; int dy = dataWindow.min.y; const Imf::ChannelList &channels = file.header().channels(); const Imf::Channel *rChannel = channels.findChannel("R"); const Imf::Channel *gChannel = channels.findChannel("G"); const Imf::Channel *bChannel = channels.findChannel("B"); const Imf::Channel *aChannel = channels.findChannel("A"); const Imf::Channel *yChannel = channels.findChannel("Y"); Imf::FrameBuffer frameBuffer; bool isScalar = request != TexelConversion::REQUEST_RGB; bool acceptsAlpha = request == TexelConversion::REQUEST_ALPHA || request == TexelConversion::REQUEST_AUTO; int targetChannels = isScalar ? 1 : 3; int sourceChannels = (rChannel ? 1 : 0) + (gChannel ? 1 : 0) + (bChannel ? 1 : 0); std::unique_ptr<float[]> texels(new float[w*h*targetChannels]); std::unique_ptr<float[]> img; size_t texelSize = sizeof(float)*targetChannels; char *base = reinterpret_cast<char *>(texels.get() - (dx + dy*w)*targetChannels); if (isScalar && (yChannel != nullptr || (acceptsAlpha && aChannel != nullptr))) { // The user requested a scalar texture and the file either contains an alpha channel or a luminance channel // The alpha channel is only allowed if it was explicitly requested or an auto conversion was requested // RGB -> Scalar falls through and is handled later const char *channelName = (acceptsAlpha && aChannel != nullptr) ? "A" : "Y"; frameBuffer.insert(channelName, Imf::Slice(Imf::FLOAT, base, texelSize, texelSize*w)); } else if (!isScalar && (rChannel || gChannel || bChannel)) { // The user wants RGB and we have (some) RGB channels if (rChannel) frameBuffer.insert("R", Imf::Slice(Imf::FLOAT, base + 0*sizeof(float), texelSize, texelSize*w)); if (gChannel) frameBuffer.insert("G", Imf::Slice(Imf::FLOAT, base + 1*sizeof(float), texelSize, texelSize*w)); if (bChannel) frameBuffer.insert("B", Imf::Slice(Imf::FLOAT, base + 2*sizeof(float), texelSize, texelSize*w)); // If some channels are missing, replace them with black if (!rChannel || !gChannel || !bChannel) std::memset(texels.get(), 0, texelSize*w*h); } else if (!isScalar && yChannel) { // The user wants RGB and we have just luminance -> duplicate luminance across all three channels // This is not the best solution, but we don't want to deal with chroma subsampled images frameBuffer.insert("Y", Imf::Slice(Imf::FLOAT, base, texelSize, texelSize*w)); } else if (isScalar) { // The user wants a scalar texture and we have (some) RGB channels // We can't read directly into the destination, but have to read to a temporary // and do a conversion to scalar later img.reset(new float[sourceChannels*w*h]); char *imgBase = reinterpret_cast<char *>(img.get() - (dx + dy*w)*sourceChannels); const Imf::Channel *channels[] = {rChannel, gChannel, bChannel}; const char *channelNames[] = {"R", "G", "B"}; int texel = 0; for (int i = 0; i < 3; ++i) { if (channels[i]) { frameBuffer.insert(channelNames[i], Imf::Slice(Imf::FLOAT, imgBase + texel*sizeof(float), sourceChannels*sizeof(float), sourceChannels*sizeof(float)*w)); texel++; } } } else { return false; } file.setFrameBuffer(frameBuffer); file.readPixels(dataWindow.min.y, dataWindow.max.y); if (img) { // We only get here if we need to make a scalar texture from an RGB input int texel = 0; int rLocation = -1, gLocation = -1, bLocation = -1; if (rChannel) rLocation = texel++; if (gChannel) gLocation = texel++; if (bChannel) bLocation = texel++; for (int i = 0; i < w*h; ++i) { float r = rChannel ? img[i*sourceChannels + rLocation] : 0.0f; float g = gChannel ? img[i*sourceChannels + gLocation] : 0.0f; float b = bChannel ? img[i*sourceChannels + bLocation] : 0.0f; texels[i] = convertToScalar(request, r, g, b, 0.0f, false); } } else if (!isScalar && yChannel) { // Here we need to duplicate the Y channel stored in R across G and B for (int i = 0; i < w*h; ++i) texels[i*3 + 1] = texels[i*3 + 2] = texels[i*3]; } return std::move(texels); } catch(const std::exception &e) { std::cout << "OpenEXR loader failed: " << e.what() << std::endl; return nullptr; } }
bool OpenEXROutput::write_scanlines (int ybegin, int yend, int z, TypeDesc format, const void *data, stride_t xstride, stride_t ystride) { yend = std::min (yend, spec().y+spec().height); bool native = (format == TypeDesc::UNKNOWN); imagesize_t scanlinebytes = spec().scanline_bytes(native); size_t pixel_bytes = m_spec.pixel_bytes (native); if (native && xstride == AutoStride) xstride = (stride_t) pixel_bytes; stride_t zstride = AutoStride; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, m_spec.width, m_spec.height); const imagesize_t limit = 16*1024*1024; // Allocate 16 MB, or 1 scanline int chunk = std::max (1, int(limit / scanlinebytes)); bool ok = true; for ( ; ok && ybegin < yend; ybegin += chunk) { int y1 = std::min (ybegin+chunk, yend); int nscanlines = y1 - ybegin; const void *d = to_native_rectangle (m_spec.x, m_spec.x+m_spec.width, ybegin, y1, z, z+1, format, data, xstride, ystride, zstride, m_scratch); // Compute where OpenEXR needs to think the full buffers starts. // OpenImageIO requires that 'data' points to where the client wants // to put the pixels being read, but OpenEXR's frameBuffer.insert() // wants where the address of the "virtual framebuffer" for the // whole image. char *buf = (char *)d - m_spec.x * pixel_bytes - ybegin * scanlinebytes; try { Imf::FrameBuffer frameBuffer; size_t chanoffset = 0; for (int c = 0; c < m_spec.nchannels; ++c) { size_t chanbytes = m_spec.channelformats.size() ? m_spec.channelformats[c].size() : m_spec.format.size(); frameBuffer.insert (m_spec.channelnames[c].c_str(), Imf::Slice (m_pixeltype[c], buf + chanoffset, pixel_bytes, scanlinebytes)); chanoffset += chanbytes; } m_output_scanline->setFrameBuffer (frameBuffer); m_output_scanline->writePixels (nscanlines); } catch (const std::exception &e) { error ("Failed OpenEXR write: %s", e.what()); return false; } data = (const char *)data + ystride*nscanlines; } // If we allocated more than 1M, free the memory. It's not wasteful, // because it means we're writing big chunks at a time, and therefore // there will be few allocations and deletions. if (m_scratch.size() > 1*1024*1024) { std::vector<unsigned char> dummy; std::swap (m_scratch, dummy); } return true; }
// All the work is done here int joinEXRs( int tilesX, int tilesY, const char* baseName, bool deleteTiles, bool Verbose ) { int exitCode = 0; // Expand names if( Verbose ) printf("Image file name = '%s', tilesX=%d, tilesY=%d\n", baseName, tilesX, tilesY); int numTiles = tilesX * tilesY; // Allocate memory: char ** tileNames = new char * [numTiles]; Imf::InputFile ** iFiles = new Imf::InputFile * [numTiles]; for( int i = 0; i < numTiles; i++) { tileNames[i] = new char[FILENAME_MAXLEN]; iFiles[i] = 0; } // Insert tile info and check if files exist int nonEmptyTile = -1; struct stat stFileInfo; for( int i = 0; i < numTiles; i++) { sprintf( tileNames[i], "%s.tile_%d.exr", baseName, i); if( Verbose ) printf("Tile name %d = '%s'\n", i, tileNames[i]); if( stat( tileNames[i], &stFileInfo ) == 0 ) { // File exists - so open it and check for validness iFiles[i] = new Imf::InputFile( tileNames[i]); if( false == iFiles[i]->isComplete()) { fprintf( stderr, "Error: File '%s' is incomplete or is not an OpenEXR file.\n", tileNames[i]); fflush( stderr); delete iFiles[i]; iFiles[i] = 0; exitCode = 1; } else if( nonEmptyTile == -1 ) { nonEmptyTile = i; } } else { fprintf( stderr, "Error: File '%s' not founded.\n", tileNames[i]); fflush( stderr); exitCode = 1; } } if( nonEmptyTile < 0) // All tiles were empty { fprintf( stderr, "Error: No tile files founded.\n"); fflush( stderr); } else { // Gather info from a non-empty tile file Imf::Header inHeader = iFiles[nonEmptyTile]->header(); Imath::Box2i imageBox = inHeader.displayWindow(); // size of the resulting image int imageWidth = imageBox.max.x - imageBox.min.x + 1; int imageHeight = imageBox.max.y - imageBox.min.y + 1; // Iterate through all the channels and reserve mem for the whole display window // also add channels to the header of the output file Imf::Header outHeader( imageWidth, imageHeight); std::map< Imf::Name, ChannelInfo* > chInfos; // this will hold pixel data and stride for each channel in input files Imf::ChannelList channels = inHeader.channels(); Imf::ChannelList::ConstIterator itCh; for( itCh = channels.begin(); itCh != channels.end(); itCh++ ) { chInfos[itCh.name()] = new ChannelInfo( typeSize( itCh.channel().type), imageHeight, imageWidth ); outHeader.channels().insert( itCh.name(), Imf::Channel( itCh.channel().type)); if( Verbose) printf("Channel: '%s' | stride: %d\n", itCh.name(), typeSize( itCh.channel().type)); } // Collect data from files Imath::Box2i tileBox; // each tile's data window Imath::Box2i resultBox; // resulting data window (should be sum of all tiles' data windows) Imf::FrameBuffer fb; for( int i = 0; i < numTiles; i++) { if( iFiles[i] == 0) // no file for this tile continue; tileBox = iFiles[i]->header().dataWindow(); resultBox.extendBy( tileBox ); if( Verbose) printf("Data win: xmin=%d xmax=%d ymin=%d ymax=%d\n", tileBox.min.x, tileBox.max.x, tileBox.min.y, tileBox.max.y); channels = iFiles[i]->header().channels(); for( itCh = channels.begin(); itCh != channels.end(); itCh++ ) fb.insert( itCh.name(), Imf::Slice( itCh.channel().type, // pixel type (char*)&chInfos[itCh.name()]->array2d[0][0], // base chInfos[itCh.name()]->stride, // x stride chInfos[itCh.name()]->stride * imageWidth, // y stride 1, 1, 0.0 ) ); // x,y sampling, fill value iFiles[i]->setFrameBuffer(fb); iFiles[i]->readPixels( tileBox.min.y, tileBox.max.y); } // Write out everything: outHeader.dataWindow() = resultBox; Imf::OutputFile imageFile( baseName, outHeader ); imageFile.setFrameBuffer(fb); imageFile.writePixels( resultBox.max.y-resultBox.min.y+1 ); printf("Joined EXR image successfully written.\n"); // Free files: for( int i = 0; i < numTiles; i++) if( iFiles[i] != 0 ) delete iFiles[i]; delete [] iFiles; if( deleteTiles ) { for( int i = 0; i < numTiles; i++) if( unlink( tileNames[i]) != 0) { perror("Remove"); printf("Can't remove file '%s'\n", tileNames[i]); } } } // Free names: for( int i = 0; i < numTiles; i++) delete [] tileNames[i]; delete [] tileNames; return exitCode; }
int FileEXR::write_frame(VFrame *frame, VFrame *data, FrameWriterUnit *unit) { EXRUnit *exr_unit = (EXRUnit*)unit; VFrame *output_frame; data->set_compressed_size(0); int native_cmodel = asset->exr_use_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT; int components = cmodel_components(native_cmodel); if(frame->get_color_model() != native_cmodel) { if(!exr_unit->temp_frame) exr_unit->temp_frame = new VFrame(0, -1, asset->width, asset->height, native_cmodel, -1); BC_CModels::transfer(exr_unit->temp_frame->get_rows(), /* Leave NULL if non existent */ frame->get_rows(), exr_unit->temp_frame->get_y(), /* Leave NULL if non existent */ exr_unit->temp_frame->get_u(), exr_unit->temp_frame->get_v(), frame->get_y(), /* Leave NULL if non existent */ frame->get_u(), frame->get_v(), 0, /* Dimensions to capture from input frame */ 0, asset->width, asset->height, 0, /* Dimensions to project on output frame */ 0, asset->width, asset->height, frame->get_color_model(), native_cmodel, 0, /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */ asset->width, /* For planar use the luma rowspan */ asset->height); output_frame = exr_unit->temp_frame; } else output_frame = frame; Imf::Header header(output_frame->get_w(), output_frame->get_h()); header.compression() = (Imf::Compression)compression_to_exr( asset->exr_compression); header.channels().insert("R", Imf::Channel(Imf::FLOAT)); header.channels().insert("G", Imf::Channel(Imf::FLOAT)); header.channels().insert("B", Imf::Channel(Imf::FLOAT)); if(asset->exr_use_alpha) header.channels().insert("A", Imf::Channel(Imf::FLOAT)); EXROStream exr_stream(data); Imf::OutputFile file(exr_stream, header); Imf::FrameBuffer framebuffer; float **rows = (float**)output_frame->get_rows(); framebuffer.insert("R", Imf::Slice(Imf::FLOAT, (char*)(rows[0]), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); framebuffer.insert("G", Imf::Slice(Imf::FLOAT, (char*)(rows[0] + 1), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); framebuffer.insert("B", Imf::Slice(Imf::FLOAT, (char*)(rows[0] + 2), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); if(asset->exr_use_alpha) framebuffer.insert("A", Imf::Slice(Imf::FLOAT, (char*)(rows[0] + 3), sizeof(float) * components, sizeof(float) * components * output_frame->get_w())); file.setFrameBuffer(framebuffer); file.writePixels(asset->height); return 0; }
bool ImageWriterEXR::writeStandardImage(const std::string& filePath, const OutputImage& image, unsigned int channels, bool fullFloat) { unsigned int width = image.getWidth(); unsigned int height = image.getHeight(); Imf::Header header(width, height); Imf::StringAttribute sourceAttribute; sourceAttribute.value() = "Created with Imagine 0.98"; header.insert("comments", sourceAttribute); Imf::PixelType pixelType = (fullFloat) ? Imf::FLOAT : Imf::HALF; if (channels & ImageWriter::RGB) { header.channels().insert("R", Imf::Channel(pixelType)); header.channels().insert("G", Imf::Channel(pixelType)); header.channels().insert("B", Imf::Channel(pixelType)); } if (channels & ImageWriter::ALPHA) header.channels().insert("A", Imf::Channel(pixelType)); // if we haven't got any depth data, don't bother if (!(image.components() & COMPONENT_DEPTH)) channels = channels & ~ImageWriter::DEPTH; if (channels & ImageWriter::DEPTH) header.channels().insert("Z", Imf::Channel(pixelType)); // if we haven't got any normal data, don't bother if (!(image.components() & COMPONENT_NORMAL)) channels = channels & ~ImageWriter::NORMALS; if (channels & ImageWriter::NORMALS) { header.channels().insert("normal.X", Imf::Channel(pixelType)); header.channels().insert("normal.Y", Imf::Channel(pixelType)); header.channels().insert("normal.Z", Imf::Channel(pixelType)); } // if we haven't got any wpp data, don't bother if (!(image.components() & COMPONENT_WPP)) channels = channels & ~ImageWriter::WPP; if (channels & ImageWriter::WPP) { header.channels().insert("wpp.X", Imf::Channel(pixelType)); header.channels().insert("wpp.Y", Imf::Channel(pixelType)); header.channels().insert("wpp.Z", Imf::Channel(pixelType)); } // if we haven't got shadows, don't write them if (!(image.components() & COMPONENT_SHADOWS)) channels = channels & ~ImageWriter::SHADOWS; if (channels & ImageWriter::SHADOWS) { // cope with Nuke's limitations by putting ".r" on the end so it shows up in channel list... header.channels().insert("shadows.r", Imf::Channel(pixelType)); } unsigned int rgbStride = (channels & ImageWriter::ALPHA) ? 4 : 3; T* rgba = NULL; if (channels & ImageWriter::RGB) rgba = new T[width * height * rgbStride]; T* pDepth = NULL; if (channels & ImageWriter::DEPTH) pDepth = new T[width * height]; T* pNormal = NULL; if (channels & ImageWriter::NORMALS) pNormal = new T[width * height * 3]; T* pWPP = NULL; if (channels & ImageWriter::WPP) pWPP = new T[width * height * 3]; T* pShadows = NULL; if (channels & ImageWriter::SHADOWS) pShadows = new T[width * height]; const Colour4f* pRow = NULL; const float* pDepthRow = NULL; const Colour3f* pNormalRow = NULL; const Colour3f* pWPPRow = NULL; const float* pShadowsRow = NULL; for (unsigned int y = 0; y < height; y++) { pRow = image.colourRowPtr(y); if (channels & ImageWriter::DEPTH) pDepthRow = image.depthRowPtr(y); if (channels & ImageWriter::NORMALS) pNormalRow = image.normalRowPtr(y); if (channels & ImageWriter::WPP) pWPPRow = image.wppRowPtr(y); if (channels & ImageWriter::SHADOWS) pShadowsRow = image.shadowsRowPtr(y); unsigned int rgbStartPos = width * y * rgbStride; unsigned int normalStartPos = width * y * 3; unsigned int wppStartPos = width * y * 3; for (unsigned int x = 0; x < width; x++) { unsigned int pixelPos = rgbStartPos + (x * rgbStride); if (channels & ImageWriter::RGB) { float red = ColourSpace::convertSRGBToLinearAccurate(pRow->r); float green = ColourSpace::convertSRGBToLinearAccurate(pRow->g); float blue = ColourSpace::convertSRGBToLinearAccurate(pRow->b); rgba[pixelPos++] = red; rgba[pixelPos++] = green; rgba[pixelPos++] = blue; } else { pixelPos += 3; } if (channels & ImageWriter::ALPHA) rgba[pixelPos++] = pRow->a; if (channels & ImageWriter::DEPTH) { pDepth[(width * y) + x] = *pDepthRow++; } if (channels & ImageWriter::NORMALS && pNormalRow) { const Colour3f& normal = *pNormalRow; unsigned int normalPixelPos = normalStartPos + (x * 3); pNormal[normalPixelPos++] = normal.r; pNormal[normalPixelPos++] = normal.g; pNormal[normalPixelPos++] = normal.b; pNormalRow++; } if (channels & ImageWriter::WPP && pWPPRow) { const Colour3f& wpp = *pWPPRow; unsigned int wppPixelPos = wppStartPos + (x * 3); pWPP[wppPixelPos++] = wpp.r; pWPP[wppPixelPos++] = wpp.g; pWPP[wppPixelPos++] = wpp.b; pWPPRow++; } if (channels & ImageWriter::SHADOWS) { pShadows[(width * y) + x] = *pShadowsRow++; } pRow++; } } Imf::FrameBuffer fb; if (channels & ImageWriter::RGB) { fb.insert("R", Imf::Slice(pixelType, (char *)rgba, rgbStride*sizeof(T), rgbStride*width*sizeof(T))); fb.insert("G", Imf::Slice(pixelType, (char *)rgba + sizeof(T), rgbStride*sizeof(T), rgbStride*width*sizeof(T))); fb.insert("B", Imf::Slice(pixelType, (char *)rgba + 2 * sizeof(T), rgbStride*sizeof(T), rgbStride*width*sizeof(T))); } if (channels & ImageWriter::ALPHA) fb.insert("A", Imf::Slice(pixelType, (char *)rgba+3*sizeof(T), rgbStride*sizeof(T), rgbStride*width*sizeof(T))); if (channels & ImageWriter::DEPTH) { fb.insert("Z", Imf::Slice(pixelType, (char *)&(*pDepth), sizeof(T), width * sizeof(T))); } if (channels & ImageWriter::NORMALS) { fb.insert("normal.X", Imf::Slice(pixelType, (char *)pNormal, 3 * sizeof(T), 3 * width * sizeof(T))); fb.insert("normal.Y", Imf::Slice(pixelType, (char *)pNormal + sizeof(T), 3*sizeof(T), 3*width*sizeof(T))); fb.insert("normal.Z", Imf::Slice(pixelType, (char *)pNormal + 2 * sizeof(T), 3*sizeof(T), 3*width*sizeof(T))); } if (channels & ImageWriter::WPP) { fb.insert("wpp.X", Imf::Slice(pixelType, (char *)pWPP, 3 * sizeof(T), 3 * width * sizeof(T))); fb.insert("wpp.Y", Imf::Slice(pixelType, (char *)pWPP + sizeof(T), 3*sizeof(T), 3*width*sizeof(T))); fb.insert("wpp.Z", Imf::Slice(pixelType, (char *)pWPP + 2 * sizeof(T), 3*sizeof(T), 3*width*sizeof(T))); } if (channels & ImageWriter::SHADOWS) { fb.insert("shadows.r", Imf::Slice(pixelType, (char *)&(*pShadows), sizeof(T), width * sizeof(T))); } Imf::OutputFile file(filePath.c_str(), header); file.setFrameBuffer(fb); file.writePixels(height); if (rgba) delete [] rgba; if (pDepth) delete [] pDepth; if (pNormal) delete [] pNormal; if (pWPP) delete [] pWPP; if (pShadows) delete [] pShadows; return true; }