/*
 * Copyright © 2000 Keith Packard
 *
 * 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 "xftint.h"

/*
 * Ok, this is a pain.  To share source pictures across multiple destinations,
 * the screen for each drawable must be discovered.
 */

static int
_XftDrawScreen (Display *dpy, Drawable drawable, Visual *visual)
{
    int		    s;
    Window	    root;
    int		    x, y;
    unsigned int    width, height, borderWidth, depth;
    /* Special case the most common environment */
    if (ScreenCount (dpy) == 1)
	return 0;
    /*
     * If we've got a visual, look for the screen that points at it.
     * This requires no round trip.
     */
    if (visual)
    {
	for (s = 0; s < ScreenCount (dpy); s++)
	{
	    XVisualInfo	template, *ret;
	    int		nret;

	    template.visualid = visual->visualid;
	    template.screen = s;
	    ret = XGetVisualInfo (dpy, VisualIDMask|VisualScreenMask,
				  &template, &nret);
	    if (ret)
	    {
		XFree (ret);
		return s;
	    }
	}
    }
    /*
     * Otherwise, as the server for the drawable geometry and find
     * the screen from the root window.
     * This takes a round trip.
     */
    if (XGetGeometry (dpy, drawable, &root, &x, &y, &width, &height,
		      &borderWidth, &depth))
    {
	for (s = 0; s < ScreenCount (dpy); s++)
	{
	    if (RootWindow (dpy, s) == root)
		return s;
	}
    }
    /*
     * Make a guess -- it's probably wrong, but then the app probably
     * handed us a bogus drawable in this case
     */
    return 0;
}

_X_HIDDEN unsigned int
XftDrawDepth (XftDraw *draw)
{
    if (!draw->depth)
    {
	Window		    root;
	int		    x, y;
	unsigned int	    width, height, borderWidth, depth;
	if (XGetGeometry (draw->dpy, draw->drawable,
			  &root, &x, &y, &width, &height,
			  &borderWidth, &depth))
	    draw->depth = depth;
    }
    return draw->depth;
}

_X_HIDDEN unsigned int
XftDrawBitsPerPixel (XftDraw	*draw)
{
    if (!draw->bits_per_pixel)
    {
	XPixmapFormatValues *formats;
	int		    nformats;
	unsigned int	    depth;

	if ((depth = XftDrawDepth (draw)) &&
	    (formats = XListPixmapFormats (draw->dpy, &nformats)))
	{
	    int	i;

	    for (i = 0; i < nformats; i++)
	    {
		if (formats[i].depth == depth)
		{
		    draw->bits_per_pixel = formats[i].bits_per_pixel;
		    break;
		}
	    }
	    XFree (formats);
	}
    }
    return draw->bits_per_pixel;
}

_X_EXPORT XftDraw *
XftDrawCreate (Display   *dpy,
	       Drawable  drawable,
	       Visual    *visual,
	       Colormap  colormap)
{
    XftDraw	*draw;

    draw = (XftDraw *) malloc (sizeof (XftDraw));
    if (!draw)
	return NULL;

    draw->dpy = dpy;
    draw->drawable = drawable;
    draw->screen = _XftDrawScreen (dpy, drawable, visual);
    draw->depth = 0;		/* don't find out unless we need to know */
    draw->bits_per_pixel = 0;	/* don't find out unless we need to know */
    draw->visual = visual;
    draw->colormap = colormap;
    draw->render.pict = 0;
    draw->core.gc = NULL;
    draw->core.use_pixmap = 0;
    draw->clip_type = XftClipTypeNone;
    draw->subwindow_mode = ClipByChildren;
    XftMemAlloc (XFT_MEM_DRAW, sizeof (XftDraw));
    return draw;
}

