This commit is contained in:
rsc 2004-04-21 22:19:33 +00:00
parent a01e58366c
commit 28994509cc
82 changed files with 13293 additions and 0 deletions

210
src/cmd/jpg/bmp.c Normal file
View file

@ -0,0 +1,210 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "imagefile.h"
int cflag = 0;
int dflag = 0;
int eflag = 0;
int nineflag = 0;
int threeflag = 0;
int output = 0;
ulong outchan = CMAP8;
int defaultcolor = 1;
Image *image;
enum{
Border = 2,
Edge = 5
};
char *show(int, char*);
Rawimage** readbmp(int fd, int colorspace);
void
eresized(int new)
{
Rectangle r;
if(new && getwindow(display, Refnone) < 0){
fprint(2, "bmp: can't reattach to window\n");
exits("resize");
}
if(image == nil)
return;
r = insetrect(screen->clipr, Edge+Border);
r.max.x = r.min.x+Dx(image->r);
r.max.y = r.min.y+Dy(image->r);
border(screen, r, -Border, nil, ZP);
draw(screen, r, image, nil, image->r.min);
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
int fd, i;
char *err;
ARGBEGIN{
case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
threeflag++;
/* fall through */
case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
cflag++;
dflag++;
output++;
defaultcolor = 0;
outchan = RGB24;
break;
case 'c': /* produce encoded, compressed, bitmap file; no display by default */
cflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
case 'd': /* suppress display of image */
dflag++;
break;
case 'e': /* disable floyd-steinberg error diffusion */
eflag++;
break;
case 'k': /* force black and white */
defaultcolor = 0;
outchan = GREY8;
break;
case 'v': /* force RGBV */
defaultcolor = 0;
outchan = CMAP8;
break;
case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
nineflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
default:
fprint(2, "usage: bmp -39cdektv [file.bmp ...]\n");
exits("usage");
}ARGEND;
err = nil;
if(argc == 0)
err = show(0, "<stdin>");
else{
for(i=0; i<argc; i++){
fd = open(argv[i], OREAD);
if(fd < 0){
fprint(2, "bmp: can't open %s: %r\n", argv[i]);
err = "open";
}else{
err = show(fd, argv[i]);
close(fd);
}
if((nineflag || cflag) && argc>1 && err==nil){
fprint(2, "bmp: exiting after one file\n");
break;
}
}
}
exits(err);
}
int
init(void)
{
static int inited;
if(inited == 0){
if(initdraw(0, 0, 0) < 0){
fprint(2, "bmp: initdraw failed: %r");
return -1;
}
einit(Ekeyboard|Emouse);
inited++;
}
return 1;
}
char*
show(int fd, char *name)
{
Rawimage **array, *r, *c;
Image *i;
int j, ch;
char buf[32];
array = readbmp(fd, CRGB);
if(array == nil || array[0]==nil){
fprint(2, "bmp: decode %s failed: %r\n", name);
return "decode";
}
if(!dflag){
if(init() < 0)
return "initdraw";
if(defaultcolor && screen->depth>8)
outchan = RGB24;
}
r = array[0];
if(outchan == CMAP8)
c = torgbv(r, !eflag);
else{
if(outchan==GREY8 || (r->chandesc==CY && threeflag==0))
c = totruecolor(r, CY);
else
c = totruecolor(r, CRGB24);
}
if(c == nil){
fprint(2, "bmp: converting %s to local format failed: %r\n", name);
return "torgbv";
}
if(!dflag){
if(r->chandesc == CY)
i = allocimage(display, c->r, GREY8, 0, 0);
else
i = allocimage(display, c->r, outchan, 0, 0);
if(i == nil){
fprint(2, "bmp: allocimage %s failed: %r\n", name);
return "allocimage";
}
if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
fprint(2, "bmp: loadimage %s failed: %r\n", name);
return "loadimage";
}
image = i;
eresized(0);
if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
exits(nil);
draw(screen, screen->clipr, display->white, nil, ZP);
image = nil;
freeimage(i);
}
if(nineflag){
chantostr(buf, outchan);
print("%11s %11d %11d %11d %11d ", buf,
c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
if(write(1, c->chans[0], c->chanlen) != c->chanlen){
fprint(2, "bmp: %s: write error %r\n", name);
return "write";
}
}else if(cflag){
if(writerawimage(1, c) < 0){
fprint(2, "bmp: %s: write error: %r\n", name);
return "write";
}
}
for(j=0; j<r->nchans; j++)
free(r->chans[j]);
free(r);
free(array);
if(c){
free(c->chans[0]);
free(c);
}
return nil;
}

37
src/cmd/jpg/bmp.h Normal file
View file

@ -0,0 +1,37 @@
#define BMP_RGB 0
#define BMP_RLE8 1
#define BMP_RLE4 2
#define BMP_BITFIELDS 3
typedef struct {
uchar red;
uchar green;
uchar blue;
uchar alpha;
} Rgb;
typedef struct {
short type;
long size;
short reserved1;
short reserved2;
long offbits;
} Filehdr;
typedef struct {
long size; /* Size of the Bitmap-file */
long lReserved; /* Reserved */
long dataoff; /* Picture data location */
long hsize; /* Header-Size */
long width; /* Picture width (pixels) */
long height; /* Picture height (pixels) */
short planes; /* Planes (must be 1) */
short bpp; /* Bits per pixel (1, 4, 8 or 24) */
long compression; /* Compression mode */
long imagesize; /* Image size (bytes) */
long hres; /* Horizontal Resolution (pels/meter) */
long vres; /* Vertical Resolution (pels/meter) */
long colours; /* Used Colours (Col-Table index) */
long impcolours; /* Important colours (Col-Table index) */
} Infohdr;

121
src/cmd/jpg/close.c Normal file
View file

@ -0,0 +1,121 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
float c1 = 1.402;
float c2 = 0.34414;
float c3 = 0.71414;
float c4 = 1.772;
int
closest(int Y, int Cb, int Cr)
{
double r, g, b;
double diff, min;
int rgb, R, G, B, v, i;
int y1, cb1, cr1;
Cb -= 128;
Cr -= 128;
r = Y+c1*Cr;
g = Y-c2*Cb-c3*Cr;
b = Y+c4*Cb;
//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b);
min = 1000000.;
v = 1000;
for(i=0; i<256; i++){
rgb = cmap2rgb(i);
R = (rgb >> 16) & 0xFF;
G = (rgb >> 8) & 0xFF;
B = (rgb >> 0) & 0xFF;
diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b);
// y1 = 0.5870*G + 0.114*B + 0.299*R;
// cb1 = (B-y1)/1.772;
// cr1 = (R-y1)/1.402;
if(diff < min){
// if(Y==0 && y1!=0)
// continue;
// if(Y==256-16 && y1<256-16)
// continue;
// if(Cb==0 && cb1!=0)
// continue;
// if(Cb==256-16 && cb1<256-16)
// continue;
// if(Cr==0 && cr1!=0)
// continue;
// if(Cr==256-16 && cr1<256-16)
// continue;
//print("%d %d %d\n", R, G, B);
min = diff;
v = i;
}
}
if(v > 255)
abort();
return v;
}
#define SHIFT 5
#define INC (1<<SHIFT)
typedef struct Color Color;
struct Color
{
int col;
Color *next;
};
Color *col[INC*INC*INC];
void
add(int c, int y, int cb, int cr)
{
Color *cp;
y >>= 8-SHIFT;
cb >>= 8-SHIFT;
cr >>= 8-SHIFT;
cp = col[cr+INC*(cb+INC*y)];
while(cp != nil){
if(cp->col == c)
return;
cp = cp->next;
}
cp = malloc(sizeof(Color));
cp->col = c;
cp->next = col[cr+INC*(cb+INC*y)];
col[cr+INC*(cb+INC*y)] = cp;
}
void
main(void)
{
int y, cb, cr, n;
Color *cp;
for(y=0; y<256; y++){
for(cb=0; cb<256; cb++)
for(cr=0;cr<256;cr++)
add(closest(y, cb, cr), y, cb, cr);
fprint(2, "%d done\n", y);
}
for(y=0; y<INC*INC*INC; y++){
n = 0;
cp = col[y];
while(cp != nil){
n++;
cp = cp->next;
}
cp = col[y];
while(cp != nil){
n++;
print("%d ", cp->col);
cp = cp->next;
}
print("\n");
}
}

421
src/cmd/jpg/gif.c Normal file
View file

@ -0,0 +1,421 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "imagefile.h"
int cflag = 0;
int dflag = 0;
int eflag = 0;
int nineflag = 0;
int threeflag = 0;
int output = 0;
ulong outchan = CMAP8;
Image **allims;
Image **allmasks;
int which;
int defaultcolor = 1;
enum{
Border = 2,
Edge = 5
};
char *show(int, char*);
Rectangle
imager(void)
{
Rectangle r;
if(allims==nil || allims[0]==nil)
return screen->r;
r = insetrect(screen->clipr, Edge+Border);
r.max.x = r.min.x+Dx(allims[0]->r);
r.max.y = r.min.y+Dy(allims[0]->r);
return r;
}
void
eresized(int new)
{
Rectangle r;
if(new && getwindow(display, Refnone) < 0){
fprint(2, "gif: can't reattach to window\n");
exits("resize");
}
if(allims==nil || allims[which]==nil)
return;
r = imager();
border(screen, r, -Border, nil, ZP);
r.min.x += allims[which]->r.min.x - allims[0]->r.min.x;
r.min.y += allims[which]->r.min.y - allims[0]->r.min.y;
drawop(screen, r, allims[which], allmasks[which], allims[which]->r.min, S);
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
int fd, i;
char *err;
ARGBEGIN{
case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
threeflag++;
/* fall through */
case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
cflag++;
dflag++;
output++;
defaultcolor = 0;
outchan = RGB24;
break;
case 'c': /* produce encoded, compressed, bitmap file; no display by default */
cflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
case 'd': /* suppress display of image */
dflag++;
break;
case 'e': /* disable floyd-steinberg error diffusion */
eflag++;
break;
case 'k': /* force black and white */
defaultcolor = 0;
outchan = GREY8;
break;
case 'v': /* force RGBV */
defaultcolor = 0;
outchan = CMAP8;
break;
case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
nineflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
default:
fprint(2, "usage: gif -39cdektv [file.gif ...]\n");
exits("usage");
}ARGEND;
err = nil;
if(argc == 0)
err = show(0, "<stdin>");
else{
for(i=0; i<argc; i++){
fd = open(argv[i], OREAD);
if(fd < 0){
fprint(2, "gif: can't open %s: %r\n", argv[i]);
err = "open";
}else{
err = show(fd, argv[i]);
close(fd);
}
if(output && argc>1 && err==nil){
fprint(2, "gif: exiting after one file\n");
break;
}
}
}
exits(err);
}
Image*
transparency(Rawimage *r, char *name)
{
Image *i;
int j, index;
uchar *pic, *mpic, *mask;
if((r->gifflags&TRANSP) == 0)
return nil;
i = allocimage(display, r->r, GREY8, 0, 0);
if(i == nil){
fprint(2, "gif: allocimage for mask of %s failed: %r\n", name);
return nil;
}
pic = r->chans[0];
mask = malloc(r->chanlen);
if(mask == nil){
fprint(2, "gif: malloc for mask of %s failed: %r\n", name);
freeimage(i);
return nil;
}
index = r->giftrindex;
mpic = mask;
for(j=0; j<r->chanlen; j++)
if(*pic++ == index)
*mpic++ = 0;
else
*mpic++ = 0xFF;
if(loadimage(i, i->r, mask, r->chanlen) < 0){
fprint(2, "gif: loadimage for mask of %s failed: %r\n", name);
free(mask);
freeimage(i);
return nil;
}
free(mask);
return i;
}
/* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */
uchar*
expand(uchar *u, int chanlen, int nchan)
{
int j, k;
uchar *v, *up, *vp;
v = malloc(chanlen*(nchan+1));
if(v == nil){
fprint(2, "gif: malloc fails: %r\n");
exits("malloc");
}
up = u;
vp = v;
for(j=0; j<chanlen; j++){
*vp++ = 0xFF;
for(k=0; k<nchan; k++)
*vp++ = *up++;
}
return v;
}
void
addalpha(Rawimage *i)
{
char buf[32];
switch(outchan){
case CMAP8:
i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
i->chanlen = 2*(i->chanlen/1);
i->chandesc = CRGBVA16;
outchan = CHAN2(CMap, 8, CAlpha, 8);
break;
case GREY8:
i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
i->chanlen = 2*(i->chanlen/1);
i->chandesc = CYA16;
outchan = CHAN2(CGrey, 8, CAlpha, 8);
break;
case RGB24:
i->chans[0] = expand(i->chans[0], i->chanlen/3, 3);
i->chanlen = 4*(i->chanlen/3);
i->chandesc = CRGBA32;
outchan = RGBA32;
break;
default:
chantostr(buf, outchan);
fprint(2, "gif: can't add alpha to type %s\n", buf);
exits("err");
}
}
/*
* Called only when writing output. If the output is RGBA32,
* we must write four bytes per pixel instead of two.
* There's always at least two: data plus alpha.
* r is used only for reference; the image is already in c.
*/
void
whiteout(Rawimage *r, Rawimage *c)
{
int i, trindex;
uchar *rp, *cp;
rp = r->chans[0];
cp = c->chans[0];
trindex = r->giftrindex;
if(outchan == RGBA32)
for(i=0; i<r->chanlen; i++){
if(*rp == trindex){
*cp++ = 0x00;
*cp++ = 0xFF;
*cp++ = 0xFF;
*cp++ = 0xFF;
}else{
*cp++ = 0xFF;
cp += 3;
}
rp++;
}
else
for(i=0; i<r->chanlen; i++){
if(*rp == trindex){
*cp++ = 0x00;
*cp++ = 0xFF;
}else{
*cp++ = 0xFF;
cp++;
}
rp++;
}
}
int
init(void)
{
static int inited;
if(inited == 0){
if(initdraw(0, 0, 0) < 0){
fprint(2, "gif: initdraw failed: %r\n");
return -1;
}
einit(Ekeyboard|Emouse);
inited++;
}
return 1;
}
char*
show(int fd, char *name)
{
Rawimage **images, **rgbv;
Image **ims, **masks;
int j, k, n, ch, nloop, loopcount, dt;
char *err;
char buf[32];
err = nil;
images = readgif(fd, CRGB);
if(images == nil){
fprint(2, "gif: decode %s failed: %r\n", name);
return "decode";
}
for(n=0; images[n]; n++)
;
ims = malloc((n+1)*sizeof(Image*));
masks = malloc((n+1)*sizeof(Image*));
rgbv = malloc((n+1)*sizeof(Rawimage*));
if(masks==nil || rgbv==nil || ims==nil){
fprint(2, "gif: malloc of masks for %s failed: %r\n", name);
err = "malloc";
goto Return;
}
memset(masks, 0, (n+1)*sizeof(Image*));
memset(ims, 0, (n+1)*sizeof(Image*));
memset(rgbv, 0, (n+1)*sizeof(Rawimage*));
if(!dflag){
if(init() < 0){
err = "initdraw";
goto Return;
}
if(defaultcolor && screen->depth>8)
outchan = RGB24;
}
for(k=0; k<n; k++){
if(outchan == CMAP8)
rgbv[k] = torgbv(images[k], !eflag);
else{
if(outchan==GREY8 || (images[k]->chandesc==CY && threeflag==0))
rgbv[k] = totruecolor(images[k], CY);
else
rgbv[k] = totruecolor(images[k], CRGB24);
}
if(rgbv[k] == nil){
fprint(2, "gif: converting %s to local format failed: %r\n", name);
err = "torgbv";
goto Return;
}
if(!dflag){
masks[k] = transparency(images[k], name);
if(rgbv[k]->chandesc == CY)
ims[k] = allocimage(display, rgbv[k]->r, GREY8, 0, 0);
else
ims[k] = allocimage(display, rgbv[k]->r, outchan, 0, 0);
if(ims[k] == nil){
fprint(2, "gif: allocimage %s failed: %r\n", name);
err = "allocimage";
goto Return;
}
if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){
fprint(2, "gif: loadimage %s failed: %r\n", name);
err = "loadimage";
goto Return;
}
}
}
allims = ims;
allmasks = masks;
loopcount = images[0]->gifloopcount;
if(!dflag){
nloop = 0;
do{
for(k=0; k<n; k++){
which = k;
eresized(0);
dt = images[k]->gifdelay*10;
if(dt < 50)
dt = 50;
while(n==1 || ecankbd()){
if((ch=ekbd())=='q' || ch==0x7F || ch==0x04) /* an odd, democratic list */
exits(nil);
if(ch == '\n')
goto Out;
}sleep(dt);
}
/* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/
}while(loopcount==0 || ++nloop<loopcount);
/* loop count has run out */
ekbd();
Out:
drawop(screen, screen->clipr, display->white, nil, ZP, S);
}
if(n>1 && output)
fprint(2, "gif: warning: only writing first image in %d-image GIF %s\n", n, name);
if(nineflag){
if(images[0]->gifflags&TRANSP){
addalpha(rgbv[0]);
whiteout(images[0], rgbv[0]);
}
chantostr(buf, outchan);
print("%11s %11d %11d %11d %11d ", buf,
rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.max.x, rgbv[0]->r.max.y);
if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){
fprint(2, "gif: %s: write error %r\n", name);
return "write";
}
}else if(cflag){
if(images[0]->gifflags&TRANSP){
addalpha(rgbv[0]);
whiteout(images[0], rgbv[0]);
}
if(writerawimage(1, rgbv[0]) < 0){
fprint(2, "gif: %s: write error: %r\n", name);
return "write";
}
}
Return:
allims = nil;
allmasks = nil;
for(k=0; images[k]; k++){
for(j=0; j<images[k]->nchans; j++)
free(images[k]->chans[j]);
free(images[k]->cmap);
if(rgbv[k])
free(rgbv[k]->chans[0]);
freeimage(ims[k]);
freeimage(masks[k]);
free(images[k]);
free(rgbv[k]);
}
free(images);
free(masks);
free(ims);
return err;
}

506
src/cmd/jpg/ico.c Normal file
View file

@ -0,0 +1,506 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include <cursor.h>
typedef struct Icon Icon;
struct Icon
{
Icon *next;
uchar w; /* icon width */
uchar h; /* icon height */
ushort ncolor; /* number of colors */
ushort nplane; /* number of bit planes */
ushort bits; /* bits per pixel */
ulong len; /* length of data */
ulong offset; /* file offset to data */
Image *img;
Image *mask;
Rectangle r; /* relative */
Rectangle sr; /* abs */
};
typedef struct Header Header;
struct Header
{
uint n;
Icon *first;
Icon *last;
};
int debug;
Mouse mouse;
Header h;
Image *background;
ushort
gets(uchar *p)
{
return p[0] | (p[1]<<8);
}
ulong
getl(uchar *p)
{
return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
}
int
Bgetheader(Biobuf *b, Header *h)
{
Icon *icon;
int i;
uchar buf[40];
memset(h, 0, sizeof(*h));
if(Bread(b, buf, 6) != 6)
goto eof;
if(gets(&buf[0]) != 0)
goto header;
if(gets(&buf[2]) != 1)
goto header;
h->n = gets(&buf[4]);
for(i = 0; i < h->n; i++){
icon = mallocz(sizeof(*icon), 1);
if(icon == nil)
sysfatal("malloc: %r");
if(Bread(b, buf, 16) != 16)
goto eof;
icon->w = buf[0];
icon->h = buf[1];
icon->ncolor = buf[2] == 0 ? 256 : buf[2];
if(buf[3] != 0)
goto header;
icon->nplane = gets(&buf[4]);
icon->bits = gets(&buf[6]);
icon->len = getl(&buf[8]);
icon->offset = getl(&buf[12]);
if(i == 0)
h->first = icon;
else
h->last->next = icon;
h->last = icon;
}
return 0;
eof:
werrstr("unexpected EOF");
return -1;
header:
werrstr("unknown header format");
return -1;
}
uchar*
transcmap(Icon *icon, uchar *map)
{
uchar *m, *p;
int i;
p = m = malloc(sizeof(int)*(1<<icon->bits));
for(i = 0; i < icon->ncolor; i++){
*p++ = rgb2cmap(map[2], map[1], map[0]);
map += 4;
}
return m;
}
Image*
xor2img(Icon *icon, uchar *xor, uchar *map)
{
uchar *data;
Image *img;
int inxlen;
uchar *from, *to;
int s, byte, mask;
int x, y;
inxlen = 4*((icon->bits*icon->w+31)/32);
to = data = malloc(icon->w*icon->h);
/* rotate around the y axis, go to 8 bits, and convert color */
mask = (1<<icon->bits)-1;
for(y = 0; y < icon->h; y++){
s = -1;
byte = 0;
from = xor + (icon->h - 1 - y)*inxlen;
for(x = 0; x < icon->w; x++){
if(s < 0){
byte = *from++;
s = 8-icon->bits;
}
*to++ = map[(byte>>s) & mask];
s -= icon->bits;
}
}
/* stick in an image */
img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill);
loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
free(data);
return img;
}
Image*
and2img(Icon *icon, uchar *and)
{
uchar *data;
Image *img;
int inxlen;
int outxlen;
uchar *from, *to;
int x, y;
inxlen = 4*((icon->w+31)/32);
to = data = malloc(inxlen*icon->h);
/* rotate around the y axis and invert bits */
outxlen = (icon->w+7)/8;
for(y = 0; y < icon->h; y++){
from = and + (icon->h - 1 - y)*inxlen;
for(x = 0; x < outxlen; x++){
*to++ = ~(*from++);
}
}
/* stick in an image */
img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill);
loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
free(data);
return img;
}
int
Bgeticon(Biobuf *b, Icon *icon)
{
ulong l;
ushort s;
uchar *xor;
uchar *and;
uchar *cm;
uchar *buf;
uchar *map2map;
Image *img;
Bseek(b, icon->offset, 0);
buf = malloc(icon->len);
if(buf == nil)
return -1;
if(Bread(b, buf, icon->len) != icon->len){
werrstr("unexpected EOF");
return -1;
}
/* this header's info takes precedence over previous one */
if(getl(buf) != 40){
werrstr("bad icon header");
return -1;
}
l = getl(buf+4);
if(l != icon->w)
icon->w = l;
l = getl(buf+8);
if(l>>1 != icon->h)
icon->h = l>>1;
s = gets(buf+12);
if(s != icon->nplane)
icon->nplane = s;
s = gets(buf+14);
if(s != icon->bits)
icon->bits = s;
/* limit what we handle */
switch(icon->bits){
case 1:
case 2:
case 4:
case 8:
break;
default:
werrstr("don't support %d bit pixels", icon->bits);
return -1;
}
if(icon->nplane != 1){
werrstr("don't support %d planes", icon->nplane);
return -1;
}
cm = buf + 40;
xor = cm + 4*icon->ncolor;
and = xor + icon->h*4*((icon->bits*icon->w+31)/32);
/* translate the color map to a plan 9 one */
map2map = transcmap(icon, cm);
/* convert the images */
icon->img = xor2img(icon, xor, map2map);
icon->mask = and2img(icon, and);
/* so that we save an image with a white background */
img = allocimage(display, icon->img->r, CMAP8, 0, DWhite);
draw(img, icon->img->r, icon->img, icon->mask, ZP);
icon->img = img;
free(buf);
free(map2map);
return 0;
}
void
usage(void)
{
fprint(2, "usage: %s [file]\n", argv0);
exits("usage");
}
enum
{
Mimage,
Mmask,
Mexit,
Up= 1,
Down= 0,
};
char *menu3str[] = {
[Mimage] "write image",
[Mmask] "write mask",
[Mexit] "exit",
0,
};
Menu menu3 = {
menu3str
};
Cursor sight = {
{-7, -7},
{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
};
void
buttons(int ud)
{
while((mouse.buttons==0) != ud)
mouse = emouse();
}
void
mesg(char *fmt, ...)
{
va_list arg;
char buf[1024];
static char obuf[1024];
va_start(arg, fmt);
vseprint(buf, buf+sizeof(buf), fmt, arg);
va_end(arg);
string(screen, screen->r.min, background, ZP, font, obuf);
string(screen, screen->r.min, display->white, ZP, font, buf);
strcpy(obuf, buf);
}
void
doimage(Icon *icon)
{
int rv;
char file[256];
int fd;
rv = -1;
snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
fd = create(file, OWRITE, 0664);
if(fd >= 0){
rv = writeimage(fd, icon->img, 0);
close(fd);
}
if(rv < 0)
mesg("error writing %s: %r", file);
else
mesg("created %s", file);
}
void
domask(Icon *icon)
{
int rv;
char file[64];
int fd;
rv = -1;
snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
fd = create(file, OWRITE, 0664);
if(fd >= 0){
rv = writeimage(fd, icon->mask, 0);
close(fd);
}
if(rv < 0)
mesg("error writing %s: %r", file);
else
mesg("created %s", file);
}
void
apply(void (*f)(Icon*))
{
Icon *icon;
esetcursor(&sight);
buttons(Down);
if(mouse.buttons == 4)
for(icon = h.first; icon; icon = icon->next)
if(ptinrect(mouse.xy, icon->sr)){
buttons(Up);
f(icon);
break;
}
buttons(Up);
esetcursor(0);
}
void
menu(void)
{
int sel;
sel = emenuhit(3, &mouse, &menu3);
switch(sel){
case Mimage:
apply(doimage);
break;
case Mmask:
apply(domask);
break;
case Mexit:
exits(0);
break;
}
}
void
mousemoved(void)
{
Icon *icon;
for(icon = h.first; icon; icon = icon->next)
if(ptinrect(mouse.xy, icon->sr)){
mesg("%dx%d", icon->w, icon->h);
return;
}
mesg("");
}
enum
{
BORDER= 1,
};
void
eresized(int new)
{
Icon *icon;
Rectangle r;
if(new && getwindow(display, Refnone) < 0)
sysfatal("can't reattach to window");
draw(screen, screen->clipr, background, nil, ZP);
r.max.x = screen->r.min.x;
r.min.y = screen->r.min.y + font->height + 2*BORDER;
for(icon = h.first; icon != nil; icon = icon->next){
r.min.x = r.max.x + BORDER;
r.max.x = r.min.x + Dx(icon->img->r);
r.max.y = r.min.y + Dy(icon->img->r);
draw(screen, r, icon->img, nil, ZP);
border(screen, r, -BORDER, display->black, ZP);
icon->sr = r;
}
flushimage(display, 1);
}
void
main(int argc, char **argv)
{
Biobuf in;
Icon *icon;
int fd;
Rectangle r;
Event e;
ARGBEGIN{
case 'd':
debug = 1;
break;
}ARGEND;
fd = -1;
switch(argc){
case 0:
fd = 0;
break;
case 1:
fd = open(argv[0], OREAD);
if(fd < 0)
sysfatal("opening: %r");
break;
default:
usage();
break;
}
Binit(&in, fd, OREAD);
if(Bgetheader(&in, &h) < 0)
sysfatal("reading header: %r");
initdraw(nil, nil, "ico");
background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (128<<24)|(128<<16)|(128<<8)|0xFF);
einit(Emouse|Ekeyboard);
r.min = Pt(4, 4);
for(icon = h.first; icon != nil; icon = icon->next){
if(Bgeticon(&in, icon) < 0){
fprint(2, "bad rectangle: %r\n");
continue;
}
if(debug)
fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
r.max = addpt(r.min, Pt(icon->w, icon->h));
icon->r = r;
r.min.x += r.max.x;
}
eresized(0);
for(;;)
switch(event(&e)){
case Ekeyboard:
break;
case Emouse:
mouse = e.mouse;
if(mouse.buttons & 4)
menu();
else
mousemoved();
break;
}
exits(0);
}

82
src/cmd/jpg/imagefile.h Normal file
View file

@ -0,0 +1,82 @@
typedef struct Rawimage Rawimage;
struct Rawimage
{
Rectangle r;
uchar *cmap;
int cmaplen;
int nchans;
uchar *chans[4];
int chandesc;
int chanlen;
int fields; /* defined by format */
int gifflags; /* gif only; graphics control extension flag word */
int gifdelay; /* gif only; graphics control extension delay in cs */
int giftrindex; /* gif only; graphics control extension transparency index */
int gifloopcount; /* number of times to loop in animation; 0 means forever */
};
enum
{
/* Channel descriptors */
CRGB = 0, /* three channels, no map */
CYCbCr = 1, /* three channels, no map, level-shifted 601 color space */
CY = 2, /* one channel, luminance */
CRGB1 = 3, /* one channel, map present */
CRGBV = 4, /* one channel, map is RGBV, understood */
CRGB24 = 5, /* one channel in correct data order for loadimage(RGB24) */
CRGBA32 = 6, /* one channel in correct data order for loadimage(RGBA32) */
CYA16 = 7, /* one channel in correct data order for loadimage(Grey8+Alpha8) */
CRGBVA16= 8, /* one channel in correct data order for loadimage(CMAP8+Alpha8) */
/* GIF flags */
TRANSP = 1,
INPUT = 2,
DISPMASK = 7<<2
};
enum{ /* PNG flags */
II_GAMMA = 1 << 0,
II_COMMENT = 1 << 1,
};
typedef struct ImageInfo {
ulong fields_set;
double gamma;
char *comment;
} ImageInfo;
Rawimage** readjpg(int, int);
Rawimage** Breadjpg(Biobuf *b, int);
Rawimage** readpng(int, int);
Rawimage** Breadpng(Biobuf *b, int);
Rawimage** readgif(int, int);
Rawimage** readpixmap(int, int);
Rawimage* torgbv(Rawimage*, int);
Rawimage* totruecolor(Rawimage*, int);
int writerawimage(int, Rawimage*);
void* _remaperror(char*, ...);
#ifndef _MEMDRAW_H_
typedef struct Memimage Memimage; /* avoid necessity to include memdraw.h */
#endif
char* startgif(Biobuf*, Image*, int);
char* writegif(Biobuf*, Image*, char*, int, int);
void endgif(Biobuf*);
char* memstartgif(Biobuf*, Memimage*, int);
char* memwritegif(Biobuf*, Memimage*, char*, int, int);
void memendgif(Biobuf*);
Image* onechan(Image*);
Memimage* memonechan(Memimage*);
char* writeppm(Biobuf*, Image*, char*);
char* memwriteppm(Biobuf*, Memimage*, char*);
Image* multichan(Image*);
Memimage* memmultichan(Memimage*);
char* memwritepng(Biobuf*, Memimage*, ImageInfo*);
extern int drawlog2[];

346
src/cmd/jpg/jpegdump.c Normal file
View file

@ -0,0 +1,346 @@
/* jpeg parser by tom szymanski */
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
/* subroutines done by macros */
#define min(A,B) ((A)<(B) ? (A) : (B))
#define max(A,B) ((A)>(B) ? (A) : (B))
#define maxeql(A,B) if (A < (B)) A = (B);
#define mineql(A,B) if (A > (B)) A = (B);
#define eatarg0 (argc--, argv++)
#define arrayLength(A) ((sizeof A)/ (sizeof A[0]))
FILE *infile;
char *fname;
/* Routines to print error messages of varying severity */
/* externally visible variables */
int warncnt;
char *myname;
void getname (char *arg) {
/* Save name of invoking program for use by error routines */
register char *p;
p = strrchr (arg, '/');
if (p == NULL)
myname = arg;
else
myname = ++p;
}
static void introduction (void) {
warncnt++;
fflush (stdout);
if (myname != NULL)
fprintf (stderr, "%s: ", myname);
}
void warn (char *fmt, ...) {
va_list args;
introduction ();
va_start (args, fmt);
vfprintf (stderr, fmt, args);
va_end (args);
fputc ('\n', stderr);
fflush (stderr);
}
void quit (char *fmt, ...) {
va_list args;
introduction ();
va_start (args, fmt);
vfprintf (stderr, fmt, args);
va_end (args);
fputc ('\n', stderr);
fflush (stderr);
exit (1);
}
void fatal (char *fmt, ...) {
va_list args;
introduction ();
va_start (args, fmt);
vfprintf (stderr, fmt, args);
va_end (args);
fprintf (stderr, "\nbetter get help!\n");
fflush (stderr);
abort ();
}
int toption = 0;
int dqt[16][64];
int get1 (void) {
unsigned char x;
if (fread(&x, 1, 1, infile) == 0)
quit ("unexpected EOF");
return x;
}
int get2 (void) {
int x;
x = get1() << 8;
return x | get1();
}
void eatmarker (int kind) {
int l, c;
l = get2();
printf ("%02x len=%d\n", kind, l);
for (l -= 2; l > 0; l--)
get1();
}
char *sofName[16] = {
"Baseline sequential DCT - Huffman coding",
"Extended sequential DCT - Huffman coding",
"Progressive DCT - Huffman coding",
"Lossless - Huffman coding",
"4 is otherwise used",
"Sequential DCT - differential Huffman coding",
"Progressive DCT - differential Huffman coding",
"Lossless - differential Huffman coding",
"8 is reserved",
"Extended Sequential DCT - arithmetic coding",
"Progressive DCT - arithmetic coding",
"Lossless - arithmetic coding",
"c is otherwise used",
"Sequential DCT - differential arithmetic coding",
"Progressive DCT - differential arithmetic coding",
"Lossless - differential arithmetic coding",
};
void get_sof (int kind) {
int i, length, height, width, precision, ncomponents;
int id, sf, tab;
length = get2();
precision = get1();
height = get2();
width = get2();
ncomponents = get1();
printf ("SOF%d:\t%s\n", kind - 0xc0, sofName[kind - 0xc0]);
printf ("\t%d wide, %d high, %d deep, %d components\n",
width, height, precision, ncomponents);
for (i = 0; i < ncomponents; i++) {
id = get1();
sf = get1();
tab = get1();
printf ("\tcomponent %d: %d hsample, %d vsample, quantization table %d\n",
id, sf >> 4, sf & 0xf, tab);
}
}
void get_com (int kind) {
int l, c;
l = get2();
printf ("COM len=%d '", l);
for (l -= 2; l > 0; l--)
putchar (c = get1());
printf ("'\n");
}
void get_app (int kind) {
int l, c, first;
char buf[6];
int nbuf, nok;
l = get2();
printf ("APP%d len=%d\n", kind - 0xe0, l);
nbuf = 0;
nok = 0;
first = 1;
/* dump printable strings in comment */
for (l -= 2; l > 0; l--){
c = get1();
if(isprint(c)){
if(nbuf >= sizeof buf){
if(!first && nbuf == nok)
printf(" ");
printf("%.*s", nbuf, buf);
nbuf = 0;
first = 0;
}
buf[nbuf++] = c;
nok++;
}else{
if(nok >= sizeof buf)
if(nbuf > 0)
printf("%.*s", nbuf, buf);
nbuf = 0;
nok = 0;
}
}
if(nok >= sizeof buf)
if(nbuf > 0){
if(!first && nbuf == nok)
printf(" ");
printf("%.*s", nbuf, buf);
}
}
void get_dac (int kind) {
eatmarker (kind);
}
int get1dqt (void) {
int t, p, i, *tab;
t = get1();
p = t >> 4;
t = t & 0xf;
printf ("DQT:\tp = %d, table = %d\n", p, t);
tab = &dqt[t][0];
for (i = 0; i < 64; i++)
tab[i] = p ? get2() : get1();
if (toption) {
for (i = 0; i < 64; i++)
printf ("\t%q[%02d] = %d\n", i, tab[i]);
}
return p ? 65 : 129;
}
void get_dqt (int kind) {
int length;
length = get2() - 2;
while (length > 0)
length -= get1dqt();
}
int get1dht (void) {
int l, tcth, p, i, j, v[16], vv[16][256];
tcth = get1();
printf ("DHT:\tclass = %d, table = %d\n", tcth >> 4, tcth & 0xf);
for (i = 0; i < 16; i++)
v[i] = get1();
l = 17;
for (i = 0; i < 16; i++)
for (j = 0; j < v[i]; j++) {
vv[i][j] = get1();
l += 1;
}
if (toption) {
for (i = 0; i < 16; i++)
printf ("\t%l[%02d] = %d\n", i+1, v[i]);
for (i = 0; i < 16; i++)
for (j = 0; j < v[i]; j++)
printf ("\t%v[%02d,%02d] = %d\n", i+1, j+1, vv[i][j]);
}
return l;
}
void get_dht (int kind) {
int length;
length = get2() - 2;
while (length > 0)
length -= get1dht();
}
void get_sos (int kind) {
int i, length, ncomponents, id, dcac, ahal;
length = get2();
ncomponents = get1();
printf ("SOS:\t%d components\n", ncomponents);
for (i = 0; i < ncomponents; i++) {
id = get1();
dcac = get1();
printf ("\tcomponent %d: %d DC, %d AC\n", id, dcac >> 4, dcac & 0xf);
}
printf ("\tstart spectral %d\n", get1());
printf ("\tend spectral %d\n", get1());
ahal = get1();
printf ("\tah = %d, al = %d\n", ahal >> 4, ahal &0xf);
}
main (int argc, char *argv[]) {
int l, stuff, i, j, c;
while (argc > 1 && argv[1][0] == '-') {
switch (argv[1][1]) {
case 't':
toption = 1;
break;
default:
warn ("bad option '%c'", argv[1][1]);
}
eatarg0;
}
fname = argv[1];
infile = fopen (fname, "r");
if (infile == NULL)
quit ("can't open %s\n", fname);
Start:
// if (get1() != 0xff || get1() != 0xd8)
// quit ("not JFIF");
// printf ("SOI\n");
// get_app (0xe0);
for (;;) {
c = get1();
if (c != 0xff)
quit ("expected marker, got %2x", c);
do {
c = get1();
} while (c == 0xff);
marker:
switch (c) {
case 0xc0: case 0xc1: case 0xc2: case 0xc3:
case 0xc5: case 0xc6: case 0xc7:
case 0xc8: case 0xc9: case 0xca: case 0xcb:
case 0xcd: case 0xce: case 0xcf:
get_sof (c);
break;
case 0xc4:
get_dht (c);
break;
case 0xcc:
get_dac (c);
break;
case 0xd8:
printf ("SOI\n");
break;
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb:
case 0xec: case 0xed: case 0xee: case 0xef:
get_app(c);
break;
case 0xda:
get_sos (c);
goto newentropy;
case 0xdb:
get_dqt (c);
break;
case 0xfe:
get_com (c);
break;
case 0xd9:
printf ("EOI\n");
if((c=getc(infile)) == EOF)
exit(0);
ungetc(c, infile);
goto Start;
default:
eatmarker (c);
}
continue;
newentropy:
l = stuff = 0;
entropy:
while ((c = get1()) != 0xff)
l += 1;
while (c == 0xff)
c = get1();
if (c == 0) {
stuff += 1;
goto entropy;
}
printf ("sequence length %d with %d stuffs\n", l, stuff);
if (0xd0 <= c && c <= 0xd7) {
printf ("restart %d\n", c - 0xd0);
goto newentropy;
}
goto marker;
}
}

342
src/cmd/jpg/jpg.c Normal file
View file

@ -0,0 +1,342 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "imagefile.h"
int cflag = 0;
int dflag = 0;
int eflag = 0;
int jflag = 0;
int fflag = 0;
int Fflag = 0;
int nineflag = 0;
int threeflag = 0;
int colorspace = CYCbCr; /* default for 8-bit displays: combine color rotation with dither */
int output = 0;
ulong outchan = CMAP8;
Image *image;
int defaultcolor = 1;
enum{
Border = 2,
Edge = 5
};
char *show(int, char*, int);
void
eresized(int new)
{
Rectangle r;
if(new && getwindow(display, Refnone) < 0){
fprint(2, "jpg: can't reattach to window\n");
exits("resize");
}
if(image == nil)
return;
r = insetrect(screen->clipr, Edge+Border);
r.max.x = r.min.x+Dx(image->r);
r.max.y = r.min.y+Dy(image->r);
border(screen, r, -Border, nil, ZP);
draw(screen, r, image, nil, image->r.min);
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
int fd, i, yflag;
char *err;
char buf[12+1];
yflag = 0;
ARGBEGIN{
case 'c': /* produce encoded, compressed, bitmap file; no display by default */
cflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
case 'd': /* suppress display of image */
dflag++;
break;
case 'e': /* disable floyd-steinberg error diffusion */
eflag++;
break;
case 'F':
Fflag++; /* make a movie */
fflag++; /* merge two fields per image */
break;
case 'f':
fflag++; /* merge two fields per image */
break;
case 'J': /* decode jpeg only; no display or remap (for debugging, etc.) */
jflag++;
break;
case 'k': /* force black and white */
defaultcolor = 0;
outchan = GREY8;
break;
case 'r':
colorspace = CRGB;
break;
case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
threeflag++;
/* fall through */
case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
cflag++;
dflag++;
output++;
defaultcolor = 0;
outchan = RGB24;
break;
case 'v': /* force RGBV */
defaultcolor = 0;
outchan = CMAP8;
break;
case 'y': /* leave it in CYCbCr; for debugging only */
yflag = 1;
colorspace = CYCbCr;
break;
case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
nineflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
default:
fprint(2, "usage: jpg -39cdefFkJrtv [file.jpg ...]\n");
exits("usage");
}ARGEND;
if(yflag==0 && dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */
fd = open("/dev/screen", OREAD);
if(fd > 0){
buf[12] = '\0';
if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8)
colorspace = CRGB;
close(fd);
}
}
err = nil;
if(argc == 0)
err = show(0, "<stdin>", outchan);
else{
for(i=0; i<argc; i++){
fd = open(argv[i], OREAD);
if(fd < 0){
fprint(2, "jpg: can't open %s: %r\n", argv[i]);
err = "open";
}else{
err = show(fd, argv[i], outchan);
close(fd);
}
if((nineflag || cflag) && argc>1 && err==nil){
fprint(2, "jpg: exiting after one file\n");
break;
}
}
}
exits(err);
}
Rawimage**
vidmerge(Rawimage **aa1, Rawimage **aa2)
{
Rawimage **aao, *ao, *a1, *a2;
int i, c, row, col;
aao = nil;
for (i = 0; aa1[i]; i++) {
a1 = aa1[i];
a2 = aa2[i];
if (a2 == nil){
fprint(2, "jpg: vidmerge: unequal lengths\n");
return nil;
}
aao = realloc(aao, (i+2)*sizeof(Rawimage *));
if (aao == nil){
fprint(2, "jpg: vidmerge: realloc\n");
return nil;
}
aao[i+1] = nil;
ao = aao[i] = malloc(sizeof(Rawimage));
if (ao == nil){
fprint(2, "jpg: vidmerge: realloc\n");
return nil;
}
memcpy(ao, a1, sizeof(Rawimage));
if (!eqrect(a1->r , a2->r)){
fprint(2, "jpg: vidmerge: rects different in img %d\n", i);
return nil;
}
if (a1->cmaplen != a2->cmaplen){
fprint(2, "jpg: vidmerge: cmaplen different in img %d\n", i);
return nil;
}
if (a1->nchans != a2->nchans){
fprint(2, "jpg: vidmerge: nchans different in img %d\n", i);
return nil;
}
if (a1->fields != a2->fields){
fprint(2, "jpg: vidmerge: fields different in img %d\n", i);
return nil;
}
ao->r.max.y += Dy(ao->r);
ao->chanlen += ao->chanlen;
if (ao->chanlen != Dx(ao->r)*Dy(ao->r)){
fprint(2, "jpg: vidmerge: chanlen wrong %d != %d*%d\n",
ao->chanlen, Dx(ao->r), Dy(ao->r));
return nil;
}
row = Dx(a1->r);
for (c = 0; c < ao->nchans; c++) {
uchar *po, *p1, *p2;
ao->chans[c] = malloc(ao->chanlen);
po = ao->chans[c];
p1 = a1->chans[c];
p2 = a2->chans[c];
for (col = 0; col < Dy(a1->r); col++) {
memcpy(po, p1, row);
po += row, p1 += row;
memcpy(po, p2, row);
po += row, p2 += row;
}
free(a1->chans[c]);
free(a2->chans[c]);
}
if(a2->cmap != nil)
free(a2->cmap);
free(a1);
free(a2);
}
if (aa2[i] != nil)
fprint(2, "jpg: vidmerge: unequal lengths\n");
free(aa1);
free(aa2);
return aao;
}
char*
show(int fd, char *name, int outc)
{
Rawimage **array, *r, *c;
static int inited;
Image *i;
int j, ch, outchan;
Biobuf b;
char buf[32];
if(Binit(&b, fd, OREAD) < 0)
return nil;
outchan = outc;
rpt: array = Breadjpg(&b, colorspace);
if(array == nil || array[0]==nil){
fprint(2, "jpg: decode %s failed: %r\n", name);
return "decode";
}
if (fflag) {
Rawimage **a;
a = Breadjpg(&b, colorspace);
if(a == nil || a[0]==nil){
fprint(2, "jpg: decode %s-2 failed: %r\n", name);
return "decode";
}
array = vidmerge(a, array);
} else
Bterm(&b);
r = array[0];
c = nil;
if(jflag)
goto Return;
if(!dflag && !inited){
if(initdraw(0, 0, 0) < 0){
fprint(2, "jpg: initdraw failed: %r\n");
return "initdraw";
}
if(Fflag == 0)
einit(Ekeyboard|Emouse);
if(defaultcolor && screen->depth>8 && outchan==CMAP8)
outchan = RGB24;
inited++;
}
if(outchan == CMAP8)
c = torgbv(r, !eflag);
else{
if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){
c = totruecolor(r, CY);
outchan = GREY8;
}else
c = totruecolor(r, CRGB24);
}
if(c == nil){
fprint(2, "jpg: conversion of %s failed: %r\n", name);
return "torgbv";
}
if(!dflag){
if(c->chandesc == CY)
i = allocimage(display, c->r, GREY8, 0, 0);
else
i = allocimage(display, c->r, outchan, 0, 0);
if(i == nil){
fprint(2, "jpg: allocimage %s failed: %r\n", name);
return "allocimage";
}
if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
fprint(2, "jpg: loadimage %s failed: %r\n", name);
return "loadimage";
}
image = i;
eresized(0);
if (Fflag) {
freeimage(i);
for(j=0; j<r->nchans; j++)
free(r->chans[j]);
free(r->cmap);
free(r);
free(array);
goto rpt;
}
if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
exits(nil);
draw(screen, screen->clipr, display->white, nil, ZP);
image = nil;
freeimage(i);
}
if(nineflag){
chantostr(buf, outchan);
print("%11s %11d %11d %11d %11d ", buf,
c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
if(write(1, c->chans[0], c->chanlen) != c->chanlen){
fprint(2, "jpg: %s: write error %r\n", name);
return "write";
}
}else if(cflag){
if(writerawimage(1, c) < 0){
fprint(2, "jpg: %s: write error: %r\n", name);
return "write";
}
}
Return:
for(j=0; j<r->nchans; j++)
free(r->chans[j]);
free(r->cmap);
free(r);
free(array);
if(c){
free(c->chans[0]);
free(c);
}
if (Fflag) goto rpt;
return nil;
}

52
src/cmd/jpg/mkfile Normal file
View file

@ -0,0 +1,52 @@
<$PLAN9/src/mkhdr
TARG=jpg\
gif\
togif\
ppm\
toppm\
png\
topng\
yuv\
ico\
toico\
bmp\
IMFILES=\
torgbv.$O\
totruecolor.$O\
writerawimage.$O\
HFILES=imagefile.h\
SHORTLIB=draw flate bio 9
LDFLAGS=$LDFLAGS -L$X11/lib -lX11
<$PLAN9/src/mkmany
$O.jpg: $IMFILES readjpg.$O jpg.$O
$O.gif: $IMFILES readgif.$O gif.$O
$O.togif: writegif.$O onechan.$O togif.$O torgbv.$O multichan.$O
$O.ppm: $IMFILES readppm.$O ppm.$O
$O.toppm: writeppm.$O multichan.$O toppm.$O
$O.png: $IMFILES readpng.$O png.$O multichan.$O
$O.topng: writepng.$O topng.$O
$O.yuv: $IMFILES readyuv.$O yuv.$O
$O.bmp: $IMFILES readbmp.$O bmp.$O
torgbv.$O: ycbcr.h rgbv.h
ycbcr.h: rgbycc.c
9c rgbycc.c
9l -o o.rgbycc rgbycc.c
./o.rgbycc >ycbcr.h
rgbv.h: rgbrgbv.c
9c rgbrgbv.c
9l -o o.rgbrgbv rgbrgbv.c
./o.rgbrgbv >rgbv.h
nuke:V: nuke-headers
nuke-headers:V:
rm -f rgbv.h ycbcr.h

60
src/cmd/jpg/multichan.c Normal file
View file

@ -0,0 +1,60 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include "imagefile.h"
/* Separate colors, if not a grey scale or bitmap, into one byte per color per pixel, no alpha or X */
/* Result is GREY[1248] or RGB24 */
int drawlog2[] = {
0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5
};
static
int
notrans(ulong chan)
{
switch(chan){
case GREY1:
case GREY2:
case GREY4:
case GREY8:
case RGB24:
return 1;
}
return 0;
}
Image*
multichan(Image *i)
{
Image *ni;
if(notrans(i->chan))
return i;
ni = allocimage(display, i->r, RGB24, 0, DNofill);
if(ni == nil)
return ni;
draw(ni, ni->r, i, nil, i->r.min);
return ni;
}
Memimage*
memmultichan(Memimage *i)
{
Memimage *ni;
if(notrans(i->chan))
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;
}

229
src/cmd/jpg/onechan.c Normal file
View file

@ -0,0 +1,229 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include "imagefile.h"
/* Convert image to a single channel, one byte per pixel */
static
int
notrans(ulong chan)
{
switch(chan){
case GREY1:
case GREY2:
case GREY4:
case CMAP8:
case GREY8:
return 1;
}
return 0;
}
static
int
easycase(ulong chan)
{
switch(chan){
case RGB16:
case RGB24:
case RGBA32:
case ARGB32:
return 1;
}
return 0;
}
/*
* Convert to one byte per pixel, RGBV or grey, depending
*/
static
uchar*
load(Image *image, Memimage *memimage)
{
uchar *data, *p, *q0, *q1, *q2;
uchar *rgbv;
int depth, ndata, dx, dy, i, v;
ulong chan, pixel;
Rectangle r;
Rawimage ri, *nri;
if(memimage == nil){
r = image->r;
depth = image->depth;
chan = image->chan;
}else{
r = memimage->r;
depth = memimage->depth;
chan = memimage->chan;
}
dx = Dx(r);
dy = Dy(r);
/*
* Read image data into memory
* potentially one extra byte on each end of each scan line.
*/
ndata = dy*(2+bytesperline(r, depth));
data = malloc(ndata);
if(data == nil)
return nil;
if(memimage != nil)
ndata = unloadmemimage(memimage, r, data, ndata);
else
ndata = unloadimage(image, r, data, ndata);
if(ndata < 0){
werrstr("onechan: %r");
free(data);
return nil;
}
/*
* Repack
*/
memset(&ri, 0, sizeof(ri));
ri.r = r;
ri.cmap = nil;
ri.cmaplen = 0;
ri.nchans = 3;
ri.chanlen = dx*dy;
ri.chans[0] = malloc(ri.chanlen);
ri.chans[1] = malloc(ri.chanlen);
ri.chans[2] = malloc(ri.chanlen);
if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){
Err:
free(ri.chans[0]);
free(ri.chans[1]);
free(ri.chans[2]);
free(data);
return nil;
}
ri.chandesc = CRGB;
p = data;
q0 = ri.chans[0];
q1 = ri.chans[1];
q2 = ri.chans[2];
switch(chan){
default:
werrstr("can't handle image type 0x%lux", chan);
goto Err;
case RGB16:
for(i=0; i<ri.chanlen; i++, p+=2){
pixel = (p[1]<<8)|p[0]; /* rrrrrggg gggbbbbb */
v = (pixel & 0xF800) >> 8;
*q0++ = v | (v>>5);
v = (pixel & 0x07E0) >> 3;
*q1++ = v | (v>>6);
v = (pixel & 0x001F) << 3;
*q2++ = v | (v>>5);
}
break;
case RGB24:
for(i=0; i<ri.chanlen; i++){
*q2++ = *p++;
*q1++ = *p++;
*q0++ = *p++;
}
break;
case RGBA32:
for(i=0; i<ri.chanlen; i++){
*q2++ = *p++;
*q1++ = *p++;
*q0++ = *p++;
p++;
}
break;
case ARGB32:
for(i=0; i<ri.chanlen; i++){
p++;
*q2++ = *p++;
*q1++ = *p++;
*q0++ = *p++;
}
break;
}
rgbv = nil;
nri = torgbv(&ri, 1);
if(nri != nil){
rgbv = nri->chans[0];
free(nri);
}
free(ri.chans[0]);
free(ri.chans[1]);
free(ri.chans[2]);
free(data);
return rgbv;
}
Image*
onechan(Image *i)
{
uchar *data;
Image *ni;
if(notrans(i->chan))
return i;
if(easycase(i->chan))
data = load(i, nil);
else{
ni = allocimage(display, i->r, RGB24, 0, DNofill);
if(ni == nil)
return ni;
draw(ni, ni->r, i, nil, i->r.min);
data = load(ni, nil);
freeimage(ni);
}
if(data == nil)
return nil;
ni = allocimage(display, i->r, CMAP8, 0, DNofill);
if(ni != nil)
if(loadimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
freeimage(ni);
ni = nil;
}
free(data);
return ni;
}
Memimage*
memonechan(Memimage *i)
{
uchar *data;
Memimage *ni;
if(notrans(i->chan))
return i;
if(easycase(i->chan))
data = load(nil, i);
else{
ni = allocmemimage(i->r, RGB24);
if(ni == nil)
return ni;
memimagedraw(ni, ni->r, i, i->r.min, nil, ZP, S);
data = load(nil, ni);
freememimage(ni);
}
if(data == nil)
return nil;
ni = allocmemimage(i->r, CMAP8);
if(ni != nil)
if(loadmemimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
freememimage(ni);
ni = nil;
}
free(data);
return ni;
}

224
src/cmd/jpg/png.c Normal file
View file

@ -0,0 +1,224 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "imagefile.h"
extern int debug;
int cflag = 0;
int dflag = 0;
int eflag = 0;
int nineflag = 0;
int threeflag = 0;
int colorspace = CRGB;
int output = 0;
ulong outchan = CMAP8;
Image *image;
int defaultcolor = 1;
enum{
Border = 2,
Edge = 5
};
char *show(int, char*, int);
void
eresized(int new)
{
Rectangle r;
if(new && getwindow(display, Refnone) < 0){
fprint(2, "png: can't reattach to window\n");
exits("resize");
}
if(image == nil)
return;
r = insetrect(screen->clipr, Edge+Border);
r.max.x = r.min.x+Dx(image->r);
r.max.y = r.min.y+Dy(image->r);
border(screen, r, -Border, nil, ZP);
draw(screen, r, image, nil, image->r.min);
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
int fd, i;
char *err;
char buf[12+1];
ARGBEGIN{
case 'c': /* produce encoded, compressed, bitmap file; no display by default */
cflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
case 'D':
debug++;
break;
case 'd': /* suppress display of image */
dflag++;
break;
case 'e': /* disable floyd-steinberg error diffusion */
eflag++;
break;
case 'k': /* force black and white */
defaultcolor = 0;
outchan = GREY8;
break;
case 'r':
colorspace = CRGB;
break;
case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
threeflag++;
/* fall through */
case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
cflag++;
dflag++;
output++;
defaultcolor = 0;
outchan = RGB24;
break;
case 'v': /* force RGBV */
defaultcolor = 0;
outchan = CMAP8;
break;
case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
nineflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
default:
fprint(2, "usage: png -39cdekrtv [file.png ...]\n");
exits("usage");
}ARGEND;
if(dflag==0 && colorspace==CYCbCr){ /* see if we should convert right to RGB */
fd = open("/dev/screen", OREAD);
if(fd > 0){
buf[12] = '\0';
if(read(fd, buf, 12)==12 && chantodepth(strtochan(buf))>8)
colorspace = CRGB;
close(fd);
}
}
err = nil;
if(argc == 0)
err = show(0, "<stdin>", outchan);
else{
for(i=0; i<argc; i++){
fd = open(argv[i], OREAD);
if(fd < 0){
fprint(2, "png: can't open %s: %r\n", argv[i]);
err = "open";
}else{
err = show(fd, argv[i], outchan);
close(fd);
}
if((nineflag || cflag) && argc>1 && err==nil){
fprint(2, "png: exiting after one file\n");
break;
}
}
}
exits(err);
}
char*
show(int fd, char *name, int outc)
{
Rawimage **array, *r, *c;
static int inited;
Image *i;
int j, ch, outchan;
Biobuf b;
char buf[32];
if(Binit(&b, fd, OREAD) < 0)
return nil;
outchan = outc;
array = Breadpng(&b, colorspace);
if(array == nil || array[0]==nil){
fprint(2, "png: decode %s failed: %r\n", name);
return "decode";
}
Bterm(&b);
r = array[0];
if(!dflag && !inited){
if(initdraw(0, 0, 0) < 0){
fprint(2, "png: initdraw failed: %r\n");
return "initdraw";
}
einit(Ekeyboard|Emouse);
if(defaultcolor && screen->depth>8 && outchan==CMAP8)
outchan = RGB24;
inited++;
}
if(outchan == CMAP8)
c = torgbv(r, !eflag);
else{
if(outchan==GREY8 || (r->chandesc==CY && threeflag==0)){
c = totruecolor(r, CY);
outchan = GREY8;
}else
c = totruecolor(r, CRGB24);
}
if(c == nil){
fprint(2, "png: conversion of %s failed: %r\n", name);
return "torgbv";
}
if(!dflag){
if(c->chandesc == CY)
i = allocimage(display, c->r, GREY8, 0, 0);
else
i = allocimage(display, c->r, outchan, 0, 0);
if(i == nil){
fprint(2, "png: allocimage %s failed: %r\n", name);
return "allocimage";
}
if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
fprint(2, "png: loadimage %s failed: %r\n", name);
return "loadimage";
}
image = i;
eresized(0);
if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
exits(nil);
draw(screen, screen->clipr, display->white, nil, ZP);
image = nil;
freeimage(i);
}
if(nineflag){
chantostr(buf, outchan);
print("%11s %11d %11d %11d %11d ", buf,
c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
if(write(1, c->chans[0], c->chanlen) != c->chanlen){
fprint(2, "png: %s: write error %r\n", name);
return "write";
}
}else if(cflag){
if(writerawimage(1, c) < 0){
fprint(2, "png: %s: write error: %r\n", name);
return "write";
}
}
for(j=0; j<r->nchans; j++)
free(r->chans[j]);
free(r->cmap);
free(r);
free(array);
if(c){
free(c->chans[0]);
free(c);
}
return nil;
}

209
src/cmd/jpg/ppm.c Normal file
View file

