/*
 * $XFree86: xc/programs/Xserver/fb/fbcompose.c,v 1.16 2002/11/06 22:45:35 keithp Exp $
 *
 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "fb.h"
#include "picturestr.h"
#include "mipict.h"
#include "fbpict.h"

/*
 * General purpose compositing code optimized for minimal memory
 * references
 *
 * All work is done on canonical ARGB values, functions for fetching
 * and storing these exist for each format.
 */

/*
 * Combine src and mask using IN
 */

CARD32
fbCombineMaskU (FbCompositeOperand   *src,
		FbCompositeOperand   *msk)
{
    CARD32  x;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    if (!msk)
	return (*src->fetch) (src);
    
    a = (*msk->fetch) (msk) >> 24;
    if (!a)
	return 0;
    
    x = (*src->fetch) (src);
    if (a == 0xff)
	return x;
    
    m = FbInU(x,0,a,t);
    n = FbInU(x,8,a,t);
    o = FbInU(x,16,a,t);
    p = FbInU(x,24,a,t);
    return m|n|o|p;
}

FbCompSrc
fbCombineMaskC (FbCompositeOperand   *src,
		FbCompositeOperand   *msk)
{
    FbCompSrc	s;
    CARD32	x;
    CARD32	a;
    CARD16	xa;
    CARD16	t;
    CARD32	m,n,o,p;

    if (!msk)
    {
	x = (*src->fetch) (src);
	s.value = x;
	x = x >> 24;
	x |= x << 8;
	x |= x << 16;
	s.alpha = x;
	return s;
    }
    
    a = (*msk->fetcha) (msk);
    if (!a)
    {
	s.value = 0;
	s.alpha = 0;
	return s;
    }
    
    x = (*src->fetch) (src);
    if (a == 0xffffffff)
    {
	s.value = x;
	x = x >> 24;
	x |= x << 8;
	x |= x << 16;
	s.alpha = x;
	return s;
    }
    
    m = FbInC(x,0,a,t);
    n = FbInC(x,8,a,t);
    o = FbInC(x,16,a,t);
    p = FbInC(x,24,a,t);
    s.value = m|n|o|p;
    xa = x >> 24;
    m = FbInU(a,0,xa,t);
    n = FbInU(a,8,xa,t);
    o = FbInU(a,16,xa,t);
    p = FbInU(a,24,xa,t);
    s.alpha = m|n|o|p;
    return s;
}

CARD32
fbCombineMaskValueC (FbCompositeOperand   *src,
		     FbCompositeOperand   *msk)
{
    CARD32	x;
    CARD32	a;
    CARD16	t;
    CARD32	m,n,o,p;

    if (!msk)
    {
	return (*src->fetch) (src);
    }
    
    a = (*msk->fetcha) (msk);
    if (!a)
	return a;
    
    x = (*src->fetch) (src);
    if (a == 0xffffffff)
	return x;
    
    m = FbInC(x,0,a,t);
    n = FbInC(x,8,a,t);
    o = FbInC(x,16,a,t);
    p = FbInC(x,24,a,t);
    return m|n|o|p;
}

/*
 * Combine src and mask using IN, generating only the alpha component
 */
CARD32
fbCombineMaskAlphaU (FbCompositeOperand   *src,
		     FbCompositeOperand   *msk)
{
    CARD32  x;
    CARD16  a;
    CARD16  t;

    if (!msk)
	return (*src->fetch) (src);
    
    a = (*msk->fetch) (msk) >> 24;
    if (!a)
	return 0;
    
    x = (*src->fetch) (src);
    if (a == 0xff)
	return x;
    
    return FbInU(x,24,a,t);
}

CARD32
fbCombineMaskAlphaC (FbCompositeOperand   *src,
		     FbCompositeOperand   *msk)
{
    CARD32	x;
    CARD32	a;
    CARD16	t;
    CARD32	m,n,o,p;

    if (!msk)
	return (*src->fetch) (src);
    
    a = (*msk->fetcha) (msk);
    if (!a)
	return 0;
    
    x = (*src->fetcha) (src);
    if (a == 0xffffffff)
	return x;
    
    m = FbInC(x,0,a,t);
    n = FbInC(x,8,a,t);
    o = FbInC(x,16,a,t);
    p = FbInC(x,24,a,t);
    return m|n|o|p;
}

/*
 * All of the composing functions
 */
void
fbCombineClear (FbCompositeOperand   *src,
		FbCompositeOperand   *msk,
		FbCompositeOperand   *dst)
{
    (*dst->store) (dst, 0);
}

void
fbCombineSrcU (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    (*dst->store) (dst, fbCombineMaskU (src, msk));
}

void
fbCombineSrcC (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    (*dst->store) (dst, fbCombineMaskValueC (src, msk));
}

void
fbCombineDst (FbCompositeOperand    *src,
	      FbCompositeOperand    *msk,
	      FbCompositeOperand    *dst)
{
    /* noop */
}

void
fbCombineOverU (FbCompositeOperand   *src,
		FbCompositeOperand   *msk,
		FbCompositeOperand   *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskU (src, msk);
    a = ~s >> 24;
    if (a != 0xff)
    {
	if (a)
	{
	    d = (*dst->fetch) (dst);
	    m = FbOverU(s,d,0,a,t);
	    n = FbOverU(s,d,8,a,t);
	    o = FbOverU(s,d,16,a,t);
	    p = FbOverU(s,d,24,a,t);
	    s = m|n|o|p;
	}
	(*dst->store) (dst, s);
    }
}

void
fbCombineOverC (FbCompositeOperand   *src,
		FbCompositeOperand   *msk,
		FbCompositeOperand   *dst)
{
    FbCompSrc	cs;
    CARD32  s, d;
    CARD32  a;
    CARD16  t;
    CARD32  m,n,o,p;

    cs = fbCombineMaskC (src, msk);
    s = cs.value;
    a = ~cs.alpha;
    if (a != 0xffffffff)
    {
	if (a)
	{
	    d = (*dst->fetch) (dst);
	    m = FbOverC(s,d,0,a,t);
	    n = FbOverC(s,d,8,a,t);
	    o = FbOverC(s,d,16,a,t);
	    p = FbOverC(s,d,24,a,t);
	    s = m|n|o|p;
	}
	(*dst->store) (dst, s);
    }
}

void
fbCombineOverReverseU (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    d = (*dst->fetch) (dst);
    a = ~d >> 24;
    if (a)
    {
	s = fbCombineMaskU (src, msk);
	if (a != 0xff)
	{
	    m = FbOverU(d,s,0,a,t);
	    n = FbOverU(d,s,8,a,t);
	    o = FbOverU(d,s,16,a,t);
	    p = FbOverU(d,s,24,a,t);
	    s = m|n|o|p;
	}
	(*dst->store) (dst, s);
    }
}

void
fbCombineOverReverseC (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD32  a;
    CARD16  t;
    CARD32  m,n,o,p;

    d = (*dst->fetch) (dst);
    a = ~d >> 24;
    if (a)
    {
	s = fbCombineMaskValueC (src, msk);
	if (a != 0xff)
	{
	    m = FbOverU(d,s,0,a,t);
	    n = FbOverU(d,s,8,a,t);
	    o = FbOverU(d,s,16,a,t);
	    p = FbOverU(d,s,24,a,t);
	    s = m|n|o|p;
	}
	(*dst->store) (dst, s);
    }
}

void
fbCombineInU (FbCompositeOperand	    *src,
	      FbCompositeOperand	    *msk,
	      FbCompositeOperand	    *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    d = (*dst->fetch) (dst);
    a = d >> 24;
    s = 0;
    if (a)
    {
	s = fbCombineMaskU (src, msk);
	if (a != 0xff)
	{
	    m = FbInU(s,0,a,t);
	    n = FbInU(s,8,a,t);
	    o = FbInU(s,16,a,t);
	    p = FbInU(s,24,a,t);
	    s = m|n|o|p;
	}
    }
    (*dst->store) (dst, s);
}

void
fbCombineInC (FbCompositeOperand	    *src,
	      FbCompositeOperand	    *msk,
	      FbCompositeOperand	    *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    d = (*dst->fetch) (dst);
    a = d >> 24;
    s = 0;
    if (a)
    {
	s = fbCombineMaskValueC (src, msk);
	if (a != 0xff)
	{
	    m = FbInU(s,0,a,t);
	    n = FbInU(s,8,a,t);
	    o = FbInU(s,16,a,t);
	    p = FbInU(s,24,a,t);
	    s = m|n|o|p;
	}
    }
    (*dst->store) (dst, s);
}

void
fbCombineInReverseU (FbCompositeOperand  *src,
		     FbCompositeOperand  *msk,
		     FbCompositeOperand  *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskAlphaU (src, msk);
    a = s >> 24;
    if (a != 0xff)
    {
	d = 0;
	if (a)
	{
	    d = (*dst->fetch) (dst);
	    m = FbInU(d,0,a,t);
	    n = FbInU(d,8,a,t);
	    o = FbInU(d,16,a,t);
	    p = FbInU(d,24,a,t);
	    d = m|n|o|p;
	}
	(*dst->store) (dst, d);
    }
}

void
fbCombineInReverseC (FbCompositeOperand  *src,
		     FbCompositeOperand  *msk,
		     FbCompositeOperand  *dst)
{
    CARD32  s, d;
    CARD32  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskAlphaC (src, msk);
    a = s;
    if (a != 0xffffffff)
    {
	d = 0;
	if (a)
	{
	    d = (*dst->fetch) (dst);
	    m = FbInC(d,0,a,t);
	    n = FbInC(d,8,a,t);
	    o = FbInC(d,16,a,t);
	    p = FbInC(d,24,a,t);
	    d = m|n|o|p;
	}
	(*dst->store) (dst, d);
    }
}

