/*
 * Copyright © 2022 Thomas E. Dickey
 * 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 the above copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The above copyright holders make no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) 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"
#include FT_OUTLINE_H
#include FT_LCD_FILTER_H

#include FT_SYNTHESIS_H

#include FT_GLYPH_H

typedef double m3x3[3][3];

static void
m3x3_uniform(m3x3 m)
{
    m[0][0] = m[1][1] = m[2][2] = 1.0;
    m[0][1] = m[1][0] = m[0][2] = m[1][2] = m[2][0] = m[2][1] = 0;
}

static void
m3x3_transform(FT_Vector *v, m3x3 m)
{
    double x, y;

    x = (double)v->x;
    y = (double)v->y;
    v->x = (FT_Pos)(x * m[0][0] + y * m[0][1] + m[0][2] + 0.5);
    v->y = (FT_Pos)(x * m[1][0] + y * m[1][1] + m[1][2] + 0.5);
}

static void
m3x3_invert(m3x3 m, m3x3 mi)
{
    double det;

    det  = m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]);
    det -= m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]);
    det += m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
    det  = 1.0 / det;
    mi[0][0] = det * (m[1][1] * m[2][2] - m[1][2] * m[2][1]);
    mi[1][0] = det * (m[1][2] * m[2][0] - m[1][0] * m[2][2]);
    mi[2][0] = det * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
    mi[0][1] = det * (m[0][2] * m[2][1] - m[0][1] * m[2][2]);
    mi[1][1] = det * (m[0][0] * m[2][2] - m[0][2] * m[2][0]);
    mi[2][1] = det * (m[0][1] * m[2][0] - m[0][0] * m[2][1]);
    mi[0][2] = det * (m[0][1] * m[1][2] - m[0][2] * m[1][1]);
    mi[1][2] = det * (m[0][2] * m[1][0] - m[0][0] * m[1][2]);
    mi[2][2] = det * (m[0][0] * m[1][1] - m[0][1] * m[1][0]);
}

/*
 * Validate the memory info for a font
 */

static void
_XftFontValidateMemory (Display *dpy _X_UNUSED, XftFont *public)
{
    XftFontInt	    *font = (XftFontInt *) public;
    unsigned long   glyph_memory;
    FT_UInt	    glyphindex;
    XftGlyph	    *xftg;

    glyph_memory = 0;
    for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++)
    {
	xftg = font->glyphs[glyphindex];
	if (xftg)
	{
	    glyph_memory += xftg->glyph_memory;
	}
    }
    if (glyph_memory != font->glyph_memory)
	printf ("Font glyph cache incorrect has %lu bytes, should have %lu\n",
		font->glyph_memory, glyph_memory);
}

/*
 * Validate the glyph usage-links for a font.
 */
static void
_XftValidateGlyphUsage(XftFontInt *font)
{
    if (font->newest != FT_UINT_MAX) {
	FT_UInt forward;
	FT_UInt reverse;
	FT_UInt next;
	XftGlyphUsage *x1st = (XftGlyphUsage *) font->glyphs[font->newest];
	XftGlyphUsage *xuse = x1st;
	for (forward = 1,
	     next = x1st->newer;
	     xuse != NULL &&
	     next != font->newest;
	     next = xuse->newer) {
	    if (next >= font->num_glyphs) {
		printf("Xft: out of range; %d\n", next);
		break;
	    }
	    if (++forward > font->total_inuse) {
		printf("Xft: too many in-use glyphs (%d vs %d)\n",
		       forward, font->total_inuse);
		if (forward > font->total_inuse + 10)
		    break;
	    }
	    xuse = (XftGlyphUsage *) font->glyphs[next];
	}
	if (forward < font->total_inuse) {
	    printf("Xft: too few in-use glyphs (%u vs %d)\n",
		   forward, font->total_inuse);
	}
	for (reverse = 1,
	     next = x1st->older;
	     xuse != NULL &&
	     next != font->newest;
	     next = xuse->older) {
	    if (next >= font->num_glyphs) {
		printf("Xft out of range; %d\n", next);
		break;
	    }
	    if (++reverse > font->total_inuse) {
		printf("Xft: too many in-use glyphs (%d vs %d)\n",
		       reverse, font->total_inuse);
		if (reverse > font->total_inuse + 10)
		    break;
	    }
	    xuse = (XftGlyphUsage *) font->glyphs[next];
	}
	if (reverse < font->total_inuse) {
	    printf("Xft: too few in-use glyphs (%u vs %d)\n",
		   reverse, font->total_inuse);
	}
	if (forward != reverse) {
	    printf("Xft: forward %d vs reverse %d\n",
		   forward, reverse);
	    exit(1);
	}
    }
}

/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot
 * into a different format. For example, we want to convert a
 * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit
 * ARGB or ABGR bitmap.
 *
 * this function prepares a target descriptor for this operation.
 *
 * input :: target bitmap descriptor. The function will set its
 *          'width', 'rows' and 'pitch' fields, and only these
 *
 * slot  :: the glyph slot containing the source bitmap. this
 *          function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP
 *
 * mode  :: the requested final rendering mode. supported values are
 *          MONO, NORMAL (i.e. gray), LCD and LCD_V
 *
 * the function returns the size in bytes of the corresponding buffer,
 * it's up to the caller to allocate the corresponding memory block
 * before calling _fill_xrender_bitmap
 *
 * it also returns -1 in case of error (e.g. incompatible arguments,
 * like trying to convert a gray bitmap into a monochrome one)
 */
