rmagick 1.13.0 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rmagick might be problematic. Click here for more details.
- data/ChangeLog +34 -0
- data/README.html +12 -29
- data/README.txt +10 -26
- data/configure +768 -73
- data/configure.ac +29 -26
- data/doc/comtasks.html +3 -4
- data/doc/constants.html +85 -67
- data/doc/draw.html +22 -0
- data/doc/ex/dissolve.rb +13 -0
- data/doc/ex/edge.rb +1 -1
- data/doc/ex/mask.rb +37 -0
- data/doc/ex/sketch.rb +25 -0
- data/doc/ex/watermark.rb +23 -0
- data/doc/ilist.html +11 -13
- data/doc/image1.html +601 -52
- data/doc/image2.html +637 -28
- data/doc/image3.html +339 -54
- data/doc/imageattrs.html +211 -41
- data/doc/imusage.html +41 -2
- data/doc/index.html +8 -6
- data/doc/info.html +57 -42
- data/doc/optequiv.html +1919 -0
- data/doc/rvg.html +45 -42
- data/doc/scripts/doc.js +14 -1
- data/doc/scripts/stripeTables.js +23 -0
- data/doc/usage.html +66 -15
- data/{doc/ex → examples}/demo.rb +0 -0
- data/examples/find_similar_region.rb +34 -0
- data/examples/import_export.rb +1 -1
- data/examples/pattern_fill.rb +2 -2
- data/examples/rotating_text.rb +2 -4
- data/examples/thumbnail.rb +1 -1
- data/ext/RMagick/MANIFEST +9 -4
- data/ext/RMagick/extconf.rb.in +1 -1
- data/ext/RMagick/rmagick.h +47 -10
- data/ext/RMagick/rmagick_config.h.in +24 -0
- data/ext/RMagick/rmdraw.c +32 -7
- data/ext/RMagick/rmilist.c +55 -37
- data/ext/RMagick/rmimage.c +1588 -447
- data/ext/RMagick/rminfo.c +94 -3
- data/ext/RMagick/rmmain.c +68 -7
- data/ext/RMagick/rmutil.c +67 -9
- data/lib/RMagick.rb +190 -53
- data/lib/rvg/stretchable.rb +17 -13
- data/rmagick.gemspec +1 -1
- metadata +11 -6
- data/doc/ex/level_channel.rb +0 -33
- data/doc/ex/opaque.rb +0 -14
data/ext/RMagick/rmimage.c
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
/* $Id: rmimage.c,v 1.
|
1
|
+
/* $Id: rmimage.c,v 1.183 2006/09/27 21:26:35 rmagick Exp $ */
|
2
2
|
/*============================================================================\
|
3
3
|
| Copyright (C) 2006 by Timothy P. Hunter
|
4
4
|
| Name: rmimage.c
|
@@ -28,27 +28,30 @@ static VALUE cropper(int, int, VALUE *, VALUE);
|
|
28
28
|
static VALUE effect_image(VALUE, int, VALUE *, effector_t *);
|
29
29
|
static VALUE flipflop(int, VALUE, flipper_t);
|
30
30
|
static VALUE rd_image(VALUE, VALUE, reader_t);
|
31
|
-
static VALUE rotate(int,
|
31
|
+
static VALUE rotate(int, int, VALUE *, VALUE);
|
32
32
|
static VALUE scale(int, int, VALUE *, VALUE, scaler_t *);
|
33
33
|
static VALUE threshold_image(int, VALUE *, VALUE, thresholder_t);
|
34
34
|
static VALUE xform_image(int, VALUE, VALUE, VALUE, VALUE, VALUE, xformer_t);
|
35
35
|
static VALUE array_from_images(Image *);
|
36
|
-
static ChannelType extract_channels(int *, VALUE *);
|
37
|
-
static void raise_ChannelType_error(VALUE);
|
38
36
|
|
39
37
|
static ImageAttribute *Next_Attribute;
|
40
38
|
|
39
|
+
static const char *BlackPointCompensationKey = "PROFILE:black-point-compensation";
|
40
|
+
|
41
41
|
|
42
42
|
|
43
43
|
|
44
44
|
/*
|
45
|
-
|
46
|
-
Purpose: call
|
45
|
+
Static: adaptive_method
|
46
|
+
Purpose: call Adaptive(Blur|Sharpen)Image
|
47
47
|
*/
|
48
|
-
|
49
|
-
|
48
|
+
#if defined(HAVE_ADAPTIVEBLURIMAGECHANNEL) || defined(HAVE_ADAPTIVESHARPENIMAGE)
|
49
|
+
static VALUE adaptive_method(
|
50
|
+
int argc,
|
51
|
+
VALUE *argv,
|
52
|
+
VALUE self,
|
53
|
+
Image *fp(const Image *, const double, const double, ExceptionInfo *))
|
50
54
|
{
|
51
|
-
#if defined(HAVE_ADAPTIVESHARPENIMAGE)
|
52
55
|
Image *image, *new_image;
|
53
56
|
double radius = 0.0;
|
54
57
|
double sigma = 1.0;
|
@@ -71,7 +74,7 @@ Image_adaptive_sharpen(int argc, VALUE *argv, VALUE self)
|
|
71
74
|
|
72
75
|
GetExceptionInfo(&exception);
|
73
76
|
|
74
|
-
new_image =
|
77
|
+
new_image = (fp)(image, radius, sigma, &exception);
|
75
78
|
rm_check_exception(&exception, new_image, DestroyOnError);
|
76
79
|
|
77
80
|
DestroyExceptionInfo(&exception);
|
@@ -79,24 +82,20 @@ Image_adaptive_sharpen(int argc, VALUE *argv, VALUE self)
|
|
79
82
|
rm_ensure_result(new_image);
|
80
83
|
|
81
84
|
return rm_image_new(new_image);
|
82
|
-
|
83
|
-
#else
|
84
|
-
|
85
|
-
rm_not_implemented();
|
86
|
-
return (VALUE)0;
|
87
|
-
#endif
|
88
85
|
}
|
89
86
|
|
90
87
|
|
88
|
+
|
91
89
|
/*
|
92
|
-
|
93
|
-
Purpose:
|
90
|
+
Static: adaptive_channel_method
|
91
|
+
Purpose: call Adaptive(Blur|Sharpen)ImageChannel
|
94
92
|
*/
|
95
|
-
VALUE
|
96
|
-
|
93
|
+
static VALUE adaptive_channel_method(
|
94
|
+
int argc,
|
95
|
+
VALUE *argv,
|
96
|
+
VALUE self,
|
97
|
+
Image *fp(const Image *, const ChannelType, const double, const double, ExceptionInfo *))
|
97
98
|
{
|
98
|
-
#if defined(HAVE_ADAPTIVESHARPENIMAGE)
|
99
|
-
|
100
99
|
Image *image, *new_image;
|
101
100
|
double radius = 0.0;
|
102
101
|
double sigma = 1.0;
|
@@ -122,7 +121,7 @@ Image_adaptive_sharpen_channel(int argc, VALUE *argv, VALUE self)
|
|
122
121
|
|
123
122
|
GetExceptionInfo(&exception);
|
124
123
|
|
125
|
-
new_image =
|
124
|
+
new_image = (fp)(image, channels, radius, sigma, &exception);
|
126
125
|
rm_check_exception(&exception, new_image, DestroyOnError);
|
127
126
|
|
128
127
|
DestroyExceptionInfo(&exception);
|
@@ -130,6 +129,130 @@ Image_adaptive_sharpen_channel(int argc, VALUE *argv, VALUE self)
|
|
130
129
|
rm_ensure_result(new_image);
|
131
130
|
|
132
131
|
return rm_image_new(new_image);
|
132
|
+
}
|
133
|
+
#endif
|
134
|
+
|
135
|
+
|
136
|
+
/*
|
137
|
+
Method: Image#adaptive_blur(radius=0.0, sigma=1.0)
|
138
|
+
Purpose: call AdaptiveBlurImage
|
139
|
+
*/
|
140
|
+
VALUE
|
141
|
+
Image_adaptive_blur(int argc, VALUE *argv, VALUE self)
|
142
|
+
{
|
143
|
+
#if defined(HAVE_ADAPTIVEBLURIMAGECHANNEL)
|
144
|
+
return adaptive_method(argc, argv, self, AdaptiveBlurImage);
|
145
|
+
#else
|
146
|
+
rm_not_implemented();
|
147
|
+
return (VALUE)0;
|
148
|
+
#endif
|
149
|
+
}
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
/*
|
154
|
+
Method: Image#adaptive_blur_channel(radius=0.0, sigma=1.0[ , channel...])
|
155
|
+
Purpose: call AdaptiveBlurImageChannel
|
156
|
+
*/
|
157
|
+
VALUE
|
158
|
+
Image_adaptive_blur_channel(int argc, VALUE *argv, VALUE self)
|
159
|
+
{
|
160
|
+
#if defined(HAVE_ADAPTIVEBLURIMAGECHANNEL)
|
161
|
+
return adaptive_channel_method(argc, argv, self, AdaptiveBlurImageChannel);
|
162
|
+
#else
|
163
|
+
rm_not_implemented();
|
164
|
+
return (VALUE)0;
|
165
|
+
#endif
|
166
|
+
}
|
167
|
+
|
168
|
+
|
169
|
+
/*
|
170
|
+
Method: Image#adaptive_resize(scale)
|
171
|
+
Image#adaptive_resize(cols, rows)
|
172
|
+
Purpose: Call AdaptiveResizeImage
|
173
|
+
*/
|
174
|
+
VALUE
|
175
|
+
Image_adaptive_resize(int argc, VALUE *argv, VALUE self)
|
176
|
+
{
|
177
|
+
#if defined(HAVE_ADAPTIVERESIZEIMAGE)
|
178
|
+
|
179
|
+
Image *image, *new_image;
|
180
|
+
unsigned long rows, columns;
|
181
|
+
double scale, drows, dcols;
|
182
|
+
ExceptionInfo exception;
|
183
|
+
|
184
|
+
Data_Get_Struct(self, Image, image);
|
185
|
+
|
186
|
+
switch (argc)
|
187
|
+
{
|
188
|
+
case 2:
|
189
|
+
rows = NUM2ULONG(argv[1]);
|
190
|
+
columns = NUM2ULONG(argv[0]);
|
191
|
+
break;
|
192
|
+
case 1:
|
193
|
+
scale = NUM2DBL(argv[0]);
|
194
|
+
if (scale < 0.0)
|
195
|
+
{
|
196
|
+
rb_raise(rb_eArgError, "invalid scale value (%g given)", scale);
|
197
|
+
}
|
198
|
+
drows = scale * image->rows + 0.5;
|
199
|
+
dcols = scale * image->columns + 0.5;
|
200
|
+
if (drows > ULONG_MAX || dcols > ULONG_MAX)
|
201
|
+
{
|
202
|
+
rb_raise(rb_eRangeError, "resized image too big");
|
203
|
+
}
|
204
|
+
rows = (unsigned long) drows;
|
205
|
+
columns = (unsigned long) dcols;
|
206
|
+
break;
|
207
|
+
default:
|
208
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
|
209
|
+
break;
|
210
|
+
}
|
211
|
+
|
212
|
+
GetExceptionInfo(&exception);
|
213
|
+
new_image = AdaptiveResizeImage(image, columns, rows, &exception);
|
214
|
+
rm_check_exception(&exception, new_image, DestroyOnError);
|
215
|
+
|
216
|
+
DestroyExceptionInfo(&exception);
|
217
|
+
rm_ensure_result(new_image);
|
218
|
+
|
219
|
+
return rm_image_new(new_image);
|
220
|
+
|
221
|
+
#else
|
222
|
+
rm_not_implemented();
|
223
|
+
return (VALUE)0;
|
224
|
+
#endif
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
|
229
|
+
/*
|
230
|
+
Method: Image#adaptive_sharpen(radius=0.0, sigma=1.0)
|
231
|
+
Purpose: call AdaptiveSharpenImage
|
232
|
+
*/
|
233
|
+
VALUE
|
234
|
+
Image_adaptive_sharpen(int argc, VALUE *argv, VALUE self)
|
235
|
+
{
|
236
|
+
#if defined(HAVE_ADAPTIVESHARPENIMAGE)
|
237
|
+
return adaptive_method(argc, argv, self, AdaptiveSharpenImage);
|
238
|
+
#else
|
239
|
+
|
240
|
+
rm_not_implemented();
|
241
|
+
return (VALUE)0;
|
242
|
+
#endif
|
243
|
+
}
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
/*
|
248
|
+
Method: Image#adaptive_sharpen_channel(radius=0.0, sigma=1.0[, channel...])
|
249
|
+
Purpose: Call AdaptiveSharpenImageChannel
|
250
|
+
*/
|
251
|
+
VALUE
|
252
|
+
Image_adaptive_sharpen_channel(int argc, VALUE *argv, VALUE self)
|
253
|
+
{
|
254
|
+
#if defined(HAVE_ADAPTIVESHARPENIMAGE)
|
255
|
+
return adaptive_channel_method(argc, argv, self, AdaptiveSharpenImageChannel);
|
133
256
|
#else
|
134
257
|
|
135
258
|
rm_not_implemented();
|
@@ -255,6 +378,137 @@ Image_add_noise_channel(int argc, VALUE *argv, VALUE self)
|
|
255
378
|
#endif
|
256
379
|
}
|
257
380
|
|
381
|
+
|
382
|
+
/*
|
383
|
+
Method: Image#add_profile(name)
|
384
|
+
Purpose: adds all the profiles in the specified file
|
385
|
+
Notes: `name' is the profile filename
|
386
|
+
*/
|
387
|
+
VALUE
|
388
|
+
Image_add_profile(VALUE self, VALUE name)
|
389
|
+
{
|
390
|
+
#if defined(HAVE_GETNEXTIMAGEPROFILE)
|
391
|
+
// ImageMagick code based on the code for the "-profile" option in mogrify.c
|
392
|
+
Image *image, *profile_image;
|
393
|
+
ImageInfo *info;
|
394
|
+
ExceptionInfo exception;
|
395
|
+
char *profile_name;
|
396
|
+
char *profile_filename = NULL;
|
397
|
+
long profile_filename_l = 0;
|
398
|
+
const StringInfo *profile;
|
399
|
+
|
400
|
+
rm_check_frozen(self);
|
401
|
+
Data_Get_Struct(self, Image, image);
|
402
|
+
|
403
|
+
// ProfileImage issues a warning if something goes wrong.
|
404
|
+
profile_filename = STRING_PTR_LEN(name, profile_filename_l);
|
405
|
+
|
406
|
+
info = CloneImageInfo(NULL);
|
407
|
+
info->client_data= (void *)GetImageProfile(image,"8bim");
|
408
|
+
|
409
|
+
strncpy(info->filename, profile_filename, min(profile_filename_l, sizeof(info->filename)));
|
410
|
+
info->filename[MaxTextExtent-1] = '\0';
|
411
|
+
|
412
|
+
GetExceptionInfo(&exception);
|
413
|
+
profile_image = ReadImage(info, &exception);
|
414
|
+
DestroyImageInfo(info);
|
415
|
+
rm_check_exception(&exception, profile_image, DestroyOnError);
|
416
|
+
DestroyExceptionInfo(&exception);
|
417
|
+
rm_ensure_result(profile_image);
|
418
|
+
|
419
|
+
ResetImageProfileIterator(profile_image);
|
420
|
+
profile_name = GetNextImageProfile(profile_image);
|
421
|
+
while (profile_name)
|
422
|
+
{
|
423
|
+
profile = GetImageProfile(profile_image, profile_name);
|
424
|
+
if (profile)
|
425
|
+
{
|
426
|
+
(void)ProfileImage(image, profile_name, profile->datum, (unsigned long)profile->length, False);
|
427
|
+
if (image->exception.severity >= ErrorException)
|
428
|
+
{
|
429
|
+
break;
|
430
|
+
}
|
431
|
+
}
|
432
|
+
profile_name = GetNextImageProfile(profile_image);
|
433
|
+
}
|
434
|
+
|
435
|
+
DestroyImage(profile_image);
|
436
|
+
rm_check_image_exception(image, RetainOnError);
|
437
|
+
|
438
|
+
#else
|
439
|
+
|
440
|
+
// GraphicsMagick code based on the code for the "-profile" option in command.c
|
441
|
+
Image *image, *profile_image;
|
442
|
+
ImageInfo *info;
|
443
|
+
ExceptionInfo exception;
|
444
|
+
char *profile_filename = NULL;
|
445
|
+
long profile_filename_l = 0;
|
446
|
+
ProfileInfo *generic;
|
447
|
+
const unsigned char *profile;
|
448
|
+
size_t profile_l;
|
449
|
+
long x;
|
450
|
+
|
451
|
+
rm_check_frozen(self);
|
452
|
+
Data_Get_Struct(self, Image, image);
|
453
|
+
|
454
|
+
// ProfileImage issues a warning if something goes wrong.
|
455
|
+
profile_filename = STRING_PTR_LEN(name, profile_filename_l);
|
456
|
+
|
457
|
+
info = CloneImageInfo(NULL);
|
458
|
+
info->client_data= (void *) &image->iptc_profile;
|
459
|
+
strncpy(info->filename, profile_filename, min(profile_filename_l, sizeof(info->filename)));
|
460
|
+
info->filename[MaxTextExtent-1] = '\0';
|
461
|
+
|
462
|
+
GetExceptionInfo(&exception);
|
463
|
+
profile_image = ReadImage(info, &exception);
|
464
|
+
DestroyImageInfo(info);
|
465
|
+
rm_check_exception(&exception, profile_image, DestroyOnError);
|
466
|
+
DestroyExceptionInfo(&exception);
|
467
|
+
rm_ensure_result(profile_image);
|
468
|
+
|
469
|
+
/* IPTC NewsPhoto Profile */
|
470
|
+
profile = GetImageProfile(profile_image, "IPTC", &profile_l);
|
471
|
+
if (profile)
|
472
|
+
{
|
473
|
+
(void)SetImageProfile(image, "IPTC", profile, profile_l);
|
474
|
+
if (image->exception.severity >= ErrorException)
|
475
|
+
{
|
476
|
+
goto done;
|
477
|
+
}
|
478
|
+
}
|
479
|
+
|
480
|
+
/* ICC ICM Profile */
|
481
|
+
profile = GetImageProfile(profile_image, "ICM", &profile_l);
|
482
|
+
if (profile)
|
483
|
+
{
|
484
|
+
(void)SetImageProfile(image, "ICM", profile, profile_l);
|
485
|
+
if (image->exception.severity >= ErrorException)
|
486
|
+
{
|
487
|
+
goto done;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
|
491
|
+
/* Generic Profiles */
|
492
|
+
for (x = 0; x < (long)profile_image->generic_profiles; x++)
|
493
|
+
{
|
494
|
+
generic = profile_image->generic_profile + x;
|
495
|
+
(void)SetImageProfile(image, generic->name, generic->info, generic->length);
|
496
|
+
if (image->exception.severity >= ErrorException)
|
497
|
+
{
|
498
|
+
break;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
|
502
|
+
done:
|
503
|
+
DestroyImage(profile_image);
|
504
|
+
rm_check_image_exception(image, RetainOnError);
|
505
|
+
|
506
|
+
#endif
|
507
|
+
|
508
|
+
return self;
|
509
|
+
}
|
510
|
+
|
511
|
+
|
258
512
|
/*
|
259
513
|
Method: Image#affine_transform(affine_matrix)
|
260
514
|
Purpose: transforms an image as dictated by the affine matrix argument
|
@@ -409,7 +663,7 @@ static VALUE
|
|
409
663
|
crisscross(
|
410
664
|
int bang,
|
411
665
|
VALUE self,
|
412
|
-
Image *
|
666
|
+
Image *fp(const Image *, ExceptionInfo *))
|
413
667
|
{
|
414
668
|
Image *image, *new_image;
|
415
669
|
ExceptionInfo exception;
|
@@ -449,6 +703,7 @@ static VALUE auto_orient(int bang, VALUE self)
|
|
449
703
|
#if defined(HAVE_TRANSPOSEIMAGE) || defined(HAVE_TRANSVERSEIMAGE)
|
450
704
|
Image *image;
|
451
705
|
volatile VALUE new_image;
|
706
|
+
VALUE degrees[1];
|
452
707
|
|
453
708
|
Data_Get_Struct(self, Image, image);
|
454
709
|
|
@@ -459,7 +714,8 @@ static VALUE auto_orient(int bang, VALUE self)
|
|
459
714
|
break;
|
460
715
|
|
461
716
|
case BottomRightOrientation:
|
462
|
-
|
717
|
+
degrees[0] = rb_float_new(180.0);
|
718
|
+
new_image = rotate(bang, 1, degrees, self);
|
463
719
|
break;
|
464
720
|
|
465
721
|
case BottomLeftOrientation:
|
@@ -471,7 +727,8 @@ static VALUE auto_orient(int bang, VALUE self)
|
|
471
727
|
break;
|
472
728
|
|
473
729
|
case RightTopOrientation:
|
474
|
-
|
730
|
+
degrees[0] = rb_float_new(90.0);
|
731
|
+
new_image = rotate(bang, 1, degrees, self);
|
475
732
|
break;
|
476
733
|
|
477
734
|
case RightBottomOrientation:
|
@@ -479,7 +736,8 @@ static VALUE auto_orient(int bang, VALUE self)
|
|
479
736
|
break;
|
480
737
|
|
481
738
|
case LeftBottomOrientation:
|
482
|
-
|
739
|
+
degrees[0] = rb_float_new(270.0);
|
740
|
+
new_image = rotate(bang, 1, degrees, self);
|
483
741
|
break;
|
484
742
|
|
485
743
|
default: // Return IMMEDIATELY
|
@@ -517,7 +775,6 @@ Image_auto_orient_bang(VALUE self)
|
|
517
775
|
}
|
518
776
|
|
519
777
|
|
520
|
-
|
521
778
|
/*
|
522
779
|
Method: Image#background_color
|
523
780
|
Purpose: Return the name of the background color as a String.
|
@@ -531,6 +788,7 @@ Image_background_color(VALUE self)
|
|
531
788
|
return PixelPacket_to_Color_Name(image, &image->background_color);
|
532
789
|
}
|
533
790
|
|
791
|
+
|
534
792
|
/*
|
535
793
|
Method: Image#background_color=
|
536
794
|
Purpose: Set the the background color to the specified color spec.
|
@@ -546,6 +804,7 @@ Image_background_color_eq(VALUE self, VALUE color)
|
|
546
804
|
return self;
|
547
805
|
}
|
548
806
|
|
807
|
+
|
549
808
|
/*
|
550
809
|
Method: Image#base_columns
|
551
810
|
Purpose: Return the number of rows (before transformations)
|
@@ -591,6 +850,44 @@ VALUE Image_base_rows(VALUE self)
|
|
591
850
|
}
|
592
851
|
|
593
852
|
|
853
|
+
/*
|
854
|
+
Method: Image#bias -> bias
|
855
|
+
Image#bias = a number between 0.0 and 1.0 or "NN%"
|
856
|
+
Purpose: Get/set image bias (used when convolving an image)
|
857
|
+
*/
|
858
|
+
VALUE Image_bias(VALUE self)
|
859
|
+
{
|
860
|
+
#if defined(HAVE_IMAGE_BIAS)
|
861
|
+
Image *image;
|
862
|
+
|
863
|
+
Data_Get_Struct(self, Image, image);
|
864
|
+
return rb_float_new(image->bias);
|
865
|
+
#else
|
866
|
+
rm_not_implemented();
|
867
|
+
return (VALUE)0;
|
868
|
+
#endif
|
869
|
+
}
|
870
|
+
|
871
|
+
|
872
|
+
VALUE Image_bias_eq(VALUE self, VALUE pct)
|
873
|
+
{
|
874
|
+
#if defined(HAVE_IMAGE_BIAS)
|
875
|
+
Image *image;
|
876
|
+
double bias;
|
877
|
+
|
878
|
+
rm_check_frozen(self);
|
879
|
+
Data_Get_Struct(self, Image, image);
|
880
|
+
bias = rm_percentage(pct);
|
881
|
+
image->bias = bias * MaxRGB;
|
882
|
+
|
883
|
+
return self;
|
884
|
+
|
885
|
+
#else
|
886
|
+
rm_not_implemented();
|
887
|
+
return (VALUE)0;
|
888
|
+
#endif
|
889
|
+
|
890
|
+
}
|
594
891
|
|
595
892
|
/*
|
596
893
|
* Method: Image#bilevel_channel(threshold, channel=AllChannels)
|
@@ -622,23 +919,388 @@ Image_bilevel_channel(int argc, VALUE *argv, VALUE self)
|
|
622
919
|
|
623
920
|
return rm_image_new(new_image);
|
624
921
|
|
625
|
-
#else
|
626
|
-
rm_not_implemented();
|
627
|
-
return (VALUE)0;
|
628
|
-
#endif
|
629
|
-
}
|
922
|
+
#else
|
923
|
+
rm_not_implemented();
|
924
|
+
return (VALUE)0;
|
925
|
+
#endif
|
926
|
+
}
|
927
|
+
|
928
|
+
|
929
|
+
/*
|
930
|
+
Method: Image#black_point_compensation
|
931
|
+
Purpose: Return current value
|
932
|
+
*/
|
933
|
+
VALUE
|
934
|
+
Image_black_point_compensation(VALUE self)
|
935
|
+
{
|
936
|
+
Image *image;
|
937
|
+
const ImageAttribute *attr;
|
938
|
+
volatile VALUE value;
|
939
|
+
|
940
|
+
Data_Get_Struct(self, Image, image);
|
941
|
+
|
942
|
+
attr = GetImageAttribute(image, BlackPointCompensationKey);
|
943
|
+
if (attr && rm_strcasecmp(attr->value, "true") == 0)
|
944
|
+
{
|
945
|
+
value = Qtrue;
|
946
|
+
}
|
947
|
+
else
|
948
|
+
{
|
949
|
+
value = Qfalse;
|
950
|
+
}
|
951
|
+
return value;
|
952
|
+
}
|
953
|
+
|
954
|
+
|
955
|
+
/*
|
956
|
+
Method: Image#black_point_compensation=true or false
|
957
|
+
Purpose: Set black point compensation attribute
|
958
|
+
*/
|
959
|
+
VALUE
|
960
|
+
Image_black_point_compensation_eq(VALUE self, VALUE arg)
|
961
|
+
{
|
962
|
+
Image *image;
|
963
|
+
char *value;
|
964
|
+
|
965
|
+
rm_check_frozen(self);
|
966
|
+
Data_Get_Struct(self, Image, image);
|
967
|
+
|
968
|
+
(void) SetImageAttribute(image, BlackPointCompensationKey, NULL);
|
969
|
+
value = RTEST(arg) ? "true" : "false";
|
970
|
+
(void) SetImageAttribute(image, BlackPointCompensationKey, value);
|
971
|
+
|
972
|
+
return self;
|
973
|
+
}
|
974
|
+
|
975
|
+
|
976
|
+
/*
|
977
|
+
* Method: Image#black_threshold(red_channel [, green_channel
|
978
|
+
* [, blue_channel [, opacity_channel]]]);
|
979
|
+
* Purpose: Call BlackThresholdImage
|
980
|
+
*/
|
981
|
+
VALUE
|
982
|
+
Image_black_threshold(int argc, VALUE *argv, VALUE self)
|
983
|
+
{
|
984
|
+
#if defined(HAVE_BLACKTHRESHOLDIMAGE)
|
985
|
+
return threshold_image(argc, argv, self, BlackThresholdImage);
|
986
|
+
#else
|
987
|
+
rm_not_implemented();
|
988
|
+
return (VALUE)0;
|
989
|
+
#endif
|
990
|
+
}
|
991
|
+
|
992
|
+
|
993
|
+
/*
|
994
|
+
Static: get_relative_offsets
|
995
|
+
Purpose: compute offsets using the gravity to determine what the
|
996
|
+
offsets are relative to
|
997
|
+
*/
|
998
|
+
static void
|
999
|
+
get_relative_offsets(
|
1000
|
+
VALUE grav,
|
1001
|
+
Image *image,
|
1002
|
+
Image *mark,
|
1003
|
+
long *x_offset,
|
1004
|
+
long *y_offset)
|
1005
|
+
{
|
1006
|
+
MagickEnum *magick_enum;
|
1007
|
+
GravityType gravity;
|
1008
|
+
|
1009
|
+
VALUE_TO_ENUM(grav, gravity, GravityType);
|
1010
|
+
|
1011
|
+
switch(gravity)
|
1012
|
+
{
|
1013
|
+
case NorthEastGravity:
|
1014
|
+
case EastGravity:
|
1015
|
+
*x_offset = (long)(image->columns) - (long)(mark->columns) - *x_offset;
|
1016
|
+
break;
|
1017
|
+
case SouthWestGravity:
|
1018
|
+
case SouthGravity:
|
1019
|
+
*y_offset = (long)(image->rows) - (long)(mark->rows) - *y_offset;
|
1020
|
+
break;
|
1021
|
+
case SouthEastGravity:
|
1022
|
+
*x_offset = (long)(image->columns) - (long)(mark->columns) - *x_offset;
|
1023
|
+
*y_offset = (long)(image->rows) - (long)(mark->rows) - *y_offset;
|
1024
|
+
break;
|
1025
|
+
default:
|
1026
|
+
Data_Get_Struct(grav, MagickEnum, magick_enum);
|
1027
|
+
rb_warning("gravity type `%s' has no effect", rb_id2name(magick_enum->id));
|
1028
|
+
break;
|
1029
|
+
}
|
1030
|
+
|
1031
|
+
}
|
1032
|
+
|
1033
|
+
|
1034
|
+
/*
|
1035
|
+
Static: get_offsets_from_gravity
|
1036
|
+
Purpose: compute watermark offsets from gravity type
|
1037
|
+
*/
|
1038
|
+
static void
|
1039
|
+
get_offsets_from_gravity(
|
1040
|
+
GravityType gravity,
|
1041
|
+
Image *image,
|
1042
|
+
Image *mark,
|
1043
|
+
long *x_offset,
|
1044
|
+
long *y_offset)
|
1045
|
+
{
|
1046
|
+
|
1047
|
+
switch (gravity)
|
1048
|
+
{
|
1049
|
+
case ForgetGravity:
|
1050
|
+
case NorthWestGravity:
|
1051
|
+
*x_offset = 0;
|
1052
|
+
*y_offset = 0;
|
1053
|
+
break;
|
1054
|
+
case NorthGravity:
|
1055
|
+
*x_offset = ((long)(image->columns) - (long)(mark->columns)) / 2;
|
1056
|
+
*y_offset = 0;
|
1057
|
+
break;
|
1058
|
+
case NorthEastGravity:
|
1059
|
+
*x_offset = (long)(image->columns) - (long)(mark->columns);
|
1060
|
+
*y_offset = 0;
|
1061
|
+
break;
|
1062
|
+
case WestGravity:
|
1063
|
+
*x_offset = 0;
|
1064
|
+
*y_offset = ((long)(image->rows) - (long)(mark->rows)) / 2;
|
1065
|
+
break;
|
1066
|
+
case StaticGravity:
|
1067
|
+
case CenterGravity:
|
1068
|
+
default:
|
1069
|
+
*x_offset = ((long)(image->columns) - (long)(mark->columns)) / 2;
|
1070
|
+
*y_offset = ((long)(image->rows) - (long)(mark->rows)) / 2;
|
1071
|
+
break;
|
1072
|
+
case EastGravity:
|
1073
|
+
*x_offset = (long)(image->columns) - (long)(mark->columns);
|
1074
|
+
*y_offset = ((long)(image->rows) - (long)(mark->rows)) / 2;
|
1075
|
+
break;
|
1076
|
+
case SouthWestGravity:
|
1077
|
+
*x_offset = 0;
|
1078
|
+
*y_offset = (long)(image->rows) - (long)(mark->rows);
|
1079
|
+
break;
|
1080
|
+
case SouthGravity:
|
1081
|
+
*x_offset = ((long)(image->columns) - (long)(mark->columns)) / 2;
|
1082
|
+
*y_offset = (long)(image->rows) - (long)(mark->rows);
|
1083
|
+
break;
|
1084
|
+
case SouthEastGravity:
|
1085
|
+
*x_offset = (long)(image->columns) - (long)(mark->columns);
|
1086
|
+
*y_offset = (long)(image->rows) - (long)(mark->rows);
|
1087
|
+
break;
|
1088
|
+
}
|
1089
|
+
}
|
1090
|
+
|
1091
|
+
|
1092
|
+
/*
|
1093
|
+
Static: check_for_long_value
|
1094
|
+
Purpose: called from rb_protect, returns the number if obj is really
|
1095
|
+
a numeric value.
|
1096
|
+
*/
|
1097
|
+
static VALUE check_for_long_value(VALUE obj)
|
1098
|
+
{
|
1099
|
+
long t;
|
1100
|
+
t = NUM2LONG(obj);
|
1101
|
+
t = t; // placate gcc
|
1102
|
+
return (VALUE)0;
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
|
1106
|
+
/*
|
1107
|
+
Static: get_composite_offsets
|
1108
|
+
Purpose: compute x- and y-offset of source image for a compositing method
|
1109
|
+
*/
|
1110
|
+
static void get_composite_offsets(
|
1111
|
+
int argc,
|
1112
|
+
VALUE *argv,
|
1113
|
+
Image *dest,
|
1114
|
+
Image *src,
|
1115
|
+
long *x_offset,
|
1116
|
+
long *y_offset)
|
1117
|
+
{
|
1118
|
+
GravityType gravity;
|
1119
|
+
int exc = 0;
|
1120
|
+
|
1121
|
+
if (CLASS_OF(argv[0]) == Class_GravityType)
|
1122
|
+
{
|
1123
|
+
VALUE_TO_ENUM(argv[0], gravity, GravityType);
|
1124
|
+
|
1125
|
+
switch (argc)
|
1126
|
+
{
|
1127
|
+
// Gravity + offset(s). Offsets are relative to the image edges
|
1128
|
+
// as specified by the gravity.
|
1129
|
+
case 3:
|
1130
|
+
*y_offset = NUM2LONG(argv[2]);
|
1131
|
+
case 2:
|
1132
|
+
*x_offset = NUM2LONG(argv[1]);
|
1133
|
+
get_relative_offsets(argv[0], dest, src, x_offset, y_offset);
|
1134
|
+
break;
|
1135
|
+
case 1:
|
1136
|
+
// No offsets specified. Compute offset based on the gravity alone.
|
1137
|
+
get_offsets_from_gravity(gravity, dest, src, x_offset, y_offset);
|
1138
|
+
break;
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
// Gravity not specified at all. Offsets are measured from the
|
1142
|
+
// NorthWest corner. The arguments must be numbers.
|
1143
|
+
else
|
1144
|
+
{
|
1145
|
+
*x_offset = rb_protect(check_for_long_value, argv[0], &exc);
|
1146
|
+
if (exc)
|
1147
|
+
{
|
1148
|
+
rb_raise(rb_eArgError, "expected GravityType, got %s", rb_obj_classname(argv[0]));
|
1149
|
+
}
|
1150
|
+
*x_offset = NUM2LONG(argv[0]);
|
1151
|
+
if (argc > 1)
|
1152
|
+
{
|
1153
|
+
*y_offset = NUM2LONG(argv[1]);
|
1154
|
+
}
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
}
|
1158
|
+
|
1159
|
+
|
1160
|
+
/*
|
1161
|
+
Static: blend_geometry
|
1162
|
+
Purpose: Convert 2 doubles to a blend or dissolve geometry string.
|
1163
|
+
Notes: the geometry buffer needs to be at least 16 characters long.
|
1164
|
+
For safety's sake this function asserts that it is at least
|
1165
|
+
20 characters long.
|
1166
|
+
The percentages must be in the range -1000 < n < 1000. This
|
1167
|
+
is far in excess of what xMagick will allow.
|
1168
|
+
*/
|
1169
|
+
static void
|
1170
|
+
blend_geometry(
|
1171
|
+
char *geometry,
|
1172
|
+
size_t geometry_l,
|
1173
|
+
double src_percent,
|
1174
|
+
double dst_percent)
|
1175
|
+
{
|
1176
|
+
int sz = 0;
|
1177
|
+
int fw, prec;
|
1178
|
+
|
1179
|
+
if (fabs(src_percent) >= 1000.0 || fabs(dst_percent) >= 1000.0)
|
1180
|
+
{
|
1181
|
+
if (fabs(src_percent) < 1000.0)
|
1182
|
+
{
|
1183
|
+
src_percent = dst_percent;
|
1184
|
+
}
|
1185
|
+
rb_raise(rb_eArgError, "%g is out of range +/-999.99", src_percent);
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
assert(geometry_l >= 20);
|
1189
|
+
memset(geometry, 0xdf, geometry_l);
|
1190
|
+
|
1191
|
+
fw = 4;
|
1192
|
+
prec = 0;
|
1193
|
+
if (src_percent != (int)(src_percent))
|
1194
|
+
{
|
1195
|
+
prec = 2;
|
1196
|
+
fw += 3;
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
sz = sprintf(geometry, "%*.*f", -fw, prec, src_percent);
|
1200
|
+
assert(sz < geometry_l);
|
1201
|
+
|
1202
|
+
sz = strcspn(geometry, " ");
|
1203
|
+
|
1204
|
+
// if dst_percent was nil don't add to the geometry
|
1205
|
+
if (dst_percent != -1.0)
|
1206
|
+
{
|
1207
|
+
fw = 4;
|
1208
|
+
prec = 0;
|
1209
|
+
if (dst_percent != (int)(dst_percent))
|
1210
|
+
{
|
1211
|
+
prec = 2;
|
1212
|
+
fw += 3;
|
1213
|
+
}
|
1214
|
+
|
1215
|
+
|
1216
|
+
sz += sprintf(geometry+sz, "x%*.*f", -fw, prec, dst_percent);
|
1217
|
+
assert(sz < geometry_l);
|
1218
|
+
sz = strcspn(geometry, " ");
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
if (sz < geometry_l)
|
1222
|
+
{
|
1223
|
+
memset(geometry+sz, 0x00, geometry_l-sz);
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
|
1229
|
+
static VALUE
|
1230
|
+
special_composite(
|
1231
|
+
Image *image,
|
1232
|
+
Image *overlay,
|
1233
|
+
double image_pct,
|
1234
|
+
double overlay_pct,
|
1235
|
+
long x_off,
|
1236
|
+
long y_off,
|
1237
|
+
CompositeOperator op)
|
1238
|
+
{
|
1239
|
+
Image *new_image;
|
1240
|
+
char geometry[20];
|
1241
|
+
|
1242
|
+
blend_geometry(geometry, sizeof(geometry), image_pct, overlay_pct);
|
1243
|
+
CloneString(&overlay->geometry, geometry);
|
1244
|
+
|
1245
|
+
new_image = rm_clone_image(image);
|
1246
|
+
(void) CompositeImage(new_image, op, overlay, x_off, y_off);
|
1247
|
+
|
1248
|
+
rm_check_image_exception(new_image, DestroyOnError);
|
1249
|
+
|
1250
|
+
return rm_image_new(new_image);
|
1251
|
+
}
|
1252
|
+
|
1253
|
+
|
1254
|
+
/*
|
1255
|
+
Method: Image#blend(overlay, src_percent, dst_percent, x_offset=0, y_offset=0)
|
1256
|
+
Image#dissolve(overlay, src_percent, dst_percent, gravity, x_offset=0, y_offset=0)
|
1257
|
+
Purpose: Corresponds to the composite -blend operation
|
1258
|
+
Notes: `percent' can be a number or a string in the form "NN%"
|
1259
|
+
The default value for dst_percent is 100.0-src_percent
|
1260
|
+
*/
|
1261
|
+
VALUE
|
1262
|
+
Image_blend(int argc, VALUE *argv, VALUE self)
|
1263
|
+
{
|
1264
|
+
#if defined(HAVE_COLORDODGECOMPOSITEOP)
|
1265
|
+
Image *image, *overlay;
|
1266
|
+
double src_percent, dst_percent;
|
1267
|
+
long x_offset = 0L, y_offset = 0L;
|
1268
|
+
|
1269
|
+
Data_Get_Struct(self, Image, image);
|
1270
|
+
|
1271
|
+
if (argc < 1)
|
1272
|
+
{
|
1273
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
1274
|
+
}
|
1275
|
+
|
1276
|
+
if (argc > 3)
|
1277
|
+
{
|
1278
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, overlay);
|
1279
|
+
get_composite_offsets(argc-3, &argv[3], image, overlay, &x_offset, &y_offset);
|
1280
|
+
// There must be 3 arguments left
|
1281
|
+
argc = 3;
|
1282
|
+
}
|
1283
|
+
|
1284
|
+
switch (argc)
|
1285
|
+
{
|
1286
|
+
case 3:
|
1287
|
+
dst_percent = rm_percentage(argv[2]) * 100.0;
|
1288
|
+
src_percent = rm_percentage(argv[1]) * 100.0;
|
1289
|
+
break;
|
1290
|
+
case 2:
|
1291
|
+
src_percent = rm_percentage(argv[1]) * 100.0;
|
1292
|
+
dst_percent = FMAX(100.0 - src_percent, 0);
|
1293
|
+
break;
|
1294
|
+
default:
|
1295
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
1296
|
+
break;
|
1297
|
+
}
|
1298
|
+
|
1299
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, overlay);
|
630
1300
|
|
1301
|
+
return special_composite(image, overlay, src_percent, dst_percent
|
1302
|
+
, x_offset, y_offset, BlendCompositeOp);
|
631
1303
|
|
632
|
-
/*
|
633
|
-
* Method: Image#black_threshold(red_channel [, green_channel
|
634
|
-
* [, blue_channel [, opacity_channel]]]);
|
635
|
-
* Purpose: Call BlackThresholdImage
|
636
|
-
*/
|
637
|
-
VALUE
|
638
|
-
Image_black_threshold(int argc, VALUE *argv, VALUE self)
|
639
|
-
{
|
640
|
-
#if defined(HAVE_BLACKTHRESHOLDIMAGE)
|
641
|
-
return threshold_image(argc, argv, self, BlackThresholdImage);
|
642
1304
|
#else
|
643
1305
|
rm_not_implemented();
|
644
1306
|
return (VALUE)0;
|
@@ -1338,35 +2000,6 @@ Image_chromaticity_eq(VALUE self, VALUE chroma)
|
|
1338
2000
|
return self;
|
1339
2001
|
}
|
1340
2002
|
|
1341
|
-
/*
|
1342
|
-
Method: Image#clip_mask=(mask-image)
|
1343
|
-
Purpose: associates a clip mask with the image
|
1344
|
-
Notes: pass "nil" for the mask-image to remove the current clip mask.
|
1345
|
-
The two images must have the same dimensions.
|
1346
|
-
*/
|
1347
|
-
VALUE
|
1348
|
-
Image_clip_mask_eq(VALUE self, VALUE mask)
|
1349
|
-
{
|
1350
|
-
Image *image, *mask_image;
|
1351
|
-
Image *clip_mask;
|
1352
|
-
|
1353
|
-
rm_check_frozen(self);
|
1354
|
-
Data_Get_Struct(self, Image, image);
|
1355
|
-
|
1356
|
-
if (mask != Qnil)
|
1357
|
-
{
|
1358
|
-
Data_Get_Struct(ImageList_cur_image(mask), Image, mask_image);
|
1359
|
-
clip_mask = rm_clone_image(mask_image);
|
1360
|
-
|
1361
|
-
(void) SetImageClipMask(image, clip_mask);
|
1362
|
-
}
|
1363
|
-
else
|
1364
|
-
{
|
1365
|
-
(void) SetImageClipMask(image, NULL);
|
1366
|
-
}
|
1367
|
-
|
1368
|
-
return self;
|
1369
|
-
}
|
1370
2003
|
|
1371
2004
|
/*
|
1372
2005
|
Method: Image#clone
|
@@ -1489,179 +2122,186 @@ Image_color_histogram(VALUE self)
|
|
1489
2122
|
#endif
|
1490
2123
|
}
|
1491
2124
|
|
2125
|
+
|
2126
|
+
|
1492
2127
|
/*
|
1493
|
-
|
1494
|
-
Purpose:
|
1495
|
-
|
2128
|
+
Static: set_profile(target_image, name, profile_image)
|
2129
|
+
Purpose: The `profile_image' argument is an IPTC or ICC profile. Store
|
2130
|
+
all the profiles in the profile in the target image.
|
2131
|
+
Called from Image_color_profile_eq and Image_iptc_profile_eq
|
1496
2132
|
*/
|
1497
|
-
VALUE
|
1498
|
-
Image_color_profile(VALUE self)
|
2133
|
+
static VALUE set_profile(VALUE self, const char *name, VALUE profile)
|
1499
2134
|
{
|
1500
|
-
|
1501
|
-
|
2135
|
+
#if defined(HAVE_GETNEXTIMAGEPROFILE)
|
2136
|
+
Image *image, *profile_image;
|
2137
|
+
ImageInfo *info;
|
2138
|
+
const MagickInfo *m;
|
2139
|
+
ExceptionInfo exception;
|
2140
|
+
char *profile_name;
|
2141
|
+
char *profile_blob;
|
2142
|
+
long profile_length;
|
2143
|
+
const StringInfo *profile_data;
|
1502
2144
|
|
1503
|
-
|
1504
|
-
|
1505
|
-
/* but the implementations are different. IM 6.0.0 */
|
1506
|
-
/* uses a StringInfo type. That's our feature test. */
|
2145
|
+
rm_check_frozen(self);
|
2146
|
+
Data_Get_Struct(self, Image, image);
|
1507
2147
|
|
1508
|
-
|
1509
|
-
char *str;
|
1510
|
-
StringInfo *str_info;
|
2148
|
+
profile_blob = STRING_PTR_LEN(profile, profile_length);
|
1511
2149
|
|
1512
|
-
|
2150
|
+
GetExceptionInfo(&exception);
|
2151
|
+
m = GetMagickInfo(name, &exception);
|
2152
|
+
CHECK_EXCEPTION()
|
1513
2153
|
|
1514
|
-
|
1515
|
-
if (!
|
2154
|
+
info = CloneImageInfo(NULL);
|
2155
|
+
if (!info)
|
1516
2156
|
{
|
1517
|
-
|
2157
|
+
rb_raise(rb_eNoMemError, "not enough memory to continue");
|
1518
2158
|
}
|
1519
|
-
|
2159
|
+
|
2160
|
+
strncpy(info->magick, m->name, MaxTextExtent);
|
2161
|
+
info->magick[MaxTextExtent-1] = '\0';
|
2162
|
+
|
2163
|
+
profile_image = BlobToImage(info, profile_blob, profile_length, &exception);
|
2164
|
+
DestroyImageInfo(info);
|
2165
|
+
CHECK_EXCEPTION()
|
2166
|
+
DestroyExceptionInfo(&exception);
|
2167
|
+
|
2168
|
+
ResetImageProfileIterator(profile_image);
|
2169
|
+
profile_name = GetNextImageProfile(profile_image);
|
2170
|
+
while (profile_name)
|
1520
2171
|
{
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
2172
|
+
if (rm_strcasecmp(profile_name, name) == 0)
|
2173
|
+
{
|
2174
|
+
profile_data = GetImageProfile(profile_image, profile_name);
|
2175
|
+
if (profile)
|
2176
|
+
{
|
2177
|
+
(void)ProfileImage(image, profile_name, profile_data->datum
|
2178
|
+
, (unsigned long)profile_data->length, False);
|
2179
|
+
if (image->exception.severity >= ErrorException)
|
2180
|
+
{
|
2181
|
+
break;
|
2182
|
+
}
|
2183
|
+
}
|
2184
|
+
}
|
2185
|
+
profile_name = GetNextImageProfile(profile_image);
|
1524
2186
|
}
|
1525
2187
|
|
1526
|
-
|
1527
|
-
|
1528
|
-
size_t length;
|
2188
|
+
DestroyImage(profile_image);
|
2189
|
+
rm_check_image_exception(image, RetainOnError);
|
1529
2190
|
|
2191
|
+
#else
|
2192
|
+
|
2193
|
+
Image *image, *profile_image;
|
2194
|
+
ImageInfo *info;
|
2195
|
+
ExceptionInfo exception;
|
2196
|
+
const MagickInfo *m;
|
2197
|
+
char *profile_blob;
|
2198
|
+
long profile_length;
|
2199
|
+
const unsigned char *profile_data;
|
2200
|
+
size_t profile_data_l;
|
2201
|
+
|
2202
|
+
rm_check_frozen(self);
|
1530
2203
|
Data_Get_Struct(self, Image, image);
|
1531
2204
|
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
2205
|
+
profile_blob = STRING_PTR_LEN(profile, profile_length);
|
2206
|
+
|
2207
|
+
GetExceptionInfo(&exception);
|
2208
|
+
m = GetMagickInfo(name, &exception);
|
2209
|
+
CHECK_EXCEPTION()
|
2210
|
+
|
2211
|
+
info = CloneImageInfo(NULL);
|
2212
|
+
if (!info)
|
1536
2213
|
{
|
1537
|
-
|
2214
|
+
rb_raise(rb_eNoMemError, "not enough memory to continue");
|
1538
2215
|
}
|
1539
2216
|
|
1540
|
-
|
2217
|
+
strncpy(info->magick, m->name, MaxTextExtent);
|
2218
|
+
info->magick[MaxTextExtent-1] = '\0';
|
1541
2219
|
|
1542
|
-
|
1543
|
-
|
2220
|
+
profile_image = BlobToImage(info, profile_blob, profile_length, &exception);
|
2221
|
+
DestroyImageInfo(info);
|
2222
|
+
CHECK_EXCEPTION()
|
2223
|
+
DestroyExceptionInfo(&exception);
|
1544
2224
|
|
1545
|
-
//
|
1546
|
-
|
1547
|
-
if (image->color_profile.info == NULL)
|
2225
|
+
// GraphicsMagick uses "ICM" to refer to the ICC profile.
|
2226
|
+
if (rm_strcasecmp(name, "ICC") == 0)
|
1548
2227
|
{
|
1549
|
-
|
2228
|
+
profile_data = GetImageProfile(profile_image, "ICM", &profile_data_l);
|
1550
2229
|
}
|
1551
|
-
else
|
1552
|
-
&& image->color_profile.info)
|
2230
|
+
else
|
1553
2231
|
{
|
1554
|
-
|
1555
|
-
image->color_profile.info = NULL;
|
2232
|
+
profile_data = GetImageProfile(profile_image, name, &profile_data_l);
|
1556
2233
|
}
|
1557
|
-
if (
|
2234
|
+
if (profile_data)
|
1558
2235
|
{
|
1559
|
-
|
2236
|
+
(void)SetImageProfile(image, name, profile_data, profile_data_l);
|
1560
2237
|
}
|
1561
|
-
|
1562
|
-
|
2238
|
+
|
2239
|
+
DestroyImage(profile_image);
|
2240
|
+
rm_check_image_exception(image, RetainOnError);
|
2241
|
+
|
1563
2242
|
#endif
|
1564
2243
|
|
1565
|
-
return
|
2244
|
+
return self;
|
1566
2245
|
}
|
1567
2246
|
|
2247
|
+
|
1568
2248
|
/*
|
1569
|
-
Method: Image#color_profile
|
1570
|
-
Purpose:
|
1571
|
-
Notes:
|
2249
|
+
Method: Image#color_profile
|
2250
|
+
Purpose: Return the ICC color profile as a String.
|
2251
|
+
Notes: If there is no profile, returns ""
|
2252
|
+
This method has no real use but is retained for compatibility
|
2253
|
+
with earlier releases of RMagick, where it had no real use either.
|
1572
2254
|
*/
|
1573
2255
|
VALUE
|
1574
|
-
|
2256
|
+
Image_color_profile(VALUE self)
|
1575
2257
|
{
|
1576
2258
|
Image *image;
|
1577
2259
|
|
1578
|
-
#if defined(HAVE_GETIMAGEPROFILE)
|
1579
|
-
|
1580
|
-
/* Both IM 6.0.0 and GM 1.1. define SetImageProfile */
|
1581
|
-
/* but the implementations are different. IM 6.0.0 */
|
1582
|
-
/* uses a StringInfo type. That's our feature test. */
|
1583
|
-
|
1584
2260
|
#if defined(HAVE_ACQUIRESTRINGINFO)
|
1585
2261
|
|
1586
|
-
StringInfo *
|
1587
|
-
unsigned int status = True;
|
2262
|
+
const StringInfo *profile;
|
1588
2263
|
|
1589
|
-
rm_check_frozen(self);
|
1590
2264
|
Data_Get_Struct(self, Image, image);
|
1591
|
-
|
1592
|
-
if (profile
|
2265
|
+
profile = GetImageProfile(image, "icc");
|
2266
|
+
if (!profile)
|
1593
2267
|
{
|
1594
|
-
|
1595
|
-
(void)RemoveImageProfile(image, "icc");
|
1596
|
-
#else
|
1597
|
-
str_info = RemoveImageProfile(image, "icc");
|
1598
|
-
if(str_info)
|
1599
|
-
{
|
1600
|
-
DestroyStringInfo(str_info);
|
1601
|
-
}
|
1602
|
-
#endif
|
2268
|
+
return Qnil;
|
1603
2269
|
}
|
1604
|
-
else
|
1605
|
-
{
|
1606
|
-
str_info = StringToStringInfo(STRING_PTR(profile));
|
1607
|
-
if (str_info)
|
1608
|
-
{
|
1609
|
-
if (str_info->length > 0)
|
1610
|
-
{
|
1611
|
-
status = SetImageProfile(image, "icc", str_info);
|
1612
|
-
}
|
1613
2270
|
|
1614
|
-
|
2271
|
+
return rb_str_new((char *)profile->datum, (long)profile->length);
|
1615
2272
|
|
1616
|
-
|
1617
|
-
{
|
1618
|
-
rb_raise(rb_eNoMemError, "not enough memory to continue");
|
1619
|
-
}
|
1620
|
-
}
|
1621
|
-
}
|
2273
|
+
#else
|
1622
2274
|
|
1623
|
-
|
1624
|
-
|
1625
|
-
long prof_l = 0;
|
2275
|
+
const unsigned char *profile;
|
2276
|
+
size_t length;
|
1626
2277
|
|
1627
|
-
rm_check_frozen(self);
|
1628
2278
|
Data_Get_Struct(self, Image, image);
|
1629
2279
|
|
1630
|
-
|
2280
|
+
profile = GetImageProfile(image, "ICM", &length);
|
2281
|
+
if (!profile)
|
1631
2282
|
{
|
1632
|
-
|
1633
|
-
}
|
1634
|
-
else
|
1635
|
-
{
|
1636
|
-
prof = (unsigned char *)STRING_PTR_LEN(profile, prof_l);
|
1637
|
-
(void) SetImageProfile(image, "icc", prof, (size_t)prof_l);
|
2283
|
+
return Qnil;
|
1638
2284
|
}
|
1639
|
-
#endif /* defined(HAVE_SETIMAGEPROFILE) */
|
1640
2285
|
|
1641
|
-
|
1642
|
-
|
1643
|
-
char *prof = NULL;
|
1644
|
-
long prof_l = 0;
|
2286
|
+
return rb_str_new((char *)profile, (long)length);
|
1645
2287
|
|
1646
|
-
|
1647
|
-
|
2288
|
+
#endif
|
2289
|
+
}
|
1648
2290
|
|
2291
|
+
/*
|
2292
|
+
Method: Image#color_profile=(String)
|
2293
|
+
Purpose: Set the ICC color profile. The argument is a string.
|
2294
|
+
Notes: Pass nil to remove any existing profile.
|
2295
|
+
Removes any existing profile before adding the new one.
|
2296
|
+
*/
|
2297
|
+
VALUE
|
2298
|
+
Image_color_profile_eq(VALUE self, VALUE profile)
|
2299
|
+
{
|
2300
|
+
(void) Image_delete_profile(self, rb_str_new2("ICC"));
|
1649
2301
|
if (profile != Qnil)
|
1650
2302
|
{
|
1651
|
-
|
1652
|
-
}
|
1653
|
-
|
1654
|
-
magick_free(image->color_profile.info);
|
1655
|
-
image->color_profile.info = NULL;
|
1656
|
-
|
1657
|
-
if (prof_l > 0)
|
1658
|
-
{
|
1659
|
-
image->color_profile.info = magick_malloc((size_t)prof_l);
|
1660
|
-
memcpy(image->color_profile.info, prof, (size_t)prof_l);
|
1661
|
-
image->color_profile.length = prof_l;
|
2303
|
+
(void) set_profile(self, "ICC", profile);
|
1662
2304
|
}
|
1663
|
-
|
1664
|
-
#endif
|
1665
2305
|
return self;
|
1666
2306
|
}
|
1667
2307
|
|
@@ -2856,6 +3496,26 @@ Image_depth(VALUE self)
|
|
2856
3496
|
DEF_ATTR_ACCESSOR(Image, delay, ulong)
|
2857
3497
|
|
2858
3498
|
|
3499
|
+
/*
|
3500
|
+
Method: Image#delete_profile(name)
|
3501
|
+
Purpose: call ProfileImage
|
3502
|
+
Notes: name is the name of the profile to be deleted
|
3503
|
+
*/
|
3504
|
+
VALUE
|
3505
|
+
Image_delete_profile(VALUE self, VALUE name)
|
3506
|
+
{
|
3507
|
+
Image *image;
|
3508
|
+
|
3509
|
+
rm_check_frozen(self);
|
3510
|
+
Data_Get_Struct(self, Image, image);
|
3511
|
+
|
3512
|
+
(void) ProfileImage(image, STRING_PTR(name), NULL, 0, True);
|
3513
|
+
rm_check_image_exception(image, RetainOnError);
|
3514
|
+
|
3515
|
+
return self;
|
3516
|
+
}
|
3517
|
+
|
3518
|
+
|
2859
3519
|
/*
|
2860
3520
|
Method: Image#despeckle
|
2861
3521
|
Purpose: reduces the speckle noise in an image while preserving the
|
@@ -2912,6 +3572,53 @@ VALUE Image_difference(VALUE self, VALUE other)
|
|
2912
3572
|
DEF_ATTR_READER(Image, directory, str)
|
2913
3573
|
|
2914
3574
|
|
3575
|
+
/*
|
3576
|
+
Method: Image#displace(displacement_map, x_amp, y_amp, x_offset=0, y_offset=0)
|
3577
|
+
Image#displace(displacement_map, x_amp, y_amp, gravity, x_offset=0, y_offset=0)
|
3578
|
+
Purpose: Implement the -displace option of xMagick's composite command
|
3579
|
+
Notes: If y_amp is omitted the default is x_amp.
|
3580
|
+
*/
|
3581
|
+
VALUE
|
3582
|
+
Image_displace(int argc, VALUE *argv, VALUE self)
|
3583
|
+
{
|
3584
|
+
|
3585
|
+
Image *image, *displacement_map;
|
3586
|
+
double x_amplitude, y_amplitude;
|
3587
|
+
long x_offset = 0L, y_offset = 0L;
|
3588
|
+
|
3589
|
+
Data_Get_Struct(self, Image, image);
|
3590
|
+
|
3591
|
+
if (argc < 2)
|
3592
|
+
{
|
3593
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
3594
|
+
}
|
3595
|
+
|
3596
|
+
if (argc > 3)
|
3597
|
+
{
|
3598
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, displacement_map);
|
3599
|
+
get_composite_offsets(argc-3, &argv[3], image, displacement_map, &x_offset, &y_offset);
|
3600
|
+
// There must be 3 arguments left
|
3601
|
+
argc = 3;
|
3602
|
+
}
|
3603
|
+
|
3604
|
+
switch (argc)
|
3605
|
+
{
|
3606
|
+
case 3:
|
3607
|
+
y_amplitude = NUM2DBL(argv[2]);
|
3608
|
+
x_amplitude = NUM2DBL(argv[1]);
|
3609
|
+
break;
|
3610
|
+
case 2:
|
3611
|
+
x_amplitude = NUM2DBL(argv[1]);
|
3612
|
+
y_amplitude = x_amplitude;
|
3613
|
+
break;
|
3614
|
+
}
|
3615
|
+
|
3616
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, displacement_map);
|
3617
|
+
return special_composite(image, displacement_map, x_amplitude, y_amplitude
|
3618
|
+
, x_offset, y_offset, DisplaceCompositeOp);
|
3619
|
+
}
|
3620
|
+
|
3621
|
+
|
2915
3622
|
/*
|
2916
3623
|
Method: Image#dispatch(x, y, columns, rows, map <, float>)
|
2917
3624
|
Purpose: Extracts pixel data from the image and returns it as an
|
@@ -3044,23 +3751,127 @@ Image_dispose(VALUE self)
|
|
3044
3751
|
{
|
3045
3752
|
Image *image;
|
3046
3753
|
|
3047
|
-
Data_Get_Struct(self, Image, image);
|
3048
|
-
return DisposeType_new(image->dispose);
|
3049
|
-
}
|
3754
|
+
Data_Get_Struct(self, Image, image);
|
3755
|
+
return DisposeType_new(image->dispose);
|
3756
|
+
}
|
3757
|
+
|
3758
|
+
/*
|
3759
|
+
Method: Image#dispose=
|
3760
|
+
Purpose: Set the dispose attribute
|
3761
|
+
*/
|
3762
|
+
VALUE
|
3763
|
+
Image_dispose_eq(VALUE self, VALUE dispose)
|
3764
|
+
{
|
3765
|
+
Image *image;
|
3766
|
+
|
3767
|
+
rm_check_frozen(self);
|
3768
|
+
Data_Get_Struct(self, Image, image);
|
3769
|
+
VALUE_TO_ENUM(dispose, image->dispose, DisposeType);
|
3770
|
+
return self;
|
3771
|
+
}
|
3772
|
+
|
3773
|
+
|
3774
|
+
|
3775
|
+
#if defined(GRAPHICSMAGICK)
|
3776
|
+
/*
|
3777
|
+
Static: create_mattes
|
3778
|
+
Purpose: GraphicsMagick establishes the source image mattes in
|
3779
|
+
command.c, before calling CompositeImage. This function does
|
3780
|
+
that step for Image_dissolve when we're built for GraphicsMagick.
|
3781
|
+
*/
|
3782
|
+
static void
|
3783
|
+
create_mattes(Image *image, double src_percent)
|
3784
|
+
{
|
3785
|
+
long x, y;
|
3786
|
+
PixelPacket *q;
|
3787
|
+
|
3788
|
+
if (!image->matte)
|
3789
|
+
{
|
3790
|
+
SetImageOpacity(image,OpaqueOpacity);
|
3791
|
+
}
|
3792
|
+
|
3793
|
+
for (y = 0; y < (long) image->rows; y++)
|
3794
|
+
{
|
3795
|
+
q = GetImagePixels(image, 0, y, image->columns, 1);
|
3796
|
+
|
3797
|
+
if (q == NULL)
|
3798
|
+
{
|
3799
|
+
break;
|
3800
|
+
}
|
3801
|
+
|
3802
|
+
for (x = 0; x < (long) image->columns; x++)
|
3803
|
+
{
|
3804
|
+
q->opacity = (Quantum) (((MaxRGB - q->opacity) * src_percent) / 100.0);
|
3805
|
+
q += 1;
|
3806
|
+
}
|
3807
|
+
|
3808
|
+
if (!SyncImagePixels(image))
|
3809
|
+
{
|
3810
|
+
break;
|
3811
|
+
}
|
3812
|
+
}
|
3813
|
+
}
|
3814
|
+
#endif
|
3815
|
+
|
3816
|
+
/*
|
3817
|
+
Method: Image#dissolve(overlay, src_percent, dst_percent, x_offset=0, y_offset=0)
|
3818
|
+
Image#dissolve(overlay, src_percent, dst_percent, gravity, x_offset=0, y_offset=0)
|
3819
|
+
Purpose: Corresponds to the composite -dissolve operation
|
3820
|
+
Notes: `percent' can be a number or a string in the form "NN%"
|
3821
|
+
The "default" value of dst_percent is -1.0, which tells
|
3822
|
+
blend_geometry to leave it out of the geometry string.
|
3823
|
+
*/
|
3824
|
+
VALUE
|
3825
|
+
Image_dissolve(int argc, VALUE *argv, VALUE self)
|
3826
|
+
{
|
3827
|
+
Image *image, *overlay;
|
3828
|
+
double src_percent, dst_percent = -1.0;
|
3829
|
+
long x_offset = 0L, y_offset = 0L;
|
3830
|
+
volatile VALUE composite;
|
3831
|
+
|
3832
|
+
Data_Get_Struct(self, Image, image);
|
3833
|
+
|
3834
|
+
if (argc < 1)
|
3835
|
+
{
|
3836
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
3837
|
+
}
|
3838
|
+
|
3839
|
+
if (argc > 3)
|
3840
|
+
{
|
3841
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, overlay);
|
3842
|
+
get_composite_offsets(argc-3, &argv[3], image, overlay, &x_offset, &y_offset);
|
3843
|
+
// There must be 3 arguments left
|
3844
|
+
argc = 3;
|
3845
|
+
}
|
3846
|
+
|
3847
|
+
switch (argc)
|
3848
|
+
{
|
3849
|
+
case 3:
|
3850
|
+
dst_percent = rm_percentage(argv[2]) * 100.0;
|
3851
|
+
case 2:
|
3852
|
+
src_percent = rm_percentage(argv[1]) * 100.0;
|
3853
|
+
break;
|
3854
|
+
default:
|
3855
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
3856
|
+
break;
|
3857
|
+
}
|
3858
|
+
|
3859
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, overlay);
|
3050
3860
|
|
3051
|
-
|
3052
|
-
|
3053
|
-
|
3054
|
-
|
3055
|
-
|
3056
|
-
Image_dispose_eq(VALUE self, VALUE dispose)
|
3057
|
-
{
|
3058
|
-
Image *image;
|
3861
|
+
// GraphicsMagick needs an extra step (ref: GM's command.c)
|
3862
|
+
#if defined(GRAPHICSMAGICK)
|
3863
|
+
overlay = rm_clone_image(overlay);
|
3864
|
+
create_mattes(overlay, src_percent);
|
3865
|
+
#endif
|
3059
3866
|
|
3060
|
-
|
3061
|
-
|
3062
|
-
|
3063
|
-
|
3867
|
+
composite = special_composite(image, overlay, src_percent, dst_percent
|
3868
|
+
, x_offset, y_offset, DissolveCompositeOp);
|
3869
|
+
|
3870
|
+
#if defined(GRAPHICSMAGICK)
|
3871
|
+
DestroyImage(overlay);
|
3872
|
+
#endif
|
3873
|
+
|
3874
|
+
return composite;
|
3064
3875
|
}
|
3065
3876
|
|
3066
3877
|
|
@@ -3129,6 +3940,10 @@ Image__dump(VALUE self, VALUE depth)
|
|
3129
3940
|
Data_Get_Struct(self, Image, image);
|
3130
3941
|
|
3131
3942
|
info = CloneImageInfo(NULL);
|
3943
|
+
if (!info)
|
3944
|
+
{
|
3945
|
+
rb_raise(rb_eNoMemError, "not enough memory to continue");
|
3946
|
+
}
|
3132
3947
|
strcpy(info->magick, image->magick);
|
3133
3948
|
|
3134
3949
|
GetExceptionInfo(&exception);
|
@@ -3181,7 +3996,7 @@ Image_dup(VALUE self)
|
|
3181
3996
|
/*
|
3182
3997
|
Method: Image#each_profile
|
3183
3998
|
Purpose: Iterate over image profiles
|
3184
|
-
Notes:
|
3999
|
+
Notes: ImageMagick only
|
3185
4000
|
*/
|
3186
4001
|
VALUE
|
3187
4002
|
Image_each_profile(VALUE self)
|
@@ -3189,8 +4004,7 @@ Image_each_profile(VALUE self)
|
|
3189
4004
|
#if defined(HAVE_GETNEXTIMAGEPROFILE)
|
3190
4005
|
Image *image;
|
3191
4006
|
volatile VALUE ary, val;
|
3192
|
-
char *
|
3193
|
-
StringInfo *str_info;
|
4007
|
+
char *name;
|
3194
4008
|
|
3195
4009
|
Data_Get_Struct(self, Image, image);
|
3196
4010
|
|
@@ -3202,18 +4016,36 @@ Image_each_profile(VALUE self)
|
|
3202
4016
|
while (name)
|
3203
4017
|
{
|
3204
4018
|
rb_ary_store(ary, 0, rb_str_new2(name));
|
3205
|
-
|
3206
|
-
str_info = (StringInfo *)GetImageProfile(image, name);
|
3207
|
-
if (str_info)
|
4019
|
+
#if defined(HAVE_ACQUIRESTRINGINFO)
|
3208
4020
|
{
|
3209
|
-
|
3210
|
-
|
3211
|
-
|
4021
|
+
const StringInfo *profile;
|
4022
|
+
|
4023
|
+
profile = GetImageProfile(image, name);
|
4024
|
+
if (!profile)
|
4025
|
+
{
|
4026
|
+
rb_ary_store(ary, 1, Qnil);
|
4027
|
+
}
|
4028
|
+
else
|
4029
|
+
{
|
4030
|
+
rb_ary_store(ary, 1, rb_str_new((char *)profile->datum, (long)profile->length));
|
4031
|
+
}
|
3212
4032
|
}
|
3213
|
-
|
4033
|
+
#else
|
3214
4034
|
{
|
3215
|
-
|
4035
|
+
unsigned char *profile;
|
4036
|
+
size_t length;
|
4037
|
+
|
4038
|
+
profile = GetImageProfile(image, "iptc", &length);
|
4039
|
+
if (!profile)
|
4040
|
+
{
|
4041
|
+
rb_ary_store(ary, 1, Qnil);
|
4042
|
+
}
|
4043
|
+
else
|
4044
|
+
{
|
4045
|
+
rb_ary_store(ary, 1, rb_string_new((char *)profile, (long)length));
|
4046
|
+
}
|
3216
4047
|
}
|
4048
|
+
#endif
|
3217
4049
|
val = rb_yield(ary);
|
3218
4050
|
name = GetNextImageProfile(image);
|
3219
4051
|
}
|
@@ -3482,7 +4314,7 @@ Image_export_pixels(int argc, VALUE *argv, VALUE self)
|
|
3482
4314
|
CHECK_EXCEPTION()
|
3483
4315
|
|
3484
4316
|
// Should never get here...
|
3485
|
-
|
4317
|
+
rm_magick_error("ExportImagePixels failed with no explanation.", NULL);
|
3486
4318
|
}
|
3487
4319
|
|
3488
4320
|
DestroyExceptionInfo(&exception);
|
@@ -3606,7 +4438,7 @@ Image_export_pixels_to_str(int argc, VALUE *argv, VALUE self)
|
|
3606
4438
|
CHECK_EXCEPTION()
|
3607
4439
|
|
3608
4440
|
// Should never get here...
|
3609
|
-
|
4441
|
+
rm_magick_error("ExportImagePixels failed with no explanation.", NULL);
|
3610
4442
|
}
|
3611
4443
|
|
3612
4444
|
DestroyExceptionInfo(&exception);
|
@@ -3695,6 +4527,60 @@ Image_filter_eq(VALUE self, VALUE filter)
|
|
3695
4527
|
}
|
3696
4528
|
|
3697
4529
|
|
4530
|
+
/*
|
4531
|
+
* Method: Image#find_similar_region(target, x=0, y=0)
|
4532
|
+
* Purpose: Search for a region in the image that is "similar" to the
|
4533
|
+
* target image.
|
4534
|
+
*/
|
4535
|
+
VALUE
|
4536
|
+
Image_find_similar_region(int argc, VALUE *argv, VALUE self)
|
4537
|
+
{
|
4538
|
+
#if defined(HAVE_ISIMAGESIMILAR)
|
4539
|
+
Image *image, *target;
|
4540
|
+
volatile VALUE region;
|
4541
|
+
long x = 0L, y = 0L;
|
4542
|
+
ExceptionInfo exception;
|
4543
|
+
unsigned int okay;
|
4544
|
+
|
4545
|
+
Data_Get_Struct(self, Image, image);
|
4546
|
+
|
4547
|
+
switch (argc)
|
4548
|
+
{
|
4549
|
+
case 3:
|
4550
|
+
y = NUM2LONG(argv[2]);
|
4551
|
+
case 2:
|
4552
|
+
x = NUM2LONG(argv[1]);
|
4553
|
+
case 1:
|
4554
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, target);
|
4555
|
+
break;
|
4556
|
+
default:
|
4557
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 to 3)", argc);
|
4558
|
+
break;
|
4559
|
+
}
|
4560
|
+
|
4561
|
+
GetExceptionInfo(&exception);
|
4562
|
+
okay = IsImageSimilar(image, target, &x, &y, &exception);
|
4563
|
+
CHECK_EXCEPTION();
|
4564
|
+
DestroyExceptionInfo(&exception);
|
4565
|
+
|
4566
|
+
if (!okay)
|
4567
|
+
{
|
4568
|
+
return Qnil;
|
4569
|
+
}
|
4570
|
+
|
4571
|
+
region = rb_ary_new2(2);
|
4572
|
+
rb_ary_store(region, 0L, LONG2NUM(x));
|
4573
|
+
rb_ary_store(region, 1L, LONG2NUM(y));
|
4574
|
+
|
4575
|
+
return region;
|
4576
|
+
|
4577
|
+
#else
|
4578
|
+
rm_not_implemented();
|
4579
|
+
return (VALUE)0;
|
4580
|
+
#endif
|
4581
|
+
}
|
4582
|
+
|
4583
|
+
|
3698
4584
|
/*
|
3699
4585
|
Method: Image#flip
|
3700
4586
|
Image#flip!
|
@@ -3995,17 +4881,17 @@ Image_gamma_channel(int argc, VALUE *argv, VALUE self)
|
|
3995
4881
|
|
3996
4882
|
|
3997
4883
|
/*
|
3998
|
-
Method: Image#gamma_correct(red_gamma<, green_gamma<, blue_gamma
|
3999
|
-
<, opacity_gamma>>>)
|
4884
|
+
Method: Image#gamma_correct(red_gamma<, green_gamma<, blue_gamma>>>)
|
4000
4885
|
Purpose: gamma-correct an image
|
4001
4886
|
Notes: At least red_gamma must be specified. If one or more levels are
|
4002
4887
|
omitted, the last specified number is used as the default.
|
4888
|
+
For backward compatibility accept a 4th argument but ignore it.
|
4003
4889
|
*/
|
4004
4890
|
VALUE
|
4005
4891
|
Image_gamma_correct(int argc, VALUE *argv, VALUE self)
|
4006
4892
|
{
|
4007
4893
|
Image *image, *new_image;
|
4008
|
-
double red_gamma, green_gamma, blue_gamma
|
4894
|
+
double red_gamma, green_gamma, blue_gamma;
|
4009
4895
|
char gamma[50];
|
4010
4896
|
|
4011
4897
|
switch(argc)
|
@@ -4019,31 +4905,25 @@ Image_gamma_correct(int argc, VALUE *argv, VALUE self)
|
|
4019
4905
|
{
|
4020
4906
|
rb_raise(rb_eArgError, "invalid gamma value (%f)", red_gamma);
|
4021
4907
|
}
|
4022
|
-
green_gamma = blue_gamma =
|
4908
|
+
green_gamma = blue_gamma = red_gamma;
|
4023
4909
|
break;
|
4024
4910
|
case 2:
|
4025
4911
|
red_gamma = NUM2DBL(argv[0]);
|
4026
4912
|
green_gamma = NUM2DBL(argv[1]);
|
4027
|
-
blue_gamma =
|
4913
|
+
blue_gamma = green_gamma;
|
4028
4914
|
break;
|
4029
4915
|
case 3:
|
4030
|
-
red_gamma = NUM2DBL(argv[0]);
|
4031
|
-
green_gamma = NUM2DBL(argv[1]);
|
4032
|
-
blue_gamma = NUM2DBL(argv[2]);
|
4033
|
-
opacity_gamma = blue_gamma;
|
4034
|
-
break;
|
4035
4916
|
case 4:
|
4036
4917
|
red_gamma = NUM2DBL(argv[0]);
|
4037
4918
|
green_gamma = NUM2DBL(argv[1]);
|
4038
4919
|
blue_gamma = NUM2DBL(argv[2]);
|
4039
|
-
opacity_gamma = NUM2DBL(argv[3]);
|
4040
4920
|
break;
|
4041
4921
|
default:
|
4042
|
-
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 to
|
4922
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 to 3)", argc);
|
4043
4923
|
break;
|
4044
4924
|
}
|
4045
4925
|
|
4046
|
-
sprintf(gamma, "%f,%f,%f
|
4926
|
+
sprintf(gamma, "%f,%f,%f", red_gamma, green_gamma, blue_gamma);
|
4047
4927
|
Data_Get_Struct(self, Image, image);
|
4048
4928
|
|
4049
4929
|
new_image = rm_clone_image(image);
|
@@ -4501,7 +5381,7 @@ Image_import_pixels(int argc, VALUE *argv, VALUE self)
|
|
4501
5381
|
{
|
4502
5382
|
rm_check_image_exception(image, RetainOnError);
|
4503
5383
|
// Shouldn't get here...
|
4504
|
-
|
5384
|
+
rm_magick_error("ImportImagePixels failed with no explanation.", NULL);
|
4505
5385
|
}
|
4506
5386
|
|
4507
5387
|
return self;
|
@@ -4643,6 +5523,7 @@ Image_inspect(VALUE self)
|
|
4643
5523
|
return rb_str_new2(buffer);
|
4644
5524
|
}
|
4645
5525
|
|
5526
|
+
|
4646
5527
|
/*
|
4647
5528
|
Method: Image#interlace
|
4648
5529
|
Purpose: get the interlace attribute
|
@@ -4657,6 +5538,7 @@ Image_interlace(VALUE self)
|
|
4657
5538
|
return InterlaceType_new(image->interlace);
|
4658
5539
|
}
|
4659
5540
|
|
5541
|
+
|
4660
5542
|
/*
|
4661
5543
|
Method: Image#interlace=
|
4662
5544
|
Purpose: set the interlace attribute
|
@@ -4672,81 +5554,50 @@ Image_interlace_eq(VALUE self, VALUE interlace)
|
|
4672
5554
|
return self;
|
4673
5555
|
}
|
4674
5556
|
|
5557
|
+
|
4675
5558
|
/*
|
4676
5559
|
Method: Image#iptc_profile
|
4677
5560
|
Purpose: Return the IPTC profile as a String.
|
4678
|
-
Notes: If there is no profile, returns
|
5561
|
+
Notes: If there is no profile, returns Qnil
|
4679
5562
|
*/
|
4680
5563
|
VALUE
|
4681
5564
|
Image_iptc_profile(VALUE self)
|
4682
5565
|
{
|
4683
5566
|
Image *image;
|
4684
|
-
volatile VALUE profile;
|
4685
|
-
|
4686
|
-
#if defined(HAVE_GETIMAGEPROFILE)
|
4687
|
-
/* Both IM 6.0.0 and GM 1.1. define GetImageProfile */
|
4688
|
-
/* but the implementations are different. IM 6.0.0 */
|
4689
|
-
/* uses a StringInfo type. That's our feature test. */
|
4690
5567
|
|
4691
5568
|
#if defined(HAVE_ACQUIRESTRINGINFO)
|
4692
|
-
StringInfo *
|
4693
|
-
char *str;
|
5569
|
+
const StringInfo *profile;
|
4694
5570
|
|
4695
5571
|
Data_Get_Struct(self, Image, image);
|
4696
5572
|
|
4697
|
-
profile =
|
4698
|
-
|
4699
|
-
str_info = (StringInfo *)GetImageProfile(image, "iptc");
|
4700
|
-
if (str_info)
|
5573
|
+
profile = GetImageProfile(image, "iptc");
|
5574
|
+
if (!profile)
|
4701
5575
|
{
|
4702
|
-
|
4703
|
-
profile = rb_str_new2(str);
|
4704
|
-
DestroyString(str);
|
5576
|
+
return Qnil;
|
4705
5577
|
}
|
4706
5578
|
|
4707
|
-
|
4708
|
-
const unsigned char *prof;
|
4709
|
-
size_t length;
|
4710
|
-
|
4711
|
-
Data_Get_Struct(self, Image, image);
|
4712
|
-
|
4713
|
-
profile = Qnil; /* Assume no profile defined */
|
4714
|
-
|
4715
|
-
prof = GetImageProfile(image, "iptc", &length);
|
4716
|
-
if (prof)
|
4717
|
-
{
|
4718
|
-
profile = rb_str_new((char *)prof, (long) length);
|
4719
|
-
}
|
4720
|
-
#endif
|
5579
|
+
return rb_str_new((char *)profile->datum, (long)profile->length);
|
4721
5580
|
|
4722
5581
|
#else
|
4723
5582
|
|
5583
|
+
const unsigned char *profile;
|
5584
|
+
size_t length;
|
5585
|
+
|
4724
5586
|
Data_Get_Struct(self, Image, image);
|
4725
5587
|
|
4726
|
-
|
4727
|
-
|
4728
|
-
if (image->iptc_profile.info == NULL)
|
4729
|
-
{
|
4730
|
-
image->iptc_profile.length = 0;
|
4731
|
-
}
|
4732
|
-
else if (image->iptc_profile.length == 0
|
4733
|
-
&& image->iptc_profile.info)
|
5588
|
+
profile = GetImageProfile(image, "iptc", &length);
|
5589
|
+
if (!profile)
|
4734
5590
|
{
|
4735
|
-
|
4736
|
-
image->iptc_profile.info = NULL;
|
5591
|
+
return Qnil;
|
4737
5592
|
}
|
4738
5593
|
|
4739
|
-
|
4740
|
-
{
|
4741
|
-
profile = Qnil;
|
4742
|
-
}
|
4743
|
-
profile = rb_str_new((const char *)image->iptc_profile.info
|
4744
|
-
, image->iptc_profile.length);
|
4745
|
-
#endif
|
5594
|
+
return rb_str_new((char *)profile, (long)length);
|
4746
5595
|
|
4747
|
-
|
5596
|
+
#endif
|
4748
5597
|
}
|
4749
5598
|
|
5599
|
+
|
5600
|
+
|
4750
5601
|
/*
|
4751
5602
|
Method: Image#iptc_profile=(String)
|
4752
5603
|
Purpose: Set the IPTC profile. The argument is a string.
|
@@ -4755,90 +5606,11 @@ Image_iptc_profile(VALUE self)
|
|
4755
5606
|
VALUE
|
4756
5607
|
Image_iptc_profile_eq(VALUE self, VALUE profile)
|
4757
5608
|
{
|
4758
|
-
|
4759
|
-
|
4760
|
-
#if defined(HAVE_GETIMAGEPROFILE)
|
4761
|
-
/* Both IM 6.0.0 and GM 1.1. define GetImageProfile */
|
4762
|
-
/* but the implementations are different. IM 6.0.0 */
|
4763
|
-
/* uses a StringInfo type. That's our feature test. */
|
4764
|
-
|
4765
|
-
#if defined(HAVE_ACQUIRESTRINGINFO)
|
4766
|
-
StringInfo *str_info;
|
4767
|
-
unsigned int status = True;
|
4768
|
-
|
4769
|
-
rm_check_frozen(self);
|
4770
|
-
Data_Get_Struct(self, Image, image);
|
4771
|
-
|
4772
|
-
if (profile == Qnil)
|
4773
|
-
{
|
4774
|
-
#if defined(HAVE_NEW_REMOVEIMAGEPROFILE)
|
4775
|
-
(void)RemoveImageProfile(image, "iptc");
|
4776
|
-
#else
|
4777
|
-
str_info = RemoveImageProfile(image, "iptc");
|
4778
|
-
if(str_info)
|
4779
|
-
{
|
4780
|
-
DestroyStringInfo(str_info);
|
4781
|
-
}
|
4782
|
-
#endif
|
4783
|
-
}
|
4784
|
-
else
|
4785
|
-
{
|
4786
|
-
str_info = StringToStringInfo(STRING_PTR(profile));
|
4787
|
-
if (str_info)
|
4788
|
-
{
|
4789
|
-
if (str_info->length > 0)
|
4790
|
-
{
|
4791
|
-
status = SetImageProfile(image, "iptc", str_info);
|
4792
|
-
}
|
4793
|
-
|
4794
|
-
DestroyStringInfo(str_info);
|
4795
|
-
|
4796
|
-
if(!status)
|
4797
|
-
{
|
4798
|
-
rb_raise(rb_eNoMemError, "not enough memory to continue");
|
4799
|
-
}
|
4800
|
-
}
|
4801
|
-
}
|
4802
|
-
#else /* !defined(HAVE_ACQUIRESTRINGINFO) */
|
4803
|
-
const unsigned char *prof = NULL;
|
4804
|
-
long prof_l = 0;
|
4805
|
-
|
4806
|
-
rm_check_frozen(self);
|
4807
|
-
Data_Get_Struct(self, Image, image);
|
4808
|
-
|
4809
|
-
if (profile == Qnil)
|
4810
|
-
{
|
4811
|
-
(void) SetImageProfile(image, "iptc", NULL, 0);
|
4812
|
-
}
|
4813
|
-
else
|
4814
|
-
{
|
4815
|
-
prof = (unsigned char *)STRING_PTR_LEN(profile, prof_l);
|
4816
|
-
(void) SetImageProfile(image, "iptc", prof, (size_t)prof_l);
|
4817
|
-
}
|
4818
|
-
#endif
|
4819
|
-
|
4820
|
-
#else
|
4821
|
-
|
4822
|
-
char *prof = NULL;
|
4823
|
-
long prof_l = 0;
|
4824
|
-
|
4825
|
-
rm_check_frozen(self);
|
4826
|
-
Data_Get_Struct(self, Image, image);
|
4827
|
-
|
5609
|
+
(void) Image_delete_profile(self, rb_str_new2("IPTC"));
|
4828
5610
|
if (profile != Qnil)
|
4829
5611
|
{
|
4830
|
-
|
5612
|
+
(void) set_profile(self, "IPTC", profile);
|
4831
5613
|
}
|
4832
|
-
magick_free(image->iptc_profile.info);
|
4833
|
-
image->iptc_profile.info = NULL;
|
4834
|
-
if (prof_l > 0)
|
4835
|
-
{
|
4836
|
-
image->iptc_profile.info = magick_malloc((size_t)prof_l);
|
4837
|
-
memcpy(image->iptc_profile.info, prof, (size_t)prof_l);
|
4838
|
-
image->iptc_profile.length = (size_t) prof_l;
|
4839
|
-
}
|
4840
|
-
|
4841
|
-
#endif
|
4842
5614
|
return self;
|
4843
5615
|
}
|
4844
5616
|
|
@@ -5060,44 +5832,172 @@ Image_magnify_bang(VALUE self)
|
|
5060
5832
|
return magnify(True, self, MagnifyImage);
|
5061
5833
|
}
|
5062
5834
|
|
5063
|
-
/*
|
5064
|
-
Method: Image#map(map_image, dither=false)
|
5065
|
-
Purpose: Call MapImage
|
5066
|
-
Returns: a new image
|
5067
|
-
*/
|
5835
|
+
/*
|
5836
|
+
Method: Image#map(map_image, dither=false)
|
5837
|
+
Purpose: Call MapImage
|
5838
|
+
Returns: a new image
|
5839
|
+
*/
|
5840
|
+
|
5841
|
+
VALUE
|
5842
|
+
Image_map(int argc, VALUE *argv, VALUE self)
|
5843
|
+
{
|
5844
|
+
Image *image, *new_image;
|
5845
|
+
Image *map;
|
5846
|
+
volatile VALUE map_obj, map_arg;
|
5847
|
+
unsigned int dither = False;
|
5848
|
+
|
5849
|
+
switch (argc)
|
5850
|
+
{
|
5851
|
+
case 2:
|
5852
|
+
dither = RTEST(argv[1]);
|
5853
|
+
case 1:
|
5854
|
+
map_arg = argv[0];
|
5855
|
+
break;
|
5856
|
+
default:
|
5857
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
|
5858
|
+
break;
|
5859
|
+
}
|
5860
|
+
|
5861
|
+
|
5862
|
+
Data_Get_Struct(self, Image, image);
|
5863
|
+
new_image = rm_clone_image(image);
|
5864
|
+
|
5865
|
+
map_obj = ImageList_cur_image(map_arg);
|
5866
|
+
Data_Get_Struct(map_obj, Image, map);
|
5867
|
+
(void) MapImage(new_image, map, dither);
|
5868
|
+
rm_check_image_exception(new_image, DestroyOnError);
|
5869
|
+
|
5870
|
+
return rm_image_new(new_image);
|
5871
|
+
}
|
5872
|
+
|
5873
|
+
|
5874
|
+
/*
|
5875
|
+
Method: Image#mask
|
5876
|
+
Purpose: Return the image's clip mask, or nil if it doesn't have a clip
|
5877
|
+
mask.
|
5878
|
+
Notes: Distinguish from Image#clip_mask
|
5879
|
+
*/
|
5880
|
+
VALUE
|
5881
|
+
Image_mask(VALUE self)
|
5882
|
+
{
|
5883
|
+
Image *image, *mask;
|
5884
|
+
ExceptionInfo exception;
|
5885
|
+
|
5886
|
+
Data_Get_Struct(self, Image, image);
|
5887
|
+
|
5888
|
+
GetExceptionInfo(&exception);
|
5889
|
+
|
5890
|
+
#if defined(HAVE_GETIMAGECLIPMASK)
|
5891
|
+
|
5892
|
+
// The returned clip mask is a clone, ours to keep.
|
5893
|
+
mask = GetImageClipMask(image, &exception);
|
5894
|
+
rm_check_exception(&exception, mask, DestroyOnError);
|
5895
|
+
|
5896
|
+
#else
|
5897
|
+
mask = image->clip_mask;
|
5898
|
+
#endif
|
5899
|
+
|
5900
|
+
DestroyExceptionInfo(&exception);
|
5901
|
+
|
5902
|
+
return mask ? rm_image_new(mask) : Qnil;
|
5903
|
+
}
|
5904
|
+
|
5905
|
+
|
5906
|
+
/*
|
5907
|
+
Method: Image#mask=(mask-image)
|
5908
|
+
Purpose: associates a clip mask with the image
|
5909
|
+
Notes: pass "nil" for the mask-image to remove the current clip mask.
|
5910
|
+
If the clip mask is not the same size as the target image,
|
5911
|
+
resizes the clip mask to match the target.
|
5912
|
+
Notes: Distinguish from Image#clip_mask=
|
5913
|
+
*/
|
5914
|
+
VALUE
|
5915
|
+
Image_mask_eq(VALUE self, VALUE mask)
|
5916
|
+
{
|
5917
|
+
Image *image, *mask_image, *resized_image;
|
5918
|
+
Image *clip_mask;
|
5919
|
+
long x, y;
|
5920
|
+
PixelPacket *q;
|
5921
|
+
ExceptionInfo exception;
|
5922
|
+
|
5923
|
+
rm_check_frozen(self);
|
5924
|
+
Data_Get_Struct(self, Image, image);
|
5925
|
+
|
5926
|
+
if (mask != Qnil)
|
5927
|
+
{
|
5928
|
+
Data_Get_Struct(ImageList_cur_image(mask), Image, mask_image);
|
5929
|
+
clip_mask = rm_clone_image(mask_image);
|
5930
|
+
|
5931
|
+
// Resize if necessary
|
5932
|
+
if (clip_mask->columns != image->columns || clip_mask->rows != image->rows)
|
5933
|
+
{
|
5934
|
+
GetExceptionInfo(&exception);
|
5935
|
+
resized_image = ResizeImage(clip_mask, image->columns, image->rows
|
5936
|
+
, UndefinedFilter, 0.0, &exception);
|
5937
|
+
rm_check_exception(&exception, resized_image, DestroyOnError);
|
5938
|
+
DestroyExceptionInfo(&exception);
|
5939
|
+
rm_ensure_result(resized_image);
|
5940
|
+
(void) DestroyImage(clip_mask);
|
5941
|
+
clip_mask = resized_image;
|
5942
|
+
}
|
5943
|
+
|
5944
|
+
// The following section is copied from mogrify.c (6.2.8-8)
|
5945
|
+
for (y = 0; y < (long) clip_mask->rows; y++)
|
5946
|
+
{
|
5947
|
+
q = GetImagePixels(clip_mask, 0, y, clip_mask->columns, 1);
|
5948
|
+
if (!q)
|
5949
|
+
{
|
5950
|
+
break;
|
5951
|
+
}
|
5952
|
+
for (x = 0; x < (long) clip_mask->columns; x++)
|
5953
|
+
{
|
5954
|
+
if (clip_mask->matte == False)
|
5955
|
+
{
|
5956
|
+
q->opacity = PIXEL_INTENSITY(q);
|
5957
|
+
}
|
5958
|
+
q->red = q->opacity;
|
5959
|
+
q->green = q->opacity;
|
5960
|
+
q->blue = q->opacity;
|
5961
|
+
q += 1;
|
5962
|
+
}
|
5963
|
+
if (SyncImagePixels(clip_mask) == False)
|
5964
|
+
{
|
5965
|
+
(void) DestroyImage(clip_mask);
|
5966
|
+
rm_magick_error("SyncImagePixels failed", NULL);
|
5967
|
+
}
|
5968
|
+
}
|
5969
|
+
|
5970
|
+
#if defined(HAVE_SETIMAGESTORAGECLASS)
|
5971
|
+
if (SetImageStorageClass(clip_mask, DirectClass) == False)
|
5972
|
+
{
|
5973
|
+
(void) DestroyImage(clip_mask);
|
5974
|
+
rm_magick_error("SetImageStorageClass failed", NULL);
|
5975
|
+
}
|
5976
|
+
#else
|
5977
|
+
if (clip_mask->storage_class == PseudoClass)
|
5978
|
+
{
|
5979
|
+
SyncImage(image);
|
5980
|
+
clip_mask->storage_class = DirectClass;
|
5981
|
+
}
|
5982
|
+
#endif
|
5983
|
+
|
5984
|
+
clip_mask->matte = True;
|
5068
5985
|
|
5069
|
-
|
5070
|
-
|
5071
|
-
{
|
5072
|
-
Image *image, *new_image;
|
5073
|
-
Image *map;
|
5074
|
-
volatile VALUE map_obj, map_arg;
|
5075
|
-
unsigned int dither = False;
|
5986
|
+
// SetImageClipMask clones the clip_mask image. We can
|
5987
|
+
// destroy our copy after SetImageClipMask is done with it.
|
5076
5988
|
|
5077
|
-
|
5989
|
+
(void) SetImageClipMask(image, clip_mask);
|
5990
|
+
(void) DestroyImage(clip_mask);
|
5991
|
+
}
|
5992
|
+
else
|
5078
5993
|
{
|
5079
|
-
|
5080
|
-
dither = RTEST(argv[1]);
|
5081
|
-
case 1:
|
5082
|
-
map_arg = argv[0];
|
5083
|
-
break;
|
5084
|
-
default:
|
5085
|
-
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
|
5086
|
-
break;
|
5994
|
+
(void) SetImageClipMask(image, NULL);
|
5087
5995
|
}
|
5088
5996
|
|
5089
|
-
|
5090
|
-
Data_Get_Struct(self, Image, image);
|
5091
|
-
new_image = rm_clone_image(image);
|
5092
|
-
|
5093
|
-
map_obj = ImageList_cur_image(map_arg);
|
5094
|
-
Data_Get_Struct(map_obj, Image, map);
|
5095
|
-
(void) MapImage(new_image, map, dither);
|
5096
|
-
rm_check_image_exception(new_image, DestroyOnError);
|
5097
|
-
|
5098
|
-
return rm_image_new(new_image);
|
5997
|
+
return self;
|
5099
5998
|
}
|
5100
5999
|
|
6000
|
+
|
5101
6001
|
DEF_ATTR_ACCESSOR(Image, matte, bool)
|
5102
6002
|
|
5103
6003
|
/*
|
@@ -5400,37 +6300,48 @@ Image_montage_eq(
|
|
5400
6300
|
return self;
|
5401
6301
|
}
|
5402
6302
|
|
6303
|
+
|
5403
6304
|
/*
|
5404
|
-
|
5405
|
-
Purpose:
|
5406
|
-
operator of the given radius and standard deviation (sigma).
|
5407
|
-
For reasonable results, radius should be larger than sigma.
|
5408
|
-
Use a radius of 0 and motion_blur selects a suitable radius
|
5409
|
-
for you. Angle gives the angle of the blurring motion.
|
6305
|
+
Static: motion_blur(int argc, VALUE *argv, VALUE self, magick_api)
|
6306
|
+
Purpose: called from Image_motion_blur and Image_sketch
|
5410
6307
|
*/
|
5411
|
-
VALUE
|
5412
|
-
|
6308
|
+
static VALUE
|
6309
|
+
motion_blur(
|
6310
|
+
int argc,
|
6311
|
+
VALUE *argv,
|
5413
6312
|
VALUE self,
|
5414
|
-
|
5415
|
-
VALUE sigma_arg,
|
5416
|
-
VALUE angle_arg)
|
6313
|
+
Image *fp(const Image *, const double, const double, const double, ExceptionInfo *))
|
5417
6314
|
{
|
5418
6315
|
Image *image, *new_image;
|
5419
|
-
double radius
|
6316
|
+
double radius = 0.0;
|
6317
|
+
double sigma = 1.0;
|
6318
|
+
double angle = 0.0;
|
5420
6319
|
ExceptionInfo exception;
|
5421
6320
|
|
5422
|
-
|
5423
|
-
|
5424
|
-
|
5425
|
-
|
6321
|
+
switch (argc)
|
6322
|
+
{
|
6323
|
+
case 3:
|
6324
|
+
angle = NUM2DBL(argv[2]);
|
6325
|
+
case 2:
|
6326
|
+
sigma = NUM2DBL(argv[1]);
|
6327
|
+
case 1:
|
6328
|
+
radius = NUM2DBL(argv[0]);
|
6329
|
+
case 0:
|
6330
|
+
break;
|
6331
|
+
default:
|
6332
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 to 3)", argc);
|
6333
|
+
break;
|
6334
|
+
}
|
5426
6335
|
|
5427
6336
|
if (sigma == 0.0)
|
5428
6337
|
{
|
5429
6338
|
rb_raise(rb_eArgError, "sigma must be != 0.0");
|
5430
6339
|
}
|
5431
6340
|
|
6341
|
+
Data_Get_Struct(self, Image, image);
|
6342
|
+
|
5432
6343
|
GetExceptionInfo(&exception);
|
5433
|
-
new_image =
|
6344
|
+
new_image = (fp)(image, radius, sigma, angle, &exception);
|
5434
6345
|
rm_check_exception(&exception, new_image, DestroyOnError);
|
5435
6346
|
|
5436
6347
|
DestroyExceptionInfo(&exception);
|
@@ -5440,6 +6351,22 @@ Image_motion_blur(
|
|
5440
6351
|
return rm_image_new(new_image);
|
5441
6352
|
}
|
5442
6353
|
|
6354
|
+
|
6355
|
+
/*
|
6356
|
+
Method: Image#motion_blur(radius=0.0, sigma=1.0, angle=0.0)
|
6357
|
+
Purpose: simulates motion blur. Convolves the image with a Gaussian
|
6358
|
+
operator of the given radius and standard deviation (sigma).
|
6359
|
+
For reasonable results, radius should be larger than sigma.
|
6360
|
+
Use a radius of 0 and motion_blur selects a suitable radius
|
6361
|
+
for you. Angle gives the angle of the blurring motion.
|
6362
|
+
*/
|
6363
|
+
VALUE
|
6364
|
+
Image_motion_blur(int argc, VALUE *argv, VALUE self)
|
6365
|
+
{
|
6366
|
+
return motion_blur(argc, argv, self, MotionBlurImage);
|
6367
|
+
}
|
6368
|
+
|
6369
|
+
|
5443
6370
|
/*
|
5444
6371
|
Method: Image#negate(grayscale=false)
|
5445
6372
|
Purpose: negates the colors in the reference image. The grayscale option
|
@@ -5517,8 +6444,7 @@ Image_negate_channel(int argc, VALUE *argv, VALUE self)
|
|
5517
6444
|
/*
|
5518
6445
|
Method: Image.new(cols, rows<, fill>) <{info block}>
|
5519
6446
|
Purpose: Create a new Image with "cols" columns and "rows" rows.
|
5520
|
-
If the fill argument is omitted,
|
5521
|
-
using the background color
|
6447
|
+
If the fill argument is omitted, fill with the background color
|
5522
6448
|
Returns: A new Image
|
5523
6449
|
Note: This routine creates an Info structure to use when allocating
|
5524
6450
|
the Image structure. The caller can supply an info parm block to
|
@@ -6116,6 +7042,47 @@ Image_pixel_color(
|
|
6116
7042
|
return Pixel_from_PixelPacket(&old_color);
|
6117
7043
|
}
|
6118
7044
|
|
7045
|
+
|
7046
|
+
/*
|
7047
|
+
Method: Image.pixel_interpolation_method
|
7048
|
+
Image.pixel_interpolation_method=method
|
7049
|
+
Purpose: Get/set the "interpolate" field in the Image structure.
|
7050
|
+
Ref: Image.interpolate_pixel_color
|
7051
|
+
*/
|
7052
|
+
VALUE
|
7053
|
+
Image_pixel_interpolation_method(VALUE self)
|
7054
|
+
{
|
7055
|
+
#if defined(HAVE_INTERPOLATEPIXELCOLOR)
|
7056
|
+
Image *image;
|
7057
|
+
|
7058
|
+
Data_Get_Struct(self, Image, image);
|
7059
|
+
return InterpolatePixelMethod_new(image->interpolate);
|
7060
|
+
|
7061
|
+
#else
|
7062
|
+
rm_not_implemented();
|
7063
|
+
return (VALUE)0;
|
7064
|
+
#endif
|
7065
|
+
}
|
7066
|
+
|
7067
|
+
|
7068
|
+
VALUE
|
7069
|
+
Image_pixel_interpolation_method_eq(VALUE self, VALUE method)
|
7070
|
+
{
|
7071
|
+
#if defined(HAVE_INTERPOLATEPIXELCOLOR)
|
7072
|
+
Image *image;
|
7073
|
+
|
7074
|
+
rm_check_frozen(self);
|
7075
|
+
Data_Get_Struct(self, Image, image);
|
7076
|
+
VALUE_TO_ENUM(method, image->interpolate, InterpolatePixelMethod);
|
7077
|
+
return self;
|
7078
|
+
|
7079
|
+
#else
|
7080
|
+
rm_not_implemented();
|
7081
|
+
return (VALUE)0;
|
7082
|
+
#endif
|
7083
|
+
}
|
7084
|
+
|
7085
|
+
|
6119
7086
|
#if 0
|
6120
7087
|
/*
|
6121
7088
|
Method: Image.plasma(x1, y1, x2, y2, attenuate, depth)
|
@@ -6234,36 +7201,26 @@ Image_preview(VALUE self, VALUE preview)
|
|
6234
7201
|
|
6235
7202
|
/*
|
6236
7203
|
Method: Image#profile!(name, profile)
|
6237
|
-
Purpose:
|
6238
|
-
|
6239
|
-
History: added 'True' value for 'clone' argument for IM 5.4.7
|
7204
|
+
Purpose: If "profile" is nil, deletes the profile. Otherwise "profile"
|
7205
|
+
must be a string containing the specified profile.
|
6240
7206
|
*/
|
6241
7207
|
VALUE
|
6242
|
-
Image_profile_bang(
|
6243
|
-
VALUE self,
|
6244
|
-
VALUE name,
|
6245
|
-
VALUE profile)
|
7208
|
+
Image_profile_bang(VALUE self, VALUE name, VALUE profile)
|
6246
7209
|
{
|
6247
|
-
Image *image;
|
6248
|
-
char *prof = NULL;
|
6249
|
-
long prof_l = 0;
|
6250
|
-
|
6251
|
-
rm_check_frozen(self);
|
6252
|
-
Data_Get_Struct(self, Image, image);
|
6253
7210
|
|
6254
|
-
|
6255
|
-
if (profile != Qnil)
|
7211
|
+
if (profile == Qnil)
|
6256
7212
|
{
|
6257
|
-
|
7213
|
+
return Image_delete_profile(self, name);
|
7214
|
+
}
|
7215
|
+
else
|
7216
|
+
{
|
7217
|
+
return set_profile(self, STRING_PTR(name), profile);
|
6258
7218
|
}
|
6259
|
-
(void) ProfileImage(image, STRING_PTR(name), (const unsigned char *)prof
|
6260
|
-
, (size_t)prof_l, True);
|
6261
|
-
rm_check_image_exception(image, RetainOnError);
|
6262
7219
|
|
6263
|
-
return self;
|
6264
7220
|
}
|
6265
7221
|
|
6266
7222
|
|
7223
|
+
|
6267
7224
|
#if defined(HAVE_IMAGE_QUALITY)
|
6268
7225
|
DEF_ATTR_READER(Image, quality, ulong)
|
6269
7226
|
#endif
|
@@ -6440,6 +7397,14 @@ Image_quantum_operator(int argc, VALUE *argv, VALUE self)
|
|
6440
7397
|
case LShiftQuantumOperator:
|
6441
7398
|
qop = LeftShiftEvaluateOperator;
|
6442
7399
|
break;
|
7400
|
+
#if defined(HAVE_MAXEVALUATEOPERATOR)
|
7401
|
+
case MaxQuantumOperator:
|
7402
|
+
qop = MaxEvaluateOperator;
|
7403
|
+
break;
|
7404
|
+
case MinQuantumOperator:
|
7405
|
+
qop = MinEvaluateOperator;
|
7406
|
+
break;
|
7407
|
+
#endif
|
6443
7408
|
case MultiplyQuantumOperator:
|
6444
7409
|
qop = MultiplyEvaluateOperator;
|
6445
7410
|
break;
|
@@ -6458,7 +7423,7 @@ Image_quantum_operator(int argc, VALUE *argv, VALUE self)
|
|
6458
7423
|
}
|
6459
7424
|
|
6460
7425
|
GetExceptionInfo(&exception);
|
6461
|
-
(void) EvaluateImageChannel(image, channel,
|
7426
|
+
(void) EvaluateImageChannel(image, channel, qop, rvalue, &exception);
|
6462
7427
|
CHECK_EXCEPTION()
|
6463
7428
|
|
6464
7429
|
DestroyExceptionInfo(&exception);
|
@@ -7005,7 +7970,7 @@ resize(int bang, int argc, VALUE *argv, VALUE self)
|
|
7005
7970
|
dcols = scale * image->columns + 0.5;
|
7006
7971
|
if (drows > ULONG_MAX || dcols > ULONG_MAX)
|
7007
7972
|
{
|
7008
|
-
rb_raise(rb_eRangeError, "
|
7973
|
+
rb_raise(rb_eRangeError, "resized image too big");
|
7009
7974
|
}
|
7010
7975
|
rows = (unsigned long) drows;
|
7011
7976
|
columns = (unsigned long) dcols;
|
@@ -7071,21 +8036,51 @@ Image_roll(VALUE self, VALUE x_offset, VALUE y_offset)
|
|
7071
8036
|
|
7072
8037
|
|
7073
8038
|
/*
|
7074
|
-
Method: Image#rotate(degrees)
|
8039
|
+
Method: Image#rotate(degrees [,'<' | '>'])
|
7075
8040
|
Purpose: creates a new image that is a rotated copy of an existing one
|
7076
8041
|
Image#rotate!(degrees)
|
7077
8042
|
Purpose: rotates the image by the specified number of degrees
|
8043
|
+
Note: If the 2nd argument is '<' rotate only if width < height.
|
8044
|
+
If the 2nd argument is '>' rotate only if width > height.
|
7078
8045
|
*/
|
7079
8046
|
static VALUE
|
7080
|
-
rotate(int bang, VALUE
|
8047
|
+
rotate(int bang, int argc, VALUE *argv, VALUE self)
|
7081
8048
|
{
|
7082
8049
|
Image *image, *new_image;
|
8050
|
+
double degrees;
|
8051
|
+
char *arrow;
|
8052
|
+
long arrow_l;
|
7083
8053
|
ExceptionInfo exception;
|
7084
8054
|
|
7085
8055
|
Data_Get_Struct(self, Image, image);
|
8056
|
+
|
8057
|
+
switch (argc)
|
8058
|
+
{
|
8059
|
+
case 2:
|
8060
|
+
arrow = STRING_PTR_LEN(argv[1], arrow_l);
|
8061
|
+
if (arrow_l != 1 || (*arrow != '<' && *arrow != '>'))
|
8062
|
+
{
|
8063
|
+
rb_raise(rb_eArgError, "second argument must be '<' or '>', '%s' given", arrow);
|
8064
|
+
}
|
8065
|
+
if (*arrow == '>' && image->columns <= image->rows)
|
8066
|
+
{
|
8067
|
+
return Qnil;
|
8068
|
+
}
|
8069
|
+
if (*arrow == '<' && image->columns >= image->rows)
|
8070
|
+
{
|
8071
|
+
return Qnil;
|
8072
|
+
}
|
8073
|
+
case 1:
|
8074
|
+
degrees = NUM2DBL(argv[0]);
|
8075
|
+
break;
|
8076
|
+
default:
|
8077
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
|
8078
|
+
break;
|
8079
|
+
}
|
8080
|
+
|
7086
8081
|
GetExceptionInfo(&exception);
|
7087
8082
|
|
7088
|
-
new_image = RotateImage(image,
|
8083
|
+
new_image = RotateImage(image, degrees, &exception);
|
7089
8084
|
rm_check_exception(&exception, new_image, DestroyOnError);
|
7090
8085
|
|
7091
8086
|
DestroyExceptionInfo(&exception);
|
@@ -7102,16 +8097,16 @@ rotate(int bang, VALUE self, VALUE degrees)
|
|
7102
8097
|
}
|
7103
8098
|
|
7104
8099
|
VALUE
|
7105
|
-
Image_rotate(VALUE
|
8100
|
+
Image_rotate(int argc, VALUE *argv, VALUE self)
|
7106
8101
|
{
|
7107
|
-
return rotate(False,
|
8102
|
+
return rotate(False, argc, argv, self);
|
7108
8103
|
}
|
7109
8104
|
|
7110
8105
|
VALUE
|
7111
|
-
Image_rotate_bang(VALUE
|
8106
|
+
Image_rotate_bang(int argc, VALUE *argv, VALUE self)
|
7112
8107
|
{
|
7113
8108
|
rm_check_frozen(self);
|
7114
|
-
return rotate(True,
|
8109
|
+
return rotate(True, argc, argv, self);
|
7115
8110
|
}
|
7116
8111
|
|
7117
8112
|
DEF_ATTR_READER(Image, rows, int)
|
@@ -7192,7 +8187,7 @@ scale(int bang, int argc, VALUE *argv, VALUE self, scaler_t *scaler)
|
|
7192
8187
|
dcols = scale * image->columns + 0.5;
|
7193
8188
|
if (drows > ULONG_MAX || dcols > ULONG_MAX)
|
7194
8189
|
{
|
7195
|
-
rb_raise(rb_eRangeError, "
|
8190
|
+
rb_raise(rb_eRangeError, "resized image too big");
|
7196
8191
|
}
|
7197
8192
|
rows = (unsigned long) drows;
|
7198
8193
|
columns = (unsigned long) dcols;
|
@@ -7721,6 +8716,24 @@ Image_signature(VALUE self)
|
|
7721
8716
|
return rb_str_new(signature->value, 64);
|
7722
8717
|
}
|
7723
8718
|
|
8719
|
+
|
8720
|
+
|
8721
|
+
/*
|
8722
|
+
Method: Image#sketch(radius=0.0, sigma=1.0, angle=0.0)
|
8723
|
+
Purpose: Call SketchImage
|
8724
|
+
*/
|
8725
|
+
VALUE
|
8726
|
+
Image_sketch(int argc, VALUE *argv, VALUE self)
|
8727
|
+
{
|
8728
|
+
#if defined(HAVE_SKETCHIMAGE)
|
8729
|
+
return motion_blur(argc, argv, self, SketchImage);
|
8730
|
+
#else
|
8731
|
+
rm_not_implemented();
|
8732
|
+
return (VALUE)0;
|
8733
|
+
#endif
|
8734
|
+
}
|
8735
|
+
|
8736
|
+
|
7724
8737
|
/*
|
7725
8738
|
Method: Image#solarize(threshold=50.0)
|
7726
8739
|
Purpose: applies a special effect to the image, similar to the effect
|
@@ -8351,7 +9364,7 @@ thumbnail(int bang, int argc, VALUE *argv, VALUE self)
|
|
8351
9364
|
dcols = scale * image->columns + 0.5;
|
8352
9365
|
if (drows > ULONG_MAX || dcols > ULONG_MAX)
|
8353
9366
|
{
|
8354
|
-
rb_raise(rb_eRangeError, "
|
9367
|
+
rb_raise(rb_eRangeError, "resized image too big");
|
8355
9368
|
}
|
8356
9369
|
rows = (unsigned long) drows;
|
8357
9370
|
columns = (unsigned long) dcols;
|
@@ -8711,6 +9724,46 @@ Image_transparent(int argc, VALUE *argv, VALUE self)
|
|
8711
9724
|
}
|
8712
9725
|
|
8713
9726
|
|
9727
|
+
/*
|
9728
|
+
Method: Image#transparent_color
|
9729
|
+
Purpose: Return the name of the transparent color as a String.
|
9730
|
+
*/
|
9731
|
+
VALUE
|
9732
|
+
Image_transparent_color(VALUE self)
|
9733
|
+
{
|
9734
|
+
#if defined(HAVE_IMAGE_TRANSPARENT_COLOR)
|
9735
|
+
Image *image;
|
9736
|
+
|
9737
|
+
Data_Get_Struct(self, Image, image);
|
9738
|
+
return PixelPacket_to_Color_Name(image, &image->transparent_color);
|
9739
|
+
#else
|
9740
|
+
rm_not_implemented();
|
9741
|
+
return (VALUE)0;
|
9742
|
+
#endif
|
9743
|
+
}
|
9744
|
+
|
9745
|
+
|
9746
|
+
/*
|
9747
|
+
Method: Image#transparent_color=
|
9748
|
+
Purpose: Set the the transparent color to the specified color spec.
|
9749
|
+
*/
|
9750
|
+
VALUE
|
9751
|
+
Image_transparent_color_eq(VALUE self, VALUE color)
|
9752
|
+
{
|
9753
|
+
#if defined(HAVE_IMAGE_TRANSPARENT_COLOR)
|
9754
|
+
Image *image;
|
9755
|
+
|
9756
|
+
rm_check_frozen(self);
|
9757
|
+
Data_Get_Struct(self, Image, image);
|
9758
|
+
Color_to_PixelPacket(&image->transparent_color, color);
|
9759
|
+
return self;
|
9760
|
+
#else
|
9761
|
+
rm_not_implemented();
|
9762
|
+
return (VALUE)0;
|
9763
|
+
#endif
|
9764
|
+
}
|
9765
|
+
|
9766
|
+
|
8714
9767
|
/*
|
8715
9768
|
* Method: Image#transpose
|
8716
9769
|
* Image#transpose!
|
@@ -8863,6 +9916,34 @@ VALUE Image_image_type(VALUE self)
|
|
8863
9916
|
}
|
8864
9917
|
|
8865
9918
|
|
9919
|
+
/*
|
9920
|
+
Method: Image#unique_colors
|
9921
|
+
Purpose: Call UniqueImageColors
|
9922
|
+
*/
|
9923
|
+
VALUE
|
9924
|
+
Image_unique_colors(VALUE self)
|
9925
|
+
{
|
9926
|
+
#if defined(HAVE_UNIQUEIMAGECOLORS)
|
9927
|
+
Image *image, *new_image;
|
9928
|
+
ExceptionInfo exception;
|
9929
|
+
|
9930
|
+
Data_Get_Struct(self, Image, image);
|
9931
|
+
GetExceptionInfo(&exception);
|
9932
|
+
|
9933
|
+
new_image = UniqueImageColors(image, &exception);
|
9934
|
+
rm_check_exception(&exception, new_image, DestroyOnError);
|
9935
|
+
DestroyExceptionInfo(&exception);
|
9936
|
+
|
9937
|
+
rm_ensure_result(new_image);
|
9938
|
+
|
9939
|
+
return rm_image_new(new_image);
|
9940
|
+
#else
|
9941
|
+
rm_not_implemented();
|
9942
|
+
return (VALUE)0;
|
9943
|
+
#endif
|
9944
|
+
}
|
9945
|
+
|
9946
|
+
|
8866
9947
|
/*
|
8867
9948
|
Method: Image#units
|
8868
9949
|
Purpose: Get the resolution type field
|
@@ -9097,6 +10178,64 @@ Image_virtual_pixel_method_eq(VALUE self, VALUE method)
|
|
9097
10178
|
return self;
|
9098
10179
|
}
|
9099
10180
|
|
10181
|
+
|
10182
|
+
|
10183
|
+
|
10184
|
+
/*
|
10185
|
+
Method: Image#watermark(mark, brightness=100.0, saturation=100.0
|
10186
|
+
, [gravity,] x_off=0, y_off=0)
|
10187
|
+
Purpose: add a watermark to an image
|
10188
|
+
Notes: x_off and y_off can be negative, which means measure from the right/bottom
|
10189
|
+
of the target image.
|
10190
|
+
*/
|
10191
|
+
VALUE
|
10192
|
+
Image_watermark(int argc, VALUE *argv, VALUE self)
|
10193
|
+
{
|
10194
|
+
Image *image, *overlay, *new_image;
|
10195
|
+
double src_percent = 100.0, dst_percent = 100.0;
|
10196
|
+
long x_offset = 0L, y_offset = 0L;
|
10197
|
+
char geometry[20];
|
10198
|
+
|
10199
|
+
Data_Get_Struct(self, Image, image);
|
10200
|
+
|
10201
|
+
if (argc < 1)
|
10202
|
+
{
|
10203
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
10204
|
+
}
|
10205
|
+
|
10206
|
+
if (argc > 3)
|
10207
|
+
{
|
10208
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, overlay);
|
10209
|
+
get_composite_offsets(argc-3, &argv[3], image, overlay, &x_offset, &y_offset);
|
10210
|
+
// There must be 3 arguments left
|
10211
|
+
argc = 3;
|
10212
|
+
}
|
10213
|
+
|
10214
|
+
switch (argc)
|
10215
|
+
{
|
10216
|
+
case 3:
|
10217
|
+
dst_percent = rm_percentage(argv[2]) * 100.0;
|
10218
|
+
case 2:
|
10219
|
+
src_percent = rm_percentage(argv[1]) * 100.0;
|
10220
|
+
case 1:
|
10221
|
+
Data_Get_Struct(ImageList_cur_image(argv[0]), Image, overlay);
|
10222
|
+
break;
|
10223
|
+
default:
|
10224
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 to 6)", argc);
|
10225
|
+
break;
|
10226
|
+
}
|
10227
|
+
|
10228
|
+
blend_geometry(geometry, sizeof(geometry), src_percent, dst_percent);
|
10229
|
+
CloneString(&overlay->geometry, geometry);
|
10230
|
+
|
10231
|
+
new_image = rm_clone_image(image);
|
10232
|
+
(void) CompositeImage(new_image, ModulateCompositeOp, overlay, x_offset, y_offset);
|
10233
|
+
|
10234
|
+
rm_check_image_exception(new_image, DestroyOnError);
|
10235
|
+
|
10236
|
+
return rm_image_new(new_image);
|
10237
|
+
}
|
10238
|
+
|
9100
10239
|
/*
|
9101
10240
|
Method: Image#wave(amplitude=25.0, wavelength=150.0)
|
9102
10241
|
Purpose: creates a "ripple" effect in the image by shifting the pixels
|
@@ -9218,8 +10357,12 @@ Image_write(VALUE self, VALUE file)
|
|
9218
10357
|
return self;
|
9219
10358
|
}
|
9220
10359
|
|
10360
|
+
|
9221
10361
|
DEF_ATTR_ACCESSOR(Image, x_resolution, dbl)
|
9222
10362
|
|
10363
|
+
DEF_ATTR_ACCESSOR(Image, y_resolution, dbl)
|
10364
|
+
|
10365
|
+
|
9223
10366
|
/*
|
9224
10367
|
Static: cropper
|
9225
10368
|
Purpose: determine if the argument list is
|
@@ -9439,11 +10582,9 @@ xform_image(
|
|
9439
10582
|
|
9440
10583
|
}
|
9441
10584
|
|
9442
|
-
DEF_ATTR_ACCESSOR(Image, y_resolution, dbl)
|
9443
|
-
|
9444
10585
|
|
9445
10586
|
/*
|
9446
|
-
|
10587
|
+
Extern: extract_channels
|
9447
10588
|
Purpose: Remove all the ChannelType arguments from the
|
9448
10589
|
end of the argument list.
|
9449
10590
|
Returns: A ChannelType value suitable for passing into
|
@@ -9451,7 +10592,7 @@ DEF_ATTR_ACCESSOR(Image, y_resolution, dbl)
|
|
9451
10592
|
no channel arguments were found. Returns the
|
9452
10593
|
number of remaining arguments.
|
9453
10594
|
*/
|
9454
|
-
|
10595
|
+
ChannelType extract_channels(
|
9455
10596
|
int *argc,
|
9456
10597
|
VALUE *argv)
|
9457
10598
|
{
|
@@ -9487,11 +10628,11 @@ static ChannelType extract_channels(
|
|
9487
10628
|
|
9488
10629
|
|
9489
10630
|
/*
|
9490
|
-
|
10631
|
+
Extern: raise_ChannelType_error
|
9491
10632
|
Purpose: raise TypeError when an non-ChannelType object
|
9492
10633
|
is unexpectedly encountered
|
9493
10634
|
*/
|
9494
|
-
|
10635
|
+
void
|
9495
10636
|
raise_ChannelType_error(VALUE arg)
|
9496
10637
|
{
|
9497
10638
|
rb_raise(rb_eTypeError, "argument needs to be a ChannelType (%s given)"
|