1 #include "stdinc.h"
2 #include "dat.h"
3 #include "fns.h"
4 
5 enum
6 {
7 	IDATSIZE	= 20000,
8 	FilterNone = 0
9 };
10 
11 typedef struct ZlibR ZlibR;
12 typedef struct ZlibW ZlibW;
13 
14 struct ZlibR
15 {
16 	uchar *data;
17 	int width;
18 	int dx;
19 	int dy;
20 	int x;
21 	int y;
22 	int pixwid;
23 };
24 
25 struct ZlibW
26 {
27 	Hio *io;
28 	uchar *buf;
29 	uchar *b;
30 	uchar *e;
31 };
32 
33 static u32int *crctab;
34 static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
35 
36 static void
put4(uchar * a,ulong v)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 }
44 
45 static void
chunk(Hio * io,char * type,uchar * d,int n)46 chunk(Hio *io, char *type, uchar *d, int n)
47 {
48 	uchar buf[4];
49 	ulong crc = 0;
50 
51 	if(strlen(type) != 4)
52 		return;
53 	put4(buf, n);
54 	hwrite(io, buf, 4);
55 	hwrite(io, type, 4);
56 	hwrite(io, d, n);
57 	crc = blockcrc(crctab, crc, type, 4);
58 	crc = blockcrc(crctab, crc, d, n);
59 	put4(buf, crc);
60 	hwrite(io, buf, 4);
61 }
62 
63 static int
zread(void * va,void * buf,int n)64 zread(void *va, void *buf, int n)
65 {
66 	int a, i, pixels, pixwid;
67 	uchar *b, *e, *img;
68 	ZlibR *z;
69 
70 	z = va;
71 	pixwid = z->pixwid;
72 	b = buf;
73 	e = b+n;
74 	while(b+pixwid <= e){
75 		if(z->y >= z->dy)
76 			break;
77 		if(z->x == 0)
78 			*b++ = FilterNone;
79 		pixels = (e-b)/pixwid;
80 		if(pixels > z->dx - z->x)
81 			pixels = z->dx - z->x;
82 		img = z->data + z->width*z->y + pixwid*z->x;
83 		memmove(b, img, pixwid*pixels);
84 		if(pixwid == 4){
85 			/*
86 			 * Convert to non-premultiplied alpha.
87 			 */
88 			for(i=0; i<pixels; i++, b+=4){
89 				a = b[3];
90 				if(a != 0 && a != 255){
91 					if(b[0] >= a)
92 						b[0] = a;
93 					b[0] = (b[0]*255)/a;
94 					if(b[1] >= a)
95 						b[1] = a;
96 					b[1] = (b[1]*255)/a;
97 					if(b[2] >= a)
98 						b[2] = a;
99 					b[2] = (b[2]*255)/a;
100 				}
101 			}
102 		}else
103 			b += pixwid*pixels;
104 
105 		z->x += pixels;
106 		if(z->x >= z->dx){
107 			z->x = 0;
108 			z->y++;
109 		}
110 	}
111 	return b - (uchar*)buf;
112 }
113 
114 static void
IDAT(ZlibW * z)115 IDAT(ZlibW *z)
116 {
117 	chunk(z->io, "IDAT", z->buf, z->b - z->buf);
118 	z->b = z->buf;
119 }
120 
121 static int
zwrite(void * va,void * buf,int n)122 zwrite(void *va, void *buf, int n)
123 {
124 	int m;
125 	uchar *b, *e;
126 	ZlibW *z;
127 
128 	z = va;
129 	b = buf;
130 	e = b+n;
131 
132 	while(b < e){
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*
memRGBA(Memimage * i)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 	qlock(&memdrawlock);
165 	ni = allocmemimage(i->r, dst);
166 	if(ni)
167 		memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
168 	qunlock(&memdrawlock);
169 	return ni;
170 }
171 
172 int
writepng(Hio * io,Memimage * m)173 writepng(Hio *io, Memimage *m)
174 {
175 	static int first = 1;
176 	static QLock lk;
177 	uchar buf[200], *h;
178 	Memimage *rgb;
179 	ZlibR zr;
180 	ZlibW zw;
181 
182 	if(first){
183 		qlock(&lk);
184 		if(first){
185 			deflateinit();
186 			crctab = mkcrctab(0xedb88320);
187 			first = 0;
188 		}
189 		qunlock(&lk);
190 	}
191 
192 	rgb = memRGBA(m);
193 	if(rgb == nil)
194 		return -1;
195 
196 	hwrite(io, PNGmagic, sizeof PNGmagic);
197 
198 	/* IHDR chunk */
199 	h = buf;
200 	put4(h, Dx(m->r)); h += 4;
201 	put4(h, Dy(m->r)); h += 4;
202 	*h++ = 8;	/* 8 bits per channel */
203 	if(rgb->chan == BGR24)
204 		*h++ = 2;		/* RGB */
205 	else
206 		*h++ = 6;		/* RGBA */
207 	*h++ = 0;	/* compression - deflate */
208 	*h++ = 0;	/* filter - none */
209 	*h++ = 0;	/* interlace - none */
210 	chunk(io, "IHDR", buf, h-buf);
211 
212 	/* image data */
213 	zr.dx = Dx(m->r);
214 	zr.dy = Dy(m->r);
215 	zr.width = rgb->width * sizeof(u32int);
216 	zr.data = rgb->data->bdata;
217 	zr.x = 0;
218 	zr.y = 0;
219 	zr.pixwid = chantodepth(rgb->chan)/8;
220 	zw.io = io;
221 	zw.buf = vtmalloc(IDATSIZE);
222 	zw.b = zw.buf;
223 	zw.e = zw.b + IDATSIZE;
224 	if(deflatezlib(&zw, zwrite, &zr, zread, 6, 0) < 0){
225 		free(zw.buf);
226 		return -1;
227 	}
228 	if(zw.b > zw.buf)
229 		IDAT(&zw);
230 	free(zw.buf);
231 	chunk(io, "IEND", nil, 0);
232 
233 	if(m != rgb){
234 		qlock(&memdrawlock);
235 		freememimage(rgb);
236 		qunlock(&memdrawlock);
237 	}
238 	return 0;
239 }
240