<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/rsc/draw.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/rsc/draw.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
#include &lt;u.h&gt;
#include &lt;libc.h&gt;
#include &lt;draw.h&gt;
#include &lt;memdraw.h&gt;

int drawdebug;
static int      tablesbuilt;

/* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b &lt; 256 */
#define RGB2K(r,g,b)    ((156763*(r)+307758*(g)+59769*(b))&gt;&gt;19)

/*
* For 16-bit values, x / 255 == (t = x+1, (t+(t&gt;&gt;8)) &gt;&gt; 8).
* We add another 127 to round to the nearest value rather
* than truncate.
*
* CALCxy does x bytewise calculations on y input images (x=1,4; y=1,2).
* CALC2x does two parallel 16-bit calculations on y input images (y=1,2).
*/
#define CALC11(a, v, tmp) \
       (tmp=(a)*(v)+128, (tmp+(tmp&gt;&gt;8))&gt;&gt;8)

#define CALC12(a1, v1, a2, v2, tmp) \
       (tmp=(a1)*(v1)+(a2)*(v2)+128, (tmp+(tmp&gt;&gt;8))&gt;&gt;8)

#define MASK 0xFF00FF

#define CALC21(a, vvuu, tmp) \
       (tmp=(a)*(vvuu)+0x00800080, ((tmp+((tmp&gt;&gt;8)&amp;MASK))&gt;&gt;8)&amp;MASK)

#define CALC41(a, rgba, tmp1, tmp2) \
       (CALC21(a, rgba &amp; MASK, tmp1) | \
        (CALC21(a, (rgba&gt;&gt;8)&amp;MASK, tmp2)&lt;&lt;8))

#define CALC22(a1, vvuu1, a2, vvuu2, tmp) \
       (tmp=(a1)*(vvuu1)+(a2)*(vvuu2)+0x00800080, ((tmp+((tmp&gt;&gt;8)&amp;MASK))&gt;&gt;8)&amp;MASK)

#define CALC42(a1, rgba1, a2, rgba2, tmp1, tmp2) \
       (CALC22(a1, rgba1 &amp; MASK, a2, rgba2 &amp; MASK, tmp1) | \
        (CALC22(a1, (rgba1&gt;&gt;8) &amp; MASK, a2, (rgba2&gt;&gt;8) &amp; MASK, tmp2)&lt;&lt;8))

static void mktables(void);
typedef int Subdraw(Memdrawparam*);
static Subdraw chardraw, alphadraw, memoptdraw;

static Memimage*        memones;
static Memimage*        memzeros;
Memimage *memwhite;
Memimage *memblack;
Memimage *memtransparent;
Memimage *memopaque;

int     __ifmt(Fmt*);

void
memimageinit(void)
{
       static int didinit = 0;

       if(didinit)
               return;

       didinit = 1;

       mktables();
       _memmkcmap();

       fmtinstall('R', Rfmt);
       fmtinstall('P', Pfmt);
       fmtinstall('b', __ifmt);

       memones = allocmemimage(Rect(0,0,1,1), GREY1);
       memones-&gt;flags |= Frepl;
       memones-&gt;clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
       *byteaddr(memones, ZP) = ~0;

       memzeros = allocmemimage(Rect(0,0,1,1), GREY1);
       memzeros-&gt;flags |= Frepl;
       memzeros-&gt;clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
       *byteaddr(memzeros, ZP) = 0;

       if(memones == nil || memzeros == nil)
               assert(0 /*cannot initialize memimage library */);      /* RSC BUG */

       memwhite = memones;
       memblack = memzeros;
       memopaque = memones;
       memtransparent = memzeros;
}

u32int _imgtorgba(Memimage*, u32int);
u32int _rgbatoimg(Memimage*, u32int);
u32int _pixelbits(Memimage*, Point);

#define DBG if(drawdebug)
static Memdrawparam par;

Memdrawparam*
_memimagedrawsetup(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1, int op)
{
       if(mask == nil)
               mask = memopaque;

DBG     print("memimagedraw %p/%luX %R @ %p %p/%luX %P %p/%luX %P... ", dst, dst-&gt;chan, r, dst-&gt;data-&gt;bdata, src, src-&gt;chan, p0, mask, mask-&gt;chan, p1);

       if(drawclip(dst, &amp;r, src, &amp;p0, mask, &amp;p1, &amp;par.sr, &amp;par.mr) == 0){
/*              if(drawdebug) */
/*                      iprint("empty clipped rectangle\n"); */
               return nil;
       }

       if(op &lt; Clear || op &gt; SoverD){
/*              if(drawdebug) */
/*                      iprint("op out of range: %d\n", op); */
               return nil;
       }

       par.op = op;
       par.dst = dst;
       par.r = r;
       par.src = src;
       /* par.sr set by drawclip */
       par.mask = mask;
       /* par.mr set by drawclip */

       par.state = 0;
       if(src-&gt;flags&amp;Frepl){
               par.state |= Replsrc;
               if(Dx(src-&gt;r)==1 &amp;&amp; Dy(src-&gt;r)==1){
                       par.sval = pixelbits(src, src-&gt;r.min);
                       par.state |= Simplesrc;
                       par.srgba = _imgtorgba(src, par.sval);
                       par.sdval = _rgbatoimg(dst, par.srgba);
                       if((par.srgba&amp;0xFF) == 0 &amp;&amp; (op&amp;DoutS)){
/*                              if (drawdebug) iprint("fill with transparent source\n"); */
                               return nil;     /* no-op successfully handled */
                       }
                       if((par.srgba&amp;0xFF) == 0xFF)
                               par.state |= Fullsrc;
               }
       }

       if(mask-&gt;flags &amp; Frepl){
               par.state |= Replmask;
               if(Dx(mask-&gt;r)==1 &amp;&amp; Dy(mask-&gt;r)==1){
                       par.mval = pixelbits(mask, mask-&gt;r.min);
                       if(par.mval == 0 &amp;&amp; (op&amp;DoutS)){
/*                              if(drawdebug) iprint("fill with zero mask\n"); */
                               return nil;     /* no-op successfully handled */
                       }
                       par.state |= Simplemask;
                       if(par.mval == ~0)
                               par.state |= Fullmask;
                       par.mrgba = _imgtorgba(mask, par.mval);
               }
       }

/*      if(drawdebug) */
/*              iprint("dr %R sr %R mr %R...", r, par.sr, par.mr); */
DBG print("draw dr %R sr %R mr %R %lux\n", r, par.sr, par.mr, par.state);

       return &amp;par;
}

void
_memimagedraw(Memdrawparam *par)
{
       /*
        * Now that we've clipped the parameters down to be consistent, we
        * simply try sub-drawing routines in order until we find one that was able
        * to handle us.  If the sub-drawing routine returns zero, it means it was
        * unable to satisfy the request, so we do not return.
        */

       /*
        * Hardware support.  Each video driver provides this function,
        * which checks to see if there is anything it can help with.
        * There could be an if around this checking to see if dst is in video memory.
        */
DBG print("test hwdraw\n");
       if(hwdraw(par)){
/*if(drawdebug) iprint("hw handled\n"); */
DBG print("hwdraw handled\n");
               return;
       }
       /*
        * Optimizations using memmove and memset.
        */
DBG print("test memoptdraw\n");
       if(memoptdraw(par)){
/*if(drawdebug) iprint("memopt handled\n"); */
DBG print("memopt handled\n");
               return;
       }

       /*
        * Character drawing.
        * Solid source color being painted through a boolean mask onto a high res image.
        */
DBG print("test chardraw\n");
       if(chardraw(par)){
/*if(drawdebug) iprint("chardraw handled\n"); */
DBG print("chardraw handled\n");
               return;
       }

       /*
        * General calculation-laden case that does alpha for each pixel.
        */
DBG print("do alphadraw\n");
       alphadraw(par);
/*if(drawdebug) iprint("alphadraw handled\n"); */
DBG print("alphadraw handled\n");
}
#undef DBG

/*
* Clip the destination rectangle further based on the properties of the
* source and mask rectangles.  Once the destination rectangle is properly
* clipped, adjust the source and mask rectangles to be the same size.
* Then if source or mask is replicated, move its clipped rectangle
* so that its minimum point falls within the repl rectangle.
*
* Return zero if the final rectangle is null.
*/
int
drawclip(Memimage *dst, Rectangle *r, Memimage *src, Point *p0, Memimage *mask, Point *p1, Rectangle *sr, Rectangle *mr)
{
       Point rmin, delta;
       int splitcoords;
       Rectangle omr;

       if(r-&gt;min.x&gt;=r-&gt;max.x || r-&gt;min.y&gt;=r-&gt;max.y)
               return 0;
       splitcoords = (p0-&gt;x!=p1-&gt;x) || (p0-&gt;y!=p1-&gt;y);
       /* clip to destination */
       rmin = r-&gt;min;
       if(!rectclip(r, dst-&gt;r) || !rectclip(r, dst-&gt;clipr))
               return 0;
       /* move mask point */
       p1-&gt;x += r-&gt;min.x-rmin.x;
       p1-&gt;y += r-&gt;min.y-rmin.y;
       /* move source point */
       p0-&gt;x += r-&gt;min.x-rmin.x;
       p0-&gt;y += r-&gt;min.y-rmin.y;
       /* map destination rectangle into source */
       sr-&gt;min = *p0;
       sr-&gt;max.x = p0-&gt;x+Dx(*r);
       sr-&gt;max.y = p0-&gt;y+Dy(*r);
       /* sr is r in source coordinates; clip to source */
       if(!(src-&gt;flags&amp;Frepl) &amp;&amp; !rectclip(sr, src-&gt;r))
               return 0;
       if(!rectclip(sr, src-&gt;clipr))
               return 0;
       /* compute and clip rectangle in mask */
       if(splitcoords){
               /* move mask point with source */
               p1-&gt;x += sr-&gt;min.x-p0-&gt;x;
               p1-&gt;y += sr-&gt;min.y-p0-&gt;y;
               mr-&gt;min = *p1;
               mr-&gt;max.x = p1-&gt;x+Dx(*sr);
               mr-&gt;max.y = p1-&gt;y+Dy(*sr);
               omr = *mr;
               /* mr is now rectangle in mask; clip it */
               if(!(mask-&gt;flags&amp;Frepl) &amp;&amp; !rectclip(mr, mask-&gt;r))
                       return 0;
               if(!rectclip(mr, mask-&gt;clipr))
                       return 0;
               /* reflect any clips back to source */
               sr-&gt;min.x += mr-&gt;min.x-omr.min.x;
               sr-&gt;min.y += mr-&gt;min.y-omr.min.y;
               sr-&gt;max.x += mr-&gt;max.x-omr.max.x;
               sr-&gt;max.y += mr-&gt;max.y-omr.max.y;
               *p1 = mr-&gt;min;
       }else{
               if(!(mask-&gt;flags&amp;Frepl) &amp;&amp; !rectclip(sr, mask-&gt;r))
                       return 0;
               if(!rectclip(sr, mask-&gt;clipr))
                       return 0;
               *p1 = sr-&gt;min;
       }

       /* move source clipping back to destination */
       delta.x = r-&gt;min.x - p0-&gt;x;
       delta.y = r-&gt;min.y - p0-&gt;y;
       r-&gt;min.x = sr-&gt;min.x + delta.x;
       r-&gt;min.y = sr-&gt;min.y + delta.y;
       r-&gt;max.x = sr-&gt;max.x + delta.x;
       r-&gt;max.y = sr-&gt;max.y + delta.y;

       /* move source rectangle so sr-&gt;min is in src-&gt;r */
       if(src-&gt;flags&amp;Frepl) {
               delta.x = drawreplxy(src-&gt;r.min.x, src-&gt;r.max.x, sr-&gt;min.x) - sr-&gt;min.x;
               delta.y = drawreplxy(src-&gt;r.min.y, src-&gt;r.max.y, sr-&gt;min.y) - sr-&gt;min.y;
               sr-&gt;min.x += delta.x;
               sr-&gt;min.y += delta.y;
               sr-&gt;max.x += delta.x;
               sr-&gt;max.y += delta.y;
       }
       *p0 = sr-&gt;min;

       /* move mask point so it is in mask-&gt;r */
       *p1 = drawrepl(mask-&gt;r, *p1);
       mr-&gt;min = *p1;
       mr-&gt;max.x = p1-&gt;x+Dx(*sr);
       mr-&gt;max.y = p1-&gt;y+Dy(*sr);

       assert(Dx(*sr) == Dx(*mr) &amp;&amp; Dx(*mr) == Dx(*r));
       assert(Dy(*sr) == Dy(*mr) &amp;&amp; Dy(*mr) == Dy(*r));
       assert(ptinrect(*p0, src-&gt;r));
       assert(ptinrect(*p1, mask-&gt;r));
       assert(ptinrect(r-&gt;min, dst-&gt;r));

       return 1;
}

