221 lines
4.2 KiB
C
221 lines
4.2 KiB
C
|
|
// 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;
|
||
|
|
}
|