/*
 *
 * Copyright © 2000 SuSE, 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 SuSE not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  SuSE makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
 * 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.
 *
 * Author:  Keith Packard, SuSE, Inc.
 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include "misc.h"
#include "scrnintstr.h"
#include "os.h"
#include "regionstr.h"
#include "validate.h"
#include "windowstr.h"
#include "input.h"
#include "resource.h"
#include "colormapst.h"
#include "cursorstr.h"
#include "dixstruct.h"
#include "gcstruct.h"
#include "servermd.h"
#include "picturestr.h"
#include "xace.h"

DevPrivateKeyRec PictureScreenPrivateKeyRec;
DevPrivateKeyRec PictureWindowPrivateKeyRec;
static int	PictureGeneration;
RESTYPE		PictureType;
RESTYPE		PictFormatType;
RESTYPE		GlyphSetType;
int		PictureCmapPolicy = PictureCmapPolicyDefault;

Bool
PictureDestroyWindow (WindowPtr pWindow)
{
    ScreenPtr		pScreen = pWindow->drawable.pScreen;
    PicturePtr		pPicture;
    PictureScreenPtr    ps = GetPictureScreen(pScreen);
    Bool		ret;

    while ((pPicture = GetPictureWindow(pWindow)))
    {
	SetPictureWindow(pWindow, pPicture->pNext);
	if (pPicture->id)
	    FreeResource (pPicture->id, PictureType);
	FreePicture ((pointer) pPicture, pPicture->id);
    }
    pScreen->DestroyWindow = ps->DestroyWindow;
    ret = (*pScreen->DestroyWindow) (pWindow);
    ps->DestroyWindow = pScreen->DestroyWindow;
    pScreen->DestroyWindow = PictureDestroyWindow;
    return ret;
}

Bool
PictureCloseScreen (int index, ScreenPtr pScreen)
{
    PictureScreenPtr    ps = GetPictureScreen(pScreen);
    Bool                ret;
    int			n;

    pScreen->CloseScreen = ps->CloseScreen;
    ret = (*pScreen->CloseScreen) (index, pScreen);
    PictureResetFilters (pScreen);
    for (n = 0; n < ps->nformats; n++)
	if (ps->formats[n].type == PictTypeIndexed)
	    (*ps->CloseIndexed) (pScreen, &ps->formats[n]);
    GlyphUninit (pScreen);
    SetPictureScreen(pScreen, 0);
    free(ps->formats);
    free(ps);
    return ret;
}

void
PictureStoreColors (ColormapPtr pColormap, int ndef, xColorItem *pdef)
{
    ScreenPtr		pScreen = pColormap->pScreen;
    PictureScreenPtr    ps = GetPictureScreen(pScreen);

    pScreen->StoreColors = ps->StoreColors;
    (*pScreen->StoreColors) (pColormap, ndef, pdef);
    ps->StoreColors = pScreen->StoreColors;
    pScreen->StoreColors = PictureStoreColors;

    if (pColormap->class == PseudoColor || pColormap->class == GrayScale)
    {
	PictFormatPtr	format = ps->formats;
	int		nformats = ps->nformats;

	while (nformats--)
	{
	    if (format->type == PictTypeIndexed &&
		format->index.pColormap == pColormap)
	    {
		(*ps->UpdateIndexed) (pScreen, format, ndef, pdef);
		break;
	    }
	    format++;
	}
    }
}

static int
visualDepth (ScreenPtr pScreen, VisualPtr pVisual)
{
    int		d, v;
    DepthPtr	pDepth;

    for (d = 0; d < pScreen->numDepths; d++)
    {
	pDepth = &pScreen->allowedDepths[d];
	for (v = 0; v < pDepth->numVids; v++)
	    if (pDepth->vids[v] == pVisual->vid)
		return pDepth->depth;
    }
    return 0;
}

typedef struct _formatInit {
    CARD32  format;
    CARD8   depth;
} FormatInitRec, *FormatInitPtr;

static int
addFormat (FormatInitRec    formats[256],
	   int		    nformat,
	   CARD32	    format,
	   CARD8	    depth)
{
    int	n;

    for (n = 0; n < nformat; n++)
	if (formats[n].format == format && formats[n].depth == depth)
	    return nformat;
    formats[nformat].format = format;
    formats[nformat].depth = depth;
    return ++nformat;
}

#define Mask(n)	((n) == 32 ? 0xffffffff : ((1 << (n))-1))

PictFormatPtr
PictureCreateDefaultFormats (ScreenPtr pScreen, int *nformatp)
{
    int		    nformats, f;
    PictFormatPtr   pFormats;
    FormatInitRec   formats[1024];
    CARD32	    format;
    CARD8	    depth;
    VisualPtr	    pVisual;
    int		    v;
    int		    bpp;
    int		    type;
    int		    r, g, b;
    int		    d;
    DepthPtr	    pDepth;

    nformats = 0;
    /* formats required by protocol */
    formats[nformats].format = PICT_a1;
    formats[nformats].depth = 1;
    nformats++;
    formats[nformats].format = PICT_FORMAT(BitsPerPixel(8),
					   PICT_TYPE_A,
					   8, 0, 0, 0);
    formats[nformats].depth = 8;
    nformats++;
    formats[nformats].format = PICT_FORMAT(BitsPerPixel(4),
					   PICT_TYPE_A,
					   4, 0, 0, 0);
    formats[nformats].depth = 4;
    nformats++;
    formats[nformats].format = PICT_a8r8g8b8;
    formats[nformats].depth = 32;
    nformats++;
    formats[nformats].format = PICT_x8r8g8b8;
    formats[nformats].depth = 32;
    nformats++;
    formats[nformats].format = PICT_b8g8r8a8;
    formats[nformats].depth = 32;
    nformats++;
    formats[nformats].format = PICT_b8g8r8x8;
    formats[nformats].depth = 32;
    nformats++;

    /* now look through the depths and visuals adding other formats */
    for (v = 0; v < pScreen->numVisuals; v++)
    {
	pVisual = &pScreen->visuals[v];
	depth = visualDepth (pScreen, pVisual);
	if (!depth)
	    continue;
    	bpp = BitsPerPixel (depth);
	switch (pVisual->class) {
	case DirectColor:
	case TrueColor:
	    r = Ones (pVisual->redMask);
	    g = Ones (pVisual->greenMask);
	    b = Ones (pVisual->blueMask);
	    type = PICT_TYPE_OTHER;
	    /*
	     * Current rendering code supports only three direct formats,
	     * fields must be packed together at the bottom of the pixel
	     */
	    if (pVisual->offsetBlue == 0 &&
		pVisual->offsetGreen == b &&
		pVisual->offsetRed == b + g)
	    {
		type = PICT_TYPE_ARGB;
	    }
	    else if (pVisual->offsetRed == 0 &&
		     pVisual->offsetGreen == r && 
		     pVisual->offsetBlue == r + g)
	    {
		type = PICT_TYPE_ABGR;
	    }
	    else if (pVisual->offsetRed == pVisual->offsetGreen - r &&
		     pVisual->offsetGreen == pVisual->offsetBlue - g && 
		     pVisual->offsetBlue == bpp - b)
	    {
		type = PICT_TYPE_BGRA;
	    }
	    if (type != PICT_TYPE_OTHER)
	    {
		format = PICT_FORMAT(bpp, type, 0, r, g, b);
		nformats = addFormat (formats, nformats, format, depth);
	    }
	    break;
	case StaticColor:
	case PseudoColor:
	    format = PICT_VISFORMAT (bpp, PICT_TYPE_COLOR, v);
	    nformats = addFormat (formats, nformats, format, depth);
	    break;
	case StaticGray:
	case GrayScale:
	    format = PICT_VISFORMAT (bpp, PICT_TYPE_GRAY, v);
	    nformats = addFormat (formats, nformats, format, depth);
	    break;
	}
    }
    /*
     * Walk supported depths and add useful Direct formats
     */
    for (d = 0; d < pScreen->numDepths; d++)
    {
	pDepth = &pScreen->allowedDepths[d];
	bpp = BitsPerPixel (pDepth->depth);
	format = 0;
	switch (bpp) {
	case 16:
	    /* depth 12 formats */
	    if (pDepth->depth >= 12)
	    {
		nformats = addFormat (formats, nformats,
				      PICT_x4r4g4b4, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_x4b4g4r4, pDepth->depth);
	    }
	    /* depth 15 formats */
	    if (pDepth->depth >= 15)
	    {
		nformats = addFormat (formats, nformats,
				      PICT_x1r5g5b5, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_x1b5g5r5, pDepth->depth);
	    }
	    /* depth 16 formats */
	    if (pDepth->depth >= 16) 
	    {
		nformats = addFormat (formats, nformats,
				      PICT_a1r5g5b5, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_a1b5g5r5, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_r5g6b5, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_b5g6r5, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_a4r4g4b4, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_a4b4g4r4, pDepth->depth);
	    }
	    break;
	case 24:
	    if (pDepth->depth >= 24)
	    {
		nformats = addFormat (formats, nformats,
				      PICT_r8g8b8, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_b8g8r8, pDepth->depth);
	    }
	    break;
	case 32:
	    if (pDepth->depth >= 24)
	    {
		nformats = addFormat (formats, nformats,
				      PICT_x8r8g8b8, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_x8b8g8r8, pDepth->depth);
	    }
	    if (pDepth->depth >= 30)
	    {
		nformats = addFormat (formats, nformats,
				      PICT_a2r10g10b10, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_x2r10g10b10, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_a2b10g10r10, pDepth->depth);
		nformats = addFormat (formats, nformats,
				      PICT_x2b10g10r10, pDepth->depth);
	    }
	    break;
	}
    }
    

    pFormats = calloc(nformats, sizeof (PictFormatRec));
    if (!pFormats)
	return 0;
    for (f = 0; f < nformats; f++)
    {
        pFormats[f].id = FakeClientID (0);
	pFormats[f].depth = formats[f].depth;
	format = formats[f].format;
	pFormats[f].format = format;
	switch (PICT_FORMAT_TYPE(format)) {
	case PICT_TYPE_ARGB:
	    pFormats[f].type = PictTypeDirect;
	    
	    pFormats[f].direct.alphaMask = Mask(PICT_FORMAT_A(format));
	    if (pFormats[f].direct.alphaMask)
		pFormats[f].direct.alpha = (PICT_FORMAT_R(format) +
					    PICT_FORMAT_G(format) +
					    PICT_FORMAT_B(format));
	    
	    pFormats[f].direct.redMask = Mask(PICT_FORMAT_R(format));
	    pFormats[f].direct.red = (PICT_FORMAT_G(format) + 
				      PICT_FORMAT_B(format));
	    
	    pFormats[f].direct.greenMask = Mask(PICT_FORMAT_G(format));
	    pFormats[f].direct.green = PICT_FORMAT_B(format);
	    
	    pFormats[f].direct.blueMask = Mask(PICT_FORMAT_B(format));
	    pFormats[f].direct.blue = 0;
	    break;

	case PICT_TYPE_ABGR:
	    pFormats[f].type = PictTypeDirect;
	    
	    pFormats[f].direct.alphaMask = Mask(PICT_FORMAT_A(format));
	    if (pFormats[f].direct.alphaMask)
		pFormats[f].direct.alpha = (PICT_FORMAT_B(format) +
					    PICT_FORMAT_G(format) +
					    PICT_FORMAT_R(format));
	    
	    pFormats[f].direct.blueMask = Mask(PICT_FORMAT_B(format));
	    pFormats[f].direct.blue = (PICT_FORMAT_G(format) + 
				       PICT_FORMAT_R(format));
	    
	    pFormats[f].direct.greenMask = Mask(PICT_FORMAT_G(format));
	    pFormats[f].direct.green = PICT_FORMAT_R(format);
	    
	    pFormats[f].direct.redMask = Mask(PICT_FORMAT_R(format));
	    pFormats[f].direct.red = 0;
	    break;

	case PICT_TYPE_BGRA:
	    pFormats[f].type = PictTypeDirect;
	    
	    pFormats[f].direct.blueMask = Mask(PICT_FORMAT_B(format));
	    pFormats[f].direct.blue = (PICT_FORMAT_BPP(format) - PICT_FORMAT_B(format));

	    pFormats[f].direct.greenMask = Mask(PICT_FORMAT_G(format));
	    pFormats[f].direct.green = (PICT_FORMAT_BPP(format) - PICT_FORMAT_B(format) -
					PICT_FORMAT_G(format));

	    pFormats[f].direct.redMask = Mask(PICT_FORMAT_R(format));
	    pFormats[f].direct.red = (PICT_FORMAT_BPP(format) - PICT_FORMAT_B(format) -
				      PICT_FORMAT_G(format) - PICT_FORMAT_R(format));

	    pFormats[f].direct.alphaMask = Mask(PICT_FORMAT_A(format));
	    pFormats[f].direct.alpha = 0;
	    break;

	case PICT_TYPE_A:
	    pFormats[f].type = PictTypeDirect;

	    pFormats[f].direct.alpha = 0;
	    pFormats[f].direct.alphaMask = Mask(PICT_FORMAT_A(format));

	    /* remaining fields already set to zero */
	    break;
	    
	case PICT_TYPE_COLOR:
	case PICT_TYPE_GRAY:
	    pFormats[f].type = PictTypeIndexed;
	    pFormats[f].index.vid = pScreen->visuals[PICT_FORMAT_VIS(format)].vid;
	    break;
	}
    }
    *nformatp = nformats;
    return pFormats;
}

static VisualPtr
PictureFindVisual (ScreenPtr pScreen, VisualID visual)
{
    int         i;
    VisualPtr   pVisual;
    for (i = 0, pVisual = pScreen->visuals;
         i < pScreen->numVisuals;
         i++, pVisual++)
    {
        if (pVisual->vid == visual)
            return pVisual;
    }
    return 0;
}

Bool
PictureInitIndexedFormat(ScreenPtr pScreen, PictFormatPtr format)
{
    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);

    if (format->type != PictTypeIndexed || format->index.pColormap)
	return TRUE;

    if (format->index.vid == pScreen->rootVisual) {
	dixLookupResourceByType((pointer *)&format->index.pColormap,
				pScreen->defColormap, RT_COLORMAP,
				serverClient, DixGetAttrAccess);
    } else {
	VisualPtr pVisual = PictureFindVisual(pScreen, format->index.vid);
	if (CreateColormap(FakeClientID (0), pScreen, pVisual,
		    &format->index.pColormap, AllocNone, 0)
		!= Success)
	    return FALSE;
    }
    if (!ps->InitIndexed(pScreen, format))
	return FALSE;
    return TRUE;
}

static Bool
PictureInitIndexedFormats (ScreenPtr pScreen)
{
    PictureScreenPtr    ps = GetPictureScreenIfSet(pScreen);
    PictFormatPtr	format;
    int			nformat;

    if (!ps)
	return FALSE;
    format = ps->formats;
    nformat = ps->nformats;
    while (nformat--)
	if (!PictureInitIndexedFormat(pScreen, format++))
	    return FALSE;
    return TRUE;
}

