vips 8.6.3

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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +47 -0
  4. data/.yardopts +2 -0
  5. data/Gemfile +4 -0
  6. data/README.md +64 -0
  7. data/Rakefile +28 -0
  8. data/example/annotate.rb +17 -0
  9. data/example/daltonize8.rb +75 -0
  10. data/example/example1.rb +12 -0
  11. data/example/example2.rb +34 -0
  12. data/example/example3.rb +19 -0
  13. data/example/example4.rb +18 -0
  14. data/example/example5.rb +31 -0
  15. data/example/inheritance_with_refcount.rb +286 -0
  16. data/example/thumb.rb +31 -0
  17. data/example/trim8.rb +41 -0
  18. data/example/watermark.rb +44 -0
  19. data/example/wobble.rb +36 -0
  20. data/ext/Rakefile +35 -0
  21. data/lib/vips.rb +597 -0
  22. data/lib/vips/access.rb +11 -0
  23. data/lib/vips/align.rb +11 -0
  24. data/lib/vips/angle.rb +12 -0
  25. data/lib/vips/angle45.rb +16 -0
  26. data/lib/vips/bandformat.rb +20 -0
  27. data/lib/vips/coding.rb +14 -0
  28. data/lib/vips/compass_direction.rb +17 -0
  29. data/lib/vips/direction.rb +11 -0
  30. data/lib/vips/extend.rb +17 -0
  31. data/lib/vips/gobject.rb +122 -0
  32. data/lib/vips/gvalue.rb +281 -0
  33. data/lib/vips/image.rb +1500 -0
  34. data/lib/vips/interesting.rb +14 -0
  35. data/lib/vips/interpolate.rb +62 -0
  36. data/lib/vips/interpretation.rb +28 -0
  37. data/lib/vips/kernel.rb +22 -0
  38. data/lib/vips/methods.rb +2145 -0
  39. data/lib/vips/object.rb +244 -0
  40. data/lib/vips/operation.rb +365 -0
  41. data/lib/vips/operationboolean.rb +14 -0
  42. data/lib/vips/operationcomplex.rb +12 -0
  43. data/lib/vips/operationcomplex2.rb +10 -0
  44. data/lib/vips/operationcomplexget.rb +11 -0
  45. data/lib/vips/operationmath.rb +18 -0
  46. data/lib/vips/operationmath2.rb +10 -0
  47. data/lib/vips/operationrelational.rb +15 -0
  48. data/lib/vips/operationround.rb +11 -0
  49. data/lib/vips/size.rb +13 -0
  50. data/lib/vips/version.rb +4 -0
  51. data/vips.gemspec +36 -0
  52. metadata +209 -0
