Why not?
This commit is contained in:
parent
a01e58366c
commit
28994509cc
82 changed files with 13293 additions and 0 deletions
334
src/cmd/jpg/readpng.c
Normal file
334
src/cmd/jpg/readpng.c
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
// work in progress... this version only good enough to read
|
||||
// non-interleaved, 24bit RGB images
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <ctype.h>
|
||||
#include <bio.h>
|
||||
#include <flate.h>
|
||||
#include <draw.h>
|
||||
#include "imagefile.h"
|
||||
|
||||
int debug;
|
||||
|
||||
enum{ IDATSIZE=1000000,
|
||||
/* filtering algorithms, supposedly increase compression */
|
||||
FilterNone = 0, /* new[x][y] = buf[x][y] */
|
||||
FilterSub = 1, /* new[x][y] = buf[x][y] + new[x-1][y] */
|
||||
FilterUp = 2, /* new[x][y] = buf[x][y] + new[x][y-1] */
|
||||
FilterAvg = 3, /* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */
|
||||
FilterPaeth= 4, /* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
|
||||
FilterLast = 5,
|
||||
PropertyBit = 1<<5,
|
||||
};
|
||||
|
||||
typedef struct ZlibR{
|
||||
Biobuf *bi;
|
||||
uchar *buf;
|
||||
uchar *b; // next byte to decompress
|
||||
uchar *e; // past end of buf
|
||||
} ZlibR;
|
||||
|
||||
typedef struct ZlibW{
|
||||
uchar *r, *g, *b; // Rawimage channels
|
||||
int chan; // next channel to write
|
||||
int col; // column index of current pixel
|
||||
// -1 = one-byte pseudo-column for filter spec
|
||||
int row; // row index of current pixel
|
||||
int ncol, nrow; // image width, height
|
||||
int filter; // algorithm for current scanline
|
||||
} ZlibW;
|
||||
|
||||
static ulong *crctab;
|
||||
static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
|
||||
static char memerr[] = "ReadPNG: malloc failed: %r";
|
||||
|
||||
static ulong
|
||||
get4(uchar *a)
|
||||
{
|
||||
return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
pnginit(void)
|
||||
{
|
||||
static int inited;
|
||||
|
||||
if(inited)
|
||||
return;
|
||||
inited = 1;
|
||||
crctab = mkcrctab(0xedb88320);
|
||||
if(crctab == nil)
|
||||
sysfatal("mkcrctab error");
|
||||
inflateinit();
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
pngmalloc(ulong n, int clear)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(n);
|
||||
if(p == nil)
|
||||
sysfatal(memerr);
|
||||
if(clear)
|
||||
memset(p, 0, n);
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
getchunk(Biobuf *b, char *type, uchar *d, int m)
|
||||
{
|
||||
uchar buf[8];
|
||||
ulong crc = 0, crc2;
|
||||
int n, nr;
|
||||
|
||||
if(Bread(b, buf, 8) != 8)
|
||||
return -1;
|
||||
n = get4(buf);
|
||||
memmove(type, buf+4, 4);
|
||||
type[4] = 0;
|
||||
if(n > m)
|
||||
sysfatal("getchunk needed %d, had %d", n, m);
|
||||
nr = Bread(b, d, n);
|
||||
if(nr != n)
|
||||
sysfatal("getchunk read %d, expected %d", nr, n);
|
||||
crc = blockcrc(crctab, crc, type, 4);
|
||||
crc = blockcrc(crctab, crc, d, n);
|
||||
if(Bread(b, buf, 4) != 4)
|
||||
sysfatal("getchunk tlr failed");
|
||||
crc2 = get4(buf);
|
||||
if(crc != crc2)
|
||||
sysfatal("getchunk crc failed");
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
zread(void *va)
|
||||
{
|
||||
ZlibR *z = va;
|
||||
char type[5];
|
||||
int n;
|
||||
|
||||
if(z->b >= z->e){
|
||||
refill_buffer:
|
||||
z->b = z->buf;
|
||||
n = getchunk(z->bi, type, z->b, IDATSIZE);
|
||||
if(n < 0 || strcmp(type, "IEND") == 0)
|
||||
return -1;
|
||||
z->e = z->b + n;
|
||||
if(type[0] & PropertyBit)
|
||||
goto refill_buffer; /* skip auxiliary chunks for now */
|
||||
if(strcmp(type,"IDAT") != 0)
|
||||
sysfatal("unrecognized mandatory chunk %s", type);
|
||||
}
|
||||
return *z->b++;
|
||||
}
|
||||
|
||||
static uchar
|
||||
paeth(uchar a, uchar b, uchar c)
|
||||
{
|
||||
int p, pa, pb, pc;
|
||||
|
||||
p = (int)a + (int)b - (int)c;
|
||||
pa = abs(p - (int)a);
|
||||
pb = abs(p - (int)b);
|
||||
pc = abs(p - (int)c);
|
||||
|
||||
if(pa <= pb && pa <= pc)
|
||||
return a;
|
||||
else if(pb <= pc)
|
||||
return b;
|
||||
return c;
|
||||
}
|
||||
|
||||
static void
|
||||
unfilter(int alg, uchar *buf, uchar *ebuf, int up)
|
||||
{
|
||||
switch(alg){
|
||||
case FilterSub:
|
||||
while (++buf < ebuf)
|
||||
*buf += buf[-1];
|
||||
break;
|
||||
case FilterUp:
|
||||
if (up != 0)
|
||||
do
|
||||
*buf += buf[up];
|
||||
while (++buf < ebuf);
|
||||
break;
|
||||
case FilterAvg:
|
||||
if (up == 0)
|
||||
while (++buf < ebuf)
|
||||
*buf += buf[-1]/2;
|
||||
else{
|
||||
*buf += buf[up]/2;
|
||||
while (++buf < ebuf)
|
||||
*buf += (buf[-1]+buf[up])/2;
|
||||
}
|
||||
break;
|
||||
case FilterPaeth:
|
||||
if (up == 0)
|
||||
while (++buf < ebuf)
|
||||
*buf += buf[-1];
|
||||
else{
|
||||
*buf += paeth(0, buf[up], 0);
|
||||
while (++buf < ebuf)
|
||||
*buf += paeth(buf[-1], buf[up], buf[up-1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
zwrite(void *va, void *vb, int n)
|
||||
{
|
||||
ZlibW *z = va;
|
||||
uchar *buf = vb;
|
||||
int i, up;
|
||||
for(i=0; i<n; i++){
|
||||
if(z->col == -1){
|
||||
// set filter byte
|
||||
z->filter = *buf++;
|
||||
if (z->filter >= FilterLast)
|
||||
sysfatal("unknown filter algorithm %d for row %d", z->row, z->filter);
|
||||
z->col++;
|
||||
continue;
|
||||
}
|
||||
switch(z->chan){
|
||||
case 0:
|
||||
*z->r++ = *buf++;
|
||||
z->chan = 1;
|
||||
break;
|
||||
case 1:
|
||||
*z->g++ = *buf++;
|
||||
z->chan = 2;
|
||||
break;
|
||||
case 2:
|
||||
*z->b++ = *buf++;
|
||||
z->chan = 0;
|
||||
z->col++;
|
||||
if(z->col == z->ncol){
|
||||
if (z->filter){
|
||||
if(z->row == 0)
|
||||
up = 0;
|
||||
else
|
||||
up = -z->ncol;
|
||||
unfilter(z->filter, z->r - z->col, z->r, up);
|
||||
unfilter(z->filter, z->g - z->col, z->g, up);
|
||||
unfilter(z->filter, z->b - z->col, z->b, up);
|
||||
}
|
||||
z->col = -1;
|
||||
z->row++;
|
||||
if((z->row >= z->nrow) && (i < n-1) )
|
||||
sysfatal("header said %d rows; data goes further", z->nrow);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static Rawimage*
|
||||
readslave(Biobuf *b)
|
||||
{
|
||||
ZlibR zr;
|
||||
ZlibW zw;
|
||||
Rawimage *image;
|
||||
char type[5];
|
||||
uchar *buf, *h;
|
||||
int k, n, nrow, ncol, err;
|
||||
|
||||
buf = pngmalloc(IDATSIZE, 0);
|
||||
Bread(b, buf, sizeof PNGmagic);
|
||||
if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
|
||||
sysfatal("bad PNGmagic");
|
||||
|
||||
n = getchunk(b, type, buf, IDATSIZE);
|
||||
if(n < 13 || strcmp(type,"IHDR") != 0)
|
||||
sysfatal("missing IHDR chunk");
|
||||
h = buf;
|
||||
ncol = get4(h); h += 4;
|
||||
nrow = get4(h); h += 4;
|
||||
if(ncol <= 0 || nrow <= 0)
|
||||
sysfatal("impossible image size nrow=%d ncol=%d", nrow, ncol);
|
||||
if(debug)
|
||||
fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol);
|
||||
if(*h++ != 8)
|
||||
sysfatal("only 24 bit per pixel supported for now [%d]", h[-1]);
|
||||
if(*h++ != 2)
|
||||
sysfatal("only rgb supported for now [%d]", h[-1]);
|
||||
if(*h++ != 0)
|
||||
sysfatal("only deflate supported for now [%d]", h[-1]);
|
||||
if(*h++ != FilterNone)
|
||||
sysfatal("only FilterNone supported for now [%d]", h[-1]);
|
||||
if(*h != 0)
|
||||
sysfatal("only non-interlaced supported for now [%d]", h[-1]);
|
||||
|
||||
image = pngmalloc(sizeof(Rawimage), 1);
|
||||
image->r = Rect(0, 0, ncol, nrow);
|
||||
image->cmap = nil;
|
||||
image->cmaplen = 0;
|
||||
image->chanlen = ncol*nrow;
|
||||
image->fields = 0;
|
||||
image->gifflags = 0;
|
||||
image->gifdelay = 0;
|
||||
image->giftrindex = 0;
|
||||
image->chandesc = CRGB;
|
||||
image->nchans = 3;
|
||||
for(k=0; k<3; k++)
|
||||
image->chans[k] = pngmalloc(ncol*nrow, 0);
|
||||
zr.bi = b;
|
||||
zr.buf = buf;
|
||||
zr.b = zr.e = buf + IDATSIZE;
|
||||
zw.r = image->chans[0];
|
||||
zw.g = image->chans[1];
|
||||
zw.b = image->chans[2];
|
||||
zw.chan = 0;
|
||||
zw.col = -1;
|
||||
zw.row = 0;
|
||||
zw.ncol = ncol;
|
||||
zw.nrow = nrow;
|
||||
err = inflatezlib(&zw, zwrite, &zr, zread);
|
||||
if(err)
|
||||
sysfatal("inflatezlib %s\n", flateerr(err));
|
||||
free(buf);
|
||||
return image;
|
||||
}
|
||||
|
||||
Rawimage**
|
||||
Breadpng(Biobuf *b, int colorspace)
|
||||
{
|
||||
Rawimage *r, **array;
|
||||
char buf[ERRMAX];
|
||||
|
||||
buf[0] = '\0';
|
||||
if(colorspace != CRGB){
|
||||
errstr(buf, sizeof buf); /* throw it away */
|
||||
werrstr("ReadPNG: unknown color space %d", colorspace);
|
||||
return nil;
|
||||
}
|
||||
pnginit();
|
||||
array = malloc(2*sizeof(*array));
|
||||
if(array==nil)
|
||||
return nil;
|
||||
errstr(buf, sizeof buf); /* throw it away */
|
||||
r = readslave(b);
|
||||
array[0] = r;
|
||||
array[1] = nil;
|
||||
return array;
|
||||
}
|
||||
|
||||
Rawimage**
|
||||
readpng(int fd, int colorspace)
|
||||
{
|
||||
Rawimage** a;
|
||||
Biobuf b;
|
||||
|
||||
if(Binit(&b, fd, OREAD) < 0)
|
||||
return nil;
|
||||
a = Breadpng(&b, colorspace);
|
||||
Bterm(&b);
|
||||
return a;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue