ruby-vips 1.0.6 → 2.0.0

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.
@@ -1,362 +1,127 @@
1
- # This module provides a set of overrides for the [vips image processing
2
- # library](https://jcupitt.github.io/libvips/)
3
- # used via the [gobject-introspection
4
- # gem](https://rubygems.org/gems/gobject-introspection).
1
+ # This module provides an interface to the vips image processing library
2
+ # via ruby-ffi.
5
3
  #
6
- # It needs vips-8.2 or later to be installed,
7
- # and `Vips-8.0.typelib`, the vips typelib, needs to be on your
8
- # `GI_TYPELIB_PATH`.
9
- #
10
- # # Example
11
- #
12
- # ```ruby
13
- # require 'vips'
14
- #
15
- # if ARGV.length < 2
16
- # raise "usage: #{$PROGRAM_NAME}: input-file output-file"
17
- # end
18
- #
19
- # im = Vips::Image.new_from_file ARGV[0], :access => :sequential
20
- #
21
- # im *= [1, 2, 1]
22
- #
23
- # mask = Vips::Image.new_from_array [
24
- # [-1, -1, -1],
25
- # [-1, 16, -1],
26
- # [-1, -1, -1]], 8
27
- # im = im.conv mask
28
- #
29
- # im.write_to_file ARGV[1]
30
- # ```
31
- #
32
- # This example loads a file, boosts the green channel (I'm not sure why),
33
- # sharpens the image, and saves it back to disc again.
34
- #
35
- # Reading this example line by line, we have:
36
- #
37
- # ```ruby
38
- # im = Vips::Image.new_from_file ARGV[0], :access => :sequential
39
- # ```
40
- #
41
- # {Image.new_from_file} can load any image file supported by vips. In this
42
- # example, we will be accessing pixels top-to-bottom as we sweep through the
43
- # image reading and writing, so `:sequential` access mode is best for us. The
44
- # default mode is `:random`, this allows for full random access to image pixels,
45
- # but is slower and needs more memory. See {Access}
46
- # for full details
47
- # on the various modes available. You can also load formatted images from
48
- # memory buffers, create images that wrap C-style memory arrays, or make images
49
- # from constants.
50
- #
51
- # The next line:
52
- #
53
- # ```ruby
54
- # im *= [1, 2, 1]
55
- # ```
56
- #
57
- # Multiplying the image by an array constant uses one array element for each
58
- # image band. This line assumes that the input image has three bands and will
59
- # double the middle band. For RGB images, that's doubling green.
60
- #
61
- # Next we have:
62
- #
63
- # ```ruby
64
- # mask = Vips::Image.new_from_array [
65
- # [-1, -1, -1],
66
- # [-1, 16, -1],
67
- # [-1, -1, -1]], 8
68
- # im = im.conv mask
69
- # ```
70
- #
71
- # {Image.new_from_array} creates an image from an array constant. The 8 at
72
- # the end sets the scale: the amount to divide the image by after
73
- # integer convolution. See the libvips API docs for `vips_conv()` (the operation
74
- # invoked by {Image#conv}) for details on the convolution operator.
75
- #
76
- # Finally:
77
- #
78
- # ```ruby
79
- # im.write_to_file ARGV[1]
80
- # ```
81
- #
82
- # {Image#write_to_file} writes an image back to the filesystem. It can
83
- # write any format supported by vips: the file type is set from the filename
84
- # suffix. You can also write formatted images to memory buffers, or dump
85
- # image data to a raw memory array.
86
- #
87
- # # How it works
88
- #
89
- # The C sources to libvips include a set of specially formatted
90
- # comments which describe its interfaces. When you compile the library,
91
- # gobject-introspection generates `Vips-8.0.typelib`, a file
92
- # describing how to use libvips.
93
- #
94
- # The `gobject-introspection` gem loads this typelib and uses it to let you
95
- # call
96
- # functions in libvips directly from Ruby. However, the interface you get
97
- # from raw gobject-introspection is rather ugly, so the `ruby-vips` gem
98
- # adds a set
99
- # of overrides which try to make it nicer to use.
100
- #
101
- # The API you end up with is a Ruby-ish version of the [VIPS C
102
- # API](https://jcupitt.github.io/libvips/API/current).
103
- # Full documentation
104
- # on the operations and what they do is there, you can use it directly. This
105
- # document explains the extra features of the Ruby API and lists the available
106
- # operations very briefly.
107
- #
108
- # # Automatic wrapping
109
- #
110
- # `ruby-vips` adds a {Image.method_missing} handler to {Image} and uses
111
- # it to look up vips operations. For example, the libvips operation `add`, which
112
- # appears in C as `vips_add()`, appears in Ruby as {Image#add}.
113
- #
114
- # The operation's list of required arguments is searched and the first input
115
- # image is set to the value of `self`. Operations which do not take an input
116
- # image, such as {Image.black}, appear as class methods. The remainder of
117
- # the arguments you supply in the function call are used to set the other
118
- # required input arguments. If the final supplied argument is a hash, it is used
119
- # to set any optional input arguments. The result is the required output
120
- # argument if there is only one result, or an array of values if the operation
121
- # produces several results. If the operation has optional output objects, they
122
- # are returned as a final hash.
123
- #
124
- # For example, {Image#min}, the vips operation that searches an image for
125
- # the minimum value, has a large number of optional arguments. You can use it to
126
- # find the minimum value like this:
127
- #
128
- # ```ruby
129
- # min_value = image.min
130
- # ```
131
- #
132
- # You can ask it to return the position of the minimum with `:x` and `:y`.
133
- #
134
- # ```ruby
135
- # min_value, opts = min :x => true, :y => true
136
- # x_pos = opts['x']
137
- # y_pos = opts['y']
138
- # ```
139
- #
140
- # Now `x_pos` and `y_pos` will have the coordinates of the minimum value.
141
- # There's actually a convenience function for this, {Image#minpos}.
142
- #
143
- # You can also ask for the top *n* minimum, for example:
144
- #
145
- # ```ruby
146
- # min_value, opts = min :size => 10, :x_array => true, :y_array => true
147
- # x_pos = opts['x_array']
148
- # y_pos = opts['y_array']
149
- # ```
150
- #
151
- # Now `x_pos` and `y_pos` will be 10-element arrays.
152
- #
153
- # Because operations are member functions and return the result image, you can
154
- # chain them. For example, you can write:
155
- #
156
- # ```ruby
157
- # result_image = image.real.cos
158
- # ```
159
- #
160
- # to calculate the cosine of the real part of a complex image.
161
- # There are also a full set
162
- # of arithmetic operator overloads, see below.
163
- #
164
- # libvips types are also automatically wrapped. The override looks at the type
165
- # of argument required by the operation and converts the value you supply,
166
- # when it can. For example, {Image#linear} takes a `VipsArrayDouble` as
167
- # an argument
168
- # for the set of constants to use for multiplication. You can supply this
169
- # value as an integer, a float, or some kind of compound object and it
170
- # will be converted for you. You can write:
171
- #
172
- # ```ruby
173
- # result_image = image.linear 1, 3
174
- # result_image = image.linear 12.4, 13.9
175
- # result_image = image.linear [1, 2, 3], [4, 5, 6]
176
- # result_image = image.linear 1, [4, 5, 6]
177
- # ```
178
- #
179
- # And so on. A set of overloads are defined for {Image#linear}, see below.
180
- #
181
- # It does a couple of more ambitious conversions. It will automatically convert
182
- # to and from the various vips types, like `VipsBlob` and `VipsArrayImage`. For
183
- # example, you can read the ICC profile out of an image like this:
184
- #
185
- # ```ruby
186
- # profile = im.get_value "icc-profile-data"
187
- # ```
188
- #
189
- # and profile will be a byte array.
190
- #
191
- # If an operation takes several input images, you can use a constant for all but
192
- # one of them and the wrapper will expand the constant to an image for you. For
193
- # example, {Image#ifthenelse} uses a condition image to pick pixels
194
- # between a then and an else image:
195
- #
196
- # ```ruby
197
- # result_image = condition_image.ifthenelse then_image, else_image
198
- # ```
199
- #
200
- # You can use a constant instead of either the then or the else parts and it
201
- # will be expanded to an image for you. If you use a constant for both then and
202
- # else, it will be expanded to match the condition image. For example:
203
- #
204
- # ```ruby
205
- # result_image = condition_image.ifthenelse [0, 255, 0], [255, 0, 0]
206
- # ```
207
- #
208
- # Will make an image where true pixels are green and false pixels are red.
209
- #
210
- # This is useful for {Image#bandjoin}, the thing to join two or more
211
- # images up bandwise. You can write:
212
- #
213
- # ```ruby
214
- # rgba = rgb.bandjoin 255
215
- # ```
216
- #
217
- # to append a constant 255 band to an image, perhaps to add an alpha channel. Of
218
- # course you can also write:
219
- #
220
- # ```ruby
221
- # result_image = image1.bandjoin image2
222
- # result_image = image1.bandjoin [image2, image3]
223
- # result_image = Vips::Image.bandjoin [image1, image2, image3]
224
- # result_image = image1.bandjoin [image2, 255]
225
- # ```
226
- #
227
- # and so on.
228
- #
229
- # # Automatic YARD documentation
230
- #
231
- # The bulk of these API docs are generated automatically by
232
- # {Vips::generate_yard}. It examines
233
- # libvips and writes a summary of each operation and the arguments and options
234
- # that that operation expects.
235
- #
236
- # Use the [C API
237
- # docs](https://jcupitt.github.io/libvips/API/current)
238
- # for more detail.
239
- #
240
- # # Exceptions
241
- #
242
- # The wrapper spots errors from vips operations and raises the {Vips::Error}
243
- # exception. You can catch it in the usual way.
244
- #
245
- # # Enums
246
- #
247
- # The libvips enums, such as `VipsBandFormat` appear in ruby-vips as classes
248
- # like {Vips::BandFormat}. Overloads let you manipulate them in the obvious
249
- # way. For example:
250
- #
251
- # ```ruby
252
- # irb(main):002:0> im = Vips::Image.new_from_file "IMG_1867.JPG"
253
- # => #<Vips::Image:0x13e9760 ptr=0x1a88010>
254
- # irb(main):003:0> im.format
255
- # => #<Vips::BandFormat uchar>
256
- # irb(main):004:0> im.format == :uchar
257
- # => true
258
- # irb(main):005:0> im.format == "uchar"
259
- # => true
260
- # irb(main):007:0> im.format == 0
261
- # => true
262
- # ```
263
- #
264
- # The `0` is the C value of the enum.
265
- #
266
- # # Draw operations
267
- #
268
- # Paint operations like {Image#draw_circle} and {Image#draw_line}
269
- # modify their input image. This
270
- # makes them hard to use with the rest of libvips: you need to be very careful
271
- # about the order in which operations execute or you can get nasty crashes.
272
- #
273
- # The wrapper spots operations of this type and makes a private copy of the
274
- # image in memory before calling the operation. This stops crashes, but it does
275
- # make it inefficient. If you draw 100 lines on an image, for example, you'll
276
- # copy the image 100 times. The wrapper does make sure that memory is recycled
277
- # where possible, so you won't have 100 copies in memory.
278
- #
279
- # If you want to avoid the copies, you'll need to call drawing operations
280
- # yourself.
281
- #
282
- # # Overloads
283
- #
284
- # The wrapper defines the usual set of arithmetic, boolean and relational
285
- # overloads on image. You can mix images, constants and lists of constants
286
- # (almost) freely. For example, you can write:
287
- #
288
- # ```ruby
289
- # result_image = ((image * [1, 2, 3]).abs < 128) | 4
290
- # ```
291
- #
292
- # # Expansions
293
- #
294
- # Some vips operators take an enum to select an action, for example
295
- # {Image#math} can be used to calculate sine of every pixel like this:
296
- #
297
- # ```ruby
298
- # result_image = image.math :sin
299
- # ```
300
- #
301
- # This is annoying, so the wrapper expands all these enums into separate members
302
- # named after the enum. So you can write:
303
- #
304
- # ```ruby
305
- # result_image = image.sin
306
- # ```
307
- #
308
- # # Convenience functions
309
- #
310
- # The wrapper defines a few extra useful utility functions:
311
- # {Image#get_value}, {Image#set_value}, {Image#bandsplit},
312
- # {Image#maxpos}, {Image#minpos},
313
- # {Image#median}.
4
+ # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
+ # License:: MIT
6
+
7
+ require 'ffi'
314
8
 
