reduce verbosity
[gnash.git] / libbase / GnashImagePng.cpp
blob16ded78a17bbd966759c5c89c4b726004ead199a
1 // GnashImagePng.cpp: libpng 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 #ifdef HAVE_CONFIG_H
22 # include "gnashconfig.h"
23 #endif
25 #include "GnashImagePng.h"
27 #include <sstream>
28 #include <boost/scoped_array.hpp>
30 extern "C" {
31 #ifdef HAVE_PNG_H
32 # include <png.h>
33 #endif
36 #include "GnashImage.h"
37 #include "utility.h"
38 #include "log.h"
39 #include "GnashException.h"
40 #include "IOChannel.h"
42 namespace gnash {
43 namespace image {
45 namespace {
47 void
48 error(png_struct*, const char* msg)
50 std::ostringstream ss;
51 ss << _("PNG error: ") << msg;
52 throw ParserException(ss.str());
55 void
56 warning(png_struct*, const char* msg)
58 log_debug(_("PNG warning: %s"), msg);
61 void
62 readData(png_structp pngptr, png_bytep data, png_size_t length)
64 // Do not call unless the PNG exists.
65 assert(pngptr);
66 IOChannel* in = reinterpret_cast<IOChannel*>(png_get_io_ptr(pngptr));
67 in->read(reinterpret_cast<char*>(data), length);
70 void
71 writeData(png_structp pngptr, png_bytep data, png_size_t length)
73 // Do not call unless the PNG exists.
74 assert(pngptr);
75 IOChannel* out = reinterpret_cast<IOChannel*>(png_get_io_ptr(pngptr));
76 out->write(reinterpret_cast<char*>(data), length);
79 void
80 flushData(png_structp /*pngptr*/)
84 class PngInput : public Input
87 public:
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)
96 Input(in),
97 _pngPtr(0),
98 _infoPtr(0),
99 _rowPtrs(0),
100 _pixelData(0),
101 _currentRow(0)
103 init();
106 ~PngInput();
108 /// Begin processing the image data.
109 void read();
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);
128 private:
130 // State needed for input.
131 png_structp _pngPtr;
132 png_infop _infoPtr;
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.
137 size_t _currentRow;
139 void init();
141 // Return number of components (i.e. == 3 for RGB
142 // data).
143 size_t getComponents() const;
147 // Class object for writing PNG image data.
148 class PngOutput : public Output
151 public:
153 /// Create an output object bound to a gnash::IOChannel
155 /// @param out The IOChannel used for output. Must be kept alive
156 /// throughout
157 /// @param quality Unused in PNG output
158 PngOutput(boost::shared_ptr<IOChannel> out, size_t width,
159 size_t height, int quality);
161 ~PngOutput();
163 void writeImageRGB(const unsigned char* rgbData);
165 void writeImageRGBA(const unsigned char* rgbaData);
167 private:
169 /// Initialize libpng.
170 void init();
172 /// Libpng structures for image and output state.
173 png_structp _pngPtr;
174 png_infop _infoPtr;
178 PngInput::~PngInput()
180 png_destroy_read_struct(&_pngPtr, &_infoPtr, 0);
183 size_t
184 PngInput::getHeight() const
186 assert(_pngPtr && _infoPtr);
187 return png_get_image_height(_pngPtr, _infoPtr);
190 size_t
191 PngInput::getWidth() const
193 assert(_pngPtr && _infoPtr);
194 return png_get_image_width(_pngPtr, _infoPtr);
197 size_t
198 PngInput::getComponents() const
200 return png_get_channels(_pngPtr, _infoPtr);
203 void
204 PngInput::readScanline(unsigned char* imageData)
206 assert(_currentRow < getHeight());
207 assert(_rowPtrs);
209 // Data packed as RGB / RGBA
210 const size_t size = getWidth() * getComponents();
212 std::copy(_rowPtrs[_currentRow], _rowPtrs[_currentRow] + size, imageData);
214 ++_currentRow;
218 void
219 PngInput::init()
221 // Initialize png library.
222 _pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, &error,
223 &warning);
225 if (!_pngPtr) return;
227 _infoPtr = png_create_info_struct(_pngPtr);
229 if (!_infoPtr) {
230 png_destroy_read_struct(&_pngPtr, 0, 0);
231 return;
235 void
236 PngInput::read()
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);
257 #else
258 png_set_expand_gray_1_2_4_to_8(_pngPtr);
259 #endif
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);
266 _type = TYPE_RGBA;
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");
276 _type = TYPE_RGBA;
278 else {
279 log_debug("Loading PNG image without alpha");
280 _type = TYPE_RGB;
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
304 // each row.
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());
318 /// PNG output
321 PngOutput::PngOutput(boost::shared_ptr<IOChannel> out, size_t width,
322 size_t height, int /*quality*/)
324 Output(out, width, height),
325 _pngPtr(0),
326 _infoPtr(0)
328 init();
332 PngOutput::~PngOutput()
334 png_destroy_write_struct(&_pngPtr, &_infoPtr);
338 void
339 PngOutput::init()
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);
348 if (!_infoPtr) {
349 png_destroy_write_struct(&_pngPtr, static_cast<png_infopp>(NULL));
350 return;
354 void
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]);
361 // RGBA
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);
379 void
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]);
386 // RGB
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
405 std::auto_ptr<Input>
406 createPngInput(boost::shared_ptr<IOChannel> in)
408 std::auto_ptr<Input> ret(new PngInput(in));
409 ret->read();
410 return ret;
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));
418 return outChannel;
421 } // namespace image
422 } // namespace gnash
424 // Local Variables:
425 // mode: C++
426 // c-basic-offset: 8
427 // tab-width: 8
428 // indent-tabs-mode: t
429 // End: