bool ImageSource::frameHasAlphaAtIndex(size_t index)
{
#ifdef ANDROID_ANIMATED_GIF
    if (m_decoder.m_gifDecoder) {
        if (!m_decoder.m_gifDecoder->supportsAlpha())
            return false;

        RGBA32Buffer* buffer =
                m_decoder.m_gifDecoder->frameBufferAtIndex(index);
        if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
            return false;

        return buffer->hasAlpha();
    }
#else
    SkASSERT(0 == index);
#endif

    if (NULL == m_decoder.m_image)
        return true;    // if we're not sure, assume the worse-case
    const PrivateAndroidImageSourceRec& decoder = *m_decoder.m_image;
    // if we're 16bit, we know even without all the data available
    if (decoder.bitmap().getConfig() == SkBitmap::kRGB_565_Config)
        return false;

    if (!decoder.fAllDataReceived)
        return true;    // if we're not sure, assume the worse-case
    
    return !decoder.bitmap().isOpaque();
}
示例#2
0
bool ImageSource::frameIsCompleteAtIndex(size_t index)
{
    if (!m_decoder)
        return false;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    return buffer && buffer->status() == RGBA32Buffer::FrameComplete;
}
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;
    
    return buffer->asNewNativeImage();
}
示例#4
0
float ImageSource::frameDurationAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    return buffer->duration() / 1000.0f;
}
示例#5
0
bool ImageSource::frameHasAlphaAtIndex(size_t index)
{
    if (!m_decoder || !m_decoder->supportsAlpha())
        return false;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return false;

    return buffer->hasAlpha();
}
bool ImageSource::frameIsCompleteAtIndex(size_t index)
{
#ifdef ANDROID_ANIMATED_GIF
    if (m_decoder.m_gifDecoder) {
        RGBA32Buffer* buffer =
                m_decoder.m_gifDecoder->frameBufferAtIndex(index);
        return buffer && buffer->status() == RGBA32Buffer::FrameComplete;
    }
#else
    SkASSERT(0 == index);
#endif
	return m_decoder.m_image && m_decoder.m_image->fAllDataReceived;
}
示例#7
0
RGBA32Buffer* BMPImageDecoder::frameBufferAtIndex(size_t index)
{
    if (index)
        return 0;

    if (m_frameBufferCache.isEmpty())
        m_frameBufferCache.resize(1);

    RGBA32Buffer* buffer = &m_frameBufferCache.first();
    if (buffer->status() != RGBA32Buffer::FrameComplete)
        decode(false);
    return buffer;
}
RGBA32Buffer* RxIImageDecoder::frameBufferAtIndex(size_t index)
{
    if (index)
        return 0;

    RGBA32Buffer* frame = getUntouchedFrameBufferAtIndex(index);

    if (!frame)
        return 0;

    if (frame->status() != RGBA32Buffer::FrameComplete)
        decode(false);
    return frame;
}
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    return cairo_image_surface_create_for_data((unsigned char*)buffer->bytes().data(),
                                               CAIRO_FORMAT_ARGB32,
                                               size().width(),
                                               buffer->height(),
                                               size().width()*4);
}
示例#10
0
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    // Note that the buffer can have NULL bytes even when it is marked as
    // non-empty. It seems "FrameEmpty" is only set before the frame has been
    // initialized. If it is decoded and it happens to be empty, it will be
    // marked as "FrameComplete" but will still have NULL bytes.
    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    // Copy the bitmap.  The pixel data is refcounted internally by SkBitmap, so
    // this doesn't cost much.  
    return buffer->asNewNativeImage();
}
float ImageSource::frameDurationAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    float duration = buffer->duration() / 1000.0f;

    // Follow other ports (and WinIE's) behavior to slow annoying ads that
    // specify a 0 duration.
    if (duration < 0.051f)
        return 0.100f;
    return duration;
}
示例#12
0
float ImageSource::frameDurationAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    // Many annoying ads specify a 0 duration to make an image flash as quickly
    // as possible.  We follow WinIE's behavior and use a duration of 100 ms
    // for any frames that specify a duration of <= 50 ms.  See
    // <http://bugs.webkit.org/show_bug.cgi?id=14413> or Radar 4051389 for
    // more.
    const float duration = buffer->duration() / 1000.0f;
    return (duration < 0.051f) ? 0.100f : duration;
}
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!initialized())
        return 0;

    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    // Cairo does not like zero height images.
    // If we have a zero height image, just pretend we don't have enough data yet.
    if (!size().height())
        return 0;

    return buffer->asNewNativeImage();
}
SkBitmapRef* ImageSource::createFrameAtIndex(size_t index)
{
#ifdef ANDROID_ANIMATED_GIF
    if (m_decoder.m_gifDecoder) {
        RGBA32Buffer* buffer =
                m_decoder.m_gifDecoder->frameBufferAtIndex(index);
        if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
            return 0;
        SkBitmap& bitmap = buffer->bitmap();
        SkPixelRef* pixelRef = bitmap.pixelRef();
        if (pixelRef)
            pixelRef->setURI(m_decoder.m_url);
        return new SkBitmapRef(bitmap);
    }
#else
    SkASSERT(index == 0);
#endif
    SkASSERT(m_decoder.m_image != NULL);
    m_decoder.m_image->ref();
    return m_decoder.m_image;
}
float ImageSource::frameDurationAtIndex(size_t index)
{
    float duration = 0;
#ifdef ANDROID_ANIMATED_GIF
    if (m_decoder.m_gifDecoder) {
        RGBA32Buffer* buffer
                = m_decoder.m_gifDecoder->frameBufferAtIndex(index);
        if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
            return 0;
        duration = buffer->duration() / 1000.0f;
    }
#else
    SkASSERT(index == 0);
#endif

    // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
    // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
    // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more.
    if (duration <= 0.010f)
        duration = 0.100f;
    return duration;
}
示例#16
0
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!initialized())
        return 0;

    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    // Cairo does not like zero height images.
    // If we have a zero height image, just pretend we don't have enough data yet.
    if (!buffer->height())
        return 0;

    return cairo_image_surface_create_for_data((unsigned char*)buffer->bytes().data(),
                                               CAIRO_FORMAT_ARGB32,
                                               size().width(),
                                               buffer->height(),
                                               size().width()*4);
}
示例#17
0
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;
    
    IntRect imageRect = buffer->rect();
    unsigned char* bytes = (unsigned char*)buffer->bytes().data();
    long colorSize = buffer->bytes().size();
    
    typedef wxPixelData<wxBitmap, wxAlphaPixelFormat> PixelData;

    int width = size().width();
    int height = size().height();

    wxBitmap* bmp = new wxBitmap(width, height, 32);
    PixelData data(*bmp);
    
    int rowCounter = 0;
    long pixelCounter = 0;
    
    PixelData::Iterator p(data);
    
    PixelData::Iterator rowStart = p; 
    
    // NB: It appears that the data is in BGRA format instead of RGBA format.
    // This code works properly on both ppc and intel, meaning the issue is
    // likely not an issue of byte order getting mixed up on different archs. 
    for (long i = 0; i < buffer->bytes().size()*4; i+=4) {
        p.Red() = bytes[i+2];
        p.Green() = bytes[i+1];
        p.Blue() = bytes[i+0];
        p.Alpha() = bytes[i+3];
        
        p++;

        pixelCounter++;
        if ( (pixelCounter % width ) == 0 ) {
            rowCounter++;
            p = rowStart;
            p.MoveTo(data, 0, rowCounter);
        }

    }

    bmp->UseAlpha();
    ASSERT(bmp->IsOk());
    return bmp;
}
示例#18
0
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;
        
    DEBUG( "ImageSource::createFrameAtIndex() %i %i %i\n",
    	size().width(), buffer->height(), buffer->bytes().size() );
    
    os::Bitmap* pcBitmap = new os::Bitmap( size().width(), buffer->height(), os::CS_RGBA32 );
    memcpy( pcBitmap->LockRaster(), reinterpret_cast<unsigned char*>(buffer->bytes().data()),
    			 ( buffer->height() * size().width() ) * 4  );
	pcBitmap->UnlockRaster();    			 
    return( pcBitmap );
}
示例#19
0
NativeImagePtr ImageSource::createFrameAtIndex(size_t index)
{
    if (!initialized())
        return 0;

    if (!m_decoder)
        return 0;

    RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index);
    if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty)
        return 0;

    // Cairo does not like zero height images.
    // If we have a zero height image, just pretend we don't have enough data yet.
    if (!buffer->height())
        return 0;
    /* <lab126> */
    struct timespec startTime;
    KINDLE_BEGIN(Kindle_Debug_Perf)
    clock_gettime(CLOCK_MONOTONIC, &startTime);
    KINDLE_END()

    if (m_imageDitherType == USE_SIMPLE_ALGORITHM) {
        int imageWidth = size().width();
        int imageHeight = buffer->height();
        if(!dither_inited)
            for(int i=0; i<3333; i++)
                dither_buffer[i]=rand()%17;
        for (int n = 0; n < imageHeight; n++)
        {
            for (int m = 0; m < imageWidth; m++)
            {
                int rgb=buffer->bytes().data()[ (n*imageWidth) + m ];
                int r = (rgb >> 16) & 0xff;
                int g = (rgb >> 8) & 0xff;
                int b = rgb & 0xff;
                int gray = (r == g && g == b) ?  r : ((77 * r + 150 * g + 29 * b) >> 8);
                dither_index=(dither_index+1)%3333;
                int ngray=gray+dither_buffer[dither_index];
                ngray-=ngray%17;
                if(ngray>0xff) ngray=0xff;
                buffer->bytes().data()[ (n*imageWidth) + m ] =   (rgb&STRIP_ALPHA)|(ngray<<16)|(ngray<<8)|ngray;
            }
        }
    }
    bool decode(const Vector<char>& data, RxIImageDecoder::RxIFormat fmt)
    {
        int i = 0;
        int j = 0;
        bool result = false;
        RGBA32Buffer* frameBuffer = 0;
        unsigned char* rgbData = 0;

        if (!m_memoryManager)
            return false;

        m_memoryReader = new MemReader((unsigned char*)data.data(), data.size());

        // The rest of the code expects that the first 8 bytes has already been read.
        // The signature for these files is 8 bytes.

        m_memoryReader->SetOffset(8);


        switch (fmt) {
        case RxIImageDecoder::FormatRdi:
            m_rxi = RdiBirth(m_memoryManager, m_memoryReader);
            break;
        case RxIImageDecoder::FormatRgi:
            m_rxi = RgiBirth(m_memoryManager, m_memoryReader);
            break;
        case RxIImageDecoder::FormatRpi:
            m_rxi = RpiBirth(m_memoryManager, m_memoryReader);
            break;
        case RxIImageDecoder::FormatRwi:
            m_rxi = RwiBirth(m_memoryManager, m_memoryReader);
            break;
        case RxIImageDecoder::FormatUnknown:
            ASSERT_NOT_REACHED();
            break;
        }

        if (!m_rxi) {
            delete m_memoryReader;
            m_memoryReader = 0;
            return false;
        }
        m_format = fmt;

        switch (fmt) {
        case RxIImageDecoder::FormatRdi:
            result = RdiGetWH(m_rxi, &m_w, &m_h);
            break;
        case RxIImageDecoder::FormatRgi:
            result = RgiGetWH(m_rxi, &m_w, &m_h);
            break;
        case RxIImageDecoder::FormatRpi:
            result = RpiGetWH(m_rxi, &m_w, &m_h);
            break;
        case RxIImageDecoder::FormatRwi:
            result = RwiGetWH(m_rxi, &m_w, &m_h);
            break;
        case RxIImageDecoder::FormatUnknown:
            ASSERT_NOT_REACHED();
            break;
        }

        if (!result) {
            delete m_memoryReader;
            m_memoryReader = 0;
            return false;
        }

        // We can fill in the size now that the header is available.
        if (!m_decoder->setSize(m_w, m_h))
            return m_decoder->setFailed();

        frameBuffer = m_decoder->getUntouchedFrameBufferAtIndex(0);

        if (!frameBuffer) {
            delete m_memoryReader;
            m_memoryReader = 0;
            m_sizeInitialized = false;
            return false;
        }

        if (frameBuffer->status() == RGBA32Buffer::FrameEmpty) {
            if (!frameBuffer->setSize(m_w, m_h))
                return m_decoder->setFailed();
            frameBuffer->setStatus(RGBA32Buffer::FramePartial);

            if ( fmt == RxIImageDecoder::FormatRpi ) {
                // RPI format has alpha which we must support.
                frameBuffer->setHasAlpha(true);
            } else {
                frameBuffer->setHasAlpha(false);
            }
            // the frame always fills the entire image.
            frameBuffer->setRect(IntRect(IntPoint(0, 0), m_decoder->size()));
        }

        // note that we don't cleanup frameBuffer at all since we didn't allocate it.
        if ( fmt == RxIImageDecoder::FormatRgi ) {
            rgbData = (unsigned char*) malloc(m_w * m_h * 3);
        } else if ( fmt == RxIImageDecoder::FormatRpi ) {
            // extra channel is for alpha
            rgbData = (unsigned char*) malloc(m_w * m_h * 4);
        }

        MemWriter mw(m_w * m_h * 3);
        switch (fmt) {
        case RxIImageDecoder::FormatRdi:
            result = RdiGetRGB(m_rxi, &mw);
            break;
        case RxIImageDecoder::FormatRgi:
            result = RgiGetRGB(m_rxi, rgbData);
            break;
        case RxIImageDecoder::FormatRpi:
            result = RpiGetRGB(m_rxi, rgbData);
            break;
        case RxIImageDecoder::FormatRwi:
            result = RwiGetRGB(m_rxi, &mw);
            break;
        case RxIImageDecoder::FormatUnknown:
            ASSERT_NOT_REACHED();
            break;
        }

        if (result) {
            unsigned char* ptr = 0;
            if ((fmt == RxIImageDecoder::FormatRdi) || (fmt == RxIImageDecoder::FormatRwi))
                ptr = (unsigned char*)mw.GetBuffer();
            else
                ptr = rgbData;

            for (i = 0; i < m_h; ++i) {
                for (j = 0; j < m_w; ++j) {
                    unsigned char r, g, b;
                    unsigned char a = 0xFF;
                    r = *ptr;
                    ptr++;
                    g = *ptr;
                    ptr++;
                    b = *ptr;
                    ptr++;
                    if ( fmt == RxIImageDecoder::FormatRpi ) {
                        a = *ptr;
                        ptr++;
                    }
                    frameBuffer->setRGBA(j, i, r, g, b, a);
                }
            }
        }
        m_decoder->rxiComplete();

        // cleaning up
        free(rgbData);
        rgbData = 0;
        delete m_memoryReader;
        m_memoryReader = 0;
        m_sizeInitialized = false;

        switch (fmt) {
        case RxIImageDecoder::FormatRdi:
            RdiDeath(m_rxi);
            break;
        case RxIImageDecoder::FormatRgi:
            RgiDeath(m_rxi);
            break;
        case RxIImageDecoder::FormatRpi:
            RpiDeath(m_rxi);
            break;
        case RxIImageDecoder::FormatRwi:
            RwiDeath(m_rxi);
            break;
        case RxIImageDecoder::FormatUnknown:
            ASSERT_NOT_REACHED();
            break;
        }
        m_rxi = 0;
        return true;
    }
