summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/image-decoders/ImageDecoder.h
blob: 13069de1c4d2ffe60dd02461c339f6416c98d0ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/*
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2008-2009 Torch Mobile, Inc.
 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
 * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#ifndef ImageDecoder_h
#define ImageDecoder_h

#include "IntRect.h"
#include "ImageSource.h"
#include "PlatformString.h"
#include "SharedBuffer.h"
#include <wtf/Assertions.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>

#if PLATFORM(SKIA)
#include "NativeImageSkia.h"
#include "SkColorPriv.h"
#elif PLATFORM(QT)
#include <QPixmap>
#include <QImage>
#endif

namespace WebCore {

    // FIXME: Do we want better encapsulation?
    typedef Vector<char> ColorProfile;

    // The ImageFrame object represents the decoded image data in RGBA32
    // format.  This buffer is what all decoders write a single frame into.
    // Frames are then instantiated for drawing by being handed this buffer.
    class ImageFrame {
    public:
        enum FrameStatus { FrameEmpty, FramePartial, FrameComplete };
        enum FrameDisposalMethod {
            // If you change the numeric values of these, make sure you audit
            // all users, as some users may cast raw values to/from these
            // constants.
            DisposeNotSpecified,      // Leave frame in framebuffer
            DisposeKeep,              // Leave frame in framebuffer
            DisposeOverwriteBgcolor,  // Clear frame to transparent
            DisposeOverwritePrevious, // Clear frame to previous framebuffer
                                      // contents
        };
#if PLATFORM(SKIA) || PLATFORM(QT)
        typedef uint32_t PixelData;
#else
        typedef unsigned PixelData;
#endif

        ImageFrame();

        ImageFrame(const ImageFrame& other) { operator=(other); }

        // For backends which refcount their data, this operator doesn't need to
        // create a new copy of the image data, only increase the ref count.
        ImageFrame& operator=(const ImageFrame& other);

        // Deletes the pixel data entirely; used by ImageDecoder to save memory
        // when we no longer need to display a frame and only need its metadata.
        void clear();

        // Zeroes the pixel data in the buffer, setting it to fully-transparent.
        void zeroFill();

        // Creates a new copy of the image data in |other|, so the two images
        // can be modified independently.  Returns whether the copy succeeded.
        bool copyBitmapData(const ImageFrame&);

        // Creates a new reference to the image data in |other|.  The two images
        // share a common backing store.
        void copyReferenceToBitmapData(const ImageFrame&);

        // Copies the pixel data at [(startX, startY), (endX, startY)) to the
        // same X-coordinates on each subsequent row up to but not including
        // endY.
        void copyRowNTimes(int startX, int endX, int startY, int endY)
        {
            ASSERT(startX < width());
            ASSERT(endX <= width());
            ASSERT(startY < height());
            ASSERT(endY <= height());
            const int rowBytes = (endX - startX) * sizeof(PixelData);
            const PixelData* const startAddr = getAddr(startX, startY);
            for (int destY = startY + 1; destY < endY; ++destY)
                memcpy(getAddr(startX, destY), startAddr, rowBytes);
        }

#if PLATFORM(ANDROID)
        NativeImageSkia& bitmap() { return m_bitmap; }
        const NativeImageSkia& bitmap() const { return m_bitmap; }
#endif

        // Allocates space for the pixel data.  Must be called before any pixels
        // are written. Will return true on success, false if the memory
        // allocation fails.  Calling this multiple times is undefined and may
        // leak memory.
        bool setSize(int newWidth, int newHeight);

        // To be used by ImageSource::createFrameAtIndex().  Returns a pointer
        // to the underlying native image data.  This pointer will be owned by
        // the BitmapImage and freed in FrameData::clear().
        NativeImagePtr asNewNativeImage() const;

        bool hasAlpha() const;
        const IntRect& rect() const { return m_rect; }
        FrameStatus status() const { return m_status; }
        unsigned duration() const { return m_duration; }
        FrameDisposalMethod disposalMethod() const { return m_disposalMethod; }
        bool premultiplyAlpha() const { return m_premultiplyAlpha; }

        void setHasAlpha(bool alpha);
        void setColorProfile(const ColorProfile&);
        void setRect(const IntRect& r) { m_rect = r; }
        void setStatus(FrameStatus status);
        void setDuration(unsigned duration) { m_duration = duration; }
        void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; }
        void setPremultiplyAlpha(bool premultiplyAlpha) { m_premultiplyAlpha = premultiplyAlpha; }

        inline void setRGBA(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a)
        {
            setRGBA(getAddr(x, y), r, g, b, a);
        }

#if PLATFORM(QT)
        void setPixmap(const QPixmap& pixmap);
#endif

    private:
#if PLATFORM(CG)
        typedef RetainPtr<CFMutableDataRef> NativeBackingStore;
#else
        typedef Vector<PixelData> NativeBackingStore;
#endif

        int width() const;
        int height() const;

        inline PixelData* getAddr(int x, int y)
        {
#if PLATFORM(SKIA)
            return m_bitmap.getAddr32(x, y);
#elif PLATFORM(QT)
            m_image = m_pixmap.toImage();
            m_pixmap = QPixmap();
            return reinterpret_cast_ptr<QRgb*>(m_image.scanLine(y)) + x;
#else
            return m_bytes + (y * width()) + x;
#endif
        }

        inline void setRGBA(PixelData* dest, unsigned r, unsigned g, unsigned b, unsigned a)
        {
            if (m_premultiplyAlpha && !a)
                *dest = 0;
            else {
                if (m_premultiplyAlpha && a < 255) {
                    float alphaPercent = a / 255.0f;
                    r = static_cast<unsigned>(r * alphaPercent);
                    g = static_cast<unsigned>(g * alphaPercent);
                    b = static_cast<unsigned>(b * alphaPercent);
                }
#if PLATFORM(ANDROID)
                *dest = SkPackARGB32(a, r, g, b);
#else
                *dest = (a << 24 | r << 16 | g << 8 | b);
#endif
            }
        }

#if PLATFORM(SKIA)
        NativeImageSkia m_bitmap;
#elif PLATFORM(QT)
        mutable QPixmap m_pixmap;
        mutable QImage m_image;
        bool m_hasAlpha;
        IntSize m_size;
#else
        NativeBackingStore m_backingStore;
        PixelData* m_bytes; // The memory is backed by m_backingStore.
        IntSize m_size; // The size of the buffer.  This should be the
                        // same as ImageDecoder::m_size.
        bool m_hasAlpha; // Whether or not any of the pixels in the buffer
                         // have transparency.
        ColorProfile m_colorProfile;
#endif
        IntRect m_rect; // The rect of the original specified frame within
                        // the overall buffer.  This will always just be
                        // the entire buffer except for GIF frames whose
                        // original rect was smaller than the overall
                        // image size.
        FrameStatus m_status; // Whether or not this frame is completely
                              // finished decoding.
        unsigned m_duration; // The animation delay.
        FrameDisposalMethod m_disposalMethod; // What to do with this frame's data when
                                              // initializing the next frame.
        bool m_premultiplyAlpha; // Whether to premultiply alpha into R, G, B
                                 // channels; by default it's true.
    };

    // The ImageDecoder class represents a base class for specific image format
    // decoders (e.g., GIF, JPG, PNG, ICO) to derive from.  All decoders decode
    // into RGBA32 format and the base class manages the RGBA32 frame cache.
    //
    // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write
    // directly to scaled output buffers by down sampling. Call
    // setMaxNumPixels() to specify the biggest size that decoded images can
    // have. Image decoders will deflate those images that are bigger than
    // m_maxNumPixels. (Not supported by all image decoders yet)
    class ImageDecoder : public Noncopyable {
    public:
        ImageDecoder(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
            : m_scaled(false)
            , m_premultiplyAlpha(alphaOption == ImageSource::AlphaPremultiplied)
            , m_ignoreGammaAndColorProfile(gammaAndColorProfileOption == ImageSource::GammaAndColorProfileIgnored)
            , m_sizeAvailable(false)
            , m_maxNumPixels(-1)
            , m_isAllDataReceived(false)
            , m_failed(false)
        {
        }

        virtual ~ImageDecoder() {}

        // Factory function to create an ImageDecoder.  Ports that subclass
        // ImageDecoder can provide their own implementation of this to avoid
        // needing to write a dedicated setData() implementation.
        static ImageDecoder* create(const SharedBuffer& data, ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);

        // The the filename extension usually associated with an undecoded image
        // of this type.
        virtual String filenameExtension() const = 0;

        bool isAllDataReceived() const { return m_isAllDataReceived; }

        virtual void setData(SharedBuffer* data, bool allDataReceived)
        {
            if (m_failed)
                return;
            m_data = data;
            m_isAllDataReceived = allDataReceived;
        }

        // Whether or not the size information has been decoded yet. This
        // default implementation just returns true if the size has been set and
        // we have not seen a failure. Decoders may want to override this to
        // lazily decode enough of the image to get the size.
        virtual bool isSizeAvailable()
        {
            return !m_failed && m_sizeAvailable; 
        }

        // Returns the size of the image.
        virtual IntSize size() const
        {
            return m_size;
        }

        IntSize scaledSize() const
        {
            return m_scaled ? IntSize(m_scaledColumns.size(), m_scaledRows.size()) : size();
        }

        // Returns the size of frame |index|.  This will only differ from size()
        // for formats where different frames are different sizes (namely ICO,
        // where each frame represents a different icon within the master file).
        // Notably, this does not return different sizes for different GIF
        // frames, since while these may be stored as smaller rectangles, during
        // decoding they are composited to create a full-size frame.
        virtual IntSize frameSizeAtIndex(size_t) const
        {
            return size();
        }

        // Called by the image decoders to set their decoded size, this also
        // checks the size for validity. It will return true if the size was
        // set, or false if there is an error. On error, the m_failed flag will
        // be set and the caller should immediately stop decoding.
        virtual bool setSize(unsigned width, unsigned height)
        {
            if (isOverSize(width, height))
                return setFailed();
            m_size = IntSize(width, height);
            m_sizeAvailable = true;
            return true;
        }

        // The total number of frames for the image.  Classes that support
        // multiple frames will scan the image data for the answer if they need
        // to (without necessarily decoding all of the individual frames).
        virtual size_t frameCount() { return 1; }

        // The number of repetitions to perform for an animation loop.
        virtual int repetitionCount() const { return cAnimationNone; }

        // Called to obtain the ImageFrame full of decoded data for rendering.
        // The decoder plugin will decode as much of the frame as it can before
        // handing back the buffer.
        virtual ImageFrame* frameBufferAtIndex(size_t) = 0;

        // Whether or not the underlying image format even supports alpha
        // transparency.
        virtual bool supportsAlpha() const { return true; }

        void setIgnoreGammaAndColorProfile(bool flag) { m_ignoreGammaAndColorProfile = flag; }

        // Whether or not the gamma and color profile are applied.
        bool ignoresGammaAndColorProfile() const { return m_ignoreGammaAndColorProfile; }

        // Sets the "decode failure" flag.  For caller convenience (since so
        // many callers want to return false after calling this), returns false
        // to enable easy tailcalling.  Subclasses may override this to also
        // clean up any local data.
        virtual bool setFailed()
        {
            m_failed = true;
            return false;
        }

        bool failed() const { return m_failed; }

        // Wipe out frames in the frame buffer cache before |clearBeforeFrame|,
        // assuming this can be done without breaking decoding.  Different
        // decoders place different restrictions on what frames are safe to
        // destroy, so this is left to them to implement.
        // For convenience's sake, we provide a default (empty) implementation,
        // since in practice only GIFs will ever use this.
        virtual void clearFrameBufferCache(size_t clearBeforeFrame) { }

#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
        void setMaxNumPixels(int m) { m_maxNumPixels = m; }
#endif

    protected:
        void prepareScaleDataIfNecessary();
        int upperBoundScaledX(int origX, int searchStart = 0);
        int lowerBoundScaledX(int origX, int searchStart = 0);
        int upperBoundScaledY(int origY, int searchStart = 0);
        int lowerBoundScaledY(int origY, int searchStart = 0);
        int scaledY(int origY, int searchStart = 0);

        RefPtr<SharedBuffer> m_data; // The encoded data.
        Vector<ImageFrame> m_frameBufferCache;
        ColorProfile m_colorProfile;
        bool m_scaled;
        Vector<int> m_scaledColumns;
        Vector<int> m_scaledRows;
        bool m_premultiplyAlpha;
        bool m_ignoreGammaAndColorProfile;

    private:
        // Some code paths compute the size of the image as "width * height * 4"
        // and return it as a (signed) int.  Avoid overflow.
        static bool isOverSize(unsigned width, unsigned height)
        {
            // width * height must not exceed (2 ^ 29) - 1, so that we don't
            // overflow when we multiply by 4.
            unsigned long long total_size = static_cast<unsigned long long>(width)
                                          * static_cast<unsigned long long>(height);
            return total_size > ((1 << 29) - 1);
        }

        IntSize m_size;
        bool m_sizeAvailable;
        int m_maxNumPixels;
        bool m_isAllDataReceived;
        bool m_failed;
    };

} // namespace WebCore

#endif