@ -0,0 +1,209 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "imagefile.h"
int cflag = 0;
int dflag = 0;
int eflag = 0;
int nineflag = 0;
int threeflag = 0;
int output = 0;
ulong outchan = CMAP8;
int defaultcolor = 1;
Image *image;
enum{
Border = 2,
Edge = 5
};
char *show(int, char*);
void
eresized(int new)
{
Rectangle r;
if(new && getwindow(display, Refnone) < 0){
fprint(2, "ppm: can't reattach to window\n");
exits("resize");
}
if(image == nil)
return;
r = insetrect(screen->clipr, Edge+Border);
r.max.x = r.min.x+Dx(image->r);
r.max.y = r.min.y+Dy(image->r);
border(screen, r, -Border, nil, ZP);
draw(screen, r, image, nil, image->r.min);
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
int fd, i;
char *err;
ARGBEGIN{
case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
threeflag++;
/* fall through */
case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
cflag++;
dflag++;
output++;
defaultcolor = 0;
outchan = RGB24;
break;
case 'c': /* produce encoded, compressed, bitmap file; no display by default */
cflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
case 'd': /* suppress display of image */
dflag++;
break;
case 'e': /* disable floyd-steinberg error diffusion */
eflag++;
break;
case 'k': /* force black and white */
defaultcolor = 0;
outchan = GREY8;
break;
case 'v': /* force RGBV */
defaultcolor = 0;
outchan = CMAP8;
break;
case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
nineflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
default:
fprint(2, "usage: ppm -39cdektv [file.ppm ...]\n");
exits("usage");
}ARGEND;
err = nil;
if(argc == 0)
err = show(0, "<stdin>");
else{
for(i=0; i<argc; i++){
fd = open(argv[i], OREAD);
if(fd < 0){
fprint(2, "ppm: can't open %s: %r\n", argv[i]);
err = "open";
}else{
err = show(fd, argv[i]);
close(fd);
}
if((nineflag || cflag) && argc>1 && err==nil){
fprint(2, "ppm: exiting after one file\n");
break;
}
}
}
exits(err);
}
int
init(void)
{
static int inited;
if(inited == 0){
if(initdraw(0, 0, 0) < 0){
fprint(2, "ppm: initdraw failed: %r");
return -1;
}
einit(Ekeyboard|Emouse);
inited++;
}
return 1;
}
char*
show(int fd, char *name)
{
Rawimage **array, *r, *c;
Image *i;
int j, ch;
char buf[32];
array = readpixmap(fd, CRGB);
if(array == nil || array[0]==nil){
fprint(2, "ppm: decode %s failed: %r\n", name);
return "decode";
}
if(!dflag){
if(init() < 0)
return "initdraw";
if(defaultcolor && screen->depth>8)
outchan = RGB24;
}
r = array[0];
if(outchan == CMAP8)
c = torgbv(r, !eflag);
else{
if(outchan==GREY8 || (r->chandesc==CY && threeflag==0))
c = totruecolor(r, CY);
else
c = totruecolor(r, CRGB24);
}
if(c == nil){
fprint(2, "ppm: converting %s to local format failed: %r\n", name);
return "torgbv";
}
if(!dflag){
if(r->chandesc == CY)
i = allocimage(display, c->r, GREY8, 0, 0);
else
i = allocimage(display, c->r, outchan, 0, 0);
if(i == nil){
fprint(2, "ppm: allocimage %s failed: %r\n", name);
return "allocimage";
}
if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
fprint(2, "ppm: loadimage %s failed: %r\n", name);
return "loadimage";
}
image = i;
eresized(0);
if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
exits(nil);
draw(screen, screen->clipr, display->white, nil, ZP);
image = nil;
freeimage(i);
}
if(nineflag){
chantostr(buf, outchan);
print("%11s %11d %11d %11d %11d ", buf,
c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
if(write(1, c->chans[0], c->chanlen) != c->chanlen){
fprint(2, "ppm: %s: write error %r\n", name);
return "write";
}
}else if(cflag){
if(writerawimage(1, c) < 0){
fprint(2, "ppm: %s: write error: %r\n", name);
return "write";
}
}
for(j=0; j<r->nchans; j++)
free(r->chans[j]);
free(r->cmap);
free(r);
free(array);
if(c){
free(c->chans[0]);
free(c);
}
return nil;
}

626
src/cmd/jpg/readbmp.c Normal file
View file