_X_EXPORT XftDraw *
XftDrawCreateBitmap (Display	*dpy,
		     Pixmap	bitmap)
{
    XftDraw	*draw;

    draw = (XftDraw *) malloc (sizeof (XftDraw));
    if (!draw)
	return NULL;
    draw->dpy = dpy;
    draw->drawable = (Drawable) bitmap;
    draw->screen = _XftDrawScreen (dpy, bitmap, NULL);
    draw->depth = 1;
    draw->bits_per_pixel = 1;
    draw->visual = NULL;
    draw->colormap = 0;
    draw->render.pict = 0;
    draw->core.gc = NULL;
    draw->core.use_pixmap = 0;
    draw->clip_type = XftClipTypeNone;
    draw->subwindow_mode = ClipByChildren;
    XftMemAlloc (XFT_MEM_DRAW, sizeof (XftDraw));
    return draw;
}

_X_EXPORT XftDraw *
XftDrawCreateAlpha (Display *dpy,
		    Pixmap  pixmap,
		    int	    depth)
{
    XftDraw	*draw;

    draw = (XftDraw *) malloc (sizeof (XftDraw));
    if (!draw)
	return NULL;
    draw->dpy = dpy;
    draw->drawable = (Drawable) pixmap;
    draw->screen = _XftDrawScreen (dpy, pixmap, NULL);
    draw->depth = depth;
    draw->bits_per_pixel = 0;	/* don't find out until we need it */
    draw->visual = NULL;
    draw->colormap = 0;
    draw->render.pict = 0;
    draw->core.gc = NULL;
    draw->core.use_pixmap = 0;
    draw->clip_type = XftClipTypeNone;
    draw->subwindow_mode = ClipByChildren;
    XftMemAlloc (XFT_MEM_DRAW, sizeof (XftDraw));
    return draw;
}

static XRenderPictFormat *
_XftDrawFormat (XftDraw	*draw)
{
    XftDisplayInfo  *info = _XftDisplayInfoGet (draw->dpy, True);

    if (!info || !info->hasRender)
	return NULL;

    if (draw->visual == NULL)
    {
	XRenderPictFormat   pf;

	pf.type = PictTypeDirect;
	pf.depth = XftDrawDepth (draw);
	pf.direct.alpha = 0;
	pf.direct.alphaMask = (1 << pf.depth) - 1;
	return XRenderFindFormat (draw->dpy,
				  (PictFormatType|
				   PictFormatDepth|
				   PictFormatAlpha|
				   PictFormatAlphaMask),
				  &pf,
				  0);
    }
    else
	return XRenderFindVisualFormat (draw->dpy, draw->visual);
}

_X_EXPORT void
XftDrawChange (XftDraw	*draw,
	       Drawable	drawable)
{
    draw->drawable = drawable;
    if (draw->render.pict)
    {
	XRenderFreePicture (draw->dpy, draw->render.pict);
	draw->render.pict = 0;
    }
    if (draw->core.gc)
    {
	XFreeGC (draw->dpy, draw->core.gc);
	draw->core.gc = NULL;
    }
}

_X_EXPORT Display *
XftDrawDisplay (XftDraw *draw)
{
    return draw->dpy;
}

_X_EXPORT Drawable
XftDrawDrawable (XftDraw *draw)
{
    return draw->drawable;
}

_X_EXPORT Colormap
XftDrawColormap (XftDraw *draw)
{
    return draw->colormap;
}

_X_EXPORT Visual *
XftDrawVisual (XftDraw *draw)
{
    return draw->visual;
}

_X_EXPORT void
XftDrawDestroy (XftDraw	*draw)
{
    if (draw->render.pict)
	XRenderFreePicture (draw->dpy, draw->render.pict);
    if (draw->core.gc)
	XFreeGC (draw->dpy, draw->core.gc);
    switch (draw->clip_type) {
    case XftClipTypeRegion:
	XDestroyRegion (draw->clip.region);
	break;
    case XftClipTypeRectangles:
	free (draw->clip.rect);
	break;
    case XftClipTypeNone:
	break;
    }
    XftMemFree (XFT_MEM_DRAW, sizeof (XftDraw));
    free (draw);
}

