/indra/llimage/llpngwrapper.cpp
C++ | 380 lines | 258 code | 47 blank | 75 comment | 34 complexity | 20ba0ad23f897a0ed303cd1b08618ced MD5 | raw file
Possible License(s): LGPL-2.1
1/* 2 * @file llpngwrapper.cpp 3 * @brief Encapsulates libpng read/write functionality. 4 * 5 * $LicenseInfo:firstyear=2007&license=viewerlgpl$ 6 * Second Life Viewer Source Code 7 * Copyright (C) 2010, Linden Research, Inc. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; 12 * version 2.1 of the License only. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA 24 * $/LicenseInfo$ 25 */ 26 27#include "linden_common.h" 28#include "stdtypes.h" 29#include "llerror.h" 30 31#include "llimage.h" 32#include "llpngwrapper.h" 33 34// --------------------------------------------------------------------------- 35// LLPngWrapper 36// --------------------------------------------------------------------------- 37 38LLPngWrapper::LLPngWrapper() 39 : mReadPngPtr( NULL ), 40 mReadInfoPtr( NULL ), 41 mWritePngPtr( NULL ), 42 mWriteInfoPtr( NULL ), 43 mRowPointers( NULL ), 44 mWidth( 0 ), 45 mHeight( 0 ), 46 mBitDepth( 0 ), 47 mColorType( 0 ), 48 mChannels( 0 ), 49 mInterlaceType( 0 ), 50 mCompressionType( 0 ), 51 mFilterMethod( 0 ), 52 mFinalSize( 0 ), 53 mGamma(0.f) 54{ 55} 56 57LLPngWrapper::~LLPngWrapper() 58{ 59 releaseResources(); 60} 61 62// Checks the src for a valid PNG header 63BOOL LLPngWrapper::isValidPng(U8* src) 64{ 65 const int PNG_BYTES_TO_CHECK = 8; 66 67 int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK); 68 if (sig != 0) 69 { 70 mErrorMessage = "Invalid or corrupt PNG file"; 71 return FALSE; 72 } 73 74 return TRUE; 75} 76 77// Called by the libpng library when a fatal encoding or decoding error 78// occurs. We simply throw the error message and let our try/catch 79// block clean up. 80void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg) 81{ 82 throw msg; 83} 84 85// Called by the libpng library when reading (decoding) the PNG file. We 86// copy the PNG data from our internal buffer into the PNG's data buffer. 87void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length) 88{ 89 PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); 90 U8 *src = &dataInfo->mData[dataInfo->mOffset]; 91 memcpy(dest, src, length); 92 dataInfo->mOffset += static_cast<U32>(length); 93} 94 95// Called by the libpng library when writing (encoding) the PNG file. We 96// copy the encoded result into our data buffer. 97void LLPngWrapper::writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length) 98{ 99 PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); 100 U8 *dest = &dataInfo->mData[dataInfo->mOffset]; 101 memcpy(dest, src, length); 102 dataInfo->mOffset += static_cast<U32>(length); 103} 104 105// Flush the write output pointer 106void LLPngWrapper::writeFlush(png_structp png_ptr) 107{ 108 // no-op since we're just writing to memory 109} 110 111// Read the PNG file using the libpng. The low-level interface is used here 112// because we want to do various transformations (including applying gama) 113// which can't be done with the high-level interface. 114// The scanline also begins at the bottom of 115// the image (per SecondLife conventions) instead of at the top, so we 116// must assign row-pointers in "reverse" order. 117BOOL LLPngWrapper::readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop) 118{ 119 try 120 { 121 // Create and initialize the png structures 122 mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 123 this, &errorHandler, NULL); 124 if (mReadPngPtr == NULL) 125 { 126 throw "Problem creating png read structure"; 127 } 128 129 // Allocate/initialize the memory for image information. 130 mReadInfoPtr = png_create_info_struct(mReadPngPtr); 131 132 // Set up the input control 133 PngDataInfo dataPtr; 134 dataPtr.mData = src; 135 dataPtr.mOffset = 0; 136 137 png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback); 138 png_set_sig_bytes(mReadPngPtr, 0); 139 140 // setup low-level read and get header information 141 png_read_info(mReadPngPtr, mReadInfoPtr); 142 png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight, 143 &mBitDepth, &mColorType, &mInterlaceType, 144 &mCompressionType, &mFilterMethod); 145 146 // Normalize the image, then get updated image information 147 // after transformations have been applied 148 normalizeImage(); 149 updateMetaData(); 150 151 // If a raw object is supplied, read the PNG image into its 152 // data space 153 if (rawImage != NULL) 154 { 155 rawImage->resize(static_cast<U16>(mWidth), 156 static_cast<U16>(mHeight), mChannels); 157 U8 *dest = rawImage->getData(); 158 int offset = mWidth * mChannels; 159 160 // Set up the row pointers and read the image 161 mRowPointers = new U8* [mHeight]; 162 for (U32 i=0; i < mHeight; i++) 163 { 164 mRowPointers[i] = &dest[(mHeight-i-1)*offset]; 165 } 166 167 png_read_image(mReadPngPtr, mRowPointers); 168 169 // Finish up, ensures all metadata are updated 170 png_read_end(mReadPngPtr, NULL); 171 } 172 173 // If an info object is supplied, copy the relevant info 174 if (infop != NULL) 175 { 176 infop->mHeight = static_cast<U16>(mHeight); 177 infop->mWidth = static_cast<U16>(mWidth); 178 infop->mComponents = mChannels; 179 } 180 181 mFinalSize = dataPtr.mOffset; 182 } 183 catch (png_const_charp msg) 184 { 185 mErrorMessage = msg; 186 releaseResources(); 187 return (FALSE); 188 } 189 190 // Clean up and return 191 releaseResources(); 192 return (TRUE); 193} 194 195// Do transformations to normalize the input to 8-bpp RGBA 196void LLPngWrapper::normalizeImage() 197{ 198 // 1. Expand any palettes 199 // 2. Convert grayscales to RGB 200 // 3. Create alpha layer from transparency 201 // 4. Ensure 8-bpp for all images 202 // 5. Set (or guess) gamma 203 204 if (mColorType == PNG_COLOR_TYPE_PALETTE) 205 { 206 png_set_palette_to_rgb(mReadPngPtr); 207 } 208 if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8) 209 { 210 png_set_expand_gray_1_2_4_to_8(mReadPngPtr); 211 } 212 if (mColorType == PNG_COLOR_TYPE_GRAY 213 || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA) 214 { 215 png_set_gray_to_rgb(mReadPngPtr); 216 } 217 if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS)) 218 { 219 png_set_tRNS_to_alpha(mReadPngPtr); 220 } 221 if (mBitDepth < 8) 222 { 223 png_set_packing(mReadPngPtr); 224 } 225 else if (mBitDepth == 16) 226 { 227 png_set_strip_16(mReadPngPtr); 228 } 229 230#if LL_DARWIN 231 const F64 SCREEN_GAMMA = 1.8; 232#else 233 const F64 SCREEN_GAMMA = 2.2; 234#endif 235 236 if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma)) 237 { 238 png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma); 239 } 240 else 241 { 242 png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA); 243 } 244} 245 246// Read out the image meta-data 247void LLPngWrapper::updateMetaData() 248{ 249 png_read_update_info(mReadPngPtr, mReadInfoPtr); 250 mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr); 251 mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr); 252 mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr); 253 mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr); 254 mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr); 255} 256 257// Method to write raw image into PNG at dest. The raw scanline begins 258// at the bottom of the image per SecondLife conventions. 259BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest) 260{ 261 try 262 { 263 S8 numComponents = rawImage->getComponents(); 264 switch (numComponents) 265 { 266 case 1: 267 mColorType = PNG_COLOR_TYPE_GRAY; 268 break; 269 case 2: 270 mColorType = PNG_COLOR_TYPE_GRAY_ALPHA; 271 break; 272 case 3: 273 mColorType = PNG_COLOR_TYPE_RGB; 274 break; 275 case 4: 276 mColorType = PNG_COLOR_TYPE_RGB_ALPHA; 277 break; 278 default: 279 mColorType = -1; 280 } 281 282 if (mColorType == -1) 283 { 284 throw "Unsupported image: unexpected number of channels"; 285 } 286 287 mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 288 NULL, &errorHandler, NULL); 289 if (!mWritePngPtr) 290 { 291 throw "Problem creating png write structure"; 292 } 293 294 mWriteInfoPtr = png_create_info_struct(mWritePngPtr); 295 296 // Setup write function 297 PngDataInfo dataPtr; 298 dataPtr.mData = dest; 299 dataPtr.mOffset = 0; 300 png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush); 301 302 // Setup image params 303 mWidth = rawImage->getWidth(); 304 mHeight = rawImage->getHeight(); 305 mBitDepth = 8; // Fixed to 8-bpp in SL 306 mChannels = numComponents; 307 mInterlaceType = PNG_INTERLACE_NONE; 308 mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT; 309 mFilterMethod = PNG_FILTER_TYPE_DEFAULT; 310 311 // Write header 312 png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight, 313 mBitDepth, mColorType, mInterlaceType, 314 mCompressionType, mFilterMethod); 315 316 // Get data and compute row size 317 const U8* data = rawImage->getData(); 318 int offset = mWidth * mChannels; 319 320 // Ready to write, start with the header 321 png_write_info(mWritePngPtr, mWriteInfoPtr); 322 323 // Write image (sorry, must const-cast for libpng) 324 const U8 * rowPointer; 325 for (U32 i=0; i < mHeight; i++) 326 { 327 rowPointer = &data[(mHeight-1-i)*offset]; 328 png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer)); 329 } 330 331 // Finish up 332 png_write_end(mWritePngPtr, mWriteInfoPtr); 333 mFinalSize = dataPtr.mOffset; 334 } 335 catch (png_const_charp msg) 336 { 337 mErrorMessage = msg; 338 releaseResources(); 339 return (FALSE); 340 } 341 342 releaseResources(); 343 return TRUE; 344} 345 346// Cleanup various internal structures 347void LLPngWrapper::releaseResources() 348{ 349 if (mReadPngPtr || mReadInfoPtr) 350 { 351 png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, NULL); 352 mReadPngPtr = NULL; 353 mReadInfoPtr = NULL; 354 } 355 356 if (mWritePngPtr || mWriteInfoPtr) 357 { 358 png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr); 359 mWritePngPtr = NULL; 360 mWriteInfoPtr = NULL; 361 } 362 363 if (mRowPointers) 364 { 365 delete[] mRowPointers; 366 mRowPointers = NULL; 367 } 368} 369 370// Get final image size after compression 371U32 LLPngWrapper::getFinalSize() 372{ 373 return mFinalSize; 374} 375 376// Get last error message, if any 377const std::string& LLPngWrapper::getErrorMessage() 378{ 379 return mErrorMessage; 380}