@ -0,0 +1,626 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include "imagefile.h"
#include "bmp.h"
/*
MS-BMP file reader
(c) 2003, I.P.Keller
aims to decode *all* valid bitmap formats, although some of the
flavours couldn't be verified due to lack of suitable test-files.
the following flavours are supported:
Bit/Pix Orientation Compression Tested?
1 top->bottom n/a yes
1 bottom->top n/a yes
4 top->bottom no yes
4 bottom->top no yes
4 top->bottom RLE4 yes, but not with displacement
8 top->bottom no yes
8 bottom->top no yes
8 top->bottom RLE8 yes, but not with displacement
16 top->bottom no no
16 bottom->top no no
16 top->bottom BITMASK no
16 bottom->top BITMASK no
24 top->bottom n/a yes
24 bottom->top n/a yes
32 top->bottom no no
32 bottom->top no no
32 top->bottom BITMASK no
32 bottom->top BITMASK no
OS/2 1.x bmp files are recognised as well, but testing was very limited.
verifying was done with a number of test files, generated by
different tools. nevertheless, the tests were in no way exhaustive
enough to guarantee bug-free decoding. caveat emptor!
*/
static short
r16(Biobuf*b)
{
short s;
s = Bgetc(b);
s |= ((short)Bgetc(b)) << 8;
return s;
}
static long
r32(Biobuf*b)
{
long l;
l = Bgetc(b);
l |= ((long)Bgetc(b)) << 8;
l |= ((long)Bgetc(b)) << 16;
l |= ((long)Bgetc(b)) << 24;
return l;
}
/* get highest bit set */
static int
msb(ulong x)
{
int i;
for(i = 32; i; i--, x <<= 1)
if(x & 0x80000000L)
return i;
return 0;
}
/* Load a 1-Bit encoded BMP file (uncompressed) */
static int
load_1T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
{
long ix, iy, i = 0, step_up = 0, padded_width = ((width + 31) / 32) * 32;
int val = 0, n;
if(height > 0) { /* bottom-up */
i = (height - 1) * width;
step_up = -2 * width;
} else
height = -height;
for(iy = height; iy; iy--, i += step_up)
for(ix = 0, n = 0; ix < padded_width; ix++, n--) {
if(!n) {
val = Bgetc(b);
n = 8;
}
if(ix < width) {
buf[i] = clut[val & 0x80 ? 1 : 0];
i++;
}
val <<= 1;
}
return 0;
}
/* Load a 4-Bit encoded BMP file (uncompressed) */
static int
load_4T(Biobuf* b, long width, long height, Rgb* buf, Rgb* clut)
{
long ix, iy, i = 0, step_up = 0, skip = (4 - (((width % 8) + 1) / 2)) & 3;
uint valH, valL;
if(height > 0) { /* bottom-up */
i = (height - 1) * width;
step_up = -2 * width;
} else
height = -height;
for(iy = height; iy; iy--, i += step_up) {
for(ix = 0; ix < width; ) {
valH = valL = Bgetc(b) & 0xff;
valH >>= 4;
buf[i] = clut[valH];
i++; ix++;
if(ix < width) {
valL &= 0xf;
buf[i] = clut[valL];
i++; ix++;
}
}
Bseek(b, skip, 1);
}
return 0;
}
/* Load a 4-Bit encoded BMP file (RLE4-compressed) */
static int
load_4C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
{
long ix, iy = height -1;
uint val, valS, skip;
Rgb* p;
while(iy >= 0) {
ix = 0;
while(ix < width) {
val = Bgetc(b);
if(0 != val) {
valS = (uint)Bgetc(b);
p = &buf[ix + iy * width];
while(val--) {
*p = clut[0xf & (valS >> 4)];
p++;
ix++;
if(0 < val) {
*p = clut[0xf & valS];
p++;
ix++;
val--;
}
}
} else {
/* Special modes... */
val = Bgetc(b);
switch(val) {
case 0: /* End-Of-Line detected */
ix = width;
iy--;
break;
case 1: /* End-Of-Picture detected -->> abort */
ix = width;
iy = -1;
break;
case 2: /* Position change detected */
val = Bgetc(b);
ix += val;
val = Bgetc(b);
iy -= val;
break;
default:/* Transparent data sequence detected */
p = &buf[ix + iy * width];
if((1 == (val & 3)) || (2 == (val & 3)))
skip = 1;
else
skip = 0;
while(val--) {
valS = (uint)Bgetc(b);
*p = clut[0xf & (valS >> 4)];
p++;
ix++;
if(0 < val) {
*p = clut[0xf & valS];
p++;
ix++;
val--;
}
}
if(skip)
Bgetc(b);
break;
}
}
}
}
return 0;
}
/* Load a 8-Bit encoded BMP file (uncompressed) */
static int
load_8T(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
{
long ix, iy, i = 0, step_up = 0, skip = (4 - (width % 4)) & 3;
if(height > 0) { /* bottom-up */
i = (height - 1) * width;
step_up = -2 * width;
} else
height = -height;
for(iy = height; iy; iy--, i += step_up) {
for(ix = 0; ix < width; ix++, i++)
buf[i] = clut[Bgetc(b) & 0xff];
Bseek(b, skip, 1);
}
return 0;
}
/* Load a 8-Bit encoded BMP file (RLE8-compressed) */
static int
load_8C(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
{
long ix, iy = height -1;
int val, valS, skip;
Rgb* p;
while(iy >= 0) {
ix = 0;
while(ix < width) {
val = Bgetc(b);
if(0 != val) {
valS = Bgetc(b);
p = &buf[ix + iy * width];
while(val--) {
*p = clut[valS];
p++;
ix++;
}
} else {
/* Special modes... */
val = Bgetc(b);
switch(val) {
case 0: /* End-Of-Line detected */
ix = width;
iy--;
break;
case 1: /* End-Of-Picture detected */
ix = width;
iy = -1;
break;
case 2: /* Position change detected */
val = Bgetc(b);
ix += val;
val = Bgetc(b);
iy -= val;
break;
default: /* Transparent (not compressed) sequence detected */
p = &buf[ix + iy * width];
if(val & 1)
skip = 1;
else
skip = 0;
while(val--) {
valS = Bgetc(b);
*p = clut[valS];
p++;
ix++;
}
if(skip)
/* Align data stream */
Bgetc(b);
break;
}
}
}
}
return 0;
}
/* Load a 16-Bit encoded BMP file (uncompressed) */
static int
load_16(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
{
uchar c[2];
long ix, iy, i = 0, step_up = 0;
if(height > 0) { /* bottom-up */
i = (height - 1) * width;
step_up = -2 * width;
} else
height = -height;
if(clut) {
unsigned mask_blue = (unsigned)clut[0].blue +
((unsigned)clut[0].green << 8);
unsigned mask_green = (unsigned)clut[1].blue +
((unsigned)clut[1].green << 8);
unsigned mask_red = (unsigned)clut[2].blue +
((unsigned)clut[2].green << 8);
int shft_blue = msb((ulong)mask_blue) - 8;
int shft_green = msb((ulong)mask_green) - 8;
int shft_red = msb((ulong)mask_red) - 8;
for(iy = height; iy; iy--, i += step_up)
for(ix = 0; ix < width; ix++, i++) {
unsigned val;
Bread(b, c, sizeof(c));
val = (unsigned)c[0] + ((unsigned)c[1] << 8);
buf[i].alpha = 0;
if(shft_blue >= 0)
buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
else
buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
if(shft_green >= 0)
buf[i].green = (uchar)((val & mask_green) >> shft_green);
else
buf[i].green = (uchar)((val & mask_green) << -shft_green);
if(shft_red >= 0)
buf[i].red = (uchar)((val & mask_red) >> shft_red);
else
buf[i].red = (uchar)((val & mask_red) << -shft_red);
}
} else
for(iy = height; iy; iy--, i += step_up)
for(ix = 0; ix < width; ix++, i++) {
Bread(b, c, sizeof(c));
buf[i].blue = (uchar)((c[0] << 3) & 0xf8);
buf[i].green = (uchar)(((((unsigned)c[1] << 6) +
(((unsigned)c[0]) >> 2))) & 0xf8);
buf[i].red = (uchar)((c[1] << 1) & 0xf8);
}
return 0;
}
/* Load a 24-Bit encoded BMP file (uncompressed) */
static int
load_24T(Biobuf* b, long width, long height, Rgb* buf)
{
long ix, iy, i = 0, step_up = 0, skip = (4 - ((width * 3) % 4)) & 3;
if(height > 0) { /* bottom-up */
i = (height - 1) * width;
step_up = -2 * width;
} else
height = -height;
for(iy = height; iy; iy--, i += step_up) {
for(ix = 0; ix < width; ix++, i++) {
buf[i].alpha = 0;
buf[i].blue = Bgetc(b);
buf[i].green = Bgetc(b);
buf[i].red = Bgetc(b);
}
Bseek(b, skip, 1);
}
return 0;
}
/* Load a 32-Bit encoded BMP file (uncompressed) */
static int
load_32(Biobuf *b, long width, long height, Rgb* buf, Rgb* clut)
{
uchar c[4];
long ix, iy, i = 0, step_up = 0;
if(height > 0) { /* bottom-up */
i = (height - 1) * width;
step_up = -2 * width;
} else
height = -height;
if(clut) {
ulong mask_blue = (ulong)clut[0].blue +
((ulong)clut[0].green << 8) +
((ulong)clut[0].red << 16) +
((ulong)clut[0].alpha << 24);
ulong mask_green = (ulong)clut[1].blue +
((ulong)clut[1].green << 8) +
((ulong)clut[1].red << 16) +
((ulong)clut[1].alpha << 24);
ulong mask_red = (ulong)clut[2].blue +
((ulong)clut[2].green << 8) +
((ulong)clut[2].red << 16) +
((ulong)clut[2].alpha << 24);
int shft_blue = msb(mask_blue) - 8;
int shft_green = msb(mask_green) - 8;
int shft_red = msb(mask_red) - 8;
for(iy = height; iy; iy--, i += step_up)
for(ix = 0; ix < width; ix++, i++) {
ulong val;
Bread(b, c, sizeof(c));
val = (ulong)c[0] + ((ulong)c[1] << 8) +
((ulong)c[2] << 16) + ((ulong)c[1] << 24);
buf[i].alpha = 0;
if(shft_blue >= 0)
buf[i].blue = (uchar)((val & mask_blue) >> shft_blue);
else
buf[i].blue = (uchar)((val & mask_blue) << -shft_blue);
if(shft_green >= 0)
buf[i].green = (uchar)((val & mask_green) >> shft_green);
else
buf[i].green = (uchar)((val & mask_green) << -shft_green);
if(shft_red >= 0)
buf[i].red = (uchar)((val & mask_red) >> shft_red);
else
buf[i].red = (uchar)((val & mask_red) << -shft_red);
}
} else
for(iy = height; iy; iy--, i += step_up)
for(ix = 0; ix < width; ix++, i++) {
Bread(b, c, nelem(c));
buf[i].blue = c[0];
buf[i].green = c[1];
buf[i].red = c[2];
}
return 0;
}
static Rgb*
ReadBMP(Biobuf *b, int *width, int *height)
{
int colours, num_coltab = 0;
Filehdr bmfh;
Infohdr bmih;
Rgb clut[256];
Rgb* buf;
bmfh.type = r16(b);
if(bmfh.type != 0x4d42) /* signature must be 'BM' */
sysfatal("bad magic number, not a BMP file");
bmfh.size = r32(b);
bmfh.reserved1 = r16(b);
bmfh.reserved2 = r16(b);
bmfh.offbits = r32(b);
memset(&bmih, 0, sizeof(bmih));
bmih.size = r32(b);
if(bmih.size == 0x0c) { /* OS/2 1.x version */
bmih.width = r16(b);
bmih.height = r16(b);
bmih.planes = r16(b);
bmih.bpp = r16(b);
bmih.compression = BMP_RGB;
} else { /* Windows */
bmih.width = r32(b);
bmih.height = r32(b);
bmih.planes = r16(b);
bmih.bpp = r16(b);
bmih.compression = r32(b);
bmih.imagesize = r32(b);
bmih.hres = r32(b);
bmih.vres = r32(b);
bmih.colours = r32(b);
bmih.impcolours = r32(b);
}
if(bmih.bpp < 16) {
/* load colour table */
if(bmih.impcolours)
num_coltab = (int)bmih.impcolours;
else
num_coltab = 1 << bmih.bpp;
} else if(bmih.compression == BMP_BITFIELDS &&
(bmih.bpp == 16 || bmih.bpp == 32))
/* load bitmasks */
num_coltab = 3;
if(num_coltab) {
int i;
Bseek(b, bmih.size + sizeof(Infohdr), 0);
for(i = 0; i < num_coltab; i++) {
clut[i].blue = (uchar)Bgetc(b);
clut[i].green = (uchar)Bgetc(b);
clut[i].red = (uchar)Bgetc(b);
clut[i].alpha = (uchar)Bgetc(b);
}
}
*width = bmih.width;
*height = bmih.height;
colours = bmih.bpp;
Bseek(b, bmfh.offbits, 0);
if ((buf = calloc(sizeof(Rgb), *width * abs(*height))) == nil)
sysfatal("no memory");
switch(colours) {
case 1:
load_1T(b, *width, *height, buf, clut);
break;
case 4:
if(bmih.compression == BMP_RLE4)
load_4C(b, *width, *height, buf, clut);
else
load_4T(b, *width, *height, buf, clut);
break;
case 8:
if(bmih.compression == BMP_RLE8)
load_8C(b, *width, *height, buf, clut);
else
load_8T(b, *width, *height, buf, clut);
break;
case 16:
load_16(b, *width, *height, buf,
bmih.compression == BMP_BITFIELDS ? clut : nil);
break;
case 24:
load_24T(b, *width, *height, buf);
break;
case 32:
load_32(b, *width, *height, buf,
bmih.compression == BMP_BITFIELDS ? clut : nil);
break;
}
return buf;
}
Rawimage**
Breadbmp(Biobuf *bp, int colourspace)
{
Rawimage *a, **array;
int c, width, height;
uchar *r, *g, *b;
Rgb *s, *e;
Rgb *bmp;
char ebuf[128];
a = nil;
bmp = nil;
array = nil;
USED(a);
USED(bmp);
if (colourspace != CRGB) {
errstr(ebuf, sizeof ebuf); /* throw it away */
werrstr("ReadRGB: unknown colour space %d", colourspace);
return nil;
}
if ((bmp = ReadBMP(bp, &width, &height)) == nil)
return nil;
if ((a = calloc(sizeof(Rawimage), 1)) == nil)
goto Error;
for (c = 0; c < 3; c++)
if ((a->chans[c] = calloc(width, height)) == nil)
goto Error;
if ((array = calloc(sizeof(Rawimage *), 2)) == nil)
goto Error;
array[0] = a;
array[1] = nil;
a->nchans = 3;
a->chandesc = CRGB;
a->chanlen = width * height;
a->r = Rect(0, 0, width, height);
s = bmp;
e = s + width * height;
r = a->chans[0];
g = a->chans[1];
b = a->chans[2];
do {
*r++ = s->red;
*g++ = s->green;
*b++ = s->blue;
}while(++s < e);
free(bmp);
return array;
Error:
if (a)
for (c = 0; c < 3; c++)
if (a->chans[c])
free(a->chans[c]);
if (a)
free(a);
if (array)
free(array);
if (bmp)
free(bmp);
return nil;
}
Rawimage**
readbmp(int fd, int colorspace)
{
Rawimage * *a;
Biobuf b;
if (Binit(&b, fd, OREAD) < 0)
return nil;
a = Breadbmp(&b, colorspace);
Bterm(&b);
return a;
}

535
src/cmd/jpg/readgif.c Normal file
View file

@ -0,0 +1,535 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include "imagefile.h"
typedef struct Entry Entry;
typedef struct Header Header;
struct Entry{
int prefix;
int exten;
};
struct Header{
Biobuf *fd;
char err[256];
jmp_buf errlab;
uchar buf[3*256];
char vers[8];
uchar *globalcmap;
int screenw;
int screenh;
int fields;
int bgrnd;
int aspect;
int flags;
int delay;
int trindex;
int loopcount;
Entry tbl[4096];
Rawimage **array;
Rawimage *new;
uchar *pic;
};
static char readerr[] = "ReadGIF: read error: %r";
static char extreaderr[] = "ReadGIF: can't read extension: %r";
static char memerr[] = "ReadGIF: malloc failed: %r";
static Rawimage** readarray(Header*);
static Rawimage* readone(Header*);
static void readheader(Header*);
static void skipextension(Header*);
static uchar* readcmap(Header*, int);
static uchar* decode(Header*, Rawimage*, Entry*);
static void interlace(Header*, Rawimage*);
static
void
clear(void *pp)
{
void **p = (void**)pp;
if(*p){
free(*p);
*p = nil;
}
}
static
void
giffreeall(Header *h, int freeimage)
{
int i;
if(h->fd){
Bterm(h->fd);
h->fd = nil;
}
clear(&h->pic);
if(h->new){
clear(&h->new->cmap);
clear(&h->new->chans[0]);
clear(&h->new);
}
clear(&h->globalcmap);
if(freeimage && h->array!=nil){
for(i=0; h->array[i]; i++){
clear(&h->array[i]->cmap);
clear(&h->array[i]->chans[0]);
}
clear(&h->array);
}
}
static
void
giferror(Header *h, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vseprint(h->err, h->err+sizeof h->err, fmt, arg);
va_end(arg);
werrstr(h->err);
giffreeall(h, 1);
longjmp(h->errlab, 1);
}
Rawimage**
readgif(int fd, int colorspace)
{
Rawimage **a;
Biobuf b;
Header *h;
char buf[ERRMAX];
buf[0] = '\0';
USED(colorspace);
if(Binit(&b, fd, OREAD) < 0)
return nil;
h = malloc(sizeof(Header));
if(h == nil){
Bterm(&b);
return nil;
}
memset(h, 0, sizeof(Header));
h->fd = &b;
errstr(buf, sizeof buf); /* throw it away */
if(setjmp(h->errlab))
a = nil;
else
a = readarray(h);
giffreeall(h, 0);
free(h);
return a;
}
static
void
inittbl(Header *h)
{
int i;
Entry *tbl;
tbl = h->tbl;
for(i=0; i<258; i++) {
tbl[i].prefix = -1;
tbl[i].exten = i;
}
}
static
Rawimage**
readarray(Header *h)
{
Entry *tbl;
Rawimage *new, **array;
int c, nimages;
tbl = h->tbl;
readheader(h);
if(h->fields & 0x80)
h->globalcmap = readcmap(h, (h->fields&7)+1);
array = malloc(sizeof(Rawimage**));
if(array == nil)
giferror(h, memerr);
nimages = 0;
array[0] = nil;
h->array = array;
for(;;){
switch(c = Bgetc(h->fd)){
case Beof:
goto Return;
case 0x21: /* Extension (ignored) */
skipextension(h);
break;
case 0x2C: /* Image Descriptor */
inittbl(h);
new = readone(h);
if(new->fields & 0x80){
new->cmaplen = 3*(1<<((new->fields&7)+1));
new->cmap = readcmap(h, (new->fields&7)+1);
}else{
new->cmaplen = 3*(1<<((h->fields&7)+1));
new->cmap = malloc(new->cmaplen);
memmove(new->cmap, h->globalcmap, new->cmaplen);
}
h->new = new;
new->chans[0] = decode(h, new, tbl);
if(new->fields & 0x40)
interlace(h, new);
new->gifflags = h->flags;
new->gifdelay = h->delay;
new->giftrindex = h->trindex;
new->gifloopcount = h->loopcount;
array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
if(array == nil)
giferror(h, memerr);
array[nimages++] = new;
array[nimages] = nil;
h->array = array;
h->new = nil;
break;
case 0x3B: /* Trailer */
goto Return;
default:
fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
goto Return;
}
}
Return:
if(array[0]==nil || array[0]->chans[0] == nil)
giferror(h, "ReadGIF: no picture in file");
return array;
}
static
void
readheader(Header *h)
{
if(Bread(h->fd, h->buf, 13) != 13)
giferror(h, "ReadGIF: can't read header: %r");
memmove(h->vers, h->buf, 6);
if(strcmp(h->vers, "GIF87a")!=0 && strcmp(h->vers, "GIF89a")!=0)
giferror(h, "ReadGIF: can't recognize format %s", h->vers);
h->screenw = h->buf[6]+(h->buf[7]<<8);
h->screenh = h->buf[8]+(h->buf[9]<<8);
h->fields = h->buf[10];
h->bgrnd = h->buf[11];
h->aspect = h->buf[12];
h->flags = 0;
h->delay = 0;
h->trindex = 0;
h->loopcount = -1;
}
static
uchar*
readcmap(Header *h, int size)
{
uchar *map;
if(size > 8)
giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
size = 3*(1<<size);
if(Bread(h->fd, h->buf, size) != size)
giferror(h, "ReadGIF: short read on color map");
map = malloc(size);
if(map == nil)
giferror(h, memerr);
memmove(map, h->buf, size);
return map;
}
static
Rawimage*
readone(Header *h)
{
Rawimage *i;
int left, top, width, height;
if(Bread(h->fd, h->buf, 9) != 9)
giferror(h, "ReadGIF: can't read image descriptor: %r");
i = malloc(sizeof(Rawimage));
if(i == nil)
giferror(h, memerr);
left = h->buf[0]+(h->buf[1]<<8);
top = h->buf[2]+(h->buf[3]<<8);
width = h->buf[4]+(h->buf[5]<<8);
height = h->buf[6]+(h->buf[7]<<8);
i->fields = h->buf[8];
i->r.min.x = left;
i->r.min.y = top;
i->r.max.x = left+width;
i->r.max.y = top+height;
i->nchans = 1;
i->chandesc = CRGB1;
return i;
}
static
int
readdata(Header *h, uchar *data)
{
int nbytes, n;
nbytes = Bgetc(h->fd);
if(nbytes < 0)
giferror(h, "ReadGIF: can't read data: %r");
if(nbytes == 0)
return 0;
n = Bread(h->fd, data, nbytes);
if(n < 0)
giferror(h, "ReadGIF: can't read data: %r");
if(n != nbytes)
fprint(2, "ReadGIF: short data subblock\n");
return n;
}
static
void
graphiccontrol(Header *h)
{
if(Bread(h->fd, h->buf, 5+1) != 5+1)
giferror(h, readerr);
h->flags = h->buf[1];
h->delay = h->buf[2]+(h->buf[3]<<8);
h->trindex = h->buf[4];
}
static
void
skipextension(Header *h)
{
int type, hsize, hasdata, n;
uchar data[256];
hsize = 0;
hasdata = 0;
type = Bgetc(h->fd);
switch(type){
case Beof:
giferror(h, extreaderr);
break;
case 0x01: /* Plain Text Extension */
hsize = 13;
hasdata = 1;
break;
case 0xF9: /* Graphic Control Extension */
graphiccontrol(h);
return;
case 0xFE: /* Comment Extension */
hasdata = 1;
break;
case 0xFF: /* Application Extension */
hsize = Bgetc(h->fd);
/* standard says this must be 11, but Adobe likes to put out 10-byte ones,
* so we pay attention to the field. */
hasdata = 1;
break;
default:
giferror(h, "ReadGIF: unknown extension");
}
if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize)
giferror(h, extreaderr);
if(!hasdata){
if(h->buf[hsize-1] != 0)
giferror(h, "ReadGIF: bad extension format");
return;
}
/* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
n = readdata(h, data);
if(n == 0)
return;
if(n==3 && data[0]==1)
h->loopcount = data[1] | (data[2]<<8);
}
while(readdata(h, data) != 0)
;
}
static
uchar*
decode(Header *h, Rawimage *i, Entry *tbl)
{
int c, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
int csize, nentry, maxentry, first, ocode, ndata, nb;
uchar *pic;
uchar stack[4096], data[256];
if(Bread(h->fd, h->buf, 1) != 1)
giferror(h, "ReadGIF: can't read data: %r");
codesize = h->buf[0];
if(codesize>8 || 0>codesize)
giferror(h, "ReadGIF: can't handle codesize %d", codesize);
if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
&& (codesize!=2 || i->cmaplen!=3*2)) /* peculiar GIF bitmap files... */
giferror(h, "ReadGIF: codesize %d doesn't match color map 3*%d", codesize, i->cmaplen/3);
CTM =1<<codesize;
EOD = CTM+1;
piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
i->chanlen = piclen;
pic = malloc(piclen);
if(pic == nil)
giferror(h, memerr);
h->pic = pic;
pici = 0;
ndata = 0;
datai = 0;
nbits = 0;
sreg = 0;
fc = 0;
Loop:
for(;;){
csize = codesize+1;
nentry = EOD+1;
maxentry = (1<<csize)-1;
first = 1;
ocode = -1;
for(;; ocode = incode) {
while(nbits < csize) {
if(datai == ndata){
ndata = readdata(h, data);
if(ndata == 0)
goto Return;
datai = 0;
}
c = data[datai++];
sreg |= c<<nbits;
nbits += 8;
}
code = sreg & ((1<<csize) - 1);
sreg >>= csize;
nbits -= csize;
if(code == EOD){
ndata = readdata(h, data);
if(ndata != 0)
fprint(2, "ReadGIF: unexpected data past EOD");
goto Return;
}
if(code == CTM)
goto Loop;
stacki = (sizeof stack)-1;
incode = code;
/* special case for KwKwK */
if(code == nentry) {
stack[stacki--] = fc;
code = ocode;
}
if(code > nentry)
giferror(h, "ReadGIF: bad code %x %x", code, nentry);
for(c=code; c>=0; c=tbl[c].prefix)
stack[stacki--] = tbl[c].exten;
nb = (sizeof stack)-(stacki+1);
if(pici+nb > piclen){
/* this common error is harmless
* we have to keep reading to keep the blocks in sync */
;
}else{
memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
pici += nb;
}
fc = stack[stacki+1];
if(first){
first = 0;
continue;
}
#define early 0 /* peculiar tiff feature here for reference */
if(nentry == maxentry-early) {
if(csize >= 12)
continue;
csize++;
maxentry = (1<<csize);
if(csize < 12)
maxentry--;
}
tbl[nentry].prefix = ocode;
tbl[nentry].exten = fc;
nentry++;
}
}
Return:
h->pic = nil;
return pic;
}
static
void
interlace(Header *h, Rawimage *image)
{
uchar *pic;
Rectangle r;
int dx, yy, y;
uchar *ipic;
pic = image->chans[0];
r = image->r;
dx = r.max.x-r.min.x;
ipic = malloc(dx*(r.max.y-r.min.y));
if(ipic == nil)
giferror(h, nil);
/* Group 1: every 8th row, starting with row 0 */
yy = 0;
for(y=r.min.y; y<r.max.y; y+=8){
memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
yy++;
}
/* Group 2: every 8th row, starting with row 4 */
for(y=r.min.y+4; y<r.max.y; y+=8){
memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
yy++;
}
/* Group 3: every 4th row, starting with row 2 */
for(y=r.min.y+2; y<r.max.y; y+=4){
memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
yy++;
}
/* Group 4: every 2nd row, starting with row 1 */
for(y=r.min.y+1; y<r.max.y; y+=2){
memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
yy++;
}
free(image->chans[0]);
image->chans[0] = ipic;
}

1661
src/cmd/jpg/readjpg.c Normal file

File diff suppressed because it is too large Load diff

334
src/cmd/jpg/readpng.c Normal file
View 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;
}

238
src/cmd/jpg/readppm.c Normal file
View file

@ -0,0 +1,238 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <ctype.h>
#include "imagefile.h"
Rawimage *readppm(Biobuf*, Rawimage*);
/*
* fetch a non-comment character.
*/
static
int
Bgetch(Biobuf *b)
{
int c;
for(;;) {
c = Bgetc(b);
if(c == '#') {
while((c = Bgetc(b)) != Beof && c != '\n')
;
}
return c;
}
}
/*
* fetch a nonnegative decimal integer.
*/
static
int
Bgetint(Biobuf *b)
{
int c;
int i;
while((c = Bgetch(b)) != Beof && !isdigit(c))
;
if(c == Beof)
return -1;
i = 0;
do {
i = i*10 + (c-'0');
} while((c = Bgetch(b)) != Beof && isdigit(c));
return i;
}
static
int
Bgetdecimalbit(Biobuf *b)
{
int c;
while((c = Bgetch(b)) != Beof && c != '0' && c != '1')
;
if(c == Beof)
return -1;
return c == '1';
}
static int bitc, nbit;
static
int
Bgetbit(Biobuf *b)
{
if(nbit == 0) {
nbit = 8;
bitc = Bgetc(b);
if(bitc == -1)
return -1;
}
nbit--;
return (bitc >> (nbit-1)) & 0x1;
}
static
void
Bflushbit(Biobuf *b)
{
USED(b);
nbit = 0;
}
Rawimage**
readpixmap(int fd, int colorspace)
{
Rawimage **array, *a;
Biobuf b;
char buf[ERRMAX];
int i;
char *e;
USED(colorspace);
if(Binit(&b, fd, OREAD) < 0)
return nil;
werrstr("");
e = "out of memory";
if((array = malloc(sizeof *array)) == nil)
goto Error;
if((array[0] = malloc(sizeof *array[0])) == nil)
goto Error;
memset(array[0], 0, sizeof *array[0]);
for(i=0; i<3; i++)
array[0]->chans[i] = nil;
e = "bad file format";
switch(Bgetc(&b)) {
case 'P':
Bungetc(&b);
a = readppm(&b, array[0]);
break;
default:
a = nil;
break;
}
if(a == nil)
goto Error;
array[0] = a;
return array;
Error:
if(array)
free(array[0]);
free(array);
errstr(buf, sizeof buf);
if(buf[0] == 0)
strcpy(buf, e);
errstr(buf, sizeof buf);
return nil;
}
typedef struct Pix Pix;
struct Pix {
char magic;
int maxcol;
int (*fetch)(Biobuf*);
int nchan;
int chandesc;
int invert;
void (*flush)(Biobuf*);
};
static Pix pix[] = {
{ '1', 1, Bgetdecimalbit, 1, CY, 1, nil }, /* portable bitmap */
{ '4', 1, Bgetbit, 1, CY, 1, Bflushbit }, /* raw portable bitmap */
{ '2', 0, Bgetint, 1, CY, 0, nil }, /* portable greymap */
{ '5', 0, Bgetc, 1, CY, 0, nil }, /* raw portable greymap */
{ '3', 0, Bgetint, 3, CRGB, 0, nil }, /* portable pixmap */
{ '6', 0, Bgetc, 3, CRGB, 0, nil }, /* raw portable pixmap */
{ 0 },
};
Rawimage*
readppm(Biobuf *b, Rawimage *a)
{
int i, ch, wid, ht, r, c;
int maxcol, nchan, invert;
int (*fetch)(Biobuf*);
uchar *rgb[3];
char buf[ERRMAX];
char *e;
Pix *p;
e = "bad file format";
if(Bgetc(b) != 'P')
goto Error;
c = Bgetc(b);
for(p=pix; p->magic; p++)
if(p->magic == c)
break;
if(p->magic == 0)
goto Error;
wid = Bgetint(b);
ht = Bgetint(b);
if(wid <= 0 || ht <= 0)
goto Error;
a->r = Rect(0,0,wid,ht);
maxcol = p->maxcol;
if(maxcol == 0) {
maxcol = Bgetint(b);
if(maxcol <= 0)
goto Error;
}
e = "out of memory";
for(i=0; i<p->nchan; i++)
if((rgb[i] = a->chans[i] = malloc(wid*ht)) == nil)
goto Error;
a->nchans = p->nchan;
a->chanlen = wid*ht;
a->chandesc = p->chandesc;
e = "error reading file";
fetch = p->fetch;
nchan = p->nchan;
invert = p->invert;
for(r=0; r<ht; r++) {
for(c=0; c<wid; c++) {
for(i=0; i<nchan; i++) {
if((ch = (*fetch)(b)) < 0)
goto Error;
if(invert)
ch = maxcol - ch;
*rgb[i]++ = (ch * 255)/maxcol;
}
}
if(p->flush)
(*p->flush)(b);
}
return a;
Error:
errstr(buf, sizeof buf);
if(buf[0] == 0)
strcpy(buf, e);
errstr(buf, sizeof buf);
for(i=0; i<3; i++)
free(a->chans[i]);
free(a->cmap);
return nil;
}

190
src/cmd/jpg/readyuv.c Normal file
View file

@ -0,0 +1,190 @@
/* readyuv.c - read an Abekas A66 style image file. Steve Simon, 2003 */
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <ctype.h>
#include "imagefile.h"
/*
* ITU/CCIR Rec601 states:
*
* R = y + 1.402 * Cr
* B = Y + 1.77305 * Cb
* G = Y - 0.72414 * Cr - 0.34414 * Cb
*
* using 8 bit traffic
* Y = 16 + 219 * Y
* Cr = 128 + 224 * Cr
* Cb = 128 + 224 * Cb
* or, if 10bit is used
* Y = 64 + 876 * Y
* Cr = 512 + 896 * Cr
* Cb = 512 + 896 * Cb
*/
enum {
PAL = 576, NTSC = 486 };
static int lsbtab[] = { 6, 4, 2, 0};
static int
clip(int x)
{
x >>= 18;
if (x > 255)
return 0xff;
if (x <= 0)
return 0;
return x;
}
Rawimage**
Breadyuv(Biobuf *bp, int colourspace)
{
Dir * d;
Rawimage * a, **array;
char *e, ebuf[128];
ushort * mux, *end, *frm;
uchar buf[720 * 2], *r, *g, *b;
int y1, y2, cb, cr, sz, c, l, w, base, bits, lines;
frm = 0;
if (colourspace != CYCbCr) {
errstr(ebuf, sizeof ebuf); /* throw it away */
werrstr("ReadYUV: unknown colour space %d", colourspace);
return nil;
}
if ((a = calloc(sizeof(Rawimage), 1)) == nil)
sysfatal("no memory");
if ((array = calloc(sizeof(Rawimage * ), 2)) == nil)
sysfatal("no memory");
array[0] = a;
array[1] = nil;
if ((d = dirfstat(Bfildes(bp))) != nil) {
sz = d->length;
free(d);
} else {
fprint(2, "cannot stat input, assuming 720x576x10bit\n");
sz = 720 * PAL * 2L + (720 * PAL / 2L);
}
switch (sz) {
case 720 * PAL * 2: // 625 x 8bit
bits = 8;
lines = PAL;
break;
case 720 * NTSC * 2: // 525 x 8bit
bits = 8;
lines = NTSC;
break;
case 720 * PAL * 2 + (720 * PAL / 2) : // 625 x 10bit
bits = 10;
lines = PAL;
break;
case 720 * NTSC * 2 + (720 * NTSC / 2) : // 525 x 10bit
bits = 10;
lines = NTSC;
break;
default:
e = "unknown file size";
goto Error;
}
// print("bits=%d pixels=%d lines=%d\n", bits, 720, lines);
//
a->nchans = 3;
a->chandesc = CRGB;
a->chanlen = 720 * lines;
a->r = Rect(0, 0, 720, lines);
e = "no memory";
if ((frm = malloc(720 * 2 * lines * sizeof(ushort))) == nil)
goto Error;
for (c = 0; c < 3; c++)
if ((a->chans[c] = malloc(720 * lines)) == nil)
goto Error;
e = "read file";
for (l = 0; l < lines; l++) {
if (Bread(bp, buf, 720 * 2) == -1)
goto Error;
base = l * 720 * 2;
for (w = 0; w < 720 * 2; w++)
frm[base + w] = ((ushort)buf[w]) << 2;
}
if (bits == 10)
for (l = 0; l < lines; l++) {
if (Bread(bp, buf, 720 / 2) == -1)
goto Error;
base = l * 720 * 2;
for (w = 0; w < 720 * 2; w++)
frm[base + w] |= buf[w / 4] >> lsbtab[w % 4];
}
mux = frm;
end = frm + 720 * lines * 2;
r = a->chans[0];
g = a->chans[1];
b = a->chans[2];
while (mux < end) {
cb = *mux++ - 512;
y1 = (*mux++ - 64) * 76310;
cr = *mux++ - 512;
y2 = (*mux++ - 64) * 76310;
*r++ = clip((104635 * cr) + y1);
*g++ = clip((-25690 * cb + -53294 * cr) + y1);
*b++ = clip((132278 * cb) + y1);
*r++ = clip((104635 * cr) + y2);
*g++ = clip((-25690 * cb + -53294 * cr) + y2);
*b++ = clip((132278 * cb) + y2);
}
free(frm);
return array;
Error:
errstr(ebuf, sizeof ebuf);
if (ebuf[0] == 0)
strcpy(ebuf, e);
errstr(ebuf, sizeof ebuf);
for (c = 0; c < 3; c++)
free(a->chans[c]);
free(a->cmap);
free(array[0]);
free(array);
free(frm);
return nil;
}
Rawimage**
readyuv(int fd, int colorspace)
{
Rawimage * *a;
Biobuf b;
if (Binit(&b, fd, OREAD) < 0)
return nil;
a = Breadyuv(&b, colorspace);
Bterm(&b);
return a;
}

69
src/cmd/jpg/rgbrgbv.c Normal file
View file

@ -0,0 +1,69 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
/*
* This version of closest() is now (feb 20, 2001) installed as rgb2cmap in libdraw
*/
int
closest(int cr, int cg, int cb)
{
int i, r, g, b, sq;
ulong rgb;
int best, bestsq;
best = 0;
bestsq = 0x7FFFFFFF;
for(i=0; i<256; i++){
rgb = cmap2rgb(i);
r = (rgb>>16) & 0xFF;
g = (rgb>>8) & 0xFF;
b = (rgb>>0) & 0xFF;
sq = (r-cr)*(r-cr)+(g-cg)*(g-cg)+(b-cb)*(b-cb);
if(sq < bestsq){
bestsq = sq;
best = i;
}
}
return best;
}
void
main(int argc, char *argv[])
{
int i, rgb;
int r, g, b;
uchar close[16*16*16];
/* rgbmap */
print("uint rgbmap[256] = {\n");
for(i=0; i<256; i++){
if(i%8 == 0)
print("\t");
rgb = cmap2rgb(i);
r = (rgb>>16) & 0xFF;
g = (rgb>>8) & 0xFF;
b = (rgb>>0) & 0xFF;
print("0x%.6ulX, ", (r<<16) | (g<<8) | b);
if(i%8 == 7)
print("\n");
}
print("};\n\n");
/* closestrgb */
print("uchar closestrgb[16*16*16] = {\n");
for(r=0; r<256; r+=16)
for(g=0; g<256; g+=16)
for(b=0; b<256; b+=16)
close[(b/16)+16*((g/16)+16*(r/16))] = closest(r+8, g+8, b+8);
for(i=0; i<16*16*16; i++){
if(i%16 == 0)
print("\t");
print("%d,", close[i]);
if(i%16 == 15)
print("\n");
}
print("};\n\n");
exits(nil);
}

120
src/cmd/jpg/rgbycc.c Normal file
View file

@ -0,0 +1,120 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
float c1 = 1.402;
float c2 = 0.34414;
float c3 = 0.71414;
float c4 = 1.772;
int
closest(int Y, int Cb, int Cr)
{
double r, g, b;
double diff, min;
int rgb, R, G, B, v, i;
int y1, cb1, cr1;
Cb -= 128;
Cr -= 128;
r = Y+c1*Cr;
g = Y-c2*Cb-c3*Cr;
b = Y+c4*Cb;
//print("YCbCr: %d %d %d, RGB: %g %g %g\n", Y, Cb, Cr, r, g, b);
min = 1000000.;
v = 1000;
for(i=0; i<256; i++){
rgb = cmap2rgb(i);
R = (rgb >> 16) & 0xFF;
G = (rgb >> 8) & 0xFF;
B = (rgb >> 0) & 0xFF;
diff = (R-r)*(R-r) + (G-g)*(G-g) + (B-b)*(B-b);
y1 = 0.5870*G + 0.114*B + 0.299*R;
cb1 = (B-y1)/1.772;
cr1 = (R-y1)/1.402;
if(diff < min){
// if(Y==0 && y1!=0)
// continue;
if(Y==256-16 && y1<256-16)
continue;
// if(Cb==0 && cb1!=0)
// continue;
if(Cb==256-16 && cb1<256-16)
continue;
// if(Cr==0 && cr1!=0)
// continue;
if(Cr==256-16 && cr1<256-16)
continue;
//print("%d %d %d\n", R, G, B);
min = diff;
v = i;
}
}
if(v > 255)
abort();
return v;
}
void
main(int argc, char *argv[])
{
int i, rgb;
int r, g, b;
double Y, Cr, Cb;
int y, cb, cr;
uchar close[16*16*16];
//print("%d\n", closest(atoi(argv[1]), atoi(argv[2]), atoi(argv[3])));
//exits("X");
/* ycbcrmap */
print("uint ycbcrmap[256] = {\n");
for(i=0; i<256; i++){
if(i%8 == 0)
print("\t");
rgb = cmap2rgb(i);
r = (rgb>>16) & 0xFF;
g = (rgb>>8) & 0xFF;
b = (rgb>>0) & 0xFF;
Y = 0.5870*g + 0.114*b + 0.299*r;
Cr = (r-Y)/1.402 + 128.;
Cb = (b-Y)/1.772 + 128.;
if(Y<0. || Y>=256. || Cr<0. || Cr>=256. || Cb<0. || Cb>=256.)
print("bad at %d: %d %d %d; %g %g %g\n", i, r, g, b, Y, Cb, Cr);
r = Y;
g = Cb;
b = Cr;
print("0x%.6ulX, ", (r<<16) | (g<<8) | b);
if(i%8 == 7)
print("\n");
}
print("};\n\n");
/* closestycbcr */
print("uchar closestycbcr[16*16*16] = {\n");
for(y=0; y<256; y+=16)
for(cb=0; cb<256; cb+=16)
for(cr=0; cr<256; cr+=16)
close[(cr/16)+16*((cb/16)+16*(y/16))] = closest(y, cb, cr);
if(0){
/*weird: set white for nearly white */
for(cb=128-32; cb<=128+32; cb+=16)
for(cr=128-32; cr<=128+32; cr+=16)
close[(cr/16)+16*((cb/16)+16*(255/16))] = 0;
/*weird: set black for nearly black */
for(cb=128-32; cb<=128+32; cb+=16)
for(cr=128-32; cr<=128+32; cr+=16)
close[(cr/16)+16*((cb/16)+16*(0/16))] = 255;
}
for(i=0; i<16*16*16; i++){
if(i%16 == 0)
print("\t");
print("%d,", close[i]);
if(i%16 == 15)
print("\n");
}
print("};\n\n");
exits(nil);
}

147
src/cmd/jpg/togif.c Normal file
View file

@ -0,0 +1,147 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <ctype.h>
#include <bio.h>
#include "imagefile.h"
void
usage(void)
{
fprint(2, "usage: togif [-l loopcount] [-c 'comment'] [-d Δt (ms)] [-t transparency-index] [file ... [-d Δt] file ...]\n");
exits("usage");
}
#define UNSET (-12345678)
void
main(int argc, char *argv[])
{
Biobuf bout;
Memimage *i, *ni;
int fd, j, dt, trans, loop;
char buf[256];
char *err, *comment, *s;
comment = nil;
dt = -1;
trans = -1;
loop = UNSET;
ARGBEGIN{
case 'l':
s = ARGF();
if(s==nil || (!isdigit(s[0]) && s[0]!='-'))
usage();
loop = atoi(s);
break;
case 'c':
comment = ARGF();
if(comment == nil)
usage();
break;
case 'd':
s = ARGF();
if(s==nil || !isdigit(s[0]))
usage();
dt = atoi(s);
break;
case 't':
s = ARGF();
if(s==nil || !isdigit(s[0]))
usage();
trans = atoi(s);
if(trans > 255)
usage();
break;
default:
usage();
}ARGEND
if(Binit(&bout, 1, OWRITE) < 0)
sysfatal("Binit failed: %r");
memimageinit();
err = nil;
if(argc == 0){
i = readmemimage(0);
if(i == nil)
sysfatal("reading input: %r");
ni = memonechan(i);
if(ni == nil)
sysfatal("converting image to RGBV: %r");
if(i != ni){
freememimage(i);
i = ni;
}
err = memstartgif(&bout, i, -1);
if(err == nil){
if(comment)
err = memwritegif(&bout, i, comment, dt, trans);
else{
snprint(buf, sizeof buf, "Converted by Plan 9 from <stdin>");
err = memwritegif(&bout, i, buf, dt, trans);
}
}
}else{
if(loop == UNSET){
if(argc == 1)
loop = -1; /* no loop for single image */
else
loop = 0; /* the default case: 0 means infinite loop */
}
for(j=0; j<argc; j++){
if(argv[j][0] == '-' && argv[j][1]=='d'){
/* time change */
if(argv[j][2] == '\0'){
s = argv[++j];
if(j == argc)
usage();
}else
s = &argv[j][2];
if(!isdigit(s[0]))
usage();
dt = atoi(s);
if(j == argc-1) /* last argument must be file */
usage();
continue;
}
fd = open(argv[j], OREAD);
if(fd < 0)
sysfatal("can't open %s: %r", argv[j]);
i = readmemimage(fd);
if(i == nil)
sysfatal("can't readimage %s: %r", argv[j]);
close(fd);
ni = memonechan(i);
if(ni == nil)
sysfatal("converting image to RGBV: %r");
if(i != ni){
freememimage(i);
i = ni;
}
if(j == 0){
err = memstartgif(&bout, i, loop);
if(err != nil)
break;
}
if(comment)
err = memwritegif(&bout, i, comment, dt, trans);
else{
snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[j]);
err = memwritegif(&bout, i, buf, dt, trans);
}
if(err != nil)
break;
freememimage(i);
comment = nil;
}
}
memendgif(&bout);
if(err != nil)
fprint(2, "togif: %s\n", err);
exits(err);
}

