Exemple #1
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;
}