From 5a0ae1117906416b8204e0cf1751f129f84bd447 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:45:00 -0500 Subject: Second Life viewer sources 1.17.2.0 --- linden/indra/llimage/llimagepng.cpp | 147 +++++++++++++ linden/indra/llimage/llimagepng.h | 51 +++++ linden/indra/llimage/llpngwrapper.cpp | 388 ++++++++++++++++++++++++++++++++++ linden/indra/llimage/llpngwrapper.h | 102 +++++++++ 4 files changed, 688 insertions(+) create mode 100644 linden/indra/llimage/llimagepng.cpp create mode 100644 linden/indra/llimage/llimagepng.h create mode 100644 linden/indra/llimage/llpngwrapper.cpp create mode 100644 linden/indra/llimage/llpngwrapper.h (limited to 'linden/indra/llimage') diff --git a/linden/indra/llimage/llimagepng.cpp b/linden/indra/llimage/llimagepng.cpp new file mode 100644 index 0000000..d6b62a8 --- /dev/null +++ b/linden/indra/llimage/llimagepng.cpp @@ -0,0 +1,147 @@ +/* + * @file llimagepng.cpp + * @brief LLImageFormatted glue to encode / decode PNG files. + * + * Copyright (c) 2007 Peekay Semyorka. + * Copyright (c) 2007-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" +#include "stdtypes.h" +#include "llerror.h" + +#include "llimage.h" +#include "llpngwrapper.h" +#include "llimagepng.h" + +// --------------------------------------------------------------------------- +// LLImagePNG +// --------------------------------------------------------------------------- +LLImagePNG::LLImagePNG() + : LLImageFormatted(IMG_CODEC_PNG), + mTmpWriteBuffer(NULL) +{ +} + +LLImagePNG::~LLImagePNG() +{ + if (mTmpWriteBuffer) + { + delete[] mTmpWriteBuffer; + } +} + +// Virtual +// Parse PNG image information and set the appropriate +// width, height and component (channel) information. +BOOL LLImagePNG::updateData() +{ + resetLastError(); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("Uninitialized instance of LLImagePNG"); + return FALSE; + } + + // Decode the PNG data and extract sizing information + LLPngWrapper pngWrapper; + LLPngWrapper::ImageInfo infop; + if (! pngWrapper.readPng(getData(), NULL, &infop)) + { + setLastError(pngWrapper.getErrorMessage()); + return FALSE; + } + + setSize(infop.mWidth, infop.mHeight, infop.mComponents); + + return TRUE; +} + +// Virtual +// Decode an in-memory PNG image into the raw RGB or RGBA format +// used within SecondLife. +BOOL LLImagePNG::decode(LLImageRaw* raw_image, F32 decode_time) +{ + llassert_always(raw_image); + + resetLastError(); + + // Check to make sure that this instance has been initialized with data + if (!getData() || (0 == getDataSize())) + { + setLastError("LLImagePNG trying to decode an image with no data!"); + return FALSE; + } + + // Decode the PNG data into the raw image + LLPngWrapper pngWrapper; + if (! pngWrapper.readPng(getData(), raw_image)) + { + setLastError(pngWrapper.getErrorMessage()); + return FALSE; + } + + return TRUE; +} + +// Virtual +// Encode the in memory RGB image into PNG format. +BOOL LLImagePNG::encode(const LLImageRaw* raw_image, F32 encode_time) +{ + llassert_always(raw_image); + + resetLastError(); + + // Image logical size + setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents()); + + // Temporary buffer to hold the encoded image. Note: the final image + // size should be much smaller due to compression. + if (mTmpWriteBuffer) + { + delete[] mTmpWriteBuffer; + } + U32 bufferSize = getWidth() * getHeight() * getComponents() + 1024; + U8* mTmpWriteBuffer = new U8[ bufferSize ]; + + // Delegate actual encoding work to wrapper + LLPngWrapper pngWrapper; + if (! pngWrapper.writePng(raw_image, mTmpWriteBuffer)) + { + setLastError(pngWrapper.getErrorMessage()); + return FALSE; + } + + // Resize internal buffer and copy from temp + U32 encodedSize = pngWrapper.getFinalSize(); + allocateData(encodedSize); + memcpy(getData(), mTmpWriteBuffer, encodedSize); + + delete[] mTmpWriteBuffer; + + return TRUE; +} + diff --git a/linden/indra/llimage/llimagepng.h b/linden/indra/llimage/llimagepng.h new file mode 100644 index 0000000..abdff7c --- /dev/null +++ b/linden/indra/llimage/llimagepng.h @@ -0,0 +1,51 @@ +/* + * @file llimagepng.h + * + * Copyright (c) 2007 Peekay Semyorka. + * Copyright (c) 2007-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLIMAGEPNG_H +#define LL_LLIMAGEPNG_H + +#include "stdtypes.h" +#include "llimage.h" + +class LLImagePNG : public LLImageFormatted +{ +protected: + ~LLImagePNG(); + +public: + LLImagePNG(); + + BOOL updateData(); + BOOL decode(LLImageRaw* raw_image, F32 decode_time = 0.0); + BOOL encode(const LLImageRaw* raw_image, F32 encode_time = 0.0); + +private: + U8* mTmpWriteBuffer; +}; + +#endif diff --git a/linden/indra/llimage/llpngwrapper.cpp b/linden/indra/llimage/llpngwrapper.cpp new file mode 100644 index 0000000..52616d1 --- /dev/null +++ b/linden/indra/llimage/llpngwrapper.cpp @@ -0,0 +1,388 @@ +/* + * @file llpngwrapper.cpp + * @brief Encapsulates libpng read/write functionality. + * + * Copyright (c) 2007 Peekay Semyorka. + * Copyright (c) 2007-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" +#include "stdtypes.h" +#include "llerror.h" + +#include "llimage.h" +#include "llpngwrapper.h" + +// --------------------------------------------------------------------------- +// LLPngWrapper +// --------------------------------------------------------------------------- + +LLPngWrapper::LLPngWrapper() + : mReadPngPtr( NULL ), + mReadInfoPtr( NULL ), + mWritePngPtr( NULL ), + mWriteInfoPtr( NULL ), + mRowPointers( NULL ), + mBitDepth( 0 ), + mColorType( 0 ), + mChannels( 0 ), + mInterlaceType( 0 ), + mCompressionType( 0 ), + mFilterMethod( 0 ), + mFinalSize( 0 ) +{ +} + +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 simply throw the error message and let our try/catch +// block clean up. +void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg) +{ + throw 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); + U8 *src = &dataInfo->mData[dataInfo->mOffset]; + memcpy(dest, src, length); + dataInfo->mOffset += static_cast(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); + U8 *dest = &dataInfo->mData[dataInfo->mOffset]; + memcpy(dest, src, length); + dataInfo->mOffset += static_cast(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 setting the +// matte background if any, and 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, 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) + { + throw "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; + + 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) + { + rawImage->resize(static_cast(mWidth), + static_cast(mHeight), mChannels); + 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(mHeight); + infop->mWidth = static_cast(mWidth); + infop->mComponents = mChannels; + } + + mFinalSize = dataPtr.mOffset; + } + catch (png_const_charp msg) + { + mErrorMessage = msg; + 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. Apply background matte if any + // 6. 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_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); + } + mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor); + if (mHasBKGD) + { + png_set_background(mReadPngPtr, mBackgroundColor, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + } + +#if LL_DARWIN + const F64 SCREEN_GAMMA = 1.8; +#else + const F64 SCREEN_GAMMA = 2.2; +#endif + + 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); + mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor); +} + +// 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) +{ + 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) + { + throw "Unsupported image: unexpected number of channels"; + } + + mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, &errorHandler, NULL); + if (!mWritePngPtr) + { + throw "Problem creating png write structure"; + } + + mWriteInfoPtr = png_create_info_struct(mWritePngPtr); + + // Setup write function + PngDataInfo dataPtr; + dataPtr.mData = dest; + dataPtr.mOffset = 0; + 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(rowPointer)); + } + + // Finish up + png_write_end(mWritePngPtr, mWriteInfoPtr); + mFinalSize = dataPtr.mOffset; + } + catch (png_const_charp msg) + { + mErrorMessage = msg; + releaseResources(); + return (FALSE); + } + + releaseResources(); + return TRUE; +} + +// Cleanup various internal structures +void LLPngWrapper::releaseResources() +{ + if (mReadPngPtr || mReadInfoPtr) + { + png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, png_infopp_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 +LLString LLPngWrapper::getErrorMessage() +{ + return mErrorMessage; +} diff --git a/linden/indra/llimage/llpngwrapper.h b/linden/indra/llimage/llpngwrapper.h new file mode 100644 index 0000000..1b5b97f --- /dev/null +++ b/linden/indra/llimage/llpngwrapper.h @@ -0,0 +1,102 @@ +/* + * @file llpngwrapper.h + * + * Copyright (c) 2007 Peekay Semyorka. + * Copyright (c) 2007-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLPNGWRAPPER_H +#define LL_LLPNGWRAPPER_H + +#include "libpng12/png.h" +#include "llimage.h" + +class LLPngWrapper +{ +public: + LLPngWrapper(); + virtual ~LLPngWrapper(); + +public: + struct ImageInfo + { + U16 mWidth; + U16 mHeight; + S8 mComponents; + }; + + BOOL isValidPng(U8* src); + BOOL readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop = NULL); + BOOL writePng(const LLImageRaw* rawImage, U8* dst); + U32 getFinalSize(); + LLString getErrorMessage(); + +protected: + void normalizeImage(); + void updateMetaData(); + +private: + + // Structure for writing/reading PNG data to/from memory + // as opposed to using a file. + struct PngDataInfo + { + U8 *mData; + U32 mOffset; + }; + + static void writeFlush(png_structp png_ptr); + static void errorHandler(png_structp png_ptr, png_const_charp msg); + static void readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length); + static void writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length); + + void releaseResources(); + + png_structp mReadPngPtr; + png_infop mReadInfoPtr; + png_structp mWritePngPtr; + png_infop mWriteInfoPtr; + + U8 **mRowPointers; + + png_uint_32 mWidth; + png_uint_32 mHeight; + S32 mBitDepth; + S32 mColorType; + S32 mChannels; + S32 mInterlaceType; + S32 mCompressionType; + S32 mFilterMethod; + + U32 mFinalSize; + + BOOL mHasBKGD; + png_color_16p mBackgroundColor; + + F64 mGamma; + + LLString mErrorMessage; +}; + +#endif -- cgit v1.1