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