summaryrefslogtreecommitdiff
path: root/indra/llimage
diff options
context:
space:
mode:
authorKelly Washington <kelly@lindenlab.com>2007-06-21 22:40:22 +0000
committerKelly Washington <kelly@lindenlab.com>2007-06-21 22:40:22 +0000
commite03bb0606a10f29c8b94909a713a5bb5c69e88b7 (patch)
tree6d8d07894579438c8cc70e08f5730c3c95dfe768 /indra/llimage
parent2638f12f95eea692502836cf6548b4a0b234d009 (diff)
merge -r62831:64079 branches/maintenance to release
Diffstat (limited to 'indra/llimage')
-rw-r--r--indra/llimage/llimage.cpp3
-rw-r--r--indra/llimage/llimage.h3
-rw-r--r--indra/llimage/llimagepng.cpp127
-rw-r--r--indra/llimage/llimagepng.h31
-rw-r--r--indra/llimage/llimageworker.cpp2
-rw-r--r--indra/llimage/llpngwrapper.cpp368
-rw-r--r--indra/llimage/llpngwrapper.h82
7 files changed, 613 insertions, 3 deletions
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index b6f6cdfc24..5ef3e9de3b 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -1085,7 +1085,8 @@ file_extensions[] =
{ "jpg", IMG_CODEC_JPEG },
{ "jpeg", IMG_CODEC_JPEG },
{ "mip", IMG_CODEC_DXT },
- { "dxt", IMG_CODEC_DXT }
+ { "dxt", IMG_CODEC_DXT },
+ { "png", IMG_CODEC_PNG }
};
#define NUM_FILE_EXTENSIONS sizeof(file_extensions)/sizeof(file_extensions[0])
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 0e007c2c86..99e60c48a8 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -47,7 +47,8 @@ enum
IMG_CODEC_TGA = 4,
IMG_CODEC_JPEG = 5,
IMG_CODEC_DXT = 6,
- IMG_CODEC_EOF = 7
+ IMG_CODEC_PNG = 7,
+ IMG_CODEC_EOF = 8
};
//============================================================================
diff --git a/indra/llimage/llimagepng.cpp b/indra/llimage/llimagepng.cpp
new file mode 100644
index 0000000000..4d884822ed
--- /dev/null
+++ b/indra/llimage/llimagepng.cpp
@@ -0,0 +1,127 @@
+/*
+ * @file llimagepng.cpp
+ * @brief LLImageFormatted glue to encode / decode PNG files.
+ *
+ * Copyright (c) 2007 Peekay Semyorka.
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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/indra/llimage/llimagepng.h b/indra/llimage/llimagepng.h
new file mode 100644
index 0000000000..e12a5cc1f9
--- /dev/null
+++ b/indra/llimage/llimagepng.h
@@ -0,0 +1,31 @@
+/*
+ * @file llimagepng.h
+ *
+ * Copyright (c) 2007 Peekay Semyorka.
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
index 1e625fbe28..9a200cdf61 100644
--- a/indra/llimage/llimageworker.cpp
+++ b/indra/llimage/llimageworker.cpp
@@ -1,5 +1,5 @@
/**
- * @file llimage.cpp
+ * @file llimageworker.cpp
* @brief Base class for images.
*
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp
new file mode 100644
index 0000000000..462edc5974
--- /dev/null
+++ b/indra/llimage/llpngwrapper.cpp
@@ -0,0 +1,368 @@
+/*
+ * @file llpngwrapper.cpp
+ * @brief Encapsulates libpng read/write functionality.
+ *
+ * Copyright (c) 2007 Peekay Semyorka.
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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<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);
+ 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 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<U16>(mWidth),
+ static_cast<U16>(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<U16>(mHeight);
+ infop->mWidth = static_cast<U16>(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<png_bytep>(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/indra/llimage/llpngwrapper.h b/indra/llimage/llpngwrapper.h
new file mode 100644
index 0000000000..3d83992cfd
--- /dev/null
+++ b/indra/llimage/llpngwrapper.h
@@ -0,0 +1,82 @@
+/*
+ * @file llpngwrapper.h
+ *
+ * Copyright (c) 2007 Peekay Semyorka.
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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