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 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_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 (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; }
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; } }
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; }