vips 8.8.4 → 8.12.1

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.
data/lib/vips/image.rb CHANGED
@@ -4,16 +4,19 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
8
 
9
9
  module Vips
10
10
  private
11
11
 
12
12
  attach_function :vips_image_new_matrix_from_array,
13
- [:int, :int, :pointer, :int], :pointer
13
+ [:int, :int, :pointer, :int], :pointer
14
14
 
15
15
  attach_function :vips_image_copy_memory, [:pointer], :pointer
16
16
 
17
+ attach_function :vips_image_set_progress, [:pointer, :bool], :void
18
+ attach_function :vips_image_set_kill, [:pointer, :bool], :void
19
+
17
20
  attach_function :vips_filename_get_filename, [:string], :pointer
18
21
  attach_function :vips_filename_get_options, [:string], :pointer
19
22
 
@@ -22,30 +25,35 @@ module Vips
22
25
  attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
23
26
  attach_function :vips_foreign_find_save_buffer, [:string], :string
24
27
 
28
+ if Vips.at_least_libvips?(8, 9)
29
+ attach_function :vips_foreign_find_load_source, [:pointer], :string
30
+ attach_function :vips_foreign_find_save_target, [:string], :string
31
+ end
32
+
25
33
  attach_function :vips_image_write_to_memory,
26
- [:pointer, SizeStruct.ptr], :pointer
34
+ [:pointer, SizeStruct.ptr], :pointer
27
35
 
28
36
  attach_function :vips_image_get_typeof, [:pointer, :string], :GType
29
37
  attach_function :vips_image_get,
30
- [:pointer, :string, GObject::GValue.ptr], :int
38
+ [:pointer, :string, GObject::GValue.ptr], :int
39
+
40
+ attach_function :vips_image_get_width, [:pointer], :int
41
+ attach_function :vips_image_get_height, [:pointer], :int
42
+ attach_function :vips_image_get_bands, [:pointer], :int
31
43
 
32
- # vips_image_get_fields was added in libvips 8.5
33
- begin
44
+ if Vips.at_least_libvips?(8, 5)
34
45
  attach_function :vips_image_get_fields, [:pointer], :pointer
35
- rescue FFI::NotFoundError
36
- nil
46
+ attach_function :vips_image_hasalpha, [:pointer], :int
37
47
  end
38
48
 
39
- # vips_addalpha was added in libvips 8.6
40
- if Vips::at_least_libvips?(8, 6)
49
+ if Vips.at_least_libvips?(8, 6)
41
50
  attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
42
51
  end
43
- if Vips::at_least_libvips?(8, 5)
44
- attach_function :vips_image_hasalpha, [:pointer], :int
45
- end
46
52
 
53
+ # move these three lines to mutableimage when we finally remove set and
54
+ # remove in this class
47
55
  attach_function :vips_image_set,
48
- [:pointer, :string, GObject::GValue.ptr], :void
56
+ [:pointer, :string, GObject::GValue.ptr], :void
49
57
  attach_function :vips_image_remove, [:pointer, :string], :void
50
58
 
51
59
  attach_function :vips_band_format_iscomplex, [:int], :int
@@ -53,6 +61,11 @@ module Vips
53
61
 
54
62
  attach_function :nickname_find, :vips_nickname_find, [:GType], :string
55
63
 
64
+ attach_function :vips_image_invalidate_all, [:pointer], :void
65
+
66
+ attach_function :vips_image_new_from_memory, [:pointer, :size_t, :int, :int, :int, :int], :pointer
67
+ attach_function :vips_image_new_from_memory_copy, [:pointer, :size_t, :int, :int, :int, :int], :pointer
68
+
56
69
  # turn a raw pointer that must be freed into a self-freeing Ruby string
57
70
  def self.p2str(pointer)
58
71
  pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
@@ -67,6 +80,15 @@ module Vips
67
80
  class Image < Vips::Object
68
81
  alias_method :parent_get_typeof, :get_typeof
69
82
 
83
+ def close
84
+ Vips.vips_image_invalidate_all(self)
85
+ end
86
+
87
+ # FFI sets a pointer's size to this magic value if the size of the memory
88
+ # chunk the pointer points to is unknown to FFI.
89
+ UNKNOWN_POINTER_SIZE = FFI::Pointer.new(1).size
90
+ private_constant :UNKNOWN_POINTER_SIZE
91
+
70
92
  private
71
93
 
72
94
  # the layout of the VipsImage struct
@@ -94,17 +116,17 @@ module Vips
94
116
  # handy for overloads ... want to be able to apply a function to an
95
117
  # array or to a scalar
96
118
  def self.smap x, &block
97
- x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.(x)
119
+ x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.call(x)
98
120
  end
99
121
 
100
122
  def self.complex? format
101
123
  format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
102
- Vips::vips_band_format_iscomplex(format_number) != 0
124
+ Vips.vips_band_format_iscomplex(format_number) != 0
103
125
  end
104
126
 
105
127
  def self.float? format
106
128
  format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
107
- Vips::vips_band_format_isfloat(format_number) != 0
129
+ Vips.vips_band_format_isfloat(format_number) != 0
108
130
  end
109
131
 
110
132
  # run a complex operation on a complex image, or an image with an even
@@ -113,12 +135,12 @@ module Vips
113
135
  def self.run_cmplx image, &block
114
136
  original_format = image.format
115
137
 
116
- unless Image::complex? image.format
138
+ unless Image.complex? image.format
117
139
  if image.bands % 2 != 0
118
- raise Error, "not an even number of bands"
140
+ raise Vips::Error, "not an even number of bands"
119
141
  end
120
142
 
121
- unless Image::float? image.format
143
+ unless Image.float? image.format
122
144
  image = image.cast :float
123
145
  end
124
146
 
@@ -126,9 +148,9 @@ module Vips
126
148
  image = image.copy format: new_format, bands: image.bands / 2
127
149
  end
128
150
 
129
- image = block.(image)
151
+ image = block.call(image)
130
152
 
131
- unless Image::complex? original_format
153
+ unless Image.complex? original_format
132
154
  new_format = image.format == :dpcomplex ? :double : :float
133
155
  image = image.copy format: new_format, bands: image.bands * 2
134
156
  end
@@ -145,34 +167,6 @@ module Vips
145
167
  end
146
168
  end
147
169
 
