rmagick 1.9.0 → 1.9.1

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.

@@ -1,4 +1,4 @@
1
- MANIFEST for RMagick-1.9.0 - 18:03:12 07/15/05
1
+ MANIFEST for RMagick-1.9.1 - 18:06:01 09/07/05
2
2
 
3
3
  configure
4
4
  README.html
@@ -23,6 +23,7 @@ examples/rotating_text.rb
23
23
  examples/pattern_fill.rb
24
24
  examples/import_export.rb
25
25
  examples/image_opacity.rb
26
+ examples/identify.rb
26
27
  examples/histogram.rb
27
28
  examples/describe.rb
28
29
  uninstall.rb
@@ -134,7 +135,6 @@ doc/ex/rectangle.rb
134
135
  doc/ex/rect02.rb
135
136
  doc/ex/rect01.rb
136
137
  doc/ex/random_threshold_channel.rb
137
- doc/ex/random_channel_threshold.rb
138
138
  doc/ex/raise.rb
139
139
  doc/ex/radial_blur.rb
140
140
  doc/ex/quantize-m.rb
@@ -219,7 +219,6 @@ doc/ex/circle01.rb
219
219
  doc/ex/circle.rb
220
220
  doc/ex/chop.rb
221
221
  doc/ex/charcoal.rb
222
- doc/ex/channel_threshold.rb
223
222
  doc/ex/channel.rb
224
223
  doc/ex/cbezier6.rb
225
224
  doc/ex/cbezier5.rb
@@ -1,4 +1,4 @@
1
- /* $Id: rmagick.h,v 1.89 2005/06/19 20:26:34 rmagick Exp $ */
1
+ /* $Id: rmagick.h,v 1.92 2005/09/05 20:27:27 rmagick Exp $ */
2
2
  /*=============================================================================
3
3
  | Copyright (C) 2005 by Timothy P. Hunter
4
4
  | Name: rmagick.h
@@ -272,6 +272,7 @@ EXTERN VALUE Class_PaintMethod;
272
272
  EXTERN VALUE Class_PreviewType;
273
273
  EXTERN VALUE Class_RenderingIntent;
274
274
  EXTERN VALUE Class_ResolutionType;
275
+ EXTERN VALUE Class_StorageType;
275
276
  EXTERN VALUE Class_StretchType;
276
277
  EXTERN VALUE Class_StyleType;
277
278
  EXTERN VALUE Class_WeightType;
@@ -774,7 +775,7 @@ extern VALUE Image_get_pixels(VALUE, VALUE, VALUE, VALUE, VALUE);
774
775
  extern VALUE Image_gray_q(VALUE);
775
776
  extern VALUE Image_grayscale_pseudo_class(int, VALUE *, VALUE);
776
777
  extern VALUE Image_implode(int, VALUE *, VALUE);
777
- extern VALUE Image_import_pixels(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE);
778
+ extern VALUE Image_import_pixels(int, VALUE *, VALUE);
778
779
  extern VALUE Image_init_copy(VALUE, VALUE);
779
780
  extern VALUE Image_inspect(VALUE);
780
781
  extern VALUE Image_level(int, VALUE *, VALUE);
@@ -893,7 +894,9 @@ extern VALUE Color_to_s(VALUE);
893
894
  extern VALUE Color_from_ColorInfo(const ColorInfo *);
894
895
  extern VALUE ClassType_new(ClassType);
895
896
  extern VALUE ColorspaceType_new(ColorspaceType);
897
+ extern VALUE CompositeOperator_new(CompositeOperator);
896
898
  extern VALUE CompressionType_new(CompressionType);
899
+ extern VALUE DisposeType_new(DisposeType);
897
900
  extern VALUE EndianType_new(EndianType);
898
901
  extern VALUE FilterTypes_new(FilterTypes);
899
902
  extern VALUE Font_to_s(VALUE);
@@ -955,6 +958,7 @@ extern VALUE Statistics_new(ImageStatistics *);
955
958
  #if defined(HAVE_IMAGE_ORIENTATION)
956
959
  extern VALUE OrientationType_new(OrientationType);
957
960
  #endif
961
+ extern const char *StorageType_name(StorageType);
958
962
 
959
963
  #if defined(HAVE_RB_DEFINE_ALLOC_FUNC)
960
964
  extern VALUE Enum_alloc(VALUE);
@@ -81,6 +81,7 @@
81
81
  #undef HAVE_GETMULTILINETYPEMETRICS
82
82
  /* API changed in IM 6.1.3 */
83
83
  #undef HAVE_OLD_GETMAGICKINFOLIST
84
+ #undef HAVE_GETNEXTIMAGEATTRIBUTE
84
85
  #undef HAVE_GETNEXTIMAGEINLIST
85
86
  /* Introduced in IM 6.0.0 */
86
87
  #undef HAVE_GETNEXTIMAGEPROFILE
@@ -88,6 +89,7 @@
88
89
  #undef HAVE_GETTYPEINFOLIST
89
90
  /* API changed in IM 6.1.3 */
90
91
  #undef HAVE_OLD_GETTYPEINFOLIST
92
+ #undef HAVE_GRAYCHANNEL
91
93
  /* Introduced in GM 1.0 */
92
94
  #undef HAVE_GRAYSCALEPSEUDOCLASSIMAGE
93
95
  /* Introduced in IM 6.0.0 */
@@ -104,6 +106,7 @@
104
106
  #undef HAVE_IMAGE_QUALITY
105
107
  /* Introduced in IM 6.0.0 */
106
108
  #undef HAVE_IMAGE_ORIENTATION
109
+ #undef HAVE_IMAGE_STORAGE_CLASS
107
110
  /* Introduced in IM 5.5.6 */
108
111
  #undef HAVE_IMAGEINFO_NUMBER_SCENES
109
112
  /* Introduced in IM 5.5.7, GM 1.1 */
@@ -136,6 +139,8 @@
136
139
  /* Introduced in IM 5.5.1, GM 1.0 */
137
140
  #undef HAVE_PREVIEWIMAGE
138
141
  #undef HAVE_QUANTUMOPERATORREGIONIMAGE
142
+ /* Introduced in IM 6.2.0 */
143
+ #undef HAVE_QUANTUMPIXEL
139
144
  #undef HAVE_RADIALBLURIMAGE
140
145
  #undef HAVE_RANDOMCHANNELTHRESHOLDIMAGE
141
146
  #undef HAVE_RANDOMTHRESHOLDIMAGECHANNEL