void
fbCombineOutU (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    d = (*dst->fetch) (dst);
    a = ~d >> 24;
    s = 0;
    if (a)
    {
	s = fbCombineMaskU (src, msk);
	if (a != 0xff)
	{
	    m = FbInU(s,0,a,t);
	    n = FbInU(s,8,a,t);
	    o = FbInU(s,16,a,t);
	    p = FbInU(s,24,a,t);
	    s = m|n|o|p;
	}
    }
    (*dst->store) (dst, s);
}

void
fbCombineOutC (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    d = (*dst->fetch) (dst);
    a = ~d >> 24;
    s = 0;
    if (a)
    {
	s = fbCombineMaskValueC (src, msk);
	if (a != 0xff)
	{
	    m = FbInU(s,0,a,t);
	    n = FbInU(s,8,a,t);
	    o = FbInU(s,16,a,t);
	    p = FbInU(s,24,a,t);
	    s = m|n|o|p;
	}
    }
    (*dst->store) (dst, s);
}

void
fbCombineOutReverseU (FbCompositeOperand *src,
		      FbCompositeOperand *msk,
		      FbCompositeOperand *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskAlphaU (src, msk);
    a = ~s >> 24;
    if (a != 0xff)
    {
	d = 0;
	if (a)
	{
	    d = (*dst->fetch) (dst);
	    m = FbInU(d,0,a,t);
	    n = FbInU(d,8,a,t);
	    o = FbInU(d,16,a,t);
	    p = FbInU(d,24,a,t);
	    d = m|n|o|p;
	}
	(*dst->store) (dst, d);
    }
}

void
fbCombineOutReverseC (FbCompositeOperand *src,
		      FbCompositeOperand *msk,
		      FbCompositeOperand *dst)
{
    CARD32  s, d;
    CARD32  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskAlphaC (src, msk);
    a = ~s;
    if (a != 0xffffffff)
    {
	d = 0;
	if (a)
	{
	    d = (*dst->fetch) (dst);
	    m = FbInC(d,0,a,t);
	    n = FbInC(d,8,a,t);
	    o = FbInC(d,16,a,t);
	    p = FbInC(d,24,a,t);
	    d = m|n|o|p;
	}
	(*dst->store) (dst, d);
    }
}

void
fbCombineAtopU (FbCompositeOperand   *src,
		FbCompositeOperand   *msk,
		FbCompositeOperand   *dst)
{
    CARD32  s, d;
    CARD16  ad, as;
    CARD16  t,u,v;
    CARD32  m,n,o,p;
    
    s = fbCombineMaskU (src, msk);
    d = (*dst->fetch) (dst);
    ad = ~s >> 24;
    as = d >> 24;
    m = FbGen(s,d,0,as,ad,t,u,v);
    n = FbGen(s,d,8,as,ad,t,u,v);
    o = FbGen(s,d,16,as,ad,t,u,v);
    p = FbGen(s,d,24,as,ad,t,u,v);
    (*dst->store) (dst, m|n|o|p);
}

void
fbCombineAtopC (FbCompositeOperand   *src,
		FbCompositeOperand   *msk,
		FbCompositeOperand   *dst)
{
    FbCompSrc	cs;
    CARD32  s, d;
    CARD32  ad;
    CARD16  as;
    CARD16  t, u, v;
    CARD32  m,n,o,p;
    
    cs = fbCombineMaskC (src, msk);
    d = (*dst->fetch) (dst);
    s = cs.value;
    ad = cs.alpha;
    as = d >> 24;
    m = FbGen(s,d,0,as,FbGet8(ad,0),t,u,v);
    n = FbGen(s,d,8,as,FbGet8(ad,8),t,u,v);
    o = FbGen(s,d,16,as,FbGet8(ad,16),t,u,v);
    p = FbGen(s,d,24,as,FbGet8(ad,24),t,u,v);
    (*dst->store) (dst, m|n|o|p);
}

void
fbCombineAtopReverseU (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  ad, as;
    CARD16  t, u, v;
    CARD32  m,n,o,p;
    
    s = fbCombineMaskU (src, msk);
    d = (*dst->fetch) (dst);
    ad = s >> 24;
    as = ~d >> 24;
    m = FbGen(s,d,0,as,ad,t,u,v);
    n = FbGen(s,d,8,as,ad,t,u,v);
    o = FbGen(s,d,16,as,ad,t,u,v);
    p = FbGen(s,d,24,as,ad,t,u,v);
    (*dst->store) (dst, m|n|o|p);
}

void
fbCombineAtopReverseC (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    FbCompSrc	cs;
    CARD32  s, d, ad;
    CARD16  as;
    CARD16  t, u, v;
    CARD32  m,n,o,p;
    
    cs = fbCombineMaskC (src, msk);
    d = (*dst->fetch) (dst);
    s = cs.value;
    ad = cs.alpha;
    as = ~d >> 24;
    m = FbGen(s,d,0,as,FbGet8(ad,0),t,u,v);
    n = FbGen(s,d,8,as,FbGet8(ad,8),t,u,v);
    o = FbGen(s,d,16,as,FbGet8(ad,16),t,u,v);
    p = FbGen(s,d,24,as,FbGet8(ad,24),t,u,v);
    (*dst->store) (dst, m|n|o|p);
}

void
fbCombineXorU (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  ad, as;
    CARD16  t, u, v;
    CARD32  m,n,o,p;
    
    s = fbCombineMaskU (src, msk);
    d = (*dst->fetch) (dst);
    ad = ~s >> 24;
    as = ~d >> 24;
    m = FbGen(s,d,0,as,ad,t,u,v);
    n = FbGen(s,d,8,as,ad,t,u,v);
    o = FbGen(s,d,16,as,ad,t,u,v);
    p = FbGen(s,d,24,as,ad,t,u,v);
    (*dst->store) (dst, m|n|o|p);
}

void
fbCombineXorC (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    FbCompSrc	cs;
    CARD32  s, d, ad;
    CARD16  as;
    CARD16  t, u, v;
    CARD32  m,n,o,p;
    
    cs = fbCombineMaskC (src, msk);
    d = (*dst->fetch) (dst);
    s = cs.value;
    ad = ~cs.alpha;
    as = ~d >> 24;
    m = FbGen(s,d,0,as,ad,t,u,v);
    n = FbGen(s,d,8,as,ad,t,u,v);
    o = FbGen(s,d,16,as,ad,t,u,v);
    p = FbGen(s,d,24,as,ad,t,u,v);
    (*dst->store) (dst, m|n|o|p);
}

void
fbCombineAddU (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskU (src, msk);
    if (s == ~0)
	(*dst->store) (dst, s);
    else
    {
	d = (*dst->fetch) (dst);
	if (s && d != ~0)
	{
	    m = FbAdd(s,d,0,t);
	    n = FbAdd(s,d,8,t);
	    o = FbAdd(s,d,16,t);
	    p = FbAdd(s,d,24,t);
	    (*dst->store) (dst, m|n|o|p);
	}
    }
}

void
fbCombineAddC (FbCompositeOperand    *src,
	       FbCompositeOperand    *msk,
	       FbCompositeOperand    *dst)
{
    CARD32  s, d;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskValueC (src, msk);
    if (s == ~0)
	(*dst->store) (dst, s);
    else
    {
	d = (*dst->fetch) (dst);
	if (s && d != ~0)
	{
	    m = FbAdd(s,d,0,t);
	    n = FbAdd(s,d,8,t);
	    o = FbAdd(s,d,16,t);
	    p = FbAdd(s,d,24,t);
	    (*dst->store) (dst, m|n|o|p);
	}
    }
}

void
fbCombineSaturateU (FbCompositeOperand   *src,
		    FbCompositeOperand   *msk,
		    FbCompositeOperand   *dst)
{
    CARD32  s = fbCombineMaskU (src, msk), d;
#if 0
    CARD16  sa, da;
    CARD16  ad, as;
    CARD16  t;
    CARD32  m,n,o,p;
    
    d = (*dst->fetch) (dst);
    sa = s >> 24;
    da = ~d >> 24;
    if (sa <= da)
    {
	m = FbAdd(s,d,0,t);
	n = FbAdd(s,d,8,t);
	o = FbAdd(s,d,16,t);
	p = FbAdd(s,d,24,t);
    }
    else
    {
	as = (da << 8) / sa;
	ad = 0xff;
	m = FbGen(s,d,0,as,ad,t,u,v);
	n = FbGen(s,d,8,as,ad,t,u,v);
	o = FbGen(s,d,16,as,ad,t,u,v);
	p = FbGen(s,d,24,as,ad,t,u,v);
    }
    (*dst->store) (dst, m|n|o|p);
#else
    if ((s >> 24) == 0xff)
	(*dst->store) (dst, s);
    else
    {
	d = (*dst->fetch) (dst);
	if ((s >> 24) > (d >> 24))
	    (*dst->store) (dst, s);
    }
#endif
}

void
fbCombineSaturateC (FbCompositeOperand   *src,
		    FbCompositeOperand   *msk,
		    FbCompositeOperand   *dst)
{
    FbCompSrc	cs;
    CARD32  s, d;
    CARD16  sa, sr, sg, sb, da;
    CARD16  t, u, v;
    CARD32  m,n,o,p;
    
    cs = fbCombineMaskC (src, msk);
    d = (*dst->fetch) (dst);
    s = cs.value;
    sa = (cs.alpha >> 24) & 0xff;
    sr = (cs.alpha >> 16) & 0xff;
    sg = (cs.alpha >>  8) & 0xff;
    sb = (cs.alpha      ) & 0xff;
    da = ~d >> 24;
    
    if (sb <= da)
	m = FbAdd(s,d,0,t);
    else
	m = FbGen (s, d, 0, (da << 8) / sb, 0xff, t, u, v);
    
    if (sg <= da)
	n = FbAdd(s,d,8,t);
    else
	n = FbGen (s, d, 8, (da << 8) / sg, 0xff, t, u, v);
    
    if (sr < da)
	o = FbAdd(s,d,16,t);
    else
	o = FbGen (s, d, 16, (da << 8) / sr, 0xff, t, u, v);

    if (sa <= da)
	p = FbAdd(s,d,24,t);
    else
	p = FbGen (s, d, 24, (da << 8) / sa, 0xff, t, u, v);
    
    (*dst->store) (dst, m|n|o|p);
}

/*
 * All of the disjoint composing functions

 The four entries in the first column indicate what source contributions
 come from each of the four areas of the picture -- areas covered by neither
 A nor B, areas covered only by A, areas covered only by B and finally
 areas covered by both A and B.
 
		Disjoint			Conjoint
		Fa		Fb		Fa		Fb
(0,0,0,0)	0		0		0		0
(0,A,0,A)	1		0		1		0
(0,0,B,B)	0		1		0		1
(0,A,B,A)	1		min((1-a)/b,1)	1		max(1-a/b,0)
(0,A,B,B)	min((1-b)/a,1)	1		max(1-b/a,0)	1		
(0,0,0,A)	max(1-(1-b)/a,0) 0		min(1,b/a)	0
(0,0,0,B)	0		max(1-(1-a)/b,0) 0		min(a/b,1)
(0,A,0,0)	min(1,(1-b)/a)	0		max(1-b/a,0)	0
(0,0,B,0)	0		min(1,(1-a)/b)	0		max(1-a/b,0)
(0,0,B,A)	max(1-(1-b)/a,0) min(1,(1-a)/b)	 min(1,b/a)	max(1-a/b,0)
(0,A,0,B)	min(1,(1-b)/a)	max(1-(1-a)/b,0) max(1-b/a,0)	min(1,a/b)
(0,A,B,0)	min(1,(1-b)/a)	min(1,(1-a)/b)	max(1-b/a,0)	max(1-a/b,0)

 */

#define CombineAOut 1
#define CombineAIn  2
#define CombineBOut 4
#define CombineBIn  8

#define CombineClear	0
#define CombineA	(CombineAOut|CombineAIn)
#define CombineB	(CombineBOut|CombineBIn)
#define CombineAOver	(CombineAOut|CombineBOut|CombineAIn)
#define CombineBOver	(CombineAOut|CombineBOut|CombineBIn)
#define CombineAAtop	(CombineBOut|CombineAIn)
#define CombineBAtop	(CombineAOut|CombineBIn)
#define CombineXor	(CombineAOut|CombineBOut)

/* portion covered by a but not b */
CARD8
fbCombineDisjointOutPart (CARD8 a, CARD8 b)
{
    /* min (1, (1-b) / a) */
    
    b = ~b;		    /* 1 - b */
    if (b >= a)		    /* 1 - b >= a -> (1-b)/a >= 1 */
	return 0xff;	    /* 1 */
    return FbIntDiv(b,a);   /* (1-b) / a */
}

/* portion covered by both a and b */
CARD8
fbCombineDisjointInPart (CARD8 a, CARD8 b)
{
    /* max (1-(1-b)/a,0) */
    /*  = - min ((1-b)/a - 1, 0) */
    /*  = 1 - min (1, (1-b)/a) */

    b = ~b;		    /* 1 - b */
    if (b >= a)		    /* 1 - b >= a -> (1-b)/a >= 1 */
	return 0;	    /* 1 - 1 */
    return ~FbIntDiv(b,a);  /* 1 - (1-b) / a */
}

void
fbCombineDisjointGeneralU (FbCompositeOperand   *src,
			   FbCompositeOperand   *msk,
			   FbCompositeOperand   *dst,
			   CARD8		combine)
{
    CARD32  s, d;
    CARD32  m,n,o,p;
    CARD16  Fa, Fb, t, u, v;
    CARD8   sa, da;

    s = fbCombineMaskU (src, msk);
    sa = s >> 24;
    
    d = (*dst->fetch) (dst);
    da = d >> 24;
    
    switch (combine & CombineA) {
    default:
	Fa = 0;
	break;
    case CombineAOut:
	Fa = fbCombineDisjointOutPart (sa, da);
	break;
    case CombineAIn:
	Fa = fbCombineDisjointInPart (sa, da);
	break;
    case CombineA:
	Fa = 0xff;
	break;
    }
    
    switch (combine & CombineB) {
    default:
	Fb = 0;
	break;
    case CombineBOut:
	Fb = fbCombineDisjointOutPart (da, sa);
	break;
    case CombineBIn:
	Fb = fbCombineDisjointInPart (da, sa);
	break;
    case CombineB:
	Fb = 0xff;
	break;
    }
    m = FbGen (s,d,0,Fa,Fb,t,u,v);
    n = FbGen (s,d,8,Fa,Fb,t,u,v);
    o = FbGen (s,d,16,Fa,Fb,t,u,v);
    p = FbGen (s,d,24,Fa,Fb,t,u,v);
    s = m|n|o|p;
    (*dst->store) (dst, s);
}

void
fbCombineDisjointGeneralC (FbCompositeOperand   *src,
			   FbCompositeOperand   *msk,
			   FbCompositeOperand   *dst,
			   CARD8		combine)
{
    FbCompSrc	cs;
    CARD32  s, d;
    CARD32  m,n,o,p;
    CARD32  Fa;
    CARD16  Fb, t, u, v;
    CARD32  sa;
    CARD8   da;

    cs = fbCombineMaskC (src, msk);
    s = cs.value;
    sa = cs.alpha;
    
    d = (*dst->fetch) (dst);
    da = d >> 24;
    
    switch (combine & CombineA) {
    default:
	Fa = 0;
	break;
    case CombineAOut:
	m = fbCombineDisjointOutPart ((CARD8) (sa >> 0), da);
	n = fbCombineDisjointOutPart ((CARD8) (sa >> 8), da) << 8;
	o = fbCombineDisjointOutPart ((CARD8) (sa >> 16), da) << 16;
	p = fbCombineDisjointOutPart ((CARD8) (sa >> 24), da) << 24;
	Fa = m|n|o|p;
	break;
    case CombineAIn:
	m = fbCombineDisjointOutPart ((CARD8) (sa >> 0), da);
	n = fbCombineDisjointOutPart ((CARD8) (sa >> 8), da) << 8;
	o = fbCombineDisjointOutPart ((CARD8) (sa >> 16), da) << 16;
	p = fbCombineDisjointOutPart ((CARD8) (sa >> 24), da) << 24;
	Fa = m|n|o|p;
	break;
    case CombineA:
	Fa = 0xffffffff;
	break;
    }
    
    switch (combine & CombineB) {
    default:
	Fb = 0;
	break;
    case CombineBOut:
	Fb = fbCombineDisjointOutPart (da, sa);
	break;
    case CombineBIn:
	Fb = fbCombineDisjointInPart (da, sa);
	break;
    case CombineB:
	Fb = 0xff;
	break;
    }
    m = FbGen (s,d,0,FbGet8(Fa,0),Fb,t,u,v);
    n = FbGen (s,d,8,FbGet8(Fa,8),Fb,t,u,v);
    o = FbGen (s,d,16,FbGet8(Fa,16),Fb,t,u,v);
    p = FbGen (s,d,24,FbGet8(Fa,24),Fb,t,u,v);
    s = m|n|o|p;
    (*dst->store) (dst, s);
}

void
fbCombineDisjointOverU (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskU (src, msk);
    a = s >> 24;
    if (a != 0x00)
    {
	if (a != 0xff)
	{
	    d = (*dst->fetch) (dst);
	    a = fbCombineDisjointOutPart (d >> 24, a);
	    m = FbOverU(s,d,0,a,t);
	    n = FbOverU(s,d,8,a,t);
	    o = FbOverU(s,d,16,a,t);
	    p = FbOverU(s,d,24,a,t);
	    s = m|n|o|p;
	}
	(*dst->store) (dst, s);
    }
}

void
fbCombineDisjointOverC (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineAOver);
}

void
fbCombineDisjointOverReverseU (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineBOver);
}

void
fbCombineDisjointOverReverseC (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineBOver);
}

void
fbCombineDisjointInU (FbCompositeOperand	    *src,
		      FbCompositeOperand	    *msk,
		      FbCompositeOperand	    *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineAIn);
}

void
fbCombineDisjointInC (FbCompositeOperand	    *src,
		      FbCompositeOperand	    *msk,
		      FbCompositeOperand	    *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineAIn);
}

void
fbCombineDisjointInReverseU (FbCompositeOperand  *src,
			     FbCompositeOperand  *msk,
			     FbCompositeOperand  *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineBIn);
}

void
fbCombineDisjointInReverseC (FbCompositeOperand  *src,
			     FbCompositeOperand  *msk,
			     FbCompositeOperand  *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineBIn);
}

void
fbCombineDisjointOutU (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineAOut);
}

void
fbCombineDisjointOutC (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineAOut);
}

void
fbCombineDisjointOutReverseU (FbCompositeOperand *src,
			      FbCompositeOperand *msk,
			      FbCompositeOperand *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineBOut);
}

void
fbCombineDisjointOutReverseC (FbCompositeOperand *src,
			      FbCompositeOperand *msk,
			      FbCompositeOperand *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineBOut);
}

void
fbCombineDisjointAtopU (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineAAtop);
}

void
fbCombineDisjointAtopC (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineAAtop);
}

void
fbCombineDisjointAtopReverseU (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineBAtop);
}

void
fbCombineDisjointAtopReverseC (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineBAtop);
}

void
fbCombineDisjointXorU (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralU (src, msk, dst, CombineXor);
}

void
fbCombineDisjointXorC (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineDisjointGeneralC (src, msk, dst, CombineXor);
}

/* portion covered by a but not b */
CARD8
fbCombineConjointOutPart (CARD8 a, CARD8 b)
{
    /* max (1-b/a,0) */
    /* = 1-min(b/a,1) */
    
    /* min (1, (1-b) / a) */
    
    if (b >= a)		    /* b >= a -> b/a >= 1 */
	return 0x00;	    /* 0 */
    return ~FbIntDiv(b,a);   /* 1 - b/a */
}

/* portion covered by both a and b */
CARD8
fbCombineConjointInPart (CARD8 a, CARD8 b)
{
    /* min (1,b/a) */

    if (b >= a)		    /* b >= a -> b/a >= 1 */
	return 0xff;	    /* 1 */
    return FbIntDiv(b,a);   /* b/a */
}

void
fbCombineConjointGeneralU (FbCompositeOperand   *src,
			   FbCompositeOperand   *msk,
			   FbCompositeOperand   *dst,
			   CARD8		combine)
{
    CARD32  s, d;
    CARD32  m,n,o,p;
    CARD16  Fa, Fb, t, u, v;
    CARD8   sa, da;

    s = fbCombineMaskU (src, msk);
    sa = s >> 24;
    
    d = (*dst->fetch) (dst);
    da = d >> 24;
    
    switch (combine & CombineA) {
    default:
	Fa = 0;
	break;
    case CombineAOut:
	Fa = fbCombineConjointOutPart (sa, da);
	break;
    case CombineAIn:
	Fa = fbCombineConjointInPart (sa, da);
	break;
    case CombineA:
	Fa = 0xff;
	break;
    }
    
    switch (combine & CombineB) {
    default:
	Fb = 0;
	break;
    case CombineBOut:
	Fb = fbCombineConjointOutPart (da, sa);
	break;
    case CombineBIn:
	Fb = fbCombineConjointInPart (da, sa);
	break;
    case CombineB:
	Fb = 0xff;
	break;
    }
    m = FbGen (s,d,0,Fa,Fb,t,u,v);
    n = FbGen (s,d,8,Fa,Fb,t,u,v);
    o = FbGen (s,d,16,Fa,Fb,t,u,v);
    p = FbGen (s,d,24,Fa,Fb,t,u,v);
    s = m|n|o|p;
    (*dst->store) (dst, s);
}

void
fbCombineConjointGeneralC (FbCompositeOperand   *src,
			   FbCompositeOperand   *msk,
			   FbCompositeOperand   *dst,
			   CARD8		combine)
{
    FbCompSrc	cs;
    CARD32  s, d;
    CARD32  m,n,o,p;
    CARD32  Fa;
    CARD16  Fb, t, u, v;
    CARD32  sa;
    CARD8   da;

    cs = fbCombineMaskC (src, msk);
    s = cs.value;
    sa = cs.alpha;
    
    d = (*dst->fetch) (dst);
    da = d >> 24;
    
    switch (combine & CombineA) {
    default:
	Fa = 0;
	break;
    case CombineAOut:
	m = fbCombineConjointOutPart ((CARD8) (sa >> 0), da);
	n = fbCombineConjointOutPart ((CARD8) (sa >> 8), da) << 8;
	o = fbCombineConjointOutPart ((CARD8) (sa >> 16), da) << 16;
	p = fbCombineConjointOutPart ((CARD8) (sa >> 24), da) << 24;
	Fa = m|n|o|p;
	break;
    case CombineAIn:
	m = fbCombineConjointOutPart ((CARD8) (sa >> 0), da);
	n = fbCombineConjointOutPart ((CARD8) (sa >> 8), da) << 8;
	o = fbCombineConjointOutPart ((CARD8) (sa >> 16), da) << 16;
	p = fbCombineConjointOutPart ((CARD8) (sa >> 24), da) << 24;
	Fa = m|n|o|p;
	break;
    case CombineA:
	Fa = 0xffffffff;
	break;
    }
    
    switch (combine & CombineB) {
    default:
	Fb = 0;
	break;
    case CombineBOut:
	Fb = fbCombineConjointOutPart (da, sa);
	break;
    case CombineBIn:
	Fb = fbCombineConjointInPart (da, sa);
	break;
    case CombineB:
	Fb = 0xff;
	break;
    }
    m = FbGen (s,d,0,FbGet8(Fa,0),Fb,t,u,v);
    n = FbGen (s,d,8,FbGet8(Fa,8),Fb,t,u,v);
    o = FbGen (s,d,16,FbGet8(Fa,16),Fb,t,u,v);
    p = FbGen (s,d,24,FbGet8(Fa,24),Fb,t,u,v);
    s = m|n|o|p;
    (*dst->store) (dst, s);
}

void
fbCombineConjointOverU (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineAOver);
/*
    CARD32  s, d;
    CARD16  a;
    CARD16  t;
    CARD32  m,n,o,p;

    s = fbCombineMaskU (src, msk);
    a = s >> 24;
    if (a != 0x00)
    {
	if (a != 0xff)
	{
	    d = (*dst->fetch) (dst);
	    a = fbCombineConjointOutPart (d >> 24, a);
	    m = FbOverU(s,d,0,a,t);
	    n = FbOverU(s,d,8,a,t);
	    o = FbOverU(s,d,16,a,t);
	    p = FbOverU(s,d,24,a,t);
	    s = m|n|o|p;
	}
	(*dst->store) (dst, s);
    }
 */
}

void
fbCombineConjointOverC (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineAOver);
}

void
fbCombineConjointOverReverseU (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineBOver);
}

void
fbCombineConjointOverReverseC (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineBOver);
}

void
fbCombineConjointInU (FbCompositeOperand	    *src,
		      FbCompositeOperand	    *msk,
		      FbCompositeOperand	    *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineAIn);
}

void
fbCombineConjointInC (FbCompositeOperand	    *src,
		      FbCompositeOperand	    *msk,
		      FbCompositeOperand	    *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineAIn);
}

void
fbCombineConjointInReverseU (FbCompositeOperand  *src,
			     FbCompositeOperand  *msk,
			     FbCompositeOperand  *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineBIn);
}

void
fbCombineConjointInReverseC (FbCompositeOperand  *src,
			     FbCompositeOperand  *msk,
			     FbCompositeOperand  *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineBIn);
}

void
fbCombineConjointOutU (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineAOut);
}

void
fbCombineConjointOutC (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineAOut);
}

void
fbCombineConjointOutReverseU (FbCompositeOperand *src,
			      FbCompositeOperand *msk,
			      FbCompositeOperand *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineBOut);
}

void
fbCombineConjointOutReverseC (FbCompositeOperand *src,
			      FbCompositeOperand *msk,
			      FbCompositeOperand *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineBOut);
}

void
fbCombineConjointAtopU (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineAAtop);
}

void
fbCombineConjointAtopC (FbCompositeOperand   *src,
			FbCompositeOperand   *msk,
			FbCompositeOperand   *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineAAtop);
}

void
fbCombineConjointAtopReverseU (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineBAtop);
}

void
fbCombineConjointAtopReverseC (FbCompositeOperand    *src,
			       FbCompositeOperand    *msk,
			       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineBAtop);
}

void
fbCombineConjointXorU (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralU (src, msk, dst, CombineXor);
}

void
fbCombineConjointXorC (FbCompositeOperand    *src,
		       FbCompositeOperand    *msk,
		       FbCompositeOperand    *dst)
{
    fbCombineConjointGeneralC (src, msk, dst, CombineXor);
}

FbCombineFunc	fbCombineFuncU[] = {
    fbCombineClear,
    fbCombineSrcU,
    fbCombineDst,
    fbCombineOverU,
    fbCombineOverReverseU,
    fbCombineInU,
    fbCombineInReverseU,
    fbCombineOutU,
    fbCombineOutReverseU,
    fbCombineAtopU,
    fbCombineAtopReverseU,
    fbCombineXorU,
    fbCombineAddU,
    fbCombineDisjointOverU, /* Saturate */
    0,
    0,
    fbCombineClear,
    fbCombineSrcU,
    fbCombineDst,
    fbCombineDisjointOverU,
    fbCombineDisjointOverReverseU,
    fbCombineDisjointInU,
    fbCombineDisjointInReverseU,
    fbCombineDisjointOutU,
    fbCombineDisjointOutReverseU,
    fbCombineDisjointAtopU,
    fbCombineDisjointAtopReverseU,
    fbCombineDisjointXorU,
    0,
    0,
    0,
    0,
    fbCombineClear,
    fbCombineSrcU,
    fbCombineDst,
    fbCombineConjointOverU,
    fbCombineConjointOverReverseU,
    fbCombineConjointInU,
    fbCombineConjointInReverseU,
    fbCombineConjointOutU,
    fbCombineConjointOutReverseU,
    fbCombineConjointAtopU,
    fbCombineConjointAtopReverseU,
    fbCombineConjointXorU,
};

