llpngwrapper.cpp

Go to the documentation of this file.
00001 /*
00002  * @file llpngwrapper.cpp
00003  * @brief Encapsulates libpng read/write functionality.
00004  *
00005  * $LicenseInfo:firstyear=2007&license=viewergpl$
00006  * 
00007  * Copyright (c) 2007-2008, Linden Research, Inc.
00008  * 
00009  * Second Life Viewer Source Code
00010  * The source code in this file ("Source Code") is provided by Linden Lab
00011  * to you under the terms of the GNU General Public License, version 2.0
00012  * ("GPL"), unless you have obtained a separate licensing agreement
00013  * ("Other License"), formally executed by you and Linden Lab.  Terms of
00014  * the GPL can be found in doc/GPL-license.txt in this distribution, or
00015  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
00016  * 
00017  * There are special exceptions to the terms and conditions of the GPL as
00018  * it is applied to this Source Code. View the full text of the exception
00019  * in the file doc/FLOSS-exception.txt in this software distribution, or
00020  * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
00021  * 
00022  * By copying, modifying or distributing this software, you acknowledge
00023  * that you have read and understood your obligations described above,
00024  * and agree to abide by those obligations.
00025  * 
00026  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
00027  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
00028  * COMPLETENESS OR PERFORMANCE.
00029  * $/LicenseInfo$
00030  */
00031 
00032 #include "linden_common.h"
00033 #include "stdtypes.h"
00034 #include "llerror.h"
00035 
00036 #include "llimage.h"
00037 #include "llpngwrapper.h"
00038 
00039 // ---------------------------------------------------------------------------
00040 // LLPngWrapper
00041 // ---------------------------------------------------------------------------
00042 
00043 LLPngWrapper::LLPngWrapper()
00044         : mReadPngPtr( NULL ),
00045       mReadInfoPtr( NULL ),
00046           mWritePngPtr( NULL ),
00047           mWriteInfoPtr( NULL ),
00048           mRowPointers( NULL ),
00049           mBitDepth( 0 ),
00050           mColorType( 0 ),
00051           mChannels( 0 ),
00052           mInterlaceType( 0 ),
00053           mCompressionType( 0 ),
00054           mFilterMethod( 0 ),
00055           mFinalSize( 0 )
00056 {
00057 }
00058 
00059 LLPngWrapper::~LLPngWrapper()
00060 {
00061         releaseResources();
00062 }
00063 
00064 // Checks the src for a valid PNG header
00065 BOOL LLPngWrapper::isValidPng(U8* src)
00066 {
00067         const int PNG_BYTES_TO_CHECK = 8;
00068 
00069         int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK);
00070         if (sig != 0)
00071         {
00072                 mErrorMessage = "Invalid or corrupt PNG file";
00073                 return FALSE;
00074         }
00075 
00076         return TRUE;
00077 }
00078 
00079 // Called by the libpng library when a fatal encoding or decoding error
00080 // occurs.  We simply throw the error message and let our try/catch
00081 // block clean up.
00082 void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg)
00083 {
00084         throw msg;
00085 }
00086 
00087 // Called by the libpng library when reading (decoding) the PNG file. We
00088 // copy the PNG data from our internal buffer into the PNG's data buffer.
00089 void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length)
00090 {
00091         PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
00092         U8 *src = &dataInfo->mData[dataInfo->mOffset];
00093         memcpy(dest, src, length);
00094         dataInfo->mOffset += static_cast<U32>(length);
00095 }
00096 
00097 // Called by the libpng library when writing (encoding) the PNG file. We
00098 // copy the encoded result into our data buffer.
00099 void LLPngWrapper::writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length)
00100 {
00101         PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
00102         U8 *dest = &dataInfo->mData[dataInfo->mOffset];
00103         memcpy(dest, src, length);
00104         dataInfo->mOffset += static_cast<U32>(length);
00105 }
00106 
00107 // Flush the write output pointer
00108 void LLPngWrapper::writeFlush(png_structp png_ptr)
00109 {
00110         // no-op since we're just writing to memory
00111 }
00112 
00113 // Read the PNG file using the libpng.  The low-level interface is used here
00114 // because we want to do various transformations (including setting the
00115 // matte background if any, and applying gama) which can't be done with
00116 // the high-level interface. The scanline also begins at the bottom of
00117 // the image (per SecondLife conventions) instead of at the top, so we
00118 // must assign row-pointers in "reverse" order.
00119 BOOL LLPngWrapper::readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop)
00120 {
00121         try
00122         {
00123                 // Create and initialize the png structures
00124                 mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00125                         this, &errorHandler, NULL);
00126                 if (mReadPngPtr == NULL)
00127                 {
00128                         throw "Problem creating png read structure";
00129                 }
00130 
00131                 // Allocate/initialize the memory for image information.
00132                 mReadInfoPtr = png_create_info_struct(mReadPngPtr);
00133 
00134                 // Set up the input control
00135                 PngDataInfo dataPtr;
00136                 dataPtr.mData = src;
00137                 dataPtr.mOffset = 0;
00138 
00139                 png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback);
00140                 png_set_sig_bytes(mReadPngPtr, 0);
00141 
00142                 // setup low-level read and get header information
00143                 png_read_info(mReadPngPtr, mReadInfoPtr);
00144                 png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight,
00145                         &mBitDepth, &mColorType, &mInterlaceType,
00146                         &mCompressionType, &mFilterMethod);
00147 
00148                 // Normalize the image, then get updated image information
00149                 // after transformations have been applied
00150                 normalizeImage();
00151                 updateMetaData();
00152 
00153                 // If a raw object is supplied, read the PNG image into its
00154                 // data space
00155                 if (rawImage != NULL)
00156                 {
00157                         rawImage->resize(static_cast<U16>(mWidth),
00158                                 static_cast<U16>(mHeight), mChannels);
00159                         U8 *dest = rawImage->getData();
00160                         int offset = mWidth * mChannels;
00161 
00162                         // Set up the row pointers and read the image
00163                         mRowPointers = new U8* [mHeight];
00164                         for (U32 i=0; i < mHeight; i++)
00165                         {
00166                                 mRowPointers[i] = &dest[(mHeight-i-1)*offset];
00167                         }
00168 
00169                         png_read_image(mReadPngPtr, mRowPointers);
00170 
00171                         // Finish up, ensures all metadata are updated
00172                         png_read_end(mReadPngPtr, NULL);
00173                 }
00174 
00175                 // If an info object is supplied, copy the relevant info
00176                 if (infop != NULL)
00177                 {
00178                         infop->mHeight = static_cast<U16>(mHeight);
00179                         infop->mWidth = static_cast<U16>(mWidth);
00180                         infop->mComponents = mChannels;
00181                 }
00182 
00183                 mFinalSize = dataPtr.mOffset;
00184         }
00185         catch (png_const_charp msg)
00186         {
00187                 mErrorMessage = msg;
00188                 releaseResources();
00189                 return (FALSE);
00190         }
00191 
00192         // Clean up and return
00193         releaseResources();
00194         return (TRUE);
00195 }
00196 
00197 // Do transformations to normalize the input to 8-bpp RGBA
00198 void LLPngWrapper::normalizeImage()
00199 {
00200         //              1. Expand any palettes
00201         //              2. Convert grayscales to RGB
00202         //              3. Create alpha layer from transparency
00203         //              4. Ensure 8-bpp for all images
00204         //              5. Apply background matte if any
00205         //              6. Set (or guess) gamma
00206 
00207         if (mColorType == PNG_COLOR_TYPE_PALETTE)
00208         {
00209                 png_set_palette_to_rgb(mReadPngPtr);
00210         }
00211     if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8)
00212         {
00213                 png_set_gray_1_2_4_to_8(mReadPngPtr);
00214         }
00215         if (mColorType == PNG_COLOR_TYPE_GRAY
00216                 || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
00217         {
00218                 png_set_gray_to_rgb(mReadPngPtr);
00219         }
00220         if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS))
00221         {
00222                 png_set_tRNS_to_alpha(mReadPngPtr);
00223         }
00224         if (mBitDepth < 8)
00225         {
00226                 png_set_packing(mReadPngPtr);
00227         }
00228         else if (mBitDepth == 16)
00229         {
00230                 png_set_strip_16(mReadPngPtr);
00231         }
00232         mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor);
00233         if (mHasBKGD)
00234         {
00235                 png_set_background(mReadPngPtr, mBackgroundColor,
00236                         PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00237         }
00238 
00239 #if LL_DARWIN
00240         const F64 SCREEN_GAMMA = 1.8;
00241 #else
00242         const F64 SCREEN_GAMMA = 2.2;
00243 #endif
00244 
00245         if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma))
00246         {
00247                 png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma);
00248         }
00249         else
00250         {
00251                 png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA);
00252         }
00253 }
00254 
00255 // Read out the image meta-data
00256 void LLPngWrapper::updateMetaData()
00257 {
00258         png_read_update_info(mReadPngPtr, mReadInfoPtr);
00259     mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr);
00260     mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr);
00261     mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr);
00262     mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr);
00263         mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr);
00264         mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor);
00265 }
00266 
00267 // Method to write raw image into PNG at dest. The raw scanline begins
00268 // at the bottom of the image per SecondLife conventions.
00269 BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
00270 {
00271         try
00272         {
00273                 S8 numComponents = rawImage->getComponents();
00274                 switch (numComponents)
00275                 {
00276                 case 1:
00277                         mColorType = PNG_COLOR_TYPE_GRAY;
00278                         break;
00279                 case 2:
00280                         mColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
00281                         break;
00282                 case 3:
00283                         mColorType = PNG_COLOR_TYPE_RGB;
00284                         break;
00285                 case 4:
00286                         mColorType = PNG_COLOR_TYPE_RGB_ALPHA;
00287                         break;
00288                 default:
00289                         mColorType = -1;
00290                 }
00291 
00292                 if (mColorType == -1)
00293                 {
00294                         throw "Unsupported image: unexpected number of channels";
00295                 }
00296 
00297                 mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
00298                         NULL, &errorHandler, NULL);
00299                 if (!mWritePngPtr)
00300                 {
00301                         throw "Problem creating png write structure";
00302                 }
00303 
00304                 mWriteInfoPtr = png_create_info_struct(mWritePngPtr);
00305 
00306                 // Setup write function
00307                 PngDataInfo dataPtr;
00308                 dataPtr.mData = dest;
00309                 dataPtr.mOffset = 0;
00310                 png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush);
00311 
00312                 // Setup image params
00313                 mWidth = rawImage->getWidth();
00314                 mHeight = rawImage->getHeight();
00315                 mBitDepth = 8;  // Fixed to 8-bpp in SL
00316                 mChannels = numComponents;
00317                 mInterlaceType = PNG_INTERLACE_NONE;
00318                 mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT;
00319                 mFilterMethod = PNG_FILTER_TYPE_DEFAULT;
00320 
00321                 // Write header
00322                 png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight,
00323                         mBitDepth, mColorType, mInterlaceType,
00324                         mCompressionType, mFilterMethod);
00325 
00326                 // Get data and compute row size
00327                 const U8* data = rawImage->getData();
00328                 int offset = mWidth * mChannels;
00329 
00330                 // Ready to write, start with the header
00331                 png_write_info(mWritePngPtr, mWriteInfoPtr);
00332 
00333                 // Write image (sorry, must const-cast for libpng)
00334                 const U8 * rowPointer;
00335                 for (U32 i=0; i < mHeight; i++)
00336                 {
00337                         rowPointer = &data[(mHeight-1-i)*offset];
00338                         png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer));
00339                 }
00340 
00341                 // Finish up
00342                 png_write_end(mWritePngPtr, mWriteInfoPtr);
00343                 mFinalSize = dataPtr.mOffset;
00344         }
00345         catch (png_const_charp msg)
00346         {
00347                 mErrorMessage = msg;
00348                 releaseResources();
00349                 return (FALSE);
00350         }
00351 
00352         releaseResources();
00353         return TRUE;
00354 }
00355 
00356 // Cleanup various internal structures
00357 void LLPngWrapper::releaseResources()
00358 {
00359         if (mReadPngPtr || mReadInfoPtr)
00360         {
00361                 png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, png_infopp_NULL);
00362                 mReadPngPtr = NULL;
00363                 mReadInfoPtr = NULL;
00364         }
00365 
00366         if (mWritePngPtr || mWriteInfoPtr)
00367         {
00368                 png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr);
00369                 mWritePngPtr = NULL;
00370                 mWriteInfoPtr = NULL;
00371         }
00372 
00373         if (mRowPointers)
00374         {
00375                 delete[] mRowPointers;
00376                 mRowPointers = NULL;
00377         }
00378 }
00379 
00380 // Get final image size after compression
00381 U32 LLPngWrapper::getFinalSize()
00382 {
00383         return mFinalSize;
00384 }
00385 
00386 // Get last error message, if any
00387 LLString LLPngWrapper::getErrorMessage()
00388 {
00389         return mErrorMessage;
00390 }

Generated on Fri May 16 08:32:11 2008 for SecondLife by  doxygen 1.5.5