/*
* Conversion tables.
*/
static uchar replbit[1+8][256];         /* replbit[x][y] is the replication of the x-bit quantity y to 8-bit depth */
static uchar conv18[256][8];            /* conv18[x][y] is the yth pixel in the depth-1 pixel x */
static uchar conv28[256][4];            /* ... */
static uchar conv48[256][2];

/*
* bitmap of how to replicate n bits to fill 8, for 1 ≤ n ≤ 8.
* the X's are where to put the bottom (ones) bit of the n-bit pattern.
* only the top 8 bits of the result are actually used.
* (the lower 8 bits are needed to get bits in the right place
* when n is not a divisor of 8.)
*
* Should check to see if its easier to just refer to replmul than
* use the precomputed values in replbit.  On PCs it may well
* be; on machines with slow multiply instructions it probably isn't.
*/
#define a ((((((((((((((((0
#define X *2+1)
#define _ *2)
static int replmul[1+8] = {
       0,
       a X X X X X X X X X X X X X X X X,
       a _ X _ X _ X _ X _ X _ X _ X _ X,
       a _ _ X _ _ X _ _ X _ _ X _ _ X _,
       a _ _ _ X _ _ _ X _ _ _ X _ _ _ X,
       a _ _ _ _ X _ _ _ _ X _ _ _ _ X _,
       a _ _ _ _ _ X _ _ _ _ _ X _ _ _ _,
       a _ _ _ _ _ _ X _ _ _ _ _ _ X _ _,
       a _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X,
};
#undef a
#undef X
#undef _

static void
mktables(void)
{
       int i, j, mask, sh, small;

       if(tablesbuilt)
               return;

       fmtinstall('R', Rfmt);
       fmtinstall('P', Pfmt);
       tablesbuilt = 1;

       /* bit replication up to 8 bits */
       for(i=0; i&lt;256; i++){
               for(j=0; j&lt;=8; j++){ /* j &lt;= 8 [sic] */
                       small = i &amp; ((1&lt;&lt;j)-1);
                       replbit[j][i] = (small*replmul[j])&gt;&gt;8;
               }
       }

       /* bit unpacking up to 8 bits, only powers of 2 */
       for(i=0; i&lt;256; i++){
               for(j=0, sh=7, mask=1; j&lt;8; j++, sh--)
                       conv18[i][j] = replbit[1][(i&gt;&gt;sh)&amp;mask];

               for(j=0, sh=6, mask=3; j&lt;4; j++, sh-=2)
                       conv28[i][j] = replbit[2][(i&gt;&gt;sh)&amp;mask];

               for(j=0, sh=4, mask=15; j&lt;2; j++, sh-=4)
                       conv48[i][j] = replbit[4][(i&gt;&gt;sh)&amp;mask];
       }
}

static uchar ones = 0xff;

/*
* General alpha drawing case.  Can handle anything.
*/
typedef struct  Buffer  Buffer;
struct Buffer {
       /* used by most routines */
       uchar   *red;
       uchar   *grn;
       uchar   *blu;
       uchar   *alpha;
       uchar   *grey;
       u32int  *rgba;
       int     delta;  /* number of bytes to add to pointer to get next pixel to the right */

       /* used by boolcalc* for mask data */
       uchar   *m;             /* ptr to mask data r.min byte; like p-&gt;bytermin */
       int             mskip;  /* no. of left bits to skip in *m */
       uchar   *bm;            /* ptr to mask data img-&gt;r.min byte; like p-&gt;bytey0s */
       int             bmskip; /* no. of left bits to skip in *bm */
       uchar   *em;            /* ptr to mask data img-&gt;r.max.x byte; like p-&gt;bytey0e */
       int             emskip; /* no. of right bits to skip in *em */
};

typedef struct  Param   Param;
typedef Buffer  Readfn(Param*, uchar*, int);
typedef void    Writefn(Param*, uchar*, Buffer);
typedef Buffer  Calcfn(Buffer, Buffer, Buffer, int, int, int);

enum {
       MAXBCACHE = 16
};

/* giant rathole to customize functions with */
struct Param {
       Readfn  *replcall;
       Readfn  *greymaskcall;
       Readfn  *convreadcall;
       Writefn *convwritecall;

       Memimage *img;
       Rectangle       r;
       int     dx;     /* of r */
       int     needbuf;
       int     convgrey;
       int     alphaonly;

       uchar   *bytey0s;               /* byteaddr(Pt(img-&gt;r.min.x, img-&gt;r.min.y)) */
       uchar   *bytermin;      /* byteaddr(Pt(r.min.x, img-&gt;r.min.y)) */
       uchar   *bytey0e;               /* byteaddr(Pt(img-&gt;r.max.x, img-&gt;r.min.y)) */
       int             bwidth;

       int     replcache;      /* if set, cache buffers */
       Buffer  bcache[MAXBCACHE];
       u32int  bfilled;
       uchar   *bufbase;
       int     bufoff;
       int     bufdelta;

       int     dir;

       int     convbufoff;
       uchar   *convbuf;
       Param   *convdpar;
       int     convdx;
};

static uchar *drawbuf;
static int      ndrawbuf;
static int      mdrawbuf;
static Param spar, mpar, dpar;  /* easier on the stacks */
static Readfn   greymaskread, replread, readptr;
static Writefn  nullwrite;
static Calcfn   alphacalc0, alphacalc14, alphacalc2810, alphacalc3679, alphacalc5, alphacalc11, alphacalcS;
static Calcfn   boolcalc14, boolcalc236789, boolcalc1011;

static Readfn*  readfn(Memimage*);
static Readfn*  readalphafn(Memimage*);
static Writefn* writefn(Memimage*);

static Calcfn*  boolcopyfn(Memimage*, Memimage*);
static Readfn*  convfn(Memimage*, Param*, Memimage*, Param*);

static Calcfn *alphacalc[Ncomp] =
{
       alphacalc0,             /* Clear */
       alphacalc14,            /* DoutS */
       alphacalc2810,          /* SoutD */
       alphacalc3679,          /* DxorS */
       alphacalc14,            /* DinS */
       alphacalc5,             /* D */
       alphacalc3679,          /* DatopS */
       alphacalc3679,          /* DoverS */
       alphacalc2810,          /* SinD */
       alphacalc3679,          /* SatopD */
       alphacalc2810,          /* S */
       alphacalc11,            /* SoverD */
};

static Calcfn *boolcalc[Ncomp] =
{
       alphacalc0,             /* Clear */
       boolcalc14,             /* DoutS */
       boolcalc236789,         /* SoutD */
       boolcalc236789,         /* DxorS */
       boolcalc14,             /* DinS */
       alphacalc5,             /* D */
       boolcalc236789,         /* DatopS */
       boolcalc236789,         /* DoverS */
       boolcalc236789,         /* SinD */
       boolcalc236789,         /* SatopD */
       boolcalc1011,           /* S */
       boolcalc1011,           /* SoverD */
};

static int
allocdrawbuf(void)
{
       uchar *p;

       if(ndrawbuf &gt; mdrawbuf){
               p = realloc(drawbuf, ndrawbuf);
               if(p == nil){
                       werrstr("memimagedraw out of memory");
                       return -1;
               }
               drawbuf = p;
               mdrawbuf = ndrawbuf;
       }
       return 0;
}

static void
getparam(Param *p, Memimage *img, Rectangle r, int convgrey, int needbuf)
{
       int nbuf;

       memset(p, 0, sizeof *p);

       p-&gt;img = img;
       p-&gt;r = r;
       p-&gt;dx = Dx(r);
       p-&gt;needbuf = needbuf;
       p-&gt;convgrey = convgrey;

       assert(img-&gt;r.min.x &lt;= r.min.x &amp;&amp; r.min.x &lt; img-&gt;r.max.x);

       p-&gt;bytey0s = byteaddr(img, Pt(img-&gt;r.min.x, img-&gt;r.min.y));
       p-&gt;bytermin = byteaddr(img, Pt(r.min.x, img-&gt;r.min.y));
       p-&gt;bytey0e = byteaddr(img, Pt(img-&gt;r.max.x, img-&gt;r.min.y));
       p-&gt;bwidth = sizeof(u32int)*img-&gt;width;

       assert(p-&gt;bytey0s &lt;= p-&gt;bytermin &amp;&amp; p-&gt;bytermin &lt;= p-&gt;bytey0e);

       if(p-&gt;r.min.x == p-&gt;img-&gt;r.min.x)
               assert(p-&gt;bytermin == p-&gt;bytey0s);

       nbuf = 1;
       if((img-&gt;flags&amp;Frepl) &amp;&amp; Dy(img-&gt;r) &lt;= MAXBCACHE &amp;&amp; Dy(img-&gt;r) &lt; Dy(r)){
               p-&gt;replcache = 1;
               nbuf = Dy(img-&gt;r);
       }
       p-&gt;bufdelta = 4*p-&gt;dx;
       p-&gt;bufoff = ndrawbuf;
       ndrawbuf += p-&gt;bufdelta*nbuf;
}

