1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "WEBPImageDecoder.h"
31#include <wtf/UniqueArray.h>
32
33#if USE(WEBP)
34
35namespace WebCore {
36
37// Convenience function to improve code readability, as WebPDemuxGetFrame is +1 based.
38bool webpFrameAtIndex(WebPDemuxer* demuxer, size_t index, WebPIterator* webpFrame)
39{
40 return WebPDemuxGetFrame(demuxer, index + 1, webpFrame);
41}
42
43WEBPImageDecoder::WEBPImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
44 : ScalableImageDecoder(alphaOption, gammaAndColorProfileOption)
45{
46}
47
48WEBPImageDecoder::~WEBPImageDecoder() = default;
49
50void WEBPImageDecoder::setData(SharedBuffer& data, bool allDataReceived)
51{
52 if (failed())
53 return;
54
55 // We need to ensure that the header is parsed everytime new data arrives, as the number
56 // of frames may change until we have the complete data. If the size has not been obtained
57 // yet, the call to ScalableImageDecoder::setData() will call parseHeader() and set
58 // m_headerParsed to true, so the call to parseHeader() at the end won't do anything. If the size
59 // is available, then parseHeader() is only called once.
60 m_headerParsed = false;
61 ScalableImageDecoder::setData(data, allDataReceived);
62 parseHeader();
63}
64
65RepetitionCount WEBPImageDecoder::repetitionCount() const
66{
67 if (failed())
68 return RepetitionCountOnce;
69
70 return m_repetitionCount ? m_repetitionCount : RepetitionCountInfinite;
71}
72
73ScalableImageDecoderFrame* WEBPImageDecoder::frameBufferAtIndex(size_t index)
74{
75 if (index >= frameCount())
76 return 0;
77
78 // The size of m_frameBufferCache may be smaller than the index requested. This can happen
79 // because new data may have arrived with a bigger frameCount, but decode() hasn't been called
80 // yet, which is the one that resizes the cache.
81 if ((m_frameBufferCache.size() > index) && m_frameBufferCache[index].isComplete())
82 return &m_frameBufferCache[index];
83
84 decode(index, isAllDataReceived());
85
86 return &m_frameBufferCache[index];
87}
88
89size_t WEBPImageDecoder::findFirstRequiredFrameToDecode(size_t frameIndex, WebPDemuxer* demuxer)
90{
91 // The first frame doesn't depend on any other.
92 if (!frameIndex)
93 return 0;
94
95 // Go backwards and find the first complete frame.
96 size_t firstIncompleteFrame = frameIndex;
97 for (; firstIncompleteFrame; --firstIncompleteFrame) {
98 if (m_frameBufferCache[firstIncompleteFrame - 1].isComplete())
99 break;
100 }
101
102 // Check if there are any independent frames between firstIncompleteFrame and frameIndex.
103 for (size_t firstIndependentFrame = frameIndex; firstIndependentFrame > firstIncompleteFrame ; --firstIndependentFrame) {
104 WebPIterator webpFrame;
105 if (!webpFrameAtIndex(demuxer, firstIndependentFrame, &webpFrame))
106 continue;
107
108 IntRect frameRect(webpFrame.x_offset, webpFrame.y_offset, webpFrame.width, webpFrame.height);
109 if (!frameRect.contains({ { }, size() }))
110 continue;
111
112 // This frame covers the whole area and doesn't have alpha, so it can be rendered without
113 // dependencies.
114 if (!webpFrame.has_alpha)
115 return firstIndependentFrame;
116
117 // This frame covers the whole area and its disposalMethod is RestoreToBackground, which means
118 // that the next frame will be rendered on top of a transparent background, and can be decoded
119 // without dependencies. This can only be checked for frames prior to frameIndex.
120 if (firstIndependentFrame < frameIndex && m_frameBufferCache[firstIndependentFrame].disposalMethod() == ScalableImageDecoderFrame::DisposalMethod::RestoreToBackground)
121 return firstIndependentFrame + 1;
122 }
123
124 return firstIncompleteFrame;
125}
126
127void WEBPImageDecoder::decode(size_t frameIndex, bool allDataReceived)
128{
129 if (failed())
130 return;
131
132 // This can be executed both in the main thread (when not using async decoding) or in the decoding thread.
133 // When executed in the decoding thread, a call to setData() from the main thread may change the data
134 // the WebPDemuxer is using, leaving it in an inconsistent state, so we need to protect the data.
135 RefPtr<SharedBuffer> protectedData(m_data);
136 WebPData inputData = { reinterpret_cast<const uint8_t*>(protectedData->data()), protectedData->size() };
137 WebPDemuxState demuxerState;
138 WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &demuxerState);
139 if (!demuxer) {
140 setFailed();
141 return;
142 }
143
144 m_frameBufferCache.resize(m_frameCount);
145
146 // It is a fatal error if all data is received and we have decoded all frames available but the file is truncated.
147 if (frameIndex >= m_frameBufferCache.size() - 1 && allDataReceived && demuxer && demuxerState != WEBP_DEMUX_DONE) {
148 WebPDemuxDelete(demuxer);
149 setFailed();
150 return;
151 }
152
153 for (size_t i = findFirstRequiredFrameToDecode(frameIndex, demuxer); i <= frameIndex; i++)
154 decodeFrame(i, demuxer);
155
156 WebPDemuxDelete(demuxer);
157}
158
159void WEBPImageDecoder::decodeFrame(size_t frameIndex, WebPDemuxer* demuxer)
160{
161 if (failed())
162 return;
163
164 WebPIterator webpFrame;
165 if (!webpFrameAtIndex(demuxer, frameIndex, &webpFrame))
166 return;
167
168 const uint8_t* dataBytes = reinterpret_cast<const uint8_t*>(webpFrame.fragment.bytes);
169 size_t dataSize = webpFrame.fragment.size;
170 bool blend = webpFrame.blend_method == WEBP_MUX_BLEND ? true : false;
171
172 ASSERT(m_frameBufferCache.size() > frameIndex);
173 auto& buffer = m_frameBufferCache[frameIndex];
174 buffer.setDuration(Seconds::fromMilliseconds(webpFrame.duration));
175 buffer.setDisposalMethod(webpFrame.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? ScalableImageDecoderFrame::DisposalMethod::RestoreToBackground : ScalableImageDecoderFrame::DisposalMethod::DoNotDispose);
176 ASSERT(!buffer.isComplete());
177
178 if (buffer.isInvalid() && !initFrameBuffer(frameIndex, &webpFrame)) {
179 setFailed();
180 return;
181 }
182
183 WebPDecBuffer decoderBuffer;
184 WebPInitDecBuffer(&decoderBuffer);
185 decoderBuffer.colorspace = MODE_RGBA;
186 decoderBuffer.u.RGBA.stride = webpFrame.width * sizeof(uint32_t);
187 decoderBuffer.u.RGBA.size = decoderBuffer.u.RGBA.stride * webpFrame.height;
188 decoderBuffer.is_external_memory = 1;
189 auto p = makeUniqueArray<uint8_t>(decoderBuffer.u.RGBA.size);
190 decoderBuffer.u.RGBA.rgba = p.get();
191 if (!decoderBuffer.u.RGBA.rgba) {
192 setFailed();
193 return;
194 }
195
196 WebPIDecoder* decoder = WebPINewDecoder(&decoderBuffer);
197 if (!decoder) {
198 setFailed();
199 return;
200 }
201
202 switch (WebPIUpdate(decoder, dataBytes, dataSize)) {
203 case VP8_STATUS_OK:
204 applyPostProcessing(frameIndex, decoder, decoderBuffer, blend);
205 buffer.setDecodingStatus(DecodingStatus::Complete);
206 break;
207 case VP8_STATUS_SUSPENDED:
208 if (!isAllDataReceived()) {
209 applyPostProcessing(frameIndex, decoder, decoderBuffer, blend);
210 buffer.setDecodingStatus(DecodingStatus::Partial);
211 break;
212 }
213 // Fallthrough.
214 default:
215 setFailed();
216 }
217
218 WebPIDelete(decoder);
219}
220
221bool WEBPImageDecoder::initFrameBuffer(size_t frameIndex, const WebPIterator* webpFrame)
222{
223 if (frameIndex >= frameCount())
224 return false;
225
226 auto& buffer = m_frameBufferCache[frameIndex];
227
228 // Initialize the frame rect in our buffer.
229 IntRect frameRect(webpFrame->x_offset, webpFrame->y_offset, webpFrame->width, webpFrame->height);
230
231 // Make sure the frameRect doesn't extend outside the buffer.
232 frameRect.intersect({ { }, size() });
233
234 if (!frameIndex || !m_frameBufferCache[frameIndex - 1].backingStore()) {
235 // This frame doesn't rely on any previous data.
236 if (!buffer.initialize(size(), m_premultiplyAlpha))
237 return false;
238 } else {
239 const auto& prevBuffer = m_frameBufferCache[frameIndex - 1];
240 ASSERT(prevBuffer.isComplete());
241
242 // Preserve the last frame as the starting state for this frame.
243 if (!prevBuffer.backingStore() || !buffer.initialize(*prevBuffer.backingStore()))
244 return false;
245
246 if (prevBuffer.disposalMethod() == ScalableImageDecoderFrame::DisposalMethod::RestoreToBackground) {
247 // We want to clear the previous frame to transparent, without
248 // affecting pixels in the image outside of the frame.
249 const IntRect& prevRect = prevBuffer.backingStore()->frameRect();
250 buffer.backingStore()->clearRect(prevRect);
251 }
252 }
253
254 buffer.setHasAlpha(webpFrame->has_alpha);
255 buffer.backingStore()->setFrameRect(frameRect);
256
257 return true;
258}
259
260void WEBPImageDecoder::applyPostProcessing(size_t frameIndex, WebPIDecoder* decoder, WebPDecBuffer& decoderBuffer, bool blend)
261{
262 auto& buffer = m_frameBufferCache[frameIndex];
263 int decodedWidth = 0;
264 int decodedHeight = 0;
265 if (!WebPIDecGetRGB(decoder, &decodedHeight, &decodedWidth, 0, 0))
266 return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062
267 if (decodedHeight <= 0)
268 return;
269
270 const IntRect& frameRect = buffer.backingStore()->frameRect();
271 ASSERT_WITH_SECURITY_IMPLICATION(decodedWidth == frameRect.width());
272 ASSERT_WITH_SECURITY_IMPLICATION(decodedHeight <= frameRect.height());
273 const int left = frameRect.x();
274 const int top = frameRect.y();
275
276 for (int y = 0; y < decodedHeight; y++) {
277 const int canvasY = top + y;
278 for (int x = 0; x < decodedWidth; x++) {
279 const int canvasX = left + x;
280 auto* address = buffer.backingStore()->pixelAt(canvasX, canvasY);
281 uint8_t* pixel = decoderBuffer.u.RGBA.rgba + (y * frameRect.width() + x) * sizeof(uint32_t);
282 if (blend && (pixel[3] < 255))
283 buffer.backingStore()->blendPixel(address, pixel[0], pixel[1], pixel[2], pixel[3]);
284 else
285 buffer.backingStore()->setPixel(address, pixel[0], pixel[1], pixel[2], pixel[3]);
286 }
287 }
288}
289
290void WEBPImageDecoder::parseHeader()
291{
292 if (m_headerParsed)
293 return;
294
295 m_headerParsed = true;
296
297 const unsigned webpHeaderSize = 30; // RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8_FRAME_HEADER_SIZE
298 if (m_data->size() < webpHeaderSize)
299 return; // Await VP8X header so WebPDemuxPartial succeeds.
300
301 WebPData inputData = { reinterpret_cast<const uint8_t*>(m_data->data()), m_data->size() };
302 WebPDemuxState demuxerState;
303 WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &demuxerState);
304 if (!demuxer) {
305 setFailed();
306 return;
307 }
308
309 m_frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
310 if (!m_frameCount) {
311 WebPDemuxDelete(demuxer);
312 return; // Wait until the encoded image frame data arrives.
313 }
314
315 int width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
316 int height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
317 if (!isSizeAvailable() && !setSize(IntSize(width, height))) {
318 WebPDemuxDelete(demuxer);
319 return;
320 }
321
322 m_formatFlags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
323 if (!(m_formatFlags & ANIMATION_FLAG))
324 m_repetitionCount = WebCore::RepetitionCountNone;
325 else {
326 // Since we have parsed at least one frame, even if partially,
327 // the global animation (ANIM) properties have been read since
328 // an ANIM chunk must precede the ANMF frame chunks.
329 m_repetitionCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
330 ASSERT(m_repetitionCount == (m_repetitionCount & 0xffff)); // Loop count is always <= 16 bits.
331 if (!m_repetitionCount)
332 m_repetitionCount = WebCore::RepetitionCountInfinite;
333 }
334
335 WebPDemuxDelete(demuxer);
336}
337
338void WEBPImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
339{
340 if (m_frameBufferCache.isEmpty())
341 return;
342
343 // We don't want to delete the last frame in the cache, as is may be needed for
344 // decoding when new data arrives. See GIFImageDecoder for the full explanation.
345 clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1);
346
347 // Also from GIFImageDecoder: We need to preserve frames such that:
348 // * We don't clear |clearBeforeFrame|.
349 // * We don't clear the frame we're currently decoding.
350 // * We don't clear any frame from which a future initFrameBuffer() call will copy bitmap data.
351 //
352 // In WEBP every frame depends on the previous one or none. That means that frames after clearBeforeFrame
353 // won't need any frame before them to render, so we can clear them all.
354 for (int i = clearBeforeFrame - 1; i >= 0; i--) {
355 auto& buffer = m_frameBufferCache[i];
356 if (!buffer.isInvalid())
357 buffer.clear();
358 }
359}
360
361} // namespace WebCore
362
363#endif
364