1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 * Copyright (C) 2008-2009 Torch Mobile, Inc.
4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "ScalableImageDecoder.h"
25
26#include "BMPImageDecoder.h"
27#include "GIFImageDecoder.h"
28#include "ICOImageDecoder.h"
29#include "JPEGImageDecoder.h"
30#include "PNGImageDecoder.h"
31#include "SharedBuffer.h"
32#if USE(OPENJPEG)
33#include "JPEG2000ImageDecoder.h"
34#endif
35#if USE(WEBP)
36#include "WEBPImageDecoder.h"
37#endif
38
39#include <algorithm>
40#include <cmath>
41
42
43namespace WebCore {
44
45namespace {
46
47static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer)
48{
49 unsigned bytesExtracted = 0;
50 for (const auto& element : sharedBuffer) {
51 if (bytesExtracted + element.segment->size() <= bufferLength) {
52 memcpy(buffer + bytesExtracted, element.segment->data(), element.segment->size());
53 bytesExtracted += element.segment->size();
54 } else {
55 ASSERT(bufferLength - bytesExtracted < element.segment->size());
56 memcpy(buffer + bytesExtracted, element.segment->data(), bufferLength - bytesExtracted);
57 bytesExtracted = bufferLength;
58 break;
59 }
60 }
61 return bytesExtracted;
62}
63
64bool matchesGIFSignature(char* contents)
65{
66 return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
67}
68
69bool matchesPNGSignature(char* contents)
70{
71 return !memcmp(contents, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8);
72}
73
74bool matchesJPEGSignature(char* contents)
75{
76 return !memcmp(contents, "\xFF\xD8\xFF", 3);
77}
78
79#if USE(OPENJPEG)
80bool matchesJP2Signature(char* contents)
81{
82 return !memcmp(contents, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A", 12)
83 || !memcmp(contents, "\x0D\x0A\x87\x0A", 4);
84}
85
86bool matchesJ2KSignature(char* contents)
87{
88 return !memcmp(contents, "\xFF\x4F\xFF\x51", 4);
89}
90#endif
91
92#if USE(WEBP)
93bool matchesWebPSignature(char* contents)
94{
95 return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
96}
97#endif
98
99bool matchesBMPSignature(char* contents)
100{
101 return !memcmp(contents, "BM", 2);
102}
103
104bool matchesICOSignature(char* contents)
105{
106 return !memcmp(contents, "\x00\x00\x01\x00", 4);
107}
108
109bool matchesCURSignature(char* contents)
110{
111 return !memcmp(contents, "\x00\x00\x02\x00", 4);
112}
113
114}
115
116RefPtr<ScalableImageDecoder> ScalableImageDecoder::create(SharedBuffer& data, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
117{
118 static const unsigned lengthOfLongestSignature = 14; // To wit: "RIFF????WEBPVP"
119 char contents[lengthOfLongestSignature];
120 unsigned length = copyFromSharedBuffer(contents, lengthOfLongestSignature, data);
121 if (length < lengthOfLongestSignature)
122 return nullptr;
123
124 if (matchesGIFSignature(contents))
125 return GIFImageDecoder::create(alphaOption, gammaAndColorProfileOption);
126
127 if (matchesPNGSignature(contents))
128 return PNGImageDecoder::create(alphaOption, gammaAndColorProfileOption);
129
130 if (matchesICOSignature(contents) || matchesCURSignature(contents))
131 return ICOImageDecoder::create(alphaOption, gammaAndColorProfileOption);
132
133 if (matchesJPEGSignature(contents))
134 return JPEGImageDecoder::create(alphaOption, gammaAndColorProfileOption);
135
136#if USE(OPENJPEG)
137 if (matchesJP2Signature(contents))
138 return JPEG2000ImageDecoder::create(JPEG2000ImageDecoder::Format::JP2, alphaOption, gammaAndColorProfileOption);
139
140 if (matchesJ2KSignature(contents))
141 return JPEG2000ImageDecoder::create(JPEG2000ImageDecoder::Format::J2K, alphaOption, gammaAndColorProfileOption);
142#endif
143
144#if USE(WEBP)
145 if (matchesWebPSignature(contents))
146 return WEBPImageDecoder::create(alphaOption, gammaAndColorProfileOption);
147#endif
148
149 if (matchesBMPSignature(contents))
150 return BMPImageDecoder::create(alphaOption, gammaAndColorProfileOption);
151
152 return nullptr;
153}
154
155namespace {
156
157enum MatchType {
158 Exact,
159 UpperBound,
160 LowerBound
161};
162
163inline void fillScaledValues(Vector<int>& scaledValues, double scaleRate, int length)
164{
165 double inflateRate = 1. / scaleRate;
166 scaledValues.reserveCapacity(static_cast<int>(length * scaleRate + 0.5));
167 for (int scaledIndex = 0; ; ++scaledIndex) {
168 int index = static_cast<int>(scaledIndex * inflateRate + 0.5);
169 if (index >= length)
170 break;
171 scaledValues.append(index);
172 }
173}
174
175template <MatchType type> int getScaledValue(const Vector<int>& scaledValues, int valueToMatch, int searchStart)
176{
177 if (scaledValues.isEmpty())
178 return valueToMatch;
179
180 const int* dataStart = scaledValues.data();
181 const int* dataEnd = dataStart + scaledValues.size();
182 const int* matched = std::lower_bound(dataStart + searchStart, dataEnd, valueToMatch);
183 switch (type) {
184 case Exact:
185 return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : -1;
186 case LowerBound:
187 return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : matched - dataStart - 1;
188 case UpperBound:
189 default:
190 return matched != dataEnd ? matched - dataStart : -1;
191 }
192}
193
194}
195
196bool ScalableImageDecoder::frameIsCompleteAtIndex(size_t index) const
197{
198 LockHolder lockHolder(m_mutex);
199 if (index >= m_frameBufferCache.size())
200 return false;
201
202 auto& frame = m_frameBufferCache[index];
203 return frame.isComplete();
204}
205
206bool ScalableImageDecoder::frameHasAlphaAtIndex(size_t index) const
207{
208 LockHolder lockHolder(m_mutex);
209 if (m_frameBufferCache.size() <= index)
210 return true;
211
212 auto& frame = m_frameBufferCache[index];
213 if (!frame.isComplete())
214 return true;
215 return frame.hasAlpha();
216}
217
218unsigned ScalableImageDecoder::frameBytesAtIndex(size_t index, SubsamplingLevel) const
219{
220 LockHolder lockHolder(m_mutex);
221 if (m_frameBufferCache.size() <= index)
222 return 0;
223 // FIXME: Use the dimension of the requested frame.
224 return (m_size.area() * sizeof(uint32_t)).unsafeGet();
225}
226
227Seconds ScalableImageDecoder::frameDurationAtIndex(size_t index) const
228{
229 LockHolder lockHolder(m_mutex);
230 if (index >= m_frameBufferCache.size())
231 return 0_s;
232
233 // Returning 0_s in case of an incomplete frame can break display of animated image formats.
234 // We pick up the decoded duration if it's available, otherwise the default 0_s value is
235 // adjusted below.
236 Seconds duration = 0_s;
237 auto& frame = m_frameBufferCache[index];
238 if (frame.isComplete())
239 duration = frame.duration();
240
241 // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
242 // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
243 // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
244 // for more information.
245 if (duration < 11_ms)
246 return 100_ms;
247 return duration;
248}
249
250NativeImagePtr ScalableImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, const DecodingOptions&)
251{
252 LockHolder lockHolder(m_mutex);
253 // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail.
254 if (size().isEmpty())
255 return nullptr;
256
257 auto* buffer = frameBufferAtIndex(index);
258 if (!buffer || buffer->isInvalid() || !buffer->hasBackingStore())
259 return nullptr;
260
261 // Return the buffer contents as a native image. For some ports, the data
262 // is already in a native container, and this just increments its refcount.
263 return buffer->backingStore()->image();
264}
265
266void ScalableImageDecoder::prepareScaleDataIfNecessary()
267{
268 m_scaled = false;
269 m_scaledColumns.clear();
270 m_scaledRows.clear();
271
272 int width = size().width();
273 int height = size().height();
274 int numPixels = height * width;
275 if (m_maxNumPixels <= 0 || numPixels <= m_maxNumPixels)
276 return;
277
278 m_scaled = true;
279 double scale = sqrt(m_maxNumPixels / (double)numPixels);
280 fillScaledValues(m_scaledColumns, scale, width);
281 fillScaledValues(m_scaledRows, scale, height);
282}
283
284int ScalableImageDecoder::upperBoundScaledX(int origX, int searchStart)
285{
286 return getScaledValue<UpperBound>(m_scaledColumns, origX, searchStart);
287}
288
289int ScalableImageDecoder::lowerBoundScaledX(int origX, int searchStart)
290{
291 return getScaledValue<LowerBound>(m_scaledColumns, origX, searchStart);
292}
293
294int ScalableImageDecoder::upperBoundScaledY(int origY, int searchStart)
295{
296 return getScaledValue<UpperBound>(m_scaledRows, origY, searchStart);
297}
298
299int ScalableImageDecoder::lowerBoundScaledY(int origY, int searchStart)
300{
301 return getScaledValue<LowerBound>(m_scaledRows, origY, searchStart);
302}
303
304int ScalableImageDecoder::scaledY(int origY, int searchStart)
305{
306 return getScaledValue<Exact>(m_scaledRows, origY, searchStart);
307}
308
309}
310