ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

png.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // Since we need to work with libpng here, we need to allow all symbols
00024 // to avoid compilation issues.
00025 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00026 #include "common/scummsys.h"
00027 
00028 #ifdef USE_PNG
00029 #include <png.h>
00030 #endif
00031 
00032 #include "image/png.h"
00033 
00034 #include "graphics/pixelformat.h"
00035 #include "graphics/surface.h"
00036 
00037 #include "common/array.h"
00038 #include "common/stream.h"
00039 
00040 namespace Image {
00041 
00042 PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0), _skipSignature(false) {
00043 }
00044 
00045 PNGDecoder::~PNGDecoder() {
00046     destroy();
00047 }
00048 
00049 void PNGDecoder::destroy() {
00050     if (_outputSurface) {
00051         _outputSurface->free();
00052         delete _outputSurface;
00053         _outputSurface = 0;
00054     }
00055     delete[] _palette;
00056     _palette = NULL;
00057 }
00058 
00059 #ifdef USE_PNG
00060 // libpng-error-handling:
00061 void pngError(png_structp pngptr, png_const_charp errorMsg) {
00062     error("%s", errorMsg);
00063 }
00064 
00065 void pngWarning(png_structp pngptr, png_const_charp warningMsg) {
00066     warning("%s", warningMsg);
00067 }
00068 
00069 // libpng-I/O-helpers:
00070 void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) {
00071     void *readIOptr = png_get_io_ptr(pngPtr);
00072     Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr;
00073     stream->read(data, length);
00074 }
00075 
00076 void pngWriteToStream(png_structp pngPtr, png_bytep data, png_size_t length) {
00077     void *writeIOptr = png_get_io_ptr(pngPtr);
00078     Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
00079     stream->write(data, length);
00080 }
00081 
00082 void pngFlushStream(png_structp pngPtr) {
00083     void *writeIOptr = png_get_io_ptr(pngPtr);
00084     Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
00085     stream->flush();
00086 }
00087 #endif
00088 
00089 /*
00090  * This code is based on Broken Sword 2.5 engine
00091  *
00092  * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
00093  *
00094  * Licensed under GNU GPL v2
00095  *
00096  */
00097 
00098 bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
00099 #ifdef USE_PNG
00100     destroy();
00101 
00102     // First, check the PNG signature (if not set to skip it)
00103     if (!_skipSignature) {
00104         if (stream.readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
00105             return false;
00106         }
00107         if (stream.readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) {
00108             return false;
00109         }
00110     }
00111 
00112     // The following is based on the guide provided in:
00113     //http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3
00114     //http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf
00115     // along with the png-loading code used in the sword25-engine.
00116     png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00117     if (!pngPtr) {
00118         return false;
00119     }
00120     png_infop infoPtr = png_create_info_struct(pngPtr);
00121     if (!infoPtr) {
00122         png_destroy_read_struct(&pngPtr, NULL, NULL);
00123         return false;
00124     }
00125 
00126     png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
00127     // TODO: The manual says errors should be handled via setjmp
00128 
00129     png_set_read_fn(pngPtr, &stream, pngReadFromStream);
00130     png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE);
00131     // We already verified the PNG-header
00132     png_set_sig_bytes(pngPtr, 8);
00133 
00134     // Read PNG header
00135     png_read_info(pngPtr, infoPtr);
00136 
00137     // No handling for unknown chunks yet.
00138     int bitDepth, colorType, width, height, interlaceType;
00139     png_uint_32 w, h;
00140     png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL);
00141     width = w;
00142     height = h;
00143 
00144     // Allocate memory for the final image data.
00145     // To keep memory framentation low this happens before allocating memory for temporary image data.
00146     _outputSurface = new Graphics::Surface();
00147 
00148     // Images of all color formats except PNG_COLOR_TYPE_PALETTE
00149     // will be transformed into ARGB images
00150     if (colorType == PNG_COLOR_TYPE_PALETTE && !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
00151         int numPalette = 0;
00152         png_colorp palette = NULL;
00153         uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette);
00154         if (success != PNG_INFO_PLTE) {
00155             png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
00156             return false;
00157         }
00158         _paletteColorCount = numPalette;
00159         _palette = new byte[_paletteColorCount * 3];
00160         for (int i = 0; i < _paletteColorCount; i++) {
00161             _palette[(i * 3)] = palette[i].red;
00162             _palette[(i * 3) + 1] = palette[i].green;
00163             _palette[(i * 3) + 2] = palette[i].blue;
00164 
00165         }
00166         _outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
00167         png_set_packing(pngPtr);
00168     } else {
00169         bool isAlpha = (colorType & PNG_COLOR_MASK_ALPHA);
00170         if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
00171             isAlpha = true;
00172             png_set_expand(pngPtr);
00173         }
00174         _outputSurface->create(width, height, Graphics::PixelFormat(4,
00175                                8, 8, 8, isAlpha ? 8 : 0, 24, 16, 8, 0));
00176         if (!_outputSurface->getPixels()) {
00177             error("Could not allocate memory for output image.");
00178         }
00179         if (bitDepth == 16)
00180             png_set_strip_16(pngPtr);
00181         if (bitDepth < 8)
00182             png_set_expand(pngPtr);
00183         if (colorType == PNG_COLOR_TYPE_GRAY ||
00184             colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
00185             png_set_gray_to_rgb(pngPtr);
00186 
00187         // PNGs are Big-Endian:
00188 #ifdef SCUMM_LITTLE_ENDIAN
00189         png_set_bgr(pngPtr);
00190         png_set_swap_alpha(pngPtr);
00191         if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
00192             png_set_filler(pngPtr, 0xff, PNG_FILLER_BEFORE);
00193 #else
00194         if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
00195             png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER);
00196 #endif
00197 
00198     }
00199 
00200     // After the transformations have been registered, the image data is read again.
00201     png_set_interlace_handling(pngPtr);
00202     png_read_update_info(pngPtr, infoPtr);
00203     png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);
00204     width = w;
00205     height = h;
00206 
00207     if (interlaceType == PNG_INTERLACE_NONE) {
00208         // PNGs without interlacing can simply be read row by row.
00209         for (int i = 0; i < height; i++) {
00210             png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL);
00211         }
00212     } else {
00213         // PNGs with interlacing require us to allocate an auxillary
00214         // buffer with pointers to all row starts.
00215 
00216         // Allocate row pointer buffer
00217         png_bytep *rowPtr = new png_bytep[height];
00218         if (!rowPtr) {
00219             error("Could not allocate memory for row pointers.");
00220         }
00221 
00222         // Initialize row pointers
00223         for (int i = 0; i < height; i++)
00224             rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i);
00225 
00226         // Read image data
00227         png_read_image(pngPtr, rowPtr);
00228 
00229         // Free row pointer buffer
00230         delete[] rowPtr;
00231     }
00232 
00233     // Read additional data at the end.
00234     png_read_end(pngPtr, NULL);
00235 
00236     // Destroy libpng structures
00237     png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
00238 
00239     return true;
00240 #else
00241     return false;
00242 #endif
00243 }
00244 
00245 bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const bool bottomUp) {
00246 #ifdef USE_PNG
00247     const Graphics::PixelFormat requiredFormat_3byte(3, 8, 8, 8, 0, 16, 8, 0, 0);
00248     const Graphics::PixelFormat requiredFormat_4byte(4, 8, 8, 8, 8, 0, 8, 16, 24);
00249 
00250     if (input.format.bytesPerPixel == 3) {
00251         if (input.format != requiredFormat_3byte) {
00252             warning("Cannot currently write PNG with 3-byte pixel format other than %s", requiredFormat_3byte.toString().c_str());
00253             return false;
00254         }
00255     } else if (input.format.bytesPerPixel != 4) {
00256         warning("Cannot currently write PNG with pixel format of bpp other than 3, 4");
00257         return false;
00258     }
00259 
00260     int colorType;
00261     Graphics::Surface *tmp = NULL;
00262     const Graphics::Surface *surface;
00263 
00264     if (input.format == requiredFormat_3byte) {
00265         surface = &input;
00266         colorType = PNG_COLOR_TYPE_RGB;
00267     } else {
00268         if (input.format == requiredFormat_4byte) {
00269             surface = &input;
00270         } else {
00271             surface = tmp = input.convertTo(requiredFormat_4byte);
00272         }
00273         colorType = PNG_COLOR_TYPE_RGB_ALPHA;
00274     }
00275 
00276     png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00277     if (!pngPtr) {
00278         return false;
00279     }
00280     png_infop infoPtr = png_create_info_struct(pngPtr);
00281     if (!infoPtr) {
00282         png_destroy_write_struct(&pngPtr, NULL);
00283         return false;
00284     }
00285 
00286     png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
00287     // TODO: The manual says errors should be handled via setjmp
00288 
00289     png_set_write_fn(pngPtr, &out, pngWriteToStream, pngFlushStream);
00290 
00291     png_set_IHDR(pngPtr, infoPtr, surface->w, surface->h, 8, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00292 
00293     Common::Array<const uint8 *> rows;
00294     rows.reserve(surface->h);
00295     if (bottomUp) {
00296         for (uint y = surface->h; y-- > 0;) {
00297             rows.push_back((const uint8 *)surface->getBasePtr(0, y));
00298         }
00299     } else {
00300         for (uint y = 0; y < surface->h; ++y) {
00301             rows.push_back((const uint8 *)surface->getBasePtr(0, y));
00302         }
00303     }
00304 
00305     png_set_rows(pngPtr, infoPtr, const_cast<uint8 **>(&rows.front()));
00306     png_write_png(pngPtr, infoPtr, 0, NULL);
00307     png_destroy_write_struct(&pngPtr, &infoPtr);
00308 
00309     // free tmp surface
00310     if (tmp) {
00311         tmp->free();
00312         delete tmp;
00313     }
00314 
00315     return true;
00316 #else
00317     return false;
00318 #endif
00319 }
00320 
00321 } // End of namespace Image


Generated on Sat Feb 16 2019 05:01:01 for ResidualVM by doxygen 1.7.1
curved edge   curved edge