/*
 * 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"

_X_HIDDEN void
XftRectCore (XftDraw		*draw,
	     _Xconst XftColor	*color,
	     int		x,
	     int		y,
	     unsigned int	width,
	     unsigned int	height)
{
    if (color->color.alpha >= 0x8000)
    {
	XSetForeground (draw->dpy, draw->core.gc, color->pixel);
	XFillRectangle (draw->dpy, draw->drawable, draw->core.gc,
			x, y, width, height);
    }
}

/*
 * Use the core protocol to draw the glyphs
 */

static void
_XftSharpGlyphMono (XftDraw	*draw,
		    XftGlyph	*glyph,
		    int		x,
		    int		y)
{
    unsigned char   *srcLine = glyph->bitmap, *src;
    unsigned char   bits, bitsMask;
    int		    width = glyph->metrics.width;
    int		    stride = ((width + 31) & ~31) >> 3;
    int		    height = glyph->metrics.height;
    int		    w;
    int		    xspan, lenspan;

    x -= glyph->metrics.x;
    y -= glyph->metrics.y;
    while (height--)
    {
	src = srcLine;
	srcLine += stride;
	w = width;

	bitsMask = 0x80;    /* FreeType is always MSB first */
	bits = *src++;

	xspan = x;
	while (w)
	{
	    if (bits & bitsMask)
	    {
		lenspan = 0;
		do
		{
		    lenspan++;
		    if (lenspan == w)
			break;
		    bitsMask = bitsMask >> 1;
		    if (!bitsMask)
		    {
			bits = *src++;
			bitsMask = 0x80;
		    }
		} while (bits & bitsMask);
		XFillRectangle (draw->dpy, draw->drawable,
				draw->core.gc, xspan, y, lenspan, 1);
		xspan += lenspan;
		w -= lenspan;
	    }
	    else
	    {
		do
		{
		    w--;
		    xspan++;
		    if (!w)
			break;
		    bitsMask = bitsMask >> 1;
		    if (!bitsMask)
		    {
			bits = *src++;
			bitsMask = 0x80;
		    }
		} while (!(bits & bitsMask));
	    }
	}
	y++;
    }
}

/*
 * Draw solid color text from an anti-aliased bitmap.  This is a
 * fallback for cases where a particular drawable has no AA code
 */
static void
_XftSharpGlyphGray (XftDraw	*draw,
		    XftGlyph	*glyph,
		    int		x,
		    int		y)
{
    unsigned char   *srcLine = glyph->bitmap, *src, bits;
    int		    width = glyph->metrics.width;
    int		    stride = ((width + 3) & ~3);
    int		    height = glyph->metrics.height;
    int		    w;
    int		    xspan, lenspan;

    x -= glyph->metrics.x;
    y -= glyph->metrics.y;
    while (height--)
    {
	src = srcLine;
	srcLine += stride;
	w = width;

	bits = *src++;
	xspan = x;
	while (w)
	{
	    if (bits >= 0x80)
	    {
		lenspan = 0;
		do
		{
		    lenspan++;
		    if (lenspan == w)
			break;
		    bits = *src++;
		} while (bits >= 0x80);
		XFillRectangle (draw->dpy, draw->drawable,
				draw->core.gc, xspan, y, lenspan, 1);
		xspan += lenspan;
		w -= lenspan;
	    }
	    else
	    {
		do
		{
		    w--;
		    xspan++;
		    if (!w)
			break;
		    bits = *src++;
		} while (bits < 0x80);
	    }
	}
	y++;
    }
}

static void
_XftSharpGlyphRgba (XftDraw	*draw,
		    XftGlyph	*glyph,
		    int		x,
		    int		y)
{
    CARD32	    *srcLine = glyph->bitmap, *src, bits;
    int		    width = glyph->metrics.width;
    int		    stride = ((width + 3) & ~3);
    int		    height = glyph->metrics.height;
    int		    w;
    int		    xspan, lenspan;

    x -= glyph->metrics.x;
    y -= glyph->metrics.y;
    while (height--)
    {
	src = srcLine;
	srcLine += stride;
	w = width;

	bits = *src++;
	xspan = x;
	while (w)
	{
	    if (bits >= 0x80000000)
	    {
		lenspan = 0;
		do
		{
		    lenspan++;
		    if (lenspan == w)
			break;
		    bits = *src++;
		} while (bits >= 0x80000000);
		XFillRectangle (draw->dpy, draw->drawable,
				draw->core.gc, xspan, y, lenspan, 1);
		xspan += lenspan;
		w -= lenspan;
	    }
	    else
	    {
		do
		{
		    w--;
		    xspan++;
		    if (!w)
			break;
		    bits = *src++;
		} while (bits < 0x80000000);
	    }
	}
	y++;
    }
}

typedef void (*XftSharpGlyph) (XftDraw	*draw,
			       XftGlyph	*glyph,
			       int	x,
			       int	y);

static XftSharpGlyph
_XftSharpGlyphFind (XftDraw *draw, XftFont *public)
{
    XftFontInt *font = (XftFontInt *) public;

    if (!font->info.antialias)
	return _XftSharpGlyphMono;
    else switch (font->info.rgba) {
    case FC_RGBA_RGB:
    case FC_RGBA_BGR:
    case FC_RGBA_VRGB:
    case FC_RGBA_VBGR:
	return _XftSharpGlyphRgba;
    default:
	return _XftSharpGlyphGray;
    }
}

