123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * See PNG 1.2 spec, also RFC 2083.
- */
- #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 ZlibR;
- typedef struct ZlibW ZlibW;
- struct ZlibR
- {
- uint8_t *data;
- int width;
- int dx;
- int dy;
- int x;
- int y;
- int pixwid;
- };
- struct ZlibW
- {
- Biobuf *io;
- uint8_t *buf;
- uint8_t *b;
- uint8_t *e;
- };
- static uint32_t *crctab;
- static uint8_t PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
- static void
- put4(uint8_t *a, uint32_t v)
- {
- a[0] = v>>24;
- a[1] = v>>16;
- a[2] = v>>8;
- a[3] = v;
- }
- static void
- chunk(Biobuf *bo, char *type, uint8_t *d, int n)
- {
- uint8_t buf[4];
- uint32_t 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)
- {
- int a, i, pixels, pixwid;
- uint8_t *b, *e, *img;
- ZlibR *z;
- z = va;
- pixwid = z->pixwid;
- b = buf;
- e = b+n;
- while(b+pixwid < e){ /* one less for filter alg byte */
- if(z->y >= z->dy)
- break;
- if(z->x == 0)
- *b++ = FilterNone;
- pixels = (e-b)/pixwid;
- if(pixels > z->dx - z->x)
- pixels = z->dx - z->x;
- img = z->data + z->width*z->y + pixwid*z->x;
- memmove(b, img, pixwid*pixels);
- if(pixwid == 4){
- /*
- * Convert to non-premultiplied alpha.
- */
- for(i=0; i<pixels; i++, b+=4){
- a = b[3];
- if(a == 0)
- b[0] = b[1] = b[2] = 0;
- else if(a != 255){
- if(b[0] >= a)
- b[0] = a;
- b[0] = (b[0]*255)/a;
- if(b[1] >= a)
- b[1] = a;
- b[1] = (b[1]*255)/a;
- if(b[2] >= a)
- b[2] = a;
- b[2] = (b[2]*255)/a;
- }
- }
- }else
- b += pixwid*pixels;
- z->x += pixels;
- if(z->x >= z->dx){
- z->x = 0;
- z->y++;
- }
- }
- return b - (uint8_t*)buf;
- }
- static void
- IDAT(ZlibW *z)
- {
- chunk(z->io, "IDAT", z->buf, z->b - z->buf);
- z->b = z->buf;
- }
- static int
- zwrite(void *va, void *buf, int n)
- {
- int m;
- uint8_t *b, *e;
- ZlibW *z;
- z = va;
- b = buf;
- e = b+n;
- while(b < e){
- 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*
- memRGBA(Memimage *i)
- {
- Memimage *ni;
- char buf[32];
- uint32_t dst;
-
- /*
- * [A]BGR because we want R,G,B,[A] in big-endian order. Sigh.
- */
- chantostr(buf, i->chan);
- if(strchr(buf, 'a'))
- dst = ABGR32;
- else
- dst = BGR24;
-
- if(i->chan == dst)
- return i;
- ni = allocmemimage(i->r, dst);
- if(ni == nil)
- return ni;
- memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
- return ni;
- }
- char*
- memwritepng(Biobuf *io, Memimage *m, ImageInfo *II)
- {
- int err, n;
- uint8_t buf[200], *h;
- uint32_t vgamma;
- Tm *tm;
- Memimage *rgb;
- ZlibR zr;
- ZlibW zw;
- crctab = mkcrctab(0xedb88320);
- if(crctab == nil)
- sysfatal("mkcrctab error");
- deflateinit();
- rgb = memRGBA(m);
- if(rgb == nil)
- return "allocmemimage nil";
- Bwrite(io, PNGmagic, sizeof PNGmagic);
- /* IHDR chunk */
- h = buf;
- put4(h, Dx(m->r)); h += 4;
- put4(h, Dy(m->r)); h += 4;
- *h++ = 8; /* 8 bits per channel */
- if(rgb->chan == BGR24)
- *h++ = 2; /* RGB */
- else
- *h++ = 6; /* RGBA */
- *h++ = 0; /* compression - deflate */
- *h++ = 0; /* filter - none */
- *h++ = 0; /* interlace - none */
- chunk(io, "IHDR", buf, h-buf);
- /* time - using now is suspect */
- 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(io, "tIME", buf, h-buf);
- /* gamma */
- if(II->fields_set & II_GAMMA){
- vgamma = II->gamma*100000;
- put4(buf, vgamma);
- chunk(io, "gAMA", buf, 4);
- }
- /* comment */
- 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(io, "tEXt", buf, n+strlen((char*)buf+n));
- }
- /* image data */
- zr.dx = Dx(m->r);
- zr.dy = Dy(m->r);
- zr.width = rgb->width * sizeof(uint32_t);
- zr.data = rgb->data->bdata;
- zr.x = 0;
- zr.y = 0;
- zr.pixwid = chantodepth(rgb->chan)/8;
- zw.io = io;
- zw.buf = malloc(IDATSIZE);
- if(zw.buf == nil)
- sysfatal("malloc: %r");
- zw.b = zw.buf;
- zw.e = zw.b + IDATSIZE;
- if((err=deflatezlib(&zw, zwrite, &zr, zread, 6, 0)) < 0)
- sysfatal("deflatezlib %s", flateerr(err));
- if(zw.b > zw.buf)
- IDAT(&zw);
- free(zw.buf);
- chunk(io, "IEND", nil, 0);
- if(m != rgb)
- freememimage(rgb);
- return nil;
- }
|