148
- # Write can fail due to no file descriptors and memory can fill if
149
- # large objects are not collected fairly soon. We can't try a
150
- # write and GC and retry on fail, since the write may take a
151
- # long time and may not be repeatable.
152
- #
153
- # GCing before every write would have a horrible effect on
154
- # performance, so as a compromise we GC every @@gc_interval writes.
155
- #
156
- # ruby2.1 introduced a generational GC which is fast enough to be
157
- # able to GC on every write.
158
-
159
- @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
160
-
161
- @@gc_interval = 100
162
- @@gc_countdown = @@gc_interval
163
-
164
- def write_gc
165
- if @@generational_gc
166
- GC.start full_mark: false
167
- else
168
- @@gc_countdown -= 1
169
- if @@gc_countdown < 0
170
- @@gc_countdown = @@gc_interval
171
- GC.start
172
- end
173
- end
174
- end
175
-
176
170
  public
177
171
 
178
172
  def inspect
@@ -187,15 +181,19 @@ module Vips
187
181
  # 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
188
182
  return false if name == :to_hash
189
183
 
184
+ super
185
+ end
186
+
187
+ def respond_to_missing? name, include_all = false
190
188
  # respond to all vips operations by nickname
191
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
189
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
192
190
 
193
191
  super
194
192
  end
195
193
 
196
- def self.respond_to? name, include_all = false
194
+ def self.respond_to_missing? name, include_all = false
197
195
  # respond to all vips operations by nickname
198
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
196
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
199
197
 
200
198
  super
201
199
  end
@@ -219,13 +217,13 @@ module Vips
219
217
  # load options, for example:
220
218
  #
221
219
  # ```
222
- # image = Vips::new_from_file "fred.jpg[shrink=2]"
220
+ # image = Vips::Image.new_from_file "fred.jpg[shrink=2]"
223
221
  # ```
224
222
  #
225
223
  # You can also supply options as a hash, for example:
226
224
  #
227
225
  # ```
228
- # image = Vips::new_from_file "fred.jpg", shrink: 2
226
+ # image = Vips::Image.new_from_file "fred.jpg", shrink: 2
229
227
  # ```
230
228
  #
231
229
  # The full set of options available depend upon the load operation that
@@ -253,18 +251,18 @@ module Vips
253
251
  def self.new_from_file name, **opts
254
252
  # very common, and Vips::vips_filename_get_filename will segv if we
255
253
  # pass this
256
- raise Vips::Error, "filename is nil" if name == nil
254
+ raise Vips::Error, "filename is nil" if name.nil?
257
255
 
258
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
259
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
260
- loader = Vips::vips_foreign_find_load filename
261
- raise Vips::Error if loader == nil
256
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
257
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
258
+ loader = Vips.vips_foreign_find_load filename
259
+ raise Vips::Error if loader.nil?
262
260
 
263
261
  Operation.call loader, [filename], opts, option_string
264
262
  end
265
263
 
266
- # Create a new {Image} for an image encoded, in a format such as
267
- # JPEG, in a binary string. Load options may be passed as
264
+ # Create a new {Image} for an image encoded in a format such as
265
+ # JPEG in a binary string. Load options may be passed as
268
266
  # strings or appended as a hash. For example:
269
267
  #
270
268
  # ```
@@ -295,17 +293,157 @@ module Vips
295
293
  # @macro vips.loadopts
296
294
  # @return [Image] the loaded image
297
295
  def self.new_from_buffer data, option_string, **opts
298
- loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
299
- raise Vips::Error if loader == nil
296
+ loader = Vips.vips_foreign_find_load_buffer data, data.bytesize
297
+ raise Vips::Error if loader.nil?
300
298
 
301
299
  Vips::Operation.call loader, [data], opts, option_string
302
300
  end
303
301
 
