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