FbCombineFunc	fbCombineFuncC[] = {
    fbCombineClear,
    fbCombineSrcC,
    fbCombineDst,
    fbCombineOverC,
    fbCombineOverReverseC,
    fbCombineInC,
    fbCombineInReverseC,
    fbCombineOutC,
    fbCombineOutReverseC,
    fbCombineAtopC,
    fbCombineAtopReverseC,
    fbCombineXorC,
    fbCombineAddC,
    fbCombineDisjointOverC, /* Saturate */
    0,
    0,
    fbCombineClear,	    /* 0x10 */
    fbCombineSrcC,
    fbCombineDst,
    fbCombineDisjointOverC,
    fbCombineDisjointOverReverseC,
    fbCombineDisjointInC,
    fbCombineDisjointInReverseC,
    fbCombineDisjointOutC,
    fbCombineDisjointOutReverseC,
    fbCombineDisjointAtopC,
    fbCombineDisjointAtopReverseC,
    fbCombineDisjointXorC,  /* 0x1b */
    0,
    0,
    0,
    0,
    fbCombineClear,
    fbCombineSrcC,
    fbCombineDst,
    fbCombineConjointOverC,
    fbCombineConjointOverReverseC,
    fbCombineConjointInC,
    fbCombineConjointInReverseC,
    fbCombineConjointOutC,
    fbCombineConjointOutReverseC,
    fbCombineConjointAtopC,
    fbCombineConjointAtopReverseC,
    fbCombineConjointXorC,
};

/*
 * All of the fetch functions
 */

CARD32
fbFetch_a8r8g8b8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    return ((CARD32 *)line)[offset >> 5];
}

CARD32
fbFetch_x8r8g8b8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    return ((CARD32 *)line)[offset >> 5] | 0xff000000;
}

CARD32
fbFetch_a8b8g8r8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD32 *)line)[offset >> 5];

    return ((pixel & 0xff000000) |
	    ((pixel >> 16) & 0xff) |
	    (pixel & 0x0000ff00) |
	    ((pixel & 0xff) << 16));
}

CARD32
fbFetch_x8b8g8r8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD32 *)line)[offset >> 5];

    return ((0xff000000) |
	    ((pixel >> 16) & 0xff) |
	    (pixel & 0x0000ff00) |
	    ((pixel & 0xff) << 16));
}

CARD32
fbFetch_r8g8b8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
#if IMAGE_BYTE_ORDER == MSBFirst
    return (0xff000000 |
	    (pixel[0] << 16) |
	    (pixel[1] << 8) |
	    (pixel[2]));
#else
    return (0xff000000 |
	    (pixel[2] << 16) |
	    (pixel[1] << 8) |
	    (pixel[0]));
#endif
}

CARD32
fbFetch_b8g8r8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
#if IMAGE_BYTE_ORDER == MSBFirst
    return (0xff000000 |
	    (pixel[2] << 16) |
	    (pixel[1] << 8) |
	    (pixel[0]));
#else
    return (0xff000000 |
	    (pixel[0] << 16) |
	    (pixel[1] << 8) |
	    (pixel[2]));
#endif
}

CARD32
fbFetch_r5g6b5 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  r,g,b;

    r = ((pixel & 0xf800) | ((pixel & 0xe000) >> 5)) << 8;
    g = ((pixel & 0x07e0) | ((pixel & 0x0600) >> 6)) << 5;
    b = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) >> 2;
    return (0xff000000 | r | g | b);
}

CARD32
fbFetch_b5g6r5 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  r,g,b;

    b = ((pixel & 0xf800) | ((pixel & 0xe000) >> 5)) >> 8;
    g = ((pixel & 0x07e0) | ((pixel & 0x0600) >> 6)) << 5;
    r = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) << 14;
    return (0xff000000 | r | g | b);
}

CARD32
fbFetch_a1r5g5b5 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  a,r,g,b;

    a = (CARD32) ((CARD8) (0 - ((pixel & 0x8000) >> 15))) << 24;
    r = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) << 9;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    b = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) >> 2;
    return (a | r | g | b);
}

CARD32
fbFetch_x1r5g5b5 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  r,g,b;

    r = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) << 9;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    b = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) >> 2;
    return (0xff000000 | r | g | b);
}

CARD32
fbFetch_a1b5g5r5 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  a,r,g,b;

    a = (CARD32) ((CARD8) (0 - ((pixel & 0x8000) >> 15))) << 24;
    b = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) >> 7;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    r = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) << 14;
    return (a | r | g | b);
}

CARD32
fbFetch_x1b5g5r5 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  r,g,b;

    b = ((pixel & 0x7c00) | ((pixel & 0x7000) >> 5)) >> 7;
    g = ((pixel & 0x03e0) | ((pixel & 0x0380) >> 5)) << 6;
    r = ((pixel & 0x001c) | ((pixel & 0x001f) << 5)) << 14;
    return (0xff000000 | r | g | b);
}

CARD32
fbFetch_a4r4g4b4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  a,r,g,b;

    a = ((pixel & 0xf000) | ((pixel & 0xf000) >> 4)) << 16;
    r = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) << 12;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    b = ((pixel & 0x000f) | ((pixel & 0x000f) << 4));
    return (a | r | g | b);
}
    
CARD32
fbFetch_x4r4g4b4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  r,g,b;

    r = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) << 12;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    b = ((pixel & 0x000f) | ((pixel & 0x000f) << 4));
    return (0xff000000 | r | g | b);
}
    
CARD32
fbFetch_a4b4g4r4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  a,r,g,b;

    a = ((pixel & 0xf000) | ((pixel & 0xf000) >> 4)) << 16;
    b = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) << 12;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    r = ((pixel & 0x000f) | ((pixel & 0x000f) << 4));
    return (a | r | g | b);
}
    
CARD32
fbFetch_x4b4g4r4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD16 *) line)[offset >> 4];
    CARD32  r,g,b;

    b = ((pixel & 0x0f00) | ((pixel & 0x0f00) >> 4)) << 12;
    g = ((pixel & 0x00f0) | ((pixel & 0x00f0) >> 4)) << 8;
    r = ((pixel & 0x000f) | ((pixel & 0x000f) << 4));
    return (0xff000000 | r | g | b);
}
    
CARD32
fbFetch_a8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];
    
    return pixel << 24;
}

CARD32
fbFetcha_a8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];
    
    pixel |= pixel << 8;
    pixel |= pixel << 16;
    return pixel;
}

CARD32
fbFetch_r3g3b2 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];
    CARD32  r,g,b;
    
    r = ((pixel & 0xe0) | ((pixel & 0xe0) >> 3) | ((pixel & 0xc0) >> 6)) << 16;
    g = ((pixel & 0x1c) | ((pixel & 0x18) >> 3) | ((pixel & 0x1c) << 3)) << 8;
    b = (((pixel & 0x03)     ) | 
	 ((pixel & 0x03) << 2) | 
	 ((pixel & 0x03) << 4) |
	 ((pixel & 0x03) << 6));
    return (0xff000000 | r | g | b);
}

CARD32
fbFetch_b2g3r3 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];
    CARD32  r,g,b;
    
    b = (((pixel & 0xc0)     ) | 
	 ((pixel & 0xc0) >> 2) |
	 ((pixel & 0xc0) >> 4) |
	 ((pixel & 0xc0) >> 6));
    g = ((pixel & 0x38) | ((pixel & 0x38) >> 3) | ((pixel & 0x30) << 2)) << 8;
    r = (((pixel & 0x07)     ) | 
	 ((pixel & 0x07) << 3) | 
	 ((pixel & 0x06) << 6)) << 16;
    return (0xff000000 | r | g | b);
}

CARD32
fbFetch_a2r2g2b2 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];
    CARD32   a,r,g,b;

    a = ((pixel & 0xc0) * 0x55) << 18;
    r = ((pixel & 0x30) * 0x55) << 12;
    g = ((pixel & 0x0c) * 0x55) << 6;
    b = ((pixel & 0x03) * 0x55);
    return a|r|g|b;
}

CARD32
fbFetch_a2b2g2r2 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];
    CARD32   a,r,g,b;

    a = ((pixel & 0xc0) * 0x55) << 18;
    b = ((pixel & 0x30) * 0x55) >> 6;
    g = ((pixel & 0x0c) * 0x55) << 6;
    r = ((pixel & 0x03) * 0x55) << 16;
    return a|r|g|b;
}

CARD32
fbFetch_c8 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32   pixel = ((CARD8 *) line)[offset>>3];

    return op->indexed->rgba[pixel];
}

#define Fetch8(l,o)    (((CARD8 *) (l))[(o) >> 3])
#if IMAGE_BYTE_ORDER == MSBFirst
#define Fetch4(l,o)    ((o) & 2 ? Fetch8(l,o) & 0xf : Fetch8(l,o) >> 4)
#else
#define Fetch4(l,o)    ((o) & 2 ? Fetch8(l,o) >> 4 : Fetch8(l,o) & 0xf)
#endif

CARD32
fbFetch_a4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);
    
    pixel |= pixel << 4;
    return pixel << 24;
}

CARD32
fbFetcha_a4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);
    
    pixel |= pixel << 4;
    pixel |= pixel << 8;
    pixel |= pixel << 16;
    return pixel;
}

CARD32
fbFetch_r1g2b1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);
    CARD32  r,g,b;

    r = ((pixel & 0x8) * 0xff) << 13;
    g = ((pixel & 0x6) * 0x55) << 7;
    b = ((pixel & 0x1) * 0xff);
    return 0xff000000|r|g|b;
}

