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(); } }
VP8StatusCode DecodeWebPIncremental( const uint8_t* const data, size_t data_size, int verbose, WebPDecoderConfig* const config) { Stopwatch stop_watch; VP8StatusCode status = VP8_STATUS_OK; if (config == NULL) return VP8_STATUS_INVALID_PARAM; PrintAnimationWarning(config); StopwatchReset(&stop_watch); // Decoding call. { WebPIDecoder* const idec = WebPIDecode(data, data_size, config); if (idec == NULL) { fprintf(stderr, "Failed during WebPINewDecoder().\n"); return VP8_STATUS_OUT_OF_MEMORY; } else { #ifdef WEBP_EXPERIMENTAL_FEATURES size_t size = 0; const size_t incr = 2 + (data_size / 20); while (size < data_size) { size_t next_size = size + (rand() % incr); if (next_size > data_size) next_size = data_size; status = WebPIUpdate(idec, data, next_size); if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) break; size = next_size; } #else status = WebPIUpdate(idec, data, data_size); #endif WebPIDelete(idec); } } if (verbose) { const double decode_time = StopwatchReadAndReset(&stop_watch); fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time); } return status; }
bool WEBPImageDecoder::decode(bool onlySize) { if (failed()) return false; const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(m_data->data()); const size_t dataSize = m_data->size(); if (!ImageDecoder::isSizeAvailable()) { static const size_t imageHeaderSize = 30; if (dataSize < imageHeaderSize) return false; int width, height; #ifdef QCMS_WEBP_COLOR_CORRECTION WebPData inputData = { dataBytes, dataSize }; WebPDemuxState state; WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state); if (!demuxer) return setFailed(); width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); m_formatFlags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); m_hasAlpha = !!(m_formatFlags & ALPHA_FLAG); WebPDemuxDelete(demuxer); if (state <= WEBP_DEMUX_PARSING_HEADER) return false; #elif (WEBP_DECODER_ABI_VERSION >= 0x0163) WebPBitstreamFeatures features; if (WebPGetFeatures(dataBytes, dataSize, &features) != VP8_STATUS_OK) return setFailed(); width = features.width; height = features.height; m_hasAlpha = features.has_alpha; #else // Earlier version won't be able to display WebP files with alpha. if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) return setFailed(); m_hasAlpha = false; #endif if (!setSize(width, height)) return setFailed(); } ASSERT(ImageDecoder::isSizeAvailable()); if (onlySize) return true; ASSERT(!m_frameBufferCache.isEmpty()); ImageFrame& buffer = m_frameBufferCache[0]; ASSERT(buffer.status() != ImageFrame::FrameComplete); if (buffer.status() == ImageFrame::FrameEmpty) { if (!buffer.setSize(size().width(), size().height())) return setFailed(); buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(m_hasAlpha); buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } if (!m_decoder) { WEBP_CSP_MODE mode = outputMode(m_hasAlpha); if (!m_premultiplyAlpha) mode = outputMode(false); if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) mode = MODE_RGBA; // Decode to RGBA for input to libqcms. int rowStride = size().width() * sizeof(ImageFrame::PixelData); uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(0, 0)); int outputSize = size().height() * rowStride; m_decoder = WebPINewRGB(mode, output, outputSize, rowStride); if (!m_decoder) return setFailed(); } switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) applyColorProfile(dataBytes, dataSize, buffer); buffer.setStatus(ImageFrame::FrameComplete); clear(); return true; case VP8_STATUS_SUSPENDED: if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) applyColorProfile(dataBytes, dataSize, buffer); return false; default: clear(); return setFailed(); } }
bool WEBPImageDecoder::decode(bool onlySize) { if (failed()) return false; #if defined(__LB_SHELL__) // We dont want progressive decoding. if (!isAllDataReceived()) return false; #endif const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(m_data->data()); const size_t dataSize = m_data->size(); if (!ImageDecoder::isSizeAvailable()) { static const size_t imageHeaderSize = 30; if (dataSize < imageHeaderSize) return false; int width, height; #if (WEBP_DECODER_ABI_VERSION >= 0x0163) WebPBitstreamFeatures features; if (WebPGetFeatures(dataBytes, dataSize, &features) != VP8_STATUS_OK) return setFailed(); width = features.width; height = features.height; m_hasAlpha = features.has_alpha; #else // Earlier version won't be able to display WebP files with alpha. if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) return setFailed(); m_hasAlpha = false; #endif if (!setSize(width, height)) return setFailed(); } ASSERT(ImageDecoder::isSizeAvailable()); if (onlySize) return true; ASSERT(!m_frameBufferCache.isEmpty()); ImageFrame& buffer = m_frameBufferCache[0]; ASSERT(buffer.status() != ImageFrame::FrameComplete); if (buffer.status() == ImageFrame::FrameEmpty) { if (!buffer.setSize(size().width(), size().height())) return setFailed(); buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(m_hasAlpha); buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } if (!m_decoder) { int rowStride = size().width() * sizeof(ImageFrame::PixelData); uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(0, 0)); int outputSize = size().height() * rowStride; m_decoder = WebPINewRGB(outputMode(m_hasAlpha), output, outputSize, rowStride); if (!m_decoder) return setFailed(); } switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: buffer.setStatus(ImageFrame::FrameComplete); WebPIDelete(m_decoder); m_decoder = 0; return true; case VP8_STATUS_SUSPENDED: return false; default: WebPIDelete(m_decoder); m_decoder = 0; 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::decode(bool onlySize) { // Minimum number of bytes needed to ensure one can parse size information. static const size_t sizeOfHeader = 30; // Number of bytes per pixel. static const int bytesPerPixel = 3; if (failed()) return false; const size_t dataSize = m_data->size(); if (dataSize < sizeOfHeader) return true; int width, height; const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(m_data->data()); if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) return setFailed(); if (!ImageDecoder::isSizeAvailable() && !setSize(width, height)) return setFailed(); if (onlySize) return true; bool allDataReceived = isAllDataReceived(); int stride = width * bytesPerPixel; ASSERT(!m_frameBufferCache.isEmpty()); ImageFrame& buffer = m_frameBufferCache[0]; if (buffer.status() == ImageFrame::FrameEmpty) { ASSERT(width == size().width()); ASSERT(height == size().height()); if (!buffer.setSize(width, height)) return setFailed(); buffer.setStatus(allDataReceived ? ImageFrame::FrameComplete : ImageFrame::FramePartial); // FIXME: We currently hard code false below because libwebp doesn't support alpha yet. buffer.setHasAlpha(false); buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); m_rgbOutput.resize(height * stride); } int newLastVisibleRow = 0; // Last completed row. if (allDataReceived) { if (!WebPDecodeRGBInto(dataBytes, dataSize, m_rgbOutput.data(), m_rgbOutput.size(), stride)) return setFailed(); newLastVisibleRow = height; } else { if (!m_decoder) { m_decoder = WebPINewRGB(MODE_RGB, m_rgbOutput.data(), m_rgbOutput.size(), stride); if (!m_decoder) return setFailed(); } const VP8StatusCode status = WebPIUpdate(m_decoder, dataBytes, dataSize); if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) return setFailed(); if (!WebPIDecGetRGB(m_decoder, &newLastVisibleRow, 0, 0, 0)) return setFailed(); ASSERT(newLastVisibleRow >= 0); ASSERT(newLastVisibleRow <= height); } // FIXME: remove this data copy. for (int y = m_lastVisibleRow; y < newLastVisibleRow; ++y) { const uint8_t* const src = &m_rgbOutput[y * stride]; for (int x = 0; x < width; ++x) buffer.setRGBA(x, y, src[bytesPerPixel * x + 0], src[bytesPerPixel * x + 1], src[bytesPerPixel * x + 2], 0xff); } m_lastVisibleRow = newLastVisibleRow; if (m_lastVisibleRow == height) buffer.setStatus(ImageFrame::FrameComplete); return m_lastVisibleRow == height; }
bool WEBPImageDecoder::decode(bool onlySize) { #if PLATFORM(CHROMIUM) TRACE_EVENT("WEBPImageDecoder::decode", this, 0); #endif if (failed()) return false; const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(m_data->data()); const size_t dataSize = m_data->size(); if (!ImageDecoder::isSizeAvailable()) { static const size_t imageHeaderSize = 30; if (dataSize < imageHeaderSize) return false; int width, height; if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) return setFailed(); if (!setSize(width, height)) return setFailed(); } ASSERT(ImageDecoder::isSizeAvailable()); if (onlySize) return true; ASSERT(!m_frameBufferCache.isEmpty()); ImageFrame& buffer = m_frameBufferCache[0]; ASSERT(buffer.status() != ImageFrame::FrameComplete); if (buffer.status() == ImageFrame::FrameEmpty) { if (!buffer.setSize(size().width(), size().height())) return setFailed(); buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(false); // FIXME: webp does not support alpha yet. buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } if (!m_decoder) { int rowStride = size().width() * sizeof(ImageFrame::PixelData); uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(0, 0)); int outputSize = size().height() * rowStride; m_decoder = WebPINewRGB(outputMode(), output, outputSize, rowStride); if (!m_decoder) return setFailed(); } switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: buffer.setStatus(ImageFrame::FrameComplete); WebPIDelete(m_decoder); m_decoder = 0; return true; case VP8_STATUS_SUSPENDED: return false; default: WebPIDelete(m_decoder); m_decoder = 0; return setFailed(); } }