302
+ # Create a new {Image} from a C-style array held in memory. For example:
303
+ #
304
+ # ```
305
+ # image = Vips::Image.black(16, 16) + 128
306
+ # data = image.write_to_memory
307
+ #
308
+ # x = Vips::Image.new_from_memory data,
309
+ # image.width, image.height, image.bands, image.format
310
+ # ```
311
+ #
312
+ # Creating a new image from a memory pointer:
313
+ #
314
+ # ```
315
+ # ptr = FFI::MemoryPointer.new(:uchar, 10*10)
316
+ # # => #<FFI::MemoryPointer address=0x00007fc236db31d0 size=100>
317
+ # x = Vips::Image.new_from_memory(ptr, 10, 10, 1, :uchar)
318
+ # ```
319
+ #
320
+ # Creating a new image from an address only pointer:
321
+ #
322
+ # ```
323
+ # ptr = call_to_external_c_library(w: 10, h: 10)
324
+ # # => #<FFI::Pointer address=0x00007f9780813a00>
325
+ # ptr_slice = ptr.slice(0, 10*10)
326
+ # # => #<FFI::Pointer address=0x00007f9780813a00 size=100>
327
+ # x = Vips::Image.new_from_memory(ptr_slice, 10, 10, 1, :uchar)
328
+ # ```
329
+ #
330
+ # {new_from_memory} keeps a reference to the array of pixels you pass in
331
+ # to try to prevent that memory from being freed by the Ruby GC while it
332
+ # is being used.
333
+ #
334
+ # See {new_from_memory_copy} for a version of this method which does not
335
+ # keep a reference.
336
+ #
337
+ # @param data [String, FFI::Pointer] the data to load from
338
+ # @param width [Integer] width in pixels
339
+ # @param height [Integer] height in pixels
340
+ # @param bands [Integer] number of bands
341
+ # @param format [Symbol] band format
342
+ # @return [Image] the loaded image
343
+ def self.new_from_memory data, width, height, bands, format
344
+ # prevent data from being freed with JRuby FFI
345
+ if defined?(JRUBY_VERSION) && !data.is_a?(FFI::Pointer)
346
+ data = ::FFI::MemoryPointer.new(:char, data.bytesize).write_bytes data
347
+ end
348
+
349
+ if data.is_a?(FFI::Pointer)
350
+ # A pointer needs to know about the size of the memory it points to.
351
+ # If you have an address-only pointer, use the .slice method to wrap
352
+ # the pointer in a size aware pointer.
353
+ if data.size == UNKNOWN_POINTER_SIZE
354
+ raise Vips::Error, "size of memory is unknown"
355
+ end
356
+ size = data.size
357
+ else
358
+ size = data.bytesize
359
+ end
360
+
361
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
362
+ vi = Vips.vips_image_new_from_memory data, size,
363
+ width, height, bands, format_number
364
+ raise Vips::Error if vi.null?
365
+ image = new(vi)
366
+
367
+ # keep a secret ref to the underlying object .. this reference will be
368
+ # inherited by things that in turn depend on us, so the memory we are
369
+ # using will not be freed
370
+ image.references << data
371
+
372
+ image
373
+ end
374
+
375
+ # Create a new {Image} from memory and copies the memory area. See
376
+ # {new_from_memory} for a version of this method which does not copy the
377
+ # memory area.
378
+ #
379
+ # @param data [String, FFI::Pointer] the data to load from
380
+ # @param width [Integer] width in pixels
381
+ # @param height [Integer] height in pixels
382
+ # @param bands [Integer] number of bands
383
+ # @param format [Symbol] band format
384
+ # @return [Image] the loaded image
385
+ def self.new_from_memory_copy data, width, height, bands, format
386
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
387
+
388
+ if data.is_a?(FFI::Pointer)
389
+ if data.size == UNKNOWN_POINTER_SIZE
390
+ raise Vips::Error, "size of memory is unknown"
391
+ end
392
+ size = data.size
393
+ else
394
+ size = data.bytesize
395
+ end
396
+
397
+ vi = Vips.vips_image_new_from_memory_copy data, size,
398
+ width, height, bands, format_number
399
+ raise Vips::Error if vi.null?
400
+ new(vi)
401
+ end
402
+
403
+ # Create a new {Image} from a source. Load options may be passed as
404
+ # strings or appended as a hash. For example:
405
+ #
406
+ # ```
407
+ # source = Vips::Source.new_from_file("k2.jpg")
408
+ # image = Vips::Image.new_from_source source, "shrink=2"
409
+ # ```
410
+ #
411
+ # or alternatively:
412
+ #
413
+ # ```
414
+ # image = Vips::Image.new_from_source source, "", shrink: 2
415
+ # ```
416
+ #
417
+ # The options available depend on the file format. Try something like:
418
+ #
419
+ # ```
420
+ # $ vips jpegload_source
421
+ # ```
422
+ #
423
+ # at the command-line to see the available options. Not all loaders
424
+ # support load from source, but at least JPEG, PNG and
425
+ # TIFF images will work.
426
+ #
427
+ # Loading is fast: only enough data is read to be able to fill
428
+ # out the header. Pixels will only be read and decompressed when they are
429
+ # needed.
430
+ #
431
+ # @param source [Vips::Source] the source to load from
432
+ # @param option_string [String] load options as a string
433
+ # @macro vips.loadopts
434
+ # @return [Image] the loaded image
435
+ def self.new_from_source source, option_string, **opts
436
+ loader = Vips.vips_foreign_find_load_source source
437
+ raise Vips::Error if loader.nil?
438
+
439
+ Vips::Operation.call loader, [source], opts, option_string
440
+ end
441
+
304
442
  def self.matrix_from_array width, height, array
305
443
  ptr = FFI::MemoryPointer.new :double, array.length
306
444
  ptr.write_array_of_double array
307
- image = Vips::vips_image_new_matrix_from_array width, height,
308
- ptr, array.length
445
+ image = Vips.vips_image_new_matrix_from_array width, height,
446
+ ptr, array.length
309
447
  Vips::Image.new image
310
448
  end
311
449
 
@@ -317,13 +455,13 @@ module Vips
317
455
  # For example:
318
456
  #
319
457
  # ```
320
- # image = Vips::new_from_array [1, 2, 3]
458
+ # image = Vips::Image.new_from_array [1, 2, 3]
321
459
  # ```
322
460
  #
323
461
  # or
324
462
  #
325
463
  # ```
326
- # image = Vips::new_from_array [
464
+ # image = Vips::Image.new_from_array [
327
465
  # [-1, -1, -1],
328
466
  # [-1, 16, -1],
329
467
  # [-1, -1, -1]], 8
@@ -358,18 +496,22 @@ module Vips
358
496
  width = array.length
359
497
  end
360
498
 
499
+ unless array.length == width * height
500
+ raise Vips::Error, "Bad array dimensions."
501
+ end
502
+
361
503
  unless array.all? { |x| x.is_a? Numeric }
362
504
  raise Vips::Error, "Not all array elements are Numeric."
363
505
  end
364
506
 
365
507
  image = Vips::Image.matrix_from_array width, height, array
366
- raise Vips::Error if image == nil
508
+ raise Vips::Error if image.nil?
367
509
 
368
- # be careful to set them as double
369
- image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
370
- image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
371
-
372
- return image
510
+ image.mutate do |mutable|
511
+ # be careful to set them as double
512
+ mutable.set_type! GObject::GDOUBLE_TYPE, "scale", scale.to_f
513
+ mutable.set_type! GObject::GDOUBLE_TYPE, "offset", offset.to_f
514
+ end
373
515
  end
374
516
 
375
517
  # A new image is created with the same width, height, format,
@@ -418,16 +560,16 @@ module Vips
418
560
  #
419
561
  # @param name [String] filename to write to
420
562
  def write_to_file name, **opts
421
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
422
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
423
- saver = Vips::vips_foreign_find_save filename
424
- if saver == nil
425
- raise Vips::Error, "No known saver for '#{filename}'."
426
- end
563
+ raise Vips::Error, "filename is nil" if name.nil?
564
+
565
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
566
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
567
+ saver = Vips.vips_foreign_find_save filename
568
+ raise Vips::Error if saver.nil?
427
569
 
428
570
  Vips::Operation.call saver, [self, filename], opts, option_string
429
571
 
430
- write_gc
572
+ GC.start(full_mark: false)
431
573
  end
432
574
 
433
575
  # Write this image to a memory buffer. Save options may be encoded in
@@ -456,33 +598,112 @@ module Vips
456
598
  # @macro vips.saveopts
457
599
  # @return [String] the image saved in the specified format
458
600
  def write_to_buffer format_string, **opts
459
- filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
460
- option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
461
- saver = Vips::vips_foreign_find_save_buffer filename
462
- if saver == nil
463
- raise Vips::Error, "No known saver for '#{filename}'."
601
+ raise Vips::Error, "filename is nil" if format_string.nil?
602
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
603
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
604
+
605
+ # try to save with the new target API first, only fall back to the old
606
+ # buffer API if there's no target save for this filetype
607
+ saver = nil
608
+ if Vips.at_least_libvips?(8, 9)
609
+ Vips.vips_error_freeze
610
+ saver = Vips.vips_foreign_find_save_target filename
611
+ Vips.vips_error_thaw
464
612
  end
465
613
 
466
- buffer = Vips::Operation.call saver, [self], opts, option_string
467
- raise Vips::Error if buffer == nil
614
+ if !saver.nil?
615
+ target = Vips::Target.new_to_memory
616
+ Vips::Operation.call saver, [self, target], opts, option_string
617
+ buffer = target.get("blob")
618
+ else
619
+ saver = Vips.vips_foreign_find_save_buffer filename
620
+ raise Vips::Error if saver.nil?
621
+
622
+ buffer = Vips::Operation.call saver, [self], opts, option_string
623
+ raise Vips::Error if buffer.nil?
624
+ end
468
625
 
469
- write_gc
626
+ GC.start(full_mark: false)
470
627
 
471
628
  return buffer
472
629
  end
473
630
 
631
+ # Write this image to a target. Save options may be encoded in
632
+ # the format_string or given as a hash. For example:
633
+ #
634
+ # ```ruby
635
+ # target = Vips::Target.new_to_file "k2.jpg"
636
+ # image.write_to_target target, ".jpg[Q=90]"
637
+ # ```
638
+ #
639
+ # or equivalently:
640
+ #
641
+ # ```ruby
642
+ # image.write_to_target target, ".jpg", Q: 90
643
+ # ```
644
+ #
645
+ # The full set of save options depend on the selected saver. Try
646
+ # something like:
647
+ #
648
+ # ```
649
+ # $ vips jpegsave_target
650
+ # ```
651
+ #
652
+ # to see all the available options for JPEG save.
653
+ #
654
+ # @param target [Vips::Target] the target to write to
655
+ # @param format_string [String] save format plus string options
656
+ # @macro vips.saveopts
657
+ def write_to_target target, format_string, **opts
658
+ raise Vips::Error, "filename is nil" if format_string.nil?
659
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
660
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
661
+ saver = Vips.vips_foreign_find_save_target filename
662
+ raise Vips::Error if saver.nil?
663
+
664
+ Vips::Operation.call saver, [self, target], opts, option_string
665
+
666
+ GC.start(full_mark: false)
667
+ end
668
+
474
669
  # Write this image to a large memory buffer.
475
670
  #
476
671
  # @return [String] the pixels as a huge binary string
477
672
  def write_to_memory
478
673
  len = Vips::SizeStruct.new
479
- ptr = Vips::vips_image_write_to_memory self, len
480
- raise Vips::Error if ptr == nil
674
+ ptr = Vips.vips_image_write_to_memory self, len
675
+ raise Vips::Error if ptr.nil?
481
676
 
482
677
  # wrap up as an autopointer
483
678
  ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
484
679
 
485
- ptr.get_bytes 0, len[:value]
680
+ data = ptr.get_bytes 0, len[:value]
681
+
682
+ GC.start(full_mark: false)
683
+
684
+ return data
685
+ end
686
+
687
+ # Turn progress signalling on and off.
688
+ #
689
+ # If this is on, the most-downstream image from this image will issue
690
+ # progress signals.
691
+ #
692
+ # @see Object#signal_connect
693
+ # @param state [Boolean] progress signalling state
694
+ def set_progress state
695
+ Vips.vips_image_set_progress self, state
696
+ end
697
+
698
+ # Kill computation of this time.
699
+ #
700
+ # Set true to stop computation of this image. You can call this from a
701
+ # progress handler, for example.
702
+ #
703
+ # @see Object#signal_connect
704
+ # @param kill [Boolean] stop computation
705
+ def set_kill kill
706
+ Vips.vips_image_set_kill self, kill
486
707
  end
487
708
 
488
709
  # Get the `GType` of a metadata field. The result is 0 if no such field
@@ -494,12 +715,12 @@ module Vips
494
715
  def get_typeof name
495
716
  # on libvips before 8.5, property types must be searched first,
496
717
  # since vips_image_get_typeof returned built-in enums as int
497
- unless Vips::at_least_libvips?(8, 5)
718
+ unless Vips.at_least_libvips?(8, 5)
498
719
  gtype = parent_get_typeof name
499
720
  return gtype if gtype != 0
500
721
  end
501
722
 
502
- Vips::vips_image_get_typeof self, name
723
+ Vips.vips_image_get_typeof self, name
503
724
  end
504
725
 
505
726
  # Get a metadata item from an image. Ruby types are constructed
@@ -518,15 +739,16 @@ module Vips
518
739
  def get name
519
740
  # with old libvips, we must fetch properties (as opposed to
520
741
  # metadata) via VipsObject
521
- unless Vips::at_least_libvips?(8, 5)
742
+ unless Vips.at_least_libvips?(8, 5)
522
743
  return super if parent_get_typeof(name) != 0
523
744
  end
524
745
 
525
746
  gvalue = GObject::GValue.alloc
526
- result = Vips::vips_image_get self, name, gvalue
527
- raise Vips::Error if result != 0
747
+ raise Vips::Error if Vips.vips_image_get(self, name, gvalue) != 0
748
+ result = gvalue.get
749
+ gvalue.unset
528
750
 
529
- gvalue.get
751
+ result
530
752
  end
531
753
 
532
754
  # Get the names of all fields on an image. Use this to loop over all
@@ -537,66 +759,64 @@ module Vips
537
759
  # vips_image_get_fields() was added in libvips 8.5
538
760
  return [] unless Vips.respond_to? :vips_image_get_fields
539
761
 
540
- array = Vips::vips_image_get_fields self
762
+ array = Vips.vips_image_get_fields self
541
763
 
542
764
  names = []
543
765
  p = array
544
766
  until (q = p.read_pointer).null?
545
767
  names << q.read_string
546
- GLib::g_free q
768
+ GLib.g_free q
547
769
  p += FFI::Type::POINTER.size
548
770
  end
549
- GLib::g_free array
771
+ GLib.g_free array
550
772
 
551
773
  names
552
774
  end
553
775
 
554
- # Create a metadata item on an image of the specifed type. Ruby types
555
- # are automatically transformed into the matching `GType`, if possible.
776
+ # Mutate an image with a block. Inside the block, you can call methods
777
+ # which modify the image, such as setting or removing metadata, or
778
+ # modifying pixels.
556
779
  #
557
- # For example, you can use this to set an image's ICC profile:
780
+ # For example:
558
781
  #
559
- # ```
560
- # x = y.set_type Vips::BLOB_TYPE, "icc-profile-data", profile
782
+ # ```ruby
783
+ # image = image.mutate do |x|
784
+ # (0 ... 1).step(0.01) do |i|
785
+ # x.draw_line! 255, x.width * i, 0, 0, x.height * (1 - i)
786
+ # end
787
+ # end
561
788
  # ```
562
789
  #
563
- # where `profile` is an ICC profile held as a binary string object.
790
+ # See {MutableImage}.
791
+ def mutate
792
+ mutable = Vips::MutableImage.new self
793
+ yield mutable
794
+ mutable.image
795
+ end
796
+
797
+ # This method is deprecated.
564
798
  #
565
- # @see set
566
- # @param gtype [Integer] GType of item
567
- # @param name [String] Metadata field to set
568
- # @param value [Object] Value to set
799
+ # Please use {MutableImage#set_type!} instead.
569
800
  def set_type gtype, name, value
570
801
  gvalue = GObject::GValue.alloc
571
802
  gvalue.init gtype
572
803
  gvalue.set value
573
- Vips::vips_image_set self, name, gvalue
804
+ Vips.vips_image_set self, name, gvalue
805
+ gvalue.unset
574
806
  end
575
807
 
576
- # Set the value of a metadata item on an image. The metadata item must
577
- # already exist. Ruby types are automatically transformed into the
578
- # matching `GValue`, if possible.
579
- #
580
- # For example, you can use this to set an image's ICC profile:
581
- #
582
- # ```
583
- # x = y.set "icc-profile-data", profile
584
- # ```
585
- #
586
- # where `profile` is an ICC profile held as a binary string object.
808
+ # This method is deprecated.
587
809
  #
588
- # @see set_type
589
- # @param name [String] Metadata field to set
590
- # @param value [Object] Value to set
810
+ # Please use {MutableImage#set!} instead.
591
811
  def set name, value
592
812
  set_type get_typeof(name), name, value
593
813
  end
594
814
 
595
- # Remove a metadata item from an image.
815
+ # This method is deprecated.
596
816
  #
597
- # @param name [String] Metadata field to remove
817
+ # Please use {MutableImage#remove!} instead.
598
818
  def remove name
599
- Vips::vips_image_remove self, name
819
+ Vips.vips_image_remove self, name
600
820
  end
601
821
 
602
822
  # compatibility: old name for get
@@ -604,7 +824,9 @@ module Vips
604
824
  get name
605
825
  end
606
826
 
607
- # compatibility: old name for set
827
+ # This method is deprecated.
828
+ #
829
+ # Please use {MutableImage#set!} instead.
608
830
  def set_value name, value
609
831
  set name, value
610
832
  end
@@ -613,21 +835,21 @@ module Vips
613
835
  #
614
836
  # @return [Integer] image width, in pixels
615
837
  def width
616
- get "width"
838
+ Vips.vips_image_get_width self
617
839
  end
618
840
 
619
841
  # Get image height, in pixels.
620
842
  #
621
843
  # @return [Integer] image height, in pixels
622
844
  def height
623
- get "height"
845
+ Vips.vips_image_get_height self
624
846
  end
625
847
 
626
848
  # Get number of image bands.
627
849
  #
628
850
  # @return [Integer] number of image bands
629
851
  def bands
630
- get "bands"
852
+ Vips.vips_image_get_bands self
631
853
  end
632
854
 
633
855
  # Get image format.
@@ -711,23 +933,23 @@ module Vips
711
933
  [width, height]
712
934
  end
713
935
 
714
- if Vips::at_least_libvips?(8, 5)
936
+ if Vips.at_least_libvips?(8, 5)
715
937
  # Detect if image has an alpha channel
716
938
  #
717
939
  # @return [Boolean] true if image has an alpha channel.
718
940
  def has_alpha?
719
- return Vips::vips_image_hasalpha(self) != 0
941
+ Vips.vips_image_hasalpha(self) != 0
720
942
  end
721
943
  end
722
944
 
723
945
  # vips_addalpha was added in libvips 8.6
724
- if Vips::at_least_libvips?(8, 6)
946
+ if Vips.at_least_libvips?(8, 6)
725
947
  # Append an alpha channel to an image.
726
948
  #
727
949
  # @return [Image] new image
728
950
  def add_alpha
729
951
  ptr = GenericPtr.new
730
- result = Vips::vips_addalpha self, ptr
952
+ result = Vips.vips_addalpha self, ptr
731
953
  raise Vips::Error if result != 0
732
954
 
733
955
  Vips::Image.new ptr[:value]
@@ -742,7 +964,7 @@ module Vips
742
964
  #
743
965
  # @return [Image] new memory image
744
966
  def copy_memory
745
- new_image = Vips::vips_image_copy_memory self
967
+ new_image = Vips.vips_image_copy_memory self
746
968
  Vips::Image.new new_image
747
969
  end
748
970
 
@@ -752,7 +974,7 @@ module Vips
752
974
  #
753
975
  # @return [Image] modified image
754
976
  def draw_point ink, left, top, **opts
755
- draw_rect ink, left, top, 1, 1, opts
977
+ draw_rect ink, left, top, 1, 1, **opts
756
978
  end
757
979
 
758
980
  # Add an image, constant or array.
@@ -770,7 +992,7 @@ module Vips
770
992
  # @return [Image] result of subtraction
771
993
  def - other
772
994
  other.is_a?(Vips::Image) ?
773
- subtract(other) : linear(1, Image::smap(other) { |x| x * -1 })
995
+ subtract(other) : linear(1, Image.smap(other) { |x| x * -1 })
774
996
  end
775
997
 
776
998
  # Multiply an image, constant or array.
@@ -788,7 +1010,7 @@ module Vips
788
1010
  # @return [Image] result of division
789
1011
  def / other
790
1012
  other.is_a?(Vips::Image) ?