315
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
+ attach_function :vips_filename_get_options, [:string], :string
20
+
21
+ attach_function :vips_foreign_find_load, [:string], :string
22
+ attach_function :vips_foreign_find_save, [:string], :string
23
+ attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
24
+ attach_function :vips_foreign_find_save_buffer, [:string], :string
25
+
26
+ attach_function :vips_image_write_to_memory,
27
+ [:pointer, SizeStruct.ptr], :pointer
28
+
29
+ attach_function :vips_image_get_typeof, [:pointer, :string], :GType
30
+ attach_function :vips_image_get,
31
+ [:pointer, :string, GObject::GValue.ptr], :int
32
+ attach_function :vips_image_set,
33
+ [:pointer, :string, GObject::GValue.ptr], :void
34
+ attach_function :vips_image_remove, [:pointer, :string], :void
35
+
36
+ attach_function :vips_band_format_iscomplex, [:int], :int
37
+ attach_function :vips_band_format_isfloat, [:int], :int
38
+
39
+ attach_function :nickname_find, :vips_nickname_find, [:GType], :string
40
+
41
+ public
316
42
 
317
43
  # This class represents a libvips image. See the {Vips} module documentation
318
- # for an introduction to using this module.
44
+ # for an introduction to using this class.
319
45
 
320
- class Image
46
+ class Image < Vips::Object
321
47
  private
322
48
 
49
+ # the layout of the VipsImage struct
50
+ module ImageLayout
51
+ def self.included base
52
+ base.class_eval do
53
+ layout :parent, Vips::Object::Struct
54
+ # rest opaque
55
+ end
56
+ end
57
+ end
58
+
59
+ class Struct < Vips::Object::Struct
60
+ include ImageLayout
61
+
62
+ end
63
+
64
+ class ManagedStruct < Vips::Object::ManagedStruct
65
+ include ImageLayout
66
+
67
+ end
68
+
323
69
  # handy for overloads ... want to be able to apply a function to an
324
70
  # array or to a scalar
325
- def self.smap(x, &block)
71
+ def self.smap x, &block
326
72
  x.is_a?(Array) ? x.map {|y| smap(y, &block)} : block.(x)
327
73
  end
328
74
 
