bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2)
{
    //
    // bufferedReadPixels reads each row of tiles that intersect the
    // scan-line range (scanLine1 to scanLine2). The previous row of
    // tiles is cached in order to prevent redundent tile reads when
    // accessing scanlines sequentially.
    //

    int minY = std::min (scanLine1, scanLine2);
    int maxY = std::max (scanLine1, scanLine2);

    if (minY < ifd->minY || maxY >  ifd->maxY)
    {
        throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
			   "the image file's data window.");
    }

    //
    // The minimum and maximum y tile coordinates that intersect this
    // scanline range
    //

    int minDy = (minY - ifd->minY) / ifd->tFile->tileYSize();
    int maxDy = (maxY - ifd->minY) / ifd->tFile->tileYSize();

    //
    // Figure out which one is first in the file so we can read without seeking
    //

    int yStart, yEnd, yStep;

    if (ifd->lineOrder == DECREASING_Y)
    {
        yStart = maxDy;
        yEnd = minDy - 1;
        yStep = -1;
    }
    else
    {
        yStart = minDy;
        yEnd = maxDy + 1;
        yStep = 1;
    }

    //
    // the number of pixels in a row of tiles
    //

    Box2i levelRange = ifd->tFile->dataWindowForLevel(0);
    
    //
    // Read the tiles into our temporary framebuffer and copy them into
    // the user's buffer
    //

    for (int j = yStart; j != yEnd; j += yStep)
    {
        Box2i tileRange = ifd->tFile->dataWindowForTile (0, j, 0);

        int minYThisRow = std::max (minY, tileRange.min.y);
        int maxYThisRow = std::min (maxY, tileRange.max.y);

        if (j != ifd->cachedTileY)
        {
            //
            // We don't have any valid buffered info, so we need to read in
            // from the file.
            // if no channels are being read that are present in file, cachedBuffer will be empty
            //

            if (ifd->cachedBuffer->begin() != ifd->cachedBuffer->end())
            {
                ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j);
            }

            ifd->cachedTileY = j;
        }

        //
        // Copy the data from our cached framebuffer into the user's
        // framebuffer.
        //

        for (FrameBuffer::ConstIterator k = ifd->tFileBuffer.begin();
             k != ifd->tFileBuffer.end();
             ++k)
        {


            Slice toSlice = k.slice();		// slice to read from
            char* toPtr;

            int xStart = levelRange.min.x;
            int yStart = minYThisRow;

            while (modp (xStart, toSlice.xSampling) != 0)
                ++xStart;

            while (modp (yStart, toSlice.ySampling) != 0)
                ++yStart;

            FrameBuffer::ConstIterator c = ifd->cachedBuffer->find(k.name());
            intptr_t toBase = reinterpret_cast<intptr_t>(toSlice.base);


            if( c!=ifd->cachedBuffer->end())
            {
                //
                // output channel was read from source image: copy to output slice
                //
                Slice fromSlice = c.slice();	// slice to write to
                intptr_t fromBase = reinterpret_cast<intptr_t>(fromSlice.base);

                int size = pixelTypeSize (toSlice.type);
                char* fromPtr;

                for (int y = yStart;
                    y <= maxYThisRow;
                    y += toSlice.ySampling)
                {
                    //
                    // Set the pointers to the start of the y scanline in
                    // this row of tiles
                    //

                    fromPtr = reinterpret_cast<char*> (fromBase  +
                            (y - tileRange.min.y) * fromSlice.yStride +
                            xStart * fromSlice.xStride);

                    toPtr = reinterpret_cast<char*> (toBase +
                            divp (y, toSlice.ySampling) * toSlice.yStride +
                            divp (xStart, toSlice.xSampling) * toSlice.xStride);

                    //
                    // Copy all pixels for the scanline in this row of tiles
                    //

                    for (int x = xStart;
                        x <= levelRange.max.x;
                        x += toSlice.xSampling)
                    {
                        for (int i = 0; i < size; ++i)
                            toPtr[i] = fromPtr[i];

                        fromPtr += fromSlice.xStride * toSlice.xSampling;
                        toPtr += toSlice.xStride;
                    }
                }
            }
            else
            {

                //
                // channel wasn't present in source file: fill output slice
                //
                for (int y = yStart;
                    y <= maxYThisRow;
                    y += toSlice.ySampling)
                {

                    toPtr = reinterpret_cast<char*> (toBase+
                            divp (y, toSlice.ySampling) * toSlice.yStride +
                            divp (xStart, toSlice.xSampling) * toSlice.xStride);

                    //
                    // Copy all pixels for the scanline in this row of tiles
                    //

                    switch ( toSlice.type)
                    {
                        case UINT:
                        {
                            unsigned int fill = toSlice.fillValue;
                            for (int x = xStart;
                                x <= levelRange.max.x;
                                x += toSlice.xSampling)
                            {
                                * reinterpret_cast<unsigned int*>(toPtr) = fill;
                                toPtr += toSlice.xStride;
                            }
                            break;
                        }
                        case HALF :
                        {
                            half fill = toSlice.fillValue;
                            for (int x = xStart;
                                x <= levelRange.max.x;
                                x += toSlice.xSampling)
                            {
                                * reinterpret_cast<half*>(toPtr) = fill;
                                toPtr += toSlice.xStride;
                            }
                            break;
                        }
                        case FLOAT :
                        {
                            float fill = toSlice.fillValue;
                            for (int x = xStart;
                                x <= levelRange.max.x;
                                x += toSlice.xSampling)
                            {
                                * reinterpret_cast<float*>(toPtr) = fill;
                                toPtr += toSlice.xStride;
                            }
                            break;
                        }
                        case NUM_PIXELTYPES :
                        {
                            break;
                        }

                    }
                }
            }
        }
    }
}