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() :
00043         _outputSurface(0),
00044         _palette(0),
00045         _paletteColorCount(0),
00046         _skipSignature(false),
00047         _keepTransparencyPaletted(false),
00048         _transparentColor(-1) {
00049 }
00050 
00051 PNGDecoder::~PNGDecoder() {
00052     destroy();
00053 }
00054 
00055 void PNGDecoder::destroy() {
00056     if (_outputSurface) {
00057         _outputSurface->free();
00058         delete _outputSurface;
00059         _outputSurface = 0;
00060     }
00061     delete[] _palette;
00062     _palette = NULL;
00063 }
00064 
00065 Graphics::PixelFormat PNGDecoder::getByteOrderRgbaPixelFormat() const {
00066 #ifdef SCUMM_BIG_ENDIAN
00067     return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
00068 #else
00069     return Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
00070 #endif
00071 }
00072 
00073 #ifdef USE_PNG
00074 // libpng-error-handling:
00075 void pngError(png_structp pngptr, png_const_charp errorMsg) {
00076     error("%s", errorMsg);
00077 }
00078 
00079 void pngWarning(png_structp pngptr, png_const_charp warningMsg) {
00080     warning("%s", warningMsg);
00081 }
00082 
00083 // libpng-I/O-helpers:
00084 void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) {
00085     void *readIOptr = png_get_io_ptr(pngPtr);
00086     Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr;
00087     stream->read(data, length);
00088 }
00089 
00090 void pngWriteToStream(png_structp pngPtr, png_bytep data, png_size_t length) {
00091     void *writeIOptr = png_get_io_ptr(pngPtr);
00092     Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
00093     stream->write(data, length);
00094 }
00095 
00096 void pngFlushStream(png_structp pngPtr) {
00097     void *writeIOptr = png_get_io_ptr(pngPtr);
00098     Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
00099     stream->flush();
00100 }
00101 #endif
00102 
00103 /*
00104  * This code is based on Broken Sword 2.5 engine
00105  *
00106  * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
00107  *
00108  * Licensed under GNU GPL v2
00109  *
00110  */
00111 
00112 bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
00113 #ifdef USE_PNG
00114     destroy();
00115 
00116     // First, check the PNG signature (if not set to skip it)
00117     if (!_skipSignature) {
00118         if (stream.readUint32BE() != MKTAG(0x89, 'P', 'N', 'G')) {
00119             return false;
00120         }
00121         if (stream.readUint32BE() != MKTAG(0x0d, 0x0a, 0x1a, 0x0a)) {
00122             return false;
00123         }
00124     }
00125 
00126     // The following is based on the guide provided in:
00127     //http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3
00128     //http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf
00129     // along with the png-loading code used in the sword25-engine.
00130     png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00131     if (!pngPtr) {
00132         return false;
00133     }
00134     png_infop infoPtr = png_create_info_struct(pngPtr);
00135     if (!infoPtr) {
00136         png_destroy_read_struct(&pngPtr, NULL, NULL);
00137         return false;
00138     }
00139 
00140     png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
00141     // TODO: The manual says errors should be handled via setjmp
00142 
00143     png_set_read_fn(pngPtr, &stream, pngReadFromStream);
00144     png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE);
00145     // We already verified the PNG-header
00146     png_set_sig_bytes(pngPtr, 8);
00147 
00148     // Read PNG header
00149     png_read_info(pngPtr, infoPtr);
00150 
00151     // No handling for unknown chunks yet.
00152     int bitDepth, colorType, width, height, interlaceType;
00153     png_uint_32 w, h;
00154     png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL);
00155     width = w;
00156     height = h;
00157 
00158     // Allocate memory for the final image data.
00159     // To keep memory framentation low this happens before allocating memory for temporary image data.
00160     _outputSurface = new Graphics::Surface();
00161 
00162     // Images of all color formats except PNG_COLOR_TYPE_PALETTE
00163     // will be transformed into ARGB images
00164     if (colorType == PNG_COLOR_TYPE_PALETTE && (_keepTransparencyPaletted || !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))) {
00165         int numPalette = 0;
00166         png_colorp palette = NULL;
00167         uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette);
00168         if (success != PNG_INFO_PLTE) {
00169             png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
00170             return false;
00171         }
00172         _paletteColorCount = numPalette;
00173         _palette = new byte[_paletteColorCount * 3];
00174         for (int i = 0; i < _paletteColorCount; i++) {
00175             _palette[(i * 3)] = palette[i].red;
00176             _palette[(i * 3) + 1] = palette[i].green;
00177             _palette[(i * 3) + 2] = palette[i].blue;
00178 
00179         }
00180 
00181         if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
00182             png_bytep trans;
00183             int numTrans;
00184             png_color_16p transColor;
00185             png_get_tRNS(pngPtr, infoPtr, &trans, &numTrans, &transColor);
00186             assert(numTrans == 1);
00187             _transparentColor = *trans;
00188         }
00189 
00190         _outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
00191         png_set_packing(pngPtr);
00192     } else {
00193         if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) {
00194             png_set_expand(pngPtr);
00195         }
00196 
00197         _outputSurface->create(width, height, getByteOrderRgbaPixelFormat());
00198         if (!_outputSurface->getPixels()) {
00199             error("Could not allocate memory for output image.");
00200         }
00201         if (bitDepth == 16)
00202             png_set_strip_16(pngPtr);
00203         if (bitDepth < 8)
00204             png_set_expand(pngPtr);
00205         if (colorType == PNG_COLOR_TYPE_GRAY ||
00206             colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
00207             png_set_gray_to_rgb(pngPtr);
00208 
00209         if (colorType != PNG_COLOR_TYPE_RGB_ALPHA)
00210             png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER);
00211     }
00212 
00213     // After the transformations have been registered, the image data is read again.
00214     png_set_interlace_handling(pngPtr);
00215     png_read_update_info(pngPtr, infoPtr);
00216     png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL);
00217     width = w;
00218     height = h;
00219 
00220     if (interlaceType == PNG_INTERLACE_NONE) {
00221         // PNGs without interlacing can simply be read row by row.
00222         for (int i = 0; i < height; i++) {
00223             png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL);
00224         }
00225     } else {
00226         // PNGs with interlacing require us to allocate an auxillary
00227         // buffer with pointers to all row starts.
00228 
00229         // Allocate row pointer buffer
00230         png_bytep *rowPtr = new png_bytep[height];
00231         if (!rowPtr) {
00232             error("Could not allocate memory for row pointers.");
00233         }
00234 
00235         // Initialize row pointers
00236         for (int i = 0; i < height; i++)
00237             rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i);
00238 
00239         // Read image data
00240         png_read_image(pngPtr, rowPtr);
00241 
00242         // Free row pointer buffer
00243         delete[] rowPtr;
00244     }
00245 
00246     // Read additional data at the end.
00247     png_read_end(pngPtr, NULL);
00248 
00249     // Destroy libpng structures
00250     png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
00251 
00252     return true;
00253 #else
00254     return false;
00255 #endif
00256 }
00257 
00258 // ResidualVM specific argument: bottomUp
00259 bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const bool bottomUp) {
00260 #ifdef USE_PNG
00261 #ifdef SCUMM_LITTLE_ENDIAN
00262     const Graphics::PixelFormat requiredFormat_3byte(3, 8, 8, 8, 0, 0, 8, 16, 0);
00263     const Graphics::PixelFormat requiredFormat_4byte(4, 8, 8, 8, 8, 0, 8, 16, 24);
00264 #else
00265     const Graphics::PixelFormat requiredFormat_3byte(3, 8, 8, 8, 0, 16, 8, 0, 0);
00266     const Graphics::PixelFormat requiredFormat_4byte(4, 8, 8, 8, 8, 24, 16, 8, 0);
00267 #endif
00268 
00269     int colorType;
00270     Graphics::Surface *tmp = NULL;
00271     const Graphics::Surface *surface;
00272 
00273     if (input.format == requiredFormat_3byte) {
00274         surface = &input;
00275         colorType = PNG_COLOR_TYPE_RGB;
00276     } else {
00277         if (input.format == requiredFormat_4byte) {
00278             surface = &input;
00279         } else {
00280             surface = tmp = input.convertTo(requiredFormat_4byte);
00281         }
00282         colorType = PNG_COLOR_TYPE_RGB_ALPHA;
00283     }
00284 
00285     png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00286     if (!pngPtr) {
00287         if (tmp) {
00288             tmp->free();
00289             delete tmp;
00290         }
00291         return false;
00292     }
00293     png_infop infoPtr = png_create_info_struct(pngPtr);
00294     if (!infoPtr) {
00295         png_destroy_write_struct(&pngPtr, NULL);
00296         if (tmp) {
00297             tmp->free();
00298             delete tmp;
00299         }
00300         return false;
00301     }
00302 
00303     png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
00304     // TODO: The manual says errors should be handled via setjmp
00305 
00306     png_set_write_fn(pngPtr, &out, pngWriteToStream, pngFlushStream);
00307 
00308     png_set_IHDR(pngPtr, infoPtr, surface->w, surface->h, 8, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00309 
00310     Common::Array<const uint8 *> rows;
00311     rows.reserve(surface->h);
00312 // ResidualVM specific
00313     if (bottomUp) {
00314         for (uint y = surface->h; y-- > 0;) {
00315             rows.push_back((const uint8 *)surface->getBasePtr(0, y));
00316         }
00317     } else {
00318         for (uint y = 0; y < surface->h; ++y) {
00319             rows.push_back((const uint8 *)surface->getBasePtr(0, y));
00320         }
00321     }
00322 
00323     png_set_rows(pngPtr, infoPtr, const_cast<uint8 **>(&rows.front()));
00324     png_write_png(pngPtr, infoPtr, 0, NULL);
00325     png_destroy_write_struct(&pngPtr, &infoPtr);
00326 
00327     // free tmp surface
00328     if (tmp) {
00329         tmp->free();
00330         delete tmp;
00331     }
00332 
00333     return true;
00334 #else
00335     return false;
00336 #endif
00337 }
00338 
00339 } // End of namespace Image


Generated on Sat Aug 8 2020 05:01:27 for ResidualVM by doxygen 1.7.1
curved edge   curved edge