bool WEBPImageDecoder::updateDemuxer() { if (failed()) return false; if (m_haveAlreadyParsedThisData) return true; m_haveAlreadyParsedThisData = true; const unsigned webpHeaderSize = 30; if (m_data->size() < webpHeaderSize) return false; // Await VP8X header so WebPDemuxPartial succeeds. WebPDemuxDelete(m_demux); WebPData inputData = { reinterpret_cast<const uint8_t*>(m_data->data()), m_data->size() }; m_demux = WebPDemuxPartial(&inputData, &m_demuxState); if (!m_demux || (isAllDataReceived() && m_demuxState != WEBP_DEMUX_DONE)) return setFailed(); ASSERT(m_demuxState > WEBP_DEMUX_PARSING_HEADER); if (!WebPDemuxGetI(m_demux, WEBP_FF_FRAME_COUNT)) return false; // Wait until the encoded image frame data arrives. if (!isDecodedSizeAvailable()) { int width = WebPDemuxGetI(m_demux, WEBP_FF_CANVAS_WIDTH); int height = WebPDemuxGetI(m_demux, WEBP_FF_CANVAS_HEIGHT); if (!setSize(width, height)) return setFailed(); m_formatFlags = WebPDemuxGetI(m_demux, WEBP_FF_FORMAT_FLAGS); if (!(m_formatFlags & ANIMATION_FLAG)) { m_repetitionCount = cAnimationNone; } else { // Since we have parsed at least one frame, even if partially, // the global animation (ANIM) properties have been read since // an ANIM chunk must precede the ANMF frame chunks. m_repetitionCount = WebPDemuxGetI(m_demux, WEBP_FF_LOOP_COUNT); // Repetition count is always <= 16 bits. ASSERT(m_repetitionCount == (m_repetitionCount & 0xffff)); // Repetition count is the number of animation cycles to show, // where 0 means "infinite". But ImageSource::repetitionCount() // returns -1 for "infinite", and 0 and up for "show the image // animation one cycle more than the value". Subtract one here // to correctly handle the finite and infinite cases. --m_repetitionCount; // FIXME: Implement ICC profile support for animated images. m_formatFlags &= ~ICCP_FLAG; } #if USE(QCMSLIB) if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) readColorProfile(); #endif } ASSERT(isDecodedSizeAvailable()); return true; }
bool WEBPImageDecoder::decodeSingleFrame(const uint8_t* dataBytes, size_t dataSize, size_t frameIndex) { if (failed()) return false; ASSERT(isDecodedSizeAvailable()); ASSERT(m_frameBufferCache.size() > frameIndex); ImageFrame& buffer = m_frameBufferCache[frameIndex]; ASSERT(buffer.status() != ImageFrame::FrameComplete); if (buffer.status() == ImageFrame::FrameEmpty) { if (!buffer.setSize(size().width(), size().height())) return setFailed(); buffer.setStatus(ImageFrame::FramePartial); // The buffer is transparent outside the decoded area while the image is loading. // The correct value of 'hasAlpha' for the frame will be set when it is fully decoded. buffer.setHasAlpha(true); buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } const IntRect& frameRect = buffer.originalFrameRect(); if (!m_decoder) { WEBP_CSP_MODE mode = outputMode(m_formatFlags & ALPHA_FLAG); if (!m_premultiplyAlpha) mode = outputMode(false); #if USE(QCMSLIB) if (colorTransform()) mode = MODE_RGBA; // Decode to RGBA for input to libqcms. #endif WebPInitDecBuffer(&m_decoderBuffer); m_decoderBuffer.colorspace = mode; m_decoderBuffer.u.RGBA.stride = size().width() * sizeof(ImageFrame::PixelData); m_decoderBuffer.u.RGBA.size = m_decoderBuffer.u.RGBA.stride * frameRect.height(); m_decoderBuffer.is_external_memory = 1; m_decoder = WebPINewDecoder(&m_decoderBuffer); if (!m_decoder) return setFailed(); } m_decoderBuffer.u.RGBA.rgba = reinterpret_cast<uint8_t*>(buffer.getAddr(frameRect.x(), frameRect.y())); switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: applyPostProcessing(frameIndex); buffer.setHasAlpha((m_formatFlags & ALPHA_FLAG) || m_frameBackgroundHasAlpha); buffer.setStatus(ImageFrame::FrameComplete); clearDecoder(); return true; case VP8_STATUS_SUSPENDED: if (!isAllDataReceived() && !frameIsCompleteAtIndex(frameIndex)) { applyPostProcessing(frameIndex); return false; } // FALLTHROUGH default: clear(); return setFailed(); } }
bool WEBPImageDecoder::decodeSingleFrame(const uint8_t* dataBytes, size_t dataSize, size_t frameIndex) { if (failed()) return false; ASSERT(isDecodedSizeAvailable()); ASSERT(m_frameBufferCache.size() > frameIndex); ImageFrame& buffer = m_frameBufferCache[frameIndex]; ASSERT(buffer.getStatus() != ImageFrame::FrameComplete); if (buffer.getStatus() == ImageFrame::FrameEmpty) { if (!buffer.setSizeAndColorSpace(size().width(), size().height(), colorSpace())) return setFailed(); buffer.setStatus(ImageFrame::FramePartial); // The buffer is transparent outside the decoded area while the image is // loading. The correct alpha value for the frame will be set when it is // fully decoded. buffer.setHasAlpha(true); buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } const IntRect& frameRect = buffer.originalFrameRect(); if (!m_decoder) { WEBP_CSP_MODE mode = outputMode(m_formatFlags & ALPHA_FLAG); if (!m_premultiplyAlpha) mode = outputMode(false); if (colorTransform()) { // Swizzling between RGBA and BGRA is zero cost in a color transform. // So when we have a color transform, we should decode to whatever is // easiest for libwebp, and then let the color transform swizzle if // necessary. // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost). // Lossless webp is encoded as BGRA. This means decoding to BGRA is // either faster or the same cost as RGBA. mode = MODE_BGRA; } WebPInitDecBuffer(&m_decoderBuffer); m_decoderBuffer.colorspace = mode; m_decoderBuffer.u.RGBA.stride = size().width() * sizeof(ImageFrame::PixelData); m_decoderBuffer.u.RGBA.size = m_decoderBuffer.u.RGBA.stride * frameRect.height(); m_decoderBuffer.is_external_memory = 1; m_decoder = WebPINewDecoder(&m_decoderBuffer); if (!m_decoder) return setFailed(); } m_decoderBuffer.u.RGBA.rgba = reinterpret_cast<uint8_t*>(buffer.getAddr(frameRect.x(), frameRect.y())); switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: applyPostProcessing(frameIndex); buffer.setHasAlpha((m_formatFlags & ALPHA_FLAG) || m_frameBackgroundHasAlpha); buffer.setStatus(ImageFrame::FrameComplete); clearDecoder(); return true; case VP8_STATUS_SUSPENDED: if (!isAllDataReceived() && !frameIsCompleteAtIndex(frameIndex)) { applyPostProcessing(frameIndex); return false; } // FALLTHROUGH default: clear(); return setFailed(); } }
bool WEBPImageDecoder::updateDemuxer() { if (failed()) return false; if (m_haveAlreadyParsedThisData) return true; m_haveAlreadyParsedThisData = true; const unsigned webpHeaderSize = 30; if (m_data->size() < webpHeaderSize) return false; // Await VP8X header so WebPDemuxPartial succeeds. WebPDemuxDelete(m_demux); m_consolidatedData = m_data->getAsSkData(); WebPData inputData = { reinterpret_cast<const uint8_t*>(m_consolidatedData->data()), m_consolidatedData->size()}; m_demux = WebPDemuxPartial(&inputData, &m_demuxState); if (!m_demux || (isAllDataReceived() && m_demuxState != WEBP_DEMUX_DONE)) { if (!m_demux) m_consolidatedData.reset(); return setFailed(); } ASSERT(m_demuxState > WEBP_DEMUX_PARSING_HEADER); if (!WebPDemuxGetI(m_demux, WEBP_FF_FRAME_COUNT)) return false; // Wait until the encoded image frame data arrives. if (!isDecodedSizeAvailable()) { int width = WebPDemuxGetI(m_demux, WEBP_FF_CANVAS_WIDTH); int height = WebPDemuxGetI(m_demux, WEBP_FF_CANVAS_HEIGHT); if (!setSize(width, height)) return setFailed(); m_formatFlags = WebPDemuxGetI(m_demux, WEBP_FF_FORMAT_FLAGS); if (!(m_formatFlags & ANIMATION_FLAG)) { m_repetitionCount = cAnimationNone; } else { // Since we have parsed at least one frame, even if partially, // the global animation (ANIM) properties have been read since // an ANIM chunk must precede the ANMF frame chunks. m_repetitionCount = WebPDemuxGetI(m_demux, WEBP_FF_LOOP_COUNT); // Repetition count is always <= 16 bits. ASSERT(m_repetitionCount == (m_repetitionCount & 0xffff)); if (!m_repetitionCount) m_repetitionCount = cAnimationLoopInfinite; // FIXME: Implement ICC profile support for animated images. m_formatFlags &= ~ICCP_FLAG; } if ((m_formatFlags & ICCP_FLAG) && !ignoresColorSpace()) readColorProfile(); } ASSERT(isDecodedSizeAvailable()); size_t frameCount = WebPDemuxGetI(m_demux, WEBP_FF_FRAME_COUNT); updateAggressivePurging(frameCount); return true; }