@@ -1,4 +1,4 @@
1
- /* $Id: rmilist.c,v 1.22 2005/03/05 16:18:39 rmagick Exp $ */
1
+ /* $Id: rmilist.c,v 1.25 2005/08/07 21:21:08 rmagick Exp $ */
2
2
  /*============================================================================\
3
3
  | Copyright (C) 2005 by Timothy P. Hunter
4
4
  | Name: rmilist.c
@@ -75,8 +75,8 @@ ImageList_append(VALUE self, VALUE stack_arg)
75
75
 
76
76
  GetExceptionInfo(&exception);
77
77
  result = AppendImages(images, stack, &exception);
78
- HANDLE_ERROR
79
78
  rm_split(images);
79
+ HANDLE_ERROR
80
80
 
81
81
  return rm_image_new(result);
82
82
  }
@@ -122,8 +122,8 @@ ImageList_coalesce(VALUE self)
122
122
 
123
123
  GetExceptionInfo(&exception);
124
124
  results = CoalesceImages(images, &exception);
125
- HANDLE_ERROR
126
125
  rm_split(images);
126
+ HANDLE_ERROR
127
127
 
128
128
  return rm_imagelist_from_images(results);
129
129
  }
@@ -145,8 +145,8 @@ ImageList_deconstruct(VALUE self)
145
145
  images = rm_images_from_imagelist(self);
146
146
  GetExceptionInfo(&exception);
147
147
  new_images = DeconstructImages(images, &exception);
148
- HANDLE_ERROR
149
148
  rm_split(images);
149
+ HANDLE_ERROR
150
150
 
151
151
  return rm_imagelist_from_images(new_images);
152
152
  }
@@ -195,8 +195,8 @@ ImageList_flatten_images(VALUE self)
195
195
  images = rm_images_from_imagelist(self);
196
196
  GetExceptionInfo(&exception);
197
197
  new_image = FlattenImages(images, &exception);
198
- HANDLE_ERROR
199
198
  rm_split(images);
199
+ HANDLE_ERROR
200
200
 
201
201
  return rm_image_new(new_image);
202
202
  }
@@ -228,8 +228,8 @@ ImageList_map(VALUE self, VALUE map_image, VALUE dither_arg)
228
228
  images = rm_images_from_imagelist(self);
229
229
  GetExceptionInfo(&exception);
230
230
  clone_images = CloneImageList(images, &exception);
231
- HANDLE_ERROR
232
231
  rm_split(images);
232
+ HANDLE_ERROR
233
233
 
234
234
  // Call ImageMagick
235
235
  dither = !(dither_arg == Qfalse || dither_arg == Qnil);
@@ -285,8 +285,8 @@ ImageList_montage(VALUE self)
285
285
 
286
286
  // MontageImage can return more than one image.
287
287
  montage_seq = MontageImages(image_list, montage->info, &exception);
288
- HANDLE_ERROR
289
288
  rm_split(image_list);
289
+ HANDLE_ERROR
290
290
 
291
291
  return rm_imagelist_from_images(montage_seq);
292
292
  }
@@ -304,13 +304,15 @@ ImageList_morph(VALUE self, VALUE nimages)
304
304
  {
305
305
  Image *images, *new_images;
306
306
  ExceptionInfo exception;
307
- unsigned long number_images;
307
+ long number_images;
308
308
 
309
309
  if (rm_imagelist_length(self) < 1)
310
310
  {
311
311
  rb_raise(rb_eArgError, "no images in this image list");
312
312
  }
313
- number_images = NUM2ULONG(nimages);
313
+
314
+ // Use a signed long so we can test for a negative argument.
315
+ number_images = NUM2LONG(nimages);
314
316
  if (number_images <= 0)
315
317
  {
316
318
  rb_raise(rb_eArgError, "number of intervening images must be > 0");
@@ -318,7 +320,7 @@ ImageList_morph(VALUE self, VALUE nimages)
318
320
 
319
321
  images = rm_images_from_imagelist(self);
320
322
  GetExceptionInfo(&exception);
321
- new_images = MorphImages(images, number_images, &exception);
323
+ new_images = MorphImages(images, (unsigned long)number_images, &exception);
322
324
  HANDLE_ERROR
323
325
 
324
326
  return rm_imagelist_from_images(new_images);
@@ -338,8 +340,8 @@ ImageList_mosaic(VALUE self)
338
340
  images = rm_images_from_imagelist(self);
339
341
  GetExceptionInfo(&exception);
340
342
  new_image = MosaicImages(images, &exception);
341
- HANDLE_ERROR
342
343
  rm_split(images);
344
+ HANDLE_ERROR
343
345
 
344
346
  return rm_image_new(new_image);
345
347
  }
@@ -523,8 +525,8 @@ ImageList_quantize(int argc, VALUE *argv, VALUE self)
523
525
  GetExceptionInfo(&exception);
524
526
  images = rm_images_from_imagelist(self);
525
527
  new_images = CloneImageList(images, &exception);
526
- HANDLE_ERROR
527
528
  rm_split(images);
529
+ HANDLE_ERROR
528
530
 
529
531
  QuantizeImages(&quantize_info, new_images);
530
532
 
@@ -583,8 +585,8 @@ ImageList_to_blob(VALUE self)
583
585
  info->adjoin = True;
584
586
  GetExceptionInfo(&exception);
585
587
  blob = ImageToBlob(info, images, &length, &exception);
586
- HANDLE_ERROR
587
588
  rm_split(images);
589
+ HANDLE_ERROR
588
590
 
589
591
  return (blob && length) ? rb_str_new(blob, length) : Qnil;
590
592
  }
@@ -673,6 +675,7 @@ ImageList_write(VALUE self, VALUE file)
673
675
  for (img = images; img; img = GET_NEXT_IMAGE(img))
674
676
  {
675
677
  (void) WriteImage(info, img);
678
+ // images will be split before raising an exception
676
679
  rm_handle_all_errors(images);
677
680
  if (info->adjoin)
678
681
  {
@@ -1,4 +1,4 @@
1
- /* $Id: rmimage.c,v 1.99 2005/06/19 20:26:34 rmagick Exp $ */
1
+ /* $Id: rmimage.c,v 1.118 2005/09/07 21:51:45 rmagick Exp $ */
2
2
  /*============================================================================\
3
3
  | Copyright (C) 2005 by Timothy P. Hunter
4
4
  | Name: rmimage.c
@@ -239,52 +239,6 @@ Image_aset(VALUE self, VALUE key_arg, VALUE attr_arg)
239
239
  return self;
240
240
  }
241
241
 
242
- /*
243
- Method: Image#properties [{ |k,v| block }]
244
- Purpose: Traverse the attributes and yield to the block.
245
- If no block, return a hash of all the attribute
246
- keys & values
247
- Notes: I use the word "properties" to distinguish between
248
- these "user-added" attribute strings and Image
249
- object attributes.
250
- */
251
- VALUE
252
- Image_properties(VALUE self)
253
- {
254
- Image *image;
255
- const ImageAttribute *attr;
256
- volatile VALUE attr_hash;
257
-
258
- Data_Get_Struct(self, Image, image);
259
-
260
- // If block, iterate over attributes
261
- if (rb_block_given_p())
262
- {
263
- volatile VALUE ary = rb_ary_new2(2);
264
- for (attr = image->attributes; attr; attr = Next_Attribute)
265
- {
266
- // Store the next ptr where Image#aset can see it.
267
- // The app may decide to delete that attribute.
268
- Next_Attribute = attr->next;
269
- rb_ary_store(ary, 0, rb_str_new2(attr->key));
270
- rb_ary_store(ary, 1, rb_str_new2(attr->value));
271
- rb_yield(ary);
272
- }
273
-
274
- return self;
275
- }
276
-
277
- // otherwise return properties hash
278
- else
279
- {
280
- attr_hash = rb_hash_new();
281
- for (attr = image->attributes; attr; attr = attr->next)
282
- {
283
- rb_hash_aset(attr_hash, rb_str_new2(attr->key), rb_str_new2(attr->value));
284
- }
285
- return attr_hash;
286
- }
287
- }
288
242
 
289
243
  /*
290
244
  Method: Image#background_color
@@ -374,9 +328,13 @@ Image_bilevel_channel(int argc, VALUE *argv, VALUE self)
374
328
 
375
329
  channels = extract_channels(&argc, argv);
376
330
 
331
+ if (argc > 1)
332
+ {
333
+ raise_ChannelType_error(argv[argc-1]);
334
+ }
377
335
  if (argc == 0)
378
336
  {
379
- rb_raise(rb_eArgError, "wrong number of arguments (0 for 1 or more)");
337
+ rb_raise(rb_eArgError, "no threshold specified");
380
338
  }
381
339
 
382
340
  GetExceptionInfo(&exception);
@@ -396,32 +354,21 @@ Image_bilevel_channel(int argc, VALUE *argv, VALUE self)
396
354
 
397
355
 
398
356
  /*
399
- Method: Image#border_color
400
- Purpose: Return the name of the border color as a String.
357
+ * Method: Image#black_threshold(red_channel [, green_channel
358
+ * [, blue_channel [, opacity_channel]]]);
359
+ * Purpose: Call BlackThresholdImage
401
360
  */
