The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#define PERL_NO_GET_CONTEXT
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#define NEED_newRV_noinc
#define NEED_sv_2pv_nolen
#define NEED_sv_2pvbyte
#include "ppport.h"
#ifdef __cplusplus
}
#endif

#define i_int_hlines_testing() 1

#include "imager.h"
#include "feat.h"
#include "dynaload.h"
#include "regmach.h"
#include "imextdef.h"
#include "imextpltypes.h"
#include "imperlio.h"
#include <float.h>

#if i_int_hlines_testing()
#include "imageri.h"
#endif

#include "imperl.h"

/*

Context object management

*/

typedef im_context_t Imager__Context;

#define im_context_DESTROY(ctx) im_context_refdec((ctx), "DESTROY")

#ifdef PERL_IMPLICIT_CONTEXT

#define MY_CXT_KEY "Imager::_context" XS_VERSION

typedef struct {
  im_context_t ctx;
} my_cxt_t;

START_MY_CXT

im_context_t fallback_context;

static void
start_context(pTHX) {
  dMY_CXT;
  MY_CXT.ctx = im_context_new();
  sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);

  /* Ideally we'd free this reference, but the error message memory
     was never released on exit, so the associated memory here is reasonable
     to keep.
     With logging enabled we always need at least one context, since
     objects may be released fairly late and attempt to get the log file.
  */
  im_context_refinc(MY_CXT.ctx, "start_context");
  fallback_context = MY_CXT.ctx;
}

static im_context_t
perl_get_context(void) {
  dTHX;
  dMY_CXT;
  
  return MY_CXT.ctx ? MY_CXT.ctx : fallback_context;
}

#else

static im_context_t perl_context;

static void
start_context(pTHX) {
  perl_context = im_context_new();
  im_context_refinc(perl_context, "start_context");
}

static im_context_t
perl_get_context(void) {
  return perl_context;
}

#endif

/* used to represent channel lists parameters */
typedef struct i_channel_list_tag {
  int *channels;
  int count;
} i_channel_list;

typedef struct {
  size_t count;
  const i_sample_t *samples;
} i_sample_list;

typedef struct {
  size_t count;
  const i_fsample_t *samples;
} i_fsample_list;

/*

Allocate memory that will be discarded when mortals are discarded.

*/

static void *
malloc_temp(pTHX_ size_t size) {
  SV *sv = sv_2mortal(newSV(size));

  return SvPVX(sv);
}

static void *
calloc_temp(pTHX_ size_t size) {
  void *result = malloc_temp(aTHX_ size);
  memset(result, 0, size);

  return result;
}

/* for use with the T_AVARRAY typemap */
#define doublePtr(size) ((double *)calloc_temp(aTHX_ sizeof(double) * (size)))
#define SvDouble(sv, pname) (SvNV(sv))

#define intPtr(size) ((int *)calloc_temp(aTHX_ sizeof(int) * (size)))
#define SvInt(sv, pname) (SvIV(sv))

#define i_img_dimPtr(size) ((i_img_dim *)calloc_temp(aTHX_ sizeof(i_img_dim) * (size)))
#define SvI_img_dim(sv, pname) (SvIV(sv))

#define i_colorPtr(size) ((i_color *)calloc_temp(aTHX_ sizeof(i_color *) * (size)))

#define SvI_color(sv, pname) S_sv_to_i_color(aTHX_ sv, pname)

static i_color
S_sv_to_i_color(pTHX_ SV *sv, const char *pname) {
  if (!sv_derived_from(sv, "Imager::Color")) {
    croak("%s: not a color object", pname);
  }
  return *INT2PTR(i_color *, SvIV((SV *)SvRV(sv)));
}

/* These functions are all shared - then comes platform dependant code */
static int getstr(void *hv_t,char *key,char **store) {
  dTHX;
  SV** svpp;
  HV* hv=(HV*)hv_t;

  mm_log((1,"getstr(hv_t %p, key %s, store %p)\n",hv_t,key,store));

  if ( !hv_exists(hv,key,strlen(key)) ) return 0;

  svpp=hv_fetch(hv, key, strlen(key), 0);
  *store=SvPV(*svpp, PL_na );

  return 1;
}

static int getint(void *hv_t,char *key,int *store) {
  dTHX;
  SV** svpp;
  HV* hv=(HV*)hv_t;  

  mm_log((1,"getint(hv_t %p, key %s, store %p)\n",hv_t,key,store));

  if ( !hv_exists(hv,key,strlen(key)) ) return 0;

  svpp=hv_fetch(hv, key, strlen(key), 0);
  *store=(int)SvIV(*svpp);
  return 1;
}

static int getdouble(void *hv_t,char* key,double *store) {
  dTHX;
  SV** svpp;
  HV* hv=(HV*)hv_t;

  mm_log((1,"getdouble(hv_t %p, key %s, store %p)\n",hv_t,key,store));

  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
  svpp=hv_fetch(hv, key, strlen(key), 0);
  *store=(double)SvNV(*svpp);
  return 1;
}

static int getvoid(void *hv_t,char* key,void **store) {
  dTHX;
  SV** svpp;
  HV* hv=(HV*)hv_t;

  mm_log((1,"getvoid(hv_t %p, key %s, store %p)\n",hv_t,key,store));

  if ( !hv_exists(hv,key,strlen(key)) ) return 0;

  svpp=hv_fetch(hv, key, strlen(key), 0);
  *store = INT2PTR(void*, SvIV(*svpp));

  return 1;
}

static int getobj(void *hv_t,char *key,char *type,void **store) {
  dTHX;
  SV** svpp;
  HV* hv=(HV*)hv_t;

  mm_log((1,"getobj(hv_t %p, key %s,type %s, store %p)\n",hv_t,key,type,store));

  if ( !hv_exists(hv,key,strlen(key)) ) return 0;

  svpp=hv_fetch(hv, key, strlen(key), 0);

  if (sv_derived_from(*svpp,type)) {
    IV tmp = SvIV((SV*)SvRV(*svpp));
    *store = INT2PTR(void*, tmp);
  } else {
    mm_log((1,"getobj: key exists in hash but is not of correct type"));
    return 0;
  }

  return 1;
}

UTIL_table_t i_UTIL_table={getstr,getint,getdouble,getvoid,getobj};

void my_SvREFCNT_dec(void *p) {
  dTHX;
  SvREFCNT_dec((SV*)p);
}


static void
i_log_entry(char *string, int level) {
  mm_log((level, "%s", string));
}

static SV *
make_i_color_sv(pTHX_ const i_color *c) {
  SV *sv;
  i_color *col = mymalloc(sizeof(i_color));
  *col = *c;
  sv = sv_newmortal();
  sv_setref_pv(sv, "Imager::Color", (void *)col);

  return sv;
}

#define CBDATA_BUFSIZE 8192

struct cbdata {
  /* the SVs we use to call back to Perl */
  SV *writecb;
  SV *readcb;
  SV *seekcb;
  SV *closecb;
};

static ssize_t
call_reader(struct cbdata *cbd, void *buf, size_t size, 
            size_t maxread) {
  dTHX;
  int count;
  int result;
  SV *data;
  dSP;

  if (!SvOK(cbd->readcb)) {
    mm_log((1, "read callback called but no readcb supplied\n"));
    i_push_error(0, "read callback called but no readcb supplied");
    return -1;
  }

  ENTER;
  SAVETMPS;
  EXTEND(SP, 2);
  PUSHMARK(SP);
  PUSHs(sv_2mortal(newSViv(size)));
  PUSHs(sv_2mortal(newSViv(maxread)));
  PUTBACK;

  count = perl_call_sv(cbd->readcb, G_SCALAR);

  SPAGAIN;

  if (count != 1)
    croak("Result of perl_call_sv(..., G_SCALAR) != 1");

  data = POPs;

  if (SvOK(data)) {
    STRLEN len;
    char *ptr = SvPVbyte(data, len);
    if (len > maxread)
      croak("Too much data returned in reader callback (wanted %d, got %d, expected %d)",
      (int)size, (int)len, (int)maxread);
    
    memcpy(buf, ptr, len);
    result = len;
  }
  else {
    result = -1;
  }

  PUTBACK;
  FREETMPS;
  LEAVE;

  return result;
}

static off_t
io_seeker(void *p, off_t offset, int whence) {
  dTHX;
  struct cbdata *cbd = p;
  int count;
  off_t result;
  dSP;

  if (!SvOK(cbd->seekcb)) {
    mm_log((1, "seek callback called but no seekcb supplied\n"));
    i_push_error(0, "seek callback called but no seekcb supplied");
    return -1;
  }

  ENTER;
  SAVETMPS;
  EXTEND(SP, 2);
  PUSHMARK(SP);
  PUSHs(sv_2mortal(newSViv(offset)));
  PUSHs(sv_2mortal(newSViv(whence)));
  PUTBACK;

  count = perl_call_sv(cbd->seekcb, G_SCALAR);

  SPAGAIN;

  if (count != 1)
    croak("Result of perl_call_sv(..., G_SCALAR) != 1");

  result = POPi;

  PUTBACK;
  FREETMPS;
  LEAVE;

  return result;
}

static ssize_t
io_writer(void *p, void const *data, size_t size) {
  dTHX;
  struct cbdata *cbd = p;
  I32 count;
  SV *sv;
  dSP;
  bool success;

  if (!SvOK(cbd->writecb)) {
    mm_log((1, "write callback called but no writecb supplied\n"));
    i_push_error(0, "write callback called but no writecb supplied");
    return -1;
  }

  ENTER;
  SAVETMPS;
  EXTEND(SP, 1);
  PUSHMARK(SP);
  PUSHs(sv_2mortal(newSVpv((char *)data, size)));
  PUTBACK;

  count = perl_call_sv(cbd->writecb, G_SCALAR);

  SPAGAIN;
  if (count != 1)
    croak("Result of perl_call_sv(..., G_SCALAR) != 1");

  sv = POPs;
  success = SvTRUE(sv);


  PUTBACK;
  FREETMPS;
  LEAVE;

  return success ? size : -1;
}

static ssize_t 
io_reader(void *p, void *data, size_t size) {
  struct cbdata *cbd = p;

  return call_reader(cbd, data, size, size);
}

static int io_closer(void *p) {
  dTHX;
  struct cbdata *cbd = p;
  int success = 1;

  if (SvOK(cbd->closecb)) {
    dSP;
    I32 count;

    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;

    count = perl_call_sv(cbd->closecb, G_SCALAR);

    SPAGAIN;
    
    if (count) {
      SV *sv = POPs;
      success = SvTRUE(sv);
    }
    else
      success = 0;

    PUTBACK;
    FREETMPS;
    LEAVE;
  }

  return success ? 0 : -1;
}

static void io_destroyer(void *p) {
  dTHX;
  struct cbdata *cbd = p;

  SvREFCNT_dec(cbd->writecb);
  SvREFCNT_dec(cbd->readcb);
  SvREFCNT_dec(cbd->seekcb);
  SvREFCNT_dec(cbd->closecb);
  myfree(cbd);
}

static i_io_glue_t *
do_io_new_buffer(pTHX_ SV *data_sv) {
  const char *data;
  STRLEN length;

  data = SvPVbyte(data_sv, length);
  SvREFCNT_inc(data_sv);
  return io_new_buffer(data, length, my_SvREFCNT_dec, data_sv);
}

static const char *
describe_sv(SV *sv) {
  if (SvOK(sv)) {
    if (SvROK(sv)) {
      svtype type = SvTYPE(SvRV(sv));
      switch (type) {
      case SVt_PVCV: return "CV";
      case SVt_PVGV: return "GV";
      case SVt_PVLV: return "LV";
      default: return "some reference";
      }
    }
    else {
      return "non-reference scalar";
    }
  }
  else {
    return "undef";
  }
}

static i_io_glue_t *
do_io_new_cb(pTHX_ SV *writecb, SV *readcb, SV *seekcb, SV *closecb) {
  struct cbdata *cbd;

  cbd = mymalloc(sizeof(struct cbdata));
  cbd->writecb = newSVsv(writecb);
  cbd->readcb = newSVsv(readcb);
  cbd->seekcb = newSVsv(seekcb);
  cbd->closecb = newSVsv(closecb);

  mm_log((1, "do_io_new_cb(writecb %p (%s), readcb %p (%s), seekcb %p (%s), closecb %p (%s))\n", writecb, describe_sv(writecb), readcb, describe_sv(readcb), seekcb, describe_sv(seekcb), closecb, describe_sv(closecb)));

  return io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, 
		   io_destroyer);
}

struct value_name {
  char *name;
  int value;
};
static int lookup_name(struct value_name *names, int count, char *name, int def_value)
{
  int i;
  for (i = 0; i < count; ++i)
    if (strEQ(names[i].name, name))
      return names[i].value;

  return def_value;
}
static struct value_name transp_names[] =
{
  { "none", tr_none },
  { "threshold", tr_threshold },
  { "errdiff", tr_errdiff },
  { "ordered", tr_ordered, },
};

static struct value_name make_color_names[] =
{
  { "none", mc_none, },
  { "webmap", mc_web_map, },
  { "addi", mc_addi, },
  { "mediancut", mc_median_cut, },
  { "mono", mc_mono, },
  { "monochrome", mc_mono, },
  { "gray", mc_gray, },
  { "gray4", mc_gray4, },
  { "gray16", mc_gray16, },
};

static struct value_name translate_names[] =
{
  { "giflib", pt_giflib, },
  { "closest", pt_closest, },
  { "perturb", pt_perturb, },
  { "errdiff", pt_errdiff, },
};

static struct value_name errdiff_names[] =
{
  { "floyd", ed_floyd, },
  { "jarvis", ed_jarvis, },
  { "stucki", ed_stucki, },
  { "custom", ed_custom, },
};

