www.pudn.com > ncdzsrc.rar > png.c


/********************************************************************* 
 
  png.c 
 
  PNG reading functions. 
 
  07/15/1998 Created by Mathis Rosenhauer 
  10/02/1998 Code clean up and abstraction by Mike Balfour 
             and Mathis Rosenhauer 
  10/15/1998 Image filtering. MLR 
  11/09/1998 Bit depths 1-8 MLR 
  11/10/1998 Some additional PNG chunks recognized MLR 
  05/14/1999 Color type 2 and PNG save functions added 
  05/15/1999 Handle RGB555 while saving, use osd_fxxx 
             functions for writing MSH 
  04/27/2001 Simple MNG support MLR 
 
  TODO : Fully comply with the "Recommendations for Decoders" 
         of the W3C 
 
*********************************************************************/ 
 
#include "neogeocd.h" 
#include  
#include "zlib/zlib.h" 
 
 
/******************************************************************************** 
 
  PNG write functions 
 
********************************************************************************/ 
 
struct png_text 
{ 
	char *data; 
	int length; 
	struct png_text *next; 
}; 
 
static struct png_text *png_text_list = 0; 
 
static void convert_to_network_order(UINT32 i, UINT8 *v) 
{ 
	v[0] = (i >> 24) & 0xff; 
	v[1] = (i >> 16) & 0xff; 
	v[2] = (i >>  8) & 0xff; 
	v[3] = (i >>  0) & 0xff; 
} 
 
int png_add_text(const char *keyword, const char *text) 
{ 
	struct png_text *pt; 
 
	pt = malloc(sizeof(struct png_text)); 
	if (pt == 0) 
		return 0; 
 
	pt->length = strlen(keyword) + strlen(text) + 1; 
	pt->data = malloc(pt->length + 1); 
	if (pt->data == 0) 
		return 0; 
 
	strcpy(pt->data, keyword); 
	strcpy(pt->data + strlen(keyword) + 1, text); 
	pt->next = png_text_list; 
	png_text_list = pt; 
 
	return 1; 
} 
 
static int write_chunk(FILE *fp, UINT32 chunk_type, UINT8 *chunk_data, UINT32 chunk_length) 
{ 
	UINT32 crc; 
	UINT8 v[4]; 
	int written; 
 
	/* write length */ 
	convert_to_network_order(chunk_length, v); 
	written = fwrite(v, 1, 4, fp); 
 
	/* write type */ 
	convert_to_network_order(chunk_type, v); 
	written += fwrite(v, 1, 4, fp); 
 
	/* calculate crc */ 
	crc = crc32(0, v, 4); 
	if (chunk_length > 0) 
	{ 
		/* write data */ 
		written += fwrite(chunk_data, 1, chunk_length, fp); 
		crc = crc32(crc, chunk_data, chunk_length); 
	} 
	convert_to_network_order(crc, v); 
 
	/* write crc */ 
	written += fwrite(v, 1, 4, fp); 
 
	if (written != 3*4+chunk_length) 
	{ 
		logerror("PNG: Chunk write failed\n"); 
		return 0; 
	} 
	return 1; 
} 
 
int png_write_sig(FILE *fp) 
{ 
	/* PNG Signature */ 
	if (fwrite(PNG_Signature, 1, 8, fp) != 8) 
	{ 
		logerror("PNG: PNG sig write failed\n"); 
		return 0; 
	} 
	return 1; 
} 
 
int png_write_datastream(FILE *fp, struct png_info *p) 
{ 
	UINT8 ihdr[13]; 
	struct png_text *pt; 
 
	/* IHDR */ 
	convert_to_network_order(p->width, ihdr); 
	convert_to_network_order(p->height, ihdr+4); 
	*(ihdr+8) = p->bit_depth; 
	*(ihdr+9) = p->color_type; 
	*(ihdr+10) = p->compression_method; 
	*(ihdr+11) = p->filter_method; 
	*(ihdr+12) = p->interlace_method; 
	logerror("Type(%d) Color Depth(%d)\n", p->color_type,p->bit_depth); 
	if (write_chunk(fp, PNG_CN_IHDR, ihdr, 13)==0) 
		return 0; 
 
	/* PLTE */ 
	if (p->num_palette > 0) 
		if (write_chunk(fp, PNG_CN_PLTE, p->palette, p->num_palette*3)==0) 
			return 0; 
 
	/* IDAT */ 
	if (write_chunk(fp, PNG_CN_IDAT, p->zimage, p->zlength)==0) 
		return 0; 
 
	/* tEXt */ 
	while (png_text_list) 
	{ 
		pt = png_text_list; 
		if (write_chunk(fp, PNG_CN_tEXt, (UINT8 *)pt->data, pt->length)==0) 
			return 0; 
		free(pt->data); 
 
		png_text_list = pt->next; 
		free(pt); 
	} 
 
	/* IEND */ 
	if (write_chunk(fp, PNG_CN_IEND, NULL, 0)==0) 
		return 0; 
 
	return 1; 
} 
 