402
361
  VALUE
403
- Image_border_color(VALUE self)
362
+ Image_black_threshold(int argc, VALUE *argv, VALUE self)
404
363
  {
405
- Image *image;
406
-
407
- Data_Get_Struct(self, Image, image);
408
- return PixelPacket_to_Color_Name(image, &image->border_color);
364
+ #if defined(HAVE_BLACKTHRESHOLDIMAGE)
365
+ return threshold_image(argc, argv, self, BlackThresholdImage);
366
+ #else
367
+ rm_not_implemented();
368
+ return (VALUE)0;
369
+ #endif
409
370
  }
410
371
 
411
- /*
412
- Method: Image#border_color=
413
- Purpose: Set the the border color
414
- */
415
- VALUE
416
- Image_border_color_eq(VALUE self, VALUE color)
417
- {
418
- Image *image;
419
-
420
- rm_check_frozen(self);
421
- Data_Get_Struct(self, Image, image);
422
- Color_to_PixelPacket(&image->border_color, color);
423
- return self;
424
- }
425
372
 
426
373
  DEF_ATTR_ACCESSOR(Image, blur, dbl)
427
374
 
@@ -467,6 +414,7 @@ Image_blur_channel(int argc, VALUE *argv, VALUE self)
467
414
  #endif
468
415
  }
469
416
 
417
+
470
418
  /*
471
419
  Method: Image#blur_image(radius=0.0, sigma=1.0)
472
420
  Purpose: Blur the image
@@ -478,6 +426,7 @@ Image_blur_image(int argc, VALUE *argv, VALUE self)
478
426
  return effect_image(self, argc, argv, BlurImage);
479
427
  }
480
428
 
429
+
481
430
  /*
482
431
  Method: Image#border(width, height, color)
483
432
  Image#border!(width, height, color)
@@ -543,6 +492,36 @@ Image_border(
543
492
  return border(False, self, width, height, color);
544
493
  }
545
494
 
495
+
496
+ /*
497
+ Method: Image#border_color
498
+ Purpose: Return the name of the border color as a String.
499
+ */
500
+ VALUE
501
+ Image_border_color(VALUE self)
502
+ {
503
+ Image *image;
504
+
505
+ Data_Get_Struct(self, Image, image);
506
+ return PixelPacket_to_Color_Name(image, &image->border_color);
507
+ }
508
+
509
+ /*
510
+ Method: Image#border_color=
511
+ Purpose: Set the the border color
512
+ */
513
+ VALUE
514
+ Image_border_color_eq(VALUE self, VALUE color)
515
+ {
516
+ Image *image;
517
+
518
+ rm_check_frozen(self);
519
+ Data_Get_Struct(self, Image, image);
520
+ Color_to_PixelPacket(&image->border_color, color);
521
+ return self;
522
+ }
523
+
524
+
546
525
  /*
547
526
  Method: Image#bounding_box
548
527
  Purpose: returns the bounding box of an image canvas
@@ -560,6 +539,7 @@ VALUE Image_bounding_box(VALUE self)
560
539
  return Rectangle_from_RectangleInfo(&box);
561
540
  }
562
541
 
542
+
563
543
  /*
564
544
  Method: Image.capture(silent=false,
565
545
  frame=false,
@@ -689,6 +669,7 @@ Image_change_geometry(VALUE self, VALUE geom_arg)
689
669
  #endif
690
670
  }
691
671
 
672
+
692
673
  /*
693
674
  Method: Image#changed?
694
675
  Purpose: Return true if any pixel in the image has been altered since
@@ -733,66 +714,6 @@ Image_channel(VALUE self, VALUE channel_arg)
733
714
  }
734
715
 
735
716
 
736
- /*
737
- Method: Image#compare_channel(ref_image, metric [, channel...])
738
- Purpose: compares one or more channels in two images and returns
739
- the specified distortion metric and a comparison image.
740
- Notes: If no channels are specified, the default is AllChannels.
741
- That case is the equivalent of the CompareImages method in
742
- ImageMagick.
743
-
744
- Originally this method was called channel_compare, but
745
- that doesn't match the general naming convention that
746
- methods which accept multiple optional ChannelType
747
- arguments have names that end in _channel. So I renamed
748
- the method to compare_channel but kept channel_compare as
749
- an alias.
750
- */
751
- VALUE Image_compare_channel(
752
- int argc,
753
- VALUE *argv,
754
- VALUE self)
755
- {
756
- #if defined(HAVE_COMPAREIMAGECHANNELS)
757
-
758
- Image *image, *r_image, *difference_image;
759
- double distortion;
760
- volatile VALUE ary;
761
- MetricType metric_type;
762
- ChannelType channels;
763
- ExceptionInfo exception;
764
-
765
- channels = extract_channels(&argc, argv);
766
- if (argc < 2)
767
- {
768
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or more)", argc);
769
- }
770
-
771
- Data_Get_Struct(self, Image, image);
772
- Data_Get_Struct(ImageList_cur_image(argv[0]), Image, r_image);
773
- VALUE_TO_ENUM(argv[1], metric_type, MetricType);
774
-
775
- GetExceptionInfo(&exception);
776
- difference_image = CompareImageChannels(image
777
- , r_image
778
- , channels
779
- , metric_type
780
- , &distortion
781
- , &exception);
782
- HANDLE_ERROR
783
-
784
- ary = rb_ary_new2(2);
785
- rb_ary_store(ary, 0, rm_image_new(difference_image));
786
- rb_ary_store(ary, 1, rb_float_new(distortion));
787
-
788
- return ary;
789
- #else
790
- rm_not_implemented();
791
- return (VALUE)0;
792
- #endif
793
- }
794
-
795
-
796
717
  /*
797
718
  Method: Image#channel_depth(channel_depth=AllChannels)
798
719
  Purpose: GetImageChannelDepth
@@ -1064,22 +985,6 @@ Image_channel_mean(int argc, VALUE *argv, VALUE self)
1064
985
  #endif
1065
986
  }
1066
987
 
1067
- /*
1068
- * Method: Image#black_threshold(red_channel [, green_channel
1069
- * [, blue_channel [, opacity_channel]]]);
1070
- * Purpose: Call BlackThresholdImage
1071
- */
1072
- VALUE
1073
- Image_black_threshold(int argc, VALUE *argv, VALUE self)
1074
- {
1075
- #if defined(HAVE_BLACKTHRESHOLDIMAGE)
1076
- return threshold_image(argc, argv, self, BlackThresholdImage);
1077
- #else
1078
- rm_not_implemented();
1079
- return (VALUE)0;
1080
- #endif
1081
- }
1082
-
1083
988
 
1084
989
 
1085
990
  /*
@@ -1091,6 +996,8 @@ Image_black_threshold(int argc, VALUE *argv, VALUE self)
1091
996
  VALUE
1092
997
  Image_channel_threshold(int argc, VALUE *argv, VALUE self)
1093
998
  {
999
+ rb_warning("This method is deprecated in this release of " Q(MAGICKNAME)
1000
+ ". Use bilevel_channel instead.");
1094
1001
  return threshold_image(argc, argv, self,
1095
1002
  #if defined(HAVE_THRESHOLDIMAGECHANNEL)
1096
1003
  ThresholdImageChannel
@@ -1651,7 +1558,7 @@ Image_colormap(int argc, VALUE *argv, VALUE self)
1651
1558
  return PixelPacket_to_Color_Name(image, &color);
1652
1559
  }
1653
1560
 
1654
- DEF_ATTR_READER(Image, colors, int)
1561
+ DEF_ATTR_READER(Image, colors, ulong)
1655
1562
 
1656
1563
  /*
1657
1564
  Method: Image#colorspace
@@ -1725,8 +1632,87 @@ Image_colorspace_eq(VALUE self, VALUE colorspace)
1725
1632
  return self;
1726
1633
  }
1727
1634
 
1635
+
1728
1636
  DEF_ATTR_READER(Image, columns, int)
1729
- DEF_ATTR_READER(Image, compose, int)
1637
+
1638
+
1639
+ /*
1640
+ Method: Image#compare_channel(ref_image, metric [, channel...])
1641
+ Purpose: compares one or more channels in two images and returns
1642
+ the specified distortion metric and a comparison image.
1643
+ Notes: If no channels are specified, the default is AllChannels.
1644
+ That case is the equivalent of the CompareImages method in
1645
+ ImageMagick.
1646
+
1647
+ Originally this method was called channel_compare, but
1648
+ that doesn't match the general naming convention that
1649
+ methods which accept multiple optional ChannelType
1650
+ arguments have names that end in _channel. So I renamed
1651
+ the method to compare_channel but kept channel_compare as
1652
+ an alias.
1653
+ */
1654
+ VALUE Image_compare_channel(
1655
+ int argc,
1656
+ VALUE *argv,
1657
+ VALUE self)
1658
+ {
1659
+ #if defined(HAVE_COMPAREIMAGECHANNELS)
1660
+
1661
+ Image *image, *r_image, *difference_image;
1662
+ double distortion;
1663
+ volatile VALUE ary;
1664
+ MetricType metric_type;
1665
+ ChannelType channels;
1666
+ ExceptionInfo exception;
1667
+
1668
+ channels = extract_channels(&argc, argv);
1669
+ if (argc > 2)
1670
+ {
1671
+ raise_ChannelType_error(argv[argc-1]);
1672
+ }
1673
+ if (argc != 2)
1674
+ {
1675
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or more)", argc);
1676
+ }
1677
+
1678
+ Data_Get_Struct(self, Image, image);
1679
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, r_image);
1680
+ VALUE_TO_ENUM(argv[1], metric_type, MetricType);
1681
+
1682
+ GetExceptionInfo(&exception);
1683
+ difference_image = CompareImageChannels(image
1684
+ , r_image
1685
+ , channels
1686
+ , metric_type
1687
+ , &distortion
1688
+ , &exception);
1689
+ HANDLE_ERROR
1690
+
1691
+ ary = rb_ary_new2(2);
1692
+ rb_ary_store(ary, 0, rm_image_new(difference_image));
1693
+ rb_ary_store(ary, 1, rb_float_new(distortion));
1694
+
1695
+ return ary;
1696
+ #else
1697
+ rm_not_implemented();
1698
+ return (VALUE)0;
1699
+ #endif
1700
+ }
1701
+
1702
+
1703
+ /*
1704
+ Method: Image#compose -> composite_op
1705
+ Purpose: Return the composite operator attribute
1706
+ */
1707
+ VALUE Image_compose(VALUE self)
1708
+ {
1709
+ Image *image;
1710
+
1711
+ Data_Get_Struct(self, Image, image);
1712
+
1713
+ return CompositeOperator_new(image->compose);
1714
+ }
1715
+
1730
1716
 
1731
1717
  /*
1732
1718
  Method: Image#compose=composite_op
@@ -1773,11 +1759,11 @@ static VALUE composite(
1773
1759
  long y_offset;
1774
1760
 
1775
1761
  Data_Get_Struct(self, Image, image);
1776
- Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
1777
1762
 
1778
1763
  switch (argc)
1779
1764
  {
1780
1765
  case 3: // argv[1] is gravity, argv[2] is composite_op
1766
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
1781
1767
  VALUE_TO_ENUM(argv[1], gravity, GravityType);
1782
1768
  VALUE_TO_ENUM(argv[2], operator, CompositeOperator);
1783
1769
 
@@ -1828,12 +1814,14 @@ static VALUE composite(
1828
1814
 
1829
1815
  case 4: // argv[1], argv[2] is x_off, y_off,
1830
1816
  // argv[3] is composite_op
1817
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
1831
1818
  x_offset = NUM2LONG(argv[1]);
1832
1819
  y_offset = NUM2LONG(argv[2]);
1833
1820
  VALUE_TO_ENUM(argv[3], operator, CompositeOperator);
1834
1821
  break;
1835
1822
 
1836
1823
  case 5:
1824
+ Data_Get_Struct(ImageList_cur_image(argv[0]), Image, comp_image);
1837
1825
  VALUE_TO_ENUM(argv[1], gravity, GravityType);
1838
1826
  x_offset = NUM2LONG(argv[2]);
1839
1827
  y_offset = NUM2LONG(argv[3]);
@@ -1877,6 +1865,7 @@ static VALUE composite(
1877
1865
 
1878
1866
  if (bang)
1879
1867
  {
1868
+ rm_check_frozen(self);
1880
1869
  (void) CompositeImage(image, operator, comp_image, x_offset, y_offset);
1881
1870
  HANDLE_ERROR_IMG(image)
1882
1871
  return self;
@@ -2216,6 +2205,10 @@ Image_convolve_channel(
2216
2205
  channels = extract_channels(&argc, argv);
2217
2206
 
2218
2207
  // There are 2 required arguments.
2208
+ if (argc > 2)
2209
+ {
2210
+ raise_ChannelType_error(argv[argc-1]);
2211
+ }
2219
2212
  if (argc != 2)
2220
2213
  {
2221
2214
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or more)", argc);
@@ -2480,8 +2473,10 @@ VALUE Image_difference(VALUE self, VALUE other)
2480
2473
  return rb_ary_new3(3, mean, nmean, nmax);
2481
2474
  }
2482
2475
 
2476
+
2483
2477
  DEF_ATTR_READER(Image, directory, str)
2484
2478
 
2479
+
2485
2480
  /*
2486
2481
  Method: Image#dispatch(x, y, columns, rows, map <, float>)
2487
2482
  Purpose: Extracts pixel data from the image and returns it as an
@@ -2608,8 +2603,33 @@ VALUE Image_display(VALUE self)
2608
2603
  return self;
2609
2604
  }
2610
2605
 
2611
- DEF_ATTR_ACCESSOR(Image, dispose, ulong)
2606
+ /*
2607
+ Method: Image#dispose
2608
+ Purpose: Return the dispose attribute as a DisposeType enum
2609
+ */
2610
+ VALUE
2611
+ Image_dispose(VALUE self)
2612
+ {
2613
+ Image *image;
2614
+
2615
+ Data_Get_Struct(self, Image, image);
2616
+ return DisposeType_new(image->dispose);
2617
+ }
2618
+
2619
+ /*
2620
+ Method: Image#dispose=
2621
+ Purpose: Set the dispose attribute
2622
+ */
2623
+ VALUE
2624
+ Image_dispose_eq(VALUE self, VALUE dispose)
2625
+ {
2626
+ Image *image;
2612
2627
 
2628
+ rm_check_frozen(self);
2629
+ Data_Get_Struct(self, Image, image);
2630
+ VALUE_TO_ENUM(dispose, image->dispose, DisposeType);
2631
+ return self;
2632
+ }
2613
2633
 
2614
2634
  /*
2615
2635
  Method: Image#_dump(aDepth)
@@ -2785,9 +2805,9 @@ effect_image(
2785
2805
  }
2786
2806
 
2787
2807
  Data_Get_Struct(self, Image, image);
2788
- if (sigma <= 0.0)
2808
+ if (sigma == 0.0)
2789
2809
  {
2790
- rb_raise(rb_eArgError, "sigma must be > 0.0");
2810
+ rb_raise(rb_eArgError, "sigma must be != 0.0");
2791
2811
  }
2792
2812
 
2793
2813
  GetExceptionInfo(&exception);
@@ -3007,6 +3027,7 @@ Image_extract_info_eq(VALUE self, VALUE rect)
3007
3027
 
3008
3028
  DEF_ATTR_READER(Image, filename, str)
3009
3029
 
3030
+
3010
3031
  /*
3011
3032
  Method: Image#filesize
3012
3033
  Purpose: Return the image filesize
@@ -3019,6 +3040,7 @@ VALUE Image_filesize(VALUE self)
3019
3040
  return INT2FIX(GetBlobSize(image));
3020
3041
  }
3021
3042
 
3043
+
3022
3044
  /*
3023
3045
  Method: Image#filter, filter=
3024
3046
  Purpose: Get/set filter type
@@ -3043,6 +3065,7 @@ Image_filter_eq(VALUE self, VALUE filter)
3043
3065
  return self;
3044
3066
  }
3045
3067
 
3068
+
3046
3069
  /*
3047
3070
  Method: Image#flip
3048
3071
  Image#flip!
@@ -3285,6 +3308,7 @@ VALUE Image_fuzz_eq(VALUE self, VALUE fuzz)
3285
3308
  return self;
3286
3309
  }
3287
3310
 
3311
+
3288
3312
  DEF_ATTR_ACCESSOR(Image, gamma, dbl)
3289
3313
 
3290
3314
 
@@ -3475,7 +3499,7 @@ Image_geometry_eq(
3475
3499
  geom = STRING_PTR(geom_str);
3476
3500
  if (!IsGeometry(geom))
3477
3501
  {
3478
- rb_raise(rb_eArgError, "invalid geometry: %s", geom);
3502
+ rb_raise(rb_eTypeError, "invalid geometry: %s", geom);
3479
3503
  }
3480
3504
  magick_clone_string(&image->geometry, geom);
3481
3505
  return self;
@@ -3665,77 +3689,145 @@ Image_implode(int argc, VALUE *argv, VALUE self)
3665
3689
  Notes: See Image#export_pixels
3666
3690
  */
3667
3691
  VALUE
3668
- Image_import_pixels(
3669
- VALUE self,
3670
- VALUE x_arg,
3671
- VALUE y_arg,
3672
- VALUE cols_arg,
3673
- VALUE rows_arg,
3674
- VALUE map_arg,
3675
- VALUE pixel_ary)
3692
+ Image_import_pixels(int argc, VALUE *argv, VALUE self)
3676
3693
  {
3677
3694
  #if defined(HAVE_IMPORTIMAGEPIXELS)
3678
3695
  Image *image, *clone_image;
3679
3696
  long x_off, y_off;
3680
3697
  unsigned long cols, rows;
3681
3698
  unsigned long npixels;
3682
- long n;
3699
+ long n, buffer_l;
3683
3700
  char *map;
3684
- volatile int *pixels;
3701
+ volatile VALUE pixel_arg, pixel_ary;
3702
+ StorageType stg_type = CharPixel;
3703
+ size_t type_sz, map_l;
3704
+ volatile int *pixels = NULL;
3705
+ volatile void *buffer;
3685
3706
  unsigned int okay;
3686
3707
  ExceptionInfo exception;
3687
3708
 
3688
3709
  rm_check_frozen(self);
3710
+
3711
+ switch (argc)
3712
+ {
3713
+ case 7:
3714
+ VALUE_TO_ENUM(argv[6], stg_type, StorageType);
3715
+ case 6:
3716
+ x_off = NUM2LONG(argv[0]);
3717
+ y_off = NUM2LONG(argv[1]);
3718
+ cols = NUM2ULONG(argv[2]);
3719
+ rows = NUM2ULONG(argv[3]);
3720
+ map = STRING_PTR(argv[4]);
3721
+ pixel_arg = argv[5];
3722
+ break;
3723
+ default:
3724
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 6 or 7)", argc);
3725
+ break;
3726
+ }
3727
+
3689
3728
  Data_Get_Struct(self, Image, image);
3690
3729
 
