1/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38#ifndef GIFImageReader_h
39#define GIFImageReader_h
40
41// Define ourselves as the clientPtr.  Mozilla just hacked their C++ callback class into this old C decoder,
42// so we will too.
43#include "GIFImageDecoder.h"
44#include "SharedBuffer.h"
45#include <wtf/OwnPtr.h>
46#include <wtf/PassOwnPtr.h>
47#include <wtf/Vector.h>
48
49#define MAX_LZW_BITS          12
50#define MAX_BYTES           4097 /* 2^MAX_LZW_BITS+1 */
51#define MAX_COLORS           256
52#define GIF_COLORS             3
53
54const int cLoopCountNotSeen = -2;
55
56// List of possible parsing states.
57enum GIFState {
58    GIFType,
59    GIFGlobalHeader,
60    GIFGlobalColormap,
61    GIFImageStart,
62    GIFImageHeader,
63    GIFImageColormap,
64    GIFImageBody,
65    GIFLZWStart,
66    GIFLZW,
67    GIFSubBlock,
68    GIFExtension,
69    GIFControlExtension,
70    GIFConsumeBlock,
71    GIFSkipBlock,
72    GIFDone,
73    GIFCommentExtension,
74    GIFApplicationExtension,
75    GIFNetscapeExtensionBlock,
76    GIFConsumeNetscapeExtension,
77    GIFConsumeComment
78};
79
80struct GIFFrameContext;
81
82// LZW decoder state machine.
83class GIFLZWContext {
84    WTF_MAKE_FAST_ALLOCATED;
85public:
86    GIFLZWContext(WebCore::GIFImageDecoder* client, const GIFFrameContext* frameContext)
87        : stackp(0)
88        , codesize(0)
89        , codemask(0)
90        , clearCode(0)
91        , avail(0)
92        , oldcode(0)
93        , firstchar(0)
94        , bits(0)
95        , datum(0)
96        , ipass(0)
97        , irow(0)
98        , rowPosition(0)
99        , rowsRemaining(0)
100        , m_client(client)
101        , m_frameContext(frameContext)
102    { }
103
104    bool prepareToDecode();
105    bool outputRow();
106    bool doLZW(const unsigned char* block, size_t bytesInBlock);
107    bool hasRemainingRows() { return rowsRemaining; }
108
109private:
110    // LZW decoding states and output states.
111    size_t stackp; // Current stack pointer.
112    int codesize;
113    int codemask;
114    int clearCode; // Codeword used to trigger dictionary reset.
115    int avail; // Index of next available slot in dictionary.
116    int oldcode;
117    unsigned char firstchar;
118    int bits; // Number of unread bits in "datum".
119    int datum; // 32-bit input buffer.
120    int ipass; // Interlace pass; Ranges 1-4 if interlaced.
121    size_t irow; // Current output row, starting at zero.
122    size_t rowPosition;
123    size_t rowsRemaining; // Rows remaining to be output.
124
125    Vector<unsigned short> prefix;
126    Vector<unsigned char> suffix;
127    Vector<unsigned char> stack;
128    Vector<unsigned char> rowBuffer; // Single scanline temporary buffer.
129
130    // Initialized during construction and read-only.
131    WebCore::GIFImageDecoder* m_client;
132    const GIFFrameContext* m_frameContext;
133};
134
135// Data structure for one LZW block.
136struct GIFLZWBlock {
137    WTF_MAKE_FAST_ALLOCATED;
138public:
139    GIFLZWBlock(size_t position, size_t size)
140        : blockPosition(position)
141        , blockSize(size)
142    {
143    }
144
145    size_t blockPosition;
146    size_t blockSize;
147};
148
149// Frame output state machine.
150struct GIFFrameContext {
151    WTF_MAKE_FAST_ALLOCATED;
152public:
153    // FIXME: Move these members to private section.
154    int frameId;
155    unsigned xOffset;
156    unsigned yOffset; // With respect to "screen" origin.
157    unsigned width;
158    unsigned height;
159    int tpixel; // Index of transparent pixel.
160    WebCore::ImageFrame::FrameDisposalMethod disposalMethod; // Restore to background, leave in place, etc.
161    size_t localColormapPosition; // Per-image colormap.
162    int localColormapSize; // Size of local colormap array.
163    int datasize;
164
165    bool isLocalColormapDefined : 1;
166    bool progressiveDisplay : 1; // If true, do Haeberli interlace hack.
167    bool interlaced : 1; // True, if scanlines arrive interlaced order.
168    bool isTransparent : 1; // TRUE, if tpixel is valid.
169
170    unsigned delayTime; // Display time, in milliseconds, for this image in a multi-image GIF.
171
172    GIFFrameContext(int id)
173        : frameId(id)
174        , xOffset(0)
175        , yOffset(0)
176        , width(0)
177        , height(0)
178        , tpixel(0)
179        , disposalMethod(WebCore::ImageFrame::DisposeNotSpecified)
180        , localColormapPosition(0)
181        , localColormapSize(0)
182        , datasize(0)
183        , isLocalColormapDefined(false)
184        , progressiveDisplay(false)
185        , interlaced(false)
186        , isTransparent(false)
187        , delayTime(0)
188        , m_currentLzwBlock(0)
189        , m_isComplete(false)
190        , m_isHeaderDefined(false)
191        , m_isDataSizeDefined(false)
192    {
193    }
194
195    ~GIFFrameContext()
196    {
197    }
198
199    void addLzwBlock(size_t position, size_t size)
200    {
201        m_lzwBlocks.append(GIFLZWBlock(position, size));
202    }
203
204    bool decode(const unsigned char* data, size_t length, WebCore::GIFImageDecoder* client, bool* frameDecoded);
205
206    bool isComplete() const { return m_isComplete; }
207    void setComplete() { m_isComplete = true; }
208    bool isHeaderDefined() const { return m_isHeaderDefined; }
209    void setHeaderDefined() { m_isHeaderDefined = true; }
210    bool isDataSizeDefined() const { return m_isDataSizeDefined; }
211    void setDataSize(int size)
212    {
213        datasize = size;
214        m_isDataSizeDefined = true;
215    }
216
217private:
218    OwnPtr<GIFLZWContext> m_lzwContext;
219    Vector<GIFLZWBlock> m_lzwBlocks; // LZW blocks for this frame.
220    size_t m_currentLzwBlock;
221    bool m_isComplete;
222    bool m_isHeaderDefined;
223    bool m_isDataSizeDefined;
224};
225
226class GIFImageReader {
227    WTF_MAKE_FAST_ALLOCATED;
228public:
229    GIFImageReader(WebCore::GIFImageDecoder* client = 0)
230        : m_client(client)
231        , m_state(GIFType)
232        , m_bytesToConsume(6) // Number of bytes for GIF type, either "GIF87a" or "GIF89a".
233        , m_bytesRead(0)
234        , m_screenBgcolor(0)
235        , m_version(0)
236        , m_screenWidth(0)
237        , m_screenHeight(0)
238        , m_isGlobalColormapDefined(false)
239        , m_globalColormapPosition(0)
240        , m_globalColormapSize(0)
241        , m_loopCount(cLoopCountNotSeen)
242        , m_currentDecodingFrame(0)
243        , m_parseCompleted(false)
244    {
245    }
246
247    ~GIFImageReader()
248    {
249    }
250
251    void setData(PassRefPtr<WebCore::SharedBuffer> data) { m_data = data; }
252    // FIXME: haltAtFrame should be size_t.
253    bool decode(WebCore::GIFImageDecoder::GIFQuery, unsigned haltAtFrame);
254
255    size_t imagesCount() const
256    {
257        if (m_frames.isEmpty())
258            return 0;
259
260        // This avoids counting an empty frame when the file is truncated right after
261        // GIFControlExtension but before GIFImageHeader.
262        // FIXME: This extra complexity is not necessary and we should just report m_frames.size().
263        return m_frames.last()->isHeaderDefined() ? m_frames.size() : m_frames.size() - 1;
264    }
265    int loopCount() const { return m_loopCount; }
266
267    const unsigned char* globalColormap() const
268    {
269        return m_isGlobalColormapDefined ? data(m_globalColormapPosition) : 0;
270    }
271    int globalColormapSize() const
272    {
273        return m_isGlobalColormapDefined ? m_globalColormapSize : 0;
274    }
275
276    const unsigned char* localColormap(const GIFFrameContext* frame) const
277    {
278        return frame->isLocalColormapDefined ? data(frame->localColormapPosition) : 0;
279    }
280    int localColormapSize(const GIFFrameContext* frame) const
281    {
282        return frame->isLocalColormapDefined ? frame->localColormapSize : 0;
283    }
284
285    const GIFFrameContext* frameContext() const
286    {
287        return m_currentDecodingFrame < m_frames.size() ? m_frames[m_currentDecodingFrame].get() : 0;
288    }
289
290private:
291    bool parse(size_t dataPosition, size_t len, bool parseSizeOnly);
292    void setRemainingBytes(size_t);
293
294    const unsigned char* data(size_t dataPosition) const
295    {
296        return reinterpret_cast<const unsigned char*>(m_data->data()) + dataPosition;
297    }
298
299    void addFrameIfNecessary();
300    bool currentFrameIsFirstFrame() const
301    {
302        return m_frames.isEmpty() || (m_frames.size() == 1u && !m_frames[0]->isComplete());
303    }
304
305    WebCore::GIFImageDecoder* m_client;
306
307    // Parsing state machine.
308    GIFState m_state; // Current decoder master state.
309    size_t m_bytesToConsume; // Number of bytes to consume for next stage of parsing.
310    size_t m_bytesRead; // Number of bytes processed.
311
312    // Global (multi-image) state.
313    int m_screenBgcolor; // Logical screen background color.
314    int m_version; // Either 89 for GIF89 or 87 for GIF87.
315    unsigned m_screenWidth; // Logical screen width & height.
316    unsigned m_screenHeight;
317    bool m_isGlobalColormapDefined;
318    size_t m_globalColormapPosition; // (3* MAX_COLORS in size) Default colormap if local not supplied, 3 bytes for each color.
319    int m_globalColormapSize; // Size of global colormap array.
320    int m_loopCount; // Netscape specific extension block to control the number of animation loops a GIF renders.
321
322    Vector<OwnPtr<GIFFrameContext> > m_frames;
323    size_t m_currentDecodingFrame;
324
325    RefPtr<WebCore::SharedBuffer> m_data;
326    bool m_parseCompleted;
327};
328
329#endif
330