PageRenderTime 40ms CodeModel.GetById 2ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llimage/llpngwrapper.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 380 lines | 258 code | 47 blank | 75 comment | 34 complexity | 20ba0ad23f897a0ed303cd1b08618ced MD5 | raw file
  1/*
  2 * @file llpngwrapper.cpp
  3 * @brief Encapsulates libpng read/write functionality.
  4 *
  5 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28#include "stdtypes.h"
 29#include "llerror.h"
 30
 31#include "llimage.h"
 32#include "llpngwrapper.h"
 33
 34// ---------------------------------------------------------------------------
 35// LLPngWrapper
 36// ---------------------------------------------------------------------------
 37
 38LLPngWrapper::LLPngWrapper()
 39	: mReadPngPtr( NULL ),
 40	  mReadInfoPtr( NULL ),
 41	  mWritePngPtr( NULL ),
 42	  mWriteInfoPtr( NULL ),
 43	  mRowPointers( NULL ),
 44	  mWidth( 0 ),
 45	  mHeight( 0 ),
 46	  mBitDepth( 0 ),
 47	  mColorType( 0 ),
 48	  mChannels( 0 ),
 49	  mInterlaceType( 0 ),
 50	  mCompressionType( 0 ),
 51	  mFilterMethod( 0 ),
 52	  mFinalSize( 0 ),
 53	  mGamma(0.f)
 54{
 55}
 56
 57LLPngWrapper::~LLPngWrapper()
 58{
 59	releaseResources();
 60}
 61
 62// Checks the src for a valid PNG header
 63BOOL LLPngWrapper::isValidPng(U8* src)
 64{
 65	const int PNG_BYTES_TO_CHECK = 8;
 66
 67	int sig = png_sig_cmp((png_bytep)src, (png_size_t)0, PNG_BYTES_TO_CHECK);
 68	if (sig != 0)
 69	{
 70		mErrorMessage = "Invalid or corrupt PNG file";
 71		return FALSE;
 72	}
 73
 74	return TRUE;
 75}
 76
 77// Called by the libpng library when a fatal encoding or decoding error
 78// occurs.  We simply throw the error message and let our try/catch
 79// block clean up.
 80void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg)
 81{
 82	throw msg;
 83}
 84
 85// Called by the libpng library when reading (decoding) the PNG file. We
 86// copy the PNG data from our internal buffer into the PNG's data buffer.
 87void LLPngWrapper::readDataCallback(png_structp png_ptr, png_bytep dest, png_size_t length)
 88{
 89	PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
 90	U8 *src = &dataInfo->mData[dataInfo->mOffset];
 91	memcpy(dest, src, length);
 92	dataInfo->mOffset += static_cast<U32>(length);
 93}
 94
 95// Called by the libpng library when writing (encoding) the PNG file. We
 96// copy the encoded result into our data buffer.
 97void LLPngWrapper::writeDataCallback(png_structp png_ptr, png_bytep src, png_size_t length)
 98{
 99	PngDataInfo *dataInfo = (PngDataInfo *) png_get_io_ptr(png_ptr);
100	U8 *dest = &dataInfo->mData[dataInfo->mOffset];
101	memcpy(dest, src, length);
102	dataInfo->mOffset += static_cast<U32>(length);
103}
104
105// Flush the write output pointer
106void LLPngWrapper::writeFlush(png_structp png_ptr)
107{
108	// no-op since we're just writing to memory
109}
110
111// Read the PNG file using the libpng.  The low-level interface is used here
112// because we want to do various transformations (including applying gama)
113// which can't be done with the high-level interface.
114// The scanline also begins at the bottom of
115// the image (per SecondLife conventions) instead of at the top, so we
116// must assign row-pointers in "reverse" order.
117BOOL LLPngWrapper::readPng(U8* src, LLImageRaw* rawImage, ImageInfo *infop)
118{
119	try
120	{
121		// Create and initialize the png structures
122		mReadPngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
123			this, &errorHandler, NULL);
124		if (mReadPngPtr == NULL)
125		{
126			throw "Problem creating png read structure";
127		}
128
129		// Allocate/initialize the memory for image information.
130		mReadInfoPtr = png_create_info_struct(mReadPngPtr);
131
132		// Set up the input control
133		PngDataInfo dataPtr;
134		dataPtr.mData = src;
135		dataPtr.mOffset = 0;
136
137		png_set_read_fn(mReadPngPtr, &dataPtr, &readDataCallback);
138		png_set_sig_bytes(mReadPngPtr, 0);
139
140		// setup low-level read and get header information
141		png_read_info(mReadPngPtr, mReadInfoPtr);
142		png_get_IHDR(mReadPngPtr, mReadInfoPtr, &mWidth, &mHeight,
143			&mBitDepth, &mColorType, &mInterlaceType,
144			&mCompressionType, &mFilterMethod);
145
146		// Normalize the image, then get updated image information
147		// after transformations have been applied
148		normalizeImage();
149		updateMetaData();
150
151		// If a raw object is supplied, read the PNG image into its
152		// data space
153		if (rawImage != NULL)
154		{
155			rawImage->resize(static_cast<U16>(mWidth),
156				static_cast<U16>(mHeight), mChannels);
157			U8 *dest = rawImage->getData();
158			int offset = mWidth * mChannels;
159
160			// Set up the row pointers and read the image
161			mRowPointers = new U8* [mHeight];
162			for (U32 i=0; i < mHeight; i++)
163			{
164				mRowPointers[i] = &dest[(mHeight-i-1)*offset];
165			}
166
167			png_read_image(mReadPngPtr, mRowPointers);
168
169			// Finish up, ensures all metadata are updated
170			png_read_end(mReadPngPtr, NULL);
171		}
172
173		// If an info object is supplied, copy the relevant info
174		if (infop != NULL)
175		{
176			infop->mHeight = static_cast<U16>(mHeight);
177			infop->mWidth = static_cast<U16>(mWidth);
178			infop->mComponents = mChannels;
179		}
180
181		mFinalSize = dataPtr.mOffset;
182	}
183	catch (png_const_charp msg)
184	{
185		mErrorMessage = msg;
186		releaseResources();
187		return (FALSE);
188	}
189
190	// Clean up and return
191	releaseResources();
192	return (TRUE);
193}
194
195// Do transformations to normalize the input to 8-bpp RGBA
196void LLPngWrapper::normalizeImage()
197{
198	//		1. Expand any palettes
199	//		2. Convert grayscales to RGB
200	//		3. Create alpha layer from transparency
201	//		4. Ensure 8-bpp for all images
202	//		5. Set (or guess) gamma
203
204	if (mColorType == PNG_COLOR_TYPE_PALETTE)
205	{
206		png_set_palette_to_rgb(mReadPngPtr);
207	}
208    if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8)
209	{
210		png_set_expand_gray_1_2_4_to_8(mReadPngPtr);
211	}
212	if (mColorType == PNG_COLOR_TYPE_GRAY
213		|| mColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
214	{
215		png_set_gray_to_rgb(mReadPngPtr);
216	}
217	if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS))
218	{
219		png_set_tRNS_to_alpha(mReadPngPtr);
220	}
221	if (mBitDepth < 8)
222	{
223		png_set_packing(mReadPngPtr);
224	}
225	else if (mBitDepth == 16)
226	{
227		png_set_strip_16(mReadPngPtr);
228	}
229
230#if LL_DARWIN
231	const F64 SCREEN_GAMMA = 1.8;
232#else
233	const F64 SCREEN_GAMMA = 2.2;
234#endif
235
236	if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma))
237	{
238		png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma);
239	}
240	else
241	{
242		png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA);
243	}
244}
245
246// Read out the image meta-data
247void LLPngWrapper::updateMetaData()
248{
249	png_read_update_info(mReadPngPtr, mReadInfoPtr);
250    mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr);
251    mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr);
252    mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr);
253    mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr);
254	mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr);
255}
256
257// Method to write raw image into PNG at dest. The raw scanline begins
258// at the bottom of the image per SecondLife conventions.
259BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
260{
261	try
262	{
263		S8 numComponents = rawImage->getComponents();
264		switch (numComponents)
265		{
266		case 1:
267			mColorType = PNG_COLOR_TYPE_GRAY;
268			break;
269		case 2:
270			mColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
271			break;
272		case 3:
273			mColorType = PNG_COLOR_TYPE_RGB;
274			break;
275		case 4:
276			mColorType = PNG_COLOR_TYPE_RGB_ALPHA;
277			break;
278		default:
279			mColorType = -1;
280		}
281
282		if (mColorType == -1)
283		{
284			throw "Unsupported image: unexpected number of channels";
285		}
286
287		mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
288			NULL, &errorHandler, NULL);
289		if (!mWritePngPtr)
290		{
291			throw "Problem creating png write structure";
292		}
293
294		mWriteInfoPtr = png_create_info_struct(mWritePngPtr);
295
296		// Setup write function
297		PngDataInfo dataPtr;
298		dataPtr.mData = dest;
299		dataPtr.mOffset = 0;
300		png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush);
301
302		// Setup image params
303		mWidth = rawImage->getWidth();
304		mHeight = rawImage->getHeight();
305		mBitDepth = 8;	// Fixed to 8-bpp in SL
306		mChannels = numComponents;
307		mInterlaceType = PNG_INTERLACE_NONE;
308		mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT;
309		mFilterMethod = PNG_FILTER_TYPE_DEFAULT;
310
311		// Write header
312		png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight,
313			mBitDepth, mColorType, mInterlaceType,
314			mCompressionType, mFilterMethod);
315
316		// Get data and compute row size
317		const U8* data = rawImage->getData();
318		int offset = mWidth * mChannels;
319
320		// Ready to write, start with the header
321		png_write_info(mWritePngPtr, mWriteInfoPtr);
322
323		// Write image (sorry, must const-cast for libpng)
324		const U8 * rowPointer;
325		for (U32 i=0; i < mHeight; i++)
326		{
327			rowPointer = &data[(mHeight-1-i)*offset];
328			png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer));
329		}
330
331		// Finish up
332		png_write_end(mWritePngPtr, mWriteInfoPtr);
333		mFinalSize = dataPtr.mOffset;
334	}
335	catch (png_const_charp msg)
336	{
337		mErrorMessage = msg;
338		releaseResources();
339		return (FALSE);
340	}
341
342	releaseResources();
343	return TRUE;
344}
345
346// Cleanup various internal structures
347void LLPngWrapper::releaseResources()
348{
349	if (mReadPngPtr || mReadInfoPtr)
350	{
351		png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, NULL);
352		mReadPngPtr = NULL;
353		mReadInfoPtr = NULL;
354	}
355
356	if (mWritePngPtr || mWriteInfoPtr)
357	{
358		png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr);
359		mWritePngPtr = NULL;
360		mWriteInfoPtr = NULL;
361	}
362
363	if (mRowPointers)
364	{
365		delete[] mRowPointers;
366		mRowPointers = NULL;
367	}
368}
369
370// Get final image size after compression
371U32 LLPngWrapper::getFinalSize()
372{
373	return mFinalSize;
374}
375
376// Get last error message, if any
377const std::string& LLPngWrapper::getErrorMessage()
378{
379	return mErrorMessage;
380}