Ejemplo n.º 1
0
bool
ZfileInput::read_native_scanline(int subimage, int miplevel, int y, int z,
                                 void* data)
{
    lock_guard lock(m_mutex);
    if (!seek_subimage(subimage, miplevel))
        return false;

    if (m_next_scanline > y) {
        // User is trying to read an earlier scanline than the one we're
        // up to.  Easy fix: close the file and re-open.
        ImageSpec dummyspec;
        int subimage = current_subimage();
        if (!close() || !open(m_filename, dummyspec)
            || !seek_subimage(subimage, miplevel))
            return false;  // Somehow, the re-open failed
        ASSERT(m_next_scanline == 0 && current_subimage() == subimage);
    }
    while (m_next_scanline <= y) {
        // Keep reading until we're read the scanline we really need
        gzread(m_gz, data, m_spec.width * sizeof(float));
        ++m_next_scanline;
    }
    if (m_swab)
        swap_endian((float*)data, m_spec.width);
    return true;
}
Ejemplo n.º 2
0
bool
HdrInput::read_native_scanline(int subimage, int miplevel, int y, int z,
                               void* data)
{
    lock_guard lock(m_mutex);
    if (!seek_subimage(subimage, miplevel))
        return false;

    if (m_next_scanline > y) {
        // User is trying to read an earlier scanline than the one we're
        // up to.  Easy fix: close the file and re-open.
        ImageSpec dummyspec;
        int subimage = current_subimage();
        int miplevel = current_miplevel();
        if (!close() || !open(m_filename, dummyspec)
            || !seek_subimage(subimage, miplevel))
            return false;  // Somehow, the re-open failed
        assert(m_next_scanline == 0 && current_subimage() == subimage
               && current_miplevel() == miplevel);
    }
    while (m_next_scanline <= y) {
        // Keep reading until we're read the scanline we really need
        int r = RGBE_ReadPixels_RLE(m_fd, (float*)data, m_spec.width, 1,
                                    rgbe_error);
        ++m_next_scanline;
        if (r != RGBE_RETURN_SUCCESS) {
            error("%s", rgbe_error);
            return false;
        }
    }
    return true;
}
Ejemplo n.º 3
0
bool
JpgInput::read_native_scanline(int subimage, int miplevel, int y, int z,
                               void* data)
{
    if (!seek_subimage(subimage, miplevel))
        return false;
    if (m_raw)
        return false;
    if (y < 0 || y >= (int)m_cinfo.output_height)  // out of range scanline
        return false;
    if (m_next_scanline > y) {
        // User is trying to read an earlier scanline than the one we're
        // up to.  Easy fix: close the file and re-open.
        ImageSpec dummyspec;
        int subimage = current_subimage();
        if (!close() || !open(m_filename, dummyspec)
            || !seek_subimage(subimage, 0))
            return false;  // Somehow, the re-open failed
        assert(m_next_scanline == 0 && current_subimage() == subimage);
    }

    // Set up our custom error handler
    if (setjmp(m_jerr.setjmp_buffer)) {
        // Jump to here if there's a libjpeg internal error
        return false;
    }

    void* readdata = data;
    if (m_cmyk) {
        // If the file's data is CMYK, read into a 4-channel buffer, then
        // we'll have to convert.
        m_cmyk_buf.resize(m_spec.width * 4);
        readdata = &m_cmyk_buf[0];
        ASSERT(m_spec.nchannels == 3);
    }

    for (; m_next_scanline <= y; ++m_next_scanline) {
        // Keep reading until we've read the scanline we really need
        if (jpeg_read_scanlines(&m_cinfo, (JSAMPLE**)&readdata, 1) != 1
            || m_fatalerr) {
            error("JPEG failed scanline read (\"%s\")", filename().c_str());
            return false;
        }
    }

    if (m_cmyk)
        cmyk_to_rgb(m_spec.width, (unsigned char*)readdata, 4,
                    (unsigned char*)data, 3);

    return true;
}
Ejemplo n.º 4
0
bool
TIFFInput::open (const std::string &name, ImageSpec &newspec)
{
    m_filename = name;
    m_subimage = -1;
    return seek_subimage (0, 0, newspec);
}
Ejemplo n.º 5
0
bool
GIFInput::open (const std::string &name, ImageSpec &newspec)
{
    m_filename = name;
    m_subimage = -1;
    m_canvas.clear ();
    
    return seek_subimage (0, 0, newspec);
}
Ejemplo n.º 6
0
OIIO_PLUGIN_EXPORTS_END



