vips 8.7.0.1 → 8.8.0.1

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