791
- divide(other) : linear(Image::smap(other) { |x| 1.0 / x }, 0)
1013
+ divide(other) : linear(Image.smap(other) { |x| 1.0 / x }, 0)
792
1014
  end
793
1015
 
794
1016
  # Remainder after integer division with an image, constant or array.
@@ -914,7 +1136,7 @@ module Vips
914
1136
  # @return [Image] result of equality
915
1137
  def == other
916
1138
  # for equality, we must allow tests against nil
917
- if other == nil
1139
+ if other.nil?
918
1140
  false
919
1141
  else
920
1142
  call_enum "relational", other, :equal
@@ -927,7 +1149,7 @@ module Vips
927
1149
  # @return [Image] result of inequality
928
1150
  def != other
929
1151
  # for equality, we must allow tests against nil
930
- if other == nil
1152
+ if other.nil?
931
1153
  true
932
1154
  else
933
1155
  call_enum "relational", other, :noteq
@@ -949,38 +1171,40 @@ module Vips
949
1171
  end
950
1172
  end
951
1173
 
952
- # Convert to an Array. This will be slow for large images.
1174
+ # Convert to an Enumerator. Similar to `#to_a` but lazier.
953
1175
  #
954
- # @return [Array] array of Fixnum
955
- def to_a
956
- # we render the image to a big string, then unpack
957
- # as a Ruby array of the correct type
958
- memory = write_to_memory
959
-
1176
+ # @return [Enumerator] Enumerator of Enumerators of Arrays of Numerics
1177
+ def to_enum
960
1178
  # make the template for unpack
961
1179
  template = {
962
- char: 'c',
963
- uchar: 'C',
964
- short: 's_',
965
- ushort: 'S_',
966
- int: 'i_',
967
- uint: 'I_',
968
- float: 'f',
969
- double: 'd',
970
- complex: 'f',
971
- dpcomplex: 'd'
972
- }[format] + '*'
1180
+ char: "c",
1181
+ uchar: "C",
1182
+ short: "s_",
1183
+ ushort: "S_",
1184
+ int: "i_",
1185
+ uint: "I_",
1186
+ float: "f",
1187
+ double: "d",
1188
+ complex: "f",
1189
+ dpcomplex: "d"
1190
+ }[format] + "*"
973
1191
 
974
- # and unpack into something like [1, 2, 3, 4 ..]
975
- array = memory.unpack(template)
1192
+ # we render the image to a big string, then unpack into
1193
+ # one-dimensional array as a Ruby array of the correct type
1194
+ array = write_to_memory.unpack template
976
1195
 
977
- # gather band elements together
978
- pixel_array = array.each_slice(bands).to_a
1196
+ # gather bands of a pixel together
1197
+ pixel_array = array.each_slice bands
979
1198
 
980
- # build rows
981
- row_array = pixel_array.each_slice(width).to_a
1199
+ # gather pixels of a row together
1200
+ pixel_array.each_slice width
1201
+ end
982
1202
 
983
- return row_array
1203
+ # Convert to an Array. This will be slow for large images.
1204
+ #
1205
+ # @return [Array] Array of Arrays of Arrays of Numerics
1206
+ def to_a
1207
+ to_enum.to_a
984
1208
  end
985
1209
 
986
1210
  # Return the largest integral value not greater than the argument.
@@ -1056,10 +1280,13 @@ module Vips
1056
1280
  # @param overlay [Image, Array<Image>] images to composite
1057
1281
  # @param mode [BlendMode, Array<BlendMode>] blend modes to use
1058
1282
  # @param opts [Hash] Set of options
1059
- # @option opts [Vips::Interpretation] :compositing_space Composite images in this colour space
1283
+ # @option opts [Array<Integer>] :x x positions of overlay
1284
+ # @option opts [Array<Integer>] :y y positions of overlay
1285
+ # @option opts [Vips::Interpretation] :compositing_space Composite images
1286
+ # in this colour space
1060
1287
  # @option opts [Boolean] :premultiplied Images have premultiplied alpha
1061
1288
  # @return [Image] blended image
1062
- def composite overlay, mode, **opts
1289
+ def composite overlay, mode, **options
1063
1290
  unless overlay.is_a? Array
1064
1291
  overlay = [overlay]
1065
1292
  end
@@ -1071,7 +1298,7 @@ module Vips
1071
1298
  GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1072
1299
  end
1073
1300
 
1074
- Vips::Image.composite([self] + overlay, mode, opts)
1301
+ Vips::Image.composite([self] + overlay, mode, **options)
1075
1302
  end
1076
1303
 
1077
1304
  # Return the coordinates of the image maximum.
@@ -1080,9 +1307,9 @@ module Vips
1080
1307
  # coordinate of maximum
1081
1308
  def maxpos
1082
1309
  v, opts = max x: true, y: true
1083
- x = opts['x']
1084
- y = opts['y']
1085
- return v, x, y
1310
+ x = opts["x"]
1311
+ y = opts["y"]
1312
+ [v, x, y]
1086
1313
  end
1087
1314
 
1088
1315
  # Return the coordinates of the image minimum.
@@ -1091,9 +1318,9 @@ module Vips
1091
1318
  # coordinate of minimum
1092
1319
  def minpos
1093
1320
  v, opts = min x: true, y: true
1094
- x = opts['x']
1095
- y = opts['y']
1096
- return v, x, y
1321
+ x = opts["x"]
1322
+ y = opts["y"]
1323
+ [v, x, y]
1097
1324
  end
1098
1325
 
1099
1326
  # a median filter
@@ -1101,7 +1328,7 @@ module Vips
1101
1328
  # @param size [Integer] size of filter window
1102
1329
  # @return [Image] result of median filter
1103
1330
  def median size = 3
1104
- rank size, size, (size * size) / 2
1331
+ rank size, size, size**2 / 2
1105
1332
  end
1106
1333
 
1107
1334
  # Return the real part of a complex image.
@@ -1128,7 +1355,7 @@ module Vips
1128
1355
  # @see xyz
1129
1356
  # @return [Image] image converted to polar coordinates
1130
1357
  def polar
1131
- Image::run_cmplx(self) { |x| x.complex :polar }
1358
+ Image.run_cmplx(self) { |x| x.complex :polar }
1132
1359
  end
1133
1360
 
1134
1361
  # Return an image with polar pixels converted to rectangular.
