Example #1
0
bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels)
{
    const GIFFrameReader* frameReader = m_reader->frame_reader;
    // The pixel data and coordinates supplied to us are relative to the frame's
    // origin within the entire image size, i.e.
    // (frameReader->x_offset, frameReader->y_offset).  There is no guarantee
    // that (rowEnd - rowBuffer) == (size().width() - frameReader->x_offset), so
    // we must ensure we don't run off the end of either the source data or the
    // row's X-coordinates.
    int xBegin = upperBoundScaledX(frameReader->x_offset);
    int yBegin = upperBoundScaledY(frameReader->y_offset + rowNumber);
    int xEnd = lowerBoundScaledX(std::min(xBegin + static_cast<int>(rowEnd - rowBuffer), size().width()) - 1, xBegin + 1) + 1;
    int yEnd = lowerBoundScaledY(std::min(yBegin + static_cast<int>(repeatCount), size().height()) - 1, yBegin + 1) + 1;
    if (!rowBuffer || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))
        return true;

    // Get the colormap.
    const unsigned char* colorMap;
    unsigned colorMapSize;
    if (frameReader->is_local_colormap_defined) {
        colorMap = frameReader->local_colormap;
        colorMapSize = (unsigned)frameReader->local_colormap_size;
    } else {
        colorMap = m_reader->global_colormap;
        colorMapSize = m_reader->global_colormap_size;
    }
    if (!colorMap)
        return true;

    // Initialize the frame if necessary.
    RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
    if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex))
        return false;
    
    // Write one row's worth of data into the frame.  
    for (int x = xBegin; x < xEnd; ++x) {
        const unsigned char sourceValue = *(rowBuffer + (m_scaled ? m_scaledColumns[x] : x) - frameReader->x_offset);
        if ((!frameReader->is_transparent || (sourceValue != frameReader->tpixel)) && (sourceValue < colorMapSize)) {
            const size_t colorIndex = static_cast<size_t>(sourceValue) * 3;
            buffer.setRGBA(x, yBegin, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255);
        } else {
            m_currentBufferSawAlpha = true;
            // We may or may not need to write transparent pixels to the buffer.
            // If we're compositing against a previous image, it's wrong, and if
            // we're writing atop a cleared, fully transparent buffer, it's
            // unnecessary; but if we're decoding an interlaced gif and
            // displaying it "Haeberli"-style, we must write these for passes
            // beyond the first, or the initial passes will "show through" the
            // later ones.
            if (writeTransparentPixels)
                buffer.setRGBA(x, yBegin, 0, 0, 0, 0);
        }
    }

    // Tell the frame to copy the row data if need be.
    if (repeatCount > 1)
        buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd);

    return true;
}
Example #2
0
bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
{
    // Initialize the frame rect in our buffer.
    const GIFFrameContext* frameContext = m_reader->frameContext();
    IntRect frameRect(frameContext->xOffset, frameContext->yOffset, frameContext->width, frameContext->height);

    // Make sure the frameRect doesn't extend outside the buffer.
    if (frameRect.maxX() > size().width())
        frameRect.setWidth(size().width() - frameContext->xOffset);
    if (frameRect.maxY() > size().height())
        frameRect.setHeight(size().height() - frameContext->yOffset);

    ImageFrame* const buffer = &m_frameBufferCache[frameIndex];
    int left = upperBoundScaledX(frameRect.x());
    int right = lowerBoundScaledX(frameRect.maxX(), left);
    int top = upperBoundScaledY(frameRect.y());
    int bottom = lowerBoundScaledY(frameRect.maxY(), top);
    buffer->setOriginalFrameRect(IntRect(left, top, right - left, bottom - top));
    
    if (!frameIndex) {
        // This is the first frame, so we're not relying on any previous data.
        if (!buffer->setSize(scaledSize().width(), scaledSize().height()))
            return setFailed();
    } else {
        // The starting state for this frame depends on the previous frame's
        // disposal method.
        //
        // Frames that use the DisposeOverwritePrevious method are effectively
        // no-ops in terms of changing the starting state of a frame compared to
        // the starting state of the previous frame, so skip over them.  (If the
        // first frame specifies this method, it will get treated like
        // DisposeOverwriteBgcolor below and reset to a completely empty image.)
        const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex];
        ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod();
        while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious)) {
            prevBuffer = &m_frameBufferCache[--frameIndex];
            prevMethod = prevBuffer->disposalMethod();
        }
        ASSERT(prevBuffer->status() == ImageFrame::FrameComplete);

        if ((prevMethod == ImageFrame::DisposeNotSpecified) || (prevMethod == ImageFrame::DisposeKeep)) {
            // Preserve the last frame as the starting state for this frame.
            if (!buffer->copyBitmapData(*prevBuffer))
                return setFailed();
        } else {
            // We want to clear the previous frame to transparent, without
            // affecting pixels in the image outside of the frame.
            const IntRect& prevRect = prevBuffer->originalFrameRect();
            const IntSize& bufferSize = scaledSize();
            if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) {
                // Clearing the first frame, or a frame the size of the whole
                // image, results in a completely empty image.
                if (!buffer->setSize(bufferSize.width(), bufferSize.height()))
                    return setFailed();
            } else {
              // Copy the whole previous buffer, then clear just its frame.
              if (!buffer->copyBitmapData(*prevBuffer))
                  return setFailed();
              for (int y = prevRect.y(); y < prevRect.maxY(); ++y) {
                  for (int x = prevRect.x(); x < prevRect.maxX(); ++x)
                      buffer->setRGBA(x, y, 0, 0, 0, 0);
              }
              if ((prevRect.width() > 0) && (prevRect.height() > 0))
                  buffer->setHasAlpha(true);
            }
        }
    }

    // Update our status to be partially complete.
    buffer->setStatus(ImageFrame::FramePartial);

    // Reset the alpha pixel tracker for this frame.
    m_currentBufferSawAlpha = false;
    return true;
}