/*
+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.
*/
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
SV *data;
dSP;
- if (!SvOK(cbd->readcb))
+ 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;
off_t result;
dSP;
- if (!SvOK(cbd->seekcb))
+ 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;
dSP;
bool success;
- if (!SvOK(cbd->writecb))
+ 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;
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->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);
}
{ "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[] =
}
}
-
/* 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 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
+
#ifdef IMEXIF_ENABLE
#define i_exif_enabled() 1
#else
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
PUSHs(sv_2mortal(newSVpv(item,0)));
}
-Imager::ImgRaw
-i_img_new()
-
-Imager::ImgRaw
-i_img_empty(im,x,y)
- Imager::ImgRaw im
- i_img_dim x
- i_img_dim y
-
-Imager::ImgRaw
-i_img_empty_ch(im,x,y,ch)
- Imager::ImgRaw im
- i_img_dim x
- i_img_dim y
- int ch
-
Imager::ImgRaw
i_sametype(im, x, y)
Imager::ImgRaw im
int
i_log_enabled()
-void
-i_img_exorcise(im)
- Imager::ImgRaw im
-
-void
-i_img_destroy(im)
- Imager::ImgRaw im
-
void
i_img_info(im)
Imager::ImgRaw im
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) {
if (GIMME_V == G_ARRAY) {
EXTEND(SP, count);
for (i = 0; i < count; ++i) {
- PUSHs(sv_2mortal(newSViv(work[i])));
+ PUSHs(boolSV(work[i]));
}
}
else {
XSRETURN(col_cnt);
-Imager::ImgRaw
+void
i_transform(im,opx,opy,parm)
Imager::ImgRaw im
PREINIT:
AV* av;
SV* sv1;
int i;
- CODE:
+ i_img *result;
+ PPCODE:
if (!SvROK(ST(1))) croak("Imager: Parameter 1 must be a reference to an array\n");
if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to an array\n");
if (!SvROK(ST(3))) croak("Imager: Parameter 3 must be a reference to an array\n");
sv1=(*(av_fetch(av,i,0)));
parm[i]=(double)SvNV(sv1);
}
- RETVAL=i_transform(im,opx,opxl,opy,opyl,parm,parmlen);
+ result=i_transform(im,opx,opxl,opy,opyl,parm,parmlen);
myfree(parm);
myfree(opy);
myfree(opx);
- ST(0) = sv_newmortal();
- if (RETVAL == 0) ST(0)=&PL_sv_undef;
- else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
+ if (result) {
+ SV *result_sv = sv_newmortal();
+ EXTEND(SP, 1);
+ sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result);
+ PUSHs(result_sv);
+ }
-Imager::ImgRaw
+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 *sv1;
IV tmp;
int i;
- CODE:
+ i_img *result;
+ PPCODE:
in_imgs_count = av_len(av_in_imgs)+1;
for (i = 0; i < in_imgs_count; ++i) {
c_regs = mymalloc(c_regs_count * sizeof(i_color));
/* I don't bother initializing the colou?r registers */
- RETVAL=i_transform2(width, height, channels, ops, ops_count,
+ 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);
- ST(0) = sv_newmortal();
- if (RETVAL == 0) ST(0)=&PL_sv_undef;
- else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
+ if (result) {
+ SV *result_sv = sv_newmortal();
+ EXTEND(SP, 1);
+ sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result);
+ PUSHs(result_sv);
+ }
void
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
colors = mymalloc(sizeof(i_color) * count);
if (i_getcolors(im, index, colors, count)) {
for (i = 0; i < count; ++i) {
- i_color *pv;
- SV *sv = sv_newmortal();
- pv = mymalloc(sizeof(i_color));
- *pv = colors[i];
- sv_setref_pv(sv, "Imager::Color", (void *)pv);
+ SV *sv = make_i_color_sv(aTHX_ colors+i);
PUSHs(sv);
}
}
Imager::ImgRaw im
void
-i_gsamp(im, l, r, y, ...)
+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:
- int *chans;
- int chan_count;
i_sample_t *data;
i_img_dim count, i;
PPCODE:
- if (items < 5)
- croak("No channel numbers supplied to g_samp()");
if (l < r) {
- chan_count = items - 4;
- chans = mymalloc(sizeof(int) * chan_count);
- for (i = 0; i < chan_count; ++i)
- chans[i] = SvIV(ST(i+4));
- data = mymalloc(sizeof(i_sample_t) * (r-l) * chan_count); /* XXX: memleak? */
- count = i_gsamp(im, l, r, y, data, chans, chan_count);
- myfree(chans);
+ data = mymalloc(sizeof(i_sample_t) * (r-l) * channels.count); /* XXX: memleak? */
+ 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)
}
undef_neg_int
-i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
+i_gsamp_bits(im, l, r, y, bits, target, offset, channels)
Imager::ImgRaw im
i_img_dim l
i_img_dim r
int bits
AV *target
STRLEN offset
+ i_channel_list channels
PREINIT:
- int *chans;
- int chan_count;
unsigned *data;
i_img_dim count, i;
CODE:
if (items < 8)
croak("No channel numbers supplied to g_samp()");
if (l < r) {
- chan_count = items - 7;
- chans = mymalloc(sizeof(int) * chan_count);
- for (i = 0; i < chan_count; ++i)
- chans[i] = SvIV(ST(i+7));
- data = mymalloc(sizeof(unsigned) * (r-l) * chan_count);
- count = i_gsamp_bits(im, l, r, y, data, chans, chan_count, bits);
- myfree(chans);
+ 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]));
}
RETVAL
undef_neg_int
-i_psamp_bits(im, l, y, bits, channels_sv, data_av, data_offset = 0, pixel_count = -1)
+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
- SV *channels_sv
+ i_channel_list channels
AV *data_av
- int data_offset
- int pixel_count
+ i_img_dim data_offset
+ i_img_dim pixel_count
PREINIT:
- int chan_count;
- int *channels;
STRLEN data_count;
size_t data_used;
unsigned *data;
ptrdiff_t i;
CODE:
i_clear_error();
- if (SvOK(channels_sv)) {
- AV *channels_av;
- if (!SvROK(channels_sv) || SvTYPE(SvRV(channels_sv)) != SVt_PVAV) {
- croak("channels is not an array ref");
- }
- channels_av = (AV *)SvRV(channels_sv);
- chan_count = av_len(channels_av) + 1;
- if (chan_count < 1) {
- croak("i_psamp_bits: no channels provided");
- }
- channels = mymalloc(sizeof(int) * chan_count);
- for (i = 0; i < chan_count; ++i)
- channels[i] = SvIV(*av_fetch(channels_av, i, 0));
- }
- else {
- chan_count = im->channels;
- channels = NULL;
- }
data_count = av_len(data_av) + 1;
if (data_offset < 0) {
- croak("data_offset must by non-negative");
+ 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 * chan_count > data_count) {
- pixel_count = (data_count - data_offset) / chan_count;
+ data_offset + pixel_count * channels.count > data_count) {
+ pixel_count = (data_count - data_offset) / channels.count;
}
- data_used = pixel_count * chan_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,
- chan_count, bits);
+ RETVAL = i_psamp_bits(im, l, l + pixel_count, y, data, channels.channels,
+ channels.count, bits);
if (data)
myfree(data);
- if (channels)
- myfree(channels);
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
Imager::Color::Float cl
void
-i_gsampf(im, l, r, y, ...)
+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:
- int *chans;
- int chan_count;
i_fsample_t *data;
i_img_dim count, i;
PPCODE:
- if (items < 5)
- croak("No channel numbers supplied to g_sampf()");
if (l < r) {
- chan_count = items - 4;
- chans = mymalloc(sizeof(int) * chan_count);
- for (i = 0; i < chan_count; ++i)
- chans[i] = SvIV(ST(i+4));
- data = mymalloc(sizeof(i_fsample_t) * (r-l) * chan_count);
- count = i_gsampf(im, l, r, y, data, chans, chan_count);
- myfree(chans);
+ 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)
if (GIMME_V == G_ARRAY) {
EXTEND(SP, count);
for (i = 0; i < count; ++i) {
- SV *sv;
- i_color *col = mymalloc(sizeof(i_color));
- *col = vals[i];
- sv = sv_newmortal();
- sv_setref_pv(sv, "Imager::Color", (void *)col);
+ SV *sv = make_i_color_sv(aTHX_ vals+i);
PUSHs(sv);
}
}
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
#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