/* * Trivial draw implementation. * Color values are passed around as ulongs containing ααRRGGBB */ /* * Convert v, which is nhave bits wide, into its nwant bits wide equivalent. * Replicates to widen the value, truncates to narrow it. */ ulong replbits(ulong v, int nhave, int nwant) { v &= (1<>= (nhave-nwant); return v & ((1<>24; *r = v>>16; *g = v>>8; *b = v; } /* * Convert uchar channels into ulong pixel. */ ulong rgbatopix(uchar r, uchar g, uchar b, uchar a) { return (a<<24)|(r<<16)|(g<<8)|b; } /* * Retrieve the pixel value at pt in the image. */ ulong getpixel(Memimage *img, Point pt) { uchar r, g, b, a, *p; int nbits, npack, bpp; ulong v, c, rbits, bits; r = g = b = 0; a = ~0; /* default alpha is full */ p = byteaddr(img, pt); v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); bpp = img->depth; if(bpp<8){ /* * Sub-byte greyscale pixels. * * We want to throw away the top pt.x%npack pixels and then use the next bpp bits * in the bottom byte of v. This madness is due to having big endian bits * but little endian bytes. */ npack = 8/bpp; v >>= 8 - bpp*(pt.x%npack+1); v &= (1<chan; c; c>>=8){ nbits = NBITS(c); bits = v & ((1<>= nbits; switch(TYPE(c)){ case CRed: r = rbits; break; case CGreen: g = rbits; break; case CBlue: b = rbits; break; case CGrey: r = g = b = rbits; break; case CAlpha: a = rbits; break; case CMap: p = img->cmap->cmap2rgb + 3*bits; r = p[0]; g = p[1]; b = p[2]; break; case CIgnore: break; default: fprint(2, "unknown channel type %lud\n", TYPE(c)); abort(); } } } return rgbatopix(r, g, b, a); } /* * Return the greyscale equivalent of a pixel. */ uchar getgrey(Memimage *img, Point pt) { uchar r, g, b, a; pixtorgba(getpixel(img, pt), &r, &g, &b, &a); return RGB2K(r, g, b); } /* * Return the value at pt in image, if image is interpreted * as a mask. This means the alpha channel if present, else * the greyscale or its computed equivalent. */ uchar getmask(Memimage *img, Point pt) { if(img->flags&Falpha) return getpixel(img, pt)>>24; else return getgrey(img, pt); } #undef DBG #define DBG if(0) /* * Write a pixel to img at point pt. * * We do this by reading a 32-bit little endian * value from p and then writing it back * after tweaking the appropriate bits. Because * the data is little endian, we don't have to worry * about what the actual depth is, as long as it is * less than 32 bits. */ void putpixel(Memimage *img, Point pt, ulong nv) { uchar r, g, b, a, *p, *q; ulong c, mask, bits, v; int bpp, sh, npack, nbits; pixtorgba(nv, &r, &g, &b, &a); p = byteaddr(img, pt); v = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24); bpp = img->depth; DBG print("v %.8lux...", v); if(bpp < 8){ /* * Sub-byte greyscale pixels. We need to skip the leftmost pt.x%npack pixels, * which is equivalent to skipping the rightmost npack - pt.x%npack - 1 pixels. */ npack = 8/bpp; sh = bpp*(npack - pt.x%npack - 1); bits = RGB2K(r,g,b); DBG print("repl %lux 8 %d = %lux...", bits, bpp, replbits(bits, 8, bpp)); bits = replbits(bits, 8, bpp); mask = (1<chan; c; c>>=8){ nbits = NBITS(c); switch(TYPE(c)){ case CRed: bits = r; break; case CGreen: bits = g; break; case CBlue: bits = b; break; case CGrey: bits = RGB2K(r, g, b); break; case CAlpha: bits = a; break; case CIgnore: bits = 0; break; case CMap: q = img->cmap->rgb2cmap; bits = q[(r>>4)*16*16+(g>>4)*16+(b>>4)]; break; default: SET(bits); fprint(2, "unknown channel type %lud\n", TYPE(c)); abort(); } DBG print("repl %lux 8 %d = %lux...", bits, nbits, replbits(bits, 8, nbits)); if(TYPE(c) != CMap) bits = replbits(bits, 8, nbits); mask = (1<>8; p[2] = v>>16; p[3] = v>>24; } #undef DBG #define DBG if(0) void drawonepixel(Memimage *dst, Point dp, Memimage *src, Point sp, Memimage *mask, Point mp) { uchar m, M, sr, sg, sb, sa, sk, dr, dg, db, da, dk; pixtorgba(getpixel(dst, dp), &dr, &dg, &db, &da); pixtorgba(getpixel(src, sp), &sr, &sg, &sb, &sa); m = getmask(mask, mp); M = 255-(sa*m)/255; DBG print("dst %x %x %x %x src %x %x %x %x m %x = ", dr,dg,db,da, sr,sg,sb,sa, m); if(dst->flags&Fgrey){ /* * We need to do the conversion to grey before the alpha calculation * because the draw operator does this, and we need to be operating * at the same precision so we get exactly the same answers. */ sk = RGB2K(sr, sg, sb); dk = RGB2K(dr, dg, db); dk = (sk*m + dk*M)/255; dr = dg = db = dk; da = (sa*m + da*M)/255; }else{ /* * True color alpha calculation treats all channels (including alpha) * the same. It might have been nice to use an array, but oh well. */ dr = (sr*m + dr*M)/255; dg = (sg*m + dg*M)/255; db = (sb*m + db*M)/255; da = (sa*m + da*M)/255; } DBG print("%x %x %x %x\n", dr,dg,db,da); putpixel(dst, dp, rgbatopix(dr, dg, db, da)); }