1 # include "bitmapConfig.h"
2
3 # include <stdlib.h>
4 # include "bmintern.h"
5 # include "bmio.h"
6 # include <png.h>
7 # include <appDebugon.h>
8 # include <sioFileio.h>
9 # include <utilEndian.h>
10 # include <geoUnits.h>
11
12 # if 1
13 # ifndef png_jmpbuf
14 /* Works with older libs as well */
15 # define png_jmpbuf( pngp ) pngp->jmpbuf
16 # endif
17 # endif
18
19 /************************************************************************/
20 /* */
21 /* Read a bitmap from a PNG file. */
22 /* */
23 /************************************************************************/
24
bpPngiToBitmap(const png_structp pngp,png_info * pngi,BitmapDescription * bd)25 static int bpPngiToBitmap( const png_structp pngp,
26 png_info * pngi,
27 BitmapDescription * bd )
28 {
29 unsigned int col;
30 png_uint_32 res_x, res_y;
31 int unit_type= 0;
32
33 bd->bdPixelsWide= png_get_image_width( pngp, pngi );
34 bd->bdPixelsHigh= png_get_image_height( pngp, pngi );
35 bd->bdHasAlpha= 0;
36
37 switch( png_get_color_type( pngp, pngi ) )
38 {
39 case PNG_COLOR_TYPE_PALETTE:
40 {
41 int num_palette;
42 png_colorp palette;
43
44 png_get_PLTE( pngp, pngi, &palette, &num_palette );
45
46 bd->bdColorEncoding= BMcoRGB8PALETTE;
47 if ( utilPaletteSetCount( &(bd->bdPalette), num_palette ) )
48 { LDEB(num_palette); return -1; }
49 bd->bdBitsPerSample= 8;
50 bd->bdSamplesPerPixel= 3;
51 bd->bdBitsPerPixel= png_get_bit_depth( pngp, pngi );
52
53 for ( col= 0; col < bd->bdPalette.cpColorCount; col++ )
54 {
55 bd->bdPalette.cpColors[col].rgb8Red= palette[col].red;
56 bd->bdPalette.cpColors[col].rgb8Green= palette[col].green;
57 bd->bdPalette.cpColors[col].rgb8Blue= palette[col].blue;
58 bd->bdPalette.cpColors[col].rgb8Alpha= 255;
59 }
60 }
61 break;
62
63 case PNG_COLOR_TYPE_RGB:
64 bd->bdColorEncoding= BMcoRGB;
65 bd->bdBitsPerSample= png_get_bit_depth( pngp, pngi );
66 bd->bdSamplesPerPixel= png_get_channels( pngp, pngi );
67 bd->bdBitsPerPixel= bd->bdSamplesPerPixel* bd->bdBitsPerSample;
68 break;
69
70 case PNG_COLOR_TYPE_GRAY:
71 bd->bdColorEncoding= BMcoWHITEBLACK;
72 bd->bdBitsPerSample= png_get_bit_depth( pngp, pngi );
73 bd->bdSamplesPerPixel= png_get_channels( pngp, pngi );
74 bd->bdBitsPerPixel= bd->bdSamplesPerPixel* bd->bdBitsPerSample;
75 break;
76
77 case PNG_COLOR_TYPE_RGB_ALPHA:
78 bd->bdHasAlpha= 1;
79 bd->bdColorEncoding= BMcoRGB;
80 bd->bdBitsPerSample= png_get_bit_depth( pngp, pngi );
81 bd->bdSamplesPerPixel= png_get_channels( pngp, pngi );
82 bd->bdBitsPerPixel= bd->bdSamplesPerPixel* bd->bdBitsPerSample;
83 break;
84
85 case PNG_COLOR_TYPE_GRAY_ALPHA:
86 bd->bdHasAlpha= 1;
87 bd->bdColorEncoding= BMcoWHITEBLACK;
88 bd->bdBitsPerSample= png_get_bit_depth( pngp, pngi );
89 bd->bdSamplesPerPixel= png_get_channels( pngp, pngi );
90 bd->bdBitsPerPixel= bd->bdSamplesPerPixel* bd->bdBitsPerSample;
91 break;
92
93 default:
94 LDEB(png_get_color_type( pngp, pngi )); return -1;
95 }
96
97 bd->bdBytesPerRow= png_get_rowbytes( pngp, pngi );
98 bd->bdBufferLength= bd->bdBytesPerRow* bd->bdPixelsHigh;
99
100 if ( ! png_get_pHYs( pngp, pngi, &res_x, &res_y, &unit_type ) )
101 { unit_type= PNG_RESOLUTION_UNKNOWN; }
102
103 switch( unit_type )
104 {
105 case PNG_RESOLUTION_UNKNOWN:
106 bd->bdUnit= BMunPIXEL;
107 bd->bdXResolution= 1;
108 bd->bdYResolution= 1;
109 break;
110
111 case PNG_RESOLUTION_METER:
112 bd->bdUnit= BMunM;
113 bd->bdXResolution= res_x;
114 bd->bdYResolution= res_y;
115 break;
116
117 default:
118 LDEB(unit_type);
119 return -1;
120 }
121
122 return 0;
123 }
124
bmPngReadContents(png_info * pngi,png_struct * png,BitmapDescription * bd,unsigned char ** pBuffer)125 static int bmPngReadContents( png_info * pngi,
126 png_struct * png,
127 BitmapDescription * bd,
128 unsigned char ** pBuffer )
129 {
130 int numberOfPasses;
131 int i;
132
133 unsigned char * buffer;
134
135 numberOfPasses= 1;
136 if ( png_get_interlace_type( png, pngi ) )
137 { numberOfPasses= png_set_interlace_handling( png ); }
138
139 if ( png_get_color_type( png, pngi ) == PNG_COLOR_TYPE_RGB &&
140 png_get_bit_depth( png, pngi ) == 16 )
141 {
142 const unsigned short one= 1;
143 const unsigned char * testEndian= (const unsigned char *)&one;
144
145 if ( testEndian[0] )
146 { png_set_swap( png ); }
147 }
148
149 png_start_read_image( png );
150
151 buffer= (unsigned char *)malloc( bd->bdBufferLength );
152 if ( ! buffer )
153 { LLDEB(bd->bdBufferLength,buffer); return -1; }
154
155 for ( i= 0; i < numberOfPasses; i++ )
156 {
157 unsigned int row;
158
159 for ( row= 0; row < bd->bdPixelsHigh; row++ )
160 {
161 unsigned char * to= buffer+ row* bd->bdBytesPerRow;
162
163 png_read_rows( png, &to, NULL, 1 );
164 }
165 }
166
167 png_read_end( png, pngi );
168
169 if ( bd->bdColorEncoding == BMcoRGB8PALETTE &&
170 ! bd->bdHasAlpha )
171 { bmMakeMonochrome( bd, buffer ); }
172
173 *pBuffer= buffer; return 0;
174 }
175
bmReadPngFile(const MemoryBuffer * filename,unsigned char ** pBuffer,BitmapDescription * bd,int * pPrivateFormat)176 int bmReadPngFile( const MemoryBuffer * filename,
177 unsigned char ** pBuffer,
178 BitmapDescription * bd,
179 int * pPrivateFormat )
180 {
181 SimpleInputStream * sis;
182
183 sis= sioInFileioOpen( filename );
184 if ( ! sis )
185 { XDEB(sis); return -1; }
186
187 if ( bmPngReadPng( bd, pBuffer, sis ) )
188 { LDEB(1); sioInClose( sis ); return -1; }
189
190 sioInClose( sis );
191
192 *pPrivateFormat= 0;
193
194 return 0;
195 }
196
197 /************************************************************************/
198 /* */
199 /* Read an image from a PNG input stream. */
200 /* */
201 /************************************************************************/
202
bmReadPngBytes(png_struct * png,png_byte * buffer,png_size_t count)203 static void bmReadPngBytes( png_struct * png,
204 png_byte * buffer,
205 png_size_t count )
206 {
207 SimpleInputStream * sis;
208
209 sis= (SimpleInputStream *)png_get_io_ptr( png );
210
211 sioInReadBytes( sis, (unsigned char *)buffer, count );
212
213 return;
214 }
215
bmPngReadPng(BitmapDescription * bd,unsigned char ** pBuffer,SimpleInputStream * sis)216 int bmPngReadPng( BitmapDescription * bd,
217 unsigned char ** pBuffer,
218 SimpleInputStream * sis )
219 {
220 png_structp pngp= (png_structp)0;
221 png_infop pngip= (png_infop)0;
222
223 unsigned char * buffer;
224
225 pngp = png_create_read_struct( PNG_LIBPNG_VER_STRING, (void *)0,
226 (png_error_ptr)0, (png_error_ptr)0 );
227 if ( ! pngp )
228 { LDEB(1); return -1; }
229
230 pngip = png_create_info_struct( pngp );
231 if ( ! pngip )
232 {
233 LDEB(1);
234 png_destroy_read_struct( &pngp, (png_infop *)0, (png_infop *)0 );
235 return -1;
236 }
237
238 if ( setjmp( png_jmpbuf( pngp ) ) )
239 {
240 LDEB(1);
241 png_destroy_read_struct( &pngp, &pngip, (png_infop *)0 );
242 return -1;
243 }
244
245 png_init_io( pngp, (FILE *)0 );
246 png_set_read_fn( pngp, (void *)sis, bmReadPngBytes );
247
248 png_read_info( pngp, pngip );
249
250 if ( bpPngiToBitmap( pngp, pngip, bd ) )
251 {
252 LDEB(bd->bdPalette.cpColorCount);
253 png_destroy_read_struct( &pngp, &pngip, (png_infop *)0 );
254 return -1;
255 }
256
257 if ( bmPngReadContents( pngip, pngp, bd, &buffer ) )
258 {
259 LDEB(bd->bdBufferLength);
260 png_destroy_read_struct( &pngp, &pngip, (png_infop *)0 );
261 return -1;
262 }
263
264 *pBuffer= buffer;
265
266 png_destroy_read_struct( &pngp, &pngip, (png_infop *)0 );
267
268 return 0;
269 }
270
271 /************************************************************************/
272 /* */
273 /* Can write a bitmap to a PNG file? */
274 /* All images can be written to png. */
275 /* */
276 /************************************************************************/
277
bmCanWritePngFile(const BitmapDescription * bd,int privateFormat)278 int bmCanWritePngFile( const BitmapDescription * bd,
279 int privateFormat )
280 {
281 if ( bd->bdColorEncoding == BMcoRGB8PALETTE &&
282 bd->bdBitsPerPixel > 8 )
283 { return -1; }
284
285 return 0;
286 }
287
288 /************************************************************************/
289 /* */
290 /* Write a bitmap to a PNG file. */
291 /* */
292 /************************************************************************/
293
bpPngiFromBitmap(png_structp png,png_info * pngi,png_colorp * pPalette,const BitmapDescription * bd)294 static int bpPngiFromBitmap( png_structp png,
295 png_info * pngi,
296 png_colorp * pPalette,
297 const BitmapDescription * bd )
298 {
299 int bit_depth;
300 int color_type;
301 png_color_8 sig_bit;
302
303 sig_bit.red= sig_bit.green= sig_bit.blue= sig_bit.gray= sig_bit.alpha= 0;
304
305 switch( bd->bdUnit )
306 {
307 case BMunM:
308 png_set_pHYs( png, pngi,
309 bd->bdXResolution,
310 bd->bdYResolution,
311 PNG_RESOLUTION_METER);
312 break;
313
314 case BMunINCH:
315 png_set_pHYs( png, pngi,
316 (int)( 39.37* bd->bdXResolution ),
317 (int)( 39.37* bd->bdYResolution ),
318 PNG_RESOLUTION_METER);
319 break;
320
321 case BMunPOINT:
322 png_set_pHYs( png, pngi,
323 POINTS_PER_M* bd->bdXResolution,
324 POINTS_PER_M* bd->bdYResolution,
325 PNG_RESOLUTION_METER);
326 break;
327
328 case BMunPIXEL:
329 png_set_pHYs(png, pngi, 1, 1, PNG_RESOLUTION_UNKNOWN);
330 break;
331
332 default:
333 LDEB(bd->bdUnit);
334 png_set_pHYs(png, pngi, 1, 1, PNG_RESOLUTION_UNKNOWN);
335 break;
336 }
337
338 switch( bd->bdColorEncoding )
339 {
340 int i;
341
342 case BMcoBLACKWHITE:
343 case BMcoWHITEBLACK:
344 bit_depth= bd->bdBitsPerSample;
345 if ( bd->bdHasAlpha )
346 {
347 color_type= PNG_COLOR_TYPE_GRAY_ALPHA;
348 sig_bit.alpha= bd->bdBitsPerSample;
349 }
350 else{ color_type= PNG_COLOR_TYPE_GRAY; }
351 sig_bit.gray= bd->bdBitsPerSample;
352 break;
353
354 case BMcoRGB:
355 bit_depth= bd->bdBitsPerSample;
356 if ( bd->bdHasAlpha )
357 {
358 color_type= PNG_COLOR_TYPE_RGB_ALPHA;
359 sig_bit.alpha= bd->bdBitsPerSample;
360 }
361 else{ color_type= PNG_COLOR_TYPE_RGB; }
362 sig_bit.red= bd->bdBitsPerSample;
363 sig_bit.green= bd->bdBitsPerSample;
364 sig_bit.blue= bd->bdBitsPerSample;
365 break;
366
367 case BMcoRGB8PALETTE:
368 if ( bd->bdHasAlpha )
369 { bit_depth= bd->bdBitsPerPixel/ 2; }
370 else{ bit_depth= bd->bdBitsPerPixel; }
371 color_type= PNG_COLOR_TYPE_PALETTE;
372
373 if ( bd->bdPalette.cpColorCount + bd->bdHasAlpha >
374 PNG_MAX_PALETTE_LENGTH )
375 {
376 LLDEB(bd->bdPalette.cpColorCount,PNG_MAX_PALETTE_LENGTH);
377 return -1;
378 }
379
380 *pPalette= (png_color *)malloc( PNG_MAX_PALETTE_LENGTH*
381 sizeof( png_color ) );
382 if ( ! *pPalette )
383 { XDEB(*pPalette); return -1; }
384 sig_bit.red= bd->bdBitsPerSample;
385 sig_bit.green= bd->bdBitsPerSample;
386 sig_bit.blue= bd->bdBitsPerSample;
387 sig_bit.alpha= 0;
388
389 for ( i= 0; i < bd->bdPalette.cpColorCount; i++ )
390 {
391 (*pPalette)[i].red= bd->bdPalette.cpColors[i].rgb8Red;
392 (*pPalette)[i].green= bd->bdPalette.cpColors[i].rgb8Green;
393 (*pPalette)[i].blue= bd->bdPalette.cpColors[i].rgb8Blue;
394 }
395 /* Give rest of palette predictable contents */
396 for ( i= bd->bdPalette.cpColorCount;
397 i < PNG_MAX_PALETTE_LENGTH; i++ )
398 { (*pPalette)[i]= (*pPalette)[0]; }
399 if ( bd->bdHasAlpha )
400 {
401 if ( bd->bdHasAlpha )
402 { sig_bit.alpha= bd->bdBitsPerSample; }
403
404 (*pPalette)[i].red= 255;
405 (*pPalette)[i].green= 255;
406 (*pPalette)[i].blue= 255;
407 /*
408 (*pPalette)[i].alpha= 0;
409 */
410 }
411
412 png_set_PLTE( png, pngi, (*pPalette),
413 bd->bdPalette.cpColorCount+ bd->bdHasAlpha );
414 break;
415
416 default:
417 LDEB(bd->bdColorEncoding);
418 return -1;
419 }
420
421 png_set_sBIT( png, pngi, &sig_bit );
422 png_set_IHDR( png, pngi,
423 bd->bdPixelsWide, bd->bdPixelsHigh,
424 bit_depth, color_type,
425 PNG_INTERLACE_NONE,
426 PNG_COMPRESSION_TYPE_BASE,
427 PNG_FILTER_TYPE_BASE );
428
429 return 0;
430 }
431
bmPngWriteContents(png_structp png,png_infop pngi,const unsigned char * buffer,const BitmapDescription * bd)432 static void bmPngWriteContents( png_structp png,
433 png_infop pngi,
434 const unsigned char * buffer,
435 const BitmapDescription * bd )
436 {
437 int row;
438 unsigned char * scratch= (unsigned char *)0;
439 int color_type= png_get_color_type( png, pngi );
440
441 if ( bd->bdColorEncoding == BMcoBLACKWHITE )
442 {
443 if ( bd->bdBitsPerPixel == 1 )
444 { png_set_invert_mono( png ); }
445 else{
446 scratch= (unsigned char *)malloc( bd->bdBytesPerRow );
447 if ( ! scratch )
448 { LXDEB(bd->bdBytesPerRow,scratch); }
449 }
450 }
451
452 if ( color_type == PNG_COLOR_TYPE_RGB &&
453 bd->bdBitsPerSample == 16 )
454 {
455 const unsigned short one= 1;
456 const unsigned char * testEndian= (const unsigned char *)&one;
457
458 /* Does not work!
459 if ( testEndian[0] )
460 { png_set_swap( png ); }
461 */
462 if ( testEndian[0] )
463 {
464 scratch= (unsigned char *)malloc( bd->bdBytesPerRow );
465 if ( ! scratch )
466 { LXDEB(bd->bdBytesPerRow,scratch); }
467 }
468 }
469
470 png_write_info( png, pngi );
471
472 for ( row= 0; row < bd->bdPixelsHigh; row++ )
473 {
474 const unsigned char * from= buffer+ row* bd->bdBytesPerRow;
475
476 if ( bd->bdColorEncoding == BMcoBLACKWHITE &&
477 scratch )
478 {
479 int col;
480 unsigned char * to= scratch;
481
482 for ( col= 0; col < bd->bdBytesPerRow; col++ )
483 { *(to++)= ~*(from++); }
484
485 from= scratch;
486 }
487
488 if ( color_type == PNG_COLOR_TYPE_RGB &&
489 bd->bdBitsPerSample == 16 &&
490 scratch )
491 {
492 int col;
493 const BmUint16 * fr= (const BmUint16 *)from;
494 unsigned char * to= scratch;
495
496 for ( col= 0; col < bd->bdBytesPerRow; col += sizeof(BmUint16) )
497 {
498 utilEndianStoreBeInt16( *(fr++), to );
499 to += sizeof(BmUint16);
500 }
501
502 from= scratch;
503 }
504
505 png_write_rows( png, (unsigned char **)&from, 1 );
506 }
507
508 png_write_end( png, pngi );
509
510 if ( scratch )
511 { free( scratch ); }
512
513 return;
514 }
515
bmWritePngFile(const MemoryBuffer * filename,const unsigned char * buffer,const BitmapDescription * bd,int privateFormat)516 int bmWritePngFile( const MemoryBuffer * filename,
517 const unsigned char * buffer,
518 const BitmapDescription * bd,
519 int privateFormat )
520 {
521 SimpleOutputStream * sos;
522
523 sos= sioOutFileioOpen( filename );
524 if ( ! sos )
525 { XDEB(sos); return -1; }
526
527 if ( bmPngWritePng( bd, buffer, sos ) )
528 { LDEB(1); sioOutClose( sos ); return -1; }
529
530 sioOutClose( sos );
531
532 return 0;
533 }
534
535 /************************************************************************/
536 /* */
537 /* Write a bitmap to a png stream. */
538 /* */
539 /************************************************************************/
540
bmPngWriteBytes(png_struct * png,png_byte * buffer,png_size_t count)541 static void bmPngWriteBytes( png_struct * png,
542 png_byte * buffer,
543 png_size_t count )
544 {
545 SimpleOutputStream * sos;
546
547 sos= (SimpleOutputStream *)png_get_io_ptr( png );
548
549 if ( sioOutWriteBytes( sos, (const unsigned char *)buffer, count )
550 != count )
551 { LDEB(count); }
552
553 return;
554 }
555
bmPngFlushBytes(png_struct * png)556 static void bmPngFlushBytes( png_struct * png )
557 { return; }
558
bmPngWritePng(const BitmapDescription * bd,const unsigned char * buffer,SimpleOutputStream * sos)559 int bmPngWritePng( const BitmapDescription * bd,
560 const unsigned char * buffer,
561 SimpleOutputStream * sos )
562 {
563 int rval= 0;
564 png_structp pngp= (png_structp)0;
565 png_infop pngip= (png_infop)0;
566 png_colorp palette= (png_colorp)0;
567
568 pngp = png_create_write_struct( PNG_LIBPNG_VER_STRING, (void *)0,
569 (png_error_ptr)0, (png_error_ptr)0 );
570 if ( ! pngp )
571 { XDEB(pngp); rval= -1; goto ready; }
572
573 pngip = png_create_info_struct( pngp );
574 if ( ! pngip )
575 { XDEB(pngip); rval= -1; goto ready; }
576
577 /*
578 As the info struct is built by libpng this is not needed:
579 (The call will disappear from libpng in version 1.4)
580 png_info_init( pngi );
581 */
582
583 if ( setjmp( png_jmpbuf( pngp ) ) )
584 { LDEB(1); rval= -1; goto ready; }
585
586 png_init_io( pngp, (FILE *)0 );
587 png_set_write_fn( pngp, (void *)sos, bmPngWriteBytes, bmPngFlushBytes );
588
589 if ( bpPngiFromBitmap( pngp, pngip, &palette, bd ) )
590 { LDEB(bd->bdColorEncoding); rval= -1; goto ready; }
591
592 bmPngWriteContents( pngp, pngip, buffer, bd );
593
594 ready:
595
596 if ( palette )
597 { free( palette ); }
598
599 png_destroy_write_struct( &pngp, &pngip );
600
601 return rval;
602 }
603