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;
}