/* * 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; }