75
+ def self.complex? format
76
+ format_number = Vips::vips_enum_from_nick "complex?",
77
+ BAND_FORMAT_TYPE, format.to_s
78
+ Vips::vips_band_format_iscomplex(format_number) != 0
79
+ end
80
+
81
+ def self.float? format
82
+ format_number = Vips::vips_enum_from_nick "float?",
83
+ BAND_FORMAT_TYPE, format.to_s
84
+ Vips::vips_band_format_isfloat(format_number) != 0
85
+ end
86
+
329
87
  # run a complex operation on a complex image, or an image with an even
330
88
  # number of bands ... handy for things like running .polar on .index
331
89
  # images
332
- def self.run_cmplx(image, &block)
90
+ def self.run_cmplx image, &block
333
91
  original_format = image.format
334
92
 
335
- if not Vips::band_format_iscomplex image.format
93
+ if not Image::complex? image.format
336
94
  if image.bands % 2 != 0
337
95
  raise Error, "not an even number of bands"
338
96
  end
339
97
 
340
- if not Vips::band_format_isfloat image.format
98
+ if not Image::float? image.format
341
99
  image = image.cast :float
342
100
  end
343
101
 
344
102
  new_format = image.format == :double ? :dpcomplex : :complex
345
- image = image.copy :format => new_format,
346
- :bands => image.bands / 2
103
+ image = image.copy format: new_format, bands: image.bands / 2
347
104
  end
348
105
 
349
106
  image = block.(image)
350
107
 
351
- if not Vips::band_format_iscomplex original_format
108
+ if not Image::complex? original_format
352
109
  new_format = image.format == :dpcomplex ? :double : :float
353
- image = image.copy :format => new_format,
354
- :bands => image.bands * 2
110
+ image = image.copy format: new_format, bands: image.bands * 2
355
111
  end
356
112
 
357
113
  image
358
114
  end
359
115
 
116
+ # handy for expanding enum operations
117
+ def call_enum(name, other, enum)
118
+ if other.is_a?(Vips::Image)
119
+ Vips::Operation.call name.to_s, [self, other, enum]
120
+ else
121
+ Vips::Operation.call name.to_s + "_const", [self, enum, other]
122
+ end
123
+ end
124
+
360
125
  # Write can fail due to no file descriptors and memory can fill if
361
126
  # large objects are not collected fairly soon. We can't try a
362
127
  # write and GC and retry on fail, since the write may take a
@@ -385,38 +150,45 @@ module Vips
385
150
  end
386
151
  end
387
152
 
388
- # libvips 8.4 and earlier had a bug which swapped the args to the _const
389
- # enum operations
390
- def swap_const_args
391
- Vips::version(0) < 8 or
392
- (Vips::version(0) == 8 and Vips::version(1) <= 4)
153
+ public
154
+
155
+ def inspect
156
+ "#<Image #{width}x#{height} #{format}, #{bands} bands, " +
157
+ "#{interpretation}>"
393
158
  end
394
159
 
395
- # handy for expanding enum operations
396
- def call_enum(name, other, enum)
397
- if other.is_a?(Vips::Image)
398
- Vips::call_base name.to_s, self, "", [other, enum]
399
- else
400
- args = swap_const_args ? [other, enum] : [enum, other]
160
+ def respond_to? name, include_all = false
161
+ # To support keyword args, we need to tell Ruby that final image
162
+ # arguments cannot be hashes of keywords.
163
+ #
164
+ # https://makandracards.com/makandra/36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
165
+ return false if name == :to_hash
401
166
 
402
- Vips::call_base name.to_s + "_const", self, "", args
403
- end
167
+ # respond to all vips operations by nickname
168
+ return true if Vips::type_find("VipsOperation", name.to_s) != 0
169
+
170
+ super
404
171
  end
405
172
 
406
- public
173
+ def self.respond_to? name, include_all = false
174
+ # respond to all vips operations by nickname
175
+ return true if Vips::type_find("VipsOperation", name.to_s) != 0
407
176
 
408
- # Invoke a vips operation with {call}, using self as the first
409
- # input image argument.
177
+ super
178
+ end
179
+
180
+ # Invoke a vips operation with {Vips::Operation.call}, using self as
181
+ # the first input argument.
410
182
  #
411
183
  # @param name [String] vips operation to call
412
184
  # @return result of vips operation
413
- def method_missing(name, *args)
414
- Vips::call_base name.to_s, self, "", args
185
+ def method_missing name, *args, **options
186
+ Vips::Operation.call name.to_s, [self, *args], options
415
187
  end
416
188
 
417
- # Invoke a vips operation with {call}.
418
- def self.method_missing(name, *args)
419
- Vips::call_base name.to_s, nil, "", args
189
+ # Invoke a vips operation with {Vips::Operation.call}.
190
+ def self.method_missing name, *args, **options
191
+ Vips::Operation.call name.to_s, args, options
420
192
  end
421
193
 
422
194
  # Return a new {Image} for a file on disc. This method can load
@@ -430,7 +202,7 @@ module Vips
430
202
  # You can also supply options as a hash, for example:
431
203
  #
432
204
  # ```
433
- # image = Vips::new_from_file "fred.jpg", :shrink => 2
205
+ # image = Vips::new_from_file "fred.jpg", shrink: 2
434
206
  # ```
435
207
  #
436
208
  # The full set of options available depend upon the load operation that
@@ -447,7 +219,7 @@ module Vips
447
219
  # out the header. Pixels will only be decompressed when they are needed.
448
220
  #
449
221
  # @!macro [new] vips.loadopts
450
- # @param [Hash] opts set of options
222
+ # @param opts [Hash] set of options
451
223
  # @option opts [Boolean] :disc (true) Open large images via a
452
224
  # temporary disc file
453
225
  # @option opts [Vips::Access] :access (:random) Access mode for file
@@ -455,25 +227,22 @@ module Vips
455
227
  # @param name [String] the filename to load from
456
228
  # @macro vips.loadopts
457
229
  # @return [Image] the loaded image
458
- def self.new_from_file(name, opts = {})
459
- # very common, and Vips::filename_get_filename will segv if we pass
460
- # this
461
- if name == nil
462
- raise Error, "filename is nil"
463
- end
464
- filename = Vips::filename_get_filename name
465
- option_string = Vips::filename_get_options name
466
- loader = Vips::Foreign.find_load filename
467
- if loader == nil
468
- raise Vips::Error
469
- end
230
+ def self.new_from_file name, opts = {}
231
+ # very common, and Vips::vips_filename_get_filename will segv if we
232
+ # pass this
233
+ raise Vips::Error, "filename is nil" if name == nil
470
234
 
471
- Vips::call_base loader, nil, option_string, [filename, opts]
235
+ filename = Vips::vips_filename_get_filename name
236
+ option_string = Vips::vips_filename_get_options name
237
+ loader = Vips::vips_foreign_find_load filename
238
+ raise Vips::Error if loader == nil
239
+
240
+ Operation.call loader, [filename], opts, option_string
472
241
  end
473
242
 
474
243
  # Create a new {Image} for an image encoded, in a format such as
475
244
  # JPEG, in a memory string. Load options may be passed as
476
- # strings, or appended as a hash. For example:
245
+ # strings or appended as a hash. For example:
477
246
  #
478
247
  # ```
479
248
  # image = Vips::new_from_from_buffer memory_buffer, "shrink=2"
@@ -482,7 +251,7 @@ module Vips
482
251
  # or alternatively:
483
252
  #
484
253
  # ```
