ext/RMagick/rmimage.c in rmagick-1.12.0 vs ext/RMagick/rmimage.c in rmagick-1.13.0
- old
+ new
@@ -1,6 +1,6 @@
-/* $Id: rmimage.c,v 1.147 2006/06/02 23:26:53 rmagick Exp $ */
+/* $Id: rmimage.c,v 1.153 2006/06/28 23:07:16 rmagick Exp $ */
/*============================================================================\
| Copyright (C) 2006 by Timothy P. Hunter
| Name: rmimage.c
| Author: Tim Hunter
| Purpose: Image class method definitions for RMagick
@@ -22,23 +22,126 @@
typedef Image *(reader_t)(const Info *, ExceptionInfo *);
typedef Image *(scaler_t)(const Image *, const unsigned long, const unsigned long, ExceptionInfo *);
typedef unsigned int (thresholder_t)(Image *, const char *);
typedef Image *(xformer_t)(const Image *, const RectangleInfo *, ExceptionInfo *);
+static VALUE cropper(int, int, VALUE *, VALUE);
static VALUE effect_image(VALUE, int, VALUE *, effector_t *);
+static VALUE flipflop(int, VALUE, flipper_t);
static VALUE rd_image(VALUE, VALUE, reader_t);
+static VALUE rotate(int, VALUE, VALUE);
static VALUE scale(int, int, VALUE *, VALUE, scaler_t *);
-static VALUE cropper(int, int, VALUE *, VALUE);
-static VALUE xform_image(int, VALUE, VALUE, VALUE, VALUE, VALUE, xformer_t);
static VALUE threshold_image(int, VALUE *, VALUE, thresholder_t);
+static VALUE xform_image(int, VALUE, VALUE, VALUE, VALUE, VALUE, xformer_t);
static VALUE array_from_images(Image *);
static ChannelType extract_channels(int *, VALUE *);
static void raise_ChannelType_error(VALUE);
static ImageAttribute *Next_Attribute;
+
+
+
/*
+ Method: Image#adaptive_sharpen(radius=0.0, sigma=1.0)
+ Purpose: call AdaptiveSharpenImage
+*/
+VALUE
+Image_adaptive_sharpen(int argc, VALUE *argv, VALUE self)
+{
+#if defined(HAVE_ADAPTIVESHARPENIMAGE)
+ Image *image, *new_image;
+ double radius = 0.0;
+ double sigma = 1.0;
+ ExceptionInfo exception;
+
+ switch (argc)
+ {
+ case 2:
+ sigma = NUM2DBL(argv[1]);
+ case 1:
+ radius = NUM2DBL(argv[0]);
+ case 0:
+ break;
+ default:
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 to 2)", argc);
+ break;
+ }
+
+ Data_Get_Struct(self, Image, image);
+
+ GetExceptionInfo(&exception);
+
+ new_image = AdaptiveSharpenImage(image, radius, sigma, &exception);
+ rm_check_exception(&exception, new_image, DestroyOnError);
+
+ DestroyExceptionInfo(&exception);
+
+ rm_ensure_result(new_image);
+
+ return rm_image_new(new_image);
+
+#else
+
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+/*
+ Method: Image#adaptive_sharpen_channel(radius=0.0, sigma=1.0[, channel...])
+ Purpose: Call AdaptiveSharpenImageChannel
+*/
+VALUE
+Image_adaptive_sharpen_channel(int argc, VALUE *argv, VALUE self)
+{
+#if defined(HAVE_ADAPTIVESHARPENIMAGE)
+
+ Image *image, *new_image;
+ double radius = 0.0;
+ double sigma = 1.0;
+ ExceptionInfo exception;
+ ChannelType channels;
+
+ channels = extract_channels(&argc, argv);
+
+ switch (argc)
+ {
+ case 2:
+ sigma = NUM2DBL(argv[1]);
+ case 1:
+ radius = NUM2DBL(argv[0]);
+ case 0:
+ break;
+ default:
+ raise_ChannelType_error(argv[argc-1]);
+ break;
+ }
+
+ Data_Get_Struct(self, Image, image);
+
+ GetExceptionInfo(&exception);
+
+ new_image = AdaptiveSharpenImageChannel(image, channels, radius, sigma, &exception);
+ rm_check_exception(&exception, new_image, DestroyOnError);
+
+ DestroyExceptionInfo(&exception);
+
+ rm_ensure_result(new_image);
+
+ return rm_image_new(new_image);
+#else
+
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+
+/*
Method: Image#adaptive_threshold(width=3, height=3, offset=0)
Purpose: selects an individual threshold for each pixel based on
the range of intensity values in its local neighborhood.
This allows for thresholding of an image whose global
intensity histogram doesn't contain distinctive peaks.
@@ -296,10 +399,128 @@
return self;
}
/*
+ Static: crisscross
+ Purpose: Handle #transverse, #transform methods
+*/
+#if defined(HAVE_TRANSPOSEIMAGE) || defined(HAVE_TRANSVERSEIMAGE)
+static VALUE
+crisscross(
+ int bang,
+ VALUE self,
+ Image *(fp)(const Image *, ExceptionInfo *))
+{
+ Image *image, *new_image;
+ ExceptionInfo exception;
+
+
+ Data_Get_Struct(self, Image, image);
+ GetExceptionInfo(&exception);
+
+ new_image = (fp)(image, &exception);
+ rm_check_exception(&exception, new_image, DestroyOnError);
+
+ DestroyExceptionInfo(&exception);
+
+ rm_ensure_result(new_image);
+
+ if (bang)
+ {
+ DATA_PTR(self) = new_image;
+ DestroyImage(image);
+ return self;
+ }
+
+ return rm_image_new(new_image);
+
+}
+#endif
+
+
+/*
+ Method: Image#auto_orient
+ Purpose: Implement mogrify's -auto_orient option
+ automatically orient image based on EXIF orientation value
+ Notes: See mogrify.c in ImageMagick 6.2.8.
+*/
+static VALUE auto_orient(int bang, VALUE self)
+{
+#if defined(HAVE_TRANSPOSEIMAGE) || defined(HAVE_TRANSVERSEIMAGE)
+ Image *image;
+ volatile VALUE new_image;
+
+ Data_Get_Struct(self, Image, image);
+
+ switch (image->orientation)
+ {
+ case TopRightOrientation:
+ new_image = flipflop(bang, self, FlopImage);
+ break;
+
+ case BottomRightOrientation:
+ new_image = rotate(bang, self, rb_float_new(180.0));
+ break;
+
+ case BottomLeftOrientation:
+ new_image = flipflop(bang, self, FlipImage);
+ break;
+
+ case LeftTopOrientation:
+ new_image = crisscross(bang, self, TransposeImage);
+ break;
+
+ case RightTopOrientation:
+ new_image = rotate(bang, self, rb_float_new(90.0));
+ break;
+
+ case RightBottomOrientation:
+ new_image = crisscross(bang, self, TransverseImage);
+ break;
+
+ case LeftBottomOrientation:
+ new_image = rotate(bang, self, rb_float_new(270.0));
+ break;
+
+ default: // Return IMMEDIATELY
+ return bang ? Qnil : Image_copy(self);
+ break;
+ }
+
+
+ Data_Get_Struct(new_image, Image, image);
+ image->orientation = TopLeftOrientation;
+
+ return new_image;
+
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+VALUE
+Image_auto_orient(VALUE self)
+{
+ return auto_orient(False, self);
+}
+
+/*
+ Returns nil if the image is already properly oriented
+*/
+VALUE
+Image_auto_orient_bang(VALUE self)
+{
+ rm_check_frozen(self);
+ return auto_orient(True, self);
+}
+
+
+
+/*
Method: Image#background_color
Purpose: Return the name of the background color as a String.
*/
VALUE
Image_background_color(VALUE self)
@@ -1829,11 +2050,12 @@
static VALUE composite(
int bang,
int argc,
VALUE *argv,
- VALUE self)
+ VALUE self,
+ ChannelType channels)
{
Image *image, *new_image;
Image *comp_image;
CompositeOperator operator;
GravityType gravity;
@@ -1937,19 +2159,27 @@
}
if (bang)
{
rm_check_frozen(self);
+#if defined(HAVE_COMPOSITEIMAGECHANNEL)
+ (void) CompositeImageChannel(image, channels, operator, comp_image, x_offset, y_offset);
+#else
(void) CompositeImage(image, operator, comp_image, x_offset, y_offset);
+#endif
rm_check_image_exception(image, RetainOnError);
return self;
}
else
{
new_image = rm_clone_image(image);
+#if defined(HAVE_COMPOSITEIMAGECHANNEL)
+ (void) CompositeImageChannel(new_image, channels, operator, comp_image, x_offset, y_offset);
+#else
(void) CompositeImage(new_image, operator, comp_image, x_offset, y_offset);
+#endif
rm_check_image_exception(new_image, DestroyOnError);
return rm_image_new(new_image);
}
}
@@ -1958,19 +2188,29 @@
VALUE Image_composite_bang(
int argc,
VALUE *argv,
VALUE self)
{
- return composite(True, argc, argv, self);
+#if defined(HAVE_ALLCHANNELS)
+ ChannelType channels = (AllChannels &~ OpacityChannel);
+#else
+ ChannelType channels = (0xff &~ OpacityChannel);
+#endif
+ return composite(True, argc, argv, self, channels);
}
VALUE Image_composite(
int argc,
VALUE *argv,
VALUE self)
{
- return composite(False, argc, argv, self);
+#if defined(HAVE_ALLCHANNELS)
+ ChannelType channels = (AllChannels &~ OpacityChannel);
+#else
+ ChannelType channels = (0xff &~ OpacityChannel);
+#endif
+ return composite(False, argc, argv, self, channels);
}
/*
Method: Image#composite_affine(composite, affine_matrix)
@@ -1996,11 +2236,55 @@
rm_check_image_exception(new_image, DestroyOnError);
return rm_image_new(new_image);
}
+
/*
+ Method: Image#composite_channel(src_image, geometry, composite_operator[, channel...])
+ Image#composite_channel!(src_image, geometry, composite_operator[, channel...])
+ Purpose: Call CompositeImageChannel
+*/
+static VALUE
+composite_channel(int bang, int argc, VALUE *argv, VALUE self)
+{
+#if defined(HAVE_COMPOSITEIMAGECHANNEL)
+ ChannelType channels;
+
+ channels = extract_channels(&argc, argv);
+
+ // There must be 3, 4, or 5 remaining arguments.
+ if (argc < 3)
+ {
+ rb_raise(rb_eArgError, "composite operator not specified");
+ }
+ else if (argc > 5)
+ {
+ raise_ChannelType_error(argv[argc-1]);
+ }
+
+ return composite(bang, argc, argv, self, channels);
+
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+VALUE Image_composite_channel(int argc, VALUE *argv, VALUE self)
+{
+ return composite_channel(False, argc, argv, self);
+}
+
+
+VALUE Image_composite_channel_bang(int argc, VALUE *argv, VALUE self)
+{
+ return composite_channel(True, argc, argv, self);
+}
+
+
+/*
Method: Image#compression
Image#compression=
Purpose: Get/set the compresion attribute
*/
VALUE
@@ -2154,17 +2438,13 @@
image = AllocateImage(NULL);
if (!image)
{
rb_raise(rb_eNoMemError, "not enough memory to continue.");
}
- image->columns = width;
- image->rows = height;
-#if defined(HAVE_SETIMAGEBACKGROUNDCOLOR)
+ SetImageExtent(image, width, height);
SetImageBackgroundColor(image);
-#else
- SetImage(image, OpaqueOpacity);
-#endif
+
(void) ImportImagePixels(image, 0, 0, width, height, map, stg_type, (void *)pixels.v);
rm_check_image_exception(image, DestroyOnError);
#else
image = ConstituteImage(width, height, map, stg_type, (void *)pixels.v, &exception);
rm_check_exception(&exception, image, DestroyOnError);
@@ -2419,10 +2699,11 @@
DATA_PTR(copy) = rm_clone_image(image);
return copy;
}
+
/*
Method: Image#crop(x, y, width, height)
Image#crop(gravity, width, height)
Image#crop!(x, y, width, height)
Image#crop!(gravity, width, height)
@@ -2654,11 +2935,11 @@
unsigned long n, npixels;
volatile VALUE pixels_ary;
StorageType stg_type = FIX_STG_TYPE;
char *map;
long mapL;
- boolean okay;
+ MagickBooleanType okay;
ExceptionInfo exception;
union
{
volatile Quantum *i;
volatile double *f;
@@ -3126,15 +3407,11 @@
Image *image;
rm_check_frozen(self);
Data_Get_Struct(self, Image, image);
-#if defined(HAVE_SETIMAGEBACKGROUNDCOLOR)
SetImageBackgroundColor(image);
-#else
- SetImage(image, OpaqueOpacity);
-#endif
rm_check_image_exception(image, RetainOnError);
return self;
}
@@ -5285,35 +5562,31 @@
VALUE
Image_initialize(VALUE self, VALUE info_obj, VALUE width, VALUE height, VALUE fill)
{
Image *image;
Info *info;
- int cols, rows;
+ unsigned long cols, rows;
- cols = NUM2INT(width);
- rows = NUM2INT(height);
+ cols = NUM2ULONG(width);
+ rows = NUM2ULONG(height);
if (cols <= 0 || rows <= 0)
{
rb_raise(rb_eArgError, "invalid image size (%dx%d)", cols, rows);
}
Data_Get_Struct(info_obj, Info, info);
Data_Get_Struct(self, Image, image);
- image->columns = cols;
- image->rows = rows;
+ SetImageExtent(image, cols, rows);
+
// If the caller did not supply a fill argument, call SetImage to fill the
// image using the background color. The background color can be set by
// specifying it when creating the Info parm block.
if (!fill)
{
-#if defined(HAVE_SETIMAGEBACKGROUNDCOLOR)
SetImageBackgroundColor(image);
-#else
- SetImage(image, OpaqueOpacity);
-#endif
}
// fillobj.fill(self)
else
{
(void) rb_funcall(fill, ID_fill, 1, self);
@@ -5350,19 +5623,19 @@
{
volatile VALUE fill = 0;
Info *info;
volatile VALUE info_obj;
Image *image;
- int cols, rows;
+ unsigned long cols, rows;
switch (argc)
{
case 3:
fill = argv[2];
case 2:
- rows = NUM2INT(argv[1]);
- cols = NUM2INT(argv[0]);
+ rows = NUM2ULONG(argv[1]);
+ cols = NUM2ULONG(argv[0]);
break;
default:
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
break;
}
@@ -5378,19 +5651,18 @@
}
// NOW store a real image in the image object.
DATA_PTR(self) = image;
- image->columns = cols;
- image->rows = rows;
+ SetImageExtent(image, cols, rows);
// If the caller did not supply a fill argument, call SetImage to fill the
// image using the background color. The background color can be set by
// specifying it when creating the Info parm block.
if (!fill)
{
- SetImage(image, OpaqueOpacity);
+ SetImageBackgroundColor(image);
}
// fillobj.fill(self)
else
{
(void) rb_funcall(fill, ID_fill, 1, self);
@@ -5637,10 +5909,11 @@
DestroyExceptionInfo(&exception);
return rm_image_new(new_image);
}
+
/*
Method: Image#orientation
Purpose: Return the orientation attribute as an OrientationType enum value.
*/
VALUE
@@ -5655,11 +5928,34 @@
rm_not_implemented();
return (VALUE)0;
#endif
}
+
/*
+ Method: Image#orientation=
+ Purpose: Set the orientation attribute
+*/
+VALUE
+Image_orientation_eq(VALUE self, VALUE orientation)
+{
+#if defined(HAVE_IMAGE_ORIENTATION)
+ Image *image;
+
+ rm_check_frozen(self);
+ Data_Get_Struct(self, Image, image);
+ VALUE_TO_ENUM(orientation, image->orientation, OrientationType);
+ return self;
+
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+/*
Method: Image#page
Purpose: the page attribute getter
*/
VALUE
Image_page(VALUE self)
@@ -6246,11 +6542,56 @@
rm_not_implemented();
return (VALUE)0;
#endif
}
+/*
+ Method: Image#radial_blur_channel(angle[, channel..])
+ Purpose: Call RadialBlurImageChannel
+ Notes: Angle is in degrees
+*/
+VALUE
+Image_radial_blur_channel(
+ int argc,
+ VALUE *argv,
+ VALUE self)
+{
+#if defined(HAVE_RADIALBLURIMAGECHANNEL)
+ Image *image, *new_image;
+ ExceptionInfo exception;
+ ChannelType channels;
+ channels = extract_channels(&argc, argv);
+
+ // There must be 1 remaining argument.
+ if (argc == 0)
+ {
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1 or more)");
+ }
+ else if (argc > 1)
+ {
+ raise_ChannelType_error(argv[argc-1]);
+ }
+
+ Data_Get_Struct(self, Image, image);
+ GetExceptionInfo(&exception);
+
+ new_image = RadialBlurImageChannel(image, channels, NUM2DBL(argv[0]), &exception);
+
+ rm_check_exception(&exception, new_image, DestroyOnError);
+ DestroyExceptionInfo(&exception);
+ rm_ensure_result(new_image);
+
+ 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.
@@ -6449,22 +6790,22 @@
OpenFile *fptr;
// Ensure file is open - raise error if not
GetOpenFile(file, fptr);
rb_io_check_readable(fptr);
- info->file = GetReadFile(fptr);
+ SetImageInfoFile(info, GetReadFile(fptr));
}
else
{
// Convert arg to string. If an exception occurs raise an error condition.
file = rb_rescue(rb_String, file, file_arg_rescue, file);
filename = STRING_PTR_LEN(file, filename_l);
filename_l = min(filename_l, MaxTextExtent-1);
memcpy(info->filename, filename, (size_t)filename_l);
info->filename[filename_l] = '\0';
- info->file = NULL; // Reset FILE *, if any
+ SetImageInfoFile(info, NULL);
}
GetExceptionInfo(&exception);
images = (reader)(info, &exception);
@@ -7702,11 +8043,15 @@
GetQuantizeInfo(&qinfo);
qinfo.number_colors = MaxRGB+1;
QuantizeImage(&qinfo, image);
}
+#if defined(HAVE_SETIMAGESTORAGECLASS)
+ SetImageStorageClass(image, class_type);
+#else
image->storage_class = class_type;
+#endif
return self;
}
/*
Method: Image#store_pixels
@@ -8365,14 +8710,73 @@
return rm_image_new(new_image);
}
/*
+ * Method: Image#transpose
+ * Image#transpose!
+ * Purpose: Call TransposeImage
+ */
+VALUE
+Image_transpose(VALUE self)
+{
+#if defined(HAVE_TRANSPOSEIMAGE)
+ return crisscross(False, self, TransposeImage);
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+VALUE
+Image_transpose_bang(VALUE self)
+{
+#if defined(HAVE_TRANSPOSEIMAGE)
+ rm_check_frozen(self);
+ return crisscross(True, self, TransposeImage);
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+/*
+ * Method: Image#transverse
+ * Image#transverse!
+ * Purpose: Call TransverseImage
+ */
+VALUE
+Image_transverse(VALUE self)
+{
+#if defined(HAVE_TRANSVERSEIMAGE)
+ return crisscross(False, self, TransverseImage);
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+VALUE
+Image_transverse_bang(VALUE self)
+{
+#if defined(HAVE_TRANSVERSEIMAGE)
+ rm_check_frozen(self);
+ return crisscross(True, self, TransverseImage);
+#else
+ rm_not_implemented();
+ return (VALUE)0;
+#endif
+}
+
+
+/*
* Method: Image#trim, Image#trim!
* Purpose: convenience front-end to CropImage
* Notes: respects fuzz attribute
-*/
+ */
static VALUE
trimmer(int bang, VALUE self)
{
Image *image, *new_image;
@@ -8776,11 +9180,11 @@
OpenFile *fptr;
// Ensure file is open - raise error if not
GetOpenFile(file, fptr);
rb_io_check_writable(fptr);
- info->file = GetWriteFile(fptr);
+ SetImageInfoFile(info, GetWriteFile(fptr));
}
else
{
// Copy the filename to the Info and to the Image, then call
// SetImageInfo. (Ref: ImageMagick's utilities/convert.c.)
@@ -8802,10 +9206,10 @@
if (*info->magick == '\0')
{
return Qnil;
}
- info->file = NULL;
+ SetImageInfoFile(info, NULL);
}
info->adjoin = False;
(void) WriteImage(info, image);
rm_check_image_exception(image, RetainOnError);