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 } ZlibR;
26 typedef struct ZlibW{
27 Biobuf *bo;
28 uchar *buf;
29 uchar *b; // next place to write
30 uchar *e; // past end of buf
31 } ZlibW;
33 static ulong *crctab;
34 static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
36 static void
37 put4(uchar *a, ulong v)
38 {
39 a[0] = v>>24;
40 a[1] = v>>16;
41 a[2] = v>>8;
42 a[3] = v;
43 }
45 static void
46 chunk(Biobuf *bo, char *type, uchar *d, int n)
47 {
48 uchar buf[4];
49 ulong crc = 0;
51 if(strlen(type) != 4)
52 return;
53 put4(buf, n);
54 Bwrite(bo, buf, 4);
55 Bwrite(bo, type, 4);
56 Bwrite(bo, d, n);
57 crc = blockcrc(crctab, crc, type, 4);
58 crc = blockcrc(crctab, crc, d, n);
59 put4(buf, crc);
60 Bwrite(bo, buf, 4);
61 }
63 static int
64 zread(void *va, void *buf, int n)
65 {
66 ZlibR *z = va;
67 int nrow = z->nrow;
68 int ncol = z->ncol;
69 uchar *b = buf, *e = b+n, *img;
70 int pixels; // number of pixels in row that can be sent now
72 while(b+3 <= e){ // loop over image rows
73 if(z->row >= nrow)
74 break;
75 if(z->col==0)
76 *b++ = FilterNone;
77 pixels = (e-b)/3;
78 if(pixels > ncol - z->col)
79 pixels = ncol - z->col;
80 img = z->data + z->width * z->row + 3 * z->col;
82 memmove(b, img, 3*pixels);
83 b += 3*pixels;
85 z->col += pixels;
86 if(z->col >= ncol){
87 z->col = 0;
88 z->row++;
89 }
90 }
91 return b - (uchar*)buf;
92 }
94 static void
95 IDAT(ZlibW *z)
96 {
97 chunk(z->bo, "IDAT", z->buf, z->b - z->buf);
98 z->b = z->buf;
99 }
101 static int
102 zwrite(void *va, void *buf, int n)
104 ZlibW *z = va;
105 uchar *b = buf, *e = b+n;
106 int m;
108 while(b < e){ // loop over IDAT chunks
109 m = z->e - z->b;
110 if(m > e - b)
111 m = e - b;
112 memmove(z->b, b, m);
113 z->b += m;
114 b += m;
115 if(z->b >= z->e)
116 IDAT(z);
118 return n;
121 static Memimage*
122 memRGB(Memimage *i)
124 Memimage *ni;
126 /*
127 * BGR24 because we want R,G,B in big-endian order. Sigh.
128 */
129 if(i->chan == BGR24)
130 return i;
132 ni = allocmemimage(i->r, BGR24);
133 if(ni == nil)
134 return ni;
135 memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
136 return ni;
139 char*
140 memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II)
142 uchar buf[200], *h;
143 ulong vgamma;
144 int err, n;
145 ZlibR zr;
146 ZlibW zw;
147 int nrow = r->r.max.y - r->r.min.y;
148 int ncol = r->r.max.x - r->r.min.x;
149 Tm *tm;
150 Memimage *rgb;
152 rgb = memRGB(r);
153 if(rgb == nil)
154 return "allocmemimage nil";
155 crctab = mkcrctab(0xedb88320);
156 if(crctab == nil)
157 sysfatal("mkcrctab error");
158 deflateinit();
160 Bwrite(bo, PNGmagic, sizeof PNGmagic);
161 // IHDR chunk
162 h = buf;
163 put4(h, ncol); h += 4;
164 put4(h, nrow); h += 4;
165 *h++ = 8; // bit depth = 24 bit per pixel
166 *h++ = 2; // color type = rgb
167 *h++ = 0; // compression method = deflate
168 *h++ = 0; // filter method
169 *h++ = 0; // interlace method = no interlace
170 chunk(bo, "IHDR", buf, h-buf);
172 tm = gmtime(time(0));
173 h = buf;
174 *h++ = (tm->year + 1900)>>8;
175 *h++ = (tm->year + 1900)&0xff;
176 *h++ = tm->mon + 1;
177 *h++ = tm->mday;
178 *h++ = tm->hour;
179 *h++ = tm->min;
180 *h++ = tm->sec;
181 chunk(bo, "tIME", buf, h-buf);
183 if(II->fields_set & II_GAMMA){
184 vgamma = II->gamma*100000;
185 put4(buf, vgamma);
186 chunk(bo, "gAMA", buf, 4);
189 if(II->fields_set & II_COMMENT){
190 strncpy((char*)buf, "Comment", sizeof buf);
191 n = strlen((char*)buf)+1; // leave null between Comment and text
192 strncpy((char*)(buf+n), II->comment, sizeof buf - n);
193 chunk(bo, "tEXt", buf, n+strlen((char*)buf+n));
196 // image chunks
197 zr.nrow = nrow;
198 zr.ncol = ncol;
199 zr.width = rgb->width * sizeof(ulong);
200 zr.data = rgb->data->bdata;
201 zr.row = zr.col = 0;
202 zw.bo = bo;
203 zw.buf = malloc(IDATSIZE);
204 zw.b = zw.buf;
205 zw.e = zw.b + IDATSIZE;
206 err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0);
207 if(zw.b > zw.buf)
208 IDAT(&zw);
209 free(zw.buf);
210 if(err)
211 sysfatal("deflatezlib %s\n", flateerr(err));
212 chunk(bo, "IEND", nil, 0);
214 if(r != rgb)
215 freememimage(rgb);
216 return nil;