@@ -1141,7 +1368,7 @@ module Vips
1141
1368
  # @see xyz
1142
1369
  # @return [Image] image converted to rectangular coordinates
1143
1370
  def rect
1144
- Image::run_cmplx(self) { |x| x.complex :rect }
1371
+ Image.run_cmplx(self) { |x| x.complex :rect }
1145
1372
  end
1146
1373
 
1147
1374
  # Return the complex conjugate of an image.
@@ -1153,7 +1380,7 @@ module Vips
1153
1380
  #
1154
1381
  # @return [Image] complex conjugate
1155
1382
  def conj
1156
- Image::run_cmplx(self) { |x| x.complex :conj }
1383
+ Image.run_cmplx(self) { |x| x.complex :conj }
1157
1384
  end
1158
1385
 
1159
1386
  # Calculate the cross phase of two images.
@@ -1206,6 +1433,48 @@ module Vips
1206
1433
  math :atan
1207
1434
  end
1208
1435
 
1436
+ # Return the hyperbolic sine of an image in radians.
1437
+ #
1438
+ # @return [Image] sine of each pixel
1439
+ def sinh
1440
+ math :sinh
1441
+ end
1442
+
1443
+ # Return the hyperbolic cosine of an image in radians.
1444
+ #
1445
+ # @return [Image] cosine of each pixel
1446
+ def cosh
1447
+ math :cosh
1448
+ end
1449
+
1450
+ # Return the hyperbolic tangent of an image in radians.
1451
+ #
1452
+ # @return [Image] tangent of each pixel
1453
+ def tanh
1454
+ math :tanh
1455
+ end
1456
+
1457
+ # Return the inverse hyperbolic sine of an image in radians.
1458
+ #
1459
+ # @return [Image] inverse sine of each pixel
1460
+ def asinh
1461
+ math :asinh
1462
+ end
1463
+
1464
+ # Return the inverse hyperbolic cosine of an image in radians.
1465
+ #
1466
+ # @return [Image] inverse cosine of each pixel
1467
+ def acosh
1468
+ math :acosh
1469
+ end
1470
+
1471
+ # Return the inverse hyperbolic tangent of an image in radians.
1472
+ #
1473
+ # @return [Image] inverse tangent of each pixel
1474
+ def atanh
1475
+ math :atanh
1476
+ end
1477
+
1209
1478
  # Return the natural log of an image.
1210
1479
  #
1211
1480
  # @return [Image] natural log of each pixel
@@ -1320,30 +1589,27 @@ module Vips
1320
1589
  #
1321
1590
  # @param opts [Hash] Set of options
1322
1591
  # @return [Vips::Image] Output image
1323
- def scaleimage **opts
1324
- Vips::Image.scale self, opts
1592
+ def scaleimage **options
1593
+ Vips::Image.scale self, **options
1325
1594
  end
1326
1595
  end
1327
1596
  end
1328
1597
 
1329
1598
  module Vips
1330
- # This method generates yard comments for all the dynamically bound
1599
+ # This module generates yard comments for all the dynamically bound
1331
1600
  # vips operations.
1332
1601
  #
1333
1602
  # Regenerate with something like:
1334
1603
  #
1335
1604
  # ```
1336
1605
  # $ ruby > methods.rb
1337
- # require 'vips'; Vips::generate_yard
1606
+ # require "vips"; Vips::Yard.generate
1338
1607
  # ^D
1339
1608
  # ```
1340
1609
 
1341
- def self.generate_yard
1342
- # these have hand-written methods, see above
1343
- no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
1344
-
1610
+ module Yard
1345
1611
  # map gobject's type names to Ruby
