ruby-vips 2.0.13 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
  3. data/.github/workflows/test.yml +80 -0
  4. data/.standard.yml +17 -0
  5. data/.yardopts +0 -1
  6. data/CHANGELOG.md +44 -0
  7. data/Gemfile +3 -1
  8. data/README.md +45 -47
  9. data/Rakefile +13 -15
  10. data/TODO +19 -10
  11. data/VERSION +1 -1
  12. data/example/annotate.rb +7 -7
  13. data/example/connection.rb +26 -0
  14. data/example/daltonize8.rb +27 -29
  15. data/example/draw_lines.rb +30 -0
  16. data/example/example1.rb +5 -6
  17. data/example/example2.rb +11 -11
  18. data/example/example3.rb +9 -9
  19. data/example/example4.rb +8 -8
  20. data/example/example5.rb +8 -9
  21. data/example/inheritance_with_refcount.rb +203 -221
  22. data/example/progress.rb +30 -0
  23. data/example/thumb.rb +12 -14
  24. data/example/trim8.rb +7 -7
  25. data/example/watermark.rb +15 -36
  26. data/example/wobble.rb +25 -25
  27. data/lib/ruby-vips.rb +1 -1
  28. data/lib/vips.rb +473 -338
  29. data/lib/vips/access.rb +9 -9
  30. data/lib/vips/align.rb +7 -8
  31. data/lib/vips/angle.rb +8 -9
  32. data/lib/vips/angle45.rb +12 -13
  33. data/lib/vips/bandformat.rb +16 -18
  34. data/lib/vips/blend_mode.rb +36 -0
  35. data/lib/vips/coding.rb +11 -12
  36. data/lib/vips/compass_direction.rb +13 -14
  37. data/lib/vips/connection.rb +46 -0
  38. data/lib/vips/direction.rb +7 -8
  39. data/lib/vips/extend.rb +13 -14
  40. data/lib/vips/gobject.rb +111 -100
  41. data/lib/vips/gvalue.rb +243 -237
  42. data/lib/vips/image.rb +1501 -1338
  43. data/lib/vips/interesting.rb +10 -11
  44. data/lib/vips/interpolate.rb +50 -54
  45. data/lib/vips/interpretation.rb +25 -26
  46. data/lib/vips/kernel.rb +18 -19
  47. data/lib/vips/methods.rb +929 -309
  48. data/lib/vips/mutableimage.rb +154 -0
  49. data/lib/vips/object.rb +318 -208
  50. data/lib/vips/operation.rb +467 -320
  51. data/lib/vips/operationboolean.rb +10 -11
  52. data/lib/vips/operationcomplex.rb +8 -9
  53. data/lib/vips/operationcomplex2.rb +6 -7
  54. data/lib/vips/operationcomplexget.rb +7 -8
  55. data/lib/vips/operationmath.rb +14 -15
  56. data/lib/vips/operationmath2.rb +6 -7
  57. data/lib/vips/operationrelational.rb +11 -12
  58. data/lib/vips/operationround.rb +7 -8
  59. data/lib/vips/region.rb +73 -0
  60. data/lib/vips/size.rb +9 -10
  61. data/lib/vips/source.rb +88 -0
  62. data/lib/vips/sourcecustom.rb +89 -0
  63. data/lib/vips/target.rb +86 -0
  64. data/lib/vips/targetcustom.rb +77 -0
  65. data/lib/vips/version.rb +1 -2
  66. data/ruby-vips.gemspec +29 -20
  67. metadata +51 -40
  68. data/.travis.yml +0 -55
  69. data/install-vips.sh +0 -26
data/lib/vips/image.rb CHANGED
@@ -4,1505 +4,1668 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
8
 
9
9
  module Vips
10
- private
10
+ private
11
11
 
12
- attach_function :vips_image_new_matrix_from_array,
13
- [:int, :int, :pointer, :int], :pointer
12
+ attach_function :vips_image_new_matrix_from_array,
13
+ [:int, :int, :pointer, :int], :pointer
14
14
 
15
- attach_function :vips_image_copy_memory, [:pointer], :pointer
15
+ attach_function :vips_image_copy_memory, [:pointer], :pointer
16
16
 
17
- attach_function :vips_filename_get_filename, [:string], :pointer
18
- attach_function :vips_filename_get_options, [:string], :pointer
17
+ attach_function :vips_image_set_progress, [:pointer, :bool], :void
18
+ attach_function :vips_image_set_kill, [:pointer, :bool], :void
19
19
 
20
- attach_function :vips_foreign_find_load, [:string], :string
21
- attach_function :vips_foreign_find_save, [:string], :string
22
- attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
23
- attach_function :vips_foreign_find_save_buffer, [:string], :string
20
+ attach_function :vips_filename_get_filename, [:string], :pointer
21
+ attach_function :vips_filename_get_options, [:string], :pointer
24
22
 
25
- attach_function :vips_image_write_to_memory,
26
- [:pointer, SizeStruct.ptr], :pointer
23
+ attach_function :vips_foreign_find_load, [:string], :string
24
+ attach_function :vips_foreign_find_save, [:string], :string
25
+ attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
26
+ attach_function :vips_foreign_find_save_buffer, [:string], :string
27
27
 
28
- attach_function :vips_image_get_typeof, [:pointer, :string], :GType
29
- attach_function :vips_image_get,
30
- [:pointer, :string, GObject::GValue.ptr], :int
28
+ if Vips.at_least_libvips?(8, 9)
29
+ attach_function :vips_foreign_find_load_source, [:pointer], :string
30
+ attach_function :vips_foreign_find_save_target, [:string], :string
31
+ end
31
32
 
32
- # vips_image_get_fields was added in libvips 8.5
33
- begin
34
- attach_function :vips_image_get_fields, [:pointer], :pointer
35
- rescue FFI::NotFoundError
36
- end
33
+ attach_function :vips_image_write_to_memory,
34
+ [:pointer, SizeStruct.ptr], :pointer
37
35
 
38
- # vips_addalpha was added in libvips 8.6
39
- if Vips::at_least_libvips?(8, 6)
40
- attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
41
- end
42
- if Vips::at_least_libvips?(8, 5)
43
- attach_function :vips_image_hasalpha, [:pointer], :int
44
- end
36
+ attach_function :vips_image_get_typeof, [:pointer, :string], :GType
37
+ attach_function :vips_image_get,
38
+ [:pointer, :string, GObject::GValue.ptr], :int
45
39
 
46
- attach_function :vips_image_set,
47
- [:pointer, :string, GObject::GValue.ptr], :void
48
- attach_function :vips_image_remove, [:pointer, :string], :void
40
+ attach_function :vips_image_get_width, [:pointer], :int
41
+ attach_function :vips_image_get_height, [:pointer], :int
42
+ attach_function :vips_image_get_bands, [:pointer], :int
49
43
 
50
- attach_function :vips_band_format_iscomplex, [:int], :int
51
- attach_function :vips_band_format_isfloat, [:int], :int
44
+ if Vips.at_least_libvips?(8, 5)
45
+ attach_function :vips_image_get_fields, [:pointer], :pointer
46
+ attach_function :vips_image_hasalpha, [:pointer], :int
47
+ end
52
48
 
53
- attach_function :nickname_find, :vips_nickname_find, [:GType], :string
49
+ if Vips.at_least_libvips?(8, 6)
50
+ attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
51
+ end
54
52
 
55
- # turn a raw pointer that must be freed into a self-freeing Ruby string
56
- def self.p2str(pointer)
57
- pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
58
- pointer.read_string
59
- end
53
+ # move these three lines to mutableimage when we finally remove set and
54
+ # remove in this class
55
+ attach_function :vips_image_set,
56
+ [:pointer, :string, GObject::GValue.ptr], :void
57
+ attach_function :vips_image_remove, [:pointer, :string], :void
60
58
 
61
- public
59
+ attach_function :vips_band_format_iscomplex, [:int], :int
60
+ attach_function :vips_band_format_isfloat, [:int], :int
62
61
 
63
- # This class represents a libvips image. See the {Vips} module documentation
64
- # for an introduction to using this class.
62
+ attach_function :nickname_find, :vips_nickname_find, [:GType], :string
65
63
 
66
- class Image < Vips::Object
67
- alias_method :parent_get_typeof, :get_typeof
64
+ attach_function :vips_image_new_from_memory, [:pointer, :size_t, :int, :int, :int, :int], :pointer
65
+ attach_function :vips_image_new_from_memory_copy, [:pointer, :size_t, :int, :int, :int, :int], :pointer
68
66
 
69
- private
67
+ # turn a raw pointer that must be freed into a self-freeing Ruby string
68
+ def self.p2str(pointer)
69
+ pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
70
+ pointer.read_string
71
+ end
70
72
 
71
- # the layout of the VipsImage struct
72
- module ImageLayout
73
- def self.included base
74
- base.class_eval do
75
- layout :parent, Vips::Object::Struct
76
- # rest opaque
77
- end
78
- end
79
- end
73
+ public
80
74
 
81
- class Struct < Vips::Object::Struct
82
- include ImageLayout
75
+ # This class represents a libvips image. See the {Vips} module documentation
76
+ # for an introduction to using this class.
83
77
 
84
- end
78
+ class Image < Vips::Object
79
+ alias_method :parent_get_typeof, :get_typeof
85
80
 
86
- class ManagedStruct < Vips::Object::ManagedStruct
87
- include ImageLayout
81
+ private
88
82
 
83
+ # the layout of the VipsImage struct
84
+ module ImageLayout
85
+ def self.included base
86
+ base.class_eval do
87
+ layout :parent, Vips::Object::Struct
88
+ # rest opaque
89
89
  end
90
+ end
91
+ end
90
92
 
91
- class GenericPtr < FFI::Struct
92
- layout :value, :pointer
93
- end
93
+ class Struct < Vips::Object::Struct
94
+ include ImageLayout
95
+ end
94
96
 
95
- # handy for overloads ... want to be able to apply a function to an
96
- # array or to a scalar
97
- def self.smap x, &block
98
- x.is_a?(Array) ? x.map {|y| smap(y, &block)} : block.(x)
99
- end
97
+ class ManagedStruct < Vips::Object::ManagedStruct
98
+ include ImageLayout
99
+ end
100
100
 
101
- def self.complex? format
102
- format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
103
- Vips::vips_band_format_iscomplex(format_number) != 0
101
+ class GenericPtr < FFI::Struct
102
+ layout :value, :pointer
103
+ end
104
+
105
+ # handy for overloads ... want to be able to apply a function to an
106
+ # array or to a scalar
107
+ def self.smap x, &block
108
+ x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.call(x)
109
+ end
110
+
111
+ def self.complex? format
112
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
113
+ Vips.vips_band_format_iscomplex(format_number) != 0
114
+ end
115
+
116
+ def self.float? format
117
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
118
+ Vips.vips_band_format_isfloat(format_number) != 0
119
+ end
120
+
121
+ # run a complex operation on a complex image, or an image with an even
122
+ # number of bands ... handy for things like running .polar on .index
123
+ # images
124
+ def self.run_cmplx image, &block
125
+ original_format = image.format
126
+
127
+ unless Image.complex? image.format
128
+ if image.bands % 2 != 0
129
+ raise Vips::Error, "not an even number of bands"
104
130
  end
105
131
 
106
- def self.float? format
107
- format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
108
- Vips::vips_band_format_isfloat(format_number) != 0
132
+ unless Image.float? image.format
133
+ image = image.cast :float
109
134
  end
110
135
 
111
- # run a complex operation on a complex image, or an image with an even
112
- # number of bands ... handy for things like running .polar on .index
113
- # images
114
- def self.run_cmplx image, &block
115
- original_format = image.format
136
+ new_format = image.format == :double ? :dpcomplex : :complex
137
+ image = image.copy format: new_format, bands: image.bands / 2
138
+ end
116
139
 
117
- unless Image::complex? image.format
118
- if image.bands % 2 != 0
119
- raise Error, "not an even number of bands"
120
- end
140
+ image = block.call(image)
121
141
 
122
- unless Image::float? image.format
123
- image = image.cast :float
124
- end
142
+ unless Image.complex? original_format
143
+ new_format = image.format == :dpcomplex ? :double : :float
144
+ image = image.copy format: new_format, bands: image.bands * 2
145
+ end
125
146
 
126
- new_format = image.format == :double ? :dpcomplex : :complex
127
- image = image.copy format: new_format, bands: image.bands / 2
128
- end
147
+ image
148
+ end
129
149
 
130
- image = block.(image)
150
+ # handy for expanding enum operations
151
+ def call_enum(name, other, enum)
152
+ if other.is_a?(Vips::Image)
153
+ Vips::Operation.call name.to_s, [self, other, enum]
154
+ else
155
+ Vips::Operation.call name.to_s + "_const", [self, enum, other]
156
+ end
157
+ end
131
158
 
132
- unless Image::complex? original_format
133
- new_format = image.format == :dpcomplex ? :double : :float
134
- image = image.copy format: new_format, bands: image.bands * 2
135
- end
159
+ # Write can fail due to no file descriptors and memory can fill if
160
+ # large objects are not collected fairly soon. We can't try a
161
+ # write and GC and retry on fail, since the write may take a
162
+ # long time and may not be repeatable.
163
+ #
164
+ # GCing before every write would have a horrible effect on
165
+ # performance, so as a compromise we GC every @@gc_interval writes.
166
+ #
167
+ # ruby2.1 introduced a generational GC which is fast enough to be
168
+ # able to GC on every write.
136
169
 
137
- image
138
- end
170
+ @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
139
171
 
140
- # handy for expanding enum operations
141
- def call_enum(name, other, enum)
142
- if other.is_a?(Vips::Image)
143
- Vips::Operation.call name.to_s, [self, other, enum]
144
- else
145
- Vips::Operation.call name.to_s + "_const", [self, enum, other]
146
- end
147
- end
172
+ @@gc_interval = 100
173
+ @@gc_countdown = @@gc_interval
148
174
 
149
- # Write can fail due to no file descriptors and memory can fill if
150
- # large objects are not collected fairly soon. We can't try a
151
- # write and GC and retry on fail, since the write may take a
152
- # long time and may not be repeatable.
153
- #
154
- # GCing before every write would have a horrible effect on
155
- # performance, so as a compromise we GC every @@gc_interval writes.
156
- #
157
- # ruby2.1 introduced a generational GC which is fast enough to be
158
- # able to GC on every write.
159
-
160
- @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
161
-
162
- @@gc_interval = 100
163
- @@gc_countdown = @@gc_interval
164
-
165
- def write_gc
166
- if @@generational_gc
167
- GC.start full_mark: false
168
- else
169
- @@gc_countdown -= 1
170
- if @@gc_countdown < 0
171
- @@gc_countdown = @@gc_interval
172
- GC.start
173
- end
174
- end
175
+ def write_gc
176
+ if @@generational_gc
177
+ GC.start full_mark: false
178
+ else
179
+ @@gc_countdown -= 1
180
+ if @@gc_countdown < 0
181
+ @@gc_countdown = @@gc_interval
182
+ GC.start
175
183
  end
184
+ end
185
+ end
176
186
 
177
- public
187
+ public
178
188
 
179
- def inspect
180
- "#<Image #{width}x#{height} #{format}, #{bands} bands, " +
181
- "#{interpretation}>"
182
- end
189
+ def inspect
190
+ "#<Image #{width}x#{height} #{format}, #{bands} bands, #{interpretation}>"
191
+ end
183
192
 
184
- def respond_to? name, include_all = false
185
- # To support keyword args, we need to tell Ruby that final image
186
- # arguments cannot be hashes of keywords.
187
- #
188
- # https://makandracards.com/makandra/36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
189
- return false if name == :to_hash
193
+ def respond_to? name, include_all = false
194
+ # To support keyword args, we need to tell Ruby that final image
195
+ # arguments cannot be hashes of keywords.
196
+ #
197
+ # https://makandracards.com/makandra/
198
+ # 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
199
+ return false if name == :to_hash
190
200
 
191
- # respond to all vips operations by nickname
192
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
201
+ super
202
+ end
193
203
 
194
- super
195
- end
204
+ def respond_to_missing? name, include_all = false
205
+ # respond to all vips operations by nickname
206
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
196
207
 
197
- def self.respond_to? name, include_all = false
198
- # respond to all vips operations by nickname
199
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
208
+ super
209
+ end
200
210
 
201
- super
202
- end
211
+ def self.respond_to_missing? name, include_all = false
212
+ # respond to all vips operations by nickname
213
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
203
214
 
204
- # Invoke a vips operation with {Vips::Operation.call}, using self as
205
- # the first input argument.
206
- #
207
- # @param name [String] vips operation to call
208
- # @return result of vips operation
209
- def method_missing name, *args, **options
210
- Vips::Operation.call name.to_s, [self, *args], options
211
- end
215
+ super
216
+ end
212
217
 
213
- # Invoke a vips operation with {Vips::Operation.call}.
214
- def self.method_missing name, *args, **options
215
- Vips::Operation.call name.to_s, args, options
216
- end
218
+ # Invoke a vips operation with {Vips::Operation.call}, using self as
219
+ # the first input argument.
220
+ #
221
+ # @param name [String] vips operation to call
222
+ # @return result of vips operation
223
+ def method_missing name, *args, **options
224
+ Vips::Operation.call name.to_s, [self, *args], options
225
+ end
217
226
 
218
- # Return a new {Image} for a file on disc. This method can load
219
- # images in any format supported by vips. The filename can include
220
- # load options, for example:
221
- #
222
- # ```
223
- # image = Vips::new_from_file "fred.jpg[shrink=2]"
224
- # ```
225
- #
226
- # You can also supply options as a hash, for example:
227
- #
228
- # ```
229
- # image = Vips::new_from_file "fred.jpg", shrink: 2
230
- # ```
231
- #
232
- # The full set of options available depend upon the load operation that
233
- # will be executed. Try something like:
234
- #
235
- # ```
236
- # $ vips jpegload
237
- # ```
238
- #
239
- # at the command-line to see a summary of the available options for the
240
- # JPEG loader.
241
- #
242
- # Loading is fast: only enough of the image is loaded to be able to fill
243
- # out the header. Pixels will only be decompressed when they are needed.
244
- #
245
- # @!macro [new] vips.loadopts
246
- # @param opts [Hash] set of options
247
- # @option opts [Boolean] :disc (true) Open large images via a
248
- # temporary disc file
249
- # @option opts [Vips::Access] :access (:random) Access mode for file
250
- #
251
- # @param name [String] the filename to load from
252
- # @macro vips.loadopts
253
- # @return [Image] the loaded image
254
- def self.new_from_file name, opts = {}
255
- # very common, and Vips::vips_filename_get_filename will segv if we
256
- # pass this
257
- raise Vips::Error, "filename is nil" if name == nil
258
-
259
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
260
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
261
- loader = Vips::vips_foreign_find_load filename
262
- raise Vips::Error if loader == nil
263
-
264
- Operation.call loader, [filename], opts, option_string
265
- end
227
+ # Invoke a vips operation with {Vips::Operation.call}.
228
+ def self.method_missing name, *args, **options
229
+ Vips::Operation.call name.to_s, args, options
230
+ end
266
231
 
267
- # Create a new {Image} for an image encoded, in a format such as
268
- # JPEG, in a memory string. Load options may be passed as
269
- # strings or appended as a hash. For example:
270
- #
271
- # ```
272
- # image = Vips::Image.new_from_buffer memory_buffer, "shrink=2"
273
- # ```
274
- #
275
- # or alternatively:
276
- #
277
- # ```
278
- # image = Vips::Image.new_from_buffer memory_buffer, "", shrink: 2
279
- # ```
280
- #
281
- # The options available depend on the file format. Try something like:
282
- #
283
- # ```
284
- # $ vips jpegload_buffer
285
- # ```
286
- #
287
- # at the command-line to see the available options. Not all loaders
288
- # support load from buffer, but at least JPEG, PNG and
289
- # TIFF images will work.
290
- #
291
- # Loading is fast: only enough of the image is loaded to be able to fill
292
- # out the header. Pixels will only be decompressed when they are needed.
293
- #
294
- # @param data [String] the data to load from
295
- # @param option_string [String] load options as a string
296
- # @macro vips.loadopts
297
- # @return [Image] the loaded image
298
- def self.new_from_buffer data, option_string, opts = {}
299
- loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
300
- raise Vips::Error if loader == nil
301
-
302
- Vips::Operation.call loader, [data], opts, option_string
303
- end
232
+ # Return a new {Image} for a file on disc. This method can load
233
+ # images in any format supported by vips. The filename can include
234
+ # load options, for example:
235
+ #
236
+ # ```
237
+ # image = Vips::Image.new_from_file "fred.jpg[shrink=2]"
238
+ # ```
239
+ #
240
+ # You can also supply options as a hash, for example:
241
+ #
242
+ # ```
243
+ # image = Vips::Image.new_from_file "fred.jpg", shrink: 2
244
+ # ```
245
+ #
246
+ # The full set of options available depend upon the load operation that
247
+ # will be executed. Try something like:
248
+ #
249
+ # ```
250
+ # $ vips jpegload
251
+ # ```
252
+ #
253
+ # at the command-line to see a summary of the available options for the
254
+ # JPEG loader.
255
+ #
256
+ # Loading is fast: only enough of the image is loaded to be able to fill
257
+ # out the header. Pixels will only be decompressed when they are needed.
258
+ #
259
+ # @!macro [new] vips.loadopts
260
+ # @param opts [Hash] set of options
261
+ # @option opts [Boolean] :disc (true) Open large images via a
262
+ # temporary disc file
263
+ # @option opts [Vips::Access] :access (:random) Access mode for file
264
+ #
265
+ # @param name [String] the filename to load from
266
+ # @macro vips.loadopts
267
+ # @return [Image] the loaded image
268
+ def self.new_from_file name, **opts
269
+ # very common, and Vips::vips_filename_get_filename will segv if we
270
+ # pass this
271
+ raise Vips::Error, "filename is nil" if name.nil?
272
+
273
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
274
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
275
+ loader = Vips.vips_foreign_find_load filename
276
+ raise Vips::Error if loader.nil?
277
+
278
+ Operation.call loader, [filename], opts, option_string
279
+ end
304
280
 
305
- def self.matrix_from_array width, height, array
306
- ptr = FFI::MemoryPointer.new :double, array.length
307
- ptr.write_array_of_double array
308
- image = Vips::vips_image_new_matrix_from_array width, height,
309
- ptr, array.length
310
- Vips::Image.new image
311
- end
281
+ # Create a new {Image} for an image encoded in a format such as
282
+ # JPEG in a binary string. Load options may be passed as
283
+ # strings or appended as a hash. For example:
284
+ #
285
+ # ```
286
+ # image = Vips::Image.new_from_buffer memory_buffer, "shrink=2"
287
+ # ```
288
+ #
289
+ # or alternatively:
290
+ #
291
+ # ```
292
+ # image = Vips::Image.new_from_buffer memory_buffer, "", shrink: 2
293
+ # ```
294
+ #
295
+ # The options available depend on the file format. Try something like:
296
+ #
297
+ # ```
298
+ # $ vips jpegload_buffer
299
+ # ```
300
+ #
301
+ # at the command-line to see the available options. Not all loaders
302
+ # support load from buffer, but at least JPEG, PNG and
303
+ # TIFF images will work.
304
+ #
305
+ # Loading is fast: only enough of the image is loaded to be able to fill
306
+ # out the header. Pixels will only be decompressed when they are needed.
307
+ #
308
+ # @param data [String] the data to load from
309
+ # @param option_string [String] load options as a string
310
+ # @macro vips.loadopts
311
+ # @return [Image] the loaded image
312
+ def self.new_from_buffer data, option_string, **opts
313
+ loader = Vips.vips_foreign_find_load_buffer data, data.bytesize
314
+ raise Vips::Error if loader.nil?
315
+
316
+ Vips::Operation.call loader, [data], opts, option_string
317
+ end
312
318
 
313
- # Create a new Image from a 1D or 2D array. A 1D array becomes an
314
- # image with height 1. Use `scale` and `offset` to set the scale and
315
- # offset fields in the header. These are useful for integer
316
- # convolutions.
317
- #
318
- # For example:
319
- #
320
- # ```
321
- # image = Vips::new_from_array [1, 2, 3]
322
- # ```
323
- #
324
- # or
325
- #
326
- # ```
327
- # image = Vips::new_from_array [
328
- # [-1, -1, -1],
329
- # [-1, 16, -1],
330
- # [-1, -1, -1]], 8
331
- # ```
332
- #
333
- # for a simple sharpening mask.
334
- #
335
- # @param array [Array] the pixel data as an array of numbers
336
- # @param scale [Real] the convolution scale
337
- # @param offset [Real] the convolution offset
338
- # @return [Image] the image
339
- def self.new_from_array array, scale = 1, offset = 0
340
- # we accept a 1D array and assume height == 1, or a 2D array
341
- # and check all lines are the same length
342
- unless array.is_a? Array
343
- raise Vips::Error, "Argument is not an array."
344
- end
345
-
346
- if array[0].is_a? Array
347
- height = array.length
348
- width = array[0].length
349
- unless array.all? {|x| x.is_a? Array}
350
- raise Vips::Error, "Not a 2D array."
351
- end
352
- unless array.all? {|x| x.length == width}
353
- raise Vips::Error, "Array not rectangular."
354
- end
355
- array = array.flatten
356
- else
357
- height = 1
358
- width = array.length
359
- end
360
-
361
- unless array.all? {|x| x.is_a? Numeric}
362
- raise Vips::Error, "Not all array elements are Numeric."
363
- end
364
-
365
- image = Vips::Image.matrix_from_array width, height, array
366
- raise Vips::Error if image == nil
367
-
368
- # be careful to set them as double
369
- image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
370
- image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
371
-
372
- return image
373
- end
319
+ # Create a new {Image} from a C-style array held in memory. For example:
320
+ #
321
+ # ```
322
+ # image = Vips::Image.black(16, 16) + 128
323
+ # data = image.write_to_memory
324
+ #
325
+ # x = Vips::Image.new_from_memory data,
326
+ # image.width, image.height, image.bands, image.format
327
+ # ```
328
+ #
329
+ # {new_from_memory} keeps a reference to the array of pixels you pass in
330
+ # to try to prevent that memory from being freed by the Ruby GC while it
331
+ # is being used.
332
+ #
333
+ # See {new_from_memory_copy} for a version of this method which does not
334
+ # keep a reference.
335
+ #
336
+ # @param data [String, FFI::Pointer] the data to load from
337
+ # @param width [Integer] width in pixels
338
+ # @param height [Integer] height in pixels
339
+ # @param bands [Integer] number of bands
340
+ # @param format [Symbol] band format
341
+ # @return [Image] the loaded image
342
+ def self.new_from_memory data, width, height, bands, format
343
+ size = data.bytesize
344
+
345
+ # prevent data from being freed with JRuby FFI
346
+ if defined?(JRUBY_VERSION) && !data.is_a?(FFI::Pointer)
347
+ data = ::FFI::MemoryPointer.new(:char, data.bytesize).write_bytes data
348
+ end
349
+
350
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
351
+ vi = Vips.vips_image_new_from_memory data, size,
352
+ width, height, bands, format_number
353
+ raise Vips::Error if vi.null?
354
+ image = new(vi)
355
+
356
+ # keep a secret ref to the underlying object .. this reference will be
357
+ # inherited by things that in turn depend on us, so the memory we are
358
+ # using will not be freed
359
+ image.references << data
360
+
361
+ image
362
+ end
374
363
 
375
- # A new image is created with the same width, height, format,
376
- # interpretation, resolution and offset as self, but with every pixel
377
- # set to the specified value.
378
- #
379
- # You can pass an array to make a many-band image, or a single value to
380
- # make a one-band image.
381
- #
382
- # @param value [Real, Array<Real>] value to put in each pixel
383
- # @return [Image] constant image
384
- def new_from_image value
385
- pixel = (Vips::Image.black(1, 1) + value).cast(format)
386
- image = pixel.embed 0, 0, width, height, extend: :copy
387
- image.copy interpretation: interpretation,
388
- xres: xres, yres: yres, xoffset: xoffset, yoffset: yoffset
389
- end
364
+ # Create a new {Image} from memory and copies the memory area. See
365
+ # {new_from_memory} for a version of this method which does not copy the
366
+ # memory area.
367
+ #
368
+ # @param data [String, FFI::Pointer] the data to load from
369
+ # @param width [Integer] width in pixels
370
+ # @param height [Integer] height in pixels
371
+ # @param bands [Integer] number of bands
372
+ # @param format [Symbol] band format
373
+ # @return [Image] the loaded image
374
+ def self.new_from_memory_copy data, width, height, bands, format
375
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
376
+ vi = Vips.vips_image_new_from_memory_copy data, data.bytesize,
377
+ width, height, bands, format_number
378
+ raise Vips::Error if vi.null?
379
+ new(vi)
380
+ end
390
381
 
391
- # Write this image to a file. Save options may be encoded in the
392
- # filename or given as a hash. For example:
393
- #
394
- # ```
395
- # image.write_to_file "fred.jpg[Q=90]"
396
- # ```
397
- #
398
- # or equivalently:
399
- #
400
- # ```
401
- # image.write_to_file "fred.jpg", Q: 90
402
- # ```
403
- #
404
- # The full set of save options depend on the selected saver. Try
405
- # something like:
406
- #
407
- # ```
408
- # $ vips jpegsave
409
- # ```
410
- #
411
- # to see all the available options for JPEG save.
412
- #
413
- # @!macro [new] vips.saveopts
414
- # @param opts [Hash] set of options
415
- # @option opts [Boolean] :strip (false) Strip all metadata from image
416
- # @option opts [Array<Float>] :background (0) Background colour to
417
- # flatten alpha against, if necessary
418
- #
419
- # @param name [String] filename to write to
420
- def write_to_file name, opts = {}
421
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
422
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
423
- saver = Vips::vips_foreign_find_save filename
424
- if saver == nil
425
- raise Vips::Error, "No known saver for '#{filename}'."
426
- end
427
-
428
- Vips::Operation.call saver, [self, filename], opts, option_string
429
-
430
- write_gc
431
- end
382
+ # Create a new {Image} from a source. Load options may be passed as
383
+ # strings or appended as a hash. For example:
384
+ #
385
+ # ```
386
+ # source = Vips::Source.new_from_file("k2.jpg")
387
+ # image = Vips::Image.new_from_source source, "shrink=2"
388
+ # ```
389
+ #
390
+ # or alternatively:
391
+ #
392
+ # ```
393
+ # image = Vips::Image.new_from_source source, "", shrink: 2
394
+ # ```
395
+ #
396
+ # The options available depend on the file format. Try something like:
397
+ #
398
+ # ```
399
+ # $ vips jpegload_source
400
+ # ```
401
+ #
402
+ # at the command-line to see the available options. Not all loaders
403
+ # support load from source, but at least JPEG, PNG and
404
+ # TIFF images will work.
405
+ #
406
+ # Loading is fast: only enough data is read to be able to fill
407
+ # out the header. Pixels will only be read and decompressed when they are
408
+ # needed.
409
+ #
410
+ # @param source [Vips::Source] the source to load from
411
+ # @param option_string [String] load options as a string
412
+ # @macro vips.loadopts
413
+ # @return [Image] the loaded image
414
+ def self.new_from_source source, option_string, **opts
415
+ loader = Vips.vips_foreign_find_load_source source
416
+ raise Vips::Error if loader.nil?
417
+
418
+ Vips::Operation.call loader, [source], opts, option_string
419
+ end
432
420
 
433
- # Write this image to a memory buffer. Save options may be encoded in
434
- # the format_string or given as a hash. For example:
435
- #
436
- # ```
437
- # buffer = image.write_to_buffer ".jpg[Q=90]"
438
- # ```
439
- #
440
- # or equivalently:
441
- #
442
- # ```
443
- # image.write_to_buffer ".jpg", Q: 90
444
- # ```
445
- #
446
- # The full set of save options depend on the selected saver. Try
447
- # something like:
448
- #
449
- # ```
450
- # $ vips jpegsave
451
- # ```
452
- #
453
- # to see all the available options for JPEG save.
454
- #
455
- # @param format_string [String] save format plus options
456
- # @macro vips.saveopts
457
- # @return [String] the image saved in the specified format
458
- def write_to_buffer format_string, opts = {}
459
- filename =
460
- Vips::p2str(Vips::vips_filename_get_filename format_string)
461
- option_string =
462
- Vips::p2str(Vips::vips_filename_get_options format_string)
463
- saver = Vips::vips_foreign_find_save_buffer filename
464
- if saver == nil
465
- raise Vips::Error, "No known saver for '#{filename}'."
466
- end
467
-
468
- buffer = Vips::Operation.call saver, [self], opts, option_string
469
- raise Vips::Error if buffer == nil
470
-
471
- write_gc
472
-
473
- return buffer
474
- end
421
+ def self.matrix_from_array width, height, array
422
+ ptr = FFI::MemoryPointer.new :double, array.length
423
+ ptr.write_array_of_double array
424
+ image = Vips.vips_image_new_matrix_from_array width, height,
425
+ ptr, array.length
426
+ Vips::Image.new image
427
+ end
475
428
 
476
- # Write this image to a large memory buffer.
477
- #
478
- # @return [String] the pixels as a huge binary string
479
- def write_to_memory
480
- len = Vips::SizeStruct.new
481
- ptr = Vips::vips_image_write_to_memory self, len
429
+ # Create a new Image from a 1D or 2D array. A 1D array becomes an
430
+ # image with height 1. Use `scale` and `offset` to set the scale and
431
+ # offset fields in the header. These are useful for integer
432
+ # convolutions.
433
+ #
434
+ # For example:
435
+ #
436
+ # ```
437
+ # image = Vips::Image.new_from_array [1, 2, 3]
438
+ # ```
439
+ #
440
+ # or
441
+ #
442
+ # ```
443
+ # image = Vips::Image.new_from_array [
444
+ # [-1, -1, -1],
445
+ # [-1, 16, -1],
446
+ # [-1, -1, -1]], 8
447
+ # ```
448
+ #
449
+ # for a simple sharpening mask.
450
+ #
451
+ # @param array [Array] the pixel data as an array of numbers
452
+ # @param scale [Real] the convolution scale
453
+ # @param offset [Real] the convolution offset
454
+ # @return [Image] the image
455
+ def self.new_from_array array, scale = 1, offset = 0
456
+ # we accept a 1D array and assume height == 1, or a 2D array
457
+ # and check all lines are the same length
458
+ unless array.is_a? Array
459
+ raise Vips::Error, "Argument is not an array."
460
+ end
461
+
462
+ if array[0].is_a? Array
463
+ height = array.length
464
+ width = array[0].length
465
+ unless array.all? { |x| x.is_a? Array }
466
+ raise Vips::Error, "Not a 2D array."
467
+ end
468
+ unless array.all? { |x| x.length == width }
469
+ raise Vips::Error, "Array not rectangular."
470
+ end
471
+
472
+ array = array.flatten
473
+ else
474
+ height = 1
475
+ width = array.length
476
+ end
477
+
478
+ unless array.length == width * height
479
+ raise Vips::Error, "Bad array dimensions."
480
+ end
481
+
482
+ unless array.all? { |x| x.is_a? Numeric }
483
+ raise Vips::Error, "Not all array elements are Numeric."
484
+ end
485
+
486
+ image = Vips::Image.matrix_from_array width, height, array
487
+ raise Vips::Error if image.nil?
488
+
489
+ image.mutate do |mutable|
490
+ # be careful to set them as double
491
+ mutable.set_type! GObject::GDOUBLE_TYPE, "scale", scale.to_f
492
+ mutable.set_type! GObject::GDOUBLE_TYPE, "offset", offset.to_f
493
+ end
494
+ end
482
495
 
483
- # wrap up as an autopointer
484
- ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
496
+ # A new image is created with the same width, height, format,
497
+ # interpretation, resolution and offset as self, but with every pixel
498
+ # set to the specified value.
499
+ #
500
+ # You can pass an array to make a many-band image, or a single value to
501
+ # make a one-band image.
502
+ #
503
+ # @param value [Real, Array<Real>] value to put in each pixel
504
+ # @return [Image] constant image
505
+ def new_from_image value
506
+ pixel = (Vips::Image.black(1, 1) + value).cast(format)
507
+ image = pixel.embed 0, 0, width, height, extend: :copy
508
+ image.copy interpretation: interpretation, xres: xres, yres: yres,
509
+ xoffset: xoffset, yoffset: yoffset
510
+ end
485
511
 
486
- ptr.get_bytes 0, len[:value]
487
- end
512
+ # Write this image to a file. Save options may be encoded in the
513
+ # filename or given as a hash. For example:
514
+ #
515
+ # ```
516
+ # image.write_to_file "fred.jpg[Q=90]"
517
+ # ```
518
+ #
519
+ # or equivalently:
520
+ #
521
+ # ```
522
+ # image.write_to_file "fred.jpg", Q: 90
523
+ # ```
524
+ #
525
+ # The full set of save options depend on the selected saver. Try
526
+ # something like:
527
+ #
528
+ # ```
529
+ # $ vips jpegsave
530
+ # ```
531
+ #
532
+ # to see all the available options for JPEG save.
533
+ #
534
+ # @!macro [new] vips.saveopts
535
+ # @param opts [Hash] set of options
536
+ # @option opts [Boolean] :strip (false) Strip all metadata from image
537
+ # @option opts [Array<Float>] :background (0) Background colour to
538
+ # flatten alpha against, if necessary
539
+ #
540
+ # @param name [String] filename to write to
541
+ def write_to_file name, **opts
542
+ raise Vips::Error, "filename is nil" if name.nil?
488
543
 
489
- # Fetch a `GType` from an image. `GType` will be 0 for no such field.
490
- #
491
- # @see get
492
- # @param name [String] Metadata field to fetch
493
- # @return [Integer] GType
494
- def get_typeof name
495
- # on libvips before 8.5, property types must be searched first,
496
- # since vips_image_get_typeof returned built-in enums as int
497
- unless Vips::at_least_libvips?(8, 5)
498
- gtype = parent_get_typeof name
499
- return gtype if gtype != 0
500
- end
501
-
502
- Vips::vips_image_get_typeof self, name
503
- end
544
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
545
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
546
+ saver = Vips.vips_foreign_find_save filename
547
+ raise Vips::Error if saver.nil?
504
548
 
505
- # Get a metadata item from an image. Ruby types are constructed
506
- # automatically from the `GValue`, if possible.
507
- #
508
- # For example, you can read the ICC profile from an image like this:
509
- #
510
- # ```
511
- # profile = image.get "icc-profile-data"
512
- # ```
513
- #
514
- # and profile will be an array containing the profile.
515
- #
516
- # @param name [String] Metadata field to get
517
- # @return [Object] Value of field
518
- def get name
519
- # with old libvips, we must fetch properties (as opposed to
520
- # metadata) via VipsObject
521
- unless Vips::at_least_libvips?(8, 5)
522
- return super if parent_get_typeof(name) != 0
523
- end
524
-
525
- gvalue = GObject::GValue.alloc
526
- result = Vips::vips_image_get self, name, gvalue
527
- raise Vips::Error if result != 0
528
-
529
- return gvalue.get
530
- end
549
+ Vips::Operation.call saver, [self, filename], opts, option_string
531
550
 
532
- # Get the names of all fields on an image. Use this to loop over all
533
- # image metadata.
534
- #
535
- # @return [[String]] array of field names
536
- def get_fields
537
- # vips_image_get_fields() was added in libvips 8.5
538
- return [] unless Vips.respond_to? :vips_image_get_fields
539
-
540
- array = Vips::vips_image_get_fields self
541
-
542
- names = []
543
- p = array
544
- until ((q = p.read_pointer).null?)
545
- names << q.read_string
546
- GLib::g_free q
547
- p += FFI::Type::POINTER.size
548
- end
549
- GLib::g_free array
550
-
551
- return names
552
- end
551
+ write_gc
552
+ end
553
553
 
554
- # Create a metadata item on an image, of the specifed type. Ruby types
555
- # are automatically
556
- # transformed into the matching `GType`, if possible.
557
- #
558
- # For example, you can use this to set an image's ICC profile:
559
- #
560
- # ```
561
- # x = y.set_type Vips::BLOB_TYPE, "icc-profile-data", profile
562
- # ```
563
- #
564
- # where `profile` is an ICC profile held as a binary string object.
565
- #
566
- # @see set
567
- # @param gtype [Integer] GType of item
568
- # @param name [String] Metadata field to set
569
- # @param value [Object] Value to set
570
- def set_type gtype, name, value
571
- gvalue = GObject::GValue.alloc
572
- gvalue.init gtype
573
- gvalue.set value
574
- Vips::vips_image_set self, name, gvalue
575
- end
554
+ # Write this image to a memory buffer. Save options may be encoded in
555
+ # the format_string or given as a hash. For example:
556
+ #
557
+ # ```
558
+ # buffer = image.write_to_buffer ".jpg[Q=90]"
559
+ # ```
560
+ #
561
+ # or equivalently:
562
+ #
563
+ # ```
564
+ # image.write_to_buffer ".jpg", Q: 90
565
+ # ```
566
+ #
567
+ # The full set of save options depend on the selected saver. Try
568
+ # something like:
569
+ #
570
+ # ```
571
+ # $ vips jpegsave
572
+ # ```
573
+ #
574
+ # to see all the available options for JPEG save.
575
+ #
576
+ # @param format_string [String] save format plus options
577
+ # @macro vips.saveopts
578
+ # @return [String] the image saved in the specified format
579
+ def write_to_buffer format_string, **opts
580
+ raise Vips::Error, "filename is nil" if format_string.nil?
581
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
582
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
583
+ saver = Vips.vips_foreign_find_save_buffer filename
584
+ raise Vips::Error if saver.nil?
585
+
586
+ buffer = Vips::Operation.call saver, [self], opts, option_string
587
+ raise Vips::Error if buffer.nil?
588
+
589
+ write_gc
590
+
591
+ buffer
592
+ end
576
593
 
577
- # Set the value of a metadata item on an image. The metadata item must
578
- # already exist. Ruby types are automatically
579
- # transformed into the matching `GValue`, if possible.
580
- #
581
- # For example, you can use this to set an image's ICC profile:
582
- #
583
- # ```
584
- # x = y.set "icc-profile-data", profile
585
- # ```
586
- #
587
- # where `profile` is an ICC profile held as a binary string object.
588
- #
589
- # @see set_type
590
- # @param name [String] Metadata field to set
591
- # @param value [Object] Value to set
592
- def set name, value
593
- set_type get_typeof(name), name, value
594
- end
594
+ # Write this image to a target. Save options may be encoded in
595
+ # the format_string or given as a hash. For example:
596
+ #
597
+ # ```ruby
598
+ # target = Vips::Target.new_to_file "k2.jpg"
599
+ # image.write_to_target target, ".jpg[Q=90]"
600
+ # ```
601
+ #
602
+ # or equivalently:
603
+ #
604
+ # ```ruby
605
+ # image.write_to_target target, ".jpg", Q: 90
606
+ # ```
607
+ #
608
+ # The full set of save options depend on the selected saver. Try
609
+ # something like:
610
+ #
611
+ # ```
612
+ # $ vips jpegsave_target
613
+ # ```
614
+ #
615
+ # to see all the available options for JPEG save.
616
+ #
617
+ # @param target [Vips::Target] the target to write to
618
+ # @param format_string [String] save format plus string options
619
+ # @macro vips.saveopts
620
+ def write_to_target target, format_string, **opts
621
+ raise Vips::Error, "filename is nil" if format_string.nil?
622
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
623
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
624
+ saver = Vips.vips_foreign_find_save_target filename
625
+ raise Vips::Error if saver.nil?
626
+
627
+ Vips::Operation.call saver, [self, target], opts, option_string
628
+ write_gc
629
+ end
595
630
 
596
- # Remove a metadata item from an image.
597
- #
598
- # @param name [String] Metadata field to remove
599
- def remove name
600
- Vips::vips_image_remove self, name
601
- end
631
+ # Write this image to a large memory buffer.
632
+ #
633
+ # @return [String] the pixels as a huge binary string
634
+ def write_to_memory
635
+ len = Vips::SizeStruct.new
636
+ ptr = Vips.vips_image_write_to_memory self, len
637
+ raise Vips::Error if ptr.nil?
602
638
 
603
- # compatibility: old name for get
604
- def get_value name
605
- get name
606
- end
639
+ # wrap up as an autopointer
640
+ ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
607
641
 
608
- # compatibility: old name for set
609
- def set_value name, value
610
- set name, value
611
- end
642
+ ptr.get_bytes 0, len[:value]
643
+ end
612
644
 
613
- # Get image width, in pixels.
614
- #
615
- # @return [Integer] image width, in pixels
616
- def width
617
- get "width"
618
- end
645
+ # Turn progress signalling on and off.
646
+ #
647
+ # If this is on, the most-downstream image from this image will issue
648
+ # progress signals.
649
+ #
650
+ # @see Object#signal_connect
651
+ # @param state [Boolean] progress signalling state
652
+ def set_progress state
653
+ Vips.vips_image_set_progress self, state
654
+ end
619
655
 
620
- # Get image height, in pixels.
621
- #
622
- # @return [Integer] image height, in pixels
623
- def height
624
- get "height"
625
- end
656
+ # Kill computation of this time.
657
+ #
658
+ # Set true to stop computation of this image. You can call this from a
659
+ # progress handler, for example.
660
+ #
661
+ # @see Object#signal_connect
662
+ # @param kill [Boolean] stop computation
663
+ def set_kill kill
664
+ Vips.vips_image_set_kill self, kill
665
+ end
626
666
 
627
- # Get number of image bands.
628
- #
629
- # @return [Integer] number of image bands
630
- def bands
631
- get "bands"
632
- end
667
+ # Get the `GType` of a metadata field. The result is 0 if no such field
668
+ # exists.
669
+ #
670
+ # @see get
671
+ # @param name [String] Metadata field to fetch
672
+ # @return [Integer] GType
673
+ def get_typeof name
674
+ # on libvips before 8.5, property types must be searched first,
675
+ # since vips_image_get_typeof returned built-in enums as int
676
+ unless Vips.at_least_libvips?(8, 5)
677
+ gtype = parent_get_typeof name
678
+ return gtype if gtype != 0
679
+ end
680
+
681
+ Vips.vips_image_get_typeof self, name
682
+ end
633
683
 
634
- # Get image format.
635
- #
636
- # @return [Symbol] image format
637
- def format
638
- get "format"
639
- end
684
+ # Get a metadata item from an image. Ruby types are constructed
685
+ # automatically from the `GValue`, if possible.
686
+ #
687
+ # For example, you can read the ICC profile from an image like this:
688
+ #
689
+ # ```
690
+ # profile = image.get "icc-profile-data"
691
+ # ```
692
+ #
693
+ # and profile will be an array containing the profile.
694
+ #
695
+ # @param name [String] Metadata field to get
696
+ # @return [Object] Value of field
697
+ def get name
698
+ # with old libvips, we must fetch properties (as opposed to
699
+ # metadata) via VipsObject
700
+ unless Vips.at_least_libvips?(8, 5)
701
+ return super if parent_get_typeof(name) != 0
702
+ end
703
+
704
+ gvalue = GObject::GValue.alloc
705
+ raise Vips::Error if Vips.vips_image_get(self, name, gvalue) != 0
706
+ result = gvalue.get
707
+ gvalue.unset
708
+
709
+ result
710
+ end
640
711
 
641
- # Get image interpretation.
642
- #
643
- # @return [Symbol] image interpretation
644
- def interpretation
645
- get "interpretation"
646
- end
712
+ # Get the names of all fields on an image. Use this to loop over all
713
+ # image metadata.
714
+ #
715
+ # @return [[String]] array of field names
716
+ def get_fields
717
+ # vips_image_get_fields() was added in libvips 8.5
718
+ return [] unless Vips.respond_to? :vips_image_get_fields
719
+
720
+ array = Vips.vips_image_get_fields self
721
+
722
+ names = []
723
+ p = array
724
+ until (q = p.read_pointer).null?
725
+ names << q.read_string
726
+ GLib.g_free q
727
+ p += FFI::Type::POINTER.size
728
+ end
729
+ GLib.g_free array
730
+
731
+ names
732
+ end
647
733
 
648
- # Get image coding.
649
- #
650
- # @return [Symbol] image coding
651
- def coding
652
- get "coding"
653
- end
734
+ # Mutate an image with a block. Inside the block, you can call methods
735
+ # which modify the image, such as setting or removing metadata, or
736
+ # modifying pixels.
737
+ #
738
+ # For example:
739
+ #
740
+ # ```ruby
741
+ # image = image.mutate do |x|
742
+ # (0 ... 1).step(0.01) do |i|
743
+ # x.draw_line! 255, x.width * i, 0, 0, x.height * (1 - i)
744
+ # end
745
+ # end
746
+ # ```
747
+ #
748
+ # See {MutableImage}.
749
+ def mutate
750
+ mutable = Vips::MutableImage.new self
751
+ yield mutable
752
+ mutable.image
753
+ end
654
754
 
655
- # Get image filename, if any.
656
- #
657
- # @return [String] image filename
658
- def filename
659
- get "filename"
660
- end
755
+ # This method is deprecated.
756
+ #
757
+ # Please use {MutableImage#set_type!} instead.
758
+ def set_type gtype, name, value
759
+ gvalue = GObject::GValue.alloc
760
+ gvalue.init gtype
761
+ gvalue.set value
762
+ Vips.vips_image_set self, name, gvalue
763
+ gvalue.unset
764
+ end
661
765
 
662
- # Get image xoffset.
663
- #
664
- # @return [Integer] image xoffset
665
- def xoffset
666
- get "xoffset"
667
- end
766
+ # This method is deprecated.
767
+ #
768
+ # Please use {MutableImage#set!} instead.
769
+ def set name, value
770
+ set_type get_typeof(name), name, value
771
+ end
668
772
 
669
- # Get image yoffset.
670
- #
671
- # @return [Integer] image yoffset
672
- def yoffset
673
- get "yoffset"
674
- end
773
+ # This method is deprecated.
774
+ #
775
+ # Please use {MutableImage#remove!} instead.
776
+ def remove name
777
+ Vips.vips_image_remove self, name
778
+ end
675
779
 
676
- # Get image x resolution.
677
- #
678
- # @return [Float] image x resolution
679
- def xres
680
- get "xres"
681
- end
780
+ # compatibility: old name for get
781
+ def get_value name
782
+ get name
783
+ end
682
784
 
683
- # Get image y resolution.
684
- #
685
- # @return [Float] image y resolution
686
- def yres
687
- get "yres"
688
- end
785
+ # This method is deprecated.
786
+ #
787
+ # Please use {MutableImage#set!} instead.
788
+ def set_value name, value
789
+ set name, value
790
+ end
689
791
 
690
- # Get scale metadata.
691
- #
692
- # @return [Float] image scale
693
- def scale
694
- return 1 if get_typeof("scale") == 0
792
+ # Get image width, in pixels.
793
+ #
794
+ # @return [Integer] image width, in pixels
795
+ def width
796
+ Vips.vips_image_get_width self
797
+ end
695
798
 
696
- get "scale"
697
- end
799
+ # Get image height, in pixels.
800
+ #
801
+ # @return [Integer] image height, in pixels
802
+ def height
803
+ Vips.vips_image_get_height self
804
+ end
698
805
 
699
- # Get offset metadata.
700
- #
701
- # @return [Float] image offset
702
- def offset
703
- return 0 if get_typeof("offset") == 0
806
+ # Get number of image bands.
807
+ #
808
+ # @return [Integer] number of image bands
809
+ def bands
810
+ Vips.vips_image_get_bands self
811
+ end
704
812
 
