Blob


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. */
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"
15 enum{ IDATSIZE = 20000,
16 FilterNone = 0
17 };
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;
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;
34 static uint32 *crctab;
35 static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
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 }
46 static void
47 chunk(Biobuf *bo, char *type, uchar *d, int n)
48 {
49 uchar buf[4];
50 uint32 crc = 0;
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 }
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;
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;
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;
106 }else
107 b += pixwid*pixels;
109 z->col += pixels;
110 if(z->col >= ncol){
111 z->col = 0;
112 z->row++;
115 return b - (uchar*)buf;
118 static void
119 IDAT(ZlibW *z)
121 chunk(z->bo, "IDAT", z->buf, z->b - z->buf);
122 z->b = z->buf;
125 static int
126 zwrite(void *va, void *buf, int n)
128 ZlibW *z = va;
129 uchar *b = buf, *e = b+n;
130 int m;
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);
142 return n;
145 static Memimage*
146 memRGBA(Memimage *i)
148 Memimage *ni;
149 char buf[32];
150 ulong dst;
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;
161 if(i->chan == dst)
162 return i;
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;
171 char*
172 memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II)
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;
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();
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);
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);
215 if(II->fields_set & II_GAMMA){
216 vgamma = II->gamma*100000;
217 put4(buf, vgamma);
218 chunk(bo, "gAMA", buf, 4);
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));
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);
247 if(r != rgb)
248 freememimage(rgb);
249 return nil;