/*
 * $XFree86: xc/lib/Xft/xftrender.c,v 1.8 2001/07/13 18:16:10 keithp Exp $
 *
 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of 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 <stdlib.h>
#include "xftint.h"

void
XftRenderString8 (Display *dpy, Picture src, 
		  XftFontStruct *font, Picture dst,
		  int srcx, int srcy,
		  int x, int y,
		  XftChar8 *string, int len)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar8	    *s;
    int		    l;

    s = string;
    l = len;
    nmissing = 0;
    while (l--)
	XftGlyphCheck (dpy, font, (XftChar32) *s++, missing, &nmissing);
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    XRenderCompositeString8 (dpy, PictOpOver, src, dst,
			     font->format, font->glyphset,
			     srcx, srcy, x, y, (char *) string, len);
}

void
XftRenderString16 (Display *dpy, Picture src, 
		   XftFontStruct *font, Picture dst,
		   int srcx, int srcy,
		   int x, int y,
		   XftChar16 *string, int len)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar16	    *s;
    int		    l;

    s = string;
    l = len;
    nmissing = 0;
    while (l--)
	XftGlyphCheck (dpy, font, (XftChar32) *s++, missing, &nmissing);
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    XRenderCompositeString16 (dpy, PictOpOver, src, dst,
			      font->format, font->glyphset,
			      srcx, srcy, x, y, string, len);
}

void
XftRenderString32 (Display *dpy, Picture src, 
		   XftFontStruct *font, Picture dst,
		   int srcx, int srcy,
		   int x, int y,
		   XftChar32 *string, int len)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar32	    *s;
    int		    l;

    s = string;
    l = len;
    nmissing = 0;
    while (l--)
	XftGlyphCheck (dpy, font, (XftChar32) *s++, missing, &nmissing);
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    XRenderCompositeString32 (dpy, PictOpOver, src, dst,
			      font->format, font->glyphset,
			      srcx, srcy, x, y, string, len);
}

void
XftRenderStringUtf8 (Display *dpy, Picture src, 
		     XftFontStruct *font, Picture dst,
		     int srcx, int srcy,
		     int x, int y,
		     XftChar8 *string, int len)
{
    XftChar8	*s;
    XftChar32	c;
    XftChar32	lbuf[4096];
    XftChar32	*d;
    XftChar8	*dst8;
    XftChar16	*dst16;
    XftChar32	*dst32;
    int		rlen, clen;
    int		width = 1;
    int		n;

    /* compute needed width */
    if (!XftUtf8Len (string, len, &n, &width))
	return;
    
    d = lbuf;
    if (n * width > sizeof (lbuf))
    {
	d = (XftChar32 *) malloc (n * width);
	if (!d)
	    return;
    }
    
    switch (width) {
    case 4:
	s = string;
	rlen = len;
	dst32 = d;
	while (rlen)
	{
	    clen = XftUtf8ToUcs4 (s, &c, rlen);
	    if (clen <= 0)	/* malformed UTF8 string */
		return;
	    *dst32++ = c;
	    s += clen;
	    rlen -= clen;
	}
	dst32 = d;
	XftRenderString32 (dpy, src, font, dst, srcx, srcy, x, y,
			 dst32, n);
	break;
    case 2:
	s = string;
	rlen = len;
	dst16 = (XftChar16 *) d;
	while (rlen)
	{
	    clen = XftUtf8ToUcs4 (s, &c, rlen);
	    if (clen <= 0)	/* malformed UTF8 string */
		return;
	    *dst16++ = c;
	    s += clen;
	    rlen -= clen;
	}
	dst16 = (XftChar16 *) d;
	XftRenderString16 (dpy, src, font, dst, srcx, srcy, x, y,
			   dst16, n);
	break;
    case 1:
	s = string;
	rlen = len;
	dst8 = (XftChar8 *) d;
	while (rlen)
	{
	    clen = XftUtf8ToUcs4 (s, &c, rlen);
	    if (clen <= 0)	/* malformed UTF8 string */
		return;
	    *dst8++ = c;
	    s += clen;
	    rlen -= clen;
	}
	dst8 = (XftChar8 *) d;
	XftRenderString8 (dpy, src, font, dst, srcx, srcy, x, y,
			  dst8, n);
	break;
    }
    if (d != lbuf)
	free (d);
}
   
void
XftRenderExtents8 (Display	    *dpy,
		   XftFontStruct    *font,
		   XftChar8    *string, 
		   int		    len,
		   XGlyphInfo	    *extents)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar8	    *s, c;
    int		    l;
    XGlyphInfo	    *gi;
    int		    x, y;
    int		    left, right, top, bottom;
    int		    overall_left, overall_right;
    int		    overall_top, overall_bottom;

    s = string;
    l = len;
    nmissing = 0;
    while (l--)
	XftGlyphCheck (dpy, font, (XftChar32) *s++, missing, &nmissing);
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    
    gi = 0;
    while (len)
    {
	c = *string++;
	len--;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (gi)
	    break;
    }
    if (len == 0 && !gi)
    {
	extents->width = 0;
	extents->height = 0;
	extents->x = 0;
	extents->y = 0;
	extents->yOff = 0;
	extents->xOff = 0;
	return;
    }
    x = 0;
    y = 0;
    overall_left = x - gi->x;
    overall_top = y - gi->y;
    overall_right = overall_left + (int) gi->width;
    overall_bottom = overall_top + (int) gi->height;
    x += gi->xOff;
    y += gi->yOff;
    while (len--)
    {
	c = *string++;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (!gi)
	    continue;
	left = x - gi->x;
	top = y - gi->y;
	right = left + (int) gi->width;
	bottom = top + (int) gi->height;
	if (left < overall_left)
	    overall_left = left;
	if (top < overall_top)
	    overall_top = top;
	if (right > overall_right)
	    overall_right = right;
	if (bottom > overall_bottom)
	    overall_bottom = bottom;
	x += gi->xOff;
	y += gi->yOff;
    }
    extents->x = -overall_left;
    extents->y = -overall_top;
    extents->width = overall_right - overall_left;
    extents->height = overall_bottom - overall_top;
    extents->xOff = x;
    extents->yOff = y;
}

void
XftRenderExtents16 (Display	    *dpy,
		    XftFontStruct   *font,
		    XftChar16	    *string,
		    int		    len,
		    XGlyphInfo	    *extents)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar16	    *s, c;
    int		    l;
    XGlyphInfo	    *gi;
    int		    x, y;
    int		    left, right, top, bottom;
    int		    overall_left, overall_right;
    int		    overall_top, overall_bottom;

    s = string;
    l = len;
    nmissing = 0;
    while (l--)
	XftGlyphCheck (dpy, font, (XftChar32) *s++, missing, &nmissing);
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    
    gi = 0;
    while (len)
    {
	c = *string++;
	len--;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (gi)
	    break;
    }
    if (len == 0 && !gi)
    {
	extents->width = 0;
	extents->height = 0;
	extents->x = 0;
	extents->y = 0;
	extents->yOff = 0;
	extents->xOff = 0;
	return;
    }
    x = 0;
    y = 0;
    overall_left = x - gi->x;
    overall_top = y - gi->y;
    overall_right = overall_left + (int) gi->width;
    overall_bottom = overall_top + (int) gi->height;
    x += gi->xOff;
    y += gi->yOff;
    while (len--)
    {
	c = *string++;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (!gi)
	    continue;
	left = x - gi->x;
	top = y - gi->y;
	right = left + (int) gi->width;
	bottom = top + (int) gi->height;
	if (left < overall_left)
	    overall_left = left;
	if (top < overall_top)
	    overall_top = top;
	if (right > overall_right)
	    overall_right = right;
	if (bottom > overall_bottom)
	    overall_bottom = bottom;
	x += gi->xOff;
	y += gi->yOff;
    }
    extents->x = -overall_left;
    extents->y = -overall_top;
    extents->width = overall_right - overall_left;
    extents->height = overall_bottom - overall_top;
    extents->xOff = x;
    extents->yOff = y;
}

