diff options
Diffstat (limited to 'indra/llimage/llpngwrapper.cpp')
-rw-r--r-- | indra/llimage/llpngwrapper.cpp | 823 |
1 files changed, 415 insertions, 408 deletions
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index baf1330011..e9c11270c8 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -1,408 +1,415 @@ -/* - * @file llpngwrapper.cpp - * @brief Encapsulates libpng read/write functionality. - * - * $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$ - */ - -#include "linden_common.h" -#include "stdtypes.h" -#include "llerror.h" - -#include "llimage.h" -#include "llpngwrapper.h" - -#include "llexception.h" - -namespace { -// Failure to load an image shouldn't crash the whole viewer. -struct PngError: public LLContinueError -{ - PngError(png_const_charp msg): LLContinueError(msg) {} -}; -} // anonymous namespace - -// --------------------------------------------------------------------------- -// LLPngWrapper -// --------------------------------------------------------------------------- - -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) -{ -} - -LLPngWrapper::~LLPngWrapper() -{ - releaseResources(); -} - -// Checks the src for a valid PNG header -bool LLPngWrapper::isValidPng(U8* src) -{ - 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; - } - - 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)); -} - -// 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); -} - -// 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); -} - -// Flush the write output pointer -void LLPngWrapper::writeFlush(png_structp png_ptr) -{ - // no-op since we're just writing to memory -} - -// Read the PNG file using the libpng. The low-level interface is used here -// because we want to do various transformations (including applying gama) -// which can't be done with the high-level interface. -// The scanline also begins at the bottom of -// the image (per SecondLife conventions) instead of at the top, so we -// 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); -} - -// 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); - } - 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); - } -} - -// Read out the image meta-data -void LLPngWrapper::updateMetaData() -{ - 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); -} - -// 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; -} - -// 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; - } -} - -// Get final image size after compression -U32 LLPngWrapper::getFinalSize() -{ - return mFinalSize; -} - -// Get last error message, if any -const std::string& LLPngWrapper::getErrorMessage() -{ - return mErrorMessage; -} +/*
+ * @file llpngwrapper.cpp
+ * @brief Encapsulates libpng read/write functionality.
+ *
+ * $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$
+ */
+
+#include "linden_common.h"
+#include "stdtypes.h"
+#include "llerror.h"
+
+#include "llimage.h"
+#include "llpngwrapper.h"
+
+#include "llexception.h"
+
+namespace {
+// Failure to load an image shouldn't crash the whole viewer.
+struct PngError: public LLContinueError
+{
+ PngError(png_const_charp msg): LLContinueError(msg) {}
+};
+} // anonymous namespace
+
+// ---------------------------------------------------------------------------
+// LLPngWrapper
+// ---------------------------------------------------------------------------
+
+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)
+{
+}
+
+LLPngWrapper::~LLPngWrapper()
+{
+ releaseResources();
+}
+
+// Checks the src for a valid PNG header
+bool LLPngWrapper::isValidPng(U8* src)
+{
+ 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;
+ }
+
+ 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));
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// Flush the write output pointer
+void LLPngWrapper::writeFlush(png_structp png_ptr)
+{
+ // no-op since we're just writing to memory
+}
+
+// Read the PNG file using the libpng. The low-level interface is used here
+// because we want to do various transformations (including applying gama)
+// which can't be done with the high-level interface.
+// The scanline also begins at the bottom of
+// the image (per SecondLife conventions) instead of at the top, so we
+// 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);
+ }
+ 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);
+ }
+ 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);
+ }
+}
+
+// Read out the image meta-data
+void LLPngWrapper::updateMetaData()
+{
+ 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);
+}
+
+// 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;
+}
+
+// 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;
+ }
+}
+
+// Get final image size after compression
+U32 LLPngWrapper::getFinalSize()
+{
+ return mFinalSize;
+}
+
+// Get last error message, if any
+const std::string& LLPngWrapper::getErrorMessage()
+{
+ return mErrorMessage;
+}
|