xref: /plan9/sys/src/cmd/jpg/writepng.c (revision 9b7bf7df4595c26f1e9b67beb0c6e44c9876fb05)
1 /*
2  * See PNG 1.2 spec, also RFC 2083.
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <draw.h>
7 #include <memdraw.h>
8 #include <ctype.h>
9 #include <bio.h>
10 #include <flate.h>
11 #include "imagefile.h"
12 
13 enum
14 {
15 	IDATSIZE = 	20000,
16 	FilterNone =	0,
17 };
18 
19 typedef struct ZlibR ZlibR;
20 typedef struct ZlibW ZlibW;
21 
22 struct ZlibR
23 {
24 	uchar *data;
25 	int width;
26 	int dx;
27 	int dy;
28 	int x;
29 	int y;
30 	int pixwid;
31 };
32 
33 struct ZlibW
34 {
35 	Biobuf *io;
36 	uchar *buf;
37 	uchar *b;
38 	uchar *e;
39 };
40 
41 static ulong *crctab;
42 static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
43 
44 static void
put4(uchar * a,ulong v)45 put4(uchar *a, ulong v)
46 {
47 	a[0] = v>>24;
48 	a[1] = v>>16;
49 	a[2] = v>>8;
50 	a[3] = v;
51 }
52 
53 static void
chunk(Biobuf * bo,char * type,uchar * d,int n)54 chunk(Biobuf *bo, char *type, uchar *d, int n)
55 {
56 	uchar buf[4];
57 	ulong crc = 0;
58 
59 	if(strlen(type) != 4)
60 		return;
61 	put4(buf, n);
62 	Bwrite(bo, buf, 4);
63 	Bwrite(bo, type, 4);
64 	Bwrite(bo, d, n);
65 	crc = blockcrc(crctab, crc, type, 4);
66 	crc = blockcrc(crctab, crc, d, n);
67 	put4(buf, crc);
68 	Bwrite(bo, buf, 4);
69 }
70 
71 static int
zread(void * va,void * buf,int n)72 zread(void *va, void *buf, int n)
73 {
74 	int a, i, pixels, pixwid;
75 	uchar *b, *e, *img;
76 	ZlibR *z;
77 
78 	z = va;
79 	pixwid = z->pixwid;
80 	b = buf;
81 	e = b+n;
82 	while(b+pixwid < e){	/* one less for filter alg byte */
83 		if(z->y >= z->dy)
84 			break;
85 		if(z->x == 0)
86 			*b++ = FilterNone;
87 		pixels = (e-b)/pixwid;
88 		if(pixels > z->dx - z->x)
89 			pixels = z->dx - z->x;
90 		img = z->data + z->width*z->y + pixwid*z->x;
91 		memmove(b, img, pixwid*pixels);
92 		if(pixwid == 4){
93 			/*
94 			 * Convert to non-premultiplied alpha.
95 			 */
96 			for(i=0; i<pixels; i++, b+=4){
97 				a = b[3];
98 				if(a == 0)
99 					b[0] = b[1] = b[2] = 0;
100 				else if(a != 255){
101 					if(b[0] >= a)
102 						b[0] = a;
103 					b[0] = (b[0]*255)/a;
104 					if(b[1] >= a)
105 						b[1] = a;
106 					b[1] = (b[1]*255)/a;
107 					if(b[2] >= a)
108 						b[2] = a;
109 					b[2] = (b[2]*255)/a;
110 				}
111 			}
112 		}else
113 			b += pixwid*pixels;
114 
115 		z->x += pixels;
116 		if(z->x >= z->dx){
117 			z->x = 0;
118 			z->y++;
119 		}
120 	}
121 	return b - (uchar*)buf;
122 }
123 
124 static void
IDAT(ZlibW * z)125 IDAT(ZlibW *z)
126 {
127 	chunk(z->io, "IDAT", z->buf, z->b - z->buf);
128 	z->b = z->buf;
129 }
130 
131 static int
zwrite(void * va,void * buf,int n)132 zwrite(void *va, void *buf, int n)
133 {
134 	int m;
135 	uchar *b, *e;
136 	ZlibW *z;
137 
138 	z = va;
139 	b = buf;
140 	e = b+n;
141 
142 	while(b < e){
143 		m = z->e - z->b;
144 		if(m > e - b)
145 			m = e - b;
146 		memmove(z->b, b, m);
147 		z->b += m;
148 		b += m;
149 		if(z->b >= z->e)
150 			IDAT(z);
151 	}
152 	return n;
153 }
154 
155 static Memimage*
memRGBA(Memimage * i)156 memRGBA(Memimage *i)
157 {
158 	Memimage *ni;
159 	char buf[32];
160 	ulong dst;
161 
162 	/*
163 	 * [A]BGR because we want R,G,B,[A] in big-endian order.  Sigh.
164 	 */
165 	chantostr(buf, i->chan);
166 	if(strchr(buf, 'a'))
167 		dst = ABGR32;
168 	else
169 		dst = BGR24;
170 
171 	if(i->chan == dst)
172 		return i;
173 
174 	ni = allocmemimage(i->r, dst);
175 	if(ni == nil)
176 		return ni;
177 	memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
178 	return ni;
179 }
180 
181 char*
memwritepng(Biobuf * io,Memimage * m,ImageInfo * II)182 memwritepng(Biobuf *io, Memimage *m, ImageInfo *II)
183 {
184 	int err, n;
185 	uchar buf[200], *h;
186 	ulong vgamma;
187 	Tm *tm;
188 	Memimage *rgb;
189 	ZlibR zr;
190 	ZlibW zw;
191 
192 	crctab = mkcrctab(0xedb88320);
193 	if(crctab == nil)
194 		sysfatal("mkcrctab error");
195 	deflateinit();
196 
197 	rgb = memRGBA(m);
198 	if(rgb == nil)
199 		return "allocmemimage nil";
200 
201 	Bwrite(io, PNGmagic, sizeof PNGmagic);
202 
203 	/* IHDR chunk */
204 	h = buf;
205 	put4(h, Dx(m->r)); h += 4;
206 	put4(h, Dy(m->r)); h += 4;
207 	*h++ = 8;	/* 8 bits per channel */
208 	if(rgb->chan == BGR24)
209 		*h++ = 2;		/* RGB */
210 	else
211 		*h++ = 6;		/* RGBA */
212 	*h++ = 0;	/* compression - deflate */
213 	*h++ = 0;	/* filter - none */
214 	*h++ = 0;	/* interlace - none */
215 	chunk(io, "IHDR", buf, h-buf);
216 
217 	/* time - using now is suspect */
218 	tm = gmtime(time(0));
219 	h = buf;
220 	*h++ = (tm->year + 1900)>>8;
221 	*h++ = (tm->year + 1900)&0xff;
222 	*h++ = tm->mon + 1;
223 	*h++ = tm->mday;
224 	*h++ = tm->hour;
225 	*h++ = tm->min;
226 	*h++ = tm->sec;
227 	chunk(io, "tIME", buf, h-buf);
228 
229 	/* gamma */
230 	if(II->fields_set & II_GAMMA){
231 		vgamma = II->gamma*100000;
232 		put4(buf, vgamma);
233 		chunk(io, "gAMA", buf, 4);
234 	}
235 
236 	/* comment */
237 	if(II->fields_set & II_COMMENT){
238 		strncpy((char*)buf, "Comment", sizeof buf);
239 		n = strlen((char*)buf)+1; // leave null between Comment and text
240 		strncpy((char*)(buf+n), II->comment, sizeof buf - n);
241 		chunk(io, "tEXt", buf, n+strlen((char*)buf+n));
242 	}
243 
244 	/* image data */
245 	zr.dx = Dx(m->r);
246 	zr.dy = Dy(m->r);
247 	zr.width = rgb->width * sizeof(ulong);
248 	zr.data = rgb->data->bdata;
249 	zr.x = 0;
250 	zr.y = 0;
251 	zr.pixwid = chantodepth(rgb->chan)/8;
252 	zw.io = io;
253 	zw.buf = malloc(IDATSIZE);
254 	if(zw.buf == nil)
255 		sysfatal("malloc: %r");
256 	zw.b = zw.buf;
257 	zw.e = zw.b + IDATSIZE;
258 	if((err=deflatezlib(&zw, zwrite, &zr, zread, 6, 0)) < 0)
259 		sysfatal("deflatezlib %s", flateerr(err));
260 	if(zw.b > zw.buf)
261 		IDAT(&zw);
262 	free(zw.buf);
263 	chunk(io, "IEND", nil, 0);
264 	if(m != rgb)
265 		freememimage(rgb);
266 	return nil;
267 }
268