Bool
PictureFinishInit (void)
{
    int	    s;

    for (s = 0; s < screenInfo.numScreens; s++)
    {
	if (!PictureInitIndexedFormats (screenInfo.screens[s]))
	    return FALSE;
	(void) AnimCurInit (screenInfo.screens[s]);
    }

    return TRUE;
}

Bool
PictureSetSubpixelOrder (ScreenPtr pScreen, int subpixel)
{
    PictureScreenPtr    ps = GetPictureScreenIfSet(pScreen);

    if (!ps)
	return FALSE;
    ps->subpixel = subpixel;
    return TRUE;
    
}

int
PictureGetSubpixelOrder (ScreenPtr pScreen)
{
    PictureScreenPtr    ps = GetPictureScreenIfSet(pScreen);

    if (!ps)
	return SubPixelUnknown;
    return ps->subpixel;
}
    
PictFormatPtr
PictureMatchVisual (ScreenPtr pScreen, int depth, VisualPtr pVisual)
{
    PictureScreenPtr    ps = GetPictureScreenIfSet(pScreen);
    PictFormatPtr	format;
    int			nformat;
    int			type;

    if (!ps)
	return 0;
    format = ps->formats;
    nformat = ps->nformats;
    switch (pVisual->class) {
    case StaticGray:
    case GrayScale:
    case StaticColor:
    case PseudoColor:
	type = PictTypeIndexed;
	break;
    case TrueColor:
    case DirectColor:
	type = PictTypeDirect;
	break;
    default:
	return 0;
    }
    while (nformat--)
    {
	if (format->depth == depth && format->type == type)
	{
	    if (type == PictTypeIndexed)
	    {
		if (format->index.vid == pVisual->vid)
		    return format;
	    }
	    else
	    {
		if (format->direct.redMask << format->direct.red == 
		    pVisual->redMask &&
		    format->direct.greenMask << format->direct.green == 
		    pVisual->greenMask &&
		    format->direct.blueMask << format->direct.blue == 
		    pVisual->blueMask)
		{
		    return format;
		}
	    }
	}
	format++;
    }
    return 0;
}

PictFormatPtr
PictureMatchFormat (ScreenPtr pScreen, int depth, CARD32 f)
{
    PictureScreenPtr    ps = GetPictureScreenIfSet(pScreen);
    PictFormatPtr	format;
    int			nformat;

    if (!ps)
	return 0;
    format = ps->formats;
    nformat = ps->nformats;
    while (nformat--)
    {
	if (format->depth == depth && format->format == (f & 0xffffff))
	    return format;
	format++;
    }
    return 0;
}

int
PictureParseCmapPolicy (const char *name)
{
    if ( strcmp (name, "default" ) == 0)
	return PictureCmapPolicyDefault;
    else if ( strcmp (name, "mono" ) == 0)
	return PictureCmapPolicyMono;
    else if ( strcmp (name, "gray" ) == 0)
	return PictureCmapPolicyGray;
    else if ( strcmp (name, "color" ) == 0)
	return PictureCmapPolicyColor;
    else if ( strcmp (name, "all" ) == 0)
	return PictureCmapPolicyAll;
    else
	return PictureCmapPolicyInvalid;
}

