]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/writepng.c
cwfs: back to previous version
[plan9front.git] / sys / src / cmd / jpg / writepng.c
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
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
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
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){
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
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
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*
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*
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 }