Exemple #1
0
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());
}
Exemple #2
0
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
}
Exemple #3
0
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
}
Exemple #4
0
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;
}
Exemple #5
0
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());
}
/** \brief Convert an OpenEXR header to our own header representation.
 *
 * \param exrHeader - input header
 * \param header - output header
 */
void convertHeader(const Imf::Header& exrHeader, CqTexFileHeader& header)
{
	// Set width, height
	const Imath::Box2i& dataBox = exrHeader.dataWindow();
	header.setWidth(dataBox.max.x - dataBox.min.x+1);
	header.setHeight(dataBox.max.y - dataBox.min.y+1);
	// display window
	const Imath::Box2i& displayBox = exrHeader.displayWindow();
	header.set<Attr::DisplayWindow>( SqImageRegion(
				displayBox.max.x - displayBox.min.x,
				displayBox.max.y - displayBox.min.y,
				displayBox.min.x - dataBox.min.x,
				displayBox.min.y - dataBox.min.y) );

	// Set tiling information ?

	// Aspect ratio
	header.set<Attr::PixelAspectRatio>(exrHeader.pixelAspectRatio());

	TqChannelNameMap channelNameMap;
	// Convert channel representation
	const Imf::ChannelList& exrChannels = exrHeader.channels();
	CqChannelList& channels = header.channelList();
	for(Imf::ChannelList::ConstIterator i = exrChannels.begin();
			i != exrChannels.end(); ++i)
	{
		// use lower case names for channels; OpenEXR uses upper case.
		std::string chanName = i.name();
		std::transform(chanName.begin(), chanName.end(), chanName.begin(),
				::tolower);
		channelNameMap[chanName] = i.name();
		channels.addChannel( SqChannelInfo(chanName,
				channelTypeFromExr(i.channel().type)) );
	}
	header.set<Attr::ExrChannelNameMap>(channelNameMap);
	channels.reorderChannels();

	// Set compresssion type
	header.set<Attr::Compression>(exrCompressionToString(exrHeader.compression()));
}
Exemple #7
0
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;
    }
}
Exemple #8
0
// 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;
}
Exemple #9
0
void
OpenEXRInput::query_channels (void)
{
    m_spec.nchannels = 0;
    const Imf::ChannelList &channels (m_header->channels());
    Imf::ChannelList::ConstIterator ci;
    int c;
    int red = -1, green = -1, blue = -1, alpha = -1, zee = -1;
    for (c = 0, ci = channels.begin();  ci != channels.end();  ++c, ++ci) {
        // std::cerr << "Channel " << ci.name() << '\n';
        const char* name = ci.name();
        m_channelnames.push_back (name);
        if (red < 0 && (iequals(name, "R") || iequals(name, "Red") ||
                        iends_with(name,".R") || iends_with(name,".Red")))
            red = c;
        if (green < 0 && (iequals(name, "G") || iequals(name, "Green") ||
                          iends_with(name,".G") || iends_with(name,".Green")))
            green = c;
        if (blue < 0 && (iequals(name, "B") || iequals(name, "Blue") ||
                         iends_with(name,".B") || iends_with(name,".Blue")))
            blue = c;
        if (alpha < 0 && (iequals(name, "A") || iequals(name, "Alpha") ||
                          iends_with(name,".A") || iends_with(name,".Alpha")))
            alpha = c;
        if (zee < 0 && (iequals(name, "Z") || iequals(name, "Depth") ||
                        iends_with(name,".Z") || iends_with(name,".Depth")))
            zee = c;
        ++m_spec.nchannels;
    }
    m_userchannels.resize (m_spec.nchannels);
    int nc = 0;
    if (red >= 0) {
        m_spec.channelnames.push_back (m_channelnames[red]);
        m_userchannels[red] = nc++;
    }
    if (green >= 0) {
        m_spec.channelnames.push_back (m_channelnames[green]);
        m_userchannels[green] = nc++;
    }
    if (blue >= 0) {
        m_spec.channelnames.push_back (m_channelnames[blue]);
        m_userchannels[blue] = nc++;
    }
    if (alpha >= 0) {
        m_spec.channelnames.push_back (m_channelnames[alpha]);
        m_spec.alpha_channel = nc;
        m_userchannels[alpha] = nc++;
    }
    if (zee >= 0) {
        m_spec.channelnames.push_back (m_channelnames[zee]);
        m_spec.z_channel = nc;
        m_userchannels[zee] = nc++;
    }
    for (c = 0, ci = channels.begin();  ci != channels.end();  ++c, ++ci) {
        if (red == c || green == c || blue == c || alpha == c || zee == c)
            continue;   // Already accounted for this channel
        m_userchannels[c] = nc;
        m_spec.channelnames.push_back (ci.name());
        ++nc;
    }
    ASSERT ((int)m_spec.channelnames.size() == m_spec.nchannels);
    // FIXME: should we also figure out the layers?

    // Figure out data types -- choose the highest range
    m_spec.format = TypeDesc::UNKNOWN;
    std::vector<TypeDesc> chanformat;
    bool differing_chanformats = false;
    for (c = 0, ci = channels.begin();  ci != channels.end();  ++c, ++ci) {
        Imf::PixelType ptype = ci.channel().type;
        TypeDesc fmt = TypeDesc::HALF;
        switch (ptype) {
        case Imf::UINT :
            fmt = TypeDesc::UINT;
            if (m_spec.format == TypeDesc::UNKNOWN)
                m_spec.format = TypeDesc::UINT;
            break;
        case Imf::HALF :
            fmt = TypeDesc::HALF;
            if (m_spec.format != TypeDesc::FLOAT)
                m_spec.format = TypeDesc::HALF;
            break;
        case Imf::FLOAT :
            fmt = TypeDesc::FLOAT;
            m_spec.format = TypeDesc::FLOAT;
            break;
        default: ASSERT (0);
        }
        chanformat.push_back (fmt);
        m_pixeltype.push_back (ptype);
        if (fmt != chanformat[0])
            differing_chanformats = true;
    }
    ASSERT (m_spec.format != TypeDesc::UNKNOWN);
    if (differing_chanformats)
        m_spec.channelformats = chanformat;
}
Exemple #10
0
bool
OpenEXROutput::open (const std::string &name, const ImageSpec &userspec,
                     OpenMode mode)
{
    if (mode == AppendSubimage) {
        error ("%s does not support subimages", format_name());
        return false;
    }

    if (mode == AppendMIPLevel && (m_output_scanline || m_output_tiled)) {
        // Special case for appending to an open file -- we don't need
        // to close and reopen
        if (m_spec.tile_width && m_levelmode != Imf::ONE_LEVEL) {
            // OpenEXR does not support differing tile sizes on different
            // MIP-map levels.  Reject the open() if not using the original
            // tile sizes.
            if (userspec.tile_width != m_spec.tile_width ||
                userspec.tile_height != m_spec.tile_height) {
                error ("OpenEXR tiles must have the same size on all MIPmap levels");
                return false;
            }
            // Copy the new mip level size.  Keep everything else from the
            // original level.
            m_spec.width = userspec.width;
            m_spec.height = userspec.height;
            // N.B. do we need to copy anything else from userspec?
            ++m_miplevel;
            return true;
        }
    }

    m_spec = userspec;  // Stash the spec

    if (m_spec.width < 1 || m_spec.height < 1) {
        error ("Image resolution must be at least 1x1, you asked for %d x %d",
               userspec.width, userspec.height);
        return false;
    }
    if (m_spec.depth < 1)
        m_spec.depth = 1;
    if (m_spec.depth > 1) {
        error ("%s does not support volume images (depth > 1)", format_name());
        return false;
    }

    if (m_spec.full_width <= 0)
        m_spec.full_width = m_spec.width;
    if (m_spec.full_height <= 0)
        m_spec.full_height = m_spec.height;

    // Force use of one of the three data types that OpenEXR supports
    switch (m_spec.format.basetype) {
    case TypeDesc::UINT:
        m_spec.format = TypeDesc::UINT;
        break;
    case TypeDesc::FLOAT:
    case TypeDesc::DOUBLE:
        m_spec.format = TypeDesc::FLOAT;
        break;
    default:
        // Everything else defaults to half
        m_spec.format = TypeDesc::HALF;
    }

    Imath::Box2i dataWindow (Imath::V2i (m_spec.x, m_spec.y),
                             Imath::V2i (m_spec.width + m_spec.x - 1,
                                         m_spec.height + m_spec.y - 1));
    Imath::Box2i displayWindow (Imath::V2i (m_spec.full_x, m_spec.full_y),
                                Imath::V2i (m_spec.full_width+m_spec.full_x-1,
                                            m_spec.full_height+m_spec.full_y-1));
    m_header = new Imf::Header (displayWindow, dataWindow);

    // Insert channels into the header.  Also give the channels names if
    // the user botched it.
    static const char *default_chan_names[] = { "R", "G", "B", "A" };
    m_spec.channelnames.resize (m_spec.nchannels);
    for (int c = 0;  c < m_spec.nchannels;  ++c) {
        if (m_spec.channelnames[c].empty())
            m_spec.channelnames[c] = (c<4) ? default_chan_names[c]
                                           : Strutil::format ("unknown %d", c);
        TypeDesc format = m_spec.channelformats.size() ?
                                  m_spec.channelformats[c] : m_spec.format;
        Imf::PixelType ptype;
        switch (format.basetype) {
        case TypeDesc::UINT:
            ptype = Imf::UINT;
            format = TypeDesc::UINT;
            break;
        case TypeDesc::FLOAT:
        case TypeDesc::DOUBLE:
            ptype = Imf::FLOAT;
            format = TypeDesc::FLOAT;
            break;
        default:
            // Everything else defaults to half
            ptype = Imf::HALF;
            format = TypeDesc::HALF;
        }
        
#ifdef OPENEXR_VERSION_IS_1_6_OR_LATER
        // Hint to lossy compression methods that indicates whether
        // human perception of the quantity represented by this channel
        // is closer to linear or closer to logarithmic.  Compression
        // methods may optimize image quality by adjusting pixel data
        // quantization acording to this hint.
        
        bool pLinear = iequals (m_spec.get_string_attribute ("oiio:ColorSpace", "Linear"), "Linear");
#endif
        m_pixeltype.push_back (ptype);
        if (m_spec.channelformats.size())
            m_spec.channelformats[c] = format;
        m_header->channels().insert (m_spec.channelnames[c].c_str(),
                                     Imf::Channel(ptype, 1, 1
#ifdef OPENEXR_VERSION_IS_1_6_OR_LATER
                                     , pLinear
#endif
                                     ));
    }
    ASSERT (m_pixeltype.size() == (size_t)m_spec.nchannels);

    // Default to ZIP compression if no request came with the user spec.
    if (! m_spec.find_attribute("compression"))
        m_spec.attribute ("compression", "zip");

    // Default to increasingY line order, same as EXR.
    if (! m_spec.find_attribute("openexr:lineOrder"))
        m_spec.attribute ("openexr:lineOrder", "increasingY");

    // Automatically set date field if the client didn't supply it.
    if (! m_spec.find_attribute("DateTime")) {
        time_t now;
        time (&now);
        struct tm mytm;
        Sysutil::get_local_time (&now, &mytm);
        std::string date = Strutil::format ("%4d:%02d:%02d %2d:%02d:%02d",
                               mytm.tm_year+1900, mytm.tm_mon+1, mytm.tm_mday,
                               mytm.tm_hour, mytm.tm_min, mytm.tm_sec);
        m_spec.attribute ("DateTime", date);
    }

    m_nsubimages = 1;
    m_subimage = 0;
    m_nmiplevels = 1;
    m_miplevel = 0;

    // Figure out if we are a mipmap or an environment map
    ImageIOParameter *param = m_spec.find_attribute ("textureformat");
    const char *textureformat = param ? *(char **)param->data() : NULL;
    m_levelmode = Imf::ONE_LEVEL;  // Default to no MIP-mapping
    m_roundingmode = m_spec.get_int_attribute ("openexr:roundingmode",
                                               Imf::ROUND_DOWN);

    if (textureformat) {
        if (iequals (textureformat, "Plain Texture")) {
            m_levelmode = m_spec.get_int_attribute ("openexr:levelmode",
                                                    Imf::MIPMAP_LEVELS);
        } else if (iequals (textureformat, "CubeFace Environment")) {
            m_levelmode = m_spec.get_int_attribute ("openexr:levelmode",
                                                    Imf::MIPMAP_LEVELS);
            m_header->insert ("envmap", Imf::EnvmapAttribute(Imf::ENVMAP_CUBE));
        } else if (iequals (textureformat, "LatLong Environment")) {
            m_levelmode = m_spec.get_int_attribute ("openexr:levelmode",
                                                    Imf::MIPMAP_LEVELS);
            m_header->insert ("envmap", Imf::EnvmapAttribute(Imf::ENVMAP_LATLONG));
        } else if (iequals (textureformat, "Shadow")) {
            m_levelmode = Imf::ONE_LEVEL;  // Force one level for shadow maps
        }

        if (m_levelmode == Imf::MIPMAP_LEVELS) {
            // Compute how many mip levels there will be
            int w = m_spec.width;
            int h = m_spec.height;
            while (w > 1 && h > 1) {
                if (m_roundingmode == Imf::ROUND_DOWN) {
                    w = w / 2;
                    h = h / 2;
                } else {
                    w = (w + 1) / 2;
                    h = (h + 1) / 2;
                }
                w = std::max (1, w);
                h = std::max (1, h);
                ++m_nmiplevels;
            }
        }
    }

    // Deal with all other params
    for (size_t p = 0;  p < m_spec.extra_attribs.size();  ++p)
        put_parameter (m_spec.extra_attribs[p].name().string(),
                       m_spec.extra_attribs[p].type(),
                       m_spec.extra_attribs[p].data());

    try {
        if (m_spec.tile_width) {
            m_header->setTileDescription (
                Imf::TileDescription (m_spec.tile_width, m_spec.tile_height,
                                      Imf::LevelMode(m_levelmode),
                                      Imf::LevelRoundingMode(m_roundingmode)));
            m_output_tiled = new Imf::TiledOutputFile (name.c_str(), *m_header);
        } else {
            m_output_scanline = new Imf::OutputFile (name.c_str(), *m_header);
        }
    }
    catch (const std::exception &e) {
        error ("OpenEXR exception: %s", e.what());
        m_output_scanline = NULL;
        return false;
    }
    if (! m_output_scanline && ! m_output_tiled) {
        error ("Unknown error opening EXR file");
        return false;
    }

    return true;
}