vips 8.6.3

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