plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

writepng.c (4923B)


      1 /* based on PNG 1.2 specification, July 1999  (see also rfc2083) */
      2 /* Alpha is not supported yet because of lack of industry acceptance and */
      3 /* because Plan9 Image uses premultiplied alpha, so png can't be lossless. */
      4 /* Only 24bit color supported, because 8bit may as well use GIF. */
      5 
      6 #include <u.h>
      7 #include <libc.h>
      8 #include <draw.h>
      9 #include <memdraw.h>
     10 #include <ctype.h>
     11 #include <bio.h>
     12 #include <flate.h>
     13 #include "imagefile.h"
     14 
     15 enum{	IDATSIZE = 	20000,
     16 	FilterNone =	0
     17 };
     18 
     19 typedef struct ZlibR{
     20 	uchar *data;
     21 	int width;
     22 	int nrow, ncol;
     23 	int row, col;	/* next pixel to send */
     24 	int pixwid;
     25 } ZlibR;
     26 
     27 typedef struct ZlibW{
     28 	Biobuf *bo;
     29 	uchar *buf;
     30 	uchar *b;	/* next place to write */
     31 	uchar *e;	/* past end of buf */
     32 } ZlibW;
     33 
     34 static uint32 *crctab;
     35 static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
     36 
     37 static void
     38 put4(uchar *a, uint32 v)
     39 {
     40 	a[0] = v>>24;
     41 	a[1] = v>>16;
     42 	a[2] = v>>8;
     43 	a[3] = v;
     44 }
     45 
     46 static void
     47 chunk(Biobuf *bo, char *type, uchar *d, int n)
     48 {
     49 	uchar buf[4];
     50 	uint32 crc = 0;
     51 
     52 	if(strlen(type) != 4)
     53 		return;
     54 	put4(buf, n);
     55 	Bwrite(bo, buf, 4);
     56 	Bwrite(bo, type, 4);
     57 	Bwrite(bo, d, n);
     58 	crc = blockcrc(crctab, crc, type, 4);
     59 	crc = blockcrc(crctab, crc, d, n);
     60 	put4(buf, crc);
     61 	Bwrite(bo, buf, 4);
     62 }
     63 
     64 static int
     65 zread(void *va, void *buf, int n)
     66 {
     67 	ZlibR *z = va;
     68 	int nrow = z->nrow;
     69 	int ncol = z->ncol;
     70 	uchar *b = buf, *e = b+n, *img;
     71 	int pixels;  /* number of pixels in row that can be sent now */
     72 	int i, a, pixwid;
     73 
     74 	pixwid = z->pixwid;
     75 	while(b+pixwid <= e){ /* loop over image rows */
     76 		if(z->row >= nrow)
     77 			break;
     78 		if(z->col==0)
     79 			*b++ = FilterNone;
     80 		pixels = (e-b)/pixwid;
     81 		if(pixels > ncol - z->col)
     82 			pixels = ncol - z->col;
     83 		img = z->data + z->width * z->row + pixwid * z->col;
     84 
     85 		memmove(b, img, pixwid*pixels);
     86 		if(pixwid == 4){
     87 			/*
     88 			 * Convert to non-premultiplied alpha.
     89 			 */
     90 			for(i=0; i<pixels; i++, b+=4){
     91 				a = b[3];
     92 				if(a == 255 || a == 0)
     93 					;
     94 				else{
     95 					if(b[0] >= a)
     96 						b[0] = a;
     97 					b[0] = (b[0]*255)/a;
     98 					if(b[1] >= a)
     99 						b[1] = a;
    100 					b[1] = (b[1]*255)/a;
    101 					if(b[2] >= a)
    102 						b[2] = a;
    103 					b[2] = (b[2]*255)/a;
    104 				}
    105 			}
    106 		}else
    107 			b += pixwid*pixels;
    108 
    109 		z->col += pixels;
    110 		if(z->col >= ncol){
    111 			z->col = 0;
    112 			z->row++;
    113 		}
    114 	}
    115 	return b - (uchar*)buf;
    116 }
    117 
    118 static void
    119 IDAT(ZlibW *z)
    120 {
    121 	chunk(z->bo, "IDAT", z->buf, z->b - z->buf);
    122 	z->b = z->buf;
    123 }
    124 
    125 static int
    126 zwrite(void *va, void *buf, int n)
    127 {
    128 	ZlibW *z = va;
    129 	uchar *b = buf, *e = b+n;
    130 	int m;
    131 
    132 	while(b < e){ /* loop over IDAT chunks */
    133 		m = z->e - z->b;
    134 		if(m > e - b)
    135 			m = e - b;
    136 		memmove(z->b, b, m);
    137 		z->b += m;
    138 		b += m;
    139 		if(z->b >= z->e)
    140 			IDAT(z);
    141 	}
    142 	return n;
    143 }
    144 
    145 static Memimage*
    146 memRGBA(Memimage *i)
    147 {
    148 	Memimage *ni;
    149 	char buf[32];
    150 	ulong dst;
    151 
    152 	/*
    153 	 * [A]BGR because we want R,G,B,[A] in big-endian order.  Sigh.
    154 	 */
    155 	chantostr(buf, i->chan);
    156 	if(strchr(buf, 'a'))
    157 		dst = ABGR32;
    158 	else
    159 		dst = BGR24;
    160 
    161 	if(i->chan == dst)
    162 		return i;
    163 
    164 	ni = allocmemimage(i->r, dst);
    165 	if(ni == nil)
    166 		return ni;
    167 	memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
    168 	return ni;
    169 }
    170 
    171 char*
    172 memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II)
    173 {
    174 	uchar buf[200], *h;
    175 	ulong vgamma;
    176 	int err, n;
    177 	ZlibR zr;
    178 	ZlibW zw;
    179 	int nrow = r->r.max.y - r->r.min.y;
    180 	int ncol = r->r.max.x - r->r.min.x;
    181 	Tm *tm;
    182 	Memimage *rgb;
    183 
    184 	rgb = memRGBA(r);
    185 	if(rgb == nil)
    186 		return "allocmemimage nil";
    187 	crctab = mkcrctab(0xedb88320);
    188 	if(crctab == nil)
    189 		sysfatal("mkcrctab error");
    190 	deflateinit();
    191 
    192 	Bwrite(bo, PNGmagic, sizeof PNGmagic);
    193 	/* IHDR chunk */
    194 	h = buf;
    195 	put4(h, ncol); h += 4;
    196 	put4(h, nrow); h += 4;
    197 	*h++ = 8; /* bit depth = 24 bit per pixel */
    198 	*h++ = rgb->chan==BGR24 ? 2 : 6; /* color type = rgb */
    199 	*h++ = 0; /* compression method = deflate */
    200 	*h++ = 0; /* filter method */
    201 	*h++ = 0; /* interlace method = no interlace */
    202 	chunk(bo, "IHDR", buf, h-buf);
    203 
    204 	tm = gmtime(time(0));
    205 	h = buf;
    206 	*h++ = (tm->year + 1900)>>8;
    207 	*h++ = (tm->year + 1900)&0xff;
    208 	*h++ = tm->mon + 1;
    209 	*h++ = tm->mday;
    210 	*h++ = tm->hour;
    211 	*h++ = tm->min;
    212 	*h++ = tm->sec;
    213 	chunk(bo, "tIME", buf, h-buf);
    214 
    215 	if(II->fields_set & II_GAMMA){
    216 		vgamma = II->gamma*100000;
    217 		put4(buf, vgamma);
    218 		chunk(bo, "gAMA", buf, 4);
    219 	}
    220 
    221 	if(II->fields_set & II_COMMENT){
    222 		strncpy((char*)buf, "Comment", sizeof buf);
    223 		n = strlen((char*)buf)+1; /* leave null between Comment and text */
    224 		strncpy((char*)(buf+n), II->comment, sizeof buf - n);
    225 		chunk(bo, "tEXt", buf, n+strlen((char*)buf+n));
    226 	}
    227 
    228 	/* image chunks */
    229 	zr.nrow = nrow;
    230 	zr.ncol = ncol;
    231 	zr.width = rgb->width * sizeof(uint32);
    232 	zr.data = rgb->data->bdata;
    233 	zr.row = zr.col = 0;
    234 	zr.pixwid = chantodepth(rgb->chan)/8;
    235 	zw.bo = bo;
    236 	zw.buf = malloc(IDATSIZE);
    237 	zw.b = zw.buf;
    238 	zw.e = zw.b + IDATSIZE;
    239 	err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0);
    240 	if(zw.b > zw.buf)
    241 		IDAT(&zw);
    242 	free(zw.buf);
    243 	if(err)
    244 		sysfatal("deflatezlib %s\n", flateerr(err));
    245 	chunk(bo, "IEND", nil, 0);
    246 
    247 	if(r != rgb)
    248 		freememimage(rgb);
    249 	return nil;
    250 }