示例#21
0
void GIFImageDecoder::haveDecodedRow(unsigned frameIndex,
                                     unsigned char* rowBuffer,   // Pointer to single scanline temporary buffer
                                     unsigned char* rowEnd,
                                     unsigned rowNumber,  // The row index
                                     unsigned repeatCount) // How many times to repeat the row
{
    // Resize to the width and height of the image.
    RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
    RGBA32Buffer* previousBuffer = (frameIndex > 0) ? &m_frameBufferCache[frameIndex-1] : 0;
    bool compositeWithPreviousFrame = previousBuffer && previousBuffer->includeInNextFrame();

    if (buffer.status() == RGBA32Buffer::FrameEmpty)
        initFrameBuffer(buffer, previousBuffer, compositeWithPreviousFrame);

    if (rowBuffer == 0)
      return;

    unsigned colorMapSize;
    unsigned char* colorMap;
    m_reader->getColorMap(colorMap, colorMapSize);
    if (!colorMap)
        return;

    // The buffers that we draw are the entire image's width and height, so a final output frame is
    // width * height RGBA32 values in size.
    //
    // A single GIF frame, however, can be smaller than the entire image, i.e., it can represent some sub-rectangle
    // within the overall image.  The rows we are decoding are within this
    // sub-rectangle.  This means that if the GIF frame's sub-rectangle is (x,y,w,h) then row 0 is really row
    // y, and each row goes from x to x+w.
    unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * m_size.width() + m_reader->frameXOffset();
    unsigned* dst = buffer.bytes().data() + dstPos;
    unsigned* currDst = dst;
    unsigned char* currentRowByte = rowBuffer;
    
    bool hasAlpha = m_reader->isTransparent(); 
    bool sawAlpha = false;
    while (currentRowByte != rowEnd) {
        if ((!hasAlpha || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) {
            unsigned colorIndex = *currentRowByte * 3;
            unsigned red = colorMap[colorIndex];
            unsigned green = colorMap[colorIndex + 1];
            unsigned blue = colorMap[colorIndex + 2];
            RGBA32Buffer::setRGBA(*currDst, red, green, blue, 255);
        } else {
            if (!sawAlpha) {
                sawAlpha = true;
                buffer.setHasAlpha(true);
            }
            
            if (!compositeWithPreviousFrame)
                RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0);
        }
        currDst++;
        currentRowByte++;
    }

    if (repeatCount > 1) {
        // Copy the row |repeatCount|-1 times.
        unsigned size = (currDst - dst) * sizeof(unsigned);
        unsigned width = m_size.width();
        unsigned* end = buffer.bytes().data() + width * m_size.height();
        currDst = dst + width;
        for (unsigned i = 1; i < repeatCount; i++) {
            if (currDst + size > end) // Protect against a buffer overrun from a bogus repeatCount.
                break;
            memcpy(currDst, dst, size);
            currDst += width;
        }
    }

    // Our partial height is rowNumber + 1, e.g., row 2 is the 3rd row, so that's a height of 3.
    // Adding in repeatCount - 1 to rowNumber + 1 works out to just be rowNumber + repeatCount.
    buffer.ensureHeight(rowNumber + repeatCount);
}
示例#22
0
void GIFImageDecoder::initFrameBuffer(RGBA32Buffer& buffer, 
                                      RGBA32Buffer* previousBuffer,
                                      bool compositeWithPreviousFrame)
{
    // Initialize the frame rect in our buffer.
    IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(),
                      m_reader->frameWidth(), m_reader->frameHeight());
    buffer.setRect(frameRect);

    bool isSubRect = (frameRect.x() > 0 || frameRect.y() > 0 ||
                      frameRect.width() < m_size.width() ||
                      frameRect.height() < m_size.height());
    
    // Let's resize our buffer now to the correct width/height and then
    // initialize portions of it if needed.
    RGBA32Array& bytes = buffer.bytes();
        
    // If the disposal method of the previous frame said to stick around, then we need     
    // to copy that frame into our frame.  We also dont want to have any impact on
    // anything outside our frame's rect, so if we don't overlay the entire image,
    // then also composite with the previous frame.
    if (previousBuffer && (compositeWithPreviousFrame || isSubRect)) {
        bytes = previousBuffer->bytes();
        buffer.ensureHeight(m_size.height());
        buffer.setHasAlpha(previousBuffer->hasAlpha());
    }
    else // Resize to the width and height of the image.
        bytes.resize(m_size.width() * m_size.height());

    if (isSubRect) {
        // We need to go ahead and initialize the first frame to make sure
        // that areas outside the subrect start off transparent.
        if (!previousBuffer) {
            bytes.fill(0);
            buffer.setHasAlpha(true);
        } else if (!compositeWithPreviousFrame) {
            // Now this is an interesting case.  In the case where we fill 
            // the entire image, we effectively do a full clear of the image (and thus
            // don't have to initialize anything in our buffer).
            // 
            // However in the case where we only fill a piece of the image, two problems occur:
            // (1) We need to wipe out the area occupied by the previous frame, which
            // could also have been a subrect.
            // (2) Anything outside the previous frame's rect *and* outside our current
            // frame's rect should be left alone.
            // We have handled (2) by just initializing our buffer from the previous frame.
            // Our subrect will correctly overwrite the previous frame's contents as we
            // decode rows.  However that still leaves the problem of having to wipe out
            // the area occupied by the previous frame that does not overlap with
            // the new frame.
            if (previousBuffer->rect() != frameRect) {
                // We have to clear out the entire previous subframe.
                bool sawAlpha = buffer.hasAlpha();
                IntRect prevRect = previousBuffer->rect();
                unsigned end = prevRect.y() + prevRect.height();
                unsigned* src;
                for (unsigned i = prevRect.y(); i < end; i++) {
                    unsigned* curr = buffer.bytes().data() + (i * m_size.width() + prevRect.x());
                    unsigned* end = curr + prevRect.width();
                    while (curr != end) {
                        if (!sawAlpha) {
                            sawAlpha = true;
                            buffer.setHasAlpha(true);
                        }
                        RGBA32Buffer::setRGBA(*curr++, 0, 0, 0, 0);
                    }
                }
            }
        }
    }

    // Update our status to be partially complete.
    buffer.setStatus(RGBA32Buffer::FramePartial);
}