1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
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
));
47 stream
->serialBuffer((uint8
*)data
, (uint
)length
);
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
));
59 stream
->serialBuffer((uint8
*)data
, (uint
)length
);
62 static void setPNGWarning(png_struct
* /* png_ptr */, const char* 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 /*-------------------------------------------------------------------*\
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
);
86 nlwarning("failed to create the png read struct");
90 // allocate/initialize the memory for image information.
91 png_info
*info_ptr
= png_create_info_struct(png_ptr
);
95 png_destroy_read_struct(&png_ptr
, NULL
, NULL
);
96 nlwarning("failed to create the png info struct");
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");
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
);
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
127 // make sure it will use 8 bits per channel
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
);
154 case PNG_COLOR_TYPE_GRAY
:
155 imageDepth
= iBitDepth
;
158 case PNG_COLOR_TYPE_PALETTE
:
159 imageDepth
= iBitDepth
;
162 case PNG_COLOR_TYPE_RGB
:
163 imageDepth
= iBitDepth
* 3;
166 case PNG_COLOR_TYPE_RGB_ALPHA
:
167 imageDepth
= iBitDepth
* 4;
170 case PNG_COLOR_TYPE_GRAY_ALPHA
:
171 imageDepth
= iBitDepth
* 2;
175 imageDepth
= iBitDepth
* 4;
176 nlwarning("Unable to determine PNG color type: %d, consider it as RGBA", iColorType
);
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
)
206 resize (width
, height
, RGBA
);
208 else if (iColorType
== PNG_COLOR_TYPE_GRAY
)
210 // only get gray channel
214 resize (width
, height
, _LoadGrayscaleAsAlpha
? Alpha
: Luminance
);
216 else if (iColorType
== PNG_COLOR_TYPE_GRAY_ALPHA
)
218 // get gray and alpha channels
222 resize (width
, height
, AlphaLuminance
);
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
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");
270 /*-------------------------------------------------------------------*\
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
);
289 nlwarning("couldn't save PNG image.");
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.");
303 if (!writePNGSetJmp(png_ptr
)) return false;
305 // set the write function
306 png_set_write_fn(png_ptr
, (void*)&f
, writePNGData
, NULL
);
310 // only RGBA color, RGB color and gray type are implemented
313 iColorType
= PNG_COLOR_TYPE_GRAY
;
317 iColorType
= PNG_COLOR_TYPE_GRAY_ALPHA
;
321 iColorType
= PNG_COLOR_TYPE_RGB
;
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
);
337 if (iColorType
& PNG_COLOR_MASK_COLOR
)
339 sig_bit
.red
= sig_bit
.green
= sig_bit
.blue
= (uint8
)iBitDepth
;
343 sig_bit
.gray
= (uint8
)iBitDepth
;
346 if (iColorType
& PNG_COLOR_MASK_ALPHA
)
348 sig_bit
.alpha
= (uint8
)iBitDepth
;
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
;
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
);
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();
421 row_pointers
[y
][dstOffset
+1] = _Data
[0][srcOffset
+3];
424 else if (dstChannels
>= 3 && srcChannels
<= 2)
426 // convert gray to colors
429 row_pointers
[y
][dstOffset
+i
] = _Data
[0][srcOffset
];
435 row_pointers
[y
][dstOffset
+3] = PixelFormat
==
436 Luminance
? 255:_Data
[0][srcOffset
+1];
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
);