485
- # image = Vips::new_from_from_buffer memory_buffer, "", :shrink => 2
254
+ # image = Vips::new_from_from_buffer memory_buffer, "", shrink: 2
486
255
  # ```
487
256
  #
488
257
  # The options available depend on the file format. Try something like:
@@ -502,13 +271,19 @@ module Vips
502
271
  # @param option_string [String] load options as a string
503
272
  # @macro vips.loadopts
504
273
  # @return [Image] the loaded image
505
- def self.new_from_buffer(data, option_string, opts = {})
506
- loader = Vips::Foreign.find_load_buffer data
507
- if loader == nil
508
- raise Vips::Error
509
- end
274
+ def self.new_from_buffer data, option_string, opts = {}
275
+ loader = Vips::vips_foreign_find_load_buffer data, data.length
276
+ raise Vips::Error if loader == nil
277
+
278
+ Vips::Operation.call loader, [data], opts, option_string
279
+ end
510
280
 
511
- Vips::call_base loader, nil, option_string, [data, opts]
281
+ def self.matrix_from_array width, height, array
282
+ ptr = FFI::MemoryPointer.new :double, array.length
283
+ ptr.write_array_of_double array
284
+ image = Vips::vips_image_new_matrix_from_array width, height,
285
+ ptr, array.length
286
+ Vips::Image.new image
512
287
  end
513
288
 
514
289
  # Create a new Image from a 1D or 2D array. A 1D array becomes an
@@ -537,7 +312,7 @@ module Vips
537
312
  # @param scale [Real] the convolution scale
538
313
  # @param offset [Real] the convolution offset
539
314
  # @return [Image] the image
540
- def self.new_from_array(array, scale = 1, offset = 0)
315
+ def self.new_from_array array, scale = 1, offset = 0
541
316
  # we accept a 1D array and assume height == 1, or a 2D array
542
317
  # and check all lines are the same length
543
318
  if not array.is_a? Array
@@ -564,13 +339,11 @@ module Vips
564
339
  end
565
340
 
566
341
  image = Vips::Image.matrix_from_array width, height, array
567
- if image == nil
568
- raise Vips::Error
569
- end
342
+ raise Vips::Error if image == nil
570
343
 
571
344
  # be careful to set them as double
572
- image.set_double 'scale', scale.to_f
573
- image.set_double 'offset', offset.to_f
345
+ image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
346
+ image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
574
347
 
575
348
  return image
576
349
  end
@@ -582,14 +355,13 @@ module Vips
582
355
  # You can pass an array to make a many-band image, or a single value to
583
356
  # make a one-band image.
584
357
  #
585
- # @param pixel [Real, Array<Real>] value to put in each pixel
358
+ # @param value [Real, Array<Real>] value to put in each pixel
586
359
  # @return [Image] constant image
587
- def new_from_image(value)
360
+ def new_from_image value
588
361
  pixel = (Vips::Image.black(1, 1) + value).cast(format)
589
- image = pixel.embed(0, 0, width, height, :extend => :copy)
590
- image.copy :interpretation => interpretation,
591
- :xres => xres, :yres => yres,
592
- :xoffset => xoffset, :yoffset => yoffset
362
+ image = pixel.embed 0, 0, width, height, extend: :copy
363
+ image.copy interpretation: interpretation,
364
+ xres: xres, yres: yres, xoffset: xoffset, yoffset: yoffset
593
365
  end
594
366
 
595
367
  # Write this image to a file. Save options may be encoded in the
@@ -602,7 +374,7 @@ module Vips
602
374
  # or equivalently:
603
375
  #
604
376
  # ```
605
- # image.write_to_file "fred.jpg", :Q => 90
377
+ # image.write_to_file "fred.jpg", Q: 90
606
378
  # ```
607
379
  #
608
380
  # The full set of save options depend on the selected saver. Try
@@ -615,21 +387,21 @@ module Vips
615
387
  # to see all the available options for JPEG save.
616
388
  #
617
389
  # @!macro [new] vips.saveopts
618
- # @param [Hash] opts set of options
390
+ # @param opts [Hash] set of options
619
391
  # @option opts [Boolean] :strip (false) Strip all metadata from image
620
392
  # @option opts [Array<Float>] :background (0) Background colour to
621
393
  # flatten alpha against, if necessary
622
394
  #
623
395
  # @param name [String] filename to write to
624
- def write_to_file(name, opts = {})
625
- filename = Vips::filename_get_filename name
626
- option_string = Vips::filename_get_options name
627
- saver = Vips::Foreign.find_save filename
396
+ def write_to_file name, opts = {}
397
+ filename = Vips::vips_filename_get_filename name
398
+ option_string = Vips::vips_filename_get_options name
399
+ saver = Vips::vips_foreign_find_save filename
628
400
  if saver == nil
629
401
  raise Vips::Error, "No known saver for '#{filename}'."
630
402
  end
631
403
 
632
- Vips::call_base saver, self, option_string, [filename, opts]
404
+ Vips::Operation.call saver, [self, filename], opts, option_string
633
405
 
634
406
  write_gc
635
407
  end
@@ -644,7 +416,7 @@ module Vips
644
416
  # or equivalently:
645
417
  #
646
418
  # ```
647
- # image.write_to_buffer ".jpg", :Q => 90
419
+ # image.write_to_buffer ".jpg", Q: 90
648
420
  # ```
649
421
  #
650
422
  # The full set of save options depend on the selected saver. Try
@@ -659,63 +431,43 @@ module Vips
659
431
  # @param format_string [String] save format plus options
660
432
  # @macro vips.saveopts
661
433
  # @return [String] the image saved in the specified format
662
- def write_to_buffer(format_string, opts = {})
663
- filename = Vips::filename_get_filename format_string
664
- option_string = Vips::filename_get_options format_string
665
- saver = Vips::Foreign.find_save_buffer filename
434
+ def write_to_buffer format_string, opts = {}
435
+ filename = Vips::vips_filename_get_filename format_string
436
+ option_string = Vips::vips_filename_get_options format_string
437
+ saver = Vips::vips_foreign_find_save_buffer filename
666
438
  if saver == nil
667
439
  raise Vips::Error, "No known saver for '#{filename}'."
668
440
  end
669
441
 
670
- buffer = Vips::call_base saver, self, option_string, [opts]
442
+ buffer = Vips::Operation.call saver, [self], opts, option_string
443
+ raise Vips::Error if buffer == nil
671
444
 
672
445
  write_gc
673
446
 
674
447
  return buffer
675
448
  end
676
449
 
677
- # @!attribute [r] width
678
- # @return [Integer] image width, in pixels
679
- # @!attribute [r] height
680
- # @return [Integer] image height, in pixels
681
- # @!attribute [r] bands
682
- # @return [Integer] image bands
683
- # @!attribute [r] format
684
- # @return [Vips::BandFormat] image format
685
- # @!attribute [r] interpretation
686
- # @return [Vips::Interpretation] image interpretation
687
- # @!attribute [r] coding
688
- # @return [Vips::Coding] image coding
689
- # @!attribute [r] filename
690
- # @return [String] image filename
691
- # @!attribute [r] xres
692
- # @return [Float] horizontal image resolution, in pixels per mm
693
- # @!attribute [r] yres
694
- # @return [Float] vertical image resolution, in pixels per mm
450
+ # Write this image to a large memory buffer.
451
+ #
452
+ # @return [String] the pixels as a huge binary string
453
+ def write_to_memory
454
+ len = Vips::SizeStruct.new
455
+ ptr = Vips::vips_image_write_to_memory self, len
456
+
457
+ # wrap up as an autopointer
458
+ ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
459
+
460
+ ptr.get_bytes 0, len[:value]
461
+ end
695
462
 
