#include "ruby_vips.h" VALUE cVIPSMask; static void mask_free(vipsMask *msk) { if(msk->dmask) im_free_dmask(msk->dmask); if(msk->imask) im_free_imask(msk->imask); xfree(msk); } VALUE mask_alloc(VALUE klass) { vipsMask *msk; VALUE new = Data_Make_Struct(klass, vipsMask, 0, mask_free, msk); msk->imask = NULL; msk->dmask = NULL; return new; } static int ary_is_int_2d(VALUE ary) { Check_Type(ary, T_ARRAY); VALUE *row, *rows = RARRAY_PTR(ary); int i, j; for(i = 0; i < RARRAY_LEN(ary); i++) { Check_Type(rows[i], T_ARRAY); for(j = 0; j < RARRAY_LEN(rows[i]); j++) { row = RARRAY_PTR(rows[i]); if(TYPE(row[j]) != T_FIXNUM) return 0; } } return 1; } /* initialize an INTMASK from a ruby array */ static INTMASK* mask_ary2imask(VALUE coeffs) { INTMASK *msk; VALUE row, *rows = RARRAY_PTR(coeffs); int i, j, num_cols, num_rows = RARRAY_LEN(coeffs); num_cols = RARRAY_LEN(rows[0]); if( !(msk = im_create_imask("mask_initialize_imask", num_cols, num_rows)) ) vips_lib_error(); for(i = 0; i < num_rows; i++) { row = rows[i]; for(j = 0; j < RARRAY_LEN(row) && j < num_cols; j++) msk->coeff[i * num_cols + j] = NUM2INT(RARRAY_PTR(row)[j]); } return msk; } static DOUBLEMASK* mask_ary2dmask(VALUE coeffs) { DOUBLEMASK *msk; VALUE row, *rows = RARRAY_PTR(coeffs); int i, j, num_cols, num_rows = RARRAY_LEN(coeffs); num_cols = RARRAY_LEN(rows[0]); if (!(msk = im_create_dmask("mask_ary2dmask", num_cols, num_rows))) vips_lib_error(); for(i = 0; i < num_rows; i++) { row = rows[i]; for(j = 0; j < RARRAY_LEN(row) && j < num_cols; j++) msk->coeff[i * num_cols + j] = NUM2DBL(RARRAY_PTR(row)[j]); } return msk; } /* * call-seq: * Mask.new(coeffs, scale=1, offset=0) -> mask * * Create a new Mask object. coeffs is a two-dimensional array where * every row must have the same length. Note that some methods require a mask * where all values in coeffs are whole integers. */ static VALUE mask_initialize(int argc, VALUE *argv, VALUE obj) { VALUE coeffs, scale, offset; vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); rb_scan_args(argc, argv, "12", &coeffs, &scale, &offset); if (NIL_P(scale)) scale = INT2NUM(1); if (NIL_P(offset)) offset = INT2NUM(0); if(TYPE(scale) == T_FIXNUM && TYPE(offset) == T_FIXNUM && ary_is_int_2d(coeffs)) { msk->imask = mask_ary2imask(coeffs); msk->imask->scale = NUM2INT(scale); msk->imask->offset = NUM2INT(offset); } msk->dmask = mask_ary2dmask(coeffs); msk->dmask->scale = NUM2DBL(scale); msk->dmask->offset = NUM2DBL(offset); return obj; } /* * call-seq: * msk.xsize -> number * * Retrieve the number of columns in the mask. */ static VALUE mask_xsize(VALUE obj) { vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); return INT2FIX(msk->dmask->xsize); } /* * call-seq: * msk.ysize -> number * * Retrieve the number of rows in the mask. */ static VALUE mask_ysize(VALUE obj) { vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); return INT2FIX(msk->dmask->ysize); } /* * call-seq: * msk.scale -> number * * Retrieve the scale of the mask. */ static VALUE mask_scale(VALUE obj) { vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); return msk->imask ? INT2FIX(msk->imask->scale) : DBL2NUM(msk->dmask->scale); } /* * call-seq: * msk.offset -> number * * Retrieve the offset of the mask. */ static VALUE mask_offset(VALUE obj) { vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); return msk->imask ? INT2FIX(msk->imask->offset) : DBL2NUM(msk->dmask->offset); } VALUE imask2rb(INTMASK *msk) { int i, j; VALUE row, rows = rb_ary_new2(msk->ysize); for(i = 0; i < msk->ysize; i++) { row = rb_ary_new2(msk->xsize); rb_ary_push(rows, row); for(j = 0; j < msk->xsize; j++) { rb_ary_push(row, INT2FIX(msk->coeff[i * msk->xsize + j])); } } return rows; } VALUE dmask2rb(DOUBLEMASK *msk) { int i, j; VALUE row, rows = rb_ary_new2(msk->ysize); for(i = 0; i < msk->ysize; i++) { row = rb_ary_new2(msk->xsize); rb_ary_push(rows, row); for(j = 0; j < msk->xsize; j++) { rb_ary_push(row, DBL2NUM(msk->coeff[i * msk->xsize + j])); } } return rows; } /* * call-seq: * msk.coeff -> array * * Retrieve the two-dimensional array of coefficients for the mask. */ static VALUE mask_coeff(VALUE obj) { vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); if(msk->imask) return imask2rb(msk->imask); else return dmask2rb(msk->dmask); } /* * call-seq: * msk.int? -> true or false * * Indicate whether all coefficients, the scale and the offset in the mask are * integers. Some methods require an all-integer mask. */ static VALUE mask_int_p(VALUE obj) { vipsMask *msk; Data_Get_Struct(obj, vipsMask, msk); if(msk->imask) return Qtrue; return Qfalse; } void mask_arg2mask(VALUE arg, INTMASK **imask, DOUBLEMASK **dmask) { INTMASK *imask_t = NULL; DOUBLEMASK *dmask_t = NULL; vipsMask *data; const char *errstr; if (TYPE(arg) == T_ARRAY) { if (imask && ary_is_int_2d(arg)) imask_t = mask_ary2imask(arg); else if (dmask) dmask_t = mask_ary2dmask(arg); } else if (CLASS_OF(arg) == cVIPSMask) { Data_Get_Struct(arg, vipsMask, data); if (imask) imask_t = data->imask; if (dmask) dmask_t = data->dmask; } if (!imask_t && !dmask_t) { if (imask && dmask) errstr = "Expected an array or a Mask"; else errstr = "Expected an int array or an int Mask"; rb_raise(rb_eArgError, "%s", errstr); } if (imask) *imask = imask_t; if (dmask) *dmask = dmask_t; } /* * call-seq: * msk.int? -> true or false * * Create a one-band, band format :DOUBLE image based on mask *self*. */ static VALUE mask_to_image(VALUE obj) { vipsMask *msk; OutImg(obj, new, data, im); Data_Get_Struct(obj, vipsMask, msk); if (im_mask2vips(msk->dmask, im)) vips_lib_error(); return new; } /* * VIPS uses masks for various operations. A vips mask is a two-dimensional * array with a scale and an offset. * * All operations that accept a Mask object also accept an array, in which case * scale defaults to 1 and offset to zero. * * Some vips operations require that all values in the mask are integer values. * These operations will raise an exception if given a mask that contains * any float values. */ void init_Mask( void ) { cVIPSMask = rb_define_class_under(mVIPS, "Mask", rb_cObject); rb_define_alloc_func(cVIPSMask, mask_alloc); rb_define_method(cVIPSMask, "initialize", mask_initialize, -1); rb_define_method(cVIPSMask, "xsize", mask_xsize, 0); rb_define_method(cVIPSMask, "ysize", mask_ysize, 0); rb_define_method(cVIPSMask, "scale", mask_scale, 0); rb_define_method(cVIPSMask, "offset", mask_offset, 0); rb_define_method(cVIPSMask, "coeff", mask_coeff, 0); rb_define_method(cVIPSMask, "int?", mask_int_p, 0); rb_define_method(cVIPSMask, "to_image", mask_to_image, 0); #if 0 VALUE mVIPS = rb_define_module("VIPS"); #endif }