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(); }
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(); }
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); }