static int
_compute_xrender_bitmap_size( FT_Bitmap*	target,
			      FT_GlyphSlot	slot,
			      FT_Render_Mode	mode,
			      FT_Matrix*	matrix,
			      m3x3		m )
{
    FT_Bitmap*	ftbit;
    int		width, height, pitch;

    if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
	return -1;

    /* compute the size of the final bitmap */
    ftbit = &slot->bitmap;

    width = (int)ftbit->width;
    height = (int)ftbit->rows;

    if ( matrix && mode == FT_RENDER_MODE_NORMAL )
    {
	FT_Matrix mirror, inverse;
	FT_Vector vector;
	int xc, yc;
	int left, right, top, bottom;

	left = right = top = bottom = 0;
	for (xc = 0; xc <= 1; xc++) {
	    for (yc = 0; yc <= 1; yc++) {
		vector.x = xc * width;
		vector.y = yc * height;
		FT_Vector_Transform(&vector, matrix);
		if (xc == 0 && yc == 0) {
		    left = right = (int)vector.x;
		    top = bottom = (int)vector.y;
		} else {
		    if (left   > vector.x) left   = (int)vector.x;
		    if (right  < vector.x) right  = (int)vector.x;
		    if (bottom > vector.y) bottom = (int)vector.y;
		    if (top    < vector.y) top    = (int)vector.y;
		}
	    }
	}
	width = (int)(right - left);
	height = (int)(top - bottom);

	mirror.xx = + 0x10000;
	mirror.yy = - 0x10000;
	mirror.xy = mirror.yx = 0;
	inverse = *matrix;
	FT_Matrix_Multiply(&mirror, &inverse);
	FT_Matrix_Invert(&inverse);
	FT_Matrix_Multiply(&mirror, &inverse);

	vector.x = vector.y = 0;
	FT_Vector_Transform(&vector, &inverse);
	left = (int)vector.x;
	bottom = (int)vector.y;
	vector.x = width;
	vector.y = height;
	FT_Vector_Transform(&vector, &inverse);
	right = (int)vector.x;
	top = (int)vector.y;
	left = (right - left) - (int)ftbit->width;
	bottom = (top - bottom) - (int)ftbit->rows;

	m[0][0] = (double)inverse.xx / 0x10000;
	m[0][1] = (double)inverse.xy / 0x10000;
	m[1][0] = (double)inverse.yx / 0x10000;
	m[1][1] = (double)inverse.yy / 0x10000;
	m[0][2] = (double)-left / 2;
	m[1][2] = (double)-bottom / 2;
	m[2][0] = m[2][1] = 0.0;
	m[2][2] = 1.0;
    }
    pitch = (width+3) & ~3;

    switch ( ftbit->pixel_mode )
    {
    case FT_PIXEL_MODE_MONO:
	if ( mode == FT_RENDER_MODE_MONO )
	{
	    pitch = (((width+31) & ~31) >> 3);
	    break;
	}
	/* fall-through */

    case FT_PIXEL_MODE_GRAY:
	if ( mode == FT_RENDER_MODE_LCD ||
	     mode == FT_RENDER_MODE_LCD_V )
	{
	    /* each pixel is replicated into a 32-bit ARGB value */
	    pitch = width*4;
	}
	break;

    case FT_PIXEL_MODE_BGRA:
	pitch = width * 4;
	break;

    case FT_PIXEL_MODE_LCD:
	if ( mode != FT_RENDER_MODE_LCD )
	    return -1;

	/* horz pixel triplets are packed into 32-bit ARGB values */
	width /= 3;
	pitch = width*4;
	break;

    case FT_PIXEL_MODE_LCD_V:
	if ( mode != FT_RENDER_MODE_LCD_V )
	    return -1;

	/* vert pixel triplets are packed into 32-bit ARGB values */
	height /= 3;
	pitch = width*4;
	break;

    default:  /* unsupported source format */
	return -1;
    }

    target->width = (unsigned)width;
    target->rows = (unsigned)height;
    target->pitch = pitch;
    target->buffer = NULL;

    return pitch * height;
}

/* this functions converts the glyph bitmap found in a FT_GlyphSlot
 * into a different format while scaling by applying the given matrix
 * (see _compute_xrender_bitmap_size)
 *
 * you should call this function after _compute_xrender_bitmap_size
 *
 * target :: target bitmap descriptor. Note that its 'buffer' pointer
 *           must point to memory allocated by the caller
 *
 * source :: the source bitmap descriptor
 *
 * matrix :: the scaling matrix to apply
 */
static void
_scaled_fill_xrender_bitmap( FT_Bitmap*	target,
			     FT_Bitmap* source,
			     m3x3 m )
{
    unsigned char*	src_buf	  = source->buffer;
    unsigned char*	dst_line  = target->buffer;
    int			src_pitch = source->pitch;
    int			width     = (int) target->width;
    int			height    = (int) target->rows;
    int			pitch     = target->pitch;
    int			i, x, y;
    FT_Vector		vector, vector0;
    int			sampling_width;
    int			sampling_height;
    int			sample_count;

    if ( src_pitch < 0 )
	src_buf -= ((unsigned) src_pitch * (source->rows - 1));

    /* compute how many source pixels a target pixel spans */
    vector.x = 1;
    vector.y = 1;
    m3x3_transform(&vector, m);
    vector0.x = 0;
    vector0.y = 0;
    m3x3_transform(&vector0, m);
    sampling_width = (int) ((vector.x - vector0.x) / 2);
    sampling_height = (int) ((vector.y - vector0.y) / 2);
    if (sampling_width < 0) sampling_width = -sampling_width;
    if (sampling_height < 0) sampling_height = -sampling_height;
    sample_count = (2 * sampling_width + 1) * (2 * sampling_height + 1);

    for	( y = height; y > 0; y--, dst_line += pitch )
    {
	for ( x	= 0; x < width; x++ )
	{
	    unsigned char* src;

	    /* compute target pixel location in source space */
	    vector.x = x;
	    vector.y = height - y;
	    m3x3_transform(&vector, m);

	    if (source->pixel_mode == FT_PIXEL_MODE_BGRA)
	    {
		if (vector.x < -sampling_width
		 || vector.x > (source->width + (unsigned) sampling_width))
		    continue;
		if (vector.y < -sampling_height
		 || vector.y > (source->rows + (unsigned) sampling_height))
		    continue;
	    }
	    else
	    {
		if (vector.x < 0 || vector.x >= source->width)
		    continue;
		if (vector.y < 0 || vector.y >= source->rows)
		    continue;
	    }

	    switch ( source->pixel_mode )
	    {
	    case FT_PIXEL_MODE_MONO: /* convert mono to 8-bit gray, scale using nearest pixel */
		src = src_buf + (vector.y * src_pitch);
		if ( src[(vector.x >> 3)] & (0x80 >> (vector.x & 7)) )
		    dst_line[x] = 0xff;
		break;

	    case FT_PIXEL_MODE_GRAY: /* scale using nearest pixel */
		src = src_buf + (vector.y * src_pitch);
		dst_line[x] = src[vector.x];
		break;

	    case FT_PIXEL_MODE_BGRA: /* scale by averaging all relevant source pixels, keep BGRA format */
	    {
		int sample_x, sample_y;
		int bgra[4] = { 0, 0, 0, 0 };

		for (sample_y = - sampling_height; sample_y < sampling_height + 1; ++sample_y)
		{
		    int src_y = (int) (vector.y + sample_y);

		    if (src_y < 0 || (FT_Pos) src_y >= source->rows)
			continue;
		    src = src_buf + (src_y * src_pitch);
		    for (sample_x = - sampling_width; sample_x < sampling_width + 1; ++sample_x)
		    {
			int src_x = (int) (vector.x + sample_x);

			if (src_x < 0 || (FT_Pos) src_x >= source->width)
			    continue;
			for (i = 0; i < 4; ++i)
			    bgra[i] += src[src_x * 4 + i];
		    }
		}

		for (i = 0; i < 4; ++i)
		    dst_line[4 * x + i] = (unsigned char) (bgra[i] / sample_count);
		break;
	    }
	    }
	}
    }
}

/* this functions converts the glyph bitmap found in a FT_GlyphSlot
 * into a different format (see _compute_xrender_bitmap_size)
 *
 * you should call this function after _compute_xrender_bitmap_size
 *
 * target :: target bitmap descriptor. Note that its 'buffer' pointer
 *           must point to memory allocated by the caller
 *
 * slot   :: the glyph slot containing the source bitmap
 *
 * mode   :: the requested final rendering mode
 *
 * bgr    :: boolean, set if BGR or VBGR pixel ordering is needed
 */
static void
_fill_xrender_bitmap( FT_Bitmap*	target,
		      FT_GlyphSlot	slot,
		      FT_Render_Mode	mode,
		      int		bgr )
{
    FT_Bitmap*   ftbit = &slot->bitmap;

    {
	unsigned char*	srcLine	= ftbit->buffer;
	unsigned char*	dstLine	= target->buffer;
	int		src_pitch = ftbit->pitch;
	int		width = (int)target->width;
	int		height = (int)target->rows;
	int		pitch = target->pitch;
	int		subpixel;
	int		h;

	subpixel = ( mode == FT_RENDER_MODE_LCD ||
		     mode == FT_RENDER_MODE_LCD_V );

	if ( src_pitch < 0 )
	    srcLine -= ((unsigned)src_pitch * (ftbit->rows-1));

	switch ( ftbit->pixel_mode )
	{
	case FT_PIXEL_MODE_MONO:
	    if ( subpixel )  /* convert mono to ARGB32 values */
	    {
		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		{
		    int x;

		    for ( x = 0; x < width; x++ )
		    {
			if ( srcLine[(x >> 3)] & (0x80 >> (x & 7)) )
			    ((unsigned int*)dstLine)[x] = 0xffffffffU;
		    }
		}
	    }
	    else if ( mode == FT_RENDER_MODE_NORMAL )  /* convert mono to 8-bit gray */
	    {
		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		{
		    int x;

		    for ( x = 0; x < width; x++ )
		    {
			if ( srcLine[(x >> 3)] & (0x80 >> (x & 7)) )
			    dstLine[x] = 0xff;
		    }
		}
	    }
	    else  /* copy mono to mono */
	    {
		int bytes = (width+7) >> 3;

		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		    memcpy( dstLine, srcLine, (size_t)bytes );
	    }
	    break;

	case FT_PIXEL_MODE_GRAY:
	    if ( subpixel )  /* convert gray to ARGB32 values */
	    {
		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		{
		    int		   x;
		    unsigned int*  dst = (unsigned int*)dstLine;

		    for ( x = 0; x < width; x++ )
		    {
			unsigned int pix = srcLine[x];

			pix |= (pix << 8);
			pix |= (pix << 16);

			dst[x] = pix;
		    }
		}
	    }
	    else  /* copy gray into gray */
	    {
		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		    memcpy( dstLine, srcLine, (size_t)width );
	    }
	    break;

	case FT_PIXEL_MODE_BGRA: /* Preserve BGRA format */
	    for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		memcpy( dstLine, srcLine, (size_t) width * 4 );
	    break;

	case FT_PIXEL_MODE_LCD:
	    if ( !bgr )
	    {
		/* convert horizontal RGB into ARGB32 */
		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		{
		    int		   x;
		    unsigned char* src = srcLine;
		    unsigned int*  dst = (unsigned int*)dstLine;

		    for ( x = 0; x < width; x++, src += 3 )
		    {
			unsigned int pix;

			pix = ((unsigned int)src[0] << 16) |
			      ((unsigned int)src[1] <<  8) |
			      ((unsigned int)src[2]      ) |
			      ((unsigned int)src[1] << 24) ;

			dst[x] = pix;
		    }
		}
	    }
	    else
	    {
		/* convert horizontal BGR into ARGB32 */
		for ( h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch )
		{
		    int		   x;
		    unsigned char* src = srcLine;
		    unsigned int*  dst = (unsigned int*)dstLine;

		    for ( x = 0; x < width; x++, src += 3 )
		    {
			unsigned int pix;

			pix = ((unsigned int)src[2] << 16) |
			      ((unsigned int)src[1] <<  8) |
			      ((unsigned int)src[0]      ) |
			      ((unsigned int)src[1] << 24) ;

			dst[x] = pix;
		    }
		}
	    }
	    break;

	default:  /* FT_PIXEL_MODE_LCD_V */
	    /* convert vertical RGB into ARGB32 */
	    if ( !bgr )
	    {
		for ( h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch )
		{
		    int		   x;
		    unsigned char* src = srcLine;
		    unsigned int*  dst = (unsigned int*)dstLine;

		    for ( x = 0; x < width; x++, src += 1 )
		    {
			unsigned int  pix;

			pix = ((unsigned int)src[0]           << 16) |
			      ((unsigned int)src[src_pitch]   <<  8) |
			      ((unsigned int)src[src_pitch*2]      ) |
			      ((unsigned int)src[src_pitch]   << 24) ;

			dst[x] = pix;
		    }
		}
	    }
	    else
	    {
	    for ( h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch )
		{
		    int		   x;
		    unsigned char* src = srcLine;
		    unsigned int*  dst = (unsigned int*)dstLine;

		    for ( x = 0; x < width; x++, src += 1 )
		    {
			unsigned int  pix;

			pix = ((unsigned int)src[src_pitch*2] << 16) |
			      ((unsigned int)src[src_pitch]   <<  8) |
			      ((unsigned int)src[0]                ) |
			      ((unsigned int)src[src_pitch]   << 24) ;

			dst[x] = pix;
		    }
		}
	    }
	}
    }
}

_X_EXPORT void
XftFontLoadGlyphs (Display	    *dpy,
		   XftFont	    *pub,
		   FcBool	    need_bitmaps,
		   _Xconst FT_UInt  *glyphs,
		   int		    nglyph)
{
    XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, True);
    XftFontInt	    *font = (XftFontInt *) pub;
    FT_Error	    error;
    FT_UInt	    glyphindex;
    FT_GlyphSlot    glyphslot;
    XftGlyph	    *xftg;
    Glyph	    glyph;
    unsigned char   bufLocal[4096];
    unsigned char   *bufBitmap = bufLocal;
    int		    bufSize = sizeof (bufLocal);
    int		    size;
    int		    width;
    int		    height;
    int		    left, right, top, bottom;
    FT_Bitmap*	    ftbit;
    FT_Bitmap	    local;
    FT_Vector	    vector;
    m3x3	    m;
    FT_Face	    face;
    FT_Render_Mode  mode = FT_RENDER_MODE_MONO;
    FcBool	    transform;
    FcBool	    glyph_transform;

    if (!info)
	return;

    face = XftLockFace (&font->public);

    if (!face)
	return;

    if (font->info.color)
	mode = FT_RENDER_MODE_NORMAL;
    if (font->info.antialias)
    {
	switch (font->info.rgba) {
	case FC_RGBA_RGB:
	case FC_RGBA_BGR:
	    mode = FT_RENDER_MODE_LCD;
	    break;
	case FC_RGBA_VRGB:
	case FC_RGBA_VBGR:
	    mode = FT_RENDER_MODE_LCD_V;
	    break;
	default:
	    mode = FT_RENDER_MODE_NORMAL;
	}
    }

    transform = font->info.transform && mode != FT_RENDER_MODE_MONO;

    while (nglyph--)
    {
	glyphindex = *glyphs++;
	xftg = font->glyphs[glyphindex];
	if (!xftg)
	    continue;

	if (XftDebug() & XFT_DBG_CACHE)
	    _XftFontValidateMemory (dpy, pub);
	/*
	 * Check to see if this glyph has just been loaded,
	 * this happens when drawing the same glyph twice
	 * in a single string
	 */
	if (xftg->glyph_memory)
	    continue;

	FT_Library_SetLcdFilter( _XftFTlibrary, font->info.lcd_filter);

	error = FT_Load_Glyph (face, glyphindex, font->info.load_flags);
	if (error)
	{
	    /*
	     * If anti-aliasing or transforming glyphs and
	     * no outline version exists, fallback to the
	     * bitmap and let things look bad instead of
	     * missing the glyph
	     */
	    if (font->info.load_flags & FT_LOAD_NO_BITMAP)
		error = FT_Load_Glyph (face, glyphindex,
				       font->info.load_flags & ~FT_LOAD_NO_BITMAP);
	    if (error)
		continue;
	}

#define FLOOR(x)    ((x) & -64)
#define CEIL(x)	    (((x)+63) & -64)
#define TRUNC(x)    ((x) >> 6)
#define ROUND(x)    (((x)+32) & -64)

	glyphslot = face->glyph;

	/*
	 * Embolden if required
	 */
	if (font->info.embolden) FT_GlyphSlot_Embolden(glyphslot);

	/*
	 * Compute glyph metrics from FreeType information
	 */
	if (transform)
	{
	    /*
	     * calculate the true width by transforming all four corners.
	     */
	    int xc, yc;
	    left = right = top = bottom = 0;
	    for (xc = 0; xc <= 1; xc++) {
		for (yc = 0; yc <= 1; yc++) {
		    vector.x = glyphslot->metrics.horiBearingX + xc * glyphslot->metrics.width;
		    vector.y = glyphslot->metrics.horiBearingY - yc * glyphslot->metrics.height;
		    FT_Vector_Transform(&vector, &font->info.matrix);
		    if (XftDebug() & XFT_DBG_GLYPH)
			printf("Trans %d %d: %d %d\n", (int) xc, (int) yc,
			       (int) vector.x, (int) vector.y);
		    if (xc == 0 && yc == 0) {
			left = right = (int)vector.x;
			top = bottom = (int)vector.y;
		    } else {
			if (left   > vector.x) left   = (int)vector.x;
			if (right  < vector.x) right  = (int)vector.x;
			if (bottom > vector.y) bottom = (int)vector.y;
			if (top	   < vector.y) top    = (int)vector.y;
		    }

		}
	    }
	    left   = (int)FLOOR(left);
	    right  = (int)CEIL(right);
	    bottom = (int)FLOOR(bottom);
	    top	   = (int)CEIL(top);

	} else {
	    left   = (int)FLOOR( glyphslot->metrics.horiBearingX );
	    right  = (int)CEIL( glyphslot->metrics.horiBearingX + glyphslot->metrics.width );

	    top    = (int)CEIL( glyphslot->metrics.horiBearingY );
	    bottom = (int)FLOOR( glyphslot->metrics.horiBearingY - glyphslot->metrics.height );
	}

	/*
	 * Clip charcell glyphs to the bounding box
	 * XXX transformed?
	 */
	if (font->info.spacing >= FC_CHARCELL && !transform)
	{
	    if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT)
	    {
		if (TRUNC(bottom) > font->public.max_advance_width)
		{
		    int adjust;

		    adjust = bottom - (font->public.max_advance_width << 6);
		    if (adjust > top)
			adjust = top;
		    top -= adjust;
		    bottom -= adjust;
		}
	    }
	    else
	    {
		if (TRUNC(right) > font->public.max_advance_width)
		{
		    int adjust;

		    adjust = right - (font->public.max_advance_width << 6);
		    if (adjust > left)
			adjust = left;
		    left -= adjust;
		    right -= adjust;
		}
	    }
	}

	glyph_transform = transform;
	if ( glyphslot->format != FT_GLYPH_FORMAT_BITMAP )
	{
	    error = FT_Render_Glyph( face->glyph, mode );
	    if (error)
		continue;
	    glyph_transform = False;
	}

	FT_Library_SetLcdFilter( _XftFTlibrary, FT_LCD_FILTER_NONE );

	if (font->info.spacing >= FC_MONO)
	{
	    if (transform)
	    {
		if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT)
		{
		    vector.x = 0;
		    vector.y = -face->size->metrics.max_advance;
		}
		else
		{
		    vector.x = face->size->metrics.max_advance;
		    vector.y = 0;
		}
		FT_Vector_Transform(&vector, &font->info.matrix);
		xftg->metrics.xOff = (short)(TRUNC(ROUND(vector.x)));
		xftg->metrics.yOff = (short)(TRUNC(ROUND(vector.y)));
	    }
	    else
	    {
		if (font->info.load_flags & FT_LOAD_VERTICAL_LAYOUT)
		{
		    xftg->metrics.xOff = 0;
		    xftg->metrics.yOff = (short)(-font->public.max_advance_width);
		}
		else
		{
		    xftg->metrics.xOff = (short)(font->public.max_advance_width);
		    xftg->metrics.yOff = 0;
		}
	    }
	}
	else
	{
	    xftg->metrics.xOff = (short)(TRUNC(ROUND(glyphslot->advance.x)));
	    xftg->metrics.yOff = (short)(-TRUNC(ROUND(glyphslot->advance.y)));
	}

	/* compute the size of the final bitmap */
	ftbit = &glyphslot->bitmap;

	width = (int)ftbit->width;
	height = (int)ftbit->rows;

	if (XftDebug() & XFT_DBG_GLYPH)
	{
	    printf ("glyph %d:\n", (int) glyphindex);
	    printf (" xywh (%d %d %d %d), trans (%d %d %d %d) wh (%d %d)\n",
		    (int) glyphslot->metrics.horiBearingX,
		    (int) glyphslot->metrics.horiBearingY,
		    (int) glyphslot->metrics.width,
		    (int) glyphslot->metrics.height,
		    left, right, top, bottom,
		    width, height);
	    if (XftDebug() & XFT_DBG_GLYPHV)
	    {
		int		x, y;
		unsigned char	*line;

		line = ftbit->buffer;
		if (ftbit->pitch < 0)
		    line -= ftbit->pitch*(height-1);

		for (y = 0; y < height; y++)
		{
		    if (font->info.antialias)
		    {
			static const char    den[] = { " .:;=+*#" };
			for (x = 0; x < width; x++)
			    printf ("%c", den[line[x] >> 5]);
		    }
		    else
		    {
			for (x = 0; x < width * 8; x++)
			{
			    printf ("%c", (line[x>>3] & (1 << (x & 7))) ? '#' : ' ');
			}
		    }
		    printf ("|\n");
		    line += ftbit->pitch;
		}
		printf ("\n");
	    }
	}

	m3x3_uniform(m);
	size = _compute_xrender_bitmap_size( &local, glyphslot, mode, glyph_transform ? &font->info.matrix : NULL, m );
	if ( size < 0 )
	    continue;

	xftg->metrics.width  = (unsigned short)local.width;
	xftg->metrics.height = (unsigned short)local.rows;
	if (glyph_transform)
	{
	    m3x3 mi;

	    m3x3_invert(m, mi);
	    vector.x = - glyphslot->bitmap_left;
	    vector.y =   glyphslot->bitmap_top;
	    m3x3_transform(&vector, mi);
	    xftg->metrics.x = (short)vector.x;
	    xftg->metrics.y = (short)vector.y;
	}
	else
	{
	    xftg->metrics.x = (short)(- glyphslot->bitmap_left);
	    xftg->metrics.y = (short)(  glyphslot->bitmap_top);
	}

	/*
	 * If the glyph is relatively large (> 1% of server memory),
	 * don't send it until necessary.
	 */
	if (!need_bitmaps && ((unsigned long) size > (info->max_glyph_memory / 100)))
	    continue;

	/*
	 * Make sure there is enough buffer space for the glyph.
	 */
	if (size > bufSize)
	{
	    if (bufBitmap != bufLocal)
		free (bufBitmap);
	    bufBitmap = (unsigned char *) malloc ((size_t)size);
	    if (!bufBitmap)
		continue;
	    bufSize = size;
	}
	memset (bufBitmap, 0, (size_t)size);

	local.buffer = bufBitmap;

	if (mode == FT_RENDER_MODE_NORMAL && glyph_transform)
	    _scaled_fill_xrender_bitmap(&local, &glyphslot->bitmap, m);
	else
	    _fill_xrender_bitmap( &local, glyphslot, mode,
				 (font->info.rgba == FC_RGBA_BGR ||
				  font->info.rgba == FC_RGBA_VBGR) );

	/*
	 * Copy or convert into local buffer.
	 */

	/*
	 * Use the glyph index as the wire encoding; it
	 * might be more efficient for some locales to map
	 * these by first usage to smaller values, but that
	 * would require persistently storing the map when
	 * glyphs were freed.
	 */
	glyph = (Glyph) glyphindex;

	if (xftg->picture)
	{
	    XRenderFreePicture(dpy, xftg->picture);
	    xftg->picture = 0;
	}
	xftg->glyph_memory = (size_t)size + font->sizeof_glyph;
	if (font->format)
	{
	    if (!font->glyphset)
		font->glyphset = XRenderCreateGlyphSet (dpy, font->format);
	    if ( mode == FT_RENDER_MODE_MONO )
	    {
		/* swap bits in each byte */
		if (BitmapBitOrder (dpy) != MSBFirst)
		{
		    unsigned char   *line = (unsigned char*)bufBitmap;
		    int		    i = size;

		    while (i--)
		    {
			int c = *line;
			c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55);
			c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33);
			c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f);
			*line++ = (unsigned char)c;
		    }
		}
	    }
	    else if (glyphslot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA || mode != FT_RENDER_MODE_NORMAL)
	    {
		/* invert ARGB <=> BGRA */
		if (ImageByteOrder (dpy) != XftNativeByteOrder ())
		    XftSwapCARD32 ((CARD32 *) bufBitmap, size >> 2);
	    }

	    if (glyphslot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
	    {
		Pixmap pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy), local.width, local.rows, 32);
		GC gc = XCreateGC(dpy, pixmap, 0, NULL);
		XImage image = {
		    (int) local.width, (int) local.rows, 0, ZPixmap, (char *)bufBitmap,
		    dpy->byte_order, dpy->bitmap_unit, dpy->bitmap_bit_order, 32,
		    32, (int) (local.width * 4 - (unsigned) local.pitch), 32,
		    0, 0, 0, NULL, { NULL }
		};

		XInitImage(&image);
		XPutImage(dpy, pixmap, gc, &image, 0, 0, 0, 0, local.width, local.rows);
		xftg->picture = XRenderCreatePicture(dpy, pixmap, font->format, 0, NULL);

		XFreeGC(dpy, gc);
		XFreePixmap(dpy, pixmap);
		/*
		 * Record 256 times higher memory pressure for unrotated
		 * pictures, and maximum for rotated pictures.
		 */
		if (font->info.matrix.xy || font->info.matrix.yx)
		    xftg->glyph_memory += font->max_glyph_memory - (unsigned long) size;
		else
		    xftg->glyph_memory += (size_t)size * 255;
	    }
	    else
		XRenderAddGlyphs (dpy, font->glyphset, &glyph,
				  &xftg->metrics, 1,
				  (char *) bufBitmap, size);
	}
	else
	{
	    if (size)
	    {
		xftg->bitmap = malloc ((size_t)size);
		if (xftg->bitmap)
		    memcpy (xftg->bitmap, bufBitmap, (size_t)size);
	    }
	    else
		xftg->bitmap = NULL;
	}

	font->glyph_memory += xftg->glyph_memory;
	info->glyph_memory += xftg->glyph_memory;
	if (XftDebug() & XFT_DBG_CACHE)
	    _XftFontValidateMemory (dpy, pub);
	if (XftDebug() & XFT_DBG_CACHEV)
	    printf ("Caching glyph 0x%x size %lu\n", glyphindex,
		    xftg->glyph_memory);

	if (font->track_mem_usage) {
	    XftGlyphUsage *xuse = (XftGlyphUsage *) xftg;

	    if (font->newest == FT_UINT_MAX) {
		xuse->older = glyphindex;
	        xuse->newer = glyphindex;
		if (XftDebug() & XFT_DBG_USAGE)
		    printf("alloc %p -> %d: %p USE %d.%d\n",
			    (void *) font, glyphindex,
			    (void *) xuse, xuse->older, xuse->newer);
	    } else {
		XftGlyphUsage *xnew;
		XftGlyphUsage *xold;

		assert(font->glyphs[font->newest] != NULL);
		xnew = (XftGlyphUsage *) font->glyphs[font->newest];

		assert(font->glyphs[xnew->newer] != NULL);
		xold = (XftGlyphUsage *) font->glyphs[xnew->newer];

		xuse->older = font->newest;
		xuse->newer = xnew->newer;
		xnew->newer = glyphindex;
		xold->older = glyphindex;
		if (XftDebug() & XFT_DBG_USAGE)
		    printf("alloc %p -> %d: %p USE %d.%d, %p NEW %d.%d %p OLD %d.%d\n",
			    (void *) font, glyphindex,
			    (void *) xuse, xuse->older, xuse->newer,
			    (void *) xnew, xnew->older, xnew->newer,
			    (void *) xold, xold->older, xold->newer);
	    }

	    font->newest = glyphindex;
	    font->total_inuse++;
	    if (XftDebug() & XFT_DBG_USAGE)
		_XftValidateGlyphUsage(font);
	}
    }
    if (bufBitmap != bufLocal)
	free (bufBitmap);
    XftUnlockFace (&font->public);
}