/*
 * Draw glyphs to a target that supports anti-aliasing
 */

/*
 * Primitives for converting between RGB values and TrueColor pixels
 */

static void
_XftExamineBitfield (unsigned long mask, int *shift, int *len)
{
    int	s, l;

    s = 0;
    while ((mask & 1) == 0)
    {
	mask >>= 1;
	s++;
    }
    l = 0;
    while ((mask & 1) == 1)
    {
	mask >>= 1;
	l++;
    }
    *shift = s;
    *len = l;
}

static CARD32
_XftGetField (unsigned long l_pixel, int shift, int len)
{
    CARD32  pixel = (CARD32) l_pixel;

    pixel = pixel & (((1 << (len)) - 1) << shift);
    pixel = pixel << (32 - (shift + len)) >> 24;
    while (len < 8)
    {
	pixel |= (pixel >> len);
	len <<= 1;
    }
    return pixel;
}

static unsigned long
_XftPutField (CARD32 pixel, int shift, int len)
{
    unsigned long   l_pixel = (unsigned long) pixel;

    shift = shift - (8 - len);
    if (len <= 8)
	l_pixel &= (((1 << len) - 1) << (8 - len));
    if (shift < 0)
	l_pixel >>= -shift;
    else
	l_pixel <<= shift;
    return l_pixel;
}

/*
 * This is used when doing XftCharFontSpec/XftGlyphFontSpec where
 * some of the fonts are bitmaps and some are anti-aliased to handle
 * the bitmap portions
 */
static void
_XftSmoothGlyphMono (XImage		*image,
		     _Xconst XftGlyph	*xftg,
		     int		x,
		     int		y,
		     _Xconst XftColor   *color)
{
    unsigned char   *srcLine = xftg->bitmap, *src;
    unsigned char   bits, bitsMask;
    int		    width = xftg->metrics.width;
    int		    stride = ((width + 31) & ~31) >> 3;
    int		    height = xftg->metrics.height;
    int		    w;
    int		    xspan;
    int		    r_shift, r_len;
    int		    g_shift, g_len;
    int		    b_shift, b_len;
    unsigned long   pixel;

    _XftExamineBitfield (image->red_mask, &r_shift, &r_len);
    _XftExamineBitfield (image->green_mask, &g_shift, &g_len);
    _XftExamineBitfield (image->blue_mask, &b_shift, &b_len);
    pixel = (_XftPutField (color->color.red >> 8, r_shift, r_len) |
	     _XftPutField (color->color.green >> 8, g_shift, g_len) |
	     _XftPutField (color->color.blue >> 8, b_shift, b_len));
    x -= xftg->metrics.x;
    y -= xftg->metrics.y;
    while (height--)
    {
	src = srcLine;
	srcLine += stride;
	w = width;

	bitsMask = 0x80;    /* FreeType is always MSB first */
	bits = *src++;

	xspan = x;
	while (w--)
	{
	    if (bits & bitsMask)
		XPutPixel (image, xspan, y, pixel);
    	    bitsMask = bitsMask >> 1;
    	    if (!bitsMask)
    	    {
    		bits = *src++;
    		bitsMask = 0x80;
    	    }
	    xspan++;
	}
	y++;
    }
}

/*
 * As simple anti-aliasing is likely to be common, there are three
 * optimized versions for the usual true color pixel formats (888, 565, 555).
 * Other formats are handled by the general case
 */

#define cvt8888to0565(s)    ((((s) >> 3) & 0x001f) | \
			     (((s) >> 5) & 0x07e0) | \
			     (((s) >> 8) & 0xf800))

#define cvt0565to8888(s)    (((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | \
			     ((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | \
			     ((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000)))

#define cvt8888to0555(s)    ((((s) >> 3) & 0x001f) | \
			     (((s) >> 6) & 0x03e0) | \
			     (((s) >> 7) & 0x7c00))

#define cvt0555to8888(s)    (((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | \
			     ((((s) << 6) & 0xf800) | (((s) >> 0) & 0x300)) | \
			     ((((s) << 9) & 0xf80000) | (((s) << 4) & 0x70000)))


#define XftIntMult(a,b,t) ( (t) = (a) * (b) + 0x80, ( ( ( (t)>>8 ) + (t) )>>8 ) )
#define XftIntDiv(a,b)	 (((CARD16) (a) * 255) / (b))

#define XftGet8(v,i)   ((CARD16) (CARD8) ((v) >> i))

/*
 * There are two ways of handling alpha -- either as a single unified value or
 * a separate value for each component, hence each macro must have two
 * versions.  The unified alpha version has a 'U' at the end of the name,
 * the component version has a 'C'.  Similarly, functions which deal with
 * this difference will have two versions using the same convention.
 */

#define XftOverU(x,y,i,a,t) ((t) = XftIntMult(XftGet8(y,i),(a),(t)) + XftGet8(x,i),\
			   (CARD32) ((CARD8) ((t) | (0 - ((t) >> 8)))) << (i))

#define XftOverC(x,y,i,a,t) ((t) = XftIntMult(XftGet8(y,i),XftGet8(a,i),(t)) + XftGet8(x,i),\
			    (CARD32) ((CARD8) ((t) | (0 - ((t) >> 8)))) << (i))

#define XftInU(x,i,a,t) ((CARD32) XftIntMult(XftGet8(x,i),(a),(t)) << (i))

#define XftInC(x,i,a,t) ((CARD32) XftIntMult(XftGet8(x,i),XftGet8(a,i),(t)) << (i))

#define XftGen(x,y,i,ax,ay,t,u,v) ((t) = (XftIntMult(XftGet8(y,i),ay,(u)) + \
					 XftIntMult(XftGet8(x,i),ax,(v))),\
				  (CARD32) ((CARD8) ((t) | \
						     (0 - ((t) >> 8)))) << (i))

#define XftAdd(x,y,i,t)	((t) = XftGet8(x,i) + XftGet8(y,i), \
			 (CARD32) ((CARD8) ((t) | (0 - ((t) >> 8)))) << (i))


static CARD32
fbOver24 (CARD32 x, CARD32 y)
{
    CARD16  a = ~x >> 24;
    CARD16  t;
    CARD32  m,n,o;

    m = XftOverU(x,y,0,a,t);
    n = XftOverU(x,y,8,a,t);
    o = XftOverU(x,y,16,a,t);
    return m|n|o;
}

static CARD32
fbIn (CARD32 x, CARD8 y)
{
    CARD16  a = y;
    CARD16  t;
    CARD32  m,n,o,p;

    m = XftInU(x,0,a,t);
    n = XftInU(x,8,a,t);
    o = XftInU(x,16,a,t);
    p = XftInU(x,24,a,t);
    return m|n|o|p;
}

static void
_XftSmoothGlyphGray8888 (XImage		    *image,
			 _Xconst XftGlyph   *xftg,
			 int		    x,
			 int		    y,
			 _Xconst XftColor   *color)
{
    CARD32	src, srca;
    CARD32	r, g, b;
    CARD32	*dstLine, *dst, d;
    CARD8	*maskLine, *mask, m;
    int		dstStride, maskStride;
    int		width, height;
    int		w;

    srca = color->color.alpha >> 8;

    /* This handles only RGB and BGR */
    g = (color->color.green & 0xff00);
    if (image->red_mask == 0xff0000)
    {
	r = (color->color.red & 0xff00) << 8;
	b = color->color.blue >> 8;
    }
    else
    {
	r = color->color.red >> 8;
	b = (color->color.blue & 0xff00) << 8;
    }
    src = (srca << 24) | r | g | b;

    width = xftg->metrics.width;
    height = xftg->metrics.height;

    x -= xftg->metrics.x;
    y -= xftg->metrics.y;

    dstLine = (CARD32 *) (image->data + image->bytes_per_line * y + (x << 2));
    dstStride = image->bytes_per_line >> 2;
    maskLine = (unsigned char *) xftg->bitmap;
    maskStride = (width + 3) & ~3;

    while (height--)
    {
	dst = dstLine;
	dstLine += dstStride;
	mask = maskLine;
	maskLine += maskStride;
	w = width;

	while (w--)
	{
	    m = *mask++;
	    if (m == 0xff)
	    {
		if (srca == 0xff)
		    *dst = src;
		else
		    *dst = fbOver24 (src, *dst);
	    }
	    else if (m)
	    {
		d = fbIn (src, m);
		*dst = fbOver24 (d, *dst);
	    }
	    dst++;
	}
    }
}

static void
_XftSmoothGlyphGray565 (XImage		    *image,
			_Xconst XftGlyph    *xftg,
			int		    x,
			int		    y,
			_Xconst XftColor    *color)
{
    CARD32	src, srca;
    CARD32	r, g, b;
    CARD32	d;
    CARD16	*dstLine, *dst;
    CARD8	*maskLine, *mask, m;
    int		dstStride, maskStride;
    int		width, height;
    int		w;

    srca = color->color.alpha >> 8;

    /* This handles only RGB and BGR */
    g = (color->color.green & 0xff00);
    if (image->red_mask == 0xf800)
    {
	r = (color->color.red & 0xff00) << 8;
	b = color->color.blue >> 8;
    }
    else
    {
	r = color->color.red >> 8;
	b = (color->color.blue & 0xff00) << 8;
    }
    src = (srca << 24) | r | g | b;

    width = xftg->metrics.width;
    height = xftg->metrics.height;

    x -= xftg->metrics.x;
    y -= xftg->metrics.y;

    dstLine = (CARD16 *) (image->data + image->bytes_per_line * y + (x << 1));
    dstStride = image->bytes_per_line >> 1;
    maskLine = (unsigned char *) xftg->bitmap;
    maskStride = (width + 3) & ~3;

    while (height--)
    {
	dst = dstLine;
	dstLine += dstStride;
	mask = maskLine;
	maskLine += maskStride;
	w = width;

	while (w--)
	{
	    m = *mask++;
	    if (m == 0xff)
	    {
		if (srca == 0xff)
		    d = src;
		else
		{
		    d = *dst;
		    d = fbOver24 (src, cvt0565to8888(d));
		}
		*dst = cvt8888to0565(d);
	    }
	    else if (m)
	    {
		d = *dst;
		d = fbOver24 (fbIn(src,m), cvt0565to8888(d));
		*dst = cvt8888to0565(d);
	    }
	    dst++;
	}
    }
}

static void
_XftSmoothGlyphGray555 (XImage		    *image,
			_Xconst XftGlyph    *xftg,
			int		    x,
			int		    y,
			_Xconst XftColor    *color)
{
    CARD32	src, srca;
    CARD32	r, g, b;
    CARD32	d;
    CARD16	*dstLine, *dst;
    CARD8	*maskLine, *mask, m;
    int		dstStride, maskStride;
    int		width, height;
    int		w;

    srca = color->color.alpha >> 8;

    /* This handles only RGB and BGR */
    g = (color->color.green & 0xff00);
    if (image->red_mask == 0xf800)
    {
	r = (color->color.red & 0xff00) << 8;
	b = color->color.blue >> 8;
    }
    else
    {
	r = color->color.red >> 8;
	b = (color->color.blue & 0xff00) << 8;
    }
    src = (srca << 24) | r | g | b;

    width = xftg->metrics.width;
    height = xftg->metrics.height;

    x -= xftg->metrics.x;
    y -= xftg->metrics.y;

    dstLine = (CARD16 *) (image->data + image->bytes_per_line * y + (x << 1));
    dstStride = image->bytes_per_line >> 1;
    maskLine = (unsigned char *) xftg->bitmap;
    maskStride = (width + 3) & ~3;

    while (height--)
    {
	dst = dstLine;
	dstLine += dstStride;
	mask = maskLine;
	maskLine += maskStride;
	w = width;

	while (w--)
	{
	    m = *mask++;
	    if (m == 0xff)
	    {
		if (srca == 0xff)
		    d = src;
		else
		{
		    d = *dst;
		    d = fbOver24 (src, cvt0555to8888(d));
		}
		*dst = cvt8888to0555(d);
	    }
	    else if (m)
	    {
		d = *dst;
		d = fbOver24 (fbIn(src,m), cvt0555to8888(d));
		*dst = cvt8888to0555(d);
	    }
	    dst++;
	}
    }
}

static void
_XftSmoothGlyphGray (XImage		*image,
		     _Xconst XftGlyph	*xftg,
		     int		x,
		     int		y,
		     _Xconst XftColor   *color)
{
    CARD32	    src, srca;
    int		    r_shift, r_len;
    int		    g_shift, g_len;
    int		    b_shift, b_len;
    CARD8	    *maskLine, *mask, m;
    int		    maskStride;
    CARD32	    d;
    unsigned long   pixel;
    int		    width, height;
    int		    w, tx;

    srca = color->color.alpha >> 8;
    src = (srca << 24 |
	   (color->color.red & 0xff00) << 8 |
	   (color->color.green & 0xff00) |
	   (color->color.blue) >> 8);
    x -= xftg->metrics.x;
    y -= xftg->metrics.y;
    width = xftg->metrics.width;
    height = xftg->metrics.height;

    maskLine = (unsigned char *) xftg->bitmap;
    maskStride = (width + 3) & ~3;

    _XftExamineBitfield (image->red_mask, &r_shift, &r_len);
    _XftExamineBitfield (image->green_mask, &g_shift, &g_len);
    _XftExamineBitfield (image->blue_mask, &b_shift, &b_len);
    while (height--)
    {
	mask = maskLine;
	maskLine += maskStride;
	w = width;
	tx = x;

	while (w--)
	{
	    m = *mask++;
	    if (m == 0xff)
	    {
		if (srca == 0xff)
		    d = src;
		else
		{
		    pixel = XGetPixel (image, tx, y);
		    d = (_XftGetField (pixel, r_shift, r_len) << 16 |
			 _XftGetField (pixel, g_shift, g_len) << 8 |
			 _XftGetField (pixel, b_shift, b_len));
		    d = fbOver24 (src, d);
		}
		pixel = (_XftPutField ((d >> 16) & 0xff, r_shift, r_len) |
			 _XftPutField ((d >>  8) & 0xff, g_shift, g_len) |
			 _XftPutField ((d      ) & 0xff, b_shift, b_len));
		XPutPixel (image, tx, y, pixel);
	    }
	    else if (m)
	    {
		pixel = XGetPixel (image, tx, y);
		d = (_XftGetField (pixel, r_shift, r_len) << 16 |
		     _XftGetField (pixel, g_shift, g_len) << 8 |
		     _XftGetField (pixel, b_shift, b_len));
		d = fbOver24 (fbIn(src,m), d);
		pixel = (_XftPutField ((d >> 16) & 0xff, r_shift, r_len) |
			 _XftPutField ((d >>  8) & 0xff, g_shift, g_len) |
			 _XftPutField ((d      ) & 0xff, b_shift, b_len));
		XPutPixel (image, tx, y, pixel);
	    }
	    tx++;
	}
	y++;
    }
}

