ext/RMagick/rmimage.c in rmagick-1.9.0 vs ext/RMagick/rmimage.c in rmagick-1.9.1
- old
+ new
@@ -1,6 +1,6 @@
-/* $Id: rmimage.c,v 1.99 2005/06/19 20:26:34 rmagick Exp $ */
+/* $Id: rmimage.c,v 1.118 2005/09/07 21:51:45 rmagick Exp $ */
/*============================================================================\
| Copyright (C) 2005 by Timothy P. Hunter
| Name: rmimage.c
| Author: Tim Hunter
| Purpose: Image class method definitions for RMagick
@@ -237,57 +237,11 @@
}
}
return self;
}
-/*
- Method: Image#properties [{ |k,v| block }]
- Purpose: Traverse the attributes and yield to the block.
- If no block, return a hash of all the attribute
- keys & values
- Notes: I use the word "properties" to distinguish between
- these "user-added" attribute strings and Image
- object attributes.
-*/
-VALUE
-Image_properties(VALUE self)
-{
- Image *image;
- const ImageAttribute *attr;
- volatile VALUE attr_hash;
- Data_Get_Struct(self, Image, image);
-
- // If block, iterate over attributes
- if (rb_block_given_p())
- {
- volatile VALUE ary = rb_ary_new2(2);
- for (attr = image->attributes; attr; attr = Next_Attribute)
- {
- // Store the next ptr where Image#aset can see it.
- // The app may decide to delete that attribute.
- Next_Attribute = attr->next;
- rb_ary_store(ary, 0, rb_str_new2(attr->key));
- rb_ary_store(ary, 1, rb_str_new2(attr->value));
- rb_yield(ary);
- }
-
- return self;
- }
-
- // otherwise return properties hash
- else
- {
- attr_hash = rb_hash_new();
- for (attr = image->attributes; attr; attr = attr->next)
- {
- rb_hash_aset(attr_hash, rb_str_new2(attr->key), rb_str_new2(attr->value));
- }
- return attr_hash;
- }
-}
-
/*
Method: Image#background_color
Purpose: Return the name of the background color as a String.
*/
VALUE
@@ -372,13 +326,17 @@
ChannelType channels;
ExceptionInfo exception;
channels = extract_channels(&argc, argv);
+ if (argc > 1)
+ {
+ raise_ChannelType_error(argv[argc-1]);
+ }
if (argc == 0)
{
- rb_raise(rb_eArgError, "wrong number of arguments (0 for 1 or more)");
+ rb_raise(rb_eArgError, "no threshold specified");
}
GetExceptionInfo(&exception);
Data_Get_Struct(self, Image, image);
@@ -394,37 +352,26 @@
#endif
}
/*
- Method: Image#border_color
- Purpose: Return the name of the border color as a String.
+ * Method: Image#black_threshold(red_channel [, green_channel
+ * [, blue_channel [, opacity_channel]]]);
+ * Purpose: Call BlackThresholdImage
*/
VALUE
-Image_border_color(VALUE self)
+Image_black_threshold(int argc, VALUE *argv, VALUE self)
{
- Image *image;
-
- Data_Get_Struct(self, Image, image);
- return PixelPacket_to_Color_Name(image, &image->border_color);
+#if defined(HAVE_BLACKTHRESHOLDIMAGE)
+ return threshold_image(argc, argv, self, BlackThresholdImage);
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
}
-/*
- Method: Image#border_color=
- Purpose: Set the the border color
-*/
-VALUE
-Image_border_color_eq(VALUE self, VALUE color)
-{
- Image *image;
- rm_check_frozen(self);
- Data_Get_Struct(self, Image, image);
- Color_to_PixelPacket(&image->border_color, color);
- return self;
-}
-
DEF_ATTR_ACCESSOR(Image, blur, dbl)
/*
* Method: Image#blur_channel(radius = 0.0, sigma = 1.0, channel=AllChannels)
@@ -465,10 +412,11 @@
rm_not_implemented();
return (VALUE)0;
#endif
}
+
/*
Method: Image#blur_image(radius=0.0, sigma=1.0)
Purpose: Blur the image
Notes: The "blur" name is used for the attribute
*/
@@ -476,10 +424,11 @@
Image_blur_image(int argc, VALUE *argv, VALUE self)
{
return effect_image(self, argc, argv, BlurImage);
}
+
/*
Method: Image#border(width, height, color)
Image#border!(width, height, color)
Purpose: surrounds the image with a border of the specified width,
height, and named color
@@ -541,11 +490,41 @@
VALUE color)
{
return border(False, self, width, height, color);
}
+
/*
+ Method: Image#border_color
+ Purpose: Return the name of the border color as a String.
+*/
+VALUE
+Image_border_color(VALUE self)
+{
+ Image *image;
+
+ Data_Get_Struct(self, Image, image);
+ return PixelPacket_to_Color_Name(image, &image->border_color);
+}
+
+/*
+ Method: Image#border_color=
+ Purpose: Set the the border color
+*/
+VALUE
+Image_border_color_eq(VALUE self, VALUE color)
+{
+ Image *image;
+
+ rm_check_frozen(self);
+ Data_Get_Struct(self, Image, image);
+ Color_to_PixelPacket(&image->border_color, color);
+ return self;
+}
+
+
+/*
Method: Image#bounding_box
Purpose: returns the bounding box of an image canvas
*/
VALUE Image_bounding_box(VALUE self)
{
@@ -558,10 +537,11 @@
box = GetImageBoundingBox(image, &exception);
HANDLE_ERROR
return Rectangle_from_RectangleInfo(&box);
}
+
/*
Method: Image.capture(silent=false,
frame=false,
descend=false,
screen=false,
@@ -687,10 +667,11 @@
rm_not_implemented();
return (VALUE)0;
#endif
}
+
/*
Method: Image#changed?
Purpose: Return true if any pixel in the image has been altered since
the image was constituted.
*/
@@ -732,70 +713,10 @@
return rm_image_new(new_image);
}
/*
- Method: Image#compare_channel(ref_image, metric [, channel...])
- Purpose: compares one or more channels in two images and returns
- the specified distortion metric and a comparison image.
- Notes: If no channels are specified, the default is AllChannels.
- That case is the equivalent of the CompareImages method in
- ImageMagick.
-
- Originally this method was called channel_compare, but
- that doesn't match the general naming convention that
- methods which accept multiple optional ChannelType
- arguments have names that end in _channel. So I renamed
- the method to compare_channel but kept channel_compare as
- an alias.
-*/
-VALUE Image_compare_channel(
- int argc,
- VALUE *argv,
- VALUE self)
-{
-#if defined(HAVE_COMPAREIMAGECHANNELS)
-
- Image *image, *r_image, *difference_image;
- double distortion;
- volatile VALUE ary;
- MetricType metric_type;
- ChannelType channels;
- ExceptionInfo exception;
-
- channels = extract_channels(&argc, argv);
- if (argc < 2)
- {
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or more)", argc);
- }
-
- Data_Get_Struct(self, Image, image);
- Data_Get_Struct(ImageList_cur_image(argv[0]), Image, r_image);
- VALUE_TO_ENUM(argv[1], metric_type, MetricType);
-
- GetExceptionInfo(&exception);
- difference_image = CompareImageChannels(image
- , r_image
- , channels
- , metric_type
- , &distortion
- , &exception);
- HANDLE_ERROR
-
- ary = rb_ary_new2(2);
- rb_ary_store(ary, 0, rm_image_new(difference_image));
- rb_ary_store(ary, 1, rb_float_new(distortion));
-
- return ary;
-#else
- rm_not_implemented();
- return (VALUE)0;
-#endif
-}
-
-
-/*
Method: Image#channel_depth(channel_depth=AllChannels)
Purpose: GetImageChannelDepth
*/
VALUE
Image_channel_depth(int argc, VALUE *argv, VALUE self)
@@ -1062,37 +983,23 @@
rm_not_implemented();
return (VALUE)0;
#endif
}
-/*
- * Method: Image#black_threshold(red_channel [, green_channel
- * [, blue_channel [, opacity_channel]]]);
- * Purpose: Call BlackThresholdImage
-*/
-VALUE
-Image_black_threshold(int argc, VALUE *argv, VALUE self)
-{
-#if defined(HAVE_BLACKTHRESHOLDIMAGE)
- return threshold_image(argc, argv, self, BlackThresholdImage);
-#else
- rm_not_implemented();
- return (VALUE)0;
-#endif
-}
-
/*
Method: Image#channel_threshold(red_channel, green_channel=MaxRGB,
blue_channel=MaxRGB, opacity_channel=MaxRGB)
Purpose: Same as Image#threshold except that you can specify
a separate threshold for each channel
*/
VALUE
Image_channel_threshold(int argc, VALUE *argv, VALUE self)
{
+ rb_warning("This method is deprecated in this release of " Q(MAGICKNAME)
+ ". Use bilevel_channel instead.");
return threshold_image(argc, argv, self,
#if defined(HAVE_THRESHOLDIMAGECHANNEL)
ThresholdImageChannel
#else
ChannelThresholdImage
@@ -1649,11 +1556,11 @@
image->colormap[index] = new_color;
return PixelPacket_to_Color_Name(image, &color);
}
-DEF_ATTR_READER(Image, colors, int)
+DEF_ATTR_READER(Image, colors, ulong)
/*
Method: Image#colorspace
Purpose: Return theImage pixel interpretation. If the colorspace is
RGB the pixels are red, green, blue. If matte is true, then
@@ -1723,14 +1630,93 @@
#endif
return self;
}
+
DEF_ATTR_READER(Image, columns, int)
-DEF_ATTR_READER(Image, compose, int)
+
/*
+ Method: Image#compare_channel(ref_image, metric [, channel...])
+ Purpose: compares one or more channels in two images and returns
+ the specified distortion metric and a comparison image.
+ Notes: If no channels are specified, the default is AllChannels.
+ That case is the equivalent of the CompareImages method in
+ ImageMagick.
+
+ Originally this method was called channel_compare, but
+ that doesn't match the general naming convention that
+ methods which accept multiple optional ChannelType
+ arguments have names that end in _channel. So I renamed
+ the method to compare_channel but kept channel_compare as
+ an alias.
+*/
+VALUE Image_compare_channel(
+ int argc,
+ VALUE *argv,
+ VALUE self)
+{
+#if defined(HAVE_COMPAREIMAGECHANNELS)
+
+ Image *image, *r_image, *difference_image;
+ double distortion;
+ volatile VALUE ary;
+ MetricType metric_type;
+ ChannelType channels;
+ ExceptionInfo exception;
+
+ channels = extract_channels(&argc, argv);
+ if (argc > 2)
+ {
+ raise_ChannelType_error(argv[argc-1]);
+ }
+ if (argc != 2)
+ {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or more)", argc);
+ }
+
+ Data_Get_Struct(self, Image, image);
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, r_image);
+ VALUE_TO_ENUM(argv[1], metric_type, MetricType);
+
+ GetExceptionInfo(&exception);
+ difference_image = CompareImageChannels(image
+ , r_image
+ , channels
+ , metric_type
+ , &distortion
+ , &exception);
+ HANDLE_ERROR
+
+ ary = rb_ary_new2(2);
+ rb_ary_store(ary, 0, rm_image_new(difference_image));
+ rb_ary_store(ary, 1, rb_float_new(distortion));
+
+ return ary;
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+/*
+ Method: Image#compose -> composite_op
+ Purpose: Return the composite operator attribute
+*/
+VALUE Image_compose(VALUE self)
+{
+ Image *image;
+
+ Data_Get_Struct(self, Image, image);
+
+ return CompositeOperator_new(image->compose);
+}
+
+
+/*
Method: Image#compose=composite_op
Purpose: Set the composite operator attribute
*/
VALUE Image_compose_eq(
VALUE self,
@@ -1771,15 +1757,15 @@
ExceptionInfo exception;
long x_offset;
long y_offset;
Data_Get_Struct(self, Image, image);
- Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
switch (argc)
{
case 3: // argv[1] is gravity, argv[2] is composite_op
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
VALUE_TO_ENUM(argv[1], gravity, GravityType);
VALUE_TO_ENUM(argv[2], operator, CompositeOperator);
// convert gravity to x, y offsets
switch (gravity)
@@ -1826,16 +1812,18 @@
}
break;
case 4: // argv[1], argv[2] is x_off, y_off,
// argv[3] is composite_op
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
x_offset = NUM2LONG(argv[1]);
y_offset = NUM2LONG(argv[2]);
VALUE_TO_ENUM(argv[3], operator, CompositeOperator);
break;
case 5:
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
VALUE_TO_ENUM(argv[1], gravity, GravityType);
x_offset = NUM2LONG(argv[2]);
y_offset = NUM2LONG(argv[3]);
VALUE_TO_ENUM(argv[4], operator, CompositeOperator);
@@ -1875,10 +1863,11 @@
y_offset = 0;
}
if (bang)
{
+ rm_check_frozen(self);
(void) CompositeImage(image, operator, comp_image, x_offset, y_offset);
HANDLE_ERROR_IMG(image)
return self;
}
else
@@ -2214,10 +2203,14 @@
Data_Get_Struct(self, Image, image);
channels = extract_channels(&argc, argv);
// There are 2 required arguments.
+ if (argc > 2)
+ {
+ raise_ChannelType_error(argv[argc-1]);
+ }
if (argc != 2)
{
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or more)", argc);
}
@@ -2478,12 +2471,14 @@
nmean = rb_float_new(image->error.normalized_mean_error);
nmax = rb_float_new(image->error.normalized_maximum_error);
return rb_ary_new3(3, mean, nmean, nmax);
}
+
DEF_ATTR_READER(Image, directory, str)
+
/*
Method: Image#dispatch(x, y, columns, rows, map <, float>)
Purpose: Extracts pixel data from the image and returns it as an
array of pixels. The "x", "y", "width" and "height" parameters
specify the rectangle to be extracted. The "map" parameter
@@ -2606,14 +2601,39 @@
}
return self;
}
-DEF_ATTR_ACCESSOR(Image, dispose, ulong)
+/*
+ Method: Image#dispose
+ Purpose: Return the dispose attribute as a DisposeType enum
+*/
+VALUE
+Image_dispose(VALUE self)
+{
+ Image *image;
+ Data_Get_Struct(self, Image, image);
+ return DisposeType_new(image->dispose);
+}
/*
+ Method: Image#dispose=
+ Purpose: Set the dispose attribute
+*/
+VALUE
+Image_dispose_eq(VALUE self, VALUE dispose)
+{
+ Image *image;
+
+ rm_check_frozen(self);
+ Data_Get_Struct(self, Image, image);
+ VALUE_TO_ENUM(dispose, image->dispose, DisposeType);
+ return self;
+}
+
+/*
Method: Image#_dump(aDepth)
Purpose: implement marshalling
Returns: a string representing the dumped image
Notes: uses ImageToBlob - use the MIFF format
in the blob since it's the most general
@@ -2783,13 +2803,13 @@
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 2)", argc);
break;
}
Data_Get_Struct(self, Image, image);
- if (sigma <= 0.0)
+ if (sigma == 0.0)
{
- rb_raise(rb_eArgError, "sigma must be > 0.0");
+ rb_raise(rb_eArgError, "sigma must be != 0.0");
}
GetExceptionInfo(&exception);
new_image = (effector)(image, radius, sigma, &exception);
HANDLE_ERROR
@@ -3005,10 +3025,11 @@
}
DEF_ATTR_READER(Image, filename, str)
+
/*
Method: Image#filesize
Purpose: Return the image filesize
*/
VALUE Image_filesize(VALUE self)
@@ -3017,10 +3038,11 @@
Data_Get_Struct(self, Image, image);
return INT2FIX(GetBlobSize(image));
}
+
/*
Method: Image#filter, filter=
Purpose: Get/set filter type
*/
VALUE
@@ -3041,10 +3063,11 @@
Data_Get_Struct(self, Image, image);
VALUE_TO_ENUM(filter, image->filter, FilterTypes);
return self;
}
+
/*
Method: Image#flip
Image#flip!
Purpose: creates a vertical mirror image by reflecting the pixels around
the central x-axis
@@ -3283,10 +3306,11 @@
Data_Get_Struct(self, Image, image);
image->fuzz = rm_fuzz_to_dbl(fuzz);
return self;
}
+
DEF_ATTR_ACCESSOR(Image, gamma, dbl)
/*
* Method: Image#gamma_channel(gamma, channel=AllChannels)
@@ -3473,11 +3497,11 @@
geom_str = rb_funcall(geometry, ID_to_s, 0);
geom = STRING_PTR(geom_str);
if (!IsGeometry(geom))
{
- rb_raise(rb_eArgError, "invalid geometry: %s", geom);
+ rb_raise(rb_eTypeError, "invalid geometry: %s", geom);
}
magick_clone_string(&image->geometry, geom);
return self;
}
@@ -3663,81 +3687,149 @@
Method: Image#import_pixels
Purpose: store image pixel data from an array
Notes: See Image#export_pixels
*/
VALUE
-Image_import_pixels(
- VALUE self,
- VALUE x_arg,
- VALUE y_arg,
- VALUE cols_arg,
- VALUE rows_arg,
- VALUE map_arg,
- VALUE pixel_ary)
+Image_import_pixels(int argc, VALUE *argv, VALUE self)
{
#if defined(HAVE_IMPORTIMAGEPIXELS)
Image *image, *clone_image;
long x_off, y_off;
unsigned long cols, rows;
unsigned long npixels;
- long n;
+ long n, buffer_l;
char *map;
- volatile int *pixels;
+ volatile VALUE pixel_arg, pixel_ary;
+ StorageType stg_type = CharPixel;
+ size_t type_sz, map_l;
+ volatile int *pixels = NULL;
+ volatile void *buffer;
unsigned int okay;
ExceptionInfo exception;
rm_check_frozen(self);
- Data_Get_Struct(self, Image, image);
- map = STRING_PTR(map_arg);
- x_off = NUM2LONG(x_arg);
- y_off = NUM2LONG(y_arg);
- cols = NUM2ULONG(cols_arg);
- rows = NUM2ULONG(rows_arg);
+ switch (argc)
+ {
+ case 7:
+ VALUE_TO_ENUM(argv[6], stg_type, StorageType);
+ case 6:
+ x_off = NUM2LONG(argv[0]);
+ y_off = NUM2LONG(argv[1]);
+ cols = NUM2ULONG(argv[2]);
+ rows = NUM2ULONG(argv[3]);
+ map = STRING_PTR(argv[4]);
+ pixel_arg = argv[5];
+ break;
+ default:
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 6 or 7)", argc);
+ break;
+ }
+ Data_Get_Struct(self, Image, image);
+
if (x_off < 0 || y_off < 0 || cols <= 0 || rows <= 0)
{
rb_raise(rb_eArgError, "invalid import geometry");
}
- npixels = cols * rows * strlen(map);
+ map_l = strlen(map);
+ npixels = cols * rows * map_l;
- // rb_Array converts objects that are not Arrays to Arrays if possible,
- // and raises TypeError if it can't.
- pixel_ary = rb_Array(pixel_ary);
-
- if (RARRAY(pixel_ary)->len < npixels)
+ // Assume that any object that responds to :to_str is a string buffer containing
+ // binary pixel data.
+ if (rb_respond_to(pixel_arg, rb_intern("to_str")))
{
- rb_raise(rb_eArgError, "pixel array too small (need %lu, got %ld)"
- , npixels, RARRAY(pixel_ary)->len);
- }
+ buffer = (void *)STRING_PTR_LEN(pixel_arg, buffer_l);
+ switch (stg_type)
+ {
+ case CharPixel:
+ type_sz = 1;
+ break;
+ case ShortPixel:
+ type_sz = sizeof(unsigned short);
+ break;
+ case IntegerPixel:
+ type_sz = sizeof(unsigned int);
+ break;
+ case LongPixel:
+ type_sz = sizeof(unsigned long);
+ break;
+#if defined(HAVE_QUANTUMPIXEL)
+ case QuantumPixel:
+ type_sz = sizeof(Quantum);
+ break;
+#endif
+ default:
+ rb_raise(rb_eArgError, "unsupported storage type %s", StorageType_name(stg_type));
+ break;
+ }
- // Get array for integer pixels. Use Ruby's memory so GC will clean up after us
- // in case of an exception.
- pixels = ALLOC_N(int, npixels);
- if (!pixels) // app recovered from exception...
- {
- return self;
+ if (buffer_l % type_sz != 0)
+ {
+ rb_raise(rb_eArgError, "pixel buffer must be an exact multiple of the storage type size");
+ }
+ if ((buffer_l / type_sz) % map_l != 0)
+ {
+ rb_raise(rb_eArgError, "pixel buffer must contain an exact multiple of the map length");
+ }
+ if (buffer_l/type_sz < npixels)
+ {
+ rb_raise(rb_eArgError, "pixel buffer too small (need %lu channel values, got %ld)"
+ , npixels, buffer_l/type_sz);
+ }
}
-
- for (n = 0; n < npixels; n++)
+ // Otherwise convert the argument to an array and convert the array elements
+ // to binary pixel data.
+ else
{
- volatile VALUE p = rb_ary_entry(pixel_ary, n);
- long q = ScaleQuantumToLong(NUM2LONG(p));
- pixels[n] = (int) q;
+ // rb_Array converts an object that is not an array to an array if possible,
+ // and raises TypeError if it can't. It usually is possible.
+ pixel_ary = rb_Array(pixel_arg);
+
+ if (RARRAY(pixel_ary)->len % map_l != 0)
+ {
+ rb_raise(rb_eArgError, "pixel array must contain an exact multiple of the map length");
+ }
+ if (RARRAY(pixel_ary)->len < npixels)
+ {
+ rb_raise(rb_eArgError, "pixel array too small (need %lu elements, got %ld)"
+ , npixels, RARRAY(pixel_ary)->len);
+ }
+
+ // Get array for integer pixels. Use Ruby's memory so GC will clean up after us
+ // in case of an exception.
+ pixels = ALLOC_N(int, npixels);
+ if (!pixels) // app recovered from exception...
+ {
+ return self;
+ }
+
+ for (n = 0; n < npixels; n++)
+ {
+ volatile VALUE p = rb_ary_entry(pixel_ary, n);
+ long q = ScaleQuantumToLong(NUM2LONG(p));
+ pixels[n] = (int) q;
+ }
+ buffer = (void *) pixels;
+ stg_type = IntegerPixel;
}
+
// Import into a clone - ImportImagePixels destroys the input image if an error occurs.
GetExceptionInfo(&exception);
clone_image = CloneImage(image, 0, 0, True, &exception);
HANDLE_ERROR
- okay = ImportImagePixels(clone_image, x_off, y_off, cols, rows, map, IntegerPixel, (void *)pixels);
+ okay = ImportImagePixels(clone_image, x_off, y_off, cols, rows, map, stg_type, (const void *)buffer);
// Free pixel array before checking for errors. If an error occurred, ImportImagePixels
// destroyed the clone image, so we don't have to.
- xfree((void *)pixels);
+ if (pixels)
+ {
+ xfree((void *)pixels);
+ }
if (!okay)
{
HANDLE_ERROR_IMG(clone_image)
// Shouldn't get here...
@@ -3780,11 +3872,15 @@
x += sprintf(buffer+x, "%s=>", image->magick_filename);
}
// Print current filename.
x += sprintf(buffer+x, "%s", image->filename);
// Print scene number.
+#if defined(HAVE_GETNEXTIMAGEINLIST)
+ if ((GetPreviousImageInList(image) != NULL) && (GetNextImageInList(image) != NULL) && image->scene > 0)
+#else
if ((image->previous || image->next) && image->scene > 0)
+#endif
{
x += sprintf(buffer+x, "[%lu]", image->scene);
}
// Print format
x += sprintf(buffer+x, " %s ", image->magick);
@@ -4566,10 +4662,11 @@
Image_monitor_eq(VALUE self, VALUE monitor)
{
#if defined(HAVE_SETIMAGEPROGRESSMONITOR)
Image *image;
+ rm_check_frozen(self);
Data_Get_Struct(self, Image, image);
if (NIL_P(monitor))
{
image->progress_monitor = NULL;
@@ -4661,13 +4758,13 @@
Data_Get_Struct(self, Image, image);
radius = NUM2DBL(radius_arg);
sigma = NUM2DBL(sigma_arg);
angle = NUM2DBL(angle_arg);
- if (sigma <= 0.0)
+ if (sigma == 0.0)
{
- rb_raise(rb_eArgError, "sigma must be > 0.0");
+ rb_raise(rb_eArgError, "sigma must be != 0.0");
}
GetExceptionInfo(&exception);
new_image = MotionBlurImage(image, radius, sigma, angle, &exception);
HANDLE_ERROR
@@ -4994,18 +5091,18 @@
VALUE
Image_number_colors(VALUE self)
{
Image *image;
ExceptionInfo exception;
- unsigned int n = 0;
+ unsigned long n = 0;
Data_Get_Struct(self, Image, image);
GetExceptionInfo(&exception);
- n = GetNumberColors(image, NULL, &exception);
+ n = (unsigned long) GetNumberColors(image, NULL, &exception);
HANDLE_ERROR
- return INT2FIX(n);
+ return ULONG2NUM(n);
}
DEF_ATTR_ACCESSOR(Image, offset, long)
/*
@@ -5236,11 +5333,15 @@
GetExceptionInfo(&exception);
old_color = *AcquireImagePixels(image, x, y, 1, 1, &exception);
HANDLE_ERROR
// PseudoClass
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ if (image->storage_class == PseudoClass)
+#else
if (image->class == PseudoClass)
+#endif
{
IndexPacket *indexes = GetIndexes(image);
old_color = image->colormap[*indexes];
}
if (!image->matte)
@@ -5257,16 +5358,24 @@
return Pixel_from_PixelPacket(&image->background_color);
}
// Set the color of a pixel. Return previous color.
// Convert to DirectClass
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ if (image->storage_class == PseudoClass)
+#else
if (image->class == PseudoClass)
+#endif
{
SyncImage(image);
magick_free(image->colormap);
image->colormap = NULL;
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ image->storage_class = DirectClass;
+#else
image->class = DirectClass;
+#endif
}
pixel = GetImagePixels(image, x, y, 1, 1);
if (pixel)
{
@@ -5435,10 +5544,16 @@
HANDLE_ERROR_IMG(image)
return self;
}
+#if defined(HAVE_IMAGE_QUALITY)
+DEF_ATTR_READER(Image, quality, ulong)
+#endif
+
+
+
/*
Method: Image#quantum_depth -> 8, 16, or 32
Purpose: Return image depth to nearest quantum
Notes: IM 6.0.0 introduced GetImageQuantumDepth, IM 6.0.5
added a 2nd argument. The MagickFalse argument
@@ -5640,43 +5755,11 @@
return (VALUE)0;
#endif
}
-
/*
- Method: Image#radial_blur(angle)
- Purpose: Call RadialBlurImage
- Notes: Angle is in degrees
-*/
-VALUE
-Image_radial_blur(VALUE self, VALUE angle)
-{
-#if defined(HAVE_RADIALBLURIMAGE)
- Image *image, *new_image;
- ExceptionInfo exception;
-
- Data_Get_Struct(self, Image, image);
- GetExceptionInfo(&exception);
-
- new_image = RadialBlurImage(image, NUM2DBL(angle), &exception);
- HANDLE_ERROR
-
- return rm_image_new(new_image);
-#else
- rm_not_implemented();
- return (VALUE)0;
-#endif
-}
-
-
-#if defined(HAVE_IMAGE_QUALITY)
-DEF_ATTR_READER(Image, quality, ulong)
-#endif
-
-
-/*
Method: Image#quantize(<number_colors<, colorspace<, dither<, tree_depth<, measure_error>>>>>)
defaults: 256, Magick::RGBColorspace, true, 0, false
Purpose: call QuantizeImage
*/
VALUE
@@ -5714,11 +5797,38 @@
QuantizeImage(&quantize_info, new_image);
return rm_image_new(new_image);
}
+
/*
+ Method: Image#radial_blur(angle)
+ Purpose: Call RadialBlurImage
+ Notes: Angle is in degrees
+*/
+VALUE
+Image_radial_blur(VALUE self, VALUE angle)
+{
+#if defined(HAVE_RADIALBLURIMAGE)
+ Image *image, *new_image;
+ ExceptionInfo exception;
+
+ Data_Get_Struct(self, Image, image);
+ GetExceptionInfo(&exception);
+
+ new_image = RadialBlurImage(image, NUM2DBL(angle), &exception);
+ HANDLE_ERROR
+
+ return rm_image_new(new_image);
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+/*
Method: Image#random_channel_threshold
Purpose: changes the value of individual pixels based on the intensity of
each pixel compared to a random threshold. The result is a
low-contrast, two color image.
Args: `channel_arg' can be "all", "intensity", "opacity", "matte"
@@ -6441,11 +6551,82 @@
SetImageOpacity(image, opacity);
return self;
}
+
/*
+ Method: Image#properties [{ |k,v| block }]
+ Purpose: Traverse the attributes and yield to the block.
+ If no block, return a hash of all the attribute
+ keys & values
+ Notes: I use the word "properties" to distinguish between
+ these "user-added" attribute strings and Image
+ object attributes.
+*/
+VALUE
+Image_properties(VALUE self)
+{
+ Image *image;
+ const ImageAttribute *attr;
+ volatile VALUE attr_hash;
+
+ Data_Get_Struct(self, Image, image);
+
+ // If block, iterate over attributes
+ if (rb_block_given_p())
+ {
+ volatile VALUE ary = rb_ary_new2(2);
+
+#if defined(HAVE_GETNEXTIMAGEATTRIBUTE)
+ ResetImageAttributeIterator(image);
+ attr = GetNextImageAttribute(image);
+ while (attr)
+ {
+ rb_ary_store(ary, 0, rb_str_new2(attr->key));
+ rb_ary_store(ary, 1, rb_str_new2(attr->value));
+ rb_yield(ary);
+ attr = GetNextImageAttribute(image);
+ }
+#else
+ for (attr = image->attributes; attr; attr = Next_Attribute)
+ {
+ // Store the next ptr where Image#aset can see it.
+ // The app may decide to delete that attribute.
+ Next_Attribute = attr->next;
+ rb_ary_store(ary, 0, rb_str_new2(attr->key));
+ rb_ary_store(ary, 1, rb_str_new2(attr->value));
+ rb_yield(ary);
+ }
+#endif
+ return self;
+ }
+
+ // otherwise return properties hash
+ else
+ {
+ attr_hash = rb_hash_new();
+#if defined(HAVE_GETNEXTIMAGEATTRIBUTE)
+ ResetImageAttributeIterator(image);
+ attr = GetNextImageAttribute(image);
+ while (attr)
+ {
+ rb_hash_aset(attr_hash, rb_str_new2(attr->key), rb_str_new2(attr->value));
+ attr = GetNextImageAttribute(image);
+ }
+#else
+ for (attr = image->attributes; attr; attr = attr->next)
+ {
+ rb_hash_aset(attr_hash, rb_str_new2(attr->key), rb_str_new2(attr->value));
+ }
+#endif
+ return attr_hash;
+ }
+}
+
+
+/*
Method: Image#shade(shading=false, azimuth=30, elevation=30)
Purpose: shines a distant light on an image to create a three-dimensional
effect. You control the positioning of the light with azimuth
and elevation; azimuth is measured in degrees off the x axis
and elevation is measured in pixels above the Z axis
@@ -6544,37 +6725,10 @@
return (VALUE)0;
#endif
}
/*
- Method: Image#shave(width, height)
- Image#shave!(width, height)
- Purpose: shaves pixels from the image edges, leaving a rectangle
- of the specified width & height in the center
- Returns: shave: a new image
- shave!: self, shaved
-*/
-VALUE
-Image_shave(
- VALUE self,
- VALUE width,
- VALUE height)
-{
- return xform_image(False, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
-}
-
-VALUE
-Image_shave_bang(
- VALUE self,
- VALUE width,
- VALUE height)
-{
- rm_check_frozen(self);
- return xform_image(True, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
-}
-
-/*
Method: Image#sharpen(radius=0, sigma=1)
Purpose: sharpens an image
Returns: a new image
*/
VALUE
@@ -6628,11 +6782,41 @@
rm_not_implemented();
return (VALUE)0;
#endif
}
+
/*
+ Method: Image#shave(width, height)
+ Image#shave!(width, height)
+ Purpose: shaves pixels from the image edges, leaving a rectangle
+ of the specified width & height in the center
+ Returns: shave: a new image
+ shave!: self, shaved
+*/
+VALUE
+Image_shave(
+ VALUE self,
+ VALUE width,
+ VALUE height)
+{
+ return xform_image(False, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
+}
+
+
+VALUE
+Image_shave_bang(
+ VALUE self,
+ VALUE width,
+ VALUE height)
+{
+ rm_check_frozen(self);
+ return xform_image(True, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
+}
+
+
+/*
Method: Image#shear(x_shear, y_shear)
Purpose: Calls ShearImage
Notes: shear angles are measured in degrees
Returns: a new image
*/
@@ -6797,10 +6981,11 @@
{
rb_raise(Class_ImageMagickError, "can't get image signature");
}
res = memcmp(sigA->value, sigB->value, 64);
+ res = res > 0 ? 1 : (res < 0 ? -1 : 0); // reduce to 1, -1, 0
return INT2FIX(res);
}
/*
@@ -7000,11 +7185,15 @@
Image_class_type(VALUE self)
{
Image *image;
Data_Get_Struct(self, Image, image);
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ return ClassType_new(image->storage_class);
+#else
return ClassType_new(image->class);
+#endif
}
/*
Method: Image#class_type=
Purpose: change the image's storage class
@@ -7019,24 +7208,36 @@
rm_check_frozen(self);
Data_Get_Struct(self, Image, image);
VALUE_TO_ENUM(new_class_type, class_type, ClassType);
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ if (image->storage_class == PseudoClass && class_type == DirectClass)
+#else
if (image->class == PseudoClass && class_type == DirectClass)
+#endif
{
SyncImage(image);
magick_free(image->colormap);
image->colormap = NULL;
}
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ else if (image->storage_class == DirectClass && class_type == PseudoClass)
+#else
else if (image->class == DirectClass && class_type == PseudoClass)
+#endif
{
GetQuantizeInfo(&qinfo);
qinfo.number_colors = MaxRGB+1;
QuantizeImage(&qinfo, image);
}
+#if defined(HAVE_IMAGE_STORAGE_CLASS)
+ image->storage_class = class_type;
+#else
image->class = class_type;
+#endif
return self;
}
/*
Method: Image#store_pixels
@@ -7072,18 +7273,20 @@
{
rb_raise(rb_eRangeError, "geometry (%lux%lu%+ld%+ld) exceeds image bounds"
, cols, rows, x, y);
}
+ size = cols * rows;
+ rm_check_ary_len(new_pixels, size);
+
SetImageType(image, TrueColorType);
// Get a pointer to the pixels. Replace the values with the PixelPackets
// from the pixels argument.
pixels = GetImagePixels(image, x, y, cols, rows);
if (pixels)
{
- size = cols * rows;
for (n = 0; n < size; n++)
{
new_pixel = rb_ary_entry(new_pixels, n);
Data_Get_Struct(new_pixel, Pixel, pixel);
pixels[n] = *pixel;
@@ -7568,11 +7771,21 @@
HANDLE_ERROR
return rb_str_new2(name);
}
-DEF_ATTR_READER(Image, total_colors, ulong)
+/*
+ Method: Image#total_colors
+ Purpose: alias for Image#number_colors
+ Notes: This used to be a direct reference to the `total_colors' field in Image
+ but that field is not reliable.
+*/
+VALUE
+Image_total_colors(VALUE self)
+{
+ return Image_number_colors(self);
+}
/*
Method: Image#transparent(color-name<, opacity>)
Image#transparent(pixel<, opacity>)
Purpose: Call TransparentImage
@@ -7672,10 +7885,12 @@
/*
Method: Image#image_type=(type)
Purpose: Call SetImageType to set the type of the image
Note: Can't use type & type= b/c of Object#type.
+ This setter is useless. Leave for backward compatibility
+ but don't document it.
*/
VALUE Image_image_type_eq(VALUE self, VALUE type)
{
Image *image;
ImageType it;
@@ -7964,22 +8179,43 @@
gravity, width, height
or
gravity, x, y, width, height
If the 2nd or 3rd, compute new x, y values.
+ The argument list can have a trailing true, false, or nil argument.
+ If present and true, after cropping reset the page fields in the image.
+
Call xform_image to do the cropping.
*/
static VALUE
cropper(int bang, int argc, VALUE *argv, VALUE self)
{
volatile VALUE x, y, width, height;
unsigned long nx = 0, ny = 0;
unsigned long columns, rows;
+ int reset_page = 0;
GravityType gravity;
MagickEnum *magick_enum;
Image *image;
+ VALUE cropped;
+ // Check for a "reset page" trailing argument.
+ if (argc >= 1)
+ {
+ switch (TYPE(argv[argc-1]))
+ {
+ case T_TRUE:
+ reset_page = 1;
+ // fall thru
+ case T_FALSE:
+ case T_NIL:
+ argc -= 1;
+ default:
+ break;
+ }
+ }
+
switch (argc)
{
case 5:
Data_Get_Struct(self, Image, image);
@@ -8081,15 +8317,29 @@
x = ULONG2NUM(nx);
y = ULONG2NUM(ny);
break;
default:
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 3, 4, or 5)", argc);
+ if (reset_page)
+ {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 4, 5, or 6)", argc);
+ }
+ else
+ {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 3, 4, or 5)", argc);
+ }
break;
}
- return xform_image(bang, self, x, y, width, height, CropImage);
+ cropped = xform_image(bang, self, x, y, width, height, CropImage);
+ if (reset_page)
+ {
+ Data_Get_Struct(cropped, Image, image);
+ image->page.x = image->page.y = 0L;
+ image->page.width = image->page.height = 0UL;
+ }
+ return cropped;
}
/*
@@ -8181,14 +8431,14 @@
}
/*
Static: raise_ChannelType_error
- Purpose: raise ArgumentError when an non-ChannelType object
+ Purpose: raise TypeError when an non-ChannelType object
is unexpectedly encountered
*/
static void
raise_ChannelType_error(VALUE arg)
{
- rb_raise(rb_eArgError, "argument needs to be a ChannelType (%s given)"
+ rb_raise(rb_eTypeError, "argument needs to be a ChannelType (%s given)"
, rb_class2name(CLASS_OF(arg)));
}