extern "C" {
#include "tkSDLInt.h"
#include "tkFont.h"
}
#include "SdlTkInt.h"
#include "Xregion.h"

#ifdef AGG_CUSTOM_ALLOCATOR
#include <new>
#endif
#include "agg_bezier_arc.h"
#include "agg_conv_curve.h"
#include "agg_ellipse.h"
#include "agg_font_freetype.h"
#ifndef AGG23
#include "agg_image_accessors.h"
#endif
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgb_packed.h"
#include "agg_pixfmt_rgba.h"
#include "agg_pixfmt_gray.h"
#include "agg_renderer_base.h"
#include "agg_renderer_mclip.h"
#include "agg_renderer_primitives.h"
#include "agg_renderer_outline_aa.h"
#include "agg_rasterizer_outline_aa.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_scanline_u.h"
#include "agg_span_pattern_rgba.h"
#include "agg_span_allocator.h"
#include "agg_renderer_scanline.h"
#include "agg_conv_stroke.h"
#include "agg_conv_dash.h"
#include "agg_vcgen_stroke.h"
#include "agg_vcgen_dash.h"

#ifdef AGG23

/*
 * This is a span generator. It is used when drawing text and primitives
 * using a Bitmap as a stipple pattern.
 */
template<class ColorT,
	class Order, 
	class WrapModeX,
	class WrapModeY,
	class Allocator = agg::span_allocator<ColorT> > 
class span_stipple : public agg::span_pattern_rgba<ColorT, Order, WrapModeX, WrapModeY, Allocator>
{
public:
    typedef ColorT color_type;
    typedef Order order_type;
    typedef Allocator alloc_type;
    typedef agg::span_pattern_rgba<color_type, order_type, WrapModeX, WrapModeY, alloc_type> base_type;
    typedef typename color_type::value_type value_type;

    span_stipple(alloc_type& alloc,
	const agg::rendering_buffer& src, 
	unsigned offset_x, unsigned offset_y) :
            base_type(alloc, src, offset_x, offset_y),
            m_wrap_mode_x(src.width()),
            m_wrap_mode_y(src.height())
    {}

    //--------------------------------------------------------------------
    color_type* generate(int x, int y, unsigned len)
    {   
	color_type* span = base_type::allocator().span();
	unsigned sx = m_wrap_mode_x(x - base_type::offset_x());
	const value_type* row_ptr = 
	    (const value_type*)base_type::base_type::source_image().row(
		m_wrap_mode_y(
		    y - base_type::offset_y()));
	do
	{
	    const value_type* p = row_ptr + sx; /* 1 byte-per-pixel */
	    if (p[0]) {
		*span = m_color;
	    } else {
		span->clear();
	    }
	    sx = ++m_wrap_mode_x;
	    ++span;
	}
	while(--len);
	return base_type::allocator().span();
    }

    //---------------------------------------------------------------------
    void color(const color_type& c) { m_color = c; }
    const color_type& color() const { return m_color; }

private:
    color_type m_color;
    WrapModeX m_wrap_mode_x;
    WrapModeY m_wrap_mode_y;
};

#else

/* Needed since stipple bitmap is 1 byte per pixel */

namespace agg
{
    //-----------------------------------------------------image_accessor_wrap_gray8
    template<class PixFmt, class WrapX, class WrapY> class image_accessor_wrap_gray8
    {
    public:
        typedef PixFmt   pixfmt_type;
        typedef typename pixfmt_type::color_type color_type;
        typedef typename pixfmt_type::order_type order_type;
        typedef typename pixfmt_type::value_type value_type;

        image_accessor_wrap_gray8() {}
        explicit image_accessor_wrap_gray8(const pixfmt_type& pixf) : 
            m_pixf(&pixf), 
            m_wrap_x(pixf.width()), 
            m_wrap_y(pixf.height())
        {}

        void attach(const pixfmt_type& pixf)
        {
            m_pixf = &pixf;
        }

        AGG_INLINE const int8u* span(int x, int y, unsigned)
        {
            m_x = x;
            m_row_ptr = m_pixf->row_ptr(m_wrap_y(y));
            return m_row_ptr + m_wrap_x(x);
        }

        AGG_INLINE const int8u* next_x()
        {
            int x = ++m_wrap_x;
            return m_row_ptr + x;
        }

        AGG_INLINE const int8u* next_y()
        {
            m_row_ptr = m_pixf->row_ptr(++m_wrap_y);
            return m_row_ptr + m_wrap_x(m_x);
        }

    private:
        const pixfmt_type* m_pixf;
        const int8u*       m_row_ptr;
        int                m_x;
        WrapX              m_wrap_x;
        WrapY              m_wrap_y;
    };
}

/*
 * This is a span generator. It is used when drawing text and primitives
 * using a Bitmap as a stipple pattern.
 */
template<class Source>
class span_stipple : public agg::span_pattern_rgba<Source>
{
public:
    typedef Source source_type;
    typedef typename source_type::color_type color_type;
    typedef typename source_type::order_type order_type;
    typedef typename color_type::value_type value_type;
    typedef typename color_type::calc_type calc_type;

    span_stipple(source_type& src,
		 unsigned offset_x, unsigned offset_y) :
	    m_src(&src),
            m_offset_x(offset_x),
            m_offset_y(offset_y)
    {}

    //--------------------------------------------------------------------
    void generate(color_type* span, int x, int y, unsigned len)
    {   
	x += m_offset_x;
	y += m_offset_y;
	const Uint8* p = (const Uint8*)m_src->span(x, y, 1);
	do {
	    if (p[0]) {
		*span = m_color;
	    } else {
		span->clear();
	    }
	    p = m_src->next_x();
	    ++span;
	} while(--len);
    }


    //---------------------------------------------------------------------
    void color(const color_type& c) { m_color = c; }
    const color_type& color() const { return m_color; }

private:
    source_type* m_src;
    color_type   m_color;
    unsigned     m_offset_x;
    unsigned     m_offset_y;
};

#endif

template<class PixelFormat>
void
doDrawArc(Drawable d, GC gc, int x, int y,
    unsigned int width, unsigned int height, int start, int extent)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    REGION *rgn = 0;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h, sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    /* Apparently agg::arc is deprecated */
    agg::bezier_arc arc(xOff + x + width / 2.0, yOff + y + height / 2.0,
	width / 2.0, height / 2.0,
	agg::deg2rad(start / 64.0), agg::deg2rad(extent / 64.0));
    typedef agg::conv_curve<agg::bezier_arc, agg::curve3_div, agg::curve4_div> t_conv_curve;
    t_conv_curve curve(arc);

    /* Thing that generates scanlines */
    agg::rasterizer_scanline_aa<> rasterizer;
    rasterizer.reset();
    if (((unsigned) gc->line_width >= width / 2) ||
        ((unsigned) gc->line_width >= height / 2)) {
	rasterizer.add_path(curve);
    } else {
	agg::conv_stroke<t_conv_curve> stroke(curve);
	stroke.width((double) gc->line_width);
	rasterizer.add_path(stroke);
    }

    /* Scanline needed by the rasterizer -> renderer */
    agg::scanline_u8 scanline;

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    /* FIXME: FillOpaqueStippled not implemented */
    if ((gc->fill_style == FillStippled
	    || gc->fill_style == FillOpaqueStippled)
	    && gc->stipple != None) {

	_Pixmap *stipple = (_Pixmap *) gc->stipple;

	/* Rendering buffer that points to the bitmap */
	agg::rendering_buffer stipple_buf((agg::int8u *) stipple->sdl->pixels,
	    stipple->sdl->w, stipple->sdl->h, stipple->sdl->pitch);

	/* A span allocator holds 1 line of pixels */
	agg::span_allocator<agg::rgba8> span_allocator;

	/* Generates spans (lines of pixels) from a source buffer */
#ifdef AGG23
	typedef span_stipple<agg::rgba8, agg::order_argb,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> t_span_stipple;
	t_span_stipple span_stipple(span_allocator, stipple_buf, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#else
	typedef agg::image_accessor_wrap_gray8<PixelFormat,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> img_src_type;
	PixelFormat src_pixf(stipple_buf);
	img_src_type img_src(src_pixf);

	typedef span_stipple<img_src_type> t_span_stipple;
	t_span_stipple span_stipple(img_src, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip,
	    agg::span_allocator<agg::rgba8>, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_allocator, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#endif
    } else {
	agg::renderer_scanline_aa_solid<t_renderer_mclip> ren_scanline(ren_mclip);
	ren_scanline.color(c);

	agg::render_scanlines(rasterizer, scanline, ren_scanline);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxDrawArc(Drawable d, GC gc, int x, int y,
    unsigned int width, unsigned int height, int start, int extent)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }
    start = -start;
    extent = -extent;

    switch (format) {
    case SDLTK_RGB565:
	doDrawArc<agg::pixfmt_rgb565>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_BGR565:
	doDrawArc<agg::pixfmt_bgr565>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_RGB24:
	doDrawArc<agg::pixfmt_rgb24>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_BGR24:
	doDrawArc<agg::pixfmt_bgr24>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_RGBA32:
	doDrawArc<agg::pixfmt_rgba32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_ARGB32:
	doDrawArc<agg::pixfmt_argb32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_BGRA32:
	doDrawArc<agg::pixfmt_bgra32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_ABGR32:
	doDrawArc<agg::pixfmt_abgr32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_RGB555:
	doDrawArc<agg::pixfmt_rgb555>(d, gc, x, y, width, height, start, extent);
	break;
    }
}

template<class PixelFormat>
void
doDrawBitmap(
    Drawable src,
    Drawable dest,
    GC gc,
    int src_x, int src_y,
    unsigned int width, unsigned int height,
    int dest_x, int dest_y)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    REGION *rgn = 0;
    Region tmpRgn = 0;
    TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;

    if (IS_WINDOW(dest)) {
	rgn = SdlTkGetVisibleRegion((_Window *) dest);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(dest, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h, sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    /* The pixel format should match that of the SDL_Surface */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /*
     * If the clipping region is specified, intersect it with the visible
     * region of the window, or if this is a pixmap then use the clipping
     * region unmodified.
     */
    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
	Region clipRgn = (Region) clipPtr->value.region;
	if (rgn) {
	    tmpRgn = SdlTkRgnPoolGet();
	    XIntersectRegion(rgn, clipRgn, tmpRgn);
	    rgn = tmpRgn;
	} else {
	    rgn = clipRgn;
	}
    }

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 fg(r, g, b);

    SDL_GetRGB(gc->background, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 bg(r, g, b);
    bool transparent = true;
    Uint8 *mpixels = NULL;

    if (clipPtr && clipPtr->type == TKP_CLIP_PIXMAP) {
	if (clipPtr->value.pixmap == src) {
	    transparent = true;
	} else {
	    if (((_Pixmap *) clipPtr->value.pixmap)->sdl->pitch ==
		((_Pixmap *) src)->sdl->pitch &&
		((_Pixmap *) clipPtr->value.pixmap)->sdl->h ==
		((_Pixmap *) src)->sdl->h) {
		mpixels = (Uint8 *)
		    ((_Pixmap *) clipPtr->value.pixmap)->sdl->pixels;
	    }
	}
    } else {
	transparent = false;
    }

    Uint8 *pixels = (Uint8 *) ((_Pixmap *) src)->sdl->pixels;
    int pitch = ((_Pixmap *) src)->sdl->pitch;
    unsigned int x, y;
    for (y = src_y; y < src_y + height; y++) {
	Uint8 *row = pixels + y * pitch;
	Uint8 *mrow = (mpixels != NULL) ? (mpixels + y * pitch) : NULL;
	for (x = src_x; x < src_x + width; x++) {
	    if (transparent) {
		if (mrow) {
		    if (mrow[x]) {
			ren_mclip.copy_pixel(xOff + dest_x + (x - src_x),
			    yOff + dest_y + (y - src_y), row[x] ? fg : bg);
		    }
		} else {
		    if (row[x]) {
			ren_mclip.copy_pixel(xOff + dest_x + (x - src_x),
			    yOff + dest_y + (y - src_y), fg);
		    }
		}
	    } else {
		ren_mclip.copy_pixel(xOff + dest_x + (x - src_x),
		    yOff + dest_y + (y - src_y), row[x] ? fg : bg);
	    }
	}
    }

    if (tmpRgn) {
	SdlTkRgnPoolFree(tmpRgn);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxDrawBitmap(
    Drawable src,
    Drawable dest,
    GC gc,
    int src_x, int src_y,
    unsigned int width, unsigned int height,
    int dest_x, int dest_y)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(dest, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }

    switch (format) {
    case SDLTK_RGB565:
	doDrawBitmap<agg::pixfmt_rgb565>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_BGR565:
	doDrawBitmap<agg::pixfmt_bgr565>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_RGB24:
	doDrawBitmap<agg::pixfmt_rgb24>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_BGR24:
	doDrawBitmap<agg::pixfmt_bgr24>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_RGBA32:
	doDrawBitmap<agg::pixfmt_rgba32>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_ARGB32:
	doDrawBitmap<agg::pixfmt_argb32>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_BGRA32:
	doDrawBitmap<agg::pixfmt_bgra32>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_ABGR32:
	doDrawBitmap<agg::pixfmt_abgr32>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    case SDLTK_RGB555:
	doDrawBitmap<agg::pixfmt_rgb555>(src, dest, gc, src_x, src_y, width, height, dest_x, dest_y);
	break;
    }
}

class VertexSource_XPoints {
public:
    VertexSource_XPoints(XPoint *points, int npoints, int xOff, int yOff) :
	m_points(points),
	m_npoints(npoints),
	m_xOff(xOff),
	m_yOff(yOff),
	m_idx(0)
    {
    }
    void rewind(unsigned path_id)
    {
	m_idx = 0;
    }
    unsigned vertex(double *x, double *y)
    {
	if (m_idx == 0) {
	    *x = m_xOff + m_points[m_idx].x;
	    *y = m_yOff + m_points[m_idx].y;
	    ++m_idx;
	    return agg::path_cmd_move_to;
	}
	if (m_idx < m_npoints) {
	    *x = m_xOff + m_points[m_idx].x;
	    *y = m_yOff + m_points[m_idx].y;
	    ++m_idx;
	    return agg::path_cmd_line_to;
	}
	return agg::path_cmd_stop;
    }
private:
    XPoint *m_points;
    int m_npoints;
    int m_xOff, m_yOff;
    int m_idx;
};

template<class PixelFormat>
void
doDrawLines(Drawable d, GC gc, XPoint *points, int npoints, int mode)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    REGION *rgn = 0;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h, sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    /* Thing that generates scanlines */
    agg::rasterizer_scanline_aa<> rasterizer;
    rasterizer.reset();

    VertexSource_XPoints vertexSrc(points, npoints, xOff, yOff);

    Uint8 *dashes = (Uint8 *) &gc->dashes;

    if ((gc->line_style == LineOnOffDash) && dashes[0]) {
	agg::conv_dash<VertexSource_XPoints> dash(vertexSrc);
	agg::conv_stroke<agg::conv_dash<VertexSource_XPoints> > stroke(dash);
	unsigned dindex = 0, dlw;

	dlw = (gc->line_width <= 0) ? 1 : gc->line_width;
	dash.remove_all_dashes();
	while (dashes[dindex] && dashes[dindex + 1] &&
	       (dindex < sizeof (gc->dash_array))) {
	    dash.add_dash(dashes[dindex] * dlw, dashes[dindex + 1] * dlw);
	    dindex += 2;
	}
	dash.dash_start(gc->dash_offset);
	if (gc->line_width > 1) {
	    stroke.width((double) gc->line_width - 0.5);
	} else {
	    stroke.width(1);
	}
	if (gc->line_width >= 2) {
	    switch (gc->cap_style) {
	    case CapNotLast:
	    case CapButt:
		stroke.line_cap(agg::butt_cap);
		break;
	    case CapRound:
		stroke.line_cap(agg::round_cap);
		break;
	    default:
	 	stroke.line_cap(agg::square_cap);
		break;
	    }
	    switch (gc->join_style) {
	    case JoinMiter:
		stroke.line_join(agg::miter_join);
		break;
	    case JoinRound:
		stroke.line_join(agg::round_join);
		break;
	    default:
		stroke.line_join(agg::bevel_join);
		break;
	    }
	}
	rasterizer.add_path(stroke);
    } else {
	agg::conv_stroke<VertexSource_XPoints> stroke(vertexSrc);

	if (gc->line_width > 1) {
	    stroke.width((double) gc->line_width - 0.5);
	} else {
	    stroke.width((double) gc->line_width);
	}
	if (gc->line_width >= 2) {
	    switch (gc->cap_style) {
	    case CapNotLast:
	    case CapButt:
		stroke.line_cap(agg::butt_cap);
		break;
	    case CapRound:
		stroke.line_cap(agg::round_cap);
		break;
	    default:
	 	stroke.line_cap(agg::square_cap);
		break;
	    }
	    switch (gc->join_style) {
	    case JoinMiter:
		stroke.line_join(agg::miter_join);
		break;
	    case JoinRound:
		stroke.line_join(agg::round_join);
		break;
	    default:
		stroke.line_join(agg::bevel_join);
		break;
	    }
	}
	rasterizer.add_path(stroke);
    }

    /* Scanline needed by the rasterizer -> renderer */
    agg::scanline_u8 scanline;

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    if ((gc->fill_style == FillStippled
	    || gc->fill_style == FillOpaqueStippled)
	    && gc->stipple != None) {

	_Pixmap *stipple = (_Pixmap *) gc->stipple;

	/* Rendering buffer that points to the bitmap */
	agg::rendering_buffer stipple_buf((agg::int8u *) stipple->sdl->pixels,
	    stipple->sdl->w, stipple->sdl->h, stipple->sdl->pitch);

	/* A span allocator holds 1 line of pixels */
	agg::span_allocator<agg::rgba8> span_allocator;

	/* Generates spans (lines of pixels) from a source buffer */
#ifdef AGG23
	typedef span_stipple<agg::rgba8, agg::order_argb,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> t_span_stipple;
	t_span_stipple span_stipple(span_allocator, stipple_buf, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#else
	typedef agg::image_accessor_wrap_gray8<PixelFormat,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> img_src_type;
	PixelFormat src_pixf(stipple_buf);
	img_src_type img_src(src_pixf);

	typedef span_stipple<img_src_type> t_span_stipple;
	t_span_stipple span_stipple(img_src, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip,
	    agg::span_allocator<agg::rgba8>, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_allocator, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#endif
    } else {
	typedef agg::renderer_scanline_aa_solid<t_renderer_mclip> t_renderer_scanline_aa_solid;
	t_renderer_scanline_aa_solid ren_scanline(ren_mclip);
	ren_scanline.color(c);

	agg::render_scanlines(rasterizer, scanline, ren_scanline);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxDrawLines(Drawable d, GC gc, XPoint *points, int npoints, int mode)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }

    switch (format) {
    case SDLTK_RGB565:
	doDrawLines<agg::pixfmt_rgb565>(d, gc, points, npoints, mode);
	break;
    case SDLTK_BGR565:
	doDrawLines<agg::pixfmt_bgr565>(d, gc, points, npoints, mode);
	break;
    case SDLTK_RGB24:
	doDrawLines<agg::pixfmt_rgb24>(d, gc, points, npoints, mode);
	break;
    case SDLTK_BGR24:
	doDrawLines<agg::pixfmt_bgr24>(d, gc, points, npoints, mode);
	break;
    case SDLTK_RGBA32:
	doDrawLines<agg::pixfmt_rgba32>(d, gc, points, npoints, mode);
	break;
    case SDLTK_ARGB32:
	doDrawLines<agg::pixfmt_argb32>(d, gc, points, npoints, mode);
	break;
    case SDLTK_BGRA32:
	doDrawLines<agg::pixfmt_bgra32>(d, gc, points, npoints, mode);
	break;
    case SDLTK_ABGR32:
	doDrawLines<agg::pixfmt_abgr32>(d, gc, points, npoints, mode);
	break;
    case SDLTK_RGB555:
	doDrawLines<agg::pixfmt_rgb555>(d, gc, points, npoints, mode);
	break;
    }
}

/* http://www.libsdl.org/cgi/docwiki.cgi/Pixel_20Access */
static void
putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

void
SdlTkGfxDrawPoint(Drawable d, GC gc, int x, int y)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    REGION *rgn = 0;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    if (x >= 0 && x < sdl->w && y >= 0 && y < sdl->h) {
	if (rgn) {
	    if (XPointInRegion(rgn, x, y)) {
		putpixel(sdl, x, y, gc->foreground);
	    }
	} else {
	    putpixel(sdl, x, y, gc->foreground);
	}
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

class VertexSource_XRectangle {
public:
    VertexSource_XRectangle(int x, int y, int w, int h) :
	m_idx(0)
    {
	m_rect.x = x, m_rect.y = y, m_rect.width = w, m_rect.height = h;
    }
    void rewind(unsigned path_id)
    {
	m_idx = 0;
    }
    unsigned vertex(double *x, double *y)
    {
	if (m_idx == 0) {
	    *x = m_rect.x;
	    *y = m_rect.y;
	    ++m_idx;
	    return agg::path_cmd_move_to;
	}
	if (m_idx == 1) {
	    *x = m_rect.x + m_rect.width;
	    *y = m_rect.y;
	    ++m_idx;
	    return agg::path_cmd_line_to;
	}
	if (m_idx == 2) {
	    *x = m_rect.x + m_rect.width;
	    *y = m_rect.y + m_rect.height;
	    ++m_idx;
	    return agg::path_cmd_line_to;
	}
	if (m_idx == 3) {
	    *x = m_rect.x;
	    *y = m_rect.y + m_rect.height;
	    ++m_idx;
	    return agg::path_cmd_line_to;
	}
	if (m_idx == 4) {
	    *x = m_rect.x;
	    *y = m_rect.y;
	    ++m_idx;
	    return agg::path_cmd_end_poly | agg::path_flags_close;
	}
	return agg::path_cmd_stop;
    }
private:
    XRectangle m_rect;
    int m_idx;
};

template<class PixelFormat>
void
doDrawRect(Drawable d, GC gc, int x, int y, int w, int h)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    REGION *rgn = 0;
    Region tmpRgn = 0;
    TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);
    x += xOff;
    y += yOff;

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h, sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

#if 1
    /*
     * If the clipping region is specified, intersect it with the visible
     * region of the window, or if this is a pixmap then use the clipping
     * region unmodified.
     */
    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
	Region clipRgn = (Region) clipPtr->value.region;
	if (rgn) {
	    tmpRgn = SdlTkRgnPoolGet();
	    XIntersectRegion(rgn, clipRgn, tmpRgn);
	    rgn = tmpRgn;
	} else {
	    rgn = clipRgn;
	}
    }

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    /*
     * A 1-pixel thick line is inside the top-left, but outside the
     * bottom-right (that is what Tk expects and how Win32 draws it)
     */
    if (gc->line_width == 1) {
	agg::renderer_primitives<t_renderer_mclip> ren_prim(ren_mclip);
	ren_prim.line_color(c);
	ren_prim.rectangle(x, y, x + w, y + h);

    /* This handles 1-pixel thick lines correctly but is slower */
    } else {
	int thick = gc->line_width;
	int half = thick / 2;
	int noDups = thick; /* to avoid drawing pixels twice */
	ren_mclip.copy_bar(
	    x - half,
	    y - half,
	    x + w - half + thick - 1,
	    y - half + thick - 1, c); /* top */
	ren_mclip.copy_bar(
	    x - half,
	    y + h - half,
	    x + w - half + thick - 1,
	    y + h - half + thick - 1, c); /* bottom */
	ren_mclip.copy_bar(
	    x - half,
	    y - half + noDups,
	    x - half + thick - 1,
	    y + h - half + thick - 1 - noDups, c); /* left */
	ren_mclip.copy_bar(
	    x + w - half,
	    y - half + noDups,
	    x + w - half + thick - 1,
	    y + h - half + thick - 1 - noDups, c); /* right */
    }

#else /* aa */
    typedef agg::renderer_scanline_aa_solid<t_renderer_mclip> t_renderer_scanline_aa_solid;
    t_renderer_scanline_aa_solid ren_scanline(ren_mclip);
    ren_scanline.color(c);

    /*
     * A 1-pixel thick line appears as 2-pixel thick anti-aliased line.
     * But that is outside the bounds of a canvas rect item.
     */
    if (gc->line_width & 1) {
	x += 1;
	y += 1;
	w -= 2;
	h -= 2;
    }

    VertexSource_XRectangle vertexSrc(x, y, w, h);
    agg::conv_stroke<VertexSource_XRectangle> stroke(vertexSrc);
    stroke.width((double) gc->line_width);

    /* Thing that generates scanlines */
    agg::rasterizer_scanline_aa<> rasterizer;
    rasterizer.reset();
    rasterizer.add_path(stroke);

    /* Scanline needed by the rasterizer -> renderer */
    agg::scanline_u8 scanline;

    /*
     * If the clipping region is specified, intersect it with the visible
     * region of the window, or if this is a pixmap then use the clipping
     * region unmodified.
     */
    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
	Region clipRgn = (Region) clipPtr->value.region;
	if (rgn) {
	    tmpRgn = SdlTkRgnPoolGet();
	    XIntersectRegion(rgn, clipRgn, tmpRgn);
	    rgn = tmpRgn;
	} else {
	    rgn = clipRgn;
	}
    }

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    agg::render_scanlines(rasterizer, scanline, ren_scanline);
#endif

    if (tmpRgn) {
	SdlTkRgnPoolFree(tmpRgn);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

/*
 * This is a pixel-format renderer that simply XORs the destination pixel
 * For 3 bytes-per-pixel
 */
class pixfmt_3Bpp_xor
{
public:
    typedef agg::rendering_buffer::row_data row_data;
#ifdef AGG23
    typedef agg::rendering_buffer::span_data span_data;
#endif
    typedef agg::rgba8 color_type;

    //--------------------------------------------------------------------
    pixfmt_3Bpp_xor(agg::rendering_buffer& rb) :
	m_rbuf(&rb)
    {}

    //--------------------------------------------------------------------
    AGG_INLINE unsigned width()  const { return m_rbuf->width();  }
    AGG_INLINE unsigned height() const { return m_rbuf->height(); }

    //--------------------------------------------------------------------
    void copy_pixel(int x, int y, const color_type& c)
    {
#ifdef AGG23
	Uint8* p = (Uint8*)m_rbuf->row(y) + x;
#else
	Uint8* p = (Uint8*)m_rbuf->row_ptr(y) + x;
#endif
	p[0] ^= 0xFF;
	p[1] ^= 0xFF;
	p[2] ^= 0xFF;
    }

    //--------------------------------------------------------------------
    void copy_hline(int x, int y, 
			    unsigned len, 
			    const color_type& c)
    {
#ifdef AGG23
	Uint8* p = (Uint8*)m_rbuf->row(y) + x + x + x;
#else
	Uint8* p = (Uint8*)m_rbuf->row_ptr(y) + x + x + x;
#endif
	do {
	    p[0] ^= 0xFF;
	    p[1] ^= 0xFF;
	    p[2] ^= 0xFF;
	    p += 3;
	} while(--len);
    }

    //--------------------------------------------------------------------
    void blend_hline(int x, int y,
		    unsigned len, 
		    const color_type& c,
		    agg::int8u cover)
    {
#ifdef AGG23
	Uint8* p = (Uint8*)m_rbuf->row(y) + x + x + x;
#else
	Uint8* p = (Uint8*)m_rbuf->row_ptr(y) + x + x + x;
#endif
	do {
	    p[0] ^= 0xFF;
	    p[1] ^= 0xFF;
	    p[2] ^= 0xFF;
	    p += 3;
	} while(--len);
    }

    //--------------------------------------------------------------------
    void blend_vline(int x, int y,
		    unsigned len, 
		    const color_type& c,
		    agg::int8u cover)
    {
#ifdef AGG23
	Uint8* p = (Uint8*)m_rbuf->row(y) + x + x + x;
#else
	Uint8* p = (Uint8*)m_rbuf->row_ptr(y) + x + x + x;
#endif
	do {
	    p[0] ^= 0xFF;
	    p[1] ^= 0xFF;
	    p[2] ^= 0xFF;
#ifdef AGG23
	    p = (Uint8*)m_rbuf->next_row(p);
#else
	    p += m_rbuf->stride();
#endif
	} while(--len);
    }

private:
    agg::rendering_buffer* m_rbuf;
};

/*
 * This is a pixel-format renderer that simply XORs the destination pixel
 * For 1, 2 or 4 bytes-per-pixel
 */
template<class Type> class pixfmt_1_2_4Bpp_xor
{
public:
    typedef agg::rendering_buffer::row_data row_data;
#ifdef AGG23
    typedef agg::rendering_buffer::span_data span_data;
#endif
    typedef agg::rgba8 color_type;

    //--------------------------------------------------------------------
    pixfmt_1_2_4Bpp_xor(agg::rendering_buffer& rb) :
	m_rbuf(&rb)
    {}

    //--------------------------------------------------------------------
    AGG_INLINE unsigned width()  const { return m_rbuf->width();  }
    AGG_INLINE unsigned height() const { return m_rbuf->height(); }

    //--------------------------------------------------------------------
    void copy_pixel(int x, int y, const color_type& c)
    {
#ifdef AGG23
	Type* p = (Type*)m_rbuf->row(y) + x;
#else
	Type* p = (Type*)m_rbuf->row_ptr(y) + x;
#endif
	*p ^= 0xFFFFFFFF;
    }

    //--------------------------------------------------------------------
    void copy_hline(int x, int y, 
		    unsigned len, 
		    const color_type& c)
    {
#ifdef AGG23
	Type* p = (Type*)m_rbuf->row(y) + x;
#else
	Type* p = (Type*)m_rbuf->row_ptr(y) + x;
#endif
	do {
	    *p ^= 0xFFFFFFFF;
	    p += 1;
	} while(--len);
    }

    //--------------------------------------------------------------------
    void blend_hline(int x, int y,
		    unsigned len, 
		    const color_type& c,
		    agg::int8u cover)
    {
#ifdef AGG23
	Type* p = (Type*)m_rbuf->row(y) + x;
#else
	Type* p = (Type*)m_rbuf->row_ptr(y) + x;
#endif
	do {
	    *p ^= 0xFFFFFFFF;
	    p += 1;
	} while(--len);
    }

    //--------------------------------------------------------------------
    void blend_vline(int x, int y,
		    unsigned len, 
		    const color_type& c,
		    agg::int8u cover)
    {
#ifdef AGG23
	Type* p = (Type*)m_rbuf->row(y) + x;
#else
	Type* p = (Type*)m_rbuf->row_ptr(y) + x;
#endif
	do {
	    *p ^= 0xFFFFFFFF;
#ifdef AGG23
	    p = (Type*)m_rbuf->next_row(p);
#else
	    y++;
	    p = (Type*)m_rbuf->row_ptr(y) + x;
#endif
	} while(--len);
    }

private:
    agg::rendering_buffer* m_rbuf;
};

void
SdlTkGfxDrawRect(Drawable d, GC gc, int x, int y, int w, int h)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }

    if (gc->function == GXinvert) {
	switch (sdl->format->BitsPerPixel) {
	case 16:
	    doDrawRect<pixfmt_1_2_4Bpp_xor<Uint16> >(d, gc, x, y, w, h);
	    break;
	case 24:
	    doDrawRect<pixfmt_3Bpp_xor>(d, gc, x, y, w, h);
	    break;
	case 32:
	    doDrawRect<pixfmt_1_2_4Bpp_xor<Uint32> >(d, gc, x, y, w, h);
	    break;
	}
	return;
    }

    switch (format) {
    case SDLTK_RGB565:
	doDrawRect<agg::pixfmt_rgb565>(d, gc, x, y, w, h);
	break;
    case SDLTK_BGR565:
	doDrawRect<agg::pixfmt_bgr565>(d, gc, x, y, w, h);
	break;
    case SDLTK_RGB24:
	doDrawRect<agg::pixfmt_rgb24>(d, gc, x, y, w, h);
	break;
    case SDLTK_BGR24:
	doDrawRect<agg::pixfmt_bgr24>(d, gc, x, y, w, h);
	break;
    case SDLTK_RGBA32:
	doDrawRect<agg::pixfmt_rgba32>(d, gc, x, y, w, h);
	break;
    case SDLTK_ARGB32:
	doDrawRect<agg::pixfmt_argb32>(d, gc, x, y, w, h);
	break;
    case SDLTK_BGRA32:
	doDrawRect<agg::pixfmt_bgra32>(d, gc, x, y, w, h);
	break;
    case SDLTK_ABGR32:
	doDrawRect<agg::pixfmt_abgr32>(d, gc, x, y, w, h);
	break;
    case SDLTK_RGB555:
	doDrawRect<agg::pixfmt_rgb555>(d, gc, x, y, w, h);
	break;
    }
}

/*
 * Font manager/engine are protected by txt_mutex.
 */

TCL_DECLARE_MUTEX(txt_mutex);

typedef agg::font_engine_freetype_int16 t_font_engine;
typedef agg::font_cache_manager<t_font_engine> t_font_manager;
static t_font_engine *feng = 0;
static t_font_manager *fman = 0;

void
SdlTkGfxInitFC(void)
{
    Tcl_MutexLock(&txt_mutex);
    if (!feng) {
#ifdef AGG_CUSTOM_ALLOCATOR
	unsigned size;
	void *p;
	size = sizeof (t_font_engine);
	p = ckalloc(size);
	memset(p, 0, size);
	feng = new (p) t_font_engine;
	size = sizeof (t_font_manager);
	p = ckalloc(size);
	memset(p, 0, size);
	fman = new (p) t_font_manager(*feng);
#else
	feng = new t_font_engine;
	fman = new t_font_manager(*feng);
#endif
    }
    Tcl_MutexUnlock(&txt_mutex);
}

void
SdlTkGfxDeinitFC(void)
{
    Tcl_MutexLock(&txt_mutex);
    if (feng) {
#ifdef AGG_CUSTOM_ALLOCATOR
	void *p, *q;
	p = fman;
	q = feng;
	fman->~t_font_manager();
	feng->~t_font_engine();
	ckfree(p);
	ckfree(q);
#else
	delete fman;
	delete feng;
#endif
	fman = 0;
	feng = 0;
    }
    Tcl_MutexUnlock(&txt_mutex);
}

XFontStruct *
SdlTkGfxAllocFontStruct(_Font *_f)
{
    XFontStruct *fs = (XFontStruct *) ckalloc(sizeof (XFontStruct));

    memset(fs, 0, sizeof(XFontStruct));
    Tcl_MutexLock(&txt_mutex);
    fs->fid = (Font) _f;
    if (feng) {
	(void) feng->load_font(_f->file, _f->index, agg::glyph_ren_agg_gray8,
		(const char *) XGetFTStream(_f->file, _f->file_size));
	feng->flip_y(true);
	feng->height(_f->size);
	fs->ascent = (int) (feng->ascender() + 0.5);
	fs->descent = 0 - (int) (feng->descender() - 0.5);
    } else {
	fs->ascent = fs->descent = 1;
    }
    fs->max_bounds.width = 10; /* FIXME */
    Tcl_MutexUnlock(&txt_mutex);
    return fs;
}

extern "C" unsigned SdlTkGetNthGlyphIndex(_Font *_f, const char *s, int n);

int SdlTkGfxTextWidth(Font f, const char *string, int length, int *maxw)
{
    _Font *_f = (_Font *) f;
    int i;
    double w = 0.0;

    Tcl_MutexLock(&txt_mutex);
    if (!feng) {
	w = length;
	goto done;
    }
    (void) feng->load_font(_f->file, _f->index, agg::glyph_ren_agg_gray8,
	   (const char *) XGetFTStream(_f->file, _f->file_size));
    feng->flip_y(true);
    feng->height(_f->size);
    length /= sizeof(unsigned int) /* FcChar32 */;
    for (i = 0; i < length; i++) {
	const agg::glyph_cache *glyph = fman->glyph(SdlTkGetNthGlyphIndex(_f, string, i));
	if (glyph) {
	    w += glyph->advance_x;
	}
	if (maxw != NULL && w >= *maxw) {
	    *maxw = i;
	    break;
	}
    }
done:
    Tcl_MutexUnlock(&txt_mutex);
    return (int) w;
}

template<class PixelFormat>
void
doDrawString(Drawable d, GC gc, int x, int y, const char *string,
	     int length, double angle, int *xret, int *yret)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    _Font *_f = (_Font *) gc->font;
    double fx, fy;
    TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;
    REGION *rgn = 0;
    Region tmpRgn = 0;
    agg::glyph_rendering gr = agg::glyph_ren_native_gray8;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h,
	sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    fx = xOff + x;
    fy = yOff + y;

    if (angle != 0.0) {
	gr = agg::glyph_ren_agg_gray8;
    }

    /* agg::glyph_ren_agg_gray8 is BROKEN with MS Gothic japanese chars */
    (void) feng->load_font(_f->file, _f->index, gr,
	   (const char *) XGetFTStream(_f->file, _f->file_size));
    feng->flip_y(true);
    feng->height(_f->size);

    if (angle != 0.0) {
	agg::trans_affine mtx;

	mtx *= agg::trans_affine_rotation(agg::deg2rad(-angle));
	feng->transform(mtx);
    }

    /*
     * If the clipping region is specified, intersect it with the visible
     * region of the window, or if this is a pixmap then use the clipping
     * region unmodified.
     */
    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
	Region clipRgn = (Region) clipPtr->value.region;
	if (rgn) {
	    tmpRgn = SdlTkRgnPoolGet();
	    XIntersectRegion(rgn, clipRgn, tmpRgn);
	    rgn = tmpRgn;
	} else {
	    rgn = clipRgn;
	}
    }

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    length /= sizeof(unsigned int) /* FcChar32 */;

    /* FIXME: FillOpaqueStippled not implemented */
    if ((gc->fill_style == FillStippled
	    || gc->fill_style == FillOpaqueStippled)
	    && gc->stipple != None) {

	_Pixmap *stipple = (_Pixmap *) gc->stipple;

	/* Rendering buffer that points to the bitmap */
	agg::rendering_buffer stipple_buf((agg::int8u *) stipple->sdl->pixels,
	    stipple->sdl->w, stipple->sdl->h, stipple->sdl->pitch);

	/* A span allocator holds 1 line of pixels */
	agg::span_allocator<agg::rgba8> span_allocator;

	/* Generates spans (lines of pixels) from a source buffer */
#ifdef AGG23
	typedef span_stipple<agg::rgba8, agg::order_argb,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> t_span_stipple;
	/* FIXME: stippled text doesn't line up with other stippled primitives. */
	t_span_stipple span_stipple(span_allocator, stipple_buf, gc->ts_x_origin + 1, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_stipple);

	for (i = 0; i < length; i++) {
	    const agg::glyph_cache *glyph = fman->glyph(SdlTkGetNthGlyphIndex(_f, string, i));
	    if (glyph) {
		fman->init_embedded_adaptors(glyph, fx, fy);
		agg::render_scanlines(fman->gray8_adaptor(), fman->gray8_scanline(), ren_scanline_aa);
		fx += glyph->advance_x;
		fy += glyph->advance_y;
	    }
	}
#else
	typedef agg::image_accessor_wrap_gray8<PixelFormat,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> img_src_type;
	PixelFormat src_pixf(stipple_buf);
	img_src_type img_src(src_pixf);

	/* FIXME: stippled text doesn't line up with other stippled primitives. */
	typedef span_stipple<img_src_type> t_span_stipple;
	t_span_stipple span_stipple(img_src, gc->ts_x_origin + 1, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip,
	    agg::span_allocator<agg::rgba8>, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_allocator, span_stipple);

	for (i = 0; i < length; i++) {
	    const agg::glyph_cache *glyph = fman->glyph(SdlTkGetNthGlyphIndex(_f, string, i));
	    if (glyph) {
		fman->init_embedded_adaptors(glyph, fx, fy);
		agg::render_scanlines(fman->gray8_adaptor(), fman->gray8_scanline(), ren_scanline_aa);
		fx += glyph->advance_x;
		fy += glyph->advance_y;
	    }
	}
#endif
    } else {
	typedef agg::renderer_scanline_aa_solid<t_renderer_mclip> t_renderer_scanline_aa_solid;
	t_renderer_scanline_aa_solid ren_aa(ren_mclip);
	ren_aa.color(c);

	for (i = 0; i < length; i++) {
	    const agg::glyph_cache *glyph = fman->glyph(SdlTkGetNthGlyphIndex(_f, string, i));
	    if (glyph) {
		fman->init_embedded_adaptors(glyph, fx, fy);
		agg::render_scanlines(fman->gray8_adaptor(),
		    fman->gray8_scanline(), ren_aa);
		fx += glyph->advance_x;
		fy += glyph->advance_y;
	    }
	}
    }

    if (angle != 0.0) {
	agg::trans_affine mtx;
	feng->transform(mtx);
    }

    if (xret != NULL) {
	*xret = fx - xOff;
    }
    if (yret != NULL) {
	*yret = fy - yOff;
    }

    if (tmpRgn) {
	SdlTkRgnPoolFree(tmpRgn);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

template<class PixelFormat>
void
doDrawStringGray(Drawable d, GC gc, int x, int y, const char *string,
		 int length, double angle, int *xret, int *yret)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    _Font *_f = (_Font *) gc->font;
    double fx, fy;
    TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;
    REGION *rgn = 0;
    Region tmpRgn = 0;
    agg::glyph_rendering gr = agg::glyph_ren_native_gray8;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h,
	sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    fx = xOff + x;
    fy = yOff + y;

    if (angle != 0.0) {
	gr = agg::glyph_ren_agg_gray8;
    }

    /* agg::glyph_ren_agg_gray8 is BROKEN with MS Gothic japanese chars */
    (void) feng->load_font(_f->file, _f->index, gr,
	   (const char *) XGetFTStream(_f->file, _f->file_size));
    feng->flip_y(true);
    feng->height(_f->size);

    if (angle != 0.0) {
	agg::trans_affine mtx;

	mtx *= agg::trans_affine_rotation(agg::deg2rad(-angle));
	feng->transform(mtx);
    }

    /*
     * If the clipping region is specified, intersect it with the visible
     * region of the window, or if this is a pixmap then use the clipping
     * region unmodified.
     */
    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
	Region clipRgn = (Region) clipPtr->value.region;
	if (rgn) {
	    tmpRgn = SdlTkRgnPoolGet();
	    XIntersectRegion(rgn, clipRgn, tmpRgn);
	    rgn = tmpRgn;
	} else {
	    rgn = clipRgn;
	}
    }

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    length /= sizeof(unsigned int) /* FcChar32 */;

    typedef agg::renderer_scanline_aa_solid<t_renderer_mclip> t_renderer_scanline_aa_solid;
    t_renderer_scanline_aa_solid ren_aa(ren_mclip);
    ren_aa.color(c);

    for (i = 0; i < length; i++) {
        const agg::glyph_cache *glyph = fman->glyph(SdlTkGetNthGlyphIndex(_f, string, i));
	if (glyph) {
	    fman->init_embedded_adaptors(glyph, fx, fy);
	    agg::render_scanlines(fman->gray8_adaptor(),
		fman->gray8_scanline(), ren_aa);
	    fx += glyph->advance_x;
	    fy += glyph->advance_y;
	}
    }

    if (angle != 0.0) {
	agg::trans_affine mtx;
	feng->transform(mtx);
    }

    if (xret != NULL) {
        *xret = fx - xOff;
    }
    if (yret != NULL) {
        *yret = fy - yOff;
    }

    if (tmpRgn) {
	SdlTkRgnPoolFree(tmpRgn);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxDrawString(Drawable d, GC gc, int x, int y, const char *string,
		   int length, double angle, int *xret, int *yret)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }

    Tcl_MutexLock(&txt_mutex);

    if (!feng) {
	if (xret != NULL) {
	    *xret = x;
	}
	if (yret != NULL) {
	    *yret = y;
	}
	goto done;
    }

    switch (format) {
    case SDLTK_RGB565:
	doDrawString<agg::pixfmt_rgb565>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    case SDLTK_BGR565:
	doDrawString<agg::pixfmt_bgr565>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    case SDLTK_RGB24:
	doDrawString<agg::pixfmt_rgb24>(d, gc, x, y, string, length,
					angle, xret, yret);
	break;
    case SDLTK_BGR24:
	doDrawString<agg::pixfmt_bgr24>(d, gc, x, y, string, length,
					angle, xret, yret);
	break;
    case SDLTK_RGBA32:
	doDrawString<agg::pixfmt_rgba32>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    case SDLTK_ARGB32:
	doDrawString<agg::pixfmt_argb32>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    case SDLTK_BGRA32:
	doDrawString<agg::pixfmt_bgra32>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    case SDLTK_ABGR32:
	doDrawString<agg::pixfmt_abgr32>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    case SDLTK_GRAY8:
	doDrawStringGray<agg::pixfmt_gray8>(d, gc, x, y, string, length,
					    angle, xret, yret);
	break;
    case SDLTK_RGB555:
	doDrawString<agg::pixfmt_rgb555>(d, gc, x, y, string, length,
					 angle, xret, yret);
	break;
    }
done:
    Tcl_MutexUnlock(&txt_mutex);
}

template<class PixelFormat>
void
doFillArc(Drawable d, GC gc, int x, int y,
    unsigned int width, unsigned int height, int start, int extent)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    REGION *rgn = 0;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h,
	sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    /* Apparently agg::arc is deprecated */
    agg::bezier_arc arc(xOff + x + width / 2.0, yOff + y + height / 2.0,
	width / 2.0, height / 2.0,
	agg::deg2rad(start / 64.0), agg::deg2rad(extent / 64.0),
        gc->arc_mode == ArcPieSlice ? true : false);
    typedef agg::conv_curve<agg::bezier_arc, agg::curve3_div, agg::curve4_div> t_conv_curve;
    t_conv_curve curve(arc);

    /* Thing that generates scanlines */
    agg::rasterizer_scanline_aa<> rasterizer;
    rasterizer.reset();
    rasterizer.add_path(curve);

    /* Scanline needed by the rasterizer -> renderer */
    agg::scanline_u8 scanline;

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    /* FIXME: FillOpaqueStippled not implemented */
    if ((gc->fill_style == FillStippled
	    || gc->fill_style == FillOpaqueStippled)
	    && gc->stipple != None) {

	_Pixmap *stipple = (_Pixmap *) gc->stipple;

	/* Rendering buffer that points to the bitmap */
	agg::rendering_buffer stipple_buf((agg::int8u *) stipple->sdl->pixels,
	    stipple->sdl->w, stipple->sdl->h, stipple->sdl->pitch);

	/* A span allocator holds 1 line of pixels */
	agg::span_allocator<agg::rgba8> span_allocator;

	/* Generates spans (lines of pixels) from a source buffer */
#ifdef AGG23
	typedef span_stipple<agg::rgba8, agg::order_argb,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> t_span_stipple;
	t_span_stipple span_stipple(span_allocator, stipple_buf, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#else
	typedef agg::image_accessor_wrap_gray8<PixelFormat,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> img_src_type;
	PixelFormat src_pixf(stipple_buf);
	img_src_type img_src(src_pixf);

	typedef span_stipple<img_src_type> t_span_stipple;
	t_span_stipple span_stipple(img_src, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip,
	    agg::span_allocator<agg::rgba8>, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_allocator, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#endif
    } else {
	typedef agg::renderer_scanline_aa_solid<t_renderer_mclip> t_renderer_scanline_aa_solid;
	t_renderer_scanline_aa_solid ren_scanline(ren_mclip);
	ren_scanline.color(c);

	agg::render_scanlines(rasterizer, scanline, ren_scanline);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxFillArc(Drawable d, GC gc, int x, int y,
    unsigned int width, unsigned int height, int start, int extent)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }
    start = -start;
    extent = -extent;

    switch (format) {
    case SDLTK_RGB565:
	doFillArc<agg::pixfmt_rgb565>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_BGR565:
	doFillArc<agg::pixfmt_bgr565>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_RGB24:
	doFillArc<agg::pixfmt_rgb24>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_BGR24:
	doFillArc<agg::pixfmt_bgr24>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_RGBA32:
	doFillArc<agg::pixfmt_rgba32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_ARGB32:
	doFillArc<agg::pixfmt_argb32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_BGRA32:
	doFillArc<agg::pixfmt_bgra32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_ABGR32:
	doFillArc<agg::pixfmt_abgr32>(d, gc, x, y, width, height, start, extent);
	break;
    case SDLTK_RGB555:
	doFillArc<agg::pixfmt_rgb555>(d, gc, x, y, width, height, start, extent);
	break;
    }
}

template<class PixelFormat>
void
doFillPolygon(Drawable d, GC gc, XPoint *points, int npoints, int shape, int mode)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    REGION *rgn = 0;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h,
	sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    /* Thing that creates scanlines for a filled polygon (with aa) */
    agg::rasterizer_scanline_aa<> rasterizer;
    rasterizer.reset();
#if 1
    VertexSource_XPoints vertexSrc(points, npoints, xOff, yOff);
    rasterizer.add_path(vertexSrc);
#else
    rasterizer.move_to((xOff + points[0].x) << 8, (yOff + points[0].y) << 8);
    for (i = 1; i < npoints; i++)
	rasterizer.line_to((xOff + points[i].x) << 8, (yOff + points[i].y) << 8);
#endif
    /* Scanline needed by the rasterizer -> renderer */
    agg::scanline_u8 scanline;

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    /* FIXME: FillOpaqueStippled not implemented */
    if ((gc->fill_style == FillStippled
	    || gc->fill_style == FillOpaqueStippled)
	    && gc->stipple != None) {

	_Pixmap *stipple = (_Pixmap *) gc->stipple;

	/* Rendering buffer that points to the bitmap */
	agg::rendering_buffer stipple_buf((agg::int8u *) stipple->sdl->pixels,
	    stipple->sdl->w, stipple->sdl->h, stipple->sdl->pitch);

	/* A span allocator holds 1 line of pixels */
	agg::span_allocator<agg::rgba8> span_allocator;

	/* Generates spans (lines of pixels) from a source buffer */
#ifdef AGG23
	typedef span_stipple<agg::rgba8, agg::order_argb,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> t_span_stipple;
	t_span_stipple span_stipple(span_allocator, stipple_buf, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#else
	typedef agg::image_accessor_wrap_gray8<PixelFormat,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> img_src_type;
	PixelFormat src_pixf(stipple_buf);
	img_src_type img_src(src_pixf);

	typedef span_stipple<img_src_type> t_span_stipple;
	t_span_stipple span_stipple(img_src, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip,
	    agg::span_allocator<agg::rgba8>, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_allocator, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#endif
    } else {
	/* Thing that renders the scanlines */
	typedef agg::renderer_scanline_aa_solid<t_renderer_mclip> t_renderer_scanline_aa_solid;
	t_renderer_scanline_aa_solid ren_scanline(ren_mclip);
	ren_scanline.color(c);

	/* Thing that calculates the scanlines that make up the shape */
	agg::render_scanlines(rasterizer, scanline, ren_scanline);
    }

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxFillPolygon(Drawable d, GC gc, XPoint *points, int npoints, int shape, int mode)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }

    switch (format) {
    case SDLTK_RGB565:
	doFillPolygon<agg::pixfmt_rgb565>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_BGR565:
	doFillPolygon<agg::pixfmt_bgr565>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_RGB24:
	doFillPolygon<agg::pixfmt_rgb24>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_BGR24:
	doFillPolygon<agg::pixfmt_bgr24>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_RGBA32:
	doFillPolygon<agg::pixfmt_rgba32>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_ARGB32:
	doFillPolygon<agg::pixfmt_argb32>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_BGRA32:
	doFillPolygon<agg::pixfmt_bgra32>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_ABGR32:
	doFillPolygon<agg::pixfmt_abgr32>(d, gc, points, npoints, shape, mode);
	break;
    case SDLTK_RGB555:
	doFillPolygon<agg::pixfmt_rgb555>(d, gc, points, npoints, shape, mode);
	break;
    }
}

template<class PixelFormat>
void
doFillRect(Drawable d, GC gc, int x, int y, int w, int h)
{
    SDL_Surface *sdl;
    int xOff = 0, yOff = 0;
    long i;
    TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
    REGION *rgn = 0;
    Region tmpRgn = 0;

    if (IS_WINDOW(d)) {
	rgn = SdlTkGetVisibleRegion((_Window *) d);

	/* Window is unmapped or totally obscured */
	if (XEmptyRegion(rgn)) {
	    return;
	}
    }

    sdl = SdlTkGetDrawableSurface(d, &xOff, &yOff, NULL);
    x += xOff;
    y += yOff;

    /* Lock surface */
    if (SDL_MUSTLOCK(sdl)) {
	if (SDL_LockSurface(sdl) < 0) {
	    return;
	}
    }

    /* Rendering buffer, points to SDL_Surface memory */
    agg::rendering_buffer rbuf((agg::int8u *) sdl->pixels, sdl->w, sdl->h,
	sdl->pitch);

    /* Pixel-format renderer, a low-level pixel-rendering object */
    PixelFormat ren_pixf(rbuf);

    /* A basic renderer that does clipping to multiple boxes */
    typedef agg::renderer_mclip<PixelFormat> t_renderer_mclip;
    t_renderer_mclip ren_mclip(ren_pixf);

    /* The color, in the format used by the pixel-format renderer */
    Uint8 r, g, b;
    SDL_GetRGB(gc->foreground, SdlTkX.sdlsurf->format, &r, &g, &b);
    agg::rgba8 c(r, g, b);

    /*
     * If the clipping region is specified, intersect it with the visible
     * region of the window, or if this is a pixmap then use the clipping
     * region unmodified.
     */
    if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
	Region clipRgn = (Region) clipPtr->value.region;
	if (rgn) {
	    tmpRgn = SdlTkRgnPoolGet();
	    XIntersectRegion(rgn, clipRgn, tmpRgn);
	    rgn = tmpRgn;
	} else {
	    rgn = clipRgn;
	}
    }

    if (rgn) {
	for (i = 0; i < rgn->numRects; i++) {
	    BoxPtr box = &rgn->rects[i];
	    ren_mclip.add_clip_box(xOff + box->x1, yOff + box->y1,
		xOff + box->x2 - 1, yOff + box->y2 - 1);
	}
    }

    /* FIXME: FillOpaqueStippled not implemented */
    if ((gc->fill_style == FillStippled
	    || gc->fill_style == FillOpaqueStippled)
	    && gc->stipple != None) {

	_Pixmap *stipple = (_Pixmap *) gc->stipple;

	/* Rendering buffer that points to the bitmap */
	agg::rendering_buffer stipple_buf((agg::int8u *) stipple->sdl->pixels,
	    stipple->sdl->w, stipple->sdl->h, stipple->sdl->pitch);
#if 1
	agg::wrap_mode_repeat wrap_x(stipple_buf.width());
	agg::wrap_mode_repeat wrap_y(stipple_buf.height());
	unsigned wy = wrap_y(y - gc->ts_y_origin);
	while (h--) {
#ifdef AGG23
	    agg::int8u *row_ptr = stipple_buf.row(wy);
#else
	    agg::int8u *row_ptr = stipple_buf.row_ptr(wy);
#endif
	    unsigned x1 = x;
	    unsigned wx = wrap_x(x - gc->ts_x_origin);
	    unsigned w1 = w;
	    while (w1--) {
		agg::int8u *p = row_ptr + wx;
		if (!p[0]) {
		    ren_mclip.copy_pixel(x1, y, c);
		}
		wx = ++wrap_x;
		++x1;
	    }
	    wy = ++wrap_y;
	    ++y;
	}
#else
	/* A span allocator holds 1 line of pixels */
	agg::span_allocator<agg::rgba8> span_allocator;

	/* Generates spans (lines of pixels) from a source buffer */
#ifdef AGG23
	typedef span_stipple<agg::rgba8, agg::order_argb,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> t_span_stipple;
	t_span_stipple span_stipple(span_allocator, stipple_buf, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_stipple);
#else
	typedef agg::image_accessor_wrap_gray8<PixelFormat,
	    agg::wrap_mode_repeat, agg::wrap_mode_repeat> img_src_type;
	PixelFormat src_pixf(stipple_buf);
	img_src_type img_src(src_pixf);

	typedef span_stipple<img_src_type> t_span_stipple;
	t_span_stipple span_stipple(img_src, gc->ts_x_origin, gc->ts_y_origin);
	span_stipple.color(c);

	typedef agg::renderer_scanline_aa<t_renderer_mclip,
	    agg::span_allocator<agg::rgba8>, t_span_stipple> t_renderer_scanline_aa;
	t_renderer_scanline_aa ren_scanline_aa(ren_mclip, span_allocator, span_stipple);

	agg::render_scanlines(rasterizer, scanline, ren_scanline_aa);
#endif

	VertexSource_XRectangle vertexSrc(x, y, w, h);
	agg::rasterizer_scanline_aa<> rasterizer;
	rasterizer.reset();
	rasterizer.add_path(vertexSrc);

	/* Scanline needed by the rasterizer -> renderer */
	agg::scanline_u8 scanline;

	render_scanlines(rasterizer, scanline, ren_scanline_aa);
#endif
    } else {
	ren_mclip.copy_bar(x, y, x + w - 1, y + h - 1, c);
    }

    if (tmpRgn)
	SdlTkRgnPoolFree(tmpRgn);

    /* Unlock surface */
    if (SDL_MUSTLOCK(sdl)) {
	SDL_UnlockSurface(sdl);
    }
}

void
SdlTkGfxFillRect(Drawable d, GC gc, int x, int y, int w, int h)
{
    SDL_Surface *sdl;
    int format;

    sdl = SdlTkGetDrawableSurface(d, NULL, NULL, &format);
    if (sdl == NULL) {
	return;
    }

    if (gc->function == GXinvert) {
	switch (sdl->format->BitsPerPixel) {
	case 16:
	    doFillRect<pixfmt_1_2_4Bpp_xor<Uint16> >(d, gc, x, y, w, h);
	    break;
	case 24:
	    doFillRect<pixfmt_3Bpp_xor>(d, gc, x, y, w, h);
	    break;
	case 32:
	    doFillRect<pixfmt_1_2_4Bpp_xor<Uint32> >(d, gc, x, y, w, h);
	    break;
	}
	return;
    }

    switch (format) {
    case SDLTK_RGB565:
	doFillRect<agg::pixfmt_rgb565>(d, gc, x, y, w, h);
	break;
    case SDLTK_BGR565:
	doFillRect<agg::pixfmt_bgr565>(d, gc, x, y, w, h);
	break;
    case SDLTK_RGB24:
	doFillRect<agg::pixfmt_rgb24>(d, gc, x, y, w, h);
	break;
    case SDLTK_BGR24:
	doFillRect<agg::pixfmt_bgr24>(d, gc, x, y, w, h);
	break;
    case SDLTK_RGBA32:
	doFillRect<agg::pixfmt_rgba32>(d, gc, x, y, w, h);
	break;
    case SDLTK_ARGB32:
	doFillRect<agg::pixfmt_argb32>(d, gc, x, y, w, h);
	break;
    case SDLTK_BGRA32:
	doFillRect<agg::pixfmt_bgra32>(d, gc, x, y, w, h);
	break;
    case SDLTK_ABGR32:
	doFillRect<agg::pixfmt_abgr32>(d, gc, x, y, w, h);
	break;
    case SDLTK_RGB555:
	doFillRect<agg::pixfmt_rgb555>(d, gc, x, y, w, h);
	break;
    case SDLTK_GRAY8:
	doFillRect<agg::pixfmt_gray8>(d, gc, x, y, w, h);
	break;
    }
}

