Update with current status
[gnash.git] / libbase / GnashImagePng.cpp
blob907a8f0cc94c97a2379318e4d87cd7be28e116dc
1 // GnashImagePng.cpp: libpng wrapper for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software 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>
29 extern "C" {
30 #ifdef HAVE_PNG_H
31 # include <png.h>
32 #endif
35 #include "GnashImage.h"
36 #include "utility.h"
37 #include "log.h"
38 #include "GnashException.h"
39 #include "IOChannel.h"
41 namespace gnash {
42 namespace image {
44 namespace {
46 void
47 error(png_struct*, const char* msg)
49 std::ostringstream ss;
50 ss << _("PNG error: ") << msg;
51 throw ParserException(ss.str());
54 void
55 warning(png_struct*, const char* msg)
57 log_debug("PNG warning: %s", msg);
60 void
61 readData(png_structp pngptr, png_bytep data, png_size_t length)
63 // Do not call unless the PNG exists.
64 assert(pngptr);
65 IOChannel* in = reinterpret_cast<IOChannel*>(png_get_io_ptr(pngptr));
66 in->read(reinterpret_cast<char*>(data), length);
69 void
70 writeData(png_structp pngptr, png_bytep data, png_size_t length)
72 // Do not call unless the PNG exists.
73 assert(pngptr);
74 IOChannel* out = reinterpret_cast<IOChannel*>(png_get_io_ptr(pngptr));
75 out->write(reinterpret_cast<char*>(data), length);
78 void
79 flushData(png_structp /*pngptr*/)
83 class PngInput : public Input
86 public:
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)
95 Input(in),
96 _pngPtr(nullptr),
97 _infoPtr(nullptr),
98 _currentRow(0)
100 init();
103 ~PngInput();
105 /// Begin processing the image data.
106 void read();
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);
125 private:
127 // State needed for input.
128 png_structp _pngPtr;
129 png_infop _infoPtr;
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.
134 size_t _currentRow;
136 void init();
138 // Return number of components (i.e. == 3 for RGB
139 // data).
140 size_t getComponents() const;
144 // Class object for writing PNG image data.
145 class PngOutput : public Output
148 public:
150 /// Create an output object bound to a gnash::IOChannel
152 /// @param out The IOChannel used for output. Must be kept alive
153 /// throughout
154 /// @param quality Unused in PNG output
155 PngOutput(std::shared_ptr<IOChannel> out, size_t width,
156 size_t height, int quality);
158 ~PngOutput();
160 void writeImageRGB(const unsigned char* rgbData);
162 void writeImageRGBA(const unsigned char* rgbaData);
164 private:
166 /// Initialize libpng.
167 void init();
169 /// Libpng structures for image and output state.
170 png_structp _pngPtr;
171 png_infop _infoPtr;
175 PngInput::~PngInput()
177 png_destroy_read_struct(&_pngPtr, &_infoPtr, nullptr);
180 size_t
181 PngInput::getHeight() const
183 assert(_pngPtr && _infoPtr);
184 return png_get_image_height(_pngPtr, _infoPtr);
187 size_t
188 PngInput::getWidth() const
190 assert(_pngPtr && _infoPtr);
191 return png_get_image_width(_pngPtr, _infoPtr);
194 size_t
195 PngInput::getComponents() const
197 return png_get_channels(_pngPtr, _infoPtr);
200 void
201 PngInput::readScanline(unsigned char* imageData)
203 assert(_currentRow < getHeight());
204 assert(_rowPtrs);
206 // Data packed as RGB / RGBA
207 const size_t size = getWidth() * getComponents();
209 std::copy(_rowPtrs[_currentRow], _rowPtrs[_currentRow] + size, imageData);
211 ++_currentRow;
215 void
216 PngInput::init()
218 // Initialize png library.
219 _pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, &error,
220 &warning);
222 if (!_pngPtr) return;
224 _infoPtr = png_create_info_struct(_pngPtr);
226 if (!_infoPtr) {
227 png_destroy_read_struct(&_pngPtr, nullptr, nullptr);
228 return;
232 void
233 PngInput::read()
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);
254 #else
255 png_set_expand_gray_1_2_4_to_8(_pngPtr);
256 #endif
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);
263 _type = TYPE_RGBA;
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");
273 _type = TYPE_RGBA;
275 else {
276 log_debug("Loading PNG image without alpha");
277 _type = TYPE_RGB;
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
301 // each row.
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());
315 /// PNG output
318 PngOutput::PngOutput(std::shared_ptr<IOChannel> out, size_t width,
319 size_t height, int /*quality*/)
321 Output(out, width, height),
322 _pngPtr(nullptr),
323 _infoPtr(nullptr)
325 init();
329 PngOutput::~PngOutput()
331 png_destroy_write_struct(&_pngPtr, &_infoPtr);
335 void
336 PngOutput::init()
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);
345 if (!_infoPtr) {
346 png_destroy_write_struct(&_pngPtr, static_cast<png_infopp>(nullptr));
347 return;
351 void
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]);
358 // RGBA
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);
376 void
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]);
383 // RGB
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));
406 ret->read();
407 return ret;
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));
415 return outChannel;
418 } // namespace image
419 } // namespace gnash
421 // Local Variables:
422 // mode: C++
423 // c-basic-offset: 8
424 // tab-width: 8
425 // indent-tabs-mode: t
426 // End: