1 // GnashImagePng.cpp: libpng 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
22 # include "gnashconfig.h"
25 #include "GnashImagePng.h"
28 #include <boost/scoped_array.hpp>
36 #include "GnashImage.h"
39 #include "GnashException.h"
40 #include "IOChannel.h"
48 error(png_struct
*, const char* msg
)
50 std::ostringstream ss
;
51 ss
<< _("PNG error: ") << msg
;
52 throw ParserException(ss
.str());
56 warning(png_struct
*, const char* msg
)
58 log_debug(_("PNG warning: %s"), msg
);
62 readData(png_structp pngptr
, png_bytep data
, png_size_t length
)
64 // Do not call unless the PNG exists.
66 IOChannel
* in
= reinterpret_cast<IOChannel
*>(png_get_io_ptr(pngptr
));
67 in
->read(reinterpret_cast<char*>(data
), length
);
71 writeData(png_structp pngptr
, png_bytep data
, png_size_t length
)
73 // Do not call unless the PNG exists.
75 IOChannel
* out
= reinterpret_cast<IOChannel
*>(png_get_io_ptr(pngptr
));
76 out
->write(reinterpret_cast<char*>(data
), length
);
80 flushData(png_structp
/*pngptr*/)
84 class PngInput
: public Input
89 /// Construct a PngInput object to read from an IOChannel.
91 /// @param in The stream to read PNG data from. Ownership is shared
92 /// between caller and JpegInput, so it is freed
93 /// automatically when the last owner is destroyed.
94 PngInput(boost::shared_ptr
<IOChannel
> in
)
108 /// Begin processing the image data.
111 /// Get the image's height in pixels.
113 /// @return The height of the image in pixels.
114 size_t getHeight() const;
116 /// Get the image's width in pixels.
118 /// @return The width of the image in pixels.
119 size_t getWidth() const;
121 /// Read a scanline's worth of image data into the given buffer.
123 /// The amount of data read is getWidth() * getComponents().
125 /// @param rgbData The buffer for writing raw RGB data to.
126 void readScanline(unsigned char* imageData
);
130 // State needed for input.
133 boost::scoped_array
<png_bytep
> _rowPtrs
;
134 boost::scoped_array
<png_byte
> _pixelData
;
136 // A counter for keeping track of the last row copied.
141 // Return number of components (i.e. == 3 for RGB
143 size_t getComponents() const;
147 // Class object for writing PNG image data.
148 class PngOutput
: public Output
153 /// Create an output object bound to a gnash::IOChannel
155 /// @param out The IOChannel used for output. Must be kept alive
157 /// @param quality Unused in PNG output
158 PngOutput(boost::shared_ptr
<IOChannel
> out
, size_t width
,
159 size_t height
, int quality
);
163 void writeImageRGB(const unsigned char* rgbData
);
165 void writeImageRGBA(const unsigned char* rgbaData
);
169 /// Initialize libpng.
172 /// Libpng structures for image and output state.
178 PngInput::~PngInput()
180 png_destroy_read_struct(&_pngPtr
, &_infoPtr
, 0);
184 PngInput::getHeight() const
186 assert(_pngPtr
&& _infoPtr
);
187 return png_get_image_height(_pngPtr
, _infoPtr
);
191 PngInput::getWidth() const
193 assert(_pngPtr
&& _infoPtr
);
194 return png_get_image_width(_pngPtr
, _infoPtr
);
198 PngInput::getComponents() const
200 return png_get_channels(_pngPtr
, _infoPtr
);
204 PngInput::readScanline(unsigned char* imageData
)
206 assert(_currentRow
< getHeight());
209 // Data packed as RGB / RGBA
210 const size_t size
= getWidth() * getComponents();
212 std::copy(_rowPtrs
[_currentRow
], _rowPtrs
[_currentRow
] + size
, imageData
);
221 // Initialize png library.
222 _pngPtr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, 0, &error
,
225 if (!_pngPtr
) return;
227 _infoPtr
= png_create_info_struct(_pngPtr
);
230 png_destroy_read_struct(&_pngPtr
, 0, 0);
238 // Set our user-defined reader function
239 png_set_read_fn(_pngPtr
, _inStream
.get(), &readData
);
241 png_read_info(_pngPtr
, _infoPtr
);
243 const png_byte type
= png_get_color_type(_pngPtr
, _infoPtr
);
244 const png_byte bitDepth
= png_get_bit_depth(_pngPtr
, _infoPtr
);
246 // Convert indexed images to RGB
247 if (type
== PNG_COLOR_TYPE_PALETTE
) {
248 log_debug("Converting palette PNG to RGB(A)");
249 png_set_palette_to_rgb(_pngPtr
);
252 // Convert less-than-8-bit greyscale to 8 bit.
253 if (type
== PNG_COLOR_TYPE_GRAY
&& bitDepth
< 8) {
254 log_debug("Setting grey bit depth(%d) to 8", bitDepth
);
255 #if PNG_LIBPNG_VER_MINOR < 4
256 png_set_gray_1_2_4_to_8(_pngPtr
);
258 png_set_expand_gray_1_2_4_to_8(_pngPtr
);
262 // Apply the transparency block if it exists.
263 if (png_get_valid(_pngPtr
, _infoPtr
, PNG_INFO_tRNS
)) {
264 log_debug("Applying transparency block, image is RGBA");
265 png_set_tRNS_to_alpha(_pngPtr
);
269 // Make 16-bit data into 8-bit data
270 if (bitDepth
== 16) png_set_strip_16(_pngPtr
);
272 // Set the type of the image if it hasn't been set already.
273 if (_type
== GNASH_IMAGE_INVALID
) {
274 if (type
& PNG_COLOR_MASK_ALPHA
) {
275 log_debug("Loading PNG image with alpha");
279 log_debug("Loading PNG image without alpha");
284 // Convert 1-channel grey images to 3-channel RGB.
285 if (type
== PNG_COLOR_TYPE_GRAY
|| type
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
286 log_debug("Converting greyscale PNG to RGB(A)");
287 png_set_gray_to_rgb(_pngPtr
);
290 png_read_update_info(_pngPtr
, _infoPtr
);
292 const size_t height
= getHeight();
293 const size_t width
= getWidth();
294 const size_t components
= getComponents();
296 // We must have 3 or 4-channel data by this point.
297 assert((_type
== TYPE_RGB
&& components
== 3) ||
298 (_type
== TYPE_RGBA
&& components
== 4));
300 // Allocate space for the data
301 _pixelData
.reset(new png_byte
[width
* height
* components
]);
303 // Allocate an array of pointers to the beginning of
305 _rowPtrs
.reset(new png_bytep
[height
]);
307 // Fill in the row pointers.
308 for (size_t y
= 0; y
< height
; ++y
) {
309 _rowPtrs
[y
] = _pixelData
.get() + y
* width
* components
;
312 // Read in the image using the options set.
313 png_read_image(_pngPtr
, _rowPtrs
.get());
321 PngOutput::PngOutput(boost::shared_ptr
<IOChannel
> out
, size_t width
,
322 size_t height
, int /*quality*/)
324 Output(out
, width
, height
),
332 PngOutput::~PngOutput()
334 png_destroy_write_struct(&_pngPtr
, &_infoPtr
);
341 // Initialize png library.
342 _pngPtr
= png_create_write_struct(PNG_LIBPNG_VER_STRING
,
343 NULL
, &error
, &warning
);
344 if (!_pngPtr
) return;
346 _infoPtr
= png_create_info_struct(_pngPtr
);
349 png_destroy_write_struct(&_pngPtr
, static_cast<png_infopp
>(NULL
));
355 PngOutput::writeImageRGBA(const unsigned char* rgbaData
)
357 png_set_write_fn(_pngPtr
, _outStream
.get(), &writeData
, &flushData
);
359 boost::scoped_array
<const png_byte
*> rows(new const png_byte
*[_height
]);
362 const size_t components
= 4;
364 for (size_t y
= 0; y
< _height
; ++y
) {
365 rows
[y
] = rgbaData
+ _width
* y
* components
;
368 // libpng needs non-const. We'll hope it doesn't change our image.
369 png_set_rows(_pngPtr
, _infoPtr
, const_cast<png_bytepp
>(rows
.get()));
371 png_set_IHDR(_pngPtr
, _infoPtr
, _width
, _height
,
372 8, PNG_COLOR_TYPE_RGBA
, PNG_INTERLACE_NONE
,
373 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
375 png_write_png(_pngPtr
, _infoPtr
, PNG_TRANSFORM_IDENTITY
, NULL
);
380 PngOutput::writeImageRGB(const unsigned char* rgbData
)
382 png_set_write_fn(_pngPtr
, _outStream
.get(), &writeData
, &flushData
);
384 boost::scoped_array
<const png_byte
*> rows(new const png_byte
*[_height
]);
387 const size_t components
= 3;
389 for (size_t y
= 0; y
< _height
; ++y
) {
390 rows
[y
] = rgbData
+ _width
* y
* components
;
393 // libpng needs non-const. We'll hope it doesn't change our image.
394 png_set_rows(_pngPtr
, _infoPtr
, const_cast<png_bytepp
>(rows
.get()));
396 png_set_IHDR(_pngPtr
, _infoPtr
, _width
, _height
,
397 8, PNG_COLOR_TYPE_RGB
, PNG_INTERLACE_NONE
,
398 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
400 png_write_png(_pngPtr
, _infoPtr
, PNG_TRANSFORM_IDENTITY
, NULL
);
403 } // unnamed namespace
406 createPngInput(boost::shared_ptr
<IOChannel
> in
)
408 std::auto_ptr
<Input
> ret(new PngInput(in
));
413 std::auto_ptr
<Output
>
414 createPngOutput(boost::shared_ptr
<IOChannel
> o
, size_t width
,
415 size_t height
, int quality
)
417 std::auto_ptr
<Output
> outChannel(new PngOutput(o
, width
, height
, quality
));
428 // indent-tabs-mode: t