1/*
2 * Copyright (C) 2008-2009 Torch Mobile, Inc.
3 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Library General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Library General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Library General Public License
16 *  along with this library; see the file COPYING.LIB.  If not, write to
17 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 *  Boston, MA 02110-1301, USA.
19 *
20 */
21
22#include "config.h"
23
24#include "ImageDecoder.h"
25
26#include <algorithm>
27#include <cmath>
28
29#include "BMPImageDecoder.h"
30#include "GIFImageDecoder.h"
31#include "ICOImageDecoder.h"
32#include "JPEGImageDecoder.h"
33#include "PNGImageDecoder.h"
34#include "WEBPImageDecoder.h"
35#include "SharedBuffer.h"
36
37using namespace std;
38
39namespace WebCore {
40
41namespace {
42
43unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset)
44{
45    unsigned bytesExtracted = 0;
46    const char* moreData;
47    while (unsigned moreDataLength = sharedBuffer.getSomeData(moreData, offset)) {
48        unsigned bytesToCopy = min(bufferLength - bytesExtracted, moreDataLength);
49        memcpy(buffer + bytesExtracted, moreData, bytesToCopy);
50        bytesExtracted += bytesToCopy;
51        if (bytesExtracted == bufferLength)
52            break;
53        offset += bytesToCopy;
54    }
55    return bytesExtracted;
56}
57
58bool matchesGIFSignature(char* contents)
59{
60    return !memcmp(contents, "GIF8", 4);
61}
62
63bool matchesPNGSignature(char* contents)
64{
65    return !memcmp(contents, "\x89\x50\x4E\x47", 4);
66}
67
68bool matchesJPEGSignature(char* contents)
69{
70    return !memcmp(contents, "\xFF\xD8\xFF", 3);
71}
72
73#if USE(WEBP)
74bool matchesWebPSignature(char* contents)
75{
76    return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
77}
78#endif
79
80bool matchesBMPSignature(char* contents)
81{
82    return !memcmp(contents, "BM", 2);
83}
84
85bool matchesICOSignature(char* contents)
86{
87    return !memcmp(contents, "\x00\x00\x01\x00", 4);
88}
89
90bool matchesCURSignature(char* contents)
91{
92    return !memcmp(contents, "\x00\x00\x02\x00", 4);
93}
94
95}
96
97#if !OS(ANDROID)
98// This method requires BMPImageDecoder, PNGImageDecoder, ICOImageDecoder and
99// JPEGDecoder, which aren't used on Android, and which don't all compile.
100// TODO: Find a better fix.
101ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
102{
103    static const unsigned lengthOfLongestSignature = 14; // To wit: "RIFF????WEBPVP"
104    char contents[lengthOfLongestSignature];
105    unsigned length = copyFromSharedBuffer(contents, lengthOfLongestSignature, data, 0);
106    if (length < lengthOfLongestSignature)
107        return 0;
108
109    if (matchesGIFSignature(contents))
110        return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption);
111
112    if (matchesPNGSignature(contents))
113        return new PNGImageDecoder(alphaOption, gammaAndColorProfileOption);
114
115    if (matchesJPEGSignature(contents))
116        return new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption);
117
118#if USE(WEBP)
119    if (matchesWebPSignature(contents))
120        return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption);
121#endif
122
123    if (matchesBMPSignature(contents))
124        return new BMPImageDecoder(alphaOption, gammaAndColorProfileOption);
125
126    if (matchesICOSignature(contents) || matchesCURSignature(contents))
127        return new ICOImageDecoder(alphaOption, gammaAndColorProfileOption);
128
129    return 0;
130}
131#endif // !OS(ANDROID)
132
133#if !USE(SKIA)
134
135ImageFrame::ImageFrame()
136    : m_hasAlpha(false)
137    , m_status(FrameEmpty)
138    , m_duration(0)
139    , m_disposalMethod(DisposeNotSpecified)
140    , m_premultiplyAlpha(true)
141{
142}
143
144ImageFrame& ImageFrame::operator=(const ImageFrame& other)
145{
146    if (this == &other)
147        return *this;
148
149    copyReferenceToBitmapData(other);
150    setOriginalFrameRect(other.originalFrameRect());
151    setStatus(other.status());
152    setDuration(other.duration());
153    setDisposalMethod(other.disposalMethod());
154    setPremultiplyAlpha(other.premultiplyAlpha());
155    return *this;
156}
157
158void ImageFrame::clearPixelData()
159{
160    m_backingStore.clear();
161    m_bytes = 0;
162    m_status = FrameEmpty;
163    // NOTE: Do not reset other members here; clearFrameBufferCache() calls this
164    // to free the bitmap data, but other functions like initFrameBuffer() and
165    // frameComplete() may still need to read other metadata out of this frame
166    // later.
167}
168
169void ImageFrame::zeroFillPixelData()
170{
171    memset(m_bytes, 0, m_size.width() * m_size.height() * sizeof(PixelData));
172    m_hasAlpha = true;
173}
174
175#if !USE(CG)
176
177void ImageFrame::copyReferenceToBitmapData(const ImageFrame& other)
178{
179    ASSERT(this != &other);
180    copyBitmapData(other);
181}
182
183bool ImageFrame::copyBitmapData(const ImageFrame& other)
184{
185    if (this == &other)
186        return true;
187
188    m_backingStore = other.m_backingStore;
189    m_bytes = m_backingStore.data();
190    m_size = other.m_size;
191    setHasAlpha(other.m_hasAlpha);
192    return true;
193}
194
195bool ImageFrame::setSize(int newWidth, int newHeight)
196{
197    // NOTE: This has no way to check for allocation failure if the requested
198    // size was too big...
199    m_backingStore.resize(newWidth * newHeight);
200    m_bytes = m_backingStore.data();
201    m_size = IntSize(newWidth, newHeight);
202
203    zeroFillPixelData();
204
205    return true;
206}
207
208#endif
209
210bool ImageFrame::hasAlpha() const
211{
212    return m_hasAlpha;
213}
214
215void ImageFrame::setHasAlpha(bool alpha)
216{
217    m_hasAlpha = alpha;
218}
219
220void ImageFrame::setColorProfile(const ColorProfile& colorProfile)
221{
222    m_colorProfile = colorProfile;
223}
224
225void ImageFrame::setStatus(FrameStatus status)
226{
227    m_status = status;
228}
229
230int ImageFrame::width() const
231{
232    return m_size.width();
233}
234
235int ImageFrame::height() const
236{
237    return m_size.height();
238}
239
240#endif
241
242namespace {
243
244enum MatchType {
245    Exact,
246    UpperBound,
247    LowerBound
248};
249
250inline void fillScaledValues(Vector<int>& scaledValues, double scaleRate, int length)
251{
252    double inflateRate = 1. / scaleRate;
253    scaledValues.reserveCapacity(static_cast<int>(length * scaleRate + 0.5));
254    for (int scaledIndex = 0; ; ++scaledIndex) {
255        int index = static_cast<int>(scaledIndex * inflateRate + 0.5);
256        if (index >= length)
257            break;
258        scaledValues.append(index);
259    }
260}
261
262template <MatchType type> int getScaledValue(const Vector<int>& scaledValues, int valueToMatch, int searchStart)
263{
264    if (scaledValues.isEmpty())
265        return valueToMatch;
266
267    const int* dataStart = scaledValues.data();
268    const int* dataEnd = dataStart + scaledValues.size();
269    const int* matched = std::lower_bound(dataStart + searchStart, dataEnd, valueToMatch);
270    switch (type) {
271    case Exact:
272        return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : -1;
273    case LowerBound:
274        return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : matched - dataStart - 1;
275    case UpperBound:
276    default:
277        return matched != dataEnd ? matched - dataStart : -1;
278    }
279}
280
281}
282
283void ImageDecoder::prepareScaleDataIfNecessary()
284{
285    m_scaled = false;
286    m_scaledColumns.clear();
287    m_scaledRows.clear();
288
289    int width = size().width();
290    int height = size().height();
291    int numPixels = height * width;
292    if (m_maxNumPixels <= 0 || numPixels <= m_maxNumPixels)
293        return;
294
295    m_scaled = true;
296    double scale = sqrt(m_maxNumPixels / (double)numPixels);
297    fillScaledValues(m_scaledColumns, scale, width);
298    fillScaledValues(m_scaledRows, scale, height);
299}
300
301int ImageDecoder::upperBoundScaledX(int origX, int searchStart)
302{
303    return getScaledValue<UpperBound>(m_scaledColumns, origX, searchStart);
304}
305
306int ImageDecoder::lowerBoundScaledX(int origX, int searchStart)
307{
308    return getScaledValue<LowerBound>(m_scaledColumns, origX, searchStart);
309}
310
311int ImageDecoder::upperBoundScaledY(int origY, int searchStart)
312{
313    return getScaledValue<UpperBound>(m_scaledRows, origY, searchStart);
314}
315
316int ImageDecoder::lowerBoundScaledY(int origY, int searchStart)
317{
318    return getScaledValue<LowerBound>(m_scaledRows, origY, searchStart);
319}
320
321int ImageDecoder::scaledY(int origY, int searchStart)
322{
323    return getScaledValue<Exact>(m_scaledRows, origY, searchStart);
324}
325
326}
327