diff options
author | Jacek Antonelli | 2008-08-15 23:45:00 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:45:00 -0500 |
commit | 5a0ae1117906416b8204e0cf1751f129f84bd447 (patch) | |
tree | 3f45be01280fcf078b96011db8a75003c3fa7be5 /linden/indra/llimage/llpngwrapper.cpp | |
parent | Second Life viewer sources 1.17.1.0 (diff) | |
download | meta-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.cpp | 388 |
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 | |||
41 | LLPngWrapper::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 | |||
57 | LLPngWrapper::~LLPngWrapper() | ||
58 | { | ||
59 | releaseResources(); | ||
60 | } | ||
61 | |||
62 | // Checks the src for a valid PNG header | ||
63 | BOOL 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. | ||
80 | void 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. | ||
87 | void 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. | ||
97 | void 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 | ||
106 | void 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. | ||
117 | BOOL 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 | ||
196 | void 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 | ||
254 | void 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. | ||
267 | BOOL 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 | ||
355 | void 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 | ||
379 | U32 LLPngWrapper::getFinalSize() | ||
380 | { | ||
381 | return mFinalSize; | ||
382 | } | ||
383 | |||
384 | // Get last error message, if any | ||
385 | LLString LLPngWrapper::getErrorMessage() | ||
386 | { | ||
387 | return mErrorMessage; | ||
388 | } | ||