static void
clipy(Memimage *img, int *y)
{
       int dy;

       dy = Dy(img-&gt;r);
       if(*y == dy)
               *y = 0;
       else if(*y == -1)
               *y = dy-1;
       assert(0 &lt;= *y &amp;&amp; *y &lt; dy);
}

static void
dumpbuf(char *s, Buffer b, int n)
{
       int i;
       uchar *p;

       print("%s", s);
       for(i=0; i&lt;n; i++){
               print(" ");
               if(p=b.grey){
                       print(" k%.2uX", *p);
                       b.grey += b.delta;
               }else{
                       if(p=b.red){
                               print(" r%.2uX", *p);
                               b.red += b.delta;
                       }
                       if(p=b.grn){
                               print(" g%.2uX", *p);
                               b.grn += b.delta;
                       }
                       if(p=b.blu){
                               print(" b%.2uX", *p);
                               b.blu += b.delta;
                       }
               }
               if((p=b.alpha) != &amp;ones){
                       print(" α%.2uX", *p);
                       b.alpha += b.delta;
               }
       }
       print("\n");
}

/*
* For each scan line, we expand the pixels from source, mask, and destination
* into byte-aligned red, green, blue, alpha, and grey channels.  If buffering is not
* needed and the channels were already byte-aligned (grey8, rgb24, rgba32, rgb32),
* the readers need not copy the data: they can simply return pointers to the data.
* If the destination image is grey and the source is not, it is converted using the NTSC
* formula.
*
* Once we have all the channels, we call either rgbcalc or greycalc, depending on
* whether the destination image is color.  This is allowed to overwrite the dst buffer (perhaps
* the actual data, perhaps a copy) with its result.  It should only overwrite the dst buffer
* with the same format (i.e. red bytes with red bytes, etc.)  A new buffer is returned from
* the calculator, and that buffer is passed to a function to write it to the destination.
* If the buffer is already pointing at the destination, the writing function is a no-op.
*/
#define DBG if(drawdebug)
static int
alphadraw(Memdrawparam *par)
{
       int isgrey, starty, endy, op;
       int needbuf, dsty, srcy, masky;
       int y, dir, dx, dy;
       Buffer bsrc, bdst, bmask;
       Readfn *rdsrc, *rdmask, *rddst;
       Calcfn *calc;
       Writefn *wrdst;
       Memimage *src, *mask, *dst;
       Rectangle r, sr, mr;

       if(drawdebug)
               print("alphadraw %R\n", par-&gt;r);
       r = par-&gt;r;
       dx = Dx(r);
       dy = Dy(r);

       ndrawbuf = 0;

       src = par-&gt;src;
       mask = par-&gt;mask;
       dst = par-&gt;dst;
       sr = par-&gt;sr;
       mr = par-&gt;mr;
       op = par-&gt;op;

       isgrey = dst-&gt;flags&amp;Fgrey;

       /*
        * Buffering when src and dst are the same bitmap is sufficient but not
        * necessary.  There are stronger conditions we could use.  We could
        * check to see if the rectangles intersect, and if simply moving in the
        * correct y direction can avoid the need to buffer.
        */
       needbuf = (src-&gt;data == dst-&gt;data);

       getparam(&amp;spar, src, sr, isgrey, needbuf);
       getparam(&amp;dpar, dst, r, isgrey, needbuf);
       getparam(&amp;mpar, mask, mr, 0, needbuf);

       dir = (needbuf &amp;&amp; byteaddr(dst, r.min) &gt; byteaddr(src, sr.min)) ? -1 : 1;
       spar.dir = mpar.dir = dpar.dir = dir;

       /*
        * If the mask is purely boolean, we can convert from src to dst format
        * when we read src, and then just copy it to dst where the mask tells us to.
        * This requires a boolean (1-bit grey) mask and lack of a source alpha channel.
        *
        * The computation is accomplished by assigning the function pointers as follows:
        *      rdsrc - read and convert source into dst format in a buffer
        *      rdmask - convert mask to bytes, set pointer to it
        *      rddst - fill with pointer to real dst data, but do no reads
        *      calc - copy src onto dst when mask says to.
        *      wrdst - do nothing
        * This is slightly sleazy, since things aren't doing exactly what their names say,
        * but it avoids a fair amount of code duplication to make this a case here
        * rather than have a separate booldraw.
        */
/*if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src-&gt;flags&amp;Falpha, mask-&gt;chan, GREY1, dst-&gt;depth); */
       if(!(src-&gt;flags&amp;Falpha) &amp;&amp; mask-&gt;chan == GREY1 &amp;&amp; dst-&gt;depth &gt;= 8 &amp;&amp; op == SoverD){
/*if(drawdebug) iprint("boolcopy..."); */
               rdsrc = convfn(dst, &amp;dpar, src, &amp;spar);
               rddst = readptr;
               rdmask = readfn(mask);
               calc = boolcopyfn(dst, mask);
               wrdst = nullwrite;
       }else{
               /* usual alphadraw parameter fetching */
               rdsrc = readfn(src);
               rddst = readfn(dst);
               wrdst = writefn(dst);
               calc = alphacalc[op];

               /*
                * If there is no alpha channel, we'll ask for a grey channel
                * and pretend it is the alpha.
                */
               if(mask-&gt;flags&amp;Falpha){
                       rdmask = readalphafn(mask);
                       mpar.alphaonly = 1;
               }else{
                       mpar.greymaskcall = readfn(mask);
                       mpar.convgrey = 1;
                       rdmask = greymaskread;

                       /*
                        * Should really be above, but then boolcopyfns would have
                        * to deal with bit alignment, and I haven't written that.
                        *
                        * This is a common case for things like ellipse drawing.
                        * When there's no alpha involved and the mask is boolean,
                        * we can avoid all the division and multiplication.
                        */
                       if(mask-&gt;chan == GREY1 &amp;&amp; !(src-&gt;flags&amp;Falpha))
                               calc = boolcalc[op];
                       else if(op == SoverD &amp;&amp; !(src-&gt;flags&amp;Falpha))
                               calc = alphacalcS;
               }
       }

       /*
        * If the image has a small enough repl rectangle,
        * we can just read each line once and cache them.
        */
       if(spar.replcache){
               spar.replcall = rdsrc;
               rdsrc = replread;
       }
       if(mpar.replcache){
               mpar.replcall = rdmask;
               rdmask = replread;
       }

       if(allocdrawbuf() &lt; 0)
               return 0;

       /*
        * Before we were saving only offsets from drawbuf in the parameter
        * structures; now that drawbuf has been grown to accomodate us,
        * we can fill in the pointers.
        */
       spar.bufbase = drawbuf+spar.bufoff;
       mpar.bufbase = drawbuf+mpar.bufoff;
       dpar.bufbase = drawbuf+dpar.bufoff;
       spar.convbuf = drawbuf+spar.convbufoff;

       if(dir == 1){
               starty = 0;
               endy = dy;
       }else{
               starty = dy-1;
               endy = -1;
       }

       /*
        * srcy, masky, and dsty are offsets from the top of their
        * respective Rectangles.  they need to be contained within
        * the rectangles, so clipy can keep them there without division.
        */
       srcy = (starty + sr.min.y - src-&gt;r.min.y)%Dy(src-&gt;r);
       masky = (starty + mr.min.y - mask-&gt;r.min.y)%Dy(mask-&gt;r);
       dsty = starty + r.min.y - dst-&gt;r.min.y;

       assert(0 &lt;= srcy &amp;&amp; srcy &lt; Dy(src-&gt;r));
       assert(0 &lt;= masky &amp;&amp; masky &lt; Dy(mask-&gt;r));
       assert(0 &lt;= dsty &amp;&amp; dsty &lt; Dy(dst-&gt;r));

       if(drawdebug)
               print("alphadraw: rdsrc=%p rdmask=%p rddst=%p calc=%p wrdst=%p\n",
                       rdsrc, rdmask, rddst, calc, wrdst);
       for(y=starty; y!=endy; y+=dir, srcy+=dir, masky+=dir, dsty+=dir){
               clipy(src, &amp;srcy);
               clipy(dst, &amp;dsty);
               clipy(mask, &amp;masky);

               bsrc = rdsrc(&amp;spar, spar.bufbase, srcy);
DBG print("[");
               bmask = rdmask(&amp;mpar, mpar.bufbase, masky);
DBG print("]\n");
               bdst = rddst(&amp;dpar, dpar.bufbase, dsty);
DBG             dumpbuf("src", bsrc, dx);
DBG             dumpbuf("mask", bmask, dx);
DBG             dumpbuf("dst", bdst, dx);
               bdst = calc(bdst, bsrc, bmask, dx, isgrey, op);
DBG             dumpbuf("bdst", bdst, dx);
               wrdst(&amp;dpar, dpar.bytermin+dsty*dpar.bwidth, bdst);
       }

       return 1;
}
#undef DBG

static Buffer
alphacalc0(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op)
{
       USED(grey);
       USED(op);
       memset(bdst.rgba, 0, dx*bdst.delta);
       return bdst;
}

/*
* Do the channels in the buffers match enough
* that we can do word-at-a-time operations
* on the pixels?
*/
static int
chanmatch(Buffer *bdst, Buffer *bsrc)
{
       uchar *drgb, *srgb;

       /*
        * first, r, g, b must be in the same place
        * in the rgba word.
        */
       drgb = (uchar*)bdst-&gt;rgba;
       srgb = (uchar*)bsrc-&gt;rgba;
       if(bdst-&gt;red - drgb != bsrc-&gt;red - srgb
       || bdst-&gt;blu - drgb != bsrc-&gt;blu - srgb
       || bdst-&gt;grn - drgb != bsrc-&gt;grn - srgb)
               return 0;

       /*
        * that implies alpha is in the same place,
        * if it is there at all (it might be == &amp;ones).
        * if the destination is &amp;ones, we can scribble
        * over the rgba slot just fine.
        */
       if(bdst-&gt;alpha == &amp;ones)
               return 1;

       /*
        * if the destination is not ones but the src is,
        * then the simultaneous calculation will use
        * bogus bytes from the src's rgba.  no good.
        */
       if(bsrc-&gt;alpha == &amp;ones)
               return 0;

       /*
        * otherwise, alphas are in the same place.
        */
       return 1;
}

