aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/jpg/writepng.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/jpg/writepng.c')
-rw-r--r--src/cmd/jpg/writepng.c220
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;
+}