1346
- map_go_to_ruby = {
1612
+ MAP_GO_TO_RUBY = {
1347
1613
  "gboolean" => "Boolean",
1348
1614
  "gint" => "Integer",
1349
1615
  "gdouble" => "Float",
@@ -1351,109 +1617,92 @@ module Vips
1351
1617
  "gchararray" => "String",
1352
1618
  "VipsImage" => "Vips::Image",
1353
1619
  "VipsInterpolate" => "Vips::Interpolate",
1620
+ "VipsConnection" => "Vips::Connection",
1621
+ "VipsSource" => "Vips::Source",
1622
+ "VipsTarget" => "Vips::Target",
1623
+ "VipsSourceCustom" => "Vips::SourceCustom",
1624
+ "VipsTargetCustom" => "Vips::TargetCustom",
1354
1625
  "VipsArrayDouble" => "Array<Double>",
1355
1626
  "VipsArrayInt" => "Array<Integer>",
1356
1627
  "VipsArrayImage" => "Array<Image>",
1357
- "VipsArrayString" => "Array<String>",
1628
+ "VipsArrayString" => "Array<String>"
1358
1629
  }
1359
1630
 
1360
- generate_operation = lambda do |gtype, nickname, op|
1361
- op_flags = op.get_flags
1362
- return if (op_flags & OPERATION_DEPRECATED) != 0
1363
- return if no_generate.include? nickname
1364
-
1365
- description = Vips::vips_object_get_description op
1366
-
1367
- # find and classify all the arguments the operator can take
1368
- required_input = []
1369
- optional_input = []
1370
- required_output = []
1371
- optional_output = []
1372
- member_x = nil
1373
- op.argument_map do |pspec, argument_class, _argument_instance|
1374
- arg_flags = argument_class[:flags]
1375
- next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1376
- next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1377
-
1378
- name = pspec[:name].tr("-", "_")
1379
- # 'in' as a param name confuses yard
1380
- name = "im" if name == "in"
1381
- gtype = pspec[:value_type]
1382
- fundamental = GObject::g_type_fundamental gtype
1383
- type_name = GObject::g_type_name gtype
1384
- if map_go_to_ruby.include? type_name
1385
- type_name = map_go_to_ruby[type_name]
1386
- end
1387
- if fundamental == GObject::GFLAGS_TYPE ||
1388
- fundamental == GObject::GENUM_TYPE
1389
- type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1390
- end
1391
- blurb = GObject::g_param_spec_get_blurb pspec
1392
- value = {
1393
- name: name,
1394
- flags: arg_flags,
1395
- gtype: gtype,
1396
- type_name: type_name,
1397
- blurb: blurb
1398
- }
1399
-
1400
- if (arg_flags & ARGUMENT_INPUT) != 0
1401
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1402
- # note the first required input image, if any ... we
1403
- # will be a method of this instance
1404
- if !member_x && gtype == Vips::IMAGE_TYPE
1405
- member_x = value
1406
- else
1407
- required_input << value
1408
- end
1409
- else
1410
- optional_input << value
1411
- end
1412
- end
1631
+ # these have hand-written methods, see above
1632
+ NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
1413
1633
 
1414
- # MODIFY INPUT args count as OUTPUT as well
1415
- if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
1416
- ((arg_flags & ARGUMENT_INPUT) != 0 &&
1417
- (arg_flags & ARGUMENT_MODIFY) != 0)
1418
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1419
- required_output << value
1420
- else
1421
- optional_output << value
1422
- end
1423
- end
1634
+ # these are aliased (appear under several names)
1635
+ ALIAS = ["crop"]
1636
+
1637
+ # turn a gtype into a ruby type name
1638
+ def self.gtype_to_ruby gtype
1639
+ fundamental = GObject.g_type_fundamental gtype
1640
+ type_name = GObject.g_type_name gtype
1641
+
1642
+ if MAP_GO_TO_RUBY.include? type_name
1643
+ type_name = MAP_GO_TO_RUBY[type_name]
1424
1644
  end
1425
1645
 
1646
+ if fundamental == GObject::GFLAGS_TYPE ||
1647
+ fundamental == GObject::GENUM_TYPE
1648
+ type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1649
+ end
1650
+
1651
+ type_name
1652
+ end
1653
+
1654
+ def self.generate_operation introspect
1655
+ return if (introspect.flags & OPERATION_DEPRECATED) != 0
1656
+ return if NO_GENERATE.include? introspect.name
1657
+
1658
+ method_args = introspect.method_args
1659
+ required_output = introspect.required_output
1660
+ optional_input = introspect.doc_optional_input
1661
+ optional_output = introspect.doc_optional_output
1662
+
1426
1663
  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
1664
+ print "self." unless introspect.member_x
1665
+ print "#{introspect.name}("
1666
+ print method_args.map { |x| x[:yard_name] }.join(", ")
1667
+ print ", " if method_args.length > 0
1431
1668
  puts "**opts)"
1432
1669
 
1433
- puts "# #{description.capitalize}."
1670
+ puts "# #{introspect.description.capitalize}."
1434
1671
 
1435
- required_input.each do |arg|
1436
- puts "# @param #{arg[:name]} [#{arg[:type_name]}] #{arg[:blurb]}"
1672
+ method_args.each do |details|
1673
+ yard_name = details[:yard_name]
1674
+ gtype = details[:gtype]
1675
+ blurb = details[:blurb]
1676
+
1677
+ puts "# @param #{yard_name} [#{gtype_to_ruby(gtype)}] #{blurb}"
1437
1678
  end
1438
1679
 
1439
1680
  puts "# @param opts [Hash] Set of options"
1440
- optional_input.each do |arg|
1441
- puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
1442
- "#{arg[:blurb]}"
1681
+ optional_input.each do |arg_name, details|
1682
+ yard_name = details[:yard_name]
1683
+ gtype = details[:gtype]
1684
+ rtype = gtype_to_ruby gtype
1685
+ blurb = details[:blurb]
1686
+
1687
+ puts "# @option opts [#{rtype}] :#{yard_name} #{blurb}"
1443
1688
  end
1444
- optional_output.each do |arg|
1445
- print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
1446
- puts " Output #{arg[:blurb]}"
1689
+ optional_output.each do |arg_name, details|
1690
+ yard_name = details[:yard_name]
1691
+ gtype = details[:gtype]
1692
+ rtype = gtype_to_ruby gtype
1693
+ blurb = details[:blurb]
1694
+
1695
+ puts "# @option opts [#{rtype}] :#{yard_name} Output #{blurb}"
1447
1696
  end
1448
1697
 
1449
1698
  print "# @return ["
1450
1699
  if required_output.length == 0
1451
1700
  print "nil"
1452
1701
  elsif required_output.length == 1
1453
- print required_output.first[:type_name]
1702
+ print gtype_to_ruby(required_output.first[:gtype])
1454
1703
  else
1455
1704
  print "Array<"
1456
- print required_output.map { |x| x[:type_name] }.join(", ")
1705
+ print required_output.map { |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1457
1706
  print ">"
1458
1707
  end
1459
1708
  if optional_output.length > 0
@@ -1470,30 +1719,42 @@ module Vips
1470
1719
  puts ""
1471
1720
  end
1472
1721
 
1473
- generate_class = lambda do |gtype, _|
1474
- nickname = Vips::nickname_find gtype
1722
+ def self.generate
1723
+ alias_gtypes = {}
1724
+ ALIAS.each do |name|
1725
+ gtype = Vips.type_find "VipsOperation", name
1726
+ alias_gtypes[gtype] = name
1727
+ end
1475
1728
 
1476
- if nickname
1477
- begin
1478
- # can fail for abstract types
1479
- op = Vips::Operation.new nickname
1480
- rescue Vips::Error
1481
- nil
1729
+ generate_class = lambda do |gtype, _|
1730
+ name = if alias_gtypes.key? gtype
1731
+ alias_gtypes[gtype]
1732
+ else
1733
+ Vips.nickname_find gtype
1482
1734
  end
1483
1735
 
1484
- generate_operation.(gtype, nickname, op) if op
1485
- end
1736
+ if name
1737
+ begin
1738
+ # can fail for abstract types
1739
+ introspect = Vips::Introspect.get_yard name
1740
+ rescue Vips::Error
1741
+ nil
1742
+ end
1486
1743
 
1487
- Vips::vips_type_map gtype, generate_class, nil
1488
- end
1744
+ generate_operation(introspect) if introspect
1745
+ end
1746
+
1747
+ Vips.vips_type_map gtype, generate_class, nil
1748
+ end
1489
1749
 
1490
- puts "module Vips"
1491
- puts " class Image"
1492
- puts ""
1750
+ puts "module Vips"
1751
+ puts " class Image"
1752
+ puts ""
1493
1753
 
1494
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1754
+ generate_class.call(GObject.g_type_from_name("VipsOperation"), nil)
1495
1755
 
1496
- puts " end"
1497
- puts "end"
1756
+ puts " end"
1757
+ puts "end"
1758
+ end
1498
1759
  end
1499
1760
  end