diff -up webkitgtk-2.20.2/Source/cmake/FindWebP.cmake.no_webp_demux webkitgtk-2.20.2/Source/cmake/FindWebP.cmake --- webkitgtk-2.20.2/Source/cmake/FindWebP.cmake.no_webp_demux 2018-02-19 08:45:33.000000000 +0100 +++ webkitgtk-2.20.2/Source/cmake/FindWebP.cmake 2018-05-11 08:36:41.026235222 +0200 @@ -33,34 +33,20 @@ find_package(PkgConfig) pkg_check_modules(PC_WEBP QUIET libwebp) # Look for the header file. -find_path(WEBP_INCLUDE_DIR +find_path(WEBP_INCLUDE_DIRS NAMES webp/decode.h HINTS ${PC_WEBP_INCLUDEDIR} ${PC_WEBP_INCLUDE_DIRS} ) -list(APPEND WEBP_INCLUDE_DIRS ${WEBP_INCLUDE_DIR}) mark_as_advanced(WEBP_INCLUDE_DIRS) # Look for the library. find_library( - WEBP_LIBRARY + WEBP_LIBRARIES NAMES webp HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS} ) -list(APPEND WEBP_LIBRARIES ${WEBP_LIBRARY}) mark_as_advanced(WEBP_LIBRARIES) -find_path(WEBP_DEMUX_INCLUDE_DIR - NAMES webp/demux.h - HINTS ${PC_WEBP_INCLUDEDIR} ${PC_WEBP_INCLUDE_DIRS} -) -list(APPEND WEBP_INCLUDE_DIRS ${WEBP_DEMUX_INCLUDE_DIR}) - -find_library(WEBP_DEMUX_LIBRARY - NAMES webpdemux - HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS} -) -list(APPEND WEBP_LIBRARIES ${WEBP_DEMUX_LIBRARY}) - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WebP REQUIRED_VARS WEBP_INCLUDE_DIRS WEBP_LIBRARIES FOUND_VAR WEBP_FOUND) diff -up webkitgtk-2.20.2/Source/WebCore/platform/graphics/ImageBackingStore.h.no_webp_demux webkitgtk-2.20.2/Source/WebCore/platform/graphics/ImageBackingStore.h --- webkitgtk-2.20.2/Source/WebCore/platform/graphics/ImageBackingStore.h.no_webp_demux 2018-02-19 08:45:32.000000000 +0100 +++ webkitgtk-2.20.2/Source/WebCore/platform/graphics/ImageBackingStore.h 2018-05-11 08:36:41.027235218 +0200 @@ -147,6 +147,7 @@ public: setPixel(pixelAt(x, y), r, g, b, a); } +#if ENABLE(APNG) void blendPixel(RGBA32* dest, unsigned r, unsigned g, unsigned b, unsigned a) { if (!a) @@ -172,6 +173,7 @@ public: else *dest = makeUnPremultipliedRGBA(r, g, b, a); } +#endif static bool isOverSize(const IntSize& size) { diff -up webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp.no_webp_demux webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp --- webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp.no_webp_demux 2018-02-19 08:45:32.000000000 +0100 +++ webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp 2018-05-11 09:19:25.756550925 +0200 @@ -31,49 +31,47 @@ #if USE(WEBP) -namespace WebCore { +// Backward emulation for earlier versions than 0.1.99. +#if (WEBP_DECODER_ABI_VERSION < 0x0163) +#define MODE_rgbA MODE_RGBA +#define MODE_bgrA MODE_BGRA +#endif + +#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) +inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_rgbA : MODE_RGBA; } +#else // LITTLE_ENDIAN, output BGRA pixels. +inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_bgrA : MODE_BGRA; } +#endif -// Convenience function to improve code readability, as WebPDemuxGetFrame is +1 based. -bool webpFrameAtIndex(WebPDemuxer* demuxer, size_t index, WebPIterator* webpFrame) -{ - return WebPDemuxGetFrame(demuxer, index + 1, webpFrame); -} +namespace WebCore { WEBPImageDecoder::WEBPImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ScalableImageDecoder(alphaOption, gammaAndColorProfileOption) + , m_decoder(0) + , m_hasAlpha(false) { } -WEBPImageDecoder::~WEBPImageDecoder() = default; - -void WEBPImageDecoder::setData(SharedBuffer& data, bool allDataReceived) +WEBPImageDecoder::~WEBPImageDecoder() { - if (failed()) - return; - - // We need to ensure that the header is parsed everytime new data arrives, as the number - // of frames may change until we have the complete data. If the size has not been obtained - // yet, the call to ScalableImageDecoder::setData() will call parseHeader() and set - // m_headerParsed to true, so the call to parseHeader() at the end won't do anything. If the size - // is available, then parseHeader() is only called once. - m_headerParsed = false; - ScalableImageDecoder::setData(data, allDataReceived); - parseHeader(); + clear(); } -RepetitionCount WEBPImageDecoder::repetitionCount() const +void WEBPImageDecoder::clear() { - if (failed()) - return RepetitionCountOnce; - - return m_repetitionCount ? m_repetitionCount : RepetitionCountInfinite; + if (m_decoder) + WebPIDelete(m_decoder); + m_decoder = 0; } ImageFrame* WEBPImageDecoder::frameBufferAtIndex(size_t index) { - if (index >= frameCount()) + if (index) return 0; + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.grow(1); + // The size of m_frameBufferCache may be smaller than the index requested. This can happen // because new data may have arrived with a bigger frameCount, but decode() hasn't been called // yet, which is the one that resizes the cache. @@ -85,275 +83,73 @@ ImageFrame* WEBPImageDecoder::frameBuffe return &m_frameBufferCache[index]; } -size_t WEBPImageDecoder::findFirstRequiredFrameToDecode(size_t frameIndex, WebPDemuxer* demuxer) -{ - // The first frame doesn't depend on any other. - if (!frameIndex) - return 0; - - // Go backwards and find the first complete frame. - size_t firstIncompleteFrame = frameIndex; - for (; firstIncompleteFrame; --firstIncompleteFrame) { - if (m_frameBufferCache[firstIncompleteFrame - 1].isComplete()) - break; - } - - // Check if there are any independent frames between firstIncompleteFrame and frameIndex. - for (size_t firstIndependentFrame = frameIndex; firstIndependentFrame > firstIncompleteFrame ; --firstIndependentFrame) { - WebPIterator webpFrame; - if (!webpFrameAtIndex(demuxer, firstIndependentFrame, &webpFrame)) - continue; - - IntRect frameRect(webpFrame.x_offset, webpFrame.y_offset, webpFrame.width, webpFrame.height); - if (!frameRect.contains({ { }, size() })) - continue; - - // This frame covers the whole area and doesn't have alpha, so it can be rendered without - // dependencies. - if (!webpFrame.has_alpha) - return firstIndependentFrame; - - // This frame covers the whole area and its disposalMethod is RestoreToBackground, which means - // that the next frame will be rendered on top of a transparent background, and can be decoded - // without dependencies. This can only be checked for frames prior to frameIndex. - if (firstIndependentFrame < frameIndex && m_frameBufferCache[firstIndependentFrame].disposalMethod() == ImageFrame::DisposalMethod::RestoreToBackground) - return firstIndependentFrame + 1; - } - - return firstIncompleteFrame; -} - -void WEBPImageDecoder::decode(size_t frameIndex, bool allDataReceived) +bool WEBPImageDecoder::decode(bool onlySize, bool) { if (failed()) - return; - - // This can be executed both in the main thread (when not using async decoding) or in the decoding thread. - // When executed in the decoding thread, a call to setData() from the main thread may change the data - // the WebPDemuxer is using, leaving it in an inconsistent state, so we need to protect the data. - RefPtr protectedData(m_data); - WebPData inputData = { reinterpret_cast(protectedData->data()), protectedData->size() }; - WebPDemuxState demuxerState; - WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &demuxerState); - if (!demuxer) { - setFailed(); - return; - } - - m_frameBufferCache.resize(m_frameCount); - - // It is a fatal error if all data is received and we have decoded all frames available but the file is truncated. - if (frameIndex >= m_frameBufferCache.size() - 1 && allDataReceived && demuxer && demuxerState != WEBP_DEMUX_DONE) { - WebPDemuxDelete(demuxer); - setFailed(); - return; - } - - for (size_t i = findFirstRequiredFrameToDecode(frameIndex, demuxer); i <= frameIndex; i++) - decodeFrame(i, demuxer); + return false; - WebPDemuxDelete(demuxer); -} + const uint8_t* dataBytes = reinterpret_cast(m_data->data()); + const size_t dataSize = m_data->size(); -void WEBPImageDecoder::decodeFrame(size_t frameIndex, WebPDemuxer* demuxer) -{ - if (failed()) - return; + if (ScalableImageDecoder::encodedDataStatus() < EncodedDataStatus::SizeAvailable) { + 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(IntSize(width, height))) + return setFailed(); + } + + ASSERT(ScalableImageDecoder::encodedDataStatus() >= EncodedDataStatus::SizeAvailable); + if (onlySize) + return true; - WebPIterator webpFrame; - if (!webpFrameAtIndex(demuxer, frameIndex, &webpFrame)) - return; - - const uint8_t* dataBytes = reinterpret_cast(webpFrame.fragment.bytes); - size_t dataSize = webpFrame.fragment.size; - bool blend = webpFrame.blend_method == WEBP_MUX_BLEND ? true : false; - - ASSERT(m_frameBufferCache.size() > frameIndex); - ImageFrame& buffer = m_frameBufferCache[frameIndex]; - buffer.setDuration(Seconds::fromMilliseconds(webpFrame.duration)); - buffer.setDisposalMethod(webpFrame.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? ImageFrame::DisposalMethod::RestoreToBackground : ImageFrame::DisposalMethod::DoNotDispose); + ASSERT(!m_frameBufferCache.isEmpty()); + ImageFrame& buffer = m_frameBufferCache[0]; ASSERT(!buffer.isComplete()); - if (buffer.isInvalid() && !initFrameBuffer(frameIndex, &webpFrame)) { - setFailed(); - return; - } - - WebPDecBuffer decoderBuffer; - WebPInitDecBuffer(&decoderBuffer); - decoderBuffer.colorspace = MODE_RGBA; - decoderBuffer.u.RGBA.stride = webpFrame.width * sizeof(RGBA32); - decoderBuffer.u.RGBA.size = decoderBuffer.u.RGBA.stride * webpFrame.height; - decoderBuffer.is_external_memory = 1; - std::unique_ptr p(new uint8_t[decoderBuffer.u.RGBA.size]()); - decoderBuffer.u.RGBA.rgba = p.get(); - if (!decoderBuffer.u.RGBA.rgba) { - setFailed(); - return; - } - - WebPIDecoder* decoder = WebPINewDecoder(&decoderBuffer); - if (!decoder) { - setFailed(); - return; + if (buffer.isInvalid()) { + if (!buffer.initialize(size(), m_premultiplyAlpha)) + return setFailed(); + buffer.setDecodingStatus(DecodingStatus::Partial); + buffer.setHasAlpha(m_hasAlpha); + } + + if (!m_decoder) { + WEBP_CSP_MODE mode = outputMode(m_hasAlpha); + if (!m_premultiplyAlpha) + mode = outputMode(false); + int rowStride = size().width() * sizeof(RGBA32); + uint8_t* output = reinterpret_cast(buffer.backingStore()->pixelAt(0, 0)); + int outputSize = size().height() * rowStride; + m_decoder = WebPINewRGB(mode, output, outputSize, rowStride); + if (!m_decoder) + return setFailed(); } - switch (WebPIUpdate(decoder, dataBytes, dataSize)) { + switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: - applyPostProcessing(frameIndex, decoder, decoderBuffer, blend); buffer.setDecodingStatus(DecodingStatus::Complete); - break; + clear(); + return true; case VP8_STATUS_SUSPENDED: - if (!isAllDataReceived()) { - applyPostProcessing(frameIndex, decoder, decoderBuffer, blend); - buffer.setDecodingStatus(DecodingStatus::Partial); - break; - } - // Fallthrough. - default: - setFailed(); - } - - WebPIDelete(decoder); -} - -bool WEBPImageDecoder::initFrameBuffer(size_t frameIndex, const WebPIterator* webpFrame) -{ - if (frameIndex >= frameCount()) return false; - - ImageFrame& buffer = m_frameBufferCache[frameIndex]; - - // Initialize the frame rect in our buffer. - IntRect frameRect(webpFrame->x_offset, webpFrame->y_offset, webpFrame->width, webpFrame->height); - - // Make sure the frameRect doesn't extend outside the buffer. - frameRect.intersect({ { }, size() }); - - if (!frameIndex || !m_frameBufferCache[frameIndex - 1].backingStore()) { - // This frame doesn't rely on any previous data. - if (!buffer.initialize(size(), m_premultiplyAlpha)) - return false; - } else { - const ImageFrame& prevBuffer = m_frameBufferCache[frameIndex - 1]; - ASSERT(prevBuffer.isComplete()); - - // Preserve the last frame as the starting state for this frame. - if (!prevBuffer.backingStore() || !buffer.initialize(*prevBuffer.backingStore())) - return false; - - if (prevBuffer.disposalMethod() == ImageFrame::DisposalMethod::RestoreToBackground) { - // We want to clear the previous frame to transparent, without - // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer.backingStore()->frameRect(); - buffer.backingStore()->clearRect(prevRect); - } - } - - buffer.setHasAlpha(webpFrame->has_alpha); - buffer.backingStore()->setFrameRect(frameRect); - - return true; -} - -void WEBPImageDecoder::applyPostProcessing(size_t frameIndex, WebPIDecoder* decoder, WebPDecBuffer& decoderBuffer, bool blend) -{ - ImageFrame& buffer = m_frameBufferCache[frameIndex]; - int decodedWidth = 0; - int decodedHeight = 0; - if (!WebPIDecGetRGB(decoder, &decodedHeight, &decodedWidth, 0, 0)) - return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 - if (decodedHeight <= 0) - return; - - const IntRect& frameRect = buffer.backingStore()->frameRect(); - ASSERT_WITH_SECURITY_IMPLICATION(decodedWidth == frameRect.width()); - ASSERT_WITH_SECURITY_IMPLICATION(decodedHeight <= frameRect.height()); - const int left = frameRect.x(); - const int top = frameRect.y(); - - for (int y = 0; y < decodedHeight; y++) { - const int canvasY = top + y; - for (int x = 0; x < decodedWidth; x++) { - const int canvasX = left + x; - RGBA32* address = buffer.backingStore()->pixelAt(canvasX, canvasY); - uint8_t* pixel = decoderBuffer.u.RGBA.rgba + (y * frameRect.width() + x) * sizeof(RGBA32); - if (blend && (pixel[3] < 255)) - buffer.backingStore()->blendPixel(address, pixel[0], pixel[1], pixel[2], pixel[3]); - else - buffer.backingStore()->setPixel(address, pixel[0], pixel[1], pixel[2], pixel[3]); - } - } -} - -void WEBPImageDecoder::parseHeader() -{ - if (m_headerParsed) - return; - - m_headerParsed = true; - - const unsigned webpHeaderSize = 30; // RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8_FRAME_HEADER_SIZE - if (m_data->size() < webpHeaderSize) - return; // Await VP8X header so WebPDemuxPartial succeeds. - - WebPData inputData = { reinterpret_cast(m_data->data()), m_data->size() }; - WebPDemuxState demuxerState; - WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &demuxerState); - if (!demuxer) { - setFailed(); - return; - } - - m_frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT); - if (!m_frameCount) { - WebPDemuxDelete(demuxer); - return; // Wait until the encoded image frame data arrives. - } - - int width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); - int height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); - if (!isSizeAvailable() && !setSize(IntSize(width, height))) { - WebPDemuxDelete(demuxer); - return; - } - - m_formatFlags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); - if (!(m_formatFlags & ANIMATION_FLAG)) - m_repetitionCount = WebCore::RepetitionCountNone; - 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(demuxer, WEBP_FF_LOOP_COUNT); - ASSERT(m_repetitionCount == (m_repetitionCount & 0xffff)); // Loop count is always <= 16 bits. - if (!m_repetitionCount) - m_repetitionCount = WebCore::RepetitionCountInfinite; - } - - WebPDemuxDelete(demuxer); -} - -void WEBPImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) -{ - if (m_frameBufferCache.isEmpty()) - return; - - // We don't want to delete the last frame in the cache, as is may be needed for - // decoding when new data arrives. See GIFImageDecoder for the full explanation. - clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1); - - // Also from GIFImageDecoder: We need to preserve frames such that: - // * We don't clear |clearBeforeFrame|. - // * We don't clear the frame we're currently decoding. - // * We don't clear any frame from which a future initFrameBuffer() call will copy bitmap data. - // - // In WEBP every frame depends on the previous one or none. That means that frames after clearBeforeFrame - // won't need any frame before them to render, so we can clear them all. - for (int i = clearBeforeFrame - 1; i >= 0; i--) { - ImageFrame& buffer = m_frameBufferCache[i]; - if (!buffer.isInvalid()) - buffer.clear(); + default: + clear(); + return setFailed(); } } diff -up webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h.no_webp_demux webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h --- webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h.no_webp_demux 2018-02-19 08:45:32.000000000 +0100 +++ webkitgtk-2.20.2/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h 2018-05-11 08:36:41.029235210 +0200 @@ -33,7 +33,6 @@ #if USE(WEBP) #include "webp/decode.h" -#include "webp/demux.h" namespace WebCore { @@ -47,26 +46,19 @@ public: virtual ~WEBPImageDecoder(); String filenameExtension() const override { return ASCIILiteral("webp"); } - void setData(SharedBuffer&, bool) final; ImageFrame* frameBufferAtIndex(size_t index) override; - RepetitionCount repetitionCount() const override; - size_t frameCount() const override { return m_frameCount; } - void clearFrameBufferCache(size_t) override; private: WEBPImageDecoder(AlphaOption, GammaAndColorProfileOption); - void tryDecodeSize(bool) override { parseHeader(); } - void decode(size_t, bool); - void decodeFrame(size_t, WebPDemuxer*); - void parseHeader(); - bool initFrameBuffer(size_t, const WebPIterator*); - void applyPostProcessing(size_t, WebPIDecoder*, WebPDecBuffer&, bool); - size_t findFirstRequiredFrameToDecode(size_t, WebPDemuxer*); - - int m_repetitionCount { 0 }; - size_t m_frameCount { 0 }; - int m_formatFlags { 0 }; - bool m_headerParsed { false }; + void tryDecodeSize(bool allDataReceived) override { decode(true, allDataReceived); } + + bool decode(bool onlySize, bool allDataReceived); + + WebPIDecoder* m_decoder; + bool m_hasAlpha; + + void applyColorProfile(const uint8_t*, size_t, ImageFrame&) { }; + void clear(); }; } // namespace WebCore