rmagick 2.13.4 → 2.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.

Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -0
  3. data/.rubocop.yml +284 -0
  4. data/.simplecov +27 -0
  5. data/.travis.yml +24 -12
  6. data/ChangeLog +12 -0
  7. data/Gemfile +3 -0
  8. data/README.textile +63 -1
  9. data/Rakefile +53 -44
  10. data/before_install_linux.sh +15 -6
  11. data/deprecated/RMagick.rb +6 -0
  12. data/doc/comtasks.html +1 -1
  13. data/doc/ex/InitialCoords.rb +16 -17
  14. data/doc/ex/NewCoordSys.rb +21 -23
  15. data/doc/ex/OrigCoordSys.rb +8 -10
  16. data/doc/ex/PreserveAspectRatio.rb +182 -183
  17. data/doc/ex/RotateScale.rb +27 -28
  18. data/doc/ex/Skew.rb +27 -27
  19. data/doc/ex/Use01.rb +7 -8
  20. data/doc/ex/Use02.rb +11 -12
  21. data/doc/ex/Use03.rb +7 -7
  22. data/doc/ex/ViewBox.rb +16 -18
  23. data/doc/ex/adaptive_threshold.rb +4 -5
  24. data/doc/ex/add_noise.rb +4 -5
  25. data/doc/ex/affine.rb +4 -4
  26. data/doc/ex/affine_transform.rb +4 -4
  27. data/doc/ex/arc.rb +10 -10
  28. data/doc/ex/arcpath.rb +7 -8
  29. data/doc/ex/arcs01.rb +10 -10
  30. data/doc/ex/arcs02.rb +36 -38
  31. data/doc/ex/average.rb +5 -5
  32. data/doc/ex/axes.rb +4 -4
  33. data/doc/ex/baseline_shift01.rb +11 -12
  34. data/doc/ex/bilevel_channel.rb +2 -3
  35. data/doc/ex/blur_image.rb +2 -2
  36. data/doc/ex/border.rb +2 -2
  37. data/doc/ex/bounding_box.rb +9 -11
  38. data/doc/ex/cbezier1.rb +3 -4
  39. data/doc/ex/cbezier2.rb +3 -4
  40. data/doc/ex/cbezier3.rb +3 -4
  41. data/doc/ex/cbezier4.rb +3 -4
  42. data/doc/ex/cbezier5.rb +2 -3
  43. data/doc/ex/cbezier6.rb +3 -3
  44. data/doc/ex/channel.rb +10 -11
  45. data/doc/ex/charcoal.rb +2 -2
  46. data/doc/ex/chop.rb +2 -2
  47. data/doc/ex/circle.rb +3 -3
  48. data/doc/ex/circle01.rb +6 -7
  49. data/doc/ex/clip_path.rb +12 -12
  50. data/doc/ex/coalesce.rb +32 -35
  51. data/doc/ex/color_fill_to_border.rb +6 -6
  52. data/doc/ex/color_floodfill.rb +2 -2
  53. data/doc/ex/color_histogram.rb +17 -18
  54. data/doc/ex/color_reset.rb +2 -2
  55. data/doc/ex/colorize.rb +2 -2
  56. data/doc/ex/colors.rb +30 -30
  57. data/doc/ex/compose_mask.rb +8 -9
  58. data/doc/ex/composite.rb +14 -16
  59. data/doc/ex/composite_layers.rb +15 -15
  60. data/doc/ex/composite_tiled.rb +5 -7
  61. data/doc/ex/contrast.rb +12 -12
  62. data/doc/ex/crop.rb +3 -3
  63. data/doc/ex/crop_with_gravity.rb +13 -17
  64. data/doc/ex/cubic01.rb +18 -20
  65. data/doc/ex/cubic02.rb +64 -67
  66. data/doc/ex/cycle_colormap.rb +2 -2
  67. data/doc/ex/dissolve.rb +2 -3
  68. data/doc/ex/drawcomp.rb +34 -34
  69. data/doc/ex/drop_shadow.rb +8 -8
  70. data/doc/ex/edge.rb +2 -2
  71. data/doc/ex/ellipse.rb +4 -4
  72. data/doc/ex/ellipse01.rb +10 -11
  73. data/doc/ex/emboss.rb +2 -2
  74. data/doc/ex/enhance.rb +2 -2
  75. data/doc/ex/equalize.rb +2 -2
  76. data/doc/ex/evenodd.rb +31 -32
  77. data/doc/ex/fill_pattern.rb +12 -15
  78. data/doc/ex/flatten_images.rb +13 -13
  79. data/doc/ex/flip.rb +2 -2
  80. data/doc/ex/flop.rb +2 -2
  81. data/doc/ex/font_styles.rb +21 -23
  82. data/doc/ex/fonts.rb +14 -14
  83. data/doc/ex/frame.rb +3 -3
  84. data/doc/ex/gaussian_blur.rb +2 -2
  85. data/doc/ex/get_multiline_type_metrics.rb +6 -7
  86. data/doc/ex/get_pixels.rb +13 -14
  87. data/doc/ex/get_type_metrics.rb +26 -31
  88. data/doc/ex/gradientfill.rb +12 -12
  89. data/doc/ex/grav.rb +4 -5
  90. data/doc/ex/gravity.rb +59 -59
  91. data/doc/ex/group.rb +16 -16
  92. data/doc/ex/hatchfill.rb +10 -10
  93. data/doc/ex/image.rb +29 -31
  94. data/doc/ex/implode.rb +17 -17
  95. data/doc/ex/level.rb +2 -2
  96. data/doc/ex/level_colors.rb +4 -4
  97. data/doc/ex/line.rb +4 -5
  98. data/doc/ex/line01.rb +11 -13
  99. data/doc/ex/mask.rb +7 -8
  100. data/doc/ex/matte_fill_to_border.rb +2 -3
  101. data/doc/ex/matte_floodfill.rb +2 -3
  102. data/doc/ex/matte_replace.rb +2 -3
  103. data/doc/ex/median_filter.rb +2 -2
  104. data/doc/ex/modulate.rb +2 -2
  105. data/doc/ex/mono.rb +4 -4
  106. data/doc/ex/morph.rb +7 -8
  107. data/doc/ex/mosaic.rb +15 -15
  108. data/doc/ex/motion_blur.rb +2 -2
  109. data/doc/ex/negate.rb +2 -2
  110. data/doc/ex/negate_channel.rb +2 -2
  111. data/doc/ex/nested_rvg.rb +11 -11
  112. data/doc/ex/nonzero.rb +31 -32
  113. data/doc/ex/normalize.rb +2 -2
  114. data/doc/ex/oil_paint.rb +2 -2
  115. data/doc/ex/opacity.rb +3 -3
  116. data/doc/ex/ordered_dither.rb +2 -2
  117. data/doc/ex/path.rb +4 -5
  118. data/doc/ex/pattern1.rb +10 -10
  119. data/doc/ex/pattern2.rb +7 -7
  120. data/doc/ex/polaroid.rb +13 -14
  121. data/doc/ex/polygon.rb +6 -7
  122. data/doc/ex/polygon01.rb +10 -12
  123. data/doc/ex/polyline.rb +4 -5
  124. data/doc/ex/polyline01.rb +11 -13
  125. data/doc/ex/posterize.rb +2 -2
  126. data/doc/ex/preview.rb +3 -4
  127. data/doc/ex/qbezierpath.rb +5 -5
  128. data/doc/ex/quad01.rb +23 -25
  129. data/doc/ex/quantize-m.rb +5 -5
  130. data/doc/ex/radial_blur.rb +2 -2
  131. data/doc/ex/raise.rb +2 -2
  132. data/doc/ex/random_threshold_channel.rb +2 -2
  133. data/doc/ex/rect01.rb +6 -7
  134. data/doc/ex/rect02.rb +9 -11
  135. data/doc/ex/rectangle.rb +3 -4
  136. data/doc/ex/reduce_noise.rb +2 -2
  137. data/doc/ex/remap.rb +7 -8
  138. data/doc/ex/remap_images.rb +9 -11
  139. data/doc/ex/resize_to_fill.rb +3 -5
  140. data/doc/ex/resize_to_fit.rb +3 -5
  141. data/doc/ex/roll.rb +2 -2
  142. data/doc/ex/rotate.rb +6 -7
  143. data/doc/ex/rotate_f.rb +2 -2
  144. data/doc/ex/roundrect.rb +4 -5
  145. data/doc/ex/rubyname.rb +11 -11
  146. data/doc/ex/rvg_clippath.rb +5 -7
  147. data/doc/ex/rvg_linecap.rb +25 -26
  148. data/doc/ex/rvg_linejoin.rb +25 -26
  149. data/doc/ex/rvg_opacity.rb +10 -11
  150. data/doc/ex/rvg_pattern.rb +15 -15
  151. data/doc/ex/rvg_stroke_dasharray.rb +6 -7
  152. data/doc/ex/segment.rb +2 -2
  153. data/doc/ex/sepiatone.rb +2 -3
  154. data/doc/ex/shade.rb +2 -2
  155. data/doc/ex/shadow.rb +2 -3
  156. data/doc/ex/shave.rb +2 -2
  157. data/doc/ex/shear.rb +3 -3
  158. data/doc/ex/sketch.rb +2 -3
  159. data/doc/ex/skewx.rb +4 -5
  160. data/doc/ex/skewy.rb +3 -3
  161. data/doc/ex/smile.rb +113 -113
  162. data/doc/ex/solarize.rb +2 -2
  163. data/doc/ex/sparse_color.rb +29 -30
  164. data/doc/ex/splice.rb +2 -3
  165. data/doc/ex/spread.rb +2 -2
  166. data/doc/ex/stegano.rb +34 -34
  167. data/doc/ex/stroke_dasharray.rb +4 -5
  168. data/doc/ex/stroke_fill.rb +4 -5
  169. data/doc/ex/stroke_linecap.rb +3 -3
  170. data/doc/ex/stroke_linejoin.rb +3 -3
  171. data/doc/ex/stroke_width.rb +3 -3
  172. data/doc/ex/swirl.rb +2 -2
  173. data/doc/ex/text.rb +4 -4
  174. data/doc/ex/text01.rb +7 -8
  175. data/doc/ex/text_align.rb +2 -2
  176. data/doc/ex/text_antialias.rb +9 -10
  177. data/doc/ex/text_styles.rb +11 -13
  178. data/doc/ex/text_undercolor.rb +2 -2
  179. data/doc/ex/texture_fill_to_border.rb +6 -6
  180. data/doc/ex/texture_floodfill.rb +2 -2
  181. data/doc/ex/texturefill.rb +10 -11
  182. data/doc/ex/threshold.rb +2 -2
  183. data/doc/ex/to_blob.rb +4 -5
  184. data/doc/ex/translate.rb +6 -6
  185. data/doc/ex/transparent.rb +5 -5
  186. data/doc/ex/transpose.rb +2 -2
  187. data/doc/ex/transverse.rb +2 -2
  188. data/doc/ex/tref01.rb +11 -12
  189. data/doc/ex/triangle01.rb +5 -6
  190. data/doc/ex/trim.rb +1 -2
  191. data/doc/ex/tspan01.rb +8 -9
  192. data/doc/ex/tspan02.rb +9 -11
  193. data/doc/ex/tspan03.rb +9 -11
  194. data/doc/ex/unsharp_mask.rb +2 -2
  195. data/doc/ex/viewex.rb +19 -21
  196. data/doc/ex/vignette.rb +2 -2
  197. data/doc/ex/watermark.rb +15 -16
  198. data/doc/ex/wave.rb +2 -2
  199. data/doc/ex/wet_floor.rb +17 -18
  200. data/doc/ex/writing_mode01.rb +8 -9
  201. data/doc/ex/writing_mode02.rb +8 -8
  202. data/doc/ilist.html +1 -1
  203. data/doc/usage.html +8 -8
  204. data/examples/constitute.rb +6 -6
  205. data/examples/crop_with_gravity.rb +13 -17
  206. data/examples/demo.rb +305 -305
  207. data/examples/describe.rb +27 -28
  208. data/examples/find_similar_region.rb +16 -16
  209. data/examples/histogram.rb +228 -232
  210. data/examples/identify.rb +165 -167
  211. data/examples/image_opacity.rb +4 -4
  212. data/examples/import_export.rb +10 -10
  213. data/examples/pattern_fill.rb +9 -9
  214. data/examples/rotating_text.rb +13 -14
  215. data/examples/spinner.rb +18 -19
  216. data/examples/thumbnail.rb +13 -14
  217. data/examples/vignette.rb +8 -9
  218. data/ext/RMagick/extconf.rb +417 -370
  219. data/ext/RMagick/rmdraw.c +14 -7
  220. data/ext/RMagick/rmenum.c +5 -3
  221. data/ext/RMagick/rmmain.c +8 -3
  222. data/ext/RMagick/rmutil.c +2 -2
  223. data/lib/rmagick/version.rb +3 -3
  224. data/lib/rmagick_internal.rb +1782 -1801
  225. data/lib/rvg/clippath.rb +2 -6
  226. data/lib/rvg/container.rb +4 -12
  227. data/lib/rvg/deep_equal.rb +5 -7
  228. data/lib/rvg/describable.rb +0 -5
  229. data/lib/rvg/embellishable.rb +24 -54
  230. data/lib/rvg/misc.rb +55 -87
  231. data/lib/rvg/paint.rb +0 -4
  232. data/lib/rvg/pathdata.rb +12 -16
  233. data/lib/rvg/rvg.rb +21 -24
  234. data/lib/rvg/stretchable.rb +14 -18
  235. data/lib/rvg/stylable.rb +7 -14
  236. data/lib/rvg/text.rb +9 -23
  237. data/lib/rvg/transformable.rb +2 -9
  238. data/lib/rvg/units.rb +1 -4
  239. data/rmagick.gemspec +23 -14
  240. data/test/Draw.rb +22 -24
  241. data/test/Image1.rb +78 -82
  242. data/test/Image2.rb +143 -151
  243. data/test/Image3.rb +47 -50
  244. data/test/ImageList1.rb +7 -9
  245. data/test/ImageList2.rb +30 -34
  246. data/test/Image_attributes.rb +23 -26
  247. data/test/Import_Export.rb +25 -30
  248. data/test/Info.rb +66 -70
  249. data/test/Magick.rb +45 -50
  250. data/test/Pixel.rb +10 -13
  251. data/test/Preview.rb +6 -11
  252. data/test/test_all_basic.rb +15 -10
  253. data/test/tmpnam_test.rb +3 -3
  254. metadata +24 -6
  255. data/lib/RMagick.rb +0 -1
@@ -1930,21 +1930,28 @@ get_type_metrics(
1930
1930
  case 1: // use default image
1931
1931
  text = rm_str2cstr(argv[0], &text_l);
1932
1932
 
1933
- for (x = 0; x < text_l; x++)
1933
+ for (x = 0; x < text_l-1; x++)
1934
1934
  {
1935
1935
  // Ensure text string doesn't refer to image attributes.
1936
- if (text[x] == '%' && x < text_l-1)
1936
+ if (text[x] == '%')
1937
1937
  {
1938
1938
  int y;
1939
1939
  char spec = text[x+1];
1940
1940
 
1941
- for (y = 0; y < ATTRS_L; y++)
1941
+ if (spec == '%')
1942
1942
  {
1943
- if (spec == attrs[y])
1943
+ x++;
1944
+ }
1945
+ else
1946
+ {
1947
+ for (y = 0; y < ATTRS_L; y++)
1944
1948
  {
1945
- rb_raise(rb_eArgError,
1946
- "text string contains image attribute reference `%%%c'",
1947
- spec);
1949
+ if (spec == attrs[y])
1950
+ {
1951
+ rb_raise(rb_eArgError,
1952
+ "text string contains image attribute reference `%%%c'",
1953
+ spec);
1954
+ }
1948
1955
  }
1949
1956
  }
1950
1957
  }
@@ -176,6 +176,10 @@ Enum_spaceship(VALUE self, VALUE other)
176
176
  {
177
177
  MagickEnum *this, *that;
178
178
 
179
+ if(CLASS_OF(self) != CLASS_OF(other)) {
180
+ return Qnil;
181
+ }
182
+
179
183
  Data_Get_Struct(self, MagickEnum, this);
180
184
  Data_Get_Struct(other, MagickEnum, that);
181
185
 
@@ -188,9 +192,7 @@ Enum_spaceship(VALUE self, VALUE other)
188
192
  return INT2FIX(-1);
189
193
  }
190
194
 
191
- // Values are equal, check class.
192
-
193
- return rb_funcall(CLASS_OF(self), rm_ID_spaceship, 1, CLASS_OF(other));
195
+ return INT2FIX(0);
194
196
  }
195
197
 
196
198
 
@@ -1254,9 +1254,7 @@ Init_RMagick2(void)
1254
1254
  ENUMERATOR(IntegerInterpolatePixel)
1255
1255
  ENUMERATOR(MeshInterpolatePixel)
1256
1256
  ENUMERATOR(NearestNeighborInterpolatePixel)
1257
- #if defined(HAVE_SPLINEINTERPOLATEPIXEL)
1258
1257
  ENUMERATOR(SplineInterpolatePixel)
1259
- #endif
1260
1258
  END_ENUM
1261
1259
 
1262
1260
  #if defined(HAVE_TYPE_MAGICKFUNCTION)
@@ -1719,9 +1717,16 @@ features_constant(void)
1719
1717
  volatile VALUE features;
1720
1718
 
1721
1719
  #if defined(HAVE_GETMAGICKFEATURES)
1720
+ // 6.5.7 - latest (7.0.0)
1722
1721
  features = rb_str_new2(GetMagickFeatures());
1723
- #else
1722
+ #elif defined(MagickFeatures)
1723
+ // 6.5.7 - latest (7.0.0)
1724
+ features = rb_str_new2(MagickFeatures);
1725
+ #elif defined(MagickSupport)
1726
+ // 6.5.5 - 6.5.6
1724
1727
  features = rb_str_new2(MagickSupport);
1728
+ #else
1729
+ features = rb_str_new("unknown", 7);
1725
1730
  #endif
1726
1731
 
1727
1732
  rb_obj_freeze(features);
@@ -1609,7 +1609,7 @@ rm_error_handler(const ExceptionType severity, const char *reason, const char *d
1609
1609
  void
1610
1610
  rm_fatal_error_handler(const ExceptionType severity, const char *reason, const char *description)
1611
1611
  {
1612
- rb_raise(Class_FatalImageMagickError, GetLocaleExceptionMessage(severity, reason));
1612
+ rb_raise(Class_FatalImageMagickError, "%s", GetLocaleExceptionMessage(severity, reason));
1613
1613
  description = description;
1614
1614
  }
1615
1615
 
@@ -1649,7 +1649,7 @@ handle_exception(ExceptionInfo *exception, Image *imglist, ErrorRetention retent
1649
1649
  exception->description ? ": " : "",
1650
1650
  exception->description ? GetLocaleExceptionMessage(exception->severity, exception->description) : "");
1651
1651
  msg[sizeof(msg)-1] = '\0';
1652
- rb_warning(msg);
1652
+ rb_warning("%s", msg);
1653
1653
 
1654
1654
  // Caller deletes ExceptionInfo...
1655
1655
 
@@ -1,5 +1,5 @@
1
1
  module Magick
2
- VERSION = "2.13.4"
3
- MIN_RUBY_VERSION = "1.8.5"
4
- MIN_IM_VERSION = "6.4.9"
2
+ VERSION = '2.14.0'
3
+ MIN_RUBY_VERSION = '1.8.5'
4
+ MIN_IM_VERSION = '6.4.9'
5
5
  end
@@ -8,1091 +8,1085 @@
8
8
  # to the classes.
9
9
  #==============================================================================
10
10
 
11
+ require 'English'
11
12
  require 'RMagick2.so'
12
13
 
13
14
  module Magick
14
- @formats = nil
15
- @trace_proc = nil
16
- @exit_block_set_up = nil
15
+ @formats = nil
16
+ @trace_proc = nil
17
+ @exit_block_set_up = nil
17
18
 
18
- class << self
19
+ class << self
19
20
  def formats(&block)
20
- @formats ||= init_formats()
21
- if block_given?
22
- @formats.each { |k,v| yield k, v }
23
- self
24
- else
25
- @formats
26
- end
21
+ @formats ||= init_formats
22
+
23
+ if block_given?
24
+ @formats.each{|k, v| yield k, v }
25
+ self
26
+ else
27
+ @formats
28
+ end
27
29
  end
28
30
 
29
31
  # remove reference to the proc at exit
30
32
  def trace_proc=(p)
31
- if @trace_proc.nil? && !p.nil? && !@exit_block_set_up
32
- at_exit { @trace_proc = nil }
33
- @exit_block_set_up = true
34
- end
35
- @trace_proc = p
36
- end
37
- end
38
-
39
- # Geometry class and related enum constants
40
- class GeometryValue < Enum
41
- # no methods
42
- end
43
-
44
- PercentGeometry = GeometryValue.new(:PercentGeometry, 1).freeze
45
- AspectGeometry = GeometryValue.new(:AspectGeometry, 2).freeze
46
- LessGeometry = GeometryValue.new(:LessGeometry, 3).freeze
47
- GreaterGeometry = GeometryValue.new(:GreaterGeometry, 4).freeze
48
- AreaGeometry = GeometryValue.new(:AreaGeometry, 5).freeze
49
- MinimumGeometry = GeometryValue.new(:MinimumGeometry, 6).freeze
50
-
51
- class Geometry
52
- FLAGS = ['', '%', '!', '<', '>', '@', '^']
53
- RFLAGS = { '%' => PercentGeometry,
54
- '!' => AspectGeometry,
55
- '<' => LessGeometry,
56
- '>' => GreaterGeometry,
57
- '@' => AreaGeometry,
58
- '^' => MinimumGeometry }
59
-
60
- attr_accessor :width, :height, :x, :y, :flag
61
-
62
- def initialize(width=nil, height=nil, x=nil, y=nil, flag=nil)
63
- raise(ArgumentError, "width set to #{width.to_s}") if width.is_a? GeometryValue
64
- raise(ArgumentError, "height set to #{height.to_s}") if height.is_a? GeometryValue
65
- raise(ArgumentError, "x set to #{x.to_s}") if x.is_a? GeometryValue
66
- raise(ArgumentError, "y set to #{y.to_s}") if y.is_a? GeometryValue
67
-
68
- # Support floating-point width and height arguments so Geometry
69
- # objects can be used to specify Image#density= arguments.
70
- if width == nil
71
- @width = 0
72
- elsif width.to_f >= 0.0
73
- @width = width.to_f
74
- else
75
- Kernel.raise ArgumentError, "width must be >= 0: #{width}"
76
- end
77
- if height == nil
78
- @height = 0
79
- elsif height.to_f >= 0.0
80
- @height = height.to_f
81
- else
82
- Kernel.raise ArgumentError, "height must be >= 0: #{height}"
83
- end
84
-
85
- @x = x.to_i
86
- @y = y.to_i
87
- @flag = flag
88
-
89
- end
90
-
91
- # Construct an object from a geometry string
92
- W = /(\d+\.\d+%?)|(\d*%?)/
93
- H = W
94
- X = /(?:([-+]\d+))?/
95
- Y = X
96
- RE = /\A#{W}x?#{H}#{X}#{Y}([!<>@\^]?)\Z/
97
-
98
- def Geometry.from_s(str)
99
-
100
- m = RE.match(str)
101
- if m
102
- width = (m[1] || m[2]).to_f
103
- height = (m[3] || m[4]).to_f
104
- x = m[5].to_i
105
- y = m[6].to_i
106
- flag = RFLAGS[m[7]]
107
- else
108
- Kernel.raise ArgumentError, "invalid geometry format"
109
- end
110
- if str['%']
111
- flag = PercentGeometry
112
- end
113
- Geometry.new(width, height, x, y, flag)
114
- end
115
-
116
- # Convert object to a geometry string
117
- def to_s
118
- str = ''
119
- if @width > 0
120
- fmt = @width.truncate == @width ? "%d" : "%.2f"
121
- str << sprintf(fmt, @width)
122
- str << '%' if @flag == PercentGeometry
123
- end
124
-
125
- if (@width > 0 && @flag != PercentGeometry) || (@height > 0)
126
- str << 'x'
127
- end
33
+ if @trace_proc.nil? && !p.nil? && !@exit_block_set_up
34
+ at_exit { @trace_proc = nil }
35
+ @exit_block_set_up = true
36
+ end
128
37
 
129
- if @height > 0
130
- fmt = @height.truncate == @height ? "%d" : "%.2f"
131
- str << sprintf(fmt, @height)
132
- str << '%' if @flag == PercentGeometry
133
- end
134
- str << sprintf("%+d%+d", @x, @y) if (@x != 0 || @y != 0)
135
- if @flag != PercentGeometry
136
- str << FLAGS[@flag.to_i]
38
+ @trace_proc = p
39
+ end
40
+ end
41
+
42
+ # Geometry class and related enum constants
43
+ class GeometryValue < Enum
44
+ # no methods
45
+ end
46
+
47
+ PercentGeometry = GeometryValue.new(:PercentGeometry, 1).freeze
48
+ AspectGeometry = GeometryValue.new(:AspectGeometry, 2).freeze
49
+ LessGeometry = GeometryValue.new(:LessGeometry, 3).freeze
50
+ GreaterGeometry = GeometryValue.new(:GreaterGeometry, 4).freeze
51
+ AreaGeometry = GeometryValue.new(:AreaGeometry, 5).freeze
52
+ MinimumGeometry = GeometryValue.new(:MinimumGeometry, 6).freeze
53
+
54
+ class Geometry
55
+ FLAGS = ['', '%', '!', '<', '>', '@', '^']
56
+ RFLAGS = { '%' => PercentGeometry,
57
+ '!' => AspectGeometry,
58
+ '<' => LessGeometry,
59
+ '>' => GreaterGeometry,
60
+ '@' => AreaGeometry,
61
+ '^' => MinimumGeometry }
62
+
63
+ attr_accessor :width, :height, :x, :y, :flag
64
+
65
+ def initialize(width=nil, height=nil, x=nil, y=nil, flag=nil)
66
+ fail(ArgumentError, "width set to #{width}") if width.is_a? GeometryValue
67
+ fail(ArgumentError, "height set to #{height}") if height.is_a? GeometryValue
68
+ fail(ArgumentError, "x set to #{x}") if x.is_a? GeometryValue
69
+ fail(ArgumentError, "y set to #{y}") if y.is_a? GeometryValue
70
+
71
+ # Support floating-point width and height arguments so Geometry
72
+ # objects can be used to specify Image#density= arguments.
73
+ if width.nil?
74
+ @width = 0
75
+ elsif width.to_f >= 0.0
76
+ @width = width.to_f
77
+ else
78
+ Kernel.raise ArgumentError, "width must be >= 0: #{width}"
79
+ end
80
+ if height.nil?
81
+ @height = 0
82
+ elsif height.to_f >= 0.0
83
+ @height = height.to_f
84
+ else
85
+ Kernel.raise ArgumentError, "height must be >= 0: #{height}"
86
+ end
87
+
88
+ @x = x.to_i
89
+ @y = y.to_i
90
+ @flag = flag
91
+ end
92
+
93
+ # Construct an object from a geometry string
94
+ W = /(\d+\.\d+%?)|(\d*%?)/
95
+ H = W
96
+ X = /(?:([-+]\d+))?/
97
+ Y = X
98
+ RE = /\A#{W}x?#{H}#{X}#{Y}([!<>@\^]?)\Z/
99
+
100
+ def self.from_s(str)
101
+ m = RE.match(str)
102
+ if m
103
+ width = (m[1] || m[2]).to_f
104
+ height = (m[3] || m[4]).to_f
105
+ x = m[5].to_i
106
+ y = m[6].to_i
107
+ flag = RFLAGS[m[7]]
108
+ else
109
+ Kernel.raise ArgumentError, 'invalid geometry format'
110
+ end
111
+ if str['%']
112
+ flag = PercentGeometry
113
+ end
114
+ Geometry.new(width, height, x, y, flag)
115
+ end
116
+
117
+ # Convert object to a geometry string
118
+ def to_s
119
+ str = ''
120
+ if @width > 0
121
+ fmt = @width.truncate == @width ? '%d' : '%.2f'
122
+ str << sprintf(fmt, @width)
123
+ str << '%' if @flag == PercentGeometry
124
+ end
125
+
126
+ if (@width > 0 && @flag != PercentGeometry) || (@height > 0)
127
+ str << 'x'
128
+ end
129
+
130
+ if @height > 0
131
+ fmt = @height.truncate == @height ? '%d' : '%.2f'
132
+ str << sprintf(fmt, @height)
133
+ str << '%' if @flag == PercentGeometry
134
+ end
135
+ str << sprintf('%+d%+d', @x, @y) if @x != 0 || @y != 0
136
+ if @flag != PercentGeometry
137
+ str << FLAGS[@flag.to_i]
138
+ end
139
+ str
137
140
  end
138
- str
139
141
  end
140
- end
141
-
142
-
143
- class Draw
144
142
 
145
- # Thse hashes are used to map Magick constant
146
- # values to the strings used in the primitives.
147
- ALIGN_TYPE_NAMES = {
143
+ class Draw
144
+ # Thse hashes are used to map Magick constant
145
+ # values to the strings used in the primitives.
146
+ ALIGN_TYPE_NAMES = {
148
147
  LeftAlign.to_i => 'left',
149
148
  RightAlign.to_i => 'right',
150
149
  CenterAlign.to_i => 'center'
150
+ }.freeze
151
+ ANCHOR_TYPE_NAMES = {
152
+ StartAnchor.to_i => 'start',
153
+ MiddleAnchor.to_i => 'middle',
154
+ EndAnchor.to_i => 'end'
151
155
  }.freeze
152
- ANCHOR_TYPE_NAMES = {
153
- StartAnchor.to_i => 'start',
154
- MiddleAnchor.to_i => 'middle',
155
- EndAnchor.to_i => 'end'
156
+ DECORATION_TYPE_NAMES = {
157
+ NoDecoration.to_i => 'none',
158
+ UnderlineDecoration.to_i => 'underline',
159
+ OverlineDecoration.to_i => 'overline',
160
+ LineThroughDecoration.to_i => 'line-through'
156
161
  }.freeze
157
- DECORATION_TYPE_NAMES = {
158
- NoDecoration.to_i => 'none',
159
- UnderlineDecoration.to_i => 'underline',
160
- OverlineDecoration.to_i => 'overline',
161
- LineThroughDecoration.to_i => 'line-through'
162
+ FONT_WEIGHT_NAMES = {
163
+ AnyWeight.to_i => 'all',
164
+ NormalWeight.to_i => 'normal',
165
+ BoldWeight.to_i => 'bold',
166
+ BolderWeight.to_i => 'bolder',
167
+ LighterWeight.to_i => 'lighter',
162
168
  }.freeze
163
- FONT_WEIGHT_NAMES = {
164
- AnyWeight.to_i => 'all',
165
- NormalWeight.to_i => 'normal',
166
- BoldWeight.to_i => 'bold',
167
- BolderWeight.to_i => 'bolder',
168
- LighterWeight.to_i => 'lighter',
169
+ GRAVITY_NAMES = {
170
+ NorthWestGravity.to_i => 'northwest',
171
+ NorthGravity.to_i => 'north',
172
+ NorthEastGravity.to_i => 'northeast',
173
+ WestGravity.to_i => 'west',
174
+ CenterGravity.to_i => 'center',
175
+ EastGravity.to_i => 'east',
176
+ SouthWestGravity.to_i => 'southwest',
177
+ SouthGravity.to_i => 'south',
178
+ SouthEastGravity.to_i => 'southeast'
169
179
  }.freeze
170
- GRAVITY_NAMES = {
171
- NorthWestGravity.to_i => 'northwest',
172
- NorthGravity.to_i => 'north',
173
- NorthEastGravity.to_i => 'northeast',
174
- WestGravity.to_i => 'west',
175
- CenterGravity.to_i => 'center',
176
- EastGravity.to_i => 'east',
177
- SouthWestGravity.to_i => 'southwest',
178
- SouthGravity.to_i => 'south',
179
- SouthEastGravity.to_i => 'southeast'
180
+ PAINT_METHOD_NAMES = {
181
+ PointMethod.to_i => 'point',
182
+ ReplaceMethod.to_i => 'replace',
183
+ FloodfillMethod.to_i => 'floodfill',
184
+ FillToBorderMethod.to_i => 'filltoborder',
185
+ ResetMethod.to_i => 'reset'
180
186
  }.freeze
181
- PAINT_METHOD_NAMES = {
182
- PointMethod.to_i => 'point',
183
- ReplaceMethod.to_i => 'replace',
184
- FloodfillMethod.to_i => 'floodfill',
185
- FillToBorderMethod.to_i => 'filltoborder',
186
- ResetMethod.to_i => 'reset'
187
+ STRETCH_TYPE_NAMES = {
188
+ NormalStretch.to_i => 'normal',
189
+ UltraCondensedStretch.to_i => 'ultra-condensed',
190
+ ExtraCondensedStretch.to_i => 'extra-condensed',
191
+ CondensedStretch.to_i => 'condensed',
192
+ SemiCondensedStretch.to_i => 'semi-condensed',
193
+ SemiExpandedStretch.to_i => 'semi-expanded',
194
+ ExpandedStretch.to_i => 'expanded',
195
+ ExtraExpandedStretch.to_i => 'extra-expanded',
196
+ UltraExpandedStretch.to_i => 'ultra-expanded',
197
+ AnyStretch.to_i => 'all'
187
198
  }.freeze
188
- STRETCH_TYPE_NAMES = {
189
- NormalStretch.to_i => 'normal',
190
- UltraCondensedStretch.to_i => 'ultra-condensed',
191
- ExtraCondensedStretch.to_i => 'extra-condensed',
192
- CondensedStretch.to_i => 'condensed',
193
- SemiCondensedStretch.to_i => 'semi-condensed',
194
- SemiExpandedStretch.to_i => 'semi-expanded',
195
- ExpandedStretch.to_i => 'expanded',
196
- ExtraExpandedStretch.to_i => 'extra-expanded',
197
- UltraExpandedStretch.to_i => 'ultra-expanded',
198
- AnyStretch.to_i => 'all'
199
+ STYLE_TYPE_NAMES = {
200
+ NormalStyle.to_i => 'normal',
201
+ ItalicStyle.to_i => 'italic',
202
+ ObliqueStyle.to_i => 'oblique',
203
+ AnyStyle.to_i => 'all'
199
204
  }.freeze
200
- STYLE_TYPE_NAMES = {
201
- NormalStyle.to_i => 'normal',
202
- ItalicStyle.to_i => 'italic',
203
- ObliqueStyle.to_i => 'oblique',
204
- AnyStyle.to_i => 'all'
205
- }.freeze
206
-
207
- private
208
- def enquote(str)
209
- if str.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(str)
205
+
206
+ private
207
+
208
+ def enquote(str)
209
+ if str.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(str)
210
210
  return str
211
- else
211
+ else
212
212
  return '"' + str + '"'
213
- end
214
- end
215
-
216
- public
217
-
218
- # Apply coordinate transformations to support scaling (s), rotation (r),
219
- # and translation (t). Angles are specified in radians.
220
- def affine(sx, rx, ry, sy, tx, ty)
221
- primitive "affine " + sprintf("%g,%g,%g,%g,%g,%g", sx, rx, ry, sy, tx, ty)
222
- end
223
-
224
- # Draw an arc.
225
- def arc(startX, startY, endX, endY, startDegrees, endDegrees)
226
- primitive "arc " + sprintf("%g,%g %g,%g %g,%g",
227
- startX, startY, endX, endY, startDegrees, endDegrees)
228
- end
229
-
230
- # Draw a bezier curve.
231
- def bezier(*points)
232
- if points.length == 0
233
- Kernel.raise ArgumentError, "no points specified"
234
- elsif points.length % 2 != 0
235
- Kernel.raise ArgumentError, "odd number of arguments specified"
236
- end
237
- primitive "bezier " + points.join(',')
238
- end
239
-
240
- # Draw a circle
241
- def circle(originX, originY, perimX, perimY)
242
- primitive "circle " + sprintf("%g,%g %g,%g", originX, originY, perimX, perimY)
243
- end
244
-
245
- # Invoke a clip-path defined by def_clip_path.
246
- def clip_path(name)
247
- primitive "clip-path #{name}"
248
- end
249
-
250
- # Define the clipping rule.
251
- def clip_rule(rule)
252
- if ( not ["evenodd", "nonzero"].include?(rule.downcase) )
213
+ end
214
+ end
215
+
216
+ public
217
+
218
+ # Apply coordinate transformations to support scaling (s), rotation (r),
219
+ # and translation (t). Angles are specified in radians.
220
+ def affine(sx, rx, ry, sy, tx, ty)
221
+ primitive 'affine ' + sprintf('%g,%g,%g,%g,%g,%g', sx, rx, ry, sy, tx, ty)
222
+ end
223
+
224
+ # Draw an arc.
225
+ def arc(startX, startY, endX, endY, startDegrees, endDegrees)
226
+ primitive 'arc ' + sprintf('%g,%g %g,%g %g,%g',
227
+ startX, startY, endX, endY, startDegrees, endDegrees)
228
+ end
229
+
230
+ # Draw a bezier curve.
231
+ def bezier(*points)
232
+ if points.length == 0
233
+ Kernel.raise ArgumentError, 'no points specified'
234
+ elsif points.length.odd?
235
+ Kernel.raise ArgumentError, 'odd number of arguments specified'
236
+ end
237
+ primitive 'bezier ' + points.join(',')
238
+ end
239
+
240
+ # Draw a circle
241
+ def circle(originX, originY, perimX, perimY)
242
+ primitive 'circle ' + sprintf('%g,%g %g,%g', originX, originY, perimX, perimY)
243
+ end
244
+
245
+ # Invoke a clip-path defined by def_clip_path.
246
+ def clip_path(name)
247
+ primitive "clip-path #{name}"
248
+ end
249
+
250
+ # Define the clipping rule.
251
+ def clip_rule(rule)
252
+ unless ['evenodd', 'nonzero'].include?(rule.downcase)
253
253
  Kernel.raise ArgumentError, "Unknown clipping rule #{rule}"
254
+ end
255
+ primitive "clip-rule #{rule}"
254
256
  end
255
- primitive "clip-rule #{rule}"
256
- end
257
-
258
- # Define the clip units
259
- def clip_units(unit)
260
- if ( not ["userspace", "userspaceonuse", "objectboundingbox"].include?(unit.downcase) )
257
+
258
+ # Define the clip units
259
+ def clip_units(unit)
260
+ unless ['userspace', 'userspaceonuse', 'objectboundingbox'].include?(unit.downcase)
261
261
  Kernel.raise ArgumentError, "Unknown clip unit #{unit}"
262
- end
263
- primitive "clip-units #{unit}"
264
- end
265
-
266
- # Set color in image according to specified colorization rule. Rule is one of
267
- # point, replace, floodfill, filltoborder,reset
268
- def color(x, y, method)
269
- if ( not PAINT_METHOD_NAMES.has_key?(method.to_i) )
262
+ end
263
+ primitive "clip-units #{unit}"
264
+ end
265
+
266
+ # Set color in image according to specified colorization rule. Rule is one of
267
+ # point, replace, floodfill, filltoborder,reset
268
+ def color(x, y, method)
269
+ unless PAINT_METHOD_NAMES.has_key?(method.to_i)
270
270
  Kernel.raise ArgumentError, "Unknown PaintMethod: #{method}"
271
- end
272
- primitive "color #{x},#{y},#{PAINT_METHOD_NAMES[method.to_i]}"
273
- end
274
-
275
- # Specify EITHER the text decoration (none, underline, overline,
276
- # line-through) OR the text solid background color (any color name or spec)
277
- def decorate(decoration)
278
- if ( DECORATION_TYPE_NAMES.has_key?(decoration.to_i) )
271
+ end
272
+ primitive "color #{x},#{y},#{PAINT_METHOD_NAMES[method.to_i]}"
273
+ end
274
+
275
+ # Specify EITHER the text decoration (none, underline, overline,
276
+ # line-through) OR the text solid background color (any color name or spec)
277
+ def decorate(decoration)
278
+ if DECORATION_TYPE_NAMES.has_key?(decoration.to_i)
279
279
  primitive "decorate #{DECORATION_TYPE_NAMES[decoration.to_i]}"
280
- else
280
+ else
281
281
  primitive "decorate #{enquote(decoration)}"
282
- end
283
- end
284
-
285
- # Define a clip-path. A clip-path is a sequence of primitives
286
- # bracketed by the "push clip-path <name>" and "pop clip-path"
287
- # primitives. Upon advice from the IM guys, we also bracket
288
- # the clip-path primitives with "push(pop) defs" and "push
289
- # (pop) graphic-context".
290
- def define_clip_path(name)
291
- begin
292
- push('defs')
293
- push('clip-path', name)
294
- push('graphic-context')
295
- yield
296
- ensure
297
- pop('graphic-context')
298
- pop('clip-path')
299
- pop('defs')
300
- end
301
- end
302
-
303
- # Draw an ellipse
304
- def ellipse(originX, originY, width, height, arcStart, arcEnd)
305
- primitive "ellipse " + sprintf("%g,%g %g,%g %g,%g",
306
- originX, originY, width, height, arcStart, arcEnd)
307
- end
308
-
309
- # Let anything through, but the only defined argument
310
- # is "UTF-8". All others are apparently ignored.
311
- def encoding(encoding)
312
- primitive "encoding #{encoding}"
313
- end
314
-
315
- # Specify object fill, a color name or pattern name
316
- def fill(colorspec)
317
- primitive "fill #{enquote(colorspec)}"
318
- end
319
- alias fill_color fill
320
- alias fill_pattern fill
321
-
322
- # Specify fill opacity (use "xx%" to indicate percentage)
323
- def fill_opacity(opacity)
324
- primitive "fill-opacity #{opacity}"
325
- end
326
-
327
- def fill_rule(rule)
328
- if ( not ["evenodd", "nonzero"].include?(rule.downcase) )
282
+ end
283
+ end
284
+
285
+ # Define a clip-path. A clip-path is a sequence of primitives
286
+ # bracketed by the "push clip-path <name>" and "pop clip-path"
287
+ # primitives. Upon advice from the IM guys, we also bracket
288
+ # the clip-path primitives with "push(pop) defs" and "push
289
+ # (pop) graphic-context".
290
+ def define_clip_path(name)
291
+ push('defs')
292
+ push('clip-path', name)
293
+ push('graphic-context')
294
+ yield
295
+ ensure
296
+ pop('graphic-context')
297
+ pop('clip-path')
298
+ pop('defs')
299
+ end
300
+
301
+ # Draw an ellipse
302
+ def ellipse(originX, originY, width, height, arcStart, arcEnd)
303
+ primitive 'ellipse ' + sprintf('%g,%g %g,%g %g,%g',
304
+ originX, originY, width, height, arcStart, arcEnd)
305
+ end
306
+
307
+ # Let anything through, but the only defined argument
308
+ # is "UTF-8". All others are apparently ignored.
309
+ def encoding(encoding)
310
+ primitive "encoding #{encoding}"
311
+ end
312
+
313
+ # Specify object fill, a color name or pattern name
314
+ def fill(colorspec)
315
+ primitive "fill #{enquote(colorspec)}"
316
+ end
317
+ alias_method :fill_color, :fill
318
+ alias_method :fill_pattern, :fill
319
+
320
+ # Specify fill opacity (use "xx%" to indicate percentage)
321
+ def fill_opacity(opacity)
322
+ primitive "fill-opacity #{opacity}"
323
+ end
324
+
325
+ def fill_rule(rule)
326
+ unless ['evenodd', 'nonzero'].include?(rule.downcase)
329
327
  Kernel.raise ArgumentError, "Unknown fill rule #{rule}"
330
- end
331
- primitive "fill-rule #{rule}"
332
- end
333
-
334
- # Specify text drawing font
335
- def font(name)
336
- primitive "font \'#{name}\'"
337
- end
338
-
339
- def font_family(name)
340
- primitive "font-family \'#{name}\'"
341
- end
342
-
343
- def font_stretch(stretch)
344
- if ( not STRETCH_TYPE_NAMES.has_key?(stretch.to_i) )
345
- Kernel.raise ArgumentError, "Unknown stretch type"
346
- end
347
- primitive "font-stretch #{STRETCH_TYPE_NAMES[stretch.to_i]}"
348
- end
349
-
350
- def font_style(style)
351
- if ( not STYLE_TYPE_NAMES.has_key?(style.to_i) )
352
- Kernel.raise ArgumentError, "Unknown style type"
353
- end
354
- primitive "font-style #{STYLE_TYPE_NAMES[style.to_i]}"
355
- end
356
-
357
- # The font weight argument can be either a font weight
358
- # constant or [100,200,...,900]
359
- def font_weight(weight)
360
- if ( FONT_WEIGHT_NAMES.has_key?(weight.to_i) )
328
+ end
329
+ primitive "fill-rule #{rule}"
330
+ end
331
+
332
+ # Specify text drawing font
333
+ def font(name)
334
+ primitive "font \'#{name}\'"
335
+ end
336
+
337
+ def font_family(name)
338
+ primitive "font-family \'#{name}\'"
339
+ end
340
+
341
+ def font_stretch(stretch)
342
+ unless STRETCH_TYPE_NAMES.has_key?(stretch.to_i)
343
+ Kernel.raise ArgumentError, 'Unknown stretch type'
344
+ end
345
+ primitive "font-stretch #{STRETCH_TYPE_NAMES[stretch.to_i]}"
346
+ end
347
+
348
+ def font_style(style)
349
+ unless STYLE_TYPE_NAMES.has_key?(style.to_i)
350
+ Kernel.raise ArgumentError, 'Unknown style type'
351
+ end
352
+ primitive "font-style #{STYLE_TYPE_NAMES[style.to_i]}"
353
+ end
354
+
355
+ # The font weight argument can be either a font weight
356
+ # constant or [100,200,...,900]
357
+ def font_weight(weight)
358
+ if FONT_WEIGHT_NAMES.has_key?(weight.to_i)
361
359
  primitive "font-weight #{FONT_WEIGHT_NAMES[weight.to_i]}"
362
- else
360
+ else
363
361
  primitive "font-weight #{weight}"
364
- end
365
- end
366
-
367
- # Specify the text positioning gravity, one of:
368
- # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
369
- def gravity(grav)
370
- if ( not GRAVITY_NAMES.has_key?(grav.to_i) )
371
- Kernel.raise ArgumentError, "Unknown text positioning gravity"
372
- end
373
- primitive "gravity #{GRAVITY_NAMES[grav.to_i]}"
374
- end
375
-
376
- # IM 6.5.5-8 and later
377
- def interline_spacing(space)
378
- begin
379
- Float(space)
380
- rescue ArgumentError
381
- Kernel.raise ArgumentError, "invalid value for interline_spacing"
382
- rescue TypeError
383
- Kernel.raise TypeError, "can't convert #{space.class} into Float"
384
- end
385
- primitive "interline-spacing #{space}"
386
- end
387
-
388
- # IM 6.4.8-3 and later
389
- def interword_spacing(space)
390
- begin
362
+ end
363
+ end
364
+
365
+ # Specify the text positioning gravity, one of:
366
+ # NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast
367
+ def gravity(grav)
368
+ unless GRAVITY_NAMES.has_key?(grav.to_i)
369
+ Kernel.raise ArgumentError, 'Unknown text positioning gravity'
370
+ end
371
+ primitive "gravity #{GRAVITY_NAMES[grav.to_i]}"
372
+ end
373
+
374
+ # IM 6.5.5-8 and later
375
+ def interline_spacing(space)
376
+ begin
391
377
  Float(space)
392
378
  rescue ArgumentError
393
- Kernel.raise ArgumentError, "invalid value for interword_spacing"
379
+ Kernel.raise ArgumentError, 'invalid value for interline_spacing'
394
380
  rescue TypeError
395
- Kernel.raise TypeError, "can't convert #{space.class} into Float"
396
- end
397
- primitive "interword-spacing #{space}"
398
- end
399
-
400
- # IM 6.4.8-3 and later
401
- def kerning(space)
402
- begin
381
+ Kernel.raise TypeError, "can't convert #{space.class} into Float"
382
+ end
383
+ primitive "interline-spacing #{space}"
384
+ end
385
+
386
+ # IM 6.4.8-3 and later
387
+ def interword_spacing(space)
388
+ begin
403
389
  Float(space)
404
390
  rescue ArgumentError
405
- Kernel.raise ArgumentError, "invalid value for kerning"
391
+ Kernel.raise ArgumentError, 'invalid value for interword_spacing'
406
392
  rescue TypeError
407
- Kernel.raise TypeError, "can't convert #{space.class} into Float"
408
- end
409
- primitive "kerning #{space}"
410
- end
411
-
412
- # Draw a line
413
- def line(startX, startY, endX, endY)
414
- primitive "line " + sprintf("%g,%g %g,%g", startX, startY, endX, endY)
415
- end
416
-
417
- # Set matte (make transparent) in image according to the specified
418
- # colorization rule
419
- def matte(x, y, method)
420
- if ( not PAINT_METHOD_NAMES.has_key?(method.to_i) )
421
- Kernel.raise ArgumentError, "Unknown paint method"
422
- end
423
- primitive "matte #{x},#{y} #{PAINT_METHOD_NAMES[method.to_i]}"
424
- end
425
-
426
- # Specify drawing fill and stroke opacities. If the value is a string
427
- # ending with a %, the number will be multiplied by 0.01.
428
- def opacity(opacity)
429
- if (Numeric === opacity)
430
- if (opacity < 0 || opacity > 1.0)
431
- Kernel.raise ArgumentError, "opacity must be >= 0 and <= 1.0"
393
+ Kernel.raise TypeError, "can't convert #{space.class} into Float"
394
+ end
395
+ primitive "interword-spacing #{space}"
396
+ end
397
+
398
+ # IM 6.4.8-3 and later
399
+ def kerning(space)
400
+ begin
401
+ Float(space)
402
+ rescue ArgumentError
403
+ Kernel.raise ArgumentError, 'invalid value for kerning'
404
+ rescue TypeError
405
+ Kernel.raise TypeError, "can't convert #{space.class} into Float"
406
+ end
407
+ primitive "kerning #{space}"
408
+ end
409
+
410
+ # Draw a line
411
+ def line(startX, startY, endX, endY)
412
+ primitive 'line ' + sprintf('%g,%g %g,%g', startX, startY, endX, endY)
413
+ end
414
+
415
+ # Set matte (make transparent) in image according to the specified
416
+ # colorization rule
417
+ def matte(x, y, method)
418
+ unless PAINT_METHOD_NAMES.has_key?(method.to_i)
419
+ Kernel.raise ArgumentError, 'Unknown paint method'
420
+ end
421
+ primitive "matte #{x},#{y} #{PAINT_METHOD_NAMES[method.to_i]}"
422
+ end
423
+
424
+ # Specify drawing fill and stroke opacities. If the value is a string
425
+ # ending with a %, the number will be multiplied by 0.01.
426
+ def opacity(opacity)
427
+ if (Numeric === opacity)
428
+ if opacity < 0 || opacity > 1.0
429
+ Kernel.raise ArgumentError, 'opacity must be >= 0 and <= 1.0'
432
430
  end
433
- end
434
- primitive "opacity #{opacity}"
435
- end
436
-
437
- # Draw using SVG-compatible path drawing commands. Note that the
438
- # primitive requires that the commands be surrounded by quotes or
439
- # apostrophes. Here we simply use apostrophes.
440
- def path(cmds)
441
- primitive "path '" + cmds + "'"
442
- end
443
-
444
- # Define a pattern. In the block, call primitive methods to
445
- # draw the pattern. Reference the pattern by using its name
446
- # as the argument to the 'fill' or 'stroke' methods
447
- def pattern(name, x, y, width, height)
448
- begin
449
- push('defs')
450
- push("pattern #{name} #{x} #{y} #{width} #{height}")
451
- push('graphic-context')
452
- yield
453
- ensure
454
- pop('graphic-context')
455
- pop('pattern')
456
- pop('defs')
457
- end
458
- end
459
-
460
- # Set point to fill color.
461
- def point(x, y)
462
- primitive "point #{x},#{y}"
463
- end
464
-
465
- # Specify the font size in points. Yes, the primitive is "font-size" but
466
- # in other places this value is called the "pointsize". Give it both names.
467
- def pointsize(points)
468
- primitive "font-size #{points}"
469
- end
470
- alias font_size pointsize
471
-
472
- # Draw a polygon
473
- def polygon(*points)
474
- if points.length == 0
475
- Kernel.raise ArgumentError, "no points specified"
476
- elsif points.length % 2 != 0
477
- Kernel.raise ArgumentError, "odd number of points specified"
478
- end
479
- primitive "polygon " + points.join(',')
480
- end
481
-
482
- # Draw a polyline
483
- def polyline(*points)
484
- if points.length == 0
485
- Kernel.raise ArgumentError, "no points specified"
486
- elsif points.length % 2 != 0
487
- Kernel.raise ArgumentError, "odd number of points specified"
488
- end
489
- primitive "polyline " + points.join(',')
490
- end
491
-
492
- # Return to the previously-saved set of whatever
493
- # pop('graphic-context') (the default if no arguments)
494
- # pop('defs')
495
- # pop('gradient')
496
- # pop('pattern')
497
-
498
- def pop(*what)
499
- if what.length == 0
500
- primitive "pop graphic-context"
501
- else
431
+ end
432
+ primitive "opacity #{opacity}"
433
+ end
434
+
435
+ # Draw using SVG-compatible path drawing commands. Note that the
436
+ # primitive requires that the commands be surrounded by quotes or
437
+ # apostrophes. Here we simply use apostrophes.
438
+ def path(cmds)
439
+ primitive "path '" + cmds + "'"
440
+ end
441
+
442
+ # Define a pattern. In the block, call primitive methods to
443
+ # draw the pattern. Reference the pattern by using its name
444
+ # as the argument to the 'fill' or 'stroke' methods
445
+ def pattern(name, x, y, width, height)
446
+ push('defs')
447
+ push("pattern #{name} #{x} #{y} #{width} #{height}")
448
+ push('graphic-context')
449
+ yield
450
+ ensure
451
+ pop('graphic-context')
452
+ pop('pattern')
453
+ pop('defs')
454
+ end
455
+
456
+ # Set point to fill color.
457
+ def point(x, y)
458
+ primitive "point #{x},#{y}"
459
+ end
460
+
461
+ # Specify the font size in points. Yes, the primitive is "font-size" but
462
+ # in other places this value is called the "pointsize". Give it both names.
463
+ def pointsize(points)
464
+ primitive "font-size #{points}"
465
+ end
466
+ alias_method :font_size, :pointsize
467
+
468
+ # Draw a polygon
469
+ def polygon(*points)
470
+ if points.length == 0
471
+ Kernel.raise ArgumentError, 'no points specified'
472
+ elsif points.length.odd?
473
+ Kernel.raise ArgumentError, 'odd number of points specified'
474
+ end
475
+ primitive 'polygon ' + points.join(',')
476
+ end
477
+
478
+ # Draw a polyline
479
+ def polyline(*points)
480
+ if points.length == 0
481
+ Kernel.raise ArgumentError, 'no points specified'
482
+ elsif points.length.odd?
483
+ Kernel.raise ArgumentError, 'odd number of points specified'
484
+ end
485
+ primitive 'polyline ' + points.join(',')
486
+ end
487
+
488
+ # Return to the previously-saved set of whatever
489
+ # pop('graphic-context') (the default if no arguments)
490
+ # pop('defs')
491
+ # pop('gradient')
492
+ # pop('pattern')
493
+
494
+ def pop(*what)
495
+ if what.length == 0
496
+ primitive 'pop graphic-context'
497
+ else
502
498
  # to_s allows a Symbol to be used instead of a String
503
- primitive "pop " + what.map {|w| w.to_s}.join(' ')
504
- end
505
- end
506
-
507
- # Push the current set of drawing options. Also you can use
508
- # push('graphic-context') (the default if no arguments)
509
- # push('defs')
510
- # push('gradient')
511
- # push('pattern')
512
- def push(*what)
513
- if what.length == 0
514
- primitive "push graphic-context"
515
- else
499
+ primitive 'pop ' + what.map {|w| w.to_s}.join(' ')
500
+ end
501
+ end
502
+
503
+ # Push the current set of drawing options. Also you can use
504
+ # push('graphic-context') (the default if no arguments)
505
+ # push('defs')
506
+ # push('gradient')
507
+ # push('pattern')
508
+ def push(*what)
509
+ if what.length == 0
510
+ primitive 'push graphic-context'
511
+ else
516
512
  # to_s allows a Symbol to be used instead of a String
517
- primitive "push " + what.map {|w| w.to_s}.join(' ')
513
+ primitive 'push ' + what.map {|w| w.to_s}.join(' ')
514
+ end
515
+ end
516
+
517
+ # Draw a rectangle
518
+ def rectangle(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
519
+ primitive 'rectangle ' + sprintf('%g,%g %g,%g',
520
+ upper_left_x, upper_left_y, lower_right_x, lower_right_y)
521
+ end
522
+
523
+ # Specify coordinate space rotation. "angle" is measured in degrees
524
+ def rotate(angle)
525
+ primitive "rotate #{angle}"
526
+ end
527
+
528
+ # Draw a rectangle with rounded corners
529
+ def roundrectangle(center_x, center_y, width, height, corner_width, corner_height)
530
+ primitive 'roundrectangle ' + sprintf('%g,%g,%g,%g,%g,%g',
531
+ center_x, center_y, width, height, corner_width, corner_height)
532
+ end
533
+
534
+ # Specify scaling to be applied to coordinate space on subsequent drawing commands.
535
+ def scale(x, y)
536
+ primitive "scale #{x},#{y}"
537
+ end
538
+
539
+ def skewx(angle)
540
+ primitive "skewX #{angle}"
541
+ end
542
+
543
+ def skewy(angle)
544
+ primitive "skewY #{angle}"
545
+ end
546
+
547
+ # Specify the object stroke, a color name or pattern name.
548
+ def stroke(colorspec)
549
+ primitive "stroke #{enquote(colorspec)}"
550
+ end
551
+ alias_method :stroke_color, :stroke
552
+ alias_method :stroke_pattern, :stroke
553
+
554
+ # Specify if stroke should be antialiased or not
555
+ def stroke_antialias(bool)
556
+ bool = bool ? '1' : '0'
557
+ primitive "stroke-antialias #{bool}"
558
+ end
559
+
560
+ # Specify a stroke dash pattern
561
+ def stroke_dasharray(*list)
562
+ if list.length == 0
563
+ primitive 'stroke-dasharray none'
564
+ else
565
+ list.each do |x|
566
+ if x <= 0
567
+ Kernel.raise ArgumentError, "dash array elements must be > 0 (#{x} given)"
568
+ end
569
+ end
570
+ primitive "stroke-dasharray #{list.join(',')}"
571
+ end
518
572
  end
519
- end
520
-
521
- # Draw a rectangle
522
- def rectangle(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
523
- primitive "rectangle " + sprintf("%g,%g %g,%g",
524
- upper_left_x, upper_left_y, lower_right_x, lower_right_y)
525
- end
526
-
527
- # Specify coordinate space rotation. "angle" is measured in degrees
528
- def rotate(angle)
529
- primitive "rotate #{angle}"
530
- end
531
-
532
- # Draw a rectangle with rounded corners
533
- def roundrectangle(center_x, center_y, width, height, corner_width, corner_height)
534
- primitive "roundrectangle " + sprintf("%g,%g,%g,%g,%g,%g",
535
- center_x, center_y, width, height, corner_width, corner_height)
536
- end
537
-
538
- # Specify scaling to be applied to coordinate space on subsequent drawing commands.
539
- def scale(x, y)
540
- primitive "scale #{x},#{y}"
541
- end
542
-
543
- def skewx(angle)
544
- primitive "skewX #{angle}"
545
- end
546
-
547
- def skewy(angle)
548
- primitive "skewY #{angle}"
549
- end
550
-
551
- # Specify the object stroke, a color name or pattern name.
552
- def stroke(colorspec)
553
- primitive "stroke #{enquote(colorspec)}"
554
- end
555
- alias stroke_color stroke
556
- alias stroke_pattern stroke
557
-
558
- # Specify if stroke should be antialiased or not
559
- def stroke_antialias(bool)
560
- bool = bool ? '1' : '0'
561
- primitive "stroke-antialias #{bool}"
562
- end
563
-
564
- # Specify a stroke dash pattern
565
- def stroke_dasharray(*list)
566
- if list.length == 0
567
- primitive "stroke-dasharray none"
568
- else
569
- list.each { |x|
570
- if x <= 0 then
571
- Kernel.raise ArgumentError, "dash array elements must be > 0 (#{x} given)"
572
- end
573
- }
574
- primitive "stroke-dasharray #{list.join(',')}"
573
+
574
+ # Specify the initial offset in the dash pattern
575
+ def stroke_dashoffset(value=0)
576
+ primitive "stroke-dashoffset #{value}"
575
577
  end
576
- end
577
-
578
- # Specify the initial offset in the dash pattern
579
- def stroke_dashoffset(value=0)
580
- primitive "stroke-dashoffset #{value}"
581
- end
582
-
583
- def stroke_linecap(value)
584
- if ( not ["butt", "round", "square"].include?(value.downcase) )
578
+
579
+ def stroke_linecap(value)
580
+ unless ['butt', 'round', 'square'].include?(value.downcase)
585
581
  Kernel.raise ArgumentError, "Unknown linecap type: #{value}"
582
+ end
583
+ primitive "stroke-linecap #{value}"
586
584
  end
587
- primitive "stroke-linecap #{value}"
588
- end
589
-
590
- def stroke_linejoin(value)
591
- if ( not ["round", "miter", "bevel"].include?(value.downcase) )
585
+
586
+ def stroke_linejoin(value)
587
+ unless ['round', 'miter', 'bevel'].include?(value.downcase)
592
588
  Kernel.raise ArgumentError, "Unknown linejoin type: #{value}"
589
+ end
590
+ primitive "stroke-linejoin #{value}"
591
+ end
592
+
593
+ def stroke_miterlimit(value)
594
+ if value < 1
595
+ Kernel.raise ArgumentError, 'miterlimit must be >= 1'
596
+ end
597
+ primitive "stroke-miterlimit #{value}"
598
+ end
599
+
600
+ # Specify opacity of stroke drawing color
601
+ # (use "xx%" to indicate percentage)
602
+ def stroke_opacity(value)
603
+ primitive "stroke-opacity #{value}"
604
+ end
605
+
606
+ # Specify stroke (outline) width in pixels.
607
+ def stroke_width(pixels)
608
+ primitive "stroke-width #{pixels}"
609
+ end
610
+
611
+ # Draw text at position x,y. Add quotes to text that is not already quoted.
612
+ def text(x, y, text)
613
+ if text.to_s.empty?
614
+ Kernel.raise ArgumentError, 'missing text argument'
615
+ end
616
+ if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
617
+ ; # text already quoted
618
+ elsif !text['\'']
619
+ text = '\''+text+'\''
620
+ elsif !text['"']
621
+ text = '"'+text+'"'
622
+ elsif !(text['{'] || text['}'])
623
+ text = '{'+text+'}'
624
+ else
625
+ # escape existing braces, surround with braces
626
+ text = '{' + text.gsub(/[}]/) { |b| '\\' + b } + '}'
627
+ end
628
+ primitive "text #{x},#{y} #{text}"
593
629
  end
594
- primitive "stroke-linejoin #{value}"
595
- end
596
-
597
- def stroke_miterlimit(value)
598
- if (value < 1)
599
- Kernel.raise ArgumentError, "miterlimit must be >= 1"
600
- end
601
- primitive "stroke-miterlimit #{value}"
602
- end
603
-
604
- # Specify opacity of stroke drawing color
605
- # (use "xx%" to indicate percentage)
606
- def stroke_opacity(value)
607
- primitive "stroke-opacity #{value}"
608
- end
609
-
610
- # Specify stroke (outline) width in pixels.
611
- def stroke_width(pixels)
612
- primitive "stroke-width #{pixels}"
613
- end
614
-
615
- # Draw text at position x,y. Add quotes to text that is not already quoted.
616
- def text(x, y, text)
617
- if text.to_s.empty?
618
- Kernel.raise ArgumentError, "missing text argument"
619
- end
620
- if text.length > 2 && /\A(?:\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})\z/.match(text)
621
- ; # text already quoted
622
- elsif !text['\'']
623
- text = '\''+text+'\''
624
- elsif !text['"']
625
- text = '"'+text+'"'
626
- elsif !(text['{'] || text['}'])
627
- text = '{'+text+'}'
628
- else
629
- # escape existing braces, surround with braces
630
- text = '{' + text.gsub(/[}]/) { |b| '\\' + b } + '}'
631
- end
632
- primitive "text #{x},#{y} #{text}"
633
- end
634
-
635
- # Specify text alignment relative to a given point
636
- def text_align(alignment)
637
- if ( not ALIGN_TYPE_NAMES.has_key?(alignment.to_i) )
630
+
631
+ # Specify text alignment relative to a given point
632
+ def text_align(alignment)
633
+ unless ALIGN_TYPE_NAMES.has_key?(alignment.to_i)
638
634
  Kernel.raise ArgumentError, "Unknown alignment constant: #{alignment}"
635
+ end
636
+ primitive "text-align #{ALIGN_TYPE_NAMES[alignment.to_i]}"
639
637
  end
640
- primitive "text-align #{ALIGN_TYPE_NAMES[alignment.to_i]}"
641
- end
642
-
643
- # SVG-compatible version of text_align
644
- def text_anchor(anchor)
645
- if ( not ANCHOR_TYPE_NAMES.has_key?(anchor.to_i) )
638
+
639
+ # SVG-compatible version of text_align
640
+ def text_anchor(anchor)
641
+ unless ANCHOR_TYPE_NAMES.has_key?(anchor.to_i)
646
642
  Kernel.raise ArgumentError, "Unknown anchor constant: #{anchor}"
643
+ end
644
+ primitive "text-anchor #{ANCHOR_TYPE_NAMES[anchor.to_i]}"
645
+ end
646
+
647
+ # Specify if rendered text is to be antialiased.
648
+ def text_antialias(boolean)
649
+ boolean = boolean ? '1' : '0'
650
+ primitive "text-antialias #{boolean}"
651
+ end
652
+
653
+ # Specify color underneath text
654
+ def text_undercolor(color)
655
+ primitive "text-undercolor #{enquote(color)}"
656
+ end
657
+
658
+ # Specify center of coordinate space to use for subsequent drawing
659
+ # commands.
660
+ def translate(x, y)
661
+ primitive "translate #{x},#{y}"
662
+ end
663
+ end # class Magick::Draw
664
+
665
+ # Define IPTC record number:dataset tags for use with Image#get_iptc_dataset
666
+ module IPTC
667
+ module Envelope
668
+ Model_Version = '1:00'
669
+ Destination = '1:05'
670
+ File_Format = '1:20'
671
+ File_Format_Version = '1:22'
672
+ Service_Identifier = '1:30'
673
+ Envelope_Number = '1:40'
674
+ Product_ID = '1:50'
675
+ Envelope_Priority = '1:60'
676
+ Date_Sent = '1:70'
677
+ Time_Sent = '1:80'
678
+ Coded_Character_Set = '1:90'
679
+ UNO = '1:100'
680
+ Unique_Name_of_Object = '1:100'
681
+ ARM_Identifier = '1:120'
682
+ ARM_Version = '1:122'
683
+ end
684
+
685
+ module Application
686
+ Record_Version = '2:00'
687
+ Object_Type_Reference = '2:03'
688
+ Object_Name = '2:05'
689
+ Title = '2:05'
690
+ Edit_Status = '2:07'
691
+ Editorial_Update = '2:08'
692
+ Urgency = '2:10'
693
+ Subject_Reference = '2:12'
694
+ Category = '2:15'
695
+ Supplemental_Category = '2:20'
696
+ Fixture_Identifier = '2:22'
697
+ Keywords = '2:25'
698
+ Content_Location_Code = '2:26'
699
+ Content_Location_Name = '2:27'
700
+ Release_Date = '2:30'
701
+ Release_Time = '2:35'
702
+ Expiration_Date = '2:37'
703
+ Expiration_Time = '2:35'
704
+ Special_Instructions = '2:40'
705
+ Action_Advised = '2:42'
706
+ Reference_Service = '2:45'
707
+ Reference_Date = '2:47'
708
+ Reference_Number = '2:50'
709
+ Date_Created = '2:55'
710
+ Time_Created = '2:60'
711
+ Digital_Creation_Date = '2:62'
712
+ Digital_Creation_Time = '2:63'
713
+ Originating_Program = '2:65'
714
+ Program_Version = '2:70'
715
+ Object_Cycle = '2:75'
716
+ By_Line = '2:80'
717
+ Author = '2:80'
718
+ By_Line_Title = '2:85'
719
+ Author_Position = '2:85'
720
+ City = '2:90'
721
+ Sub_Location = '2:92'
722
+ Province = '2:95'
723
+ State = '2:95'
724
+ Country_Primary_Location_Code = '2:100'
725
+ Country_Primary_Location_Name = '2:101'
726
+ Original_Transmission_Reference = '2:103'
727
+ Headline = '2:105'
728
+ Credit = '2:110'
729
+ Source = '2:115'
730
+ Copyright_Notice = '2:116'
731
+ Contact = '2:118'
732
+ Abstract = '2:120'
733
+ Caption = '2:120'
734
+ Editor = '2:122'
735
+ Caption_Writer = '2:122'
736
+ Rasterized_Caption = '2:125'
737
+ Image_Type = '2:130'
738
+ Image_Orientation = '2:131'
739
+ Language_Identifier = '2:135'
740
+ Audio_Type = '2:150'
741
+ Audio_Sampling_Rate = '2:151'
742
+ Audio_Sampling_Resolution = '2:152'
743
+ Audio_Duration = '2:153'
744
+ Audio_Outcue = '2:154'
745
+ ObjectData_Preview_File_Format = '2:200'
746
+ ObjectData_Preview_File_Format_Version = '2:201'
747
+ ObjectData_Preview_Data = '2:202'
748
+ end
749
+
750
+ module Pre_ObjectData_Descriptor
751
+ Size_Mode = '7:10'
752
+ Max_Subfile_Size = '7:20'
753
+ ObjectData_Size_Announced = '7:90'
754
+ Maximum_ObjectData_Size = '7:95'
755
+ end
756
+
757
+ module ObjectData
758
+ Subfile = '8:10'
759
+ end
760
+
761
+ module Post_ObjectData_Descriptor
762
+ Confirmed_ObjectData_Size = '9:10'
763
+ end
764
+
765
+ # Make all constants above immutable
766
+ constants.each do |record|
767
+ rec = const_get(record)
768
+ rec.constants.each { |ds| rec.const_get(ds).freeze }
769
+ end
770
+ end # module Magick::IPTC
771
+
772
+ # Ruby-level Magick::Image methods
773
+ class Image
774
+ include Comparable
775
+
776
+ alias_method :affinity, :remap
777
+
778
+ # Provide an alternate version of Draw#annotate, for folks who
779
+ # want to find it in this class.
780
+ def annotate(draw, width, height, x, y, text, &block)
781
+ check_destroyed
782
+ draw.annotate(self, width, height, x, y, text, &block)
783
+ self
647
784
  end
648
- primitive "text-anchor #{ANCHOR_TYPE_NAMES[anchor.to_i]}"
649
- end
650
-
651
- # Specify if rendered text is to be antialiased.
652
- def text_antialias(boolean)
653
- boolean = boolean ? '1' : '0'
654
- primitive "text-antialias #{boolean}"
655
- end
656
-
657
- # Specify color underneath text
658
- def text_undercolor(color)
659
- primitive "text-undercolor #{enquote(color)}"
660
- end
661
-
662
- # Specify center of coordinate space to use for subsequent drawing
663
- # commands.
664
- def translate(x, y)
665
- primitive "translate #{x},#{y}"
666
- end
667
- end # class Magick::Draw
668
-
669
-
670
- # Define IPTC record number:dataset tags for use with Image#get_iptc_dataset
671
- module IPTC
672
- module Envelope
673
- Model_Version = "1:00"
674
- Destination = "1:05"
675
- File_Format = "1:20"
676
- File_Format_Version = "1:22"
677
- Service_Identifier = "1:30"
678
- Envelope_Number = "1:40"
679
- Product_ID = "1:50"
680
- Envelope_Priority = "1:60"
681
- Date_Sent = "1:70"
682
- Time_Sent = "1:80"
683
- Coded_Character_Set = "1:90"
684
- UNO = "1:100"
685
- Unique_Name_of_Object = "1:100"
686
- ARM_Identifier = "1:120"
687
- ARM_Version = "1:122"
688
- end
689
-
690
- module Application
691
- Record_Version = "2:00"
692
- Object_Type_Reference = "2:03"
693
- Object_Name = "2:05"
694
- Title = "2:05"
695
- Edit_Status = "2:07"
696
- Editorial_Update = "2:08"
697
- Urgency = "2:10"
698
- Subject_Reference = "2:12"
699
- Category = "2:15"
700
- Supplemental_Category = "2:20"
701
- Fixture_Identifier = "2:22"
702
- Keywords = "2:25"
703
- Content_Location_Code = "2:26"
704
- Content_Location_Name = "2:27"
705
- Release_Date = "2:30"
706
- Release_Time = "2:35"
707
- Expiration_Date = "2:37"
708
- Expiration_Time = "2:35"
709
- Special_Instructions = "2:40"
710
- Action_Advised = "2:42"
711
- Reference_Service = "2:45"
712
- Reference_Date = "2:47"
713
- Reference_Number = "2:50"
714
- Date_Created = "2:55"
715
- Time_Created = "2:60"
716
- Digital_Creation_Date = "2:62"
717
- Digital_Creation_Time = "2:63"
718
- Originating_Program = "2:65"
719
- Program_Version = "2:70"
720
- Object_Cycle = "2:75"
721
- By_Line = "2:80"
722
- Author = "2:80"
723
- By_Line_Title = "2:85"
724
- Author_Position = "2:85"
725
- City = "2:90"
726
- Sub_Location = "2:92"
727
- Province = "2:95"
728
- State = "2:95"
729
- Country_Primary_Location_Code = "2:100"
730
- Country_Primary_Location_Name = "2:101"
731
- Original_Transmission_Reference = "2:103"
732
- Headline = "2:105"
733
- Credit = "2:110"
734
- Source = "2:115"
735
- Copyright_Notice = "2:116"
736
- Contact = "2:118"
737
- Abstract = "2:120"
738
- Caption = "2:120"
739
- Editor = "2:122"
740
- Caption_Writer = "2:122"
741
- Rasterized_Caption = "2:125"
742
- Image_Type = "2:130"
743
- Image_Orientation = "2:131"
744
- Language_Identifier = "2:135"
745
- Audio_Type = "2:150"
746
- Audio_Sampling_Rate = "2:151"
747
- Audio_Sampling_Resolution = "2:152"
748
- Audio_Duration = "2:153"
749
- Audio_Outcue = "2:154"
750
- ObjectData_Preview_File_Format = "2:200"
751
- ObjectData_Preview_File_Format_Version = "2:201"
752
- ObjectData_Preview_Data = "2:202"
753
- end
754
-
755
- module Pre_ObjectData_Descriptor
756
- Size_Mode = "7:10"
757
- Max_Subfile_Size = "7:20"
758
- ObjectData_Size_Announced = "7:90"
759
- Maximum_ObjectData_Size = "7:95"
760
- end
761
-
762
- module ObjectData
763
- Subfile = "8:10"
764
- end
765
-
766
- module Post_ObjectData_Descriptor
767
- Confirmed_ObjectData_Size = "9:10"
768
- end
769
-
770
- # Make all constants above immutable
771
- constants.each do |record|
772
- rec = const_get(record)
773
- rec.constants.each { |ds| rec.const_get(ds).freeze }
774
- end
775
-
776
- end # module Magick::IPTC
777
-
778
- # Ruby-level Magick::Image methods
779
- class Image
780
- include Comparable
781
-
782
- alias_method :affinity, :remap
783
-
784
- # Provide an alternate version of Draw#annotate, for folks who
785
- # want to find it in this class.
786
- def annotate(draw, width, height, x, y, text, &block)
787
- check_destroyed
788
- draw.annotate(self, width, height, x, y, text, &block)
789
- self
790
- end
791
-
792
- # Set the color at x,y
793
- def color_point(x, y, fill)
794
- f = copy
795
- f.pixel_color(x, y, fill)
796
- return f
797
- end
798
-
799
- # Set all pixels that have the same color as the pixel at x,y and
800
- # are neighbors to the fill color
801
- def color_floodfill(x, y, fill)
802
- target = pixel_color(x, y)
803
- color_flood_fill(target, fill, x, y, Magick::FloodfillMethod)
804
- end
805
-
806
- # Set all pixels that are neighbors of x,y and are not the border color
807
- # to the fill color
808
- def color_fill_to_border(x, y, fill)
809
- color_flood_fill(border_color, fill, x, y, Magick::FillToBorderMethod)
810
- end
811
-
812
- # Set all pixels to the fill color. Very similar to Image#erase!
813
- # Accepts either String or Pixel arguments
814
- def color_reset!(fill)
815
- save = background_color
816
- # Change the background color _outside_ the begin block
817
- # so that if this object is frozen the exeception will be
818
- # raised before we have to handle it explicitly.
819
- self.background_color = fill
820
- begin
821
- erase!
822
- ensure
785
+
786
+ # Set the color at x,y
787
+ def color_point(x, y, fill)
788
+ f = copy
789
+ f.pixel_color(x, y, fill)
790
+ f
791
+ end
792
+
793
+ # Set all pixels that have the same color as the pixel at x,y and
794
+ # are neighbors to the fill color
795
+ def color_floodfill(x, y, fill)
796
+ target = pixel_color(x, y)
797
+ color_flood_fill(target, fill, x, y, Magick::FloodfillMethod)
798
+ end
799
+
800
+ # Set all pixels that are neighbors of x,y and are not the border color
801
+ # to the fill color
802
+ def color_fill_to_border(x, y, fill)
803
+ color_flood_fill(border_color, fill, x, y, Magick::FillToBorderMethod)
804
+ end
805
+
806
+ # Set all pixels to the fill color. Very similar to Image#erase!
807
+ # Accepts either String or Pixel arguments
808
+ def color_reset!(fill)
809
+ save = background_color
810
+ # Change the background color _outside_ the begin block
811
+ # so that if this object is frozen the exeception will be
812
+ # raised before we have to handle it explicitly.
813
+ self.background_color = fill
814
+ begin
815
+ erase!
816
+ ensure
823
817
  self.background_color = save
824
- end
825
- self
826
- end
827
-
828
- # Used by ImageList methods - see ImageList#cur_image
829
- def cur_image
830
- self
831
- end
832
-
833
- # Thanks to Russell Norris!
834
- def each_pixel
835
- get_pixels(0, 0, columns, rows).each_with_index do |p, n|
836
- yield(p, n%columns, n/columns)
837
- end
838
- self
839
- end
840
-
841
- # Retrieve EXIF data by entry or all. If one or more entry names specified,
842
- # return the values associated with the entries. If no entries specified,
843
- # return all entries and values. The return value is an array of [name,value]
844
- # arrays.
845
- def get_exif_by_entry(*entry)
846
- ary = Array.new
847
- if entry.length == 0
848
- exif_data = self['EXIF:*']
849
- if exif_data
850
- exif_data.split("\n").each { |exif| ary.push(exif.split('=')) }
851
- end
852
- else
853
- get_exif_by_entry() # ensure properties is populated with exif data
854
- entry.each do |name|
855
- rval = self["EXIF:#{name}"]
856
- ary.push([name, rval])
857
818
  end
819
+ self
858
820
  end
859
- return ary
860
- end
861
-
862
- # Retrieve EXIF data by tag number or all tag/value pairs. The return value is a hash.
863
- def get_exif_by_number(*tag)
864
- hash = Hash.new
865
- if tag.length == 0
866
- exif_data = self['EXIF:!']
867
- if exif_data
868
- exif_data.split("\n").each do |exif|
869
- tag, value = exif.split('=')
870
- tag = tag[1,4].hex
871
- hash[tag] = value
821
+
822
+ # Used by ImageList methods - see ImageList#cur_image
823
+ def cur_image
824
+ self
825
+ end
826
+
827
+ # Thanks to Russell Norris!
828
+ def each_pixel
829
+ get_pixels(0, 0, columns, rows).each_with_index do |p, n|
830
+ yield(p, n%columns, n/columns)
831
+ end
832
+ self
833
+ end
834
+
835
+ # Retrieve EXIF data by entry or all. If one or more entry names specified,
836
+ # return the values associated with the entries. If no entries specified,
837
+ # return all entries and values. The return value is an array of [name,value]
838
+ # arrays.
839
+ def get_exif_by_entry(*entry)
840
+ ary = []
841
+ if entry.length == 0
842
+ exif_data = self['EXIF:*']
843
+ if exif_data
844
+ exif_data.split("\n").each { |exif| ary.push(exif.split('=')) }
845
+ end
846
+ else
847
+ get_exif_by_entry # ensure properties is populated with exif data
848
+ entry.each do |name|
849
+ rval = self["EXIF:#{name}"]
850
+ ary.push([name, rval])
872
851
  end
873
852
  end
874
- else
875
- get_exif_by_number() # ensure properties is populated with exif data
876
- tag.each do |num|
877
- rval = self['#%04X' % num.to_i]
878
- hash[num] = rval == 'unknown' ? nil : rval
853
+ ary
854
+ end
855
+
856
+ # Retrieve EXIF data by tag number or all tag/value pairs. The return value is a hash.
857
+ def get_exif_by_number(*tag)
858
+ hash = {}
859
+ if tag.length == 0
860
+ exif_data = self['EXIF:!']
861
+ if exif_data
862
+ exif_data.split("\n").each do |exif|
863
+ tag, value = exif.split('=')
864
+ tag = tag[1,4].hex
865
+ hash[tag] = value
866
+ end
867
+ end
868
+ else
869
+ get_exif_by_number # ensure properties is populated with exif data
870
+ tag.each do |num|
871
+ rval = self['#%04X' % num.to_i]
872
+ hash[num] = rval == 'unknown' ? nil : rval
873
+ end
879
874
  end
880
- end
881
- return hash
882
- end
883
-
884
- # Retrieve IPTC information by record number:dataset tag constant defined in
885
- # Magick::IPTC, above.
886
- def get_iptc_dataset(ds)
887
- self['IPTC:'+ds]
888
- end
889
-
890
- # Iterate over IPTC record number:dataset tags, yield for each non-nil dataset
891
- def each_iptc_dataset
892
- Magick::IPTC.constants.each do |record|
875
+ hash
876
+ end
877
+
878
+ # Retrieve IPTC information by record number:dataset tag constant defined in
879
+ # Magick::IPTC, above.
880
+ def get_iptc_dataset(ds)
881
+ self['IPTC:'+ds]
882
+ end
883
+
884
+ # Iterate over IPTC record number:dataset tags, yield for each non-nil dataset
885
+ def each_iptc_dataset
886
+ Magick::IPTC.constants.each do |record|
893
887
  rec = Magick::IPTC.const_get(record)
894
- rec.constants.each do |dataset|
888
+ rec.constants.each do |dataset|
895
889
  data_field = get_iptc_dataset(rec.const_get(dataset))
896
- yield(dataset, data_field) unless data_field.nil?
897
- end
890
+ yield(dataset, data_field) unless data_field.nil?
891
+ end
892
+ end
893
+ nil
898
894
  end
899
- nil
900
- end
901
-
902
- # Patches problematic change to the order of arguments in 1.11.0.
903
- # Before this release, the order was
904
- # black_point, gamma, white_point
905
- # RMagick 1.11.0 changed this to
906
- # black_point, white_point, gamma
907
- # This fix tries to determine if the arguments are in the old order and
908
- # if so, swaps the gamma and white_point arguments. Then it calls
909
- # level2, which simply accepts the arguments as given.
910
-
911
- # Inspect the gamma and white point values and swap them if they
912
- # look like they're in the old order.
913
-
914
- # (Thanks to Al Evans for the suggestion.)
915
- def level(black_point=0.0, white_point=nil, gamma=nil)
916
- black_point = Float(black_point)
917
-
918
- white_point ||= Magick::QuantumRange - black_point
919
- white_point = Float(white_point)
920
-
921
- gamma_arg = gamma
922
- gamma ||= 1.0
923
- gamma = Float(gamma)
924
-
925
- if gamma.abs > 10.0 || white_point.abs <= 10.0 || white_point.abs < gamma.abs
926
- gamma, white_point = white_point, gamma
927
- unless gamma_arg
928
- white_point = Magick::QuantumRange - black_point
895
+
896
+ # Patches problematic change to the order of arguments in 1.11.0.
897
+ # Before this release, the order was
898
+ # black_point, gamma, white_point
899
+ # RMagick 1.11.0 changed this to
900
+ # black_point, white_point, gamma
901
+ # This fix tries to determine if the arguments are in the old order and
902
+ # if so, swaps the gamma and white_point arguments. Then it calls
903
+ # level2, which simply accepts the arguments as given.
904
+
905
+ # Inspect the gamma and white point values and swap them if they
906
+ # look like they're in the old order.
907
+
908
+ # (Thanks to Al Evans for the suggestion.)
909
+ def level(black_point=0.0, white_point=nil, gamma=nil)
910
+ black_point = Float(black_point)
911
+
912
+ white_point ||= Magick::QuantumRange - black_point
913
+ white_point = Float(white_point)
914
+
915
+ gamma_arg = gamma
916
+ gamma ||= 1.0
917
+ gamma = Float(gamma)
918
+
919
+ if gamma.abs > 10.0 || white_point.abs <= 10.0 || white_point.abs < gamma.abs
920
+ gamma, white_point = white_point, gamma
921
+ unless gamma_arg
922
+ white_point = Magick::QuantumRange - black_point
923
+ end
924
+ end
925
+
926
+ level2(black_point, white_point, gamma)
927
+ end
928
+
929
+ # These four methods are equivalent to the Draw#matte method
930
+ # with the "Point", "Replace", "Floodfill", "FilltoBorder", and
931
+ # "Replace" arguments, respectively.
932
+
933
+ # Make the pixel at (x,y) transparent.
934
+ def matte_point(x, y)
935
+ f = copy
936
+ f.opacity = OpaqueOpacity unless f.matte
937
+ pixel = f.pixel_color(x,y)
938
+ pixel.opacity = TransparentOpacity
939
+ f.pixel_color(x, y, pixel)
940
+ f
941
+ end
942
+
943
+ # Make transparent all pixels that are the same color as the
944
+ # pixel at (x, y).
945
+ def matte_replace(x, y)
946
+ f = copy
947
+ f.opacity = OpaqueOpacity unless f.matte
948
+ target = f.pixel_color(x, y)
949
+ f.transparent(target)
950
+ end
951
+
952
+ # Make transparent any pixel that matches the color of the pixel
953
+ # at (x,y) and is a neighbor.
954
+ def matte_floodfill(x, y)
955
+ f = copy
956
+ f.opacity = OpaqueOpacity unless f.matte
957
+ target = f.pixel_color(x, y)
958
+ f.matte_flood_fill(target, TransparentOpacity,
959
+ x, y, FloodfillMethod)
960
+ end
961
+
962
+ # Make transparent any neighbor pixel that is not the border color.
963
+ def matte_fill_to_border(x, y)
964
+ f = copy
965
+ f.opacity = Magick::OpaqueOpacity unless f.matte
966
+ f.matte_flood_fill(border_color, TransparentOpacity,
967
+ x, y, FillToBorderMethod)
968
+ end
969
+
970
+ # Make all pixels transparent.
971
+ def matte_reset!
972
+ self.opacity = Magick::TransparentOpacity
973
+ self
974
+ end
975
+
976
+ # Force an image to exact dimensions without changing the aspect ratio.
977
+ # Resize and crop if necessary. (Thanks to Jerett Taylor!)
978
+ def resize_to_fill(ncols, nrows=nil, gravity=CenterGravity)
979
+ copy.resize_to_fill!(ncols, nrows, gravity)
980
+ end
981
+
982
+ def resize_to_fill!(ncols, nrows=nil, gravity=CenterGravity)
983
+ nrows ||= ncols
984
+ if ncols != columns || nrows != rows
985
+ scale = [ncols/columns.to_f, nrows/rows.to_f].max
986
+ resize!(scale*columns+0.5, scale*rows+0.5)
987
+ end
988
+ crop!(gravity, ncols, nrows, true) if ncols != columns || nrows != rows
989
+ self
990
+ end
991
+
992
+ # Preserve aliases used < RMagick 2.0.1
993
+ alias_method :crop_resized, :resize_to_fill
994
+ alias_method :crop_resized!, :resize_to_fill!
995
+
996
+ # Convenience method to resize retaining the aspect ratio.
997
+ # (Thanks to Robert Manni!)
998
+ def resize_to_fit(cols, rows=nil)
999
+ rows ||= cols
1000
+ change_geometry(Geometry.new(cols, rows)) do |ncols, nrows|
1001
+ resize(ncols, nrows)
929
1002
  end
930
1003
  end
931
-
932
- return level2(black_point, white_point, gamma)
933
- end
934
-
935
- # These four methods are equivalent to the Draw#matte method
936
- # with the "Point", "Replace", "Floodfill", "FilltoBorder", and
937
- # "Replace" arguments, respectively.
938
-
939
- # Make the pixel at (x,y) transparent.
940
- def matte_point(x, y)
941
- f = copy
942
- f.opacity = OpaqueOpacity unless f.matte
943
- pixel = f.pixel_color(x,y)
944
- pixel.opacity = TransparentOpacity
945
- f.pixel_color(x, y, pixel)
946
- return f
947
- end
948
-
949
- # Make transparent all pixels that are the same color as the
950
- # pixel at (x, y).
951
- def matte_replace(x, y)
952
- f = copy
953
- f.opacity = OpaqueOpacity unless f.matte
954
- target = f.pixel_color(x, y)
955
- f.transparent(target)
956
- end
957
-
958
- # Make transparent any pixel that matches the color of the pixel
959
- # at (x,y) and is a neighbor.
960
- def matte_floodfill(x, y)
961
- f = copy
962
- f.opacity = OpaqueOpacity unless f.matte
963
- target = f.pixel_color(x, y)
964
- f.matte_flood_fill(target, TransparentOpacity,
965
- x, y, FloodfillMethod)
966
- end
967
-
968
- # Make transparent any neighbor pixel that is not the border color.
969
- def matte_fill_to_border(x, y)
970
- f = copy
971
- f.opacity = Magick::OpaqueOpacity unless f.matte
972
- f.matte_flood_fill(border_color, TransparentOpacity,
973
- x, y, FillToBorderMethod)
974
- end
975
-
976
- # Make all pixels transparent.
977
- def matte_reset!
978
- self.opacity = Magick::TransparentOpacity
979
- self
980
- end
981
-
982
- # Force an image to exact dimensions without changing the aspect ratio.
983
- # Resize and crop if necessary. (Thanks to Jerett Taylor!)
984
- def resize_to_fill(ncols, nrows=nil, gravity=CenterGravity)
985
- copy.resize_to_fill!(ncols, nrows, gravity)
986
- end
987
-
988
- def resize_to_fill!(ncols, nrows=nil, gravity=CenterGravity)
989
- nrows ||= ncols
990
- if ncols != columns || nrows != rows
991
- scale = [ncols/columns.to_f, nrows/rows.to_f].max
992
- resize!(scale*columns+0.5, scale*rows+0.5)
993
- end
994
- crop!(gravity, ncols, nrows, true) if ncols != columns || nrows != rows
995
- self
996
- end
997
-
998
- # Preserve aliases used < RMagick 2.0.1
999
- alias_method :crop_resized, :resize_to_fill
1000
- alias_method :crop_resized!, :resize_to_fill!
1001
-
1002
- # Convenience method to resize retaining the aspect ratio.
1003
- # (Thanks to Robert Manni!)
1004
- def resize_to_fit(cols, rows=nil)
1005
- rows ||= cols
1006
- change_geometry(Geometry.new(cols, rows)) do |ncols, nrows|
1007
- resize(ncols, nrows)
1008
- end
1009
- end
1010
-
1011
- def resize_to_fit!(cols, rows=nil)
1012
- rows ||= cols
1013
- change_geometry(Geometry.new(cols, rows)) do |ncols, nrows|
1014
- resize!(ncols, nrows)
1004
+
1005
+ def resize_to_fit!(cols, rows=nil)
1006
+ rows ||= cols
1007
+ change_geometry(Geometry.new(cols, rows)) do |ncols, nrows|
1008
+ resize!(ncols, nrows)
1009
+ end
1015
1010
  end
1016
- end
1017
-
1018
- # Replace matching neighboring pixels with texture pixels
1019
- def texture_floodfill(x, y, texture)
1020
- target = pixel_color(x, y)
1021
- texture_flood_fill(target, texture, x, y, FloodfillMethod)
1022
- end
1023
-
1024
- # Replace neighboring pixels to border color with texture pixels
1025
- def texture_fill_to_border(x, y, texture)
1026
- texture_flood_fill(border_color, texture, x, y, FillToBorderMethod)
1027
- end
1028
-
1029
- # Construct a view. If a block is present, yield and pass the view
1030
- # object, otherwise return the view object.
1031
- def view(x, y, width, height)
1032
- view = View.new(self, x, y, width, height)
1033
-
1034
- if block_given?
1035
- begin
1011
+
1012
+ # Replace matching neighboring pixels with texture pixels
1013
+ def texture_floodfill(x, y, texture)
1014
+ target = pixel_color(x, y)
1015
+ texture_flood_fill(target, texture, x, y, FloodfillMethod)
1016
+ end
1017
+
1018
+ # Replace neighboring pixels to border color with texture pixels
1019
+ def texture_fill_to_border(x, y, texture)
1020
+ texture_flood_fill(border_color, texture, x, y, FillToBorderMethod)
1021
+ end
1022
+
1023
+ # Construct a view. If a block is present, yield and pass the view
1024
+ # object, otherwise return the view object.
1025
+ def view(x, y, width, height)
1026
+ view = View.new(self, x, y, width, height)
1027
+
1028
+ if block_given?
1029
+ begin
1036
1030
  yield(view)
1037
1031
  ensure
1038
- view.sync
1032
+ view.sync
1033
+ end
1034
+ return nil
1035
+ else
1036
+ return view
1039
1037
  end
1040
- return nil
1041
- else
1042
- return view
1043
1038
  end
1044
- end
1045
-
1046
- # Magick::Image::View class
1047
- class View
1048
- attr_reader :x, :y, :width, :height
1049
- attr_accessor :dirty
1050
-
1051
- def initialize(img, x, y, width, height)
1052
- img.check_destroyed
1053
- if width <= 0 || height <= 0
1054
- Kernel.raise ArgumentError, "invalid geometry (#{width}x#{height}+#{x}+#{y})"
1039
+
1040
+ # Magick::Image::View class
1041
+ class View
1042
+ attr_reader :x, :y, :width, :height
1043
+ attr_accessor :dirty
1044
+
1045
+ def initialize(img, x, y, width, height)
1046
+ img.check_destroyed
1047
+ if width <= 0 || height <= 0
1048
+ Kernel.raise ArgumentError, "invalid geometry (#{width}x#{height}+#{x}+#{y})"
1049
+ end
1050
+ if x < 0 || y < 0 || (x+width) > img.columns || (y+height) > img.rows
1051
+ Kernel.raise RangeError, "geometry (#{width}x#{height}+#{x}+#{y}) exceeds image boundary"
1052
+ end
1053
+ @view = img.get_pixels(x, y, width, height)
1054
+ @img = img
1055
+ @x = x
1056
+ @y = y
1057
+ @width = width
1058
+ @height = height
1059
+ @dirty = false
1055
1060
  end
1056
- if x < 0 || y < 0 || (x+width) > img.columns || (y+height) > img.rows
1057
- Kernel.raise RangeError, "geometry (#{width}x#{height}+#{x}+#{y}) exceeds image boundary"
1061
+
1062
+ def [](*args)
1063
+ rows = Rows.new(@view, @width, @height, args)
1064
+ rows.add_observer(self)
1065
+ rows
1058
1066
  end
1059
- @view = img.get_pixels(x, y, width, height)
1060
- @img = img
1061
- @x = x
1062
- @y = y
1063
- @width = width
1064
- @height = height
1065
- @dirty = false
1066
- end
1067
-
1068
- def [](*args)
1069
- rows = Rows.new(@view, @width, @height, args)
1070
- rows.add_observer(self)
1071
- return rows
1072
- end
1073
-
1074
- # Store changed pixels back to image
1075
- def sync(force=false)
1076
- @img.store_pixels(x, y, width, height, @view) if (@dirty || force)
1077
- return (@dirty || force)
1078
- end
1079
-
1080
- # Get update from Rows - if @dirty ever becomes
1081
- # true, don't change it back to false!
1082
- def update(rows)
1083
- @dirty = true
1084
- rows.delete_observer(self) # No need to tell us again.
1085
- nil
1086
- end
1087
-
1088
- # Magick::Image::View::Pixels
1089
- # Defines channel attribute getters/setters
1090
- class Pixels < Array
1091
- include Observable
1092
-
1093
- # Define a getter and a setter for each channel.
1094
- [:red, :green, :blue, :opacity].each do |c|
1095
- module_eval <<-END_EVAL
1067
+
1068
+ # Store changed pixels back to image
1069
+ def sync(force=false)
1070
+ @img.store_pixels(x, y, width, height, @view) if @dirty || force
1071
+ @dirty || force
1072
+ end
1073
+
1074
+ # Get update from Rows - if @dirty ever becomes
1075
+ # true, don't change it back to false!
1076
+ def update(rows)
1077
+ @dirty = true
1078
+ rows.delete_observer(self) # No need to tell us again.
1079
+ nil
1080
+ end
1081
+
1082
+ # Magick::Image::View::Pixels
1083
+ # Defines channel attribute getters/setters
1084
+ class Pixels < Array
1085
+ include Observable
1086
+
1087
+ # Define a getter and a setter for each channel.
1088
+ [:red, :green, :blue, :opacity].each do |c|
1089
+ module_eval <<-END_EVAL
1096
1090
  def #{c}
1097
1091
  return collect { |p| p.#{c} }
1098
1092
  end
@@ -1103,262 +1097,256 @@ class Image
1103
1097
  nil
1104
1098
  end
1105
1099
  END_EVAL
1106
- end
1107
-
1108
- end # class Magick::Image::View::Pixels
1109
-
1110
- # Magick::Image::View::Rows
1111
- class Rows
1112
- include Observable
1113
-
1114
- def initialize(view, width, height, rows)
1115
- @view = view
1116
- @width = width
1117
- @height = height
1118
- @rows = rows
1119
- end
1120
-
1121
- def [](*args)
1122
- cols(args)
1123
-
1124
- # Both View::Pixels and Magick::Pixel implement Observable
1125
- if @unique
1126
- pixels = @view[@rows[0]*@width + @cols[0]]
1127
- pixels.add_observer(self)
1128
- else
1129
- pixels = View::Pixels.new
1130
- each do |x|
1131
- p = @view[x]
1132
- p.add_observer(self)
1133
- pixels << p
1100
+ end
1101
+ end # class Magick::Image::View::Pixels
1102
+
1103
+ # Magick::Image::View::Rows
1104
+ class Rows
1105
+ include Observable
1106
+
1107
+ def initialize(view, width, height, rows)
1108
+ @view = view
1109
+ @width = width
1110
+ @height = height
1111
+ @rows = rows
1112
+ end
1113
+
1114
+ def [](*args)
1115
+ cols(args)
1116
+
1117
+ # Both View::Pixels and Magick::Pixel implement Observable
1118
+ if @unique
1119
+ pixels = @view[@rows[0]*@width + @cols[0]]
1120
+ pixels.add_observer(self)
1121
+ else
1122
+ pixels = View::Pixels.new
1123
+ each do |x|
1124
+ p = @view[x]
1125
+ p.add_observer(self)
1126
+ pixels << p
1127
+ end
1134
1128
  end
1129
+ pixels
1135
1130
  end
1136
- pixels
1137
- end
1138
-
1139
- def []=(*args)
1140
- rv = args.delete_at(-1) # get rvalue
1141
- if ! rv.is_a?(Pixel) # must be a Pixel or a color name
1142
- begin
1131
+
1132
+ def []=(*args)
1133
+ rv = args.delete_at(-1) # get rvalue
1134
+ unless rv.is_a?(Pixel) # must be a Pixel or a color name
1135
+ begin
1143
1136
  rv = Pixel.from_color(rv)
1144
1137
  rescue TypeError
1145
- Kernel.raise TypeError, "cannot convert #{rv.class} into Pixel"
1138
+ Kernel.raise TypeError, "cannot convert #{rv.class} into Pixel"
1139
+ end
1146
1140
  end
1141
+ cols(args)
1142
+ each { |x| @view[x] = rv.dup }
1143
+ changed
1144
+ notify_observers(self)
1145
+ nil
1147
1146
  end
1148
- cols(args)
1149
- each { |x| @view[x] = rv.dup }
1150
- changed
1151
- notify_observers(self)
1152
- nil
1153
- end
1154
-
1155
- # A pixel has been modified. Tell the view.
1156
- def update(pixel)
1157
- changed
1158
- notify_observers(self)
1159
- pixel.delete_observer(self) # Don't need to hear again.
1160
- nil
1161
- end
1162
-
1163
- private
1164
-
1165
- def cols(*args)
1166
- @cols = args[0] # remove the outermost array
1167
- @unique = false
1168
-
1169
- # Convert @rows to an Enumerable object
1170
- case @rows.length
1171
- when 0 # Create a Range for all the rows
1172
- @rows = Range.new(0, @height, true)
1173
- when 1 # Range, Array, or a single integer
1174
- # if the single element is already an Enumerable
1175
- # object, get it.
1176
- if @rows.first.respond_to? :each
1147
+
1148
+ # A pixel has been modified. Tell the view.
1149
+ def update(pixel)
1150
+ changed
1151
+ notify_observers(self)
1152
+ pixel.delete_observer(self) # Don't need to hear again.
1153
+ nil
1154
+ end
1155
+
1156
+ private
1157
+
1158
+ def cols(*args)
1159
+ @cols = args[0] # remove the outermost array
1160
+ @unique = false
1161
+
1162
+ # Convert @rows to an Enumerable object
1163
+ case @rows.length
1164
+ when 0 # Create a Range for all the rows
1165
+ @rows = Range.new(0, @height, true)
1166
+ when 1 # Range, Array, or a single integer
1167
+ # if the single element is already an Enumerable
1168
+ # object, get it.
1169
+ if @rows.first.respond_to? :each
1177
1170
  @rows = @rows.first
1178
- else
1171
+ else
1179
1172
  @rows = Integer(@rows.first)
1180
- if @rows < 0
1173
+ if @rows < 0
1181
1174
  @rows += @height
1182
- end
1183
- if @rows < 0 || @rows > @height-1
1175
+ end
1176
+ if @rows < 0 || @rows > @height-1
1184
1177
  Kernel.raise IndexError, "index [#{@rows}] out of range"
1178
+ end
1179
+ # Convert back to an array
1180
+ @rows = Array.new(1, @rows)
1181
+ @unique = true
1182
+ end
1183
+ when 2
1184
+ # A pair of integers representing the starting column and the number of columns
1185
+ start = Integer(@rows[0])
1186
+ length = Integer(@rows[1])
1187
+
1188
+ # Negative start -> start from last row
1189
+ if start < 0
1190
+ start += @height
1185
1191
  end
1186
- # Convert back to an array
1187
- @rows = Array.new(1, @rows)
1188
- @unique = true
1189
- end
1190
- when 2
1191
- # A pair of integers representing the starting column and the number of columns
1192
- start = Integer(@rows[0])
1193
- length = Integer(@rows[1])
1194
-
1195
- # Negative start -> start from last row
1196
- if start < 0
1197
- start += @height
1198
- end
1199
-
1200
- if start > @height || start < 0 || length < 0
1201
- Kernel.raise IndexError, "index [#{@rows.first}] out of range"
1202
- else
1203
- if start + length > @height
1192
+
1193
+ if start > @height || start < 0 || length < 0
1194
+ Kernel.raise IndexError, "index [#{@rows.first}] out of range"
1195
+ else
1196
+ if start + length > @height
1204
1197
  length = @height - length
1205
- length = [length, 0].max
1198
+ length = [length, 0].max
1199
+ end
1206
1200
  end
1207
- end
1208
- # Create a Range for the specified set of rows
1209
- @rows = Range.new(start, start+length, true)
1210
- end
1211
-
1212
- case @cols.length
1213
- when 0 # all rows
1214
- @cols = Range.new(0, @width, true) # convert to range
1215
- @unique = false
1216
- when 1 # Range, Array, or a single integer
1217
- # if the single element is already an Enumerable
1218
- # object, get it.
1219
- if @cols.first.respond_to? :each
1220
- @cols = @cols.first
1201
+ # Create a Range for the specified set of rows
1202
+ @rows = Range.new(start, start+length, true)
1203
+ end
1204
+
1205
+ case @cols.length
1206
+ when 0 # all rows
1207
+ @cols = Range.new(0, @width, true) # convert to range
1221
1208
  @unique = false
1222
- else
1209
+ when 1 # Range, Array, or a single integer
1210
+ # if the single element is already an Enumerable
1211
+ # object, get it.
1212
+ if @cols.first.respond_to? :each
1213
+ @cols = @cols.first
1214
+ @unique = false
1215
+ else
1223
1216
  @cols = Integer(@cols.first)
1224
- if @cols < 0
1217
+ if @cols < 0
1225
1218
  @cols += @width
1226
- end
1227
- if @cols < 0 || @cols > @width-1
1219
+ end
1220
+ if @cols < 0 || @cols > @width-1
1228
1221
  Kernel.raise IndexError, "index [#{@cols}] out of range"
1222
+ end
1223
+ # Convert back to array
1224
+ @cols = Array.new(1, @cols)
1225
+ @unique &&= true
1226
+ end
1227
+ when 2
1228
+ # A pair of integers representing the starting column and the number of columns
1229
+ start = Integer(@cols[0])
1230
+ length = Integer(@cols[1])
1231
+
1232
+ # Negative start -> start from last row
1233
+ if start < 0
1234
+ start += @width
1229
1235
  end
1230
- # Convert back to array
1231
- @cols = Array.new(1, @cols)
1232
- @unique &&= true
1233
- end
1234
- when 2
1235
- # A pair of integers representing the starting column and the number of columns
1236
- start = Integer(@cols[0])
1237
- length = Integer(@cols[1])
1238
-
1239
- # Negative start -> start from last row
1240
- if start < 0
1241
- start += @width
1242
- end
1243
-
1244
- if start > @width || start < 0 || length < 0
1245
- ; #nop
1246
- else
1247
- if start + length > @width
1248
- length = @width - length
1249
- length = [length, 0].max
1236
+
1237
+ if start > @width || start < 0 || length < 0
1238
+ ; #nop
1239
+ else
1240
+ if start + length > @width
1241
+ length = @width - length
1242
+ length = [length, 0].max
1243
+ end
1250
1244
  end
1251
- end
1252
- # Create a Range for the specified set of columns
1253
- @cols = Range.new(start, start+length, true)
1254
- @unique = false
1245
+ # Create a Range for the specified set of columns
1246
+ @cols = Range.new(start, start+length, true)
1247
+ @unique = false
1248
+ end
1255
1249
  end
1256
-
1257
- end
1258
-
1259
- # iterator called from subscript methods
1260
- def each
1261
- maxrows = @height - 1
1262
- maxcols = @width - 1
1263
-
1264
- @rows.each do |j|
1265
- if j > maxrows
1250
+
1251
+ # iterator called from subscript methods
1252
+ def each
1253
+ maxrows = @height - 1
1254
+ maxcols = @width - 1
1255
+
1256
+ @rows.each do |j|
1257
+ if j > maxrows
1266
1258
  Kernel.raise IndexError, "index [#{j}] out of range"
1267
- end
1268
- @cols.each do |i|
1269
- if i > maxcols
1259
+ end
1260
+ @cols.each do |i|
1261
+ if i > maxcols
1270
1262
  Kernel.raise IndexError, "index [#{i}] out of range"
1263
+ end
1264
+ yield j*@width + i
1271
1265
  end
1272
- yield j*@width + i
1273
1266
  end
1267
+ nil # useless return value
1274
1268
  end
1275
- nil # useless return value
1276
- end
1277
-
1278
- end # class Magick::Image::View::Rows
1279
-
1280
- end # class Magick::Image::View
1281
-
1282
- end # class Magick::Image
1283
-
1284
- class ImageList
1285
-
1286
- include Comparable
1287
- include Enumerable
1288
- attr_reader :scene
1289
-
1290
- private
1291
-
1292
- def get_current()
1293
- return @images[@scene].__id__ rescue nil
1294
- end
1295
-
1296
- protected
1297
-
1298
- def is_an_image(obj)
1299
- unless obj.kind_of? Magick::Image
1269
+ end # class Magick::Image::View::Rows
1270
+ end # class Magick::Image::View
1271
+ end # class Magick::Image
1272
+
1273
+ class ImageList
1274
+ include Comparable
1275
+ include Enumerable
1276
+ attr_reader :scene
1277
+
1278
+ private
1279
+
1280
+ def get_current
1281
+ return @images[@scene].__id__ rescue nil
1282
+ end
1283
+
1284
+ protected
1285
+
1286
+ def is_an_image(obj)
1287
+ unless obj.is_a? Magick::Image
1300
1288
  Kernel.raise ArgumentError, "Magick::Image required (#{obj.class} given)"
1289
+ end
1290
+ true
1301
1291
  end
1302
- true
1303
- end
1304
-
1305
- # Ensure array is always an array of Magick::Image objects
1306
- def is_an_image_array(ary)
1307
- unless ary.respond_to? :each
1292
+
1293
+ # Ensure array is always an array of Magick::Image objects
1294
+ def is_an_image_array(ary)
1295
+ unless ary.respond_to? :each
1308
1296
  Kernel.raise ArgumentError, "Magick::ImageList or array of Magick::Images required (#{ary.class} given)"
1309
- end
1310
- ary.each { |obj| is_an_image obj }
1311
- true
1312
- end
1313
-
1314
- # Find old current image, update scene number
1315
- # current is the id of the old current image.
1316
- def set_current(current)
1317
- if length() == 0
1297
+ end
1298
+ ary.each { |obj| is_an_image obj }
1299
+ true
1300
+ end
1301
+
1302
+ # Find old current image, update scene number
1303
+ # current is the id of the old current image.
1304
+ def set_current(current)
1305
+ if length == 0
1318
1306
  self.scene = nil
1319
- return
1320
- # Don't bother looking for current image
1321
- elsif scene() == nil || scene() >= length()
1322
- self.scene = length() - 1
1323
- return
1324
- elsif current != nil
1307
+ return
1308
+ # Don't bother looking for current image
1309
+ elsif scene.nil? || scene >= length
1310
+ self.scene = length - 1
1311
+ return
1312
+ elsif !current.nil?
1325
1313
  # Find last instance of "current" in the list.
1326
1314
  # If "current" isn't in the list, set current to last image.
1327
- self.scene = length() - 1
1328
- each_with_index do |f,i|
1315
+ self.scene = length - 1
1316
+ each_with_index do |f,i|
1329
1317
  if f.__id__ == current
1330
- self.scene = i
1318
+ self.scene = i
1331
1319
  end
1320
+ end
1321
+ return
1322
+ end
1323
+ self.scene = length - 1
1324
+ end
1325
+
1326
+ public
1327
+
1328
+ # Allow scene to be set to nil
1329
+ def scene=(n)
1330
+ if n.nil?
1331
+ Kernel.raise IndexError, 'scene number out of bounds' unless @images.length == 0
1332
+ @scene = nil
1333
+ return @scene
1334
+ elsif @images.length == 0
1335
+ Kernel.raise IndexError, 'scene number out of bounds'
1336
+ end
1337
+
1338
+ n = Integer(n)
1339
+ if n < 0 || n > length - 1
1340
+ Kernel.raise IndexError, 'scene number out of bounds'
1332
1341
  end
1333
- return
1334
- end
1335
- self.scene = length() - 1
1336
- end
1337
-
1338
- public
1339
-
1340
- # Allow scene to be set to nil
1341
- def scene=(n)
1342
- if n.nil?
1343
- Kernel.raise IndexError, "scene number out of bounds" unless @images.length == 0
1344
- @scene = nil
1345
- return @scene
1346
- elsif @images.length == 0
1347
- Kernel.raise IndexError, "scene number out of bounds"
1348
- end
1349
-
1350
- n = Integer(n)
1351
- if n < 0 || n > length - 1
1352
- Kernel.raise IndexError, "scene number out of bounds"
1353
- end
1354
- @scene = n
1355
- return @scene
1356
- end
1357
-
1358
- # All the binary operators work the same way.
1359
- # 'other' should be either an ImageList or an Array
1360
- %w{& + - |}.each do |op|
1361
- module_eval <<-END_BINOPS
1342
+ @scene = n
1343
+ @scene
1344
+ end
1345
+
1346
+ # All the binary operators work the same way.
1347
+ # 'other' should be either an ImageList or an Array
1348
+ %w{& + - |}.each do |op|
1349
+ module_eval <<-END_BINOPS
1362
1350
  def #{op}(other)
1363
1351
  ilist = self.class.new
1364
1352
  begin
@@ -1375,590 +1363,583 @@ public
1375
1363
  return ilist
1376
1364
  end
1377
1365
  END_BINOPS
1378
- end
1379
-
1380
- def *(n)
1381
- unless n.kind_of? Integer
1382
- Kernel.raise ArgumentError, "Integer required (#{n.class} given)"
1383
1366
  end
1384
- current = get_current()
1385
- ilist = self.class.new
1386
- (@images * n).each {|image| ilist << image}
1387
- ilist.set_current current
1388
- return ilist
1389
- end
1390
-
1391
- def <<(obj)
1392
- is_an_image obj
1393
- @images << obj
1394
- @scene = @images.length - 1
1395
- self
1396
- end
1397
-
1398
- # Compare ImageLists
1399
- # Compare each image in turn until the result of a comparison
1400
- # is not 0. If all comparisons return 0, then
1401
- # return if A.scene != B.scene
1402
- # return A.length <=> B.length
1403
- def <=>(other)
1404
- unless other.kind_of? self.class
1405
- Kernel.raise TypeError, "#{self.class} required (#{other.class} given)"
1406
- end
1407
- size = [self.length, other.length].min
1408
- size.times do |x|
1409
- r = self[x] <=> other[x]
1367
+
1368
+ def *(n)
1369
+ unless n.is_a? Integer
1370
+ Kernel.raise ArgumentError, "Integer required (#{n.class} given)"
1371
+ end
1372
+ current = get_current
1373
+ ilist = self.class.new
1374
+ (@images * n).each {|image| ilist << image}
1375
+ ilist.set_current current
1376
+ ilist
1377
+ end
1378
+
1379
+ def <<(obj)
1380
+ is_an_image obj
1381
+ @images << obj
1382
+ @scene = @images.length - 1
1383
+ self
1384
+ end
1385
+
1386
+ # Compare ImageLists
1387
+ # Compare each image in turn until the result of a comparison
1388
+ # is not 0. If all comparisons return 0, then
1389
+ # return if A.scene != B.scene
1390
+ # return A.length <=> B.length
1391
+ def <=>(other)
1392
+ unless other.is_a? self.class
1393
+ Kernel.raise TypeError, "#{self.class} required (#{other.class} given)"
1394
+ end
1395
+ size = [length, other.length].min
1396
+ size.times do |x|
1397
+ r = self[x] <=> other[x]
1398
+ return r unless r == 0
1399
+ end
1400
+ if @scene.nil? && other.scene.nil?
1401
+ return 0
1402
+ elsif @scene.nil? && !other.scene.nil?
1403
+ Kernel.raise TypeError, "cannot convert nil into #{other.scene.class}"
1404
+ elsif ! @scene.nil? && other.scene.nil?
1405
+ Kernel.raise TypeError, "cannot convert nil into #{scene.class}"
1406
+ end
1407
+ r = scene <=> other.scene
1410
1408
  return r unless r == 0
1409
+ length <=> other.length
1411
1410
  end
1412
- if @scene.nil? && other.scene.nil?
1413
- return 0
1414
- elsif @scene.nil? && ! other.scene.nil?
1415
- Kernel.raise TypeError, "cannot convert nil into #{other.scene.class}"
1416
- elsif ! @scene.nil? && other.scene.nil?
1417
- Kernel.raise TypeError, "cannot convert nil into #{self.scene.class}"
1418
- end
1419
- r = self.scene <=> other.scene
1420
- return r unless r == 0
1421
- return self.length <=> other.length
1422
- end
1423
-
1424
- def [](*args)
1425
- a = @images[*args]
1426
- if a.respond_to?(:each) then
1427
- ilist = self.class.new
1428
- a.each {|image| ilist << image}
1429
- a = ilist
1411
+
1412
+ def [](*args)
1413
+ a = @images[*args]
1414
+ if a.respond_to?(:each)
1415
+ ilist = self.class.new
1416
+ a.each {|image| ilist << image}
1417
+ a = ilist
1418
+ end
1419
+ a
1420
+ end
1421
+
1422
+ def []=(*args)
1423
+ obj = @images.[]=(*args)
1424
+ if obj && obj.respond_to?(:each)
1425
+ is_an_image_array(obj)
1426
+ set_current obj.last.__id__
1427
+ elsif obj
1428
+ is_an_image(obj)
1429
+ set_current obj.__id__
1430
+ else
1431
+ set_current nil
1432
+ end
1433
+ obj
1430
1434
  end
1431
- return a
1432
- end
1433
-
1434
- def []=(*args)
1435
- obj = @images.[]=(*args)
1436
- if obj && obj.respond_to?(:each) then
1437
- is_an_image_array(obj)
1438
- set_current obj.last.__id__
1439
- elsif obj
1440
- is_an_image(obj)
1441
- set_current obj.__id__
1442
- else
1443
- set_current nil
1444
- end
1445
- return obj
1446
- end
1447
-
1448
- [:at, :each, :each_index, :empty?, :fetch,
1449
- :first, :hash, :include?, :index, :length, :rindex, :sort!].each do |mth|
1450
- module_eval <<-END_SIMPLE_DELEGATES
1435
+
1436
+ [:at, :each, :each_index, :empty?, :fetch,
1437
+ :first, :hash, :include?, :index, :length, :rindex, :sort!].each do |mth|
1438
+ module_eval <<-END_SIMPLE_DELEGATES
1451
1439
  def #{mth}(*args, &block)
1452
1440
  @images.#{mth}(*args, &block)
1453
1441
  end
1454
1442
  END_SIMPLE_DELEGATES
1455
- end
1456
- alias_method :size, :length
1457
-
1458
- # Array#nitems is not available in 1.9
1459
- if Array.instance_methods.include?("nitems")
1460
- def nitems()
1461
- @images.nitems()
1462
- end
1463
- end
1464
-
1465
- def clear
1466
- @scene = nil
1467
- @images.clear
1468
- end
1469
-
1470
- def clone
1471
- ditto = dup
1472
- ditto.freeze if frozen?
1473
- return ditto
1474
- end
1475
-
1476
- # override Enumerable#collect
1477
- def collect(&block)
1478
- current = get_current()
1479
- a = @images.collect(&block)
1480
- ilist = self.class.new
1481
- a.each {|image| ilist << image}
1482
- ilist.set_current current
1483
- return ilist
1484
- end
1485
-
1486
- def collect!(&block)
1487
- @images.collect!(&block)
1488
- is_an_image_array @images
1489
- self
1490
- end
1491
-
1492
- # Make a deep copy
1493
- def copy
1494
- ditto = self.class.new
1495
- @images.each { |f| ditto << f.copy }
1496
- ditto.scene = @scene
1497
- ditto.taint if tainted?
1498
- return ditto
1499
- end
1500
-
1501
- # Return the current image
1502
- def cur_image
1503
- if ! @scene
1504
- Kernel.raise IndexError, "no images in this list"
1505
1443
  end
1506
- @images[@scene]
1507
- end
1508
-
1509
- # ImageList#map took over the "map" name. Use alternatives.
1510
- alias_method :__map__, :collect
1511
- alias_method :map!, :collect!
1512
- alias_method :__map__!, :collect!
1513
-
1514
- # ImageMagic used affinity in 6.4.3, switch to remap in 6.4.4.
1515
- alias_method :affinity, :remap
1516
-
1517
- def compact
1518
- current = get_current()
1519
- ilist = self.class.new
1520
- a = @images.compact
1521
- a.each {|image| ilist << image}
1522
- ilist.set_current current
1523
- return ilist
1524
- end
1525
-
1526
- def compact!
1527
- current = get_current()
1528
- a = @images.compact! # returns nil if no changes were made
1529
- set_current current
1530
- return a.nil? ? nil : self
1531
- end
1532
-
1533
- def concat(other)
1534
- is_an_image_array other
1535
- other.each {|image| @images << image}
1536
- @scene = length-1
1537
- return self
1538
- end
1539
-
1540
- # Set same delay for all images
1541
- def delay=(d)
1542
- if Integer(d) < 0
1543
- raise ArgumentError, "delay must be greater than or equal to 0"
1444
+ alias_method :size, :length
1445
+
1446
+ # Array#nitems is not available in 1.9
1447
+ if Array.instance_methods.include?('nitems')
1448
+ def nitems
1449
+ @images.nitems
1450
+ end
1451
+ end
1452
+
1453
+ def clear
1454
+ @scene = nil
1455
+ @images.clear
1456
+ end
1457
+
1458
+ def clone
1459
+ ditto = dup
1460
+ ditto.freeze if frozen?
1461
+ ditto
1462
+ end
1463
+
1464
+ # override Enumerable#collect
1465
+ def collect(&block)
1466
+ current = get_current
1467
+ a = @images.collect(&block)
1468
+ ilist = self.class.new
1469
+ a.each {|image| ilist << image}
1470
+ ilist.set_current current
1471
+ ilist
1472
+ end
1473
+
1474
+ def collect!(&block)
1475
+ @images.collect!(&block)
1476
+ is_an_image_array @images
1477
+ self
1478
+ end
1479
+
1480
+ # Make a deep copy
1481
+ def copy
1482
+ ditto = self.class.new
1483
+ @images.each { |f| ditto << f.copy }
1484
+ ditto.scene = @scene
1485
+ ditto.taint if tainted?
1486
+ ditto
1487
+ end
1488
+
1489
+ # Return the current image
1490
+ def cur_image
1491
+ unless @scene
1492
+ Kernel.raise IndexError, 'no images in this list'
1493
+ end
1494
+ @images[@scene]
1495
+ end
1496
+
1497
+ # ImageList#map took over the "map" name. Use alternatives.
1498
+ alias_method :__map__, :collect
1499
+ alias_method :map!, :collect!
1500
+ alias_method :__map__!, :collect!
1501
+
1502
+ # ImageMagic used affinity in 6.4.3, switch to remap in 6.4.4.
1503
+ alias_method :affinity, :remap
1504
+
1505
+ def compact
1506
+ current = get_current
1507
+ ilist = self.class.new
1508
+ a = @images.compact
1509
+ a.each {|image| ilist << image}
1510
+ ilist.set_current current
1511
+ ilist
1512
+ end
1513
+
1514
+ def compact!
1515
+ current = get_current
1516
+ a = @images.compact! # returns nil if no changes were made
1517
+ set_current current
1518
+ a.nil? ? nil : self
1519
+ end
1520
+
1521
+ def concat(other)
1522
+ is_an_image_array other
1523
+ other.each {|image| @images << image}
1524
+ @scene = length-1
1525
+ self
1526
+ end
1527
+
1528
+ # Set same delay for all images
1529
+ def delay=(d)
1530
+ if Integer(d) < 0
1531
+ fail ArgumentError, 'delay must be greater than or equal to 0'
1532
+ end
1533
+ @images.each { |f| f.delay = Integer(d) }
1534
+ end
1535
+
1536
+ def delete(obj, &block)
1537
+ is_an_image obj
1538
+ current = get_current
1539
+ a = @images.delete(obj, &block)
1540
+ set_current current
1541
+ a
1542
+ end
1543
+
1544
+ def delete_at(ndx)
1545
+ current = get_current
1546
+ a = @images.delete_at(ndx)
1547
+ set_current current
1548
+ a
1549
+ end
1550
+
1551
+ def delete_if(&block)
1552
+ current = get_current
1553
+ @images.delete_if(&block)
1554
+ set_current current
1555
+ self
1556
+ end
1557
+
1558
+ def dup
1559
+ ditto = self.class.new
1560
+ @images.each {|img| ditto << img}
1561
+ ditto.scene = @scene
1562
+ ditto.taint if tainted?
1563
+ ditto
1564
+ end
1565
+
1566
+ def eql?(other)
1567
+ is_an_image_array other
1568
+ eql = other.eql?(@images)
1569
+ begin # "other" is another ImageList
1570
+ eql &&= @scene == other.scene
1571
+ rescue NoMethodError
1572
+ # "other" is a plain Array
1573
+ end
1574
+ eql
1575
+ end
1576
+
1577
+ def fill(*args, &block)
1578
+ is_an_image args[0] unless block_given?
1579
+ current = get_current
1580
+ @images.fill(*args, &block)
1581
+ is_an_image_array self
1582
+ set_current current
1583
+ self
1584
+ end
1585
+
1586
+ # Override Enumerable's find_all
1587
+ def find_all(&block)
1588
+ current = get_current
1589
+ a = @images.find_all(&block)
1590
+ ilist = self.class.new
1591
+ a.each {|image| ilist << image}
1592
+ ilist.set_current current
1593
+ ilist
1594
+ end
1595
+ alias_method :select, :find_all
1596
+
1597
+ def from_blob(*blobs, &block)
1598
+ if (blobs.length == 0)
1599
+ Kernel.raise ArgumentError, 'no blobs given'
1600
+ end
1601
+ blobs.each do |b|
1602
+ Magick::Image.from_blob(b, &block).each { |n| @images << n }
1603
+ end
1604
+ @scene = length - 1
1605
+ self
1544
1606
  end
1545
- @images.each { |f| f.delay = Integer(d) }
1546
- end
1547
-
1548
- def delete(obj, &block)
1549
- is_an_image obj
1550
- current = get_current()
1551
- a = @images.delete(obj, &block)
1552
- set_current current
1553
- return a
1554
- end
1555
-
1556
- def delete_at(ndx)
1557
- current = get_current()
1558
- a = @images.delete_at(ndx)
1559
- set_current current
1560
- return a
1561
- end
1562
-
1563
- def delete_if(&block)
1564
- current = get_current()
1565
- @images.delete_if(&block)
1566
- set_current current
1567
- self
1568
- end
1569
-
1570
- def dup
1571
- ditto = self.class.new
1572
- @images.each {|img| ditto << img}
1573
- ditto.scene = @scene
1574
- ditto.taint if tainted?
1575
- return ditto
1576
- end
1577
-
1578
- def eql?(other)
1579
- is_an_image_array other
1580
- eql = other.eql?(@images)
1581
- begin # "other" is another ImageList
1582
- eql &&= @scene == other.scene
1607
+
1608
+ # Initialize new instances
1609
+ def initialize(*filenames, &block)
1610
+ @images = []
1611
+ @scene = nil
1612
+ filenames.each do |f|
1613
+ Magick::Image.read(f, &block).each { |n| @images << n }
1614
+ end
1615
+ if length > 0
1616
+ @scene = length - 1 # last image in array
1617
+ end
1618
+ self
1619
+ end
1620
+
1621
+ def insert(index, *args)
1622
+ args.each {|image| is_an_image image}
1623
+ current = get_current
1624
+ @images.insert(index, *args)
1625
+ set_current current
1626
+ self
1627
+ end
1628
+
1629
+ # Call inspect for all the images
1630
+ def inspect
1631
+ img = []
1632
+ @images.each {|image| img << image.inspect }
1633
+ img = '[' + img.join(",\n") + "]\nscene=#{@scene}"
1634
+ end
1635
+
1636
+ # Set the number of iterations of an animated GIF
1637
+ def iterations=(n)
1638
+ n = Integer(n)
1639
+ if n < 0 || n > 65535
1640
+ Kernel.raise ArgumentError, 'iterations must be between 0 and 65535'
1641
+ end
1642
+ @images.each {|f| f.iterations=n}
1643
+ self
1644
+ end
1645
+
1646
+ def last(*args)
1647
+ if args.length == 0
1648
+ a = @images.last
1649
+ else
1650
+ a = @images.last(*args)
1651
+ ilist = self.class.new
1652
+ a.each {|img| ilist << img}
1653
+ @scene = a.length - 1
1654
+ a = ilist
1655
+ end
1656
+ a
1657
+ end
1658
+
1659
+ # Custom marshal/unmarshal for Ruby 1.8.
1660
+ def marshal_dump
1661
+ ary = [@scene]
1662
+ @images.each {|i| ary << Marshal.dump(i)}
1663
+ ary
1664
+ end
1665
+
1666
+ def marshal_load(ary)
1667
+ @scene = ary.shift
1668
+ @images = []
1669
+ ary.each {|a| @images << Marshal.load(a)}
1670
+ end
1671
+
1672
+ # The ImageList class supports the Magick::Image class methods by simply sending
1673
+ # the method to the current image. If the method isn't explicitly supported,
1674
+ # send it to the current image in the array. If there are no images, send
1675
+ # it up the line. Catch a NameError and emit a useful message.
1676
+ def method_missing(methID, *args, &block)
1677
+ if @scene
1678
+ @images[@scene].send(methID, *args, &block)
1679
+ else
1680
+ super
1681
+ end
1583
1682
  rescue NoMethodError
1584
- # "other" is a plain Array
1585
- end
1586
- return eql
1587
- end
1588
-
1589
- def fill(*args, &block)
1590
- is_an_image args[0] unless block_given?
1591
- current = get_current()
1592
- @images.fill(*args, &block)
1593
- is_an_image_array self
1594
- set_current current
1595
- self
1596
- end
1597
-
1598
- # Override Enumerable's find_all
1599
- def find_all(&block)
1600
- current = get_current()
1601
- a = @images.find_all(&block)
1602
- ilist = self.class.new
1603
- a.each {|image| ilist << image}
1604
- ilist.set_current current
1605
- return ilist
1606
- end
1607
- alias_method :select, :find_all
1608
-
1609
- def from_blob(*blobs, &block)
1610
- if (blobs.length == 0)
1611
- Kernel.raise ArgumentError, "no blobs given"
1612
- end
1613
- blobs.each { |b|
1614
- Magick::Image.from_blob(b, &block).each { |n| @images << n }
1615
- }
1616
- @scene = length - 1
1617
- self
1618
- end
1619
-
1620
- # Initialize new instances
1621
- def initialize(*filenames, &block)
1622
- @images = []
1623
- @scene = nil
1624
- filenames.each { |f|
1625
- Magick::Image.read(f, &block).each { |n| @images << n }
1626
- }
1627
- if length > 0
1628
- @scene = length - 1 # last image in array
1629
- end
1630
- self
1631
- end
1632
-
1633
- def insert(index, *args)
1634
- args.each {|image| is_an_image image}
1635
- current = get_current()
1636
- @images.insert(index, *args)
1637
- set_current current
1638
- return self
1639
- end
1640
-
1641
- # Call inspect for all the images
1642
- def inspect
1643
- img = []
1644
- @images.each {|image| img << image.inspect }
1645
- img = "[" + img.join(",\n") + "]\nscene=#{@scene}"
1646
- end
1647
-
1648
- # Set the number of iterations of an animated GIF
1649
- def iterations=(n)
1650
- n = Integer(n)
1651
- if n < 0 || n > 65535
1652
- Kernel.raise ArgumentError, "iterations must be between 0 and 65535"
1653
- end
1654
- @images.each {|f| f.iterations=n}
1655
- self
1656
- end
1657
-
1658
- def last(*args)
1659
- if args.length == 0
1660
- a = @images.last
1661
- else
1662
- a = @images.last(*args)
1663
- ilist = self.class.new
1664
- a.each {|img| ilist << img}
1665
- @scene = a.length - 1
1666
- a = ilist
1667
- end
1668
- return a
1669
- end
1670
-
1671
- # Custom marshal/unmarshal for Ruby 1.8.
1672
- def marshal_dump()
1673
- ary = [@scene]
1674
- @images.each {|i| ary << Marshal.dump(i)}
1675
- ary
1676
- end
1677
-
1678
- def marshal_load(ary)
1679
- @scene = ary.shift
1680
- @images = []
1681
- ary.each {|a| @images << Marshal.load(a)}
1682
- end
1683
-
1684
- # The ImageList class supports the Magick::Image class methods by simply sending
1685
- # the method to the current image. If the method isn't explicitly supported,
1686
- # send it to the current image in the array. If there are no images, send
1687
- # it up the line. Catch a NameError and emit a useful message.
1688
- def method_missing(methID, *args, &block)
1689
- begin
1690
- if @scene
1691
- @images[@scene].send(methID, *args, &block)
1692
- else
1693
- super
1683
+ Kernel.raise NoMethodError, "undefined method `#{methID.id2name}' for #{self.class}"
1684
+ rescue Exception
1685
+ $ERROR_POSITION.delete_if { |s| /:in `send'$/.match(s) || /:in `method_missing'$/.match(s) }
1686
+ Kernel.raise
1687
+ end
1688
+
1689
+ # Create a new image and add it to the end
1690
+ def new_image(cols, rows, *fill, &info_blk)
1691
+ self << Magick::Image.new(cols, rows, *fill, &info_blk)
1692
+ end
1693
+
1694
+ def partition(&block)
1695
+ a = @images.partition(&block)
1696
+ t = self.class.new
1697
+ a[0].each { |img| t << img}
1698
+ t.set_current nil
1699
+ f = self.class.new
1700
+ a[1].each { |img| f << img}
1701
+ f.set_current nil
1702
+ [t, f]
1703
+ end
1704
+
1705
+ # Ping files and concatenate the new images
1706
+ def ping(*files, &block)
1707
+ if (files.length == 0)
1708
+ Kernel.raise ArgumentError, 'no files given'
1709
+ end
1710
+ files.each do |f|
1711
+ Magick::Image.ping(f, &block).each { |n| @images << n }
1694
1712
  end
1695
- rescue NoMethodError
1696
- Kernel.raise NoMethodError, "undefined method `#{methID.id2name}' for #{self.class}"
1697
- rescue Exception
1698
- $@.delete_if { |s| /:in `send'$/.match(s) || /:in `method_missing'$/.match(s) }
1699
- Kernel.raise
1700
- end
1701
- end
1702
-
1703
- # Create a new image and add it to the end
1704
- def new_image(cols, rows, *fill, &info_blk)
1705
- self << Magick::Image.new(cols, rows, *fill, &info_blk)
1706
- end
1707
-
1708
- def partition(&block)
1709
- a = @images.partition(&block)
1710
- t = self.class.new
1711
- a[0].each { |img| t << img}
1712
- t.set_current nil
1713
- f = self.class.new
1714
- a[1].each { |img| f << img}
1715
- f.set_current nil
1716
- [t, f]
1717
- end
1718
-
1719
- # Ping files and concatenate the new images
1720
- def ping(*files, &block)
1721
- if (files.length == 0)
1722
- Kernel.raise ArgumentError, "no files given"
1723
- end
1724
- files.each { |f|
1725
- Magick::Image.ping(f, &block).each { |n| @images << n }
1726
- }
1727
- @scene = length - 1
1728
- self
1729
- end
1730
-
1731
- def pop
1732
- current = get_current()
1733
- a = @images.pop # can return nil
1734
- set_current current
1735
- return a
1736
- end
1737
-
1738
- def push(*objs)
1739
- objs.each do |image|
1713
+ @scene = length - 1
1714
+ self
1715
+ end
1716
+
1717
+ def pop
1718
+ current = get_current
1719
+ a = @images.pop # can return nil
1720
+ set_current current
1721
+ a
1722
+ end
1723
+
1724
+ def push(*objs)
1725
+ objs.each do |image|
1740
1726
  is_an_image image
1741
- @images << image
1742
- end
1743
- @scene = length - 1
1744
- self
1745
- end
1746
-
1747
- # Read files and concatenate the new images
1748
- def read(*files, &block)
1749
- if (files.length == 0)
1750
- Kernel.raise ArgumentError, "no files given"
1751
- end
1752
- files.each { |f|
1753
- Magick::Image.read(f, &block).each { |n| @images << n }
1754
- }
1755
- @scene = length - 1
1756
- self
1757
- end
1758
-
1759
- # override Enumerable's reject
1760
- def reject(&block)
1761
- current = get_current()
1762
- ilist = self.class.new
1763
- a = @images.reject(&block)
1764
- a.each {|image| ilist << image}
1765
- ilist.set_current current
1766
- return ilist
1767
- end
1768
-
1769
- def reject!(&block)
1770
- current = get_current()
1771
- a = @images.reject!(&block)
1772
- @images = a if !a.nil?
1773
- set_current current
1774
- return a.nil? ? nil : self
1775
- end
1776
-
1777
- def replace(other)
1778
- is_an_image_array other
1779
- current = get_current()
1780
- @images.clear
1781
- other.each {|image| @images << image}
1782
- @scene = self.length == 0 ? nil : 0
1783
- set_current current
1784
- self
1785
- end
1786
-
1787
- # Ensure respond_to? answers correctly when we are delegating to Image
1788
- alias_method :__respond_to__?, :respond_to?
1789
- def respond_to?(methID, priv=false)
1790
- return true if __respond_to__?(methID, priv)
1791
- if @scene
1792
- @images[@scene].respond_to?(methID, priv)
1793
- else
1794
- super
1727
+ @images << image
1728
+ end
1729
+ @scene = length - 1
1730
+ self
1731
+ end
1732
+
1733
+ # Read files and concatenate the new images
1734
+ def read(*files, &block)
1735
+ if (files.length == 0)
1736
+ Kernel.raise ArgumentError, 'no files given'
1737
+ end
1738
+ files.each do |f|
1739
+ Magick::Image.read(f, &block).each { |n| @images << n }
1740
+ end
1741
+ @scene = length - 1
1742
+ self
1795
1743
  end
1796
- end
1797
-
1798
- def reverse
1799
- current = get_current()
1800
- a = self.class.new
1801
- @images.reverse_each {|image| a << image}
1802
- a.set_current current
1803
- return a
1804
- end
1805
-
1806
- def reverse!
1807
- current = get_current()
1808
- @images.reverse!
1809
- set_current current
1810
- self
1811
- end
1812
-
1813
- def reverse_each
1814
- @images.reverse_each {|image| yield(image)}
1815
- self
1816
- end
1817
-
1818
- def shift
1819
- current = get_current()
1820
- a = @images.shift
1821
- set_current current
1822
- return a
1823
- end
1824
-
1825
- def slice(*args)
1826
- current = get_current()
1827
- slice = @images.slice(*args)
1828
- if slice
1744
+
1745
+ # override Enumerable's reject
1746
+ def reject(&block)
1747
+ current = get_current
1829
1748
  ilist = self.class.new
1830
- if slice.respond_to?(:each) then
1831
- slice.each {|image| ilist << image}
1749
+ a = @images.reject(&block)
1750
+ a.each {|image| ilist << image}
1751
+ ilist.set_current current
1752
+ ilist
1753
+ end
1754
+
1755
+ def reject!(&block)
1756
+ current = get_current
1757
+ a = @images.reject!(&block)
1758
+ @images = a unless a.nil?
1759
+ set_current current
1760
+ a.nil? ? nil : self
1761
+ end
1762
+
1763
+ def replace(other)
1764
+ is_an_image_array other
1765
+ current = get_current
1766
+ @images.clear
1767
+ other.each {|image| @images << image}
1768
+ @scene = length == 0 ? nil : 0
1769
+ set_current current
1770
+ self
1771
+ end
1772
+
1773
+ # Ensure respond_to? answers correctly when we are delegating to Image
1774
+ alias_method :__respond_to__?, :respond_to?
1775
+ def respond_to?(methID, priv=false)
1776
+ return true if __respond_to__?(methID, priv)
1777
+ if @scene
1778
+ @images[@scene].respond_to?(methID, priv)
1832
1779
  else
1833
- ilist << slice
1780
+ super
1834
1781
  end
1835
- else
1836
- ilist = nil
1837
- end
1838
- return ilist
1839
- end
1840
-
1841
- def slice!(*args)
1842
- current = get_current()
1843
- a = @images.slice!(*args)
1844
- set_current current
1845
- return a
1846
- end
1847
-
1848
- def ticks_per_second=(t)
1849
- if Integer(t) < 0
1850
- Kernel.raise ArgumentError, "ticks_per_second must be greater than or equal to 0"
1851
1782
  end
1852
- @images.each { |f| f.ticks_per_second = Integer(t) }
1853
- end
1854
-
1855
- def to_a
1856
- a = Array.new
1857
- @images.each {|image| a << image}
1858
- return a
1859
- end
1860
-
1861
- def uniq
1862
- current = get_current()
1863
- a = self.class.new
1864
- @images.uniq.each {|image| a << image}
1865
- a.set_current current
1866
- return a
1867
- end
1868
-
1869
- def uniq!(*args)
1870
- current = get_current()
1871
- a = @images.uniq!
1872
- set_current current
1873
- return a.nil? ? nil : self
1874
- end
1875
-
1876
- # @scene -> new object
1877
- def unshift(obj)
1878
- is_an_image obj
1879
- @images.unshift(obj)
1880
- @scene = 0
1881
- self
1882
- end
1883
-
1884
- def values_at(*args)
1885
- a = @images.values_at(*args)
1886
- a = self.class.new
1887
- @images.values_at(*args).each {|image| a << image}
1888
- a.scene = a.length - 1
1889
- return a
1890
- end
1891
- alias_method :indexes, :values_at
1892
- alias_method :indices, :values_at
1893
-
1894
- end # Magick::ImageList
1895
-
1896
-
1897
- # Collects non-specific optional method arguments
1898
- class OptionalMethodArguments
1899
- def initialize(img)
1900
- @img = img
1901
- end
1902
-
1903
- # miscellaneous options like -verbose
1904
- def method_missing(mth, val)
1905
- @img.define(mth.to_s.tr('_', '-'), val)
1906
- end
1907
-
1908
- # set(key, val) corresponds to -set option:key val
1909
- def define(key, val = nil)
1910
- @img.define(key, val)
1911
- end
1912
-
1913
- # accepts Pixel object or color name
1914
- def highlight_color=(color)
1915
- color = @img.to_color(color) if color.respond_to?(:to_color)
1916
- @img.define("highlight-color", color)
1783
+
1784
+ def reverse
1785
+ current = get_current
1786
+ a = self.class.new
1787
+ @images.reverse_each {|image| a << image}
1788
+ a.set_current current
1789
+ a
1790
+ end
1791
+
1792
+ def reverse!
1793
+ current = get_current
1794
+ @images.reverse!
1795
+ set_current current
1796
+ self
1797
+ end
1798
+
1799
+ def reverse_each
1800
+ @images.reverse_each {|image| yield(image)}
1801
+ self
1802
+ end
1803
+
1804
+ def shift
1805
+ current = get_current
1806
+ a = @images.shift
1807
+ set_current current
1808
+ a
1809
+ end
1810
+
1811
+ def slice(*args)
1812
+ current = get_current
1813
+ slice = @images.slice(*args)
1814
+ if slice
1815
+ ilist = self.class.new
1816
+ if slice.respond_to?(:each)
1817
+ slice.each {|image| ilist << image}
1818
+ else
1819
+ ilist << slice
1820
+ end
1821
+ else
1822
+ ilist = nil
1823
+ end
1824
+ ilist
1825
+ end
1826
+
1827
+ def slice!(*args)
1828
+ current = get_current
1829
+ a = @images.slice!(*args)
1830
+ set_current current
1831
+ a
1832
+ end
1833
+
1834
+ def ticks_per_second=(t)
1835
+ if Integer(t) < 0
1836
+ Kernel.raise ArgumentError, 'ticks_per_second must be greater than or equal to 0'
1837
+ end
1838
+ @images.each { |f| f.ticks_per_second = Integer(t) }
1839
+ end
1840
+
1841
+ def to_a
1842
+ a = []
1843
+ @images.each {|image| a << image}
1844
+ a
1845
+ end
1846
+
1847
+ def uniq
1848
+ current = get_current
1849
+ a = self.class.new
1850
+ @images.uniq.each {|image| a << image}
1851
+ a.set_current current
1852
+ a
1853
+ end
1854
+
1855
+ def uniq!(*args)
1856
+ current = get_current
1857
+ a = @images.uniq!
1858
+ set_current current
1859
+ a.nil? ? nil : self
1860
+ end
1861
+
1862
+ # @scene -> new object
1863
+ def unshift(obj)
1864
+ is_an_image obj
1865
+ @images.unshift(obj)
1866
+ @scene = 0
1867
+ self
1868
+ end
1869
+
1870
+ def values_at(*args)
1871
+ a = @images.values_at(*args)
1872
+ a = self.class.new
1873
+ @images.values_at(*args).each {|image| a << image}
1874
+ a.scene = a.length - 1
1875
+ a
1876
+ end
1877
+ alias_method :indexes, :values_at
1878
+ alias_method :indices, :values_at
1879
+ end # Magick::ImageList
1880
+
1881
+ # Collects non-specific optional method arguments
1882
+ class OptionalMethodArguments
1883
+ def initialize(img)
1884
+ @img = img
1885
+ end
1886
+
1887
+ # miscellaneous options like -verbose
1888
+ def method_missing(mth, val)
1889
+ @img.define(mth.to_s.tr('_', '-'), val)
1890
+ end
1891
+
1892
+ # set(key, val) corresponds to -set option:key val
1893
+ def define(key, val = nil)
1894
+ @img.define(key, val)
1895
+ end
1896
+
1897
+ # accepts Pixel object or color name
1898
+ def highlight_color=(color)
1899
+ color = @img.to_color(color) if color.respond_to?(:to_color)
1900
+ @img.define('highlight-color', color)
1901
+ end
1902
+
1903
+ # accepts Pixel object or color name
1904
+ def lowlight_color=(color)
1905
+ color = @img.to_color(color) if color.respond_to?(:to_color)
1906
+ @img.define('lowlight-color', color)
1907
+ end
1908
+ end
1909
+
1910
+ # Example fill class. Fills the image with the specified background
1911
+ # color, then crosshatches with the specified crosshatch color.
1912
+ # @dist is the number of pixels between hatch lines.
1913
+ # See Magick::Draw examples.
1914
+ class HatchFill
1915
+ def initialize(bgcolor, hatchcolor='white', dist=10)
1916
+ @bgcolor = bgcolor
1917
+ @hatchpixel = Pixel.from_color(hatchcolor)
1918
+ @dist = dist
1919
+ end
1920
+
1921
+ def fill(img) # required
1922
+ img.background_color = @bgcolor
1923
+ img.erase! # sets image to background color
1924
+ pixels = Array.new([img.rows, img.columns].max, @hatchpixel)
1925
+ @dist.step((img.columns-1)/@dist*@dist, @dist) do |x|
1926
+ img.store_pixels(x,0,1,img.rows,pixels)
1927
+ end
1928
+ @dist.step((img.rows-1)/@dist*@dist, @dist) do |y|
1929
+ img.store_pixels(0,y,img.columns,1,pixels)
1930
+ end
1931
+ end
1917
1932
  end
1918
1933
 
1919
- # accepts Pixel object or color name
1920
- def lowlight_color=(color)
1921
- color = @img.to_color(color) if color.respond_to?(:to_color)
1922
- @img.define("lowlight-color", color)
1934
+ # Fill class with solid monochromatic color
1935
+ class SolidFill
1936
+ def initialize(bgcolor)
1937
+ @bgcolor = bgcolor
1938
+ end
1939
+
1940
+ def fill(img)
1941
+ img.background_color = @bgcolor
1942
+ img.erase!
1943
+ end
1923
1944
  end
1924
- end
1925
-
1926
-
1927
- # Example fill class. Fills the image with the specified background
1928
- # color, then crosshatches with the specified crosshatch color.
1929
- # @dist is the number of pixels between hatch lines.
1930
- # See Magick::Draw examples.
1931
- class HatchFill
1932
- def initialize(bgcolor, hatchcolor="white", dist=10)
1933
- @bgcolor = bgcolor
1934
- @hatchpixel = Pixel.from_color(hatchcolor)
1935
- @dist = dist
1936
- end
1937
-
1938
- def fill(img) # required
1939
- img.background_color = @bgcolor
1940
- img.erase! # sets image to background color
1941
- pixels = Array.new([img.rows, img.columns].max, @hatchpixel)
1942
- @dist.step((img.columns-1)/@dist*@dist, @dist) { |x|
1943
- img.store_pixels(x,0,1,img.rows,pixels)
1944
- }
1945
- @dist.step((img.rows-1)/@dist*@dist, @dist) { |y|
1946
- img.store_pixels(0,y,img.columns,1,pixels)
1947
- }
1948
- end
1949
- end
1950
-
1951
- # Fill class with solid monochromatic color
1952
- class SolidFill
1953
- def initialize(bgcolor)
1954
- @bgcolor = bgcolor
1955
- end
1956
-
1957
- def fill(img)
1958
- img.background_color = @bgcolor
1959
- img.erase!
1960
- end
1961
- end
1962
-
1963
1945
  end # Magick
1964
-