696
463
  # Fetch a `GType` from an image. `GType` will be 0 for no such field.
697
464
  #
698
465
  # @see get
699
- # @see get_value
700
- # @!method get_typeof(name)
701
466
  # @param name [String] Metadata field to fetch
702
467
  # @return [Integer] GType
703
-
704
- # Fetch a `GValue` from an image. The return status is 0 for success, -1
705
- # for failure.
706
- #
707
- # @see get_value
708
- # @see get_typeof
709
- # @!method get(name)
710
- # @param name [String] Metadata field to fetch
711
- # @return [Integer, GValue] Return status, GValue from image
712
-
713
- # Set a `GValue` on an image
714
- #
715
- # @see set_value
716
- # @!method set(name, value)
717
- # @param name [String] Metadata field to set
718
- # @param value [GValue] GValue to set
468
+ def get_typeof name
469
+ Vips::vips_image_get_typeof self, name
470
+ end
719
471
 
720
472
  # Get a metadata item from an image. Ruby types are constructed
721
473
  # automatically from the `GValue`, if possible.
@@ -723,63 +475,177 @@ module Vips
723
475
  # For example, you can read the ICC profile from an image like this:
724
476
  #
725
477
  # ```
726
- # profile = image.get_value "icc-profile-data"
478
+ # profile = image.get "icc-profile-data"
727
479
  # ```
728
480
  #
729
481
  # and profile will be an array containing the profile.
730
482
  #
731
- # @see get
732
- # @param name [String] Metadata field to set
483
+ # @param name [String] Metadata field to get
733
484
  # @return [Object] Value of field
734
- def get_value(name)
735
- ret, gval = get name
736
- if ret != 0
737
- raise Vips::Error, "Field #{name} not found."
485
+ def get name
486
+ gvalue = GObject::GValue.alloc
487
+ result = Vips::vips_image_get self, name, gvalue
488
+ if result != 0
489
+ raise Vips::Error
738
490
  end
739
- value = gval.value
740
491
 
741
- Argument::unwrap(value)
492
+ return gvalue.get
742
493
  end
743
494
 
744
- # Set a metadata item on an image. Ruby types are automatically
745
- # transformed into the matching `GValue`, if possible.
495
+ # Create a metadata item on an image, of the specifed type. Ruby types
496
+ # are automatically
497
+ # transformed into the matching `GType`, if possible.
746
498
  #
747
499
  # For example, you can use this to set an image's ICC profile:
748
500
  #
749
501
  # ```
750
- # x = y.set_value "icc-profile-data", profile
502
+ # x = y.set Vips::BLOB_TYPE, "icc-profile-data", profile
751
503
  # ```
752
504
  #
753
505
  # where `profile` is an ICC profile held as a binary string object.
754
506
  #
755
507
  # @see set
508
+ # @param gtype [Integer] GType of item
756
509
  # @param name [String] Metadata field to set
757
510
  # @param value [Object] Value to set
758
- def set_value(name, value)
759
- gtype = get_typeof name
760
- if gtype != 0
761
- # array-ize
762
- value = Argument::arrayize gtype, value
763
-
764
- # blob-ize
765
- if gtype.type_is_a? GLib::Type["VipsBlob"]
766
- if not value.is_a? Vips::Blob
767
- value = Vips::Blob.copy value
768
- end
769
- end
511
+ def set_type gtype, name, value
512
+ gvalue = GObject::GValue.alloc
513
+ gvalue.init gtype
514
+ gvalue.set value
515
+ Vips::vips_image_set self, name, gvalue
516
+ end
770
517
 
771
- # image-ize
772
- if gtype.type_is_a? GLib::Type["VipsImage"]
773
- if not value.is_a? Vips::Image
774
- value = imageize match_image, value
775
- end
776
- end
518
+ # Set the value of a metadata item on an image. The metadata item must
519
+ # already exist. Ruby types are automatically
520
+ # transformed into the matching `GValue`, if possible.
521
+ #
522
+ # For example, you can use this to set an image's ICC profile:
523
+ #
524
+ # ```
525
+ # x = y.set "icc-profile-data", profile
526
+ # ```
527
+ #
528
+ # where `profile` is an ICC profile held as a binary string object.
529
+ #
530
+ # @see set_type
531
+ # @param name [String] Metadata field to set
532
+ # @param value [Object] Value to set
533
+ def set name, value
534
+ set_type get_typeof(name), name, value
535
+ end
777
536
 
778
- end
537
+ # Remove a metadata item from an image.
538
+ #
539
+ # @param name [String] Metadata field to remove
540
+ def remove name
541
+ Vips::vips_image_remove self, name
542
+ end
779
543
 
544
+ # compatibility: old name for get
545
+ def get_value name
546
+ get name
547
+ end
548
+
549
+ # compatibility: old name for set
550
+ def set_value name, value
780
551
  set name, value
781
552
  end
782
553
 
554
+ # Get image width, in pixels.
555
+ #
556
+ # @return [Integer] image width, in pixels
557
+ def width
558
+ get "width"
559
+ end
560
+
561
+ # Get image height, in pixels.
562
+ #
563
+ # @return [Integer] image height, in pixels
564
+ def height
565
+ get "height"
566
+ end
567
+
568
+ # Get number of image bands.
569
+ #
570
+ # @return [Integer] number of image bands
571
+ def bands
572
+ get "bands"
573
+ end
574
+
575
+ # Get image format.
576
+ #
577
+ # @return [Symbol] image format
578
+ def format
579
+ get "format"
580
+ end
581
+
582
+ # Get image interpretation.
583
+ #
584
+ # @return [Symbol] image interpretation
585
+ def interpretation
586
+ get "interpretation"
587
+ end
588
+
589
+ # Get image coding.
590
+ #
591
+ # @return [Symbol] image coding
592
+ def coding
593
+ get "coding"
594
+ end
595
+
596
+ # Get image filename, if any.
597
+ #
598
+ # @return [String] image filename
599
+ def filename
600
+ get "filename"
601
+ end
602
+
603
+ # Get image xoffset.
604
+ #
605
+ # @return [Integer] image xoffset
606
+ def xoffset
607
+ get "xoffset"
608
+ end
609
+
610
+ # Get image yoffset.
611
+ #
612
+ # @return [Integer] image yoffset
613
+ def yoffset
614
+ get "yoffset"
615
+ end
616
+
617
+ # Get image x resolution.
618
+ #
619
+ # @return [Float] image x resolution
620
+ def xres
621
+ get "xres"
622
+ end
623
+
624
+ # Get image y resolution.
625
+ #
626
+ # @return [Float] image y resolution
627
+ def yres
628
+ get "yres"
629
+ end
630
+
631
+ # Get scale metadata.
632
+ #
633
+ # @return [Float] image scale
634
+ def scale
635
+ return 1 if get_typeof("scale") == 0
636
+
637
+ get "scale"
638
+ end
639
+
640
+ # Get offset metadata.
641
+ #
642
+ # @return [Float] image offset
643
+ def offset
644
+ return 0 if get_typeof("offset") == 0
645
+
646
+ get "offset"
647
+ end
648
+
783
649
  # Get the image size.
784
650
  #
785
651
  # @return [Integer, Integer] image width and height
@@ -787,11 +653,16 @@ module Vips
787
653
  [width, height]
788
654
  end
789
655
 
656
+ def copy_memory
657
+ new_image = Vips::vips_image_copy_memory self
658
+ Vips::Image.new new_image
659
+ end
660
+
790
661
  # Add an image, constant or array.
791
662
  #
792
663
  # @param other [Image, Real, Array<Real>] Thing to add to self
793
664
  # @return [Image] result of addition
794
- def +(other)
665
+ def + other
795
666
  other.is_a?(Vips::Image) ?
796
667
  add(other) : linear(1, other)
797
668
  end
@@ -800,7 +671,7 @@ module Vips
800
671
  #
801
672
  # @param other [Image, Real, Array<Real>] Thing to subtract from self
802
673
  # @return [Image] result of subtraction
803
- def -(other)
674
+ def - other
804
675
  other.is_a?(Vips::Image) ?
805
676
  subtract(other) : linear(1, Image::smap(other) {|x| x * -1})
806
677
  end
@@ -809,7 +680,7 @@ module Vips
809
680
  #
810
681
  # @param other [Image, Real, Array<Real>] Thing to multiply by self
811
682
  # @return [Image] result of multiplication
812
- def *(other)
683
+ def * other
813
684
  other.is_a?(Vips::Image) ?
814
685
  multiply(other) : linear(other, 0)
815
686
  end
@@ -818,7 +689,7 @@ module Vips
818
689
  #
819
690
  # @param other [Image, Real, Array<Real>] Thing to divide self by
820
691
  # @return [Image] result of division
821
- def /(other)
692
+ def / other
822
693
  other.is_a?(Vips::Image) ?
823
694
  divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0)
824
695
  end
@@ -827,7 +698,7 @@ module Vips
827
698
  #
828
699
  # @param other [Image, Real, Array<Real>] self modulo this
829
700
  # @return [Image] result of modulo
830
- def %(other)
701
+ def % other
831
702
  other.is_a?(Vips::Image) ?
832
703
  remainder(other) : remainder_const(other)
833
704
  end
@@ -836,48 +707,48 @@ module Vips
836
707
  #
837
708
  # @param other [Image, Real, Array<Real>] self to the power of this
838
709
  # @return [Image] result of power
839
- def **(other)
840
- call_enum("math2", other, :pow)
710
+ def ** other
711
+ call_enum "math2", other, :pow
841
712
  end
842
713
 
843
714
  # Integer left shift with an image, constant or array.
844
715
  #
845
716
  # @param other [Image, Real, Array<Real>] shift left by this much
846
717
  # @return [Image] result of left shift
847
- def <<(other)
848
- call_enum("boolean", other, :lshift)
718
+ def << other
719
+ call_enum "boolean", other, :lshift
849
720
  end
850
721
 
851
722
  # Integer right shift with an image, constant or array.
852
723
  #
853
724
  # @param other [Image, Real, Array<Real>] shift right by this much
854
725
  # @return [Image] result of right shift
855
- def >>(other)
856
- call_enum("boolean", other, :rshift)
726
+ def >> other
727
+ call_enum "boolean", other, :rshift
857
728
  end
858
729
 
859
730
  # Integer bitwise OR with an image, constant or array.
860
731
  #
861
732
  # @param other [Image, Real, Array<Real>] bitwise OR with this
862
733
  # @return [Image] result of bitwise OR
863
- def |(other)
864
- call_enum("boolean", other, :or)
734
+ def | other
735
+ call_enum "boolean", other, :or
865
736
  end
866
737
 
867
738
  # Integer bitwise AND with an image, constant or array.
868
739
  #
869
740
  # @param other [Image, Real, Array<Real>] bitwise AND with this
870
741
  # @return [Image] result of bitwise AND
871
- def &(other)
872
- call_enum("boolean", other, :and)
742
+ def & other
743
+ call_enum "boolean", other, :and
873
744
  end
874
745
 
875
746
  # Integer bitwise EOR with an image, constant or array.
876
747
  #
877
748
  # @param other [Image, Real, Array<Real>] bitwise EOR with this
878
749
  # @return [Image] result of bitwise EOR
879
- def ^(other)
880
- call_enum("boolean", other, :eor)
750
+ def ^ other
751
+ call_enum "boolean", other, :eor
881
752
  end
882
753
 
883
754
  # Equivalent to image ^ -1
@@ -910,8 +781,8 @@ module Vips
910
781
  #
911
782
  # @param other [Image, Real, Array<Real>] relational less than with this
912
783
  # @return [Image] result of less than
913
- def <(other)
914
- call_enum("relational", other, :less)
784
+ def < other
785
+ call_enum "relational", other, :less
915
786
  end
916
787
 
917
788
  # Relational less than or equal to with an image, constant or array.
@@ -919,16 +790,16 @@ module Vips
919
790
  # @param other [Image, Real, Array<Real>] relational less than or
920
791
  # equal to with this
921
792
  # @return [Image] result of less than or equal to
922
- def <=(other)
923
- call_enum("relational", other, :lesseq)
793
+ def <= other
794
+ call_enum "relational", other, :lesseq
924
795
  end
925
796
 
926
797
  # Relational more than with an image, constant or array.
927
798
  #
928
799
  # @param other [Image, Real, Array<Real>] relational more than with this
929
800
  # @return [Image] result of more than
930
- def >(other)
931
- call_enum("relational", other, :more)
801
+ def > other
802
+ call_enum "relational", other, :more
932
803
  end
933
804
 
934
805
  # Relational more than or equal to with an image, constant or array.
@@ -936,20 +807,20 @@ module Vips
936
807
  # @param other [Image, Real, Array<Real>] relational more than or
937
808
  # equal to with this
938
809
  # @return [Image] result of more than or equal to
939
- def >=(other)
940
- call_enum("relational", other, :moreeq)
810
+ def >= other
811
+ call_enum "relational", other, :moreeq
941
812
  end
942
813
 
943
814
  # Compare equality to nil, an image, constant or array.
944
815
  #
945
816
  # @param other [nil, Image, Real, Array<Real>] test equality to this
946
817
  # @return [Image] result of equality
947
- def ==(other)
818
+ def == other
948
819
  # for equality, we must allow tests against nil
949
820
  if other == nil
950
821
  false
951
822
  else
952
- call_enum("relational", other, :equal)
823
+ call_enum "relational", other, :equal
953
824
  end
954
825
  end
955
826
 
@@ -957,12 +828,12 @@ module Vips
957
828
  #
958
829
  # @param other [nil, Image, Real, Array<Real>] test inequality to this
959
830
  # @return [Image] result of inequality
960
- def !=(other)
831
+ def != other
961
832
  # for equality, we must allow tests against nil
962
833
  if other == nil
963
834
  true
964
835
  else
965
- call_enum("relational", other, :noteq)
836
+ call_enum "relational", other, :noteq
966
837
  end
967
838
  end
968
839
 
@@ -970,11 +841,11 @@ module Vips
970
841
  #
971
842
  # @param index [Numeric, Range] extract these band(s)
972
843
  # @return [Image] extracted band(s)
973
- def [](index)
844
+ def [] index
974
845
  if index.is_a? Range
975
846
  n = index.end - index.begin
976
847
  n += 1 if not index.exclude_end?
977
- extract_band index.begin, :n => n
848
+ extract_band index.begin, n: n
978
849
  elsif index.is_a? Numeric
979
850
  extract_band index
980
851
  else
@@ -988,7 +859,7 @@ module Vips
988
859
  def to_a
