vips 8.6.3.2 → 8.8.2

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