static struct value_name orddith_names[] =
{
  { "random", od_random, },
  { "dot8", od_dot8, },
  { "dot4", od_dot4, },
  { "hline", od_hline, },
  { "vline", od_vline, },
  { "/line", od_slashline, },
  { "slashline", od_slashline, },
  { "\\line", od_backline, },
  { "backline", od_backline, },
  { "tiny", od_tiny, },
  { "custom", od_custom, },
};

/* look through the hash for quantization options */
static void
ip_handle_quant_opts(pTHX_ i_quantize *quant, HV *hv)
{
  /*** POSSIBLY BROKEN: do I need to unref the SV from hv_fetch ***/
  SV **sv;
  int i;
  STRLEN len;
  char *str;

  quant->mc_colors = mymalloc(quant->mc_size * sizeof(i_color));

  sv = hv_fetch(hv, "transp", 6, 0);
  if (sv && *sv && (str = SvPV(*sv, len))) {
    quant->transp = 
      lookup_name(transp_names, sizeof(transp_names)/sizeof(*transp_names), 
		  str, tr_none);
    if (quant->transp != tr_none) {
      quant->tr_threshold = 127;
      sv = hv_fetch(hv, "tr_threshold", 12, 0);
      if (sv && *sv)
	quant->tr_threshold = SvIV(*sv);
    }
    if (quant->transp == tr_errdiff) {
      sv = hv_fetch(hv, "tr_errdiff", 10, 0);
      if (sv && *sv && (str = SvPV(*sv, len)))
	quant->tr_errdiff = lookup_name(errdiff_names, sizeof(errdiff_names)/sizeof(*errdiff_names), str, ed_floyd);
    }
    if (quant->transp == tr_ordered) {
      quant->tr_orddith = od_tiny;
      sv = hv_fetch(hv, "tr_orddith", 10, 0);
      if (sv && *sv && (str = SvPV(*sv, len)))
	quant->tr_orddith = lookup_name(orddith_names, sizeof(orddith_names)/sizeof(*orddith_names), str, od_random);

      if (quant->tr_orddith == od_custom) {
	sv = hv_fetch(hv, "tr_map", 6, 0);
	if (sv && *sv && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
	  AV *av = (AV*)SvRV(*sv);
	  len = av_len(av) + 1;
	  if (len > sizeof(quant->tr_custom))
	    len = sizeof(quant->tr_custom);
	  for (i = 0; i < len; ++i) {
	    SV **sv2 = av_fetch(av, i, 0);
	    if (sv2 && *sv2) {
	      quant->tr_custom[i] = SvIV(*sv2);
	    }
	  }
	  while (i < sizeof(quant->tr_custom))
	    quant->tr_custom[i++] = 0;
	}
      }
    }
  }
  quant->make_colors = mc_median_cut;
  sv = hv_fetch(hv, "make_colors", 11, 0);
  if (sv && *sv && (str = SvPV(*sv, len))) {
    quant->make_colors = 
      lookup_name(make_color_names, sizeof(make_color_names)/sizeof(*make_color_names), str, mc_median_cut);
  }
  sv = hv_fetch(hv, "colors", 6, 0);
  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
    /* needs to be an array of Imager::Color
       note that the caller allocates the mc_color array and sets mc_size
       to it's size */
    AV *av = (AV *)SvRV(*sv);
    quant->mc_count = av_len(av)+1;
    if (quant->mc_count > quant->mc_size)
      quant->mc_count = quant->mc_size;
    for (i = 0; i < quant->mc_count; ++i) {
      SV **sv1 = av_fetch(av, i, 0);
      if (sv1 && *sv1 && SvROK(*sv1) && sv_derived_from(*sv1, "Imager::Color")) {
	i_color *col = INT2PTR(i_color *, SvIV((SV*)SvRV(*sv1)));
	quant->mc_colors[i] = *col;
      }
    }
  }
  sv = hv_fetch(hv, "max_colors", 10, 0);
  if (sv && *sv) {
    i = SvIV(*sv);
    if (i <= quant->mc_size && i >= quant->mc_count)
      quant->mc_size = i;
  }

  quant->translate = pt_closest;
  sv = hv_fetch(hv, "translate", 9, 0);
  if (sv && *sv && (str = SvPV(*sv, len))) {
    quant->translate = lookup_name(translate_names, sizeof(translate_names)/sizeof(*translate_names), str, pt_closest);
  }
  sv = hv_fetch(hv, "errdiff", 7, 0);
  if (sv && *sv && (str = SvPV(*sv, len))) {
    quant->errdiff = lookup_name(errdiff_names, sizeof(errdiff_names)/sizeof(*errdiff_names), str, ed_floyd);
  }
  if (quant->translate == pt_errdiff && quant->errdiff == ed_custom) {
    /* get the error diffusion map */
    sv = hv_fetch(hv, "errdiff_width", 13, 0);
    if (sv && *sv)
      quant->ed_width = SvIV(*sv);
    sv = hv_fetch(hv, "errdiff_height", 14, 0);
    if (sv && *sv)
      quant->ed_height = SvIV(*sv);
    sv = hv_fetch(hv, "errdiff_orig", 12, 0);
    if (sv && *sv)
      quant->ed_orig = SvIV(*sv);
    if (quant->ed_width > 0 && quant->ed_height > 0) {
      int sum = 0;
      quant->ed_map = mymalloc(sizeof(int)*quant->ed_width*quant->ed_height);
      sv = hv_fetch(hv, "errdiff_map", 11, 0);
      if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
	AV *av = (AV*)SvRV(*sv);
	len = av_len(av) + 1;
	if (len > quant->ed_width * quant->ed_height)
	  len = quant->ed_width * quant->ed_height;
	for (i = 0; i < len; ++i) {
	  SV **sv2 = av_fetch(av, i, 0);
	  if (sv2 && *sv2) {
	    quant->ed_map[i] = SvIV(*sv2);
	    sum += quant->ed_map[i];
	  }
	}
      }
      if (!sum) {
	/* broken map */
	myfree(quant->ed_map);
	quant->ed_map = 0;
	quant->errdiff = ed_floyd;
      }
    }
  }
  sv = hv_fetch(hv, "perturb", 7, 0);
  if (sv && *sv)
    quant->perturb = SvIV(*sv);
}

static void
ip_cleanup_quant_opts(pTHX_ i_quantize *quant) {
  myfree(quant->mc_colors);
  if (quant->ed_map)
    myfree(quant->ed_map);
}

/* copies the color map from the hv into the colors member of the HV */
static void
ip_copy_colors_back(pTHX_ HV *hv, i_quantize *quant) {
  SV **sv;
  AV *av;
  int i;
  SV *work;

  sv = hv_fetch(hv, "colors", 6, 0);
  if (!sv || !*sv || !SvROK(*sv) || SvTYPE(SvRV(*sv)) != SVt_PVAV) {
    /* nothing to do */
    return;
  }

  av = (AV *)SvRV(*sv);
  av_clear(av);
  av_extend(av, quant->mc_count+1);
  for (i = 0; i < quant->mc_count; ++i) {
    i_color *in = quant->mc_colors+i;
    Imager__Color c = ICL_new_internal(in->rgb.r, in->rgb.g, in->rgb.b, 255);
    work = sv_newmortal();
    sv_setref_pv(work, "Imager::Color", (void *)c);
    SvREFCNT_inc(work);
    av_push(av, work);
  }
}

/* loads the segments of a fountain fill into an array */
static i_fountain_seg *
load_fount_segs(pTHX_ AV *asegs, int *count) {
  /* Each element of segs must contain:
     [ start, middle, end, c0, c1, segtype, colortrans ]
     start, middle, end are doubles from 0 to 1
     c0, c1 are Imager::Color::Float or Imager::Color objects
     segtype, colortrans are ints
  */
  int i, j;
  AV *aseg;
  i_fountain_seg *segs;
  double work[3];
  int worki[2];

  *count = av_len(asegs)+1;
  if (*count < 1) 
    croak("i_fountain must have at least one segment");
  segs = mymalloc(sizeof(i_fountain_seg) * *count);
  for(i = 0; i < *count; i++) {
    SV **sv1 = av_fetch(asegs, i, 0);
    if (!sv1 || !*sv1 || !SvROK(*sv1) 
        || SvTYPE(SvRV(*sv1)) != SVt_PVAV) {
      myfree(segs);
      croak("i_fountain: segs must be an arrayref of arrayrefs");
    }
    aseg = (AV *)SvRV(*sv1);
    if (av_len(aseg) != 7-1) {
      myfree(segs);
      croak("i_fountain: a segment must have 7 members");
    }
    for (j = 0; j < 3; ++j) {
      SV **sv2 = av_fetch(aseg, j, 0);
      if (!sv2 || !*sv2) {
        myfree(segs);
        croak("i_fountain: XS error");
      }
      work[j] = SvNV(*sv2);
    }
    segs[i].start  = work[0];
    segs[i].middle = work[1];
    segs[i].end    = work[2];
    for (j = 0; j < 2; ++j) {
      SV **sv3 = av_fetch(aseg, 3+j, 0);
      if (!sv3 || !*sv3 || !SvROK(*sv3) ||
          (!sv_derived_from(*sv3, "Imager::Color")
           && !sv_derived_from(*sv3, "Imager::Color::Float"))) {
        myfree(segs);
        croak("i_fountain: segs must contain colors in elements 3 and 4");
      }
      if (sv_derived_from(*sv3, "Imager::Color::Float")) {
        segs[i].c[j] = *INT2PTR(i_fcolor *, SvIV((SV *)SvRV(*sv3)));
      }
      else {
        i_color c = *INT2PTR(i_color *, SvIV((SV *)SvRV(*sv3)));
        int ch;
        for (ch = 0; ch < MAXCHANNELS; ++ch) {
          segs[i].c[j].channel[ch] = c.channel[ch] / 255.0;
        }
      }
    }
    for (j = 0; j < 2; ++j) {
      SV **sv2 = av_fetch(aseg, j+5, 0);
      if (!sv2 || !*sv2) {
        myfree(segs);
        croak("i_fountain: XS error");
      }
      worki[j] = SvIV(*sv2);
    }
    segs[i].type = worki[0];
    segs[i].color = worki[1];
  }

  return segs;
}

/* validates the indexes supplied to i_ppal

i_ppal() doesn't do that for speed, but I'm not comfortable doing that
for calls from perl.

*/
static void
validate_i_ppal(i_img *im, i_palidx const *indexes, int count) {
  int color_count = i_colorcount(im);
  int i;

  if (color_count == -1)
    croak("i_plin() called on direct color image");
  
  for (i = 0; i < count; ++i) {
    if (indexes[i] >= color_count) {
      croak("i_plin() called with out of range color index %d (max %d)",
        indexes[i], color_count-1);
    }
  }
}

/* I don't think ICLF_* names belong at the C interface
   this makes the XS code think we have them, to let us avoid 
   putting function bodies in the XS code
*/
#define ICLF_new_internal(r, g, b, a) i_fcolor_new((r), (g), (b), (a))
#define ICLF_DESTROY(cl) i_fcolor_destroy(cl)

#ifdef IMAGER_LOG
#define i_log_enabled() 1
#else
#define i_log_enabled() 0
#endif

#if i_int_hlines_testing()

typedef i_int_hlines *Imager__Internal__Hlines;

static i_int_hlines *
i_int_hlines_new(i_img_dim start_y, i_img_dim count_y, i_img_dim start_x, i_img_dim count_x) {
  i_int_hlines *result = mymalloc(sizeof(i_int_hlines));
  i_int_init_hlines(result, start_y, count_y, start_x, count_x);

  return result;
}

static i_int_hlines *
i_int_hlines_new_img(i_img *im) {
  i_int_hlines *result = mymalloc(sizeof(i_int_hlines));
  i_int_init_hlines_img(result, im);

  return result;
}

static void
i_int_hlines_DESTROY(i_int_hlines *hlines) {
  i_int_hlines_destroy(hlines);
  myfree(hlines);
}

#define i_int_hlines_CLONE_SKIP(cls) 1

static int seg_compare(const void *vleft, const void *vright) {
  const i_int_hline_seg *left = vleft;
  const i_int_hline_seg *right = vright;

  return left->minx - right->minx;
}

static SV *
i_int_hlines_dump(i_int_hlines *hlines) {
  dTHX;
  SV *dump = newSVpvf("start_y: %" i_DF " limit_y: %" i_DF " start_x: %" i_DF " limit_x: %" i_DF"\n",
	i_DFc(hlines->start_y), i_DFc(hlines->limit_y), i_DFc(hlines->start_x), i_DFc(hlines->limit_x));
  i_img_dim y;
  
  for (y = hlines->start_y; y < hlines->limit_y; ++y) {
    i_int_hline_entry *entry = hlines->entries[y-hlines->start_y];
    if (entry) {
      int i;
      /* sort the segments, if any */
      if (entry->count)
        qsort(entry->segs, entry->count, sizeof(i_int_hline_seg), seg_compare);

      sv_catpvf(dump, " %" i_DF " (%" i_DF "):", i_DFc(y), i_DFc(entry->count));
      for (i = 0; i < entry->count; ++i) {
        sv_catpvf(dump, " [%" i_DF ", %" i_DF ")", i_DFc(entry->segs[i].minx), 
                  i_DFc(entry->segs[i].x_limit));
      }
      sv_catpv(dump, "\n");
    }
  }

  return dump;
}

#endif

static off_t
i_sv_off_t(pTHX_ SV *sv) {
#if LSEEKSIZE > IVSIZE
  return (off_t)SvNV(sv);
#else
  return (off_t)SvIV(sv);
#endif
}

static SV *
i_new_sv_off_t(pTHX_ off_t off) {
#if LSEEKSIZE > IVSIZE
  return newSVnv(off);
#else
  return newSViv(off);
#endif
}

static im_pl_ext_funcs im_perl_funcs =
{
  IMAGER_PL_API_VERSION,
  IMAGER_PL_API_LEVEL,
  ip_handle_quant_opts,
  ip_cleanup_quant_opts,
  ip_copy_colors_back
};

#define PERL_PL_SET_GLOBAL_CALLBACKS \
  sv_setiv(get_sv(PERL_PL_FUNCTION_TABLE_NAME, 1), PTR2IV(&im_perl_funcs));

#define IIM_new i_img_8_new
#define IIM_DESTROY i_img_destroy
typedef int SysRet;

#ifdef IMEXIF_ENABLE
#define i_exif_enabled() 1
#else
#define i_exif_enabled() 0
#endif

/* trying to use more C style names, map them here */
#define i_io_DESTROY(ig) io_glue_destroy(ig)

#define i_img_get_width(im) ((im)->xsize)
#define i_img_get_height(im) ((im)->ysize)

#define i_img_epsilonf() (DBL_EPSILON * 4)

/* avoid some xsubpp strangeness */
#define NEWLINE '\n'

MODULE = Imager		PACKAGE = Imager::Color	PREFIX = ICL_

Imager::Color
ICL_new_internal(r,g,b,a)
               unsigned char     r
               unsigned char     g
               unsigned char     b
               unsigned char     a

void
ICL_DESTROY(cl)
               Imager::Color    cl


void
ICL_set_internal(cl,r,g,b,a)
               Imager::Color    cl
               unsigned char     r
               unsigned char     g
               unsigned char     b
               unsigned char     a
	   PPCODE:
	       ICL_set_internal(cl, r, g, b, a);
	       EXTEND(SP, 1);
	       PUSHs(ST(0));

void
ICL_info(cl)
               Imager::Color    cl


void
ICL_rgba(cl)
	      Imager::Color	cl
	    PPCODE:
		EXTEND(SP, 4);
		PUSHs(sv_2mortal(newSViv(cl->rgba.r)));
		PUSHs(sv_2mortal(newSViv(cl->rgba.g)));
		PUSHs(sv_2mortal(newSViv(cl->rgba.b)));
		PUSHs(sv_2mortal(newSViv(cl->rgba.a)));

Imager::Color
i_hsv_to_rgb(c)
        Imager::Color c
      CODE:
        RETVAL = mymalloc(sizeof(i_color));
        *RETVAL = *c;
        i_hsv_to_rgb(RETVAL);
      OUTPUT:
        RETVAL
        
Imager::Color
i_rgb_to_hsv(c)
        Imager::Color c
      CODE:
        RETVAL = mymalloc(sizeof(i_color));
        *RETVAL = *c;
        i_rgb_to_hsv(RETVAL);
      OUTPUT:
        RETVAL
        


MODULE = Imager        PACKAGE = Imager::Color::Float  PREFIX=ICLF_

Imager::Color::Float
ICLF_new_internal(r, g, b, a)
        double r
        double g
        double b
        double a

void
ICLF_DESTROY(cl)
        Imager::Color::Float    cl

void
ICLF_rgba(cl)
        Imager::Color::Float    cl
      PREINIT:
        int ch;
      PPCODE:
        EXTEND(SP, MAXCHANNELS);
        for (ch = 0; ch < MAXCHANNELS; ++ch) {
        /* printf("%d: %g\n", ch, cl->channel[ch]); */
          PUSHs(sv_2mortal(newSVnv(cl->channel[ch])));
        }

void
ICLF_set_internal(cl,r,g,b,a)
        Imager::Color::Float    cl
        double     r
        double     g
        double     b
        double     a
      PPCODE:
        cl->rgba.r = r;
        cl->rgba.g = g;
        cl->rgba.b = b;
        cl->rgba.a = a;                
        EXTEND(SP, 1);
        PUSHs(ST(0));

Imager::Color::Float
i_hsv_to_rgb(c)
        Imager::Color::Float c
      CODE:
        RETVAL = mymalloc(sizeof(i_fcolor));
        *RETVAL = *c;
        i_hsv_to_rgbf(RETVAL);
      OUTPUT:
        RETVAL
        
Imager::Color::Float
i_rgb_to_hsv(c)
        Imager::Color::Float c
      CODE:
        RETVAL = mymalloc(sizeof(i_fcolor));
        *RETVAL = *c;
        i_rgb_to_hsvf(RETVAL);
      OUTPUT:
        RETVAL

MODULE = Imager		PACKAGE = Imager::ImgRaw	PREFIX = IIM_

Imager::ImgRaw
IIM_new(x,y,ch)
               i_img_dim     x
	       i_img_dim     y
	       int     ch

void
IIM_DESTROY(im)
               Imager::ImgRaw    im



MODULE = Imager		PACKAGE = Imager

PROTOTYPES: ENABLE


Imager::IO
io_new_fd(fd)
                         int     fd

Imager::IO
io_new_bufchain()


Imager::IO
io_new_buffer(data_sv)
	  SV   *data_sv
	CODE:
	  RETVAL = do_io_new_buffer(aTHX_ data_sv);
        OUTPUT:
          RETVAL

Imager::IO
io_new_cb(writecb, readcb, seekcb, closecb, maxwrite = CBDATA_BUFSIZE)
        SV *writecb;
        SV *readcb;
        SV *seekcb;
        SV *closecb;
      CODE:
        RETVAL = do_io_new_cb(aTHX_ writecb, readcb, seekcb, closecb);
      OUTPUT:
        RETVAL

SV *
io_slurp(ig)
        Imager::IO     ig
	     PREINIT:
	      unsigned char*    data;
	      size_t    tlength;
	     CODE:
 	      data    = NULL;
              tlength = io_slurp(ig, &data);
              RETVAL = newSVpv((char *)data,tlength);
              myfree(data);
	     OUTPUT:
	      RETVAL


undef_int
i_set_image_file_limits(width, height, bytes)
	i_img_dim width
	i_img_dim height
	size_t bytes

void
i_get_image_file_limits()
      PREINIT:
        i_img_dim width, height;
	size_t bytes;
      PPCODE:
        if (i_get_image_file_limits(&width, &height, &bytes)) {
	  EXTEND(SP, 3);
          PUSHs(sv_2mortal(newSViv(width)));
          PUSHs(sv_2mortal(newSViv(height)));
          PUSHs(sv_2mortal(newSVuv(bytes)));
        }

bool
i_int_check_image_file_limits(width, height, channels, sample_size)
	i_img_dim width
	i_img_dim height
	int channels
	size_t sample_size
  PROTOTYPE: DISABLE

MODULE = Imager		PACKAGE = Imager::IO	PREFIX = io_

Imager::IO
io_new_fd(class, fd)
	int fd
    CODE:
	RETVAL = io_new_fd(fd);
    OUTPUT:
	RETVAL

Imager::IO
io_new_buffer(class, data_sv)
	SV *data_sv
    CODE:
        RETVAL = do_io_new_buffer(aTHX_ data_sv);
    OUTPUT:
        RETVAL

Imager::IO
io_new_cb(class, writecb, readcb, seekcb, closecb)
        SV *writecb;
        SV *readcb;
        SV *seekcb;
        SV *closecb;
    CODE:
        RETVAL = do_io_new_cb(aTHX_ writecb, readcb, seekcb, closecb);
    OUTPUT:
        RETVAL

Imager::IO
io_new_bufchain(class)
    CODE:
	RETVAL = io_new_bufchain();
    OUTPUT:
        RETVAL

Imager::IO
io__new_perlio(class, io)
	PerlIO *io
  CODE:
        RETVAL = im_io_new_perlio(aTHX_ io);
  OUTPUT:
	RETVAL

SV *
io_slurp(class, ig)
        Imager::IO     ig
    PREINIT:
	unsigned char*    data;
	size_t    tlength;
    CODE:
	data    = NULL;
	tlength = io_slurp(ig, &data);
	RETVAL = newSVpv((char *)data,tlength);
	myfree(data);
    OUTPUT:
	RETVAL

MODULE = Imager		PACKAGE = Imager::IO	PREFIX = i_io_

IV
i_io_raw_write(ig, data_sv)
	Imager::IO ig
	SV *data_sv
      PREINIT:
        void *data;
	STRLEN size;
      CODE:
	data = SvPVbyte(data_sv, size);
        RETVAL = i_io_raw_write(ig, data, size);
      OUTPUT:
	RETVAL

void
i_io_raw_read(ig, buffer_sv, size)
	Imager::IO ig
	SV *buffer_sv
	IV size
      PREINIT:
        void *buffer;
	ssize_t result;
      PPCODE:
        if (size <= 0)
	  croak("size negative in call to i_io_raw_read()");
        /* prevent an undefined value warning if they supplied an 
	   undef buffer.
           Orginally conditional on !SvOK(), but this will prevent the
	   downgrade from croaking */
	sv_setpvn(buffer_sv, "", 0);
#ifdef SvUTF8
	if (SvUTF8(buffer_sv))
          sv_utf8_downgrade(buffer_sv, FALSE);
#endif
	buffer = SvGROW(buffer_sv, size+1);
        result = i_io_raw_read(ig, buffer, size);
        if (result >= 0) {
	  SvCUR_set(buffer_sv, result);
	  *SvEND(buffer_sv) = '\0';
	  SvPOK_only(buffer_sv);
	  EXTEND(SP, 1);
	  PUSHs(sv_2mortal(newSViv(result)));
	}
	ST(1) = buffer_sv;
	SvSETMAGIC(ST(1));

void
i_io_raw_read2(ig, size)
	Imager::IO ig
	IV size
      PREINIT:
	SV *buffer_sv;
        void *buffer;
	ssize_t result;
      PPCODE:
        if (size <= 0)
	  croak("size negative in call to i_io_read2()");
	buffer_sv = newSV(size);
	buffer = SvGROW(buffer_sv, size+1);
        result = i_io_raw_read(ig, buffer, size);
        if (result >= 0) {
	  SvCUR_set(buffer_sv, result);
	  *SvEND(buffer_sv) = '\0';
	  SvPOK_only(buffer_sv);
	  EXTEND(SP, 1);
	  PUSHs(sv_2mortal(buffer_sv));
	}
	else {
          /* discard it */
	  SvREFCNT_dec(buffer_sv);
        }

off_t
i_io_raw_seek(ig, position, whence)
	Imager::IO ig
	off_t position
	int whence

int
i_io_raw_close(ig)
	Imager::IO ig

void
i_io_DESTROY(ig)
        Imager::IO     ig

int
i_io_CLONE_SKIP(...)
    CODE:
        (void)items; /* avoid unused warning for XS variable */
	RETVAL = 1;
    OUTPUT:
	RETVAL

int
i_io_getc(ig)
	Imager::IO ig

int
i_io_putc(ig, c)
	Imager::IO ig
        int c

int
i_io_close(ig)
	Imager::IO ig

int
i_io_flush(ig)
	Imager::IO ig

int
i_io_peekc(ig)
	Imager::IO ig

int
i_io_seek(ig, off, whence)
	Imager::IO ig
	off_t off
        int whence

void
i_io_peekn(ig, size)
	Imager::IO ig
	STRLEN size
      PREINIT:
	SV *buffer_sv;
        void *buffer;
	ssize_t result;
      PPCODE:
	buffer_sv = newSV(size+1);
	buffer = SvGROW(buffer_sv, size+1);
        result = i_io_peekn(ig, buffer, size);
        if (result >= 0) {
	  SvCUR_set(buffer_sv, result);
	  *SvEND(buffer_sv) = '\0';
	  SvPOK_only(buffer_sv);
	  EXTEND(SP, 1);
	  PUSHs(sv_2mortal(buffer_sv));
	}
	else {
          /* discard it */
	  SvREFCNT_dec(buffer_sv);
        }

void
i_io_read(ig, buffer_sv, size)
	Imager::IO ig
	SV *buffer_sv
	IV size
      PREINIT:
        void *buffer;
	ssize_t result;
      PPCODE:
        if (size <= 0)
	  croak("size negative in call to i_io_read()");
        /* prevent an undefined value warning if they supplied an 
	   undef buffer.
           Orginally conditional on !SvOK(), but this will prevent the
	   downgrade from croaking */
	sv_setpvn(buffer_sv, "", 0);
#ifdef SvUTF8
	if (SvUTF8(buffer_sv))
          sv_utf8_downgrade(buffer_sv, FALSE);
#endif
	buffer = SvGROW(buffer_sv, size+1);
        result = i_io_read(ig, buffer, size);
        if (result >= 0) {
	  SvCUR_set(buffer_sv, result);
	  *SvEND(buffer_sv) = '\0';
	  SvPOK_only(buffer_sv);
	  EXTEND(SP, 1);
	  PUSHs(sv_2mortal(newSViv(result)));
	}
	ST(1) = buffer_sv;
	SvSETMAGIC(ST(1));

void
i_io_read2(ig, size)
	Imager::IO ig
	STRLEN size
      PREINIT:
	SV *buffer_sv;
        void *buffer;
	ssize_t result;
      PPCODE:
        if (size == 0)
	  croak("size zero in call to read2()");
	buffer_sv = newSV(size);
	buffer = SvGROW(buffer_sv, size+1);
        result = i_io_read(ig, buffer, size);
        if (result > 0) {
	  SvCUR_set(buffer_sv, result);
	  *SvEND(buffer_sv) = '\0';
	  SvPOK_only(buffer_sv);
	  EXTEND(SP, 1);
	  PUSHs(sv_2mortal(buffer_sv));
	}
	else {
          /* discard it */
	  SvREFCNT_dec(buffer_sv);
        }

void
i_io_gets(ig, size = 8192, eol = NEWLINE)
	Imager::IO ig
	STRLEN size
	int eol
      PREINIT:
	SV *buffer_sv;
        void *buffer;
	ssize_t result;
      PPCODE:
        if (size < 2)
	  croak("size too small in call to gets()");
	buffer_sv = sv_2mortal(newSV(size+1));
	buffer = SvPVX(buffer_sv);
        result = i_io_gets(ig, buffer, size+1, eol);
        if (result > 0) {
	  SvCUR_set(buffer_sv, result);
	  *SvEND(buffer_sv) = '\0';
	  SvPOK_only(buffer_sv);
	  EXTEND(SP, 1);
	  PUSHs(buffer_sv);
	}

IV
i_io_write(ig, data_sv)
	Imager::IO ig
	SV *data_sv
      PREINIT:
        void *data;
	STRLEN size;
      CODE:
	data = SvPVbyte(data_sv, size);
        RETVAL = i_io_write(ig, data, size);
      OUTPUT:
	RETVAL

void
i_io_dump(ig, flags = I_IO_DUMP_DEFAULT)
	Imager::IO ig
	int flags

bool
i_io_set_buffered(ig, flag = 1)
	Imager::IO ig
	int flag

bool
i_io_is_buffered(ig)
	Imager::IO ig

bool
i_io_eof(ig)
	Imager::IO ig

bool
i_io_error(ig)
	Imager::IO ig

MODULE = Imager		PACKAGE = Imager

PROTOTYPES: ENABLE

void
i_list_formats()
	     PREINIT:
	      char*    item;
	       int     i;
	     PPCODE:
	       i=0;
	       while( (item=i_format_list[i++]) != NULL ) {
		      EXTEND(SP, 1);
		      PUSHs(sv_2mortal(newSVpv(item,0)));
	       }

Imager::ImgRaw
i_sametype(im, x, y)
    Imager::ImgRaw im
               i_img_dim x
               i_img_dim y

Imager::ImgRaw
i_sametype_chans(im, x, y, channels)
    Imager::ImgRaw im
               i_img_dim x
               i_img_dim y
               int channels

int
i_init_log(name_sv,level)
	      SV*    name_sv
	       int     level
	PREINIT:
	  const char *name = SvOK(name_sv) ? SvPV_nolen(name_sv) : NULL;
	CODE:
	  RETVAL = i_init_log(name, level);
	OUTPUT:
	  RETVAL

void
i_log_entry(string,level)
	      char*    string
	       int     level

int
i_log_enabled()

void
i_img_info(im)
    Imager::ImgRaw     im
	     PREINIT:
	       i_img_dim     info[4];
	     PPCODE:
   	       i_img_info(im,info);
               EXTEND(SP, 4);
               PUSHs(sv_2mortal(newSViv(info[0])));
               PUSHs(sv_2mortal(newSViv(info[1])));
               PUSHs(sv_2mortal(newSViv(info[2])));
               PUSHs(sv_2mortal(newSViv(info[3])));




void
i_img_setmask(im,ch_mask)
    Imager::ImgRaw     im
	       int     ch_mask

int
i_img_getmask(im)
    Imager::ImgRaw     im

int
i_img_getchannels(im)
    Imager::ImgRaw     im

void
i_img_getdata(im)
    Imager::ImgRaw     im
             PPCODE:
	       EXTEND(SP, 1);
               PUSHs(im->idata ? 
	             sv_2mortal(newSVpv((char *)im->idata, im->bytes)) 
		     : &PL_sv_undef);

IV
i_img_get_width(im)
    Imager::ImgRaw	im

IV
i_img_get_height(im)
    Imager::ImgRaw	im


void
i_img_is_monochrome(im)
	Imager::ImgRaw im
      PREINIT:
	int zero_is_white;
	int result;
      PPCODE:
	result = i_img_is_monochrome(im, &zero_is_white);
	if (result) {
	  if (GIMME_V == G_ARRAY) {
	    EXTEND(SP, 2);
	    PUSHs(&PL_sv_yes);
	    PUSHs(sv_2mortal(newSViv(zero_is_white)));
 	  }
	  else {
	    EXTEND(SP, 1);
	    PUSHs(&PL_sv_yes);
	  }
	}

void
i_line(im,x1,y1,x2,y2,val,endp)
    Imager::ImgRaw     im
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
     Imager::Color     val
	       int     endp

void
i_line_aa(im,x1,y1,x2,y2,val,endp)
    Imager::ImgRaw     im
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
     Imager::Color     val
	       int     endp

void
i_box(im,x1,y1,x2,y2,val)
    Imager::ImgRaw     im
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
     Imager::Color     val

void
i_box_filled(im,x1,y1,x2,y2,val)
    Imager::ImgRaw     im
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
	   Imager::Color    val

int
i_box_filledf(im,x1,y1,x2,y2,val)
    Imager::ImgRaw     im
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
	   Imager::Color::Float    val

void
i_box_cfill(im,x1,y1,x2,y2,fill)
    Imager::ImgRaw     im
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
	   Imager::FillHandle    fill

void
i_arc(im,x,y,rad,d1,d2,val)
    Imager::ImgRaw     im
	       i_img_dim     x
	       i_img_dim     y
             double     rad
             double     d1
             double     d2
	   Imager::Color    val

void
i_arc_aa(im,x,y,rad,d1,d2,val)
    Imager::ImgRaw     im
	    double     x
	    double     y
            double     rad
            double     d1
            double     d2
	   Imager::Color    val

void
i_arc_cfill(im,x,y,rad,d1,d2,fill)
    Imager::ImgRaw     im
	       i_img_dim     x
	       i_img_dim     y
             double     rad
             double     d1
             double     d2
	   Imager::FillHandle    fill

void
i_arc_aa_cfill(im,x,y,rad,d1,d2,fill)
    Imager::ImgRaw     im
	    double     x
	    double     y
            double     rad
            double     d1
            double     d2
	   Imager::FillHandle	fill


void
i_circle_aa(im,x,y,rad,val)
    Imager::ImgRaw     im
	     double     x
	     double     y
             double     rad
	   Imager::Color    val

int
i_circle_out(im,x,y,rad,val)
    Imager::ImgRaw     im
	     i_img_dim     x
	     i_img_dim     y
             i_img_dim     rad
	   Imager::Color    val

int
i_circle_out_aa(im,x,y,rad,val)
    Imager::ImgRaw     im
	     i_img_dim     x
	     i_img_dim     y
             i_img_dim     rad
	   Imager::Color    val

int
i_arc_out(im,x,y,rad,d1,d2,val)
    Imager::ImgRaw     im
	     i_img_dim     x
	     i_img_dim     y
             i_img_dim     rad
	     double d1
	     double d2
	   Imager::Color    val

int
i_arc_out_aa(im,x,y,rad,d1,d2,val)
    Imager::ImgRaw     im
	     i_img_dim     x
	     i_img_dim     y
             i_img_dim     rad
	     double d1
	     double d2
	   Imager::Color    val


void
i_bezier_multi(im,x,y,val)
    Imager::ImgRaw     im
    double *x
    double *y
    Imager::Color  val
  PREINIT:
    STRLEN size_x;
    STRLEN size_y;
  PPCODE:
    if (size_x != size_y)
      croak("Imager: x and y arrays to i_bezier_multi must be equal length\n");
    i_bezier_multi(im,size_x,x,y,val);

int
i_poly_aa(im,x,y,val)
    Imager::ImgRaw     im
    double *x
    double *y
    Imager::Color  val
  PREINIT:
    STRLEN   size_x;
    STRLEN   size_y;
  CODE:
    if (size_x != size_y)
      croak("Imager: x and y arrays to i_poly_aa must be equal length\n");
    RETVAL = i_poly_aa(im, size_x, x, y, val);
  OUTPUT:
    RETVAL

int
i_poly_aa_cfill(im, x, y, fill)
    Imager::ImgRaw     im
    double *x
    double *y
    Imager::FillHandle     fill
  PREINIT:
    STRLEN size_x;
    STRLEN size_y;
  CODE:
    if (size_x != size_y)
      croak("Imager: x and y arrays to i_poly_aa_cfill must be equal length\n");
    RETVAL = i_poly_aa_cfill(im, size_x, x, y, fill);
  OUTPUT:
    RETVAL

undef_int
i_flood_fill(im,seedx,seedy,dcol)
    Imager::ImgRaw     im
	       i_img_dim     seedx
	       i_img_dim     seedy
     Imager::Color     dcol

undef_int
i_flood_cfill(im,seedx,seedy,fill)
    Imager::ImgRaw     im
	       i_img_dim     seedx
	       i_img_dim     seedy
     Imager::FillHandle     fill

undef_int
i_flood_fill_border(im,seedx,seedy,dcol, border)
    Imager::ImgRaw     im
	       i_img_dim     seedx
	       i_img_dim     seedy
     Imager::Color     dcol
     Imager::Color     border

undef_int
i_flood_cfill_border(im,seedx,seedy,fill, border)
    Imager::ImgRaw     im
	       i_img_dim     seedx
	       i_img_dim     seedy
     Imager::FillHandle     fill
     Imager::Color     border


void
i_copyto(im,src,x1,y1,x2,y2,tx,ty)
    Imager::ImgRaw     im
    Imager::ImgRaw     src
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
	       i_img_dim     tx
	       i_img_dim     ty


void
i_copyto_trans(im,src,x1,y1,x2,y2,tx,ty,trans)
    Imager::ImgRaw     im
    Imager::ImgRaw     src
	       i_img_dim     x1
	       i_img_dim     y1
	       i_img_dim     x2
	       i_img_dim     y2
	       i_img_dim     tx
	       i_img_dim     ty
     Imager::Color     trans

Imager::ImgRaw
i_copy(src)
    Imager::ImgRaw     src


undef_int
i_rubthru(im,src,tx,ty,src_minx,src_miny,src_maxx,src_maxy)
    Imager::ImgRaw     im
    Imager::ImgRaw     src
	       i_img_dim     tx
	       i_img_dim     ty
	       i_img_dim     src_minx
	       i_img_dim     src_miny
	       i_img_dim     src_maxx
	       i_img_dim     src_maxy

undef_int
i_compose(out, src, out_left, out_top, src_left, src_top, width, height, combine = ic_normal, opacity = 0.0)
    Imager::ImgRaw out
    Imager::ImgRaw src
	i_img_dim out_left
 	i_img_dim out_top
	i_img_dim src_left
	i_img_dim src_top
	i_img_dim width
	i_img_dim height
	int combine
	double opacity

undef_int
i_compose_mask(out, src, mask, out_left, out_top, src_left, src_top, mask_left, mask_top, width, height, combine = ic_normal, opacity = 0.0)
    Imager::ImgRaw out
    Imager::ImgRaw src
    Imager::ImgRaw mask
	i_img_dim out_left
 	i_img_dim out_top
	i_img_dim src_left
	i_img_dim src_top
	i_img_dim mask_left
	i_img_dim mask_top
	i_img_dim width
	i_img_dim height
	int combine
	double opacity

Imager::ImgRaw
i_combine(src_av, channels_av = NULL)
	AV *src_av
	AV *channels_av
  PREINIT:
	i_img **imgs = NULL;
	STRLEN in_count;
	int *channels = NULL;
	int i;
	SV **psv;
	IV tmp;
  CODE:
	in_count = av_len(src_av) + 1;
	if (in_count > 0) {
	  imgs = mymalloc(sizeof(i_img*) * in_count);
	  channels = mymalloc(sizeof(int) * in_count);
	  for (i = 0; i < in_count; ++i) {
	    psv = av_fetch(src_av, i, 0);
	    if (!psv || !*psv || !sv_derived_from(*psv, "Imager::ImgRaw")) {
	      myfree(imgs);
	      myfree(channels);
	      croak("imgs must contain only images");
	    }
	    tmp = SvIV((SV*)SvRV(*psv));
	    imgs[i] = INT2PTR(i_img*, tmp);
	    if (channels_av &&
	        (psv = av_fetch(channels_av, i, 0)) != NULL &&
		*psv) {
	      channels[i] = SvIV(*psv);
	    }
	    else {
	      channels[i] = 0;
	    }
	  }
	}
	RETVAL = i_combine(imgs, channels, in_count);
	myfree(imgs);
	myfree(channels);
  OUTPUT:
	RETVAL

undef_int
i_flipxy(im, direction)
    Imager::ImgRaw     im
	       int     direction

Imager::ImgRaw
i_rotate90(im, degrees)
    Imager::ImgRaw      im
               int      degrees

Imager::ImgRaw
i_rotate_exact(im, amount, ...)
    Imager::ImgRaw      im
            double      amount
      PREINIT:
	i_color *backp = NULL;
	i_fcolor *fbackp = NULL;
	int i;
	SV * sv1;
      CODE:
	/* extract the bg colors if any */
	/* yes, this is kind of strange */
	for (i = 2; i < items; ++i) {
          sv1 = ST(i);
          if (sv_derived_from(sv1, "Imager::Color")) {
	    IV tmp = SvIV((SV*)SvRV(sv1));
	    backp = INT2PTR(i_color *, tmp);
	  }
	  else if (sv_derived_from(sv1, "Imager::Color::Float")) {
	    IV tmp = SvIV((SV*)SvRV(sv1));
	    fbackp = INT2PTR(i_fcolor *, tmp);
	  }
	}
	RETVAL = i_rotate_exact_bg(im, amount, backp, fbackp);
      OUTPUT:
	RETVAL

Imager::ImgRaw
i_matrix_transform(im, xsize, ysize, matrix_av, ...)
    Imager::ImgRaw      im
    i_img_dim      xsize
    i_img_dim      ysize
    AV *matrix_av
  PREINIT:
    double matrix[9];
    STRLEN len;
    SV *sv1;
    int i;
    i_color *backp = NULL;
    i_fcolor *fbackp = NULL;
  CODE:
    len=av_len(matrix_av)+1;
    if (len > 9)
      len = 9;
    for (i = 0; i < len; ++i) {
      sv1=(*(av_fetch(matrix_av,i,0)));
      matrix[i] = SvNV(sv1);
    }
    for (; i < 9; ++i)
      matrix[i] = 0;
    /* extract the bg colors if any */
    /* yes, this is kind of strange */
    for (i = 4; i < items; ++i) {
      sv1 = ST(i);
      if (sv_derived_from(sv1, "Imager::Color")) {
        IV tmp = SvIV((SV*)SvRV(sv1));
	backp = INT2PTR(i_color *, tmp);
      }
      else if (sv_derived_from(sv1, "Imager::Color::Float")) {
        IV tmp = SvIV((SV*)SvRV(sv1));
        fbackp = INT2PTR(i_fcolor *, tmp);
      }
    }
    RETVAL = i_matrix_transform_bg(im, xsize, ysize, matrix, backp, fbackp);
  OUTPUT:
    RETVAL

undef_int
i_gaussian(im,stdev)
    Imager::ImgRaw     im
	    double     stdev

void
i_unsharp_mask(im,stdev,scale)
    Imager::ImgRaw     im
	     double    stdev
             double    scale

int
i_conv(im,coef)
	Imager::ImgRaw     im
	AV *coef
     PREINIT:
	double*    c_coef;
	int     len;
	SV* sv1;
	int i;
    CODE:
	len = av_len(coef) + 1;
	c_coef=mymalloc( len * sizeof(double) );
	for(i = 0; i  < len; i++) {
	  sv1 = (*(av_fetch(coef, i, 0)));
	  c_coef[i] = (double)SvNV(sv1);
	}
	RETVAL = i_conv(im, c_coef, len);
	myfree(c_coef);
    OUTPUT:
	RETVAL

Imager::ImgRaw
i_convert(src, avmain)
    Imager::ImgRaw     src
    AV *avmain
	PREINIT:
    	  double *coeff;
	  int outchan;
	  int inchan;
          SV **temp;
          AV *avsub;
	  int len;
	  int i, j;
        CODE:
	  outchan = av_len(avmain)+1;
          /* find the biggest */
          inchan = 0;
	  for (j=0; j < outchan; ++j) {
	    temp = av_fetch(avmain, j, 0);
	    if (temp && SvROK(*temp) && SvTYPE(SvRV(*temp)) == SVt_PVAV) {
	      avsub = (AV*)SvRV(*temp);
	      len = av_len(avsub)+1;
	      if (len > inchan)
		inchan = len;
	    }
	    else {
	      i_push_errorf(0, "invalid matrix: element %d is not an array ref", j);
	      XSRETURN(0);
	    }
          }
          coeff = mymalloc(sizeof(double) * outchan * inchan);
	  for (j = 0; j < outchan; ++j) {
	    avsub = (AV*)SvRV(*av_fetch(avmain, j, 0));
	    len = av_len(avsub)+1;
	    for (i = 0; i < len; ++i) {
	      temp = av_fetch(avsub, i, 0);
	      if (temp)
		coeff[i+j*inchan] = SvNV(*temp);
	      else
	 	coeff[i+j*inchan] = 0;
	    }
	    while (i < inchan)
	      coeff[i++ + j*inchan] = 0;
	  }
	  RETVAL = i_convert(src, coeff, outchan, inchan);
          myfree(coeff);
	OUTPUT:
	  RETVAL


undef_int
i_map(im, pmaps_av)
    Imager::ImgRaw     im
    AV *pmaps_av
  PREINIT:
    unsigned int mask = 0;
    AV *avsub;
    SV **temp;
    int len;
    int i, j;
    unsigned char (*maps)[256];
  CODE:
    len = av_len(pmaps_av)+1;
    if (im->channels < len)
      len = im->channels;
    maps = mymalloc( len * sizeof(unsigned char [256]) );
    for (j=0; j<len ; j++) {
      temp = av_fetch(pmaps_av, j, 0);
      if (temp && SvROK(*temp) && (SvTYPE(SvRV(*temp)) == SVt_PVAV) ) {
        avsub = (AV*)SvRV(*temp);
        if(av_len(avsub) != 255)
          continue;
        mask |= 1<<j;
        for (i=0; i<256 ; i++) {
          int val;
          temp = av_fetch(avsub, i, 0);
          val = temp ? SvIV(*temp) : 0;
          if (val<0) val = 0;
          if (val>255) val = 255;
          maps[j][i] = val;
        }
      }
    }
    i_map(im, maps, mask);
    myfree(maps);
    RETVAL = 1;
  OUTPUT:
    RETVAL

float
i_img_diff(im1,im2)
    Imager::ImgRaw     im1
    Imager::ImgRaw     im2

double
i_img_diffd(im1,im2)
    Imager::ImgRaw     im1
    Imager::ImgRaw     im2

int
i_img_samef(im1, im2, epsilon = i_img_epsilonf(), what=NULL)
    Imager::ImgRaw    im1
    Imager::ImgRaw    im2
    double epsilon
    const char *what

double
i_img_epsilonf()

bool
_is_color_object(sv)
	SV* sv
    CODE:
        SvGETMAGIC(sv);
        RETVAL = SvOK(sv) && SvROK(sv) &&
	   (sv_derived_from(sv, "Imager::Color")
          || sv_derived_from(sv, "Imager::Color::Float"));
    OUTPUT:
        RETVAL

#ifdef HAVE_LIBTT


Imager::Font::TT
i_tt_new(fontname)
	      char*     fontname


MODULE = Imager         PACKAGE = Imager::Font::TT      PREFIX=TT_

#define TT_DESTROY(handle) i_tt_destroy(handle)

void
TT_DESTROY(handle)
     Imager::Font::TT   handle

int
TT_CLONE_SKIP(...)
    CODE:
        (void)items; /* avoid unused warning */
        RETVAL = 1;
    OUTPUT:
        RETVAL


MODULE = Imager         PACKAGE = Imager


undef_int
i_tt_text(handle,im,xb,yb,cl,points,str_sv,smooth,utf8,align=1)
  Imager::Font::TT     handle
    Imager::ImgRaw     im
	       i_img_dim     xb
	       i_img_dim     yb
     Imager::Color     cl
             double     points
	      SV *     str_sv
	       int     smooth
               int     utf8
               int     align
             PREINIT:
               char *str;
               STRLEN len;
             CODE:
               str = SvPV(str_sv, len);
#ifdef SvUTF8
               if (SvUTF8(str_sv))
                 utf8 = 1;
#endif
               RETVAL = i_tt_text(handle, im, xb, yb, cl, points, str, 
                                  len, smooth, utf8, align);
             OUTPUT:
               RETVAL                


undef_int
i_tt_cp(handle,im,xb,yb,channel,points,str_sv,smooth,utf8,align=1)
  Imager::Font::TT     handle
    Imager::ImgRaw     im
	       i_img_dim     xb
	       i_img_dim     yb
	       int     channel
             double     points
	      SV *     str_sv
	       int     smooth
               int     utf8
               int     align
             PREINIT:
               char *str;
               STRLEN len;
             CODE:
               str = SvPV(str_sv, len);
#ifdef SvUTF8
               if (SvUTF8(str_sv))
                 utf8 = 1;
#endif
               RETVAL = i_tt_cp(handle, im, xb, yb, channel, points, str, len,
                                smooth, utf8, align);
             OUTPUT:
                RETVAL


void
i_tt_bbox(handle,point,str_sv,utf8)
  Imager::Font::TT     handle
	     double     point
	       SV*    str_sv
               int     utf8
	     PREINIT:
	       i_img_dim cords[BOUNDING_BOX_COUNT];
	       int rc;
               char *  str;
               STRLEN len;
               int i;
	     PPCODE:
               str = SvPV(str_sv, len);
#ifdef SvUTF8
               if (SvUTF8(ST(2)))
                 utf8 = 1;
#endif
  	       if ((rc=i_tt_bbox(handle,point,str,len,cords, utf8))) {
                 EXTEND(SP, rc);
                 for (i = 0; i < rc; ++i) {
                   PUSHs(sv_2mortal(newSViv(cords[i])));
                 }
               }

void
i_tt_has_chars(handle, text_sv, utf8)
        Imager::Font::TT handle
        SV  *text_sv
        int utf8
      PREINIT:
        char const *text;
        STRLEN len;
        char *work;
        size_t count;
        size_t i;
      PPCODE:
        i_clear_error();
        text = SvPV(text_sv, len);
#ifdef SvUTF8
        if (SvUTF8(text_sv))
          utf8 = 1;
#endif
        work = mymalloc(len);
        count = i_tt_has_chars(handle, text, len, utf8, work);
        if (GIMME_V == G_ARRAY) {
          EXTEND(SP, count);
          for (i = 0; i < count; ++i) {
            PUSHs(boolSV(work[i]));
          }
        }
        else {
          EXTEND(SP, 1);
          PUSHs(sv_2mortal(newSVpv(work, count)));
        }
        myfree(work);

void
i_tt_dump_names(handle)
        Imager::Font::TT handle

void
i_tt_face_name(handle)
        Imager::Font::TT handle
      PREINIT:
        char name[255];
        size_t len;
      PPCODE:
        len = i_tt_face_name(handle, name, sizeof(name));
        if (len) {
          EXTEND(SP, 1);
          PUSHs(sv_2mortal(newSVpv(name, len-1)));
        }

void
i_tt_glyph_name(handle, text_sv, utf8 = 0)
        Imager::Font::TT handle
        SV *text_sv
        int utf8
      PREINIT:
        char const *text;
        STRLEN work_len;
        size_t len;
        size_t outsize;
        char name[255];
	SSize_t count = 0;
      PPCODE:
        i_clear_error();
        text = SvPV(text_sv, work_len);
#ifdef SvUTF8
        if (SvUTF8(text_sv))
          utf8 = 1;
#endif
        len = work_len;
        while (len) {
          unsigned long ch;
          if (utf8) {
            ch = i_utf8_advance(&text, &len);
            if (ch == ~0UL) {
              i_push_error(0, "invalid UTF8 character");
              XSRETURN_EMPTY;
            }
          }
          else {
            ch = *text++;
            --len;
          }
          EXTEND(SP, count+1);
          if ((outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) != 0) {
	    ST(count) = sv_2mortal(newSVpv(name, 0));
          }
          else {
	    ST(count) = &PL_sv_undef;
          }
          ++count;
        }
	XSRETURN(count);

#endif 

const char *
i_test_format_probe(ig, length)
        Imager::IO     ig
	       int     length

Imager::ImgRaw
i_readpnm_wiol(ig, allow_incomplete)
        Imager::IO     ig
	       int     allow_incomplete


void
i_readpnm_multi_wiol(ig, allow_incomplete)
        Imager::IO ig
	       int     allow_incomplete
      PREINIT:
        i_img **imgs;
        int count=0;
        int i;
      PPCODE:
        imgs = i_readpnm_multi_wiol(ig, &count, allow_incomplete);
        if (imgs) {
          EXTEND(SP, count);
          for (i = 0; i < count; ++i) {
            SV *sv = sv_newmortal();
            sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
            PUSHs(sv);
          }
          myfree(imgs);
        }

undef_int
i_writeppm_wiol(im, ig)
    Imager::ImgRaw     im
        Imager::IO     ig





Imager::ImgRaw
i_readraw_wiol(ig,x,y,datachannels,storechannels,intrl)
        Imager::IO     ig
	       i_img_dim     x
	       i_img_dim     y
	       int     datachannels
	       int     storechannels
	       int     intrl

undef_int
i_writeraw_wiol(im,ig)
    Imager::ImgRaw     im
        Imager::IO     ig

undef_int
i_writebmp_wiol(im,ig)
    Imager::ImgRaw     im
        Imager::IO     ig

Imager::ImgRaw
i_readbmp_wiol(ig, allow_incomplete=0)
        Imager::IO     ig
        int            allow_incomplete


undef_int
i_writetga_wiol(im,ig, wierdpack, compress, idstring)
    Imager::ImgRaw     im
        Imager::IO     ig
               int     wierdpack
               int     compress
              char*    idstring
            PREINIT:
                int idlen;
	       CODE:
                idlen  = SvCUR(ST(4));
                RETVAL = i_writetga_wiol(im, ig, wierdpack, compress, idstring, idlen);
                OUTPUT:
                RETVAL


Imager::ImgRaw
i_readtga_wiol(ig, length)
        Imager::IO     ig
               int     length




Imager::ImgRaw
i_scaleaxis(im,Value,Axis)
    Imager::ImgRaw     im
             double     Value
	       int     Axis

Imager::ImgRaw
i_scale_nn(im,scx,scy)
    Imager::ImgRaw     im
             double    scx
             double    scy

Imager::ImgRaw
i_scale_mixing(im, width, height)
    Imager::ImgRaw     im
	       i_img_dim     width
	       i_img_dim     height

Imager::ImgRaw
i_haar(im)
    Imager::ImgRaw     im

int
i_count_colors(im,maxc)
    Imager::ImgRaw     im
               int     maxc

void
i_get_anonymous_color_histo(im, maxc = 0x40000000)
   Imager::ImgRaw  im
   int maxc
    PREINIT:
        int i;
        unsigned int * col_usage = NULL;
        int col_cnt;
    PPCODE:
	col_cnt = i_get_anonymous_color_histo(im, &col_usage, maxc);
        EXTEND(SP, col_cnt);
        for (i = 0; i < col_cnt; i++)  {
            PUSHs(sv_2mortal(newSViv( col_usage[i])));
        }
        myfree(col_usage);
        XSRETURN(col_cnt);


void
i_transform(im, opx, opy, parm)
    Imager::ImgRaw     im
    int *opx
    int *opy
    double *parm
             PREINIT:
	     STRLEN size_opx, size_opy, size_parm;
	     i_img *result;
             PPCODE:
             result=i_transform(im,opx,size_opx,opy,size_opy,parm,size_parm);
 	     if (result) {
	       SV *result_sv = sv_newmortal();
	       EXTEND(SP, 1);
	       sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result);
	       PUSHs(result_sv);
	     }

void
i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
	SV *sv_width
	SV *sv_height
	SV *sv_ops
	AV *av_n_regs
	AV *av_c_regs
	AV *av_in_imgs
	int channels
	     PREINIT:
             i_img_dim width;
             i_img_dim height;
	     struct rm_op *ops;
	     STRLEN ops_len;
	     int ops_count;
             double *n_regs;
             int n_regs_count;
             i_color *c_regs;
	     int c_regs_count;
             int in_imgs_count;
             i_img **in_imgs;
             SV *sv1;
             IV tmp;
	     int i;
	     i_img *result;
             PPCODE:

             in_imgs_count = av_len(av_in_imgs)+1;
	     for (i = 0; i < in_imgs_count; ++i) {
	       sv1 = *av_fetch(av_in_imgs, i, 0);
	       if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
		 croak("sv_in_img must contain only images");
	       }
	     }
             if (in_imgs_count > 0) {
               in_imgs = mymalloc(in_imgs_count*sizeof(i_img*));
               for (i = 0; i < in_imgs_count; ++i) {              
	         sv1 = *av_fetch(av_in_imgs,i,0);
	         if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
		   croak("Parameter 5 must contain only images");
	         }
                 tmp = SvIV((SV*)SvRV(sv1));
	         in_imgs[i] = INT2PTR(i_img*, tmp);
	       }
	     }
             else {
	       /* no input images */
	       in_imgs = NULL;
             }
             /* default the output size from the first input if possible */
             if (SvOK(sv_width))
	       width = SvIV(sv_width);
             else if (in_imgs_count)
	       width = in_imgs[0]->xsize;
             else
	       croak("No output image width supplied");

             if (SvOK(sv_height))
	       height = SvIV(sv_height);
             else if (in_imgs_count)
	       height = in_imgs[0]->ysize;
             else
	       croak("No output image height supplied");

	     ops = (struct rm_op *)SvPV(sv_ops, ops_len);
             if (ops_len % sizeof(struct rm_op))
	         croak("Imager: Parameter 3 must be a bitmap of regops\n");
	     ops_count = ops_len / sizeof(struct rm_op);

	     n_regs_count = av_len(av_n_regs)+1;
             n_regs = mymalloc(n_regs_count * sizeof(double));
	     for (i = 0; i < n_regs_count; ++i) {
	       sv1 = *av_fetch(av_n_regs,i,0);
	       if (SvOK(sv1))
	         n_regs[i] = SvNV(sv1);
	     }
             c_regs_count = av_len(av_c_regs)+1;
             c_regs = mymalloc(c_regs_count * sizeof(i_color));
             /* I don't bother initializing the colou?r registers */

	     result=i_transform2(width, height, channels, ops, ops_count, 
				 n_regs, n_regs_count, 
				 c_regs, c_regs_count, in_imgs, in_imgs_count);
	     if (in_imgs)
	         myfree(in_imgs);
             myfree(n_regs);
	     myfree(c_regs);
 	     if (result) {
	       SV *result_sv = sv_newmortal();
	       EXTEND(SP, 1);
	       sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result);
	       PUSHs(result_sv);
	     }


void
i_contrast(im,intensity)
    Imager::ImgRaw     im
             float     intensity

void
i_hardinvert(im)
    Imager::ImgRaw     im

void
i_hardinvertall(im)
    Imager::ImgRaw     im

void
i_noise(im,amount,type)
    Imager::ImgRaw     im
             float     amount
     unsigned char     type

void
i_bumpmap(im,bump,channel,light_x,light_y,strength)
    Imager::ImgRaw     im
    Imager::ImgRaw     bump
               int     channel
         i_img_dim     light_x
         i_img_dim     light_y
         i_img_dim     strength


void
i_bumpmap_complex(im,bump,channel,tx,ty,Lx,Ly,Lz,cd,cs,n,Ia,Il,Is)
    Imager::ImgRaw     im
    Imager::ImgRaw     bump
               int     channel
               i_img_dim     tx
               i_img_dim     ty
             double     Lx
             double     Ly
             double     Lz
             float     cd
             float     cs
             float     n
     Imager::Color     Ia
     Imager::Color     Il
     Imager::Color     Is



void
i_postlevels(im,levels)
    Imager::ImgRaw     im
             int       levels

void
i_mosaic(im,size)
    Imager::ImgRaw     im
         i_img_dim     size

void
i_watermark(im,wmark,tx,ty,pixdiff)
    Imager::ImgRaw     im
    Imager::ImgRaw     wmark
               i_img_dim     tx
               i_img_dim     ty
               int     pixdiff


void
i_autolevels(im,lsat,usat,skew)
    Imager::ImgRaw     im
             float     lsat
             float     usat
             float     skew

void
i_radnoise(im,xo,yo,rscale,ascale)
    Imager::ImgRaw     im
             float     xo
             float     yo
             float     rscale
             float     ascale

void
i_turbnoise(im, xo, yo, scale)
    Imager::ImgRaw     im
             float     xo
             float     yo
             float     scale


void
i_gradgen(im, xo, yo, ac, dmeasure)
    Imager::ImgRaw     im
    i_img_dim *xo
    i_img_dim *yo
    i_color *ac
    int dmeasure
      PREINIT:
	STRLEN size_xo;
	STRLEN size_yo;
        STRLEN size_ac;
      CODE:
        if (size_xo != size_yo || size_xo != size_ac)
	  croak("i_gradgen: x, y and color arrays must be the same size");
	if (size_xo < 2)
          croak("Usage: i_gradgen array refs must have more than 1 entry each");
        i_gradgen(im, size_xo, xo, yo, ac, dmeasure);

Imager::ImgRaw
i_diff_image(im, im2, mindist=0)
    Imager::ImgRaw     im
    Imager::ImgRaw     im2
            double     mindist

undef_int
i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
    Imager::ImgRaw     im
            double     xa
            double     ya
            double     xb
            double     yb
               int     type
               int     repeat
               int     combine
               int     super_sample
            double     ssample_param
      PREINIT:
        AV *asegs;
        int count;
        i_fountain_seg *segs;
      CODE:
	if (!SvROK(ST(10)) || ! SvTYPE(SvRV(ST(10))))
	    croak("i_fountain: argument 11 must be an array ref");
        
	asegs = (AV *)SvRV(ST(10));
        segs = load_fount_segs(aTHX_ asegs, &count);
        RETVAL = i_fountain(im, xa, ya, xb, yb, type, repeat, combine, 
                            super_sample, ssample_param, count, segs);
        myfree(segs);
      OUTPUT:
        RETVAL

Imager::FillHandle
i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
            double     xa
            double     ya
            double     xb
            double     yb
               int     type
               int     repeat
               int     combine
               int     super_sample
            double     ssample_param
      PREINIT:
        AV *asegs;
        int count;
        i_fountain_seg *segs;
      CODE:
	if (!SvROK(ST(9)) || ! SvTYPE(SvRV(ST(9))))
	    croak("i_fountain: argument 11 must be an array ref");
        
	asegs = (AV *)SvRV(ST(9));
        segs = load_fount_segs(aTHX_ asegs, &count);
        RETVAL = i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, 
                                  super_sample, ssample_param, count, segs);
        myfree(segs);        
      OUTPUT:
        RETVAL

Imager::FillHandle
i_new_fill_opacity(other_fill, alpha_mult)
    Imager::FillHandle other_fill
    double alpha_mult

void
i_errors()
      PREINIT:
        i_errmsg *errors;
	int i;
	AV *av;
	SV *sv;
      PPCODE:
	errors = i_errors();
	i = 0;
	while (errors[i].msg) {
	  av = newAV();
	  sv = newSVpv(errors[i].msg, strlen(errors[i].msg));
	  if (!av_store(av, 0, sv)) {
	    SvREFCNT_dec(sv);
	  }
	  sv = newSViv(errors[i].code);
	  if (!av_store(av, 1, sv)) {
	    SvREFCNT_dec(sv);
	  }
	  PUSHs(sv_2mortal(newRV_noinc((SV*)av)));
	  ++i;
	}

void
i_clear_error()

void
i_push_error(code, msg)
	int code
	const char *msg

undef_int
i_nearest_color(im, ...)
    Imager::ImgRaw     im
      PREINIT:
	int num;
	i_img_dim *xo;
	i_img_dim *yo;
        i_color *ival;
	int dmeasure;
	int i;
	SV *sv;
	AV *axx;
	AV *ayy;
	AV *ac;
      CODE:
	if (items != 5)
	    croak("Usage: i_nearest_color(im, xo, yo, ival, dmeasure)");
	if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
	    croak("i_nearest_color: Second argument must be an array ref");
	if (!SvROK(ST(2)) || ! SvTYPE(SvRV(ST(2))))
	    croak("i_nearest_color: Third argument must be an array ref");
	if (!SvROK(ST(3)) || ! SvTYPE(SvRV(ST(3))))
	    croak("i_nearest_color: Fourth argument must be an array ref");
	axx = (AV *)SvRV(ST(1));
	ayy = (AV *)SvRV(ST(2));
	ac  = (AV *)SvRV(ST(3));
	dmeasure = (int)SvIV(ST(4));
	
        num = av_len(axx) < av_len(ayy) ? av_len(axx) : av_len(ayy);
	num = num <= av_len(ac) ? num : av_len(ac);
	num++; 
	if (num < 2) croak("Usage: i_nearest_color array refs must have more than 1 entry each");
	xo = mymalloc( sizeof(i_img_dim) * num );
	yo = mymalloc( sizeof(i_img_dim) * num );
	ival = mymalloc( sizeof(i_color) * num );
	for(i = 0; i<num; i++) {
	  xo[i]   = (i_img_dim)SvIV(* av_fetch(axx, i, 0));
	  yo[i]   = (i_img_dim)SvIV(* av_fetch(ayy, i, 0));
          sv = *av_fetch(ac, i, 0);
	  if ( !sv_derived_from(sv, "Imager::Color") ) {
	    free(axx); free(ayy); free(ac);
            croak("i_nearest_color: Element of fourth argument is not derived from Imager::Color");
	  }
	  ival[i] = *INT2PTR(i_color *, SvIV((SV *)SvRV(sv)));
	}
        RETVAL = i_nearest_color(im, num, xo, yo, ival, dmeasure);
      OUTPUT:
        RETVAL

void
malloc_state()

void
DSO_open(filename)
             char*       filename
	     PREINIT:
	       void *rc;
	       char *evstr;
	     PPCODE:
	       rc=DSO_open(filename,&evstr);
               if (rc!=NULL) {
                 if (evstr!=NULL) {
                   EXTEND(SP,2); 
                   PUSHs(sv_2mortal(newSViv(PTR2IV(rc))));
                   PUSHs(sv_2mortal(newSVpvn(evstr, strlen(evstr))));
                 } else {
                   EXTEND(SP,1);
                   PUSHs(sv_2mortal(newSViv(PTR2IV(rc))));
                 }
               }


undef_int
DSO_close(dso_handle)
             void*       dso_handle

void
DSO_funclist(dso_handle_v)
             void*       dso_handle_v
	     PREINIT:
	       int i;
	       DSO_handle *dso_handle;
	       func_ptr *functions;
	     PPCODE:
	       dso_handle=(DSO_handle*)dso_handle_v;
	       functions = DSO_funclist(dso_handle);
	       i=0;
	       while( functions[i].name != NULL) {
	         EXTEND(SP,1);
		 PUSHs(sv_2mortal(newSVpv(functions[i].name,0)));
	         EXTEND(SP,1);
		 PUSHs(sv_2mortal(newSVpv(functions[i++].pcode,0)));
	       }

void
DSO_call(handle,func_index,hv)
	       void*  handle
	       int    func_index
	       HV *hv
	     PPCODE:
	       DSO_call( (DSO_handle *)handle,func_index,hv);

Imager::Color
i_get_pixel(im, x, y)
	Imager::ImgRaw im
	i_img_dim x
	i_img_dim y;
      CODE:
	RETVAL = (i_color *)mymalloc(sizeof(i_color));
	if (i_gpix(im, x, y, RETVAL) != 0) {
          myfree(RETVAL);
	  XSRETURN_UNDEF;
        }
      OUTPUT:
        RETVAL
        

int
i_ppix(im, x, y, cl)
        Imager::ImgRaw im
        i_img_dim x
        i_img_dim y
        Imager::Color cl

Imager::ImgRaw
i_img_pal_new(x, y, channels, maxpal)
	i_img_dim x
        i_img_dim y
        int     channels
	int	maxpal

Imager::ImgRaw
i_img_to_pal(src, quant)
        Imager::ImgRaw src
      PREINIT:
        HV *hv;
        i_quantize quant;
      CODE:
        if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
          croak("i_img_to_pal: second argument must be a hash ref");
        hv = (HV *)SvRV(ST(1));
        memset(&quant, 0, sizeof(quant));
	quant.version = 1;
        quant.mc_size = 256;
	ip_handle_quant_opts(aTHX_ &quant, hv);
        RETVAL = i_img_to_pal(src, &quant);
        if (RETVAL) {
          ip_copy_colors_back(aTHX_ hv, &quant);
        }
	ip_cleanup_quant_opts(aTHX_ &quant);
      OUTPUT:
        RETVAL

Imager::ImgRaw
i_img_to_rgb(src)
        Imager::ImgRaw src

