/*
 * Copyright © 2004 Eric Anholt
 *
 * 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 Eric Anholt not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Eric Anholt makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL ERIC ANHOLT 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.
 */
/* $Header: /cvs/xorg/xc/programs/Xserver/miext/cw/cw_render.c,v 1.14 2005/07/03 07:02:01 daniels Exp $ */

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

#include <string.h>

#include "gcstruct.h"
#include "windowstr.h"
#include "cw.h"

#ifdef RENDER

#define cwPsDecl(pScreen)	\
    PictureScreenPtr	ps = GetPictureScreen (pScreen);	\
    cwScreenPtr		pCwScreen = getCwScreen (pScreen)

#define cwPicturePrivate					\
    cwPicturePtr    pPicturePrivate = getCwPicture(pPicture)

#define cwSrcPictureDecl							\
    int		    src_picture_x_off, src_picture_y_off;			\
    PicturePtr	    pBackingSrcPicture = cwGetBackingPicture(pSrcPicture,	\
							     &src_picture_x_off,\
							     &src_picture_y_off)

#define cwDstPictureDecl							\
    int		    dst_picture_x_off, dst_picture_y_off;			\
    PicturePtr	    pBackingDstPicture = cwGetBackingPicture(pDstPicture,	\
							     &dst_picture_x_off,\
							     &dst_picture_y_off)

#define cwMskPictureDecl							\
    int		    msk_picture_x_off = 0, msk_picture_y_off = 0;		\
    PicturePtr	    pBackingMskPicture = (!pMskPicture ? 0 :	    		\
					  cwGetBackingPicture(pMskPicture,	\
							      &msk_picture_x_off,\
							      &msk_picture_y_off))

#define cwPsUnwrap(elt) {	\
    ps->elt = pCwScreen->elt;	\
}

#define cwPsWrap(elt,func) {	\
    pCwScreen->elt = ps->elt;	\
    ps->elt = func;		\
}

static cwPicturePtr
cwCreatePicturePrivate (PicturePtr pPicture)
{
    WindowPtr	    pWindow = (WindowPtr) pPicture->pDrawable;
    PixmapPtr	    pPixmap = getCwPixmap (pWindow);
    int		    error;
    cwPicturePtr    pPicturePrivate;

    pPicturePrivate = malloc (sizeof (cwPictureRec));
    if (!pPicturePrivate)
	return NULL;
    
    pPicturePrivate->pBackingPicture = CreatePicture (0, &pPixmap->drawable, 
						      pPicture->pFormat,
						      0, 0, serverClient,
						      &error);
    if (!pPicturePrivate->pBackingPicture)
    {
	free (pPicturePrivate);
	return NULL;
    }

    /*
     * Ensure that this serial number does not match the window's
     */
    pPicturePrivate->serialNumber = pPixmap->drawable.serialNumber;
    pPicturePrivate->stateChanges = (1 << (CPLastBit + 1)) - 1;
    
    setCwPicture(pPicture, pPicturePrivate);

    return pPicturePrivate;
}

static void
cwDestroyPicturePrivate (PicturePtr pPicture)
{
    cwPicturePrivate;

    if (pPicturePrivate)
    {
	if (pPicturePrivate->pBackingPicture)
	    FreePicture (pPicturePrivate->pBackingPicture, 0);
	free (pPicturePrivate);
	setCwPicture(pPicture, NULL);
    }
}

static PicturePtr
cwGetBackingPicture (PicturePtr pPicture, int *x_off, int *y_off)
{
    cwPicturePrivate;

    if (pPicturePrivate)
    {
	DrawablePtr pDrawable = pPicture->pDrawable;
	WindowPtr   pWindow = (WindowPtr) pDrawable;
	PixmapPtr   pPixmap = getCwPixmap (pWindow);

	*x_off = pDrawable->x - pPixmap->screen_x;
	*y_off = pDrawable->y - pPixmap->screen_y;

	return pPicturePrivate->pBackingPicture;
    }
    else
    {
	*x_off = *y_off = 0;
	return pPicture;
    }
}
    
static void
cwDestroyPicture (PicturePtr pPicture)
{
    ScreenPtr		pScreen = pPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    
    cwPsUnwrap(DestroyPicture);
    cwDestroyPicturePrivate (pPicture);
    (*ps->DestroyPicture) (pPicture);
    cwPsWrap(DestroyPicture, cwDestroyPicture);
}

static void
cwChangePicture (PicturePtr pPicture, Mask mask)
{
    ScreenPtr		pScreen = pPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwPicturePtr	pPicturePrivate = getCwPicture(pPicture);
    
    cwPsUnwrap(ChangePicture);
    (*ps->ChangePicture) (pPicture, mask);
    if (pPicturePrivate)
	pPicturePrivate->stateChanges |= mask;
    cwPsWrap(ChangePicture, cwChangePicture);
}


static void
cwValidatePicture (PicturePtr pPicture,
		   Mask       mask)
{
    DrawablePtr		pDrawable = pPicture->pDrawable;
    ScreenPtr		pScreen = pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwPicturePrivate;
    
    cwPsUnwrap(ValidatePicture);

    /*
     * Must call ValidatePicture to ensure pPicture->pCompositeClip is valid
     */
    (*ps->ValidatePicture) (pPicture, mask);
    
    if (!cwDrawableIsRedirWindow (pDrawable))
    {
	if (pPicturePrivate)
	    cwDestroyPicturePrivate (pPicture);
    }
    else
    {
	PicturePtr  pBackingPicture;
	DrawablePtr pBackingDrawable;
	int	    x_off, y_off;
	
	pBackingDrawable = cwGetBackingDrawable(pDrawable, &x_off, &y_off);

	if (pPicturePrivate && 
	    pPicturePrivate->pBackingPicture->pDrawable != pBackingDrawable)
	{
	    cwDestroyPicturePrivate (pPicture);
	    pPicturePrivate = 0;
	}

	if (!pPicturePrivate)
	{
	    pPicturePrivate = cwCreatePicturePrivate (pPicture);
	    if (!pPicturePrivate)
	    {
		cwPsWrap(ValidatePicture, cwValidatePicture);
		return;
	    }
	}

	pBackingPicture = pPicturePrivate->pBackingPicture;

	/*
	 * Always copy transform and filters because there's no
	 * indication of when they've changed
	 */
	SetPictureTransform(pBackingPicture, pPicture->transform);
	
	if (pBackingPicture->filter != pPicture->filter ||
	    pPicture->filter_nparams > 0)
	{
	    char    *filter = PictureGetFilterName (pPicture->filter);
	    
	    SetPictureFilter(pBackingPicture,
			     filter, strlen (filter),
			     pPicture->filter_params,
			     pPicture->filter_nparams);
	}

	pPicturePrivate->stateChanges |= mask;

	if (pPicturePrivate->serialNumber != pDrawable->serialNumber ||
	    (pPicturePrivate->stateChanges & (CPClipXOrigin|CPClipYOrigin|CPClipMask)))
	{
	    SetPictureClipRegion (pBackingPicture, 
				  x_off - pDrawable->x,
				  y_off - pDrawable->y,
				  pPicture->pCompositeClip);
    
	    pPicturePrivate->serialNumber = pDrawable->serialNumber;
	    pPicturePrivate->stateChanges &= ~(CPClipXOrigin | CPClipYOrigin | CPClipMask);
	}

	CopyPicture(pPicture, pPicturePrivate->stateChanges, pBackingPicture);

	ValidatePicture (pBackingPicture);
    }
    cwPsWrap(ValidatePicture, cwValidatePicture);
}

