The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#ifdef PERL_CAPI
#define WIN32IO_IS_STDIO
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <gd.h>
#include <gdfontg.h>
#include <gdfontl.h>
#include <gdfontmb.h>
#include <gdfonts.h>
#include <gdfontt.h>
#include <errno.h>

/* 2.0.x: < 2.1.0-alpha */
#ifndef GD_VERSION_STRING
# if defined(GD2_VERS) && (GD2_VERS==2)
#   ifdef VERSION_33
#    define GD_VERSION 20033
#    define GD_VERSION_STRING "2.0.33"
#   else
#    define GD_VERSION 20032
#    define GD_VERSION_STRING "2.0.x"
#   endif
# else
#   define GD_VERSION 10000
#   define GD_VERSION_STRING "?"
# endif
#else
#  define GD_VERSION ((GD_MAJOR_VERSION * 10000) +\
                      (GD_MINOR_VERSION * 100) +\
                      GD_RELEASE_VERSION)
#endif

#ifdef FCGI
 #include <fcgi_stdio.h>
#else
 #ifdef USE_SFIO
  #include <config.h>
 #else
  #include <stdio.h>
 #endif
 #include <perlio.h>
#endif
/* Copyright 1995 - 1998, Lincoln D. Stein.  See accompanying README file for
	usage restrictions */

#ifndef PERL_REVISION
#   ifndef __PATCHLEVEL_H_INCLUDED__
#       include "patchlevel.h"
#   endif
#   ifndef PERL_REVISION
#       define PERL_REVISION    (5)
        /* Replace: 1 */
#       define PERL_VERSION     PATCHLEVEL
#       define PERL_SUBVERSION  SUBVERSION
        /* Replace PERL_PATCHLEVEL with PERL_VERSION */
        /* Replace: 0 */
#   endif
#endif

#if (PERL_VERSION == 5) && (PERL_SUBVERSION==3)
#ifndef PL_na
# define PL_na na
#endif
#ifndef SvPV_nolen
# define SvPV_nolen(sv) SvPV(sv, PL_na)
#endif
#endif /* 5.00503 */

#ifndef dMY_CXT
# define dMY_CXT (void)0
#endif
#ifndef PERL_UNUSED_ARG
# define PERL_UNUSED_ARG(x) ((void)sizeof(x))
#endif

#ifndef mPUSHp
#define mPUSHp(p,l)	PUSHs(sv_2mortal(newSVpvn((p), (l))))
#endif
#ifndef mPUSHi
#define mPUSHi(i)	PUSHs(sv_2mortal(newSViv((i))))
#endif
#ifndef mPUSHn
#define mPUSHn(n)	PUSHs(sv_2mortal(newSVnv((n))))
#endif
#ifndef mXPUSHp
# define mXPUSHp(p,l)	STMT_START { EXTEND(sp,1); mPUSHp((p), (l)); } STMT_END
#endif
#ifndef mXPUSHi
# define mXPUSHi(i)	STMT_START { EXTEND(sp,1); mPUSHi((i)); } STMT_END
#endif
#ifndef mXPUSHn
# define mXPUSHn(i)	STMT_START { EXTEND(sp,1); mPUSHn((i)); } STMT_END
#endif
#ifndef hv_fetchs
# define hv_fetchs(H, K, L) hv_fetch((H), (K), sizeof(K)-1, (L))
#endif

#ifdef WIN32
# define snprintf _snprintf
#endif

#ifndef START_MY_CXT
static int truecolor_default = 0;
#endif

#include "const-c.inc"

typedef gdImagePtr	GD__Image;
typedef gdFontPtr	GD__Font;
typedef PerlIO          * InputStream;

#ifdef PERL_OBJECT
#  ifdef WIN32
#define GDIMAGECREATEFROMPNG(x)  gdImageCreateFromPng((FILE*)x)
#define GDIMAGECREATEFROMXBM(x)  gdImageCreateFromXbm((FILE*)x)
#define GDIMAGECREATEFROMJPEG(x) gdImageCreateFromJpeg((FILE*)x)
#define GDIMAGECREATEFROMGIF(x)  gdImageCreateFromGif((FILE*)x)
#define GDIMAGECREATEFROMWBMP(x) gdImageCreateFromWBMP((FILE*)x)
#define GDIMAGECREATEFROMGD(x)   gdImageCreateFromGd((FILE*)x)
#define GDIMAGECREATEFROMGD2(x)  gdImageCreateFromGd2((FILE*)x)
#define GDIMAGECREATEFROMGD2PART(x,a,b,c,d) gdImageCreateFromGd2Part((FILE*)x,a,b,c,d)
#  endif
#else
#  ifdef USE_PERLIO
#define GDIMAGECREATEFROMPNG(x) gdImageCreateFromPng(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMXBM(x) gdImageCreateFromXbm(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMJPEG(x) gdImageCreateFromJpeg(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMGIF(x)  gdImageCreateFromGif(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMWBMP(x) gdImageCreateFromWBMP(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMGD(x) gdImageCreateFromGd(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMGD2(x) gdImageCreateFromGd2(PerlIO_findFILE(x))
#define GDIMAGECREATEFROMGD2PART(x,a,b,c,d) gdImageCreateFromGd2Part(PerlIO_findFILE(x),a,b,c,d)
#  else
#define GDIMAGECREATEFROMPNG(x) gdImageCreateFromPng(x)
#define GDIMAGECREATEFROMXBM(x) gdImageCreateFromXbm(x)
#define GDIMAGECREATEFROMJPEG(x) gdImageCreateFromJpeg(x)
#define GDIMAGECREATEFROMGIF(x) gdImageCreateFromGif(x)
#define GDIMAGECREATEFROMWBMP(x) gdImageCreateFromWBMP(x)
#define GDIMAGECREATEFROMGD(x) gdImageCreateFromGd(x)
#define GDIMAGECREATEFROMGD2(x) gdImageCreateFromGd2(x)
#define GDIMAGECREATEFROMGD2PART(x,a,b,c,d) gdImageCreateFromGd2Part(x,a,b,c,d)
#  endif
#endif

#define littleendian(a) \
    (a[3]<<24)+(a[2]<<16)+(a[1]<<8)+a[0]

/* definitions required to create images from in-memory buffers */
		     
typedef struct bufIOCtx {
  gdIOCtx    ctx;
  char*      data;
  int        length;
  int        pos;
} bufIOCtx;

typedef struct bufIOCtx *bufIOCtxPtr;

static int bufGetC (gdIOCtxPtr ctx) {
  bufIOCtxPtr bctx = (bufIOCtxPtr) ctx;

  if (bctx->pos >= bctx->length) return EOF;
  return bctx->data[bctx->pos];
}

static int bufGetBuf (gdIOCtxPtr ctx, void* buf, int len) {
  bufIOCtxPtr bctx = (bufIOCtxPtr) ctx;
  int remain,rlen;

  remain = bctx->length - bctx->pos;
  if (remain >= len) {
    rlen = len;
  } else {
    if (remain <= 0) return EOF;
    rlen = remain;
  }
  memcpy(buf,(void*)(bctx->data + bctx->pos),rlen);
  bctx->pos += rlen;
  return rlen;
}

static int bufSeek (gdIOCtxPtr ctx, const int pos) {
  bufIOCtxPtr bctx = (bufIOCtxPtr) ctx;
  bctx->pos = pos;
  if (bctx->pos > bctx->length)
    bctx->pos = bctx->length;
  return TRUE;
}

static long bufTell (gdIOCtxPtr ctx) {
  bufIOCtxPtr bctx = (bufIOCtxPtr) ctx;
  return bctx->pos;
}

static void bufFree(gdIOCtxPtr ctx) {
  Safefree(ctx);
}

static gdIOCtx* newDynamicCtx (char* data, int length) {
  bufIOCtxPtr   ctx;
  
#ifdef Newz
  Newz(0,ctx,1,bufIOCtx);
#else
  Newxz(ctx,1,bufIOCtx);
#endif
  if (ctx == NULL) return NULL;
  ctx->data   = data;
  ctx->pos    = 0;
  ctx->length = length;

  ctx->ctx.getC   = bufGetC;
  ctx->ctx.getBuf = bufGetBuf;
  ctx->ctx.seek   = bufSeek;
  ctx->ctx.tell   = bufTell;
  ctx->ctx.gd_free = bufFree;
  ctx->ctx.putC   = NULL;
  ctx->ctx.putBuf = NULL;
  return (gdIOCtx*)ctx;
}

/* helper routines for image transformation */
static GD__Image
gd_cloneDim(GD__Image src, int x, int y) {
  GD__Image dst;
  if (gdImageTrueColor(src)) {
     dst = (GD__Image) gdImageCreateTrueColor(x,y);
     if (!dst)
       croak("gdImageCreateTrueColor error");
  } else {
     int i;
     dst = (GD__Image) gdImageCreatePalette(x,y);
     if (!dst)
       croak("gdImageCreatePalette error");
     /* copy across the palette information */
     for (i = 0; i < gdMaxColors; i++) {
	dst->red[i]   = src->red[i];
	dst->green[i] = src->green[i];
	dst->blue[i]  = src->blue[i];
	dst->alpha[i] = src->alpha[i];
	dst->open[i]  = src->open[i];
     }
     dst->colorsTotal = src->colorsTotal;
     dst->transparent = src->transparent;
     dst->interlace   = src->interlace;
     dst->thick       = src->thick;
  }
  return(dst);
}

void
get_xformbounds(GD__Image src, int *x, int *y,
			int *x1, int *y1, int *x2, int *y2)
{
   *x  = gdImageSX(src);
   *y  = gdImageSY(src);
   *x1 = *x - 1;
   *y1 = *y - 1;
   *x2 = *x / 2;
   *y2 = *y / 2;
}

/* helper macros for image transformations */
#define GDGetImagePixel(im,x,y) \
	gdImageTrueColor(im) ? \
	gdImageTrueColorPixel(im,x,y) : \
	gdImagePalettePixel(im,x,y)

#define GDSetImagePixel(im,x,y,p) \
	gdImageTrueColor(im) ? \
	(gdImageTrueColorPixel(im,x,y) = p) : \
	(gdImagePalettePixel(im,x,y) = p)

#define GDCopyImagePixel(dst,dx,dy,src,sx,sy) \
	gdImageTrueColor(src) ? \
	(gdImageTrueColorPixel(dst,dx,dy)=gdImageTrueColorPixel(src,sx,sy)) : \
	(gdImagePalettePixel(dst,dx,dy)=gdImagePalettePixel(src,sx,sy))

/* Check the image format being returned */
void
gd_chkimagefmt(GD__Image image, int truecolor) {
  if ((image != NULL)
      && !truecolor) {			/* return a palette image */
     if (gdImageTrueColor(image)) {
	gdImageTrueColorToPalette(image,1,gdMaxColors);
     }
  }
}

/* GLOBAL THREAD-SAFE DATA */

#ifdef START_MY_CXT

#define MY_CXT_KEY "GD::_guts" XS_VERSION
typedef struct {
  /* Current image true color default
   *  0 - create palette based images by default
   *  1 - create true color images by default
   */
  int truecolor_default;
} my_cxt_t;
#define truecolor_default MY_CXT.truecolor_default

START_MY_CXT
#endif

MODULE = GD		PACKAGE = GD

INCLUDE: const-xs.inc

void
VERSION_STRING()
    PPCODE:
    mXPUSHp(GD_VERSION_STRING,sizeof(GD_VERSION_STRING)-1);

void
LIBGD_VERSION()
    PPCODE:
    mXPUSHn(GD_VERSION/10000.0);

BOOT:
{
#ifdef START_MY_CXT
   MY_CXT_INIT;
#endif
   truecolor_default = 0;
}

MODULE = GD		PACKAGE = GD::Image	PREFIX=gd

# Set the new image true color default
#   0 - create palette based images by default
#   1 - create true color images by default
int
gdtrueColor(packname="GD::Image", ...)
	char *	packname
  PROTOTYPE: $$
  PREINIT:
        dMY_CXT;
        int previous_value = truecolor_default;
  CODE:
        PERL_UNUSED_ARG(packname);
        if (items > 1)
          truecolor_default = (int)SvIV(ST(1));
        RETVAL = previous_value;
  OUTPUT:
        RETVAL

GD::Image
gd_new(packname="GD::Image", x=64, y=64, ...)
	char *	packname
	int	x
	int	y
  PROTOTYPE: $;$$$
  PREINIT:
        gdImagePtr theImage;
        dMY_CXT;
        int truecolor = truecolor_default;
  CODE:
    PERL_UNUSED_ARG(packname);
    if (items > 3)
      truecolor = (int)SvIV(ST(3));
    if (truecolor) {
      theImage = (GD__Image) gdImageCreateTrueColor(x,y);
      if (!theImage)
        croak("gdImageCreateTrueColor error");
    } else {
      theImage = (GD__Image) gdImageCreate(x,y);
      if (!theImage)
        croak("gdImageCreate error");
    }
    RETVAL = theImage;
  OUTPUT:
    RETVAL

#ifdef HAVE_PNG
GD::Image
gd_newFromPng(packname="GD::Image", filehandle, ...)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$;$
  PREINIT:
        dMY_CXT;
        int truecolor = truecolor_default;
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = (GD__Image) GDIMAGECREATEFROMPNG(filehandle);
        if (!RETVAL)
          croak("gdImageCreateFromPng error");
        if (items > 2) truecolor = (int)SvIV(ST(2));
	gd_chkimagefmt(RETVAL, truecolor);
  OUTPUT:
	RETVAL

GD::Image
gdnewFromPngData(packname="GD::Image", imageData, ...)
	char *	packname
	SV *	imageData
  PROTOTYPE: $$;$
  PREINIT:
	gdIOCtx* ctx;
        char*    data;
        STRLEN   len;
	dMY_CXT;
        int truecolor = truecolor_default;
  CODE:
	PERL_UNUSED_ARG(packname);
	data = SvPV(imageData,len);
        ctx = newDynamicCtx(data,len);
	RETVAL = (GD__Image) gdImageCreateFromPngCtx(ctx);
        (ctx->gd_free)(ctx);
        if (!RETVAL)
          croak("gdImageCreateFromPngCtx error");
        if (items > 2) truecolor = (int)SvIV(ST(2));
	gd_chkimagefmt(RETVAL, truecolor);
  OUTPUT:
	RETVAL

#endif

GD::Image
gdnewFromGdData(packname="GD::Image", imageData)
	char *	packname
	SV *	imageData
  PROTOTYPE: $$
  PREINIT:
        char*    data;
        STRLEN   len;
  CODE:
	PERL_UNUSED_ARG(packname);
	data = SvPV(imageData,len);
	RETVAL = (GD__Image) gdImageCreateFromGdPtr(len,(void*) data);
        if (!RETVAL)
          croak("gdImageCreateFromGdPtr error");
  OUTPUT:
	RETVAL

GD::Image
gdnewFromGd2Data(packname="GD::Image", imageData)
	char *	packname
	SV *	imageData
  PROTOTYPE: $$
  PREINIT:
          char*    data;
          STRLEN   len;
  CODE:
        PERL_UNUSED_ARG(packname);
	data = SvPV(imageData,len);
	RETVAL = (GD__Image) gdImageCreateFromGd2Ptr(len,(void*) data);
        if (!RETVAL)
          croak("gdImageCreateFromGd2Ptr error");
  OUTPUT:
	RETVAL

#ifdef HAVE_JPEG
GD::Image
gdnewFromJpegData(packname="GD::Image", imageData, ...)
	char *	packname
	SV *    imageData
  PROTOTYPE: $$;$
  PREINIT:
	gdIOCtx* ctx;
        char*    data;
        STRLEN   len;
	dMY_CXT;
	int     truecolor = truecolor_default;
  CODE:
	PERL_UNUSED_ARG(packname);
	data = SvPV(imageData,len);
        ctx = newDynamicCtx(data,len);
        RETVAL = (GD__Image) gdImageCreateFromJpegCtx(ctx);
        (ctx->gd_free)(ctx);
        if (!RETVAL)
          croak("gdImageCreateFromJpegCtx error");
        if (items > 2) truecolor = (int)SvIV(ST(2));
	gd_chkimagefmt(RETVAL, truecolor);
  OUTPUT:
	RETVAL

#endif

GD::Image
gdnewFromWBMPData(packname="GD::Image", imageData, ...)
	char *	packname
	SV *    imageData
  PROTOTYPE: $$;$
  PREINIT:
	gdIOCtx* ctx;
        char*    data;
        STRLEN   len;
	dMY_CXT;
	int      truecolor = truecolor_default;
  CODE:
	PERL_UNUSED_ARG(packname);
	data = SvPV(imageData,len);
        ctx = newDynamicCtx(data,len);
	RETVAL = (GD__Image) gdImageCreateFromWBMPCtx(ctx);
        (ctx->gd_free)(ctx);
        if (!RETVAL)
          croak("gdImageCreateFromWBMPCtx error");
        if (items > 2) truecolor = (int)SvIV(ST(2));
	gd_chkimagefmt(RETVAL, truecolor);
  OUTPUT:
	RETVAL

GD::Image
gd_newFromXbm(packname="GD::Image", filehandle)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = GDIMAGECREATEFROMXBM(filehandle);
        if (!RETVAL)
          croak("gdImageCreateFromXbm error");
  OUTPUT:
	RETVAL

GD::Image
gd_newFromGd(packname="GD::Image", filehandle)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = GDIMAGECREATEFROMGD(filehandle);
        if (!RETVAL)
          croak("gdImageCreateFromGd error");
  OUTPUT:
	RETVAL

GD::Image
gd_newFromGd2(packname="GD::Image", filehandle)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = GDIMAGECREATEFROMGD2(filehandle);
        if (!RETVAL)
          croak("gdImageCreateFromGd2 error");
  OUTPUT:
	RETVAL

#ifdef HAVE_JPEG
GD::Image
gd_newFromJpeg(packname="GD::Image", filehandle, ...)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$;$
  PREINIT:
	dMY_CXT;
        int     truecolor = truecolor_default;
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = GDIMAGECREATEFROMJPEG(filehandle);
        if (!RETVAL)
          croak("gdImageCreateFromJpeg error");
        if (items > 2) truecolor = (int)SvIV(ST(2));
	gd_chkimagefmt(RETVAL, truecolor);
  OUTPUT:
        RETVAL

#endif

GD::Image
gd_newFromWBMP(packname="GD::Image", filehandle)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$
  PREINIT:
	gdImagePtr img;
	SV* errormsg;
  CODE:
	PERL_UNUSED_ARG(packname);
	img = GDIMAGECREATEFROMWBMP(filehandle);
        if (img == NULL) {
          errormsg = perl_get_sv("@",0);
	  if (errormsg != NULL)
	    sv_setpv(errormsg,"libgd was not built with WBMP support\n");
          else
            croak("gdImageCreateFromWbmp error");
	  XSRETURN_EMPTY;
        }
        RETVAL = img;
  OUTPUT:
        RETVAL

GD::Image
gdnewFromXpm(packname="GD::Image", filename)
	char *	packname
	char *	filename
  PROTOTYPE: $$
  PREINIT:
  	gdImagePtr img;
	SV* errormsg;
  CODE:
	PERL_UNUSED_ARG(packname);
#ifdef HAVE_XPM
        img = (GD__Image) gdImageCreateFromXpm(filename);
        if (img == NULL) {
            errormsg = perl_get_sv("@",0);
            if (errormsg != NULL)
              sv_setpv(errormsg,"libgd was not built with xpm support\n");
          else
            croak("gdImageCreateFromXpm error");
          XSRETURN_EMPTY;
        }
        RETVAL = img;
#else
        errormsg = perl_get_sv("@",0);
        sv_setpv(errormsg,"libgd was not built with xpm support\n");
        XSRETURN_EMPTY;
#endif
  OUTPUT:
        RETVAL

GD::Image
gd_newFromGd2Part(packname="GD::Image", filehandle,srcX,srcY,width,height)
	char *	packname
	InputStream	filehandle
	int	srcX
	int	srcY
	int	width
	int	height
  PROTOTYPE: $$$$$$
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = GDIMAGECREATEFROMGD2PART(filehandle,srcX,srcY,width,height);
        if (!RETVAL)
            croak("gdImageCreateFromGd2Part error");
  OUTPUT:
	RETVAL

#ifdef HAVE_GIF
GD::Image
gd_newFromGif(packname="GD::Image", filehandle)
	char *	packname
	InputStream	filehandle
  PROTOTYPE: $$;$
  CODE:
	PERL_UNUSED_ARG(packname);
	RETVAL = GDIMAGECREATEFROMGIF(filehandle);
	if (!RETVAL)
            croak("gdImageCreateFromGif error");
  OUTPUT:
	RETVAL

GD::Image
gdnewFromGifData(packname="GD::Image", imageData)
	char *	packname
	SV *    imageData
  PROTOTYPE: $$;$
  PREINIT:
	gdIOCtx* ctx;
        char*    data;
        STRLEN   len;
  CODE:
	PERL_UNUSED_ARG(packname);
	data = SvPV(imageData,len);
        ctx = newDynamicCtx(data,len);
	RETVAL = (GD__Image) gdImageCreateFromGifCtx(ctx);
        (ctx->gd_free)(ctx);
        if (!RETVAL)
           croak("gdImageCreateFromGifCtx error");
  OUTPUT:
	RETVAL

#endif

void
gdDESTROY(image)
	GD::Image	image
  PROTOTYPE: $
  CODE:
	gdImageDestroy(image);

SV* gdSTORABLE_freeze(image,cloning)
     GD::Image image
     int       cloning
  PROTOTYPE: $$
  CODE:
     {
       void*     data;
       int       size;

       if (cloning) XSRETURN_UNDEF;
       data = gdImageGd2Ptr(image,0,GD2_FMT_COMPRESSED,&size);
       if (!data)
         croak("gdImageGd2Ptr error");
       RETVAL = newSVpvn((char*)data,size);
       gdFree(data);
     }
  OUTPUT:
     RETVAL

void gdSTORABLE_thaw(object,cloning,serialized)
     SV*      object
     int      cloning
     SV*      serialized
  PREINIT:
     STRLEN    length;
     void*     data;
     GD__Image image;
  CODE:
     {
       if (cloning) XSRETURN_UNDEF;
       data = (void*) SvPV(serialized,length);
       image = gdImageCreateFromGd2Ptr(length,data);
       if (!image)
         croak("gdImageCreateFromGd2Ptr error");
       sv_setiv(SvRV(object),(IV)image);
     }

#ifdef HAVE_PNG
SV*
gdpng(image, ...)
	GD::Image	image
  PROTOTYPE: $;$
  PREINIT:
  CODE:
  {
	void*         data;
	int           size;
	int           level;
        if (items > 1) {
	  level = (int)SvIV(ST(1));
	  data  = (void *) gdImagePngPtrEx(image,&size,level);
          if (!data)
            croak("gdImagePngPtrEx error");
	} else {
	  data = (void *) gdImagePngPtr(image,&size);
          if (!data)
            croak("gdImagePngPtr error");
	}
	RETVAL = newSVpvn((char*) data,size);
	gdFree(data);
  }
  OUTPUT:
    RETVAL

#endif

#ifdef HAVE_JPEG
SV*
gdjpeg(image,quality=-1)
  GD::Image	image
  int           quality
  PROTOTYPE: $
  PREINIT:
	SV* errormsg;
	void*         data;
	int           size;
  CODE:
    data = (void *) gdImageJpegPtr(image,&size,quality);
    if (data == NULL) {
      errormsg = perl_get_sv("@",0);
      if (errormsg != NULL)
        sv_setpv(errormsg,"libgd was not built with jpeg support\n");
      else
        croak("gdImageJpegPtr error");
      XSRETURN_EMPTY;
    }
    RETVAL = newSVpvn((char*) data,size);
    gdFree(data);
  OUTPUT:
    RETVAL

#endif

SV*
gdgifanimbegin(image,globalcm=-1,loops=-1)
  GD::Image	image
  int           globalcm
  int           loops
  PROTOTYPE: $$$
  PREINIT:
	void*         data;
	int           size;
  CODE:
#ifdef HAVE_ANIMGIF
    data = (void *) gdImageGifAnimBeginPtr(image,&size,globalcm,loops);
    if (!data)
      croak("gdImageGifAnimBeginPtr error");
    RETVAL = newSVpvn((char*) data,size);
    gdFree(data);
#else
    die("libgd 2.0.33 or higher required for animated GIF support");
#endif
  OUTPUT:
    RETVAL

SV*
gdgifanimadd(image,localcm=-1,leftofs=-1,topofs=-1,delay=-1,disposal=-1,previm=0)
      GD::Image	image
      int           localcm
      int           leftofs
      int           topofs
      int           delay
      int           disposal
      GD::Image	previm
  PROTOTYPE: $$$$$$$
  PREINIT:
	void*         data;
	int           size;
  CODE:
#ifdef HAVE_ANIMGIF
    data = (void *) gdImageGifAnimAddPtr(image,&size,localcm,leftofs,topofs,
                                             delay,disposal,previm);
    if (!data)
      croak("gdImageGifAnimAddPtr error");
    RETVAL = newSVpvn((char*) data,size);
    gdFree(data);
#else
    die("libgd 2.0.33 or higher required for animated GIF support");
#endif
  OUTPUT:
    RETVAL

SV*
gdgifanimend(image)
  GD::Image	image
  PROTOTYPE: $
  PREINIT:
	void*         data;
	int           size;
  CODE:
    PERL_UNUSED_ARG(image);
#ifdef HAVE_ANIMGIF
    data = (void *) gdImageGifAnimEndPtr(&size);
    if (!data)
      croak("gdImageGifAnimEndPtr error");
    RETVAL = newSVpvn((char*) data,size);
    gdFree(data);
#else
    die("libgd 2.0.33 or higher required for animated GIF support");
#endif
  OUTPUT:
    RETVAL

SV*
gdwbmp(image,fg)
  GD::Image	image
  int           fg
  PROTOTYPE: $
  PREINIT:
	SV* errormsg;
	void*         data;
	int           size;
  CODE:
    data = (void *) gdImageWBMPPtr(image,&size,fg);
    if (data == NULL) {
      errormsg = perl_get_sv("@",0);
      if (errormsg != NULL)
        sv_setpv(errormsg,"libgd was not built with WBMP support\n");
      else
        croak("gdImageWBMPPtr error");
      XSRETURN_EMPTY;
    }
    RETVAL = newSVpvn((char*) data,size);
    gdFree(data);
  OUTPUT:
    RETVAL

#ifdef HAVE_GIF
SV*
gdgif(image)
  GD::Image	image
  PROTOTYPE: $
  PREINIT:
	SV* errormsg;
	void*         data;
	int           size;
  CODE:
    data = (void *) gdImageGifPtr(image,&size);
    if (data == NULL) {
      errormsg = perl_get_sv("@",0);
      if (errormsg != NULL)
        sv_setpv(errormsg,"libgd was not built with gif support\n");
      else
        croak("gdImageGifPtr error");
      XSRETURN_EMPTY;
    }
    RETVAL = newSVpvn((char*) data,size);
    gdFree(data);
  OUTPUT:
    RETVAL

#endif

SV*
gdgd(image)
  GD::Image	image
  PROTOTYPE: $
  CODE:
  {
	void*         data;
	int           size;
	data = gdImageGdPtr(image,&size);
        if (!data)
          croak("gdImageGdPtr error");
	RETVAL = newSVpvn((char*) data,size);
	gdFree(data);
  }
  OUTPUT:
    RETVAL

SV*
gdgd2(image)
  GD::Image	image
  PROTOTYPE: $
  CODE:
  {
	void*         data;
	int           size;
	data = gdImageGd2Ptr(image,0,GD2_FMT_COMPRESSED,&size);
        if (!data)
          croak("gdImageGd2Ptr error");
	RETVAL = newSVpvn((char*) data,size);
	gdFree(data);
  }
  OUTPUT:
    RETVAL

int
gdtransparent(image, ...)
	GD::Image	image
  PROTOTYPE: $;$
  PREINIT:
	int color;
  CODE:
	if (items > 1) {
          color=(int)SvIV(ST(1));
          gdImageColorTransparent(image,color);
        }
        RETVAL = gdImageGetTransparent(image);
  OUTPUT:
	RETVAL

void
gdgetBounds(image)
	GD::Image	image
  PROTOTYPE: $
  PPCODE:
    mXPUSHi(gdImageSX(image));
    mXPUSHi(gdImageSY(image));

int
gdisTrueColor(image)
	GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = gdImageTrueColor(image);
  OUTPUT:
    RETVAL

void
gdtrueColorToPalette(image, dither=0, colors=gdMaxColors)
	GD::Image	image
	int		dither
	int		colors
  PROTOTYPE: $;$$
  CODE:
    gdImageTrueColorToPalette(image,dither,colors);

GD::Image
gdcreatePaletteFromTrueColor(image, dither=0, colors=gdMaxColors)
	GD::Image	image
	int		dither
	int		colors
  PROTOTYPE: $;$$
  CODE:
    RETVAL = gdImageCreatePaletteFromTrueColor(image,dither,colors);
    if (!RETVAL)
      croak("gdImageCreatePaletteFromTrueColor error");
  OUTPUT:
    RETVAL

#if GD_VERSION >= 20100

GD::Image
gdneuQuant(image, colors=gdMaxColors, samplefactor=5)
	GD::Image	image
	int		colors
	int		samplefactor
  PROTOTYPE: $;$$
  CODE:
    RETVAL = gdImageNeuQuant(image,colors,samplefactor);
    if (!RETVAL)
      XSRETURN_UNDEF;
  OUTPUT:
    RETVAL

int
gdcolorMatch(image, im2)
	GD::Image	image
	GD::Image	im2
  PROTOTYPE: $$
  CODE:
    RETVAL = gdImageColorMatch(image,im2);
  OUTPUT:
    RETVAL

#endif

void
gdrgb(image,color)
	GD::Image	image
	int		color
  PROTOTYPE: $$
  PPCODE:
    mXPUSHi(gdImageRed(image,color));
    mXPUSHi(gdImageGreen(image,color));
    mXPUSHi(gdImageBlue(image,color));

void
gdalpha(image,color)
	GD::Image	image
	int		color
  PROTOTYPE: $$
  PPCODE:
    mXPUSHi(gdImageAlpha(image,color));

int
gdboundsSafe(image,x,y)
	GD::Image	image
	int		x
	int		y
  PROTOTYPE: $$$
  CODE:
    RETVAL = gdImageBoundsSafe(image,x,y);
    if (RETVAL == 0)
      XSRETURN_UNDEF;
  OUTPUT:
    RETVAL

int
gdgetPixel(image,x,y)
	GD::Image	image
	int		x
	int		y
  PROTOTYPE: $$$
  CODE:
    RETVAL = gdImageGetPixel(image,x,y);
  OUTPUT:
    RETVAL

void
gdsetPixel(image,x,y,color)
	GD::Image	image
	int		x
	int		y
	int		color
  PROTOTYPE: $$$$
  CODE:
    gdImageSetPixel(image,x,y,color);

GD::Image
gdcopyRotate90(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, y, x);

	for (j=0;j<y;j++) {
	   for (i=0;i<x;i++) {
	      GDCopyImagePixel(RETVAL,y1-j,i,src,i,j);
	   }
	}
  OUTPUT:
  	RETVAL

GD::Image
gdcopyRotate180(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, x, y);

	for (j=0;j<y;j++) {
          for (i=0;i<x;i++) {
            GDCopyImagePixel(RETVAL,x1-i,y1-j,src,i,j);
          }
        }
  OUTPUT:
	RETVAL

GD::Image
gdcopyRotate270(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, y, x);
		
	for (i=0;i<x;i++) {
	   for (j=0;j<y;j++) {
	      GDCopyImagePixel(RETVAL,j,x1-i,src,i,j);
	   }
	}
  OUTPUT:
	RETVAL

GD::Image
gdcopyFlipHorizontal(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, x, y);

	for (j=0;j<y;j++) {
	   for (i=0;i<x;i++) {
	      GDCopyImagePixel(RETVAL,x1-i,j,src,i,j);
	   }
	}
  OUTPUT:
	RETVAL

GD::Image
gdcopyFlipVertical(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, x, y);

	for (j=0;j<y;j++) {
          for (i=0;i<x;i++) {
            GDCopyImagePixel(RETVAL,i,y1-j,src,i,j);
          }
        }
  OUTPUT:
	RETVAL

GD::Image
gdcopyTranspose(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, x, y);

	for (j=0;j<y;j++) {
          for (i=0;i<x;i++) {
            GDCopyImagePixel(RETVAL,j,i,src,i,j);
          }
        }
  OUTPUT:
	RETVAL

GD::Image
gdcopyReverseTranspose(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);
	RETVAL = gd_cloneDim(src, x, y);

	for (j=0;j<y;j++) {
          for (i=0;i<x;i++) {
            GDCopyImagePixel(RETVAL,y1-j,x1-i,src,i,j);
          }
        }
  OUTPUT:
	RETVAL

void
gdrotate180(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);

	for (j=0; j<y2; j++) {
          for (i=0; i<x; i++) {
            int tmp = GDGetImagePixel(src,x1-i,y1-j);
            GDCopyImagePixel(src,x1-i,y1-j,src,i,j);
            GDSetImagePixel(src,i,j,tmp);
          }
        }
        if (y % 2 == 1) {
          for (i=0; i<x2; i++) {
            int tmp = GDGetImagePixel(src,x1-i,j);
            GDCopyImagePixel(src,x1-i,j,src,i,j);
            GDSetImagePixel(src,i,j,tmp);
          }
        }

void
gdcopyRotated(dst,src,dstX,dstY,srcX,srcY,srcW,srcH,angle)
        GD::Image       dst
	GD::Image	src
        double          dstX
        double          dstY
        int             srcX
        int             srcY
        int             srcW
        int             srcH
        int             angle
  PROTOTYPE: $$$$$$$$$
  CODE:
    {
#ifdef VERSION_33
        gdImageCopyRotated(dst,src,dstX,dstY,srcX,srcY,srcW,srcH,angle);
#else
        die("libgd 2.0.33 or higher required for copyRotated support");
#endif
    }

void
gdflipHorizontal(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);

	for (j=0;j<y;j++) {
          for (i=0;i<x2;i++) {
            int tmp = GDGetImagePixel(src,x1-i,j);
            GDCopyImagePixel(src,x1-i,j,src,i,j);
            GDSetImagePixel(src,i,j,tmp);
          }
        }

void
gdflipVertical(src)
	GD::Image	src
  PROTOTYPE: $
  PREINIT:
	int x, y, x1, y1, x2, y2, i, j;
  CODE:
	get_xformbounds(src, &x, &y, &x1, &y1, &x2, &y2);

	for (j=0; j<y2; j++) {
          for (i=0; i<x; i++) {
            int tmp = GDGetImagePixel(src,i,y1-j);
            GDCopyImagePixel(src,i,y1-j,src,i,j);
            GDSetImagePixel(src,i,j,tmp);
          }
        }

void
gdline(image,x1,y1,x2,y2,color)
	GD::Image	image
	int		x1
	int		y1
	int		x2
	int		y2
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageLine(image,x1,y1,x2,y2,color);

void
gddashedLine(image,x1,y1,x2,y2,color)
	GD::Image	image
	int		x1
	int		y1
	int		x2
	int		y2
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageDashedLine(image,x1,y1,x2,y2,color);

void
gdopenPolygon(image,poly,color)
	GD::Image	image
	SV *		poly
	int		color
  PROTOTYPE: $$$
  CODE:
	{
		dSP ;
		int length,count ;
		int x,y,i ;
		gdPointPtr polyptr;

		ENTER ;
		SAVETMPS ;
		PUSHMARK(sp) ;
		XPUSHs(poly) ;
		PUTBACK ;
		count = perl_call_method("length",G_SCALAR) ;
		SPAGAIN ;
		if (count != 1)
                  croak("Didn't get a single result from GD::Poly::length() call.\n");
		length = POPi ;
		PUTBACK ;
		FREETMPS ;
		LEAVE ;

		polyptr = (gdPointPtr)safemalloc(sizeof(gdPoint)*length);
		if (polyptr == NULL)
                  croak("safemalloc() returned NULL in GD::Image::poly().\n");
		
		for (i=0;i<length;i++) {
                  ENTER ;
                  SAVETMPS ;
                  PUSHMARK(sp) ;
                  XPUSHs(poly) ;
                  mXPUSHi(i) ;
                  PUTBACK ;
                  count = perl_call_method("getPt",G_ARRAY) ;
                  SPAGAIN ;
                  if (count != 2)
                    croak("Didn't get a single result from GD::Poly::length() call.\n");
                  y = POPi ;
                  x = POPi ;
                  PUTBACK ;
                  FREETMPS ;
                  LEAVE ;

                  polyptr[i].x = x;
                  polyptr[i].y = y;
		}

		gdImagePolygon(image,polyptr,length,color);
		safefree((char*) polyptr);
	}

void
gdunclosedPolygon(image,poly,color)
	GD::Image	image
	SV *		poly
	int		color
  PROTOTYPE: $$$
  CODE:
	{
		dSP ;
		int length,count ;
		int x,y,i ;
		gdPointPtr polyptr;
#ifdef HAVE_UNCLOSEDPOLY
		ENTER ;
		SAVETMPS ;
		PUSHMARK(sp) ;
		XPUSHs(poly) ;
		PUTBACK ;
		count = perl_call_method("length",G_SCALAR) ;
		SPAGAIN ;
		if (count != 1)
                  croak("Didn't get a single result from GD::Poly::length() call.\n");
		length = POPi ;
		PUTBACK ;
		FREETMPS ;
		LEAVE ;

		polyptr = (gdPointPtr)safemalloc(sizeof(gdPoint)*length);
		if (polyptr == NULL)
                  croak("safemalloc() returned NULL in GD::Image::poly().\n");
		
		for (i=0;i<length;i++) {
                  ENTER ;
                  SAVETMPS ;
                  PUSHMARK(sp) ;
                  XPUSHs(poly) ;
                  mXPUSHi(i) ;
                  PUTBACK ;
                  count = perl_call_method("getPt",G_ARRAY) ;
                  SPAGAIN ;
                  if (count != 2)
                    croak("Didn't get a single result from GD::Poly::length() call.\n");
                  y = POPi ;
                  x = POPi ;
                  PUTBACK ;
                  FREETMPS ;
                  LEAVE ;

                  polyptr[i].x = x;
                  polyptr[i].y = y;
		}

		gdImageOpenPolygon(image,polyptr,length,color);
		safefree((char*) polyptr);
#else
                die("libgd 2.0.33 or higher required for unclosed polygon support");
#endif
	}

void
gdfilledPolygon(image,poly,color)
	GD::Image	image
	SV *		poly
	int		color
  PROTOTYPE: $$$
  CODE:
	{
		dSP ;
		int length,count ;
		int x,y,i ;
		gdPointPtr polyptr;

		ENTER ;
		SAVETMPS ;
		PUSHMARK(sp) ;
		XPUSHs(poly) ;
		PUTBACK ;
		count = perl_call_method("length",G_SCALAR) ;
		SPAGAIN ;
		if (count != 1)
                  croak("Didn't get a single result from GD::Poly::length() call.\n");
		length = POPi ;
		PUTBACK ;
		FREETMPS ;
		LEAVE ;

		polyptr = (gdPointPtr)safemalloc(sizeof(gdPoint)*length);
		if (polyptr == NULL)
                  croak("safemalloc() returned NULL in GD::Image::poly().\n");
		
		for (i=0;i<length;i++) {
                  ENTER ;
                  SAVETMPS ;
                  PUSHMARK(sp) ;
                  XPUSHs(poly) ;
                  mXPUSHi(i) ;
                  PUTBACK ;
                  count = perl_call_method("getPt",G_ARRAY) ;
                  SPAGAIN ;
                  if (count != 2)
                    croak("Didn't get a single result from GD::Poly::length() call.\n");
                  y = POPi ;
                  x = POPi ;
                  PUTBACK ;
                  FREETMPS ;
                  LEAVE ;

                  polyptr[i].x = x;
                  polyptr[i].y = y;
		}

		gdImageFilledPolygon(image,polyptr,length,color);
		safefree((char*) polyptr);
	}

void
gdrectangle(image,x1,y1,x2,y2,color)
	GD::Image	image
	int		x1
	int		y1
	int		x2
	int		y2
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageRectangle(image,x1,y1,x2,y2,color);

void
gdfilledRectangle(image,x1,y1,x2,y2,color)
	GD::Image	image
	int		x1
	int		y1
	int		x2
	int		y2
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageFilledRectangle(image,x1,y1,x2,y2,color);

void
gdfilledEllipse(image,cx,cy,w,h,color)
	GD::Image	image
	int		cx
	int		cy
	int		w
	int		h
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageFilledEllipse(image,cx,cy,w,h,color);

void
gdarc(image,cx,cy,w,h,s,e,color)
	GD::Image	image
	int		cx
	int		cy
	int		w
	int		h
	int		s
	int		e
	int		color
  PROTOTYPE: $$$$$$$$
  CODE:
    gdImageArc(image,cx,cy,w,h,s,e,color);

