123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- // 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;
- }
|