static void
_XftSmoothGlyphRgba (XImage		*image,
		     _Xconst XftGlyph	*xftg,
		     int		x,
		     int		y,
		     _Xconst XftColor   *color)
{
    CARD32	    src, srca;
    int		    r_shift, r_len;
    int		    g_shift, g_len;
    int		    b_shift, b_len;
    CARD32	    *mask, ma;
    CARD32	    d;
    unsigned long   pixel;
    int		    width, height;
    int		    w, tx;

    srca = color->color.alpha >> 8;
    src = (srca << 24 |
	   (color->color.red & 0xff00) << 8 |
	   (color->color.green & 0xff00) |
	   (color->color.blue) >> 8);
    x -= xftg->metrics.x;
    y -= xftg->metrics.y;
    width = xftg->metrics.width;
    height = xftg->metrics.height;

    mask = (CARD32 *) xftg->bitmap;

    _XftExamineBitfield (image->red_mask, &r_shift, &r_len);
    _XftExamineBitfield (image->green_mask, &g_shift, &g_len);
    _XftExamineBitfield (image->blue_mask, &b_shift, &b_len);
    while (height--)
    {
	w = width;
	tx = x;

	while (w--)
	{
	    ma = *mask++;
	    if (ma == 0xffffffff)
	    {
		if (srca == 0xff)
		    d = src;
		else
		{
		    pixel = XGetPixel (image, tx, y);
		    d = (_XftGetField (pixel, r_shift, r_len) << 16 |
			 _XftGetField (pixel, g_shift, g_len) << 8 |
			 _XftGetField (pixel, b_shift, b_len));
		    d = fbOver24 (src, d);
		}
		pixel = (_XftPutField ((d >> 16) & 0xff, r_shift, r_len) |
			 _XftPutField ((d >>  8) & 0xff, g_shift, g_len) |
			 _XftPutField ((d      ) & 0xff, b_shift, b_len));
		XPutPixel (image, tx, y, pixel);
	    }
	    else if (ma)
	    {
		CARD32	m,n,o;
		pixel = XGetPixel (image, tx, y);
		d = (_XftGetField (pixel, r_shift, r_len) << 16 |
		     _XftGetField (pixel, g_shift, g_len) << 8 |
		     _XftGetField (pixel, b_shift, b_len));
#define XftInOverC(src,srca,msk,dst,i,result) { \
    CARD16  __a = XftGet8(msk,i); \
    CARD32  __t, __ta; \
    CARD32  __i; \
    __t = XftIntMult (XftGet8(src,i), __a,__i); \
    __ta = (CARD8) ~XftIntMult (srca, __a,__i); \
    __t = __t + XftIntMult(XftGet8(dst,i),__ta,__i); \
    __t = (CARD32) (CARD8) (__t | (-(__t >> 8))); \
    result = __t << (i); \
}
		XftInOverC(src,srca,ma,d,0,m);
		XftInOverC(src,srca,ma,d,8,n);
		XftInOverC(src,srca,ma,d,16,o);
		d = m | n | o;
		pixel = (_XftPutField ((d >> 16) & 0xff, r_shift, r_len) |
			 _XftPutField ((d >>  8) & 0xff, g_shift, g_len) |
			 _XftPutField ((d      ) & 0xff, b_shift, b_len));
		XPutPixel (image, tx, y, pixel);
	    }
	    tx++;
	}
	y++;
    }
}

static FcBool
_XftSmoothGlyphPossible (XftDraw *draw)
{
    if (!draw->visual)
	return FcFalse;
    if (draw->visual->class != TrueColor)
	return FcFalse;
    return FcTrue;
}

typedef	void (*XftSmoothGlyph) (XImage		    *image,
				_Xconst XftGlyph    *xftg,
				int		    x,
				int		    y,
				_Xconst XftColor    *color);

static XftSmoothGlyph
_XftSmoothGlyphFind (XftDraw *draw, XftFont *public)
{
    XftFontInt *font = (XftFontInt *) public;

    if (!font->info.antialias)
	return _XftSmoothGlyphMono;
    else switch (font->info.rgba) {
    case FC_RGBA_RGB:
    case FC_RGBA_BGR:
    case FC_RGBA_VRGB:
    case FC_RGBA_VBGR:
	return _XftSmoothGlyphRgba;
    default:
	switch (XftDrawBitsPerPixel (draw)) {
	case 32:
	    if ((draw->visual->red_mask   == 0xff0000 &&
		 draw->visual->green_mask == 0x00ff00 &&
		 draw->visual->blue_mask  == 0x0000ff) ||
		(draw->visual->red_mask   == 0x0000ff &&
		 draw->visual->green_mask == 0x00ff00 &&
		 draw->visual->blue_mask  == 0xff0000))
	    {
		return _XftSmoothGlyphGray8888;
	    }
	    break;
	case 16:
	    if ((draw->visual->red_mask   == 0xf800 &&
		 draw->visual->green_mask == 0x07e0 &&
		 draw->visual->blue_mask  == 0x001f) ||
		(draw->visual->red_mask   == 0x001f &&
		 draw->visual->green_mask == 0x07e0 &&
		 draw->visual->blue_mask  == 0xf800))
	    {
		return _XftSmoothGlyphGray565;
	    }
	    if ((draw->visual->red_mask   == 0x7c00 &&
		 draw->visual->green_mask == 0x03e0 &&
		 draw->visual->blue_mask  == 0x001f) ||
		(draw->visual->red_mask   == 0x001f &&
		 draw->visual->green_mask == 0x03e0 &&
		 draw->visual->blue_mask  == 0x7c00))
	    {
		return _XftSmoothGlyphGray555;
	    }
	    break;
	default:
	    break;
	}
	return _XftSmoothGlyphGray;
    }
}

static XftGlyph *
_XftGlyphDefault (Display *dpy, XftFont   *public)
{
    XftFontInt	    *font = (XftFontInt *) public;
    FT_UInt	    missing[XFT_NMISSING];
    int		    nmissing;
    FcBool	    glyphs_loaded = FcFalse;

    if (XftFontCheckGlyph (dpy, public, FcTrue, 0, missing, &nmissing))
	glyphs_loaded = FcTrue;
    if (nmissing)
	XftFontLoadGlyphs (dpy, public, glyphs_loaded, missing, nmissing);
    return font->glyphs[0];
}

static int XftGetImageErrorHandler (Display *dpy, XErrorEvent *error_event)
{
    return 0;
}

_X_HIDDEN void
XftGlyphCore (XftDraw		*draw,
	      _Xconst XftColor	*color,
	      XftFont		*public,
	      int		x,
	      int		y,
	      _Xconst FT_UInt	*glyphs,
	      int		nglyphs)
{
    Display	    *dpy = draw->dpy;
    XftFontInt	    *font = (XftFontInt *) public;
    XftGlyph	    *xftg;
    FT_UInt	    glyph;
    _Xconst FT_UInt *g;
    FT_UInt	    missing[XFT_NMISSING];
    FcBool	    glyphs_loaded;
    int		    nmissing;
    int		    n;
    XErrorHandler   prev_error;

    /*
     * Load missing glyphs
     */
    g = glyphs;
    n = nglyphs;
    nmissing = 0;
    glyphs_loaded = FcFalse;
    while (n--)
	if (XftFontCheckGlyph (dpy, public, FcTrue, *g++, missing, &nmissing))
	    glyphs_loaded = FcTrue;
    if (nmissing)
	XftFontLoadGlyphs (dpy, public, FcTrue, missing, nmissing);

    g = glyphs;
    n = nglyphs;
    if ((font->info.antialias || color->color.alpha != 0xffff) &&
	_XftSmoothGlyphPossible (draw))
    {
	XGlyphInfo	gi;
	XImage		*image;
        unsigned int    depth;
	int		ox, oy;
	XftSmoothGlyph	smooth = _XftSmoothGlyphFind (draw, public);

	XftGlyphExtents (dpy, public, glyphs, nglyphs, &gi);
	if (!gi.width || !gi.height)
	    goto bail1;
	ox = x - gi.x;
	oy = y - gi.y;
	/*
	 * Try to get bits directly from the drawable; if that fails,
	 * use a temporary pixmap.  When it does fail, assume it
	 * will probably fail for a while and keep using temporary
	 * pixmaps for a while to avoid double round trips.
	 */
	if (draw->core.use_pixmap == 0)
	{
	    prev_error = XSetErrorHandler (XftGetImageErrorHandler);
	    image = XGetImage (dpy, draw->drawable,
			       ox, oy,
			       gi.width, gi.height, AllPlanes,
			       ZPixmap);
	    XSetErrorHandler (prev_error);
	    if (!image)
		draw->core.use_pixmap = XFT_ASSUME_PIXMAP;
	}
	else
	{
	    draw->core.use_pixmap--;
	    image = NULL;
	}
	if (!image && (depth = XftDrawDepth (draw)))
	{
	    Pixmap	pix;
	    GC		gc;
	    XGCValues	gcv;

	    pix = XCreatePixmap (dpy, draw->drawable,
				 gi.width, gi.height, depth);
	    gcv.graphics_exposures = False;
	    gc = XCreateGC (dpy, pix, GCGraphicsExposures, &gcv);
	    XCopyArea (dpy, draw->drawable, pix, gc, ox, oy,
		       gi.width, gi.height, 0, 0);
	    XFreeGC (dpy, gc);
	    image = XGetImage (dpy, pix, 0, 0, gi.width, gi.height, AllPlanes,
			       ZPixmap);
	    XFreePixmap (dpy, pix);
	}
	if (!image)
	    goto bail1;
	image->red_mask = draw->visual->red_mask;
	image->green_mask = draw->visual->green_mask;
	image->blue_mask = draw->visual->blue_mask;
	if (image->byte_order != XftNativeByteOrder ())
	    XftSwapImage (image);
	while (n--)
	{
	    glyph = *g++;
	    if (glyph >= font->num_glyphs || !(xftg = font->glyphs[glyph]))
		xftg = _XftGlyphDefault (dpy, public);
	    if (xftg)
	    {
		(*smooth) (image, xftg, x - ox, y - oy, color);
		x += xftg->metrics.xOff;
		y += xftg->metrics.yOff;
	    }
	}
	if (image->byte_order != XftNativeByteOrder ())
	    XftSwapImage (image);
	XPutImage (dpy, draw->drawable, draw->core.gc, image, 0, 0, ox, oy,
		   gi.width, gi.height);
	XDestroyImage (image);
    }
    else
    {
	XftSharpGlyph	sharp = _XftSharpGlyphFind (draw, public);
	while (n--)
	{
	    glyph = *g++;
	    if (glyph >= font->num_glyphs || !(xftg = font->glyphs[glyph]))
		xftg = _XftGlyphDefault (dpy, public);
	    if (xftg)
	    {
		(*sharp) (draw, xftg, x, y);
		x += xftg->metrics.xOff;
		y += xftg->metrics.yOff;
	    }
	}
    }
bail1:
    if (glyphs_loaded)
	_XftFontManageMemory (dpy, public);
}

#define NUM_LOCAL   1024

