conterm/libmemdraw/drawtest.c
Russ Cox 934846f35c a
2005-08-08 12:50:13 +00:00

1005 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <memdraw.h>
#define DBG if(0)
#define RGB2K(r,g,b) ((299*((ulong)(r))+587*((ulong)(g))+114*((ulong)(b)))/1000)
/*
* This program tests the 'memimagedraw' primitive stochastically.
* It tests the combination aspects of it thoroughly, but since the
* three images it uses are disjoint, it makes no check of the
* correct behavior when images overlap. That is, however, much
* easier to get right and to test.
*/
void drawonepixel(Memimage*, Point, Memimage*, Point, Memimage*, Point);
void verifyone(void);
void verifyline(void);
void verifyrect(void);
void verifyrectrepl(int, int);
void putpixel(Memimage *img, Point pt, ulong nv);
ulong rgbatopix(uchar, uchar, uchar, uchar);
char *dchan, *schan, *mchan;
int dbpp, sbpp, mbpp;
int drawdebug=0;
int seed;
int niters = 100;
int dbpp; /* bits per pixel in destination */
int sbpp; /* bits per pixel in src */
int mbpp; /* bits per pixel in mask */
int dpm; /* pixel mask at high part of byte, in destination */
int nbytes; /* in destination */
int Xrange = 64;
int Yrange = 8;
Memimage *dst;
Memimage *src;
Memimage *mask;
Memimage *stmp;
Memimage *mtmp;
Memimage *ones;
uchar *dstbits;
uchar *srcbits;
uchar *maskbits;
ulong *savedstbits;
void
rdb(void)
{
}
int
iprint(char *fmt, ...)
{
int n;
va_list va;
char buf[1024];
va_start(va, fmt);
n = doprint(buf, buf+sizeof buf, fmt, va) - buf;
va_end(va);
write(1,buf,n);
return 1;
}
void
main(int argc, char *argv[])
{
memimageinit();
seed = time(0);
ARGBEGIN{
case 'x':
Xrange = atoi(ARGF());
break;
case 'y':
Yrange = atoi(ARGF());
break;
case 'n':
niters = atoi(ARGF());
break;
case 's':
seed = atoi(ARGF());
break;
}ARGEND
dchan = "r8g8b8";
schan = "r8g8b8";
mchan = "r8g8b8";
switch(argc){
case 3: mchan = argv[2];
case 2: schan = argv[1];
case 1: dchan = argv[0];
case 0: break;
default: goto Usage;
Usage:
fprint(2, "usage: dtest [dchan [schan [mchan]]]\n");
exits("usage");
}
fmtinstall('b', numbconv); /* binary! */
fprint(2, "%s -x %d -y %d -s 0x%x %s %s %s\n", argv0, Xrange, Yrange, seed, dchan, schan, mchan);
srand(seed);
dst = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(dchan));
src = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan));
mask = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan));
stmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(schan));
mtmp = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan));
ones = allocmemimage(Rect(0, 0, Xrange, Yrange), strtochan(mchan));
// print("chan %lux %lux %lux %lux %lux %lux\n", dst->chan, src->chan, mask->chan, stmp->chan, mtmp->chan, ones->chan);
if(dst==0 || src==0 || mask==0 || mtmp==0 || ones==0) {
Alloc:
fprint(2, "dtest: allocation failed: %r\n");
exits("alloc");
}
nbytes = (4*Xrange+4)*Yrange;
srcbits = malloc(nbytes);
dstbits = malloc(nbytes);
maskbits = malloc(nbytes);
savedstbits = malloc(nbytes);
if(dstbits==0 || srcbits==0 || maskbits==0 || savedstbits==0)
goto Alloc;
dbpp = dst->depth;
sbpp = src->depth;
mbpp = mask->depth;
dpm = 0xFF ^ (0xFF>>dbpp);
memset(ones->data->bdata, 0xFF, ones->width*sizeof(ulong)*Yrange);
fprint(2, "dtest: verify single pixel operation\n");
verifyone();
fprint(2, "dtest: verify full line non-replicated\n");
verifyline();
fprint(2, "dtest: verify full rectangle non-replicated\n");
verifyrect();
fprint(2, "dtest: verify full rectangle source replicated\n");
verifyrectrepl(1, 0);
fprint(2, "dtest: verify full rectangle mask replicated\n");
verifyrectrepl(0, 1);
fprint(2, "dtest: verify full rectangle source and mask replicated\n");
verifyrectrepl(1, 1);
exits(0);
}
/*
* Dump out an ASCII representation of an image. The label specifies
* a list of characters to put at various points in the picture.
*/
static void
Bprintr5g6b5(Biobuf *bio, char*, ulong v)
{
int r,g,b;
r = (v>>11)&31;
g = (v>>5)&63;
b = v&31;
Bprint(bio, "%.2x%.2x%.2x", r,g,b);
}
static void
Bprintr5g5b5a1(Biobuf *bio, char*, ulong v)
{
int r,g,b,a;
r = (v>>11)&31;
g = (v>>6)&31;
b = (v>>1)&31;
a = v&1;
Bprint(bio, "%.2x%.2x%.2x%.2x", r,g,b,a);
}
void
dumpimage(char *name, Memimage *img, void *vdata, Point labelpt)
{
Biobuf b;
uchar *data;
uchar *p;
char *arg;
void (*fmt)(Biobuf*, char*, ulong);
int npr, x, y, nb, bpp;
ulong v, mask;
Rectangle r;
fmt = nil;
arg = nil;
switch(img->depth){
case 1:
case 2:
case 4:
fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
arg = "%.1ux";
break;
case 8:
fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
arg = "%.2ux";
break;
case 16:
arg = nil;
if(img->chan == RGB16)
fmt = Bprintr5g6b5;
else{
fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
arg = "%.4ux";
}
break;
case 24:
fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
arg = "%.6lux";
break;
case 32:
fmt = (void(*)(Biobuf*,char*,ulong))Bprint;
arg = "%.8lux";
break;
}
if(fmt == nil){
fprint(2, "bad format\n");
abort();
}
r = img->r;
Binit(&b, 2, OWRITE);
data = vdata;
bpp = img->depth;
Bprint(&b, "%s\t%d\tr %R clipr %R repl %d data %p *%P\n", name, r.min.x, r, img->clipr, (img->flags&Frepl) ? 1 : 0, vdata, labelpt);
mask = (1ULL<<bpp)-1;
// for(y=r.min.y; y<r.max.y; y++){
for(y=0; y<Yrange; y++){
nb = 0;
v = 0;
p = data+(byteaddr(img, Pt(0,y))-(uchar*)img->data->bdata);
Bprint(&b, "%-4d\t", y);
// for(x=r.min.x; x<r.max.x; x++){
for(x=0; x<Xrange; x++){
if(x==0)
Bprint(&b, "\t");
if(x != 0 && (x%8)==0)
Bprint(&b, " ");
npr = 0;
if(x==labelpt.x && y==labelpt.y){
Bprint(&b, "*");
npr++;
}
if(npr == 0)
Bprint(&b, " ");
while(nb < bpp){
v &= (1<<nb)-1;
v |= (ulong)(*p++) << nb;
nb += 8;
}
nb -= bpp;
// print("bpp %d v %.8lux mask %.8lux nb %d\n", bpp, v, mask, nb);
fmt(&b, arg, (v>>nb)&mask);
}
Bprint(&b, "\n");
}
Bterm(&b);
}
/*
* Verify that the destination pixel has the specified value.
* The value is in the high bits of v, suitably masked, but must
* be extracted from the destination Memimage.
*/
void
checkone(Point p, Point sp, Point mp)
{
int delta;
uchar *dp, *sdp;
delta = (uchar*)byteaddr(dst, p)-(uchar*)dst->data->bdata;
dp = (uchar*)dst->data->bdata+delta;
sdp = (uchar*)savedstbits+delta;
if(memcmp(dp, sdp, (dst->depth+7)/8) != 0) {
fprint(2, "dtest: one bad pixel drawing at dst %P from source %P mask %P\n", p, sp, mp);
fprint(2, " %.2ux %.2ux %.2ux %.2ux should be %.2ux %.2ux %.2ux %.2ux\n",
dp[0], dp[1], dp[2], dp[3], sdp[0], sdp[1], sdp[2], sdp[3]);
fprint(2, "addresses dst %p src %p mask %p\n", dp, byteaddr(src, sp), byteaddr(mask, mp));
dumpimage("src", src, src->data->bdata, sp);
dumpimage("mask", mask, mask->data->bdata, mp);
dumpimage("origdst", dst, dstbits, p);
dumpimage("dst", dst, dst->data->bdata, p);
dumpimage("gooddst", dst, savedstbits, p);
abort();
}
}
/*
* Verify that the destination line has the same value as the saved line.
*/
#define RECTPTS(r) (r).min.x, (r).min.y, (r).max.x, (r).max.y
void
checkline(Rectangle r, Point sp, Point mp, int y, Memimage *stmp, Memimage *mtmp)
{
ulong *dp;
int nb;
ulong *saved;
dp = wordaddr(dst, Pt(0, y));
saved = savedstbits + y*dst->width;
if(dst->depth < 8)
nb = Xrange/(8/dst->depth);
else
nb = Xrange*(dst->depth/8);
if(memcmp(dp, saved, nb) != 0){
fprint(2, "dtest: bad line at y=%d; saved %p dp %p\n", y, saved, dp);
fprint(2, "draw dst %R src %P mask %P\n", r, sp, mp);
dumpimage("src", src, src->data->bdata, sp);
if(stmp) dumpimage("stmp", stmp, stmp->data->bdata, sp);
dumpimage("mask", mask, mask->data->bdata, mp);
if(mtmp) dumpimage("mtmp", mtmp, mtmp->data->bdata, mp);
dumpimage("origdst", dst, dstbits, r.min);
dumpimage("dst", dst, dst->data->bdata, r.min);
dumpimage("gooddst", dst, savedstbits, r.min);
abort();
}
}
/*
* Fill the bits of an image with random data.
* The Memimage parameter is used only to make sure
* the data is well formatted: only ucbits is written.
*/
void
fill(Memimage *img, uchar *ucbits)
{
int i, x, y;
ushort *up;
uchar alpha, r, g, b;
void *data;
if((img->flags&Falpha) == 0){
up = (ushort*)ucbits;
for(i=0; i<nbytes/2; i++)
*up++ = lrand() >> 7;
if(i+i != nbytes)
*(uchar*)up = lrand() >> 7;
}else{
data = img->data->bdata;
img->data->bdata = ucbits;
for(x=img->r.min.x; x<img->r.max.x; x++)
for(y=img->r.min.y; y<img->r.max.y; y++){
alpha = rand() >> 4;
r = rand()%(alpha+1);
g = rand()%(alpha+1);
b = rand()%(alpha+1);
putpixel(img, Pt(x,y), rgbatopix(r,g,b,alpha));
}
img->data->bdata = data;
}
}
/*
* Mask is preset; do the rest
*/
void
verifyonemask(void)
{
Point dp, sp, mp;
fill(dst, dstbits);
fill(src, srcbits);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
dp.x = nrand(Xrange);
dp.y = nrand(Yrange);
sp.x = nrand(Xrange);
sp.y = nrand(Yrange);
mp.x = nrand(Xrange);
mp.y = nrand(Yrange);
drawonepixel(dst, dp, src, sp, mask, mp);
memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memimagedraw(dst, Rect(dp.x, dp.y, dp.x+1, dp.y+1), src, sp, mask, mp, SoverD);
memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
checkone(dp, sp, mp);
}
void
verifyone(void)
{
int i;
/* mask all zeros */
memset(maskbits, 0, nbytes);
for(i=0; i<niters; i++)
verifyonemask();
/* mask all ones */
memset(maskbits, 0xFF, nbytes);
for(i=0; i<niters; i++)
verifyonemask();
/* random mask */
for(i=0; i<niters; i++){
fill(mask, maskbits);
verifyonemask();
}
}
/*
* Mask is preset; do the rest
*/
void
verifylinemask(void)
{
Point sp, mp, tp, up;
Rectangle dr;
int x;
fill(dst, dstbits);
fill(src, srcbits);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
dr.min.x = nrand(Xrange-1);
dr.min.y = nrand(Yrange-1);
dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x);
dr.max.y = dr.min.y + 1;
sp.x = nrand(Xrange);
sp.y = nrand(Yrange);
mp.x = nrand(Xrange);
mp.y = nrand(Yrange);
tp = sp;
up = mp;
for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
memimagedraw(dst, Rect(x, dr.min.y, x+1, dr.min.y+1), src, tp, mask, up, SoverD);
memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), dr.min.y, nil, nil);
}
void
verifyline(void)
{
int i;
/* mask all ones */
memset(maskbits, 0xFF, nbytes);
for(i=0; i<niters; i++)
verifylinemask();
/* mask all zeros */
memset(maskbits, 0, nbytes);
for(i=0; i<niters; i++)
verifylinemask();
/* random mask */
for(i=0; i<niters; i++){
fill(mask, maskbits);
verifylinemask();
}
}
/*
* Mask is preset; do the rest
*/
void
verifyrectmask(void)
{
Point sp, mp, tp, up;
Rectangle dr;
int x, y;
fill(dst, dstbits);
fill(src, srcbits);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
dr.min.x = nrand(Xrange-1);
dr.min.y = nrand(Yrange-1);
dr.max.x = dr.min.x + 1 + nrand(Xrange-1-dr.min.x);
dr.max.y = dr.min.y + 1 + nrand(Yrange-1-dr.min.y);
sp.x = nrand(Xrange);
sp.y = nrand(Yrange);
mp.x = nrand(Xrange);
mp.y = nrand(Yrange);
tp = sp;
up = mp;
for(y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++){
for(x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
memimagedraw(dst, Rect(x, y, x+1, y+1), src, tp, mask, up, SoverD);
tp.x = sp.x;
up.x = mp.x;
}
memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
for(y=0; y<Yrange; y++)
checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, nil, nil);
}
void
verifyrect(void)
{
int i;
/* mask all zeros */
memset(maskbits, 0, nbytes);
for(i=0; i<niters; i++)
verifyrectmask();
/* mask all ones */
memset(maskbits, 0xFF, nbytes);
for(i=0; i<niters; i++)
verifyrectmask();
/* random mask */
for(i=0; i<niters; i++){
fill(mask, maskbits);
verifyrectmask();
}
}
Rectangle
randrect(void)
{
Rectangle r;
r.min.x = nrand(Xrange-1);
r.min.y = nrand(Yrange-1);
r.max.x = r.min.x + 1 + nrand(Xrange-1-r.min.x);
r.max.y = r.min.y + 1 + nrand(Yrange-1-r.min.y);
return r;
}
/*
* Return coordinate corresponding to x withing range [minx, maxx)
*/
int
tilexy(int minx, int maxx, int x)
{
int sx;
sx = (x-minx) % (maxx-minx);
if(sx < 0)
sx += maxx-minx;
return sx+minx;
}
void
replicate(Memimage *i, Memimage *tmp)
{
Rectangle r, r1;
int x, y, nb;
/* choose the replication window (i->r) */
r.min.x = nrand(Xrange-1);
r.min.y = nrand(Yrange-1);
/* make it trivial more often than pure chance allows */
switch(lrand()&0){
case 1:
r.max.x = r.min.x + 2;
r.max.y = r.min.y + 2;
if(r.max.x < Xrange && r.max.y < Yrange)
break;
/* fall through */
case 0:
r.max.x = r.min.x + 1;
r.max.y = r.min.y + 1;
break;
default:
if(r.min.x+3 >= Xrange)
r.max.x = Xrange;
else
r.max.x = r.min.x+3 + nrand(Xrange-(r.min.x+3));
if(r.min.y+3 >= Yrange)
r.max.y = Yrange;
else
r.max.y = r.min.y+3 + nrand(Yrange-(r.min.y+3));
}
assert(r.min.x >= 0);
assert(r.max.x <= Xrange);
assert(r.min.y >= 0);
assert(r.max.y <= Yrange);
/* copy from i to tmp so we have just the replicated bits */
nb = tmp->width*sizeof(ulong)*Yrange;
memset(tmp->data->bdata, 0, nb);
memimagedraw(tmp, r, i, r.min, ones, r.min, SoverD);
memmove(i->data->bdata, tmp->data->bdata, nb);
/* i is now a non-replicated instance of the replication */
/* replicate it by hand through tmp */
memset(tmp->data->bdata, 0, nb);
x = -(tilexy(r.min.x, r.max.x, 0)-r.min.x);
for(; x<Xrange; x+=Dx(r)){
y = -(tilexy(r.min.y, r.max.y, 0)-r.min.y);
for(; y<Yrange; y+=Dy(r)){
/* set r1 to instance of tile by translation */
r1.min.x = x;
r1.min.y = y;
r1.max.x = r1.min.x+Dx(r);
r1.max.y = r1.min.y+Dy(r);
memimagedraw(tmp, r1, i, r.min, ones, r.min, SoverD);
}
}
i->flags |= Frepl;
i->r = r;
i->clipr = randrect();
// fprint(2, "replicate [[%d %d] [%d %d]] [[%d %d][%d %d]]\n", r.min.x, r.min.y, r.max.x, r.max.y,
// i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
tmp->clipr = i->clipr;
}
/*
* Mask is preset; do the rest
*/
void
verifyrectmaskrepl(int srcrepl, int maskrepl)
{
Point sp, mp, tp, up;
Rectangle dr;
int x, y;
Memimage *s, *m;
// print("verfrect %d %d\n", srcrepl, maskrepl);
src->flags &= ~Frepl;
src->r = Rect(0, 0, Xrange, Yrange);
src->clipr = src->r;
stmp->flags &= ~Frepl;
stmp->r = Rect(0, 0, Xrange, Yrange);
stmp->clipr = src->r;
mask->flags &= ~Frepl;
mask->r = Rect(0, 0, Xrange, Yrange);
mask->clipr = mask->r;
mtmp->flags &= ~Frepl;
mtmp->r = Rect(0, 0, Xrange, Yrange);
mtmp->clipr = mask->r;
fill(dst, dstbits);
fill(src, srcbits);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
memmove(src->data->bdata, srcbits, src->width*sizeof(ulong)*Yrange);
memmove(mask->data->bdata, maskbits, mask->width*sizeof(ulong)*Yrange);
if(srcrepl){
replicate(src, stmp);
s = stmp;
}else
s = src;
if(maskrepl){
replicate(mask, mtmp);
m = mtmp;
}else
m = mask;
dr = randrect();
sp.x = nrand(Xrange);
sp.y = nrand(Yrange);
mp.x = nrand(Xrange);
mp.y = nrand(Yrange);
DBG print("smalldraws\n");
for(tp.y=sp.y,up.y=mp.y,y=dr.min.y; y<dr.max.y && tp.y<Yrange && up.y<Yrange; y++,tp.y++,up.y++)
for(tp.x=sp.x,up.x=mp.x,x=dr.min.x; x<dr.max.x && tp.x<Xrange && up.x<Xrange; x++,tp.x++,up.x++)
memimagedraw(dst, Rect(x, y, x+1, y+1), s, tp, m, up, SoverD);
memmove(savedstbits, dst->data->bdata, dst->width*sizeof(ulong)*Yrange);
memmove(dst->data->bdata, dstbits, dst->width*sizeof(ulong)*Yrange);
DBG print("bigdraw\n");
memimagedraw(dst, dr, src, sp, mask, mp, SoverD);
for(y=0; y<Yrange; y++)
checkline(dr, drawrepl(src->r, sp), drawrepl(mask->r, mp), y, srcrepl?stmp:nil, maskrepl?mtmp:nil);
}
void
verifyrectrepl(int srcrepl, int maskrepl)
{
int i;
/* mask all ones */
memset(maskbits, 0xFF, nbytes);
for(i=0; i<niters; i++)
verifyrectmaskrepl(srcrepl, maskrepl);
/* mask all zeros */
memset(maskbits, 0, nbytes);
for(i=0; i<niters; i++)
verifyrectmaskrepl(srcrepl, maskrepl);
/* random mask */
for(i=0; i<niters; i++){
fill(mask, maskbits);
verifyrectmaskrepl(srcrepl, maskrepl);
}
}
/*
* 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)-1;
for(; nhave<nwant; nhave*=2)
v |= v<<nhave;
v >>= (nhave-nwant);
return v & ((1<<nwant)-1);
}
/*
* Decode a pixel into the uchar* values.
*/
void
pixtorgba(ulong v, uchar *r, uchar *g, uchar *b, uchar *a)
{
*a = v>>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<<bpp)-1;
r = g = b = replbits(v, bpp, 8);
}else{
/*
* General case. We need to parse the channel descriptor and do what it says.
* In all channels but the color map, we replicate to 8 bits because that's the
* precision that all calculations are done at.
*
* In the case of the color map, we leave the bits alone, in case a color map
* with less than 8 bits of index is used. This is currently disallowed, so it's
* sort of silly.
*/
for(c=img->chan; c; c>>=8){
nbits = NBITS(c);
bits = v & ((1<<nbits)-1);
rbits = replbits(bits, nbits, 8);
v >>= 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<<bpp)-1;
DBG print("bits %lux mask %lux sh %d...", bits, mask, sh);
mask <<= sh;
bits <<= sh;
DBG print("(%lux & %lux) | (%lux & %lux)", v, ~mask, bits, mask);
v = (v & ~mask) | (bits & mask);
} else {
/*
* General case. We need to parse the channel descriptor again.
*/
sh = 0;
for(c=img->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<<nbits)-1;
DBG print("bits %lux mask %lux sh %d...", bits, mask, sh);
bits <<= sh;
mask <<= sh;
v = (v & ~mask) | (bits & mask);
sh += nbits;
}
}
DBG print("v %.8lux\n", v);
p[0] = v;
p[1] = v>>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));
}