vips 8.6.3.2 → 8.8.2

Sign up to get free protection for your applications and to get access to all the features.
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