705
- get "offset"
706
- end
813
+ # Get image format.
814
+ #
815
+ # @return [Symbol] image format
816
+ def format
817
+ get "format"
818
+ end
707
819
 
708
- # Get the image size.
709
- #
710
- # @return [Integer, Integer] image width and height
711
- def size
712
- [width, height]
713
- end
820
+ # Get image interpretation.
821
+ #
822
+ # @return [Symbol] image interpretation
823
+ def interpretation
824
+ get "interpretation"
825
+ end
714
826
 
715
- if Vips::at_least_libvips?(8, 5)
716
- # Detect if image has an alpha channel
717
- #
718
- # @return [Boolean] true if image has an alpha channel.
719
- def has_alpha?
720
- return Vips::vips_image_hasalpha(self) != 0
721
- end
722
- end
827
+ # Get image coding.
828
+ #
829
+ # @return [Symbol] image coding
830
+ def coding
831
+ get "coding"
832
+ end
723
833
 
724
- # vips_addalpha was added in libvips 8.6
725
- if Vips::at_least_libvips?(8, 6)
726
- # Append an alpha channel to an image.
727
- #
728
- # @return [Image] new image
729
- def add_alpha
730
- ptr = GenericPtr.new
731
- result = Vips::vips_addalpha self, ptr
732
- raise Vips::Error if result != 0
733
-
734
- Vips::Image.new ptr[:value]
735
- end
736
- end
834
+ # Get image filename, if any.
835
+ #
836
+ # @return [String] image filename
837
+ def filename
838
+ get "filename"
839
+ end
737
840
 
738
- # Copy an image to a memory area.
739
- #
740
- # This can be useful for reusing results, but can obviously use a lot of
741
- # memory for large images. See {Image#tilecache} for a way of caching
742
- # parts of an image.
743
- #
744
- # @return [Image] new memory image
745
- def copy_memory
746
- new_image = Vips::vips_image_copy_memory self
747
- Vips::Image.new new_image
748
- end
841
+ # Get image xoffset.
842
+ #
843
+ # @return [Integer] image xoffset
844
+ def xoffset
845
+ get "xoffset"
846
+ end
749
847
 
750
- # Draw a point on an image.
751
- #
752
- # See {Image#draw_rect}.
753
- #
754
- # @return [Image] modified image
755
- def draw_point ink, left, top, opts = {}
756
- draw_rect ink, left, top, 1, 1, opts
757
- end
848
+ # Get image yoffset.
849
+ #
850
+ # @return [Integer] image yoffset
851
+ def yoffset
852
+ get "yoffset"
853
+ end
758
854
 
759
- # Add an image, constant or array.
760
- #
761
- # @param other [Image, Real, Array<Real>] Thing to add to self
762
- # @return [Image] result of addition
763
- def + other
764
- other.is_a?(Vips::Image) ?
765
- add(other) : linear(1, other)
766
- end
855
+ # Get image x resolution.
856
+ #
857
+ # @return [Float] image x resolution
858
+ def xres
859
+ get "xres"
860
+ end
767
861
 
768
- # Subtract an image, constant or array.
769
- #
770
- # @param other [Image, Real, Array<Real>] Thing to subtract from self
771
- # @return [Image] result of subtraction
772
- def - other
773
- other.is_a?(Vips::Image) ?
774
- subtract(other) : linear(1, Image::smap(other) {|x| x * -1})
775
- end
862
+ # Get image y resolution.
863
+ #
864
+ # @return [Float] image y resolution
865
+ def yres
866
+ get "yres"
867
+ end
776
868
 
777
- # Multiply an image, constant or array.
778
- #
779
- # @param other [Image, Real, Array<Real>] Thing to multiply by self
780
- # @return [Image] result of multiplication
781
- def * other
782
- other.is_a?(Vips::Image) ?
783
- multiply(other) : linear(other, 0)
784
- end
869
+ # Get scale metadata.
870
+ #
871
+ # @return [Float] image scale
872
+ def scale
873
+ return 1 if get_typeof("scale") == 0
785
874
 
786
- # Divide an image, constant or array.
787
- #
788
- # @param other [Image, Real, Array<Real>] Thing to divide self by
789
- # @return [Image] result of division
790
- def / other
791
- other.is_a?(Vips::Image) ?
792
- divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0)
793
- end
875
+ get "scale"
876
+ end
794
877
 
795
- # Remainder after integer division with an image, constant or array.
796
- #
797
- # @param other [Image, Real, Array<Real>] self modulo this
798
- # @return [Image] result of modulo
799
- def % other
800
- other.is_a?(Vips::Image) ?
801
- remainder(other) : remainder_const(other)
802
- end
878
+ # Get offset metadata.
879
+ #
880
+ # @return [Float] image offset
881
+ def offset
882
+ return 0 if get_typeof("offset") == 0
803
883
 
804
- # Raise to power of an image, constant or array.
805
- #
806
- # @param other [Image, Real, Array<Real>] self to the power of this
807
- # @return [Image] result of power
808
- def ** other
809
- call_enum "math2", other, :pow
810
- end
884
+ get "offset"
885
+ end
811
886
 
812
- # Integer left shift with an image, constant or array.
813
- #
814
- # @param other [Image, Real, Array<Real>] shift left by this much
815
- # @return [Image] result of left shift
816
- def << other
817
- call_enum "boolean", other, :lshift
818
- end
887
+ # Get the image size.
888
+ #
889
+ # @return [Integer, Integer] image width and height
890
+ def size
891
+ [width, height]
892
+ end
819
893
 
820
- # Integer right shift with an image, constant or array.
821
- #
822
- # @param other [Image, Real, Array<Real>] shift right by this much
823
- # @return [Image] result of right shift
824
- def >> other
825
- call_enum "boolean", other, :rshift
826
- end
894
+ if Vips.at_least_libvips?(8, 5)
895
+ # Detect if image has an alpha channel
896
+ #
897
+ # @return [Boolean] true if image has an alpha channel.
898
+ def has_alpha?
899
+ Vips.vips_image_hasalpha(self) != 0
900
+ end
901
+ end
827
902
 
828
- # Integer bitwise OR with an image, constant or array.
829
- #
830
- # @param other [Image, Real, Array<Real>] bitwise OR with this
831
- # @return [Image] result of bitwise OR
832
- def | other
833
- call_enum "boolean", other, :or
834
- end
903
+ # vips_addalpha was added in libvips 8.6
904
+ if Vips.at_least_libvips?(8, 6)
905
+ # Append an alpha channel to an image.
906
+ #
907
+ # @return [Image] new image
908
+ def add_alpha
909
+ ptr = GenericPtr.new
910
+ result = Vips.vips_addalpha self, ptr
911
+ raise Vips::Error if result != 0
912
+
913
+ Vips::Image.new ptr[:value]
914
+ end
915
+ end
835
916
 
836
- # Integer bitwise AND with an image, constant or array.
837
- #
838
- # @param other [Image, Real, Array<Real>] bitwise AND with this
839
- # @return [Image] result of bitwise AND
840
- def & other
841
- call_enum "boolean", other, :and
842
- end
917
+ # Copy an image to a memory area.
918
+ #
919
+ # This can be useful for reusing results, but can obviously use a lot of
920
+ # memory for large images. See {Image#tilecache} for a way of caching
921
+ # parts of an image.
922
+ #
923
+ # @return [Image] new memory image
924
+ def copy_memory
925
+ new_image = Vips.vips_image_copy_memory self
926
+ Vips::Image.new new_image
927
+ end
843
928
 
844
- # Integer bitwise EOR with an image, constant or array.
845
- #
846
- # @param other [Image, Real, Array<Real>] bitwise EOR with this
847
- # @return [Image] result of bitwise EOR
848
- def ^ other
849
- call_enum "boolean", other, :eor
850
- end
929
+ # Draw a point on an image.
930
+ #
931
+ # See {Image#draw_rect}.
932
+ #
933
+ # @return [Image] modified image
934
+ def draw_point ink, left, top, **opts
935
+ draw_rect ink, left, top, 1, 1, **opts
936
+ end
851
937
 
852
- # Equivalent to image ^ -1
853
- #
854
- # @return [Image] image with bits flipped
855
- def !
856
- self ^ -1
857
- end
938
+ # Add an image, constant or array.
939
+ #
940
+ # @param other [Image, Real, Array<Real>] Thing to add to self
941
+ # @return [Image] result of addition
942
+ def + other
943
+ other.is_a?(Vips::Image) ?
944
+ add(other) : linear(1, other)
945
+ end
858
946
 
859
- # Equivalent to image ^ -1
860
- #
861
- # @return [Image] image with bits flipped
862
- def ~
863
- self ^ -1
864
- end
947
+ # Subtract an image, constant or array.
948
+ #
949
+ # @param other [Image, Real, Array<Real>] Thing to subtract from self
950
+ # @return [Image] result of subtraction
951
+ def - other
952
+ other.is_a?(Vips::Image) ?
953
+ subtract(other) : linear(1, Image.smap(other) { |x| x * -1 })
954
+ end
865
955
 
866
- # @return [Image] image
867
- def +@
868
- self
869
- end
956
+ # Multiply an image, constant or array.
957
+ #
958
+ # @param other [Image, Real, Array<Real>] Thing to multiply by self
959
+ # @return [Image] result of multiplication
960
+ def * other
961
+ other.is_a?(Vips::Image) ?
962
+ multiply(other) : linear(other, 0)
963
+ end
870
964
 
871
- # Equivalent to image * -1
872
- #
873
- # @return [Image] negative of image
874
- def -@
875
- self * -1
876
- end
965
+ # Divide an image, constant or array.
966
+ #
967
+ # @param other [Image, Real, Array<Real>] Thing to divide self by
968
+ # @return [Image] result of division
969
+ def / other
970
+ other.is_a?(Vips::Image) ?
971
+ divide(other) : linear(Image.smap(other) { |x| 1.0 / x }, 0)
972
+ end
877
973
 
878
- # Relational less than with an image, constant or array.
879
- #
880
- # @param other [Image, Real, Array<Real>] relational less than with this
881
- # @return [Image] result of less than
882
- def < other
883
- call_enum "relational", other, :less
884
- end
974
+ # Remainder after integer division with an image, constant or array.
975
+ #
976
+ # @param other [Image, Real, Array<Real>] self modulo this
977
+ # @return [Image] result of modulo
978
+ def % other
979
+ other.is_a?(Vips::Image) ?
980
+ remainder(other) : remainder_const(other)
981
+ end
885
982
 
886
- # Relational less than or equal to with an image, constant or array.
887
- #
888
- # @param other [Image, Real, Array<Real>] relational less than or
889
- # equal to with this
890
- # @return [Image] result of less than or equal to
891
- def <= other
892
- call_enum "relational", other, :lesseq
893
- end
983
+ # Raise to power of an image, constant or array.
984
+ #
985
+ # @param other [Image, Real, Array<Real>] self to the power of this
986
+ # @return [Image] result of power
987
+ def ** other
988
+ call_enum "math2", other, :pow
989
+ end
894
990
 
895
- # Relational more than with an image, constant or array.
896
- #
897
- # @param other [Image, Real, Array<Real>] relational more than with this
898
- # @return [Image] result of more than
899
- def > other
900
- call_enum "relational", other, :more
901
- end
991
+ # Integer left shift with an image, constant or array.
992
+ #
993
+ # @param other [Image, Real, Array<Real>] shift left by this much
994
+ # @return [Image] result of left shift
995
+ def << other
996
+ call_enum "boolean", other, :lshift
997
+ end
902
998
 
903
- # Relational more than or equal to with an image, constant or array.
904
- #
905
- # @param other [Image, Real, Array<Real>] relational more than or
906
- # equal to with this
907
- # @return [Image] result of more than or equal to
908
- def >= other
909
- call_enum "relational", other, :moreeq
910
- end
999
+ # Integer right shift with an image, constant or array.
1000
+ #
1001
+ # @param other [Image, Real, Array<Real>] shift right by this much
1002
+ # @return [Image] result of right shift
1003
+ def >> other
1004
+ call_enum "boolean", other, :rshift
1005
+ end
911
1006
 
912
- # Compare equality to nil, an image, constant or array.
913
- #
914
- # @param other [nil, Image, Real, Array<Real>] test equality to this
915
- # @return [Image] result of equality
916
- def == other
917
- # for equality, we must allow tests against nil
918
- if other == nil
919
- false
920
- else
921
- call_enum "relational", other, :equal
922
- end
923
- end
1007
+ # Integer bitwise OR with an image, constant or array.
1008
+ #
1009
+ # @param other [Image, Real, Array<Real>] bitwise OR with this
1010
+ # @return [Image] result of bitwise OR
1011
+ def | other
1012
+ call_enum "boolean", other, :or
1013
+ end
924
1014
 
925
- # Compare inequality to nil, an image, constant or array.
926
- #
927
- # @param other [nil, Image, Real, Array<Real>] test inequality to this
928
- # @return [Image] result of inequality
929
- def != other
930
- # for equality, we must allow tests against nil
931
- if other == nil
932
- true
933
- else
934
- call_enum "relational", other, :noteq
935
- end
936
- end
1015
+ # Integer bitwise AND with an image, constant or array.
1016
+ #
1017
+ # @param other [Image, Real, Array<Real>] bitwise AND with this
1018
+ # @return [Image] result of bitwise AND
1019
+ def & other
1020
+ call_enum "boolean", other, :and
1021
+ end
937
1022
 
938
- # Fetch bands using a number or a range
939
- #
940
- # @param index [Numeric, Range] extract these band(s)
941
- # @return [Image] extracted band(s)
942
- def [] index
943
- if index.is_a? Range
944
- n = index.size
945
- extract_band index.begin, n: n
946
- elsif index.is_a? Numeric
947
- extract_band index
948
- else
949
- raise Vips::Error, "[] index is not range or numeric."
950
- end
951
- end
1023
+ # Integer bitwise EOR with an image, constant or array.
1024
+ #
1025
+ # @param other [Image, Real, Array<Real>] bitwise EOR with this
1026
+ # @return [Image] result of bitwise EOR
1027
+ def ^ other
1028
+ call_enum "boolean", other, :eor
1029
+ end
952
1030
 
953
- # Convert to an Array. This will be slow for large images.
954
- #
955
- # @return [Array] array of Fixnum
956
- def to_a
957
- # we render the image to a big string, then unpack
958
- # as a Ruby array of the correct type
959
- memory = write_to_memory
960
-
961
- # make the template for unpack
962
- template = {
963
- :char => 'c',
964
- :uchar => 'C',
965
- :short => 's_',
966
- :ushort => 'S_',
967
- :int => 'i_',
968
- :uint => 'I_',
969
- :float => 'f',
970
- :double => 'd',
971
- :complex => 'f',
972
- :dpcomplex => 'd'
973
- }[format] + '*'
974
-
975
- # and unpack into something like [1, 2, 3, 4 ..]
976
- array = memory.unpack(template)
977
-
978
- # gather band elements together
979
- pixel_array = array.each_slice(bands).to_a
980
-
981
- # build rows
982
- row_array = pixel_array.each_slice(width).to_a
983
-
984
- return row_array
985
- end
1031
+ # Equivalent to image ^ -1
1032
+ #
1033
+ # @return [Image] image with bits flipped
1034
+ def !
1035
+ self ^ -1
1036
+ end
986
1037
 
987
- # Return the largest integral value not greater than the argument.
988
- #
989
- # @return [Image] floor of image
990
- def floor
991
- round :floor
992
- end
1038
+ # Equivalent to image ^ -1
1039
+ #
1040
+ # @return [Image] image with bits flipped
1041
+ def ~
1042
+ self ^ -1
1043
+ end
993
1044
 
994
- # Return the smallest integral value not less than the argument.
995
- #
996
- # @return [Image] ceil of image
997
- def ceil
998
- round :ceil
999
- end
1045
+ # @return [Image] image
1046
+ def +@
1047
+ self
1048
+ end
1000
1049
 
1001
- # Return the nearest integral value.
1002
- #
1003
- # @return [Image] rint of image
1004
- def rint
1005
- round :rint
1006
- end
1050
+ # Equivalent to image * -1
1051
+ #
1052
+ # @return [Image] negative of image
1053
+ def -@
1054
+ self * -1
1055
+ end
1007
1056
 
1008
- # AND the bands of an image together
1009
- #
1010
- # @return [Image] all bands ANDed together
1011
- def bandand
1012
- bandbool :and
1013
- end
1057
+ # Relational less than with an image, constant or array.
1058
+ #
1059
+ # @param other [Image, Real, Array<Real>] relational less than with this
1060
+ # @return [Image] result of less than
1061
+ def < other
1062
+ call_enum "relational", other, :less
1063
+ end
1014
1064
 
1015
- # OR the bands of an image together
1016
- #
1017
- # @return [Image] all bands ORed together
1018
- def bandor
1019
- bandbool :or
1020
- end
1065
+ # Relational less than or equal to with an image, constant or array.
1066
+ #
1067
+ # @param other [Image, Real, Array<Real>] relational less than or
1068
+ # equal to with this
1069
+ # @return [Image] result of less than or equal to
1070
+ def <= other
1071
+ call_enum "relational", other, :lesseq
1072
+ end
1021
1073
 
1022
- # EOR the bands of an image together
1023
- #
1024
- # @return [Image] all bands EORed together
1025
- def bandeor
1026
- bandbool :eor
1027
- end
1074
+ # Relational more than with an image, constant or array.
1075
+ #
1076
+ # @param other [Image, Real, Array<Real>] relational more than with this
1077
+ # @return [Image] result of more than
1078
+ def > other
1079
+ call_enum "relational", other, :more
1080
+ end
1028
1081
 
1029
- # Split an n-band image into n separate images.
1030
- #
1031
- # @return [Array<Image>] Array of n one-band images
1032
- def bandsplit
1033
- (0...bands).map {|i| extract_band i}
1034
- end
1082
+ # Relational more than or equal to with an image, constant or array.
1083
+ #
1084
+ # @param other [Image, Real, Array<Real>] relational more than or
1085
+ # equal to with this
1086
+ # @return [Image] result of more than or equal to
1087
+ def >= other
1088
+ call_enum "relational", other, :moreeq
1089
+ end
1035
1090
 
1036
- # Join a set of images bandwise.
1037
- #
1038
- # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1039
- # @return [Image] many band image
1040
- def bandjoin other
1041
- unless other.is_a? Array
1042
- other = [other]
1043
- end
1044
-
1045
- # if other is just Numeric, we can use bandjoin_const
1046
- not_all_real = !other.all?{|x| x.is_a? Numeric}
1047
-
1048
- if not_all_real
1049
- Vips::Image.bandjoin([self] + other)
1050
- else
1051
- bandjoin_const other
1052
- end
1053
- end
1091
+ # Compare equality to nil, an image, constant or array.
1092
+ #
1093
+ # @param other [nil, Image, Real, Array<Real>] test equality to this
1094
+ # @return [Image] result of equality
1095
+ def == other
1096
+ # for equality, we must allow tests against nil
1097
+ if other.nil?
1098
+ false
1099
+ else
1100
+ call_enum "relational", other, :equal
1101
+ end
1102
+ end
1054
1103
 
1055
- # Composite a set of images with a set of blend modes.
1056
- #
1057
- # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1058
- # @return [Image] many band image
1059
- def composite other, mode, opts = {}
1060
- unless other.is_a? Array
1061
- other = [other]
1062
- end
1063
- unless mode.is_a? Array
1064
- mode = [mode]
1065
- end
1066
-
1067
- mode = mode.map do |x|
1068
- GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1069
- end
1070
-
1071
- Vips::Image.composite([self] + other, mode, opts)
1072
- end
1104
+ # Compare inequality to nil, an image, constant or array.
1105
+ #
1106
+ # @param other [nil, Image, Real, Array<Real>] test inequality to this
1107
+ # @return [Image] result of inequality
1108
+ def != other
1109
+ # for equality, we must allow tests against nil
1110
+ if other.nil?
1111
+ true
1112
+ else
1113
+ call_enum "relational", other, :noteq
1114
+ end
1115
+ end
1073
1116
 
1074
- # Return the coordinates of the image maximum.
1075
- #
1076
- # @return [Real, Real, Real] maximum value, x coordinate of maximum, y
1077
- # coordinate of maximum
1078
- def maxpos
1079
- v, opts = max x: true, y: true
1080
- x = opts['x']
1081
- y = opts['y']
1082
- return v, x, y
1083
- end
1117
+ # Fetch bands using a number or a range
1118
+ #
1119
+ # @param index [Numeric, Range] extract these band(s)
1120
+ # @return [Image] extracted band(s)
1121
+ def [] index
1122
+ if index.is_a? Range
1123
+ n = index.size
1124
+ extract_band index.begin, n: n
1125
+ elsif index.is_a? Numeric
1126
+ extract_band index
1127
+ else
1128
+ raise Vips::Error, "[] index is not range or numeric."
1129
+ end
1130
+ end
1084
1131
 
1085
- # Return the coordinates of the image minimum.
1086
- #
1087
- # @return [Real, Real, Real] minimum value, x coordinate of minimum, y
1088
- # coordinate of minimum
1089
- def minpos
1090
- v, opts = min x: true, y: true
1091
- x = opts['x']
1092
- y = opts['y']
1093
- return v, x, y
1094
- end
1132
+ # Convert to an Array. This will be slow for large images.
1133
+ #
1134
+ # @return [Array] array of Fixnum
1135
+ def to_a
1136
+ # we render the image to a big string, then unpack
1137
+ # as a Ruby array of the correct type
1138
+ memory = write_to_memory
1139
+
1140
+ # make the template for unpack
1141
+ template = {
1142
+ char: "c",
1143
+ uchar: "C",
1144
+ short: "s_",
1145
+ ushort: "S_",
1146
+ int: "i_",
1147
+ uint: "I_",
1148
+ float: "f",
1149
+ double: "d",
1150
+ complex: "f",
1151
+ dpcomplex: "d"
1152
+ }[format] + "*"
1153
+
1154
+ # and unpack into something like [1, 2, 3, 4 ..]
1155
+ array = memory.unpack(template)
1156
+
1157
+ # gather band elements together
1158
+ pixel_array = array.each_slice(bands).to_a
1159
+
1160
+ # build rows
1161
+ pixel_array.each_slice(width).to_a
1162
+ end
1095
1163
 
1096
- # get the value of a pixel as an array
1097
- #
1098
- # @param x [Integer] x coordinate to sample
1099
- # @param y [Integer] y coordinate to sample
1100
- # @return [Array<Float>] the pixel values as an array
1101
- def getpoint x, y
1102
- # vips has an operation that does this, but we can't call it via
1103
- # gobject-introspection 3.1 since it's missing array double
1104
- # get
1105
- #
1106
- # remove this def when gobject-introspection updates
1107
- crop(x, y, 1, 1).bandsplit.map(&:avg)
1108
- end
1164
+ # Return the largest integral value not greater than the argument.
1165
+ #
1166
+ # @return [Image] floor of image
1167
+ def floor
1168
+ round :floor
1169
+ end
1109
1170
 
1110
- # a median filter
1111
- #
1112
- # @param size [Integer] size of filter window
1113
- # @return [Image] result of median filter
1114
- def median size = 3
1115
- rank size, size, (size * size) / 2
1116
- end
1171
+ # Return the smallest integral value not less than the argument.
1172
+ #
1173
+ # @return [Image] ceil of image
1174
+ def ceil
1175
+ round :ceil
1176
+ end
1117
1177
 
1118
- # Return the real part of a complex image.
1119
- #
1120
- # @return [Image] real part of complex image
1121
- def real
1122
- complexget :real
1123
- end
1178
+ # Return the nearest integral value.
1179
+ #
1180
+ # @return [Image] rint of image
1181
+ def rint
1182
+ round :rint
1183
+ end
1124
1184
 
1125
- # Return the imaginary part of a complex image.
1126
- #
1127
- # @return [Image] imaginary part of complex image
1128
- def imag
1129
- complexget :imag
1130
- end
1185
+ # AND the bands of an image together
1186
+ #
1187
+ # @return [Image] all bands ANDed together
1188
+ def bandand
1189
+ bandbool :and
1190
+ end
1131
1191
 
1132
- # Return an image with rectangular pixels converted to polar.
1133
- #
1134
- # The image
1135
- # can be complex, in which case the return image will also be complex,
1136
- # or must have an even number of bands, in which case pairs of
1137
- # bands are treated as (x, y) coordinates.
1138
- #
1139
- # @see xyz
1140
- # @return [Image] image converted to polar coordinates
1141
- def polar
1142
- Image::run_cmplx(self) {|x| x.complex :polar}
1143
- end
1192
+ # OR the bands of an image together
1193
+ #
1194
+ # @return [Image] all bands ORed together
1195
+ def bandor
1196
+ bandbool :or
1197
+ end
1144
1198
 
1145
- # Return an image with polar pixels converted to rectangular.
1146
- #
1147
- # The image
1148
- # can be complex, in which case the return image will also be complex,
1149
- # or must have an even number of bands, in which case pairs of
1150
- # bands are treated as (x, y) coordinates.
1151
- #
1152
- # @see xyz
1153
- # @return [Image] image converted to rectangular coordinates
1154
- def rect
1155
- Image::run_cmplx(self) {|x| x.complex :rect}
1156
- end
1199
+ # EOR the bands of an image together
1200
+ #
1201
+ # @return [Image] all bands EORed together
1202
+ def bandeor
1203
+ bandbool :eor
1204
+ end
1157
1205
 
1158
- # Return the complex conjugate of an image.
1159
- #
1160
- # The image
1161
- # can be complex, in which case the return image will also be complex,
1162
- # or must have an even number of bands, in which case pairs of
1163
- # bands are treated as (x, y) coordinates.
1164
- #
1165
- # @return [Image] complex conjugate
1166
- def conj
1167
- Image::run_cmplx(self) {|x| x.complex :conj}
1168
- end
1206
+ # Split an n-band image into n separate images.
1207
+ #
1208
+ # @return [Array<Image>] Array of n one-band images
1209
+ def bandsplit
1210
+ (0...bands).map { |i| extract_band i }
1211
+ end
1169
1212
 
