File: readPNG.c

package info (click to toggle)
xmhtml 1.1.10-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,296 kB
  • sloc: ansic: 70,372; makefile: 480; sh: 176; perl: 36
file content (889 lines) | stat: -rw-r--r-- 24,759 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
/*****
* readPNG.c : XmHTML png image loading routines
*
* This file Version	$Revision: 1.11 $
*
* Creation date:		Wed Feb 19 03:21:11 GMT+0100 1997
* Last modification: 	$Date: 1998/04/27 07:03:17 $
* By:					$Author: newt $
* Current State:		$State: Exp $
*
* Author:				newt
*
* Portions Copyright (C) 1994 by John Bradley. Used by permission.
* Copyright (C) 1994-1997 by Ripley Software Development 
* All Rights Reserved
*
* This file is part of the XmHTML Widget Library.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****/
/*****
* ChangeLog 
* $Log: readPNG.c,v $
* Revision 1.11  1998/04/27 07:03:17  newt
* Small bugfix for libpng 1.0.1 + tka stuff
*
* Revision 1.10  1998/04/04 06:28:35  newt
* XmHTML Beta 1.1.3
*
* Revision 1.9  1997/10/23 00:25:24  newt
* XmHTML Beta 1.1.0 release
*
* Revision 1.8  1997/08/31 17:42:05  newt
* debug level change
*
* Revision 1.7  1997/08/30 01:33:04  newt
* Fixed a few small bugs in alpha channel support & 24->8bit image conversion.
* Streamlined the ppm_quant code and added QuickRGB.
* Fixed bad proto for _XmHTMLReReadPNG when PNG support has been disabled.
*
* Revision 1.6  1997/08/01 13:12:08  newt
* Total rewrite: XmHTML now has *FULL* PNG support.
*
* Revision 1.5  1997/05/28 01:55:45  newt
* Image depth support.
*
* Revision 1.4  1997/04/29 14:31:13  newt
* Header files modifications.
*
* Revision 1.3  1997/03/20 08:15:18  newt
* Integrated png image support.
*
* Revision 1.2  1997/03/11 19:58:58  newt
* ImageBuffer changes
*
* Revision 1.1  1997/03/02 23:02:56  newt
* Initial Revision
*
*****/ 
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_LIBPNG
#include <png.h>
#include <setjmp.h>
#include <math.h>		/* required for full alpha channel processing */
#endif

#include "toolkit.h"
#include XmHTMLPrivateHeader

#ifdef HAVE_LIBPNG

/*** External Function Prototype Declarations ***/

/*** Public Variable Declarations ***/

/*** Private Datatype Declarations ****/

/*** Private Function Prototype Declarations ****/

/* various macros used by the quantization routines */
#define STORE_COLOR(R,G,B,P) do{ \
	GETR(img_data->cmap[P]) = (R); GETG(img_data->cmap[P]) = (G); \
	GETB(img_data->cmap[P]) = (B); GETP(img_data->cmap[P]) = P; \
}while(0)

/*** Private Variable Declarations ***/
/* background gamma correction used in alpha channel processing */
#define BG_GAMMA_CORRECTION		2.2222222222

/* the maximum value a color component can have */
#define MAX_RGB_VAL	255

/*****
* Name: 		my_png_error
* Return Type: 	void
* Description: 	png error function
* In: 
*	png_ptr:	current png stream;
*	msg:		message to be displayed;
* Returns:
*	nothing.
* Note:
*	This routine displays a warning message and terminates PNG reading
*	by jumping to the point where the error function was set.
*****/
static void
my_png_error(png_structp png_ptr, String msg)
{
	ImageBuffer *ib = (ImageBuffer*)png_get_io_ptr(png_ptr);

	_XmHTMLWarning(__WFUNC__(NULL, "png_error"), XMHTML_MSG_121, "png",
		ib->file, msg);
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	longjmp(png_jmpbuf(png_ptr), 1);
#else
 	longjmp(png_ptr->jmpbuf, 1);
#endif
}

/*****
* Name: 		my_png_read
* Return Type: 	void
* Description: 	function called by png when it needs another chunk of data
* In: 
*	png_ptr:	current png stream;
*	data:		return buffer;
*	len:		no of bytes to be copied.
* Returns:
*	nothing but len bytes are copied into the return buffer.
* Note:
*	This function is used instead of the default png reader (which uses fread)
*	as we have the file data in memory.
*****/
static void
my_png_read(png_structp png_ptr, png_bytep data, png_size_t len)
{
	ImageBuffer *ib = (ImageBuffer*)png_get_io_ptr(png_ptr);
	int size = (int)len;

	if(ib->size > ib->next)
	{
		if(ib->next + size > ib->size)
			size = ib->size - ib->next;
		memcpy(data, ib->buffer + ib->next, size);
		ib->next += size;
		return;
	}
	my_png_error(png_ptr, "Read Error");
}

/*****
* Name: 		_XmHTMLReadPNG
* Return Type: 	XmHTMLRawImageData*
* Description: 	reads a PNG (Portable Network Graphics) image
* In: 
*
* Returns:
*	loaded image data upon success or NULL on failure
*****/
XmHTMLRawImageData*
_XmHTMLReadPNG(Widget html, ImageBuffer *ib)
{
	png_structp png_ptr;
	png_infop info_ptr;
	Byte *data;
	int i, idx, npass;
	int width, height, color_type;
	int ncolors, max_colors;
	double gamma, fg_gamma;
	Boolean has_alpha = False, has_cmap = False, do_gamma = True;
	png_bytep *row_ptrs;
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	png_colorp palette;
#endif
	char msg[128];
	static XmHTMLRawImageData *img_data;

	img_data = NULL;
	data = 0;

	_XmHTMLDebug(15, ("readPNG.c: _XmHTMLreadPNG Start, loading %s\n",
		ib->file));

	/*
	* get configuration defaults
	*/
	if(XmIsHTML(html))
	{
		max_colors = HTML_ATTR(max_image_colors);
		gamma      = HTML_ATTR(screen_gamma);
	}
	else
	{
		/* external image support. Requires an xmimage configuration */
		if(_xmimage_cfg == NULL)
			return(NULL);

		if(_xmimage_cfg->flags && ImageQuantize)
			max_colors = _xmimage_cfg->ncolors;
		else
			max_colors = XmHTML_MAX_IMAGE_COLORS;
		if(_xmimage_cfg->flags & ImageScreenGamma)
			gamma = _xmimage_cfg->gamma;
		else
			gamma = XmHTML_DEFAULT_GAMMA;
	}

	/* We set up the normal PNG error routines, then override with longjmp. */
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
		NULL, NULL, NULL);

	/* Create and initialize the info structure */
	if((info_ptr = png_create_info_struct(png_ptr)) == NULL)
	{
		/* failed, too bad */
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		return((XmHTMLRawImageData*)NULL);
	}
	/* now set error handler */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	if(setjmp(png_jmpbuf(png_ptr)))
#else
 	if(setjmp(png_ptr->jmpbuf))
#endif
	{
		/* 
		* PNG signalled an error. Destroy image data, free any allocated
		* buffers and return NULL.
		*/
		_XmHTMLDebug(15, ("_XmHTMLreadPNG: end, libpng internal error.\n"));
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		if(img_data)
			FreeRawImage(img_data);
		if(data)
			free(data);
		return((XmHTMLRawImageData*)NULL);
	}
	/*
	* We have the entire image in memory, so we need to set our own
	* ``fread'' function for libpng to use.
	*/
	png_set_read_fn(png_ptr, ib, &my_png_read);

	/* by now we already know this is a png */
	ib->next = 8;
	png_set_sig_bytes(png_ptr, 8);

	/* get png info */
	png_read_info(png_ptr, info_ptr);

	/* allocate raw image data */
	img_data = (XmHTMLRawImageData*)malloc(sizeof(XmHTMLRawImageData));

	ResetRawImage(img_data);

	/* save width & height */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	width  = img_data->width  = png_get_image_width(png_ptr, info_ptr);
	height = img_data->height = png_get_image_height(png_ptr, info_ptr);
#else
	width  = img_data->width  = info_ptr->width;
	height = img_data->height = info_ptr->height;
#endif

	/* image depth */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	ib->depth = png_get_bit_depth(png_ptr, info_ptr);
#else
	ib->depth = info_ptr->bit_depth;
#endif

	/* no of colors */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	png_get_PLTE(png_ptr, info_ptr, &palette, &ncolors);
	img_data->cmapsize = ncolors;
#else
	ncolors = img_data->cmapsize = info_ptr->num_palette;
#endif

	/* type of image */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	color_type = png_get_color_type(png_ptr, info_ptr);
#else
	color_type = info_ptr->color_type;
#endif

	/*
	* The fun stuff. This is based on readPNG by Greg Roelofs as found
	* in xpaint 2.4.8, which falls under the same distribution note
	* as readGIF by David Koblas (which is the original author of xpaint).
	* It has been quite heavily modified but it was an invaluable starting
	* point!
	*/
	switch(color_type)
	{
		case PNG_COLOR_TYPE_PALETTE:
			_XmHTMLDebug(15, ("readPNG.c: PNG_COLOR_TYPE_PALETTE\n"));
			img_data->color_class = XmIMAGE_COLORSPACE_INDEXED;
			/*
			* paletted images never contain more than 256 colors but
			* check anyway.
			*/
			if(ncolors > 256)
			{
				sprintf(msg, "PNG_COLOR_TYPE_PALETTE: %i colors reported "
					"while max is 256.", ncolors);
				my_png_error(png_ptr, msg);
			}

			/*
			* Paletted images with transparency info are expanded to
			* RGB with alpha channel.
			* Actual image creation is postponed until the image is
			* needed.
			*/
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
			if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
#else
			if(info_ptr->valid & PNG_INFO_tRNS)
#endif
			{
				_XmHTMLDebug(15, ("readPNG.c: tRNS chunk present\n"));
				png_set_expand(png_ptr);
				has_alpha = True;
				do_gamma = False;
			}
			else
			{
				/* store colormap and allocate buffer for read image data */
				AllocRawImageCmap(img_data, ncolors);
				for(i = 0; i < ncolors; i++)
				{
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
					GETR(img_data->cmap[i]) = palette[i].red;
					GETG(img_data->cmap[i]) = palette[i].green;
					GETB(img_data->cmap[i]) = palette[i].blue;
#else
					GETR(img_data->cmap[i]) = info_ptr->palette[i].red;
					GETG(img_data->cmap[i]) = info_ptr->palette[i].green;
					GETB(img_data->cmap[i]) = info_ptr->palette[i].blue;
#endif
				}
				has_cmap = True;
				data = (Byte*)malloc(width*height*sizeof(Byte));
			}
			break;
		case PNG_COLOR_TYPE_RGB:
			_XmHTMLDebug(15, ("readPNG.c: PNG_COLOR_TYPE_RGB\n"));
			img_data->color_class = XmIMAGE_COLORSPACE_RGB;
			if(ib->depth == 16)
			{
				_XmHTMLDebug(15, ("readPNG: stripping 48-bit RGB image to "
					"24 bits\n"));
				png_set_strip_16(png_ptr);
				ib->depth = 8;
			}
			/* image data */
			data = (Byte*)malloc(width*height*sizeof(Byte)*3);
			break;

		case PNG_COLOR_TYPE_GRAY:
			_XmHTMLDebug(15, ("readPNG.c: PNG_COLOR_TYPE_GRAY\n"));
			img_data->color_class = XmIMAGE_COLORSPACE_GRAYSCALE;
			if(ib->depth == 16)
			{
				_XmHTMLDebug(15, ("readPNG: stripping 16-bit grayscale image "
					"to 8 bits\n"));
				png_set_strip_16(png_ptr);
				ib->depth = 8;
			}
			/*
			* grayscale with transparency is expanded to RGB with alpha
			* channel.
			*/
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
			if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
#else
			if(info_ptr->valid & PNG_INFO_tRNS)
#endif
			{
				_XmHTMLDebug(15, ("readPNG.c: tRNS chunk present\n"));
				png_set_gray_to_rgb(png_ptr);
				png_set_expand(png_ptr);
				has_alpha = True;
				do_gamma = False;
				break;
			}

			/* fill in appropriate grayramp */
			switch(ib->depth)
			{
				case 1:
					/* allocate colormap */
					ncolors = 2;
					AllocRawImageCmap(img_data, ncolors);
					/* fill it */
					STORE_COLOR(0, 0, 0, 0);
					STORE_COLOR(255, 255, 255, 1);
					break;
				case 2:
					/* allocate colormap */
					ncolors = 4;
					AllocRawImageCmap(img_data, ncolors);
					/* fill it */
					STORE_COLOR(0, 0, 0, 0);
					STORE_COLOR(85,  85,  85,  1);  /* 255/3 */
					STORE_COLOR(170, 170, 170, 2);
					STORE_COLOR(255, 255, 255, 3);
					break;
				case 4: 
					/* allocate colormap */
					ncolors = 16;
					AllocRawImageCmap(img_data, ncolors);
					/* fill it */
					for (i = 0;  i < 16;  ++i)
					{
						idx = i * 17;  /* 255/15 */
						STORE_COLOR(idx, idx, idx, i);
					}
					break;
				case 8: 
					/* allocate colormap */
					ncolors = 256;
					AllocRawImageCmap(img_data, ncolors);
					/* fill it */
					for (i = 0;  i < 256;  ++i)
						STORE_COLOR(i, i, i, i);
					break;
			}
			/* valid colormap */
			has_cmap = True;

			/* image data */
			data = (Byte*)malloc(width*height*sizeof(Byte));
			break;

		case PNG_COLOR_TYPE_RGB_ALPHA:
			_XmHTMLDebug(15, ("readPNG.c: PNG_COLOR_TYPE_RGB_ALPHA\n"));
			img_data->color_class = XmIMAGE_COLORSPACE_RGB;
			do_gamma = False;
			has_alpha = True;

			/* strip 16bit down to 8bits */
            if(ib->depth == 16)
                png_set_strip_16(png_ptr);
			break;
		case PNG_COLOR_TYPE_GRAY_ALPHA:
			_XmHTMLDebug(15, ("readPNG.c: PNG_COLOR_TYPE_GRAY_ALPHA\n"));
			img_data->color_class = XmIMAGE_COLORSPACE_GRAYSCALE;
			do_gamma = False;
			has_alpha = True;

			/* expand to rgb */
			png_set_gray_to_rgb(png_ptr);
			break;
		default:
			sprintf(msg, "bad PNG image: unknown color type (%d)",
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
				png_get_color_type(png_ptr, info_ptr));
#else
				info_ptr->color_type);
#endif
			my_png_error(png_ptr, msg);
			break;
	}

	/*
	* We only substitute background pixel if we don't have an alpha channel
	* Doing that for alpha channel images would change the colortype of the
	* current image, leading to weird results.
	*/
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	if(!has_alpha && png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD))
	{
		png_color_16p background;
		png_get_bKGD(png_ptr, info_ptr, &background);
		png_set_background(png_ptr, background,
			PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
		img_data->bg = background->index;
	}
#else
	if(!has_alpha && info_ptr->valid & PNG_INFO_bKGD)
	{
		png_set_background(png_ptr, &(info_ptr->background),
			PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
		img_data->bg = info_ptr->background.index;
	}
#endif

	/* handle gamma correction */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	if(png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA))
		png_get_gAMA(png_ptr, info_ptr, &fg_gamma);
#else
	if(info_ptr->valid & PNG_INFO_gAMA)
		fg_gamma = info_ptr->gamma;
#endif
	else
		fg_gamma = 0.45;

	/* set it */
	if(do_gamma)
		png_set_gamma(png_ptr, gamma, fg_gamma);

	/* dithering gets handled by caller */

	/* one byte per pixel */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	if(png_get_bit_depth(png_ptr, info_ptr) < 8)
#else
	if(info_ptr->bit_depth < 8)
#endif
		png_set_packing(png_ptr);

	/* no tRNS chunk handling, we've expanded it to an alpha channel. */

	/* handle interlacing */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	if(png_get_interlace_type(png_ptr, info_ptr))
#else
	if(info_ptr->interlace_type)
#endif
		npass = png_set_interlace_handling(png_ptr);

	/* and now update everything */
	png_read_update_info(png_ptr, info_ptr);

	/* has possibly changed if we have promoted GrayScale or tRNS chunks */
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
	color_type = png_get_color_type(png_ptr, info_ptr);
#else
	color_type = info_ptr->color_type;
#endif

	/* new color_type? */
	if(color_type == PNG_COLOR_TYPE_RGB_ALPHA)
	{
		png_bytep png_data;
		/*
		* RGB image with Alpha channel. Actual decoding of this image is
		* delayed until it's required.
		*
		* This approach is needed as at this point we don't know the position
		* of the image in the document and hence we can't correctly handle
		* proper alpha channel processing.
		*
		* Setting the delayed_creation flag instructs XmHTML to create an
		* empty XmHTMLImage structure and delays the actual image composition
		* until the position of this image is known. At that point the painter
		* will call doAlphaChannel to do the actual image creation. 
		*/
		row_ptrs = (png_bytep*)malloc(height*sizeof(png_bytep));
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
		png_data = (png_bytep)malloc(height*png_get_rowbytes(png_ptr, info_ptr));
#else
		png_data = (png_bytep)malloc(height*info_ptr->rowbytes);
#endif

		for(i = 0; i < height; i++)
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
			row_ptrs[i] = (png_bytep)png_data + i*png_get_rowbytes(png_ptr, info_ptr);
#else
			row_ptrs[i] = (png_bytep)png_data + i*info_ptr->rowbytes;
#endif

		/* read it */
		png_read_image(png_ptr, row_ptrs);

		img_data->data = png_data;			/* raw image data */

		/* no longer needed */
		free(row_ptrs);

		/* set flag so XmHTML will know what to do with this image */
		img_data->delayed_creation = True;

		/* PNG cleanup */
		png_read_end(png_ptr, info_ptr);
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

		_XmHTMLDebug(15, ("_XmHTMLreadPNG: end, image creation delayed.\n"));

		/* store image gamma value. We need it later on */
		img_data->fg_gamma = fg_gamma; 
		img_data->type = IMAGE_PNG;
		return(img_data);
	}

	/* no alpha channel */
	row_ptrs = (png_bytep*)malloc(height*sizeof(png_bytep));

	for(i = 0; i < height; ++i)
#if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4
		row_ptrs[i] = (png_bytep)data + i*png_get_rowbytes(png_ptr, info_ptr);
#else
		row_ptrs[i] = (png_bytep)data + i*info_ptr->rowbytes;
#endif

	/* read it */
	png_read_image(png_ptr, row_ptrs);

	/* no longer needed */
	free(row_ptrs);

	/*****
	* We're lucky: having a colormap means we have an indexed image.
	*****/
	if(has_cmap)
	{
		img_data->data = data;
		/* upscale colormap to 16bit values */
		for(i = 0; i < img_data->cmapsize; i++)
		{
			GETR(img_data->cmap[i]) <<= 8;
			GETG(img_data->cmap[i]) <<= 8;
			GETB(img_data->cmap[i]) <<= 8;
		}
	}
	else
	{
		/*****
		* RGB image. Convert to paletted image.
		* First allocate a buffer which will receive the final image data.
		*****/
		img_data->data = (Byte*)malloc(width*height*sizeof(Byte));

		/* convert it. On return, the colormap will hold 16bit RGB values */
		if(XmIsHTML(html))
			_XmHTMLConvert24to8(data, img_data,
				HTML_ATTR(max_image_colors), HTML_ATTR(rgb_conv_mode));
		else
			_XmHTMLConvert24to8(data, img_data, XmHTML_MAX_IMAGE_COLORS,
				XmBEST);

		/* and we no longer need the image data itself */
		free(data);
	}

	/* PNG cleanup */
	png_read_end(png_ptr, info_ptr);
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

	_XmHTMLDebug(15, ("_XmHTMLreadPNG: end, image loaded.\n"));
	
	return(img_data);
}