void
i_img_make_palette(HV *quant_hv, ...)
      PREINIT:
        size_t count = items - 1;
	i_quantize quant;
	i_img **imgs = NULL;
	ssize_t i;
      PPCODE:
        if (count <= 0)
	  croak("Please supply at least one image (%d)", (int)count);
        imgs = mymalloc(sizeof(i_img *) * count);
	for (i = 0; i < count; ++i) {
	  SV *img_sv = ST(i + 1);
	  if (SvROK(img_sv) && sv_derived_from(img_sv, "Imager::ImgRaw")) {
	    imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(img_sv)));
	  }
	  else {
	    myfree(imgs);
	    croak("Image %d is not an image object", (int)i+1);
          }
	}
        memset(&quant, 0, sizeof(quant));
	quant.version = 1;
	quant.mc_size = 256;
        ip_handle_quant_opts(aTHX_ &quant, quant_hv);
	i_quant_makemap(&quant, imgs, count);
	EXTEND(SP, quant.mc_count);
	for (i = 0; i < quant.mc_count; ++i) {
	  SV *sv_c = make_i_color_sv(aTHX_ quant.mc_colors + i);
	  PUSHs(sv_c);
	}
 	ip_cleanup_quant_opts(aTHX_ &quant);
	

void
i_gpal(im, l, r, y)
        Imager::ImgRaw  im
        i_img_dim     l
        i_img_dim     r
        i_img_dim     y
      PREINIT:
        i_palidx *work;
        int count, i;
      PPCODE:
        if (l < r) {
          work = mymalloc((r-l) * sizeof(i_palidx));
          count = i_gpal(im, l, r, y, work);
          if (GIMME_V == G_ARRAY) {
            EXTEND(SP, count);
            for (i = 0; i < count; ++i) {
              PUSHs(sv_2mortal(newSViv(work[i])));
            }
          }
          else {
            EXTEND(SP, 1);
            PUSHs(sv_2mortal(newSVpv((char *)work, count * sizeof(i_palidx))));
          }
          myfree(work);
        }
        else {
          if (GIMME_V != G_ARRAY) {
            EXTEND(SP, 1);
            PUSHs(&PL_sv_undef);
          }
        }

int
i_ppal(im, l, y, ...)
        Imager::ImgRaw  im
        i_img_dim     l
        i_img_dim     y
      PREINIT:
        i_palidx *work;
        i_img_dim i;
      CODE:
        if (items > 3) {
          work = malloc_temp(aTHX_ sizeof(i_palidx) * (items-3));
          for (i=0; i < items-3; ++i) {
            work[i] = SvIV(ST(i+3));
          }
          validate_i_ppal(im, work, items - 3);
          RETVAL = i_ppal(im, l, l+items-3, y, work);
        }
        else {
          RETVAL = 0;
        }
      OUTPUT:
        RETVAL

int
i_ppal_p(im, l, y, data)
        Imager::ImgRaw  im
        i_img_dim     l
        i_img_dim     y
        SV *data
      PREINIT:
        i_palidx const *work;
        STRLEN len;
      CODE:
        work = (i_palidx const *)SvPV(data, len);
        len /= sizeof(i_palidx);
        if (len > 0) {
          validate_i_ppal(im, work, len);
          RETVAL = i_ppal(im, l, l+len, y, work);
        }
        else {
          RETVAL = 0;
        }
      OUTPUT:
        RETVAL

SysRet
i_addcolors(im, ...)
        Imager::ImgRaw  im
      PREINIT:
        i_color *colors;
        int i;
      CODE:
        if (items < 2)
          croak("i_addcolors: no colors to add");
        colors = mymalloc((items-1) * sizeof(i_color));
        for (i=0; i < items-1; ++i) {
          if (sv_isobject(ST(i+1)) 
              && sv_derived_from(ST(i+1), "Imager::Color")) {
            IV tmp = SvIV((SV *)SvRV(ST(i+1)));
            colors[i] = *INT2PTR(i_color *, tmp);
          }
          else {
            myfree(colors);
            croak("i_addcolor: pixels must be Imager::Color objects");
          }
        }
        RETVAL = i_addcolors(im, colors, items-1);
      OUTPUT:
        RETVAL

undef_int 
i_setcolors(im, index, ...)
        Imager::ImgRaw  im
        int index
      PREINIT:
        i_color *colors;
        int i;
      CODE:
        if (items < 3)
          croak("i_setcolors: no colors to add");
        colors = mymalloc((items-2) * sizeof(i_color));
        for (i=0; i < items-2; ++i) {
          if (sv_isobject(ST(i+2)) 
              && sv_derived_from(ST(i+2), "Imager::Color")) {
            IV tmp = SvIV((SV *)SvRV(ST(i+2)));
            colors[i] = *INT2PTR(i_color *, tmp);
          }
          else {
            myfree(colors);
            croak("i_setcolors: pixels must be Imager::Color objects");
          }
        }
        RETVAL = i_setcolors(im, index, colors, items-2);
        myfree(colors);
      OUTPUT:
	RETVAL

void
i_getcolors(im, index, count=1)
        Imager::ImgRaw im
        int index
	int count
      PREINIT:
        i_color *colors;
        int i;
      PPCODE:
        if (count < 1)
          croak("i_getcolors: count must be positive");
        colors = malloc_temp(aTHX_ sizeof(i_color) * count);
        if (i_getcolors(im, index, colors, count)) {
	  EXTEND(SP, count);
          for (i = 0; i < count; ++i) {
            SV *sv = make_i_color_sv(aTHX_ colors+i);
            PUSHs(sv);
          }
        }

undef_neg_int
i_colorcount(im)
        Imager::ImgRaw im

undef_neg_int
i_maxcolors(im)
        Imager::ImgRaw im

i_palidx
i_findcolor(im, color)
        Imager::ImgRaw im
        Imager::Color color
      CODE:
        if (!i_findcolor(im, color, &RETVAL)) {
	  XSRETURN_UNDEF;
        }
      OUTPUT:
        RETVAL

int
i_img_bits(im)
        Imager::ImgRaw  im

int
i_img_type(im)
        Imager::ImgRaw  im

int
i_img_virtual(im)
        Imager::ImgRaw  im

void
i_gsamp(im, l, r, y, channels)
        Imager::ImgRaw im
        i_img_dim l
        i_img_dim r
        i_img_dim y
        i_channel_list channels
      PREINIT:
        i_sample_t *data;
        i_img_dim count, i;
      PPCODE:
        if (l < r) {
          data = mymalloc(sizeof(i_sample_t) * (r-l) * channels.count);
          count = i_gsamp(im, l, r, y, data, channels.channels, channels.count);
          if (GIMME_V == G_ARRAY) {
            EXTEND(SP, count);
            for (i = 0; i < count; ++i)
              PUSHs(sv_2mortal(newSViv(data[i])));
          }
          else {
            EXTEND(SP, 1);
            PUSHs(sv_2mortal(newSVpv((char *)data, count * sizeof(i_sample_t))));
          }
	  myfree(data);
        }
        else {
          if (GIMME_V != G_ARRAY) {
	    XSRETURN_UNDEF;
          }
        }

undef_neg_int
i_gsamp_bits(im, l, r, y, bits, target, offset, channels)
        Imager::ImgRaw im
        i_img_dim l
        i_img_dim r
        i_img_dim y
	int bits
	AV *target
	STRLEN offset
        i_channel_list channels
      PREINIT:
        unsigned *data;
        i_img_dim count, i;
      CODE:
	i_clear_error();
        if (items < 8)
          croak("No channel numbers supplied to g_samp()");
        if (l < r) {
          data = mymalloc(sizeof(unsigned) * (r-l) * channels.count);
          count = i_gsamp_bits(im, l, r, y, data, channels.channels, channels.count, bits);
	  for (i = 0; i < count; ++i) {
	    av_store(target, i+offset, newSVuv(data[i]));
	  }
	  myfree(data);
	  RETVAL = count;
        }
        else {
	  RETVAL = 0;
        }
      OUTPUT:
	RETVAL

undef_neg_int
i_psamp_bits(im, l, y, bits, channels, data_av, data_offset = 0, pixel_count = -1)
        Imager::ImgRaw im
        i_img_dim l
        i_img_dim y
	int bits
	i_channel_list channels
	AV *data_av
        i_img_dim data_offset
        i_img_dim pixel_count
      PREINIT:
	STRLEN data_count;
	size_t data_used;
	unsigned *data;
	ptrdiff_t i;
      CODE:
	i_clear_error();

	data_count = av_len(data_av) + 1;
	if (data_offset < 0) {
	  croak("data_offset must be non-negative");
	}
	if (data_offset > data_count) {
	  croak("data_offset greater than number of samples supplied");
        }
	if (pixel_count == -1 || 
	    data_offset + pixel_count * channels.count > data_count) {
	  pixel_count = (data_count - data_offset) / channels.count;
	}

	data_used = pixel_count * channels.count;
	data = mymalloc(sizeof(unsigned) * data_count);
	for (i = 0; i < data_used; ++i)
	  data[i] = SvUV(*av_fetch(data_av, data_offset + i, 0));

	RETVAL = i_psamp_bits(im, l, l + pixel_count, y, data, channels.channels, 
	                      channels.count, bits);

	if (data)
	  myfree(data);
      OUTPUT:
	RETVAL

undef_neg_int
i_psamp(im, x, y, channels, data, offset = 0, width = -1)
	Imager::ImgRaw im
	i_img_dim x
	i_img_dim y
	i_channel_list channels
        i_sample_list data
	i_img_dim offset
	i_img_dim width
    PREINIT:
	i_img_dim r;
    CODE:
	i_clear_error();
	if (offset < 0) {
	  i_push_error(0, "offset must be non-negative");
	  XSRETURN_UNDEF;
	}
	if (offset > 0) {
	  if (offset > data.count) {
	    i_push_error(0, "offset greater than number of samples supplied");
	    XSRETURN_UNDEF;
	  }
	  data.samples += offset;
	  data.count -= offset;
	}
	if (width == -1 ||
	    width * channels.count > data.count) {
	  width = data.count / channels.count;
        }
	r = x + width;
	RETVAL = i_psamp(im, x, r, y, data.samples, channels.channels, channels.count);
    OUTPUT:
	RETVAL

undef_neg_int
i_psampf(im, x, y, channels, data, offset = 0, width = -1)
	Imager::ImgRaw im
	i_img_dim x
	i_img_dim y
	i_channel_list channels
        i_fsample_list data
	i_img_dim offset
	i_img_dim width
    PREINIT:
	i_img_dim r;
    CODE:
	i_clear_error();
	if (offset < 0) {
	  i_push_error(0, "offset must be non-negative");
	  XSRETURN_UNDEF;
	}
	if (offset > 0) {
	  if (offset > data.count) {
	    i_push_error(0, "offset greater than number of samples supplied");
	    XSRETURN_UNDEF;
	  }
	  data.samples += offset;
	  data.count -= offset;
	}
	if (width == -1 ||
	    width * channels.count > data.count) {
	  width = data.count / channels.count;
        }
	r = x + width;
	RETVAL = i_psampf(im, x, r, y, data.samples, channels.channels, channels.count);
    OUTPUT:
	RETVAL

Imager::ImgRaw
i_img_masked_new(targ, mask, x, y, w, h)
        Imager::ImgRaw targ
        i_img_dim x
        i_img_dim y
        i_img_dim w
        i_img_dim h
      PREINIT:
        i_img *mask;
      CODE:
        if (SvOK(ST(1))) {
          if (!sv_isobject(ST(1)) 
              || !sv_derived_from(ST(1), "Imager::ImgRaw")) {
            croak("i_img_masked_new: parameter 2 must undef or an image");
          }
          mask = INT2PTR(i_img *, SvIV((SV *)SvRV(ST(1))));
        }
        else
          mask = NULL;
        RETVAL = i_img_masked_new(targ, mask, x, y, w, h);
      OUTPUT:
        RETVAL

int
i_plin(im, l, y, ...)
        Imager::ImgRaw  im
        i_img_dim     l
        i_img_dim     y
      PREINIT:
        i_color *work;
        STRLEN i;
        STRLEN len;
        size_t count;
      CODE:
        if (items > 3) {
          if (items == 4 && SvOK(ST(3)) && !SvROK(ST(3))) {
	    /* supplied as a byte string */
            work = (i_color *)SvPV(ST(3), len);
            count = len / sizeof(i_color);
	    if (count * sizeof(i_color) != len) {
              croak("i_plin: length of scalar argument must be multiple of sizeof i_color");
            }
            RETVAL = i_plin(im, l, l+count, y, work);
          }
	  else {
            work = mymalloc(sizeof(i_color) * (items-3));
            for (i=0; i < items-3; ++i) {
              if (sv_isobject(ST(i+3)) 
                  && sv_derived_from(ST(i+3), "Imager::Color")) {
                IV tmp = SvIV((SV *)SvRV(ST(i+3)));
                work[i] = *INT2PTR(i_color *, tmp);
              }
              else {
                myfree(work);
                croak("i_plin: pixels must be Imager::Color objects");
              }
            }
            RETVAL = i_plin(im, l, l+items-3, y, work);
            myfree(work);
          }
        }
        else {
          RETVAL = 0;
        }
      OUTPUT:
        RETVAL

int
i_ppixf(im, x, y, cl)
        Imager::ImgRaw im
        i_img_dim x
        i_img_dim y
        Imager::Color::Float cl

void
i_gsampf(im, l, r, y, channels)
        Imager::ImgRaw im
        i_img_dim l
        i_img_dim r
        i_img_dim y
	i_channel_list channels
      PREINIT:
        i_fsample_t *data;
        i_img_dim count, i;
      PPCODE:
        if (l < r) {
          data = mymalloc(sizeof(i_fsample_t) * (r-l) * channels.count);
          count = i_gsampf(im, l, r, y, data, channels.channels, channels.count);
          if (GIMME_V == G_ARRAY) {
            EXTEND(SP, count);
            for (i = 0; i < count; ++i)
              PUSHs(sv_2mortal(newSVnv(data[i])));
          }
          else {
            EXTEND(SP, 1);
            PUSHs(sv_2mortal(newSVpv((void *)data, count * sizeof(i_fsample_t))));
          }
          myfree(data);
        }
        else {
          if (GIMME_V != G_ARRAY) {
	    XSRETURN_UNDEF;
          }
        }

