ruby-vips 2.0.13 → 2.0.14

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