aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llimage/llpngwrapper.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:00 -0500
committerJacek Antonelli2008-08-15 23:45:00 -0500
commit5a0ae1117906416b8204e0cf1751f129f84bd447 (patch)
tree3f45be01280fcf078b96011db8a75003c3fa7be5 /linden/indra/llimage/llpngwrapper.cpp
parentSecond Life viewer sources 1.17.1.0 (diff)
downloadmeta-impy-5a0ae1117906416b8204e0cf1751f129f84bd447.zip
meta-impy-5a0ae1117906416b8204e0cf1751f129f84bd447.tar.gz
meta-impy-5a0ae1117906416b8204e0cf1751f129f84bd447.tar.bz2
meta-impy-5a0ae1117906416b8204e0cf1751f129f84bd447.tar.xz
Second Life viewer sources 1.17.2.0
Diffstat (limited to 'linden/indra/llimage/llpngwrapper.cpp')
-rw-r--r--linden/indra/llimage/llpngwrapper.cpp388
1 files changed, 388 insertions, 0 deletions
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 @@
1/*
2 * @file llpngwrapper.cpp
3 * @brief Encapsulates libpng read/write functionality.
4 *
5 * Copyright (c) 2007 Peekay Semyorka.
6 * Copyright (c) 2007-2007, Linden Research, Inc.
7 *
8 * Second Life Viewer Source Code
9 * The source code in this file ("Source Code") is provided by Linden Lab
10 * to you under the terms of the GNU General Public License, version 2.0
11 * ("GPL"), unless you have obtained a separate licensing agreement
12 * ("Other License"), formally executed by you and Linden Lab. Terms of
13 * the GPL can be found in doc/GPL-license.txt in this distribution, or
14 * online at http://secondlife.com/developers/opensource/gplv2
15 *
16 * There are special exceptions to the terms and conditions of the GPL as
17 * it is applied to this Source Code. View the full text of the exception
18 * in the file doc/FLOSS-exception.txt in this software distribution, or
19 * online at http://secondlife.com/developers/opensource/flossexception
20 *
21 * By copying, modifying or distributing this software, you acknowledge
22 * that you have read and understood your obligations described above,
23 * and agree to abide by those obligations.
24 *
25 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
26 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
27 * COMPLETENESS OR PERFORMANCE.
28 */
29
30#include "linden_common.h"
31#include "stdtypes.h"
32#include "llerror.h"
33
34#include "llimage.h"
35#include "llpngwrapper.h"
36
37// ---------------------------------------------------------------------------
38// LLPngWrapper
39// ---------------------------------------------------------------------------
40
41LLPngWrapper::LLPngWrapper()
42 : mReadPngPtr( NULL ),
43 mReadInfoPtr( NULL ),
44 mWritePngPtr( NULL ),
45 mWriteInfoPtr( NULL ),
46 mRowPointers( NULL ),
47 mBitDepth( 0 ),
48 mColorType( 0 ),
49 mChannels( 0 ),
50 mInterlaceType( 0 ),
51 mCompressionType( 0 ),
52 mFilterMethod( 0 ),
53 mFinalSize( 0 )
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 setting the
113// matte background if any, and applying gama) which can't be done with
114// the high-level interface. 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. Apply background matte if any
203 // 6. Set (or guess) gamma
204
205 if (mColorType == PNG_COLOR_TYPE_PALETTE)
206 {
207 png_set_palette_to_rgb(mReadPngPtr);
208 }
209 if (mColorType == PNG_COLOR_TYPE_GRAY && mBitDepth < 8)
210 {
211 png_set_gray_1_2_4_to_8(mReadPngPtr);
212 }
213 if (mColorType == PNG_COLOR_TYPE_GRAY
214 || mColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
215 {
216 png_set_gray_to_rgb(mReadPngPtr);
217 }
218 if (png_get_valid(mReadPngPtr, mReadInfoPtr, PNG_INFO_tRNS))
219 {
220 png_set_tRNS_to_alpha(mReadPngPtr);
221 }
222 if (mBitDepth < 8)
223 {
224 png_set_packing(mReadPngPtr);
225 }
226 else if (mBitDepth == 16)
227 {
228 png_set_strip_16(mReadPngPtr);
229 }
230 mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor);
231 if (mHasBKGD)
232 {
233 png_set_background(mReadPngPtr, mBackgroundColor,
234 PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
235 }
236
237#if LL_DARWIN
238 const F64 SCREEN_GAMMA = 1.8;
239#else
240 const F64 SCREEN_GAMMA = 2.2;
241#endif
242
243 if (png_get_gAMA(mReadPngPtr, mReadInfoPtr, &mGamma))
244 {
245 png_set_gamma(mReadPngPtr, SCREEN_GAMMA, mGamma);
246 }
247 else
248 {
249 png_set_gamma(mReadPngPtr, SCREEN_GAMMA, 1/SCREEN_GAMMA);
250 }
251}
252
253// Read out the image meta-data
254void LLPngWrapper::updateMetaData()
255{
256 png_read_update_info(mReadPngPtr, mReadInfoPtr);
257 mWidth = png_get_image_width(mReadPngPtr, mReadInfoPtr);
258 mHeight = png_get_image_height(mReadPngPtr, mReadInfoPtr);
259 mBitDepth = png_get_bit_depth(mReadPngPtr, mReadInfoPtr);
260 mColorType = png_get_color_type(mReadPngPtr, mReadInfoPtr);
261 mChannels = png_get_channels(mReadPngPtr, mReadInfoPtr);
262 mHasBKGD = png_get_bKGD(mReadPngPtr, mReadInfoPtr, &mBackgroundColor);
263}
264
265// Method to write raw image into PNG at dest. The raw scanline begins
266// at the bottom of the image per SecondLife conventions.
267BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
268{
269 try
270 {
271 S8 numComponents = rawImage->getComponents();
272 switch (numComponents)
273 {
274 case 1:
275 mColorType = PNG_COLOR_TYPE_GRAY;
276 break;
277 case 2:
278 mColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
279 break;
280 case 3:
281 mColorType = PNG_COLOR_TYPE_RGB;
282 break;
283 case 4:
284 mColorType = PNG_COLOR_TYPE_RGB_ALPHA;
285 break;
286 default:
287 mColorType = -1;
288 }
289
290 if (mColorType == -1)
291 {
292 throw "Unsupported image: unexpected number of channels";
293 }
294
295 mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
296 NULL, &errorHandler, NULL);
297 if (!mWritePngPtr)
298 {
299 throw "Problem creating png write structure";
300 }
301
302 mWriteInfoPtr = png_create_info_struct(mWritePngPtr);
303
304 // Setup write function
305 PngDataInfo dataPtr;
306 dataPtr.mData = dest;
307 dataPtr.mOffset = 0;
308 png_set_write_fn(mWritePngPtr, &dataPtr, &writeDataCallback, &writeFlush);
309
310 // Setup image params
311 mWidth = rawImage->getWidth();
312 mHeight = rawImage->getHeight();
313 mBitDepth = 8; // Fixed to 8-bpp in SL
314 mChannels = numComponents;
315 mInterlaceType = PNG_INTERLACE_NONE;
316 mCompressionType = PNG_COMPRESSION_TYPE_DEFAULT;
317 mFilterMethod = PNG_FILTER_TYPE_DEFAULT;
318
319 // Write header
320 png_set_IHDR(mWritePngPtr, mWriteInfoPtr, mWidth, mHeight,
321 mBitDepth, mColorType, mInterlaceType,
322 mCompressionType, mFilterMethod);
323
324 // Get data and compute row size
325 const U8* data = rawImage->getData();
326 int offset = mWidth * mChannels;
327
328 // Ready to write, start with the header
329 png_write_info(mWritePngPtr, mWriteInfoPtr);
330
331 // Write image (sorry, must const-cast for libpng)
332 const U8 * rowPointer;
333 for (U32 i=0; i < mHeight; i++)
334 {
335 rowPointer = &data[(mHeight-1-i)*offset];
336 png_write_row(mWritePngPtr, const_cast<png_bytep>(rowPointer));
337 }
338
339 // Finish up
340 png_write_end(mWritePngPtr, mWriteInfoPtr);
341 mFinalSize = dataPtr.mOffset;
342 }
343 catch (png_const_charp msg)
344 {
345 mErrorMessage = msg;
346 releaseResources();
347 return (FALSE);
348 }
349
350 releaseResources();
351 return TRUE;
352}
353
354// Cleanup various internal structures
355void LLPngWrapper::releaseResources()
356{
357 if (mReadPngPtr || mReadInfoPtr)
358 {
359 png_destroy_read_struct(&mReadPngPtr, &mReadInfoPtr, png_infopp_NULL);
360 mReadPngPtr = NULL;
361 mReadInfoPtr = NULL;
362 }
363
364 if (mWritePngPtr || mWriteInfoPtr)
365 {
366 png_destroy_write_struct(&mWritePngPtr, &mWriteInfoPtr);
367 mWritePngPtr = NULL;
368 mWriteInfoPtr = NULL;
369 }
370
371 if (mRowPointers)
372 {
373 delete[] mRowPointers;
374 mRowPointers = NULL;
375 }
376}
377
378// Get final image size after compression
379U32 LLPngWrapper::getFinalSize()
380{
381 return mFinalSize;
382}
383
384// Get last error message, if any
385LLString LLPngWrapper::getErrorMessage()
386{
387 return mErrorMessage;
388}