diff options
Diffstat (limited to 'src/cmd/jpg/writepng.c')
-rw-r--r-- | src/cmd/jpg/writepng.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/cmd/jpg/writepng.c b/src/cmd/jpg/writepng.c new file mode 100644 index 00000000..44bfe10c --- /dev/null +++ b/src/cmd/jpg/writepng.c @@ -0,0 +1,220 @@ +// based on PNG 1.2 specification, July 1999 (see also rfc2083) +// Alpha is not supported yet because of lack of industry acceptance and +// because Plan9 Image uses premultiplied alpha, so png can't be lossless. +// Only 24bit color supported, because 8bit may as well use GIF. + +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <ctype.h> +#include <bio.h> +#include <flate.h> +#include "imagefile.h" + +enum{ IDATSIZE = 20000, + FilterNone = 0, +}; + +typedef struct ZlibR{ + uchar *data; + int width; + int nrow, ncol; + int row, col; // next pixel to send +} ZlibR; + +typedef struct ZlibW{ + Biobuf *bo; + uchar *buf; + uchar *b; // next place to write + uchar *e; // past end of buf +} ZlibW; + +static ulong *crctab; +static uchar PNGmagic[] = {137,80,78,71,13,10,26,10}; + +static void +put4(uchar *a, ulong v) +{ + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +static void +chunk(Biobuf *bo, char *type, uchar *d, int n) +{ + uchar buf[4]; + ulong crc = 0; + + if(strlen(type) != 4) + return; + put4(buf, n); + Bwrite(bo, buf, 4); + Bwrite(bo, type, 4); + Bwrite(bo, d, n); + crc = blockcrc(crctab, crc, type, 4); + crc = blockcrc(crctab, crc, d, n); + put4(buf, crc); + Bwrite(bo, buf, 4); +} + +static int +zread(void *va, void *buf, int n) +{ + ZlibR *z = va; + int nrow = z->nrow; + int ncol = z->ncol; + uchar *b = buf, *e = b+n, *img; + int i, pixels; // number of pixels in row that can be sent now + + while(b+3 <= e){ // loop over image rows + if(z->row >= nrow) + break; + if(z->col==0) + *b++ = FilterNone; + pixels = (e-b)/3; + if(pixels > ncol - z->col) + pixels = ncol - z->col; + img = z->data + z->width * z->row + 3 * z->col; + + // Plan 9 image format is BGR?!!! + // memmove(b, img, 3*pixels); + // b += 3*pixels; + for(i=0; i<pixels; i++, img += 3){ + *b++ = img[2]; + *b++ = img[1]; + *b++ = img[0]; + } + + z->col += pixels; + if(z->col >= ncol){ + z->col = 0; + z->row++; + } + } + return b - (uchar*)buf; +} + +static void +IDAT(ZlibW *z) +{ + chunk(z->bo, "IDAT", z->buf, z->b - z->buf); + z->b = z->buf; +} + +static int +zwrite(void *va, void *buf, int n) +{ + ZlibW *z = va; + uchar *b = buf, *e = b+n; + int m; + + while(b < e){ // loop over IDAT chunks + m = z->e - z->b; + if(m > e - b) + m = e - b; + memmove(z->b, b, m); + z->b += m; + b += m; + if(z->b >= z->e) + IDAT(z); + } + return n; +} + +static Memimage* +memRGB(Memimage *i) +{ + Memimage *ni; + + if(i->chan == RGB24) + return i; + + ni = allocmemimage(i->r, RGB24); + if(ni == nil) + return ni; + memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S); + return ni; +} + +char* +memwritepng(Biobuf *bo, Memimage *r, ImageInfo *II) +{ + uchar buf[200], *h; + ulong vgamma; + int err, n; + ZlibR zr; + ZlibW zw; + int nrow = r->r.max.y - r->r.min.y; + int ncol = r->r.max.x - r->r.min.x; + Tm *tm; + Memimage *rgb; + + rgb = memRGB(r); + if(rgb == nil) + return "allocmemimage nil"; + crctab = mkcrctab(0xedb88320); + if(crctab == nil) + sysfatal("mkcrctab error"); + deflateinit(); + + Bwrite(bo, PNGmagic, sizeof PNGmagic); + // IHDR chunk + h = buf; + put4(h, ncol); h += 4; + put4(h, nrow); h += 4; + *h++ = 8; // bit depth = 24 bit per pixel + *h++ = 2; // color type = rgb + *h++ = 0; // compression method = deflate + *h++ = 0; // filter method + *h++ = 0; // interlace method = no interlace + chunk(bo, "IHDR", buf, h-buf); + + tm = gmtime(time(0)); + h = buf; + *h++ = (tm->year + 1900)>>8; + *h++ = (tm->year + 1900)&0xff; + *h++ = tm->mon + 1; + *h++ = tm->mday; + *h++ = tm->hour; + *h++ = tm->min; + *h++ = tm->sec; + chunk(bo, "tIME", buf, h-buf); + + if(II->fields_set & II_GAMMA){ + vgamma = II->gamma*100000; + put4(buf, vgamma); + chunk(bo, "gAMA", buf, 4); + } + + if(II->fields_set & II_COMMENT){ + strncpy((char*)buf, "Comment", sizeof buf); + n = strlen((char*)buf)+1; // leave null between Comment and text + strncpy((char*)(buf+n), II->comment, sizeof buf - n); + chunk(bo, "tEXt", buf, n+strlen((char*)buf+n)); + } + + // image chunks + zr.nrow = nrow; + zr.ncol = ncol; + zr.width = rgb->width * sizeof(ulong); + zr.data = rgb->data->bdata; + zr.row = zr.col = 0; + zw.bo = bo; + zw.buf = malloc(IDATSIZE); + zw.b = zw.buf; + zw.e = zw.b + IDATSIZE; + err = deflatezlib(&zw, zwrite, &zr, zread, 6, 0); + if(zw.b > zw.buf) + IDAT(&zw); + free(zw.buf); + if(err) + sysfatal("deflatezlib %s\n", flateerr(err)); + chunk(bo, "IEND", nil, 0); + + if(r != rgb) + freememimage(rgb); + return nil; +} |