void
XftRenderExtents32 (Display	    *dpy,
		    XftFontStruct   *font,
		    XftChar32	    *string,
		    int		    len,
		    XGlyphInfo	    *extents)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar32	    *s, c;
    int		    l;
    XGlyphInfo	    *gi;
    int		    x, y;
    int		    left, right, top, bottom;
    int		    overall_left, overall_right;
    int		    overall_top, overall_bottom;

    s = string;
    l = len;
    nmissing = 0;
    while (l--)
	XftGlyphCheck (dpy, font, (XftChar32) *s++, missing, &nmissing);
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    
    gi = 0;
    while (len)
    {
	c = *string++;
	len--;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (gi)
	    break;
    }
    if (len == 0 && !gi)
    {
	extents->width = 0;
	extents->height = 0;
	extents->x = 0;
	extents->y = 0;
	extents->yOff = 0;
	extents->xOff = 0;
	return;
    }
    x = 0;
    y = 0;
    overall_left = x - gi->x;
    overall_top = y - gi->y;
    overall_right = overall_left + (int) gi->width;
    overall_bottom = overall_top + (int) gi->height;
    x += gi->xOff;
    y += gi->yOff;
    while (len--)
    {
	c = *string++;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (!gi)
	    continue;
	left = x - gi->x;
	top = y - gi->y;
	right = left + (int) gi->width;
	bottom = top + (int) gi->height;
	if (left < overall_left)
	    overall_left = left;
	if (top < overall_top)
	    overall_top = top;
	if (right > overall_right)
	    overall_right = right;
	if (bottom > overall_bottom)
	    overall_bottom = bottom;
	x += gi->xOff;
	y += gi->yOff;
    }
    extents->x = -overall_left;
    extents->y = -overall_top;
    extents->width = overall_right - overall_left;
    extents->height = overall_bottom - overall_top;
    extents->xOff = x;
    extents->yOff = y;
}

void
XftRenderExtentsUtf8 (Display	    *dpy,
		      XftFontStruct *font,
		      XftChar8	    *string, 
		      int	    len,
		      XGlyphInfo    *extents)
{
    XftChar32	    missing[XFT_NMISSING];
    int		    nmissing;
    XftChar8	    *s;
    XftChar32	    c;
    int		    l, clen;
    XGlyphInfo	    *gi;
    int		    x, y;
    int		    left, right, top, bottom;
    int		    overall_left, overall_right;
    int		    overall_top, overall_bottom;

    s = string;
    l = len;
    nmissing = 0;
    while (l)
    {
	clen = XftUtf8ToUcs4 (s, &c, l);
	if (clen < 0)
	    break;
	XftGlyphCheck (dpy, font, (XftChar32) c, missing, &nmissing);
	s += clen;
	l -= clen;
    }
    if (nmissing)
	XftGlyphLoad (dpy, font, missing, nmissing);
    
    gi = 0;
    while (len)
    {
	clen = XftUtf8ToUcs4 (string, &c, len);
	if (clen < 0)
	{
	    len = 0;
	    break;
	}
	len -= clen;
	string += clen;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (gi)
	    break;
    }
    if (len == 0 && !gi)
    {
	extents->width = 0;
	extents->height = 0;
	extents->x = 0;
	extents->y = 0;
	extents->yOff = 0;
	extents->xOff = 0;
	return;
    }
    x = 0;
    y = 0;
    overall_left = x - gi->x;
    overall_top = y - gi->y;
    overall_right = overall_left + (int) gi->width;
    overall_bottom = overall_top + (int) gi->height;
    x += gi->xOff;
    y += gi->yOff;
    while (len)
    {
	clen = XftUtf8ToUcs4 (string, &c, len);
	if (clen < 0)
	    break;
	len -= clen;
	string += clen;
	gi = c < font->nrealized ? font->realized[c] : 0;
	if (!gi)
	    continue;
	left = x - gi->x;
	top = y - gi->y;
	right = left + (int) gi->width;
	bottom = top + (int) gi->height;
	if (left < overall_left)
	    overall_left = left;
	if (top < overall_top)
	    overall_top = top;
	if (right > overall_right)
	    overall_right = right;
	if (bottom > overall_bottom)
	    overall_bottom = bottom;
	x += gi->xOff;
	y += gi->yOff;
    }
    extents->x = -overall_left;
    extents->y = -overall_top;
    extents->width = overall_right - overall_left;
    extents->height = overall_bottom - overall_top;
    extents->xOff = x;
    extents->yOff = y;
}