_X_EXPORT void
XftFontUnloadGlyphs (Display		*dpy,
		     XftFont		*pub,
		     _Xconst FT_UInt	*glyphs,
		     int		nglyph)
{
    XftDisplayInfo  *info = _XftDisplayInfoGet (dpy, False);
    XftFontInt	    *font = (XftFontInt *) pub;
    XftGlyph	    *xftg;
    FT_UInt	    glyphindex;
    Glyph	    glyphBuf[1024];
    int		    nused;

    nused = 0;
    while (nglyph--)
    {
	glyphindex = *glyphs++;
	xftg = font->glyphs[glyphindex];
	if (!xftg)
	    continue;
	if (xftg->glyph_memory)
	{
	    if (XftDebug() & XFT_DBG_CACHEV)
		printf ("Uncaching glyph 0x%x size %lu\n",
			glyphindex, xftg->glyph_memory);
	    if (font->format)
	    {
		if (xftg->picture)
		    XRenderFreePicture(dpy, xftg->picture);
		else if (font->glyphset)
		{
		    glyphBuf[nused++] = (Glyph) glyphindex;
		    if (nused == sizeof (glyphBuf) / sizeof (glyphBuf[0]))
		    {
			XRenderFreeGlyphs (dpy, font->glyphset, glyphBuf, nused);
			nused = 0;
		    }
		}
	    }
	    else if (xftg->bitmap)
		free (xftg->bitmap);
	    font->glyph_memory -= xftg->glyph_memory;
	    if (info)
		info->glyph_memory -= xftg->glyph_memory;
	}

	if (font->track_mem_usage) {
	    XftGlyphUsage *xuse = (XftGlyphUsage *) xftg;
	    XftGlyphUsage *xtmp;

	    if (XftDebug() & XFT_DBG_USAGE)
		printf("free %p -> %p USE %d.%d\n",
		       (void *) font, (void *) xuse, xuse->older, xuse->newer);

	    if (xuse->older != FT_UINT_MAX) {
		xtmp = (XftGlyphUsage *) font->glyphs[xuse->older];
		if (xtmp != NULL) {
		    /* update link around to oldest glyph */
		    xtmp->newer = xuse->newer;
		}
		if (font->newest == glyphindex) {
		    if (font->newest == xuse->older)
			font->newest = FT_UINT_MAX;
		    else
			font->newest = xuse->older;
		}
	    }
	    if (xuse->newer != FT_UINT_MAX) {
		xtmp = (XftGlyphUsage *) font->glyphs[xuse->newer];
		if (xtmp != NULL) {
		    /* update link around to newest glyph */
		    xtmp->older = xuse->older;
		}
	    }
	    if (font->total_inuse) {
		font->total_inuse--;
	    } else {
		fprintf (stderr, "Xft: glyph count error\n");
	    }
	    if (XftDebug() & XFT_DBG_USAGE)
		_XftValidateGlyphUsage(font);
	}

	free (xftg);
	XftMemFree (XFT_MEM_GLYPH, font->sizeof_glyph);
	font->glyphs[glyphindex] = NULL;
    }
    if (font->glyphset && nused)
	XRenderFreeGlyphs (dpy, font->glyphset, glyphBuf, nused);
}