989
860
  # we render the image to a big string, then unpack
990
861
  # as a Ruby array of the correct type
991
- memory = write_to_memory.pack('c*')
862
+ memory = write_to_memory
992
863
 
993
864
  # make the template for unpack
994
865
  template = {
@@ -1002,7 +873,7 @@ module Vips
1002
873
  :double => 'd',
1003
874
  :complex => 'f',
1004
875
  :dpcomplex => 'd'
1005
- }[format.nick.to_sym] + '*'
876
+ }[format] + '*'
1006
877
 
1007
878
  # and unpack into something like [1, 2, 3, 4 ..]
1008
879
  array = memory.unpack(template)
@@ -1071,7 +942,7 @@ module Vips
1071
942
  #
1072
943
  # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1073
944
  # @return [Image] many band image
1074
- def bandjoin(other)
945
+ def bandjoin other
1075
946
  if not other.is_a? Array
1076
947
  other = [other]
1077
948
  end
@@ -1091,7 +962,7 @@ module Vips
1091
962
  # @return [Real, Real, Real] maximum value, x coordinate of maximum, y
1092
963
  # coordinate of maximum
1093
964
  def maxpos
1094
- v, opts = max :x => true, :y => true
965
+ v, opts = max x: true, y: true
1095
966
  x = opts['x']
1096
967
  y = opts['y']
1097
968
  return v, x, y
@@ -1102,7 +973,7 @@ module Vips
1102
973
  # @return [Real, Real, Real] minimum value, x coordinate of minimum, y
1103
974
  # coordinate of minimum
1104
975
  def minpos
1105
- v, opts = min :x => true, :y => true
976
+ v, opts = min x: true, y: true
1106
977
  x = opts['x']
1107
978
  y = opts['y']
1108
979
  return v, x, y
@@ -1113,7 +984,7 @@ module Vips
1113
984
  # @param x [Integer] x coordinate to sample
1114
985
  # @param y [Integer] y coordinate to sample
1115
986
  # @return [Array<Float>] the pixel values as an array
1116
- def getpoint(x, y)
987
+ def getpoint x, y
1117
988
  # vips has an operation that does this, but we can't call it via
1118
989
  # gobject-introspection 3.1 since it's missing array double
1119
990
  # get
@@ -1126,8 +997,8 @@ module Vips
1126
997
  #
1127
998
  # @param size [Integer] size of filter window
1128
999
  # @return [Image] result of median filter
1129
- def median(size = 3)
1130
- rank(size, size, (size * size) / 2)
1000
+ def median size = 3
1001
+ rank size, size, (size * size) / 2
1131
1002
  end
1132
1003
 
1133
1004
  # Return the real part of a complex image.
@@ -1182,6 +1053,14 @@ module Vips
1182
1053
  Image::run_cmplx(self) {|x| x.complex :conj}
1183
1054
  end
1184
1055
 
1056
+ # Calculate the cross phase of two images.
1057
+ #
1058
+ # @param other [Image, Real, Array<Real>] cross phase with this
1059
+ # @return [Image] cross phase
1060
+ def cross_phase other
1061
+ complex2 other, :cross_phase
1062
+ end
1063
+
1185
1064
  # Return the sine of an image in degrees.
1186
1065
  #
1187
1066
  # @return [Image] sine of each pixel
@@ -1274,7 +1153,7 @@ module Vips
1274
1153
  # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1275
1154
  # element
1276
1155
  # @return [Image] eroded image
1277
- def erode(mask)
1156
+ def erode mask
1278
1157
  morph mask, :erode
1279
1158
  end
1280
1159
 
@@ -1286,7 +1165,7 @@ module Vips
1286
1165
  # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1287
1166
  # element
1288
1167
  # @return [Image] dilated image
1289
- def dilate(mask)
1168
+ def dilate mask
1290
1169
  morph mask, :dilate
1291
1170
  end
1292
1171
 
@@ -1317,28 +1196,28 @@ module Vips
1317
1196
  #
1318
1197
  # @param th [Image, Real, Array<Real>] true values
1319
1198
  # @param el [Image, Real, Array<Real>] false values
1320
- # @param [Hash] opts set of options
1199
+ # @param opts [Hash] set of options
1321
1200
  # @option opts [Boolean] :blend (false) Blend smoothly between th and el
1322
1201
  # @return [Image] merged image
1323
1202
  def ifthenelse(th, el, opts = {})
1324
1203
  match_image = [th, el, self].find {|x| x.is_a? Vips::Image}
1325
1204
 
1326
1205
  if not th.is_a? Vips::Image
1327
- th = Argument::imageize match_image, th
1206
+ th = Operation.imageize match_image, th
1328
1207
  end
1329
1208
  if not el.is_a? Vips::Image
1330
- el = Argument::imageize match_image, el
1209
+ el = Operation.imageize match_image, el
1331
1210
  end
1332
1211
 
1333
- Vips::call_base "ifthenelse", self, "", [th, el, opts]
1212
+ Vips::Operation.call "ifthenelse", [self, th, el], opts
1334
1213
  end
1335
1214
 
1336
1215
  # Scale an image to uchar. This is the vips `scale` operation, but
1337
1216
  # renamed to avoid a clash with the `.scale` property.
1338
1217
  #
1339
- # @param [Hash] opts Set of options
1218
+ # @param opts [Hash] Set of options
1340
1219
  # @return [Vips::Image] Output image
1341
- def scaleimage(opts = {})
1220
+ def scaleimage opts = {}
1342
1221
  Vips::Image.scale self, opts
1343
1222
  end
1344
1223
 
@@ -1359,97 +1238,125 @@ module Vips
1359
1238
  # these have hand-written methods, see above
1360
1239
  no_generate = ["scale", "bandjoin", "ifthenelse"]
1361
1240
 
1362
- generate_operation = lambda do |op|
1363
- flags = op.flags
1364
- return if (flags & :deprecated) != 0
1365
- nickname = Vips::nickname_find op.gtype
1366
-
1241
+ # map gobject's type names to Ruby
1242
+ map_go_to_ruby = {
1243
+ "gboolean" => "Boolean",
1244
+ "gint" => "Integer",
1245
+ "gdouble" => "Float",
1246
+ "gfloat" => "Float",
1247
+ "gchararray" => "String",
1248
+ "VipsImage" => "Vips::Image",
1249
+ "VipsInterpolate" => "Vips::Interpolate",
1250
+ "VipsArrayDouble" => "Array<Double>",
1251
+ "VipsArrayInt" => "Array<Integer>",
1252
+ "VipsArrayImage" => "Array<Image>",
1253
+ "VipsArrayString" => "Array<String>",
1254
+ }
1255
+
1256
+ generate_operation = lambda do |gtype, nickname, op|
1257
+ op_flags = op.get_flags
1258
+ return if (op_flags & OPERATION_DEPRECATED) != 0
1367
1259
  return if no_generate.include? nickname
