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 | |
35 | namespace WebCore { |
36 | |
37 | // Convenience function to improve code readability, as WebPDemuxGetFrame is +1 based. |
38 | bool webpFrameAtIndex(WebPDemuxer* demuxer, size_t index, WebPIterator* webpFrame) |
39 | { |
40 | return WebPDemuxGetFrame(demuxer, index + 1, webpFrame); |
41 | } |
42 | |
43 | WEBPImageDecoder::WEBPImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) |
44 | : ScalableImageDecoder(alphaOption, gammaAndColorProfileOption) |
45 | { |
46 | } |
47 | |
48 | WEBPImageDecoder::~WEBPImageDecoder() = default; |
49 | |
50 | void 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 | |
65 | RepetitionCount WEBPImageDecoder::repetitionCount() const |
66 | { |
67 | if (failed()) |
68 | return RepetitionCountOnce; |
69 | |
70 | return m_repetitionCount ? m_repetitionCount : RepetitionCountInfinite; |
71 | } |
72 | |
73 | ScalableImageDecoderFrame* 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 | |
89 | size_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 | |
127 | void 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 | |
159 | void 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 | |
221 | bool 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 | |
260 | void 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 | |
290 | void WEBPImageDecoder::() |
291 | { |
292 | if (m_headerParsed) |
293 | return; |
294 | |
295 | m_headerParsed = true; |
296 | |
297 | const unsigned = 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 | |
338 | void 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 | |