_X_EXPORT FcBool
XftFontCheckGlyph (Display	*dpy,
		   XftFont	*pub,
		   FcBool	need_bitmaps,
		   FT_UInt	glyph,
		   FT_UInt	*missing,
		   int		*nmissing)
{
    XftFontInt	    *font = (XftFontInt *) pub;
    XftGlyph	    *xftg;
    int		    n;

    if (glyph >= font->num_glyphs)
	return FcFalse;
    xftg = font->glyphs[glyph];
    if (!xftg || (need_bitmaps && !xftg->glyph_memory))
    {
	if (!xftg)
	{
	    xftg = malloc (font->sizeof_glyph);
	    if (!xftg)
		return FcFalse;
	    XftMemAlloc (XFT_MEM_GLYPH, font->sizeof_glyph);

	    xftg->bitmap = NULL;
	    xftg->glyph_memory = 0;
	    xftg->picture = 0;
	    font->glyphs[glyph] = xftg;

	    if (font->track_mem_usage) {
		XftGlyphUsage *xuse = (XftGlyphUsage *) xftg;
		xuse->older = FT_UINT_MAX;
		xuse->newer = FT_UINT_MAX;
	    }
	}
	n = *nmissing;
	missing[n++] = glyph;
	if (n == XFT_NMISSING)
	{
	    XftFontLoadGlyphs (dpy, pub, need_bitmaps, missing, n);
	    n = 0;
	}
	*nmissing = n;
	return FcTrue;
    }

    /*
     * Make unloading faster by moving newly-referenced glyphs to the front
     * of the list, leaving the less-used glyphs on the end.
     */
    if (font->track_mem_usage
     && font->total_inuse > 10
     && font->newest != FT_UINT_MAX
     && font->newest != glyph)
    {
	XftGlyphUsage *xuse = (XftGlyphUsage *) xftg;
	XftGlyphUsage *xtmp = (XftGlyphUsage *) font->glyphs[font->newest];
	XftGlyphUsage *xold;
	XftGlyphUsage *xnew;

	/* delink */
	xold = (XftGlyphUsage *) font->glyphs[xuse->older];
	xnew = (XftGlyphUsage *) font->glyphs[xuse->newer];
	assert(xold != NULL);
	assert(xnew != NULL);
	xold->newer = xuse->newer;
	xnew->older = xuse->older;

	/* relink */
	xnew = (XftGlyphUsage *) font->glyphs[xtmp->newer];
	assert(xnew != NULL);
	xnew->older = glyph;
	xuse->older = font->newest;
	xuse->newer = xtmp->newer;
	xtmp->newer = glyph;

	font->newest = glyph;
    }
    return FcFalse;
}