int png_filter(struct png_info *p) 
{ 
	int i; 
	UINT8 *src, *dst; 
 
	if ((p->fimage = (UINT8 *)malloc(p->height * (p->rowbytes + 1))) == NULL) 
	{ 
		logerror("PNG: Out of memory.\n"); 
		return 0; 
	} 
 
	dst = p->fimage; 
	src = p->image; 
 
	for (i = 0; i < p->height; i++) 
	{ 
		*dst++ = 0; /* No filter */ 
		memcpy(dst, src, p->rowbytes); 
		src += p->rowbytes; 
		dst += p->rowbytes; 
	} 
	return 1; 
} 
 
int png_deflate_image(struct png_info *p) 
{ 
	unsigned long zbuff_size; 
 
	zbuff_size = (p->height * (p->rowbytes + 1)) * 1.1 + 12; 
 
	if ((p->zimage = (UINT8 *)malloc(zbuff_size)) == NULL) 
	{ 
		logerror("PNG: Out of memory.\n"); 
		return 0; 
	} 
 
	if (compress(p->zimage, &zbuff_size, p->fimage, p->height * (p->rowbytes + 1)) != Z_OK) 
	{ 
		logerror("PNG: Error while deflating image\n"); 
		return 0; 
	} 
	p->zlength = zbuff_size; 
 
	return 1; 
} 
 
static int png_pack_buffer(struct png_info *p) 
{ 
	UINT8 *outp, *inp; 
	int i,j,k; 
 
	outp = inp = p->image; 
 
	if (p->bit_depth < 8) 
	{ 
		for (i = 0; i < p->height; i++) 
		{ 
			for (j=0; jwidth/(8/p->bit_depth); j++) 
			{ 
				for (k=8/p->bit_depth-1; k>=0; k--) 
					*outp |= *inp++ << k * p->bit_depth; 
				outp++; 
				*outp = 0; 
			} 
			if (p->width % (8/p->bit_depth)) 
			{ 
				for (k=p->width%(8/p->bit_depth)-1; k>=0; k--) 
					*outp |= *inp++ << k * p->bit_depth; 
				outp++; 
				*outp = 0; 
			} 
		} 
	} 
	return 1; 
} 
 
 
/********************************************************************* 
 
  Writes an osd_bitmap in a PNG file. If the depth of the bitmap 
  is 8, a color type 3 PNG with palette is written. Otherwise a 
  color type 2 true color RGB PNG is written. 
 
 *********************************************************************/ 
 
static int png_create_datastream(FILE *fp, struct osd_bitmap *bitmap) 
{ 
	int i, j; 
	UINT8 *ip; 
	struct png_info p; 
 
	memset(&p, 0, sizeof (struct png_info)); 
	p.xscale = p.yscale = p.source_gamma = 0.0; 
	p.palette = p.trans = p.image = p.zimage = p.fimage = NULL; 
	p.width = bitmap->width; 
	p.height = bitmap->height; 
	p.color_type = 2; 
	p.rowbytes = p.width * 3; 
	p.bit_depth = 8; 
 
	if ((p.image = (UINT8 *)malloc(p.height * p.rowbytes))==NULL) 
	{ 
		logerror("PNG: Out of memory.\n"); 
		return 0; 
	} 
 
	ip = p.image; 
 
	for (i = 0; i < p.height; i++) 
		for (j = 0; j < p.width; j++) 
		{ 
			int r, g, b; 
 
			osd_get_pen(((UINT16 *)bitmap->line[i])[j], &r, &g, &b); 
			*ip++ = (UINT8)r; 
			*ip++ = (UINT8)g; 
			*ip++ = (UINT8)b; 
		} 
 
	if (png_filter(&p) == 0) 
		return 0; 
 
	if (png_deflate_image(&p) == 0) 
		return 0; 
 
	if (png_write_datastream(fp, &p) == 0) 
		return 0; 
 
	if (p.palette) free(p.palette); 
	if (p.image) free(p.image); 
	if (p.zimage) free(p.zimage); 
	if (p.fimage) free(p.fimage); 
	return 1; 
} 
 
int png_write_bitmap(FILE *fp, struct osd_bitmap *bitmap) 
{ 
	png_add_text("Software", APPNAME); 
	png_add_text("System", OSNAME); 
 
	if (png_write_sig(fp) == 0) 
		return 0; 
 
	if (png_create_datastream(fp, bitmap) == 0) 
		return 0; 
 
	return 1; 
}