png.c (3726B)
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 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 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 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 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 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* 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 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 }