added support for TGA images, not used much these days but still somtimes useful. doesn't handle colour mapped images or alpha channels. resumbitted: does now do reflect/flip if the image's origin is not in the top left hand corner. -Steve Reference: /n/sources/patch/applied/tga-images Date: Mon Oct 20 17:30:18 CES 2008 Signed-off-by: steve@quintile.net --- /sys/src/cmd/jpg/tga.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jpg/tga.c Mon Oct 20 17:28:33 2008 @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#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** readtga(int fd); + +void +eresized(int new) +{ + Rectangle r; + + if(new && getwindow(display, Refnone) < 0){ + fprint(2, "tga: 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); + drawop(screen, r, image, nil, image->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: tga -39cdektv [file ...]\n"); + exits("usage"); + }ARGEND; + + err = nil; + if(argc == 0) + err = show(0, ""); + else{ + for(i=0; i1 && err==nil){ + fprint(2, "tga: 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, "tga: 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 = readtga(fd); + if(array == nil || array[0]==nil){ + fprint(2, "tga: 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, "tga: 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, "tga: allocimage %s failed: %r\n", name); + return "allocimage"; + } + + if(loadimage(i, i->r, c->chans[0], c->chanlen) < 0){ + fprint(2, "tga: 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, "tga: %s: write error %r\n", name); + return "write"; + } + }else if(cflag){ + if(writerawimage(1, c) < 0){ + fprint(2, "tga: %s: write error: %r\n", name); + return "write"; + } + } + for(j=0; jnchans; j++) + free(r->chans[j]); + free(r->cmap); + free(r); + free(array); + if(c){ + free(c->chans[0]); + free(c); + } + return nil; +} --- /sys/src/cmd/jpg/readtga.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/jpg/readtga.c Mon Oct 20 17:28:35 2008 @@ -0,0 +1,416 @@ +/* + * TGA is a fairly dead standard, however in the video industry + * it is still used a little for test patterns and the like. + * + * Thus we ignore any alpha channels, and colour mapped images. + */ + +#include +#include +#include +#include +#include +#include "imagefile.h" + +enum { + HdrLen = 18, +}; + +typedef struct { + int idlen; /* length of string after header */ + int cmaptype; /* 1 => datatype = 1 => colourmapped */ + int datatype; /* see below */ + int cmaporigin; /* index of first entry in colour map */ + int cmaplen; /* length of olour map */ + int cmapbpp; /* bips per pixel of colour map: 16, 24, or 32 */ + int xorigin; /* source image origin */ + int yorigin; + int width; + int height; + int bpp; /* bits per pixel of image: 16, 24, or 32 */ + int descriptor; + uchar *cmap; /* colour map (optional) */ +} Tga; + +/* + * descriptor: + * d0-3 = number of attribute bits per pixel + * d4 = reserved, always zero + * d6-7 = origin: 0=lower left, 1=upper left, 2=lower right, 3=upper right + * d8-9 = interleave: 0=progressive, 1=2 way, 3 = 4 way, 4 = reserved. + */ + +char *datatype[] = { + [0] "No image data", + [1] "color mapped", + [2] "RGB", + [3] "B&W", + [9] "RLE color-mapped", + [10] "RLE RGB", + [11] "Compressed B&W", + [32] "Compressed color", + [33] "Quadtree compressed color", +}; + +static int +Bgeti(Biobuf *bp) +{ + int x, y; + + if((x = Bgetc(bp)) < 0) + return -1; + if((y = Bgetc(bp)) < 0) + return -1; + return (y<<8)|x; +} + +static Tga * +rdhdr(Biobuf *bp) +{ + int n; + Tga *h; + + if((h = malloc(sizeof(Tga))) == nil) + return nil; + if((h->idlen = Bgetc(bp)) == -1) + return nil; + if((h->cmaptype = Bgetc(bp)) == -1) + return nil; + if((h->datatype = Bgetc(bp)) == -1) + return nil; + if((h->cmaporigin = Bgeti(bp)) == -1) + return nil; + if((h->cmaplen = Bgeti(bp)) == -1) + return nil; + if((h->cmapbpp = Bgetc(bp)) == -1) + return nil; + if((h->xorigin = Bgeti(bp)) == -1) + return nil; + if((h->yorigin = Bgeti(bp)) == -1) + return nil; + if((h->width = Bgeti(bp)) == -1) + return nil; + if((h->height = Bgeti(bp)) == -1) + return nil; + if((h->bpp = Bgetc(bp)) == -1) + return nil; + if((h->descriptor = Bgetc(bp)) == -1) + return nil; + + /* skip over ID, usually empty anyway */ + if(Bseek(bp, h->idlen, 1) < 0){ + free(h); + return nil; + } + + if(h->cmaptype == 0){ + h->cmap = 0; + return h; + } + + n = (h->cmapbpp/8)*h->cmaplen; + if((h->cmap = malloc(n)) == nil){ + free(h); + return nil; + } + if(Bread(bp, h->cmap, n) != n){ + free(h); + free(h->cmap); + return nil; + } + return h; +} + +static int +luma(Biobuf *bp, uchar *l, int num) +{ + return Bread(bp, l, num); +} + +static int +luma_rle(Biobuf *bp, uchar *l, int num) +{ + uchar len; + int i, got; + + for(got = 0; got < num; got += len){ + if(Bread(bp, &len, 1) != 1) + break; + if(len & 0x80){ + len &= 0x7f; + len += 1; /* run of zero is meaningless */ + if(luma(bp, l, 1) != 1) + break; + for(i = 0; i < len && got < num; i++) + l[i+1] = *l; + } + else{ + len += 1; /* raw block of zero is meaningless */ + if(luma(bp, l, len) != len) + break; + } + l += len; + } + return got; +} + + +static int +rgba(Biobuf *bp, int bpp, uchar *r, uchar *g, uchar *b, int num) +{ + int i; + uchar x, y, buf[4]; + + switch(bpp){ + case 16: + for(i = 0; i < num; i++){ + if(Bread(bp, buf, 2) != 2) + break; + x = buf[0]; + y = buf[1]; + *b++ = (x&0x1f)<<3; + *g++ = ((y&0x03)<<6) | ((x&0xe0)>>2); + *r++ = (y&0x1f)<<3; + } + break; + case 24: + for(i = 0; i < num; i++){ + if(Bread(bp, buf, 3) != 3) + break; + *b++ = buf[0]; + *g++ = buf[1]; + *r++ = buf[2]; + } + break; + case 32: + for(i = 0; i < num; i++){ + if(Bread(bp, buf, 4) != 4) + break; + *b++ = buf[0]; + *g++ = buf[1]; + *r++ = buf[2]; + } + break; + default: + i = 0; + break; + } + return i; +} + +static int +rgba_rle(Biobuf *bp, int bpp, uchar *r, uchar *g, uchar *b, int num) +{ + uchar len; + int i, got; + + for(got = 0; got < num; got += len){ + if(Bread(bp, &len, 1) != 1) + break; + if(len & 0x80){ + len &= 0x7f; + len += 1; /* run of zero is meaningless */ + if(rgba(bp, bpp, r, g, b, 1) != 1) + break; + for(i = 0; i < len-1 && got < num; i++){ + r[i+1] = *r; + g[i+1] = *g; + b[i+1] = *b; + } + } + else{ + len += 1; /* raw block of zero is meaningless */ + if(rgba(bp, bpp, r, g, b, len) != len) + break; + } + r += len; + g += len; + b += len; + } + return got; +} + +int +flip(Rawimage *ar) +{ + int w, h, c, l; + uchar *t, *s, *d; + + w = Dx(ar->r); + h = Dy(ar->r); + if((t = malloc(w)) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + return -1; + } + + for(c = 0; c < ar->nchans; c++){ + s = ar->chans[c]; + d = ar->chans[c] + ar->chanlen - w; + for(l = 0; l < (h/2); l++){ + memcpy(t, s, w); + memcpy(s, d, w); + memcpy(d, t, w); + s += w; + d -= w; + } + } + free(t); + return 0; +} + +int +reflect(Rawimage *ar) +{ + int w, h, c, l, p; + uchar t, *sol, *eol, *s, *d; + + w = Dx(ar->r); + h = Dy(ar->r); + + for(c = 0; c < ar->nchans; c++){ + sol = ar->chans[c]; + eol = ar->chans[c] +w -1; + for(l = 0; l < h; l++){ + s = sol; + d = eol; + for(p = 0; p < w/2; p++){ + t = *s; + *s = *d; + *d = t; + s++; + d--; + } + sol += w; + eol += w; + } + } + return 0; +} + + +Rawimage** +Breadtga(Biobuf *bp) +{ + Tga *h; + int n, c, num; + uchar *r, *g, *b; + Rawimage *ar, **array; + + if((h = rdhdr(bp)) == nil){ + werrstr("ReadTGA: bad header %r"); + return nil; + } + + if(0){ + fprint(2, "idlen=%d\n", h->idlen); + fprint(2, "cmaptype=%d\n", h->cmaptype); + fprint(2, "datatype=%s\n", datatype[h->datatype]); + fprint(2, "cmaporigin=%d\n", h->cmaporigin); + fprint(2, "cmaplen=%d\n", h->cmaplen); + fprint(2, "cmapbpp=%d\n", h->cmapbpp); + fprint(2, "xorigin=%d\n", h->xorigin); + fprint(2, "yorigin=%d\n", h->yorigin); + fprint(2, "width=%d\n", h->width); + fprint(2, "height=%d\n", h->height); + fprint(2, "bpp=%d\n", h->bpp); + fprint(2, "descriptor=%d\n", h->descriptor); + } + + array = nil; + if((ar = calloc(sizeof(Rawimage), 1)) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + goto Error; + } + + if((array = calloc(sizeof(Rawimage *), 2)) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + goto Error; + } + array[0] = ar; + array[1] = nil; + + if(h->datatype == 3){ + ar->nchans = 1; + ar->chandesc = CY; + } + else{ + ar->nchans = 3; + ar->chandesc = CRGB; + } + + ar->chanlen = h->width*h->height; + ar->r = Rect(0, 0, h->width, h->height); + for (c = 0; c < ar->nchans; c++) + if ((ar->chans[c] = malloc(h->width*h->height)) == nil){ + werrstr("ReadTGA: no memory - %r\n"); + goto Error; + } + r = ar->chans[0]; + g = ar->chans[1]; + b = ar->chans[2]; + + num = h->width*h->height; + switch(h->datatype){ + case 2: + if(rgba(bp, h->bpp, r, g, b, num) != num){ + werrstr("ReadTGA: decode fail - %r\n"); + goto Error; + } + break; + case 3: + if(luma(bp, r, num) != num){ + werrstr("ReadTGA: decode fail - %r\n"); + goto Error; + } + break; + case 10: + if((n = rgba_rle(bp, h->bpp, r, g, b, num)) != num){ + werrstr("ReadTGA: decode fail (%d!=%d) - %r\n", n, num); + goto Error; + } + break; + case 11: + if(luma_rle(bp, r, num) != num){ + werrstr("ReadTGA: decode fail - %r\n"); + goto Error; + } + break; + default: + werrstr("ReadTGA: type=%d (%s) unsupported\n", h->datatype, datatype[h->datatype]); + goto Error; + } + + if(h->xorigin != 0) + reflect(ar); + if(h->yorigin == 0) + flip(ar); + + free(h->cmap); + free(h); + return array; +Error: + + if(ar) + for (c = 0; c < ar->nchans; c++) + free(ar->chans[c]); + free(ar); + free(array); + free(h->cmap); + free(h); + return nil; +} + +Rawimage** +readtga(int fd) +{ + Rawimage * *a; + Biobuf b; + + if (Binit(&b, fd, OREAD) < 0) + return nil; + a = Breadtga(&b); + Bterm(&b); + return a; +} + + --- /sys/src/cmd/jpg/mkfile Mon Oct 20 17:28:39 2008 +++ /sys/src/cmd/jpg/mkfile Mon Oct 20 17:28:37 2008 @@ -11,6 +11,9 @@ ico\ toico\ bmp\ + wbmp\ + towbmp\ + tga\ IMFILES=\ torgbv.$O\ @@ -41,6 +44,7 @@