diff options
Diffstat (limited to 'indra/llimage/llpngwrapper.cpp')
-rw-r--r-- | indra/llimage/llpngwrapper.cpp | 830 |
1 files changed, 415 insertions, 415 deletions
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp index e9c11270c8..a5fb7a3167 100644 --- a/indra/llimage/llpngwrapper.cpp +++ b/indra/llimage/llpngwrapper.cpp @@ -1,415 +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);
- }
- 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;
-}
+/* + * @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; +} |