/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- error
- warning
- readData
- writeData
- flushData
- _currentRow
- getHeight
- getWidth
- getComponents
- readScanline
- init
- read
- _infoPtr
- init
- writeImageRGBA
- writeImageRGB
- createPngInput
- createPngOutput
// GnashImagePng.cpp: libpng wrapper for Gnash.
//
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
// 2011 Free Software Foundation, Inc
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
#ifdef HAVE_CONFIG_H
# include "gnashconfig.h"
#endif
#include "GnashImagePng.h"
#include <sstream>
#include <boost/scoped_array.hpp>
extern "C" {
#ifdef HAVE_PNG_H
# include <png.h>
#endif
}
#include "GnashImage.h"
#include "utility.h"
#include "log.h"
#include "GnashException.h"
#include "IOChannel.h"
namespace gnash {
namespace image {
namespace {
void
error(png_struct*, const char* msg)
{
std::ostringstream ss;
ss << _("PNG error: ") << msg;
throw ParserException(ss.str());
}
void
warning(png_struct*, const char* msg)
{
log_debug(_("PNG warning: %s"), msg);
}
void
readData(png_structp pngptr, png_bytep data, png_size_t length)
{
// Do not call unless the PNG exists.
assert(pngptr);
IOChannel* in = reinterpret_cast<IOChannel*>(png_get_io_ptr(pngptr));
in->read(reinterpret_cast<char*>(data), length);
}
void
writeData(png_structp pngptr, png_bytep data, png_size_t length)
{
// Do not call unless the PNG exists.
assert(pngptr);
IOChannel* out = reinterpret_cast<IOChannel*>(png_get_io_ptr(pngptr));
out->write(reinterpret_cast<char*>(data), length);
}
void
flushData(png_structp /*pngptr*/)
{
}
class PngInput : public Input
{
public:
/// Construct a PngInput object to read from an IOChannel.
//
/// @param in The stream to read PNG data from. Ownership is shared
/// between caller and JpegInput, so it is freed
/// automatically when the last owner is destroyed.
PngInput(boost::shared_ptr<IOChannel> in)
:
Input(in),
_pngPtr(0),
_infoPtr(0),
_rowPtrs(0),
_pixelData(0),
_currentRow(0)
{
init();
}
~PngInput();
/// Begin processing the image data.
void read();
/// Get the image's height in pixels.
//
/// @return The height of the image in pixels.
size_t getHeight() const;
/// Get the image's width in pixels.
//
/// @return The width of the image in pixels.
size_t getWidth() const;
/// Read a scanline's worth of image data into the given buffer.
//
/// The amount of data read is getWidth() * getComponents().
///
/// @param rgbData The buffer for writing raw RGB data to.
void readScanline(unsigned char* imageData);
private:
// State needed for input.
png_structp _pngPtr;
png_infop _infoPtr;
boost::scoped_array<png_bytep> _rowPtrs;
boost::scoped_array<png_byte> _pixelData;
// A counter for keeping track of the last row copied.
size_t _currentRow;
void init();
// Return number of components (i.e. == 3 for RGB
// data).
size_t getComponents() const;
};
// Class object for writing PNG image data.
class PngOutput : public Output
{
public:
/// Create an output object bound to a gnash::IOChannel
//
/// @param out The IOChannel used for output. Must be kept alive
/// throughout
/// @param quality Unused in PNG output
PngOutput(boost::shared_ptr<IOChannel> out, size_t width,
size_t height, int quality);
~PngOutput();
void writeImageRGB(const unsigned char* rgbData);
void writeImageRGBA(const unsigned char* rgbaData);
private:
/// Initialize libpng.
void init();
/// Libpng structures for image and output state.
png_structp _pngPtr;
png_infop _infoPtr;
};
PngInput::~PngInput()
{
png_destroy_read_struct(&_pngPtr, &_infoPtr, 0);
}
size_t
PngInput::getHeight() const
{
assert(_pngPtr && _infoPtr);
return png_get_image_height(_pngPtr, _infoPtr);
}
size_t
PngInput::getWidth() const
{
assert(_pngPtr && _infoPtr);
return png_get_image_width(_pngPtr, _infoPtr);
}
size_t
PngInput::getComponents() const
{
return png_get_channels(_pngPtr, _infoPtr);
}
void
PngInput::readScanline(unsigned char* imageData)
{
assert(_currentRow < getHeight());
assert(_rowPtrs);
// Data packed as RGB / RGBA
const size_t size = getWidth() * getComponents();
std::copy(_rowPtrs[_currentRow], _rowPtrs[_currentRow] + size, imageData);
++_currentRow;
}
void
PngInput::init()
{
// Initialize png library.
_pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, &error,
&warning);
if (!_pngPtr) return;
_infoPtr = png_create_info_struct(_pngPtr);
if (!_infoPtr) {
png_destroy_read_struct(&_pngPtr, 0, 0);
return;
}
}
void
PngInput::read()
{
// Set our user-defined reader function
png_set_read_fn(_pngPtr, _inStream.get(), &readData);
png_read_info(_pngPtr, _infoPtr);
const png_byte type = png_get_color_type(_pngPtr, _infoPtr);
const png_byte bitDepth = png_get_bit_depth(_pngPtr, _infoPtr);
// Convert indexed images to RGB
if (type == PNG_COLOR_TYPE_PALETTE) {
log_debug("Converting palette PNG to RGB(A)");
png_set_palette_to_rgb(_pngPtr);
}
// Convert less-than-8-bit greyscale to 8 bit.
if (type == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
log_debug("Setting grey bit depth(%d) to 8", bitDepth);
#if PNG_LIBPNG_VER_MINOR < 4
png_set_gray_1_2_4_to_8(_pngPtr);
#else
png_set_expand_gray_1_2_4_to_8(_pngPtr);
#endif
}
// Apply the transparency block if it exists.
if (png_get_valid(_pngPtr, _infoPtr, PNG_INFO_tRNS)) {
log_debug("Applying transparency block, image is RGBA");
png_set_tRNS_to_alpha(_pngPtr);
_type = TYPE_RGBA;
}
// Make 16-bit data into 8-bit data
if (bitDepth == 16) png_set_strip_16(_pngPtr);
// Set the type of the image if it hasn't been set already.
if (_type == GNASH_IMAGE_INVALID) {
if (type & PNG_COLOR_MASK_ALPHA) {
log_debug("Loading PNG image with alpha");
_type = TYPE_RGBA;
}
else {
log_debug("Loading PNG image without alpha");
_type = TYPE_RGB;
}
}
// Convert 1-channel grey images to 3-channel RGB.
if (type == PNG_COLOR_TYPE_GRAY || type == PNG_COLOR_TYPE_GRAY_ALPHA) {
log_debug("Converting greyscale PNG to RGB(A)");
png_set_gray_to_rgb(_pngPtr);
}
png_read_update_info(_pngPtr, _infoPtr);
const size_t height = getHeight();
const size_t width = getWidth();
const size_t components = getComponents();
// We must have 3 or 4-channel data by this point.
assert((_type == TYPE_RGB && components == 3) ||
(_type == TYPE_RGBA && components == 4));
// Allocate space for the data
_pixelData.reset(new png_byte[width * height * components]);
// Allocate an array of pointers to the beginning of
// each row.
_rowPtrs.reset(new png_bytep[height]);
// Fill in the row pointers.
for (size_t y = 0; y < height; ++y) {
_rowPtrs[y] = _pixelData.get() + y * width * components;
}
// Read in the image using the options set.
png_read_image(_pngPtr, _rowPtrs.get());
}
///
/// PNG output
///
PngOutput::PngOutput(boost::shared_ptr<IOChannel> out, size_t width,
size_t height, int /*quality*/)
:
Output(out, width, height),
_pngPtr(0),
_infoPtr(0)
{
init();
}
PngOutput::~PngOutput()
{
png_destroy_write_struct(&_pngPtr, &_infoPtr);
}
void
PngOutput::init()
{
// Initialize png library.
_pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, &error, &warning);
if (!_pngPtr) return;
_infoPtr = png_create_info_struct(_pngPtr);
if (!_infoPtr) {
png_destroy_write_struct(&_pngPtr, static_cast<png_infopp>(NULL));
return;
}
}
void
PngOutput::writeImageRGBA(const unsigned char* rgbaData)
{
png_set_write_fn(_pngPtr, _outStream.get(), &writeData, &flushData);
boost::scoped_array<const png_byte*> rows(new const png_byte*[_height]);
// RGBA
const size_t components = 4;
for (size_t y = 0; y < _height; ++y) {
rows[y] = rgbaData + _width * y * components;
}
// libpng needs non-const. We'll hope it doesn't change our image.
png_set_rows(_pngPtr, _infoPtr, const_cast<png_bytepp>(rows.get()));
png_set_IHDR(_pngPtr, _infoPtr, _width, _height,
8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_png(_pngPtr, _infoPtr, PNG_TRANSFORM_IDENTITY, NULL);
}
void
PngOutput::writeImageRGB(const unsigned char* rgbData)
{
png_set_write_fn(_pngPtr, _outStream.get(), &writeData, &flushData);
boost::scoped_array<const png_byte*> rows(new const png_byte*[_height]);
// RGB
const size_t components = 3;
for (size_t y = 0; y < _height; ++y) {
rows[y] = rgbData + _width * y * components;
}
// libpng needs non-const. We'll hope it doesn't change our image.
png_set_rows(_pngPtr, _infoPtr, const_cast<png_bytepp>(rows.get()));
png_set_IHDR(_pngPtr, _infoPtr, _width, _height,
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_png(_pngPtr, _infoPtr, PNG_TRANSFORM_IDENTITY, NULL);
}
} // unnamed namespace
std::auto_ptr<Input>
createPngInput(boost::shared_ptr<IOChannel> in)
{
std::auto_ptr<Input> ret(new PngInput(in));
ret->read();
return ret;
}
std::auto_ptr<Output>
createPngOutput(boost::shared_ptr<IOChannel> o, size_t width,
size_t height, int quality)
{
std::auto_ptr<Output> outChannel(new PngOutput(o, width, height, quality));
return outChannel;
}
} // namespace image
} // namespace gnash
// Local Variables:
// mode: C++
// c-basic-offset: 8
// tab-width: 8
// indent-tabs-mode: t
// End: