update version to the release
[gnash.git] / libbase / GnashImageGif.cpp
blob8e8c913ced47ded37e287740c9c59cf23e4c2051
1 // GnashImageGif.cpp: gif_lib wrapper for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // 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
21 #include "GnashImageGif.h"
23 #include <sstream>
24 #include <algorithm>
25 #include <boost/scoped_array.hpp>
27 extern "C" {
28 #include <gif_lib.h>
31 #include "GnashImage.h"
32 #include "utility.h"
33 #include "log.h"
34 #include "GnashException.h"
35 #include "IOChannel.h"
37 namespace gnash {
38 namespace image {
40 namespace {
42 int
43 readData(GifFileType* ft, GifByteType* data, int length)
45 // Do not read until opened.
46 assert(ft);
47 IOChannel* in = reinterpret_cast<IOChannel*>(ft->UserData);
48 return in->read(reinterpret_cast<char*>(data), length);
51 class GifInput : public Input
54 public:
56 /// Construct a GifInput object to read from an IOChannel.
58 /// @param in The stream to read GIF data from. Ownership is shared
59 /// between caller and GifInput, so it is freed
60 /// automatically when the last owner is destroyed.
61 GifInput(boost::shared_ptr<IOChannel> in);
63 ~GifInput();
65 /// Begin processing the image data.
66 void read();
68 /// Get the image's height in pixels.
70 /// @return The height of the image in pixels.
71 size_t getHeight() const;
73 /// Get the image's width in pixels.
75 /// @return The width of the image in pixels.
76 size_t getWidth() const;
78 /// Get number of components (channels)
80 /// @return The number of components, e.g. 3 for RGB
81 size_t getComponents() const { return 3; }
83 /// Read a scanline's worth of image data into the given buffer.
85 /// The amount of data read is getWidth() * getComponents().
86 ///
87 /// @param rgbData The buffer for writing raw RGB data to.
88 void readScanline(unsigned char* rgb_data);
90 private:
92 /// Initialize gif_lib
93 void init();
95 /// Process a single image record
97 /// @return false if no image was parsed, true if we have an image.
98 bool processRecord(GifRecordType record);
100 // State needed for input.
101 GifFileType* _gif;
103 // A counter for keeping track of the last row copied.
104 size_t _currentRow;
106 typedef boost::scoped_array<GifPixelType> PixelRow;
108 // A 2-dimensional scoped array holding the unpacked pixel data.
109 boost::scoped_array<PixelRow> _gifData;
113 GifInput::GifInput(boost::shared_ptr<IOChannel> in)
115 Input(in),
116 _gif(0),
117 _currentRow(0)
121 GifInput::~GifInput()
123 // Clean up allocated data.
124 DGifCloseFile(_gif);
127 size_t
128 GifInput::getHeight() const
130 assert (_gif);
131 return _gif->SHeight;
134 size_t
135 GifInput::getWidth() const
137 assert (_gif);
138 return _gif->SWidth;
141 void
142 GifInput::readScanline(unsigned char* rgbData)
145 const ColorMapObject* const colormap = (_gif->Image.ColorMap) ?
146 _gif->Image.ColorMap : _gif->SColorMap;
148 assert(colormap);
150 unsigned char* ptr = rgbData;
152 for (size_t i = 0, e = getWidth(); i < e; ++i) {
154 const GifColorType* const mapentry =
155 &colormap->Colors[_gifData[_currentRow][i]];
157 *ptr++ = mapentry->Red;
158 *ptr++ = mapentry->Green;
159 *ptr++ = mapentry->Blue;
162 _currentRow++;
166 bool
167 GifInput::processRecord(GifRecordType record)
169 switch (record) {
171 case IMAGE_DESC_RECORD_TYPE:
173 // Fill the _gif->Image fields
174 if (DGifGetImageDesc(_gif) != GIF_OK) {
175 throw ParserException(_("GIF: Error retrieving image "
176 "description"));
178 const int backgroundColor = _gif->SBackGroundColor;
180 // Set the height dimension of the array
181 _gifData.reset(new PixelRow[getHeight()]);
183 // The GIF 'screen' width and height
184 const size_t screenWidth = getWidth();
185 const size_t screenHeight = getHeight();
187 // Set all the pixels to the background colour.
188 for (size_t i = 0; i < screenHeight; ++i) {
189 // Set the width dimension of the array
190 _gifData[i].reset(new GifPixelType[screenWidth]);
191 // Fill all the pixels with the background color.
192 std::fill_n(_gifData[i].get(), screenWidth,
193 backgroundColor);
196 // The position of the image on the GIF 'screen'
197 const size_t imageHeight = _gif->Image.Height;
198 const size_t imageWidth = _gif->Image.Width;
199 const size_t imageTop = _gif->Image.Top;
200 const size_t imageLeft = _gif->Image.Left;
202 if (imageHeight + imageTop > screenHeight ||
203 imageWidth + imageLeft > screenWidth) {
204 throw ParserException(_("GIF: invalid image data "
205 "(bounds outside GIF screen)"));
208 // Handle interlaced data in four passes.
209 if (_gif->Image.Interlace) {
210 log_debug(_("Found interlaced GIF (%d x %d)"),
211 screenWidth, screenHeight);
213 // The order of interlaced GIFs.
214 const int interlacedOffsets[] = { 0, 4, 2, 1 };
215 const int interlacedJumps[] = { 8, 8, 4, 2 };
217 for (size_t i = 0; i < 4; ++i) {
219 for (size_t j = imageTop + interlacedOffsets[i];
220 j < imageTop + imageHeight;
221 j += interlacedJumps[i]) {
223 if (DGifGetLine(_gif, &_gifData[j][imageLeft],
224 imageWidth) != GIF_OK) {
226 throw ParserException(_("GIF: failed reading "
227 "pixel data"));
232 // One record is enough.
233 return true;
236 // Non-interlaced data.
237 log_debug(_("Found non-interlaced GIF (%d x %d)"),
238 screenWidth, screenHeight);
240 for (size_t i = imageTop; i < imageHeight; ++i) {
241 // Read the gif data into the gif array.
242 if (DGifGetLine(_gif, &_gifData[i][imageLeft], imageWidth)
243 != GIF_OK) {
244 throw ParserException(_("GIF: failed reading "
245 "pixel data"));
248 // One record is enough.
249 return true;
252 case EXTENSION_RECORD_TYPE:
253 // Skip all extension records.
254 GifByteType* extension;
255 int extCode;
256 DGifGetExtension(_gif, &extCode, &extension);
257 while (extension) {
258 if (DGifGetExtensionNext(_gif, &extension) == GIF_ERROR) {
259 break;
262 break;
263 default:
264 break;
266 return false;
269 void
270 GifInput::read()
272 _gif = DGifOpen(_inStream.get(), &readData);
274 GifRecordType record;
276 // Parse the (first?) image into memory.
277 // Is there a multi-dimensional smart array? It's silly to
278 // have to allocate each row separately and can mean a lot
279 // of reallocation.
280 do {
282 if (DGifGetRecordType(_gif, &record) != GIF_OK) {
283 throw ParserException(_("GIF: Error retrieving record type"));
285 if (processRecord(record)) break;
287 } while (record != TERMINATE_RECORD_TYPE);
289 // Set the type to RGB
290 // TODO: implement RGBA!
291 _type = TYPE_RGB;
295 } // unnamed namespace
297 std::auto_ptr<Input>
298 createGifInput(boost::shared_ptr<IOChannel> in)
300 std::auto_ptr<Input> ret(new GifInput(in));
301 ret->read();
302 return ret;
305 } // namespace image
306 } // namespace gnash
308 // Local Variables:
309 // mode: C++
310 // c-basic-offset: 8
311 // tab-width: 8
312 // indent-tabs-mode: t
313 // End: