Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / bitmap_png.cpp
blob2d0518ec90117fe8b7c1ce150201aa4ca85ea0e7
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
22 #include "nel/misc/bitmap.h"
24 #include "nel/misc/stream.h"
25 #include "nel/misc/file.h"
26 #include "nel/misc/rgba.h"
27 #include "nel/misc/dynloadlib.h"
28 #include <png.h>
29 #include <csetjmp>
31 using namespace std;
33 #ifdef DEBUG_NEW
34 #define new DEBUG_NEW
35 #endif
37 namespace NLMISC
40 static void readPNGData(png_structp png_ptr, png_bytep data, png_size_t length)
42 IStream *stream = static_cast<IStream*>(png_get_io_ptr(png_ptr));
44 try
46 if (stream)
47 stream->serialBuffer((uint8*)data, (uint)length);
49 catch (...)
51 png_error(png_ptr, "Read error while decoding PNG file");
55 static void writePNGData(png_structp png_ptr, png_bytep data, png_size_t length)
57 IStream *stream = static_cast<IStream*>(png_get_io_ptr(png_ptr));
58 if (stream)
59 stream->serialBuffer((uint8*)data, (uint)length);
62 static void setPNGWarning(png_struct * /* png_ptr */, const char* message)
64 nlwarning(message);
67 static void setPNGError(png_struct *png_ptr, const char* message)
69 setPNGWarning(png_ptr, message);
71 longjmp(png_jmpbuf(png_ptr), 1);
74 /*-------------------------------------------------------------------*\
75 readPNG
76 \*-------------------------------------------------------------------*/
77 uint8 CBitmap::readPNG( NLMISC::IStream &f )
79 if(!f.isReading()) return false;
81 // initialize the info header
82 png_struct *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, setPNGError, setPNGWarning);
84 if (png_ptr == NULL)
86 nlwarning("failed to create the png read struct");
87 return 0;
90 // allocate/initialize the memory for image information.
91 png_info *info_ptr = png_create_info_struct(png_ptr);
93 if (info_ptr == NULL)
95 png_destroy_read_struct(&png_ptr, NULL, NULL);
96 nlwarning("failed to create the png info struct");
97 return 0;
100 if (setjmp(png_jmpbuf(png_ptr)))
102 // free all of the memory associated with the png_ptr and info_ptr
103 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
104 // if we get here, we had a problem reading the file
105 nlwarning("Error while reading PNG");
106 return 0;
109 // set the read function
110 png_set_read_fn(png_ptr, (void*)&f, readPNGData);
112 // set number of bit already read (in order to step back)
113 png_set_sig_bytes(png_ptr, 4);
115 // read header info and use it
116 png_read_info(png_ptr, info_ptr);
118 // get header infos
119 png_uint_32 width, height;
120 int iBitDepth, iColorType;
121 png_get_IHDR(png_ptr, info_ptr, &width, &height, &iBitDepth, &iColorType, NULL, NULL, NULL);
123 // expand images of all color-type and bit-depth to 3x8 bit RGB images
124 // let the library process things like alpha, transparency, background
125 double dGamma;
127 // make sure it will use 8 bits per channel
128 if (iBitDepth == 16)
129 png_set_strip_16(png_ptr);
130 else if (iBitDepth < 8)
131 png_set_packing(png_ptr);
133 // expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
134 if (iBitDepth < 8 || iColorType == PNG_COLOR_TYPE_PALETTE || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
135 png_set_expand(png_ptr);
137 // if required set gamma conversion
138 if (png_get_gAMA(png_ptr, info_ptr, &dGamma))
139 png_set_gamma(png_ptr, (double) 2.2, dGamma);
141 // add alpha byte after each RGB triplet if it doesn't exist
142 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
144 // after the transformations have been registered update info_ptr data
145 png_read_update_info(png_ptr, info_ptr);
147 // get again width, height and the new bit-depth and color-type
148 png_get_IHDR(png_ptr, info_ptr, &width, &height, &iBitDepth, &iColorType, NULL, NULL, NULL);
150 uint8 imageDepth;
152 switch(iColorType)
154 case PNG_COLOR_TYPE_GRAY:
155 imageDepth = iBitDepth;
156 break;
158 case PNG_COLOR_TYPE_PALETTE:
159 imageDepth = iBitDepth;
160 break;
162 case PNG_COLOR_TYPE_RGB:
163 imageDepth = iBitDepth * 3;
164 break;
166 case PNG_COLOR_TYPE_RGB_ALPHA:
167 imageDepth = iBitDepth * 4;
168 break;
170 case PNG_COLOR_TYPE_GRAY_ALPHA:
171 imageDepth = iBitDepth * 2;
172 break;
174 default:
175 imageDepth = iBitDepth * 4;
176 nlwarning("Unable to determine PNG color type: %d, consider it as RGBA", iColorType);
177 break;
180 // at this point, the image must be converted to an 24bit image RGB
182 // rowbytes is the width x number of channels
183 uint32 rowbytes = (uint32)png_get_rowbytes(png_ptr, info_ptr);
184 uint32 srcChannels = png_get_channels(png_ptr, info_ptr);
186 // allocates buffer to copy image data
187 png_bytepp row_pointers = (png_bytepp)png_malloc(png_ptr, height * sizeof(png_bytep));
189 for (uint row = 0; row < height; row++)
190 row_pointers[row] = (png_bytep)png_malloc(png_ptr, rowbytes);
192 // effective read of the image
193 png_read_image(png_ptr, row_pointers);
195 // read rest of file, and get additional chunks in info_ptr
196 png_read_end(png_ptr, info_ptr);
198 uint32 dstChannels = 0, firstChannel = 0, lastChannel = 0;
200 if (iColorType == PNG_COLOR_TYPE_RGBA || iColorType == PNG_COLOR_TYPE_RGB || iColorType == PNG_COLOR_TYPE_PALETTE)
202 // get all channels
203 dstChannels = 4;
204 firstChannel = 0;
205 lastChannel = 3;
206 resize (width, height, RGBA);
208 else if (iColorType == PNG_COLOR_TYPE_GRAY)
210 // only get gray channel
211 dstChannels = 1;
212 firstChannel = 0;
213 lastChannel = 0;
214 resize (width, height, _LoadGrayscaleAsAlpha ? Alpha : Luminance);
216 else if (iColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
218 // get gray and alpha channels
219 dstChannels = 2;
220 firstChannel = 0;
221 lastChannel = 1;
222 resize (width, height, AlphaLuminance);
224 else
226 png_error(png_ptr, "unknown iColorType");
229 for (uint32 y = 0; y < height; y++)
231 for (uint32 x = 0; x < width; x++)
233 uint32 dstOffset = y*width*dstChannels+x*dstChannels;
234 const uint32 srcOffset = x*srcChannels;
236 for (uint32 pix = firstChannel; pix <= lastChannel; pix++)
237 _Data[0][dstOffset++] = row_pointers[y][srcOffset+pix];
241 // free allocated memory to copy each rows
242 for (uint row = 0; row < height; row++)
243 png_free(png_ptr, row_pointers[row]);
245 // free allocated memory to copy the image
246 png_free(png_ptr, row_pointers);
248 // clean up after the read, and free any memory allocated
249 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
251 //return the size of a pixel, either 8,24,32 bit
252 return imageDepth;
255 // small helper to avoid local variables
256 static bool writePNGSetJmp(png_struct *png_ptr)
258 if (setjmp(png_jmpbuf(png_ptr)))
260 // free all of the memory associated with the png_ptr
261 png_destroy_write_struct(&png_ptr, (png_info**)NULL);
262 // if we get here, we had a problem writing the file
263 nlwarning("Error while writing PNG");
264 return false;
267 return true;
270 /*-------------------------------------------------------------------*\
271 writePNG
272 \*-------------------------------------------------------------------*/
273 bool CBitmap::writePNG( NLMISC::IStream &f, uint32 d)
275 if(f.isReading()) return false;
277 if (PixelFormat > AlphaLuminance) return false;
278 if (!_Width || !_Height) return false;
280 if (d == 0) d = bitPerPixels[PixelFormat];
282 if (d!=32 && d!=24 && d!=16 && d!=8) return false;
284 // create image write structure
285 png_struct *png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, setPNGError, setPNGWarning);
287 if (!png_ptr)
289 nlwarning("couldn't save PNG image.");
290 return false;
293 // create info structure
294 png_info *info_ptr = png_create_info_struct(png_ptr);
296 if (info_ptr == NULL)
298 png_destroy_write_struct( &png_ptr, (png_info**)NULL );
299 nlwarning("couldn't save PNG image.");
300 return false;
303 if (!writePNGSetJmp(png_ptr)) return false;
305 // set the write function
306 png_set_write_fn(png_ptr, (void*)&f, writePNGData, NULL);
308 int iColorType;
310 // only RGBA color, RGB color and gray type are implemented
311 if (d == 8)
313 iColorType = PNG_COLOR_TYPE_GRAY;
315 else if (d == 16)
317 iColorType = PNG_COLOR_TYPE_GRAY_ALPHA;
319 else if (d == 24)
321 iColorType = PNG_COLOR_TYPE_RGB;
323 else
325 iColorType = PNG_COLOR_TYPE_RGBA;
328 // we don't have to implement 16bits formats because NeL only uses 8bits
329 const int iBitDepth = 8;
331 // set correct values for PNG header
332 png_set_IHDR(png_ptr, info_ptr, _Width, _Height, iBitDepth, iColorType,
333 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
335 png_color_8 sig_bit;
337 if (iColorType & PNG_COLOR_MASK_COLOR)
339 sig_bit.red = sig_bit.green = sig_bit.blue = (uint8)iBitDepth;
341 else
343 sig_bit.gray = (uint8)iBitDepth;
346 if (iColorType & PNG_COLOR_MASK_ALPHA)
348 sig_bit.alpha = (uint8)iBitDepth;
350 else
352 sig_bit.alpha = 0;
355 // set bit depths
356 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
358 // Optional gamma chunk is strongly suggested if you have any guess
359 // as to the correct gamma of the image.
360 // double gamma = 2.2;
361 // png_set_gAMA(png_ptr, info_ptr, gamma);
363 // Optionally write comments into the image
364 // png_text text_ptr[3];
365 // text_ptr[0].key = "Title";
366 // text_ptr[0].text = "Mona Lisa";
367 // text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
368 // text_ptr[1].key = "Author";
369 // text_ptr[1].text = "Leonardo DaVinci";
370 // text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
371 // text_ptr[2].key = "Description";
372 // text_ptr[2].text = "<long text>";
373 // text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt;
375 // png_set_text(png_ptr, info_ptr, text_ptr, 3);
377 // write the file header information
378 png_write_info(png_ptr, info_ptr);
380 // shift the pixels up to a legal bit depth
381 png_set_shift(png_ptr, &sig_bit);
383 // pack pixels into bytes
384 png_set_packing(png_ptr);
386 // rowbytes is the width x number of channels
387 uint32 rowbytes = (uint32)png_get_rowbytes(png_ptr, info_ptr);
388 uint32 dstChannels = png_get_channels(png_ptr, info_ptr);
390 // get channels number of bitmap
391 sint srcChannels = bitPerPixels[PixelFormat]/8;
393 // get size of bitmap
394 sint height = _Height;
395 sint width = _Width;
397 // allocates buffer to copy image data
398 png_bytepp row_pointers = (png_bytepp)png_malloc(png_ptr, height * sizeof(png_bytep));
400 for (sint row = 0; row < height; row++)
401 row_pointers[row] = (png_bytep)png_malloc(png_ptr, rowbytes);
403 sint y, x, i;
405 for(y=0; y<height; ++y)
407 for(x=0; x<width; ++x)
409 const uint srcOffset = y*width*srcChannels + x*srcChannels;
410 const uint dstOffset = x*dstChannels;
412 if (dstChannels <= 2 && srcChannels == 4)
414 // convert colors to gray
415 row_pointers[y][dstOffset] = CRGBA(_Data[0][srcOffset+0],
416 _Data[0][srcOffset+1], _Data[0][srcOffset+2]).toGray();
418 if (sig_bit.alpha)
420 // copy alpha value
421 row_pointers[y][dstOffset+1] = _Data[0][srcOffset+3];
424 else if (dstChannels >= 3 && srcChannels <= 2)
426 // convert gray to colors
427 for(i=0; i<3; ++i)
429 row_pointers[y][dstOffset+i] = _Data[0][srcOffset];
432 if (sig_bit.alpha)
434 // copy alpha value
435 row_pointers[y][dstOffset+3] = PixelFormat ==
436 Luminance ? 255:_Data[0][srcOffset+1];
439 else
441 // copy all values
442 for(i=0; i<(sint)dstChannels; ++i)
444 row_pointers[y][dstOffset+i] = _Data[0][srcOffset+i];
450 // writing image data
451 png_write_image(png_ptr, row_pointers);
453 // writing the rest of the file
454 png_write_end(png_ptr, info_ptr);
456 // free allocated memory to copy each rows
457 for (sint row = 0; row < height; row++)
458 png_free(png_ptr, row_pointers[row]);
460 // free allocated memory to copy the image
461 png_free(png_ptr, row_pointers);
463 // clean up after the write, and free any memory allocated
464 png_destroy_write_struct(&png_ptr, &info_ptr);
466 return true;
469 }//namespace