1260
+ description = Vips::vips_object_get_description op
1261
+
1262
+ # find and classify all the arguments the operator can take
1263
+ required_input = []
1264
+ optional_input = []
1265
+ required_output = []
1266
+ optional_output = []
1267
+ member_x = nil
1268
+ op.argument_map do |pspec, argument_class, argument_instance|
1269
+ arg_flags = argument_class[:flags]
1270
+ next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1271
+ next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1272
+
1273
+ name = pspec[:name].gsub("-", "_")
1274
+ # 'in' as a param name confuses yard
1275
+ name = "im" if name == "in"
1276
+ gtype = pspec[:value_type]
1277
+ fundamental = GObject::g_type_fundamental gtype
1278
+ type_name = GObject::g_type_name gtype
1279
+ if map_go_to_ruby.include? type_name
1280
+ type_name = map_go_to_ruby[type_name]
1281
+ end
1282
+ if fundamental == GObject::GFLAGS_TYPE or
1283
+ fundamental == GObject::GENUM_TYPE
1284
+ type_name =~ /Vips(.*)/
1285
+ type_name = "Vips::" + $~[1]
1286
+ end
1287
+ blurb = GObject::g_param_spec_get_blurb pspec
1288
+ value = {:name => name,
1289
+ :flags => arg_flags,
1290
+ :gtype => gtype,
1291
+ :type_name => type_name,
1292
+ :blurb => blurb}
1293
+
1294
+ if (arg_flags & ARGUMENT_INPUT) != 0
1295
+ if (arg_flags & ARGUMENT_REQUIRED) != 0
1296
+ # note the first required input image, if any ... we
1297
+ # will be a method of this instance
1298
+ if not member_x and gtype == Vips::IMAGE_TYPE
1299
+ member_x = value
1300
+ else
1301
+ required_input << value
1302
+ end
1303
+ else
1304
+ optional_input << value
1305
+ end
1306
+ end
1368
1307
 
1369
- all_args = op.get_args.select {|arg| not arg.isset}
1370
-
1371
- # separate args into various categories
1372
-
1373
- required_input = all_args.select do |arg|
1374
- (arg.flags & :input) != 0 and
1375
- (arg.flags & :required) != 0
1376
- end
1377
-
1378
- optional_input = all_args.select do |arg|
1379
- (arg.flags & :input) != 0 and
1380
- (arg.flags & :required) == 0
1381
- end
1382
-
1383
- required_output = all_args.select do |arg|
1384
- (arg.flags & :output) != 0 and
1385
- (arg.flags & :required) != 0
1386
- end
1387
-
1388
- # required input args with :modify are copied and appended to
1389
- # output
1390
- modified_required_input = required_input.select do |arg|
1391
- (arg.flags & :modify) != 0
1392
- end
1393
- required_output += modified_required_input
1394
-
1395
- optional_output = all_args.select do |arg|
1396
- (arg.flags & :output) != 0 and
1397
- (arg.flags & :required) == 0
1398
- end
1399
-
1400
- # optional input args with :modify are copied and appended to
1401
- # output
1402
- modified_optional_input = optional_input.select do |arg|
1403
- (arg.flags & :modify) != 0
1404
- end
1405
- optional_output += modified_optional_input
1308
+ # MODIFY INPUT args count as OUTPUT as well
1309
+ if (arg_flags & ARGUMENT_OUTPUT) != 0 or
1310
+ ((arg_flags & ARGUMENT_INPUT) != 0 and
1311
+ (arg_flags & ARGUMENT_MODIFY) != 0)
1312
+ if (arg_flags & ARGUMENT_REQUIRED) != 0 and
1313
+ required_output << value
1314
+ else
1315
+ optional_output << value
1316
+ end
1317
+ end
1406
1318
 
1407
- # find the first input image, if any ... we will be a method of this
1408
- # instance
1409
- member_x = required_input.find do |x|
1410
- x.gtype.type_is_a? GLib::Type["VipsImage"]
1411
- end
1412
- if member_x != nil
1413
- required_input.delete member_x
1414
1319
  end
1415
1320
 
1416
1321
  print "# @!method "
1417
1322
  print "self." if not member_x
1418
1323
  print "#{nickname}("
1419
- print required_input.map(&:name).join(", ")
1324
+ print required_input.map{|x| x[:name]}.join(", ")
1420
1325
  print ", " if required_input.length > 0
1421
1326
  puts "opts = {})"
1422
1327
 
1423
- puts "# #{op.description.capitalize}."
1328
+ puts "# #{description.capitalize}."
1424
1329
 
1425
- required_input.each do |arg|
1426
- puts "# @param #{arg.name} [#{arg.type}] #{arg.blurb}"
1330
+ required_input.each do |arg|
1331
+ puts "# @param #{arg[:name]} [#{arg[:type_name]}] " +
1332
+ "#{arg[:blurb]}"
1427
1333
  end
1428
1334
 
1429
- puts "# @param [Hash] opts Set of options"
1430
- optional_input.each do |arg|
1431
- puts "# @option opts [#{arg.type}] :#{arg.name} #{arg.blurb}"
1335
+ puts "# @param opts [Hash] Set of options"
1336
+ optional_input.each do |arg|
1337
+ puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
1338
+ "#{arg[:blurb]}"
1432
1339
  end
1433
- optional_output.each do |arg|
1434
- print "# @option opts [#{arg.type}] :#{arg.name}"
1435
- puts " Output #{arg.blurb}"
1340
+ optional_output.each do |arg|
1341
+ print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
1342
+ puts " Output #{arg[:blurb]}"
1436
1343
  end
1437
1344
 
1438
1345
  print "# @return ["
1439
1346
  if required_output.length == 0
1440
1347
  print "nil"
1441
1348
  elsif required_output.length == 1
1442
- print required_output[0].type
1349
+ print required_output.first[:type_name]
1443
1350
  elsif
1444
1351
  print "Array<"
1445
- print required_output.map(&:type).join(", ")
1352
+ print required_output.map{|x| x[:type_name]}.join(", ")
1446
1353
  print ">"
1447
1354
  end
1448
1355
  if optional_output.length > 0
1449
1356
  print ", Hash<Symbol => Object>"
1450
1357
  end
1451
1358
  print "] "
1452
- print required_output.map(&:blurb).join(", ")
1359
+ print required_output.map{|x| x[:blurb]}.join(", ")
1453
1360
  if optional_output.length > 0
1454
1361
  print ", " if required_output.length > 0
1455
1362
  print "Hash of optional output items"
@@ -1459,35 +1366,30 @@ module Vips
1459
1366
  puts ""
1460
1367
  end
1461
1368
 
1462
- generate_class = lambda do |gtype|
1463
- begin
1464
- # can fail for abstract types
1465
- # can't find a way to get to #abstract? from a gtype
1466
- op = Vips::Operation.new gtype.name
1467
- rescue
1468
- op = nil
1469
- end
1369
+ generate_class = lambda do |gtype, a|
1370
+ nickname = Vips::nickname_find gtype
1470
1371
 
1471
- generate_operation.(op) if op
1372
+ if nickname
1373
+ begin
1374
+ # can fail for abstract types
1375
+ op = Vips::Operation.new nickname
1376
+ rescue
1377
+ end
1472
1378
 
1473
- gtype.children.each do |x|
1474
- generate_class.(x)
1379
+ generate_operation.(gtype, nickname, op) if op
1475
1380
  end
1381
+
1382
+ Vips::vips_type_map gtype, generate_class, nil
1476
1383
  end
1477
1384
 
1478
1385
  puts "module Vips"
1479
1386
  puts " class Image"
1480
1387
  puts ""
1481
1388
 
1482
- # gobject-introspection 3.0.7 crashes a lot if it GCs while doing
1483
- # something
1484
- GC.disable
1485
-
1486
- generate_class.(GLib::Type["VipsOperation"])
1389
+ generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1487
1390
 
1488
1391
  puts " end"
1489
1392
  puts "end"
1490
1393
  end
1491
1394
 
1492
1395
  end
1493
-