ref: a2bf5224f6a466910d1cd308e4c173dc50456d30
dir: /i_tga.c/
#include "quakedef.h" typedef struct Header Header; typedef struct Footer Footer; typedef struct Tga Tga; struct Header { int idlen; int paltype; int imgtype; int palfirst; int pallen; int palbpp; int ox; int oy; int w; int h; int bpp; int flags; }; struct Tga { Header; int alphasz; int pxsz; byte *pal; bool hasalpha; }; struct Footer { u32int extoff; u32int ddoff; char sig[18]; }; enum { FlagOriginRight = 1<<4, FlagOriginTop = 1<<5, FlagAlphaSizeMask = 0x0f, ImgTypePaletted = 1, ImgTypeTrueColor, ImgTypeMonoChrome, ImgTypeFlagRLE = 1<<3, ExtAreaAttrTypeOffset = 0x1ee, AttrTypeAlpha = 3, AttrTypePremultipliedAlpha, HeaderSize = 18, FooterSize = 26, }; static const char tga_sig[] = "TRUEVISION-XFILE.\0"; static void w2rgb(int w, byte *r, byte *g, byte *b) { *b = (w>>0) & 31; *b = (*b<<3) + (*b>>2); *g = (w>>5) & 31; *g = (*g<<3) + (*g>>2); *r = (w>>10) & 31; *r = (*r<<3) + (*r>>2); } static byte * getpixel(Tga *h, pixel_t *out, byte *p, int *sz) { byte r, g, b, a; int w; if(*sz < h->pxsz){ werrstr("truncated?"); return nil; } r = g = b = a = 0xff; switch(h->pxsz){ case 4: if(h->hasalpha) a = p[3]; /* fallthrough */ case 3: b = p[0]; g = p[1]; r = p[2]; break; case 2: if(h->imgtype == ImgTypeMonoChrome){ r = g = b = p[0]; if(h->hasalpha) a = p[1]; }else{ w = p[1]<<8 | p[0]; w2rgb(w, &r, &g, &b); if(h->hasalpha && (w & (1<<15)) == 0) a = 0; } break; case 1: if(h->pal != nil){ w = p[0]; if(w >= h->pallen){ werrstr("out of palette range: %d/%d", w, h->pallen); return nil; } if(h->palbpp == 24){ w *= 3; b = h->pal[w+0]; g = h->pal[w+1]; r = h->pal[w+2]; }else if(h->palbpp == 32){ w *= 4; b = h->pal[w+0]; g = h->pal[w+1]; r = h->pal[w+2]; if(h->hasalpha) a = h->pal[w+3]; }else if(h->palbpp == 16){ w *= 2; w = h->pal[w+1]<<8 | h->pal[w+0]; w2rgb(w, &r, &g, &b); } }else{ r = g = b = p[0]; } break; } *out = a == 0 ? 0 : (a<<24 | r<<16 | g<<8 | b<<0); *sz -= h->pxsz; return p + h->pxsz; } int TGA_Encode(byte **out, char *id, pixel_t *pix, int w, int h) { int sz, n, i; byte *p; n = id == nil ? 0 : strlen(id); sz = HeaderSize + n + w*h*3; *out = p = emalloc(sz); *p++ = n; /* idlen */ *p++ = 0; /* paltype */ *p++ = ImgTypeTrueColor; /* imgtype */ *p++ = 0; *p++ = 0; /* palfirst */ *p++ = 0; *p++ = 0; /* pallen */ *p++ = 0; /* palbpp */ *p++ = 0; *p++ = 0; /* ox */ *p++ = 0; *p++ = 0; /* oy */ *p++ = w & 0xff; *p++ = w>>8; /* w */ *p++ = h & 0xff; *p++ = h>>8; /* h */ *p++ = 24; /* bpp */ *p++ = FlagOriginTop; /* flags */ memmove(p, id, n); p += n; /* id */ n = w * h; for(i = 0; i < n; i++){ *p++ = pix[i] >> 0; *p++ = pix[i] >> 8; *p++ = pix[i] >> 16; } return sz; } qpic_t * TGA_Decode(byte *in, int sz, bool *premult) { int i, n, cnt, x, y; pixel_t pix; byte *p, b; qpic_t *q; bool rle; Footer f; Tga h; if(sz < HeaderSize){ werrstr("invalid size: %d", sz); goto err; } h.idlen = *in++; h.paltype = *in++; h.imgtype = *in++; h.palfirst = le16u(in); h.pallen = le16u(in); h.palbpp = *in++; h.ox = le16u(in); h.oy = le16u(in); h.w = le16u(in); h.h = le16u(in); h.bpp = *in++; h.flags = *in++; sz -= HeaderSize; if(h.idlen >= sz){ werrstr("invalid idlen: %d", h.idlen); goto err; } in += h.idlen; sz -= h.idlen; if(h.w < 1 || h.h < 1){ werrstr("invalid size: %dx%d", h.w, h.h); goto err; } rle = h.imgtype & ImgTypeFlagRLE; h.imgtype &= 3; h.alphasz = h.flags & FlagAlphaSizeMask; if(h.alphasz != 0 && h.alphasz != 1 && h.alphasz != 8){ werrstr("invalid alpha size: %d", h.alphasz); goto err; } *premult = false; h.hasalpha = (h.alphasz > 0 || h.bpp == 32) || (h.imgtype == ImgTypeMonoChrome && h.bpp == 16) || (h.imgtype == ImgTypePaletted && h.palbpp == 32); h.pxsz = h.bpp >> 3; if(sz > h.idlen + FooterSize){ p = in + sz - FooterSize; f.extoff = le32u(p); f.ddoff = le32u(p); if(f.extoff != 0 && memcmp(p, tga_sig, sizeof(f.sig)) == 0){ f.extoff += ExtAreaAttrTypeOffset - HeaderSize; if(f.extoff >= (u32int)sz){ werrstr("extoff out of range: %d", (int)f.extoff); goto err; } switch(in[f.extoff]){ case AttrTypeAlpha: h.hasalpha = true; break; case AttrTypePremultipliedAlpha: h.hasalpha = true; *premult = true; default: h.hasalpha = false; break; } } } if( (h.imgtype == ImgTypePaletted && ( h.paltype != 1 || h.bpp != 8 || (h.palbpp != 15 && h.palbpp != 16 && h.palbpp != 24 && h.palbpp != 32))) || (h.imgtype == ImgTypeTrueColor && ( h.bpp != 32 && h.bpp != 16 && (h.bpp != 24 || h.hasalpha))) || (h.imgtype == ImgTypeMonoChrome && ( (h.bpp != 16 && h.hasalpha) || (h.bpp != 8 && !h.hasalpha)))){ werrstr("invalid/unsupported: imgtype=%d bpp=%d pal=[type=%d bpp=%d] hasalpha=%d", h.imgtype, h.bpp, h.paltype, h.palbpp, h.hasalpha ); goto err; } if(h.imgtype == ImgTypePaletted){ if(h.pallen <= h.palfirst){ werrstr("invalid palfirst/pallen: %d/%d", h.palfirst, h.pallen); goto err; } h.pallen -= h.palfirst; n = (h.palbpp+1) >> 3; i = n*((int)h.palfirst + (int)h.pallen); if(i >= sz){ werrstr("truncated?"); goto err; } h.pal = in + n*h.palfirst; in += i; sz -= i; }else{ h.pal = nil; } q = Hunk_Alloc(sizeof(*q) + h.w*h.h*sizeof(pixel_t)); q->width = h.w; q->height = h.h; n = q->width * q->height; if(rle){ for(i = 0; i < n && sz > 0;){ b = *in++; sz--; cnt = b; if(cnt & (1<<7)){ cnt &= ~(1<<7); if((in = getpixel(&h, &q->pixels[i++], in, &sz)) == nil) goto err; for(; cnt > 0 && i < n; cnt--, i++) q->pixels[i] = q->pixels[i-1]; }else{ for(cnt++; cnt > 0 && i < n; cnt--, i++){ if((in = getpixel(&h, &q->pixels[i], in, &sz)) == nil) goto err; } } } }else{ for(i = 0; i < n; i++){ if((in = getpixel(&h, &q->pixels[i], in, &sz)) == nil) goto err; } } if((h.flags & FlagOriginRight) != 0){ for(y = 0; y < q->height; y++){ for(x = 0; x < q->width/2; x++){ pix = q->pixels[x]; q->pixels[x] = q->pixels[q->width-x-1]; q->pixels[q->width-x-1] = pix; } } } if((h.flags & FlagOriginTop) == 0){ for(y = 0; y < q->height/2; y++){ for(x = 0; x < q->width; x++){ pix = q->pixels[y*q->width+x]; q->pixels[y*q->width+x] = q->pixels[(q->height-y-1)*q->width+x]; q->pixels[(q->height-y-1)*q->width+x] = pix; } } } return q; err: werrstr("TGA_Decode: %s", lerr()); return nil; }