322
src/cmd/jpg/toico.c Normal file
View file

@ -0,0 +1,322 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
enum
{
FileHdrLen= 6,
IconDescrLen= 16,
IconHdrLen= 40,
};
typedef struct Icon Icon;
struct Icon
{
Icon *next;
char *file;
uchar w; /* icon width */
uchar h; /* icon height */
ushort ncolor; /* number of colors */
ushort nplane; /* number of bit planes */
ushort bits; /* bits per pixel */
ulong len; /* length of data */
ulong offset; /* file offset to data */
uchar map[4*256]; /* color map */
Image *img;
uchar *xor;
int xorlen;
uchar *and;
int andlen;
};
typedef struct Header Header;
struct Header
{
uint n;
Icon *first;
Icon *last;
};
void
Bputs(Biobuf *b, ushort x)
{
Bputc(b, x&0xff);
Bputc(b, x>>8);
}
void
Bputl(Biobuf *b, ulong x)
{
Bputs(b, x&0xffff);
Bputs(b, x>>16);
}
Header h;
void* emalloc(int);
void mk8bit(Icon*, int);
void mkxorand(Icon*, int);
void readicon(char*);
void
main(int argc, char **argv)
{
int i;
Biobuf *b, out;
Icon *icon;
ulong offset;
ulong len;
ARGBEGIN{
}ARGEND;
/* read in all the images */
initdraw(nil, nil, nil);
if(argc < 1){
readicon("/fd/0");
} else {
for(i = 0; i < argc; i++)
readicon(argv[i]);
}
/* create the .ico file */
b = &out;
Binit(b, 1, OWRITE);
/* offset to first icon */
offset = FileHdrLen + h.n*IconDescrLen;
/* file header is */
Bputs(b, 0);
Bputs(b, 1);
Bputs(b, h.n);
/* icon description */
for(icon = h.first; icon != nil; icon = icon->next){
Bputc(b, icon->w);
Bputc(b, icon->h);
Bputc(b, icon->ncolor);
Bputc(b, 0);
Bputs(b, icon->nplane);
Bputs(b, icon->bits);
len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen;
Bputl(b, len);
Bputl(b, offset);
offset += len;
}
/* icons */
for(icon = h.first; icon != nil; icon = icon->next){
/* icon header (BMP like) */
Bputl(b, IconHdrLen);
Bputl(b, icon->w);
Bputl(b, 2*icon->h);
Bputs(b, icon->nplane);
Bputs(b, icon->bits);
Bputl(b, 0); /* compression info */
Bputl(b, 0);
Bputl(b, 0);
Bputl(b, 0);
Bputl(b, 0);
Bputl(b, 0);
/* color map */
if(Bwrite(b, icon->map, 4*icon->ncolor) < 0)
sysfatal("writing color map: %r");
/* xor bits */
if(Bwrite(b, icon->xor, icon->xorlen) < 0)
sysfatal("writing xor bits: %r");
/* and bits */
if(Bwrite(b, icon->and, icon->andlen) < 0)
sysfatal("writing and bits: %r");
}
Bterm(b);
exits(0);
}
void
readicon(char *file)
{
int fd;
Icon *icon;
fd = open(file, OREAD);
if(fd < 0)
sysfatal("opening %s: %r", file);
icon = emalloc(sizeof(Icon));
icon->img = readimage(display, fd, 0);
if(icon->img == nil)
sysfatal("reading image %s: %r", file);
close(fd);
if(h.first)
h.last->next = icon;
else
h.first = icon;
h.last = icon;
h.n++;
icon->h = Dy(icon->img->r);
icon->w = Dx(icon->img->r);
icon->bits = 1<<icon->img->depth;
icon->nplane = 1;
/* convert to 8 bits per pixel */
switch(icon->img->chan){
case GREY8:
case CMAP8:
break;
case GREY1:
case GREY2:
case GREY4:
mk8bit(icon, 1);
break;
default:
mk8bit(icon, 0);
break;
}
icon->bits = 8;
icon->file = file;
/* create xor/and masks, minimizing bits per pixel */
mkxorand(icon, icon->img->chan == GREY8);
}
void*
emalloc(int len)
{
void *x;
x = mallocz(len, 1);
if(x == nil)
sysfatal("memory: %r");
return x;
}
/* convert to 8 bit */
void
mk8bit(Icon *icon, int grey)
{
Image *img;
img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill);
if(img == nil)
sysfatal("can't allocimage: %r");
draw(img, img->r, icon->img, nil, ZP);
freeimage(icon->img);
icon->img = img;
}
/* make xor and and mask */
void
mkxorand(Icon *icon, int grey)
{
int i, x, y, s, sa;
uchar xx[256];
uchar *data, *p, *e;
int ndata;
uchar *mp;
int ncolor;
ulong color;
int bits;
uchar andbyte, xorbyte;
uchar *ato, *xto;
int xorrl, andrl;
ndata = icon->h * icon->w;
data = emalloc(ndata);
if(unloadimage(icon->img, icon->img->r, data, ndata) < 0)
sysfatal("can't unload %s: %r", icon->file);
e = data + ndata;
/* find colors used */
memset(xx, 0, sizeof xx);
for(p = data; p < e; p++)
xx[*p]++;
/* count the colors and create a mapping from plan 9 */
mp = icon->map;
ncolor = 0;
for(i = 0; i < 256; i++){
if(xx[i] == 0)
continue;
if(grey){
*mp++ = i;
*mp++ = i;
*mp++ = i;
*mp++ = 0;
} else {
color = cmap2rgb(i);
*mp++ = color;
*mp++ = color>>8;
*mp++ = color>>16;
*mp++ = 0;
}
xx[i] = ncolor;
ncolor++;
}
/* get minimum number of pixels per bit (with a color map) */
if(ncolor <= 2){
ncolor = 2;
bits = 1;
} else if(ncolor <= 4){
ncolor = 4;
bits = 2;
} else if(ncolor <= 16){
ncolor = 16;
bits = 4;
} else {
ncolor = 256;
bits = 8;
}
icon->bits = bits;
icon->ncolor = ncolor;
/* the xor mask rows are justified to a 32 bit boundary */
/* the and mask is 1 bit grey */
xorrl = 4*((bits*icon->w + 31)/32);
andrl = 4*((icon->w + 31)/32);
icon->xor = emalloc(xorrl * icon->h);
icon->and = emalloc(andrl * icon->h);
icon->xorlen = xorrl*icon->h;
icon->andlen = andrl*icon->h;
/* make both masks. they're upside down relative to plan9 ones */
p = data;
for(y = 0; y < icon->h; y++){
andbyte = 0;
xorbyte = 0;
sa = s = 0;
xto = icon->xor + (icon->h-1-y)*xorrl;
ato = icon->and + (icon->h-1-y)*andrl;
for(x = 0; x < icon->w; x++){
xorbyte <<= bits;
xorbyte |= xx[*p];
s += bits;
if(s == 8){
*xto++ = xorbyte;
xorbyte = 0;
s = 0;
}
andbyte <<= 1;
if(*p == 0xff)
andbyte |= 1;
sa++;
if(sa == 0){
*ato++ = andbyte;
sa = 0;
andbyte = 0;
}
p++;
}
}
free(data);
}

70
src/cmd/jpg/topng.c Normal file
View file

@ -0,0 +1,70 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <ctype.h>
#include <bio.h>
#include <flate.h>
#include "imagefile.h"
void
usage(void)
{
fprint(2, "usage: topng [-c 'comment'] [-g 'gamma'] [file]\n");
exits("usage");
}
void
main(int argc, char *argv[])
{
Biobuf bout;
Memimage *i;
int fd;
char *err, *filename;
ImageInfo II;
ARGBEGIN{
case 'c':
II.comment = ARGF();
if(II.comment == nil)
usage();
II.fields_set |= II_COMMENT;
break;
case 'g':
II.gamma = atof(ARGF());
if(II.gamma == 0.)
usage();
II.fields_set |= II_GAMMA;
break;
case 't':
break;
default:
usage();
}ARGEND
if(Binit(&bout, 1, OWRITE) < 0)
sysfatal("Binit failed: %r");
memimageinit();
if(argc == 0){
fd = 0;
filename = "<stdin>";
}else{
fd = open(argv[0], OREAD);
if(fd < 0)
sysfatal("can't open %s: %r", argv[0]);
filename = argv[0];
}
i = readmemimage(fd);
if(i == nil)
sysfatal("can't readimage %s: %r", filename);
close(fd);
err = memwritepng(&bout, i, &II);
freememimage(i);
if(err != nil)
fprint(2, "topng: %s\n", err);
exits(err);
}

90
src/cmd/jpg/toppm.c Normal file
View file

@ -0,0 +1,90 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <ctype.h>
#include <bio.h>
#include "imagefile.h"
void
usage(void)
{
fprint(2, "usage: toppm [-c 'comment'] [file]\n");
exits("usage");
}
void
main(int argc, char *argv[])
{
Biobuf bout;
Memimage *i, *ni;
int fd;
char buf[256];
char *err, *comment;
comment = nil;
ARGBEGIN{
case 'c':
comment = ARGF();
if(comment == nil)
usage();
if(strchr(comment, '\n') != nil){
fprint(2, "ppm: comment cannot contain newlines\n");
usage();
}
break;
default:
usage();
}ARGEND
if(argc > 1)
usage();
if(Binit(&bout, 1, OWRITE) < 0)
sysfatal("Binit failed: %r");
memimageinit();
err = nil;
if(argc == 0){
i = readmemimage(0);
if(i == nil)
sysfatal("reading input: %r");
ni = memmultichan(i);
if(ni == nil)
sysfatal("converting image to RGBV: %r");
if(i != ni){
freememimage(i);
i = ni;
}
if(err == nil)
err = memwriteppm(&bout, i, comment);
}else{
fd = open(argv[0], OREAD);
if(fd < 0)
sysfatal("can't open %s: %r", argv[0]);
i = readmemimage(fd);
if(i == nil)
sysfatal("can't readimage %s: %r", argv[0]);
close(fd);
ni = memmultichan(i);
if(ni == nil)
sysfatal("converting image to RGBV: %r");
if(i != ni){
freememimage(i);
i = ni;
}
if(comment)
err = memwriteppm(&bout, i, comment);
else{
snprint(buf, sizeof buf, "Converted by Plan 9 from %s", argv[0]);
err = memwriteppm(&bout, i, buf);
}
freememimage(i);
}
if(err != nil)
fprint(2, "toppm: %s\n", err);
exits(err);
}

