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#pragma once
39
40// Define ourselves as the clientPtr. Mozilla just hacked their C++ callback class into this old C decoder,
41// so we will too.
42#include "GIFImageDecoder.h"
43#include "SharedBuffer.h"
44#include <wtf/Vector.h>
45
46#define MAX_LZW_BITS 12
47#define MAX_BYTES 4097 /* 2^MAX_LZW_BITS+1 */
48#define MAX_COLORS 256
49#define GIF_COLORS 3
50
51const int cLoopCountNotSeen = -2;
52
53// List of possible parsing states.
54enum GIFState {
55 GIFType,
56 GIFGlobalHeader,
57 GIFGlobalColormap,
58 GIFImageStart,
59 GIFImageHeader,
60 GIFImageColormap,
61 GIFImageBody,
62 GIFLZWStart,
63 GIFLZW,
64 GIFSubBlock,
65 GIFExtension,
66 GIFControlExtension,
67 GIFConsumeBlock,
68 GIFSkipBlock,
69 GIFDone,
70 GIFCommentExtension,
71 GIFApplicationExtension,
72 GIFNetscapeExtensionBlock,
73 GIFConsumeNetscapeExtension,
74 GIFConsumeComment
75};
76
77struct GIFFrameContext;
78
79// LZW decoder state machine.
80class GIFLZWContext {
81 WTF_MAKE_FAST_ALLOCATED;
82public:
83 GIFLZWContext(WebCore::GIFImageDecoder* client, const GIFFrameContext* frameContext)
84 : stackp(0)
85 , codesize(0)
86 , codemask(0)
87 , clearCode(0)
88 , avail(0)
89 , oldcode(0)
90 , firstchar(0)
91 , bits(0)
92 , datum(0)
93 , ipass(0)
94 , irow(0)
95 , rowPosition(0)
96 , rowsRemaining(0)
97 , m_client(client)
98 , m_frameContext(frameContext)
99 { }
100
101 bool prepareToDecode();
102 bool outputRow();
103 bool doLZW(const unsigned char* block, size_t bytesInBlock);
104 bool hasRemainingRows() { return rowsRemaining; }
105
106private:
107 // LZW decoding states and output states.
108 size_t stackp; // Current stack pointer.
109 int codesize;
110 int codemask;
111 int clearCode; // Codeword used to trigger dictionary reset.
112 int avail; // Index of next available slot in dictionary.
113 int oldcode;
114 unsigned char firstchar;
115 int bits; // Number of unread bits in "datum".
116 int datum; // 32-bit input buffer.
117 int ipass; // Interlace pass; Ranges 1-4 if interlaced.
118 size_t irow; // Current output row, starting at zero.
119 size_t rowPosition;
120 size_t rowsRemaining; // Rows remaining to be output.
121
122 Vector<unsigned short> prefix;
123 Vector<unsigned char> suffix;
124 Vector<unsigned char> stack;
125 Vector<unsigned char> rowBuffer; // Single scanline temporary buffer.
126
127 // Initialized during construction and read-only.
128 WebCore::GIFImageDecoder* m_client;
129 const GIFFrameContext* m_frameContext;
130};
131
132// Data structure for one LZW block.
133struct GIFLZWBlock {
134 WTF_MAKE_FAST_ALLOCATED;
135public:
136 GIFLZWBlock(size_t position, size_t size)
137 : blockPosition(position)
138 , blockSize(size)
139 {
140 }
141
142 size_t blockPosition;
143 size_t blockSize;
144};
145
146// Frame output state machine.
147struct GIFFrameContext {
148 WTF_MAKE_FAST_ALLOCATED;
149public:
150 // FIXME: Move these members to private section.
151 int frameId;
152 unsigned xOffset;
153 unsigned yOffset; // With respect to "screen" origin.
154 unsigned width;
155 unsigned height;
156 int tpixel; // Index of transparent pixel.
157 WebCore::ScalableImageDecoderFrame::DisposalMethod disposalMethod; // Restore to background, leave in place, etc.
158 size_t localColormapPosition; // Per-image colormap.
159 int localColormapSize; // Size of local colormap array.
160 int datasize;
161
162 bool isLocalColormapDefined : 1;
163 bool progressiveDisplay : 1; // If true, do Haeberli interlace hack.
164 bool interlaced : 1; // True, if scanlines arrive interlaced order.
165 bool isTransparent : 1; // TRUE, if tpixel is valid.
166
167 unsigned delayTime; // Display time, in milliseconds, for this image in a multi-image GIF.
168
169 GIFFrameContext(int id)
170 : frameId(id)
171 , xOffset(0)
172 , yOffset(0)
173 , width(0)
174 , height(0)
175 , tpixel(0)
176 , disposalMethod(WebCore::ScalableImageDecoderFrame::DisposalMethod::Unspecified)
177 , localColormapPosition(0)
178 , localColormapSize(0)
179 , datasize(0)
180 , isLocalColormapDefined(false)
181 , progressiveDisplay(false)
182 , interlaced(false)
183 , isTransparent(false)
184 , delayTime(0)
185 , m_currentLzwBlock(0)
186 , m_isComplete(false)
187 , m_isHeaderDefined(false)
188 , m_isDataSizeDefined(false)
189 {
190 }
191
192 void addLzwBlock(size_t position, size_t size)
193 {
194 m_lzwBlocks.append(GIFLZWBlock(position, size));
195 }
196
197 bool decode(const unsigned char* data, size_t length, WebCore::GIFImageDecoder* client, bool* frameDecoded);
198
199 bool isComplete() const { return m_isComplete; }
200 void setComplete() { m_isComplete = true; }
201 bool isHeaderDefined() const { return m_isHeaderDefined; }
202 void setHeaderDefined() { m_isHeaderDefined = true; }
203 bool isDataSizeDefined() const { return m_isDataSizeDefined; }
204 void setDataSize(int size)
205 {
206 datasize = size;
207 m_isDataSizeDefined = true;
208 }
209
210private:
211 std::unique_ptr<GIFLZWContext> m_lzwContext;
212 Vector<GIFLZWBlock> m_lzwBlocks; // LZW blocks for this frame.
213 size_t m_currentLzwBlock;
214 bool m_isComplete;
215 bool m_isHeaderDefined;
216 bool m_isDataSizeDefined;
217};
218
219class GIFImageReader {
220 WTF_MAKE_FAST_ALLOCATED;
221public:
222 GIFImageReader(WebCore::GIFImageDecoder* client = 0)
223 : m_client(client)
224 , m_state(GIFType)
225 , m_bytesToConsume(6) // Number of bytes for GIF type, either "GIF87a" or "GIF89a".
226 , m_bytesRead(0)
227 , m_screenBgcolor(0)
228 , m_version(0)
229 , m_screenWidth(0)
230 , m_screenHeight(0)
231 , m_isGlobalColormapDefined(false)
232 , m_globalColormapPosition(0)
233 , m_globalColormapSize(0)
234 , m_loopCount(cLoopCountNotSeen)
235 , m_currentDecodingFrame(0)
236 , m_parseCompleted(false)
237 {
238 }
239
240 void setData(WebCore::SharedBuffer* data) { m_data = data; }
241 // FIXME: haltAtFrame should be size_t.
242 bool decode(WebCore::GIFImageDecoder::GIFQuery, unsigned haltAtFrame);
243
244 size_t imagesCount() const
245 {
246 if (m_frames.isEmpty())
247 return 0;
248
249 // This avoids counting an empty frame when the file is truncated right after
250 // GIFControlExtension but before GIFImageHeader.
251 // FIXME: This extra complexity is not necessary and we should just report m_frames.size().
252 return m_frames.last()->isHeaderDefined() ? m_frames.size() : m_frames.size() - 1;
253 }
254 int loopCount() const { return m_loopCount; }
255
256 const unsigned char* globalColormap() const
257 {
258 return m_isGlobalColormapDefined ? data(m_globalColormapPosition) : 0;
259 }
260 int globalColormapSize() const
261 {
262 return m_isGlobalColormapDefined ? m_globalColormapSize : 0;
263 }
264
265 const unsigned char* localColormap(const GIFFrameContext* frame) const
266 {
267 return frame->isLocalColormapDefined ? data(frame->localColormapPosition) : 0;
268 }
269 int localColormapSize(const GIFFrameContext* frame) const
270 {
271 return frame->isLocalColormapDefined ? frame->localColormapSize : 0;
272 }
273
274 const GIFFrameContext* frameContext() const
275 {
276 return m_currentDecodingFrame < m_frames.size() ? m_frames[m_currentDecodingFrame].get() : 0;
277 }
278
279 const GIFFrameContext* frameContext(size_t frame) const
280 {
281 return frame < m_frames.size() ? m_frames[frame].get() : nullptr;
282 }
283
284private:
285 bool parse(size_t dataPosition, size_t len, bool parseSizeOnly);
286 void setRemainingBytes(size_t);
287
288 const unsigned char* data(size_t dataPosition) const
289 {
290 return reinterpret_cast<const unsigned char*>(m_data->data()) + dataPosition;
291 }
292
293 void addFrameIfNecessary();
294 bool currentFrameIsFirstFrame() const
295 {
296 return m_frames.isEmpty() || (m_frames.size() == 1u && !m_frames[0]->isComplete());
297 }
298
299 WebCore::GIFImageDecoder* m_client;
300
301 // Parsing state machine.
302 GIFState m_state; // Current decoder master state.
303 size_t m_bytesToConsume; // Number of bytes to consume for next stage of parsing.
304 size_t m_bytesRead; // Number of bytes processed.
305
306 // Global (multi-image) state.
307 int m_screenBgcolor; // Logical screen background color.
308 int m_version; // Either 89 for GIF89 or 87 for GIF87.
309 unsigned m_screenWidth; // Logical screen width & height.
310 unsigned m_screenHeight;
311 bool m_isGlobalColormapDefined;
312 size_t m_globalColormapPosition; // (3* MAX_COLORS in size) Default colormap if local not supplied, 3 bytes for each color.
313 int m_globalColormapSize; // Size of global colormap array.
314 int m_loopCount; // Netscape specific extension block to control the number of animation loops a GIF renders.
315
316 Vector<std::unique_ptr<GIFFrameContext> > m_frames;
317 size_t m_currentDecodingFrame;
318
319 RefPtr<WebCore::SharedBuffer> m_data;
320 bool m_parseCompleted;
321};
322