xref: /plan9/sys/src/cmd/unix/drawterm/libmemdraw/draw.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
17dd7cddfSDavid du Colombier #include "../lib9.h"
27dd7cddfSDavid du Colombier 
37dd7cddfSDavid du Colombier #include "../libdraw/draw.h"
47dd7cddfSDavid du Colombier #include "../libmemdraw/memdraw.h"
57dd7cddfSDavid du Colombier 
67dd7cddfSDavid du Colombier /* perfect approximation to NTSC = .299r+.587g+.114b when 0 ≤ r,g,b < 256 */
77dd7cddfSDavid du Colombier #define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier /*
107dd7cddfSDavid du Colombier  * for 0 ≤ x ≤ 255*255, (x*0x0101+0x100)>>16 is a perfect approximation.
117dd7cddfSDavid du Colombier  * for 0 ≤ x < (1<<16), x/255 = ((x+1)*0x0101)>>16 is a perfect approximation.
127dd7cddfSDavid du Colombier  * the last one is perfect for all up to 1<<16, avoids a multiply, but requires a rathole.
137dd7cddfSDavid du Colombier  */
147dd7cddfSDavid du Colombier /* #define DIV255(x) (((x)*257+256)>>16)  */
157dd7cddfSDavid du Colombier #define DIV255(x) ((((x)+1)*257)>>16)
167dd7cddfSDavid du Colombier /* #define DIV255(x) (tmp=(x)+1, (tmp+(tmp>>8))>>8) */
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier static void mktables(void);
197dd7cddfSDavid du Colombier typedef int Subdraw(Memdrawparam*);
207dd7cddfSDavid du Colombier static Subdraw chardraw, alphadraw, memoptdraw;
217dd7cddfSDavid du Colombier 
227dd7cddfSDavid du Colombier static Memimage*	memones;
237dd7cddfSDavid du Colombier static Memimage*	memzeros;
247dd7cddfSDavid du Colombier Memimage *memwhite;
257dd7cddfSDavid du Colombier Memimage *memblack;
267dd7cddfSDavid du Colombier Memimage *memtransparent;
277dd7cddfSDavid du Colombier Memimage *memopaque;
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier int
307dd7cddfSDavid du Colombier Rconv(va_list *o, Fconv *f)
317dd7cddfSDavid du Colombier {
327dd7cddfSDavid du Colombier 	Rectangle r;
337dd7cddfSDavid du Colombier 	char buf[128];
347dd7cddfSDavid du Colombier 
357dd7cddfSDavid du Colombier 	r = va_arg(*o, Rectangle);
367dd7cddfSDavid du Colombier 	sprint(buf, "%P %P", r.min, r.max);
377dd7cddfSDavid du Colombier 	strconv(buf, f);
387dd7cddfSDavid du Colombier 	return sizeof r;
397dd7cddfSDavid du Colombier }
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier int
427dd7cddfSDavid du Colombier Pconv(va_list *o, Fconv *f)
437dd7cddfSDavid du Colombier {
447dd7cddfSDavid du Colombier 	Point p;
457dd7cddfSDavid du Colombier 	char buf[64];
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier 	p = va_arg(*o, Point);
487dd7cddfSDavid du Colombier 	sprint(buf, "[%d %d]", p.x, p.y);
497dd7cddfSDavid du Colombier 	strconv(buf, f);
507dd7cddfSDavid du Colombier 	return sizeof p;
517dd7cddfSDavid du Colombier }
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier void
547dd7cddfSDavid du Colombier _memimageinit(void)
557dd7cddfSDavid du Colombier {
567dd7cddfSDavid du Colombier 	static int didinit = 0;
577dd7cddfSDavid du Colombier 
587dd7cddfSDavid du Colombier 	if(didinit)
597dd7cddfSDavid du Colombier 		return;
607dd7cddfSDavid du Colombier 
617dd7cddfSDavid du Colombier 	didinit = 1;
627dd7cddfSDavid du Colombier 
637dd7cddfSDavid du Colombier 	fmtinstall('R', Rconv);
647dd7cddfSDavid du Colombier 	fmtinstall('P', Pconv);
657dd7cddfSDavid du Colombier 
667dd7cddfSDavid du Colombier 	mktables();
677dd7cddfSDavid du Colombier 	memmkcmap();
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier 	memones = allocmemimage(Rect(0,0,1,1), GREY1);
707dd7cddfSDavid du Colombier 	memones->flags |= Frepl;
717dd7cddfSDavid du Colombier 	memones->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
727dd7cddfSDavid du Colombier 	*byteaddr(memones, ZP) = ~0;
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier 	memzeros = allocmemimage(Rect(0,0,1,1), GREY1);
757dd7cddfSDavid du Colombier 	memzeros->flags |= Frepl;
767dd7cddfSDavid du Colombier 	memzeros->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
777dd7cddfSDavid du Colombier 	*byteaddr(memzeros, ZP) = 0;
787dd7cddfSDavid du Colombier 
797dd7cddfSDavid du Colombier 	if(memones == nil || memzeros == nil)
807dd7cddfSDavid du Colombier 		assert(0 /*cannot initialize memimage library */);	/* RSC BUG */
817dd7cddfSDavid du Colombier 
827dd7cddfSDavid du Colombier 	memwhite = memones;
837dd7cddfSDavid du Colombier 	memblack = memzeros;
847dd7cddfSDavid du Colombier 	memopaque = memones;
857dd7cddfSDavid du Colombier 	memtransparent = memzeros;
867dd7cddfSDavid du Colombier }
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier int
897dd7cddfSDavid du Colombier drawreplxy(int min, int max, int x)
907dd7cddfSDavid du Colombier {
917dd7cddfSDavid du Colombier 	int sx;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	sx = (x-min)%(max-min);
947dd7cddfSDavid du Colombier 	if(sx < 0)
957dd7cddfSDavid du Colombier 		sx += max-min;
967dd7cddfSDavid du Colombier 	return sx+min;
977dd7cddfSDavid du Colombier }
987dd7cddfSDavid du Colombier 
997dd7cddfSDavid du Colombier Point
1007dd7cddfSDavid du Colombier drawrepl(Rectangle r, Point p)
1017dd7cddfSDavid du Colombier {
1027dd7cddfSDavid du Colombier 	p.x = drawreplxy(r.min.x, r.max.x, p.x);
1037dd7cddfSDavid du Colombier 	p.y = drawreplxy(r.min.y, r.max.y, p.y);
1047dd7cddfSDavid du Colombier 	return p;
1057dd7cddfSDavid du Colombier }
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier #define DBG if(0)
1087dd7cddfSDavid du Colombier static	Memdrawparam par;	/* sleazily left for the X implementation */
1097dd7cddfSDavid du Colombier Memdrawparam*
1107dd7cddfSDavid du Colombier _memimagedrawsetup(Memimage *dst, Rectangle r, Memimage *src, Point p0, Memimage *mask, Point p1)
1117dd7cddfSDavid du Colombier {
1127dd7cddfSDavid du Colombier 	static int n = 0;
1137dd7cddfSDavid du Colombier 
1147dd7cddfSDavid du Colombier 	if(mask == nil)
1157dd7cddfSDavid du Colombier 		mask = memopaque;
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 	if(drawdebug)
1187dd7cddfSDavid du Colombier 		iprint("memimagedraw %p/%luX %R %p/%luX %P %p/%luX %P... ", dst, dst->chan, r, src, src->chan, p0, mask, mask->chan, p1);
1197dd7cddfSDavid du Colombier 
1207dd7cddfSDavid du Colombier 	if(drawclip(dst, &r, src, &p0, mask, &p1, &par.sr, &par.mr) == 0){
1217dd7cddfSDavid du Colombier 		if(drawdebug)
1227dd7cddfSDavid du Colombier 			iprint("empty clipped rectangle\n");
1237dd7cddfSDavid du Colombier 		return nil;
1247dd7cddfSDavid du Colombier 	}
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	par.dst = dst;
1277dd7cddfSDavid du Colombier 	par.r = r;
1287dd7cddfSDavid du Colombier 	par.src = src;
1297dd7cddfSDavid du Colombier 	/* par.sr set by drawclip */
1307dd7cddfSDavid du Colombier 	par.mask = mask;
1317dd7cddfSDavid du Colombier 	/* par.mr set by drawclip */
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier 	par.state = 0;
1347dd7cddfSDavid du Colombier 	if(src->flags&Frepl){
1357dd7cddfSDavid du Colombier 		par.state |= Replsrc;
1367dd7cddfSDavid du Colombier 		if(Dx(src->r)==1 && Dy(src->r)==1){
1377dd7cddfSDavid du Colombier 			par.sval = pixelbits(src, src->r.min);
1387dd7cddfSDavid du Colombier 			par.state |= Simplesrc;
1397dd7cddfSDavid du Colombier 			par.srgba = _imgtorgba(src, par.sval);
1407dd7cddfSDavid du Colombier 			par.sdval = _rgbatoimg(dst, par.srgba);
1417dd7cddfSDavid du Colombier 		}
1427dd7cddfSDavid du Colombier 	}
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	if(mask->flags & Frepl){
1457dd7cddfSDavid du Colombier 		par.state |= Replmask;
1467dd7cddfSDavid du Colombier 		if(Dx(mask->r)==1 && Dy(mask->r)==1){
1477dd7cddfSDavid du Colombier 			par.mval = pixelbits(mask, mask->r.min);
1487dd7cddfSDavid du Colombier 			if(par.mval == 0){
1497dd7cddfSDavid du Colombier 				if(drawdebug) iprint("fill with zero mask\n");
1507dd7cddfSDavid du Colombier 				return nil;	/* no-op successfully handled */
1517dd7cddfSDavid du Colombier 			}
1527dd7cddfSDavid du Colombier 			par.state |= Simplemask;
1537dd7cddfSDavid du Colombier 			if(par.mval == ~0)
1547dd7cddfSDavid du Colombier 				par.state |= Fullmask;
1557dd7cddfSDavid du Colombier 			par.mrgba = _imgtorgba(mask, par.mval);
1567dd7cddfSDavid du Colombier 		}
1577dd7cddfSDavid du Colombier 	}
1587dd7cddfSDavid du Colombier 
1597dd7cddfSDavid du Colombier 	if(drawdebug)
1607dd7cddfSDavid du Colombier 		iprint("dr %R sr %R mr %R...", r, par.sr, par.mr);
1617dd7cddfSDavid du Colombier DBG print("draw dr %R sr %R mr %R\n", r, par.sr, par.mr);
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	return &par;
1647dd7cddfSDavid du Colombier }
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier void
1677dd7cddfSDavid du Colombier _memimagedraw(Memdrawparam *par)
1687dd7cddfSDavid du Colombier {
1697dd7cddfSDavid du Colombier 	if(par == nil)
1707dd7cddfSDavid du Colombier 		return;
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier 	/*
1737dd7cddfSDavid du Colombier 	 * Now that we've clipped the parameters down to be consistent, we
1747dd7cddfSDavid du Colombier 	 * simply try sub-drawing routines in order until we find one that was able
1757dd7cddfSDavid du Colombier 	 * to handle us.  If the sub-drawing routine returns zero, it means it was
1767dd7cddfSDavid du Colombier 	 * unable to satisfy the request, so we do not return.
1777dd7cddfSDavid du Colombier 	 */
1787dd7cddfSDavid du Colombier 
1797dd7cddfSDavid du Colombier 	/*
1807dd7cddfSDavid du Colombier 	 * Hardware support.  Each video driver provides this function,
1817dd7cddfSDavid du Colombier 	 * which checks to see if there is anything it can help with.
1827dd7cddfSDavid du Colombier 	 * There could be an if around this checking to see if dst is in video memory.
1837dd7cddfSDavid du Colombier 	 */
1847dd7cddfSDavid du Colombier 	if(hwdraw(par))
1857dd7cddfSDavid du Colombier {
1867dd7cddfSDavid du Colombier if(drawdebug) iprint("hw handled\n");
1877dd7cddfSDavid du Colombier 		return;
1887dd7cddfSDavid du Colombier }
1897dd7cddfSDavid du Colombier 	/*
1907dd7cddfSDavid du Colombier 	 * Optimizations using memmove and memset.
1917dd7cddfSDavid du Colombier 	 */
1927dd7cddfSDavid du Colombier 	if(memoptdraw(par)){
1937dd7cddfSDavid du Colombier if(drawdebug) iprint("memopt handled\n");
1947dd7cddfSDavid du Colombier {
1957dd7cddfSDavid du Colombier 		return;
1967dd7cddfSDavid du Colombier }
1977dd7cddfSDavid du Colombier 	}
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	/*
2007dd7cddfSDavid du Colombier 	 * Character drawing.
2017dd7cddfSDavid du Colombier 	 * Solid source color being painted through a boolean mask onto a high res image.
2027dd7cddfSDavid du Colombier 	 */
2037dd7cddfSDavid du Colombier 	if(chardraw(par)){
2047dd7cddfSDavid du Colombier if(drawdebug) iprint("chardraw handled\n");
2057dd7cddfSDavid du Colombier 		return;
2067dd7cddfSDavid du Colombier 	}
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier 	/*
2097dd7cddfSDavid du Colombier 	 * General calculation-laden case that does alpha for each pixel.
2107dd7cddfSDavid du Colombier 	 */
2117dd7cddfSDavid du Colombier 	alphadraw(par);
2127dd7cddfSDavid du Colombier if(drawdebug) iprint("alphadraw handled\n");
2137dd7cddfSDavid du Colombier 	return;
2147dd7cddfSDavid du Colombier }
2157dd7cddfSDavid du Colombier #undef DBG
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier /*
2187dd7cddfSDavid du Colombier  * Clip the destination rectangle further based on the properties of the
2197dd7cddfSDavid du Colombier  * source and mask rectangles.  Once the destination rectangle is properly
2207dd7cddfSDavid du Colombier  * clipped, adjust the source and mask rectangles to be the same size.
2217dd7cddfSDavid du Colombier  * Then if source or mask is replicated, move its clipped rectangle
2227dd7cddfSDavid du Colombier  * so that its minimum point falls within the repl rectangle.
2237dd7cddfSDavid du Colombier  *
2247dd7cddfSDavid du Colombier  * Return zero if the final rectangle is null.
2257dd7cddfSDavid du Colombier  */
2267dd7cddfSDavid du Colombier int
2277dd7cddfSDavid du Colombier drawclip(Memimage *dst, Rectangle *r, Memimage *src, Point *p0, Memimage *mask, Point *p1, Rectangle *sr, Rectangle *mr)
2287dd7cddfSDavid du Colombier {
2297dd7cddfSDavid du Colombier 	Point rmin, delta;
2307dd7cddfSDavid du Colombier 	int splitcoords;
2317dd7cddfSDavid du Colombier 	Rectangle omr;
2327dd7cddfSDavid du Colombier 	Point p;
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier 	if(r->min.x>=r->max.x || r->min.y>=r->max.y)
2357dd7cddfSDavid du Colombier 		return 0;
2367dd7cddfSDavid du Colombier 	splitcoords = (p0->x!=p1->x) || (p0->y!=p1->y);
2377dd7cddfSDavid du Colombier 	/* clip to destination */
2387dd7cddfSDavid du Colombier 	rmin = r->min;
2397dd7cddfSDavid du Colombier 	if(!rectclip(r, dst->r) || !rectclip(r, dst->clipr))
2407dd7cddfSDavid du Colombier 		return 0;
2417dd7cddfSDavid du Colombier 	/* move mask point */
2427dd7cddfSDavid du Colombier 	p1->x += r->min.x-rmin.x;
2437dd7cddfSDavid du Colombier 	p1->y += r->min.y-rmin.y;
2447dd7cddfSDavid du Colombier 	/* move source point */
2457dd7cddfSDavid du Colombier 	p0->x += r->min.x-rmin.x;
2467dd7cddfSDavid du Colombier 	p0->y += r->min.y-rmin.y;
2477dd7cddfSDavid du Colombier 	/* map destination rectangle into source */
2487dd7cddfSDavid du Colombier 	sr->min = *p0;
2497dd7cddfSDavid du Colombier 	sr->max.x = p0->x+Dx(*r);
2507dd7cddfSDavid du Colombier 	sr->max.y = p0->y+Dy(*r);
2517dd7cddfSDavid du Colombier 	/* sr is r in source coordinates; clip to source */
2527dd7cddfSDavid du Colombier 	if(!(src->flags&Frepl) && !rectclip(sr, src->r))
2537dd7cddfSDavid du Colombier 		return 0;
2547dd7cddfSDavid du Colombier 	if(!rectclip(sr, src->clipr))
2557dd7cddfSDavid du Colombier 		return 0;
2567dd7cddfSDavid du Colombier 	/* compute and clip rectangle in mask */
2577dd7cddfSDavid du Colombier 	if(splitcoords){
2587dd7cddfSDavid du Colombier 		/* move mask point with source */
2597dd7cddfSDavid du Colombier 		p1->x += sr->min.x-p0->x;
2607dd7cddfSDavid du Colombier 		p1->y += sr->min.y-p0->y;
2617dd7cddfSDavid du Colombier 		mr->min = *p1;
2627dd7cddfSDavid du Colombier 		mr->max.x = p1->x+Dx(*sr);
2637dd7cddfSDavid du Colombier 		mr->max.y = p1->y+Dy(*sr);
2647dd7cddfSDavid du Colombier 		omr = *mr;
2657dd7cddfSDavid du Colombier 		/* mr is now rectangle in mask; clip it */
2667dd7cddfSDavid du Colombier 		if(!(mask->flags&Frepl) && !rectclip(mr, mask->r))
2677dd7cddfSDavid du Colombier 			return 0;
2687dd7cddfSDavid du Colombier 		if(!rectclip(mr, mask->clipr))
2697dd7cddfSDavid du Colombier 			return 0;
2707dd7cddfSDavid du Colombier 		/* reflect any clips back to source */
2717dd7cddfSDavid du Colombier 		sr->min.x += mr->min.x-omr.min.x;
2727dd7cddfSDavid du Colombier 		sr->min.y += mr->min.y-omr.min.y;
2737dd7cddfSDavid du Colombier 		sr->max.x += mr->max.x-omr.max.x;
2747dd7cddfSDavid du Colombier 		sr->max.y += mr->max.y-omr.max.y;
2757dd7cddfSDavid du Colombier 		*p1 = mr->min;
2767dd7cddfSDavid du Colombier 	}else{
2777dd7cddfSDavid du Colombier 		if(!(mask->flags&Frepl) && !rectclip(sr, mask->r))
2787dd7cddfSDavid du Colombier 			return 0;
2797dd7cddfSDavid du Colombier 		if(!rectclip(sr, mask->clipr))
2807dd7cddfSDavid du Colombier 			return 0;
2817dd7cddfSDavid du Colombier 		*p1 = sr->min;
2827dd7cddfSDavid du Colombier 	}
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier 	/* move source clipping back to destination */
2857dd7cddfSDavid du Colombier 	delta.x = r->min.x - p0->x;
2867dd7cddfSDavid du Colombier 	delta.y = r->min.y - p0->y;
2877dd7cddfSDavid du Colombier 	r->min.x = sr->min.x + delta.x;
2887dd7cddfSDavid du Colombier 	r->min.y = sr->min.y + delta.y;
2897dd7cddfSDavid du Colombier 	r->max.x = sr->max.x + delta.x;
2907dd7cddfSDavid du Colombier 	r->max.y = sr->max.y + delta.y;
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier 	/* move source rectangle so sr->min is in src->r */
2937dd7cddfSDavid du Colombier 	if(src->flags&Frepl) {
2947dd7cddfSDavid du Colombier 		delta.x = drawreplxy(src->r.min.x, src->r.max.x, sr->min.x) - sr->min.x;
2957dd7cddfSDavid du Colombier 		delta.y = drawreplxy(src->r.min.y, src->r.max.y, sr->min.y) - sr->min.y;
2967dd7cddfSDavid du Colombier 		sr->min.x += delta.x;
2977dd7cddfSDavid du Colombier 		sr->min.y += delta.y;
2987dd7cddfSDavid du Colombier 		sr->max.x += delta.x;
2997dd7cddfSDavid du Colombier 		sr->max.y += delta.y;
3007dd7cddfSDavid du Colombier 	}
3017dd7cddfSDavid du Colombier 	*p0 = sr->min;
3027dd7cddfSDavid du Colombier 
3037dd7cddfSDavid du Colombier 	/* move mask point so it is in mask->r */
3047dd7cddfSDavid du Colombier 	/* use temporary point p to avoid warnings about unaligned volatiles on digital unix */
3057dd7cddfSDavid du Colombier 	p = *p1;
3067dd7cddfSDavid du Colombier 	p = drawrepl(mask->r, p);
3077dd7cddfSDavid du Colombier 	*p1 = p;
3087dd7cddfSDavid du Colombier 	mr->min = *p1;
3097dd7cddfSDavid du Colombier 	mr->max.x = p1->x+Dx(*sr);
3107dd7cddfSDavid du Colombier 	mr->max.y = p1->y+Dy(*sr);
3117dd7cddfSDavid du Colombier 
3127dd7cddfSDavid du Colombier 	assert(Dx(*sr) == Dx(*mr) && Dx(*mr) == Dx(*r));
3137dd7cddfSDavid du Colombier 	assert(Dy(*sr) == Dy(*mr) && Dy(*mr) == Dy(*r));
3147dd7cddfSDavid du Colombier 	assert((p=*p0, ptinrect(p, src->r)));
3157dd7cddfSDavid du Colombier 	assert((p=*p1, ptinrect(p, mask->r)));
3167dd7cddfSDavid du Colombier 	assert((p=r->min, ptinrect(p, dst->r)));
3177dd7cddfSDavid du Colombier 
3187dd7cddfSDavid du Colombier 	return 1;
3197dd7cddfSDavid du Colombier }
3207dd7cddfSDavid du Colombier 
3217dd7cddfSDavid du Colombier /*
3227dd7cddfSDavid du Colombier  * Conversion tables.
3237dd7cddfSDavid du Colombier  */
3247dd7cddfSDavid du Colombier static uchar replbit[1+8][256];		/* replbit[x][y] is the replication of the x-bit quantity y to 8-bit depth */
3257dd7cddfSDavid du Colombier static uchar conv18[256][8];		/* conv18[x][y] is the yth pixel in the depth-1 pixel x */
3267dd7cddfSDavid du Colombier static uchar conv28[256][4];		/* ... */
3277dd7cddfSDavid du Colombier static uchar conv48[256][2];
3287dd7cddfSDavid du Colombier static int	tablesbuilt;
3297dd7cddfSDavid du Colombier 
3307dd7cddfSDavid du Colombier /*
3317dd7cddfSDavid du Colombier  * bitmap of how to replicate n bits to fill 8, for 1 ≤ n ≤ 8.
3327dd7cddfSDavid du Colombier  * the X's are where to put the bottom (ones) bit of the n-bit pattern.
3337dd7cddfSDavid du Colombier  * only the top 8 bits of the result are actually used.
3347dd7cddfSDavid du Colombier  * (the lower 8 bits are needed to get bits in the right place
3357dd7cddfSDavid du Colombier  * when n is not a divisor of 8.)
3367dd7cddfSDavid du Colombier  *
3377dd7cddfSDavid du Colombier  * Should check to see if its easier to just refer to replmul than
3387dd7cddfSDavid du Colombier  * use the precomputed values in replbit.  On PCs it may well
3397dd7cddfSDavid du Colombier  * be; on machines with slow multiply instructions it probably isn't.
3407dd7cddfSDavid du Colombier  */
3417dd7cddfSDavid du Colombier #define a ((((((((((((((((0
3427dd7cddfSDavid du Colombier #define X *2+1)
3437dd7cddfSDavid du Colombier #define _ *2)
3447dd7cddfSDavid du Colombier static int replmul[1+8] = {
3457dd7cddfSDavid du Colombier 	0,
3467dd7cddfSDavid du Colombier 	a X X X X X X X X X X X X X X X X,
3477dd7cddfSDavid du Colombier 	a _ X _ X _ X _ X _ X _ X _ X _ X,
3487dd7cddfSDavid du Colombier 	a _ _ X _ _ X _ _ X _ _ X _ _ X _,
3497dd7cddfSDavid du Colombier 	a _ _ _ X _ _ _ X _ _ _ X _ _ _ X,
3507dd7cddfSDavid du Colombier 	a _ _ _ _ X _ _ _ _ X _ _ _ _ X _,
3517dd7cddfSDavid du Colombier 	a _ _ _ _ _ X _ _ _ _ _ X _ _ _ _,
3527dd7cddfSDavid du Colombier 	a _ _ _ _ _ _ X _ _ _ _ _ _ X _ _,
3537dd7cddfSDavid du Colombier 	a _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X,
3547dd7cddfSDavid du Colombier };
3557dd7cddfSDavid du Colombier #undef a
3567dd7cddfSDavid du Colombier #undef X
3577dd7cddfSDavid du Colombier #undef _
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier static void
3607dd7cddfSDavid du Colombier mktables(void)
3617dd7cddfSDavid du Colombier {
3627dd7cddfSDavid du Colombier 	int i, j, mask, sh, small;
3637dd7cddfSDavid du Colombier 
3647dd7cddfSDavid du Colombier 	if(tablesbuilt)
3657dd7cddfSDavid du Colombier 		return;
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier 	tablesbuilt = 1;
3687dd7cddfSDavid du Colombier 	/* bit replication up to 8 bits */
3697dd7cddfSDavid du Colombier 	for(i=0; i<256; i++){
3707dd7cddfSDavid du Colombier 		for(j=0; j<=8; j++){	/* j <= 8 [sic] */
3717dd7cddfSDavid du Colombier 			small = i & ((1<<j)-1);
3727dd7cddfSDavid du Colombier 			replbit[j][i] = (small*replmul[j])>>8;
3737dd7cddfSDavid du Colombier 		}
3747dd7cddfSDavid du Colombier 	}
3757dd7cddfSDavid du Colombier 
3767dd7cddfSDavid du Colombier 	/* bit unpacking up to 8 bits, only powers of 2 */
3777dd7cddfSDavid du Colombier 	for(i=0; i<256; i++){
3787dd7cddfSDavid du Colombier 		for(j=0, sh=7, mask=1; j<8; j++, sh--)
3797dd7cddfSDavid du Colombier 			conv18[i][j] = replbit[1][(i>>sh)&mask];
3807dd7cddfSDavid du Colombier 
3817dd7cddfSDavid du Colombier 		for(j=0, sh=6, mask=3; j<4; j++, sh-=2)
3827dd7cddfSDavid du Colombier 			conv28[i][j] = replbit[2][(i>>sh)&mask];
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier 		for(j=0, sh=4, mask=15; j<2; j++, sh-=4)
3857dd7cddfSDavid du Colombier 			conv48[i][j] = replbit[4][(i>>sh)&mask];
3867dd7cddfSDavid du Colombier 	}
3877dd7cddfSDavid du Colombier }
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier /*
3907dd7cddfSDavid du Colombier  * General alpha drawing case.  Can handle anything.
3917dd7cddfSDavid du Colombier  */
3927dd7cddfSDavid du Colombier typedef struct	Buffer	Buffer;
3937dd7cddfSDavid du Colombier struct Buffer {
3947dd7cddfSDavid du Colombier 	uchar	*red;
3957dd7cddfSDavid du Colombier 	uchar	*grn;
3967dd7cddfSDavid du Colombier 	uchar	*blu;
3977dd7cddfSDavid du Colombier 	uchar	*alpha;
3987dd7cddfSDavid du Colombier 	uchar	*grey;
3997dd7cddfSDavid du Colombier 
4007dd7cddfSDavid du Colombier 	int	delta;	/* number of bytes to add to pointer to get next pixel to the right */
4017dd7cddfSDavid du Colombier 	uchar	*m;		/* ptr to mask data r.min byte; like p->bytermin */
4027dd7cddfSDavid du Colombier 	int		mskip;	/* no. of left bits to skip in *m */
4037dd7cddfSDavid du Colombier 	uchar	*bm;		/* ptr to mask data img->r.min byte; like p->bytey0s */
4047dd7cddfSDavid du Colombier 	int		bmskip;	/* no. of left bits to skip in *bm */
4057dd7cddfSDavid du Colombier 	uchar	*em;		/* ptr to mask data img->r.max.x byte; like p->bytey0e */
4067dd7cddfSDavid du Colombier 	int		emskip;	/* no. of right bits to skip in *em */
4077dd7cddfSDavid du Colombier };
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier typedef struct	Param	Param;
4107dd7cddfSDavid du Colombier typedef Buffer	Readfn(Param *notusedpar, uchar *notusedbuf, int notusedi);
4117dd7cddfSDavid du Colombier typedef void	Writefn(Param *notusedpar, uchar *notusedbuf, Buffer notusedb);
4127dd7cddfSDavid du Colombier typedef Buffer	Calcfn(Buffer, Buffer, Buffer, int, int notusedi);
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier enum {
4157dd7cddfSDavid du Colombier 	MAXBCACHE = 16
4167dd7cddfSDavid du Colombier };
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier /* giant rathole to customize functions with */
4197dd7cddfSDavid du Colombier struct Param {
4207dd7cddfSDavid du Colombier 	Readfn	*replcall;
4217dd7cddfSDavid du Colombier 	Readfn	*greymaskcall;
4227dd7cddfSDavid du Colombier 	Readfn	*convreadcall;
4237dd7cddfSDavid du Colombier 	Writefn	*convwritecall;
4247dd7cddfSDavid du Colombier 
4257dd7cddfSDavid du Colombier 	Memimage *img;
4267dd7cddfSDavid du Colombier 	Rectangle	r;
4277dd7cddfSDavid du Colombier 	int	dx;	/* of r */
4287dd7cddfSDavid du Colombier 	int	needbuf;
4297dd7cddfSDavid du Colombier 	int	convgrey;
4307dd7cddfSDavid du Colombier 	int	alphaonly;
4317dd7cddfSDavid du Colombier 
4327dd7cddfSDavid du Colombier 	uchar	*bytey0s;		/* byteaddr(Pt(img->r.min.x, img->r.min.y)) */
4337dd7cddfSDavid du Colombier 	uchar	*bytermin;	/* byteaddr(Pt(r.min.x, img->r.min.y)) */
4347dd7cddfSDavid du Colombier 	uchar	*bytey0e;		/* byteaddr(Pt(img->r.max.x, img->r.min.y)) */
4357dd7cddfSDavid du Colombier 	int		bwidth;
4367dd7cddfSDavid du Colombier 
4377dd7cddfSDavid du Colombier 	int	replcache;	/* if set, cache buffers */
4387dd7cddfSDavid du Colombier 	Buffer	bcache[MAXBCACHE];
4397dd7cddfSDavid du Colombier 	ulong	bfilled;
4407dd7cddfSDavid du Colombier 	uchar	*bufbase;
4417dd7cddfSDavid du Colombier 	int	bufoff;
4427dd7cddfSDavid du Colombier 	int	bufdelta;
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier 	int	dir;
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier 	int	convbufoff;
4477dd7cddfSDavid du Colombier 	uchar	*convbuf;
4487dd7cddfSDavid du Colombier 	Param	*convdpar;
4497dd7cddfSDavid du Colombier 	int	convdx;
4507dd7cddfSDavid du Colombier };
4517dd7cddfSDavid du Colombier 
4527dd7cddfSDavid du Colombier static uchar *drawbuf;
4537dd7cddfSDavid du Colombier static int	ndrawbuf;
4547dd7cddfSDavid du Colombier static int	mdrawbuf;
4557dd7cddfSDavid du Colombier static Param spar, mpar, dpar;	/* easier on the stacks */
4567dd7cddfSDavid du Colombier static Readfn	greymaskread, replread, readptr;
4577dd7cddfSDavid du Colombier static Writefn	nullwrite;
4587dd7cddfSDavid du Colombier static Calcfn	alphacalc;
4597dd7cddfSDavid du Colombier static Calcfn	boolcalc;
4607dd7cddfSDavid du Colombier 
4617dd7cddfSDavid du Colombier static Readfn*	readfn(Memimage*);
4627dd7cddfSDavid du Colombier static Readfn*	readalphafn(Memimage*);
4637dd7cddfSDavid du Colombier static Writefn*	writefn(Memimage*);
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier static Calcfn*	boolcopyfn(Memimage*, Memimage*);
4667dd7cddfSDavid du Colombier static Readfn*	convfn(Memimage*, Param *notusedpar, Memimage*, Param*);
4677dd7cddfSDavid du Colombier static Readfn*	ptrfn(Memimage*);
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier static int
4707dd7cddfSDavid du Colombier allocdrawbuf(void)
4717dd7cddfSDavid du Colombier {
4727dd7cddfSDavid du Colombier 	uchar *p;
4737dd7cddfSDavid du Colombier 
4747dd7cddfSDavid du Colombier 	if(ndrawbuf > mdrawbuf){
4757dd7cddfSDavid du Colombier 		p = realloc(drawbuf, ndrawbuf);
4767dd7cddfSDavid du Colombier 		if(p == nil){
4777dd7cddfSDavid du Colombier 			werrstr("memimagedraw out of memory");
4787dd7cddfSDavid du Colombier 			return -1;
4797dd7cddfSDavid du Colombier 		}
4807dd7cddfSDavid du Colombier 		drawbuf = p;
4817dd7cddfSDavid du Colombier 		mdrawbuf = ndrawbuf;
4827dd7cddfSDavid du Colombier 	}
4837dd7cddfSDavid du Colombier 	return 0;
4847dd7cddfSDavid du Colombier }
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier static Param
4877dd7cddfSDavid du Colombier getparam(Memimage *img, Rectangle r, int convgrey, int needbuf)
4887dd7cddfSDavid du Colombier {
4897dd7cddfSDavid du Colombier 	Param p;
4907dd7cddfSDavid du Colombier 	int nbuf;
4917dd7cddfSDavid du Colombier 
4927dd7cddfSDavid du Colombier 	memset(&p, 0, sizeof p);
4937dd7cddfSDavid du Colombier 
4947dd7cddfSDavid du Colombier 	p.img = img;
4957dd7cddfSDavid du Colombier 	p.r = r;
4967dd7cddfSDavid du Colombier 	p.dx = Dx(r);
4977dd7cddfSDavid du Colombier 	p.needbuf = needbuf;
4987dd7cddfSDavid du Colombier 	p.convgrey = convgrey;
4997dd7cddfSDavid du Colombier 
5007dd7cddfSDavid du Colombier 	assert(img->r.min.x <= r.min.x && r.min.x < img->r.max.x);
5017dd7cddfSDavid du Colombier 
5027dd7cddfSDavid du Colombier 	p.bytey0s = byteaddr(img, Pt(img->r.min.x, img->r.min.y));
5037dd7cddfSDavid du Colombier 	p.bytermin = byteaddr(img, Pt(r.min.x, img->r.min.y));
5047dd7cddfSDavid du Colombier 	p.bytey0e = byteaddr(img, Pt(img->r.max.x, img->r.min.y));
5057dd7cddfSDavid du Colombier 	p.bwidth = sizeof(ulong)*img->width;
5067dd7cddfSDavid du Colombier 
5077dd7cddfSDavid du Colombier 	assert(p.bytey0s <= p.bytermin && p.bytermin <= p.bytey0e);
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier 	if(p.r.min.x == p.img->r.min.x)
5107dd7cddfSDavid du Colombier 		assert(p.bytermin == p.bytey0s);
5117dd7cddfSDavid du Colombier 
5127dd7cddfSDavid du Colombier 	nbuf = 1;
5137dd7cddfSDavid du Colombier 	if((img->flags&Frepl) && Dy(img->r) <= MAXBCACHE && Dy(img->r) < Dy(r)){
5147dd7cddfSDavid du Colombier 		p.replcache = 1;
5157dd7cddfSDavid du Colombier 		nbuf = Dy(img->r);
5167dd7cddfSDavid du Colombier 	}
5177dd7cddfSDavid du Colombier 	p.bufdelta = 4*p.dx;
5187dd7cddfSDavid du Colombier 	p.bufoff = ndrawbuf;
5197dd7cddfSDavid du Colombier 	ndrawbuf += p.bufdelta*nbuf;
5207dd7cddfSDavid du Colombier 
5217dd7cddfSDavid du Colombier 	return p;
5227dd7cddfSDavid du Colombier }
5237dd7cddfSDavid du Colombier 
5247dd7cddfSDavid du Colombier static void
5257dd7cddfSDavid du Colombier clipy(Memimage *img, int *y)
5267dd7cddfSDavid du Colombier {
5277dd7cddfSDavid du Colombier 	int dy;
5287dd7cddfSDavid du Colombier 
5297dd7cddfSDavid du Colombier 	dy = Dy(img->r);
5307dd7cddfSDavid du Colombier 	if(*y == dy)
5317dd7cddfSDavid du Colombier 		*y = 0;
5327dd7cddfSDavid du Colombier 	else if(*y == -1)
5337dd7cddfSDavid du Colombier 		*y = dy-1;
5347dd7cddfSDavid du Colombier 	assert(0 <= *y && *y < dy);
5357dd7cddfSDavid du Colombier }
5367dd7cddfSDavid du Colombier 
5377dd7cddfSDavid du Colombier static void
5387dd7cddfSDavid du Colombier dumpbuf(char *s, Buffer b, int n)
5397dd7cddfSDavid du Colombier {
5407dd7cddfSDavid du Colombier 	int i;
5417dd7cddfSDavid du Colombier 	uchar *p;
5427dd7cddfSDavid du Colombier 
5437dd7cddfSDavid du Colombier 	print("%s", s);
5447dd7cddfSDavid du Colombier 	for(i=0; i<n; i++){
5457dd7cddfSDavid du Colombier 		print(" ");
5467dd7cddfSDavid du Colombier 		if(p=b.grey){
5477dd7cddfSDavid du Colombier 			print(" k%.2uX", *p);
5487dd7cddfSDavid du Colombier 			b.grey += b.delta;
5497dd7cddfSDavid du Colombier 		}else{
5507dd7cddfSDavid du Colombier 			if(p=b.red){
5517dd7cddfSDavid du Colombier 				print(" r%.2uX", *p);
5527dd7cddfSDavid du Colombier 				b.red += b.delta;
5537dd7cddfSDavid du Colombier 			}
5547dd7cddfSDavid du Colombier 			if(p=b.grn){
5557dd7cddfSDavid du Colombier 				print(" g%.2uX", *p);
5567dd7cddfSDavid du Colombier 				b.grn += b.delta;
5577dd7cddfSDavid du Colombier 			}
5587dd7cddfSDavid du Colombier 			if(p=b.blu){
5597dd7cddfSDavid du Colombier 				print(" b%.2uX", *p);
5607dd7cddfSDavid du Colombier 				b.blu += b.delta;
5617dd7cddfSDavid du Colombier 			}
5627dd7cddfSDavid du Colombier 		}
5637dd7cddfSDavid du Colombier 		if(p=b.alpha){
5647dd7cddfSDavid du Colombier 			print(" α%.2uX", *p);
5657dd7cddfSDavid du Colombier 			b.alpha += b.delta;
5667dd7cddfSDavid du Colombier 		}
5677dd7cddfSDavid du Colombier 	}
5687dd7cddfSDavid du Colombier 	print("\n");
5697dd7cddfSDavid du Colombier }
5707dd7cddfSDavid du Colombier 
5717dd7cddfSDavid du Colombier /*
5727dd7cddfSDavid du Colombier  * For each scan line, we expand the pixels from source, mask, and destination
5737dd7cddfSDavid du Colombier  * into byte-aligned red, green, blue, alpha, and grey channels.  If buffering is not
5747dd7cddfSDavid du Colombier  * needed and the channels were already byte-aligned (grey8, rgb24, rgba32, rgb32),
5757dd7cddfSDavid du Colombier  * the readers need not copy the data: they can simply return pointers to the data.
5767dd7cddfSDavid du Colombier  * If the destination image is grey and the source is not, it is converted using the NTSC
5777dd7cddfSDavid du Colombier  * formula.
5787dd7cddfSDavid du Colombier  *
5797dd7cddfSDavid du Colombier  * Once we have all the channels, we call either rgbcalc or greycalc, depending on
5807dd7cddfSDavid du Colombier  * whether the destination image is color.  This is allowed to overwrite the dst buffer (perhaps
5817dd7cddfSDavid du Colombier  * the actual data, perhaps a copy) with its result.  It should only overwrite the dst buffer
5827dd7cddfSDavid du Colombier  * with the same format (i.e. red bytes with red bytes, etc.)  A new buffer is returned from
5837dd7cddfSDavid du Colombier  * the calculator, and that buffer is passed to a function to write it to the destination.
5847dd7cddfSDavid du Colombier  * If the buffer is already pointing at the destination, the writing function is a no-op.
5857dd7cddfSDavid du Colombier  */
5867dd7cddfSDavid du Colombier #define DBG if(0)
5877dd7cddfSDavid du Colombier static int
5887dd7cddfSDavid du Colombier alphadraw(Memdrawparam *par)
5897dd7cddfSDavid du Colombier {
5907dd7cddfSDavid du Colombier 	int isgrey, starty, endy;
5917dd7cddfSDavid du Colombier 	int needbuf, dsty, srcy, masky;
5927dd7cddfSDavid du Colombier 	int y, dir, dx, dy;
5937dd7cddfSDavid du Colombier 	Buffer bsrc, bdst, bmask;
5947dd7cddfSDavid du Colombier 	Readfn *rdsrc, *rdmask, *rddst;
5957dd7cddfSDavid du Colombier 	Calcfn *calc;
5967dd7cddfSDavid du Colombier 	Writefn *wrdst;
5977dd7cddfSDavid du Colombier 	Memimage *src, *mask, *dst;
5987dd7cddfSDavid du Colombier 	Rectangle r, sr, mr;
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier 	r = par->r;
6017dd7cddfSDavid du Colombier 	dx = Dx(r);
6027dd7cddfSDavid du Colombier 	dy = Dy(r);
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier 	ndrawbuf = 0;
6057dd7cddfSDavid du Colombier 
6067dd7cddfSDavid du Colombier 	src = par->src;
6077dd7cddfSDavid du Colombier 	mask = par->mask;
6087dd7cddfSDavid du Colombier 	dst = par->dst;
6097dd7cddfSDavid du Colombier 	sr = par->sr;
6107dd7cddfSDavid du Colombier 	mr = par->mr;
6117dd7cddfSDavid du Colombier 
6127dd7cddfSDavid du Colombier 	isgrey = dst->flags&Fgrey;
6137dd7cddfSDavid du Colombier 
6147dd7cddfSDavid du Colombier 	/*
6157dd7cddfSDavid du Colombier 	 * Buffering when src and dst are the same bitmap is sufficient but not
6167dd7cddfSDavid du Colombier 	 * necessary.  There are stronger conditions we could use.  We could
6177dd7cddfSDavid du Colombier 	 * check to see if the rectangles intersect, and if simply moving in the
6187dd7cddfSDavid du Colombier 	 * correct y direction can avoid the need to buffer.
6197dd7cddfSDavid du Colombier 	 */
6207dd7cddfSDavid du Colombier 	needbuf = (src->data == dst->data);
6217dd7cddfSDavid du Colombier 
6227dd7cddfSDavid du Colombier 	spar = getparam(src, sr, isgrey, needbuf);
6237dd7cddfSDavid du Colombier 	mpar = getparam(mask, mr, isgrey, needbuf);
6247dd7cddfSDavid du Colombier 	dpar = getparam(dst, r, isgrey, needbuf);
6257dd7cddfSDavid du Colombier 
6267dd7cddfSDavid du Colombier 	dir = (needbuf && byteaddr(dst, r.min) > byteaddr(src, sr.min)) ? -1 : 1;
6277dd7cddfSDavid du Colombier 	spar.dir = mpar.dir = dpar.dir = dir;
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier 	/*
6307dd7cddfSDavid du Colombier 	 * If the mask is purely boolean, we can convert from src to dst format
6317dd7cddfSDavid du Colombier 	 * when we read src, and then just copy it to dst where the mask tells us to.
6327dd7cddfSDavid du Colombier 	 * This requires a boolean (1-bit grey) mask and lack of a source alpha channel.
6337dd7cddfSDavid du Colombier 	 *
6347dd7cddfSDavid du Colombier 	 * The computation is accomplished by assigning the function pointers as follows:
6357dd7cddfSDavid du Colombier 	 *	rdsrc - read and convert source into dst format in a buffer
6367dd7cddfSDavid du Colombier 	 * 	rdmask - convert mask to bytes, set pointer to it
6377dd7cddfSDavid du Colombier 	 * 	rddst - fill with pointer to real dst data, but do no reads
6387dd7cddfSDavid du Colombier 	 *	calc - copy src onto dst when mask says to.
6397dd7cddfSDavid du Colombier 	 *	wrdst - do nothing
6407dd7cddfSDavid du Colombier 	 * This is slightly sleazy, since things aren't doing exactly what their names say,
6417dd7cddfSDavid du Colombier 	 * but it avoids a fair amount of code duplication to make this a case here
6427dd7cddfSDavid du Colombier 	 * rather than have a separate booldraw.
6437dd7cddfSDavid du Colombier 	 */
6447dd7cddfSDavid du Colombier if(drawdebug) iprint("flag %lud mchan %lux=?%x dd %d\n", src->flags&Falpha, mask->chan, GREY1, dst->depth);
6457dd7cddfSDavid du Colombier 	if(!(src->flags&Falpha) && mask->chan == GREY1 && dst->depth >= 8){
6467dd7cddfSDavid du Colombier if(drawdebug) iprint("boolcopy...");
6477dd7cddfSDavid du Colombier 		rdsrc = convfn(dst, &dpar, src, &spar);
6487dd7cddfSDavid du Colombier 		rddst = readptr;
6497dd7cddfSDavid du Colombier 		rdmask = readfn(mask);
6507dd7cddfSDavid du Colombier 		calc = boolcopyfn(dst, mask);
6517dd7cddfSDavid du Colombier 		wrdst = nullwrite;
6527dd7cddfSDavid du Colombier 	}else{
6537dd7cddfSDavid du Colombier 		/* usual alphadraw parameter fetching */
6547dd7cddfSDavid du Colombier 		rdsrc = readfn(src);
6557dd7cddfSDavid du Colombier 		rddst = readfn(dst);
6567dd7cddfSDavid du Colombier 		wrdst = writefn(dst);
6577dd7cddfSDavid du Colombier 		calc = alphacalc;
6587dd7cddfSDavid du Colombier 
6597dd7cddfSDavid du Colombier 		/*
6607dd7cddfSDavid du Colombier 		 * If there is no alpha channel, we'll ask for a grey channel
6617dd7cddfSDavid du Colombier 		 * and pretend it is the alpha.
6627dd7cddfSDavid du Colombier 		 */
6637dd7cddfSDavid du Colombier 		if(mask->flags&Falpha){
6647dd7cddfSDavid du Colombier 			rdmask = readalphafn(mask);
6657dd7cddfSDavid du Colombier 			mpar.alphaonly = 1;
6667dd7cddfSDavid du Colombier 		}else{
6677dd7cddfSDavid du Colombier 			mpar.greymaskcall = readfn(mask);
6687dd7cddfSDavid du Colombier 			mpar.convgrey = 1;
6697dd7cddfSDavid du Colombier 			rdmask = greymaskread;
6707dd7cddfSDavid du Colombier 
6717dd7cddfSDavid du Colombier 			/*
6727dd7cddfSDavid du Colombier 			 * Should really be above, but then boolcopyfns would have
6737dd7cddfSDavid du Colombier 			 * to deal with bit alignment, and I haven't written that.
6747dd7cddfSDavid du Colombier 			 *
6757dd7cddfSDavid du Colombier 			 * This is a common case for things like ellipse drawing.
6767dd7cddfSDavid du Colombier 			 * When there's no alpha involved and the mask is boolean,
6777dd7cddfSDavid du Colombier 			 * we can avoid all the division and multiplication.
6787dd7cddfSDavid du Colombier 			 */
6797dd7cddfSDavid du Colombier 			if(mask->chan == GREY1 && !(src->flags&Falpha))
6807dd7cddfSDavid du Colombier 				calc = boolcalc;
6817dd7cddfSDavid du Colombier 		}
6827dd7cddfSDavid du Colombier 	}
6837dd7cddfSDavid du Colombier 
6847dd7cddfSDavid du Colombier 	/*
6857dd7cddfSDavid du Colombier 	 * If the image has a small enough repl rectangle,
6867dd7cddfSDavid du Colombier 	 * we can just read each line once and cache them.
6877dd7cddfSDavid du Colombier 	 */
6887dd7cddfSDavid du Colombier 	if(spar.replcache){
6897dd7cddfSDavid du Colombier 		spar.replcall = rdsrc;
6907dd7cddfSDavid du Colombier 		rdsrc = replread;
6917dd7cddfSDavid du Colombier 	}
6927dd7cddfSDavid du Colombier 	if(mpar.replcache){
6937dd7cddfSDavid du Colombier 		mpar.replcall = rdmask;
6947dd7cddfSDavid du Colombier 		rdmask = replread;
6957dd7cddfSDavid du Colombier 	}
6967dd7cddfSDavid du Colombier 
6977dd7cddfSDavid du Colombier 	if(allocdrawbuf() < 0)
6987dd7cddfSDavid du Colombier 		return 0;
6997dd7cddfSDavid du Colombier 
7007dd7cddfSDavid du Colombier 	/*
7017dd7cddfSDavid du Colombier 	 * Before we were saving only offsets from drawbuf in the parameter
7027dd7cddfSDavid du Colombier 	 * structures; now that drawbuf has been grown to accomodate us,
7037dd7cddfSDavid du Colombier 	 * we can fill in the pointers.
7047dd7cddfSDavid du Colombier 	 */
7057dd7cddfSDavid du Colombier 	spar.bufbase = drawbuf+spar.bufoff;
7067dd7cddfSDavid du Colombier 	mpar.bufbase = drawbuf+mpar.bufoff;
7077dd7cddfSDavid du Colombier 	dpar.bufbase = drawbuf+dpar.bufoff;
7087dd7cddfSDavid du Colombier 	spar.convbuf = drawbuf+spar.convbufoff;
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier 	if(dir == 1){
7117dd7cddfSDavid du Colombier 		starty = 0;
7127dd7cddfSDavid du Colombier 		endy = dy;
7137dd7cddfSDavid du Colombier 	}else{
7147dd7cddfSDavid du Colombier 		starty = dy-1;
7157dd7cddfSDavid du Colombier 		endy = -1;
7167dd7cddfSDavid du Colombier 	}
7177dd7cddfSDavid du Colombier 
7187dd7cddfSDavid du Colombier 	/*
7197dd7cddfSDavid du Colombier 	 * srcy, masky, and dsty are offsets from the top of their
7207dd7cddfSDavid du Colombier 	 * respective Rectangles.  they need to be contained within
7217dd7cddfSDavid du Colombier 	 * the rectangles, so clipy can keep them there without division.
7227dd7cddfSDavid du Colombier  	 */
7237dd7cddfSDavid du Colombier 	srcy = (starty + sr.min.y - src->r.min.y)%Dy(src->r);
7247dd7cddfSDavid du Colombier 	masky = (starty + mr.min.y - mask->r.min.y)%Dy(mask->r);
7257dd7cddfSDavid du Colombier 	dsty = starty + r.min.y - dst->r.min.y;
7267dd7cddfSDavid du Colombier 
7277dd7cddfSDavid du Colombier 	assert(0 <= srcy && srcy < Dy(src->r));
7287dd7cddfSDavid du Colombier 	assert(0 <= masky && masky < Dy(mask->r));
7297dd7cddfSDavid du Colombier 	assert(0 <= dsty && dsty < Dy(dst->r));
7307dd7cddfSDavid du Colombier 
7317dd7cddfSDavid du Colombier 	for(y=starty; y!=endy; y+=dir, srcy+=dir, masky+=dir, dsty+=dir){
7327dd7cddfSDavid du Colombier 		clipy(src, &srcy);
7337dd7cddfSDavid du Colombier 		clipy(dst, &dsty);
7347dd7cddfSDavid du Colombier 		clipy(mask, &masky);
7357dd7cddfSDavid du Colombier 
7367dd7cddfSDavid du Colombier 		bsrc = rdsrc(&spar, spar.bufbase, srcy);
7377dd7cddfSDavid du Colombier DBG print("[");
7387dd7cddfSDavid du Colombier 		bmask = rdmask(&mpar, mpar.bufbase, masky);
7397dd7cddfSDavid du Colombier DBG print("]\n");
7407dd7cddfSDavid du Colombier 		bdst = rddst(&dpar, dpar.bufbase, dsty);
7417dd7cddfSDavid du Colombier DBG		dumpbuf("src", bsrc, dx);
7427dd7cddfSDavid du Colombier DBG		dumpbuf("mask", bmask, dx);
7437dd7cddfSDavid du Colombier DBG		dumpbuf("dst", bdst, dx);
7447dd7cddfSDavid du Colombier 		bdst = calc(bdst, bsrc, bmask, dx, isgrey);
7457dd7cddfSDavid du Colombier 		wrdst(&dpar, dpar.bytermin+dsty*dpar.bwidth, bdst);
7467dd7cddfSDavid du Colombier 	}
7477dd7cddfSDavid du Colombier 
7487dd7cddfSDavid du Colombier 	return 1;
7497dd7cddfSDavid du Colombier }
7507dd7cddfSDavid du Colombier #undef DBG
7517dd7cddfSDavid du Colombier 
7527dd7cddfSDavid du Colombier #define DBG if(0)
7537dd7cddfSDavid du Colombier static Buffer
7547dd7cddfSDavid du Colombier alphacalc(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey)
7557dd7cddfSDavid du Colombier {
7567dd7cddfSDavid du Colombier 	Buffer obdst;
7577dd7cddfSDavid du Colombier 	uchar *salpha, ones = ~0;
7587dd7cddfSDavid du Colombier 	int antialpha, sadelta;
7597dd7cddfSDavid du Colombier 	int i, sa, ma;
7607dd7cddfSDavid du Colombier 
7617dd7cddfSDavid du Colombier 	obdst = bdst;
7627dd7cddfSDavid du Colombier 	if((salpha = bsrc.alpha) == nil)
7637dd7cddfSDavid du Colombier 		salpha = &ones, sadelta = 0;
7647dd7cddfSDavid du Colombier 	else
7657dd7cddfSDavid du Colombier 		sadelta = bsrc.delta;
7667dd7cddfSDavid du Colombier 
7677dd7cddfSDavid du Colombier 	for(i=0; i<dx; i++){
7687dd7cddfSDavid du Colombier 		sa = *salpha;
7697dd7cddfSDavid du Colombier 		ma = *bmask.alpha;
7707dd7cddfSDavid du Colombier 		antialpha = 255-DIV255(ma*sa);
7717dd7cddfSDavid du Colombier 
7727dd7cddfSDavid du Colombier 		if(grey){
7737dd7cddfSDavid du Colombier DBG print("(%d %d in %d) over %d =", *bsrc.grey, sa, ma, *bdst.grey);
7747dd7cddfSDavid du Colombier 			*bdst.grey = DIV255(*bsrc.grey*ma+*bdst.grey*antialpha);
7757dd7cddfSDavid du Colombier DBG print(" %d\n", *bdst.grey);
7767dd7cddfSDavid du Colombier 			bsrc.grey += bsrc.delta;
7777dd7cddfSDavid du Colombier 			bdst.grey += bdst.delta;
7787dd7cddfSDavid du Colombier 		}else{
7797dd7cddfSDavid du Colombier 			*bdst.red = DIV255(*bsrc.red*ma+*bdst.red*antialpha);
7807dd7cddfSDavid du Colombier 			*bdst.grn = DIV255(*bsrc.grn*ma+*bdst.grn*antialpha);
7817dd7cddfSDavid du Colombier 			*bdst.blu = DIV255(*bsrc.blu*ma+*bdst.blu*antialpha);
7827dd7cddfSDavid du Colombier 
7837dd7cddfSDavid du Colombier 			bsrc.red += bsrc.delta;
7847dd7cddfSDavid du Colombier 			bsrc.blu += bsrc.delta;
7857dd7cddfSDavid du Colombier 			bsrc.grn += bsrc.delta;
7867dd7cddfSDavid du Colombier 
7877dd7cddfSDavid du Colombier 			bdst.red += bdst.delta;
7887dd7cddfSDavid du Colombier 			bdst.blu += bdst.delta;
7897dd7cddfSDavid du Colombier 			bdst.grn += bdst.delta;
7907dd7cddfSDavid du Colombier 		}
7917dd7cddfSDavid du Colombier 
7927dd7cddfSDavid du Colombier 		salpha += sadelta;
7937dd7cddfSDavid du Colombier 		bmask.alpha += bmask.delta;
7947dd7cddfSDavid du Colombier 
7957dd7cddfSDavid du Colombier 		if(bdst.alpha) {
7967dd7cddfSDavid du Colombier 			*bdst.alpha = DIV255(sa*ma+*bdst.alpha*(antialpha));
7977dd7cddfSDavid du Colombier 			bdst.alpha += bdst.delta;
7987dd7cddfSDavid du Colombier 		}
7997dd7cddfSDavid du Colombier 	}
8007dd7cddfSDavid du Colombier 	return obdst;
8017dd7cddfSDavid du Colombier }
8027dd7cddfSDavid du Colombier #undef DBG
8037dd7cddfSDavid du Colombier 
8047dd7cddfSDavid du Colombier #define DBG if(0)
8057dd7cddfSDavid du Colombier static Buffer
8067dd7cddfSDavid du Colombier boolcalc(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey)
8077dd7cddfSDavid du Colombier {
8087dd7cddfSDavid du Colombier 	Buffer obdst;
8097dd7cddfSDavid du Colombier 	int i, ma;
8107dd7cddfSDavid du Colombier 
8117dd7cddfSDavid du Colombier 	obdst = bdst;
8127dd7cddfSDavid du Colombier 	for(i=0; i<dx; i++){
8137dd7cddfSDavid du Colombier 		ma = *bmask.alpha;
8147dd7cddfSDavid du Colombier 
8157dd7cddfSDavid du Colombier 		if(grey){
8167dd7cddfSDavid du Colombier DBG print("(%d) in %d over %d =", *bsrc.grey, ma, *bdst.grey);
8177dd7cddfSDavid du Colombier 			if(ma)
8187dd7cddfSDavid du Colombier 				*bdst.grey = *bsrc.grey;
8197dd7cddfSDavid du Colombier DBG print(" %d\n", *bdst.grey);
8207dd7cddfSDavid du Colombier 			bsrc.grey += bsrc.delta;
8217dd7cddfSDavid du Colombier 			bdst.grey += bdst.delta;
8227dd7cddfSDavid du Colombier 		}else{
8237dd7cddfSDavid du Colombier 			if(ma){
8247dd7cddfSDavid du Colombier 				*bdst.red = *bsrc.red*ma;
8257dd7cddfSDavid du Colombier 				*bdst.grn = *bsrc.grn*ma;
8267dd7cddfSDavid du Colombier 				*bdst.blu = *bsrc.blu*ma;
8277dd7cddfSDavid du Colombier 			}
8287dd7cddfSDavid du Colombier 
8297dd7cddfSDavid du Colombier 			bsrc.red += bsrc.delta;
8307dd7cddfSDavid du Colombier 			bsrc.blu += bsrc.delta;
8317dd7cddfSDavid du Colombier 			bsrc.grn += bsrc.delta;
8327dd7cddfSDavid du Colombier 
8337dd7cddfSDavid du Colombier 			bdst.red += bdst.delta;
8347dd7cddfSDavid du Colombier 			bdst.blu += bdst.delta;
8357dd7cddfSDavid du Colombier 			bdst.grn += bdst.delta;
8367dd7cddfSDavid du Colombier 		}
8377dd7cddfSDavid du Colombier 
8387dd7cddfSDavid du Colombier 		bmask.alpha += bmask.delta;
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier 		if(bdst.alpha) {
8417dd7cddfSDavid du Colombier 			if(ma)
8427dd7cddfSDavid du Colombier 				*bdst.alpha = ma;
8437dd7cddfSDavid du Colombier 			bdst.alpha += bdst.delta;
8447dd7cddfSDavid du Colombier 		}
8457dd7cddfSDavid du Colombier 	}
8467dd7cddfSDavid du Colombier 	return obdst;
8477dd7cddfSDavid du Colombier }
8487dd7cddfSDavid du Colombier #undef DBG
8497dd7cddfSDavid du Colombier 
8507dd7cddfSDavid du Colombier /*
8517dd7cddfSDavid du Colombier  * Replicated cached scan line read.  Call the function listed in the Param,
8527dd7cddfSDavid du Colombier  * but cache the result so that for replicated images we only do the work once.
8537dd7cddfSDavid du Colombier  */
8547dd7cddfSDavid du Colombier static Buffer
8557dd7cddfSDavid du Colombier replread(Param *p, uchar *notusedbuf, int y)
8567dd7cddfSDavid du Colombier {
8577dd7cddfSDavid du Colombier 	Buffer *b;
8587dd7cddfSDavid du Colombier 
8597dd7cddfSDavid du Colombier 	b = &p->bcache[y];
8607dd7cddfSDavid du Colombier 	if((p->bfilled & (1<<y)) == 0){
8617dd7cddfSDavid du Colombier 		p->bfilled |= 1<<y;
8627dd7cddfSDavid du Colombier 		*b = p->replcall(p, p->bufbase+y*p->bufdelta, y);
8637dd7cddfSDavid du Colombier 	}
8647dd7cddfSDavid du Colombier 	return *b;
8657dd7cddfSDavid du Colombier }
8667dd7cddfSDavid du Colombier 
8677dd7cddfSDavid du Colombier /*
8687dd7cddfSDavid du Colombier  * Alpha reading function that simply relabels the grey pointer.
8697dd7cddfSDavid du Colombier  */
8707dd7cddfSDavid du Colombier static Buffer
8717dd7cddfSDavid du Colombier greymaskread(Param *p, uchar *buf, int y)
8727dd7cddfSDavid du Colombier {
8737dd7cddfSDavid du Colombier 	Buffer b;
8747dd7cddfSDavid du Colombier 
8757dd7cddfSDavid du Colombier 	b = p->greymaskcall(p, buf, y);
8767dd7cddfSDavid du Colombier 	b.alpha = b.grey;
8777dd7cddfSDavid du Colombier 	return b;
8787dd7cddfSDavid du Colombier }
8797dd7cddfSDavid du Colombier 
8807dd7cddfSDavid du Colombier #define DBG if(0)
8817dd7cddfSDavid du Colombier static Buffer
8827dd7cddfSDavid du Colombier readnbit(Param *p, uchar *buf, int y)
8837dd7cddfSDavid du Colombier {
8847dd7cddfSDavid du Colombier 	Buffer b;
8857dd7cddfSDavid du Colombier 	Memimage *img;
8867dd7cddfSDavid du Colombier 	uchar *repl, *r, *w, *ow, bits;
8877dd7cddfSDavid du Colombier 	int i, n, sh, depth, x, dx, npack, nbits;
8887dd7cddfSDavid du Colombier 
8897dd7cddfSDavid du Colombier 	b.grey = w = buf;
8907dd7cddfSDavid du Colombier 	b.red = b.blu = b.grn = w;
8917dd7cddfSDavid du Colombier 	b.alpha = nil;
8927dd7cddfSDavid du Colombier 	b.delta = 1;
8937dd7cddfSDavid du Colombier 
8947dd7cddfSDavid du Colombier 	dx = p->dx;
8957dd7cddfSDavid du Colombier 	img = p->img;
8967dd7cddfSDavid du Colombier 	depth = img->depth;
8977dd7cddfSDavid du Colombier 	repl = &replbit[depth][0];
8987dd7cddfSDavid du Colombier 	npack = 8/depth;
8997dd7cddfSDavid du Colombier 	sh = 8-depth;
9007dd7cddfSDavid du Colombier 
9017dd7cddfSDavid du Colombier 	/* copy from p->r.min.x until end of repl rectangle */
9027dd7cddfSDavid du Colombier 	x = p->r.min.x;
9037dd7cddfSDavid du Colombier 	n = dx;
9047dd7cddfSDavid du Colombier 	if(n > p->img->r.max.x - x)
9057dd7cddfSDavid du Colombier 		n = p->img->r.max.x - x;
9067dd7cddfSDavid du Colombier 
9077dd7cddfSDavid du Colombier 	r = p->bytermin + y*p->bwidth;
9087dd7cddfSDavid du Colombier DBG print("readnbit dx %d %p=%p+%d*%d, *r=%d fetch %d ", dx, r, p->bytermin, y, p->bwidth, *r, n);
9097dd7cddfSDavid du Colombier 	bits = *r++;
9107dd7cddfSDavid du Colombier 	nbits = 8;
9117dd7cddfSDavid du Colombier 	if(i=x&(npack-1)){
9127dd7cddfSDavid du Colombier DBG print("throwaway %d...", i);
9137dd7cddfSDavid du Colombier 		bits <<= depth*i;
9147dd7cddfSDavid du Colombier 		nbits -= depth*i;
9157dd7cddfSDavid du Colombier 	}
9167dd7cddfSDavid du Colombier 	for(i=0; i<n; i++){
9177dd7cddfSDavid du Colombier 		if(nbits == 0){
9187dd7cddfSDavid du Colombier DBG print("(%.2ux)...", *r);
9197dd7cddfSDavid du Colombier 			bits = *r++;
9207dd7cddfSDavid du Colombier 			nbits = 8;
9217dd7cddfSDavid du Colombier 		}
9227dd7cddfSDavid du Colombier 		*w++ = repl[bits>>sh];
9237dd7cddfSDavid du Colombier DBG print("bit %x...", repl[bits>>sh]);
9247dd7cddfSDavid du Colombier 		bits <<= depth;
9257dd7cddfSDavid du Colombier 		nbits -= depth;
9267dd7cddfSDavid du Colombier 	}
9277dd7cddfSDavid du Colombier 	dx -= n;
9287dd7cddfSDavid du Colombier 	if(dx == 0)
9297dd7cddfSDavid du Colombier 		return b;
9307dd7cddfSDavid du Colombier 
9317dd7cddfSDavid du Colombier 	assert(x+i == p->img->r.max.x);
9327dd7cddfSDavid du Colombier 
9337dd7cddfSDavid du Colombier 	/* copy from beginning of repl rectangle until where we were before. */
9347dd7cddfSDavid du Colombier 	x = p->img->r.min.x;
9357dd7cddfSDavid du Colombier 	n = dx;
9367dd7cddfSDavid du Colombier 	if(n > p->r.min.x - x)
9377dd7cddfSDavid du Colombier 		n = p->r.min.x - x;
9387dd7cddfSDavid du Colombier 
9397dd7cddfSDavid du Colombier 	r = p->bytey0s + y*p->bwidth;
9407dd7cddfSDavid du Colombier DBG print("x=%d r=%p...", x, r);
9417dd7cddfSDavid du Colombier 	bits = *r++;
9427dd7cddfSDavid du Colombier 	nbits = 8;
9437dd7cddfSDavid du Colombier 	if(i=x&(npack-1)){
9447dd7cddfSDavid du Colombier 		bits <<= depth*i;
9457dd7cddfSDavid du Colombier 		nbits -= depth*i;
9467dd7cddfSDavid du Colombier 	}
9477dd7cddfSDavid du Colombier DBG print("nbits=%d...", nbits);
9487dd7cddfSDavid du Colombier 	for(i=0; i<n; i++){
9497dd7cddfSDavid du Colombier 		if(nbits == 0){
9507dd7cddfSDavid du Colombier 			bits = *r++;
9517dd7cddfSDavid du Colombier 			nbits = 8;
9527dd7cddfSDavid du Colombier 		}
9537dd7cddfSDavid du Colombier 		*w++ = repl[bits>>sh];
9547dd7cddfSDavid du Colombier DBG print("bit %x...", repl[bits>>sh]);
9557dd7cddfSDavid du Colombier 		bits <<= depth;
9567dd7cddfSDavid du Colombier 		nbits -= depth;
9577dd7cddfSDavid du Colombier DBG print("bits %x nbits %d...", bits, nbits);
9587dd7cddfSDavid du Colombier 	}
9597dd7cddfSDavid du Colombier 	dx -= n;
9607dd7cddfSDavid du Colombier 	if(dx == 0)
9617dd7cddfSDavid du Colombier 		return b;
9627dd7cddfSDavid du Colombier 
9637dd7cddfSDavid du Colombier 	assert(dx > 0);
9647dd7cddfSDavid du Colombier 	/* now we have exactly one full scan line: just replicate the buffer itself until we are done */
9657dd7cddfSDavid du Colombier 	ow = buf;
9667dd7cddfSDavid du Colombier 	while(dx--)
9677dd7cddfSDavid du Colombier 		*w++ = *ow++;
9687dd7cddfSDavid du Colombier 
9697dd7cddfSDavid du Colombier 	return b;
9707dd7cddfSDavid du Colombier }
9717dd7cddfSDavid du Colombier #undef DBG
9727dd7cddfSDavid du Colombier 
9737dd7cddfSDavid du Colombier #define DBG if(0)
9747dd7cddfSDavid du Colombier static void
9757dd7cddfSDavid du Colombier writenbit(Param *p, uchar *w, Buffer src)
9767dd7cddfSDavid du Colombier {
9777dd7cddfSDavid du Colombier 	uchar *r;
9787dd7cddfSDavid du Colombier 	ulong bits;
9797dd7cddfSDavid du Colombier 	int i, sh, depth, npack, nbits, x, ex;
9807dd7cddfSDavid du Colombier 
9817dd7cddfSDavid du Colombier 	assert(src.grey != nil && src.delta == 1);
9827dd7cddfSDavid du Colombier 
9837dd7cddfSDavid du Colombier 	x = p->r.min.x;
9847dd7cddfSDavid du Colombier 	ex = x+p->dx;
9857dd7cddfSDavid du Colombier 	depth = p->img->depth;
9867dd7cddfSDavid du Colombier 	npack = 8/depth;
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 	i=x&(npack-1);
9897dd7cddfSDavid du Colombier 	bits = i ? (*w >> (8-depth*i)) : 0;
9907dd7cddfSDavid du Colombier 	nbits = depth*i;
9917dd7cddfSDavid du Colombier 	sh = 8-depth;
9927dd7cddfSDavid du Colombier 	r = src.grey;
9937dd7cddfSDavid du Colombier 
9947dd7cddfSDavid du Colombier 	for(; x<ex; x++){
9957dd7cddfSDavid du Colombier 		bits <<= depth;
9967dd7cddfSDavid du Colombier DBG print(" %x", *r);
9977dd7cddfSDavid du Colombier 		bits |= (*r++ >> sh);
9987dd7cddfSDavid du Colombier 		nbits += depth;
9997dd7cddfSDavid du Colombier 		if(nbits == 8){
10007dd7cddfSDavid du Colombier 			*w++ = bits;
10017dd7cddfSDavid du Colombier 			nbits = 0;
10027dd7cddfSDavid du Colombier 		}
10037dd7cddfSDavid du Colombier 	}
10047dd7cddfSDavid du Colombier 
10057dd7cddfSDavid du Colombier 	if(nbits){
10067dd7cddfSDavid du Colombier 		sh = 8-nbits;
10077dd7cddfSDavid du Colombier 		bits <<= sh;
10087dd7cddfSDavid du Colombier 		bits |= *w & ((1<<sh)-1);
10097dd7cddfSDavid du Colombier 		*w = bits;
10107dd7cddfSDavid du Colombier 	}
10117dd7cddfSDavid du Colombier DBG print("\n");
10127dd7cddfSDavid du Colombier 	return;
10137dd7cddfSDavid du Colombier }
10147dd7cddfSDavid du Colombier #undef DBG
10157dd7cddfSDavid du Colombier 
10167dd7cddfSDavid du Colombier static Buffer
10177dd7cddfSDavid du Colombier readcmap(Param *p, uchar *buf, int y)
10187dd7cddfSDavid du Colombier {
10197dd7cddfSDavid du Colombier 	Buffer b;
10207dd7cddfSDavid du Colombier 	int i, dx, convgrey;
10217dd7cddfSDavid du Colombier 	uchar *q, *cmap, *begin, *end, *r, *w;
10227dd7cddfSDavid du Colombier 
10237dd7cddfSDavid du Colombier 	begin = p->bytey0s + y*p->bwidth;
10247dd7cddfSDavid du Colombier 	r = p->bytermin + y*p->bwidth;
10257dd7cddfSDavid du Colombier 	end = p->bytey0e + y*p->bwidth;
10267dd7cddfSDavid du Colombier 	cmap = p->img->cmap->cmap2rgb;
10277dd7cddfSDavid du Colombier 	convgrey = p->convgrey;
10287dd7cddfSDavid du Colombier 
10297dd7cddfSDavid du Colombier 	w = buf;
10307dd7cddfSDavid du Colombier 	dx = p->dx;
10317dd7cddfSDavid du Colombier 	for(i=0; i<dx; i++){
10327dd7cddfSDavid du Colombier 		q = cmap+*r++*3;
10337dd7cddfSDavid du Colombier 		if(r == end)
10347dd7cddfSDavid du Colombier 			r = begin;
10357dd7cddfSDavid du Colombier 		if(convgrey){
10367dd7cddfSDavid du Colombier 			*w++ = RGB2K(q[0], q[1], q[2]);
10377dd7cddfSDavid du Colombier 		}else{
10387dd7cddfSDavid du Colombier 			*w++ = q[2];	/* blue */
10397dd7cddfSDavid du Colombier 			*w++ = q[1];	/* green */
10407dd7cddfSDavid du Colombier 			*w++ = q[0];	/* red */
10417dd7cddfSDavid du Colombier 		}
10427dd7cddfSDavid du Colombier 	}
10437dd7cddfSDavid du Colombier 
10447dd7cddfSDavid du Colombier 	if(convgrey){
10457dd7cddfSDavid du Colombier 		b.alpha = nil;
10467dd7cddfSDavid du Colombier 		b.grey = buf;
10477dd7cddfSDavid du Colombier 		b.red = b.blu = b.grn = buf;
10487dd7cddfSDavid du Colombier 		b.delta = 1;
10497dd7cddfSDavid du Colombier 	}else{
10507dd7cddfSDavid du Colombier 		b.blu = buf;
10517dd7cddfSDavid du Colombier 		b.grn = buf+1;
10527dd7cddfSDavid du Colombier 		b.red = buf+2;
10537dd7cddfSDavid du Colombier 		b.alpha = nil;
10547dd7cddfSDavid du Colombier 		b.grey = nil;
10557dd7cddfSDavid du Colombier 		b.delta = 3;
10567dd7cddfSDavid du Colombier 	}
10577dd7cddfSDavid du Colombier 	return b;
10587dd7cddfSDavid du Colombier }
10597dd7cddfSDavid du Colombier 
10607dd7cddfSDavid du Colombier static void
10617dd7cddfSDavid du Colombier writecmap(Param *p, uchar *w, Buffer src)
10627dd7cddfSDavid du Colombier {
10637dd7cddfSDavid du Colombier 	uchar *cmap, *red, *grn, *blu;
10647dd7cddfSDavid du Colombier 	int i, dx, delta;
10657dd7cddfSDavid du Colombier 
10667dd7cddfSDavid du Colombier 	cmap = p->img->cmap->rgb2cmap;
10677dd7cddfSDavid du Colombier 
10687dd7cddfSDavid du Colombier 	delta = src.delta;
10697dd7cddfSDavid du Colombier 	red= src.red;
10707dd7cddfSDavid du Colombier 	grn = src.grn;
10717dd7cddfSDavid du Colombier 	blu = src.blu;
10727dd7cddfSDavid du Colombier 
10737dd7cddfSDavid du Colombier 	dx = p->dx;
10747dd7cddfSDavid du Colombier 	for(i=0; i<dx; i++, red+=delta, grn+=delta, blu+=delta)
10757dd7cddfSDavid du Colombier 		*w++ = cmap[(*red>>4)*256+(*grn>>4)*16+(*blu>>4)];
10767dd7cddfSDavid du Colombier }
10777dd7cddfSDavid du Colombier 
10787dd7cddfSDavid du Colombier static Buffer
10797dd7cddfSDavid du Colombier readbyte(Param *p, uchar *buf, int y)
10807dd7cddfSDavid du Colombier {
10817dd7cddfSDavid du Colombier 	Buffer b;
10827dd7cddfSDavid du Colombier 	Memimage *img;
10837dd7cddfSDavid du Colombier 	int dx, isgrey, convgrey, alphaonly, copyalpha, i, nb;
10847dd7cddfSDavid du Colombier 	uchar *begin, *end, *r, *w, *rrepl, *grepl, *brepl, *arepl, *krepl;
10857dd7cddfSDavid du Colombier 	uchar ured, ugrn, ublu;
10867dd7cddfSDavid du Colombier 	ulong u;
10877dd7cddfSDavid du Colombier 
10887dd7cddfSDavid du Colombier 	img = p->img;
10897dd7cddfSDavid du Colombier 	begin = p->bytey0s + y*p->bwidth;
10907dd7cddfSDavid du Colombier 	r = p->bytermin + y*p->bwidth;
10917dd7cddfSDavid du Colombier 	end = p->bytey0e + y*p->bwidth;
10927dd7cddfSDavid du Colombier 
10937dd7cddfSDavid du Colombier 	w = buf;
10947dd7cddfSDavid du Colombier 	dx = p->dx;
10957dd7cddfSDavid du Colombier 	nb = img->depth/8;
10967dd7cddfSDavid du Colombier 
10977dd7cddfSDavid du Colombier 	convgrey = p->convgrey;	/* convert rgb to grey */
10987dd7cddfSDavid du Colombier 	isgrey = img->flags&Fgrey;
10997dd7cddfSDavid du Colombier 	alphaonly = p->alphaonly;
1100*59cc4ca5SDavid du Colombier 	copyalpha = (img->flags&Falpha) ? 1 : 0;
11017dd7cddfSDavid du Colombier 
11027dd7cddfSDavid du Colombier 	/* if we can, avoid processing everything */
11037dd7cddfSDavid du Colombier 	if(!(img->flags&Frepl) && !convgrey && (img->flags&Fbytes)){
11047dd7cddfSDavid du Colombier 		memset(&b, 0, sizeof b);
11057dd7cddfSDavid du Colombier 		if(p->needbuf){
11067dd7cddfSDavid du Colombier 			memmove(buf, r, dx*nb);
11077dd7cddfSDavid du Colombier 			r = buf;
11087dd7cddfSDavid du Colombier 		}
11097dd7cddfSDavid du Colombier 		if(copyalpha)
11107dd7cddfSDavid du Colombier 			b.alpha = r+img->shift[CAlpha]/8;
11117dd7cddfSDavid du Colombier 		if(isgrey){
11127dd7cddfSDavid du Colombier 			b.grey = r+img->shift[CGrey]/8;
11137dd7cddfSDavid du Colombier 			b.red = b.grn = b.blu = b.grey;
11147dd7cddfSDavid du Colombier 		}else{
11157dd7cddfSDavid du Colombier 			b.red = r+img->shift[CRed]/8;
11167dd7cddfSDavid du Colombier 			b.grn = r+img->shift[CGreen]/8;
11177dd7cddfSDavid du Colombier 			b.blu = r+img->shift[CBlue]/8;
11187dd7cddfSDavid du Colombier 		}
11197dd7cddfSDavid du Colombier 		b.delta = nb;
11207dd7cddfSDavid du Colombier 		return b;
11217dd7cddfSDavid du Colombier 	}
11227dd7cddfSDavid du Colombier 
11237dd7cddfSDavid du Colombier 	rrepl = replbit[img->nbits[CRed]];
11247dd7cddfSDavid du Colombier 	grepl = replbit[img->nbits[CGreen]];
11257dd7cddfSDavid du Colombier 	brepl = replbit[img->nbits[CBlue]];
11267dd7cddfSDavid du Colombier 	arepl = replbit[img->nbits[CAlpha]];
11277dd7cddfSDavid du Colombier 	krepl = replbit[img->nbits[CGrey]];
11287dd7cddfSDavid du Colombier 
11297dd7cddfSDavid du Colombier 	for(i=0; i<dx; i++){
11307dd7cddfSDavid du Colombier 		u = r[0] | (r[1]<<8) | (r[2]<<16) | (r[3]<<24);
11317dd7cddfSDavid du Colombier 		if(copyalpha)
11327dd7cddfSDavid du Colombier 			*w++ = arepl[(u>>img->shift[CAlpha]) & img->mask[CAlpha]];
11337dd7cddfSDavid du Colombier 
11347dd7cddfSDavid du Colombier 		if(isgrey)
11357dd7cddfSDavid du Colombier 			*w++ = krepl[(u >> img->shift[CGrey]) & img->mask[CGrey]];
11367dd7cddfSDavid du Colombier 		else if(!alphaonly){
11377dd7cddfSDavid du Colombier 			ured = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]];
11387dd7cddfSDavid du Colombier 			ugrn = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]];
11397dd7cddfSDavid du Colombier 			ublu = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]];
11407dd7cddfSDavid du Colombier 			if(convgrey){
11417dd7cddfSDavid du Colombier 				*w++ = RGB2K(ured, ugrn, ublu);
11427dd7cddfSDavid du Colombier 			}else{
11437dd7cddfSDavid du Colombier 				*w++ = brepl[(u >> img->shift[CBlue]) & img->mask[CBlue]];
11447dd7cddfSDavid du Colombier 				*w++ = grepl[(u >> img->shift[CGreen]) & img->mask[CGreen]];
11457dd7cddfSDavid du Colombier 				*w++ = rrepl[(u >> img->shift[CRed]) & img->mask[CRed]];
11467dd7cddfSDavid du Colombier 			}
11477dd7cddfSDavid du Colombier 		}
11487dd7cddfSDavid du Colombier 		r += nb;
11497dd7cddfSDavid du Colombier 		if(r == end)
11507dd7cddfSDavid du Colombier 			r = begin;
11517dd7cddfSDavid du Colombier 	}
11527dd7cddfSDavid du Colombier 
11537dd7cddfSDavid du Colombier 	b.alpha = copyalpha ? buf : nil;
11547dd7cddfSDavid du Colombier 	if(alphaonly){
11557dd7cddfSDavid du Colombier 		b.red = b.grn = b.blu = b.grey = nil;
11567dd7cddfSDavid du Colombier 		b.delta = 1;
11577dd7cddfSDavid du Colombier 	}else if(isgrey || convgrey){
11587dd7cddfSDavid du Colombier 		b.grey = buf+copyalpha;
11597dd7cddfSDavid du Colombier 		b.red = b.grn = b.blu = buf+copyalpha;
11607dd7cddfSDavid du Colombier 		b.delta = copyalpha+1;
11617dd7cddfSDavid du Colombier 	}else{
11627dd7cddfSDavid du Colombier 		b.blu = buf+copyalpha;
11637dd7cddfSDavid du Colombier 		b.grn = buf+copyalpha+1;
11647dd7cddfSDavid du Colombier 		b.grey = nil;
11657dd7cddfSDavid du Colombier 		b.red = buf+copyalpha+2;
11667dd7cddfSDavid du Colombier 		b.delta = copyalpha+3;
11677dd7cddfSDavid du Colombier 	}
11687dd7cddfSDavid du Colombier 	return b;
11697dd7cddfSDavid du Colombier }
11707dd7cddfSDavid du Colombier 
11717dd7cddfSDavid du Colombier #define DBG if(0)
11727dd7cddfSDavid du Colombier static void
11737dd7cddfSDavid du Colombier writebyte(Param *p, uchar *w, Buffer src)
11747dd7cddfSDavid du Colombier {
11757dd7cddfSDavid du Colombier 	Memimage *img;
11767dd7cddfSDavid du Colombier 	int i, isalpha, isgrey, nb, delta, dx, adelta;
11777dd7cddfSDavid du Colombier 	uchar ff, *red, *grn, *blu, *grey, *alpha;
11787dd7cddfSDavid du Colombier 	ulong u, mask;
11797dd7cddfSDavid du Colombier 
11807dd7cddfSDavid du Colombier 	img = p->img;
11817dd7cddfSDavid du Colombier 
11827dd7cddfSDavid du Colombier 	red = src.red;
11837dd7cddfSDavid du Colombier 	grn = src.grn;
11847dd7cddfSDavid du Colombier 	blu = src.blu;
11857dd7cddfSDavid du Colombier 	alpha = src.alpha;
11867dd7cddfSDavid du Colombier 	delta = src.delta;
11877dd7cddfSDavid du Colombier 	grey = src.grey;
11887dd7cddfSDavid du Colombier 	dx = p->dx;
11897dd7cddfSDavid du Colombier 
11907dd7cddfSDavid du Colombier 	nb = img->depth/8;
11917dd7cddfSDavid du Colombier 	mask = (nb==4) ? 0 : ~((1<<img->depth)-1);
11927dd7cddfSDavid du Colombier 
11937dd7cddfSDavid du Colombier 	isalpha = img->flags&Falpha;
11947dd7cddfSDavid du Colombier 	isgrey = img->flags&Fgrey;
11957dd7cddfSDavid du Colombier 	adelta = src.delta;
11967dd7cddfSDavid du Colombier 
11977dd7cddfSDavid du Colombier 	if(isalpha && alpha == nil){
11987dd7cddfSDavid du Colombier 		ff = 0xFF;
11997dd7cddfSDavid du Colombier 		alpha = &ff;
12007dd7cddfSDavid du Colombier 		adelta = 0;
12017dd7cddfSDavid du Colombier 	}
12027dd7cddfSDavid du Colombier 
12037dd7cddfSDavid du Colombier 	for(i=0; i<dx; i++){
12047dd7cddfSDavid du Colombier 		u = w[0] | (w[1]<<8) | (w[2]<<16) | (w[3]<<24);
12057dd7cddfSDavid du Colombier DBG print("u %.8lux...", u);
12067dd7cddfSDavid du Colombier 		u &= mask;
12077dd7cddfSDavid du Colombier DBG print("&mask %.8lux...", u);
12087dd7cddfSDavid du Colombier 		if(isgrey){
12097dd7cddfSDavid du Colombier 			u |= ((*grey >> (8-img->nbits[CGrey])) & img->mask[CGrey]) << img->shift[CGrey];
12107dd7cddfSDavid du Colombier DBG print("|grey %.8lux...", u);
12117dd7cddfSDavid du Colombier 			grey += delta;
12127dd7cddfSDavid du Colombier 		}else{
12137dd7cddfSDavid du Colombier 			u |= ((*red >> (8-img->nbits[CRed])) & img->mask[CRed]) << img->shift[CRed];
12147dd7cddfSDavid du Colombier 			u |= ((*grn >> (8-img->nbits[CGreen])) & img->mask[CGreen]) << img->shift[CGreen];
12157dd7cddfSDavid du Colombier 			u |= ((*blu >> (8-img->nbits[CBlue])) & img->mask[CBlue]) << img->shift[CBlue];
12167dd7cddfSDavid du Colombier 			red += delta;
12177dd7cddfSDavid du Colombier 			grn += delta;
12187dd7cddfSDavid du Colombier 			blu += delta;
12197dd7cddfSDavid du Colombier DBG print("|rgb %.8lux...", u);
12207dd7cddfSDavid du Colombier 		}
12217dd7cddfSDavid du Colombier 
12227dd7cddfSDavid du Colombier 		if(isalpha){
12237dd7cddfSDavid du Colombier 			u |= ((*alpha >> (8-img->nbits[CAlpha])) & img->mask[CAlpha]) << img->shift[CAlpha];
12247dd7cddfSDavid du Colombier 			alpha += adelta;
12257dd7cddfSDavid du Colombier DBG print("|alpha %.8lux...", u);
12267dd7cddfSDavid du Colombier 		}
12277dd7cddfSDavid du Colombier 
12287dd7cddfSDavid du Colombier 		w[0] = u;
12297dd7cddfSDavid du Colombier 		w[1] = u>>8;
12307dd7cddfSDavid du Colombier 		w[2] = u>>16;
12317dd7cddfSDavid du Colombier 		w[3] = u>>24;
12327dd7cddfSDavid du Colombier 		w += nb;
12337dd7cddfSDavid du Colombier 	}
12347dd7cddfSDavid du Colombier }
12357dd7cddfSDavid du Colombier #undef DBG
12367dd7cddfSDavid du Colombier 
12377dd7cddfSDavid du Colombier static Readfn*
12387dd7cddfSDavid du Colombier readfn(Memimage *img)
12397dd7cddfSDavid du Colombier {
12407dd7cddfSDavid du Colombier 	if(img->depth < 8)
12417dd7cddfSDavid du Colombier 		return readnbit;
12427dd7cddfSDavid du Colombier 	if(img->chan == CMAP8)
12437dd7cddfSDavid du Colombier 		return readcmap;
12447dd7cddfSDavid du Colombier 	return readbyte;
12457dd7cddfSDavid du Colombier }
12467dd7cddfSDavid du Colombier 
12477dd7cddfSDavid du Colombier static Readfn*
12487dd7cddfSDavid du Colombier readalphafn(Memimage *notused)
12497dd7cddfSDavid du Colombier {
12507dd7cddfSDavid du Colombier 	return readbyte;
12517dd7cddfSDavid du Colombier }
12527dd7cddfSDavid du Colombier 
12537dd7cddfSDavid du Colombier static Writefn*
12547dd7cddfSDavid du Colombier writefn(Memimage *img)
12557dd7cddfSDavid du Colombier {
12567dd7cddfSDavid du Colombier 	if(img->depth < 8)
12577dd7cddfSDavid du Colombier 		return writenbit;
12587dd7cddfSDavid du Colombier 	if(img->chan == CMAP8)
12597dd7cddfSDavid du Colombier 		return writecmap;
12607dd7cddfSDavid du Colombier 	return writebyte;
12617dd7cddfSDavid du Colombier }
12627dd7cddfSDavid du Colombier 
12637dd7cddfSDavid du Colombier static void
12647dd7cddfSDavid du Colombier nullwrite(Param *notusedpar, uchar *notusedbuf, Buffer notusedb)
12657dd7cddfSDavid du Colombier {
12667dd7cddfSDavid du Colombier }
12677dd7cddfSDavid du Colombier 
12687dd7cddfSDavid du Colombier static Buffer
12697dd7cddfSDavid du Colombier readptr(Param *p, uchar *notusedbuf, int y)
12707dd7cddfSDavid du Colombier {
12717dd7cddfSDavid du Colombier 	Buffer b;
12727dd7cddfSDavid du Colombier 	uchar *q;
12737dd7cddfSDavid du Colombier 
12747dd7cddfSDavid du Colombier 	q = p->bytermin + y*p->bwidth;
12757dd7cddfSDavid du Colombier 	b.red = q;	/* ptr to data */
12767dd7cddfSDavid du Colombier 	b.grn = b.blu = b.grey = b.alpha = nil;
12777dd7cddfSDavid du Colombier 	b.delta = p->img->depth/8;
12787dd7cddfSDavid du Colombier 	return b;
12797dd7cddfSDavid du Colombier }
12807dd7cddfSDavid du Colombier 
12817dd7cddfSDavid du Colombier static Buffer
12827dd7cddfSDavid du Colombier boolmemmove(Buffer bdst, Buffer bsrc, Buffer notusedb, int dx, int notusedi)
12837dd7cddfSDavid du Colombier {
12847dd7cddfSDavid du Colombier 	memmove(bdst.red, bsrc.red, dx*bdst.delta);
12857dd7cddfSDavid du Colombier 	return bdst;
12867dd7cddfSDavid du Colombier }
12877dd7cddfSDavid du Colombier 
12887dd7cddfSDavid du Colombier static Buffer
12897dd7cddfSDavid du Colombier boolcopy8(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int notusedi)
12907dd7cddfSDavid du Colombier {
12917dd7cddfSDavid du Colombier 	uchar *m, *r, *w, *ew;
12927dd7cddfSDavid du Colombier 
12937dd7cddfSDavid du Colombier 	m = bmask.grey;
12947dd7cddfSDavid du Colombier 	w = bdst.red;
12957dd7cddfSDavid du Colombier 	r = bsrc.red;
12967dd7cddfSDavid du Colombier 	ew = w+dx;
12977dd7cddfSDavid du Colombier 	for(; w < ew; w++,r++)
12987dd7cddfSDavid du Colombier 		if(*m++)
12997dd7cddfSDavid du Colombier 			*w = *r;
13007dd7cddfSDavid du Colombier 	return bdst;	/* not used */
13017dd7cddfSDavid du Colombier }
13027dd7cddfSDavid du Colombier 
13037dd7cddfSDavid du Colombier static Buffer
13047dd7cddfSDavid du Colombier boolcopy16(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int notusedi)
13057dd7cddfSDavid du Colombier {
13067dd7cddfSDavid du Colombier 	uchar *m;
13077dd7cddfSDavid du Colombier 	ushort *r, *w, *ew;
13087dd7cddfSDavid du Colombier 
13097dd7cddfSDavid du Colombier 	m = bmask.grey;
13107dd7cddfSDavid du Colombier 	w = (ushort*)bdst.red;
13117dd7cddfSDavid du Colombier 	r = (ushort*)bsrc.red;
13127dd7cddfSDavid du Colombier 	ew = w+dx;
13137dd7cddfSDavid du Colombier 	for(; w < ew; w++,r++)
13147dd7cddfSDavid du Colombier 		if(*m++)
13157dd7cddfSDavid du Colombier 			*w = *r;
13167dd7cddfSDavid du Colombier 	return bdst;	/* not used */
13177dd7cddfSDavid du Colombier }
13187dd7cddfSDavid du Colombier 
13197dd7cddfSDavid du Colombier static Buffer
13207dd7cddfSDavid du Colombier boolcopy24(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int notusedi)
13217dd7cddfSDavid du Colombier {
13227dd7cddfSDavid du Colombier 	uchar *m;
13237dd7cddfSDavid du Colombier 	uchar *r, *w, *ew;
13247dd7cddfSDavid du Colombier 
13257dd7cddfSDavid du Colombier 	m = bmask.grey;
13267dd7cddfSDavid du Colombier 	w = bdst.red;
13277dd7cddfSDavid du Colombier 	r = bsrc.red;
13287dd7cddfSDavid du Colombier 	ew = w+dx*3;
13297dd7cddfSDavid du Colombier 	while(w < ew){
13307dd7cddfSDavid du Colombier 		if(*m++){
13317dd7cddfSDavid du Colombier 			*w++ = *r++;
13327dd7cddfSDavid du Colombier 			*w++ = *r++;
13337dd7cddfSDavid du Colombier 			*w++ = *r++;
13347dd7cddfSDavid du Colombier 		}else{
13357dd7cddfSDavid du Colombier 			w += 3;
13367dd7cddfSDavid du Colombier 			r += 3;
13377dd7cddfSDavid du Colombier 		}
13387dd7cddfSDavid du Colombier 	}
13397dd7cddfSDavid du Colombier 	return bdst;	/* not used */
13407dd7cddfSDavid du Colombier }
13417dd7cddfSDavid du Colombier 
13427dd7cddfSDavid du Colombier static Buffer
13437dd7cddfSDavid du Colombier boolcopy32(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int notusedi)
13447dd7cddfSDavid du Colombier {
13457dd7cddfSDavid du Colombier 	uchar *m;
13467dd7cddfSDavid du Colombier 	ulong *r, *w, *ew;
13477dd7cddfSDavid du Colombier 
13487dd7cddfSDavid du Colombier 	m = bmask.grey;
13497dd7cddfSDavid du Colombier 	w = (ulong*)bdst.red;
13507dd7cddfSDavid du Colombier 	r = (ulong*)bsrc.red;
13517dd7cddfSDavid du Colombier 	ew = w+dx;
13527dd7cddfSDavid du Colombier 	for(; w < ew; w++,r++)
13537dd7cddfSDavid du Colombier 		if(*m++)
13547dd7cddfSDavid du Colombier 			*w = *r;
13557dd7cddfSDavid du Colombier 	return bdst;	/* not used */
13567dd7cddfSDavid du Colombier }
13577dd7cddfSDavid du Colombier 
13587dd7cddfSDavid du Colombier static Buffer
13597dd7cddfSDavid du Colombier genconv(Param *p, uchar *buf, int y)
13607dd7cddfSDavid du Colombier {
13617dd7cddfSDavid du Colombier 	Buffer b;
13627dd7cddfSDavid du Colombier 	int nb;
13637dd7cddfSDavid du Colombier 	uchar *r, *w, *ew;
13647dd7cddfSDavid du Colombier 
13657dd7cddfSDavid du Colombier 	/* read from source into RGB format in convbuf */
13667dd7cddfSDavid du Colombier 	b = p->convreadcall(p, p->convbuf, y);
13677dd7cddfSDavid du Colombier 
13687dd7cddfSDavid du Colombier 	/* write RGB format into dst format in buf */
13697dd7cddfSDavid du Colombier 	p->convwritecall(p->convdpar, buf, b);
13707dd7cddfSDavid du Colombier 
13717dd7cddfSDavid du Colombier 	if(p->convdx){
13727dd7cddfSDavid du Colombier 		nb = p->convdpar->img->depth/8;
13737dd7cddfSDavid du Colombier 		r = buf;
13747dd7cddfSDavid du Colombier 		w = buf+nb*p->dx;
13757dd7cddfSDavid du Colombier 		ew = buf+nb*p->convdx;
13767dd7cddfSDavid du Colombier 		while(w<ew)
13777dd7cddfSDavid du Colombier 			*w++ = *r++;
13787dd7cddfSDavid du Colombier 	}
13797dd7cddfSDavid du Colombier 
13807dd7cddfSDavid du Colombier 	b.red = buf;
13817dd7cddfSDavid du Colombier 	b.blu = b.grn = b.grey = b.alpha = nil;
13827dd7cddfSDavid du Colombier 	b.delta = 0;
13837dd7cddfSDavid du Colombier 
13847dd7cddfSDavid du Colombier 	return b;
13857dd7cddfSDavid du Colombier }
13867dd7cddfSDavid du Colombier 
13877dd7cddfSDavid du Colombier static Readfn*
13887dd7cddfSDavid du Colombier convfn(Memimage *dst, Param *dpar, Memimage *src, Param *spar)
13897dd7cddfSDavid du Colombier {
13907dd7cddfSDavid du Colombier 	if(dst->chan == src->chan && !(src->flags&Frepl)){
13917dd7cddfSDavid du Colombier 		return readptr;
13927dd7cddfSDavid du Colombier 	}
13937dd7cddfSDavid du Colombier 
13947dd7cddfSDavid du Colombier 	if(dst->chan==CMAP8 && (src->chan==GREY1||src->chan==GREY2||src->chan==GREY4)){
13957dd7cddfSDavid du Colombier 		/* cheat because we know the replicated value is exactly the color map entry. */
13967dd7cddfSDavid du Colombier 		return readnbit;
13977dd7cddfSDavid du Colombier 	}
13987dd7cddfSDavid du Colombier 
13997dd7cddfSDavid du Colombier 	spar->convreadcall = readfn(src);
14007dd7cddfSDavid du Colombier 	spar->convwritecall = writefn(dst);
14017dd7cddfSDavid du Colombier 	spar->convdpar = dpar;
14027dd7cddfSDavid du Colombier 
14037dd7cddfSDavid du Colombier 	/* allocate a conversion buffer */
14047dd7cddfSDavid du Colombier 	spar->convbufoff = ndrawbuf;
14057dd7cddfSDavid du Colombier 	ndrawbuf += spar->dx*4;
14067dd7cddfSDavid du Colombier 
14077dd7cddfSDavid du Colombier 	if(spar->dx > Dx(spar->img->r)){
14087dd7cddfSDavid du Colombier 		spar->convdx = spar->dx;
14097dd7cddfSDavid du Colombier 		spar->dx = Dx(spar->img->r);
14107dd7cddfSDavid du Colombier 	}
14117dd7cddfSDavid du Colombier 
14127dd7cddfSDavid du Colombier 	return genconv;
14137dd7cddfSDavid du Colombier }
14147dd7cddfSDavid du Colombier 
14157dd7cddfSDavid du Colombier ulong
14167dd7cddfSDavid du Colombier _pixelbits(Memimage *i, Point pt)
14177dd7cddfSDavid du Colombier {
14187dd7cddfSDavid du Colombier 	uchar *p;
14197dd7cddfSDavid du Colombier 	ulong val;
14207dd7cddfSDavid du Colombier 	int off, bpp, npack;
14217dd7cddfSDavid du Colombier 
14227dd7cddfSDavid du Colombier 	val = 0;
14237dd7cddfSDavid du Colombier 	p = byteaddr(i, pt);
14247dd7cddfSDavid du Colombier 	switch(bpp=i->depth){
14257dd7cddfSDavid du Colombier 	case 1:
14267dd7cddfSDavid du Colombier 	case 2:
14277dd7cddfSDavid du Colombier 	case 4:
14287dd7cddfSDavid du Colombier 		npack = 8/bpp;
14297dd7cddfSDavid du Colombier 		off = pt.x%npack;
14307dd7cddfSDavid du Colombier 		val = p[0] >> bpp*(npack-1-off);
14317dd7cddfSDavid du Colombier 		val &= (1<<bpp)-1;
14327dd7cddfSDavid du Colombier 		break;
14337dd7cddfSDavid du Colombier 	case 8:
14347dd7cddfSDavid du Colombier 		val = p[0];
14357dd7cddfSDavid du Colombier 		break;
14367dd7cddfSDavid du Colombier 	case 16:
14377dd7cddfSDavid du Colombier 		val = p[0]|(p[1]<<8);
14387dd7cddfSDavid du Colombier 		break;
14397dd7cddfSDavid du Colombier 	case 24:
14407dd7cddfSDavid du Colombier 		val = p[0]|(p[1]<<8)|(p[2]<<16);
14417dd7cddfSDavid du Colombier 		break;
14427dd7cddfSDavid du Colombier 	case 32:
14437dd7cddfSDavid du Colombier 		val = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
14447dd7cddfSDavid du Colombier 		break;
14457dd7cddfSDavid du Colombier 	}
14467dd7cddfSDavid du Colombier 	while(bpp<32){
14477dd7cddfSDavid du Colombier 		val |= val<<bpp;
14487dd7cddfSDavid du Colombier 		bpp *= 2;
14497dd7cddfSDavid du Colombier 	}
14507dd7cddfSDavid du Colombier 	return val;
14517dd7cddfSDavid du Colombier }
14527dd7cddfSDavid du Colombier 
14537dd7cddfSDavid du Colombier static Calcfn*
14547dd7cddfSDavid du Colombier boolcopyfn(Memimage *img, Memimage *mask)
14557dd7cddfSDavid du Colombier {
14567dd7cddfSDavid du Colombier 	if(mask->flags&Frepl && Dx(mask->r)==1 && Dy(mask->r)==1 && _pixelbits(mask, mask->r.min)==~0)
14577dd7cddfSDavid du Colombier 		return boolmemmove;
14587dd7cddfSDavid du Colombier 
14597dd7cddfSDavid du Colombier 	switch(img->depth){
14607dd7cddfSDavid du Colombier 	case 8:
14617dd7cddfSDavid du Colombier 		return boolcopy8;
14627dd7cddfSDavid du Colombier 	case 16:
14637dd7cddfSDavid du Colombier 		return boolcopy16;
14647dd7cddfSDavid du Colombier 	case 24:
14657dd7cddfSDavid du Colombier 		return boolcopy24;
14667dd7cddfSDavid du Colombier 	case 32:
14677dd7cddfSDavid du Colombier 		return boolcopy32;
14687dd7cddfSDavid du Colombier 	default:
14697dd7cddfSDavid du Colombier 		assert(0 /* boolcopyfn */);
14707dd7cddfSDavid du Colombier 	}
14717dd7cddfSDavid du Colombier 	return nil;
14727dd7cddfSDavid du Colombier }
14737dd7cddfSDavid du Colombier 
14747dd7cddfSDavid du Colombier /*
14757dd7cddfSDavid du Colombier  * Optimized draw for filling and scrolling; uses memset and memmove.
14767dd7cddfSDavid du Colombier  */
14777dd7cddfSDavid du Colombier static void
14787dd7cddfSDavid du Colombier memsetb(void *vp, uchar val, int n)
14797dd7cddfSDavid du Colombier {
14807dd7cddfSDavid du Colombier 	uchar *p, *ep;
14817dd7cddfSDavid du Colombier 
14827dd7cddfSDavid du Colombier 	p = vp;
14837dd7cddfSDavid du Colombier 	ep = p+n;
14847dd7cddfSDavid du Colombier 	while(p<ep)
14857dd7cddfSDavid du Colombier 		*p++ = val;
14867dd7cddfSDavid du Colombier }
14877dd7cddfSDavid du Colombier 
14887dd7cddfSDavid du Colombier static void
14897dd7cddfSDavid du Colombier memsets(void *vp, ushort val, int n)
14907dd7cddfSDavid du Colombier {
14917dd7cddfSDavid du Colombier 	ushort *p, *ep;
14927dd7cddfSDavid du Colombier 
14937dd7cddfSDavid du Colombier 	p = vp;
14947dd7cddfSDavid du Colombier 	ep = p+n;
14957dd7cddfSDavid du Colombier 	while(p<ep)
14967dd7cddfSDavid du Colombier 		*p++ = val;
14977dd7cddfSDavid du Colombier }
14987dd7cddfSDavid du Colombier 
14997dd7cddfSDavid du Colombier static void
15007dd7cddfSDavid du Colombier memsetl(void *vp, ulong val, int n)
15017dd7cddfSDavid du Colombier {
15027dd7cddfSDavid du Colombier 	ulong *p, *ep;
15037dd7cddfSDavid du Colombier 
15047dd7cddfSDavid du Colombier 	p = vp;
15057dd7cddfSDavid du Colombier 	ep = p+n;
15067dd7cddfSDavid du Colombier 	while(p<ep)
15077dd7cddfSDavid du Colombier 		*p++ = val;
15087dd7cddfSDavid du Colombier }
15097dd7cddfSDavid du Colombier 
15107dd7cddfSDavid du Colombier void
15117dd7cddfSDavid du Colombier memset24(void *vp, ulong val, int n)
15127dd7cddfSDavid du Colombier {
15137dd7cddfSDavid du Colombier 	uchar *p, *ep;
15147dd7cddfSDavid du Colombier 	uchar a,b,c;
15157dd7cddfSDavid du Colombier 
15167dd7cddfSDavid du Colombier 	p = vp;
15177dd7cddfSDavid du Colombier 	ep = p+3*n;
15187dd7cddfSDavid du Colombier 	a = val;
15197dd7cddfSDavid du Colombier 	b = val>>8;
15207dd7cddfSDavid du Colombier 	c = val>>16;
15217dd7cddfSDavid du Colombier 	while(p<ep){
15227dd7cddfSDavid du Colombier 		*p++ = a;
15237dd7cddfSDavid du Colombier 		*p++ = b;
15247dd7cddfSDavid du Colombier 		*p++ = c;
15257dd7cddfSDavid du Colombier 	}
15267dd7cddfSDavid du Colombier }
15277dd7cddfSDavid du Colombier 
15287dd7cddfSDavid du Colombier ulong
15297dd7cddfSDavid du Colombier _imgtorgba(Memimage *img, ulong val)
15307dd7cddfSDavid du Colombier {
15317dd7cddfSDavid du Colombier 	uchar r, g, b, a;
15327dd7cddfSDavid du Colombier 	int nb, ov, v;
15337dd7cddfSDavid du Colombier 	ulong chan;
15347dd7cddfSDavid du Colombier 	uchar *p;
15357dd7cddfSDavid du Colombier 
15367dd7cddfSDavid du Colombier 	a = 0xFF;
15377dd7cddfSDavid du Colombier 	r = g = b = 0xAA;	/* garbage */
15387dd7cddfSDavid du Colombier 	for(chan=img->chan; chan; chan>>=8){
15397dd7cddfSDavid du Colombier 		nb = NBITS(chan);
15407dd7cddfSDavid du Colombier 		ov = v = val&((1<<nb)-1);
15417dd7cddfSDavid du Colombier 		val >>= nb;
15427dd7cddfSDavid du Colombier 
15437dd7cddfSDavid du Colombier 		while(nb < 8){
15447dd7cddfSDavid du Colombier 			v |= v<<nb;
15457dd7cddfSDavid du Colombier 			nb *= 2;
15467dd7cddfSDavid du Colombier 		}
15477dd7cddfSDavid du Colombier 		v >>= (nb-8);
15487dd7cddfSDavid du Colombier 
15497dd7cddfSDavid du Colombier 		switch(TYPE(chan)){
15507dd7cddfSDavid du Colombier 		case CRed:
15517dd7cddfSDavid du Colombier 			r = v;
15527dd7cddfSDavid du Colombier 			break;
15537dd7cddfSDavid du Colombier 		case CGreen:
15547dd7cddfSDavid du Colombier 			g = v;
15557dd7cddfSDavid du Colombier 			break;
15567dd7cddfSDavid du Colombier 		case CBlue:
15577dd7cddfSDavid du Colombier 			b = v;
15587dd7cddfSDavid du Colombier 			break;
15597dd7cddfSDavid du Colombier 		case CAlpha:
15607dd7cddfSDavid du Colombier 			a = v;
15617dd7cddfSDavid du Colombier 			break;
15627dd7cddfSDavid du Colombier 		case CGrey:
15637dd7cddfSDavid du Colombier 			r = g = b = v;
15647dd7cddfSDavid du Colombier 			break;
15657dd7cddfSDavid du Colombier 		case CMap:
15667dd7cddfSDavid du Colombier 			p = img->cmap->cmap2rgb+3*ov;
15677dd7cddfSDavid du Colombier 			r = *p++;
15687dd7cddfSDavid du Colombier 			g = *p++;
15697dd7cddfSDavid du Colombier 			b = *p;
15707dd7cddfSDavid du Colombier 			break;
15717dd7cddfSDavid du Colombier 		}
15727dd7cddfSDavid du Colombier 	}
15737dd7cddfSDavid du Colombier 	return (r<<24)|(g<<16)|(b<<8)|a;
15747dd7cddfSDavid du Colombier }
15757dd7cddfSDavid du Colombier 
15767dd7cddfSDavid du Colombier ulong
15777dd7cddfSDavid du Colombier _rgbatoimg(Memimage *img, ulong rgba)
15787dd7cddfSDavid du Colombier {
15797dd7cddfSDavid du Colombier 	ulong chan;
15807dd7cddfSDavid du Colombier 	int d, nb;
15817dd7cddfSDavid du Colombier 	ulong v;
15827dd7cddfSDavid du Colombier 	uchar *p, r, g, b, a, m;
15837dd7cddfSDavid du Colombier 
15847dd7cddfSDavid du Colombier 	v = 0;
15857dd7cddfSDavid du Colombier 	r = rgba>>24;
15867dd7cddfSDavid du Colombier 	g = rgba>>16;
15877dd7cddfSDavid du Colombier 	b = rgba>>8;
15887dd7cddfSDavid du Colombier 	a = rgba;
15897dd7cddfSDavid du Colombier 	d = 0;
15907dd7cddfSDavid du Colombier 	for(chan=img->chan; chan; chan>>=8){
15917dd7cddfSDavid du Colombier 		nb = NBITS(chan);
15927dd7cddfSDavid du Colombier 		switch(TYPE(chan)){
15937dd7cddfSDavid du Colombier 		case CRed:
15947dd7cddfSDavid du Colombier 			v |= (r>>(8-nb))<<d;
15957dd7cddfSDavid du Colombier 			break;
15967dd7cddfSDavid du Colombier 		case CGreen:
15977dd7cddfSDavid du Colombier 			v |= (g>>(8-nb))<<d;
15987dd7cddfSDavid du Colombier 			break;
15997dd7cddfSDavid du Colombier 		case CBlue:
16007dd7cddfSDavid du Colombier 			v |= (b>>(8-nb))<<d;
16017dd7cddfSDavid du Colombier 			break;
16027dd7cddfSDavid du Colombier 		case CAlpha:
16037dd7cddfSDavid du Colombier 			v |= (a>>(8-nb))<<d;
16047dd7cddfSDavid du Colombier 			break;
16057dd7cddfSDavid du Colombier 		case CMap:
16067dd7cddfSDavid du Colombier 			p = img->cmap->rgb2cmap;
16077dd7cddfSDavid du Colombier 			m = p[(r>>4)*256+(g>>4)*16+(b>>4)];
1608*59cc4ca5SDavid du Colombier 			v |= (m>>(8-nb))<<d;
16097dd7cddfSDavid du Colombier 			break;
16107dd7cddfSDavid du Colombier 		case CGrey:
16117dd7cddfSDavid du Colombier 			m = RGB2K(r,g,b);
1612*59cc4ca5SDavid du Colombier 			v |= (m>>(8-nb))<<d;
16137dd7cddfSDavid du Colombier 			break;
16147dd7cddfSDavid du Colombier 		}
16157dd7cddfSDavid du Colombier 		d += nb;
16167dd7cddfSDavid du Colombier 	}
16177dd7cddfSDavid du Colombier 	return v;
16187dd7cddfSDavid du Colombier }
16197dd7cddfSDavid du Colombier 
16207dd7cddfSDavid du Colombier static int
16217dd7cddfSDavid du Colombier memoptdraw(Memdrawparam *par)
16227dd7cddfSDavid du Colombier {
16237dd7cddfSDavid du Colombier 	int y, dy, dx;
16247dd7cddfSDavid du Colombier 	ulong v;
16257dd7cddfSDavid du Colombier 	unsigned m;
16267dd7cddfSDavid du Colombier 	Memimage *src;
16277dd7cddfSDavid du Colombier 	Memimage *dst;
16287dd7cddfSDavid du Colombier 
16297dd7cddfSDavid du Colombier 	dx = Dx(par->r);
16307dd7cddfSDavid du Colombier 	dy = Dy(par->r);
16317dd7cddfSDavid du Colombier 	src = par->src;
16327dd7cddfSDavid du Colombier 	dst = par->dst;
16337dd7cddfSDavid du Colombier 
16347dd7cddfSDavid du Colombier 	/*
16357dd7cddfSDavid du Colombier 	 * If we have an opaque mask and source is one opaque pixel we can convert to the
16367dd7cddfSDavid du Colombier 	 * destination format and just replicate with memset.
16377dd7cddfSDavid du Colombier 	 */
16387dd7cddfSDavid du Colombier 	m = Simplesrc|Simplemask|Fullmask;
1639*59cc4ca5SDavid du Colombier 	if((par->state&m)==m && ((par->srgba&0xFF) == 0xFF)){
16407dd7cddfSDavid du Colombier 		uchar *dp, p[4];
1641*59cc4ca5SDavid du Colombier 		int d, dwid, ppb, np, nb;
16427dd7cddfSDavid du Colombier 		uchar lm, rm;
16437dd7cddfSDavid du Colombier 
16447dd7cddfSDavid du Colombier 		dwid = dst->width*sizeof(ulong);
16457dd7cddfSDavid du Colombier 		dp = byteaddr(dst, par->r.min);
16467dd7cddfSDavid du Colombier 		v = par->sdval;
16477dd7cddfSDavid du Colombier 		switch(dst->depth){
16487dd7cddfSDavid du Colombier 		case 1:
16497dd7cddfSDavid du Colombier 		case 2:
16507dd7cddfSDavid du Colombier 		case 4:
1651*59cc4ca5SDavid du Colombier 			for(d=dst->depth; d<8; d*=2)
1652*59cc4ca5SDavid du Colombier 				v |= (v<<d);
16537dd7cddfSDavid du Colombier 			ppb = 8/dst->depth;	/* pixels per byte */
16547dd7cddfSDavid du Colombier 			m = ppb-1;
16557dd7cddfSDavid du Colombier 			/* left edge */
16567dd7cddfSDavid du Colombier 			np = par->r.min.x&m;		/* no. pixels unused on left side of word */
16577dd7cddfSDavid du Colombier 			dx -= (ppb-np);
16587dd7cddfSDavid du Colombier 			nb = 8 - np * dst->depth;		/* no. bits used on right side of word */
16597dd7cddfSDavid du Colombier 			lm = (1<<nb)-1;
16607dd7cddfSDavid du Colombier 
16617dd7cddfSDavid du Colombier 			/* right edge */
16627dd7cddfSDavid du Colombier 			np = par->r.max.x&m;	/* no. pixels used on left side of word */
16637dd7cddfSDavid du Colombier 			dx -= np;
16647dd7cddfSDavid du Colombier 			nb = 8 - np * dst->depth;		/* no. bits unused on right side of word */
16657dd7cddfSDavid du Colombier 			rm = ~((1<<nb)-1);
16667dd7cddfSDavid du Colombier 
16677dd7cddfSDavid du Colombier 			/* lm, rm are masks that are 1 where we should touch the bits */
16687dd7cddfSDavid du Colombier 			if(dx < 0){	/* just one byte */
16697dd7cddfSDavid du Colombier 				lm &= rm;
16707dd7cddfSDavid du Colombier 				for(y=0; y<dy; y++, dp+=dwid)
16717dd7cddfSDavid du Colombier 					*dp ^= (v ^ *dp) & lm;
16727dd7cddfSDavid du Colombier 			}else if(dx == 0){	/* no full bytes */
16737dd7cddfSDavid du Colombier 				if(lm)
16747dd7cddfSDavid du Colombier 					dwid--;
16757dd7cddfSDavid du Colombier 
16767dd7cddfSDavid du Colombier 				for(y=0; y<dy; y++, dp+=dwid){
16777dd7cddfSDavid du Colombier 					if(lm){
16787dd7cddfSDavid du Colombier 						*dp ^= (v ^ *dp) & lm;
16797dd7cddfSDavid du Colombier 						dp++;
16807dd7cddfSDavid du Colombier 					}
16817dd7cddfSDavid du Colombier 					*dp ^= (v ^ *dp) & rm;
16827dd7cddfSDavid du Colombier 				}
16837dd7cddfSDavid du Colombier 			}else{		/* full bytes in middle */
16847dd7cddfSDavid du Colombier 				dx /= ppb;
16857dd7cddfSDavid du Colombier 				if(lm)
16867dd7cddfSDavid du Colombier 					dwid--;
16877dd7cddfSDavid du Colombier 				dwid -= dx;
16887dd7cddfSDavid du Colombier 
16897dd7cddfSDavid du Colombier 				for(y=0; y<dy; y++, dp+=dwid){
16907dd7cddfSDavid du Colombier 					if(lm){
16917dd7cddfSDavid du Colombier 						*dp ^= (v ^ *dp) & lm;
16927dd7cddfSDavid du Colombier 						dp++;
16937dd7cddfSDavid du Colombier 					}
16947dd7cddfSDavid du Colombier 					memset(dp, v, dx);
16957dd7cddfSDavid du Colombier 					dp += dx;
16967dd7cddfSDavid du Colombier 					*dp ^= (v ^ *dp) & rm;
16977dd7cddfSDavid du Colombier 				}
16987dd7cddfSDavid du Colombier 			}
16997dd7cddfSDavid du Colombier 			return 1;
17007dd7cddfSDavid du Colombier 		case 8:
17017dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++, dp+=dwid)
17027dd7cddfSDavid du Colombier 				memset(dp, v, dx);
17037dd7cddfSDavid du Colombier 			return 1;
17047dd7cddfSDavid du Colombier 		case 16:
17057dd7cddfSDavid du Colombier 			p[0] = v;		/* make little endian */
17067dd7cddfSDavid du Colombier 			p[1] = v>>8;
17077dd7cddfSDavid du Colombier 			v = *(ushort*)p;
17087dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++, dp+=dwid)
17097dd7cddfSDavid du Colombier 				memsets(dp, v, dx);
17107dd7cddfSDavid du Colombier 			return 1;
17117dd7cddfSDavid du Colombier 		case 24:
17127dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++, dp+=dwid)
17137dd7cddfSDavid du Colombier 				memset24(dp, v, dx);
17147dd7cddfSDavid du Colombier 			return 1;
17157dd7cddfSDavid du Colombier 		case 32:
17167dd7cddfSDavid du Colombier 			p[0] = v;		/* make little endian */
17177dd7cddfSDavid du Colombier 			p[1] = v>>8;
17187dd7cddfSDavid du Colombier 			p[2] = v>>16;
17197dd7cddfSDavid du Colombier 			p[3] = v>>24;
17207dd7cddfSDavid du Colombier 			v = *(ulong*)p;
17217dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++, dp+=dwid)
17227dd7cddfSDavid du Colombier 				memsetl(dp, v, dx);
17237dd7cddfSDavid du Colombier 			return 1;
17247dd7cddfSDavid du Colombier 		default:
17257dd7cddfSDavid du Colombier 			assert(0 /* bad dest depth in memoptdraw */);
17267dd7cddfSDavid du Colombier 		}
17277dd7cddfSDavid du Colombier 	}
17287dd7cddfSDavid du Colombier 
17297dd7cddfSDavid du Colombier 	/*
17307dd7cddfSDavid du Colombier 	 * If no source alpha, an opaque mask, we can just copy the
17317dd7cddfSDavid du Colombier 	 * source onto the destination.  If the channels are the same and
17327dd7cddfSDavid du Colombier 	 * the source is not replicated, memmove suffices.
17337dd7cddfSDavid du Colombier 	 */
17347dd7cddfSDavid du Colombier 	m = Simplemask|Fullmask;
17357dd7cddfSDavid du Colombier 	if((par->state&(m|Replsrc))==m && src->depth >= 8
17367dd7cddfSDavid du Colombier 	&& src->chan == dst->chan && !(src->flags&Falpha)){
17377dd7cddfSDavid du Colombier 		uchar *sp, *dp;
17387dd7cddfSDavid du Colombier 		long swid, dwid, nb;
17397dd7cddfSDavid du Colombier 		int dir;
17407dd7cddfSDavid du Colombier 
17417dd7cddfSDavid du Colombier 		if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min))
17427dd7cddfSDavid du Colombier 			dir = -1;
17437dd7cddfSDavid du Colombier 		else
17447dd7cddfSDavid du Colombier 			dir = 1;
17457dd7cddfSDavid du Colombier 
17467dd7cddfSDavid du Colombier 		swid = src->width*sizeof(ulong);
17477dd7cddfSDavid du Colombier 		dwid = dst->width*sizeof(ulong);
17487dd7cddfSDavid du Colombier 		sp = byteaddr(src, par->sr.min);
17497dd7cddfSDavid du Colombier 		dp = byteaddr(dst, par->r.min);
17507dd7cddfSDavid du Colombier 		if(dir == -1){
17517dd7cddfSDavid du Colombier 			sp += (dy-1)*swid;
17527dd7cddfSDavid du Colombier 			dp += (dy-1)*dwid;
17537dd7cddfSDavid du Colombier 			swid = -swid;
17547dd7cddfSDavid du Colombier 			dwid = -dwid;
17557dd7cddfSDavid du Colombier 		}
17567dd7cddfSDavid du Colombier 		nb = (dx*src->depth)/8;
17577dd7cddfSDavid du Colombier 		for(y=0; y<dy; y++, sp+=swid, dp+=dwid)
17587dd7cddfSDavid du Colombier 			memmove(dp, sp, nb);
17597dd7cddfSDavid du Colombier 		return 1;
17607dd7cddfSDavid du Colombier 	}
17617dd7cddfSDavid du Colombier 
17627dd7cddfSDavid du Colombier 	/*
17637dd7cddfSDavid du Colombier 	 * If we have a 1-bit mask, 1-bit source, and 1-bit destination, and
17647dd7cddfSDavid du Colombier 	 * they're all bit aligned, we can just use bit operators.  This happens
17657dd7cddfSDavid du Colombier 	 * when we're manipulating boolean masks, e.g. in the arc code.
17667dd7cddfSDavid du Colombier 	 */
17677dd7cddfSDavid du Colombier 	if((par->state&(Simplemask|Simplesrc|Replmask|Replsrc))==0
17687dd7cddfSDavid du Colombier 	&& dst->chan==GREY1 && src->chan==GREY1 && par->mask->chan==GREY1
17697dd7cddfSDavid du Colombier 	&& (par->r.min.x&7)==(par->sr.min.x&7) && (par->r.min.x&7)==(par->mr.min.x&7)){
17707dd7cddfSDavid du Colombier 		uchar *sp, *dp, *mp;
17717dd7cddfSDavid du Colombier 		uchar lm, rm;
17727dd7cddfSDavid du Colombier 		long swid, dwid, mwid;
17737dd7cddfSDavid du Colombier 		int i, x, dir;
17747dd7cddfSDavid du Colombier 
17757dd7cddfSDavid du Colombier 		sp = byteaddr(src, par->sr.min);
17767dd7cddfSDavid du Colombier 		dp = byteaddr(dst, par->r.min);
17777dd7cddfSDavid du Colombier 		mp = byteaddr(par->mask, par->mr.min);
17787dd7cddfSDavid du Colombier 		swid = src->width*sizeof(ulong);
17797dd7cddfSDavid du Colombier 		dwid = dst->width*sizeof(ulong);
17807dd7cddfSDavid du Colombier 		mwid = par->mask->width*sizeof(ulong);
17817dd7cddfSDavid du Colombier 
17827dd7cddfSDavid du Colombier 		if(src->data == dst->data && byteaddr(dst, par->r.min) > byteaddr(src, par->sr.min)){
17837dd7cddfSDavid du Colombier 			dir = -1;
17847dd7cddfSDavid du Colombier 		}else
17857dd7cddfSDavid du Colombier 			dir = 1;
17867dd7cddfSDavid du Colombier 
17877dd7cddfSDavid du Colombier 		lm = 0xFF>>(par->r.min.x&7);
17887dd7cddfSDavid du Colombier 		rm = 0xFF<<(8-(par->r.max.x&7));
17897dd7cddfSDavid du Colombier 		dx -= (8-(par->r.min.x&7)) + (par->r.max.x&7);
17907dd7cddfSDavid du Colombier 
17917dd7cddfSDavid du Colombier 		if(dx < 0){	/* one byte wide */
17927dd7cddfSDavid du Colombier 			lm &= rm;
17937dd7cddfSDavid du Colombier 			if(dir == -1){
17947dd7cddfSDavid du Colombier 				dp += dwid*(dy-1);
17957dd7cddfSDavid du Colombier 				sp += swid*(dy-1);
17967dd7cddfSDavid du Colombier 				mp += mwid*(dy-1);
17977dd7cddfSDavid du Colombier 				dwid = -dwid;
17987dd7cddfSDavid du Colombier 				swid = -swid;
17997dd7cddfSDavid du Colombier 				mwid = -mwid;
18007dd7cddfSDavid du Colombier 			}
18017dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++){
18027dd7cddfSDavid du Colombier 				*dp ^= (*dp ^ *sp) & *mp & lm;
18037dd7cddfSDavid du Colombier 				dp += dwid;
18047dd7cddfSDavid du Colombier 				sp += swid;
18057dd7cddfSDavid du Colombier 				mp += mwid;
18067dd7cddfSDavid du Colombier 			}
18077dd7cddfSDavid du Colombier 			return 1;
18087dd7cddfSDavid du Colombier 		}
18097dd7cddfSDavid du Colombier 
18107dd7cddfSDavid du Colombier 		dx /= 8;
18117dd7cddfSDavid du Colombier 		if(dir == 1){
18127dd7cddfSDavid du Colombier 			i = (lm!=0)+dx+(rm!=0);
18137dd7cddfSDavid du Colombier 			mwid -= i;
18147dd7cddfSDavid du Colombier 			swid -= i;
18157dd7cddfSDavid du Colombier 			dwid -= i;
18167dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
18177dd7cddfSDavid du Colombier 				if(lm){
18187dd7cddfSDavid du Colombier 					*dp ^= (*dp ^ *sp++) & *mp++ & lm;
18197dd7cddfSDavid du Colombier 					dp++;
18207dd7cddfSDavid du Colombier 				}
18217dd7cddfSDavid du Colombier 				for(x=0; x<dx; x++){
18227dd7cddfSDavid du Colombier 					*dp ^= (*dp ^ *sp++) & *mp++;
18237dd7cddfSDavid du Colombier 					dp++;
18247dd7cddfSDavid du Colombier 				}
18257dd7cddfSDavid du Colombier 				if(rm){
18267dd7cddfSDavid du Colombier 					*dp ^= (*dp ^ *sp++) & *mp++ & rm;
18277dd7cddfSDavid du Colombier 					dp++;
18287dd7cddfSDavid du Colombier 				}
18297dd7cddfSDavid du Colombier 			}
18307dd7cddfSDavid du Colombier 			return 1;
18317dd7cddfSDavid du Colombier 		}else{
18327dd7cddfSDavid du Colombier 		/* dir == -1 */
18337dd7cddfSDavid du Colombier 			i = (lm!=0)+dx+(rm!=0);
18347dd7cddfSDavid du Colombier 			dp += dwid*(dy-1)+i-1;
18357dd7cddfSDavid du Colombier 			sp += swid*(dy-1)+i-1;
18367dd7cddfSDavid du Colombier 			mp += mwid*(dy-1)+i-1;
18377dd7cddfSDavid du Colombier 			dwid = -dwid+i;
18387dd7cddfSDavid du Colombier 			swid = -swid+i;
18397dd7cddfSDavid du Colombier 			mwid = -mwid+i;
18407dd7cddfSDavid du Colombier 			for(y=0; y<dy; y++, dp+=dwid, sp+=swid, mp+=mwid){
18417dd7cddfSDavid du Colombier 				if(rm){
18427dd7cddfSDavid du Colombier 					*dp ^= (*dp ^ *sp--) & *mp-- & rm;
18437dd7cddfSDavid du Colombier 					dp--;
18447dd7cddfSDavid du Colombier 				}
18457dd7cddfSDavid du Colombier 				for(x=0; x<dx; x++){
18467dd7cddfSDavid du Colombier 					*dp ^= (*dp ^ *sp--) & *mp--;
18477dd7cddfSDavid du Colombier 					dp--;
18487dd7cddfSDavid du Colombier 				}
18497dd7cddfSDavid du Colombier 				if(lm){
18507dd7cddfSDavid du Colombier 					*dp ^= (*dp ^ *sp--) & *mp-- & lm;
18517dd7cddfSDavid du Colombier 					dp--;
18527dd7cddfSDavid du Colombier 				}
18537dd7cddfSDavid du Colombier 			}
18547dd7cddfSDavid du Colombier 		}
18557dd7cddfSDavid du Colombier 		return 1;
18567dd7cddfSDavid du Colombier 	}
18577dd7cddfSDavid du Colombier 	return 0;
18587dd7cddfSDavid du Colombier }
18597dd7cddfSDavid du Colombier 
18607dd7cddfSDavid du Colombier /*
18617dd7cddfSDavid du Colombier  * Boolean character drawing.
18627dd7cddfSDavid du Colombier  * Solid opaque color through a 1-bit greyscale mask.
18637dd7cddfSDavid du Colombier  */
18647dd7cddfSDavid du Colombier #define DBG if(0)
18657dd7cddfSDavid du Colombier static int
18667dd7cddfSDavid du Colombier chardraw(Memdrawparam *par)
18677dd7cddfSDavid du Colombier {
18687dd7cddfSDavid du Colombier 	ulong bits;
18697dd7cddfSDavid du Colombier 	int i, ddepth, dy, dx, x, bx, ex, y, npack, bsh, depth;
18707dd7cddfSDavid du Colombier 	ulong v, maskwid, dstwid;
18717dd7cddfSDavid du Colombier 	uchar *wp, *rp, *q, *wc;
18727dd7cddfSDavid du Colombier 	ushort *ws;
18737dd7cddfSDavid du Colombier 	ulong *wl;
18747dd7cddfSDavid du Colombier 	uchar sp[4];
18757dd7cddfSDavid du Colombier 	Rectangle r, mr;
18767dd7cddfSDavid du Colombier 	Memimage *mask, *src, *dst;
18777dd7cddfSDavid du Colombier 
18787dd7cddfSDavid du Colombier if(0) if(drawdebug) iprint("chardraw? mf %lux md %d sf %lux dxs %d dys %d dd %d ddat %p sdat %p\n",
18797dd7cddfSDavid du Colombier 		par->mask->flags, par->mask->depth, par->src->flags,
18807dd7cddfSDavid du Colombier 		Dx(par->src->r), Dy(par->src->r), par->dst->depth, par->dst->data, par->src->data);
18817dd7cddfSDavid du Colombier 
18827dd7cddfSDavid du Colombier 	mask = par->mask;
18837dd7cddfSDavid du Colombier 	src = par->src;
18847dd7cddfSDavid du Colombier 	dst = par->dst;
18857dd7cddfSDavid du Colombier 	r = par->r;
18867dd7cddfSDavid du Colombier 	mr = par->mr;
18877dd7cddfSDavid du Colombier 
18887dd7cddfSDavid du Colombier 	if((par->state&(Replsrc|Simplesrc|Replmask)) != (Replsrc|Simplesrc)
18897dd7cddfSDavid du Colombier 	|| mask->depth != 1 || src->flags&Falpha || dst->depth<8 || dst->data==src->data)
18907dd7cddfSDavid du Colombier 		return 0;
18917dd7cddfSDavid du Colombier 
18927dd7cddfSDavid du Colombier 	depth = mask->depth;
18937dd7cddfSDavid du Colombier 	maskwid = mask->width*sizeof(ulong);
18947dd7cddfSDavid du Colombier 	rp = byteaddr(mask, mr.min);
18957dd7cddfSDavid du Colombier 	npack = 8/depth;
18967dd7cddfSDavid du Colombier 	bsh = (mr.min.x % npack) * depth;
18977dd7cddfSDavid du Colombier 
18987dd7cddfSDavid du Colombier 	wp = byteaddr(dst, r.min);
18997dd7cddfSDavid du Colombier 	dstwid = dst->width*sizeof(ulong);
19007dd7cddfSDavid du Colombier DBG print("bsh %d\n", bsh);
19017dd7cddfSDavid du Colombier 	dy = Dy(r);
19027dd7cddfSDavid du Colombier 	dx = Dx(r);
19037dd7cddfSDavid du Colombier 
19047dd7cddfSDavid du Colombier 	ddepth = dst->depth;
19057dd7cddfSDavid du Colombier 
19067dd7cddfSDavid du Colombier 	/*
19077dd7cddfSDavid du Colombier 	 * for loop counts from bsh to bsh+dx
19087dd7cddfSDavid du Colombier 	 *
19097dd7cddfSDavid du Colombier 	 * we want the bottom bits to be the amount
19107dd7cddfSDavid du Colombier 	 * to shift the pixels down, so for n≡0 (mod 8) we want
19117dd7cddfSDavid du Colombier 	 * bottom bits 7.  for n≡1, 6, etc.
19127dd7cddfSDavid du Colombier 	 * the bits come from -n-1.
19137dd7cddfSDavid du Colombier 	 */
19147dd7cddfSDavid du Colombier 
19157dd7cddfSDavid du Colombier 	bx = -bsh-1;
19167dd7cddfSDavid du Colombier 	ex = -bsh-1-dx;
19177dd7cddfSDavid du Colombier 	SET(bits);
19187dd7cddfSDavid du Colombier 	v = par->sdval;
19197dd7cddfSDavid du Colombier 
19207dd7cddfSDavid du Colombier 	/* make little endian */
19217dd7cddfSDavid du Colombier 	sp[0] = v;
19227dd7cddfSDavid du Colombier 	sp[1] = v>>8;
19237dd7cddfSDavid du Colombier 	sp[2] = v>>16;
19247dd7cddfSDavid du Colombier 	sp[3] = v>>24;
19257dd7cddfSDavid du Colombier 
19267dd7cddfSDavid du Colombier 	for(y=0; y<dy; y++, rp+=maskwid, wp+=dstwid){
19277dd7cddfSDavid du Colombier 		q = rp;
19287dd7cddfSDavid du Colombier 		if(bsh)
19297dd7cddfSDavid du Colombier 			bits = *q++;
19307dd7cddfSDavid du Colombier 		switch(ddepth){
19317dd7cddfSDavid du Colombier 		case 8:
19327dd7cddfSDavid du Colombier 			wc = wp;
19337dd7cddfSDavid du Colombier 			for(x=bx; x>ex; x--, wc++){
19347dd7cddfSDavid du Colombier 				i = x&7;
19357dd7cddfSDavid du Colombier 				if(i == 8-1)
19367dd7cddfSDavid du Colombier 					bits = *q++;
19377dd7cddfSDavid du Colombier DBG print("bits %lux sh %d...", bits, i);
19387dd7cddfSDavid du Colombier 				if((bits>>i)&1)
19397dd7cddfSDavid du Colombier 					*wc = v;
19407dd7cddfSDavid du Colombier 			}
19417dd7cddfSDavid du Colombier 			break;
19427dd7cddfSDavid du Colombier 		case 16:
19437dd7cddfSDavid du Colombier 			ws = (ushort*)wp;
19447dd7cddfSDavid du Colombier 			v = *(ushort*)sp;
19457dd7cddfSDavid du Colombier 			for(x=bx; x>ex; x--, ws++){
19467dd7cddfSDavid du Colombier 				i = x&7;
19477dd7cddfSDavid du Colombier 				if(i == 8-1)
19487dd7cddfSDavid du Colombier 					bits = *q++;
19497dd7cddfSDavid du Colombier DBG print("bits %lux sh %d...", bits, i);
19507dd7cddfSDavid du Colombier 				if((bits>>i)&1)
19517dd7cddfSDavid du Colombier 					*ws = v;
19527dd7cddfSDavid du Colombier 			}
19537dd7cddfSDavid du Colombier 			break;
19547dd7cddfSDavid du Colombier 		case 24:
19557dd7cddfSDavid du Colombier 			wc = wp;
19567dd7cddfSDavid du Colombier 			for(x=bx; x>ex; x--, wc+=3){
19577dd7cddfSDavid du Colombier 				i = x&7;
19587dd7cddfSDavid du Colombier 				if(i == 8-1)
19597dd7cddfSDavid du Colombier 					bits = *q++;
19607dd7cddfSDavid du Colombier DBG print("bits %lux sh %d...", bits, i);
19617dd7cddfSDavid du Colombier 				if((bits>>i)&1){
19627dd7cddfSDavid du Colombier 					wc[0] = sp[0];
19637dd7cddfSDavid du Colombier 					wc[1] = sp[1];
19647dd7cddfSDavid du Colombier 					wc[2] = sp[2];
19657dd7cddfSDavid du Colombier 				}
19667dd7cddfSDavid du Colombier 			}
19677dd7cddfSDavid du Colombier 			break;
19687dd7cddfSDavid du Colombier 		case 32:
19697dd7cddfSDavid du Colombier 			wl = (ulong*)wp;
19707dd7cddfSDavid du Colombier 			v = *(ulong*)sp;
19717dd7cddfSDavid du Colombier 			for(x=bx; x>ex; x--, wl++){
19727dd7cddfSDavid du Colombier 				i = x&7;
19737dd7cddfSDavid du Colombier 				if(i == 8-1)
19747dd7cddfSDavid du Colombier 					bits = *q++;
19757dd7cddfSDavid du Colombier DBG iprint("bits %lux sh %d...", bits, i);
19767dd7cddfSDavid du Colombier 				if((bits>>i)&1)
19777dd7cddfSDavid du Colombier 					*wl = v;
19787dd7cddfSDavid du Colombier 			}
19797dd7cddfSDavid du Colombier 			break;
19807dd7cddfSDavid du Colombier 		}
19817dd7cddfSDavid du Colombier 	}
19827dd7cddfSDavid du Colombier 
19837dd7cddfSDavid du Colombier DBG print("\n");
19847dd7cddfSDavid du Colombier 	return 1;
19857dd7cddfSDavid du Colombier }
19867dd7cddfSDavid du Colombier #undef DBG
19877dd7cddfSDavid du Colombier 
1988*59cc4ca5SDavid du Colombier void
1989*59cc4ca5SDavid du Colombier _memfillcolor(Memimage *i, ulong val)
1990*59cc4ca5SDavid du Colombier {
1991*59cc4ca5SDavid du Colombier 	ulong bits;
1992*59cc4ca5SDavid du Colombier 	int d, y;
1993*59cc4ca5SDavid du Colombier 
1994*59cc4ca5SDavid du Colombier 	if(val == DNofill)
1995*59cc4ca5SDavid du Colombier 		return;
1996*59cc4ca5SDavid du Colombier 
1997*59cc4ca5SDavid du Colombier 	bits = _rgbatoimg(i, val);
1998*59cc4ca5SDavid du Colombier 	switch(i->depth){
1999*59cc4ca5SDavid du Colombier 	case 24:	/* 24-bit images suck */
2000*59cc4ca5SDavid du Colombier 		for(y=i->r.min.y; y<i->r.max.y; y++)
2001*59cc4ca5SDavid du Colombier 			memset24(byteaddr(i, Pt(i->r.min.x, y)), bits, Dx(i->r));
2002*59cc4ca5SDavid du Colombier 		break;
2003*59cc4ca5SDavid du Colombier 	default:	/* 1, 2, 4, 8, 16, 32 */
2004*59cc4ca5SDavid du Colombier 		for(d=i->depth; d<32; d*=2)
2005*59cc4ca5SDavid du Colombier 			bits = (bits << d) | bits;
2006*59cc4ca5SDavid du Colombier 		memsetl(wordaddr(i, i->r.min), bits, i->width*Dy(i->r));
2007*59cc4ca5SDavid du Colombier 		break;
2008*59cc4ca5SDavid du Colombier 	}
2009*59cc4ca5SDavid du Colombier }
2010