_X_EXPORT Picture
XftDrawSrcPicture (XftDraw *draw, _Xconst XftColor *color)
{
    Display	    *dpy = draw->dpy;
    XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, True);
    int		    i;
    XftColor	    bitmapColor;

    if (!info || !info->solidFormat)
	return 0;

    /*
     * Monochrome targets require special handling; the PictOp controls
     * the color, and the color must be opaque
     */
    if (!draw->visual && draw->depth == 1)
    {
	bitmapColor.color.alpha = 0xffff;
	bitmapColor.color.red   = 0xffff;
	bitmapColor.color.green = 0xffff;
	bitmapColor.color.blue  = 0xffff;
	color = &bitmapColor;
    }

    /*
     * See if there's one already available
     */
    for (i = 0; i < XFT_NUM_SOLID_COLOR; i++)
    {
	if (info->colors[i].pict &&
	    info->colors[i].screen == draw->screen &&
	    !memcmp ((void *) &color->color,
		     (void *) &info->colors[i].color,
		     sizeof (XRenderColor)))
	    return info->colors[i].pict;
    }
    /*
     * Pick one to replace at random
     */
    i = (unsigned int) rand () % XFT_NUM_SOLID_COLOR;
    /*
     * Recreate if it was for the wrong screen
     */
    if (info->colors[i].screen != draw->screen && info->colors[i].pict)
    {
	XRenderFreePicture (dpy, info->colors[i].pict);
	info->colors[i].pict = 0;
    }
    /*
     * Create picture if necessary
     */
    if (!info->colors[i].pict)
    {
	Pixmap			    pix;
        XRenderPictureAttributes    pa;

	pix = XCreatePixmap (dpy, RootWindow (dpy, draw->screen), 1, 1,
			     info->solidFormat->depth);
	pa.repeat = True;
	info->colors[i].pict = XRenderCreatePicture (draw->dpy,
						     pix,
						     info->solidFormat,
						     CPRepeat, &pa);
	XFreePixmap (dpy, pix);
    }
    /*
     * Set to the new color
     */
    info->colors[i].color = color->color;
    info->colors[i].screen = draw->screen;
    XRenderFillRectangle (dpy, PictOpSrc,
			  info->colors[i].pict,
			  &color->color, 0, 0, 1, 1);
    return info->colors[i].pict;
}

static int
_XftDrawOp (_Xconst XftDraw *draw, _Xconst XftColor *color)
{
    if (draw->visual || draw->depth != 1)
	return PictOpOver;
    if (color->color.alpha >= 0x8000)
	return PictOpOver;
    return PictOpOutReverse;
}

static FcBool
_XftDrawRenderPrepare (XftDraw	*draw)
{
    if (!draw->render.pict)
    {
	XRenderPictFormat	    *format;
	XRenderPictureAttributes    pa;
	unsigned long		    mask = 0;

	format = _XftDrawFormat (draw);
	if (!format)
	    return FcFalse;

	if (draw->subwindow_mode == IncludeInferiors)
	{
	    pa.subwindow_mode = IncludeInferiors;
	    mask |= CPSubwindowMode;
	}
	draw->render.pict = XRenderCreatePicture (draw->dpy, draw->drawable,
						  format, mask, &pa);
	if (!draw->render.pict)
	    return FcFalse;
	switch (draw->clip_type) {
	case XftClipTypeRegion:
	    XRenderSetPictureClipRegion (draw->dpy, draw->render.pict,
					 draw->clip.region);
	    break;
	case XftClipTypeRectangles:
	    XRenderSetPictureClipRectangles (draw->dpy, draw->render.pict,
					     draw->clip.rect->xOrigin,
					     draw->clip.rect->yOrigin,
					     XftClipRects(draw->clip.rect),
					     draw->clip.rect->n);
	    break;
	case XftClipTypeNone:
	    break;
	}
    }
    return FcTrue;
}