299
src/cmd/jpg/torgbv.c Normal file
View file

@ -0,0 +1,299 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include "imagefile.h"
#include "rgbv.h"
#include "ycbcr.h"
#define CLAMPOFF 128
static int clamp[CLAMPOFF+256+CLAMPOFF];
static int inited;
void*
_remaperror(char *fmt, ...)
{
va_list arg;
char buf[256];
va_start(arg, fmt);
vseprint(buf, buf+sizeof buf, fmt, arg);
va_end(arg);
werrstr(buf);
return nil;
}
Rawimage*
torgbv(Rawimage *i, int errdiff)
{
int j, k, rgb, x, y, er, eg, eb, col, t;
int r, g, b, r1, g1, b1;
int *ered, *egrn, *eblu, *rp, *gp, *bp;
uint *map3;
uchar *closest;
Rawimage *im;
int dx, dy;
char err[ERRMAX];
uchar *cmap, *cm, *in, *out, *inp, *outp, cmap1[3*256], map[256], *rpic, *bpic, *gpic;
err[0] = '\0';
errstr(err, sizeof err); /* throw it away */
im = malloc(sizeof(Rawimage));
if(im == nil)
return nil;
memset(im, 0, sizeof(Rawimage));
im->chans[0] = malloc(i->chanlen);
if(im->chans[0] == nil){
free(im);
return nil;
}
im->r = i->r;
im->nchans = 1;
im->chandesc = CRGBV;
im->chanlen = i->chanlen;
dx = i->r.max.x-i->r.min.x;
dy = i->r.max.y-i->r.min.y;
cmap = i->cmap;
if(inited == 0){
inited = 1;
for(j=0; j<CLAMPOFF; j++)
clamp[j] = 0;
for(j=0; j<256; j++)
clamp[CLAMPOFF+j] = (j>>4);
for(j=0; j<CLAMPOFF; j++)
clamp[CLAMPOFF+256+j] = (255>>4);
}
in = i->chans[0];
inp = in;
out = im->chans[0];
outp = out;
ered = malloc((dx+1)*sizeof(int));
egrn = malloc((dx+1)*sizeof(int));
eblu = malloc((dx+1)*sizeof(int));
if(ered==nil || egrn==nil || eblu==nil){
free(im->chans[0]);
free(im);
free(ered);
free(egrn);
free(eblu);
return _remaperror("remap: malloc failed: %r");
}
memset(ered, 0, (dx+1)*sizeof(int));
memset(egrn, 0, (dx+1)*sizeof(int));
memset(eblu, 0, (dx+1)*sizeof(int));
switch(i->chandesc){
default:
return _remaperror("remap: can't recognize channel type %d", i->chandesc);
case CRGB1:
if(cmap == nil)
return _remaperror("remap: image has no color map");
if(i->nchans != 1)
return _remaperror("remap: can't handle nchans %d", i->nchans);
for(j=1; j<=8; j++)
if(i->cmaplen == 3*(1<<j))
break;
if(j > 8)
return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
if(i->cmaplen != 3*256){
/* to avoid a range check in inner loop below, make a full-size cmap */
memmove(cmap1, cmap, i->cmaplen);
cmap = cmap1;
}
if(errdiff == 0){
k = 0;
for(j=0; j<256; j++){
r = cmap[k]>>4;
g = cmap[k+1]>>4;
b = cmap[k+2]>>4;
k += 3;
map[j] = closestrgb[b+16*(g+16*r)];
}
for(j=0; j<i->chanlen; j++)
out[j] = map[in[j]];
}else{
/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
for(y=0; y<dy; y++){
er = 0;
eg = 0;
eb = 0;
rp = ered;
gp = egrn;
bp = eblu;
for(x=0; x<dx; x++){
cm = &cmap[3 * *inp++];
r = cm[0] +*rp;
g = cm[1] +*gp;
b = cm[2] +*bp;
/* sanity checks are new */
if(r >= 256+CLAMPOFF)
r = 0;
if(g >= 256+CLAMPOFF)
g = 0;
if(b >= 256+CLAMPOFF)
b = 0;
r1 = clamp[r+CLAMPOFF];
g1 = clamp[g+CLAMPOFF];
b1 = clamp[b+CLAMPOFF];
if(r1 >= 16 || g1 >= 16 || b1 >= 16)
col = 0;
else
col = closestrgb[b1+16*(g1+16*r1)];
*outp++ = col;
rgb = rgbmap[col];
r -= (rgb>>16) & 0xFF;
t = (3*r)>>4;
*rp++ = t+er;
*rp += t;
er = r-3*t;
g -= (rgb>>8) & 0xFF;
t = (3*g)>>4;
*gp++ = t+eg;
*gp += t;
eg = g-3*t;
b -= rgb & 0xFF;
t = (3*b)>>4;
*bp++ = t+eb;
*bp += t;
eb = b-3*t;
}
}
}
break;
case CYCbCr:
closest = closestycbcr;
map3 = ycbcrmap;
goto Threecolor;
case CRGB:
closest = closestrgb;
map3 = rgbmap;
Threecolor:
if(i->nchans != 3)
return _remaperror("remap: RGB image has %d channels", i->nchans);
rpic = i->chans[0];
gpic = i->chans[1];
bpic = i->chans[2];
if(errdiff == 0){
for(j=0; j<i->chanlen; j++){
r = rpic[j]>>4;
g = gpic[j]>>4;
b = bpic[j]>>4;
out[j] = closest[b+16*(g+16*r)];
}
}else{
/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
for(y=0; y<dy; y++){
er = 0;
eg = 0;
eb = 0;
rp = ered;
gp = egrn;
bp = eblu;
for(x=0; x<dx; x++){
r = *rpic++ + *rp;
g = *gpic++ + *gp;
b = *bpic++ + *bp;
/*
* Errors can be uncorrectable if converting from YCbCr,
* since we can't guarantee that an extremal value of one of
* the components selects a color with an extremal value.
* If we don't, the errors accumulate without bound. This
* doesn't happen in RGB because the closest table can guarantee
* a color on the edge of the gamut, producing a zero error in
* that component. For the rotation YCbCr space, there may be
* no color that can guarantee zero error at the edge.
* Therefore we must clamp explicitly rather than by assuming
* an upper error bound of CLAMPOFF. The performance difference
* is miniscule anyway.
*/
if(r < 0)
r = 0;
else if(r > 255)
r = 255;
if(g < 0)
g = 0;
else if(g > 255)
g = 255;
if(b < 0)
b = 0;
else if(b > 255)
b = 255;
r1 = r>>4;
g1 = g>>4;
b1 = b>>4;
col = closest[b1+16*(g1+16*r1)];
*outp++ = col;
rgb = map3[col];
r -= (rgb>>16) & 0xFF;
t = (3*r)>>4;
*rp++ = t+er;
*rp += t;
er = r-3*t;
g -= (rgb>>8) & 0xFF;
t = (3*g)>>4;
*gp++ = t+eg;
*gp += t;
eg = g-3*t;
b -= rgb & 0xFF;
t = (3*b)>>4;
*bp++ = t+eb;
*bp += t;
eb = b-3*t;
}
}
}
break;
case CY:
if(i->nchans != 1)
return _remaperror("remap: Y image has %d chans", i->nchans);
rpic = i->chans[0];
if(errdiff == 0){
for(j=0; j<i->chanlen; j++){
r = rpic[j]>>4;
*outp++ = closestrgb[r+16*(r+16*r)];
}
}else{
/* modified floyd steinberg, coefficients (1 0) 3/16, (0, 1) 3/16, (1, 1) 7/16 */
for(y=0; y<dy; y++){
er = 0;
rp = ered;
for(x=0; x<dx; x++){
r = *inp++ + *rp;
r1 = clamp[r+CLAMPOFF];
col = closestrgb[r1+16*(r1+16*r1)];
*outp++ = col;
rgb = rgbmap[col];
r -= (rgb>>16) & 0xFF;
t = (3*r)>>4;
*rp++ = t+er;
*rp += t;
er = r-3*t;
}
}
}
break;
}
free(ered);
free(egrn);
free(eblu);
return im;
}

163
src/cmd/jpg/totruecolor.c Normal file
View file

@ -0,0 +1,163 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include "imagefile.h"
enum {
c1 = 2871, /* 1.402 * 2048 */
c2 = 705, /* 0.34414 * 2048 */
c3 = 1463, /* 0.71414 * 2048 */
c4 = 3629, /* 1.772 * 2048 */
};
Rawimage*
totruecolor(Rawimage *i, int chandesc)
{
int j, k;
Rawimage *im;
char err[ERRMAX];
uchar *rp, *gp, *bp, *cmap, *inp, *outp, cmap1[3*256];
int r, g, b, Y, Cr, Cb;
if(chandesc!=CY && chandesc!=CRGB24)
return _remaperror("remap: can't convert to chandesc %d", chandesc);
err[0] = '\0';
errstr(err, sizeof err); /* throw it away */
im = malloc(sizeof(Rawimage));
if(im == nil)
return nil;
memset(im, 0, sizeof(Rawimage));
if(chandesc == CY)
im->chanlen = i->chanlen;
else
im->chanlen = 3*i->chanlen;
im->chandesc = chandesc;
im->chans[0] = malloc(im->chanlen);
if(im->chans[0] == nil){
free(im);
return nil;
}
im->r = i->r;
im->nchans = 1;
cmap = i->cmap;
outp = im->chans[0];
switch(i->chandesc){
default:
return _remaperror("remap: can't recognize channel type %d", i->chandesc);
case CY:
if(i->nchans != 1)
return _remaperror("remap: Y image has %d chans", i->nchans);
if(chandesc == CY){
memmove(im->chans[0], i->chans[0], i->chanlen);
break;
}
/* convert to three color */
inp = i->chans[0];
for(j=0; j<i->chanlen; j++){
k = *inp++;
*outp++ = k;
*outp++ = k;
*outp++ = k;
}
break;
case CRGB1:
if(cmap == nil)
return _remaperror("remap: image has no color map");
if(i->nchans != 1)
return _remaperror("remap: can't handle nchans %d", i->nchans);
for(j=1; j<=8; j++)
if(i->cmaplen == 3*(1<<j))
break;
if(j > 8)
return _remaperror("remap: can't do colormap size 3*%d", i->cmaplen/3);
if(i->cmaplen != 3*256){
/* to avoid a range check in loop below, make a full-size cmap */
memmove(cmap1, cmap, i->cmaplen);
cmap = cmap1;
}
inp = i->chans[0];
if(chandesc == CY){
for(j=0; j<i->chanlen; j++){
k = *inp++;
r = cmap[3*k+2];
g = cmap[3*k+1];
b = cmap[3*k+0];
r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */
*outp++ = r;
}
}else{
for(j=0; j<i->chanlen; j++){
k = *inp++;
*outp++ = cmap[3*k+2];
*outp++ = cmap[3*k+1];
*outp++ = cmap[3*k+0];
}
}
break;
case CRGB:
if(i->nchans != 3)
return _remaperror("remap: can't handle nchans %d", i->nchans);
rp = i->chans[0];
gp = i->chans[1];
bp = i->chans[2];
if(chandesc == CY){
for(j=0; j<i->chanlen; j++){
r = *bp++;
g = *gp++;
b = *rp++;
r = (2125*r + 7154*g + 721*b)/10000; /* Poynton page 84 */
*outp++ = r;
}
}else
for(j=0; j<i->chanlen; j++){
*outp++ = *bp++;
*outp++ = *gp++;
*outp++ = *rp++;
}
break;
case CYCbCr:
if(i->nchans != 3)
return _remaperror("remap: can't handle nchans %d", i->nchans);
rp = i->chans[0];
gp = i->chans[1];
bp = i->chans[2];
for(j=0; j<i->chanlen; j++){
Y = *rp++ << 11;
Cb = *gp++ - 128;
Cr = *bp++ - 128;
r = (Y+c1*Cr) >> 11;
g = (Y-c2*Cb-c3*Cr) >> 11;
b = (Y+c4*Cb) >> 11;
if(r < 0)
r = 0;
if(r > 255)
r = 255;
if(g < 0)
g = 0;
if(g > 255)
g = 255;
if(b < 0)
b = 0;
if(b > 255)
b = 255;
if(chandesc == CY){
r = (2125*r + 7154*g + 721*b)/10000;
*outp++ = r;
}else{
*outp++ = b;
*outp++ = g;
*outp++ = r;
}
}
break;
}
return im;
}

568
src/cmd/jpg/writegif.c Normal file
View file

@ -0,0 +1,568 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include "imagefile.h"
enum
{
Nhash = 4001,
Nbuf = 300,
};
typedef struct Entry Entry;
typedef struct IO IO;
struct Entry
{
int index;
int prefix;
int exten;
Entry *next;
};
struct IO
{
Biobuf *fd;
uchar buf[Nbuf];
int i;
int nbits; /* bits in right side of shift register */
int sreg; /* shift register */
};
static Rectangle mainrect;
static Entry tbl[4096];
static uchar *colormap[5]; /* one for each ldepth: GREY1 GREY2 GREY4 CMAP8=rgbv plus GREY8 */
#define GREYMAP 4
static int colormapsize[] = { 2, 4, 16, 256, 256 }; /* 2 for zero is an odd property of GIF */
static void writeheader(Biobuf*, Rectangle, int, ulong, int);
static void writedescriptor(Biobuf*, Rectangle);
static char* writedata(Biobuf*, Image*, Memimage*);
static void writecomment(Biobuf *fd, char*);
static void writegraphiccontrol(Biobuf *fd, int, int);
static void* gifmalloc(ulong);
static void encode(Biobuf*, Rectangle, int, uchar*, uint);
static
char*
startgif0(Biobuf *fd, ulong chan, Rectangle r, int depth, int loopcount)
{
int i;
for(i=0; i<nelem(tbl); i++)
tbl[i] = (Entry){i, -1, i, nil};
switch(chan){
case GREY1:
case GREY2:
case GREY4:
case CMAP8:
case GREY8:
break;
default:
return "WriteGIF: can't handle channel type";
}
mainrect = r;
writeheader(fd, r, depth, chan, loopcount);
return nil;
}
char*
startgif(Biobuf *fd, Image *image, int loopcount)
{
return startgif0(fd, image->chan, image->r, image->depth, loopcount);
}
char*
memstartgif(Biobuf *fd, Memimage *memimage, int loopcount)
{
return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount);
}
static
char*
writegif0(Biobuf *fd, Image *image, Memimage *memimage, ulong chan, Rectangle r, char *comment, int dt, int trans)
{
char *err;
switch(chan){
case GREY1:
case GREY2:
case GREY4:
case CMAP8:
case GREY8:
break;
default:
return "WriteGIF: can't handle channel type";
}
writecomment(fd, comment);
writegraphiccontrol(fd, dt, trans);
writedescriptor(fd, r);
err = writedata(fd, image, memimage);
if(err != nil)
return err;
return nil;
}
char*
writegif(Biobuf *fd, Image *image, char *comment, int dt, int trans)
{
return writegif0(fd, image, nil, image->chan, image->r, comment, dt, trans);
}
char*
memwritegif(Biobuf *fd, Memimage *memimage, char *comment, int dt, int trans)
{
return writegif0(fd, nil, memimage, memimage->chan, memimage->r, comment, dt, trans);
}
/*
* Write little-endian 16-bit integer
*/
static
void
put2(Biobuf *fd, int i)
{
Bputc(fd, i);
Bputc(fd, i>>8);
}
/*
* Get color map for all ldepths, in format suitable for writing out
*/
static
void
getcolormap(void)
{
int i, col;
ulong rgb;
uchar *c;
if(colormap[0] != nil)
return;
for(i=0; i<nelem(colormap); i++)
colormap[i] = gifmalloc(3* colormapsize[i]);
c = colormap[GREYMAP]; /* GREY8 */
for(i=0; i<256; i++){
c[3*i+0] = i; /* red */
c[3*i+1] = i; /* green */
c[3*i+2] = i; /* blue */
}
c = colormap[3]; /* RGBV */
for(i=0; i<256; i++){
rgb = cmap2rgb(i);
c[3*i+0] = (rgb>>16) & 0xFF; /* red */
c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
}
c = colormap[2]; /* GREY4 */
for(i=0; i<16; i++){
col = (i<<4)|i;
rgb = cmap2rgb(col);
c[3*i+0] = (rgb>>16) & 0xFF; /* red */
c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
}
c = colormap[1]; /* GREY2 */
for(i=0; i<4; i++){
col = (i<<6)|(i<<4)|(i<<2)|i;
rgb = cmap2rgb(col);
c[3*i+0] = (rgb>>16) & 0xFF; /* red */
c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
}
c = colormap[0]; /* GREY1 */
for(i=0; i<2; i++){
if(i == 0)
col = 0;
else
col = 0xFF;
rgb = cmap2rgb(col);
c[3*i+0] = (rgb>>16) & 0xFF; /* red */
c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
}
}
/*
* Write header, logical screen descriptor, and color map
*/
static
void
writeheader(Biobuf *fd, Rectangle r, int depth, ulong chan, int loopcount)
{
/* Header */
Bprint(fd, "%s", "GIF89a");
/* Logical Screen Descriptor */
put2(fd, Dx(r));
put2(fd, Dy(r));
/* Color table present, 4 bits per color (for RGBV best case), size of color map */
Bputc(fd, (1<<7)|(3<<4)|(depth-1)); /* not right for GREY8, but GIF doesn't let us specify enough bits */
Bputc(fd, 0xFF); /* white background (doesn't matter anyway) */
Bputc(fd, 0); /* pixel aspect ratio - unused */
/* Global Color Table */
getcolormap();
if(chan == GREY8)
depth = GREYMAP;
else
depth = drawlog2[depth];
Bwrite(fd, colormap[depth], 3*colormapsize[depth]);
if(loopcount >= 0){ /* hard-to-discover way to force cycled animation */
/* Application Extension with (1 loopcountlo loopcounthi) as data */
Bputc(fd, 0x21);
Bputc(fd, 0xFF);
Bputc(fd, 11);
Bwrite(fd, "NETSCAPE2.0", 11);
Bputc(fd, 3);
Bputc(fd, 1);
put2(fd, loopcount);
Bputc(fd, 0);
}
}
/*
* Write optional comment block
*/
static
void
writecomment(Biobuf *fd, char *comment)
{
int n;
if(comment==nil || comment[0]=='\0')
return;
/* Comment extension and label */
Bputc(fd, 0x21);
Bputc(fd, 0xFE);
/* Comment data */
n = strlen(comment);
if(n > 255)
n = 255;
Bputc(fd, n);
Bwrite(fd, comment, n);
/* Block terminator */
Bputc(fd, 0x00);
}
/*
* Write optional control block (sets Delay Time)
*/
static
void
writegraphiccontrol(Biobuf *fd, int dt, int trans)
{
if(dt < 0 && trans < 0)
return;
/* Comment extension and label and block size*/
Bputc(fd, 0x21);
Bputc(fd, 0xF9);
Bputc(fd, 0x04);
/* Disposal method and other flags (none) */
if(trans >= 0)
Bputc(fd, 0x01);
else
Bputc(fd, 0x00);
/* Delay time, in centisec (argument is millisec for sanity) */
if(dt < 0)
dt = 0;
else if(dt < 10)
dt = 1;
else
dt = (dt+5)/10;
put2(fd, dt);
/* Transparency index */
if(trans < 0)
trans = 0;
Bputc(fd, trans);
/* Block terminator */
Bputc(fd, 0x00);
}
/*
* Write image descriptor
*/
static
void
writedescriptor(Biobuf *fd, Rectangle r)
{
/* Image Separator */
Bputc(fd, 0x2C);
/* Left, top, width, height */
put2(fd, r.min.x-mainrect.min.x);
put2(fd, r.min.y-mainrect.min.y);
put2(fd, Dx(r));
put2(fd, Dy(r));
/* no special processing */
Bputc(fd, 0);
}
/*
* Write data
*/
static
char*
writedata(Biobuf *fd, Image *image, Memimage *memimage)
{
char *err;
uchar *data;
int ndata, depth;
Rectangle r;
if(memimage != nil){
r = memimage->r;
depth = memimage->depth;
}else{
r = image->r;
depth = image->depth;
}
/* LZW Minimum code size */
if(depth == 1)
Bputc(fd, 2);
else
Bputc(fd, depth);
/*
* Read image data into memory
* potentially one extra byte on each end of each scan line
*/
ndata = Dy(r)*(2+(Dx(r)>>(3-drawlog2[depth])));
data = gifmalloc(ndata);
if(memimage != nil)
ndata = unloadmemimage(memimage, r, data, ndata);
else
ndata = unloadimage(image, r, data, ndata);
if(ndata < 0){
err = gifmalloc(ERRMAX);
snprint(err, ERRMAX, "WriteGIF: %r");
free(data);
return err;
}
/* Encode and emit the data */
encode(fd, r, depth, data, ndata);
free(data);
/* Block Terminator */
Bputc(fd, 0);
return nil;
}
/*
* Write trailer
*/
void
endgif(Biobuf *fd)
{
Bputc(fd, 0x3B);
Bflush(fd);
}
void
memendgif(Biobuf *fd)
{
endgif(fd);
}
/*
* Put n bits of c into output at io.buf[i];
*/
static
void
output(IO *io, int c, int n)
{
if(c < 0){
if(io->nbits != 0)
io->buf[io->i++] = io->sreg;
Bputc(io->fd, io->i);
Bwrite(io->fd, io->buf, io->i);
io->nbits = 0;
return;
}
if(io->nbits+n >= 31){
fprint(2, "panic: WriteGIF sr overflow\n");
exits("WriteGIF panic");
}
io->sreg |= c<<io->nbits;
io->nbits += n;
while(io->nbits >= 8){
io->buf[io->i++] = io->sreg;
io->sreg >>= 8;
io->nbits -= 8;
}
if(io->i >= 255){
Bputc(io->fd, 255);
Bwrite(io->fd, io->buf, 255);
memmove(io->buf, io->buf+255, io->i-255);
io->i -= 255;
}
}
/*
* LZW encoder
*/
static
void
encode(Biobuf *fd, Rectangle r, int depth, uchar *data, uint ndata)
{
int i, c, h, csize, prefix, first, sreg, nbits, bitsperpixel;
int CTM, EOD, codesize, ld0, datai, x, ld, pm;
int nentry, maxentry, early;
Entry *e, *oe;
IO *io;
Entry **hash;
first = 1;
ld = drawlog2[depth];
/* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */
ld0 = ld;
if(ld0 == 0)
ld0 = 1;
codesize = (1<<ld0);
CTM = 1<<codesize;
EOD = CTM+1;
io = gifmalloc(sizeof(IO));
io->fd = fd;
sreg = 0;
nbits = 0;
bitsperpixel = 1<<ld;
pm = (1<<bitsperpixel)-1;
datai = 0;
x = r.min.x;
hash = gifmalloc(Nhash*sizeof(Entry*));
Init:
memset(hash, 0, Nhash*sizeof(Entry*));
csize = codesize+1;
nentry = EOD+1;
maxentry = (1<<csize);
for(i = 0; i<nentry; i++){
e = &tbl[i];
h = (e->prefix<<24) | (e->exten<<8);
h %= Nhash;
if(h < 0)
h += Nhash;
e->next = hash[h];
hash[h] = e;
}
prefix = -1;
if(first)
output(io, CTM, csize);
first = 0;
/*
* Scan over pixels. Because of partially filled bytes on ends of scan lines,
* which must be ignored in the data stream passed to GIF, this is more
* complex than we'd like.
*/
Next:
for(;;){
if(ld != 3){
/* beginning of scan line is difficult; prime the shift register */
if(x == r.min.x){
if(datai == ndata)
break;
sreg = data[datai++];
nbits = 8-((x&(7>>ld))<<ld);
}
x++;
if(x == r.max.x)
x = r.min.x;
}
if(nbits == 0){
if(datai == ndata)
break;
sreg = data[datai++];
nbits = 8;
}
nbits -= bitsperpixel;
c = sreg>>nbits & pm;
h = prefix<<24 | c<<8;
h %= Nhash;
if(h < 0)
h += Nhash;
oe = nil;
for(e = hash[h]; e!=nil; e=e->next){
if(e->prefix == prefix && e->exten == c){
if(oe != nil){
oe->next = e->next;
e->next = hash[h];
hash[h] = e;
}
prefix = e->index;
goto Next;
}
oe = e;
}
output(io, prefix, csize);
early = 0; /* peculiar tiff feature here for reference */
if(nentry == maxentry-early){
if(csize == 12){
nbits += bitsperpixel; /* unget pixel */
x--;
if(ld != 3 && x == r.min.x)
datai--;
output(io, CTM, csize);
goto Init;
}
csize++;
maxentry = (1<<csize);
}
e = &tbl[nentry];
e->prefix = prefix;
e->exten = c;
e->next = hash[h];
hash[h] = e;
prefix = c;
nentry++;
}
output(io, prefix, csize);
output(io, EOD, csize);
output(io, -1, csize);
free(io);
free(hash);
}
static
void*
gifmalloc(ulong sz)
{
void *v;
v = malloc(sz);
if(v == nil) {
fprint(2, "WriteGIF: out of memory allocating %ld\n", sz);
abort();
exits("mem");
}
memset(v, 0, sz);
return v;
}