CARD32
fbFetch_b1g2r1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);
    CARD32  r,g,b;

    b = ((pixel & 0x8) * 0xff) >> 3;
    g = ((pixel & 0x6) * 0x55) << 7;
    r = ((pixel & 0x1) * 0xff) << 16;
    return 0xff000000|r|g|b;
}

CARD32
fbFetch_a1r1g1b1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);
    CARD32  a,r,g,b;

    a = ((pixel & 0x8) * 0xff) << 21;
    r = ((pixel & 0x4) * 0xff) << 14;
    g = ((pixel & 0x2) * 0xff) << 7;
    b = ((pixel & 0x1) * 0xff);
    return a|r|g|b;
}

CARD32
fbFetch_a1b1g1r1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);
    CARD32  a,r,g,b;

    a = ((pixel & 0x8) * 0xff) << 21;
    r = ((pixel & 0x4) * 0xff) >> 3;
    g = ((pixel & 0x2) * 0xff) << 7;
    b = ((pixel & 0x1) * 0xff) << 16;
    return a|r|g|b;
}

CARD32
fbFetch_c4 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = Fetch4(line, offset);

    return op->indexed->rgba[pixel];
}

CARD32
fbFetcha_a1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD32 *)line)[offset >> 5];
    CARD32  a;
#if BITMAP_BIT_ORDER == MSBFirst
    a = pixel >> (0x1f - (offset & 0x1f));
#else
    a = pixel >> (offset & 0x1f);
#endif
    a = a & 1;
    a |= a << 1;
    a |= a << 2;
    a |= a << 4;
    a |= a << 8;
    a |= a << 16;
    return a;
}

CARD32
fbFetch_a1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD32 *)line)[offset >> 5];
    CARD32  a;
#if BITMAP_BIT_ORDER == MSBFirst
    a = pixel >> (0x1f - (offset & 0x1f));
#else
    a = pixel >> (offset & 0x1f);
#endif
    a = a & 1;
    a |= a << 1;
    a |= a << 2;
    a |= a << 4;
    return a << 24;
}

CARD32
fbFetch_g1 (FbCompositeOperand *op)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel = ((CARD32 *)line)[offset >> 5];
    CARD32  a;
#if BITMAP_BIT_ORDER == MSBFirst
    a = pixel >> (0x1f - (offset & 0x1f));
#else
    a = pixel >> (offset & 0x1f);
#endif
    a = a & 1;
    return op->indexed->rgba[a];
}

/*
 * All the store functions
 */

#define Splita(v)	CARD32	a = ((v) >> 24), r = ((v) >> 16) & 0xff, g = ((v) >> 8) & 0xff, b = (v) & 0xff
#define Split(v)	CARD32	r = ((v) >> 16) & 0xff, g = ((v) >> 8) & 0xff, b = (v) & 0xff

void
fbStore_a8r8g8b8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    ((CARD32 *)line)[offset >> 5] = value;
}

void
fbStore_x8r8g8b8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    ((CARD32 *)line)[offset >> 5] = value & 0xffffff;
}

void
fbStore_a8b8g8r8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    Splita(value);
    ((CARD32 *)line)[offset >> 5] = a << 24 | b << 16 | g << 8 | r;
}

void
fbStore_x8b8g8r8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    Split(value);
    ((CARD32 *)line)[offset >> 5] = b << 16 | g << 8 | r;
}

void
fbStore_r8g8b8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    Split(value);
#if IMAGE_BYTE_ORDER == MSBFirst
    pixel[0] = r;
    pixel[1] = g;
    pixel[2] = b;
#else
    pixel[0] = b;
    pixel[1] = g;
    pixel[2] = r;
#endif
}

void
fbStore_b8g8r8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    Split(value);
#if IMAGE_BYTE_ORDER == MSBFirst
    pixel[0] = b;
    pixel[1] = g;
    pixel[2] = r;
#else
    pixel[0] = r;
    pixel[1] = g;
    pixel[2] = b;
#endif
}

void
fbStore_r5g6b5 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Split(value);
    *pixel = (((r << 8) & 0xf800) |
	      ((g << 3) & 0x07e0) |
	      ((b >> 3)         ));
}

void
fbStore_b5g6r5 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Split(value);
    *pixel = (((b << 8) & 0xf800) |
	      ((g << 3) & 0x07e0) |
	      ((r >> 3)         ));
}

void
fbStore_a1r5g5b5 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Splita(value);
    *pixel = (((a << 8) & 0x8000) |
	      ((r << 7) & 0x7c00) |
	      ((g << 2) & 0x03e0) |
	      ((b >> 3)         ));
}

void
fbStore_x1r5g5b5 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Split(value);
    *pixel = (((r << 7) & 0x7c00) |
	      ((g << 2) & 0x03e0) |
	      ((b >> 3)         ));
}

void
fbStore_a1b5g5r5 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Splita(value);
    *pixel = (((a << 8) & 0x8000) |
	      ((b << 7) & 0x7c00) |
	      ((g << 2) & 0x03e0) |
	      ((r >> 3)         ));
}

void
fbStore_x1b5g5r5 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Split(value);
    *pixel = (((b << 7) & 0x7c00) |
	      ((g << 2) & 0x03e0) |
	      ((r >> 3)         ));
}

void
fbStore_a4r4g4b4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Splita(value);
    *pixel = (((a << 8) & 0xf000) |
	      ((r << 4) & 0x0f00) |
	      ((g     ) & 0x00f0) |
	      ((b >> 4)         ));
}

void
fbStore_x4r4g4b4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Split(value);
    *pixel = (((r << 4) & 0x0f00) |
	      ((g     ) & 0x00f0) |
	      ((b >> 4)         ));
}

void
fbStore_a4b4g4r4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Splita(value);
    *pixel = (((a << 8) & 0xf000) |
	      ((b << 4) & 0x0f00) |
	      ((g     ) & 0x00f0) |
	      ((r >> 4)         ));
}

void
fbStore_x4b4g4r4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD16  *pixel = ((CARD16 *) line) + (offset >> 4);
    Split(value);
    *pixel = (((b << 4) & 0x0f00) |
	      ((g     ) & 0x00f0) |
	      ((r >> 4)         ));
}

void
fbStore_a8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    *pixel = value >> 24;
}

void
fbStore_r3g3b2 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    Split(value);
    *pixel = (((r     ) & 0xe0) |
	      ((g >> 3) & 0x1c) |
	      ((b >> 6)       ));
}

void
fbStore_b2g3r3 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    Split(value);
    *pixel = (((b     ) & 0xe0) |
	      ((g >> 3) & 0x1c) |
	      ((r >> 6)       ));
}

void
fbStore_a2r2g2b2 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    Splita(value);
    *pixel = (((a     ) & 0xc0) |
	      ((r >> 2) & 0x30) |
	      ((g >> 4) & 0x0c) |
	      ((b >> 6)       ));
}

void
fbStore_c8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    *pixel = miIndexToEnt24(op->indexed,value);
}

void
fbStore_g8 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD8   *pixel = ((CARD8 *) line) + (offset >> 3);
    *pixel = miIndexToEntY24(op->indexed,value);
}

#define Store8(l,o,v)  (((CARD8 *) l)[(o) >> 3] = (v))
#if IMAGE_BYTE_ORDER == MSBFirst
#define Store4(l,o,v)  Store8(l,o,((o) & 4 ? \
				   (Fetch8(l,o) & 0xf0) | (v) : \
				   (Fetch8(l,o) & 0x0f) | ((v) << 4)))
#else
#define Store4(l,o,v)  Store8(l,o,((o) & 4 ? \
				   (Fetch8(l,o) & 0x0f) | ((v) << 4) : \
				   (Fetch8(l,o) & 0xf0) | (v)))
#endif

void
fbStore_a4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    Store4(line,offset,value>>28);
}

void
fbStore_r1g2b1 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel;
    
    Split(value);
    pixel = (((r >> 4) & 0x8) |
	     ((g >> 5) & 0x6) |
	     ((b >> 7)      ));
    Store4(line,offset,pixel);
}

void
fbStore_b1g2r1 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel;
    
    Split(value);
    pixel = (((b >> 4) & 0x8) |
	     ((g >> 5) & 0x6) |
	     ((r >> 7)      ));
    Store4(line,offset,pixel);
}

void
fbStore_a1r1g1b1 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel;
    Splita(value);
    pixel = (((a >> 4) & 0x8) |
	     ((r >> 5) & 0x4) |
	     ((g >> 6) & 0x2) |
	     ((b >> 7)      ));
    Store4(line,offset,pixel);
}

void
fbStore_a1b1g1r1 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel;
    Splita(value);
    pixel = (((a >> 4) & 0x8) |
	     ((b >> 5) & 0x4) |
	     ((g >> 6) & 0x2) |
	     ((r >> 7)      ));
    Store4(line,offset,pixel);
}

void
fbStore_c4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel;
    
    pixel = miIndexToEnt24(op->indexed,value);
    Store4(line,offset,pixel);
}

void
fbStore_g4 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  pixel;
    
    pixel = miIndexToEntY24(op->indexed,value);
    Store4(line,offset,pixel);
}

void
fbStore_a1 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  *pixel = ((CARD32 *) line) + (offset >> 5);
    CARD32  mask = FbStipMask(offset & 0x1f, 1);

    value = value & 0x80000000 ? mask : 0;
    *pixel = (*pixel & ~mask) | value;
}

void
fbStore_g1 (FbCompositeOperand *op, CARD32 value)
{
    FbBits  *line = op->u.drawable.line; CARD32 offset = op->u.drawable.offset;
    CARD32  *pixel = ((CARD32 *) line) + (offset >> 5);
    CARD32  mask = FbStipMask(offset & 0x1f, 1);

    value = miIndexToEntY24(op->indexed,value) ? mask : 0;
    *pixel = (*pixel & ~mask) | value;
}

