1 // GnashImagePng.cpp: libpng wrapper for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
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"
35 #include "GnashImage.h"
38 #include "GnashException.h"
39 #include "IOChannel.h"
47 error(png_struct
*, const char* msg
)
49 std::ostringstream ss
;
50 ss
<< _("PNG error: ") << msg
;
51 throw ParserException(ss
.str());
55 warning(png_struct
*, const char* msg
)
57 log_debug("PNG warning: %s", msg
);
61 readData(png_structp pngptr
, png_bytep data
, png_size_t length
)
63 // Do not call unless the PNG exists.
65 IOChannel
* in
= reinterpret_cast<IOChannel
*>(png_get_io_ptr(pngptr
));
66 in
->read(reinterpret_cast<char*>(data
), length
);
70 writeData(png_structp pngptr
, png_bytep data
, png_size_t length
)
72 // Do not call unless the PNG exists.
74 IOChannel
* out
= reinterpret_cast<IOChannel
*>(png_get_io_ptr(pngptr
));
75 out
->write(reinterpret_cast<char*>(data
), length
);
79 flushData(png_structp
/*pngptr*/)
83 class PngInput
: public Input
88 /// Construct a PngInput object to read from an IOChannel.
90 /// @param in The stream to read PNG data from. Ownership is shared
91 /// between caller and JpegInput, so it is freed
92 /// automatically when the last owner is destroyed.
93 PngInput(std::shared_ptr
<IOChannel
> in
)
105 /// Begin processing the image data.
108 /// Get the image's height in pixels.
110 /// @return The height of the image in pixels.
111 size_t getHeight() const;
113 /// Get the image's width in pixels.
115 /// @return The width of the image in pixels.
116 size_t getWidth() const;
118 /// Read a scanline's worth of image data into the given buffer.
120 /// The amount of data read is getWidth() * getComponents().
122 /// @param rgbData The buffer for writing raw RGB data to.
123 void readScanline(unsigned char* imageData
);
127 // State needed for input.
130 std::unique_ptr
<png_bytep
[]> _rowPtrs
;
131 std::unique_ptr
<png_byte
[]> _pixelData
;
133 // A counter for keeping track of the last row copied.
138 // Return number of components (i.e. == 3 for RGB
140 size_t getComponents() const;
144 // Class object for writing PNG image data.
145 class PngOutput
: public Output
150 /// Create an output object bound to a gnash::IOChannel
152 /// @param out The IOChannel used for output. Must be kept alive
154 /// @param quality Unused in PNG output
155 PngOutput(std::shared_ptr
<IOChannel
> out
, size_t width
,
156 size_t height
, int quality
);
160 void writeImageRGB(const unsigned char* rgbData
);
162 void writeImageRGBA(const unsigned char* rgbaData
);
166 /// Initialize libpng.
169 /// Libpng structures for image and output state.
175 PngInput::~PngInput()
177 png_destroy_read_struct(&_pngPtr
, &_infoPtr
, nullptr);
181 PngInput::getHeight() const
183 assert(_pngPtr
&& _infoPtr
);
184 return png_get_image_height(_pngPtr
, _infoPtr
);
188 PngInput::getWidth() const
190 assert(_pngPtr
&& _infoPtr
);
191 return png_get_image_width(_pngPtr
, _infoPtr
);
195 PngInput::getComponents() const
197 return png_get_channels(_pngPtr
, _infoPtr
);
201 PngInput::readScanline(unsigned char* imageData
)
203 assert(_currentRow
< getHeight());
206 // Data packed as RGB / RGBA
207 const size_t size
= getWidth() * getComponents();
209 std::copy(_rowPtrs
[_currentRow
], _rowPtrs
[_currentRow
] + size
, imageData
);
218 // Initialize png library.
219 _pngPtr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, nullptr, &error
,
222 if (!_pngPtr
) return;
224 _infoPtr
= png_create_info_struct(_pngPtr
);
227 png_destroy_read_struct(&_pngPtr
, nullptr, nullptr);
235 // Set our user-defined reader function
236 png_set_read_fn(_pngPtr
, _inStream
.get(), &readData
);
238 png_read_info(_pngPtr
, _infoPtr
);
240 const png_byte type
= png_get_color_type(_pngPtr
, _infoPtr
);
241 const png_byte bitDepth
= png_get_bit_depth(_pngPtr
, _infoPtr
);
243 // Convert indexed images to RGB
244 if (type
== PNG_COLOR_TYPE_PALETTE
) {
245 log_debug("Converting palette PNG to RGB(A)");
246 png_set_palette_to_rgb(_pngPtr
);
249 // Convert less-than-8-bit greyscale to 8 bit.
250 if (type
== PNG_COLOR_TYPE_GRAY
&& bitDepth
< 8) {
251 log_debug("Setting grey bit depth(%d) to 8", bitDepth
);
252 #if PNG_LIBPNG_VER_MINOR < 4
253 png_set_gray_1_2_4_to_8(_pngPtr
);
255 png_set_expand_gray_1_2_4_to_8(_pngPtr
);
259 // Apply the transparency block if it exists.
260 if (png_get_valid(_pngPtr
, _infoPtr
, PNG_INFO_tRNS
)) {
261 log_debug("Applying transparency block, image is RGBA");
262 png_set_tRNS_to_alpha(_pngPtr
);
266 // Make 16-bit data into 8-bit data
267 if (bitDepth
== 16) png_set_strip_16(_pngPtr
);
269 // Set the type of the image if it hasn't been set already.
270 if (_type
== GNASH_IMAGE_INVALID
) {
271 if (type
& PNG_COLOR_MASK_ALPHA
) {
272 log_debug("Loading PNG image with alpha");
276 log_debug("Loading PNG image without alpha");
281 // Convert 1-channel grey images to 3-channel RGB.
282 if (type
== PNG_COLOR_TYPE_GRAY
|| type
== PNG_COLOR_TYPE_GRAY_ALPHA
) {
283 log_debug("Converting greyscale PNG to RGB(A)");
284 png_set_gray_to_rgb(_pngPtr
);
287 png_read_update_info(_pngPtr
, _infoPtr
);
289 const size_t height
= getHeight();
290 const size_t width
= getWidth();
291 const size_t components
= getComponents();
293 // We must have 3 or 4-channel data by this point.
294 assert((_type
== TYPE_RGB
&& components
== 3) ||
295 (_type
== TYPE_RGBA
&& components
== 4));
297 // Allocate space for the data
298 _pixelData
.reset(new png_byte
[width
* height
* components
]);
300 // Allocate an array of pointers to the beginning of
302 _rowPtrs
.reset(new png_bytep
[height
]);
304 // Fill in the row pointers.
305 for (size_t y
= 0; y
< height
; ++y
) {
306 _rowPtrs
[y
] = _pixelData
.get() + y
* width
* components
;
309 // Read in the image using the options set.
310 png_read_image(_pngPtr
, _rowPtrs
.get());
318 PngOutput::PngOutput(std::shared_ptr
<IOChannel
> out
, size_t width
,
319 size_t height
, int /*quality*/)
321 Output(out
, width
, height
),
329 PngOutput::~PngOutput()
331 png_destroy_write_struct(&_pngPtr
, &_infoPtr
);
338 // Initialize png library.
339 _pngPtr
= png_create_write_struct(PNG_LIBPNG_VER_STRING
,
340 nullptr, &error
, &warning
);
341 if (!_pngPtr
) return;
343 _infoPtr
= png_create_info_struct(_pngPtr
);
346 png_destroy_write_struct(&_pngPtr
, static_cast<png_infopp
>(nullptr));
352 PngOutput::writeImageRGBA(const unsigned char* rgbaData
)
354 png_set_write_fn(_pngPtr
, _outStream
.get(), &writeData
, &flushData
);
356 std::unique_ptr
<const png_byte
*[]> rows(new const png_byte
*[_height
]);
359 const size_t components
= 4;
361 for (size_t y
= 0; y
< _height
; ++y
) {
362 rows
[y
] = rgbaData
+ _width
* y
* components
;
365 // libpng needs non-const. We'll hope it doesn't change our image.
366 png_set_rows(_pngPtr
, _infoPtr
, const_cast<png_bytepp
>(rows
.get()));
368 png_set_IHDR(_pngPtr
, _infoPtr
, _width
, _height
,
369 8, PNG_COLOR_TYPE_RGBA
, PNG_INTERLACE_NONE
,
370 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
372 png_write_png(_pngPtr
, _infoPtr
, PNG_TRANSFORM_IDENTITY
, nullptr);
377 PngOutput::writeImageRGB(const unsigned char* rgbData
)
379 png_set_write_fn(_pngPtr
, _outStream
.get(), &writeData
, &flushData
);
381 std::unique_ptr
<const png_byte
*[]> rows(new const png_byte
*[_height
]);
384 const size_t components
= 3;
386 for (size_t y
= 0; y
< _height
; ++y
) {
387 rows
[y
] = rgbData
+ _width
* y
* components
;
390 // libpng needs non-const. We'll hope it doesn't change our image.
391 png_set_rows(_pngPtr
, _infoPtr
, const_cast<png_bytepp
>(rows
.get()));
393 png_set_IHDR(_pngPtr
, _infoPtr
, _width
, _height
,
394 8, PNG_COLOR_TYPE_RGB
, PNG_INTERLACE_NONE
,
395 PNG_COMPRESSION_TYPE_DEFAULT
, PNG_FILTER_TYPE_DEFAULT
);
397 png_write_png(_pngPtr
, _infoPtr
, PNG_TRANSFORM_IDENTITY
, nullptr);
400 } // unnamed namespace
402 std::unique_ptr
<Input
>
403 createPngInput(std::shared_ptr
<IOChannel
> in
)
405 std::unique_ptr
<Input
> ret(new PngInput(in
));
410 std::unique_ptr
<Output
>
411 createPngOutput(std::shared_ptr
<IOChannel
> o
, size_t width
,
412 size_t height
, int quality
)
414 std::unique_ptr
<Output
> outChannel(new PngOutput(o
, width
, height
, quality
));
425 // indent-tabs-mode: t