3691
- map = STRING_PTR(map_arg);
3692
- x_off = NUM2LONG(x_arg);
3693
- y_off = NUM2LONG(y_arg);
3694
- cols = NUM2ULONG(cols_arg);
3695
- rows = NUM2ULONG(rows_arg);
3730
+ if (x_off < 0 || y_off < 0 || cols <= 0 || rows <= 0)
3731
+ {
3732
+ rb_raise(rb_eArgError, "invalid import geometry");
3733
+ }
3734
+
3735
+ map_l = strlen(map);
3736
+ npixels = cols * rows * map_l;
3737
+
3738
+ // Assume that any object that responds to :to_str is a string buffer containing
3739
+ // binary pixel data.
3740
+ if (rb_respond_to(pixel_arg, rb_intern("to_str")))
3741
+ {
3742
+ buffer = (void *)STRING_PTR_LEN(pixel_arg, buffer_l);
3743
+ switch (stg_type)
3744
+ {
3745
+ case CharPixel:
3746
+ type_sz = 1;
3747
+ break;
3748
+ case ShortPixel:
3749
+ type_sz = sizeof(unsigned short);
3750
+ break;
3751
+ case IntegerPixel:
3752
+ type_sz = sizeof(unsigned int);
3753
+ break;
3754
+ case LongPixel:
3755
+ type_sz = sizeof(unsigned long);
3756
+ break;
3757
+ #if defined(HAVE_QUANTUMPIXEL)
3758
+ case QuantumPixel:
3759
+ type_sz = sizeof(Quantum);
3760
+ break;
3761
+ #endif
3762
+ default:
3763
+ rb_raise(rb_eArgError, "unsupported storage type %s", StorageType_name(stg_type));
3764
+ break;
3765
+ }
3696
3766
 
3697
- if (x_off < 0 || y_off < 0 || cols <= 0 || rows <= 0)
3698
- {
3699
- rb_raise(rb_eArgError, "invalid import geometry");
3767
+ if (buffer_l % type_sz != 0)
3768
+ {
3769
+ rb_raise(rb_eArgError, "pixel buffer must be an exact multiple of the storage type size");
3770
+ }
3771
+ if ((buffer_l / type_sz) % map_l != 0)
3772
+ {
3773
+ rb_raise(rb_eArgError, "pixel buffer must contain an exact multiple of the map length");
3774
+ }
3775
+ if (buffer_l/type_sz < npixels)
3776
+ {
3777
+ rb_raise(rb_eArgError, "pixel buffer too small (need %lu channel values, got %ld)"
3778
+ , npixels, buffer_l/type_sz);
3779
+ }
3700
3780
  }
3781
+ // Otherwise convert the argument to an array and convert the array elements
3782
+ // to binary pixel data.
3783
+ else
3784
+ {
3785
+ // rb_Array converts an object that is not an array to an array if possible,
3786
+ // and raises TypeError if it can't. It usually is possible.
3787
+ pixel_ary = rb_Array(pixel_arg);
3701
3788
 
3702
- npixels = cols * rows * strlen(map);
3703
-
3704
- // rb_Array converts objects that are not Arrays to Arrays if possible,
3705
- // and raises TypeError if it can't.
3706
- pixel_ary = rb_Array(pixel_ary);
3789
+ if (RARRAY(pixel_ary)->len % map_l != 0)
3790
+ {
3791
+ rb_raise(rb_eArgError, "pixel array must contain an exact multiple of the map length");
3792
+ }
3793
+ if (RARRAY(pixel_ary)->len < npixels)
3794
+ {
3795
+ rb_raise(rb_eArgError, "pixel array too small (need %lu elements, got %ld)"
3796
+ , npixels, RARRAY(pixel_ary)->len);
3797
+ }
3707
3798
 
3708
- if (RARRAY(pixel_ary)->len < npixels)
3709
- {
3710
- rb_raise(rb_eArgError, "pixel array too small (need %lu, got %ld)"
3711
- , npixels, RARRAY(pixel_ary)->len);
3712
- }
3799
+ // Get array for integer pixels. Use Ruby's memory so GC will clean up after us
3800
+ // in case of an exception.
3801
+ pixels = ALLOC_N(int, npixels);
3802
+ if (!pixels) // app recovered from exception...
3803
+ {
3804
+ return self;
3805
+ }
3713
3806
 
3714
- // Get array for integer pixels. Use Ruby's memory so GC will clean up after us
3715
- // in case of an exception.
3716
- pixels = ALLOC_N(int, npixels);
3717
- if (!pixels) // app recovered from exception...
3718
- {
3719
- return self;
3807
+ for (n = 0; n < npixels; n++)
3808
+ {
3809
+ volatile VALUE p = rb_ary_entry(pixel_ary, n);
3810
+ long q = ScaleQuantumToLong(NUM2LONG(p));
3811
+ pixels[n] = (int) q;
3812
+ }
3813
+ buffer = (void *) pixels;
3814
+ stg_type = IntegerPixel;
3720
3815
  }
3721
3816
 
3722
- for (n = 0; n < npixels; n++)
3723
- {
3724
- volatile VALUE p = rb_ary_entry(pixel_ary, n);
3725
- long q = ScaleQuantumToLong(NUM2LONG(p));
3726
- pixels[n] = (int) q;
3727
- }
3728
3817
 
3729
3818
  // Import into a clone - ImportImagePixels destroys the input image if an error occurs.
3730
3819
  GetExceptionInfo(&exception);
3731
3820
  clone_image = CloneImage(image, 0, 0, True, &exception);
3732
3821
  HANDLE_ERROR
3733
3822
 
3734
- okay = ImportImagePixels(clone_image, x_off, y_off, cols, rows, map, IntegerPixel, (void *)pixels);
3823
+ okay = ImportImagePixels(clone_image, x_off, y_off, cols, rows, map, stg_type, (const void *)buffer);
3735
3824
 
3736
3825
  // Free pixel array before checking for errors. If an error occurred, ImportImagePixels
3737
3826
  // destroyed the clone image, so we don't have to.
3738
- xfree((void *)pixels);
3827
+ if (pixels)
3828
+ {
3829
+ xfree((void *)pixels);
3830
+ }
3739
3831
 
3740
3832
  if (!okay)
3741
3833
  {
@@ -3782,7 +3874,11 @@ Image_inspect(VALUE self)
3782
3874
  // Print current filename.
3783
3875
  x += sprintf(buffer+x, "%s", image->filename);
3784
3876
  // Print scene number.
3877
+ #if defined(HAVE_GETNEXTIMAGEINLIST)
3878
+ if ((GetPreviousImageInList(image) != NULL) && (GetNextImageInList(image) != NULL) && image->scene > 0)
3879
+ #else
3785
3880
  if ((image->previous || image->next) && image->scene > 0)
3881
+ #endif
3786
3882
  {
3787
3883
  x += sprintf(buffer+x, "[%lu]", image->scene);
3788
3884
  }
@@ -4568,6 +4664,7 @@ Image_monitor_eq(VALUE self, VALUE monitor)
4568
4664
  #if defined(HAVE_SETIMAGEPROGRESSMONITOR)
4569
4665
  Image *image;
4570
4666
 
4667
+ rm_check_frozen(self);
4571
4668
  Data_Get_Struct(self, Image, image);
4572
4669
 
4573
4670
  if (NIL_P(monitor))
@@ -4663,9 +4760,9 @@ Image_motion_blur(
4663
4760
  sigma = NUM2DBL(sigma_arg);
4664
4761
  angle = NUM2DBL(angle_arg);
4665
4762
 
4666
- if (sigma <= 0.0)
4763
+ if (sigma == 0.0)
4667
4764
  {
4668
- rb_raise(rb_eArgError, "sigma must be > 0.0");
4765
+ rb_raise(rb_eArgError, "sigma must be != 0.0");
4669
4766
  }
4670
4767
 
4671
4768
  GetExceptionInfo(&exception);
@@ -4996,14 +5093,14 @@ Image_number_colors(VALUE self)
4996
5093
  {
4997
5094
  Image *image;
4998
5095
  ExceptionInfo exception;
4999
- unsigned int n = 0;
5096
+ unsigned long n = 0;
5000
5097
 
5001
5098
  Data_Get_Struct(self, Image, image);
5002
5099
  GetExceptionInfo(&exception);
5003
5100
 
5004
- n = GetNumberColors(image, NULL, &exception);
5101
+ n = (unsigned long) GetNumberColors(image, NULL, &exception);
5005
5102
  HANDLE_ERROR
5006
- return INT2FIX(n);
5103
+ return ULONG2NUM(n);
5007
5104
  }
5008
5105
 
5009
5106
  DEF_ATTR_ACCESSOR(Image, offset, long)
@@ -5238,7 +5335,11 @@ Image_pixel_color(
5238
5335
  HANDLE_ERROR
5239
5336
 
5240
5337
  // PseudoClass
5338
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
5339
+ if (image->storage_class == PseudoClass)
5340
+ #else
5241
5341
  if (image->class == PseudoClass)
5342
+ #endif
5242
5343
  {
5243
5344
  IndexPacket *indexes = GetIndexes(image);
5244
5345
  old_color = image->colormap[*indexes];
@@ -5259,12 +5360,20 @@ Image_pixel_color(
5259
5360
 
5260
5361
  // Set the color of a pixel. Return previous color.
5261
5362
  // Convert to DirectClass
5363
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
5364
+ if (image->storage_class == PseudoClass)
5365
+ #else
5262
5366
  if (image->class == PseudoClass)
5367
+ #endif
5263
5368
  {
5264
5369
  SyncImage(image);
5265
5370
  magick_free(image->colormap);
5266
5371
  image->colormap = NULL;
5372
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
5373
+ image->storage_class = DirectClass;
5374
+ #else
5267
5375
  image->class = DirectClass;
5376
+ #endif
5268
5377
  }
5269
5378
 
5270
5379
  pixel = GetImagePixels(image, x, y, 1, 1);
@@ -5437,6 +5546,12 @@ Image_profile_bang(
5437
5546
  }
5438
5547
 
5439
5548
 
5549
+ #if defined(HAVE_IMAGE_QUALITY)
5550
+ DEF_ATTR_READER(Image, quality, ulong)
5551
+ #endif
5552
+
5553
+
5554
+
5440
5555
  /*
5441
5556
  Method: Image#quantum_depth -> 8, 16, or 32
5442
5557
  Purpose: Return image depth to nearest quantum
@@ -5642,38 +5757,6 @@ Image_quantum_operator(int argc, VALUE *argv, VALUE self)
5642
5757
  }
5643
5758
 
5644
5759
 
5645
-
5646
- /*
5647
- Method: Image#radial_blur(angle)
5648
- Purpose: Call RadialBlurImage
5649
- Notes: Angle is in degrees
5650
- */
5651
- VALUE
5652
- Image_radial_blur(VALUE self, VALUE angle)
5653
- {
5654
- #if defined(HAVE_RADIALBLURIMAGE)
5655
- Image *image, *new_image;
5656
- ExceptionInfo exception;
5657
-
5658
- Data_Get_Struct(self, Image, image);
5659
- GetExceptionInfo(&exception);
5660
-
5661
- new_image = RadialBlurImage(image, NUM2DBL(angle), &exception);
5662
- HANDLE_ERROR
5663
-
5664
- return rm_image_new(new_image);
5665
- #else
5666
- rm_not_implemented();
5667
- return (VALUE)0;
5668
- #endif
5669
- }
5670
-
5671
-
5672
- #if defined(HAVE_IMAGE_QUALITY)
5673
- DEF_ATTR_READER(Image, quality, ulong)
5674
- #endif
5675
-
5676
-
5677
5760
  /*
5678
5761
  Method: Image#quantize(<number_colors<, colorspace<, dither<, tree_depth<, measure_error>>>>>)
5679
5762
  defaults: 256, Magick::RGBColorspace, true, 0, false
@@ -5716,6 +5799,33 @@ Image_quantize(int argc, VALUE *argv, VALUE self)
5716
5799
  }
5717
5800
 
5718
5801
 
5802
+
5803
+ /*
5804
+ Method: Image#radial_blur(angle)
5805
+ Purpose: Call RadialBlurImage
5806
+ Notes: Angle is in degrees
5807
+ */
5808
+ VALUE
5809
+ Image_radial_blur(VALUE self, VALUE angle)
5810
+ {
5811
+ #if defined(HAVE_RADIALBLURIMAGE)
5812
+ Image *image, *new_image;
5813
+ ExceptionInfo exception;
5814
+
5815
+ Data_Get_Struct(self, Image, image);
5816
+ GetExceptionInfo(&exception);
5817
+
5818
+ new_image = RadialBlurImage(image, NUM2DBL(angle), &exception);
5819
+ HANDLE_ERROR
5820
+
5821
+ return rm_image_new(new_image);
5822
+ #else
5823
+ rm_not_implemented();
5824
+ return (VALUE)0;
5825
+ #endif
5826
+ }
5827
+
5828
+
5719
5829
  /*
5720
5830
  Method: Image#random_channel_threshold
5721
5831
  Purpose: changes the value of individual pixels based on the intensity of
@@ -6443,6 +6553,77 @@ Image_opacity_eq(VALUE self, VALUE opacity_arg)
6443
6553
  return self;
6444
6554
  }
6445
6555
 
6556
+
6557
+ /*
6558
+ Method: Image#properties [{ |k,v| block }]
6559
+ Purpose: Traverse the attributes and yield to the block.
6560
+ If no block, return a hash of all the attribute
6561
+ keys & values
6562
+ Notes: I use the word "properties" to distinguish between
6563
+ these "user-added" attribute strings and Image
6564
+ object attributes.
6565
+ */
6566
+ VALUE
6567
+ Image_properties(VALUE self)
6568
+ {
6569
+ Image *image;
6570
+ const ImageAttribute *attr;
6571
+ volatile VALUE attr_hash;
6572
+
6573
+ Data_Get_Struct(self, Image, image);
6574
+
6575
+ // If block, iterate over attributes
6576
+ if (rb_block_given_p())
6577
+ {
6578
+ volatile VALUE ary = rb_ary_new2(2);
6579
+
6580
+ #if defined(HAVE_GETNEXTIMAGEATTRIBUTE)
6581
+ ResetImageAttributeIterator(image);
6582
+ attr = GetNextImageAttribute(image);
6583
+ while (attr)
6584
+ {
6585
+ rb_ary_store(ary, 0, rb_str_new2(attr->key));
6586
+ rb_ary_store(ary, 1, rb_str_new2(attr->value));
6587
+ rb_yield(ary);
6588
+ attr = GetNextImageAttribute(image);
6589
+ }
6590
+ #else
6591
+ for (attr = image->attributes; attr; attr = Next_Attribute)
6592
+ {
6593
+ // Store the next ptr where Image#aset can see it.
6594
+ // The app may decide to delete that attribute.
6595
+ Next_Attribute = attr->next;
6596
+ rb_ary_store(ary, 0, rb_str_new2(attr->key));
6597
+ rb_ary_store(ary, 1, rb_str_new2(attr->value));
6598
+ rb_yield(ary);
6599
+ }
6600
+ #endif
6601
+ return self;
6602
+ }
6603
+
6604
+ // otherwise return properties hash
6605
+ else
6606
+ {
6607
+ attr_hash = rb_hash_new();
6608
+ #if defined(HAVE_GETNEXTIMAGEATTRIBUTE)
6609
+ ResetImageAttributeIterator(image);
6610
+ attr = GetNextImageAttribute(image);
6611
+ while (attr)
6612
+ {
6613
+ rb_hash_aset(attr_hash, rb_str_new2(attr->key), rb_str_new2(attr->value));
6614
+ attr = GetNextImageAttribute(image);
6615
+ }
6616
+ #else
6617
+ for (attr = image->attributes; attr; attr = attr->next)
6618
+ {
6619
+ rb_hash_aset(attr_hash, rb_str_new2(attr->key), rb_str_new2(attr->value));
6620
+ }
6621
+ #endif
6622
+ return attr_hash;
6623
+ }
6624
+ }
6625
+
6626
+
6446
6627
  /*
6447
6628
  Method: Image#shade(shading=false, azimuth=30, elevation=30)
6448
6629
  Purpose: shines a distant light on an image to create a three-dimensional
@@ -6545,33 +6726,6 @@ Image_shadow(int argc, VALUE *argv, VALUE self)
6545
6726
  #endif
6546
6727
  }
6547
6728
 
6548
- /*
6549
- Method: Image#shave(width, height)
6550
- Image#shave!(width, height)
6551
- Purpose: shaves pixels from the image edges, leaving a rectangle
6552
- of the specified width & height in the center
6553
- Returns: shave: a new image
6554
- shave!: self, shaved
6555
- */
6556
- VALUE
6557
- Image_shave(
6558
- VALUE self,
6559
- VALUE width,
6560
- VALUE height)
6561
- {
6562
- return xform_image(False, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
6563
- }
6564
-
6565
- VALUE
6566
- Image_shave_bang(
6567
- VALUE self,
6568
- VALUE width,
6569
- VALUE height)
6570
- {
6571
- rm_check_frozen(self);
6572
- return xform_image(True, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
6573
- }
6574
-
6575
6729
  /*
6576
6730
  Method: Image#sharpen(radius=0, sigma=1)
6577
6731
  Purpose: sharpens an image
@@ -6630,6 +6784,36 @@ Image_sharpen_channel(int argc, VALUE *argv, VALUE self)
6630
6784
  #endif
6631
6785
  }
6632
6786
 
6787
+
6788
+ /*
6789
+ Method: Image#shave(width, height)
6790
+ Image#shave!(width, height)
6791
+ Purpose: shaves pixels from the image edges, leaving a rectangle
6792
+ of the specified width & height in the center
6793
+ Returns: shave: a new image
6794
+ shave!: self, shaved
6795
+ */
6796
+ VALUE
6797
+ Image_shave(
6798
+ VALUE self,
6799
+ VALUE width,
6800
+ VALUE height)
6801
+ {
6802
+ return xform_image(False, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
6803
+ }
6804
+
6805
+
6806
+ VALUE
6807
+ Image_shave_bang(
6808
+ VALUE self,
6809
+ VALUE width,
6810
+ VALUE height)
6811
+ {
6812
+ rm_check_frozen(self);
6813
+ return xform_image(True, self, INT2FIX(0), INT2FIX(0), width, height, ShaveImage);
6814
+ }
6815
+
6816
+
6633
6817
  /*
6634
6818
  Method: Image#shear(x_shear, y_shear)
6635
6819
  Purpose: Calls ShearImage
@@ -6799,6 +6983,7 @@ Image_spaceship(VALUE self, VALUE other)
6799
6983
  }
6800
6984
 
6801
6985
  res = memcmp(sigA->value, sigB->value, 64);
6986
+ res = res > 0 ? 1 : (res < 0 ? -1 : 0); // reduce to 1, -1, 0
6802
6987
 
6803
6988
  return INT2FIX(res);
6804
6989
  }
@@ -7002,7 +7187,11 @@ Image_class_type(VALUE self)
7002
7187
  Image *image;
7003
7188
  Data_Get_Struct(self, Image, image);
7004
7189
 
7190
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
7191
+ return ClassType_new(image->storage_class);
7192
+ #else
7005
7193
  return ClassType_new(image->class);
7194
+ #endif
7006
7195
  }
7007
7196
 
7008
7197
  /*
@@ -7021,20 +7210,32 @@ Image_class_type_eq(VALUE self, VALUE new_class_type)
7021
7210
  Data_Get_Struct(self, Image, image);
7022
7211
  VALUE_TO_ENUM(new_class_type, class_type, ClassType);
7023
7212
 
7213
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
7214
+ if (image->storage_class == PseudoClass && class_type == DirectClass)
7215
+ #else
7024
7216
  if (image->class == PseudoClass && class_type == DirectClass)
7217
+ #endif
7025
7218
  {
7026
7219
  SyncImage(image);
7027
7220
  magick_free(image->colormap);
7028
7221
  image->colormap = NULL;
7029
7222
  }
7223
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
7224
+ else if (image->storage_class == DirectClass && class_type == PseudoClass)
7225
+ #else
7030
7226
  else if (image->class == DirectClass && class_type == PseudoClass)
7227
+ #endif
7031
7228
  {
7032
7229
  GetQuantizeInfo(&qinfo);
7033
7230
  qinfo.number_colors = MaxRGB+1;
7034
7231
  QuantizeImage(&qinfo, image);
7035
7232
  }
7036
7233
 
7234
+ #if defined(HAVE_IMAGE_STORAGE_CLASS)
7235
+ image->storage_class = class_type;
7236
+ #else
7037
7237
  image->class = class_type;
7238
+ #endif
7038
7239
  return self;
7039
7240
  }
7040
7241
 
@@ -7074,6 +7275,9 @@ Image_store_pixels(
7074
7275
  , cols, rows, x, y);
7075
7276
  }
7076
7277
 
7278
+ size = cols * rows;
7279
+ rm_check_ary_len(new_pixels, size);
7280
+
7077
7281
  SetImageType(image, TrueColorType);
7078
7282
 
7079
7283
  // Get a pointer to the pixels. Replace the values with the PixelPackets
@@ -7081,7 +7285,6 @@ Image_store_pixels(
7081
7285
  pixels = GetImagePixels(image, x, y, cols, rows);
7082
7286
  if (pixels)
7083
7287
  {
7084
- size = cols * rows;
7085
7288
  for (n = 0; n < size; n++)
7086
7289
  {
7087
7290
  new_pixel = rb_ary_entry(new_pixels, n);
@@ -7570,7 +7773,17 @@ Image_to_color(VALUE self, VALUE pixel_arg)
7570
7773
 
7571
7774
  }
7572
7775
 
7573
- DEF_ATTR_READER(Image, total_colors, ulong)
7776
+ /*
7777
+ Method: Image#total_colors
7778
+ Purpose: alias for Image#number_colors
7779
+ Notes: This used to be a direct reference to the `total_colors' field in Image
7780
+ but that field is not reliable.
7781
+ */
7782
+ VALUE
7783
+ Image_total_colors(VALUE self)
7784
+ {
7785
+ return Image_number_colors(self);
7786
+ }
7574
7787
 
7575
7788
  /*
7576
7789
  Method: Image#transparent(color-name<, opacity>)
@@ -7674,6 +7887,8 @@ Image_trim_bang(VALUE self)
7674
7887
  Method: Image#image_type=(type)
7675
7888
  Purpose: Call SetImageType to set the type of the image
7676
7889
  Note: Can't use type & type= b/c of Object#type.
7890
+ This setter is useless. Leave for backward compatibility
7891
+ but don't document it.
7677
7892
  */
7678
7893
  VALUE Image_image_type_eq(VALUE self, VALUE type)
7679
7894
  {
@@ -7966,6 +8181,9 @@ DEF_ATTR_ACCESSOR(Image, x_resolution, dbl)
7966
8181
  gravity, x, y, width, height
7967
8182
  If the 2nd or 3rd, compute new x, y values.
7968
8183
 
8184
+ The argument list can have a trailing true, false, or nil argument.
8185
+ If present and true, after cropping reset the page fields in the image.
8186
+
7969
8187
  Call xform_image to do the cropping.
7970
8188
  */
7971
8189
  static VALUE
@@ -7974,9 +8192,27 @@ cropper(int bang, int argc, VALUE *argv, VALUE self)
7974
8192
  volatile VALUE x, y, width, height;
7975
8193
  unsigned long nx = 0, ny = 0;
7976
8194
  unsigned long columns, rows;
8195
+ int reset_page = 0;
7977
8196
  GravityType gravity;
7978
8197
  MagickEnum *magick_enum;
7979
8198
  Image *image;
8199
+ VALUE cropped;
8200
+
8201
+ // Check for a "reset page" trailing argument.
8202
+ if (argc >= 1)
8203
+ {
8204
+ switch (TYPE(argv[argc-1]))
8205
+ {
8206
+ case T_TRUE:
8207
+ reset_page = 1;
8208
+ // fall thru
8209
+ case T_FALSE:
8210
+ case T_NIL:
8211
+ argc -= 1;
8212
+ default:
8213
+ break;
8214
+ }
8215
+ }
7980
8216
 
7981
8217
  switch (argc)
7982
8218
  {
@@ -8083,11 +8319,25 @@ cropper(int bang, int argc, VALUE *argv, VALUE self)
8083
8319
  y = ULONG2NUM(ny);
8084
8320
  break;
8085
8321
  default:
8086
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 3, 4, or 5)", argc);
8322
+ if (reset_page)
8323
+ {
8324
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 4, 5, or 6)", argc);
8325
+ }
8326
+ else
8327
+ {
8328
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 3, 4, or 5)", argc);
8329
+ }
8087
8330
  break;
8088
8331
  }
8089
8332
 
8090
- return xform_image(bang, self, x, y, width, height, CropImage);
8333
+ cropped = xform_image(bang, self, x, y, width, height, CropImage);
8334
+ if (reset_page)
8335
+ {
8336
+ Data_Get_Struct(cropped, Image, image);
8337
+ image->page.x = image->page.y = 0L;
8338
+ image->page.width = image->page.height = 0UL;
8339
+ }
8340
+ return cropped;
8091
8341
  }
8092
8342
 
8093
8343
 
@@ -8183,12 +8433,12 @@ static ChannelType extract_channels(
8183
8433
 
8184
8434
  /*
8185
8435
  Static: raise_ChannelType_error
8186
- Purpose: raise ArgumentError when an non-ChannelType object
8436
+ Purpose: raise TypeError when an non-ChannelType object
8187
8437
  is unexpectedly encountered
8188
8438
  */
8189
8439
  static void
8190
8440
  raise_ChannelType_error(VALUE arg)
8191
8441
  {
8192
- rb_raise(rb_eArgError, "argument needs to be a ChannelType (%s given)"
8442
+ rb_raise(rb_eTypeError, "argument needs to be a ChannelType (%s given)"
8193
8443
  , rb_class2name(CLASS_OF(arg)));
8194
8444
  }