CARD32
fbFetch_external (FbCompositeOperand *op)
{
    CARD32  rgb = (*op[1].fetch) (&op[1]);
    CARD32  a = (*op[2].fetch) (&op[2]);

    return (rgb & 0xffffff) | (a & 0xff000000);
}


CARD32
fbFetcha_external (FbCompositeOperand *op)
{
    return (*op[2].fetch) (&op[2]);
}

void
fbStore_external (FbCompositeOperand *op, CARD32 value)
{
    (*op[1].store) (&op[1], value | 0xff000000);
    (*op[2].store) (&op[2], value & 0xff000000);
}

CARD32
fbFetch_transform (FbCompositeOperand *op)
{
    PictVector	v;
    int		x, y;
    int		minx, maxx, miny, maxy;
    int		n;
    BoxRec	box;
    CARD32	rtot, gtot, btot, atot;
    CARD32	xerr, yerr;
    CARD32	bits;

    v.vector[0] = IntToxFixed(op->u.transform.x);
    v.vector[1] = IntToxFixed(op->u.transform.y);
    v.vector[2] = xFixed1;
    if (!PictureTransformPoint (op->u.transform.transform, &v))
	return 0;
    switch (op->u.transform.filter) {
    case PictFilterNearest:
	y = xFixedToInt (v.vector[1]) + op->u.transform.top_y;
	x = xFixedToInt (v.vector[0]) + op->u.transform.left_x;
	if (POINT_IN_REGION (0, op->clip, x, y, &box))
	{
	    (*op[1].set) (&op[1], x, y);
	    bits = (*op[1].fetch) (&op[1]);
	}
	else
	    bits = 0;
	break;
    case PictFilterBilinear:
	rtot = gtot = btot = atot = 0;
	miny = xFixedToInt (v.vector[1]) + op->u.transform.top_y;
	maxy = xFixedToInt (xFixedCeil (v.vector[1])) + op->u.transform.top_y;
	
	minx = xFixedToInt (v.vector[0]) + op->u.transform.left_x;
	maxx = xFixedToInt (xFixedCeil (v.vector[0])) + op->u.transform.left_x;
	
	yerr = xFixed1 - xFixedFrac (v.vector[1]);
	for (y = miny; y <= maxy; y++)
	{
	    CARD32	lrtot = 0, lgtot = 0, lbtot = 0, latot = 0;
	    
	    xerr = xFixed1 - xFixedFrac (v.vector[0]);
	    for (x = minx; x <= maxx; x++)
	    {
		if (POINT_IN_REGION (0, op->clip, x, y, &box))
		{
		    (*op[1].set) (&op[1], x, y);
		    bits = (*op[1].fetch) (&op[1]);
		    {
			Splita(bits);
			lrtot += r * xerr;
			lgtot += g * xerr;
			lbtot += b * xerr;
			latot += a * xerr;
			n++;
		    }
		}
		xerr = xFixed1 - xerr;
	    }
	    rtot += (lrtot >> 10) * yerr;
	    gtot += (lgtot >> 10) * yerr;
	    btot += (lbtot >> 10) * yerr;
	    atot += (latot >> 10) * yerr;
	    yerr = xFixed1 - yerr;
	}
	if ((atot >>= 22) > 0xff) atot = 0xff;
	if ((rtot >>= 22) > 0xff) rtot = 0xff;
	if ((gtot >>= 22) > 0xff) gtot = 0xff;
	if ((btot >>= 22) > 0xff) btot = 0xff;
	bits = ((atot << 24) |
		(rtot << 16) |
		(gtot <<  8) |
		(btot       ));
	break;
    default:
	bits = 0;
	break;
    }
    return bits;
}

CARD32
fbFetcha_transform (FbCompositeOperand *op)
{
    PictVector	v;
    int		x, y;
    int		minx, maxx, miny, maxy;
    int		n;
    BoxRec	box;
    CARD32	rtot, gtot, btot, atot;
    CARD32	xerr, yerr;
    CARD32	bits;

    v.vector[0] = IntToxFixed(op->u.transform.x);
    v.vector[1] = IntToxFixed(op->u.transform.y);
    v.vector[2] = xFixed1;
    if (!PictureTransformPoint (op->u.transform.transform, &v))
	return 0;
    switch (op->u.transform.filter) {
    case PictFilterNearest:
	y = xFixedToInt (v.vector[1]) + op->u.transform.left_x;
	x = xFixedToInt (v.vector[0]) + op->u.transform.top_y;
	if (POINT_IN_REGION (0, op->clip, x, y, &box))
	{
	    (*op[1].set) (&op[1], x, y);
	    bits = (*op[1].fetcha) (&op[1]);
	}
	else
	    bits = 0;
	break;
    case PictFilterBilinear:
	rtot = gtot = btot = atot = 0;
	
	miny = xFixedToInt (v.vector[1]) + op->u.transform.top_y;
	maxy = xFixedToInt (xFixedCeil (v.vector[1])) + op->u.transform.top_y;
	
	minx = xFixedToInt (v.vector[0]) + op->u.transform.left_x;
	maxx = xFixedToInt (xFixedCeil (v.vector[0])) + op->u.transform.left_x;
	
	yerr = xFixed1 - xFixedFrac (v.vector[1]);
	for (y = miny; y <= maxy; y++)
	{
	    CARD32	lrtot = 0, lgtot = 0, lbtot = 0, latot = 0;
	    xerr = xFixed1 - xFixedFrac (v.vector[0]);
	    for (x = minx; x <= maxx; x++)
	    {
		if (POINT_IN_REGION (0, op->clip, x, y, &box))
		{
		    (*op[1].set) (&op[1], x, y);
		    bits = (*op[1].fetcha) (&op[1]);
		    {
			Splita(bits);
			lrtot += r * xerr;
			lgtot += g * xerr;
			lbtot += b * xerr;
			latot += a * xerr;
			n++;
		    }
		}
		x++;
		xerr = xFixed1 - xerr;
	    }
	    rtot += (lrtot >> 10) * yerr;
	    gtot += (lgtot >> 10) * yerr;
	    btot += (lbtot >> 10) * yerr;
	    atot += (latot >> 10) * yerr;
	    y++;
	    yerr = xFixed1 - yerr;
	}
	if ((atot >>= 22) > 0xff) atot = 0xff;
	if ((rtot >>= 22) > 0xff) rtot = 0xff;
	if ((gtot >>= 22) > 0xff) gtot = 0xff;
	if ((btot >>= 22) > 0xff) btot = 0xff;
	bits = ((atot << 24) |
		(rtot << 16) |
		(gtot <<  8) |
		(btot       ));
	break;
    default:
	bits = 0;
	break;
    }
    return bits;
}

FbAccessMap fbAccessMap[] = {
    /* 32bpp formats */
    { PICT_a8r8g8b8,	fbFetch_a8r8g8b8,	fbFetch_a8r8g8b8,	fbStore_a8r8g8b8 },
    { PICT_x8r8g8b8,	fbFetch_x8r8g8b8,	fbFetch_x8r8g8b8,	fbStore_x8r8g8b8 },
    { PICT_a8b8g8r8,	fbFetch_a8b8g8r8,	fbFetch_a8b8g8r8,	fbStore_a8b8g8r8 },
    { PICT_x8b8g8r8,	fbFetch_x8b8g8r8,	fbFetch_x8b8g8r8,	fbStore_x8b8g8r8 },

    /* 24bpp formats */
    { PICT_r8g8b8,	fbFetch_r8g8b8,		fbFetch_r8g8b8,		fbStore_r8g8b8 },
    { PICT_b8g8r8,	fbFetch_b8g8r8,		fbFetch_b8g8r8,		fbStore_b8g8r8 },

    /* 16bpp formats */
    { PICT_r5g6b5,	fbFetch_r5g6b5,		fbFetch_r5g6b5,		fbStore_r5g6b5 },
    { PICT_b5g6r5,	fbFetch_b5g6r5,		fbFetch_b5g6r5,		fbStore_b5g6r5 },

    { PICT_a1r5g5b5,	fbFetch_a1r5g5b5,	fbFetch_a1r5g5b5,	fbStore_a1r5g5b5 },
    { PICT_x1r5g5b5,	fbFetch_x1r5g5b5,	fbFetch_x1r5g5b5,	fbStore_x1r5g5b5 },
    { PICT_a1b5g5r5,	fbFetch_a1b5g5r5,	fbFetch_a1b5g5r5,	fbStore_a1b5g5r5 },
    { PICT_x1b5g5r5,	fbFetch_x1b5g5r5,	fbFetch_x1b5g5r5,	fbStore_x1b5g5r5 },
    { PICT_a4r4g4b4,	fbFetch_a4r4g4b4,	fbFetch_a4r4g4b4,	fbStore_a4r4g4b4 },
    { PICT_x4r4g4b4,	fbFetch_x4r4g4b4,	fbFetch_x4r4g4b4,	fbStore_x4r4g4b4 },
    { PICT_a4b4g4r4,	fbFetch_a4b4g4r4,	fbFetch_a4b4g4r4,	fbStore_a4b4g4r4 },
    { PICT_x4b4g4r4,	fbFetch_x4b4g4r4,	fbFetch_x4b4g4r4,	fbStore_x4b4g4r4 },

    /* 8bpp formats */
    { PICT_a8,		fbFetch_a8,		fbFetcha_a8,		fbStore_a8 },
    { PICT_r3g3b2,	fbFetch_r3g3b2,		fbFetch_r3g3b2,		fbStore_r3g3b2 },
    { PICT_b2g3r3,	fbFetch_b2g3r3,		fbFetch_b2g3r3,		fbStore_b2g3r3 },
    { PICT_a2r2g2b2,	fbFetch_a2r2g2b2,	fbFetch_a2r2g2b2,	fbStore_a2r2g2b2 },
    { PICT_c8,		fbFetch_c8,		fbFetch_c8,		fbStore_c8 },
    { PICT_g8,		fbFetch_c8,		fbFetch_c8,		fbStore_g8 },

    /* 4bpp formats */
    { PICT_a4,		fbFetch_a4,		fbFetcha_a4,		fbStore_a4 },
    { PICT_r1g2b1,	fbFetch_r1g2b1,		fbFetch_r1g2b1,		fbStore_r1g2b1 },
    { PICT_b1g2r1,	fbFetch_b1g2r1,		fbFetch_b1g2r1,		fbStore_b1g2r1 },
    { PICT_a1r1g1b1,	fbFetch_a1r1g1b1,	fbFetch_a1r1g1b1,	fbStore_a1r1g1b1 },
    { PICT_a1b1g1r1,	fbFetch_a1b1g1r1,	fbFetch_a1b1g1r1,	fbStore_a1b1g1r1 },
    { PICT_c4,		fbFetch_c4,		fbFetch_c4,		fbStore_c4 },
    { PICT_g4,		fbFetch_c4,		fbFetch_c4,		fbStore_g4 },

    /* 1bpp formats */
    { PICT_a1,		fbFetch_a1,		fbFetcha_a1,		fbStore_a1 },
    { PICT_g1,		fbFetch_g1,		fbFetch_g1,		fbStore_g1 },
};
#define NumAccessMap (sizeof fbAccessMap / sizeof fbAccessMap[0])