static void
cwComposite (CARD8	op,
	     PicturePtr pSrcPicture,
	     PicturePtr pMskPicture,
	     PicturePtr pDstPicture,
	     INT16	xSrc,
	     INT16	ySrc,
	     INT16	xMsk,
	     INT16	yMsk,
	     INT16	xDst,
	     INT16	yDst,
	     CARD16	width,
	     CARD16	height)
{
    ScreenPtr	pScreen = pDstPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwSrcPictureDecl;
    cwMskPictureDecl;
    cwDstPictureDecl;
    
    cwPsUnwrap(Composite);
    (*ps->Composite) (op, pBackingSrcPicture, pBackingMskPicture, pBackingDstPicture,
		      xSrc + src_picture_x_off, ySrc + src_picture_y_off,
		      xMsk + msk_picture_x_off, yMsk + msk_picture_y_off,
		      xDst + dst_picture_x_off, yDst + dst_picture_y_off,
		      width, height);
    cwPsWrap(Composite, cwComposite);
}

static void
cwCompositeRects (CARD8		op,
		  PicturePtr	pDstPicture,
		  xRenderColor  *color,
		  int		nRect,
		  xRectangle	*rects)
{
    ScreenPtr	pScreen = pDstPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwDstPictureDecl;
    int i;
    
    cwPsUnwrap(CompositeRects);
    for (i = 0; i < nRect; i++)
    {
	rects[i].x += dst_picture_x_off;
	rects[i].y += dst_picture_y_off;
    }
    (*ps->CompositeRects) (op, pBackingDstPicture, color, nRect, rects);
    cwPsWrap(CompositeRects, cwCompositeRects);
}

static void
cwTrapezoids (CARD8	    op,
	      PicturePtr    pSrcPicture,
	      PicturePtr    pDstPicture,
	      PictFormatPtr maskFormat,
	      INT16	    xSrc,
	      INT16	    ySrc,
	      int	    ntrap,
	      xTrapezoid    *traps)
{
    ScreenPtr	pScreen = pDstPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwSrcPictureDecl;
    cwDstPictureDecl;
    int i;
    
    cwPsUnwrap(Trapezoids);
    if (dst_picture_x_off || dst_picture_y_off) {
	for (i = 0; i < ntrap; i++)
	{
	    traps[i].top += dst_picture_y_off << 16;
	    traps[i].bottom += dst_picture_y_off << 16;
	    traps[i].left.p1.x += dst_picture_x_off << 16;
	    traps[i].left.p1.y += dst_picture_y_off << 16;
	    traps[i].left.p2.x += dst_picture_x_off << 16;
	    traps[i].left.p2.y += dst_picture_y_off << 16;
	    traps[i].right.p1.x += dst_picture_x_off << 16;
	    traps[i].right.p1.y += dst_picture_y_off << 16;
	    traps[i].right.p2.x += dst_picture_x_off << 16;
	    traps[i].right.p2.y += dst_picture_y_off << 16;
	}
    }
    (*ps->Trapezoids) (op, pBackingSrcPicture, pBackingDstPicture, maskFormat,
		       xSrc + src_picture_x_off, ySrc + src_picture_y_off,
		       ntrap, traps);
    cwPsWrap(Trapezoids, cwTrapezoids);
}

static void
cwTriangles (CARD8	    op,
	     PicturePtr	    pSrcPicture,
	     PicturePtr	    pDstPicture,
	     PictFormatPtr  maskFormat,
	     INT16	    xSrc,
	     INT16	    ySrc,
	     int	    ntri,
	     xTriangle	   *tris)
{
    ScreenPtr	pScreen = pDstPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwSrcPictureDecl;
    cwDstPictureDecl;
    int i;
    
    cwPsUnwrap(Triangles);
    if (dst_picture_x_off || dst_picture_y_off) {
	for (i = 0; i < ntri; i++)
	{
	    tris[i].p1.x += dst_picture_x_off << 16;
	    tris[i].p1.y += dst_picture_y_off << 16;
	    tris[i].p2.x += dst_picture_x_off << 16;
	    tris[i].p2.y += dst_picture_y_off << 16;
	    tris[i].p3.x += dst_picture_x_off << 16;
	    tris[i].p3.y += dst_picture_y_off << 16;
	}
    }
    (*ps->Triangles) (op, pBackingSrcPicture, pBackingDstPicture, maskFormat,
		      xSrc + src_picture_x_off, ySrc + src_picture_y_off,
		      ntri, tris);
    cwPsWrap(Triangles, cwTriangles);
}