bool
HdrInput::open (const std::string &name, ImageSpec &newspec)
{
    m_filename = name;
    return seek_subimage (0, 0, newspec);
}
Ejemplo n.º 7
0
bool
GIFInput::open (const std::string &name, ImageSpec &newspec)
{
    m_filename = name;
    m_subimage = -1;
    m_next_scanline = -1;
    m_cached_data.clear ();

    return seek_subimage (0, 0, newspec);
}
Ejemplo n.º 8
0
bool
PNGInput::read_native_scanline (int y, int z, void *data)
{
    y -= m_spec.y;
    if (y < 0 || y >= m_spec.height)   // out of range scanline
        return false;

    if (m_interlace_type != 0) {
        // Interlaced.  Punt and read the whole image
        if (m_buf.empty ())
            readimg ();
        size_t size = spec().scanline_bytes();
        memcpy (data, &m_buf[0] + y * size, size);
    } else {
        // Not an interlaced image -- read just one row
        if (m_next_scanline > y) {
            // User is trying to read an earlier scanline than the one we're
            // up to.  Easy fix: close the file and re-open.
            ImageSpec dummyspec;
            int subimage = current_subimage();
            if (! close ()  ||
                ! open (m_filename, dummyspec)  ||
                ! seek_subimage (subimage, dummyspec))
                return false;    // Somehow, the re-open failed
            assert (m_next_scanline == 0 && current_subimage() == subimage);
        }
        while (m_next_scanline <= y) {
            // Keep reading until we're read the scanline we really need
            // std::cerr << "reading scanline " << m_next_scanline << "\n";
            std::string s = PNG_pvt::read_next_scanline (m_png, data);
            if (s.length ()) {
                close ();
                error ("%s", s.c_str ());
                return false;
            }
            ++m_next_scanline;
        }
    }

    // PNG specifically dictates unassociated (un-"premultiplied") alpha.
    // Convert to associated unless we were requested not to do so.
    if (m_spec.alpha_channel != -1 && !m_keep_unassociated_alpha) {
        float gamma = m_spec.get_float_attribute ("oiio:Gamma", 1.0f);
        if (m_spec.format == TypeDesc::UINT16)
            associateAlpha ((unsigned short *)data, m_spec.width,
                            m_spec.nchannels, m_spec.alpha_channel, 
                            gamma);
        else
            associateAlpha ((unsigned char *)data, m_spec.width,
                            m_spec.nchannels, m_spec.alpha_channel, 
                            gamma);
    }

    return true;
}
Ejemplo n.º 9
0
bool
OpenEXRInput::open (const std::string &name, ImageSpec &newspec)
{
    // Quick check to reject non-exr files
    if (! Filesystem::is_regular (name)) {
        error ("Could not open file \"%s\"", name.c_str());
        return false;
    }
    bool tiled;
    if (! Imf::isOpenExrFile (name.c_str(), tiled)) {
        error ("\"%s\" is not an OpenEXR file", name.c_str());
        return false;
    }

    pvt::set_exr_threads ();

    m_spec = ImageSpec(); // Clear everything with default constructor
    
    try {
        m_input_stream = new OpenEXRInputStream (name.c_str());
    } catch (const std::exception &e) {
        m_input_stream = NULL;
        error ("OpenEXR exception: %s", e.what());
        return false;
    } catch (...) {   // catch-all for edge cases or compiler bugs
        m_input_stream = NULL;
        error ("OpenEXR exception: unknown");
        return false;
    }

    try {
        m_input_multipart = new Imf::MultiPartInputFile (*m_input_stream);
    } catch (const std::exception &e) {
        delete m_input_stream;
        m_input_stream = NULL;
        error ("OpenEXR exception: %s", e.what());
        return false;
    } catch (...) {   // catch-all for edge cases or compiler bugs
        m_input_stream = NULL;
        error ("OpenEXR exception: unknown");
        return false;
    }

    m_nsubimages = m_input_multipart->parts();
    m_parts.resize (m_nsubimages);
    m_subimage = -1;
    m_miplevel = -1;
    bool ok = seek_subimage (0, 0, newspec);
    if (! ok)
        close ();
    return ok;
}
Ejemplo n.º 10
0
OIIO_PLUGIN_EXPORTS_END