220
src/cmd/jpg/writepng.c Normal file
View file

@ -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;
}

164
src/cmd/jpg/writeppm.c Normal file
View file

@ -0,0 +1,164 @@
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include "imagefile.h"
#define MAXLINE 70
/*
* Write data
*/
static
char*
writedata(Biobuf *fd, Image *image, Memimage *memimage)
{
char *err;
uchar *data;
int i, x, y, ndata, depth, col, pix, xmask, pmask;
ulong chan;
Rectangle r;
if(memimage != nil){
r = memimage->r;
depth = memimage->depth;
chan = memimage->chan;
}else{
r = image->r;
depth = image->depth;
chan = image->chan;
}
/*
* Read image data into memory
* potentially one extra byte on each end of each scan line
*/
ndata = Dy(r)*(2+Dx(r)*depth/8);
data = malloc(ndata);
if(data == nil)
return "WritePPM: malloc failed";
if(memimage != nil)
ndata = unloadmemimage(memimage, r, data, ndata);
else
ndata = unloadimage(image, r, data, ndata);
if(ndata < 0){
err = malloc(ERRMAX);
if(err == nil)
return "WritePPM: malloc failed";
snprint(err, ERRMAX, "WriteGIF: %r");
free(data);
return err;
}
/* Encode and emit the data */
col = 0;
switch(chan){
case GREY1:
case GREY2:
case GREY4:
pmask = (1<<depth)-1;
xmask = 7>>drawlog2[depth];
for(y=r.min.y; y<r.max.y; y++){
i = (y-r.min.y)*bytesperline(r, depth);
for(x=r.min.x; x<r.max.x; x++){
pix = (data[i]>>depth*((xmask-x)&xmask))&pmask;
if(((x+1)&xmask) == 0)
i++;
col += Bprint(fd, "%d ", pix);
if(col >= MAXLINE-(2+1)){
Bprint(fd, "\n");
col = 0;
}else
col += Bprint(fd, " ");
}
}
break;
case GREY8:
for(i=0; i<ndata; i++){
col += Bprint(fd, "%d ", data[i]);
if(col >= MAXLINE-(4+1)){
Bprint(fd, "\n");
col = 0;
}else
col += Bprint(fd, " ");
}
break;
case RGB24:
for(i=0; i<ndata; i+=3){
col += Bprint(fd, "%d %d %d", data[i+2], data[i+1], data[i]);
if(col >= MAXLINE-(4+4+4+1)){
Bprint(fd, "\n");
col = 0;
}else
col += Bprint(fd, " ");
}
break;
default:
return "WritePPM: can't handle channel type";
}
return nil;
}
static
char*
writeppm0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, int chan, char *comment)
{
char *err;
switch(chan){
case GREY1:
Bprint(fd, "P1\n");
break;
case GREY2:
case GREY4:
case GREY8:
Bprint(fd, "P2\n");
break;
case RGB24:
Bprint(fd, "P3\n");
break;
default:
return "WritePPM: can't handle channel type";
}
if(comment!=nil && comment[0]!='\0'){
Bprint(fd, "# %s", comment);
if(comment[strlen(comment)-1] != '\n')
Bprint(fd, "\n");
}
Bprint(fd, "%d %d\n", Dx(r), Dy(r));
/* maximum pixel value */
switch(chan){
case GREY2:
Bprint(fd, "%d\n", 3);
break;
case GREY4:
Bprint(fd, "%d\n", 15);
break;
case GREY8:
case RGB24:
Bprint(fd, "%d\n", 255);
break;
}
err = writedata(fd, image, memimage);
Bprint(fd, "\n");
Bflush(fd);
return err;
}
char*
writeppm(Biobuf *fd, Image *image, char *comment)
{
return writeppm0(fd, image, nil, image->r, image->chan, comment);
}
char*
memwriteppm(Biobuf *fd, Memimage *memimage, char *comment)
{
return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment);
}

206
src/cmd/jpg/writerawimage.c Normal file
View file

@ -0,0 +1,206 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include "imagefile.h"
/*
* Hacked version for writing from Rawimage to file.
* Assumes 8 bits per component.
*/
#define HSHIFT 3 /* HSHIFT==5 runs slightly faster, but hash table is 64x bigger */
#define NHASH (1<<(HSHIFT*NMATCH))
#define HMASK (NHASH-1)
#define hupdate(h, c) ((((h)<<HSHIFT)^(c))&HMASK)
typedef struct Hlist Hlist;
struct Hlist{
uchar *s;
Hlist *next, *prev;
};
int
writerawimage(int fd, Rawimage *i)
{
uchar *outbuf, *outp, *eout; /* encoded data, pointer, end */
uchar *loutp; /* start of encoded line */
Hlist *hash; /* heads of hash chains of past strings */
Hlist *chain, *hp; /* hash chain members, pointer */
Hlist *cp; /* next Hlist to fall out of window */
int h; /* hash value */
uchar *line, *eline; /* input line, end pointer */
uchar *data, *edata; /* input buffer, end pointer */
ulong n; /* length of input buffer */
int bpl; /* input line length */
int offs, runlen; /* offset, length of consumed data */
uchar dumpbuf[NDUMP]; /* dump accumulator */
int ndump; /* length of dump accumulator */
int ncblock; /* size of buffer */
Rectangle r;
uchar *p, *q, *s, *es, *t;
char hdr[11+5*12+1], buf[16];
ulong desc;
r = i->r;
switch(i->chandesc){
default:
werrstr("can't handle chandesc %d", i->chandesc);
return -1;
case CY:
bpl = Dx(r);
desc = GREY8;
break;
case CYA16:
bpl = 2*Dx(r);
desc = CHAN2(CGrey, 8, CAlpha, 8);
break;
case CRGBV:
bpl = Dx(r);
desc = CMAP8;
break;
case CRGBVA16:
bpl = 2*Dx(r);
desc = CHAN2(CMap, 8, CAlpha, 8);
break;
case CRGB24:
bpl = 3*Dx(r);
desc = RGB24;
break;
case CRGBA32:
bpl = 4*Dx(r);
desc = RGBA32;
break;
}
ncblock = _compblocksize(r, bpl/Dx(r));
outbuf = malloc(ncblock);
hash = malloc(NHASH*sizeof(Hlist));
chain = malloc(NMEM*sizeof(Hlist));
if(outbuf == 0 || hash == 0 || chain == 0){
ErrOut:
free(outbuf);
free(hash);
free(chain);
return -1;
}
n = Dy(r)*bpl;
data = i->chans[0];
sprint(hdr, "compressed\n%11s %11d %11d %11d %11d ",
chantostr(buf, desc), r.min.x, r.min.y, r.max.x, r.max.y);
if(write(fd, hdr, 11+5*12) != 11+5*12){
werrstr("i/o error writing header");
goto ErrOut;
}
edata = data+n;
eout = outbuf+ncblock;
line = data;
r.max.y = r.min.y;
while(line != edata){
memset(hash, 0, NHASH*sizeof(Hlist));
memset(chain, 0, NMEM*sizeof(Hlist));
cp = chain;
h = 0;
outp = outbuf;
for(n = 0; n != NMATCH; n++)
h = hupdate(h, line[n]);
loutp = outbuf;
while(line != edata){
ndump = 0;
eline = line+bpl;
for(p = line; p != eline; ){
if(eline-p < NRUN)
es = eline;
else
es = p+NRUN;
q = 0;
runlen = 0;
for(hp = hash[h].next; hp; hp = hp->next){
s = p + runlen;
if(s >= es)
continue;
t = hp->s + runlen;
for(; s >= p; s--)
if(*s != *t--)
goto matchloop;
t += runlen+2;
s += runlen+2;
for(; s < es; s++)
if(*s != *t++)
break;
n = s-p;
if(n > runlen){
runlen = n;
q = hp->s;
if(n == NRUN)
break;
}
matchloop: ;
}
if(runlen < NMATCH){
if(ndump == NDUMP){
if(eout-outp < ndump+1)
goto Bfull;
*outp++ = ndump-1+128;
memmove(outp, dumpbuf, ndump);
outp += ndump;
ndump = 0;
}
dumpbuf[ndump++] = *p;
runlen = 1;
}
else{
if(ndump != 0){
if(eout-outp < ndump+1)
goto Bfull;
*outp++ = ndump-1+128;
memmove(outp, dumpbuf, ndump);
outp += ndump;
ndump = 0;
}
offs = p-q-1;
if(eout-outp < 2)
goto Bfull;
*outp++ = ((runlen-NMATCH)<<2) + (offs>>8);
*outp++ = offs&255;
}
for(q = p+runlen; p != q; p++){
if(cp->prev)
cp->prev->next = 0;
cp->next = hash[h].next;
cp->prev = &hash[h];
if(cp->next)
cp->next->prev = cp;
cp->prev->next = cp;
cp->s = p;
if(++cp == &chain[NMEM])
cp = chain;
if(edata-p > NMATCH)
h = hupdate(h, p[NMATCH]);
}
}
if(ndump != 0){
if(eout-outp < ndump+1)
goto Bfull;
*outp++ = ndump-1+128;
memmove(outp, dumpbuf, ndump);
outp += ndump;
}
line = eline;
loutp = outp;
r.max.y++;
}
Bfull:
if(loutp == outbuf){
werrstr("compressor out of sync");
goto ErrOut;
}
n = loutp-outbuf;
sprint(hdr, "%11d %11ld ", r.max.y, n);
write(fd, hdr, 2*12);
write(fd, outbuf, n);
r.min.y = r.max.y;
}
free(outbuf);
free(hash);
free(chain);
return 0;
}

211
src/cmd/jpg/yuv.c Normal file
View file

@ -0,0 +1,211 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include "imagefile.h"
int cflag = 0;
int dflag = 0;
int eflag = 0;
int nineflag = 0;
int threeflag = 0;
int output = 0;
ulong outchan = CMAP8;
int defaultcolor = 1;
Image *image;
enum{
Border = 2,
Edge = 5
};
char *show(int, char*);
Rawimage** readyuv(int fd, int colorspace);
void
eresized(int new)
{
Rectangle r;
if(new && getwindow(display, Refnone) < 0){
fprint(2, "yuv: can't reattach to window\n");
exits("resize");
}
if(image == nil)
return;
r = insetrect(screen->clipr, Edge+Border);
r.max.x = r.min.x+Dx(image->r);
r.max.y = r.min.y+Dy(image->r);
border(screen, r, -Border, nil, ZP);
draw(screen, r, image, nil, image->r.min);
flushimage(display, 1);
}
void
main(int argc, char *argv[])
{
int fd, i;
char *err;
ARGBEGIN{
case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */
threeflag++;
/* fall through */
case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */
cflag++;
dflag++;
output++;
defaultcolor = 0;
outchan = RGB24;
break;
case 'c': /* produce encoded, compressed, bitmap file; no display by default */
cflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
case 'd': /* suppress display of image */
dflag++;
break;
case 'e': /* disable floyd-steinberg error diffusion */
eflag++;
break;
case 'k': /* force black and white */
defaultcolor = 0;
outchan = GREY8;
break;
case 'v': /* force RGBV */
defaultcolor = 0;
outchan = CMAP8;
break;
case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */
nineflag++;
dflag++;
output++;
if(defaultcolor)
outchan = CMAP8;
break;
default:
fprint(2, "usage: yuv -39cdektv [file.yuv ...]\n");
exits("usage");
}ARGEND;
err = nil;
if(argc == 0)
err = show(0, "<stdin>");
else{
for(i=0; i<argc; i++){
fd = open(argv[i], OREAD);
if(fd < 0){
fprint(2, "yuv: can't open %s: %r\n", argv[i]);
err = "open";
}else{
err = show(fd, argv[i]);
close(fd);
}
if((nineflag || cflag) && argc>1 && err==nil){
fprint(2, "yuv: exiting after one file\n");
break;
}
}
}
exits(err);
}
int
init(void)
{
static int inited;
if(inited == 0){
if(initdraw(0, 0, 0) < 0){
fprint(2, "yuv: initdraw failed: %r");
return -1;
}
einit(Ekeyboard|Emouse);
inited++;
}
return 1;
}
char*
show(int fd, char *name)
{
Rawimage **array, *r, *c;
Image *i;
int j, ch;
char buf[32];
array = readyuv(fd, CYCbCr);
if(array == nil || array[0]==nil){
fprint(2, "yuv: decode %s failed: %r\n", name);
return "decode";
}
if(!dflag){
if(init() < 0)
return "initdraw";
if(defaultcolor && screen->depth>8)
outchan = RGB24;
}
r = array[0];
if(outchan == CMAP8)
c = torgbv(r, !eflag);
else{
if(outchan==GREY8 || (r->chandesc==CY && threeflag==0))
c = totruecolor(r, CY);
else
c = totruecolor(r, CRGB24);
}
if(c == nil){
fprint(2, "yuv: converting %s to local format failed: %r\n", name);
return "torgbv";
}
if(!dflag){
if(r->chandesc == CY)
i = allocimage(display, c->r, GREY8, 0, 0);
else
i = allocimage(display, c->r, outchan, 0, 0);
if(i == nil){
fprint(2, "yuv: allocimage %s failed: %r\n", name);
return "allocimage";
}
if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){
fprint(2, "yuv: loadimage %s failed: %r\n", name);
return "loadimage";
}
image = i;
eresized(0);
if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)
exits(nil);
draw(screen, screen->clipr, display->white, nil, ZP);
image = nil;
freeimage(i);
}
if(nineflag){
chantostr(buf, outchan);
print("%11s %11d %11d %11d %11d ", buf,
c->r.min.x, c->r.min.y, c->r.max.x, c->r.max.y);
if(write(1, c->chans[0], c->chanlen) != c->chanlen){
fprint(2, "yuv: %s: write error %r\n", name);
return "write";
}
}else if(cflag){
if(writerawimage(1, c) < 0){
fprint(2, "yuv: %s: write error: %r\n", name);
return "write";
}
}
for(j=0; j<r->nchans; j++)
free(r->chans[j]);
free(r->cmap);
free(r);
free(array);
if(c){
free(c->chans[0]);
free(c);
}
return nil;
}