Bool
PictureInit (ScreenPtr pScreen, PictFormatPtr formats, int nformats)
{
    PictureScreenPtr	ps;
    int			n;
    CARD32		type, a, r, g, b;
    
    if (PictureGeneration != serverGeneration)
    {
	PictureType = CreateNewResourceType (FreePicture, "PICTURE");
	if (!PictureType)
	    return FALSE;
	PictFormatType = CreateNewResourceType (FreePictFormat, "PICTFORMAT");
	if (!PictFormatType)
	    return FALSE;
	GlyphSetType = CreateNewResourceType (FreeGlyphSet, "GLYPHSET");
	if (!GlyphSetType)
	    return FALSE;
	PictureGeneration = serverGeneration;
    }
    if (!dixRegisterPrivateKey(&PictureScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
	return FALSE;

    if (!dixRegisterPrivateKey(&PictureWindowPrivateKeyRec, PRIVATE_WINDOW, 0))
	return FALSE;

    if (!formats)
    {
	formats = PictureCreateDefaultFormats (pScreen, &nformats);
	if (!formats)
	    return FALSE;
    }
    for (n = 0; n < nformats; n++)
    {
	if (!AddResource (formats[n].id, PictFormatType, (pointer) (formats+n)))
	{
	    free(formats);
	    return FALSE;
	}
	if (formats[n].type == PictTypeIndexed)
	{
            VisualPtr   pVisual = PictureFindVisual (pScreen, formats[n].index.vid);
	    if ((pVisual->class | DynamicClass) == PseudoColor)
		type = PICT_TYPE_COLOR;
	    else
		type = PICT_TYPE_GRAY;
	    a = r = g = b = 0;
	}
	else
	{
	    if ((formats[n].direct.redMask|
		 formats[n].direct.blueMask|
		 formats[n].direct.greenMask) == 0)
		type = PICT_TYPE_A;
	    else if (formats[n].direct.red > formats[n].direct.blue)
		type = PICT_TYPE_ARGB;
	    else if (formats[n].direct.red == 0)
		type = PICT_TYPE_ABGR;
	    else
		type = PICT_TYPE_BGRA;
	    a = Ones (formats[n].direct.alphaMask);
	    r = Ones (formats[n].direct.redMask);
	    g = Ones (formats[n].direct.greenMask);
	    b = Ones (formats[n].direct.blueMask);
	}
	formats[n].format = PICT_FORMAT(0,type,a,r,g,b);
    }
    ps = (PictureScreenPtr) malloc(sizeof (PictureScreenRec));
    if (!ps)
    {
	free(formats);
	return FALSE;
    }
    SetPictureScreen(pScreen, ps);

    ps->formats = formats;
    ps->fallback = formats;
    ps->nformats = nformats;
    
    ps->filters = 0;
    ps->nfilters = 0;
    ps->filterAliases = 0;
    ps->nfilterAliases = 0;

    ps->subpixel = SubPixelUnknown;

    ps->CloseScreen = pScreen->CloseScreen;
    ps->DestroyWindow = pScreen->DestroyWindow;
    ps->StoreColors = pScreen->StoreColors;
    pScreen->DestroyWindow = PictureDestroyWindow;
    pScreen->CloseScreen = PictureCloseScreen;
    pScreen->StoreColors = PictureStoreColors;

    if (!PictureSetDefaultFilters (pScreen))
    {
	PictureResetFilters (pScreen);
	SetPictureScreen(pScreen, 0);
	free(formats);
	free(ps);
	return FALSE;
    }

    return TRUE;
}

void
SetPictureToDefaults (PicturePtr    pPicture)
{
    pPicture->refcnt = 1;
    pPicture->repeat = 0;
    pPicture->graphicsExposures = FALSE;
    pPicture->subWindowMode = ClipByChildren;
    pPicture->polyEdge = PolyEdgeSharp;
    pPicture->polyMode = PolyModePrecise;
    pPicture->freeCompClip = FALSE;
    pPicture->clientClipType = CT_NONE;
    pPicture->componentAlpha = FALSE;
    pPicture->repeatType = RepeatNone;

    pPicture->alphaMap = 0;
    pPicture->alphaOrigin.x = 0;
    pPicture->alphaOrigin.y = 0;

    pPicture->clipOrigin.x = 0;
    pPicture->clipOrigin.y = 0;
    pPicture->clientClip = 0;

    pPicture->transform = 0;

    pPicture->filter = PictureGetFilterId (FilterNearest, -1, TRUE);
    pPicture->filter_params = 0;
    pPicture->filter_nparams = 0;

    pPicture->serialNumber = GC_CHANGE_SERIAL_BIT;
    pPicture->stateChanges = -1;
    pPicture->pSourcePict = 0;
}

PicturePtr
CreatePicture (Picture		pid,
	       DrawablePtr	pDrawable,
	       PictFormatPtr	pFormat,
	       Mask		vmask,
	       XID		*vlist,
	       ClientPtr	client,
	       int		*error)
{
    PicturePtr		pPicture;
    PictureScreenPtr	ps = GetPictureScreen(pDrawable->pScreen);

    pPicture = dixAllocateObjectWithPrivates(PictureRec, PRIVATE_PICTURE);
    if (!pPicture)
    {
	*error = BadAlloc;
	return 0;
    }

    pPicture->id = pid;
    pPicture->pDrawable = pDrawable;
    pPicture->pFormat = pFormat;
    pPicture->format = pFormat->format | (pDrawable->bitsPerPixel << 24);

    /* security creation/labeling check */
    *error = XaceHook(XACE_RESOURCE_ACCESS, client, pid, PictureType, pPicture,
		      RT_PIXMAP, pDrawable, DixCreateAccess|DixSetAttrAccess);
    if (*error != Success)
	goto out;

    if (pDrawable->type == DRAWABLE_PIXMAP)
    {
	++((PixmapPtr)pDrawable)->refcnt;
	pPicture->pNext = 0;
    }
    else
    {
	pPicture->pNext = GetPictureWindow(((WindowPtr) pDrawable));
	SetPictureWindow(((WindowPtr) pDrawable), pPicture);
    }

    SetPictureToDefaults (pPicture);
    
    if (vmask)
	*error = ChangePicture (pPicture, vmask, vlist, 0, client);
    else
	*error = Success;
    if (*error == Success)
	*error = (*ps->CreatePicture) (pPicture);
out:
    if (*error != Success)
    {
	FreePicture (pPicture, (XID) 0);
	pPicture = 0;
    }
    return pPicture;
}

static CARD32 xRenderColorToCard32(xRenderColor c)
{
    return
        (c.alpha >> 8 << 24) |
        (c.red >> 8 << 16) |
        (c.green & 0xff00) |
        (c.blue >> 8);
}

static unsigned int premultiply(unsigned int x)
{
    unsigned int a = x >> 24;
    unsigned int t = (x & 0xff00ff) * a + 0x800080;
    t = (t + ((t >> 8) & 0xff00ff)) >> 8;
    t &= 0xff00ff;

    x = ((x >> 8) & 0xff) * a + 0x80;
    x = (x + ((x >> 8) & 0xff));
    x &= 0xff00;
    x |= t | (a << 24);
    return x;
}

static unsigned int INTERPOLATE_PIXEL_256(unsigned int x, unsigned int a,
                                          unsigned int y, unsigned int b)
{
    CARD32 t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
    t >>= 8;
    t &= 0xff00ff;

    x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
    x &= 0xff00ff00;
    x |= t;
    return x;
}

CARD32
PictureGradientColor (PictGradientStopPtr stop1,
		      PictGradientStopPtr stop2,
		      CARD32	          x)
{
     CARD32 current_color, next_color;
     int	   dist, idist;

     current_color = xRenderColorToCard32 (stop1->color);
     next_color    = xRenderColorToCard32 (stop2->color);

     dist  = (int) (256 * (x - stop1->x) / (stop2->x - stop1->x));
     idist = 256 - dist;

     return premultiply (INTERPOLATE_PIXEL_256 (current_color, idist,
					       next_color, dist));
}

static void initGradient(SourcePictPtr pGradient, int stopCount,
                         xFixed *stopPoints, xRenderColor *stopColors, int *error)
{
    int i;
    xFixed dpos;

    if (stopCount <= 0) {
        *error = BadValue;
        return;
    }

    dpos = -1;
    for (i = 0; i < stopCount; ++i) {
        if (stopPoints[i] < dpos || stopPoints[i] > (1<<16)) {
            *error = BadValue;
            return;
        }
        dpos = stopPoints[i];
    }

    pGradient->gradient.stops = malloc(stopCount*sizeof(PictGradientStop));
    if (!pGradient->gradient.stops) {
        *error = BadAlloc;
        return;
    }

    pGradient->gradient.nstops = stopCount;

    for (i = 0; i < stopCount; ++i) {
        pGradient->gradient.stops[i].x = stopPoints[i];
        pGradient->gradient.stops[i].color = stopColors[i];
    }

    pGradient->gradient.class	       = SourcePictClassUnknown;
    pGradient->gradient.stopRange      = 0xffff;
    pGradient->gradient.colorTable     = NULL;
    pGradient->gradient.colorTableSize = 0;
}

static PicturePtr createSourcePicture(void)
{
    PicturePtr pPicture;
    pPicture = dixAllocateObjectWithPrivates(PictureRec, PRIVATE_PICTURE);
    pPicture->pDrawable = 0;
    pPicture->pFormat = 0;
    pPicture->pNext = 0;
    pPicture->format = PICT_a8r8g8b8;

    SetPictureToDefaults(pPicture);
    return pPicture;
}

PicturePtr
CreateSolidPicture (Picture pid, xRenderColor *color, int *error)
{
    PicturePtr pPicture;
    pPicture = createSourcePicture();
    if (!pPicture) {
        *error = BadAlloc;
        return 0;
    }

    pPicture->id = pid;
    pPicture->pSourcePict = (SourcePictPtr) malloc(sizeof(PictSolidFill));
    if (!pPicture->pSourcePict) {
        *error = BadAlloc;
        free(pPicture);
        return 0;
    }
    pPicture->pSourcePict->type = SourcePictTypeSolidFill;
    pPicture->pSourcePict->solidFill.color = xRenderColorToCard32(*color);
    return pPicture;
}

PicturePtr
CreateLinearGradientPicture (Picture pid, xPointFixed *p1, xPointFixed *p2,
                             int nStops, xFixed *stops, xRenderColor *colors, int *error)
{
    PicturePtr pPicture;

    if (nStops < 2) {
        *error = BadValue;
        return 0;
    }

    pPicture = createSourcePicture();
    if (!pPicture) {
        *error = BadAlloc;
        return 0;
    }

    pPicture->id = pid;
    pPicture->pSourcePict = (SourcePictPtr) malloc(sizeof(PictLinearGradient));
    if (!pPicture->pSourcePict) {
        *error = BadAlloc;
        free(pPicture);
        return 0;
    }

    pPicture->pSourcePict->linear.type = SourcePictTypeLinear;
    pPicture->pSourcePict->linear.p1 = *p1;
    pPicture->pSourcePict->linear.p2 = *p2;

    initGradient(pPicture->pSourcePict, nStops, stops, colors, error);
    if (*error) {
        free(pPicture);
        return 0;
    }
    return pPicture;
}

#define FixedToDouble(x) ((x)/65536.)

PicturePtr
CreateRadialGradientPicture (Picture pid, xPointFixed *inner, xPointFixed *outer,
                             xFixed innerRadius, xFixed outerRadius,
                             int nStops, xFixed *stops, xRenderColor *colors, int *error)
{
    PicturePtr pPicture;
    PictRadialGradient *radial;

    if (nStops < 2) {
        *error = BadValue;
        return 0;
    }

    pPicture = createSourcePicture();
    if (!pPicture) {
        *error = BadAlloc;
        return 0;
    }

    pPicture->id = pid;
    pPicture->pSourcePict = (SourcePictPtr) malloc(sizeof(PictRadialGradient));
    if (!pPicture->pSourcePict) {
        *error = BadAlloc;
        free(pPicture);
        return 0;
    }
    radial = &pPicture->pSourcePict->radial;

    radial->type = SourcePictTypeRadial;
    radial->c1.x = inner->x;
    radial->c1.y = inner->y;
    radial->c1.radius = innerRadius;
    radial->c2.x = outer->x;
    radial->c2.y = outer->y;
    radial->c2.radius = outerRadius;
    radial->cdx = (radial->c2.x - radial->c1.x) / 65536.;
    radial->cdy = (radial->c2.y - radial->c1.y) / 65536.;
    radial->dr = (radial->c2.radius - radial->c1.radius) / 65536.;
    radial->A = (  radial->cdx * radial->cdx
		   + radial->cdy * radial->cdy
		   - radial->dr  * radial->dr);
    
    initGradient(pPicture->pSourcePict, nStops, stops, colors, error);
    if (*error) {
        free(pPicture);
        return 0;
    }
    return pPicture;
}

PicturePtr
CreateConicalGradientPicture (Picture pid, xPointFixed *center, xFixed angle,
                              int nStops, xFixed *stops, xRenderColor *colors, int *error)
{
    PicturePtr pPicture;

    if (nStops < 2) {
        *error = BadValue;
        return 0;
    }

    pPicture = createSourcePicture();
    if (!pPicture) {
        *error = BadAlloc;
        return 0;
    }

    pPicture->id = pid;
    pPicture->pSourcePict = (SourcePictPtr) malloc(sizeof(PictConicalGradient));
    if (!pPicture->pSourcePict) {
        *error = BadAlloc;
        free(pPicture);
        return 0;
    }

    pPicture->pSourcePict->conical.type = SourcePictTypeConical;
    pPicture->pSourcePict->conical.center = *center;
    pPicture->pSourcePict->conical.angle = angle;

    initGradient(pPicture->pSourcePict, nStops, stops, colors, error);
    if (*error) {
        free(pPicture);
        return 0;
    }
    return pPicture;
}

#define NEXT_VAL(_type) (vlist ? (_type) *vlist++ : (_type) ulist++->val)

#define NEXT_PTR(_type) ((_type) ulist++->ptr)

int
ChangePicture (PicturePtr	pPicture,
	       Mask		vmask,
	       XID		*vlist,
	       DevUnion		*ulist,
	       ClientPtr	client)
{
    ScreenPtr pScreen = pPicture->pDrawable ? pPicture->pDrawable->pScreen : 0;
    PictureScreenPtr ps = pScreen ? GetPictureScreen(pScreen) : 0;
    BITS32		index2;
    int			error = 0;
    BITS32		maskQ;
    
    pPicture->serialNumber |= GC_CHANGE_SERIAL_BIT;
    maskQ = vmask;
    while (vmask && !error)
    {
	index2 = (BITS32) lowbit (vmask);
	vmask &= ~index2;
	pPicture->stateChanges |= index2;
	switch (index2)
	{
	case CPRepeat:
	    {
		unsigned int	newr;
		newr = NEXT_VAL(unsigned int);
		if (newr <= RepeatReflect)
		{
		    pPicture->repeat = (newr != RepeatNone);
		    pPicture->repeatType = newr;
		}
		else
		{
		    client->errorValue = newr;
		    error = BadValue;
		}
	    }
	    break;
	case CPAlphaMap:
	    {
		PicturePtr  pAlpha;
		
		if (vlist)
		{
		    Picture	pid = NEXT_VAL(Picture);

		    if (pid == None)
			pAlpha = 0;
		    else
		    {
			error = dixLookupResourceByType((pointer *)&pAlpha, pid,
						  PictureType, client,
						  DixReadAccess);
			if (error != Success)
			{
			    client->errorValue = pid;
			    break;
			}
			if (pAlpha->pDrawable == NULL ||
			    pAlpha->pDrawable->type != DRAWABLE_PIXMAP)
			{
			    client->errorValue = pid;
			    error = BadMatch;
			    break;
			}
		    }
		}
		else
		    pAlpha = NEXT_PTR(PicturePtr);
		if (!error)
		{
		    if (pAlpha && pAlpha->pDrawable->type == DRAWABLE_PIXMAP)
			pAlpha->refcnt++;
		    if (pPicture->alphaMap)
			FreePicture ((pointer) pPicture->alphaMap, (XID) 0);
		    pPicture->alphaMap = pAlpha;
		}
	    }
	    break;
	case CPAlphaXOrigin:
	    pPicture->alphaOrigin.x = NEXT_VAL(INT16);
	    break;
	case CPAlphaYOrigin:
	    pPicture->alphaOrigin.y = NEXT_VAL(INT16);
	    break;
	case CPClipXOrigin:
	    pPicture->clipOrigin.x = NEXT_VAL(INT16);
	    break;
	case CPClipYOrigin:
	    pPicture->clipOrigin.y = NEXT_VAL(INT16);
	    break;
	case CPClipMask:
	    {
		Pixmap	    pid;
		PixmapPtr   pPixmap;
		int	    clipType;
                if (!pScreen)
                    return BadDrawable;

		if (vlist)
		{
		    pid = NEXT_VAL(Pixmap);
		    if (pid == None)
		    {
			clipType = CT_NONE;
			pPixmap = NullPixmap;
		    }
		    else
		    {
			clipType = CT_PIXMAP;
			error = dixLookupResourceByType((pointer *)&pPixmap, pid,
						  RT_PIXMAP, client,
						  DixReadAccess);
			if (error != Success)
			{
			    client->errorValue = pid;
			    break;
			}
		    }
		}
		else
		{
		    pPixmap = NEXT_PTR(PixmapPtr);
		    if (pPixmap)
			clipType = CT_PIXMAP;
		    else
			clipType = CT_NONE;
		}

		if (pPixmap)
		{
		    if ((pPixmap->drawable.depth != 1) ||
			(pPixmap->drawable.pScreen != pScreen))
		    {
			error = BadMatch;
			break;
		    }
		    else
		    {
			clipType = CT_PIXMAP;
			pPixmap->refcnt++;
		    }
		}
		error = (*ps->ChangePictureClip)(pPicture, clipType,
						 (pointer)pPixmap, 0);
		break;
	    }
	case CPGraphicsExposure:
	    {
		unsigned int	newe;
		newe = NEXT_VAL(unsigned int);
		if (newe <= xTrue)
		    pPicture->graphicsExposures = newe;
		else
		{
		    client->errorValue = newe;
		    error = BadValue;
		}
	    }
	    break;
	case CPSubwindowMode:
	    {
		unsigned int	news;
		news = NEXT_VAL(unsigned int);
		if (news == ClipByChildren || news == IncludeInferiors)
		    pPicture->subWindowMode = news;
		else
		{
		    client->errorValue = news;
		    error = BadValue;
		}
	    }
	    break;
	case CPPolyEdge:
	    {
		unsigned int	newe;
		newe = NEXT_VAL(unsigned int);
		if (newe == PolyEdgeSharp || newe == PolyEdgeSmooth)
		    pPicture->polyEdge = newe;
		else
		{
		    client->errorValue = newe;
		    error = BadValue;
		}
	    }
	    break;
	case CPPolyMode:
	    {
		unsigned int	newm;
		newm = NEXT_VAL(unsigned int);
		if (newm == PolyModePrecise || newm == PolyModeImprecise)
		    pPicture->polyMode = newm;
		else
		{
		    client->errorValue = newm;
		    error = BadValue;
		}
	    }
	    break;
	case CPDither:
	    (void) NEXT_VAL(Atom); /* unimplemented */
	    break;
	case CPComponentAlpha:
	    {
		unsigned int	newca;

		newca = NEXT_VAL (unsigned int);
		if (newca <= xTrue)
		    pPicture->componentAlpha = newca;
		else
		{
		    client->errorValue = newca;
		    error = BadValue;
		}
	    }
	    break;
	default:
	    client->errorValue = maskQ;
	    error = BadValue;
	    break;
	}
    }
    if (ps)
        (*ps->ChangePicture) (pPicture, maskQ);
    return error;
}

int
SetPictureClipRects (PicturePtr	pPicture,
		     int	xOrigin,
		     int	yOrigin,
		     int	nRect,
		     xRectangle	*rects)
{
    ScreenPtr		pScreen = pPicture->pDrawable->pScreen;
    PictureScreenPtr	ps = GetPictureScreen(pScreen);
    RegionPtr		clientClip;
    int			result;

    clientClip = RegionFromRects(nRect, rects, CT_UNSORTED);
    if (!clientClip)
	return BadAlloc;
    result =(*ps->ChangePictureClip) (pPicture, CT_REGION, 
				      (pointer) clientClip, 0);
    if (result == Success)
    {
	pPicture->clipOrigin.x = xOrigin;
	pPicture->clipOrigin.y = yOrigin;
	pPicture->stateChanges |= CPClipXOrigin|CPClipYOrigin|CPClipMask;
	pPicture->serialNumber |= GC_CHANGE_SERIAL_BIT;
    }
    return result;
}

int
SetPictureClipRegion (PicturePtr    pPicture,
                      int           xOrigin,
                      int           yOrigin,
                      RegionPtr     pRegion)
{
    ScreenPtr           pScreen = pPicture->pDrawable->pScreen;
    PictureScreenPtr    ps = GetPictureScreen(pScreen);
    RegionPtr           clientClip;
    int                 result;
    int                 type;

    if (pRegion)
    {
        type = CT_REGION;
        clientClip = RegionCreate(RegionExtents(pRegion),
                                  RegionNumRects(pRegion));
        if (!clientClip)
            return BadAlloc;
        if (!RegionCopy(clientClip, pRegion))
        {
            RegionDestroy(clientClip);
            return BadAlloc;
        }
    }
    else
    {
        type = CT_NONE;
        clientClip = 0;
    }

    result =(*ps->ChangePictureClip) (pPicture, type,
                                      (pointer) clientClip, 0);
    if (result == Success)
    {
        pPicture->clipOrigin.x = xOrigin;
        pPicture->clipOrigin.y = yOrigin;
        pPicture->stateChanges |= CPClipXOrigin|CPClipYOrigin|CPClipMask;
        pPicture->serialNumber |= GC_CHANGE_SERIAL_BIT;
    }
    return result;
}

static Bool
transformIsIdentity(PictTransform *t)
{
    return ((t->matrix[0][0] == t->matrix[1][1]) &&
            (t->matrix[0][0] == t->matrix[2][2]) &&
            (t->matrix[0][0] != 0) &&
            (t->matrix[0][1] == 0) &&
            (t->matrix[0][2] == 0) &&
            (t->matrix[1][0] == 0) &&
            (t->matrix[1][2] == 0) &&
            (t->matrix[2][0] == 0) &&
            (t->matrix[2][1] == 0));
}

int
SetPictureTransform (PicturePtr	    pPicture,
		     PictTransform  *transform)
{
    if (transform && transformIsIdentity (transform))
	transform = 0;
    
    if (transform)
    {
	if (!pPicture->transform)
	{
	    pPicture->transform = (PictTransform *) malloc(sizeof (PictTransform));
	    if (!pPicture->transform)
		return BadAlloc;
	}
	*pPicture->transform = *transform;
    }
    else
    {
	free(pPicture->transform);
	pPicture->transform = NULL;
    }
    pPicture->serialNumber |= GC_CHANGE_SERIAL_BIT;

    if (pPicture->pDrawable != NULL) {
	int result;
	PictureScreenPtr ps = GetPictureScreen(pPicture->pDrawable->pScreen);

	result = (*ps->ChangePictureTransform) (pPicture, transform);

	return result;
    }

    return Success;
}

void
CopyPicture (PicturePtr	pSrc,
	     Mask	mask,
	     PicturePtr	pDst)
{
    PictureScreenPtr ps = GetPictureScreen(pSrc->pDrawable->pScreen);
    Mask origMask = mask;

    pDst->serialNumber |= GC_CHANGE_SERIAL_BIT;
    pDst->stateChanges |= mask;

    while (mask) {
	Mask bit = lowbit(mask);

	switch (bit)
	{
	case CPRepeat:
	    pDst->repeat = pSrc->repeat;
	    pDst->repeatType = pSrc->repeatType;
	    break;
	case CPAlphaMap:
	    if (pSrc->alphaMap && pSrc->alphaMap->pDrawable->type == DRAWABLE_PIXMAP)
		pSrc->alphaMap->refcnt++;
	    if (pDst->alphaMap)
		FreePicture ((pointer) pDst->alphaMap, (XID) 0);
	    pDst->alphaMap = pSrc->alphaMap;
	    break;
	case CPAlphaXOrigin:
	    pDst->alphaOrigin.x = pSrc->alphaOrigin.x;
	    break;
	case CPAlphaYOrigin:
	    pDst->alphaOrigin.y = pSrc->alphaOrigin.y;
	    break;
	case CPClipXOrigin:
	    pDst->clipOrigin.x = pSrc->clipOrigin.x;
	    break;
	case CPClipYOrigin:
	    pDst->clipOrigin.y = pSrc->clipOrigin.y;
	    break;
	case CPClipMask:
	    switch (pSrc->clientClipType) {
	    case CT_NONE:
		(*ps->ChangePictureClip)(pDst, CT_NONE, NULL, 0);
		break;
	    case CT_REGION:
		if (!pSrc->clientClip) {
		    (*ps->ChangePictureClip)(pDst, CT_NONE, NULL, 0);
		} else {
		    RegionPtr clientClip;
		    RegionPtr srcClientClip = (RegionPtr)pSrc->clientClip;

		    clientClip = RegionCreate(
			RegionExtents(srcClientClip),
			RegionNumRects(srcClientClip));
		    (*ps->ChangePictureClip)(pDst, CT_REGION, clientClip, 0);
		}
		break;
	    default:
		/* XXX: CT_PIXMAP unimplemented */
		break;
	    }
	    break;
	case CPGraphicsExposure:
	    pDst->graphicsExposures = pSrc->graphicsExposures;
	    break;
	case CPPolyEdge:
	    pDst->polyEdge = pSrc->polyEdge;
	    break;
	case CPPolyMode:
	    pDst->polyMode = pSrc->polyMode;
	    break;
	case CPDither:
	    break;
	case CPComponentAlpha:
	    pDst->componentAlpha = pSrc->componentAlpha;
	    break;
	}
	mask &= ~bit;
    }

    (*ps->ChangePicture)(pDst, origMask);
}

static void
ValidateOnePicture (PicturePtr pPicture)
{
    if (pPicture->pDrawable && pPicture->serialNumber != pPicture->pDrawable->serialNumber)
    {
	PictureScreenPtr    ps = GetPictureScreen(pPicture->pDrawable->pScreen);

	(*ps->ValidatePicture) (pPicture, pPicture->stateChanges);
	pPicture->stateChanges = 0;
	pPicture->serialNumber = pPicture->pDrawable->serialNumber;
    }
}

void
ValidatePicture(PicturePtr pPicture)
{
    ValidateOnePicture (pPicture);
    if (pPicture->alphaMap)
	ValidateOnePicture (pPicture->alphaMap);
}

int
FreePicture (pointer	value,
	     XID	pid)
{
    PicturePtr	pPicture = (PicturePtr) value;

    if (--pPicture->refcnt == 0)
    {
	free(pPicture->transform);

	if (pPicture->pSourcePict)
	{
	    if (pPicture->pSourcePict->type != SourcePictTypeSolidFill)
		free(pPicture->pSourcePict->linear.stops);

	    free(pPicture->pSourcePict);
	}

	if (pPicture->pDrawable)
	{
            ScreenPtr	    pScreen = pPicture->pDrawable->pScreen;
            PictureScreenPtr    ps = GetPictureScreen(pScreen);
	
            if (pPicture->alphaMap)
                FreePicture ((pointer) pPicture->alphaMap, (XID) 0);
            (*ps->DestroyPicture) (pPicture);
            (*ps->DestroyPictureClip) (pPicture);
            if (pPicture->pDrawable->type == DRAWABLE_WINDOW)
            {
                WindowPtr	pWindow = (WindowPtr) pPicture->pDrawable;
                PicturePtr	*pPrev;

                for (pPrev = (PicturePtr *)dixLookupPrivateAddr
			 (&pWindow->devPrivates, PictureWindowPrivateKey);
                     *pPrev;
                     pPrev = &(*pPrev)->pNext)
                {
                    if (*pPrev == pPicture)
                    {
                        *pPrev = pPicture->pNext;
                        break;
                    }
                }
            }
            else if (pPicture->pDrawable->type == DRAWABLE_PIXMAP)
            {
                (*pScreen->DestroyPixmap) ((PixmapPtr)pPicture->pDrawable);
            }
        }
	dixFreeObjectWithPrivates(pPicture, PRIVATE_PICTURE);
    }
    return Success;
}

int
FreePictFormat (pointer	pPictFormat,
		XID     pid)
{
    return Success;
}

/**
 * ReduceCompositeOp is used to choose simpler ops for cases where alpha
 * channels are always one and so math on the alpha channel per pixel becomes
 * unnecessary.  It may also avoid destination reads sometimes if apps aren't
 * being careful to avoid these cases.
 */
static CARD8
ReduceCompositeOp (CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst,
		   INT16 xSrc, INT16 ySrc, CARD16 width, CARD16 height)
{
    Bool no_src_alpha, no_dst_alpha;

    /* Sampling off the edge of a RepeatNone picture introduces alpha
     * even if the picture itself doesn't have alpha. We don't try to
     * detect every case where we don't sample off the edge, just the
     * simplest case where there is no transform on the source
     * picture.
     */
    no_src_alpha = PICT_FORMAT_COLOR(pSrc->format) &&
                   PICT_FORMAT_A(pSrc->format) == 0 &&
                   (pSrc->repeatType != RepeatNone ||
		    (!pSrc->transform &&
		     xSrc >= 0 && ySrc >= 0 &&
		     xSrc + width <= pSrc->pDrawable->width &&
		     ySrc + height <= pSrc->pDrawable->height)) &&
                   pSrc->alphaMap == NULL &&
                   pMask == NULL;
    no_dst_alpha = PICT_FORMAT_COLOR(pDst->format) &&
                   PICT_FORMAT_A(pDst->format) == 0 &&
                   pDst->alphaMap == NULL;

    /* TODO, maybe: Conjoint and Disjoint op reductions? */
 
    /* Deal with simplifications where the source alpha is always 1. */
    if (no_src_alpha)
    {
	switch (op) {
	case PictOpOver:
	    op = PictOpSrc;
	    break;
	case PictOpInReverse:
	    op = PictOpDst;
	    break;
	case PictOpOutReverse:
	    op = PictOpClear;
	    break;
	case PictOpAtop:
	    op = PictOpIn;
	    break;
	case PictOpAtopReverse:
	    op = PictOpOverReverse;
	    break;
	case PictOpXor:
	    op = PictOpOut;
	    break;
	default:
	    break;
	}
    }

    /* Deal with simplifications when the destination alpha is always 1 */
    if (no_dst_alpha)
    {
	switch (op) {
	case PictOpOverReverse:
	    op = PictOpDst;
	    break;
	case PictOpIn:
	    op = PictOpSrc;
	    break;
	case PictOpOut:
	    op = PictOpClear;
	    break;
	case PictOpAtop:
	    op = PictOpOver;
	    break;
	case PictOpXor:
	    op = PictOpOutReverse;
	    break;
	default:
	    break;
	}
    }

    /* Reduce some con/disjoint ops to the basic names. */
    switch (op) {
    case PictOpDisjointClear:
    case PictOpConjointClear:
	op = PictOpClear;
	break;
    case PictOpDisjointSrc:
    case PictOpConjointSrc:
	op = PictOpSrc;
	break;
    case PictOpDisjointDst:
    case PictOpConjointDst:
	op = PictOpDst;
	break;
    default:
	break;
    }

    return op;
}

void
CompositePicture (CARD8		op,
		  PicturePtr	pSrc,
		  PicturePtr	pMask,
		  PicturePtr	pDst,
		  INT16		xSrc,
		  INT16		ySrc,
		  INT16		xMask,
		  INT16		yMask,
		  INT16		xDst,
		  INT16		yDst,
		  CARD16	width,
		  CARD16	height)
{
    PictureScreenPtr	ps = GetPictureScreen(pDst->pDrawable->pScreen);
    
    ValidatePicture (pSrc);
    if (pMask)
	ValidatePicture (pMask);
    ValidatePicture (pDst);

    op = ReduceCompositeOp (op, pSrc, pMask, pDst, xSrc, ySrc, width, height);
    if (op == PictOpDst)
	return;

    (*ps->Composite) (op,
		       pSrc,
		       pMask,
		       pDst,
		       xSrc,
		       ySrc,
		       xMask,
		       yMask,
		       xDst,
		       yDst,
		       width,
		       height);
}

void
CompositeRects (CARD8		op,
		PicturePtr	pDst,
		xRenderColor	*color,
		int		nRect,
		xRectangle      *rects)
{
    PictureScreenPtr	ps = GetPictureScreen(pDst->pDrawable->pScreen);
    
    ValidatePicture (pDst);
    (*ps->CompositeRects) (op, pDst, color, nRect, rects);
}

void
CompositeTrapezoids (CARD8	    op,
		     PicturePtr	    pSrc,
		     PicturePtr	    pDst,
		     PictFormatPtr  maskFormat,
		     INT16	    xSrc,
		     INT16	    ySrc,
		     int	    ntrap,
		     xTrapezoid	    *traps)
{
    PictureScreenPtr	ps = GetPictureScreen(pDst->pDrawable->pScreen);
    
    ValidatePicture (pSrc);
    ValidatePicture (pDst);
    (*ps->Trapezoids) (op, pSrc, pDst, maskFormat, xSrc, ySrc, ntrap, traps);
}

void
CompositeTriangles (CARD8	    op,
		    PicturePtr	    pSrc,
		    PicturePtr	    pDst,
		    PictFormatPtr   maskFormat,
		    INT16	    xSrc,
		    INT16	    ySrc,
		    int		    ntriangles,
		    xTriangle	    *triangles)
{
    PictureScreenPtr	ps = GetPictureScreen(pDst->pDrawable->pScreen);
    
    ValidatePicture (pSrc);
    ValidatePicture (pDst);
    (*ps->Triangles) (op, pSrc, pDst, maskFormat, xSrc, ySrc, ntriangles, triangles);
}

void
CompositeTriStrip (CARD8	    op,
		   PicturePtr	    pSrc,
		   PicturePtr	    pDst,
		   PictFormatPtr    maskFormat,
		   INT16	    xSrc,
		   INT16	    ySrc,
		   int		    npoints,
		   xPointFixed	    *points)
{
    PictureScreenPtr	ps = GetPictureScreen(pDst->pDrawable->pScreen);
    
    ValidatePicture (pSrc);
    ValidatePicture (pDst);
    (*ps->TriStrip) (op, pSrc, pDst, maskFormat, xSrc, ySrc, npoints, points);
}

void
CompositeTriFan (CARD8		op,
		 PicturePtr	pSrc,
		 PicturePtr	pDst,
		 PictFormatPtr	maskFormat,
		 INT16		xSrc,
		 INT16		ySrc,
		 int		npoints,
		 xPointFixed	*points)
{
    PictureScreenPtr	ps = GetPictureScreen(pDst->pDrawable->pScreen);
    
    ValidatePicture (pSrc);
    ValidatePicture (pDst);
    (*ps->TriFan) (op, pSrc, pDst, maskFormat, xSrc, ySrc, npoints, points);
}

void
AddTraps (PicturePtr	pPicture,
	  INT16		xOff,
	  INT16		yOff,
	  int		ntrap,
	  xTrap		*traps)
{
    PictureScreenPtr	ps = GetPictureScreen(pPicture->pDrawable->pScreen);
    
    ValidatePicture (pPicture);
    (*ps->AddTraps) (pPicture, xOff, yOff, ntrap, traps);
}