@@ -0,0 +1,1500 @@
1
+ # This module provides an interface to the vips image processing library
2
+ # via ruby-ffi.
3
+ #
4
+ # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
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
45
+
46
+ attach_function :vips_image_set,
47
+ [:pointer, :string, GObject::GValue.ptr], :void
48
+ attach_function :vips_image_remove, [:pointer, :string], :void
49
+
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
84
+
85
+ class GenericPtr < FFI::Struct
86
+ layout :value, :pointer
87
+ end
88
+
89
+ # handy for overloads ... want to be able to apply a function to an
90
+ # array or to a scalar
91
+ def self.smap x, &block
92
+ x.is_a?(Array) ? x.map {|y| smap(y, &block)} : block.(x)
93
+ end
94
+
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
99
+
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
104
+
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
110
+
111
+ unless Image::complex? image.format
112
+ if image.bands % 2 != 0
113
+ raise Error, "not an even number of bands"
114
+ end
115
+
116
+ unless Image::float? image.format
117
+ image = image.cast :float
118
+ end
119
+
120
+ new_format = image.format == :double ? :dpcomplex : :complex
121
+ image = image.copy format: new_format, bands: image.bands / 2
122
+ end
123
+
124
+ image = block.(image)
125
+
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
130
+
131
+ image
132
+ end
133
+
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
142
+
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
170
+
171
+ public
172
+
173
+ def inspect
174
+ "#<Image #{width}x#{height} #{format}, #{bands} bands, " +
175
+ "#{interpretation}>"
176
+ end
177
+
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
187
+
188
+ super
189
+ end
190
+
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
194
+
195
+ super
196
+ end
197
+
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
206
+
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
211
+
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
260
+
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
297
+ end
298
+
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
305
+ end
306
+
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
361
+
362
+ # be careful to set them as double
363
+ image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
364
+ image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
365
+
366
+ return image
367
+ end
368
+
369
+ # A new image is created with the same width, height, format,
370
+ # interpretation, resolution and offset as self, but with every pixel
371
+ # set to the specified value.
372
+ #
373
+ # You can pass an array to make a many-band image, or a single value to
374
+ # make a one-band image.
375
+ #
376
+ # @param value [Real, Array<Real>] value to put in each pixel
377
+ # @return [Image] constant image
378
+ def new_from_image value
379
+ pixel = (Vips::Image.black(1, 1) + value).cast(format)
380
+ image = pixel.embed 0, 0, width, height, extend: :copy
381
+ image.copy interpretation: interpretation,
382
+ xres: xres, yres: yres, xoffset: xoffset, yoffset: yoffset
383
+ end
384
+
385
+ # Write this image to a file. Save options may be encoded in the
386
+ # filename or given as a hash. For example:
387
+ #
388
+ # ```
389
+ # image.write_to_file "fred.jpg[Q=90]"
390
+ # ```
391
+ #
392
+ # or equivalently:
393
+ #
394
+ # ```
395
+ # image.write_to_file "fred.jpg", Q: 90
396
+ # ```
397
+ #
398
+ # The full set of save options depend on the selected saver. Try
399
+ # something like:
400
+ #
401
+ # ```
402
+ # $ vips jpegsave
403
+ # ```
404
+ #
405
+ # to see all the available options for JPEG save.
406
+ #
407
+ # @!macro [new] vips.saveopts
408
+ # @param opts [Hash] set of options
409
+ # @option opts [Boolean] :strip (false) Strip all metadata from image
410
+ # @option opts [Array<Float>] :background (0) Background colour to
411
+ # flatten alpha against, if necessary
412
+ #
413
+ # @param name [String] filename to write to
414
+ def write_to_file name, opts = {}
415
+ filename = Vips::vips_filename_get_filename name
416
+ option_string = Vips::vips_filename_get_options name
417
+ saver = Vips::vips_foreign_find_save filename
418
+ if saver == nil
419
+ raise Vips::Error, "No known saver for '#{filename}'."
420
+ end
421
+
422
+ Vips::Operation.call saver, [self, filename], opts, option_string
423
+
424
+ write_gc
425
+ end
426
+
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
459
+
460
+ buffer = Vips::Operation.call saver, [self], opts, option_string
461
+ raise Vips::Error if buffer == nil
462
+
463
+ write_gc
464
+
465
+ return buffer
466
+ end
467
+
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)
477
+
478
+ ptr.get_bytes 0, len[:value]
479
+ end
480
+
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
493
+
494
+ Vips::vips_image_get_typeof self, name
495
+ end
496
+
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
516
+
517
+ gvalue = GObject::GValue.alloc
518
+ result = Vips::vips_image_get self, name, gvalue
519
+ raise Vips::Error if result != 0
520
+
521
+ return gvalue.get
522
+ end
523
+
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
542
+
543
+ return names
544
+ end
545
+
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
568
+
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
587
+
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
594
+
595
+ # compatibility: old name for get
596
+ def get_value name
597
+ get name
598
+ end
599
+
600
+ # compatibility: old name for set
601
+ def set_value name, value
602
+ set name, value
603
+ end
604
+
605
+ # Get image width, in pixels.
606
+ #
607
+ # @return [Integer] image width, in pixels
608
+ def width
609
+ get "width"
610
+ end
611
+
612
+ # Get image height, in pixels.
613
+ #
614
+ # @return [Integer] image height, in pixels
615
+ def height
616
+ get "height"
617
+ end
618
+
619
+ # Get number of image bands.
620
+ #
621
+ # @return [Integer] number of image bands
622
+ def bands
623
+ get "bands"
624
+ end
625
+
626
+ # Get image format.
627
+ #
628
+ # @return [Symbol] image format
629
+ def format
630
+ get "format"
631
+ end
632
+
633
+ # Get image interpretation.
634
+ #
635
+ # @return [Symbol] image interpretation
636
+ def interpretation
637
+ get "interpretation"
638
+ end
639
+
640
+ # Get image coding.
641
+ #
642
+ # @return [Symbol] image coding
643
+ def coding
644
+ get "coding"
645
+ end
646
+
647
+ # Get image filename, if any.
648
+ #
649
+ # @return [String] image filename
650
+ def filename
651
+ get "filename"
652
+ end
653
+
654
+ # Get image xoffset.
655
+ #
656
+ # @return [Integer] image xoffset
657
+ def xoffset
658
+ get "xoffset"
659
+ end
660
+
661
+ # Get image yoffset.
662
+ #
663
+ # @return [Integer] image yoffset
664
+ def yoffset
665
+ get "yoffset"
666
+ end
667
+
668
+ # Get image x resolution.
669
+ #
670
+ # @return [Float] image x resolution
671
+ def xres
672
+ get "xres"
673
+ end
674
+
675
+ # Get image y resolution.
676
+ #
677
+ # @return [Float] image y resolution
678
+ def yres
679
+ get "yres"
680
+ end
681
+
682
+ # Get scale metadata.
683
+ #
684
+ # @return [Float] image scale
685
+ def scale
686
+ return 1 if get_typeof("scale") == 0
687
+
688
+ get "scale"
689
+ end
690
+
691
+ # Get offset metadata.
692
+ #
693
+ # @return [Float] image offset
694
+ def offset
695
+ return 0 if get_typeof("offset") == 0
696
+
697
+ get "offset"
698
+ end
699
+
700
+ # Get the image size.
701
+ #
702
+ # @return [Integer, Integer] image width and height
703
+ def size
704
+ [width, height]
705
+ end
706
+
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
715
+
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
729
+
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
741
+
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
750
+
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
759
+
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
768
+
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
777
+
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
786
+
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
795
+
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
803
+
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
811
+
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
819
+
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
827
+
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
835
+
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
843
+
844
+ # Equivalent to image ^ -1
845
+ #
846
+ # @return [Image] image with bits flipped
847
+ def !
848
+ self ^ -1
849
+ end
850
+
851
+ # Equivalent to image ^ -1
852
+ #
853
+ # @return [Image] image with bits flipped
854
+ def ~
855
+ self ^ -1
856
+ end
857
+
858
+ # @return [Image] image
859
+ def +@
860
+ self
861
+ end
862
+
863
+ # Equivalent to image * -1
864
+ #
865
+ # @return [Image] negative of image
866
+ def -@
867
+ self * -1
868
+ end
869
+
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
877
+
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
886
+
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
894
+
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
903
+
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
916
+
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
929
+
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
944
+
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
978
+
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
985
+
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
992
+
993
+ # Return the nearest integral value.
994
+ #
995
+ # @return [Image] rint of image
996
+ def rint
997
+ round :rint
998
+ end
999
+
1000
+ # AND the bands of an image together
1001
+ #
1002
+ # @return [Image] all bands ANDed together
1003
+ def bandand
1004
+ bandbool :and
1005
+ end
1006
+
1007
+ # OR the bands of an image together
1008
+ #
1009
+ # @return [Image] all bands ORed together
1010
+ def bandor
1011
+ bandbool :or
1012
+ end
1013
+
1014
+ # EOR the bands of an image together
1015
+ #
1016
+ # @return [Image] all bands EORed together
1017
+ def bandeor
1018
+ bandbool :eor
1019
+ end
1020
+
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
1027
+
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
1036
+
1037
+ # if other is just Numeric, we can use bandjoin_const
1038
+ not_all_real = !other.all?{|x| x.is_a? Numeric}
1039
+
1040
+ if not_all_real
1041
+ Vips::Image.bandjoin([self] + other)
1042
+ else
1043
+ bandjoin_const other
1044
+ end
1045
+ end
1046
+
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
1058
+
1059
+ mode = mode.map do |x|
1060
+ GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1061
+ end
1062
+
1063
+ Vips::Image.composite([self] + other, mode, opts)
1064
+ end
1065
+
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
1076
+
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
1087
+
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
1101
+
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
1109
+
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
1116
+
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
1123
+
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
1136
+
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
1149
+
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
1161
+
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
1169
+
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
1176
+
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
1183
+
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
1190
+
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
1197
+
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
1204
+
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
1211
+
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
1218
+
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
1225
+
1226
+ # Return e ** pixel.
1227
+ #
1228
+ # @return [Image] e ** pixel
1229
+ def exp
1230
+ math :exp
1231
+ end
1232
+
1233
+ # Return 10 ** pixel.
1234
+ #
1235
+ # @return [Image] 10 ** pixel
1236
+ def exp10
1237
+ math :exp10
1238
+ end
1239
+
1240
+ # Flip horizontally.
1241
+ #
1242
+ # @return [Image] image flipped horizontally
1243
+ def fliphor
1244
+ flip :horizontal
1245
+ end
1246
+
1247
+ # Flip vertically.
1248
+ #
1249
+ # @return [Image] image flipped vertically
1250
+ def flipver
1251
+ flip :vertical
1252
+ end
1253
+
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
1265
+
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
1277
+
1278
+ # Rotate by 90 degrees clockwise.
1279
+ #
1280
+ # @return [Image] rotated image
1281
+ def rot90
1282
+ rot :d90
1283
+ end
1284
+
1285
+ # Rotate by 180 degrees clockwise.
1286
+ #
1287
+ # @return [Image] rotated image
1288
+ def rot180
1289
+ rot :d180
1290
+ end
1291
+
1292
+ # Rotate by 270 degrees clockwise.
1293
+ #
1294
+ # @return [Image] rotated image
1295
+ def rot270
1296
+ rot :d270
1297
+ end
1298
+
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
1317
+
1318
+ Vips::Operation.call "ifthenelse", [self, th, el], opts
1319
+ end
1320
+
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
1329
+
1330
+ end
1331
+
1332
+ # This method generates yard comments for all the dynamically bound
1333
+ # vips operations.
1334
+ #
1335
+ # Regenerate with something like:
1336
+ #
1337
+ # ```
1338
+ # $ ruby > methods.rb
1339
+ # require 'vips'; Vips::generate_yard
1340
+ # ^D
1341
+ # ```
1342
+
1343
+ def self.generate_yard
1344
+ # these have hand-written methods, see above
1345
+ no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
1346
+
1347
+ # map gobject's type names to Ruby
1348
+ map_go_to_ruby = {
1349
+ "gboolean" => "Boolean",
1350
+ "gint" => "Integer",
1351
+ "gdouble" => "Float",
1352
+ "gfloat" => "Float",
1353
+ "gchararray" => "String",
1354
+ "VipsImage" => "Vips::Image",
1355
+ "VipsInterpolate" => "Vips::Interpolate",
1356
+ "VipsArrayDouble" => "Array<Double>",
1357
+ "VipsArrayInt" => "Array<Integer>",
1358
+ "VipsArrayImage" => "Array<Image>",
1359
+ "VipsArrayString" => "Array<String>",
1360
+ }
1361
+
1362
+ generate_operation = lambda do |gtype, nickname, op|
1363
+ op_flags = op.get_flags
1364
+ return if (op_flags & OPERATION_DEPRECATED) != 0
1365
+ return if no_generate.include? nickname
1366
+ description = Vips::vips_object_get_description op
1367
+
1368
+ # find and classify all the arguments the operator can take
1369
+ required_input = []
1370
+ optional_input = []
1371
+ required_output = []
1372
+ optional_output = []
1373
+ member_x = nil
1374
+ op.argument_map do |pspec, argument_class, argument_instance|
1375
+ arg_flags = argument_class[:flags]
1376
+ next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1377
+ next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1378
+
1379
+ name = pspec[:name].tr("-", "_")
1380
+ # 'in' as a param name confuses yard
1381
+ name = "im" if name == "in"
1382
+ gtype = pspec[:value_type]
1383
+ fundamental = GObject::g_type_fundamental gtype
1384
+ type_name = GObject::g_type_name gtype
1385
+ if map_go_to_ruby.include? type_name
1386
+ type_name = map_go_to_ruby[type_name]
1387
+ end
1388
+ if fundamental == GObject::GFLAGS_TYPE ||
1389
+ fundamental == GObject::GENUM_TYPE
1390
+ type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1391
+ end
1392
+ blurb = GObject::g_param_spec_get_blurb pspec
1393
+ value = {:name => name,
1394
+ :flags => arg_flags,
1395
+ :gtype => gtype,
1396
+ :type_name => type_name,
1397
+ :blurb => blurb}
1398
+
1399
+ if (arg_flags & ARGUMENT_INPUT) != 0
1400
+ if (arg_flags & ARGUMENT_REQUIRED) != 0
1401
+ # note the first required input image, if any ... we
1402
+ # will be a method of this instance
1403
+ if !member_x && gtype == Vips::IMAGE_TYPE
1404
+ member_x = value
1405
+ else
1406
+ required_input << value
1407
+ end
1408
+ else
1409
+ optional_input << value
1410
+ end
1411
+ end
1412
+
1413
+ # MODIFY INPUT args count as OUTPUT as well
1414
+ if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
1415
+ ((arg_flags & ARGUMENT_INPUT) != 0 &&
1416
+ (arg_flags & ARGUMENT_MODIFY) != 0)
1417
+ if (arg_flags & ARGUMENT_REQUIRED) != 0
1418
+ required_output << value
1419
+ else
1420
+ optional_output << value
1421
+ end
1422
+ end
1423
+
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]}] " +
1437
+ "#{arg[:blurb]}"
1438
+ end
1439
+
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]}"
1448
+ end
1449
+
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 ""
1470
+
1471
+ puts ""
1472
+ end
1473
+
1474
+ generate_class = lambda do |gtype, a|
1475
+ nickname = Vips::nickname_find gtype
1476
+
1477
+ if nickname
1478
+ begin
1479
+ # can fail for abstract types
1480
+ op = Vips::Operation.new nickname
1481
+ rescue
1482
+ end
1483
+
1484
+ generate_operation.(gtype, nickname, op) if op
1485
+ end
1486
+
1487
+ Vips::vips_type_map gtype, generate_class, nil
1488
+ end
1489
+
1490
+ puts "module Vips"
1491
+ puts " class Image"
1492
+ puts ""
1493
+
1494
+ generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1495
+
1496
+ puts " end"
1497
+ puts "end"
1498
+ end
1499
+
1500
+ end