_X_EXPORT FcBool
XftCharExists (Display	    *dpy _X_UNUSED,
	       XftFont	    *pub,
	       FcChar32	     ucs4)
{
    if (pub->charset)
	return FcCharSetHasChar (pub->charset, ucs4);
    return FcFalse;
}

#define Missing	    ((FT_UInt) ~0)

_X_EXPORT FT_UInt
XftCharIndex (Display	    *dpy,
	      XftFont	    *pub,
	      FcChar32	    ucs4)
{
    XftFontInt	*font = (XftFontInt *) pub;
    FcChar32	ent, offset;
    FT_Face	face;

    if (!font->hash_value)
	return 0;

    ent = ucs4 % (FcChar32)font->hash_value;
    offset = 0;
    while (font->hash_table[ent].ucs4 != ucs4)
    {
	if (font->hash_table[ent].ucs4 == (FcChar32) ~0)
	{
	    if (!XftCharExists (dpy, pub, ucs4))
		return 0;
	    face  = XftLockFace (pub);
	    if (!face)
		return 0;
	    font->hash_table[ent].ucs4 = ucs4;
	    font->hash_table[ent].glyph = FcFreeTypeCharIndex (face, ucs4);
	    XftUnlockFace (pub);
	    break;
	}
	if (!offset)
	{
	    offset = ucs4 % (FcChar32)font->rehash_value;
	    if (!offset)
		offset = 1;
	}
	ent = ent + offset;
	if (ent >= (FcChar32)font->hash_value)
	    ent -= (FcChar32)font->hash_value;
    }
    return font->hash_table[ent].glyph;
}