static void
fbStepOver (FbCompositeOperand *op)
{
    op->u.drawable.offset += op->u.drawable.bpp;
}

static void
fbStepDown (FbCompositeOperand *op)
{
    op->u.drawable.line += op->u.drawable.stride;
    op->u.drawable.offset = op->u.drawable.start_offset;
}

static void
fbSet (FbCompositeOperand *op, int x, int y)
{
    op->u.drawable.line = op->u.drawable.top_line + y * op->u.drawable.stride;
    op->u.drawable.offset = op->u.drawable.left_offset + x * op->u.drawable.bpp;
}

static void
fbStepOver_external (FbCompositeOperand *op)
{
    (*op[1].over) (&op[1]);
    (*op[2].over) (&op[2]);
}

static void
fbStepDown_external (FbCompositeOperand *op)
{
    (*op[1].down) (&op[1]);
    (*op[2].down) (&op[2]);
}

static void
fbSet_external (FbCompositeOperand *op, int x, int y)
{
    (*op[1].set) (&op[1], x, y);
    (*op[2].set) (&op[2], 
		  x - op->u.external.alpha_dx,
		  y - op->u.external.alpha_dy);
}

static void
fbStepOver_transform (FbCompositeOperand *op)
{
    op->u.transform.x++;   
}

static void
fbStepDown_transform (FbCompositeOperand *op)
{
    op->u.transform.y++;
    op->u.transform.x = op->u.transform.start_x;
}

static void
fbSet_transform (FbCompositeOperand *op, int x, int y)
{
    op->u.transform.x = x - op->u.transform.left_x;
    op->u.transform.y = y - op->u.transform.top_y;
}


Bool
fbBuildCompositeOperand (PicturePtr	    pPict,
			 FbCompositeOperand op[4],
			 INT16		    x,
			 INT16		    y,
			 Bool		    transform,
			 Bool		    alpha)
{
    /* Check for transform */
    if (transform && pPict->transform)
    {
	if (!fbBuildCompositeOperand (pPict, &op[1], 0, 0, FALSE, alpha))
	    return FALSE;
	
	op->u.transform.top_y = pPict->pDrawable->y;
	op->u.transform.left_x = pPict->pDrawable->x;
	
	op->u.transform.start_x = x - op->u.transform.left_x;
	op->u.transform.x = op->u.transform.start_x;
	op->u.transform.y = y - op->u.transform.top_y;
	op->u.transform.transform = pPict->transform;
	op->u.transform.filter = pPict->filter;
	
	op->fetch = fbFetch_transform;
	op->fetcha = fbFetcha_transform;
	op->store = 0;
	op->over = fbStepOver_transform;
	op->down = fbStepDown_transform;
	op->set = fbSet_transform;
        op->indexed = (miIndexedPtr) pPict->pFormat->index.devPrivate;
	op->clip = op[1].clip;
	
	return TRUE;
    }
    /* Check for external alpha */
    else if (alpha && pPict->alphaMap)
    {
	if (!fbBuildCompositeOperand (pPict, &op[1], x, y, FALSE, FALSE))
	    return FALSE;
	if (!fbBuildCompositeOperand (pPict->alphaMap, &op[2],
				      x - pPict->alphaOrigin.x,
				      y - pPict->alphaOrigin.y,
				      FALSE, FALSE))
	    return FALSE;
	op->u.external.alpha_dx = pPict->alphaOrigin.x;
	op->u.external.alpha_dy = pPict->alphaOrigin.y;

	op->fetch = fbFetch_external;
	op->fetcha = fbFetcha_external;
	op->store = fbStore_external;
	op->over = fbStepOver_external;
	op->down = fbStepDown_external;
	op->set = fbSet_external;
        op->indexed = (miIndexedPtr) pPict->pFormat->index.devPrivate;
	/* XXX doesn't handle external alpha clips yet */
	op->clip = op[1].clip;
	
	return TRUE;
    }
    /* Build simple operand */
    else
    {
	int	    i;
	int	    xoff, yoff;

	for (i = 0; i < NumAccessMap; i++)
	    if (fbAccessMap[i].format == pPict->format)
	    {
		FbBits	*bits;
		FbStride	stride;
		int		bpp;

		op->fetch = fbAccessMap[i].fetch;
		op->fetcha = fbAccessMap[i].fetcha;
		op->store = fbAccessMap[i].store;
		op->over = fbStepOver;
		op->down = fbStepDown;
		op->set = fbSet;
		op->indexed = (miIndexedPtr) pPict->pFormat->index.devPrivate;
		op->clip = pPict->pCompositeClip;

		fbGetDrawable (pPict->pDrawable, bits, stride, bpp,
			       xoff, yoff);
		if (pPict->repeat && pPict->pDrawable->width == 1 && 
		    pPict->pDrawable->height == 1)
		{
		    bpp = 0;
		    stride = 0;
		}
		/*
		 * Coordinates of upper left corner of drawable
		 */
		op->u.drawable.top_line = bits + yoff * stride;
		op->u.drawable.left_offset = xoff * bpp;

		/*
		 * Starting position within drawable
		 */
		op->u.drawable.start_offset = op->u.drawable.left_offset + x * bpp;
		op->u.drawable.line = op->u.drawable.top_line + y * stride;
		op->u.drawable.offset = op->u.drawable.start_offset;

		op->u.drawable.stride = stride;
		op->u.drawable.bpp = bpp;
		return TRUE;
	    }
	return FALSE;
    }
}

void
fbCompositeGeneral (CARD8	op,
		    PicturePtr	pSrc,
		    PicturePtr	pMask,
		    PicturePtr	pDst,
		    INT16	xSrc,
		    INT16	ySrc,
		    INT16	xMask,
		    INT16	yMask,
		    INT16	xDst,
		    INT16	yDst,
		    CARD16	width,
		    CARD16	height)
{
    FbCompositeOperand	src[4],msk[4],dst[4],*pmsk;
    FbCompositeOperand	*srcPict, *srcAlpha;
    FbCompositeOperand	*dstPict, *dstAlpha;
    FbCompositeOperand	*mskPict = 0, *mskAlpha = 0;
    FbCombineFunc	f;
    int			w;

    if (!fbBuildCompositeOperand (pSrc, src, xSrc, ySrc, TRUE, TRUE))
	return;
    if (!fbBuildCompositeOperand (pDst, dst, xDst, yDst, FALSE, TRUE))
	return;
    if (pSrc->alphaMap)
    {
	srcPict = &src[1];
	srcAlpha = &src[2];
    }
    else
    {
	srcPict = &src[0];
	srcAlpha = 0;
    }
    if (pDst->alphaMap)
    {
	dstPict = &dst[1];
	dstAlpha = &dst[2];
    }
    else
    {
	dstPict = &dst[0];
	dstAlpha = 0;
    }
    f = fbCombineFuncU[op];
    if (pMask)
    {
	if (!fbBuildCompositeOperand (pMask, msk, xMask, yMask, TRUE, TRUE))
	    return;
	pmsk = msk;
	if (pMask->componentAlpha)
	    f = fbCombineFuncC[op];
	if (pMask->alphaMap)
	{
	    mskPict = &msk[1];
	    mskAlpha = &msk[2];
	}
	else
	{
	    mskPict = &msk[0];
	    mskAlpha = 0;
	}
    }
    else
	pmsk = 0;
    while (height--)
    {
	w = width;
	
	while (w--)
	{
	    (*f) (src, pmsk, dst);
	    (*src->over) (src);
	    (*dst->over) (dst);
	    if (pmsk)
		(*pmsk->over) (pmsk);
	}
	(*src->down) (src);
	(*dst->down) (dst);
	if (pmsk)
	    (*pmsk->down) (pmsk);
    }
}