static Buffer
alphacalc14(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int fd, sadelta;
       int i, sa, ma, q;
       u32int t, t1;

       obdst = bdst;
       sadelta = bsrc.alpha == &amp;ones ? 0 : bsrc.delta;
       q = bsrc.delta == 4 &amp;&amp; bdst.delta == 4 &amp;&amp; chanmatch(&amp;bdst, &amp;bsrc);

       for(i=0; i&lt;dx; i++){
               sa = *bsrc.alpha;
               ma = *bmask.alpha;
               fd = CALC11(sa, ma, t);
               if(op == DoutS)
                       fd = 255-fd;

               if(grey){
                       *bdst.grey = CALC11(fd, *bdst.grey, t);
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       if(q){
                               *bdst.rgba = CALC41(fd, *bdst.rgba, t, t1);
                               bsrc.rgba++;
                               bdst.rgba++;
                               bsrc.alpha += sadelta;
                               bmask.alpha += bmask.delta;
                               continue;
                       }
                       *bdst.red = CALC11(fd, *bdst.red, t);
                       *bdst.grn = CALC11(fd, *bdst.grn, t);
                       *bdst.blu = CALC11(fd, *bdst.blu, t);
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               if(bdst.alpha != &amp;ones){
                       *bdst.alpha = CALC11(fd, *bdst.alpha, t);
                       bdst.alpha += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               bsrc.alpha += sadelta;
       }
       return obdst;
}

static Buffer
alphacalc2810(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int fs, sadelta;
       int i, ma, da, q;
       u32int t, t1;

       obdst = bdst;
       sadelta = bsrc.alpha == &amp;ones ? 0 : bsrc.delta;
       q = bsrc.delta == 4 &amp;&amp; bdst.delta == 4 &amp;&amp; chanmatch(&amp;bdst, &amp;bsrc);

       for(i=0; i&lt;dx; i++){
               ma = *bmask.alpha;
               da = *bdst.alpha;
               if(op == SoutD)
                       da = 255-da;
               fs = ma;
               if(op != S)
                       fs = CALC11(fs, da, t);

               if(grey){
                       *bdst.grey = CALC11(fs, *bsrc.grey, t);
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       if(q){
                               *bdst.rgba = CALC41(fs, *bsrc.rgba, t, t1);
                               bsrc.rgba++;
                               bdst.rgba++;
                               bmask.alpha += bmask.delta;
                               bdst.alpha += bdst.delta;
                               continue;
                       }
                       *bdst.red = CALC11(fs, *bsrc.red, t);
                       *bdst.grn = CALC11(fs, *bsrc.grn, t);
                       *bdst.blu = CALC11(fs, *bsrc.blu, t);
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               if(bdst.alpha != &amp;ones){
                       *bdst.alpha = CALC11(fs, *bsrc.alpha, t);
                       bdst.alpha += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               bsrc.alpha += sadelta;
       }
       return obdst;
}

static Buffer
alphacalc3679(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int fs, fd, sadelta;
       int i, sa, ma, da, q;
       u32int t, t1;

       obdst = bdst;
       sadelta = bsrc.alpha == &amp;ones ? 0 : bsrc.delta;
       q = bsrc.delta == 4 &amp;&amp; bdst.delta == 4 &amp;&amp; chanmatch(&amp;bdst, &amp;bsrc);

       for(i=0; i&lt;dx; i++){
               sa = *bsrc.alpha;
               ma = *bmask.alpha;
               da = *bdst.alpha;
               if(op == SatopD)
                       fs = CALC11(ma, da, t);
               else
                       fs = CALC11(ma, 255-da, t);
               if(op == DoverS)
                       fd = 255;
               else{
                       fd = CALC11(sa, ma, t);
                       if(op != DatopS)
                               fd = 255-fd;
               }

               if(grey){
                       *bdst.grey = CALC12(fs, *bsrc.grey, fd, *bdst.grey, t);
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       if(q){
                               *bdst.rgba = CALC42(fs, *bsrc.rgba, fd, *bdst.rgba, t, t1);
                               bsrc.rgba++;
                               bdst.rgba++;
                               bsrc.alpha += sadelta;
                               bmask.alpha += bmask.delta;
                               bdst.alpha += bdst.delta;
                               continue;
                       }
                       *bdst.red = CALC12(fs, *bsrc.red, fd, *bdst.red, t);
                       *bdst.grn = CALC12(fs, *bsrc.grn, fd, *bdst.grn, t);
                       *bdst.blu = CALC12(fs, *bsrc.blu, fd, *bdst.blu, t);
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               if(bdst.alpha != &amp;ones){
                       *bdst.alpha = CALC12(fs, sa, fd, da, t);
                       bdst.alpha += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               bsrc.alpha += sadelta;
       }
       return obdst;
}

static Buffer
alphacalc5(Buffer bdst, Buffer b1, Buffer b2, int dx, int grey, int op)
{
       USED(dx);
       USED(grey);
       USED(op);
       return bdst;
}

static Buffer
alphacalc11(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int fd, sadelta;
       int i, sa, ma, q;
       u32int t, t1;

       USED(op);
       obdst = bdst;
       sadelta = bsrc.alpha == &amp;ones ? 0 : bsrc.delta;
       q = bsrc.delta == 4 &amp;&amp; bdst.delta == 4 &amp;&amp; chanmatch(&amp;bdst, &amp;bsrc);

       for(i=0; i&lt;dx; i++){
               sa = *bsrc.alpha;
               ma = *bmask.alpha;
               fd = 255-CALC11(sa, ma, t);

               if(grey){
                       *bdst.grey = CALC12(ma, *bsrc.grey, fd, *bdst.grey, t);
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       if(q){
                               *bdst.rgba = CALC42(ma, *bsrc.rgba, fd, *bdst.rgba, t, t1);
                               bsrc.rgba++;
                               bdst.rgba++;
                               bsrc.alpha += sadelta;
                               bmask.alpha += bmask.delta;
                               continue;
                       }
                       *bdst.red = CALC12(ma, *bsrc.red, fd, *bdst.red, t);
                       *bdst.grn = CALC12(ma, *bsrc.grn, fd, *bdst.grn, t);
                       *bdst.blu = CALC12(ma, *bsrc.blu, fd, *bdst.blu, t);
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               if(bdst.alpha != &amp;ones){
                       *bdst.alpha = CALC12(ma, sa, fd, *bdst.alpha, t);
                       bdst.alpha += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               bsrc.alpha += sadelta;
       }
       return obdst;
}

/*
not used yet
source and mask alpha 1
static Buffer
alphacalcS0(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int i;

       USED(op);
       obdst = bdst;
       if(bsrc.delta == bdst.delta){
               memmove(bdst.rgba, bsrc.rgba, dx*bdst.delta);
               return obdst;
       }
       for(i=0; i&lt;dx; i++){
               if(grey){
                       *bdst.grey = *bsrc.grey;
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       *bdst.red = *bsrc.red;
                       *bdst.grn = *bsrc.grn;
                       *bdst.blu = *bsrc.blu;
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               if(bdst.alpha != &amp;ones){
                       *bdst.alpha = 255;
                       bdst.alpha += bdst.delta;
               }
       }
       return obdst;
}
*/

/* source alpha 1 */
static Buffer
alphacalcS(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int fd;
       int i, ma;
       u32int t;

       USED(op);
       obdst = bdst;

       for(i=0; i&lt;dx; i++){
               ma = *bmask.alpha;
               fd = 255-ma;

               if(grey){
                       *bdst.grey = CALC12(ma, *bsrc.grey, fd, *bdst.grey, t);
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       *bdst.red = CALC12(ma, *bsrc.red, fd, *bdst.red, t);
                       *bdst.grn = CALC12(ma, *bsrc.grn, fd, *bdst.grn, t);
                       *bdst.blu = CALC12(ma, *bsrc.blu, fd, *bdst.blu, t);
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               if(bdst.alpha != &amp;ones){
                       *bdst.alpha = ma+CALC11(fd, *bdst.alpha, t);
                       bdst.alpha += bdst.delta;
               }
               bmask.alpha += bmask.delta;
       }
       return obdst;
}

static Buffer
boolcalc14(Buffer bdst, Buffer b1, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int i, ma, zero;

       obdst = bdst;

       for(i=0; i&lt;dx; i++){
               ma = *bmask.alpha;
               zero = ma ? op == DoutS : op == DinS;

               if(grey){
                       if(zero)
                               *bdst.grey = 0;
                       bdst.grey += bdst.delta;
               }else{
                       if(zero)
                               *bdst.red = *bdst.grn = *bdst.blu = 0;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               if(bdst.alpha != &amp;ones){
                       if(zero)
                               *bdst.alpha = 0;
                       bdst.alpha += bdst.delta;
               }
       }
       return obdst;
}

static Buffer
boolcalc236789(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int fs, fd;
       int i, ma, da, zero;
       u32int t;

       obdst = bdst;
       zero = !(op&amp;1);

       for(i=0; i&lt;dx; i++){
               ma = *bmask.alpha;
               da = *bdst.alpha;
               fs = da;
               if(op&amp;2)
                       fs = 255-da;
               fd = 0;
               if(op&amp;4)
                       fd = 255;

               if(grey){
                       if(ma)
                               *bdst.grey = CALC12(fs, *bsrc.grey, fd, *bdst.grey, t);
                       else if(zero)
                               *bdst.grey = 0;
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       if(ma){
                               *bdst.red = CALC12(fs, *bsrc.red, fd, *bdst.red, t);
                               *bdst.grn = CALC12(fs, *bsrc.grn, fd, *bdst.grn, t);
                               *bdst.blu = CALC12(fs, *bsrc.blu, fd, *bdst.blu, t);
                       }
                       else if(zero)
                               *bdst.red = *bdst.grn = *bdst.blu = 0;
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               if(bdst.alpha != &amp;ones){
                       if(ma)
                               *bdst.alpha = fs+CALC11(fd, da, t);
                       else if(zero)
                               *bdst.alpha = 0;
                       bdst.alpha += bdst.delta;
               }
       }
       return obdst;
}

static Buffer
boolcalc1011(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
{
       Buffer obdst;
       int i, ma, zero;

       obdst = bdst;
       zero = !(op&amp;1);

       for(i=0; i&lt;dx; i++){
               ma = *bmask.alpha;

               if(grey){
                       if(ma)
                               *bdst.grey = *bsrc.grey;
                       else if(zero)
                               *bdst.grey = 0;
                       bsrc.grey += bsrc.delta;
                       bdst.grey += bdst.delta;
               }else{
                       if(ma){
                               *bdst.red = *bsrc.red;
                               *bdst.grn = *bsrc.grn;
                               *bdst.blu = *bsrc.blu;
                       }
                       else if(zero)
                               *bdst.red = *bdst.grn = *bdst.blu = 0;
                       bsrc.red += bsrc.delta;
                       bsrc.blu += bsrc.delta;
                       bsrc.grn += bsrc.delta;
                       bdst.red += bdst.delta;
                       bdst.blu += bdst.delta;
                       bdst.grn += bdst.delta;
               }
               bmask.alpha += bmask.delta;
               if(bdst.alpha != &amp;ones){
                       if(ma)
                               *bdst.alpha = 255;
                       else if(zero)
                               *bdst.alpha = 0;
                       bdst.alpha += bdst.delta;
               }
       }
       return obdst;
}
/*
* Replicated cached scan line read.  Call the function listed in the Param,
* but cache the result so that for replicated images we only do the work once.
*/
static Buffer
replread(Param *p, uchar *s, int y)
{
       Buffer *b;

       USED(s);
       b = &amp;p-&gt;bcache[y];
       if((p-&gt;bfilled &amp; (1&lt;&lt;y)) == 0){
               p-&gt;bfilled |= 1&lt;&lt;y;
               *b = p-&gt;replcall(p, p-&gt;bufbase+y*p-&gt;bufdelta, y);
       }
       return *b;
}

/*
* Alpha reading function that simply relabels the grey pointer.
*/
static Buffer
greymaskread(Param *p, uchar *buf, int y)
{
       Buffer b;

       b = p-&gt;greymaskcall(p, buf, y);
       b.alpha = b.grey;
       return b;
}

#define DBG if(0)
static Buffer
readnbit(Param *p, uchar *buf, int y)
{
       Buffer b;
       Memimage *img;
       uchar *repl, *r, *w, *ow, bits;
       int i, n, sh, depth, x, dx, npack, nbits;

       memset(&amp;b, 0, sizeof b);
       b.rgba = (u32int*)buf;
       b.grey = w = buf;
       b.red = b.blu = b.grn = w;
       b.alpha = &amp;ones;
       b.delta = 1;

       dx = p-&gt;dx;
       img = p-&gt;img;
       depth = img-&gt;depth;
       repl = &amp;replbit[depth][0];
       npack = 8/depth;
       sh = 8-depth;

       /* copy from p-&gt;r.min.x until end of repl rectangle */
       x = p-&gt;r.min.x;
       n = dx;
       if(n &gt; p-&gt;img-&gt;r.max.x - x)
               n = p-&gt;img-&gt;r.max.x - x;

       r = p-&gt;bytermin + y*p-&gt;bwidth;
DBG print("readnbit dx %d %p=%p+%d*%d, *r=%d fetch %d ", dx, r, p-&gt;bytermin, y, p-&gt;bwidth, *r, n);
       bits = *r++;
       nbits = 8;
       if(i=x&amp;(npack-1)){
DBG print("throwaway %d...", i);
               bits &lt;&lt;= depth*i;
               nbits -= depth*i;
       }
       for(i=0; i&lt;n; i++){
               if(nbits == 0){
DBG print("(%.2ux)...", *r);
                       bits = *r++;
                       nbits = 8;
               }
               *w++ = repl[bits&gt;&gt;sh];
DBG print("bit %x...", repl[bits&gt;&gt;sh]);
               bits &lt;&lt;= depth;
               nbits -= depth;
       }
       dx -= n;
       if(dx == 0)
               return b;

       assert(x+i == p-&gt;img-&gt;r.max.x);

       /* copy from beginning of repl rectangle until where we were before. */
       x = p-&gt;img-&gt;r.min.x;
       n = dx;
       if(n &gt; p-&gt;r.min.x - x)
               n = p-&gt;r.min.x - x;

       r = p-&gt;bytey0s + y*p-&gt;bwidth;
DBG print("x=%d r=%p...", x, r);
       bits = *r++;
       nbits = 8;
       if(i=x&amp;(npack-1)){
               bits &lt;&lt;= depth*i;
               nbits -= depth*i;
       }
DBG print("nbits=%d...", nbits);
       for(i=0; i&lt;n; i++){
               if(nbits == 0){
                       bits = *r++;
                       nbits = 8;
               }
               *w++ = repl[bits&gt;&gt;sh];
DBG print("bit %x...", repl[bits&gt;&gt;sh]);
               bits &lt;&lt;= depth;
               nbits -= depth;
DBG print("bits %x nbits %d...", bits, nbits);
       }
       dx -= n;
       if(dx == 0)
               return b;

       assert(dx &gt; 0);
       /* now we have exactly one full scan line: just replicate the buffer itself until we are done */
       ow = buf;
       while(dx--)
               *w++ = *ow++;

       return b;
}
#undef DBG

#define DBG if(0)
static void
writenbit(Param *p, uchar *w, Buffer src)
{
       uchar *r;
       u32int bits;
       int i, sh, depth, npack, nbits, x, ex;

       assert(src.grey != nil &amp;&amp; src.delta == 1);

       x = p-&gt;r.min.x;
       ex = x+p-&gt;dx;
       depth = p-&gt;img-&gt;depth;
       npack = 8/depth;

       i=x&amp;(npack-1);
       bits = i ? (*w &gt;&gt; (8-depth*i)) : 0;
       nbits = depth*i;
       sh = 8-depth;
       r = src.grey;

       for(; x&lt;ex; x++){
               bits &lt;&lt;= depth;
DBG print(" %x", *r);
               bits |= (*r++ &gt;&gt; sh);
               nbits += depth;
               if(nbits == 8){
                       *w++ = bits;
                       nbits = 0;
               }
       }

       if(nbits){
               sh = 8-nbits;
               bits &lt;&lt;= sh;
               bits |= *w &amp; ((1&lt;&lt;sh)-1);
               *w = bits;
       }
DBG print("\n");
       return;
}
#undef DBG

static Buffer
readcmap(Param *p, uchar *buf, int y)
{
       Buffer b;
       int a, convgrey, copyalpha, dx, i, m;
       uchar *q, *cmap, *begin, *end, *r, *w;

       memset(&amp;b, 0, sizeof b);
       begin = p-&gt;bytey0s + y*p-&gt;bwidth;
       r = p-&gt;bytermin + y*p-&gt;bwidth;
       end = p-&gt;bytey0e + y*p-&gt;bwidth;
       cmap = p-&gt;img-&gt;cmap-&gt;cmap2rgb;
       convgrey = p-&gt;convgrey;
       copyalpha = (p-&gt;img-&gt;flags&amp;Falpha) ? 1 : 0;

       w = buf;
       dx = p-&gt;dx;
       if(copyalpha){
               b.alpha = buf++;
               a = p-&gt;img-&gt;shift[CAlpha]/8;
               m = p-&gt;img-&gt;shift[CMap]/8;
               for(i=0; i&lt;dx; i++){
                       *w++ = r[a];
                       q = cmap+r[m]*3;
                       r += 2;
                       if(r == end)
                               r = begin;
                       if(convgrey){
                               *w++ = RGB2K(q[0], q[1], q[2]);
                       }else{
                               *w++ = q[2];    /* blue */
                               *w++ = q[1];    /* green */
                               *w++ = q[0];    /* red */
                       }
               }
       }else{
               b.alpha = &amp;ones;
               for(i=0; i&lt;dx; i++){
                       q = cmap+*r++*3;
                       if(r == end)
                               r = begin;
                       if(convgrey){
                               *w++ = RGB2K(q[0], q[1], q[2]);
                       }else{
                               *w++ = q[2];    /* blue */
                               *w++ = q[1];    /* green */
                               *w++ = q[0];    /* red */
                       }
               }
       }

       b.rgba = (u32int*)(buf-copyalpha);

       if(convgrey){
               b.grey = buf;
               b.red = b.blu = b.grn = buf;
               b.delta = 1+copyalpha;
       }else{
               b.blu = buf;
               b.grn = buf+1;
               b.red = buf+2;
               b.grey = nil;
               b.delta = 3+copyalpha;
       }
       return b;
}

static void
writecmap(Param *p, uchar *w, Buffer src)
{
       uchar *cmap, *red, *grn, *blu;
       int i, dx, delta;

       cmap = p-&gt;img-&gt;cmap-&gt;rgb2cmap;

       delta = src.delta;
       red= src.red;
       grn = src.grn;
       blu = src.blu;

       dx = p-&gt;dx;
       for(i=0; i&lt;dx; i++, red+=delta, grn+=delta, blu+=delta)
               *w++ = cmap[(*red&gt;&gt;4)*256+(*grn&gt;&gt;4)*16+(*blu&gt;&gt;4)];
}

#define DBG if(drawdebug)
static Buffer
readbyte(Param *p, uchar *buf, int y)
{
       Buffer b;
       Memimage *img;
       int dx, isgrey, convgrey, alphaonly, copyalpha, i, nb;
       uchar *begin, *end, *r, *w, *rrepl, *grepl, *brepl, *arepl, *krepl;
       uchar ured, ugrn, ublu;
       u32int u;

       img = p-&gt;img;
       begin = p-&gt;bytey0s + y*p-&gt;bwidth;
       r = p-&gt;bytermin + y*p-&gt;bwidth;
       end = p-&gt;bytey0e + y*p-&gt;bwidth;

       w = buf;
       dx = p-&gt;dx;
       nb = img-&gt;depth/8;

       convgrey = p-&gt;convgrey;      /* convert rgb to grey */
       isgrey = img-&gt;flags&amp;Fgrey;
       alphaonly = p-&gt;alphaonly;
       copyalpha = (img-&gt;flags&amp;Falpha) ? 1 : 0;

       /* if we can, avoid processing everything */
       if(!(img-&gt;flags&amp;Frepl) &amp;&amp; !convgrey &amp;&amp; (img-&gt;flags&amp;Fbytes)){
               memset(&amp;b, 0, sizeof b);
               if(p-&gt;needbuf){
                       memmove(buf, r, dx*nb);
                       r = buf;
               }
               b.rgba = (u32int*)r;
               if(copyalpha)
                       b.alpha = r+img-&gt;shift[CAlpha]/8;
               else
                       b.alpha = &amp;ones;
               if(isgrey){
                       b.grey = r+img-&gt;shift[CGrey]/8;
                       b.red = b.grn = b.blu = b.grey;
               }else{
                       b.red = r+img-&gt;shift[CRed]/8;
                       b.grn = r+img-&gt;shift[CGreen]/8;
                       b.blu = r+img-&gt;shift[CBlue]/8;
               }
               b.delta = nb;
               return b;
       }

       rrepl = replbit[img-&gt;nbits[CRed]];
       grepl = replbit[img-&gt;nbits[CGreen]];
       brepl = replbit[img-&gt;nbits[CBlue]];
       arepl = replbit[img-&gt;nbits[CAlpha]];
       krepl = replbit[img-&gt;nbits[CGrey]];

       for(i=0; i&lt;dx; i++){
               u = r[0] | (r[1]&lt;&lt;8) | (r[2]&lt;&lt;16) | (r[3]&lt;&lt;24);
               if(copyalpha)
                       *w++ = arepl[(u&gt;&gt;img-&gt;shift[CAlpha]) &amp; img-&gt;mask[CAlpha]];

               if(isgrey)
                       *w++ = krepl[(u &gt;&gt; img-&gt;shift[CGrey]) &amp; img-&gt;mask[CGrey]];
               else if(!alphaonly){
                       ured = rrepl[(u &gt;&gt; img-&gt;shift[CRed]) &amp; img-&gt;mask[CRed]];
                       ugrn = grepl[(u &gt;&gt; img-&gt;shift[CGreen]) &amp; img-&gt;mask[CGreen]];
                       ublu = brepl[(u &gt;&gt; img-&gt;shift[CBlue]) &amp; img-&gt;mask[CBlue]];
                       if(convgrey){
                               *w++ = RGB2K(ured, ugrn, ublu);
                       }else{
                               *w++ = brepl[(u &gt;&gt; img-&gt;shift[CBlue]) &amp; img-&gt;mask[CBlue]];
                               *w++ = grepl[(u &gt;&gt; img-&gt;shift[CGreen]) &amp; img-&gt;mask[CGreen]];
                               *w++ = rrepl[(u &gt;&gt; img-&gt;shift[CRed]) &amp; img-&gt;mask[CRed]];
                       }
               }
               r += nb;
               if(r == end)
                       r = begin;
       }

       b.alpha = copyalpha ? buf : &amp;ones;
       b.rgba = (u32int*)buf;
       if(alphaonly){
               b.red = b.grn = b.blu = b.grey = nil;
               if(!copyalpha)
                       b.rgba = nil;
               b.delta = 1;
       }else if(isgrey || convgrey){
               b.grey = buf+copyalpha;
               b.red = b.grn = b.blu = buf+copyalpha;
               b.delta = copyalpha+1;
       }else{
               b.blu = buf+copyalpha;
               b.grn = buf+copyalpha+1;
               b.grey = nil;
               b.red = buf+copyalpha+2;
               b.delta = copyalpha+3;
       }
       return b;
}
#undef DBG

#define DBG if(drawdebug)
static void
writebyte(Param *p, uchar *w, Buffer src)
{
       Memimage *img;
       int i, isalpha, isgrey, nb, delta, dx, adelta;
       uchar ff, *red, *grn, *blu, *grey, *alpha;
       u32int u, mask;

       img = p-&gt;img;

       red = src.red;
       grn = src.grn;
       blu = src.blu;
       alpha = src.alpha;
       delta = src.delta;
       grey = src.grey;
       dx = p-&gt;dx;

       nb = img-&gt;depth/8;
       mask = (nb==4) ? 0 : ~((1&lt;&lt;img-&gt;depth)-1);

       isalpha = img-&gt;flags&amp;Falpha;
       isgrey = img-&gt;flags&amp;Fgrey;
       adelta = src.delta;

       if(isalpha &amp;&amp; (alpha == nil || alpha == &amp;ones)){
               ff = 0xFF;
               alpha = &amp;ff;
               adelta = 0;
       }

       for(i=0; i&lt;dx; i++){
               u = w[0] | (w[1]&lt;&lt;8) | (w[2]&lt;&lt;16) | (w[3]&lt;&lt;24);
DBG print("u %.8lux...", u);
               u &amp;= mask;
DBG print("&amp;mask %.8lux...", u);
               if(isgrey){
                       u |= ((*grey &gt;&gt; (8-img-&gt;nbits[CGrey])) &amp; img-&gt;mask[CGrey]) &lt;&lt; img-&gt;shift[CGrey];
DBG print("|grey %.8lux...", u);
                       grey += delta;
               }else{
                       u |= ((*red &gt;&gt; (8-img-&gt;nbits[CRed])) &amp; img-&gt;mask[CRed]) &lt;&lt; img-&gt;shift[CRed];
                       u |= ((*grn &gt;&gt; (8-img-&gt;nbits[CGreen])) &amp; img-&gt;mask[CGreen]) &lt;&lt; img-&gt;shift[CGreen];
                       u |= ((*blu &gt;&gt; (8-img-&gt;nbits[CBlue])) &amp; img-&gt;mask[CBlue]) &lt;&lt; img-&gt;shift[CBlue];
                       red += delta;
                       grn += delta;
                       blu += delta;
DBG print("|rgb %.8lux...", u);
               }

               if(isalpha){
                       u |= ((*alpha &gt;&gt; (8-img-&gt;nbits[CAlpha])) &amp; img-&gt;mask[CAlpha]) &lt;&lt; img-&gt;shift[CAlpha];
                       alpha += adelta;
DBG print("|alpha %.8lux...", u);
               }

               w[0] = u;
               w[1] = u&gt;&gt;8;
               w[2] = u&gt;&gt;16;
               w[3] = u&gt;&gt;24;
DBG print("write back %.8lux...", u);
               w += nb;
       }
}
#undef DBG

static Readfn*
readfn(Memimage *img)
{
       if(img-&gt;depth &lt; 8)
               return readnbit;
       if(img-&gt;nbits[CMap] == 8)
               return readcmap;
       return readbyte;
}

static Readfn*
readalphafn(Memimage *m)
{
       USED(m);
       return readbyte;
}

static Writefn*
writefn(Memimage *img)
{
       if(img-&gt;depth &lt; 8)
               return writenbit;
       if(img-&gt;chan == CMAP8)
               return writecmap;
       return writebyte;
}

static void
nullwrite(Param *p, uchar *s, Buffer b)
{
       USED(p);
       USED(s);
}

static Buffer
readptr(Param *p, uchar *s, int y)
{
       Buffer b;
       uchar *q;

       USED(s);
       memset(&amp;b, 0, sizeof b);
       q = p-&gt;bytermin + y*p-&gt;bwidth;
       b.red = q;      /* ptr to data */
       b.grn = b.blu = b.grey = b.alpha = nil;
       b.rgba = (u32int*)q;
       b.delta = p-&gt;img-&gt;depth/8;
       return b;
}

static Buffer
boolmemmove(Buffer bdst, Buffer bsrc, Buffer b1, int dx, int i, int o)
{
       USED(i);
       USED(o);
       memmove(bdst.red, bsrc.red, dx*bdst.delta);
       return bdst;
}

static Buffer
boolcopy8(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
{
       uchar *m, *r, *w, *ew;

       USED(i);
       USED(o);
       m = bmask.grey;
       w = bdst.red;
       r = bsrc.red;
       ew = w+dx;
       for(; w &lt; ew; w++,r++)
               if(*m++)
                       *w = *r;
       return bdst;    /* not used */
}

static Buffer
boolcopy16(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
{
       uchar *m;
       ushort *r, *w, *ew;

       USED(i);
       USED(o);
       m = bmask.grey;
       w = (ushort*)bdst.red;
       r = (ushort*)bsrc.red;
       ew = w+dx;
       for(; w &lt; ew; w++,r++)
               if(*m++)
                       *w = *r;
       return bdst;    /* not used */
}

static Buffer
boolcopy24(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
{
       uchar *m;
       uchar *r, *w, *ew;

       USED(i);
       USED(o);
       m = bmask.grey;
       w = bdst.red;
       r = bsrc.red;
       ew = w+dx*3;
       while(w &lt; ew){
               if(*m++){
                       *w++ = *r++;
                       *w++ = *r++;
                       *w++ = *r++;
               }else{
                       w += 3;
                       r += 3;
               }
       }
       return bdst;    /* not used */
}

static Buffer
boolcopy32(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int i, int o)
{
       uchar *m;
       u32int *r, *w, *ew;

       USED(i);
       USED(o);
       m = bmask.grey;
       w = (u32int*)bdst.red;
       r = (u32int*)bsrc.red;
       ew = w+dx;
       for(; w &lt; ew; w++,r++)
               if(*m++)
                       *w = *r;
       return bdst;    /* not used */
}

static Buffer
genconv(Param *p, uchar *buf, int y)
{
       Buffer b;
       int nb;
       uchar *r, *w, *ew;

       /* read from source into RGB format in convbuf */
       b = p-&gt;convreadcall(p, p-&gt;convbuf, y);

       /* write RGB format into dst format in buf */
       p-&gt;convwritecall(p-&gt;convdpar, buf, b);

       if(p-&gt;convdx){
               nb = p-&gt;convdpar-&gt;img-&gt;depth/8;
               r = buf;
               w = buf+nb*p-&gt;dx;
               ew = buf+nb*p-&gt;convdx;
               while(w&lt;ew)
                       *w++ = *r++;
       }

       b.red = buf;
       b.blu = b.grn = b.grey = b.alpha = nil;
       b.rgba = (u32int*)buf;
       b.delta = 0;

       return b;
}

static Readfn*
convfn(Memimage *dst, Param *dpar, Memimage *src, Param *spar)
{
       if(dst-&gt;chan == src-&gt;chan &amp;&amp; !(src-&gt;flags&amp;Frepl)){
/*if(drawdebug) iprint("readptr..."); */
               return readptr;
       }

       if(dst-&gt;chan==CMAP8 &amp;&amp; (src-&gt;chan==GREY1||src-&gt;chan==GREY2||src-&gt;chan==GREY4)){
               /* cheat because we know the replicated value is exactly the color map entry. */
/*if(drawdebug) iprint("Readnbit..."); */
               return readnbit;
       }

       spar-&gt;convreadcall = readfn(src);
       spar-&gt;convwritecall = writefn(dst);
       spar-&gt;convdpar = dpar;

       /* allocate a conversion buffer */
       spar-&gt;convbufoff = ndrawbuf;
       ndrawbuf += spar-&gt;dx*4;

       if(spar-&gt;dx &gt; Dx(spar-&gt;img-&gt;r)){
               spar-&gt;convdx = spar-&gt;dx;
               spar-&gt;dx = Dx(spar-&gt;img-&gt;r);
       }

/*if(drawdebug) iprint("genconv..."); */
       return genconv;
}

/*
* Do NOT call this directly.  pixelbits is a wrapper
* around this that fetches the bits from the X server
* when necessary.
*/
u32int
_pixelbits(Memimage *i, Point pt)
{
       uchar *p;
       u32int val;
       int off, bpp, npack;

       val = 0;
       p = byteaddr(i, pt);
       switch(bpp=i-&gt;depth){
       case 1:
       case 2:
       case 4:
               npack = 8/bpp;
               off = pt.x%npack;
               val = p[0] &gt;&gt; bpp*(npack-1-off);
               val &amp;= (1&lt;&lt;bpp)-1;
               break;
       case 8:
               val = p[0];
               break;
       case 16:
               val = p[0]|(p[1]&lt;&lt;8);
               break;
       case 24:
               val = p[0]|(p[1]&lt;&lt;8)|(p[2]&lt;&lt;16);
               break;
       case 32:
               val = p[0]|(p[1]&lt;&lt;8)|(p[2]&lt;&lt;16)|(p[3]&lt;&lt;24);
               break;
       }
       while(bpp&lt;32){
               val |= val&lt;&lt;bpp;
               bpp *= 2;
       }
       return val;
}

static Calcfn*
boolcopyfn(Memimage *img, Memimage *mask)
{
       if(mask-&gt;flags&amp;Frepl &amp;&amp; Dx(mask-&gt;r)==1 &amp;&amp; Dy(mask-&gt;r)==1 &amp;&amp; pixelbits(mask, mask-&gt;r.min)==~0)
               return boolmemmove;

       switch(img-&gt;depth){
       case 8:
               return boolcopy8;
       case 16:
               return boolcopy16;
       case 24:
               return boolcopy24;
       case 32:
               return boolcopy32;
       default:
               assert(0 /* boolcopyfn */);
       }
       return 0;
}

/*
* Optimized draw for filling and scrolling; uses memset and memmove.
*/
static void
memsets(void *vp, ushort val, int n)
{
       ushort *p, *ep;

       p = vp;
       ep = p+n;
       while(p&lt;ep)
               *p++ = val;
}

static void
memsetl(void *vp, u32int val, int n)
{
       u32int *p, *ep;

       p = vp;
       ep = p+n;
       while(p&lt;ep)
               *p++ = val;
}

static void
memset24(void *vp, u32int val, int n)
{
       uchar *p, *ep;
       uchar a,b,c;

       p = vp;
       ep = p+3*n;
       a = val;
       b = val&gt;&gt;8;
       c = val&gt;&gt;16;
       while(p&lt;ep){
               *p++ = a;
               *p++ = b;
               *p++ = c;
       }
}

u32int
_imgtorgba(Memimage *img, u32int val)
{
       uchar r, g, b, a;
       int nb, ov, v;
       u32int chan;
       uchar *p;

       a = 0xFF;
       r = g = b = 0xAA;       /* garbage */
       for(chan=img-&gt;chan; chan; chan&gt;&gt;=8){
               nb = NBITS(chan);
               ov = v = val&amp;((1&lt;&lt;nb)-1);
               val &gt;&gt;= nb;

               while(nb &lt; 8){
                       v |= v&lt;&lt;nb;
                       nb *= 2;
               }
               v &gt;&gt;= (nb-8);

               switch(TYPE(chan)){
               case CRed:
                       r = v;
                       break;
               case CGreen:
                       g = v;
                       break;
               case CBlue:
                       b = v;
                       break;
               case CAlpha:
                       a = v;
                       break;
               case CGrey:
                       r = g = b = v;
                       break;
               case CMap:
                       p = img-&gt;cmap-&gt;cmap2rgb+3*ov;
                       r = *p++;
                       g = *p++;
                       b = *p;
                       break;
               }
       }
       return (r&lt;&lt;24)|(g&lt;&lt;16)|(b&lt;&lt;8)|a;
}

u32int
_rgbatoimg(Memimage *img, u32int rgba)
{
       u32int chan;
       int d, nb;
       u32int v;
       uchar *p, r, g, b, a, m;

       v = 0;
       r = rgba&gt;&gt;24;
       g = rgba&gt;&gt;16;
       b = rgba&gt;&gt;8;
       a = rgba;
       d = 0;
       for(chan=img-&gt;chan; chan; chan&gt;&gt;=8){
               nb = NBITS(chan);
               switch(TYPE(chan)){
               case CRed:
                       v |= (r&gt;&gt;(8-nb))&lt;&lt;d;
                       break;
               case CGreen:
                       v |= (g&gt;&gt;(8-nb))&lt;&lt;d;
                       break;
               case CBlue:
                       v |= (b&gt;&gt;(8-nb))&lt;&lt;d;
                       break;
               case CAlpha:
                       v |= (a&gt;&gt;(8-nb))&lt;&lt;d;
                       break;
               case CMap:
                       p = img-&gt;cmap-&gt;rgb2cmap;
                       m = p[(r&gt;&gt;4)*256+(g&gt;&gt;4)*16+(b&gt;&gt;4)];
                       v |= (m&gt;&gt;(8-nb))&lt;&lt;d;
                       break;
               case CGrey:
                       m = RGB2K(r,g,b);
                       v |= (m&gt;&gt;(8-nb))&lt;&lt;d;
                       break;
               }
               d += nb;
       }
/*      print("rgba2img %.8lux = %.*lux\n", rgba, 2*d/8, v); */
       return v;
}

#define DBG if(0)
static int
memoptdraw(Memdrawparam *par)
{
       int m, y, dy, dx, op;
       u32int v;
       Memimage *src;
       Memimage *dst;

       dx = Dx(par-&gt;r);
       dy = Dy(par-&gt;r);
       src = par-&gt;src;
       dst = par-&gt;dst;
       op = par-&gt;op;

DBG print("state %lux mval %lux dd %d\n", par-&gt;state, par-&gt;mval, dst-&gt;depth);
       /*
        * If we have an opaque mask and source is one opaque pixel we can convert to the
        * destination format and just replicate with memset.
        */
       m = Simplesrc|Simplemask|Fullmask;
       if((par-&gt;state&amp;m)==m &amp;&amp; (par-&gt;srgba&amp;0xFF) == 0xFF &amp;&amp; (op ==S || op == SoverD)){
               uchar *dp, p[4];
               int d, dwid, ppb, np, nb;
               uchar lm, rm;

DBG print("memopt, dst %p, dst-&gt;data-&gt;bdata %p\n", dst, dst-&gt;data-&gt;bdata);
               dwid = dst-&gt;width*sizeof(u32int);
               dp = byteaddr(dst, par-&gt;r.min);
               v = par-&gt;sdval;
DBG print("sdval %lud, depth %d\n", v, dst-&gt;depth);
               switch(dst-&gt;depth){
               case 1:
               case 2:
               case 4:
                       for(d=dst-&gt;depth; d&lt;8; d*=2)
                               v |= (v&lt;&lt;d);
                       ppb = 8/dst-&gt;depth;  /* pixels per byte */
                       m = ppb-1;
                       /* left edge */
                       np = par-&gt;r.min.x&amp;m;             /* no. pixels unused on left side of word */
                       dx -= (ppb-np);
                       nb = 8 - np * dst-&gt;depth;            /* no. bits used on right side of word */
                       lm = (1&lt;&lt;nb)-1;
DBG print("np %d x %d nb %d lm %ux ppb %d m %ux\n", np, par-&gt;r.min.x, nb, lm, ppb, m);

                       /* right edge */
                       np = par-&gt;r.max.x&amp;m;     /* no. pixels used on left side of word */
                       dx -= np;
                       nb = 8 - np * dst-&gt;depth;            /* no. bits unused on right side of word */
                       rm = ~((1&lt;&lt;nb)-1);
DBG print("np %d x %d nb %d rm %ux ppb %d m %ux\n", np, par-&gt;r.max.x, nb, rm, ppb, m);

DBG print("dx %d Dx %d\n", dx, Dx(par-&gt;r));
                       /* lm, rm are masks that are 1 where we should touch the bits */
                       if(dx &lt; 0){  /* just one byte */
                               lm &amp;= rm;
                               for(y=0; y&lt;dy; y++, dp+=dwid)
                                       *dp ^= (v ^ *dp) &amp; lm;
                       }else if(dx == 0){      /* no full bytes */
                               if(lm)
                                       dwid--;

                               for(y=0; y&lt;dy; y++, dp+=dwid){
                                       if(lm){
DBG print("dp %p v %lux lm %ux (v ^ *dp) &amp; lm %lux\n", dp, v, lm, (v^*dp)&amp;lm);
                                               *dp ^= (v ^ *dp) &amp; lm;
                                               dp++;
                                       }
                                       *dp ^= (v ^ *dp) &amp; rm;
                               }
                       }else{          /* full bytes in middle */
                               dx /= ppb;
                               if(lm)
                                       dwid--;
                               dwid -= dx;

                               for(y=0; y&lt;dy; y++, dp+=dwid){
                                       if(lm){
                                               *dp ^= (v ^ *dp) &amp; lm;
                                               dp++;
                                       }
                                       memset(dp, v, dx);
                                       dp += dx;
                                       *dp ^= (v ^ *dp) &amp; rm;
                               }
                       }
                       return 1;
               case 8:
                       for(y=0; y&lt;dy; y++, dp+=dwid)
                               memset(dp, v, dx);
                       return 1;
               case 16:
                       p[0] = v;               /* make little endian */
                       p[1] = v&gt;&gt;8;
                       v = *(ushort*)p;
DBG print("dp=%p; dx=%d; for(y=0; y&lt;%d; y++, dp+=%d)\nmemsets(dp, v, dx);\n",
       dp, dx, dy, dwid);
                       for(y=0; y&lt;dy; y++, dp+=dwid)
                               memsets(dp, v, dx);
                       return 1;
               case 24:
                       for(y=0; y&lt;dy; y++, dp+=dwid)
                               memset24(dp, v, dx);
                       return 1;
               case 32:
                       p[0] = v;               /* make little endian */
                       p[1] = v&gt;&gt;8;
                       p[2] = v&gt;&gt;16;
                       p[3] = v&gt;&gt;24;
                       v = *(u32int*)p;
                       for(y=0; y&lt;dy; y++, dp+=dwid)
                               memsetl(dp, v, dx);
                       return 1;
               default:
                       assert(0 /* bad dest depth in memoptdraw */);
               }
       }

       /*
        * If no source alpha, an opaque mask, we can just copy the
        * source onto the destination.  If the channels are the same and
        * the source is not replicated, memmove suffices.
        */
       m = Simplemask|Fullmask;
       if((par-&gt;state&amp;(m|Replsrc))==m &amp;&amp; src-&gt;depth &gt;= 8
       &amp;&amp; src-&gt;chan == dst-&gt;chan &amp;&amp; !(src-&gt;flags&amp;Falpha) &amp;&amp; (op == S || op == SoverD)){
               uchar *sp, *dp;
               long swid, dwid, nb;
               int dir;

               if(src-&gt;data == dst-&gt;data &amp;&amp; byteaddr(dst, par-&gt;r.min) &gt; byteaddr(src, par-&gt;sr.min))
                       dir = -1;
               else
                       dir = 1;

               swid = src-&gt;width*sizeof(u32int);
               dwid = dst-&gt;width*sizeof(u32int);
               sp = byteaddr(src, par-&gt;sr.min);
               dp = byteaddr(dst, par-&gt;r.min);
               if(dir == -1){
                       sp += (dy-1)*swid;
                       dp += (dy-1)*dwid;
                       swid = -swid;
                       dwid = -dwid;
               }
               nb = (dx*src-&gt;depth)/8;
               for(y=0; y&lt;dy; y++, sp+=swid, dp+=dwid)
                       memmove(dp, sp, nb);
               return 1;
       }

       /*
        * If we have a 1-bit mask, 1-bit source, and 1-bit destination, and
        * they're all bit aligned, we can just use bit operators.  This happens
        * when we're manipulating boolean masks, e.g. in the arc code.
        */
       if((par-&gt;state&amp;(Simplemask|Simplesrc|Replmask|Replsrc))==0
       &amp;&amp; dst-&gt;chan==GREY1 &amp;&amp; src-&gt;chan==GREY1 &amp;&amp; par-&gt;mask-&gt;chan==GREY1
       &amp;&amp; (par-&gt;r.min.x&amp;7)==(par-&gt;sr.min.x&amp;7) &amp;&amp; (par-&gt;r.min.x&amp;7)==(par-&gt;mr.min.x&amp;7)){
               uchar *sp, *dp, *mp;
               uchar lm, rm;
               long swid, dwid, mwid;
               int i, x, dir;

               sp = byteaddr(src, par-&gt;sr.min);
               dp = byteaddr(dst, par-&gt;r.min);
               mp = byteaddr(par-&gt;mask, par-&gt;mr.min);
               swid = src-&gt;width*sizeof(u32int);
               dwid = dst-&gt;width*sizeof(u32int);
               mwid = par-&gt;mask-&gt;width*sizeof(u32int);

               if(src-&gt;data == dst-&gt;data &amp;&amp; byteaddr(dst, par-&gt;r.min) &gt; byteaddr(src, par-&gt;sr.min)){
                       dir = -1;
               }else
                       dir = 1;

               lm = 0xFF&gt;&gt;(par-&gt;r.min.x&amp;7);
               rm = 0xFF&lt;&lt;(8-(par-&gt;r.max.x&amp;7));
               dx -= (8-(par-&gt;r.min.x&amp;7)) + (par-&gt;r.max.x&amp;7);

               if(dx &lt; 0){  /* one byte wide */
                       lm &amp;= rm;
                       if(dir == -1){
                               dp += dwid*(dy-1);
                               sp += swid*(dy-1);
                               mp += mwid*(dy-1);
                               dwid = -dwid;
                               swid = -swid;
                               mwid = -mwid;
                       }
                       for(y=0; y&lt;dy; y++){
                               *dp ^= (*dp ^ *sp) &amp; *mp &amp; lm;
                               dp += dwid;
                               sp += swid;
                               mp += mwid;
                       }
                       return 1;
               }

               dx /= 8;
               if(dir == 1){
                       i = (lm!=0)+dx+(rm!=0);
                       mwid -= i;
                       swid -= i;
                       dwid -= i;
                       for(y=0; y&lt;dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
                               if(lm){
                                       *dp ^= (*dp ^ *sp++) &amp; *mp++ &amp; lm;
                                       dp++;
                               }
                               for(x=0; x&lt;dx; x++){
                                       *dp ^= (*dp ^ *sp++) &amp; *mp++;
                                       dp++;
                               }
                               if(rm){
                                       *dp ^= (*dp ^ *sp++) &amp; *mp++ &amp; rm;
                                       dp++;
                               }
                       }
                       return 1;
               }else{
               /* dir == -1 */
                       i = (lm!=0)+dx+(rm!=0);
                       dp += dwid*(dy-1)+i-1;
                       sp += swid*(dy-1)+i-1;
                       mp += mwid*(dy-1)+i-1;
                       dwid = -dwid+i;
                       swid = -swid+i;
                       mwid = -mwid+i;
                       for(y=0; y&lt;dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
                               if(rm){
                                       *dp ^= (*dp ^ *sp--) &amp; *mp-- &amp; rm;
                                       dp--;
                               }
                               for(x=0; x&lt;dx; x++){
                                       *dp ^= (*dp ^ *sp--) &amp; *mp--;
                                       dp--;
                               }
                               if(lm){
                                       *dp ^= (*dp ^ *sp--) &amp; *mp-- &amp; lm;
                                       dp--;
                               }
                       }
               }
               return 1;
       }
       return 0;
}
#undef DBG

/*
* Boolean character drawing.
* Solid opaque color through a 1-bit greyscale mask.
*/
#define DBG if(0)
static int
chardraw(Memdrawparam *par)
{
       u32int bits;
       int i, ddepth, dy, dx, x, bx, ex, y, npack, bsh, depth, op;
       u32int v, maskwid, dstwid;
       uchar *wp, *rp, *q, *wc;
       ushort *ws;
       u32int *wl;
       uchar sp[4];
       Rectangle r, mr;
       Memimage *mask, *src, *dst;

if(0) if(drawdebug) iprint("chardraw? mf %lux md %d sf %lux dxs %d dys %d dd %d ddat %p sdat %p\n",
               par-&gt;mask-&gt;flags, par-&gt;mask-&gt;depth, par-&gt;src-&gt;flags,
               Dx(par-&gt;src-&gt;r), Dy(par-&gt;src-&gt;r), par-&gt;dst-&gt;depth, par-&gt;dst-&gt;data, par-&gt;src-&gt;data);

       mask = par-&gt;mask;
       src = par-&gt;src;
       dst = par-&gt;dst;
       r = par-&gt;r;
       mr = par-&gt;mr;
       op = par-&gt;op;

       if((par-&gt;state&amp;(Replsrc|Simplesrc|Fullsrc|Replmask)) != (Replsrc|Simplesrc|Fullsrc)
       || mask-&gt;depth != 1 || dst-&gt;depth&lt;8 || dst-&gt;data==src-&gt;data
       || op != SoverD)
               return 0;

/*if(drawdebug) iprint("chardraw..."); */

       depth = mask-&gt;depth;
       maskwid = mask-&gt;width*sizeof(u32int);
       rp = byteaddr(mask, mr.min);
       npack = 8/depth;
       bsh = (mr.min.x % npack) * depth;

       wp = byteaddr(dst, r.min);
       dstwid = dst-&gt;width*sizeof(u32int);
DBG print("bsh %d\n", bsh);
       dy = Dy(r);
       dx = Dx(r);

       ddepth = dst-&gt;depth;

       /*
        * for loop counts from bsh to bsh+dx
        *
        * we want the bottom bits to be the amount
        * to shift the pixels down, so for n≡0 (mod 8) we want
        * bottom bits 7.  for n≡1, 6, etc.
        * the bits come from -n-1.
        */

       bx = -bsh-1;
       ex = -bsh-1-dx;
       SET(bits);
       v = par-&gt;sdval;

       /* make little endian */
       sp[0] = v;
       sp[1] = v&gt;&gt;8;
       sp[2] = v&gt;&gt;16;
       sp[3] = v&gt;&gt;24;

/*print("sp %x %x %x %x\n", sp[0], sp[1], sp[2], sp[3]); */
       for(y=0; y&lt;dy; y++, rp+=maskwid, wp+=dstwid){
               q = rp;
               if(bsh)
                       bits = *q++;
               switch(ddepth){
               case 8:
/*if(drawdebug) iprint("8loop..."); */
                       wc = wp;
                       for(x=bx; x&gt;ex; x--, wc++){
                               i = x&amp;7;
                               if(i == 8-1)
                                       bits = *q++;
DBG print("bits %lux sh %d...", bits, i);
                               if((bits&gt;&gt;i)&amp;1)
                                       *wc = v;
                       }
                       break;
               case 16:
                       ws = (ushort*)wp;
                       v = *(ushort*)sp;
                       for(x=bx; x&gt;ex; x--, ws++){
                               i = x&amp;7;
                               if(i == 8-1)
                                       bits = *q++;
DBG print("bits %lux sh %d...", bits, i);
                               if((bits&gt;&gt;i)&amp;1)
                                       *ws = v;
                       }
                       break;
               case 24:
                       wc = wp;
                       for(x=bx; x&gt;ex; x--, wc+=3){
                               i = x&amp;7;
                               if(i == 8-1)
                                       bits = *q++;
DBG print("bits %lux sh %d...", bits, i);
                               if((bits&gt;&gt;i)&amp;1){
                                       wc[0] = sp[0];
                                       wc[1] = sp[1];
                                       wc[2] = sp[2];
                               }
                       }
                       break;
               case 32:
                       wl = (u32int*)wp;
                       v = *(u32int*)sp;
                       for(x=bx; x&gt;ex; x--, wl++){
                               i = x&amp;7;
                               if(i == 8-1)
                                       bits = *q++;
DBG iprint("bits %lux sh %d...", bits, i);
                               if((bits&gt;&gt;i)&amp;1)
                                       *wl = v;
                       }
                       break;
               }
       }

DBG print("\n");
       return 1;
}
#undef DBG


/*
* Fill entire byte with replicated (if necessary) copy of source pixel,
* assuming destination ldepth is &gt;= source ldepth.
*
* This code is just plain wrong for &gt;8bpp.
*
u32int
membyteval(Memimage *src)
{
       int i, val, bpp;
       uchar uc;

       unloadmemimage(src, src-&gt;r, &amp;uc, 1);
       bpp = src-&gt;depth;
       uc &lt;&lt;= (src-&gt;r.min.x&amp;(7/src-&gt;depth))*src-&gt;depth;
       uc &amp;= ~(0xFF&gt;&gt;bpp);
       * pixel value is now in high part of byte. repeat throughout byte
       val = uc;
       for(i=bpp; i&lt;8; i&lt;&lt;=1)
               val |= val&gt;&gt;i;
       return val;
}
*
*/

void
_memfillcolor(Memimage *i, u32int val)
{
       u32int bits;
       int d, y;
       uchar p[4];

       if(val == DNofill)
               return;

       bits = _rgbatoimg(i, val);
       switch(i-&gt;depth){
       case 24:        /* 24-bit images suck */
               for(y=i-&gt;r.min.y; y&lt;i-&gt;r.max.y; y++)
                       memset24(byteaddr(i, Pt(i-&gt;r.min.x, y)), bits, Dx(i-&gt;r));
               break;
       default:        /* 1, 2, 4, 8, 16, 32 */
               for(d=i-&gt;depth; d&lt;32; d*=2)
                       bits = (bits &lt;&lt; d) | bits;
               p[0] = bits;            /* make little endian */
               p[1] = bits&gt;&gt;8;
               p[2] = bits&gt;&gt;16;
               p[3] = bits&gt;&gt;24;
               bits = *(u32int*)p;
               memsetl(wordaddr(i, i-&gt;r.min), bits, i-&gt;width*Dy(i-&gt;r));
               break;
       }
}

<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>