vips 8.8.4 → 8.12.1

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