#ifndef AGG23

#undef JoinMiter
#undef JoinRound
#undef JoinBevel
#undef CapButt
#undef CapSquare
#undef CapRound

#include "agg2d.h"

void *
SdlTkXCreateAgg2D(Display *display)
{
    Agg2D *agg2d;
    if (display == NULL) {
	return NULL;
    }
#ifdef AGG_CUSTOM_ALLOCATOR
    agg2d = agg::obj_allocator<Agg2D>::allocate();
#else
    agg2d = new Agg2D();
#endif
    return (void *) agg2d;
}

void
SdlTkXDestroyAgg2D(Display *display, void *ptr)
{
    if ((ptr == NULL) || (display == NULL)) {
	return;
    }
    if ((ptr != display->agg2d) || (display->screens == NULL)) {
	Agg2D *agg2d = (Agg2D *) ptr;
#ifdef AGG_CUSTOM_ALLOCATOR
	agg::obj_allocator<Agg2D>::deallocate(agg2d);
#else
	delete agg2d;
#endif
	if (ptr == display->agg2d) {
	    display->agg2d = NULL;
	}
    }
}

void *
SdlTkXGetAgg2D(Display *display, Drawable d)
{
    _Pixmap *_p = (_Pixmap *) d;
    SDL_Surface *sdl;
    Agg2D *agg2d;

    if (display == NULL) {
	return NULL;
    }
    if ((_p != NULL) &&
	((_p->type != DT_PIXMAP) || (_p->format != SDLTK_BGRA32))) {
	return NULL;
    }
    if (display->agg2d == NULL) {
#ifdef AGG_CUSTOM_ALLOCATOR
	agg2d = agg::obj_allocator<Agg2D>::allocate();
#else
	agg2d = new Agg2D();
#endif
	display->agg2d = (void *) agg2d;
    } else {
	agg2d = (Agg2D *) display->agg2d;
    }
    sdl = (_p != NULL) ? _p->sdl : NULL;
    if (sdl != NULL) {
	agg2d->attach((unsigned char *) sdl->pixels,
		      sdl->w, sdl->h, sdl->pitch);
    } else {
	agg2d->attach(display->agg2d_dummyfb, 1, 1, sizeof (int));
    }
    return display->agg2d;
}

#endif

/*
 * Local Variables:
 * mode: c++
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
