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