1 // GnashImageGif.cpp: gif_lib wrapper for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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"
25 #include <boost/scoped_array.hpp>
31 #include "GnashImage.h"
34 #include "GnashException.h"
35 #include "IOChannel.h"
43 readData(GifFileType
* ft
, GifByteType
* data
, int length
)
45 // Do not read until opened.
47 IOChannel
* in
= reinterpret_cast<IOChannel
*>(ft
->UserData
);
48 return in
->read(reinterpret_cast<char*>(data
), length
);
51 class GifInput
: public Input
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
);
65 /// Begin processing the image data.
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().
87 /// @param rgbData The buffer for writing raw RGB data to.
88 void readScanline(unsigned char* rgb_data
);
92 /// Initialize gif_lib
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.
103 // A counter for keeping track of the last row copied.
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
)
121 GifInput::~GifInput()
123 // Clean up allocated data.
128 GifInput::getHeight() const
131 return _gif
->SHeight
;
135 GifInput::getWidth() const
142 GifInput::readScanline(unsigned char* rgbData
)
145 const ColorMapObject
* const colormap
= (_gif
->Image
.ColorMap
) ?
146 _gif
->Image
.ColorMap
: _gif
->SColorMap
;
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
;
167 GifInput::processRecord(GifRecordType 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 "
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
,
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 "
232 // One record is enough.
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
)
244 throw ParserException(_("GIF: failed reading "
248 // One record is enough.
252 case EXTENSION_RECORD_TYPE
:
253 // Skip all extension records.
254 GifByteType
* extension
;
256 DGifGetExtension(_gif
, &extCode
, &extension
);
258 if (DGifGetExtensionNext(_gif
, &extension
) == GIF_ERROR
) {
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
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!
295 } // unnamed namespace
298 createGifInput(boost::shared_ptr
<IOChannel
> in
)
300 std::auto_ptr
<Input
> ret(new GifInput(in
));
312 // indent-tabs-mode: t