void
gdfilledArc(image,cx,cy,w,h,s,e,color,arc_style=0)
	GD::Image	image
	int		cx
	int		cy
	int		w
	int		h
	int		s
	int		e
	int		color
	int		arc_style
  PROTOTYPE: $$$$$$$$$
  CODE:
    gdImageFilledArc(image,cx,cy,w,h,s,e,color,arc_style);

void
fillToBorder(image,x,y,border,color)
	GD::Image	image
	int		x
	int		y
	int		border
	int		color
  PROTOTYPE: $$$$$
  CODE:
    gdImageFillToBorder(image,x,y,border,color);

void
fill(image,x,y,color)
	GD::Image	image
	int		x
	int		y
	int		color
  PROTOTYPE: $$$$
  CODE:
    gdImageFill(image,x,y,color);

void
setBrush(image,brush)
	GD::Image	image
	GD::Image	brush
  PROTOTYPE: $$
  CODE:
    gdImageSetBrush(image,brush);

void
setTile(image,tile)
	GD::Image	image
	GD::Image	tile
  PROTOTYPE: $$
  CODE:
    gdImageSetTile(image,tile);

void
setThickness(image,thickness)
	GD::Image	image
	int		thickness
  PROTOTYPE: $$
  CODE:
    gdImageSetThickness(image,thickness);

void
setStyle(image, ...)
	GD::Image	image
  PROTOTYPE: $;$
  PREINIT:
	int	*style;
	int	i;
  CODE:
	if (items < 2)
          return;
	style = (int*) safemalloc(sizeof(int)*(items-1));
	if (style == NULL)
          croak("malloc returned NULL at setStyle().\n");
	for(i=1; i<items; i++) {
          style[i-1]=(int)SvIV(ST(i));
        }
	gdImageSetStyle(image,style,items-1);
	safefree((char*) style);

int
colorAllocate(image,r,g,b)
	GD::Image	image
	int		r
	int		g
	int		b
  PROTOTYPE: $$$$
  CODE:
    RETVAL = gdImageColorAllocate(image,r,g,b);
  OUTPUT:
    RETVAL

int
colorAllocateAlpha(image,r,g,b,a)
      GD::Image	image
      int		r
      int		g
      int		b
      int		a
  PROTOTYPE: $$$$$
  CODE:
    RETVAL = gdImageColorAllocateAlpha(image,r,g,b,a);
  OUTPUT:
    RETVAL

int
colorClosest(image,r,g,b)
      GD::Image	image
      int		r
      int		g
      int		b
  PROTOTYPE: $$$$
  CODE:
    RETVAL = gdImageColorClosest(image,r,g,b);
  OUTPUT:
    RETVAL

int
colorClosestAlpha(image,r,g,b,a)
      GD::Image	image
      int		r
      int		g
      int		b
      int		a
  PROTOTYPE: $$$$$
  CODE:
    RETVAL = gdImageColorClosestAlpha(image,r,g,b,a);
  OUTPUT:
    RETVAL

int
colorClosestHWB(image,r,g,b)
      GD::Image	image
      int		r
      int		g
      int		b
  PROTOTYPE: $$$$
  CODE:
    RETVAL = gdImageColorClosestHWB(image,r,g,b);
  OUTPUT:
    RETVAL

int
colorExact(image,r,g,b)
      GD::Image	image
      int		r
      int		g
      int		b
  PROTOTYPE: $$$$
  CODE:
    RETVAL = gdImageColorExact(image,r,g,b);
  OUTPUT:
    RETVAL

int
colorExactAlpha(image,r,g,b,a)
      GD::Image	image
      int		r
      int		g
      int		b
      int		a
  PROTOTYPE: $$$$$
  CODE:
    RETVAL = gdImageColorExactAlpha(image,r,g,b,a);
  OUTPUT:
    RETVAL

int
colorResolve(image,r,g,b)
      GD::Image	image
      int		r
      int		g
      int		b
  PROTOTYPE: $$$$
  CODE:
    RETVAL = gdImageColorResolve(image,r,g,b);
  OUTPUT:
    RETVAL

int
colorResolveAlpha(image,r,g,b,a)
      GD::Image	image
      int		r
      int		g
      int		b
      int		a
  PROTOTYPE: $$$$$
  CODE:
    RETVAL = gdImageColorResolveAlpha(image,r,g,b,a);
  OUTPUT:
    RETVAL

int
colorsTotal(image)
      GD::Image	image
      PROTOTYPE: $
  CODE:
    if (gdImageTrueColor(image))
      XSRETURN_UNDEF;
    RETVAL = gdImageColorsTotal(image);
  OUTPUT:
    RETVAL


int
interlaced(image, ...)
      GD::Image	image
  PROTOTYPE: $;$
  CODE:
    if (items > 1) {
      if (SvOK(ST(1)))
        gdImageInterlace(image,1);
      else
        gdImageInterlace(image,0);
    }
    RETVAL = gdImageGetInterlaced(image);
  OUTPUT:
    RETVAL

int
compare(image1,image2)
      GD::Image	image1
      GD::Image	image2
  PROTOTYPE: $$
  CODE:
    RETVAL = gdImageCompare(image1,image2);
  OUTPUT:
    RETVAL

void
colorDeallocate(image,color)
      GD::Image	image
      int		color
  PROTOTYPE: $$
  CODE:
    gdImageColorDeallocate(image,color);

void
copy(destination,source,dstX,dstY,srcX,srcY,w,h)
        GD::Image	destination
        GD::Image	source
	int		dstX
	int		dstY
	int		srcX
	int		srcY
	int		w
	int		h
  PROTOTYPE: $$$$$$$$
  CODE:
    gdImageCopy(destination,source,dstX,dstY,srcX,srcY,w,h);

void
copyResized(destination,source,dstX,dstY,srcX,srcY,destW,destH,srcW,srcH)
	GD::Image	destination
	GD::Image	source
	int		dstX
	int		dstY
	int		srcX
	int		srcY
	int		destW
	int		destH
	int		srcW
	int		srcH
  PROTOTYPE: $$$$$$$$$$
  CODE:
    gdImageCopyResized(destination,source,dstX,dstY,srcX,srcY,destW,destH,srcW,srcH);

void
copyResampled(destination,source,dstX,dstY,srcX,srcY,destW,destH,srcW,srcH)
	GD::Image	destination
	GD::Image	source
	int		dstX
	int		dstY
	int		srcX
	int		srcY
	int		destW
	int		destH
	int		srcW
	int		srcH
  PROTOTYPE: $$$$$$$$$$
  CODE:
    gdImageCopyResampled(destination,source,dstX,dstY,srcX,srcY,destW,destH,srcW,srcH);

void
copyMerge(destination,source,dstX,dstY,srcX,srcY,w,h,pct)
	GD::Image	destination
	GD::Image	source
	int		dstX
	int		dstY
	int		srcX
	int		srcY
	int		w
	int		h
        int             pct
  PROTOTYPE: $$$$$$$$$
  CODE:
    gdImageCopyMerge(destination,source,dstX,dstY,srcX,srcY,w,h,pct);

void
copyMergeGray(destination,source,dstX,dstY,srcX,srcY,w,h,pct)
	GD::Image	destination
	GD::Image	source
	int		dstX
	int		dstY
	int		srcX
	int		srcY
	int		w
	int		h
        int             pct
  PROTOTYPE: $$$$$$$$$
  CODE:
    gdImageCopyMergeGray(destination,source,dstX,dstY,srcX,srcY,w,h,pct);

void
paletteCopy(destination,source)
	GD::Image	destination
	GD::Image	source
  PROTOTYPE: $$
  CODE:
    gdImagePaletteCopy(destination,source);

void
gdchar(image,font,x,y,c,color)
	GD::Image	image
	GD::Font	font
	int		x
	int		y
	char *		c
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageChar(image,font,x,y,*c,color);

void
gdcharUp(image,font,x,y,c,color)
	GD::Image	image
	GD::Font	font
	int		x
	int		y
	char *		c
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageCharUp(image,font,x,y,*c,color);

void
gdstring(image,font,x,y,s,color)
	GD::Image	image
	GD::Font	font
	int		x
	int		y
	char *		s
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageString(image,font,x,y,(unsigned char*)s,color);

void
gdstringUp(image,font,x,y,s,color)
	GD::Image	image
	GD::Font	font
	int		x
	int		y
	char *		s
	int		color
  PROTOTYPE: $$$$$$
  CODE:
    gdImageStringUp(image,font,x,y,(unsigned char*)s,color);

void
gdstringFT(image,fgcolor,fontname,ptsize,angle,x,y,string,...)
        SV *	        image
        int             fgcolor
	char *          fontname
	double          ptsize
	double          angle
	int		x
        int             y
        char *          string
  PROTOTYPE: $$$$$$$$;$
  PREINIT:
	  gdImagePtr img;
	  int        brect[8];
	  char       *err;
          char       *a;
	  SV*        errormsg;
          HV*        hash;
          SV**       value;
	  int        i;
          int        hdpi;
          int        vdpi;
          gdFTStringExtra strex;
  PPCODE:
  {
#ifndef HAVE_FT
  	errormsg = perl_get_sv("@",0);
	sv_setpv(errormsg,"libgd was not built with FreeType font support\n");
	XSRETURN_EMPTY;
#endif
        if (sv_isobject(image) && sv_derived_from(image, "GD::Image")) {
          IV tmp = SvIV((SV*)SvRV(image));
          img = (gdImagePtr) tmp;
        } else {
          img = NULL;
        }

        if (items == 9) {  /* hashref options at end */
          if (SvTYPE(SvRV(ST(8))) != SVt_PVHV)
            croak ("Usage: $gd->stringFT(image,fgcolor,fontname,ptsize,angle,x,y,string,[{options}])");
          hash  = (HV*)SvRV(ST(8));
          strex.flags       = 0;
          strex.linespacing = 0;
          strex.charmap     = 0;
          if ((value = hv_fetchs(hash,"linespacing",0))) {
            strex.flags |= gdFTEX_LINESPACE;
            strex.linespacing = SvNV(*value);
          }
          if ((value = hv_fetchs(hash,"charmap",0))) {
            strex.flags |= gdFTEX_CHARMAP;
            if (strEQ(SvPV_nolen(*value),"Unicode"))
              strex.charmap = gdFTEX_Unicode;
            else if (strEQ(SvPV_nolen(*value),"Shift_JIS"))
              strex.charmap = gdFTEX_Shift_JIS;
            else if (strEQ(SvPV_nolen(*value),"Big5"))
              strex.charmap = gdFTEX_Big5;
            else
              croak("Unknown charmap %s",SvPV_nolen(*value));
          }
#ifdef VERSION_33
          if ((value = hv_fetchs(hash,"resolution",0))) {
            strex.flags |= gdFTEX_RESOLUTION;
            a = SvPV_nolen(*value);
            if (sscanf(a,"%d,%d",&hdpi,&vdpi) == 2) {
              strex.hdpi = hdpi;
              strex.vdpi = vdpi;
            }
          }
          if ((value = hv_fetchs(hash,"kerning",0))) {
            if (!SvTRUE(*value)) {
              strex.flags |= gdFTEX_DISABLE_KERNING;
            }
            else
              strex.flags &= gdFTEX_DISABLE_KERNING;
          }
#endif
          err = gdImageStringFTEx(img,brect,fgcolor,fontname,ptsize,angle,x,y,string,&strex);
        }

        else {
          err = gdImageStringFT(img,brect,fgcolor,fontname,ptsize,angle,x,y,string);
        }
        if (err) {
          errormsg = perl_get_sv("@",0);
          if (errormsg != NULL)
            sv_setpv(errormsg,err);
          XSRETURN_EMPTY;
        } else {
          EXTEND(sp,8);
          for (i=0;i<8;i++) {
            mPUSHi(brect[i]);
          }
        }
  }

int
gdstringFTCircle(image,cx,cy,radius,textRadius,fillPortion,fontname,points,top,bottom,fgcolor)
        GD::Image       image
        int             cx
        int             cy
        double          radius
        double          textRadius
        double          fillPortion
	char *          fontname
	double          points
        char *          top
        char *          bottom
        int             fgcolor
  PROTOTYPE: $$$$$$$$$$$
  PREINIT:
        char*      err;        
        SV*        errormsg;
  CODE:
  {
#ifdef HAVE_FT
#ifdef HAVE_FTCIRCLE
	fprintf(stderr,"cx=%d,cy=%d,radius=%f,textRadius=%f,fillPortion=%f,fontname=%s,points=%f,top=%s,bottom=%s,fgcolor=%d",
		cx,cy,radius,textRadius,
		fillPortion,fontname,points,top,bottom,fgcolor);
        err = gdImageStringFTCircle(image,cx,cy,radius,textRadius,
				    fillPortion,fontname,points,top,bottom,fgcolor);
        if (err) {
	    errormsg = perl_get_sv("@",0);
	    if (errormsg != NULL)
	      sv_setpv(errormsg,err);
	    XSRETURN_EMPTY;
	  } else {
            RETVAL = 1;
	  }
#else
  	errormsg = perl_get_sv("@",0);
	sv_setpv(errormsg,"libgd must be version 2.0.33 or higher to use this function\n");
	XSRETURN_EMPTY;
#endif
#else
  	errormsg = perl_get_sv("@",0);
	sv_setpv(errormsg,"libgd was not built with FreeType support\n");
	XSRETURN_EMPTY;
#endif
  }
  OUTPUT:
    RETVAL

int
gduseFontConfig(image,flag)
     SV*         image
     int         flag
  PROTOTYPE: $$
  CODE:
  {
#ifdef HAVE_FONTCONFIG
    PERL_UNUSED_ARG(image);
    RETVAL = gdFTUseFontConfig(flag);
#else
    SV* errormsg;
    PERL_UNUSED_ARG(image);
    errormsg = perl_get_sv("@",0);
    sv_setpv(errormsg,"libgd was not built with fontconfig support\n");
    XSRETURN_EMPTY;
#endif
  }
  OUTPUT:
    RETVAL

void
gdalphaBlending(image,blending)
     GD::Image       image
     int             blending
  PROTOTYPE: $$
  CODE:
    gdImageAlphaBlending(image,blending);

void
gdsaveAlpha(image,saveAlphaArg)
     GD::Image       image
     int             saveAlphaArg
  PROTOTYPE: $$
  CODE:
    gdImageSaveAlpha(image,saveAlphaArg);

void
gdclip(image,...)
	GD::Image	image
  PROTOTYPE: $;$$$$
  PREINIT:
        int		coords[4];
        int             i;
  PPCODE:
    if (items == 5) {
      for (i=0;i<4;i++)
        coords[i] = (int)SvIV(ST(i+1));
      gdImageSetClip(image,coords[0],coords[1],coords[2],coords[3]);
    }
    else if (items > 1) /* something weird */
      croak("Usage: $gd->clip() or $gd->clip(x1,x2,y1,y2)");
    
    gdImageGetClip(image,&coords[0],&coords[1],&coords[2],&coords[3]);
    EXTEND(sp,4);
    for (i=0;i<4;i++)
      mPUSHi(coords[i]);

void
gdsetAntiAliased(image,color)
     GD::Image       image
     int             color
  PROTOTYPE: $$
  CODE:
    gdImageSetAntiAliased(image,color);

void
gdsetAntiAliasedDontBlend(image,color,flag=1)
     GD::Image       image
     int             color
     int             flag
  PROTOTYPE: $$$
  CODE:
    gdImageSetAntiAliasedDontBlend(image,color,flag);

MODULE = GD		PACKAGE = GD::Font	PREFIX=gd

GD::Font
gdload(packname="GD::Font",fontpath)
     char * packname
     char * fontpath
  PROTOTYPE: $$
  PREINIT:
       int             fontfile;
       int             datasize;
       SV*             errormsg;
       char            errstr[256];
       gdFontPtr       font;
       unsigned char   word[4];
       char*           fontdata;
  CODE:
    PERL_UNUSED_ARG(packname);
    fontfile = open(fontpath,O_RDONLY);
    if (fontfile < 0) {
      errormsg = perl_get_sv("@",0);
      snprintf(errstr,256,"could not open font file %s: %s",fontpath,strerror(errno));
      sv_setpv(errormsg,errstr);
      XSRETURN_EMPTY;
    }
    font = (gdFontPtr)safemalloc(sizeof(gdFont));
    if (font == NULL)
      croak("safemalloc() returned NULL while trying to allocate font struct.\n");
    /* read header from font - note that the file is assumed to be littleendian*/
    if (read(fontfile,word,4) < 4)
      croak("error while reading font file: %s",strerror(errno));
    font->nchars = littleendian(word);

    if (read(fontfile,word,4) < 4)
      croak("error while reading font file: %s",strerror(errno));
    font->offset = littleendian(word);

    if (read(fontfile,word,4) < 4)
      croak("error while reading font file: %s",strerror(errno));
    font->w = littleendian(word);

    if (read(fontfile,word,4) < 4)
      croak("error while reading font file: %s",strerror(errno));
    font->h = littleendian(word);

    datasize = font->nchars * font->w * font->h;
    fontdata = (char*)safemalloc(datasize);
    if (fontdata == NULL)
      croak("safemalloc() returned NULL while trying to allocate font bitmap.\n");

    if (read(fontfile,fontdata,datasize) < datasize)
      croak("error while reading font file: %s",strerror(errno));

    font->data = fontdata;

    close(fontfile); /* please don't leak file descriptors! */
    RETVAL = font;
  OUTPUT:
    RETVAL

void
gdDESTROY(self)
     GD::Font      self
  PROTOTYPE: $
  CODE:
    if (self == gdFontGetSmall()      ||
        self == gdFontGetLarge()      ||
        self == gdFontGetGiant()      ||
        self == gdFontGetMediumBold() ||
        self == gdFontGetTiny() )
      XSRETURN_EMPTY;
    safefree(self->data);
    safefree(self);

GD::Font
gdSmall(packname="GD::Font")
	char *	packname
  PROTOTYPE: $
  CODE:
    PERL_UNUSED_ARG(packname);
    RETVAL = gdFontGetSmall();
    if (!RETVAL)
      croak("gdFontGetSmall error");
  OUTPUT:
    RETVAL

GD::Font
gdLarge(packname="GD::Font")
	char *	packname
  PROTOTYPE: $
  CODE:
    PERL_UNUSED_ARG(packname);
    RETVAL = gdFontGetLarge();
    if (!RETVAL)
      croak("gdFontGetLarge error");
  OUTPUT:
    RETVAL

GD::Font
gdGiant(packname="GD::Font")
	char *	packname
  PROTOTYPE: $
  CODE:
    PERL_UNUSED_ARG(packname);
    RETVAL = gdFontGetGiant();	
    if (!RETVAL)
      croak("gdFontGetGiant error");
  OUTPUT:
    RETVAL

GD::Font
gdMediumBold(packname="GD::Font")
	char *	packname
  PROTOTYPE: $
  CODE:
    PERL_UNUSED_ARG(packname);
    RETVAL = gdFontGetMediumBold();
    if (!RETVAL)
      croak("gdFontGetMediumBold error");
  OUTPUT:
    RETVAL

GD::Font
gdTiny(packname="GD::Font")
	char *	packname
  PROTOTYPE: $
  CODE:
    PERL_UNUSED_ARG(packname);
    RETVAL = gdFontGetTiny();
    if (!RETVAL)
      croak("gdFontGetTiny error");
  OUTPUT:
    RETVAL

int
gdnchars(font)
	GD::Font	font
  PROTOTYPE: $
  CODE:
    RETVAL = font->nchars;
  OUTPUT:
    RETVAL

int
gdoffset(font)
	GD::Font	font
  PROTOTYPE: $
  CODE:
    RETVAL = font->offset;
  OUTPUT:
    RETVAL

int
gdwidth(font)
	GD::Font	font
  PROTOTYPE: $
  CODE:
    RETVAL = font->w;
  OUTPUT:
    RETVAL

int
gdheight(font)
	GD::Font	font
  PROTOTYPE: $
  CODE:
    RETVAL = font->h;
  OUTPUT:
    RETVAL

# Image filters since 2.1.0

MODULE = GD		PACKAGE = GD::Image	PREFIX=gd
  
#if GD_VERSION >= 20100

bool
gdscatter(image, sub, plus)
      GD::Image	image
      int sub
      int plus
  PROTOTYPE: $$$
  CODE:
    RETVAL = (bool)gdImageScatter(image,sub,plus);
  OUTPUT:
    RETVAL

bool
gdscatterColor(image, sub, plus, colorav)
      GD::Image	image
      int sub
      int plus
      AV *colorav
  PROTOTYPE: $$$\@
  PREINIT:
	int i;
	int *colors;
	int numcolors;
  CODE:
    numcolors = AvFILL(colorav);
    colors = safemalloc(numcolors * sizeof(int));
    for (i=0;i<numcolors;i++) {
      SV** svp = av_fetch(colorav, i, 0);
      if (svp && SvIOK(*svp))
        colors[i] = SvIV(*svp);
    }
    RETVAL = (bool)gdImageScatterColor(image,sub,plus,colors,numcolors);
    safefree(colors);
  OUTPUT:
    RETVAL

bool
gdpixelate(image, blocksize, mode)
      GD::Image	image
      int blocksize
      int mode
  PROTOTYPE: $$$
  CODE:
    RETVAL = (bool)gdImagePixelate(image,blocksize,mode);
  OUTPUT:
    RETVAL
    
bool
gdnegate(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageNegate(image);
  OUTPUT:
    RETVAL

bool
gdgrayscale(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageGrayScale(image);
  OUTPUT:
    RETVAL

bool
gdbrightness(image, brightness)
      GD::Image	image
      int brightness
  PROTOTYPE: $$
  CODE:
    RETVAL = (bool)gdImageBrightness(image,brightness);
  OUTPUT:
    RETVAL

bool
gdcontrast(image, contrast)
      GD::Image	image
      NV contrast
  PROTOTYPE: $$
  CODE:
    RETVAL = (bool)gdImageContrast(image,(double)contrast);
  OUTPUT:
    RETVAL

bool
gdcolor(image, red, green, blue, alpha)
      GD::Image	image
      int red
      int green
      int blue
      int alpha
  PROTOTYPE: $$$$$
  CODE:
    RETVAL = (bool)gdImageColor(image,red,green,blue,alpha);
  OUTPUT:
    RETVAL

bool
gdselectiveBlur(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageSelectiveBlur(image);
  OUTPUT:
    RETVAL

bool
gdedgeDetectQuick(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageEdgeDetectQuick(image);
  OUTPUT:
    RETVAL

bool
gdgaussianBlur(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageGaussianBlur(image);
  OUTPUT:
    RETVAL

bool
gdemboss(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageEmboss(image);
  OUTPUT:
    RETVAL

bool
gdmeanRemoval(image)
      GD::Image	image
  PROTOTYPE: $
  CODE:
    RETVAL = (bool)gdImageMeanRemoval(image);
  OUTPUT:
    RETVAL

bool
gdsmooth(image, weight)
      GD::Image	image
      double weight
  PROTOTYPE: $$
  CODE:
    RETVAL = (bool)gdImageSmooth(image, (float)weight);
  OUTPUT:
    RETVAL

#endif
#if GD_VERSION >= 20200

GD::Image
gdcopyGaussianBlurred(image, radius, sigma)
      GD::Image	image
      int radius
      double sigma
  PROTOTYPE: $$$
  CODE:
    RETVAL = gdImageCopyGaussianBlurred(image, radius, sigma);
    if (!RETVAL)
       croak("gdImageCopyGaussianBlurred error");
  OUTPUT:
    RETVAL

#endif

# gd_interpolation

#if GD_VERSION >= 20100

GD::Image
gdcopyScaleInterpolated(image, width, height)
      GD::Image	image
      int width
      int height
  PROTOTYPE: $$$
  CODE:
    RETVAL = gdImageScale(image,width,height);
    if (!RETVAL)
      XSRETURN_UNDEF;
  OUTPUT:
    RETVAL

GD::Image
gdcopyRotateInterpolated(image, angle, bgcolor)
      GD::Image	image
      NV angle
      int bgcolor
  PROTOTYPE: $$$
  CODE:
    RETVAL = gdImageRotateInterpolated(image,(float)angle,bgcolor);
    if (!RETVAL)
      XSRETURN_UNDEF;
  OUTPUT:
    RETVAL

#endif
#if GD_VERSION >= 20200

int
interpolationMethod(image, interpolationmethod=-1)
      GD::Image	image
      int interpolationmethod
  PROTOTYPE: $;$
  CODE:
    if (items > 1 && interpolationmethod >= 0) {
      gdImageSetInterpolationMethod(image, interpolationmethod);
    }
    RETVAL = gdImageGetInterpolationMethod(image);
  OUTPUT:
    RETVAL

#gdTransformAffineGetImage
#gdTransformAffineCopy
#gdTransformAffineBoundingBox

#endif