_X_HIDDEN void
XftGlyphSpecCore (XftDraw		*draw,
		  _Xconst XftColor	*color,
		  XftFont		*public,
		  _Xconst XftGlyphSpec	*glyphs,
		  int			nglyphs)
{
    Display	    *dpy = draw->dpy;
    XftFontInt	    *font = (XftFontInt *) public;
    XftGlyph	    *xftg;
    FT_UInt	    missing[XFT_NMISSING];
    FcBool	    glyphs_loaded;
    int		    nmissing;
    int		    i;
    XErrorHandler   prev_error;
    int		    x1, y1, x2, y2;

    /*
     * Load missing glyphs
     */
    glyphs_loaded = FcFalse;
    x1 = y1 = x2 = y2 = 0;
    for (i = 0; i < nglyphs; i++)
    {
	XGlyphInfo	gi;
	int		g_x1, g_x2, g_y1, g_y2;

	nmissing = 0;
	if (XftFontCheckGlyph (dpy, public, FcTrue, glyphs[i].glyph, missing, &nmissing))
	    glyphs_loaded = FcTrue;
	if (nmissing)
	    XftFontLoadGlyphs (dpy, public, FcTrue, missing, nmissing);

	XftGlyphExtents (dpy, public, &glyphs[i].glyph, 1, &gi);
	g_x1 = glyphs[i].x - gi.x;
	g_y1 = glyphs[i].y - gi.y;
	g_x2 = g_x1 + gi.width;
	g_y2 = g_y1 + gi.height;
	if (i)
	{
	    if (g_x1 < x1)
		x1 = g_x1;
	    if (g_y1 < y1)
		y1 = g_y1;
	    if (g_x2 > x2)
		x2 = g_x2;
	    if (g_y2 > y2)
		y2 = g_y2;
	}
	else
	{
	    x1 = g_x1;
	    y1 = g_y1;
	    x2 = g_x2;
	    y2 = g_y2;
	}
    }

    if (x1 == x2 || y1 == y2)
	goto bail1;

    if ((font->info.antialias || color->color.alpha != 0xffff) &&
	_XftSmoothGlyphPossible (draw))
    {
	XImage		*image;
        unsigned int    depth;
	int		width = x2 - x1, height = y2 - y1;
	XftSmoothGlyph	smooth = _XftSmoothGlyphFind (draw, public);

	/*
	 * Try to get bits directly from the drawable; if that fails,
	 * use a temporary pixmap.  When it does fail, assume it
	 * will probably fail for a while and keep using temporary
	 * pixmaps for a while to avoid double round trips.
	 */
	if (draw->core.use_pixmap == 0)
	{
	    prev_error = XSetErrorHandler (XftGetImageErrorHandler);
	    image = XGetImage (dpy, draw->drawable,
			       x1, y1,
			       width, height, AllPlanes,
			       ZPixmap);
	    XSetErrorHandler (prev_error);
	    if (!image)
		draw->core.use_pixmap = XFT_ASSUME_PIXMAP;
	}
	else
	{
	    draw->core.use_pixmap--;
	    image = NULL;
	}
	if (!image && (depth = XftDrawDepth (draw)))
	{
	    Pixmap	pix;
	    GC		gc;
	    XGCValues	gcv;

	    pix = XCreatePixmap (dpy, draw->drawable,
				 width, height, depth);
	    gcv.graphics_exposures = False;
	    gc = XCreateGC (dpy, pix, GCGraphicsExposures, &gcv);
	    XCopyArea (dpy, draw->drawable, pix, gc, x1, y1,
		       width, height, 0, 0);
	    XFreeGC (dpy, gc);
	    image = XGetImage (dpy, pix, 0, 0, width, height, AllPlanes,
			       ZPixmap);
	    XFreePixmap (dpy, pix);
	}
	if (!image)
	    goto bail1;
	image->red_mask = draw->visual->red_mask;
	image->green_mask = draw->visual->green_mask;
	image->blue_mask = draw->visual->blue_mask;
	if (image->byte_order != XftNativeByteOrder ())
	    XftSwapImage (image);
	for (i = 0; i < nglyphs; i++)
	{
	    FT_UInt glyph = glyphs[i].glyph;
	    if (glyph >= font->num_glyphs || !(xftg = font->glyphs[glyph]))
		xftg = _XftGlyphDefault (dpy, public);
	    if (xftg)
	    {
		(*smooth) (image, xftg, glyphs[i].x - x1,
			   glyphs[i].y - y1, color);
	    }
	}
	if (image->byte_order != XftNativeByteOrder ())
	    XftSwapImage (image);
	XPutImage (dpy, draw->drawable, draw->core.gc, image, 0, 0, x1, y1,
		   width, height);
	XDestroyImage (image);
    }
    else
    {
	XftSharpGlyph	sharp = _XftSharpGlyphFind (draw, public);
	for (i = 0; i < nglyphs; i++)
	{
	    FT_UInt glyph = glyphs[i].glyph;
	    if (glyph >= font->num_glyphs || !(xftg = font->glyphs[glyph]))
		xftg = _XftGlyphDefault (dpy, public);
	    if (xftg)
		(*sharp) (draw, xftg, glyphs[i].x, glyphs[i].y);
	}
    }
bail1:
    if (glyphs_loaded)
	_XftFontManageMemory (dpy, public);
}

