1 // GnashImageGif.cpp: gif_lib wrapper for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program 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
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20
21 #include "GnashImageGif.h"
22
23 #include <sstream>
24 #include <algorithm>
25
26 extern "C" {
27 #include <gif_lib.h>
28 }
29
30 #include "GnashImage.h"
31 #include "utility.h"
32 #include "log.h"
33 #include "GnashException.h"
34 #include "IOChannel.h"
35
36 namespace gnash {
37 namespace image {
38
39 namespace {
40
41 int
readData(GifFileType * ft,GifByteType * data,int length)42 readData(GifFileType* ft, GifByteType* data, int length)
43 {
44 // Do not read until opened.
45 assert(ft);
46 IOChannel* in = reinterpret_cast<IOChannel*>(ft->UserData);
47 return in->read(reinterpret_cast<char*>(data), length);
48 }
49
50 class GifInput : public Input
51 {
52
53 public:
54
55 /// Construct a GifInput object to read from an IOChannel.
56 //
57 /// @param in The stream to read GIF data from. Ownership is shared
58 /// between caller and GifInput, so it is freed
59 /// automatically when the last owner is destroyed.
60 GifInput(std::shared_ptr<IOChannel> in);
61
62 ~GifInput();
63
64 /// Begin processing the image data.
65 void read();
66
67 /// Get the image's height in pixels.
68 //
69 /// @return The height of the image in pixels.
70 size_t getHeight() const;
71
72 /// Get the image's width in pixels.
73 //
74 /// @return The width of the image in pixels.
75 size_t getWidth() const;
76
77 /// Get number of components (channels)
78 //
79 /// @return The number of components, e.g. 3 for RGB
getComponents() const80 size_t getComponents() const { return 3; }
81
82 /// Read a scanline's worth of image data into the given buffer.
83 //
84 /// The amount of data read is getWidth() * getComponents().
85 ///
86 /// @param rgbData The buffer for writing raw RGB data to.
87 void readScanline(unsigned char* rgb_data);
88
89 private:
90
91 /// Initialize gif_lib
92 void init();
93
94 /// Process a single image record
95 //
96 /// @return false if no image was parsed, true if we have an image.
97 bool processRecord(GifRecordType record);
98
99 // State needed for input.
100 GifFileType* _gif;
101
102 // A counter for keeping track of the last row copied.
103 size_t _currentRow;
104
105 typedef std::unique_ptr<GifPixelType[]> PixelRow;
106
107 // A 2-dimensional scoped array holding the unpacked pixel data.
108 std::unique_ptr<PixelRow[]> _gifData;
109 };
110
111
GifInput(std::shared_ptr<IOChannel> in)112 GifInput::GifInput(std::shared_ptr<IOChannel> in)
113 :
114 Input(in),
115 _gif(nullptr),
116 _currentRow(0)
117 {
118 }
119
~GifInput()120 GifInput::~GifInput()
121 {
122 // Clean up allocated data.
123 #if GIFLIB_MAJOR==5 && GIFLIB_MINOR>=1
124 DGifCloseFile(_gif, 0);
125 #else
126 DGifCloseFile(_gif);
127 #endif
128 }
129
130 size_t
getHeight() const131 GifInput::getHeight() const
132 {
133 assert (_gif);
134 return _gif->SHeight;
135 }
136
137 size_t
getWidth() const138 GifInput::getWidth() const
139 {
140 assert (_gif);
141 return _gif->SWidth;
142 }
143
144 void
readScanline(unsigned char * rgbData)145 GifInput::readScanline(unsigned char* rgbData)
146 {
147
148 const ColorMapObject* const colormap = (_gif->Image.ColorMap) ?
149 _gif->Image.ColorMap : _gif->SColorMap;
150
151 assert(colormap);
152
153 unsigned char* ptr = rgbData;
154
155 for (size_t i = 0, e = getWidth(); i < e; ++i) {
156
157 const GifColorType* const mapentry =
158 &colormap->Colors[_gifData[_currentRow][i]];
159
160 *ptr++ = mapentry->Red;
161 *ptr++ = mapentry->Green;
162 *ptr++ = mapentry->Blue;
163 }
164
165 _currentRow++;
166
167 }
168
169 bool
processRecord(GifRecordType record)170 GifInput::processRecord(GifRecordType record)
171 {
172 switch (record) {
173
174 case IMAGE_DESC_RECORD_TYPE:
175 {
176 // Fill the _gif->Image fields
177 if (DGifGetImageDesc(_gif) != GIF_OK) {
178 throw ParserException(_("GIF: Error retrieving image "
179 "description"));
180 }
181 const int backgroundColor = _gif->SBackGroundColor;
182
183 // Set the height dimension of the array
184 _gifData.reset(new PixelRow[getHeight()]);
185
186 // The GIF 'screen' width and height
187 const size_t screenWidth = getWidth();
188 const size_t screenHeight = getHeight();
189
190 // Set all the pixels to the background colour.
191 for (size_t i = 0; i < screenHeight; ++i) {
192 // Set the width dimension of the array
193 _gifData[i].reset(new GifPixelType[screenWidth]);
194 // Fill all the pixels with the background color.
195 std::fill_n(_gifData[i].get(), screenWidth,
196 backgroundColor);
197 }
198
199 // The position of the image on the GIF 'screen'
200 const size_t imageHeight = _gif->Image.Height;
201 const size_t imageWidth = _gif->Image.Width;
202 const size_t imageTop = _gif->Image.Top;
203 const size_t imageLeft = _gif->Image.Left;
204
205 if (imageHeight + imageTop > screenHeight ||
206 imageWidth + imageLeft > screenWidth) {
207 throw ParserException(_("GIF: invalid image data "
208 "(bounds outside GIF screen)"));
209 }
210
211 // Handle interlaced data in four passes.
212 if (_gif->Image.Interlace) {
213 log_debug("Found interlaced GIF (%d x %d)",
214 screenWidth, screenHeight);
215
216 // The order of interlaced GIFs.
217 const int interlacedOffsets[] = { 0, 4, 2, 1 };
218 const int interlacedJumps[] = { 8, 8, 4, 2 };
219
220 for (size_t i = 0; i < 4; ++i) {
221
222 for (size_t j = imageTop + interlacedOffsets[i];
223 j < imageTop + imageHeight;
224 j += interlacedJumps[i]) {
225
226 if (DGifGetLine(_gif, &_gifData[j][imageLeft],
227 imageWidth) != GIF_OK) {
228
229 throw ParserException(_("GIF: failed reading "
230 "pixel data"));
231
232 }
233 }
234 }
235 // One record is enough.
236 return true;
237 }
238
239 // Non-interlaced data.
240 log_debug("Found non-interlaced GIF (%d x %d)",
241 screenWidth, screenHeight);
242
243 for (size_t i = imageTop; i < imageHeight; ++i) {
244 // Read the gif data into the gif array.
245 if (DGifGetLine(_gif, &_gifData[i][imageLeft], imageWidth)
246 != GIF_OK) {
247 throw ParserException(_("GIF: failed reading "
248 "pixel data"));
249 }
250 }
251 // One record is enough.
252 return true;
253 }
254
255 case EXTENSION_RECORD_TYPE:
256 // Skip all extension records.
257 GifByteType* extension;
258 int extCode;
259 DGifGetExtension(_gif, &extCode, &extension);
260 while (extension) {
261 if (DGifGetExtensionNext(_gif, &extension) == GIF_ERROR) {
262 break;
263 }
264 }
265 break;
266 default:
267 break;
268 }
269 return false;
270 }
271
272 void
read()273 GifInput::read()
274 {
275 #if GIFLIB_MAJOR >= 5
276 int errorCode;
277 _gif = DGifOpen(_inStream.get(), &readData, &errorCode);
278 #else
279 _gif = DGifOpen(_inStream.get(), &readData);
280 #endif
281
282 if ( ! _gif ) {
283 // TODO: decode errorCode if available
284 throw ParserException("Could not open input GIF stream");
285 }
286
287 GifRecordType record;
288
289 // Parse the (first?) image into memory.
290 // Is there a multi-dimensional smart array? It's silly to
291 // have to allocate each row separately and can mean a lot
292 // of reallocation.
293 do {
294
295 if (DGifGetRecordType(_gif, &record) != GIF_OK) {
296 throw ParserException(_("GIF: Error retrieving record type"));
297 }
298 if (processRecord(record)) break;
299
300 } while (record != TERMINATE_RECORD_TYPE);
301
302 // Set the type to RGB
303 // TODO: implement RGBA!
304 _type = TYPE_RGB;
305
306 }
307
308 } // unnamed namespace
309
310 std::unique_ptr<Input>
createGifInput(std::shared_ptr<IOChannel> in)311 createGifInput(std::shared_ptr<IOChannel> in)
312 {
313 std::unique_ptr<Input> ret(new GifInput(in));
314 ret->read();
315 return ret;
316 }
317
318 } // namespace image
319 } // namespace gnash
320
321 // Local Variables:
322 // mode: C++
323 // c-basic-offset: 8
324 // tab-width: 8
325 // indent-tabs-mode: t
326 // End:
327