int
i_plinf(im, l, y, ...)
        Imager::ImgRaw  im
        i_img_dim     l
        i_img_dim     y
      PREINIT:
        i_fcolor *work;
        i_img_dim i;
        STRLEN len;
        size_t count;
      CODE:
        if (items > 3) {
          if (items == 4 && SvOK(ST(3)) && !SvROK(ST(3))) {
	    /* supplied as a byte string */
            work = (i_fcolor *)SvPV(ST(3), len);
            count = len / sizeof(i_fcolor);
	    if (count * sizeof(i_fcolor) != len) {
              croak("i_plin: length of scalar argument must be multiple of sizeof i_fcolor");
            }
            RETVAL = i_plinf(im, l, l+count, y, work);
          }
	  else {
            work = mymalloc(sizeof(i_fcolor) * (items-3));
            for (i=0; i < items-3; ++i) {
              if (sv_isobject(ST(i+3)) 
                  && sv_derived_from(ST(i+3), "Imager::Color::Float")) {
                IV tmp = SvIV((SV *)SvRV(ST(i+3)));
                work[i] = *INT2PTR(i_fcolor *, tmp);
              }
              else {
                myfree(work);
                croak("i_plinf: pixels must be Imager::Color::Float objects");
              }
            }
            /**(char *)0 = 1;*/
            RETVAL = i_plinf(im, l, l+items-3, y, work);
            myfree(work);
          }
        }
        else {
          RETVAL = 0;
        }
      OUTPUT:
        RETVAL

Imager::Color::Float
i_gpixf(im, x, y)
	Imager::ImgRaw im
	i_img_dim x
	i_img_dim y;
      CODE:
	RETVAL = (i_fcolor *)mymalloc(sizeof(i_fcolor));
	if (i_gpixf(im, x, y, RETVAL) != 0) {
          myfree(RETVAL);
          XSRETURN_UNDEF;
        }
      OUTPUT:
        RETVAL

void
i_glin(im, l, r, y)
        Imager::ImgRaw im
        i_img_dim l
        i_img_dim r
        i_img_dim y
      PREINIT:
        i_color *vals;
        i_img_dim count, i;
      PPCODE:
        if (l < r) {
          vals = mymalloc((r-l) * sizeof(i_color));
          memset(vals, 0, (r-l) * sizeof(i_color));
          count = i_glin(im, l, r, y, vals);
	  if (GIMME_V == G_ARRAY) {
            EXTEND(SP, count);
            for (i = 0; i < count; ++i) {
              SV *sv = make_i_color_sv(aTHX_ vals+i);
              PUSHs(sv);
            }
          }
          else if (count) {
	    EXTEND(SP, 1);
	    PUSHs(sv_2mortal(newSVpv((void *)vals, count * sizeof(i_color))));
          }
          myfree(vals);
        }

void
i_glinf(im, l, r, y)
        Imager::ImgRaw im
        i_img_dim l
        i_img_dim r
        i_img_dim y
      PREINIT:
        i_fcolor *vals;
        i_img_dim count, i;
        i_fcolor zero;
      PPCODE:
	for (i = 0; i < MAXCHANNELS; ++i)
	  zero.channel[i] = 0;
        if (l < r) {
          vals = mymalloc((r-l) * sizeof(i_fcolor));
          for (i = 0; i < r-l; ++i)
	    vals[i] = zero;
          count = i_glinf(im, l, r, y, vals);
          if (GIMME_V == G_ARRAY) {
            EXTEND(SP, count);
            for (i = 0; i < count; ++i) {
              SV *sv;
              i_fcolor *col = mymalloc(sizeof(i_fcolor));
              *col = vals[i];
              sv = sv_newmortal();
              sv_setref_pv(sv, "Imager::Color::Float", (void *)col);
              PUSHs(sv);
            }
          }
          else if (count) {
            EXTEND(SP, 1);
            PUSHs(sv_2mortal(newSVpv((void *)vals, count * sizeof(i_fcolor))));
          }
          myfree(vals);
        }

Imager::ImgRaw
i_img_8_new(x, y, ch)
        i_img_dim x
        i_img_dim y
        int ch

Imager::ImgRaw
i_img_16_new(x, y, ch)
        i_img_dim x
        i_img_dim y
        int ch

Imager::ImgRaw
i_img_to_rgb16(im)
       Imager::ImgRaw im

Imager::ImgRaw
i_img_double_new(x, y, ch)
        i_img_dim x
        i_img_dim y
        int ch

Imager::ImgRaw
i_img_to_drgb(im)
       Imager::ImgRaw im

undef_int
i_tags_addn(im, name_sv, code, idata)
        Imager::ImgRaw im
	SV *name_sv
        int     code
        int     idata
      PREINIT:
        char *name;
        STRLEN len;
      CODE:
        SvGETMAGIC(name_sv);
        if (SvOK(name_sv))
          name = SvPV_nomg(name_sv, len);
        else
          name = NULL;
        RETVAL = i_tags_addn(&im->tags, name, code, idata);
      OUTPUT:
        RETVAL

undef_int
i_tags_add(im, name_sv, code, data_sv, idata)
        Imager::ImgRaw  im
	SV *name_sv
        int code
	SV *data_sv
        int idata
      PREINIT:
        char *name;
        char *data;
        STRLEN len;
      CODE:
        SvGETMAGIC(name_sv);
        if (SvOK(name_sv))
          name = SvPV_nomg(name_sv, len);
        else
          name = NULL;
	SvGETMAGIC(data_sv);
        if (SvOK(data_sv))
          data = SvPV(data_sv, len);
        else {
          data = NULL;
          len = 0;
        }
        RETVAL = i_tags_add(&im->tags, name, code, data, len, idata);
      OUTPUT:
        RETVAL

SysRet
i_tags_find(im, name, start)
        Imager::ImgRaw  im
        char *name
        int start
      PREINIT:
        int entry;
      CODE:
        if (i_tags_find(&im->tags, name, start, &entry)) {
	  RETVAL = entry;
        } else {
          XSRETURN_UNDEF;
        }
      OUTPUT:
        RETVAL

SysRet
i_tags_findn(im, code, start)
        Imager::ImgRaw  im
        int             code
        int             start
      PREINIT:
        int entry;
      CODE:
        if (i_tags_findn(&im->tags, code, start, &entry)) {
          RETVAL = entry;
        }
        else {
          XSRETURN_UNDEF;
        }
      OUTPUT:
        RETVAL

int
i_tags_delete(im, entry)
        Imager::ImgRaw  im
        int             entry
      CODE:
        RETVAL = i_tags_delete(&im->tags, entry);
      OUTPUT:
        RETVAL

int
i_tags_delbyname(im, name)
        Imager::ImgRaw  im
        char *          name
      CODE:
        RETVAL = i_tags_delbyname(&im->tags, name);
      OUTPUT:
        RETVAL

int
i_tags_delbycode(im, code)
        Imager::ImgRaw  im
        int             code
      CODE:
        RETVAL = i_tags_delbycode(&im->tags, code);
      OUTPUT:
        RETVAL

void
i_tags_get(im, index)
        Imager::ImgRaw  im
        int             index
      PPCODE:
        if (index >= 0 && index < im->tags.count) {
          i_img_tag *entry = im->tags.tags + index;
          EXTEND(SP, 5);
        
          if (entry->name) {
            PUSHs(sv_2mortal(newSVpv(entry->name, 0)));
          }
          else {
            PUSHs(sv_2mortal(newSViv(entry->code)));
          }
          if (entry->data) {
            PUSHs(sv_2mortal(newSVpvn(entry->data, entry->size)));
          }
          else {
            PUSHs(sv_2mortal(newSViv(entry->idata)));
          }
        }

void
i_tags_get_string(im, what_sv)
        Imager::ImgRaw  im
        SV *what_sv
      PREINIT:
        char const *name = NULL;
        int code;
        char buffer[200];
      PPCODE:
        if (SvIOK(what_sv)) {
          code = SvIV(what_sv);
          name = NULL;
        }
        else {
          name = SvPV_nolen(what_sv);
          code = 0;
        }
        if (i_tags_get_string(&im->tags, name, code, buffer, sizeof(buffer))) {
          EXTEND(SP, 1);
          PUSHs(sv_2mortal(newSVpv(buffer, 0)));
        }

int
i_tags_count(im)
        Imager::ImgRaw  im
      CODE:
        RETVAL = im->tags.count;
      OUTPUT:
        RETVAL



MODULE = Imager         PACKAGE = Imager::FillHandle PREFIX=IFILL_

void
IFILL_DESTROY(fill)
        Imager::FillHandle fill

int
IFILL_CLONE_SKIP(...)
    CODE:
        (void)items; /* avoid unused warning for XS variable */
        RETVAL = 1;
    OUTPUT:
        RETVAL

MODULE = Imager         PACKAGE = Imager

Imager::FillHandle
i_new_fill_solid(cl, combine)
        Imager::Color cl
        int combine

Imager::FillHandle
i_new_fill_solidf(cl, combine)
        Imager::Color::Float cl
        int combine

Imager::FillHandle
i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch_sv, dx, dy)
        Imager::Color fg
        Imager::Color bg
        int combine
        int hatch
	SV *cust_hatch_sv
        i_img_dim dx
        i_img_dim dy
      PREINIT:
        unsigned char *cust_hatch;
        STRLEN len;
      CODE:
        SvGETMAGIC(cust_hatch_sv);
        if (SvOK(cust_hatch_sv)) {
          cust_hatch = (unsigned char *)SvPV_nomg(cust_hatch_sv, len);
        }
        else
          cust_hatch = NULL;
        RETVAL = i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy);
      OUTPUT:
        RETVAL

Imager::FillHandle
i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch_sv, dx, dy)
        Imager::Color::Float fg
        Imager::Color::Float bg
        int combine
        int hatch
        SV *cust_hatch_sv
        i_img_dim dx
        i_img_dim dy
      PREINIT:
        unsigned char *cust_hatch;
        STRLEN len;
      CODE:
        SvGETMAGIC(cust_hatch_sv);
        if (SvOK(cust_hatch_sv)) {
          cust_hatch = (unsigned char *)SvPV(cust_hatch_sv, len);
        }
        else
          cust_hatch = NULL;
        RETVAL = i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy);
      OUTPUT:
        RETVAL

Imager::FillHandle
i_new_fill_image(src, matrix_sv, xoff, yoff, combine)
        Imager::ImgRaw src
	SV *matrix_sv
        i_img_dim xoff
        i_img_dim yoff
        int combine
      PREINIT:
        double matrix[9];
        double *matrixp;
        AV *av;
        IV len;
        SV *sv1;
        int i;
      CODE:
        SvGETMAGIC(matrix_sv);
        if (!SvOK(matrix_sv)) {
          matrixp = NULL;
        }
        else {
          if (!SvROK(matrix_sv) || SvTYPE(SvRV(matrix_sv)) != SVt_PVAV)
            croak("i_new_fill_image: matrix parameter must be an arrayref or undef");
	  av=(AV*)SvRV(matrix_sv);
	  len=av_len(av)+1;
          if (len > 9)
            len = 9;
          for (i = 0; i < len; ++i) {
	    sv1=(*(av_fetch(av,i,0)));
	    matrix[i] = SvNV(sv1);
          }
          for (; i < 9; ++i)
            matrix[i] = 0;
          matrixp = matrix;
        }
        RETVAL = i_new_fill_image(src, matrixp, xoff, yoff, combine);
      OUTPUT:
        RETVAL

MODULE = Imager  PACKAGE = Imager::Internal::Hlines  PREFIX=i_int_hlines_

# this class is only exposed for testing

int
i_int_hlines_testing()

#if i_int_hlines_testing()

Imager::Internal::Hlines
i_int_hlines_new(start_y, count_y, start_x, count_x)
	i_img_dim start_y
	int count_y
	i_img_dim start_x
	int count_x

Imager::Internal::Hlines
i_int_hlines_new_img(im)
	Imager::ImgRaw im

void
i_int_hlines_add(hlines, y, minx, width)
	Imager::Internal::Hlines hlines
	i_img_dim y
	i_img_dim minx
	i_img_dim width

void
i_int_hlines_DESTROY(hlines)
	Imager::Internal::Hlines hlines

SV *
i_int_hlines_dump(hlines)
	Imager::Internal::Hlines hlines

int
i_int_hlines_CLONE_SKIP(cls)

#endif

MODULE = Imager  PACKAGE = Imager::Context PREFIX=im_context_

void
im_context_DESTROY(ctx)
   Imager::Context ctx

#ifdef PERL_IMPLICIT_CONTEXT

void
im_context_CLONE(...)
    CODE:
      MY_CXT_CLONE;
      (void)items;
      /* the following sv_setref_pv() will free this inc */
      im_context_refinc(MY_CXT.ctx, "CLONE");
      MY_CXT.ctx = im_context_clone(MY_CXT.ctx, "CLONE");
      sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);

#endif

BOOT:
        PERL_SET_GLOBAL_CALLBACKS;
	PERL_PL_SET_GLOBAL_CALLBACKS;
#ifdef PERL_IMPLICIT_CONTEXT
	{
          MY_CXT_INIT;
	  (void)MY_CXT;
	}
#endif
	start_context(aTHX);
	im_get_context = perl_get_context;
#ifdef HAVE_LIBTT
        i_tt_start();
#endif