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 }