static void
cwTriStrip (CARD8	    op,
	    PicturePtr	    pSrcPicture,
	    PicturePtr	    pDstPicture,
	    PictFormatPtr   maskFormat,
	    INT16	    xSrc,
	    INT16	    ySrc,
	    int		    npoint,
	    xPointFixed    *points)
{
    ScreenPtr	pScreen = pDstPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwSrcPictureDecl;
    cwDstPictureDecl;
    int i;

    cwPsUnwrap(TriStrip);
    if (dst_picture_x_off || dst_picture_y_off) {
	for (i = 0; i < npoint; i++)
	{
	    points[i].x += dst_picture_x_off << 16;
	    points[i].y += dst_picture_y_off << 16;
	}
    }
    (*ps->TriStrip) (op, pBackingSrcPicture, pBackingDstPicture, maskFormat,
		     xSrc + src_picture_x_off, ySrc + src_picture_y_off,
		     npoint, points);
    cwPsWrap(TriStrip, cwTriStrip);
}

static void
cwTriFan (CARD8		 op,
	  PicturePtr	 pSrcPicture,
	  PicturePtr	 pDstPicture,
	  PictFormatPtr  maskFormat,
	  INT16		 xSrc,
	  INT16		 ySrc,
	  int		 npoint,
	  xPointFixed   *points)
{
    ScreenPtr	pScreen = pDstPicture->pDrawable->pScreen;
    cwPsDecl(pScreen);
    cwSrcPictureDecl;
    cwDstPictureDecl;
    int i;

    cwPsUnwrap(TriFan);
    if (dst_picture_x_off || dst_picture_y_off) {
	for (i = 0; i < npoint; i++)
	{
	    points[i].x += dst_picture_x_off << 16;
	    points[i].y += dst_picture_y_off << 16;
	}
    }
    (*ps->TriFan) (op, pBackingSrcPicture, pBackingDstPicture, maskFormat,
		   xSrc + src_picture_x_off, ySrc + src_picture_y_off,
		   npoint, points);
    cwPsWrap(TriFan, cwTriFan);
}

void
cwInitializeRender (ScreenPtr pScreen)
{
    cwPsDecl (pScreen);

    cwPsWrap(DestroyPicture, cwDestroyPicture);
    cwPsWrap(ChangePicture, cwChangePicture);
    cwPsWrap(ValidatePicture, cwValidatePicture);
    cwPsWrap(Composite, cwComposite);
    cwPsWrap(CompositeRects, cwCompositeRects);
    cwPsWrap(Trapezoids, cwTrapezoids);
    cwPsWrap(Triangles, cwTriangles);
    cwPsWrap(TriStrip, cwTriStrip);
    cwPsWrap(TriFan, cwTriFan);
    /* There is no need to wrap AddTraps as far as we can tell.  AddTraps can
     * only be done on alpha-only pictures, and we won't be getting
     * alpha-only window pictures, so there's no need to translate.
     */
}

void
cwFiniRender (ScreenPtr pScreen)
{
    cwPsDecl (pScreen);

    cwPsUnwrap(DestroyPicture);
    cwPsUnwrap(ChangePicture);
    cwPsUnwrap(ValidatePicture);
    cwPsUnwrap(Composite);
    cwPsUnwrap(CompositeRects);
    cwPsUnwrap(Trapezoids);
    cwPsUnwrap(Triangles);
    cwPsUnwrap(TriStrip);
    cwPsUnwrap(TriFan);
}

#endif /* RENDER */