_X_HIDDEN void
XftGlyphFontSpecCore (XftDraw			*draw,
		      _Xconst XftColor		*color,
		      _Xconst XftGlyphFontSpec	*glyphs,
		      int			nglyphs)
{
    Display	    *dpy = draw->dpy;
    XftGlyph	    *xftg;
    FT_UInt	    missing[XFT_NMISSING];
    FcBool	    glyphs_loaded;
    int		    nmissing;
    int		    i;
    XErrorHandler   prev_error;
    int		    x1, y1, x2, y2;

    /*
     * Load missing glyphs
     */
    glyphs_loaded = FcFalse;
    x1 = y1 = x2 = y2 = 0;
    for (i = 0; i < nglyphs; i++)
    {
	XftFont		*public = glyphs[i].font;
	XGlyphInfo	gi;
	int		g_x1, g_x2, g_y1, g_y2;

	nmissing = 0;
	if (XftFontCheckGlyph (dpy, public, FcTrue, glyphs[i].glyph, missing, &nmissing))
	    glyphs_loaded = FcTrue;
	if (nmissing)
	    XftFontLoadGlyphs (dpy, public, FcTrue, missing, nmissing);

	XftGlyphExtents (dpy, public, &glyphs[i].glyph, 1, &gi);
	g_x1 = glyphs[i].x - gi.x;
	g_y1 = glyphs[i].y - gi.y;
	g_x2 = g_x1 + gi.width;
	g_y2 = g_y1 + gi.height;
	if (i)
	{
	    if (g_x1 < x1)
	    {
		if (g_x1 < 0)
		{
		    /* do nothing if the given glyphs are out of range */
		    short t = glyphs[i-1].font->max_advance_width
			+ glyphs[i-1].x;
		    if (t < 0 && glyphs[i-1].x > 0)
			goto bail1;
		}
		x1 = g_x1;
	    }
	    if (g_y1 < y1)
		y1 = g_y1;
	    if (g_x2 > x2)
		x2 = g_x2;
	    if (g_y2 > y2)
		y2 = g_y2;
	}
	else
	{
	    x1 = g_x1;
	    y1 = g_y1;
	    x2 = g_x2;
	    y2 = g_y2;
	}
    }

    if (x1 == x2 || y1 == y2)
	goto bail1;

    for (i = 0; i < nglyphs; i++)
	if (((XftFontInt *) glyphs[i].font)->info.antialias)
	    break;

    if ((i != nglyphs || color->color.alpha != 0xffff) &&
	_XftSmoothGlyphPossible (draw))
    {
	XImage		*image;
        unsigned int    depth;
	int		width = x2 - x1, height = y2 - y1;

	/*
	 * Try to get bits directly from the drawable; if that fails,
	 * use a temporary pixmap.  When it does fail, assume it
	 * will probably fail for a while and keep using temporary
	 * pixmaps for a while to avoid double round trips.
	 */
	if (draw->core.use_pixmap == 0)
	{
	    prev_error = XSetErrorHandler (XftGetImageErrorHandler);
	    image = XGetImage (dpy, draw->drawable,
			       x1, y1,
			       width, height, AllPlanes,
			       ZPixmap);
	    XSetErrorHandler (prev_error);
	    if (!image)
		draw->core.use_pixmap = XFT_ASSUME_PIXMAP;
	}
	else
	{
	    draw->core.use_pixmap--;
	    image = NULL;
	}
	if (!image && (depth = XftDrawDepth (draw)))
	{
	    Pixmap	pix;
	    GC		gc;
	    XGCValues	gcv;

	    pix = XCreatePixmap (dpy, draw->drawable,
				 width, height, depth);
	    gcv.graphics_exposures = False;
	    gc = XCreateGC (dpy, pix, GCGraphicsExposures, &gcv);
	    XCopyArea (dpy, draw->drawable, pix, gc, x1, y1,
		       width, height, 0, 0);
	    XFreeGC (dpy, gc);
	    image = XGetImage (dpy, pix, 0, 0, width, height, AllPlanes,
			       ZPixmap);
	    XFreePixmap (dpy, pix);
	}
	if (!image)
	    goto bail1;
	image->red_mask = draw->visual->red_mask;
	image->green_mask = draw->visual->green_mask;
	image->blue_mask = draw->visual->blue_mask;
	if (image->byte_order != XftNativeByteOrder ())
	    XftSwapImage (image);
	for (i = 0; i < nglyphs; i++)
	{
	    XftFont	    *public = glyphs[i].font;
	    XftFontInt	    *font = (XftFontInt *) public;
	    XftSmoothGlyph  smooth = _XftSmoothGlyphFind (draw, public);
	    FT_UInt	    glyph = glyphs[i].glyph;

	    if (glyph >= font->num_glyphs || !(xftg = font->glyphs[glyph]))
		xftg = _XftGlyphDefault (dpy, public);
	    if (xftg)
	    {
		(*smooth) (image, xftg, glyphs[i].x - x1,
			   glyphs[i].y - y1, color);
	    }
	}
	if (image->byte_order != XftNativeByteOrder ())
	    XftSwapImage (image);
	XPutImage (dpy, draw->drawable, draw->core.gc, image, 0, 0, x1, y1,
		   width, height);
	XDestroyImage (image);
    }
    else
    {
	for (i = 0; i < nglyphs; i++)
	{
	    XftFont		*public = glyphs[i].font;
	    XftFontInt		*font = (XftFontInt *) public;
	    XftSharpGlyph	sharp = _XftSharpGlyphFind (draw, public);
	    FT_UInt		glyph = glyphs[i].glyph;

	    if (glyph >= font->num_glyphs || !(xftg = font->glyphs[glyph]))
		xftg = _XftGlyphDefault (dpy, public);
	    if (xftg)
		(*sharp) (draw, xftg, glyphs[i].x, glyphs[i].y);
	}
    }
bail1:
    if (glyphs_loaded)
	for (i = 0; i < nglyphs; i++)
	    _XftFontManageMemory (dpy, glyphs[i].font);
}