bool
HdrInput::open(const std::string& name, ImageSpec& newspec)
{
    m_filename = name;

    bool ok = seek_subimage(0, 0);
    newspec = spec();
    return ok;
}
Ejemplo n.º 11
0
bool
DPXInput::open (const std::string &name, ImageSpec &newspec)
{
    // open the image
    m_stream = new InStream();
    if (! m_stream->Open(name.c_str())) {
        error ("Could not open file \"%s\"", name.c_str());
        return false;
    }
    
    m_dpx.SetInStream(m_stream);
    if (! m_dpx.ReadHeader()) {
        error ("Could not read header");
        return false;
    }

    bool ok = seek_subimage (0, 0, newspec);
    newspec = spec ();
    return ok;
}
Ejemplo n.º 12
0
bool
NullInput::open(const std::string& name, ImageSpec& newspec,
                const ImageSpec& config)
{
    m_filename = name;
    m_subimage = -1;
    m_miplevel = -1;
    m_mip      = false;
    m_topspec  = config;

    // std::vector<std::pair<string_view,string_view> > args;
    // string_view filename = deconstruct_uri (name, &args);
    std::map<std::string, std::string> args;
    std::string filename;
    if (!Strutil::get_rest_arguments(name, filename, args))
        return false;
    if (filename.empty())
        return false;

    // To keep the "null" input reader from reading from ANY name, only
    // succeed if it ends in ".null" or ".nul" --OR-- if the config has a
    // special override "null:force" set to nonzero (that lets the caller
    // guarantee a null input even if the name has no extension, say).
    if (!Strutil::ends_with(filename, ".null")
        && !Strutil::ends_with(filename, ".nul")
        && config.get_int_attribute("null:force") == 0)
        return false;

    // Override the config with default resolution if it was not set
    if (m_topspec.width <= 0)
        m_topspec.width = 1024;
    if (m_topspec.height <= 0)
        m_topspec.height = 1024;
    if (m_topspec.depth <= 0)
        m_topspec.depth = 1;
    if (m_topspec.full_width <= 0)
        m_topspec.full_width = m_topspec.width;
    if (m_topspec.full_height <= 0)
        m_topspec.full_height = m_topspec.height;
    if (m_topspec.full_depth <= 0)
        m_topspec.full_depth = m_topspec.depth;
    if (m_topspec.nchannels <= 0)
        m_topspec.nchannels = 4;
    if (m_topspec.format == TypeUnknown)
        m_topspec.format = TypeFloat;

    m_filename = filename;
    std::vector<float> fvalue;

    for (const auto& a : args) {
        if (a.first == "RES") {
            parse_res(a.second, m_topspec.width, m_topspec.height,
                      m_topspec.depth);
            m_topspec.full_x      = m_topspec.x;
            m_topspec.full_y      = m_topspec.y;
            m_topspec.full_z      = m_topspec.z;
            m_topspec.full_width  = m_topspec.width;
            m_topspec.full_height = m_topspec.height;
            m_topspec.full_depth  = m_topspec.depth;
        } else if (a.first == "TILE" || a.first == "TILES") {
            parse_res(a.second, m_topspec.tile_width, m_topspec.tile_height,
                      m_topspec.tile_depth);
        } else if (a.first == "CHANNELS") {
            m_topspec.nchannels = Strutil::from_string<int>(a.second);
            m_topspec.default_channel_names();
        } else if (a.first == "MIP") {
            m_mip = Strutil::from_string<int>(a.second);
        } else if (a.first == "TEX") {
            if (Strutil::from_string<int>(a.second)) {
                if (!m_spec.tile_width) {
                    m_topspec.tile_width  = 64;
                    m_topspec.tile_height = 64;
                    m_topspec.tile_depth  = 1;
                }
                m_topspec.attribute("wrapmodes", "black,black");
                m_topspec.attribute("textureformat", "Plain Texture");
                m_mip = true;
            }
        } else if (a.first == "TYPE") {
            m_topspec.set_format(TypeDesc(a.second));
        } else if (a.first == "PIXEL") {
            Strutil::extract_from_list_string(fvalue, a.second);
            fvalue.resize(m_topspec.nchannels);
        } else if (a.first.size() && a.second.size()) {
            parse_param(a.first, a.second, m_topspec);
        }
    }

    if (fvalue.size()) {
        // Convert float to the native type
        fvalue.resize(m_topspec.nchannels, 0.0f);
        m_value.resize(m_topspec.pixel_bytes());
        convert_types(TypeFloat, fvalue.data(), m_topspec.format,
                      m_value.data(), m_topspec.nchannels);
    }

    bool ok = seek_subimage(0, 0);
    newspec = spec();
    return ok;
}
Ejemplo n.º 13
0
bool
OpenEXRInput::open (const std::string &name, ImageSpec &newspec)
{
    // Quick check to reject non-exr files
    if (! Filesystem::is_regular (name)) {
        error ("Could not open file \"%s\"", name.c_str());
        return false;
    }
    bool tiled;
    if (! Imf::isOpenExrFile (name.c_str(), tiled)) {
        error ("\"%s\" is not an OpenEXR file", name.c_str());
        return false;
    }

    pvt::set_exr_threads ();

    m_spec = ImageSpec(); // Clear everything with default constructor
    
    try {
        m_input_stream = new OpenEXRInputStream (name.c_str());
    }
    catch (const std::exception &e) {
        m_input_stream = NULL;
        error ("OpenEXR exception: %s", e.what());
        return false;
    }

#ifdef USE_OPENEXR_VERSION2
    try {
        m_input_multipart = new Imf::MultiPartInputFile (*m_input_stream);
    }
    catch (const std::exception &e) {
        delete m_input_stream;
        m_input_stream = NULL;
        error ("OpenEXR exception: %s", e.what());
        return false;
    }

    m_nsubimages = m_input_multipart->parts();

#else
    try {
        if (tiled) {
            m_input_tiled = new Imf::TiledInputFile (*m_input_stream);
        } else {
            m_input_scanline = new Imf::InputFile (*m_input_stream);
        }
    }
    catch (const std::exception &e) {
        delete m_input_stream;
        m_input_stream = NULL;
        error ("OpenEXR exception: %s", e.what());
        return false;
    }

    if (! m_input_scanline && ! m_input_tiled) {
        error ("Unknown error opening EXR file");
        return false;
    }

    m_nsubimages = 1;  // OpenEXR 1.x did not have multipart
#endif

    m_parts.resize (m_nsubimages);
    m_subimage = -1;
    m_miplevel = -1;
    bool ok = seek_subimage (0, 0, newspec);
    if (! ok)
        close ();
    return ok;
}
Ejemplo n.º 14
0
bool
GIFInput::read_native_scanline (int _y, int z, void *data)
{
    if (_y < 0 || _y > m_spec.height)
        return false;

    bool interlacing = m_spec.get_int_attribute ("gif:Interlacing") != 0;

    // decode scanline index if image is interlaced
    int gif_y = interlacing ? decode_line_number (_y) : _y;

    if (interlacing) { // gif is interlaced so cache the scanlines
        int scanlines_cached = m_cached_data.size() / m_spec.width;
        if (gif_y >= scanlines_cached) {
            // scanline is not cached yet, read the scanline and preceding ones
            m_cached_data.resize (m_spec.width * (gif_y + 1));
            int delta_size = m_spec.width * (gif_y - scanlines_cached + 1);
            if (DGifGetLine (m_gif_file,
                             &m_cached_data[scanlines_cached * m_spec.width],
                             delta_size) == GIF_ERROR) {
                report_last_error ();
                return false;
            }
        }

        translate_scanline (&m_cached_data[gif_y * m_spec.width], data);

    } else { // no interlacing, thus no scanlines caching
        if (m_next_scanline > gif_y) {
            // requested scanline is located before the one to read next
            // random access is not supported, so reopen the file, find
            // current subimage and skip preceding image data
            ImageSpec dummyspec;
            if (! close () ||
                    ! open (m_filename, dummyspec) ||
                    ! seek_subimage (m_subimage, 0, dummyspec)) {
                return false;
            }

            int remaining_size = m_spec.width * gif_y;
            boost::scoped_array<unsigned char> buffer
            (new unsigned char[remaining_size]);
            if (DGifGetLine (m_gif_file, buffer.get(), remaining_size)
                    == GIF_ERROR) {
                report_last_error ();
                return false;
            }

        } else if (m_next_scanline < gif_y) {
            // requested scanline is located after the one to read next
            // skip the lines in between
            int delta_size = m_spec.width * (gif_y - m_next_scanline);
            boost::scoped_array<unsigned char> buffer
            (new unsigned char[delta_size]);
            if (DGifGetLine (m_gif_file,
                             buffer.get(),
                             delta_size) == GIF_ERROR) {
                report_last_error ();
                return false;
            }
        }

        // read the requested scanline
        boost::scoped_array<unsigned char> fscanline
        (new unsigned char[m_spec.width]);
        if (DGifGetLine (m_gif_file,
                         fscanline.get(),
                         m_spec.width) == GIF_ERROR) {
            report_last_error ();
            return false;
        }
        translate_scanline (fscanline.get(), data);
    }

    m_next_scanline = gif_y + 1;

    return true;
}
Ejemplo n.º 15
0
bool
TIFFInput::read_native_scanline (int y, int z, void *data)
{
    y -= m_spec.y;

    // For compression modes that don't support random access to scanlines
    // (which I *think* is only LZW), we need to emulate random access by
    // re-seeking.
    if (m_no_random_access) {
        if (m_next_scanline > y) {
            // User is trying to read an earlier scanline than the one we're
            // up to.  Easy fix: start over.
            // FIXME: I'm too tired to look into it now, but I wonder if
            // it is able to randomly seek to the first line in any
            // "strip", in which case we don't need to start from 0, just
            // start from the beginning of the strip we need.
            ImageSpec dummyspec;
            int old_subimage = current_subimage();
            int old_miplevel = current_miplevel();
            if (! close ()  ||
                ! open (m_filename, dummyspec)  ||
                ! seek_subimage (old_subimage, old_miplevel, dummyspec)) {
                return false;    // Somehow, the re-open failed
            }
            ASSERT (m_next_scanline == 0 &&
                    current_subimage() == old_subimage &&
                    current_miplevel() == old_miplevel);
        }
        while (m_next_scanline < y) {
            // Keep reading until we're read the scanline we really need
            m_scratch.resize (m_spec.scanline_bytes());
            if (TIFFReadScanline (m_tif, &m_scratch[0], m_next_scanline) < 0) {
                error ("%s", lasterr.c_str());
                return false;
            }
            ++m_next_scanline;
        }
    }
    m_next_scanline = y+1;

    int nvals = m_spec.width * m_spec.nchannels;
    m_scratch.resize (m_spec.scanline_bytes());
    bool no_bit_convert = (m_bitspersample == 8 || m_bitspersample == 16 ||
                           m_bitspersample == 32);
    if (m_photometric == PHOTOMETRIC_PALETTE) {
        // Convert from palette to RGB
        if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) {
            error ("%s", lasterr.c_str());
            return false;
        }
        palette_to_rgb (m_spec.width, &m_scratch[0], (unsigned char *)data);
    } else {
        // Not palette
        int plane_bytes = m_spec.width * m_spec.format.size();
        int planes = m_separate ? m_spec.nchannels : 1;
        std::vector<unsigned char> scratch2 (m_separate ? m_spec.scanline_bytes() : 0);
        // Where to read?  Directly into user data if no channel shuffling
        // or bit shifting is needed, otherwise into scratch space.
        unsigned char *readbuf = (no_bit_convert && !m_separate) ? (unsigned char *)data : &m_scratch[0];
        // Perform the reads.  Note that for contig, planes==1, so it will
        // only do one TIFFReadScanline.
        for (int c = 0;  c < planes;  ++c)  /* planes==1 for contig */
            if (TIFFReadScanline (m_tif, &readbuf[plane_bytes*c], y, c) < 0) {
                error ("%s", lasterr.c_str());
                return false;
            }
        if (m_bitspersample < 8) {
            // m_scratch now holds nvals n-bit values, contig or separate
            std::swap (m_scratch, scratch2);
            for (int c = 0;  c < planes;  ++c)  /* planes==1 for contig */
                bit_convert (m_separate ? m_spec.width : nvals,
                             &scratch2[plane_bytes*c], m_bitspersample,
                             m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 8);
        } else if (m_bitspersample > 8 && m_bitspersample < 16) {
            // m_scratch now holds nvals n-bit values, contig or separate
            std::swap (m_scratch, scratch2);
            for (int c = 0;  c < planes;  ++c)  /* planes==1 for contig */
                bit_convert (m_separate ? m_spec.width : nvals,
                             &scratch2[plane_bytes*c], m_bitspersample,
                             m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 16);
        }
        if (m_separate) {
            // Convert from separate (RRRGGGBBB) to contiguous (RGBRGBRGB).
            // We know the data is in m_scratch at this point, so 
            // contiguize it into the user data area.
            separate_to_contig (m_spec.width, &m_scratch[0], (unsigned char *)data);
        }
    }

    if (m_photometric == PHOTOMETRIC_MINISWHITE)
        invert_photometric (nvals, data);

    // If alpha is unassociated and we aren't requested to keep it that
    // way, multiply the colors by alpha per the usual OIIO conventions
    // to deliver associated color & alpha.
    if (m_convert_alpha)
        unassalpha_to_assocalpha (m_spec.width, data);

    return true;
}
Ejemplo n.º 16
0
bool
TIFFInput::read_native_scanline (int y, int z, void *data)
{
    y -= m_spec.y;

    // For compression modes that don't support random access to scanlines
    // (which I *think* is only LZW), we need to emulate random access by
    // re-seeking.
    if (m_no_random_access) {
        if (m_next_scanline > y) {
            // User is trying to read an earlier scanline than the one we're
            // up to.  Easy fix: start over.
            // FIXME: I'm too tired to look into it now, but I wonder if
            // it is able to randomly seek to the first line in any
            // "strip", in which case we don't need to start from 0, just
            // start from the beginning of the strip we need.
            ImageSpec dummyspec;
            int old_subimage = current_subimage();
            int old_miplevel = current_miplevel();
            if (! close ()  ||
                ! open (m_filename, dummyspec)  ||
                ! seek_subimage (old_subimage, old_miplevel, dummyspec)) {
                return false;    // Somehow, the re-open failed
            }
            ASSERT (m_next_scanline == 0 &&
                    current_subimage() == old_subimage &&
                    current_miplevel() == old_miplevel);
        }
        while (m_next_scanline < y) {
            // Keep reading until we're read the scanline we really need
            m_scratch.resize (m_spec.scanline_bytes());
            if (TIFFReadScanline (m_tif, &m_scratch[0], m_next_scanline) < 0) {
                error ("%s", lasterr.c_str());
                return false;
            }
            ++m_next_scanline;
        }
    }
    m_next_scanline = y+1;

    if (m_photometric == PHOTOMETRIC_PALETTE) {
        // Convert from palette to RGB
        m_scratch.resize (m_spec.width);
        if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) {
            error ("%s", lasterr.c_str());
            return false;
        }
        palette_to_rgb (m_spec.width, &m_scratch[0], (unsigned char *)data);
    } else if (m_planarconfig == PLANARCONFIG_SEPARATE && m_spec.nchannels > 1) {
        // Convert from separate (RRRGGGBBB) to contiguous (RGBRGBRGB)
        m_scratch.resize (m_spec.scanline_bytes());
        int plane_bytes = m_spec.width * m_spec.format.size();
        for (int c = 0;  c < m_spec.nchannels;  ++c)
            if (TIFFReadScanline (m_tif, &m_scratch[plane_bytes*c], y, c) < 0) {
                error ("%s", lasterr.c_str());
                return false;
            }
        separate_to_contig (m_spec.width, &m_scratch[0], (unsigned char *)data);
    } else if (m_bitspersample == 1 || m_bitspersample == 2 || 
               m_bitspersample == 4) {
        // <8 bit images
        m_scratch.resize (m_spec.width);
        if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) {
            error ("%s", lasterr.c_str());
            return false;
        }
        nbit_to_8bit (m_spec.width, &m_scratch[0], (unsigned char *)data,
                      m_bitspersample);
    } else {
        // Contiguous, >= 8 bit per sample -- the "usual" case
        if (TIFFReadScanline (m_tif, data, y) < 0) {
            error ("%s", lasterr.c_str());
            return false;
        }
    }

    if (m_photometric == PHOTOMETRIC_MINISWHITE)
        invert_photometric (m_spec.width * m_spec.nchannels, data);

    // If alpha is unassociated and we aren't requested to keep it that
    // way, multiply the colors by alpha per the usual OIIO conventions
    // to deliver associated color & alpha.
    if (m_convert_alpha)
        unassalpha_to_assocalpha (m_spec.width, data);

    return true;
}