/*
 * Remove glyph(s) from the font to reduce memory-usage.
 */
_X_HIDDEN void
_XftFontUncacheGlyph (Display *dpy, XftFont *pub)
{
    XftFontInt	    *font = (XftFontInt *) pub;
    unsigned long   glyph_memory;
    FT_UInt	    glyphindex;
    XftGlyph	    *xftg;

    if (!font->glyph_memory)
	return;

    if (XftDebug() & XFT_DBG_CACHE)
	_XftFontValidateMemory (dpy, pub);

    if (font->track_mem_usage)
    {
	/*
	 * Remove the oldest glyph from the font.
	 */
	if (font->newest != FT_UINT_MAX) {
	    XftGlyphUsage *xuse = (XftGlyphUsage *) font->glyphs[font->newest];
	    if ((glyphindex = xuse->newer) != FT_UINT_MAX)
		XftFontUnloadGlyphs (dpy, pub, &glyphindex, 1);
	}
    }
    else if (font->use_free_glyphs)
    {
	/*
	 * Pick a random glyph from the font and remove it from the cache
	 */
	glyph_memory = ((unsigned long)rand() % font->glyph_memory);
	for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++)
	{
	    xftg = font->glyphs[glyphindex];
	    if (xftg)
	    {
		if (xftg->glyph_memory > glyph_memory)
		{
		    XftFontUnloadGlyphs (dpy, pub, &glyphindex, 1);
		    break;
		}
		glyph_memory -= xftg->glyph_memory;
	    }
	}
    }
    else
    {
	/*
	 * Free all glyphs, since they are part of a set.
	 */
	if (font->glyphset)
	{
	    XRenderFreeGlyphSet (dpy, font->glyphset);
	    font->glyphset = 0;
	}
	for (glyphindex = 0; glyphindex < font->num_glyphs; glyphindex++)
	{
	    xftg = font->glyphs[glyphindex];
	    if (xftg)
	    {
		if (xftg->glyph_memory > 0)
		{
		    XftFontUnloadGlyphs (dpy, pub, &glyphindex, 1);
		}
	    }
	}
    }

    if (XftDebug() & XFT_DBG_CACHE)
	_XftFontValidateMemory (dpy, pub);
}

_X_HIDDEN void
_XftFontManageMemory (Display *dpy, XftFont *pub)
{
    XftFontInt	*font = (XftFontInt *) pub;

    if (font->max_glyph_memory)
    {
	if (XftDebug() & XFT_DBG_CACHE)
	{
	    if (font->glyph_memory > font->max_glyph_memory)
		printf ("Reduce memory for font 0x%lx from %lu to %lu\n",
			font->glyphset ? font->glyphset : (unsigned long) font,
			font->glyph_memory, font->max_glyph_memory);
	}
	while (font->glyph_memory > font->max_glyph_memory)
	    _XftFontUncacheGlyph (dpy, pub);
    }
    _XftDisplayManageMemory (dpy);
}