1170
- # Calculate the cross phase of two images.
1171
- #
1172
- # @param other [Image, Real, Array<Real>] cross phase with this
1173
- # @return [Image] cross phase
1174
- def cross_phase other
1175
- complex2 other, :cross_phase
1176
- end
1213
+ # Join a set of images bandwise.
1214
+ #
1215
+ # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1216
+ # @return [Image] many band image
1217
+ def bandjoin other
1218
+ unless other.is_a? Array
1219
+ other = [other]
1220
+ end
1221
+
1222
+ # if other is just Numeric, we can use bandjoin_const
1223
+ not_all_real = !other.all? { |x| x.is_a? Numeric }
1224
+
1225
+ if not_all_real
1226
+ Vips::Image.bandjoin([self] + other)
1227
+ else
1228
+ bandjoin_const other
1229
+ end
1230
+ end
1177
1231
 
1178
- # Return the sine of an image in degrees.
1179
- #
1180
- # @return [Image] sine of each pixel
1181
- def sin
1182
- math :sin
1183
- end
1232
+ # Composite a set of images with a set of blend modes.
1233
+ #
1234
+ # @param overlay [Image, Array<Image>] images to composite
1235
+ # @param mode [BlendMode, Array<BlendMode>] blend modes to use
1236
+ # @param opts [Hash] Set of options
1237
+ # @option opts [Array<Integer>] :x x positions of overlay
1238
+ # @option opts [Array<Integer>] :y y positions of overlay
1239
+ # @option opts [Vips::Interpretation] :compositing_space Composite images
1240
+ # in this colour space
1241
+ # @option opts [Boolean] :premultiplied Images have premultiplied alpha
1242
+ # @return [Image] blended image
1243
+ def composite overlay, mode, **opts
1244
+ unless overlay.is_a? Array
1245
+ overlay = [overlay]
1246
+ end
1247
+ unless mode.is_a? Array
1248
+ mode = [mode]
1249
+ end
1250
+
1251
+ mode = mode.map do |x|
1252
+ GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1253
+ end
1254
+
1255
+ Vips::Image.composite([self] + overlay, mode, **opts)
1256
+ end
1184
1257
 
1185
- # Return the cosine of an image in degrees.
1186
- #
1187
- # @return [Image] cosine of each pixel
1188
- def cos
1189
- math :cos
1190
- end
1258
+ # Return the coordinates of the image maximum.
1259
+ #
1260
+ # @return [Real, Real, Real] maximum value, x coordinate of maximum, y
1261
+ # coordinate of maximum
1262
+ def maxpos
1263
+ v, opts = max x: true, y: true
1264
+ x = opts["x"]
1265
+ y = opts["y"]
1266
+ [v, x, y]
1267
+ end
1191
1268
 
1192
- # Return the tangent of an image in degrees.
1193
- #
1194
- # @return [Image] tangent of each pixel
1195
- def tan
1196
- math :tan
1197
- end
1269
+ # Return the coordinates of the image minimum.
1270
+ #
1271
+ # @return [Real, Real, Real] minimum value, x coordinate of minimum, y
1272
+ # coordinate of minimum
1273
+ def minpos
1274
+ v, opts = min x: true, y: true
1275
+ x = opts["x"]
1276
+ y = opts["y"]
1277
+ [v, x, y]
1278
+ end
1198
1279
 
1199
- # Return the inverse sine of an image in degrees.
1200
- #
1201
- # @return [Image] inverse sine of each pixel
1202
- def asin
1203
- math :asin
1204
- end
1280
+ # a median filter
1281
+ #
1282
+ # @param size [Integer] size of filter window
1283
+ # @return [Image] result of median filter
1284
+ def median size = 3
1285
+ rank size, size, size**2 / 2
1286
+ end
1205
1287
 
1206
- # Return the inverse cosine of an image in degrees.
1207
- #
1208
- # @return [Image] inverse cosine of each pixel
1209
- def acos
1210
- math :acos
1211
- end
1288
+ # Return the real part of a complex image.
1289
+ #
1290
+ # @return [Image] real part of complex image
1291
+ def real
1292
+ complexget :real
1293
+ end
1212
1294
 
1213
- # Return the inverse tangent of an image in degrees.
1214
- #
1215
- # @return [Image] inverse tangent of each pixel
1216
- def atan
1217
- math :atan
1218
- end
1295
+ # Return the imaginary part of a complex image.
1296
+ #
1297
+ # @return [Image] imaginary part of complex image
1298
+ def imag
1299
+ complexget :imag
1300
+ end
1219
1301
 
1220
- # Return the natural log of an image.
1221
- #
1222
- # @return [Image] natural log of each pixel
1223
- def log
1224
- math :log
1225
- end
1302
+ # Return an image with rectangular pixels converted to polar.
1303
+ #
1304
+ # The image
1305
+ # can be complex, in which case the return image will also be complex,
1306
+ # or must have an even number of bands, in which case pairs of
1307
+ # bands are treated as (x, y) coordinates.
1308
+ #
1309
+ # @see xyz
1310
+ # @return [Image] image converted to polar coordinates
1311
+ def polar
1312
+ Image.run_cmplx(self) { |x| x.complex :polar }
1313
+ end
1226
1314
 
1227
- # Return the log base 10 of an image.
1228
- #
1229
- # @return [Image] base 10 log of each pixel
1230
- def log10
1231
- math :log10
1232
- end
1315
+ # Return an image with polar pixels converted to rectangular.
1316
+ #
1317
+ # The image
1318
+ # can be complex, in which case the return image will also be complex,
1319
+ # or must have an even number of bands, in which case pairs of
1320
+ # bands are treated as (x, y) coordinates.
1321
+ #
1322
+ # @see xyz
1323
+ # @return [Image] image converted to rectangular coordinates
1324
+ def rect
1325
+ Image.run_cmplx(self) { |x| x.complex :rect }
1326
+ end
1233
1327
 
1234
- # Return e ** pixel.
1235
- #
1236
- # @return [Image] e ** pixel
1237
- def exp
1238
- math :exp
1239
- end
1328
+ # Return the complex conjugate of an image.
1329
+ #
1330
+ # The image
1331
+ # can be complex, in which case the return image will also be complex,
1332
+ # or must have an even number of bands, in which case pairs of
1333
+ # bands are treated as (x, y) coordinates.
1334
+ #
1335
+ # @return [Image] complex conjugate
1336
+ def conj
1337
+ Image.run_cmplx(self) { |x| x.complex :conj }
1338
+ end
1240
1339
 
1241
- # Return 10 ** pixel.
1242
- #
1243
- # @return [Image] 10 ** pixel
1244
- def exp10
1245
- math :exp10
1246
- end
1340
+ # Calculate the cross phase of two images.
1341
+ #
1342
+ # @param other [Image, Real, Array<Real>] cross phase with this
1343
+ # @return [Image] cross phase
1344
+ def cross_phase other
1345
+ complex2 other, :cross_phase
1346
+ end
1247
1347
 
1248
- # Flip horizontally.
1249
- #
1250
- # @return [Image] image flipped horizontally
1251
- def fliphor
1252
- flip :horizontal
1253
- end
1348
+ # Return the sine of an image in degrees.
1349
+ #
1350
+ # @return [Image] sine of each pixel
1351
+ def sin
1352
+ math :sin
1353
+ end
1254
1354
 
1255
- # Flip vertically.
1256
- #
1257
- # @return [Image] image flipped vertically
1258
- def flipver
1259
- flip :vertical
1260
- end
1355
+ # Return the cosine of an image in degrees.
1356
+ #
1357
+ # @return [Image] cosine of each pixel
1358
+ def cos
1359
+ math :cos
1360
+ end
1261
1361
 
1262
- # Erode with a structuring element.
1263
- #
1264
- # The structuring element must be an array with 0 for black, 255 for
1265
- # white and 128 for don't care.
1266
- #
1267
- # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1268
- # element
1269
- # @return [Image] eroded image
1270
- def erode mask
1271
- morph mask, :erode
1272
- end
1362
+ # Return the tangent of an image in degrees.
1363
+ #
1364
+ # @return [Image] tangent of each pixel
1365
+ def tan
1366
+ math :tan
1367
+ end
1273
1368
 
1274
- # Dilate with a structuring element.
1275
- #
1276
- # The structuring element must be an array with 0 for black, 255 for
1277
- # white and 128 for don't care.
1278
- #
1279
- # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1280
- # element
1281
- # @return [Image] dilated image
1282
- def dilate mask
1283
- morph mask, :dilate
1284
- end
1369
+ # Return the inverse sine of an image in degrees.
1370
+ #
1371
+ # @return [Image] inverse sine of each pixel
1372
+ def asin
1373
+ math :asin
1374
+ end
1285
1375
 
1286
- # Rotate by 90 degrees clockwise.
1287
- #
1288
- # @return [Image] rotated image
1289
- def rot90
1290
- rot :d90
1291
- end
1376
+ # Return the inverse cosine of an image in degrees.
1377
+ #
1378
+ # @return [Image] inverse cosine of each pixel
1379
+ def acos
1380
+ math :acos
1381
+ end
1292
1382
 
1293
- # Rotate by 180 degrees clockwise.
1294
- #
1295
- # @return [Image] rotated image
1296
- def rot180
1297
- rot :d180
1298
- end
1383
+ # Return the inverse tangent of an image in degrees.
1384
+ #
1385
+ # @return [Image] inverse tangent of each pixel
1386
+ def atan
1387
+ math :atan
1388
+ end
1299
1389
 
1300
- # Rotate by 270 degrees clockwise.
1301
- #
1302
- # @return [Image] rotated image
1303
- def rot270
1304
- rot :d270
1305
- end
1390
+ # Return the natural log of an image.
1391
+ #
1392
+ # @return [Image] natural log of each pixel
1393
+ def log
1394
+ math :log
1395
+ end
1306
1396
 
1307
- # Select pixels from `th` if `self` is non-zero and from `el` if
1308
- # `self` is zero. Use the `:blend` option to fade smoothly
1309
- # between `th` and `el`.
1310
- #
1311
- # @param th [Image, Real, Array<Real>] true values
1312
- # @param el [Image, Real, Array<Real>] false values
1313
- # @param opts [Hash] set of options
1314
- # @option opts [Boolean] :blend (false) Blend smoothly between th and el
1315
- # @return [Image] merged image
1316
- def ifthenelse(th, el, opts = {})
1317
- match_image = [th, el, self].find {|x| x.is_a? Vips::Image}
1318
-
1319
- unless th.is_a? Vips::Image
1320
- th = Operation.imageize match_image, th
1321
- end
1322
- unless el.is_a? Vips::Image
1323
- el = Operation.imageize match_image, el
1324
- end
1325
-
1326
- Vips::Operation.call "ifthenelse", [self, th, el], opts
1327
- end
1397
+ # Return the log base 10 of an image.
1398
+ #
1399
+ # @return [Image] base 10 log of each pixel
1400
+ def log10
1401
+ math :log10
1402
+ end
1328
1403
 
1329
- # Scale an image to uchar. This is the vips `scale` operation, but
1330
- # renamed to avoid a clash with the `.scale` property.
1331
- #
1332
- # @param opts [Hash] Set of options
1333
- # @return [Vips::Image] Output image
1334
- def scaleimage opts = {}
1335
- Vips::Image.scale self, opts
1336
- end
1404
+ # Return e ** pixel.
1405
+ #
1406
+ # @return [Image] e ** pixel
1407
+ def exp
1408
+ math :exp
1409
+ end
1337
1410
 
1411
+ # Return 10 ** pixel.
1412
+ #
1413
+ # @return [Image] 10 ** pixel
1414
+ def exp10
1415
+ math :exp10
1338
1416
  end
1339
1417
 
1340
- # This method generates yard comments for all the dynamically bound
1341
- # vips operations.
1418
+ # Flip horizontally.
1342
1419
  #
1343
- # Regenerate with something like:
1420
+ # @return [Image] image flipped horizontally
1421
+ def fliphor
1422
+ flip :horizontal
1423
+ end
1424
+
1425
+ # Flip vertically.
1344
1426
  #
1345
- # ```
1346
- # $ ruby > methods.rb
1347
- # require 'vips'; Vips::generate_yard
1348
- # ^D
1349
- # ```
1427
+ # @return [Image] image flipped vertically
1428
+ def flipver
1429
+ flip :vertical
1430
+ end
1350
1431
 