static FcBool
_XftDrawCorePrepare (XftDraw *draw, _Xconst XftColor *color)
{
    if (!draw->core.gc)
    {
	XGCValues	gcv;
	unsigned long	mask = 0;
	if (draw->subwindow_mode == IncludeInferiors)
	{
	    gcv.subwindow_mode = IncludeInferiors;
	    mask |= GCSubwindowMode;
	}
	draw->core.gc = XCreateGC (draw->dpy, draw->drawable, mask, &gcv);
	if (!draw->core.gc)
	    return FcFalse;
	switch (draw->clip_type) {
	case XftClipTypeRegion:
	    XSetRegion (draw->dpy, draw->core.gc, draw->clip.region);
	    break;
	case XftClipTypeRectangles:
	    XSetClipRectangles (draw->dpy, draw->core.gc,
				draw->clip.rect->xOrigin,
				draw->clip.rect->yOrigin,
				XftClipRects (draw->clip.rect),
				draw->clip.rect->n,
				Unsorted);
	    break;
	case XftClipTypeNone:
	    break;
	}
    }
    XSetForeground (draw->dpy, draw->core.gc, color->pixel);
    return FcTrue;
}

_X_EXPORT Picture
XftDrawPicture (XftDraw *draw)
{
    if (!_XftDrawRenderPrepare (draw))
	return 0;
    return draw->render.pict;
}

#define NUM_LOCAL   1024

_X_EXPORT void
XftDrawGlyphs (XftDraw		*draw,
	       _Xconst XftColor	*color,
	       XftFont		*pub,
	       int		x,
	       int		y,
	       _Xconst FT_UInt	*glyphs,
	       int		nglyphs)
{
    XftFontInt	*font = (XftFontInt *) pub;

    if (font->format)
    {
	Picture	    src;

	if (_XftDrawRenderPrepare (draw) &&
	    (src = XftDrawSrcPicture (draw, color)))
	    XftGlyphRender (draw->dpy, _XftDrawOp (draw, color),
			     src, pub, draw->render.pict,
			     0, 0, x, y, glyphs, nglyphs);
    }
    else
    {
	if (_XftDrawCorePrepare (draw, color))
	    XftGlyphCore (draw, color, pub, x, y, glyphs, nglyphs);
    }
}

