void EXRImageFileWriter::write( const char* filename, const ICanvas& image, const ImageAttributes& image_attributes) { initialize_openexr(); try { // Retrieve canvas properties. const CanvasProperties& props = image.properties(); // todo: lift this limitation. assert(props.m_channel_count <= 4); // Figure out the pixel type, based on the pixel format of the image. PixelType pixel_type = FLOAT; switch (props.m_pixel_format) { case PixelFormatUInt32: pixel_type = UINT; break; case PixelFormatHalf: pixel_type = HALF; break; case PixelFormatFloat: pixel_type = FLOAT; break; default: throw ExceptionUnsupportedImageFormat(); } // Construct TileDescription object. const TileDescription tile_desc( static_cast<unsigned int>(props.m_tile_width), static_cast<unsigned int>(props.m_tile_height), ONE_LEVEL); // Construct ChannelList object. ChannelList channels; for (size_t c = 0; c < props.m_channel_count; ++c) channels.insert(ChannelName[c], Channel(pixel_type)); // Construct Header object. Header header( static_cast<int>(props.m_canvas_width), static_cast<int>(props.m_canvas_height)); header.setTileDescription(tile_desc); header.channels() = channels; // Add image attributes to the Header object. add_attributes(image_attributes, header); // Create the output file. TiledOutputFile file(filename, header); // Write tiles. for (size_t y = 0; y < props.m_tile_count_y; ++y) { for (size_t x = 0; x < props.m_tile_count_x; ++x) { const int ix = static_cast<int>(x); const int iy = static_cast<int>(y); const Box2i range = file.dataWindowForTile(ix, iy); const Tile& tile = image.tile(x, y); const size_t channel_size = Pixel::size(tile.get_pixel_format()); const size_t stride_x = channel_size * props.m_channel_count; const size_t stride_y = stride_x * tile.get_width(); const size_t tile_origin = range.min.x * stride_x + range.min.y * stride_y; const char* tile_base = reinterpret_cast<const char*>(tile.pixel(0, 0)) - tile_origin; // Construct FrameBuffer object. FrameBuffer framebuffer; for (size_t c = 0; c < props.m_channel_count; ++c) { const char* base = tile_base + c * channel_size; framebuffer.insert( ChannelName[c], Slice( pixel_type, const_cast<char*>(base), stride_x, stride_y)); } // Write tile. file.setFrameBuffer(framebuffer); file.writeTile(ix, iy); } } } catch (const BaseExc& e) { // I/O error. throw ExceptionIOError(e.what()); } }
void ProgressiveEXRImageFileReader::open(const char* filename) { assert(filename); assert(!is_open()); initialize_openexr(); try { try { // Open the file for reading, assuming the file is tiled. impl->m_tiled_file.reset(new TiledInputFile(filename)); impl->m_is_tiled = true; } catch (const ArgExc&) { // The file is not tiled, open it as a scanline file. impl->m_scanline_file.reset(new InputFile(filename)); impl->m_is_tiled = false; // Print a warning message if a logger was provided. if (impl->m_logger) { LOG_WARNING( *impl->m_logger, "the texture file %s is not tiled, performances might suffer.", filename); } } // Retrieve the file header, and perform basic sanity checks on it. const Header& header = impl->m_is_tiled ? impl->m_tiled_file->header() : impl->m_scanline_file->header(); header.sanityCheck(impl->m_is_tiled); // Retrieve the image channels. const ChannelList& channels = header.channels(); impl->m_red = channels.findChannel("R"); impl->m_green = channels.findChannel("G"); impl->m_blue = channels.findChannel("B"); impl->m_alpha = channels.findChannel("A"); if (!(impl->m_red && impl->m_green && impl->m_blue)) throw ExceptionUnsupportedImageFormat(); // Make sure all channels use the same pixel format. if (!(impl->m_red->type == impl->m_green->type && impl->m_red->type == impl->m_blue->type)) throw ExceptionUnsupportedImageFormat(); if (impl->m_alpha) { if (impl->m_red->type != impl->m_alpha->type) throw ExceptionUnsupportedImageFormat(); } // Figure out the pixel format, based on the pixel type of the file. PixelFormat pixel_format = PixelFormatFloat; switch (impl->m_red->type) { case UINT: pixel_format = PixelFormatUInt32; break; case HALF: pixel_format = PixelFormatHalf; break; case FLOAT: pixel_format = PixelFormatFloat; break; default: throw ExceptionUnsupportedImageFormat(); } // Retrieve the dimensions of the image. impl->m_dw = header.dataWindow(); // Compute the dimensions of the canvas. const size_t canvas_width = static_cast<size_t>(impl->m_dw.max.x - impl->m_dw.min.x + 1); const size_t canvas_height = static_cast<size_t>(impl->m_dw.max.y - impl->m_dw.min.y + 1); // Retrieve the dimensions of the tiles. size_t tile_width, tile_height; if (impl->m_is_tiled) { assert(header.hasTileDescription()); const TileDescription& td = header.tileDescription(); tile_width = static_cast<size_t>(td.xSize); tile_height = static_cast<size_t>(td.ySize); } else { tile_width = impl->m_default_tile_width; tile_height = impl->m_default_tile_height; } // Retrieve the number of tiles in each direction. size_t tile_count_x, tile_count_y; if (impl->m_is_tiled) { tile_count_x = static_cast<size_t>(impl->m_tiled_file->numXTiles()); tile_count_y = static_cast<size_t>(impl->m_tiled_file->numYTiles()); } else { const double nx = ceil(static_cast<double>(canvas_width) / tile_width); const double ny = ceil(static_cast<double>(canvas_height) / tile_height); tile_count_x = truncate<size_t>(nx); tile_count_y = truncate<size_t>(ny); } // Set canvas properties. impl->m_props.m_canvas_width = canvas_width; impl->m_props.m_canvas_height = canvas_height; impl->m_props.m_rcp_canvas_width = 1.0 / impl->m_props.m_canvas_width; impl->m_props.m_rcp_canvas_height = 1.0 / impl->m_props.m_canvas_height; impl->m_props.m_pixel_count = impl->m_props.m_canvas_width * impl->m_props.m_canvas_height; impl->m_props.m_tile_width = tile_width; impl->m_props.m_tile_height = tile_height; impl->m_props.m_rcp_tile_width = 1.0 / impl->m_props.m_tile_width; impl->m_props.m_rcp_tile_height = 1.0 / impl->m_props.m_tile_height; impl->m_props.m_tile_count_x = tile_count_x; impl->m_props.m_tile_count_y = tile_count_y; impl->m_props.m_tile_count = impl->m_props.m_tile_count_x * impl->m_props.m_tile_count_y; impl->m_props.m_pixel_format = pixel_format; impl->m_props.m_channel_count = impl->m_alpha ? 4 : 3; impl->m_props.m_pixel_size = impl->m_props.m_channel_count * Pixel::size(impl->m_props.m_pixel_format); // Allocate memory to store scanlines, if the file is not tiled. if (!impl->m_is_tiled) { const size_t size = impl->m_props.m_canvas_width * impl->m_props.m_pixel_size * tile_height; impl->m_scanlines.resize(size); } // No tile previously accessed. impl->m_last_tile_y = ~0; } catch (const BaseExc& e) { // I/O error. throw ExceptionIOError(e.what()); } }