diff options
Diffstat (limited to 'indra/llimage/llpngwrapper.cpp')
-rw-r--r-- | indra/llimage/llpngwrapper.cpp | 571 |
1 files changed, 289 insertions, 282 deletions
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index baf1330011..a5fb7a3167 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library 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 * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -46,86 +46,86 @@ struct PngError: public LLContinueError // --------------------------------------------------------------------------- LLPngWrapper::LLPngWrapper() - : mReadPngPtr( NULL ), - mReadInfoPtr( NULL ), - mWritePngPtr( NULL ), - mWriteInfoPtr( NULL ), - mRowPointers( NULL ), - mWidth( 0 ), - mHeight( 0 ), - mBitDepth( 0 ), - mColorType( 0 ), - mChannels( 0 ), - mInterlaceType( 0 ), - mCompressionType( 0 ), - mFilterMethod( 0 ), - mFinalSize( 0 ), - mGamma(0.f) + : mReadPngPtr( NULL ), + mReadInfoPtr( NULL ), + mWritePngPtr( NULL ), + mWriteInfoPtr( NULL ), + mRowPointers( NULL ), + mWidth( 0 ), + mHeight( 0 ), + mBitDepth( 0 ), + mColorType( 0 ), + mChannels( 0 ), + mInterlaceType( 0 ), + mCompressionType( 0 ), + mFilterMethod( 0 ), + mFinalSize( 0 ), + mGamma(0.f) { } LLPngWrapper::~LLPngWrapper() { - releaseResources(); + releaseResources(); } // Checks the src for a valid PNG header bool LLPngWrapper::isValidPng(U8* src) { - const int PNG_BYTES_TO_CHECK = 8; + const int PNG_BYTES_TO_CHECK = 8; - int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK); - if (sig != 0) - { - mErrorMessage = "Invalid or corrupt PNG file"; - return false; - } + int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK); + if (sig != 0) + { + mErrorMessage = "Invalid or corrupt PNG file"; + return false; + } - return true; + return true; } // Called by the libpng library when a fatal encoding or decoding error // occurs. We throw PngError and let our try/catch block clean up. void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg) { - LLTHROW(PngError(msg)); + LLTHROW(PngError(msg)); } // Called by the libpng library when reading (decoding) the PNG file. We // copy the PNG data from our internal buffer into the PNG's data buffer. void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length) { - PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); - if(dataInfo->mOffset + length > dataInfo->mDataSize) - { - png_error(png_ptr, "Data read error. Requested data size exceeds available data size."); - return; - } - - U8 *src = &dataInfo->mData[dataInfo->mOffset]; - memcpy(dest, src, length); - dataInfo->mOffset += static_cast<U32>(length); + PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); + if(dataInfo->mOffset + length > dataInfo->mDataSize) + { + png_error(png_ptr, "Data read error. Requested data size exceeds available data size."); + return; + } + + U8 *src = &dataInfo->mData[dataInfo->mOffset]; + memcpy(dest, src, length); + dataInfo->mOffset += static_cast<U32>(length); } // Called by the libpng library when writing (encoding) the PNG file. We // copy the encoded result into our data buffer. void LLPngWrapper::writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length) { - PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); - if (dataInfo->mOffset + length > dataInfo->mDataSize) - { - png_error(png_ptr, "Data write error. Requested data size exceeds available data size."); - return; - } - U8 *dest = &dataInfo->mData[dataInfo->mOffset]; - memcpy(dest, src, length); - dataInfo->mOffset += static_cast<U32>(length); + PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr); + if (dataInfo->mOffset + length > dataInfo->mDataSize) + { + png_error(png_ptr, "Data write error. Requested data size exceeds available data size."); + return; + } + U8 *dest = &dataInfo->mData[dataInfo->mOffset]; + memcpy(dest, src, length); + dataInfo->mOffset += static_cast<U32>(length); } // Flush the write output pointer void LLPngWrapper::writeFlush(png_structp png_ptr) { - // no-op since we're just writing to memory + // no-op since we're just writing to memory } // Read the PNG file using the libpng. The low-level interface is used here @@ -136,273 +136,280 @@ void LLPngWrapper::writeFlush(png_structp png_ptr) // must assign row-pointers in "reverse" order. bool LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInfo *infop) { - try - { - // Create and initialize the png structures - mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - this, &errorHandler, NULL); - if (mReadPngPtr == NULL) - { - LLTHROW(PngError("Problem creating png read structure")); - } - - // Allocate/initialize the memory for image information. - mReadInfoPtr = png_create_info_struct(mReadPngPtr); - - // Set up the input control - PngDataInfo dataPtr; - dataPtr.mData = src; - dataPtr.mOffset = 0; - dataPtr.mDataSize = dataSize; - - png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback); - png_set_sig_bytes(mReadPngPtr, 0); - - // setup low-level read and get header information - png_read_info(mReadPngPtr, mReadInfoPtr); - png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight, - &mBitDepth, &mColorType, &mInterlaceType, - &mCompressionType, &mFilterMethod); - - // Normalize the image, then get updated image information - // after transformations have been applied - normalizeImage(); - updateMetaData(); - - // If a raw object is supplied, read the PNG image into its - // data space - if (rawImage != NULL) - { - LLImageDataLock lock(rawImage); - - if (!rawImage->resize(static_cast<U16>(mWidth), - static_cast<U16>(mHeight), mChannels)) - { - LLTHROW(PngError("Failed to resize image")); - } - U8 *dest = rawImage->getData(); - int offset = mWidth * mChannels; - - // Set up the row pointers and read the image - mRowPointers = new U8* [mHeight]; - for (U32 i=0; i < mHeight; i++) - { - mRowPointers[i] = &dest[(mHeight-i-1)*offset]; - } - - png_read_image(mReadPngPtr, mRowPointers); - - // Finish up, ensures all metadata are updated - png_read_end(mReadPngPtr, NULL); - } - - // If an info object is supplied, copy the relevant info - if (infop != NULL) - { - infop->mHeight = static_cast<U16>(mHeight); - infop->mWidth = static_cast<U16>(mWidth); - infop->mComponents = mChannels; - } - - mFinalSize = dataPtr.mOffset; - } - catch (const PngError& msg) - { - mErrorMessage = msg.what(); - releaseResources(); - return (false); - } - catch (std::bad_alloc&) - { - mErrorMessage = "LLPngWrapper"; - releaseResources(); - return (false); - } - - // Clean up and return - releaseResources(); - return (true); + try + { + // Create and initialize the png structures + mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + this, &errorHandler, NULL); + if (mReadPngPtr == NULL) + { + LLTHROW(PngError("Problem creating png read structure")); + } + + // Allocate/initialize the memory for image information. + mReadInfoPtr = png_create_info_struct(mReadPngPtr); + + // Set up the input control + PngDataInfo dataPtr; + dataPtr.mData = src; + dataPtr.mOffset = 0; + dataPtr.mDataSize = dataSize; + + png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback); + png_set_sig_bytes(mReadPngPtr, 0); + + // setup low-level read and get header information + png_read_info(mReadPngPtr, mReadInfoPtr); + png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight, + &mBitDepth, &mColorType, &mInterlaceType, + &mCompressionType, &mFilterMethod); + + // Normalize the image, then get updated image information + // after transformations have been applied + normalizeImage(); + updateMetaData(); + + // If a raw object is supplied, read the PNG image into its + // data space + if (rawImage != NULL) + { + LLImageDataLock lock(rawImage); + + if (!rawImage->resize(static_cast<U16>(mWidth), + static_cast<U16>(mHeight), mChannels)) + { + LLTHROW(PngError("Failed to resize image")); + } + U8 *dest = rawImage->getData(); + int offset = mWidth * mChannels; + + // Set up the row pointers and read the image + mRowPointers = new U8* [mHeight]; + for (U32 i=0; i < mHeight; i++) + { + mRowPointers[i] = &dest[(mHeight-i-1)*offset]; + } + + png_read_image(mReadPngPtr, mRowPointers); + + // Finish up, ensures all metadata are updated + png_read_end(mReadPngPtr, NULL); + } + + // If an info object is supplied, copy the relevant info + if (infop != NULL) + { + infop->mHeight = static_cast<U16>(mHeight); + infop->mWidth = static_cast<U16>(mWidth); + infop->mComponents = mChannels; + } + + mFinalSize = dataPtr.mOffset; + } + catch (const PngError& msg) + { + mErrorMessage = msg.what(); + releaseResources(); + return (false); + } + catch (std::bad_alloc&) + { + mErrorMessage = "LLPngWrapper"; + releaseResources(); + return (false); + } + catch (...) + { + mErrorMessage = "LLPngWrapper"; + releaseResources(); + LOG_UNHANDLED_EXCEPTION(""); + return (false); + } + + // Clean up and return + releaseResources(); + return (true); } // Do transformations to normalize the input to 8-bpp RGBA void LLPngWrapper::normalizeImage() { - // 1. Expand any palettes - // 2. Convert grayscales to RGB - // 3. Create alpha layer from transparency - // 4. Ensure 8-bpp for all images - // 5. Set (or guess) gamma - - if (mColorType == PNG_COLOR_TYPE_PALETTE) - { - png_set_palette_to_rgb(mReadPngPtr); - } + // 1. Expand any palettes + // 2. Convert grayscales to RGB + // 3. Create alpha layer from transparency + // 4. Ensure 8-bpp for all images + // 5. Set (or guess) gamma + + if (mColorType == PNG_COLOR_TYPE_PALETTE) + { + png_set_palette_to_rgb(mReadPngPtr); + } if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8) - { - png_set_expand_gray_1_2_4_to_8(mReadPngPtr); - } - if (mColorType == PNG_COLOR_TYPE_GRAY - || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA) - { - png_set_gray_to_rgb(mReadPngPtr); - } - if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS)) - { - png_set_tRNS_to_alpha(mReadPngPtr); - } - if (mBitDepth < 8) - { - png_set_packing(mReadPngPtr); - } - else if (mBitDepth == 16) - { - png_set_strip_16(mReadPngPtr); - } - - const F64 SCREEN_GAMMA = 2.2; - if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma)) - { - png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma); - } - else - { - png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA); - } + { + png_set_expand_gray_1_2_4_to_8(mReadPngPtr); + } + if (mColorType == PNG_COLOR_TYPE_GRAY + || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(mReadPngPtr); + } + if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS)) + { + png_set_tRNS_to_alpha(mReadPngPtr); + } + if (mBitDepth < 8) + { + png_set_packing(mReadPngPtr); + } + else if (mBitDepth == 16) + { + png_set_strip_16(mReadPngPtr); + } + + const F64 SCREEN_GAMMA = 2.2; + if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma)) + { + png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma); + } + else + { + png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA); + } } // Read out the image meta-data void LLPngWrapper::updateMetaData() { - png_read_update_info(mReadPngPtr, mReadInfoPtr); + png_read_update_info(mReadPngPtr, mReadInfoPtr); mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr); mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr); mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr); mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr); - mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr); + mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr); } // Method to write raw image into PNG at dest. The raw scanline begins // at the bottom of the image per SecondLife conventions. bool LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest, size_t destSize) { - try - { - S8 numComponents = rawImage->getComponents(); - switch (numComponents) - { - case 1: - mColorType = PNG_COLOR_TYPE_GRAY; - break; - case 2: - mColorType = PNG_COLOR_TYPE_GRAY_ALPHA; - break; - case 3: - mColorType = PNG_COLOR_TYPE_RGB; - break; - case 4: - mColorType = PNG_COLOR_TYPE_RGB_ALPHA; - break; - default: - mColorType = -1; - } - - if (mColorType == -1) - { - LLTHROW(PngError("Unsupported image: unexpected number of channels")); - } - - mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, - NULL, &errorHandler, NULL); - if (!mWritePngPtr) - { - LLTHROW(PngError("Problem creating png write structure")); - } - - mWriteInfoPtr = png_create_info_struct(mWritePngPtr); - - // Setup write function - PngDataInfo dataPtr; - dataPtr.mData = dest; - dataPtr.mOffset = 0; - dataPtr.mDataSize = destSize; - png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush); - - // Setup image params - mWidth = rawImage->getWidth(); - mHeight = rawImage->getHeight(); - mBitDepth = 8; // Fixed to 8-bpp in SL - mChannels = numComponents; - mInterlaceType = PNG_INTERLACE_NONE; - mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT; - mFilterMethod = PNG_FILTER_TYPE_DEFAULT; - - // Write header - png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight, - mBitDepth, mColorType, mInterlaceType, - mCompressionType, mFilterMethod); - - // Get data and compute row size - const U8* data = rawImage->getData(); - int offset = mWidth * mChannels; - - // Ready to write, start with the header - png_write_info(mWritePngPtr, mWriteInfoPtr); - - // Write image (sorry, must const-cast for libpng) - const U8 * rowPointer; - for (U32 i=0; i < mHeight; i++) - { - rowPointer = &data[(mHeight-1-i)*offset]; - png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer)); - } - - // Finish up - png_write_end(mWritePngPtr, mWriteInfoPtr); - mFinalSize = dataPtr.mOffset; - } - catch (const PngError& msg) - { - mErrorMessage = msg.what(); - releaseResources(); - return (false); - } - - releaseResources(); - return true; + try + { + S8 numComponents = rawImage->getComponents(); + switch (numComponents) + { + case 1: + mColorType = PNG_COLOR_TYPE_GRAY; + break; + case 2: + mColorType = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + case 3: + mColorType = PNG_COLOR_TYPE_RGB; + break; + case 4: + mColorType = PNG_COLOR_TYPE_RGB_ALPHA; + break; + default: + mColorType = -1; + } + + if (mColorType == -1) + { + LLTHROW(PngError("Unsupported image: unexpected number of channels")); + } + + mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, &errorHandler, NULL); + if (!mWritePngPtr) + { + LLTHROW(PngError("Problem creating png write structure")); + } + + mWriteInfoPtr = png_create_info_struct(mWritePngPtr); + + // Setup write function + PngDataInfo dataPtr; + dataPtr.mData = dest; + dataPtr.mOffset = 0; + dataPtr.mDataSize = destSize; + png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush); + + // Setup image params + mWidth = rawImage->getWidth(); + mHeight = rawImage->getHeight(); + mBitDepth = 8; // Fixed to 8-bpp in SL + mChannels = numComponents; + mInterlaceType = PNG_INTERLACE_NONE; + mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT; + mFilterMethod = PNG_FILTER_TYPE_DEFAULT; + + // Write header + png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight, + mBitDepth, mColorType, mInterlaceType, + mCompressionType, mFilterMethod); + + // Get data and compute row size + const U8* data = rawImage->getData(); + int offset = mWidth * mChannels; + + // Ready to write, start with the header + png_write_info(mWritePngPtr, mWriteInfoPtr); + + // Write image (sorry, must const-cast for libpng) + const U8 * rowPointer; + for (U32 i=0; i < mHeight; i++) + { + rowPointer = &data[(mHeight-1-i)*offset]; + png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer)); + } + + // Finish up + png_write_end(mWritePngPtr, mWriteInfoPtr); + mFinalSize = dataPtr.mOffset; + } + catch (const PngError& msg) + { + mErrorMessage = msg.what(); + releaseResources(); + return (false); + } + + releaseResources(); + return true; } // Cleanup various internal structures void LLPngWrapper::releaseResources() { - if (mReadPngPtr || mReadInfoPtr) - { - png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, NULL); - mReadPngPtr = NULL; - mReadInfoPtr = NULL; - } - - if (mWritePngPtr || mWriteInfoPtr) - { - png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr); - mWritePngPtr = NULL; - mWriteInfoPtr = NULL; - } - - if (mRowPointers) - { - delete[] mRowPointers; - mRowPointers = NULL; - } + if (mReadPngPtr || mReadInfoPtr) + { + png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, NULL); + mReadPngPtr = NULL; + mReadInfoPtr = NULL; + } + + if (mWritePngPtr || mWriteInfoPtr) + { + png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr); + mWritePngPtr = NULL; + mWriteInfoPtr = NULL; + } + + if (mRowPointers) + { + delete[] mRowPointers; + mRowPointers = NULL; + } } // Get final image size after compression U32 LLPngWrapper::getFinalSize() { - return mFinalSize; + return mFinalSize; } // Get last error message, if any const std::string& LLPngWrapper::getErrorMessage() { - return mErrorMessage; + return mErrorMessage; } |