/* Extern: images_from_imagelist Purpose: Convert an array of Image *s to an ImageMagick scene sequence (i.e. a doubly-linked list of Images) Returns: a pointer to the head of the scene sequence list */ static Image * images_from_imagelist(VALUE imagelist) { long x, len; Image *head = NULL; volatile VALUE images, t; len = imagelist_length(imagelist); if (len == 0) { rb_raise(rb_eArgError, "no images in this image list"); } images = rb_iv_get(imagelist, "@images"); for (x = 0; x < len; x++) { Image *image; t = rb_ary_entry(images, x); image = rm_check_destroyed(t); AppendImageToList(&head, image); } return head; }
/* Method: ImageList#morph(number_images) Purpose: requires a minimum of two images. The first image is transformed into the second by a number of intervening images as specified by "number_images". Returns: a new Image with the images array set to the morph sequence. @scenes = 0 */ VALUE ImageList_morph(VALUE self, VALUE nimages) { Image *images, *new_images; ExceptionInfo exception; long number_images; if (imagelist_length(self) < 1L) { rb_raise(rb_eArgError, "no images in this image list"); } // Use a signed long so we can test for a negative argument. number_images = NUM2LONG(nimages); if (number_images <= 0) { rb_raise(rb_eArgError, "number of intervening images must be > 0"); } GetExceptionInfo(&exception); images = images_from_imagelist(self); new_images = MorphImages(images, (unsigned long)number_images, &exception); rm_split(images); rm_check_exception(&exception, new_images, DestroyOnError); (void) DestroyExceptionInfo(&exception); rm_ensure_result(new_images); return rm_imagelist_from_images(new_images); }
/** * Raise exception if imagelist is emtpy. * * No Ruby usage (internal function) * * @param imagelist the imagelist * @return the number of images * @throw ArgError */ static long check_imagelist_length(VALUE imagelist) { long len = imagelist_length(imagelist); if (len == 0) { rb_raise(rb_eArgError, "no images in this image list"); } return len; }
/* Method: ImageList#map(reference, dither=false) Purpose: Call MapImages Returns: a new ImageList with mapped images. @scene is set to self.scene */ VALUE ImageList_map(int argc, VALUE *argv, VALUE self) { Image *images, *new_images = NULL; Image *map; unsigned int dither = False; volatile VALUE scene, new_imagelist, t; ExceptionInfo exception; switch (argc) { case 2: dither = RTEST(argv[1]); case 1: t = ImageList_cur_image(argv[0]); map = rm_check_destroyed(t); break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc); break; } if (imagelist_length(self) == 0L) { rb_raise(rb_eArgError, "no images in this image list"); } // Convert image array to image sequence, clone image sequence. GetExceptionInfo(&exception); images = images_from_imagelist(self); new_images = CloneImageList(images, &exception); rm_split(images); rm_check_exception(&exception, new_images, DestroyOnError); (void) DestroyExceptionInfo(&exception); rm_ensure_result(new_images); // Call ImageMagick (void) MapImages(new_images, map, dither); rm_check_image_exception(new_images, DestroyOnError); // Set @scene in new ImageList object to same value as in self. new_imagelist = rm_imagelist_from_images(new_images); scene = rb_iv_get(self, "@scene"); (void) imagelist_scene_eq(new_imagelist, scene); return new_imagelist; }
/** * Write all the images to the specified file. If the file format supports * multi-image files, and the 'images' array contains more than one image, then * the images will be written as a single multi-image file. Otherwise each image * will be written to a separate file. * * Ruby usage: * - @verbatim ImageList#write(file) @endverbatim * * @param self this object * @param file the file * @return self */ VALUE ImageList_write(VALUE self, VALUE file) { Image *images, *img; Info *info; const MagickInfo *m; VALUE info_obj; unsigned long scene; ExceptionInfo *exception; info_obj = rm_info_new(); Data_Get_Struct(info_obj, Info, info); if (TYPE(file) == T_FILE) { OpenFile *fptr; // Ensure file is open - raise error if not GetOpenFile(file, fptr); #if defined(_WIN32) add_format_prefix(info, fptr->pathv); SetImageInfoFile(info, NULL); #else SetImageInfoFile(info, GetReadFile(fptr)); #endif } else { add_format_prefix(info, file); SetImageInfoFile(info, NULL); } // Convert the images array to an images sequence. images = images_from_imagelist(self); // Copy the filename into each image. Set a scene number to be used if // writing multiple files. (Ref: ImageMagick's utilities/convert.c for (scene = 0, img = images; img; img = GetNextImageInList(img)) { img->scene = scene++; strcpy(img->filename, info->filename); } // Find out if the format supports multi-images files. exception = AcquireExceptionInfo(); (void) SetImageInfo(info, MagickTrue, exception); rm_check_exception(exception, images, RetainOnError); m = GetMagickInfo(info->magick, exception); rm_check_exception(exception, images, RetainOnError); (void) DestroyExceptionInfo(exception); // Tell WriteImage if we want a multi-images file. if (imagelist_length(self) > 1L && m->adjoin) { info->adjoin = MagickTrue; } for (img = images; img; img = GetNextImageInList(img)) { rm_sync_image_options(img, info); (void) WriteImage(info, img); // images will be split before raising an exception rm_check_image_exception(images, RetainOnError); if (info->adjoin) { break; } } rm_split(images); RB_GC_GUARD(info_obj); return self; }
/* Method: ImageList#write(file) Purpose: Write all the images to the specified file. If the file format supports multi-image files, and the @images array contains more than one image, then the images will be written as a single multi-image file. Otherwise each image will be written to a separate file. Returns self. */ VALUE ImageList_write(VALUE self, VALUE file) { Image *images, *img; Info *info; const MagickInfo *m; volatile VALUE info_obj; char *filename; long filenameL; unsigned long scene; ExceptionInfo exception; info_obj = rm_info_new(); Data_Get_Struct(info_obj, Info, info); if (TYPE(file) == T_FILE) { OpenFile *fptr; // Ensure file is open - raise error if not GetOpenFile(file, fptr); SetImageInfoFile(info, GetReadFile(fptr)); } else { // Convert arg to string. Catch exceptions. file = rb_rescue(rb_String, file, file_arg_rescue, file); // Copy the filename to the Info and to the Image. filename = rb_str2cstr(file, &filenameL); filenameL = min(filenameL, MaxTextExtent-1); memcpy(info->filename, filename, (size_t)filenameL); info->filename[filenameL] = '\0'; SetImageInfoFile(info, NULL); } // Convert the images array to an images sequence. images = images_from_imagelist(self); // Copy the filename into each images. Set a scene number to be used if // writing multiple files. (Ref: ImageMagick's utilities/convert.c for (scene = 0, img = images; img; img = GetNextImageInList(img)) { img->scene = scene++; strcpy(img->filename, info->filename); } GetExceptionInfo(&exception); (void) SetImageInfo(info, MagickTrue, &exception); rm_check_exception(&exception, images, RetainOnError); (void) DestroyExceptionInfo(&exception); // Find out if the format supports multi-images files. GetExceptionInfo(&exception); m = GetMagickInfo(info->magick, &exception); rm_check_exception(&exception, images, RetainOnError); (void) DestroyExceptionInfo(&exception); // Tell WriteImage if we want a multi-images file. if (imagelist_length(self) > 1L && m->adjoin) { info->adjoin = MagickTrue; } for (img = images; img; img = GetNextImageInList(img)) { (void) WriteImage(info, img); // images will be split before raising an exception rm_check_image_exception(images, RetainOnError); if (info->adjoin) { break; } } rm_split(images); return self; }
/* Method: ImageList#quantize(<number_colors<, colorspace<, dither<, tree_depth<, measure_error>>>>>) defaults: 256, Magick::RGBColorspace, true, 0, false Purpose: call QuantizeImages Returns: a new ImageList with quantized images. 'scene' is set to the same value as self.scene */ VALUE ImageList_quantize(int argc, VALUE *argv, VALUE self) { Image *images, *new_images; Image *new_image; QuantizeInfo quantize_info; ExceptionInfo exception; volatile VALUE new_imagelist, scene; GetQuantizeInfo(&quantize_info); switch (argc) { case 5: quantize_info.measure_error = (MagickBooleanType) RTEST(argv[4]); case 4: quantize_info.tree_depth = (unsigned long)NUM2INT(argv[3]); case 3: quantize_info.dither = (MagickBooleanType) RTEST(argv[2]); case 2: VALUE_TO_ENUM(argv[1], quantize_info.colorspace, ColorspaceType); case 1: quantize_info.number_colors = NUM2ULONG(argv[0]); case 0: break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 5)", argc); break; } if (imagelist_length(self) == 0L) { rb_raise(rb_eArgError, "no images in this image list"); } // Convert image array to image sequence, clone image sequence. GetExceptionInfo(&exception); images = images_from_imagelist(self); new_images = CloneImageList(images, &exception); rm_split(images); rm_check_exception(&exception, new_images, DestroyOnError); (void) DestroyExceptionInfo(&exception); rm_ensure_result(new_images); (void) QuantizeImages(&quantize_info, new_images); rm_check_exception(&exception, new_images, DestroyOnError); // Create new ImageList object, convert mapped image sequence to images, // append to images array. new_imagelist = ImageList_new(); while ((new_image = ShiftImageList(&new_images))) { imagelist_push(new_imagelist, rm_image_new(new_image)); } // Set @scene in new ImageList object to same value as in self. scene = rb_iv_get(self, "@scene"); (void) rb_iv_set(new_imagelist, "@scene", scene); return new_imagelist; }