1351
- def self.generate_yard
1352
- # these have hand-written methods, see above
1353
- no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
1354
-
1355
- # map gobject's type names to Ruby
1356
- map_go_to_ruby = {
1357
- "gboolean" => "Boolean",
1358
- "gint" => "Integer",
1359
- "gdouble" => "Float",
1360
- "gfloat" => "Float",
1361
- "gchararray" => "String",
1362
- "VipsImage" => "Vips::Image",
1363
- "VipsInterpolate" => "Vips::Interpolate",
1364
- "VipsArrayDouble" => "Array<Double>",
1365
- "VipsArrayInt" => "Array<Integer>",
1366
- "VipsArrayImage" => "Array<Image>",
1367
- "VipsArrayString" => "Array<String>",
1368
- }
1369
-
1370
- generate_operation = lambda do |gtype, nickname, op|
1371
- op_flags = op.get_flags
1372
- return if (op_flags & OPERATION_DEPRECATED) != 0
1373
- return if no_generate.include? nickname
1374
- description = Vips::vips_object_get_description op
1375
-
1376
- # find and classify all the arguments the operator can take
1377
- required_input = []
1378
- optional_input = []
1379
- required_output = []
1380
- optional_output = []
1381
- member_x = nil
1382
- op.argument_map do |pspec, argument_class, argument_instance|
1383
- arg_flags = argument_class[:flags]
1384
- next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1385
- next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1386
-
1387
- name = pspec[:name].tr("-", "_")
1388
- # 'in' as a param name confuses yard
1389
- name = "im" if name == "in"
1390
- gtype = pspec[:value_type]
1391
- fundamental = GObject::g_type_fundamental gtype
1392
- type_name = GObject::g_type_name gtype
1393
- if map_go_to_ruby.include? type_name
1394
- type_name = map_go_to_ruby[type_name]
1395
- end
1396
- if fundamental == GObject::GFLAGS_TYPE ||
1397
- fundamental == GObject::GENUM_TYPE
1398
- type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1399
- end
1400
- blurb = GObject::g_param_spec_get_blurb pspec
1401
- value = {:name => name,
1402
- :flags => arg_flags,
1403
- :gtype => gtype,
1404
- :type_name => type_name,
1405
- :blurb => blurb}
1406
-
1407
- if (arg_flags & ARGUMENT_INPUT) != 0
1408
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1409
- # note the first required input image, if any ... we
1410
- # will be a method of this instance
1411
- if !member_x && gtype == Vips::IMAGE_TYPE
1412
- member_x = value
1413
- else
1414
- required_input << value
1415
- end
1416
- else
1417
- optional_input << value
1418
- end
1419
- end
1420
-
1421
- # MODIFY INPUT args count as OUTPUT as well
1422
- if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
1423
- ((arg_flags & ARGUMENT_INPUT) != 0 &&
1424
- (arg_flags & ARGUMENT_MODIFY) != 0)
1425
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1426
- required_output << value
1427
- else
1428
- optional_output << value
1429
- end
1430
- end
1431
-
1432
- end
1433
-
1434
- print "# @!method "
1435
- print "self." unless member_x
1436
- print "#{nickname}("
1437
- print required_input.map{|x| x[:name]}.join(", ")
1438
- print ", " if required_input.length > 0
1439
- puts "opts = {})"
1440
-
1441
- puts "# #{description.capitalize}."
1442
-
1443
- required_input.each do |arg|
1444
- puts "# @param #{arg[:name]} [#{arg[:type_name]}] " +
1445
- "#{arg[:blurb]}"
1446
- end
1447
-
1448
- puts "# @param opts [Hash] Set of options"
1449
- optional_input.each do |arg|
1450
- puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
1451
- "#{arg[:blurb]}"
1452
- end
1453
- optional_output.each do |arg|
1454
- print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
1455
- puts " Output #{arg[:blurb]}"
1456
- end
1457
-
1458
- print "# @return ["
1459
- if required_output.length == 0
1460
- print "nil"
1461
- elsif required_output.length == 1
1462
- print required_output.first[:type_name]
1463
- elsif
1464
- print "Array<"
1465
- print required_output.map{|x| x[:type_name]}.join(", ")
1466
- print ">"
1467
- end
1468
- if optional_output.length > 0
1469
- print ", Hash<Symbol => Object>"
1470
- end
1471
- print "] "
1472
- print required_output.map{|x| x[:blurb]}.join(", ")
1473
- if optional_output.length > 0
1474
- print ", " if required_output.length > 0
1475
- print "Hash of optional output items"
1476
- end
1477
- puts ""
1478
-
1479
- puts ""
1480
- end
1432
+ # Erode with a structuring element.
1433
+ #
1434
+ # The structuring element must be an array with 0 for black, 255 for
1435
+ # white and 128 for don't care.
1436
+ #
1437
+ # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1438
+ # element
1439
+ # @return [Image] eroded image
1440
+ def erode mask
1441
+ morph mask, :erode
1442
+ end
1481
1443
 
1482
- generate_class = lambda do |gtype, a|
1483
- nickname = Vips::nickname_find gtype
1444
+ # Dilate with a structuring element.
1445
+ #
1446
+ # The structuring element must be an array with 0 for black, 255 for
1447
+ # white and 128 for don't care.
1448
+ #
1449
+ # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1450
+ # element
1451
+ # @return [Image] dilated image
1452
+ def dilate mask
1453
+ morph mask, :dilate
1454
+ end
1484
1455
 
1485
- if nickname
1486
- begin
1487
- # can fail for abstract types
1488
- op = Vips::Operation.new nickname
1489
- rescue
1490
- end
1456
+ # Rotate by 90 degrees clockwise.
1457
+ #
1458
+ # @return [Image] rotated image
1459
+ def rot90
1460
+ rot :d90
1461
+ end
1462
+
1463
+ # Rotate by 180 degrees clockwise.
1464
+ #
1465
+ # @return [Image] rotated image
1466
+ def rot180
1467
+ rot :d180
1468
+ end
1491
1469
 
1492
- generate_operation.(gtype, nickname, op) if op
1493
- end
1470
+ # Rotate by 270 degrees clockwise.
1471
+ #
1472
+ # @return [Image] rotated image
1473
+ def rot270
1474
+ rot :d270
1475
+ end
1494
1476
 
1495
- Vips::vips_type_map gtype, generate_class, nil
1496
- end
1477
+ # Select pixels from `th` if `self` is non-zero and from `el` if
1478
+ # `self` is zero. Use the `:blend` option to fade smoothly
1479
+ # between `th` and `el`.
1480
+ #
1481
+ # @param th [Image, Real, Array<Real>] true values
1482
+ # @param el [Image, Real, Array<Real>] false values
1483
+ # @param opts [Hash] set of options
1484
+ # @option opts [Boolean] :blend (false) Blend smoothly between th and el
1485
+ # @return [Image] merged image
1486
+ def ifthenelse(th, el, **opts)
1487
+ match_image = [th, el, self].find { |x| x.is_a? Vips::Image }
1488
+
1489
+ unless th.is_a? Vips::Image
1490
+ th = Operation.imageize match_image, th
1491
+ end
1492
+ unless el.is_a? Vips::Image
1493
+ el = Operation.imageize match_image, el
1494
+ end
1495
+
1496
+ Vips::Operation.call "ifthenelse", [self, th, el], opts
1497
+ end
1497
1498
 
1498
- puts "module Vips"
1499
- puts " class Image"
1500
- puts ""
1499
+ # Scale an image to uchar. This is the vips `scale` operation, but
1500
+ # renamed to avoid a clash with the `.scale` property.
1501
+ #
1502
+ # @param opts [Hash] Set of options
1503
+ # @return [Vips::Image] Output image
1504
+ def scaleimage **opts
1505
+ Vips::Image.scale self, **opts
1506
+ end
1507
+ end
1508
+ end
1501
1509
 
1502
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1510
+ module Vips
1511
+ # This module generates yard comments for all the dynamically bound
1512
+ # vips operations.
1513
+ #
1514
+ # Regenerate with something like:
1515
+ #
1516
+ # ```
1517
+ # $ ruby > methods.rb
1518
+ # require "vips"; Vips::Yard.generate
1519
+ # ^D
1520
+ # ```
1521
+
1522
+ module Yard
1523
+ # map gobject's type names to Ruby
1524
+ MAP_GO_TO_RUBY = {
1525
+ "gboolean" => "Boolean",
1526
+ "gint" => "Integer",
1527
+ "gdouble" => "Float",
1528
+ "gfloat" => "Float",
1529
+ "gchararray" => "String",
1530
+ "VipsImage" => "Vips::Image",
1531
+ "VipsInterpolate" => "Vips::Interpolate",
1532
+ "VipsConnection" => "Vips::Connection",
1533
+ "VipsSource" => "Vips::Source",
1534
+ "VipsTarget" => "Vips::Target",
1535
+ "VipsSourceCustom" => "Vips::SourceCustom",
1536
+ "VipsTargetCustom" => "Vips::TargetCustom",
1537
+ "VipsArrayDouble" => "Array<Double>",
1538
+ "VipsArrayInt" => "Array<Integer>",
1539
+ "VipsArrayImage" => "Array<Image>",
1540
+ "VipsArrayString" => "Array<String>"
1541
+ }
1542
+
1543
+ # these have hand-written methods, see above
1544
+ NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
1545
+
1546
+ # these are aliased (appear under several names)
1547
+ ALIAS = ["crop"]
1548
+
1549
+ # turn a gtype into a ruby type name
1550
+ def self.gtype_to_ruby gtype
1551
+ fundamental = GObject.g_type_fundamental gtype
1552
+ type_name = GObject.g_type_name gtype
1553
+
1554
+ if MAP_GO_TO_RUBY.include? type_name
1555
+ type_name = MAP_GO_TO_RUBY[type_name]
1556
+ end
1557
+
1558
+ if fundamental == GObject::GFLAGS_TYPE ||
1559
+ fundamental == GObject::GENUM_TYPE
1560
+ type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1561
+ end
1562
+
1563
+ type_name
1564
+ end
1503
1565
 
1504
- puts " end"
1505
- puts "end"
1566
+ def self.generate_operation introspect
1567
+ return if (introspect.flags & OPERATION_DEPRECATED) != 0
1568
+ return if NO_GENERATE.include? introspect.name
1569
+
1570
+ method_args = introspect.method_args
1571
+ required_output = introspect.required_output
1572
+ optional_input = introspect.optional_input
1573
+ optional_output = introspect.optional_output
1574
+
1575
+ print "# @!method "
1576
+ print "self." unless introspect.member_x
1577
+ print "#{introspect.name}("
1578
+ print method_args.map { |x| x[:yard_name] }.join(", ")
1579
+ print ", " if method_args.length > 0
1580
+ puts "**opts)"
1581
+
1582
+ puts "# #{introspect.description.capitalize}."
1583
+
1584
+ method_args.each do |details|
1585
+ yard_name = details[:yard_name]
1586
+ gtype = details[:gtype]
1587
+ blurb = details[:blurb]
1588
+
1589
+ puts "# @param #{yard_name} [#{gtype_to_ruby(gtype)}] #{blurb}"
1590
+ end
1591
+
1592
+ puts "# @param opts [Hash] Set of options"
1593
+ optional_input.each do |arg_name, details|
1594
+ yard_name = details[:yard_name]
1595
+ gtype = details[:gtype]
1596
+ blurb = details[:blurb]
1597
+
1598
+ puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} #{blurb}"
1599
+ end
1600
+ optional_output.each do |arg_name, details|
1601
+ yard_name = details[:yard_name]
1602
+ gtype = details[:gtype]
1603
+ blurb = details[:blurb]
1604
+
1605
+ print "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name}"
1606
+ puts " Output #{blurb}"
1607
+ end
1608
+
1609
+ print "# @return ["
1610
+ if required_output.length == 0
1611
+ print "nil"
1612
+ elsif required_output.length == 1
1613
+ print gtype_to_ruby(required_output.first[:gtype])
1614
+ else
1615
+ print "Array<"
1616
+ print required_output.map { |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1617
+ print ">"
1618
+ end
1619
+ if optional_output.length > 0
1620
+ print ", Hash<Symbol => Object>"
1621
+ end
1622
+ print "] "
1623
+ print required_output.map { |x| x[:blurb] }.join(", ")
1624
+ if optional_output.length > 0
1625
+ print ", " if required_output.length > 0
1626
+ print "Hash of optional output items"
1627
+ end
1628
+ puts ""
1629
+
1630
+ puts ""
1506
1631
  end
1507
1632
 
1633
+ def self.generate
1634
+ alias_gtypes = {}
1635
+ ALIAS.each do |name|
1636
+ gtype = Vips.type_find "VipsOperation", name
1637
+ alias_gtypes[gtype] = name
1638
+ end
1639
+
1640
+ generate_class = lambda do |gtype, _|
1641
+ name = if alias_gtypes.key? gtype
1642
+ alias_gtypes[gtype]
1643
+ else
1644
+ Vips.nickname_find gtype
1645
+ end
1646
+
1647
+ if name
1648
+ begin
1649
+ # can fail for abstract types
1650
+ introspect = Vips::Introspect.get_yard name
1651
+ rescue Vips::Error
1652
+ nil
1653
+ end
1654
+
1655
+ generate_operation(introspect) if introspect
1656
+ end
1657
+
1658
+ Vips.vips_type_map gtype, generate_class, nil
1659
+ end
1660
+
1661
+ puts "module Vips"
1662
+ puts " class Image"
1663
+ puts ""
1664
+
1665
+ generate_class.call(GObject.g_type_from_name("VipsOperation"), nil)
1666
+
1667
+ puts " end"
1668
+ puts "end"
1669
+ end
1670
+ end
1508
1671
  end