_X_EXPORT void
XftDrawString8 (XftDraw		    *draw,
		_Xconst XftColor    *color,
		XftFont		    *pub,
		int		    x,
		int		    y,
		_Xconst FcChar8	    *string,
		int		    len)
{
    FT_UInt	    *glyphs, glyphs_local[NUM_LOCAL];
    int		    i;

    if (XftDebug () & XFT_DBG_DRAW)
	printf ("DrawString \"%*.*s\"\n", len, len, string);

    if (len <= NUM_LOCAL)
	glyphs = glyphs_local;
    else
    {
	glyphs = malloc (len * sizeof (FT_UInt));
	if (!glyphs)
	    return;
    }
    for (i = 0; i < len; i++)
	glyphs[i] = XftCharIndex (draw->dpy, pub, string[i]);
    XftDrawGlyphs (draw, color, pub, x, y, glyphs, len);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawString16 (XftDraw	    *draw,
		 _Xconst XftColor   *color,
		 XftFont	    *pub,
		 int		    x,
		 int		    y,
		 _Xconst FcChar16   *string,
		 int		    len)
{
    FT_UInt	    *glyphs, glyphs_local[NUM_LOCAL];
    int		    i;

    if (len <= NUM_LOCAL)
	glyphs = glyphs_local;
    else
    {
	glyphs = malloc (len * sizeof (FT_UInt));
	if (!glyphs)
	    return;
    }
    for (i = 0; i < len; i++)
	glyphs[i] = XftCharIndex (draw->dpy, pub, string[i]);

    XftDrawGlyphs (draw, color, pub, x, y, glyphs, len);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawString32 (XftDraw	    *draw,
		 _Xconst XftColor   *color,
		 XftFont	    *pub,
		 int		    x,
		 int		    y,
		 _Xconst FcChar32   *string,
		 int		    len)
{
    FT_UInt	    *glyphs, glyphs_local[NUM_LOCAL];
    int		    i;

    if (len <= NUM_LOCAL)
	glyphs = glyphs_local;
    else
    {
	glyphs = malloc (len * sizeof (FT_UInt));
	if (!glyphs)
	    return;
    }
    for (i = 0; i < len; i++)
	glyphs[i] = XftCharIndex (draw->dpy, pub, string[i]);

    XftDrawGlyphs (draw, color, pub, x, y, glyphs, len);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawStringUtf8 (XftDraw	    *draw,
		   _Xconst XftColor *color,
		   XftFont	    *pub,
		   int		    x,
		   int		    y,
		   _Xconst FcChar8  *string,
		   int		    len)
{
    FT_UInt	    *glyphs, *glyphs_new, glyphs_local[NUM_LOCAL];
    FcChar32	    ucs4;
    int		    i;
    int		    l;
    int		    size;

    i = 0;
    glyphs = glyphs_local;
    size = NUM_LOCAL;
    while (len && (l = FcUtf8ToUcs4 (string, &ucs4, len)) > 0)
    {
	if (i == size)
	{
	    glyphs_new = malloc (size * 2 * sizeof (FT_UInt));
	    if (!glyphs_new)
	    {
		if (glyphs != glyphs_local)
		    free (glyphs);
		return;
	    }
	    memcpy (glyphs_new, glyphs, size * sizeof (FT_UInt));
	    size *= 2;
	    if (glyphs != glyphs_local)
		free (glyphs);
	    glyphs = glyphs_new;
	}
	glyphs[i++] = XftCharIndex (draw->dpy, pub, ucs4);
	string += l;
	len -= l;
    }
    XftDrawGlyphs (draw, color, pub, x, y, glyphs, i);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawStringUtf16 (XftDraw		*draw,
		    _Xconst XftColor	*color,
		    XftFont		*pub,
		    int			x,
		    int			y,
		    _Xconst FcChar8	*string,
		    FcEndian		endian,
		    int			len)
{
    FT_UInt	    *glyphs, *glyphs_new, glyphs_local[NUM_LOCAL];
    FcChar32	    ucs4;
    int		    i;
    int		    l;
    int		    size;

    i = 0;
    glyphs = glyphs_local;
    size = NUM_LOCAL;
    while (len && (l = FcUtf16ToUcs4 (string, endian, &ucs4, len)) > 0)
    {
	if (i == size)
	{
	    glyphs_new = malloc (size * 2 * sizeof (FT_UInt));
	    if (!glyphs_new)
	    {
		if (glyphs != glyphs_local)
		    free (glyphs);
		return;
	    }
	    memcpy (glyphs_new, glyphs, size * sizeof (FT_UInt));
	    size *= 2;
	    if (glyphs != glyphs_local)
		free (glyphs);
	    glyphs = glyphs_new;
	}
	glyphs[i++] = XftCharIndex (draw->dpy, pub, ucs4);
	string += l;
	len -= l;
    }
    XftDrawGlyphs (draw, color, pub, x, y, glyphs, i);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawGlyphSpec (XftDraw		*draw,
		  _Xconst XftColor	*color,
		  XftFont		*pub,
		  _Xconst XftGlyphSpec	*glyphs,
		  int			len)
{
    XftFontInt	*font = (XftFontInt *) pub;

    if (font->format)
    {
	Picture	src;

	if (_XftDrawRenderPrepare (draw) &&
	    (src = XftDrawSrcPicture (draw, color)))
	{
	    XftGlyphSpecRender (draw->dpy, _XftDrawOp (draw, color),
				src, pub, draw->render.pict,
				0, 0, glyphs, len);
	}
    }
    else
    {
	if (_XftDrawCorePrepare (draw, color))
	    XftGlyphSpecCore (draw, color, pub, glyphs, len);
    }
}

_X_EXPORT void
XftDrawGlyphFontSpec (XftDraw			*draw,
		      _Xconst XftColor		*color,
		      _Xconst XftGlyphFontSpec	*glyphs,
		      int			len)
{
    int		i;
    int		start;

    i = 0;
    while (i < len)
    {
	start = i;
	if (((XftFontInt *) glyphs[i].font)->format)
	{
	    Picture	src;
	    while (i < len && ((XftFontInt *) glyphs[i].font)->format)
		i++;
	    if (_XftDrawRenderPrepare (draw) &&
		(src = XftDrawSrcPicture (draw, color)))
	    {
		XftGlyphFontSpecRender (draw->dpy, _XftDrawOp (draw, color),
					src, draw->render.pict,
					0, 0, glyphs + start , i - start);
	    }
	}
	else
	{
	    while (i < len && !((XftFontInt *) glyphs[i].font)->format)
		i++;
	    if (_XftDrawCorePrepare (draw, color))
		XftGlyphFontSpecCore (draw, color, glyphs + start, i - start);
	}
    }
}

_X_EXPORT void
XftDrawCharSpec (XftDraw		*draw,
		 _Xconst XftColor	*color,
		 XftFont		*pub,
		 _Xconst XftCharSpec	*chars,
		 int			len)
{
    XftGlyphSpec    *glyphs, glyphs_local[NUM_LOCAL];
    int		    i;

    if (len <= NUM_LOCAL)
	glyphs = glyphs_local;
    else
    {
	glyphs = malloc (len * sizeof (XftGlyphSpec));
	if (!glyphs)
	    return;
    }
    for (i = 0; i < len; i++)
    {
	glyphs[i].glyph = XftCharIndex(draw->dpy, pub, chars[i].ucs4);
	glyphs[i].x = chars[i].x;
	glyphs[i].y = chars[i].y;
    }

    XftDrawGlyphSpec (draw, color, pub, glyphs, len);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawCharFontSpec (XftDraw			*draw,
		     _Xconst XftColor		*color,
		     _Xconst XftCharFontSpec	*chars,
		     int			len)
{
    XftGlyphFontSpec	*glyphs, glyphs_local[NUM_LOCAL];
    int			i;

    if (len <= NUM_LOCAL)
	glyphs = glyphs_local;
    else
    {
	glyphs = malloc (len * sizeof (XftGlyphFontSpec));
	if (!glyphs)
	    return;
    }
    for (i = 0; i < len; i++)
    {
	glyphs[i].font = chars[i].font;
	glyphs[i].glyph = XftCharIndex(draw->dpy, glyphs[i].font, chars[i].ucs4);
	glyphs[i].x = chars[i].x;
	glyphs[i].y = chars[i].y;
    }

    XftDrawGlyphFontSpec (draw, color, glyphs, len);
    if (glyphs != glyphs_local)
	free (glyphs);
}

_X_EXPORT void
XftDrawRect (XftDraw		*draw,
	     _Xconst XftColor	*color,
	     int		x,
	     int		y,
	     unsigned int	width,
	     unsigned int	height)
{
    if (_XftDrawRenderPrepare (draw))
    {
	XRenderFillRectangle (draw->dpy, PictOpSrc, draw->render.pict,
			      &color->color, x, y, width, height);
    }
    else if (_XftDrawCorePrepare (draw, color))
    {
	/* note: not XftRectCore() */
	XSetForeground (draw->dpy, draw->core.gc, color->pixel);
	XFillRectangle (draw->dpy, draw->drawable, draw->core.gc,
			x, y, width, height);
    }
}

_X_EXPORT Bool
XftDrawSetClip (XftDraw	*draw,
		Region	r)
{
    Region			n = NULL;

    /*
     * Check for quick exits
     */
    if (!r && draw->clip_type == XftClipTypeNone)
	return True;

    if (r &&
	draw->clip_type == XftClipTypeRegion &&
	XEqualRegion (r, draw->clip.region))
    {
	return True;
    }

    /*
     * Duplicate the region so future changes can be short circuited
     */
    if (r)
    {
	n = XCreateRegion ();
	if (n)
	{
	    if (!XUnionRegion (n, r, n))
	    {
		XDestroyRegion (n);
		return False;
	    }
	}
    }

    /*
     * Destroy existing clip
     */
    switch (draw->clip_type) {
    case XftClipTypeRegion:
	XDestroyRegion (draw->clip.region);
	break;
    case XftClipTypeRectangles:
	free (draw->clip.rect);
	break;
    case XftClipTypeNone:
	break;
    }

    /*
     * Set the clip
     */
    if (n)
    {
	draw->clip_type = XftClipTypeRegion;
	draw->clip.region = n;
    }
    else
    {
	draw->clip_type = XftClipTypeNone;
    }
    /*
     * Apply new clip to existing objects
     */
    if (draw->render.pict)
    {
	if (n)
	    XRenderSetPictureClipRegion (draw->dpy, draw->render.pict, n);
	else
	{
	    XRenderPictureAttributes	pa;
	    pa.clip_mask = None;
	    XRenderChangePicture (draw->dpy, draw->render.pict,
				  CPClipMask, &pa);
	}
    }
    if (draw->core.gc)
    {
	if (n)
	    XSetRegion (draw->dpy, draw->core.gc, draw->clip.region);
	else
	    XSetClipMask (draw->dpy, draw->core.gc, None);
    }
    return True;
}

_X_EXPORT Bool
XftDrawSetClipRectangles (XftDraw		*draw,
			  int			xOrigin,
			  int			yOrigin,
			  _Xconst XRectangle	*rects,
			  int			n)
{
    XftClipRect	*new = NULL;

    /*
     * Check for quick exit
     */
    if (draw->clip_type == XftClipTypeRectangles &&
	draw->clip.rect->n == n &&
	(n == 0 || (draw->clip.rect->xOrigin == xOrigin &&
		    draw->clip.rect->yOrigin == yOrigin)) &&
	!memcmp (XftClipRects (draw->clip.rect), rects, n * sizeof (XRectangle)))
    {
	return True;
    }

    /*
     * Duplicate the region so future changes can be short circuited
     */
    new = malloc (sizeof (XftClipRect) + n * sizeof (XRectangle));
    if (!new)
	return False;

    new->n = n;
    new->xOrigin = xOrigin;
    new->yOrigin = yOrigin;
    memcpy (XftClipRects (new), rects, n * sizeof (XRectangle));

    /*
     * Destroy existing clip
     */
    switch (draw->clip_type) {
    case XftClipTypeRegion:
	XDestroyRegion (draw->clip.region);
	break;
    case XftClipTypeRectangles:
	free (draw->clip.rect);
	break;
    case XftClipTypeNone:
	break;
    }

    /*
     * Set the clip
     */
    draw->clip_type = XftClipTypeRectangles;
    draw->clip.rect = new;
    /*
     * Apply new clip to existing objects
     */
    if (draw->render.pict)
    {
	XRenderSetPictureClipRectangles (draw->dpy, draw->render.pict,
					 new->xOrigin,
					 new->yOrigin,
					 XftClipRects(new),
					 new->n);
    }
    if (draw->core.gc)
    {
	XSetClipRectangles (draw->dpy, draw->core.gc,
			    new->xOrigin,
			    new->yOrigin,
			    XftClipRects (new),
			    new->n,
			    Unsorted);
    }
    return True;
}

_X_EXPORT void
XftDrawSetSubwindowMode (XftDraw *draw, int mode)
{
    if (mode == draw->subwindow_mode)
	return;
    draw->subwindow_mode = mode;
    if (draw->render.pict)
    {
	XRenderPictureAttributes    pa;

	pa.subwindow_mode = mode;
	XRenderChangePicture (draw->dpy, draw->render.pict,
			      CPSubwindowMode, &pa);
    }
    if (draw->core.gc)
	XSetSubwindowMode (draw->dpy, draw->core.gc, mode);
}