/*****
* Name: 		_XmHTMLReReadPNG
* Return Type: 	XmHTMLRawImageData*
* Description: 	rereads a PNG (Portable Network Graphics) image
* In: 
*	html:		current XmHTMLWidget
*	ib:			current image buffer;
*	x,y:		absolute document position for this image;
*	is_body..:	set when this image *is* the body image.
* Returns:
*	loaded image data upon success or NULL on failure
* Note:
*	This function is *only* called for RGB images with a tRNS chunk or
*	alpha channel support. It's purpose is to reprocess the raw image data
*	so we can properly deal with the alpha channel. For overall PNG comments
*	see the above routine. 
*	This routine is called by doAlphaChannel().
*****/
XmHTMLRawImageData*
_XmHTMLReReadPNG(XmHTMLWidget html, XmHTMLRawImageData *raw_data, int x,
	int y, Boolean is_body_image)
{
	Byte *png, *rgb, *pp;			/* various ptrs */
	Byte *currLine;					/* temporary buffer */
	int i, j, k;					/* various counters */
	int width, height;				/* image dimensions */
	float gamma, fg_gamma;			/* display and image gamma */
	int background[3];				/* background pixel: R, G, B */
	int foreground[4];				/* image pixel: R, G, B, A */
	int fbpix[3];					/* final image pixel: R, G, B */
	int fb_maxsample;
	XmHTMLImage *bg_image = NULL;
	int bg_width = 0, bg_height = 0;
	Byte *bg_data = NULL;
	AlphaPtr alpha_buffer;
	static XmHTMLRawImageData *img_data; /* return data */

	/* various alpha channel processing variables */
	int ialpha;
	float alpha, compalpha;
	float gamfg, linfg, gambg, linbg, comppix, gcvideo;

	_XmHTMLDebug(15, ("readPNG.c: _XmHTMLRereadPNG Start.\n"));

	/* background alpha channel stuff */
	alpha_buffer = html->html.alpha_buffer;

	/* display gamma */
	gamma = html->html.screen_gamma;
	/* file gamma */
	fg_gamma = raw_data->fg_gamma;

	fb_maxsample = alpha_buffer->fb_maxsample;

	/*
	* If we do not have a body image (or this image *is* the body image),
	* use the background color. Else we need to get the RGB components
	* of the colors used by the background image.
	*/
	if(!is_body_image && alpha_buffer->ncolors)
	{
		bg_image  = html->html.body_image;
		bg_width  = bg_image->width;
		bg_height = bg_image->height;
		bg_data   = bg_image->html_image->data;
		_XmHTMLDebug(15, ("readPNG.c: using background image.\n"));
	}
	else
	{
		background[0] = alpha_buffer->background[0];
		background[1] = alpha_buffer->background[1];
		background[2] = alpha_buffer->background[2];
		_XmHTMLDebug(15, ("readPNG.c: using background color.\n"));
	}

	/* get image width */
	width  = raw_data->width;
	height = raw_data->height;

	AllocRawImage(img_data, width, height);

	/* raw png image data (source buffer) */
	png = raw_data->data;
	/* intermediate destination buffer, contains alpha-corrected values */
	currLine = (Byte*)malloc(width*height*sizeof(Byte)*3);
	rgb = currLine;
	/* final destination buffer */
	pp = img_data->data;

	/*****
	* Actual alpha channel processing. This code is based on the alpha
	* channel example in the official W3C PNG Recommendation which uses
	* floating point all over the place.
	*
	* Performance increases can be gained by:
	* - using lookup tables;
	* - handling body-image/body-color separate;
	* - integrate dithering.
	*****/
	for(i = 0; i < height; i++)
	{
		/* do a single scanline */
		for(j = 0; j < width; ++j)
		{
			/* tile position */
			if(bg_data)
			{
				/* compute correct tile offset */
				int dx = (j+x) % bg_width;
				int dy = (i+y) % bg_height;
				int i = dy * bg_width + dx;
				int idx = (int)bg_data[i];
				background[0] = GETR(alpha_buffer->bg_cmap[idx]);
				background[1] = GETG(alpha_buffer->bg_cmap[idx]);
				background[2] = GETB(alpha_buffer->bg_cmap[idx]);
			}

			/* get foreground color */
			foreground[0] = *png++;		/* red */
			foreground[1] = *png++;		/* green */
			foreground[2] = *png++;		/* blue */
			foreground[3] = *png++;		/* alpha */

			/*
			* Get integer version of alpha.
			* Check for opaque and transparent special cases;
			* no compositing needed if so.
			*
			* We show the whole gamma decode/correct process in floating
			* point, but it would more likely be done with lookup tables.
			*/
			ialpha = foreground[3];
			if(ialpha == 0)
			{
				/* Foreground is transparent. replace with background */
				fbpix[0] = background[0];
				fbpix[1] = background[1];
				fbpix[2] = background[2];
			}
			else if(ialpha == MAX_RGB_VAL)
			{
				/* Copy foreground pixel to frame buffer. */
				for(k = 0; k < 3; k++)
				{
					gamfg    = (float) foreground[k] / MAX_RGB_VAL;
					linfg    = pow(gamfg, 1.0/fg_gamma);
					comppix  = linfg;
					gcvideo  = pow(comppix, 1.2/gamma);
					fbpix[k] = (int) (gcvideo * fb_maxsample + 0.5);
				}
			}
			else
			{	
				/*
				* Compositing is necessary.
				* Get floating-point alpha and its complement.
				* Note: alpha is always linear; gamma does not affect it.
				*/
				alpha = (float) ialpha / MAX_RGB_VAL;
				compalpha = 1.0 - alpha;
   
				for(k = 0; k < 3; k++)
				{
					/*
					* Convert foreground and background to floating
					* point, then linearize (undo gamma encoding).
					*/
					gamfg = (float) foreground[k] / MAX_RGB_VAL;
					linfg = pow(gamfg, 1.0/fg_gamma);
					gambg = (float) background[k] / MAX_RGB_VAL;
					linbg = pow(gambg, BG_GAMMA_CORRECTION);

					/* Composite */
					comppix = linfg * alpha + linbg * compalpha;

					/*
					* Gamma correct for display.
					* Convert to integer frame buffer pixel.
					* We assume a viewing gamma of 1.2 
					*/
					gcvideo = pow(comppix, 1.2/gamma);
					fbpix[k] = (int) (gcvideo * fb_maxsample + 0.5);
				}
			}
			/* and store it */
			*rgb++ = (Byte)fbpix[0];
			*rgb++ = (Byte)fbpix[1];
			*rgb++ = (Byte)fbpix[2];
		}
	}
	/* and reduce to 8bit paletted image */
	_XmHTMLConvert24to8(currLine, img_data, HTML_ATTR(max_image_colors),
				HTML_ATTR(rgb_conv_mode));
	free(currLine);

	/* copy untouched fields */
	img_data->bg   = raw_data->bg;
	img_data->type = raw_data->type;
	img_data->color_class = raw_data->color_class;

	_XmHTMLDebug(15, ("_XmHTMLReReadPNG: end, image loaded.\n"));
	
	return(img_data);
}

#else	/* !HAVE_LIBPNG */

/* empty func if PNG isn't supported */
/* ARGSUSED */
XmHTMLRawImageData*
_XmHTMLReadPNG(Widget html, ImageBuffer *ib)
{
	return((XmHTMLRawImageData*)NULL);
}

/* ARGSUSED */
XmHTMLRawImageData*
_XmHTMLReReadPNG(XmHTMLWidget html, XmHTMLRawImageData *raw_data, int x,
	int y, Boolean is_body_image)
{
	return((XmHTMLRawImageData*)NULL);
}
#endif /* HAVE_LIBPNG */