vips 8.8.0.3 → 8.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -2
  3. data/README.md +3 -1
  4. data/example/connection.rb +17 -0
  5. data/example/daltonize8.rb +13 -15
  6. data/example/example1.rb +1 -2
  7. data/example/example4.rb +2 -2
  8. data/example/example5.rb +4 -5
  9. data/example/inheritance_with_refcount.rb +2 -19
  10. data/example/progress.rb +30 -0
  11. data/example/thumb.rb +2 -4
  12. data/example/trim8.rb +4 -4
  13. data/ext/Rakefile +2 -2
  14. data/lib/vips.rb +101 -35
  15. data/lib/vips/align.rb +0 -1
  16. data/lib/vips/angle.rb +0 -1
  17. data/lib/vips/angle45.rb +0 -1
  18. data/lib/vips/bandformat.rb +0 -2
  19. data/lib/vips/blend_mode.rb +0 -2
  20. data/lib/vips/coding.rb +0 -1
  21. data/lib/vips/compass_direction.rb +0 -1
  22. data/lib/vips/connection.rb +46 -0
  23. data/lib/vips/direction.rb +0 -1
  24. data/lib/vips/extend.rb +0 -1
  25. data/lib/vips/gobject.rb +8 -4
  26. data/lib/vips/gvalue.rb +15 -9
  27. data/lib/vips/image.rb +269 -204
  28. data/lib/vips/interesting.rb +0 -1
  29. data/lib/vips/interpolate.rb +0 -5
  30. data/lib/vips/interpretation.rb +0 -1
  31. data/lib/vips/kernel.rb +0 -1
  32. data/lib/vips/methods.rb +150 -59
  33. data/lib/vips/object.rb +126 -18
  34. data/lib/vips/operation.rb +169 -101
  35. data/lib/vips/operationboolean.rb +0 -1
  36. data/lib/vips/operationcomplex.rb +0 -1
  37. data/lib/vips/operationcomplex2.rb +0 -1
  38. data/lib/vips/operationcomplexget.rb +0 -1
  39. data/lib/vips/operationmath.rb +0 -1
  40. data/lib/vips/operationmath2.rb +0 -1
  41. data/lib/vips/operationrelational.rb +0 -1
  42. data/lib/vips/operationround.rb +0 -1
  43. data/lib/vips/region.rb +73 -0
  44. data/lib/vips/size.rb +0 -1
  45. data/lib/vips/source.rb +89 -0
  46. data/lib/vips/sourcecustom.rb +90 -0
  47. data/lib/vips/target.rb +87 -0
  48. data/lib/vips/targetcustom.rb +78 -0
  49. data/lib/vips/version.rb +1 -1
  50. metadata +14 -7
  51. data/CHANGELOG.md +0 -266
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # Various types of alignment. See {Image#join}, for example.
4
3
  #
5
4
  # * `:low` Align on the low coordinate edge
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # Various fixed 90 degree rotation angles. See {Image#rot}.
4
3
  #
5
4
  # * `:d0` no rotate
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # Various fixed 45 degree rotation angles. See {Image#rot45}.
4
3
  #
5
4
  # * `:d0` no rotate
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # The format used for each band element. Each corresponds to a native C type
4
3
  # for the current machine.
5
4
  #
@@ -16,5 +15,4 @@ module Vips
16
15
  # * `:dpcomplex` double complex (two double) format
17
16
  class BandFormat < Symbol
18
17
  end
19
-
20
18
  end
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # Blend mode to use when compositing images. See {Image#composite}.
4
3
  #
5
4
  # `:clear` - where the second object is drawn, the first is removed
@@ -31,4 +30,3 @@ module Vips
31
30
  class BlendMode < Symbol
32
31
  end
33
32
  end
34
-
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # How pixels are coded.
4
3
  #
5
4
  # Normally, pixels are uncoded and can be manipulated as you would expect.
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # A direction on a compass used for placing images. See {Image#gravity}.
4
3
  #
5
4
  # * `:centre`
@@ -0,0 +1,46 @@
1
+ # This module provides an interface to the top level bits of libvips
2
+ # via ruby-ffi.
3
+ #
4
+ # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
9
+ module Vips
10
+ if Vips::at_least_libvips?(8, 9)
11
+ attach_function :vips_connection_filename, [:pointer], :string
12
+ attach_function :vips_connection_nick, [:pointer], :string
13
+ end
14
+
15
+ # Abstract base class for connections.
16
+ class Connection < Vips::Object
17
+ # The layout of the VipsRegion struct.
18
+ module ConnectionLayout
19
+ def self.included(base)
20
+ base.class_eval do
21
+ layout :parent, Vips::Object::Struct
22
+ # rest opaque
23
+ end
24
+ end
25
+ end
26
+
27
+ class Struct < Vips::Object::Struct
28
+ include ConnectionLayout
29
+ end
30
+
31
+ class ManagedStruct < Vips::Object::ManagedStruct
32
+ include ConnectionLayout
33
+ end
34
+
35
+ # Get any filename associated with a connection, or nil.
36
+ def filename
37
+ Vips::vips_connection_filename self
38
+ end
39
+
40
+ # Get a nickname (short description) of a connection that could be shown to
41
+ # the user.
42
+ def nick
43
+ Vips::vips_connection_nick self
44
+ end
45
+ end
46
+ end
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # Operations like {Image#flip} need to be told whether to flip
4
3
  # left-right or top-bottom.
5
4
  #
@@ -1,5 +1,4 @@
1
1
  module Vips
2
-
3
2
  # When the edges of an image are extended, you can specify
4
3
  # how you want the extension done.
5
4
  # See {Image#embed}, {Image#conv}, {Image#affine} and
@@ -8,7 +8,6 @@ require 'ffi'
8
8
  require 'forwardable'
9
9
 
10
10
  module GObject
11
-
12
11
  # we have a number of things we need to inherit in different ways:
13
12
  #
14
13
  # - we want to be able to subclass GObject in Ruby in a simple way
@@ -64,7 +63,6 @@ module GObject
64
63
  # the plain struct ... cast with this
65
64
  class Struct < FFI::Struct
66
65
  include GObjectLayout
67
-
68
66
  end
69
67
 
70
68
  # don't allow ptr == nil, we never want to allocate a GObject struct
@@ -75,6 +73,9 @@ module GObject
75
73
  def initialize ptr
76
74
  # GLib::logger.debug("GObject::GObject.initialize") {"ptr = #{ptr}"}
77
75
  @struct = ffi_managed_struct.new ptr
76
+
77
+ # sometimes we need to keep refs across C calls ... hide them here
78
+ @references = []
78
79
  end
79
80
 
80
81
  # access to the casting struct for this class
@@ -98,7 +99,6 @@ module GObject
98
99
  self.const_get :ManagedStruct
99
100
  end
100
101
  end
101
-
102
102
  end
103
103
 
104
104
  class GParamSpec < FFI::Struct
@@ -114,9 +114,13 @@ module GObject
114
114
  layout :value, GParamSpec.ptr
115
115
  end
116
116
 
117
- attach_function :g_param_spec_get_blurb, [GParamSpec.ptr], :string
117
+ attach_function :g_param_spec_get_blurb, [:pointer], :string
118
118
 
119
119
  attach_function :g_object_ref, [:pointer], :void
120
120
  attach_function :g_object_unref, [:pointer], :void
121
121
 
122
+ # we just use one gcallback type for every signal, hopefully this is OK
123
+ callback :gcallback, [:pointer], :void
124
+ attach_function :g_signal_connect_data,
125
+ [:pointer, :string, :gcallback, :pointer, :pointer, :int], :long
122
126
  end
@@ -6,7 +6,6 @@
6
6
  require 'ffi'
7
7
 
8
8
  module GObject
9
-
10
9
  # Represent a GValue. Example use:
11
10
  #
12
11
  # ```ruby
@@ -14,6 +13,8 @@ module GObject
14
13
  # gvalue.init GObject::GDOUBLE_TYPE
15
14
  # gvalue.set 3.1415
16
15
  # value = gvalue.get
16
+ # # optional -- drop any ref the gvalue had
17
+ # gvalue.unset
17
18
  # ```
18
19
  #
19
20
  # Lifetime is managed automatically. It doesn't know about all GType values,
@@ -28,7 +29,9 @@ module GObject
28
29
  value = value.to_s if value.is_a? Symbol
29
30
 
30
31
  if value.is_a? String
31
- value = Vips::vips_enum_from_nick "ruby-vips", gtype, value
32
+ # libvips expects "-" as a separator in enum names, but "_" is more
33
+ # convenient for ruby, eg. :b_w
34
+ value = Vips::vips_enum_from_nick "ruby-vips", gtype, value.tr("_", "-")
32
35
  if value == -1
33
36
  raise Vips::Error
34
37
  end
@@ -134,7 +137,7 @@ module GObject
134
137
  ptr.write_array_of_pointer value
135
138
 
136
139
  # the gvalue needs a ref on each of the images
137
- value.each {|image| ::GObject::g_object_ref image}
140
+ value.each { |image| ::GObject::g_object_ref image }
138
141
 
139
142
  when Vips::BLOB_TYPE
140
143
  len = value.bytesize
@@ -156,8 +159,7 @@ module GObject
156
159
 
157
160
  else
158
161
  raise Vips::Error, "unimplemented gtype for set: " +
159
- "#{::GObject::g_type_name gtype} (#{gtype})"
160
-
162
+ "#{::GObject::g_type_name gtype} (#{gtype})"
161
163
  end
162
164
  end
163
165
  end
@@ -233,8 +235,7 @@ module GObject
233
235
 
234
236
  else
235
237
  raise Vips::Error, "unimplemented gtype for get: " +
236
- "#{::GObject::g_type_name gtype} (#{gtype})"
237
-
238
+ "#{::GObject::g_type_name gtype} (#{gtype})"
238
239
  end
239
240
  end
240
241
 
@@ -243,9 +244,15 @@ module GObject
243
244
  # }
244
245
 
245
246
  return result
246
-
247
247
  end
248
248
 
249
+ # Clear the thing held by a GValue.
250
+ #
251
+ # This happens automatically when a GValue is GCed, but this method can be
252
+ # handy if you need to drop a reference explicitly for some reason.
253
+ def unset
254
+ ::GObject::g_value_unset self
255
+ end
249
256
  end
250
257
 
251
258
  attach_function :g_value_init, [GValue.ptr, :GType], :void
@@ -277,5 +284,4 @@ module GObject
277
284
  [:pointer, :string, GValue.ptr], :void
278
285
  attach_function :g_object_get_property,
279
286
  [:pointer, :string, GValue.ptr], :void
280
-
281
287
  end
@@ -14,6 +14,9 @@ module Vips
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,6 +25,11 @@ 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
34
  [:pointer, SizeStruct.ptr], :pointer
27
35
 
@@ -29,20 +37,14 @@ module Vips
29
37
  attach_function :vips_image_get,
30
38
  [:pointer, :string, GObject::GValue.ptr], :int
31
39
 
32
- # vips_image_get_fields was added in libvips 8.5
33
- begin
40
+ if Vips::at_least_libvips?(8, 5)
34
41
  attach_function :vips_image_get_fields, [:pointer], :pointer
35
- rescue FFI::NotFoundError
36
- nil
42
+ attach_function :vips_image_hasalpha, [:pointer], :int
37
43
  end
38
44
 
39
- # vips_addalpha was added in libvips 8.6
40
45
  if Vips::at_least_libvips?(8, 6)
41
46
  attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
42
47
  end
43
- if Vips::at_least_libvips?(8, 5)
44
- attach_function :vips_image_hasalpha, [:pointer], :int
45
- end
46
48
 
47
49
  attach_function :vips_image_set,
48
50
  [:pointer, :string, GObject::GValue.ptr], :void
@@ -53,6 +55,8 @@ module Vips
53
55
 
54
56
  attach_function :nickname_find, :vips_nickname_find, [:GType], :string
55
57
 
58
+ attach_function :vips_image_invalidate_all, [:pointer], :void
59
+
56
60
  # turn a raw pointer that must be freed into a self-freeing Ruby string
57
61
  def self.p2str(pointer)
58
62
  pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
@@ -67,6 +71,10 @@ module Vips
67
71
  class Image < Vips::Object
68
72
  alias_method :parent_get_typeof, :get_typeof
69
73
 
74
+ def close
75
+ Vips.vips_image_invalidate_all(self)
76
+ end
77
+
70
78
  private
71
79
 
72
80
  # the layout of the VipsImage struct
@@ -81,12 +89,10 @@ module Vips
81
89
 
82
90
  class Struct < Vips::Object::Struct
83
91
  include ImageLayout
84
-
85
92
  end
86
93
 
87
94
  class ManagedStruct < Vips::Object::ManagedStruct
88
95
  include ImageLayout
89
-
90
96
  end
91
97
 
92
98
  class GenericPtr < FFI::Struct
@@ -96,7 +102,7 @@ module Vips
96
102
  # handy for overloads ... want to be able to apply a function to an
97
103
  # array or to a scalar
98
104
  def self.smap x, &block
99
- x.is_a?(Array) ? x.map {|y| smap(y, &block)} : block.(x)
105
+ x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.(x)
100
106
  end
101
107
 
102
108
  def self.complex? format
@@ -117,7 +123,7 @@ module Vips
117
123
 
118
124
  unless Image::complex? image.format
119
125
  if image.bands % 2 != 0
120
- raise Error, "not an even number of bands"
126
+ raise Vips::Error, "not an even number of bands"
121
127
  end
122
128
 
123
129
  unless Image::float? image.format
@@ -147,46 +153,18 @@ module Vips
147
153
  end
148
154
  end
149
155
 
150
- # Write can fail due to no file descriptors and memory can fill if
151
- # large objects are not collected fairly soon. We can't try a
152
- # write and GC and retry on fail, since the write may take a
153
- # long time and may not be repeatable.
154
- #
155
- # GCing before every write would have a horrible effect on
156
- # performance, so as a compromise we GC every @@gc_interval writes.
157
- #
158
- # ruby2.1 introduced a generational GC which is fast enough to be
159
- # able to GC on every write.
160
-
161
- @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
162
-
163
- @@gc_interval = 100
164
- @@gc_countdown = @@gc_interval
165
-
166
- def write_gc
167
- if @@generational_gc
168
- GC.start full_mark: false
169
- else
170
- @@gc_countdown -= 1
171
- if @@gc_countdown < 0
172
- @@gc_countdown = @@gc_interval
173
- GC.start
174
- end
175
- end
176
- end
177
-
178
156
  public
179
157
 
180
158
  def inspect
181
- "#<Image #{width}x#{height} #{format}, #{bands} bands, " +
182
- "#{interpretation}>"
159
+ "#<Image #{width}x#{height} #{format}, #{bands} bands, #{interpretation}>"
183
160
  end
184
161
 
185
162
  def respond_to? name, include_all = false
186
163
  # To support keyword args, we need to tell Ruby that final image
187
164
  # arguments cannot be hashes of keywords.
188
165
  #
189
- # https://makandracards.com/makandra/36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
166
+ # https://makandracards.com/makandra/
167
+ # 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
190
168
  return false if name == :to_hash
191
169
 
192
170
  # respond to all vips operations by nickname
@@ -221,13 +199,13 @@ module Vips
221
199
  # load options, for example:
222
200
  #
223
201
  # ```
224
- # image = Vips::new_from_file "fred.jpg[shrink=2]"
202
+ # image = Vips::Image.new_from_file "fred.jpg[shrink=2]"
225
203
  # ```
226
204
  #
227
205
  # You can also supply options as a hash, for example:
228
206
  #
229
207
  # ```
230
- # image = Vips::new_from_file "fred.jpg", shrink: 2
208
+ # image = Vips::Image.new_from_file "fred.jpg", shrink: 2
231
209
  # ```
232
210
  #
233
211
  # The full set of options available depend upon the load operation that
@@ -298,11 +276,50 @@ module Vips
298
276
  # @return [Image] the loaded image
299
277
  def self.new_from_buffer data, option_string, **opts
300
278
  loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
301
- raise Vips::Error if loader == nil
279
+ raise Vips::Error if loader.nil?
302
280
 
303
281
  Vips::Operation.call loader, [data], opts, option_string
304
282
  end
305
283
 
284
+ # Create a new {Image} from a source. Load options may be passed as
285
+ # strings or appended as a hash. For example:
286
+ #
287
+ # ```
288
+ # source = Vips::Source.new_from_file("k2.jpg")
289
+ # image = Vips::Image.new_from_source source, "shrink=2"
290
+ # ```
291
+ #
292
+ # or alternatively:
293
+ #
294
+ # ```
295
+ # image = Vips::Image.new_from_source source, "", shrink: 2
296
+ # ```
297
+ #
298
+ # The options available depend on the file format. Try something like:
299
+ #
300
+ # ```
301
+ # $ vips jpegload_source
302
+ # ```
303
+ #
304
+ # at the command-line to see the available options. Not all loaders
305
+ # support load from source, but at least JPEG, PNG and
306
+ # TIFF images will work.
307
+ #
308
+ # Loading is fast: only enough data is read to be able to fill
309
+ # out the header. Pixels will only be read and decompressed when they are
310
+ # needed.
311
+ #
312
+ # @param source [Vips::Source] the source to load from
313
+ # @param option_string [String] load options as a string
314
+ # @macro vips.loadopts
315
+ # @return [Image] the loaded image
316
+ def self.new_from_source source, option_string, **opts
317
+ loader = Vips::vips_foreign_find_load_source source
318
+ raise Vips::Error if loader.nil?
319
+
320
+ Vips::Operation.call loader, [source], opts, option_string
321
+ end
322
+
306
323
  def self.matrix_from_array width, height, array
307
324
  ptr = FFI::MemoryPointer.new :double, array.length
308
325
  ptr.write_array_of_double array
@@ -319,13 +336,13 @@ module Vips
319
336
  # For example:
320
337
  #
321
338
  # ```
322
- # image = Vips::new_from_array [1, 2, 3]
339
+ # image = Vips::Image.new_from_array [1, 2, 3]
323
340
  # ```
324
341
  #
325
342
  # or
326
343
  #
327
344
  # ```
328
- # image = Vips::new_from_array [
345
+ # image = Vips::Image.new_from_array [
329
346
  # [-1, -1, -1],
330
347
  # [-1, 16, -1],
331
348
  # [-1, -1, -1]], 8
@@ -347,19 +364,20 @@ module Vips
347
364
  if array[0].is_a? Array
348
365
  height = array.length
349
366
  width = array[0].length
350
- unless array.all? {|x| x.is_a? Array}
367
+ unless array.all? { |x| x.is_a? Array }
351
368
  raise Vips::Error, "Not a 2D array."
352
369
  end
353
- unless array.all? {|x| x.length == width}
370
+ unless array.all? { |x| x.length == width }
354
371
  raise Vips::Error, "Array not rectangular."
355
372
  end
373
+
356
374
  array = array.flatten
357
375
  else
358
376
  height = 1
359
377
  width = array.length
360
378
  end
361
379
 
362
- unless array.all? {|x| x.is_a? Numeric}
380
+ unless array.all? { |x| x.is_a? Numeric }
363
381
  raise Vips::Error, "Not all array elements are Numeric."
364
382
  end
365
383
 
@@ -385,8 +403,8 @@ module Vips
385
403
  def new_from_image value
386
404
  pixel = (Vips::Image.black(1, 1) + value).cast(format)
387
405
  image = pixel.embed 0, 0, width, height, extend: :copy
388
- image.copy interpretation: interpretation,
389
- xres: xres, yres: yres, xoffset: xoffset, yoffset: yoffset
406
+ image.copy interpretation: interpretation, xres: xres, yres: yres,
407
+ xoffset: xoffset, yoffset: yoffset
390
408
  end
391
409
 
392
410
  # Write this image to a file. Save options may be encoded in the
@@ -427,8 +445,6 @@ module Vips
427
445
  end
428
446
 
429
447
  Vips::Operation.call saver, [self, filename], opts, option_string
430
-
431
- write_gc
432
448
  end
433
449
 
434
450
  # Write this image to a memory buffer. Save options may be encoded in
@@ -457,29 +473,63 @@ module Vips
457
473
  # @macro vips.saveopts
458
474
  # @return [String] the image saved in the specified format
459
475
  def write_to_buffer format_string, **opts
460
- filename =
461
- Vips::p2str(Vips::vips_filename_get_filename format_string)
462
- option_string =
463
- Vips::p2str(Vips::vips_filename_get_options format_string)
476
+ filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
477
+ option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
464
478
  saver = Vips::vips_foreign_find_save_buffer filename
465
479
  if saver == nil
466
- raise Vips::Error, "No known saver for '#{filename}'."
480
+ raise Vips::Error, "No known buffer saver for '#{filename}'."
467
481
  end
468
482
 
469
483
  buffer = Vips::Operation.call saver, [self], opts, option_string
470
484
  raise Vips::Error if buffer == nil
471
485
 
472
- write_gc
473
-
474
486
  return buffer
475
487
  end
476
488
 
489
+ # Write this image to a target. Save options may be encoded in
490
+ # the format_string or given as a hash. For example:
491
+ #
492
+ # ```ruby
493
+ # target = Vips::Target.new_to_file "k2.jpg"
494
+ # image.write_to_target target, ".jpg[Q=90]"
495
+ # ```
496
+ #
497
+ # or equivalently:
498
+ #
499
+ # ```ruby
500
+ # image.write_to_target target, ".jpg", Q: 90
501
+ # ```
502
+ #
503
+ # The full set of save options depend on the selected saver. Try
504
+ # something like:
505
+ #
506
+ # ```
507
+ # $ vips jpegsave_target
508
+ # ```
509
+ #
510
+ # to see all the available options for JPEG save.
511
+ #
512
+ # @param target [Vips::Target] the target to write to
513
+ # @param format_string [String] save format plus string options
514
+ # @macro vips.saveopts
515
+ def write_to_target target, format_string, **opts
516
+ filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
517
+ option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
518
+ saver = Vips::vips_foreign_find_save_target filename
519
+ if saver == nil
520
+ raise Vips::Error, "No known target saver for '#{filename}'."
521
+ end
522
+
523
+ Vips::Operation.call saver, [self, target], opts, option_string
524
+ end
525
+
477
526
  # Write this image to a large memory buffer.
478
527
  #
479
528
  # @return [String] the pixels as a huge binary string
480
529
  def write_to_memory
481
530
  len = Vips::SizeStruct.new
482
531
  ptr = Vips::vips_image_write_to_memory self, len
532
+ raise Vips::Error if ptr == nil
483
533
 
484
534
  # wrap up as an autopointer
485
535
  ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
@@ -487,6 +537,28 @@ module Vips
487
537
  ptr.get_bytes 0, len[:value]
488
538
  end
489
539
 
540
+ # Turn progress signalling on and off.
541
+ #
542
+ # If this is on, the most-downstream image from this image will issue
543
+ # progress signals.
544
+ #
545
+ # @see Object#signal_connect
546
+ # @param state [Boolean] progress signalling state
547
+ def set_progress state
548
+ Vips::vips_image_set_progress self, state
549
+ end
550
+
551
+ # Kill computation of this time.
552
+ #
553
+ # Set true to stop computation of this image. You can call this from a
554
+ # progress handler, for example.
555
+ #
556
+ # @see Object#signal_connect
557
+ # @param kill [Boolean] stop computation
558
+ def set_kill kill
559
+ Vips::vips_image_set_kill self, kill
560
+ end
561
+
490
562
  # Get the `GType` of a metadata field. The result is 0 if no such field
491
563
  # exists.
492
564
  #
@@ -525,10 +597,11 @@ module Vips
525
597
  end
526
598
 
527
599
  gvalue = GObject::GValue.alloc
528
- result = Vips::vips_image_get self, name, gvalue
529
- raise Vips::Error if result != 0
600
+ raise Vips::Error if Vips::vips_image_get(self, name, gvalue) != 0
601
+ result = gvalue.get
602
+ gvalue.unset
530
603
 
531
- gvalue.get
604
+ result
532
605
  end
533
606
 
534
607
  # Get the names of all fields on an image. Use this to loop over all
@@ -573,6 +646,7 @@ module Vips
573
646
  gvalue.init gtype
574
647
  gvalue.set value
575
648
  Vips::vips_image_set self, name, gvalue
649
+ gvalue.unset
576
650
  end
577
651
 
578
652
  # Set the value of a metadata item on an image. The metadata item must
@@ -772,7 +846,7 @@ module Vips
772
846
  # @return [Image] result of subtraction
773
847
  def - other
774
848
  other.is_a?(Vips::Image) ?
775
- subtract(other) : linear(1, Image::smap(other) {|x| x * -1})
849
+ subtract(other) : linear(1, Image::smap(other) { |x| x * -1 })
776
850
  end
777
851
 
778
852
  # Multiply an image, constant or array.
@@ -790,7 +864,7 @@ module Vips
790
864
  # @return [Image] result of division
791
865
  def / other
792
866
  other.is_a?(Vips::Image) ?
793
- divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0)
867
+ divide(other) : linear(Image::smap(other) { |x| 1.0 / x }, 0)
794
868
  end
795
869
 
796
870
  # Remainder after integer division with an image, constant or array.
@@ -961,16 +1035,16 @@ module Vips
961
1035
 
962
1036
  # make the template for unpack
963
1037
  template = {
964
- :char => 'c',
965
- :uchar => 'C',
966
- :short => 's_',
967
- :ushort => 'S_',
968
- :int => 'i_',
969
- :uint => 'I_',
970
- :float => 'f',
971
- :double => 'd',
972
- :complex => 'f',
973
- :dpcomplex => 'd'
1038
+ char: 'c',
1039
+ uchar: 'C',
1040
+ short: 's_',
1041
+ ushort: 'S_',
1042
+ int: 'i_',
1043
+ uint: 'I_',
1044
+ float: 'f',
1045
+ double: 'd',
1046
+ complex: 'f',
1047
+ dpcomplex: 'd'
974
1048
  }[format] + '*'
975
1049
 
976
1050
  # and unpack into something like [1, 2, 3, 4 ..]
@@ -1031,7 +1105,7 @@ module Vips
1031
1105
  #
1032
1106
  # @return [Array<Image>] Array of n one-band images
1033
1107
  def bandsplit
1034
- (0...bands).map {|i| extract_band i}
1108
+ (0...bands).map { |i| extract_band i }
1035
1109
  end
1036
1110
 
1037
1111
  # Join a set of images bandwise.
@@ -1044,7 +1118,7 @@ module Vips
1044
1118
  end
1045
1119
 
1046
1120
  # if other is just Numeric, we can use bandjoin_const
1047
- not_all_real = !other.all?{|x| x.is_a? Numeric}
1121
+ not_all_real = !other.all? { |x| x.is_a? Numeric }
1048
1122
 
1049
1123
  if not_all_real
1050
1124
  Vips::Image.bandjoin([self] + other)
@@ -1061,7 +1135,7 @@ module Vips
1061
1135
  # @option opts [Vips::Interpretation] :compositing_space Composite images in this colour space
1062
1136
  # @option opts [Boolean] :premultiplied Images have premultiplied alpha
1063
1137
  # @return [Image] blended image
1064
- def composite overlay, mode, **opts
1138
+ def composite overlay, mode, **options
1065
1139
  unless overlay.is_a? Array
1066
1140
  overlay = [overlay]
1067
1141
  end
@@ -1073,7 +1147,7 @@ module Vips
1073
1147
  GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1074
1148
  end
1075
1149
 
1076
- Vips::Image.composite([self] + overlay, mode, opts)
1150
+ Vips::Image.composite([self] + overlay, mode, **options)
1077
1151
  end
1078
1152
 
1079
1153
  # Return the coordinates of the image maximum.
@@ -1130,7 +1204,7 @@ module Vips
1130
1204
  # @see xyz
1131
1205
  # @return [Image] image converted to polar coordinates
1132
1206
  def polar
1133
- Image::run_cmplx(self) {|x| x.complex :polar}
1207
+ Image::run_cmplx(self) { |x| x.complex :polar }
1134
1208
  end
1135
1209
 
1136
1210
  # Return an image with polar pixels converted to rectangular.
@@ -1143,7 +1217,7 @@ module Vips
1143
1217
  # @see xyz
1144
1218
  # @return [Image] image converted to rectangular coordinates
1145
1219
  def rect
1146
- Image::run_cmplx(self) {|x| x.complex :rect}
1220
+ Image::run_cmplx(self) { |x| x.complex :rect }
1147
1221
  end
1148
1222
 
1149
1223
  # Return the complex conjugate of an image.
@@ -1155,7 +1229,7 @@ module Vips
1155
1229
  #
1156
1230
  # @return [Image] complex conjugate
1157
1231
  def conj
1158
- Image::run_cmplx(self) {|x| x.complex :conj}
1232
+ Image::run_cmplx(self) { |x| x.complex :conj }
1159
1233
  end
1160
1234
 
1161
1235
  # Calculate the cross phase of two images.
@@ -1305,7 +1379,7 @@ module Vips
1305
1379
  # @option opts [Boolean] :blend (false) Blend smoothly between th and el
1306
1380
  # @return [Image] merged image
1307
1381
  def ifthenelse(th, el, **opts)
1308
- match_image = [th, el, self].find {|x| x.is_a? Vips::Image}
1382
+ match_image = [th, el, self].find { |x| x.is_a? Vips::Image }
1309
1383
 
1310
1384
  unless th.is_a? Vips::Image
1311
1385
  th = Operation.imageize match_image, th
@@ -1322,148 +1396,127 @@ module Vips
1322
1396
  #
1323
1397
  # @param opts [Hash] Set of options
1324
1398
  # @return [Vips::Image] Output image
1325
- def scaleimage **opts
1326
- Vips::Image.scale self, opts
1399
+ def scaleimage **options
1400
+ Vips::Image.scale self, **options
1327
1401
  end
1328
-
1329
1402
  end
1330
1403
  end
1331
1404
 
1332
1405
  module Vips
1333
-
1334
- # This method generates yard comments for all the dynamically bound
1406
+ # This module generates yard comments for all the dynamically bound
1335
1407
  # vips operations.
1336
1408
  #
1337
1409
  # Regenerate with something like:
1338
1410
  #
1339
1411
  # ```
1340
1412
  # $ ruby > methods.rb
1341
- # require 'vips'; Vips::generate_yard
1413
+ # require 'vips'; Vips::Yard.generate
1342
1414
  # ^D
1343
1415
  # ```
1344
1416
 
1345
- def self.generate_yard
1346
- # these have hand-written methods, see above
1347
- no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
1348
-
1417
+ module Yard
1349
1418
  # map gobject's type names to Ruby
1350
- map_go_to_ruby = {
1351
- "gboolean" => "Boolean",
1352
- "gint" => "Integer",
1353
- "gdouble" => "Float",
1354
- "gfloat" => "Float",
1355
- "gchararray" => "String",
1356
- "VipsImage" => "Vips::Image",
1357
- "VipsInterpolate" => "Vips::Interpolate",
1358
- "VipsArrayDouble" => "Array<Double>",
1359
- "VipsArrayInt" => "Array<Integer>",
1360
- "VipsArrayImage" => "Array<Image>",
1361
- "VipsArrayString" => "Array<String>",
1419
+ MAP_GO_TO_RUBY = {
1420
+ "gboolean" => "Boolean",
1421
+ "gint" => "Integer",
1422
+ "gdouble" => "Float",
1423
+ "gfloat" => "Float",
1424
+ "gchararray" => "String",
1425
+ "VipsImage" => "Vips::Image",
1426
+ "VipsInterpolate" => "Vips::Interpolate",
1427
+ "VipsConnection" => "Vips::Connection",
1428
+ "VipsSource" => "Vips::Source",
1429
+ "VipsTarget" => "Vips::Target",
1430
+ "VipsSourceCustom" => "Vips::SourceCustom",
1431
+ "VipsTargetCustom" => "Vips::TargetCustom",
1432
+ "VipsArrayDouble" => "Array<Double>",
1433
+ "VipsArrayInt" => "Array<Integer>",
1434
+ "VipsArrayImage" => "Array<Image>",
1435
+ "VipsArrayString" => "Array<String>",
1362
1436
  }
1363
1437
 
1364
- generate_operation = lambda do |gtype, nickname, op|
1365
- op_flags = op.get_flags
1366
- return if (op_flags & OPERATION_DEPRECATED) != 0
1367
- return if no_generate.include? nickname
1368
- description = Vips::vips_object_get_description op
1369
-
1370
- # find and classify all the arguments the operator can take
1371
- required_input = []
1372
- optional_input = []
1373
- required_output = []
1374
- optional_output = []
1375
- member_x = nil
1376
- op.argument_map do |pspec, argument_class, argument_instance|
1377
- arg_flags = argument_class[:flags]
1378
- next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1379
- next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1380
-
1381
- name = pspec[:name].tr("-", "_")
1382
- # 'in' as a param name confuses yard
1383
- name = "im" if name == "in"
1384
- gtype = pspec[:value_type]
1385
- fundamental = GObject::g_type_fundamental gtype
1386
- type_name = GObject::g_type_name gtype
1387
- if map_go_to_ruby.include? type_name
1388
- type_name = map_go_to_ruby[type_name]
1389
- end
1390
- if fundamental == GObject::GFLAGS_TYPE ||
1391
- fundamental == GObject::GENUM_TYPE
1392
- type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1393
- end
1394
- blurb = GObject::g_param_spec_get_blurb pspec
1395
- value = {:name => name,
1396
- :flags => arg_flags,
1397
- :gtype => gtype,
1398
- :type_name => type_name,
1399
- :blurb => blurb}
1400
-
1401
- if (arg_flags & ARGUMENT_INPUT) != 0
1402
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1403
- # note the first required input image, if any ... we
1404
- # will be a method of this instance
1405
- if !member_x && gtype == Vips::IMAGE_TYPE
1406
- member_x = value
1407
- else
1408
- required_input << value
1409
- end
1410
- else
1411
- optional_input << value
1412
- end
1413
- end
1438
+ # these have hand-written methods, see above
1439
+ NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
1414
1440
 
1415
- # MODIFY INPUT args count as OUTPUT as well
1416
- if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
1417
- ((arg_flags & ARGUMENT_INPUT) != 0 &&
1418
- (arg_flags & ARGUMENT_MODIFY) != 0)
1419
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1420
- required_output << value
1421
- else
1422
- optional_output << value
1423
- end
1424
- end
1441
+ # these are aliased (appear under several names)
1442
+ ALIAS = ["crop"]
1425
1443
 
1444
+ # turn a gtype into a ruby type name
1445
+ def self.gtype_to_ruby gtype
1446
+ fundamental = GObject::g_type_fundamental gtype
1447
+ type_name = GObject::g_type_name gtype
1448
+
1449
+ if MAP_GO_TO_RUBY.include? type_name
1450
+ type_name = MAP_GO_TO_RUBY[type_name]
1451
+ end
1452
+
1453
+ if fundamental == GObject::GFLAGS_TYPE ||
1454
+ fundamental == GObject::GENUM_TYPE
1455
+ type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1426
1456
  end
1427
1457
 
1458
+ type_name
1459
+ end
1460
+
1461
+ def self.generate_operation introspect
1462
+ return if (introspect.flags & OPERATION_DEPRECATED) != 0
1463
+ return if NO_GENERATE.include? introspect.name
1464
+
1465
+ method_args = introspect.method_args
1466
+ required_output = introspect.required_output
1467
+ optional_input = introspect.optional_input
1468
+ optional_output = introspect.optional_output
1469
+
1428
1470
  print "# @!method "
1429
- print "self." unless member_x
1430
- print "#{nickname}("
1431
- print required_input.map{|x| x[:name]}.join(", ")
1432
- print ", " if required_input.length > 0
1471
+ print "self." unless introspect.member_x
1472
+ print "#{introspect.name}("
1473
+ print method_args.map{ |x| x[:yard_name] }.join(", ")
1474
+ print ", " if method_args.length > 0
1433
1475
  puts "**opts)"
1434
1476
 
1435
- puts "# #{description.capitalize}."
1477
+ puts "# #{introspect.description.capitalize}."
1436
1478
 
1437
- required_input.each do |arg|
1438
- puts "# @param #{arg[:name]} [#{arg[:type_name]}] " +
1439
- "#{arg[:blurb]}"
1479
+ method_args.each do |details|
1480
+ yard_name = details[:yard_name]
1481
+ gtype = details[:gtype]
1482
+ blurb = details[:blurb]
1483
+
1484
+ puts "# @param #{yard_name} [#{gtype_to_ruby(gtype)}] #{blurb}"
1440
1485
  end
1441
1486
 
1442
1487
  puts "# @param opts [Hash] Set of options"
1443
- optional_input.each do |arg|
1444
- puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
1445
- "#{arg[:blurb]}"
1488
+ optional_input.each do |arg_name, details|
1489
+ yard_name = details[:yard_name]
1490
+ gtype = details[:gtype]
1491
+ blurb = details[:blurb]
1492
+
1493
+ puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} " +
1494
+ "#{blurb}"
1446
1495
  end
1447
- optional_output.each do |arg|
1448
- print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
1449
- puts " Output #{arg[:blurb]}"
1496
+ optional_output.each do |arg_name, details|
1497
+ yard_name = details[:yard_name]
1498
+ gtype = details[:gtype]
1499
+ blurb = details[:blurb]
1500
+
1501
+ print "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name}"
1502
+ puts " Output #{blurb}"
1450
1503
  end
1451
1504
 
1452
1505
  print "# @return ["
1453
1506
  if required_output.length == 0
1454
1507
  print "nil"
1455
1508
  elsif required_output.length == 1
1456
- print required_output.first[:type_name]
1457
- elsif
1458
- print "Array<"
1459
- print required_output.map{|x| x[:type_name]}.join(", ")
1509
+ print gtype_to_ruby(required_output.first[:gtype])
1510
+ else
1511
+ print "Array<"
1512
+ print required_output.map{ |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1460
1513
  print ">"
1461
1514
  end
1462
1515
  if optional_output.length > 0
1463
1516
  print ", Hash<Symbol => Object>"
1464
1517
  end
1465
1518
  print "] "
1466
- print required_output.map{|x| x[:blurb]}.join(", ")
1519
+ print required_output.map{ |x| x[:blurb] }.join(", ")
1467
1520
  if optional_output.length > 0
1468
1521
  print ", " if required_output.length > 0
1469
1522
  print "Hash of optional output items"
@@ -1473,30 +1526,42 @@ module Vips
1473
1526
  puts ""
1474
1527
  end
1475
1528
 
1476
- generate_class = lambda do |gtype, a|
1477
- nickname = Vips::nickname_find gtype
1529
+ def self.generate
1530
+ alias_gtypes = {}
1531
+ ALIAS.each do |name|
1532
+ gtype = Vips::type_find "VipsOperation", name
1533
+ alias_gtypes[gtype] = name
1534
+ end
1478
1535
 
1479
- if nickname
1480
- begin
1481
- # can fail for abstract types
1482
- op = Vips::Operation.new nickname
1483
- rescue
1536
+ generate_class = lambda do |gtype, _|
1537
+ if alias_gtypes.key? gtype
1538
+ name = alias_gtypes[gtype]
1539
+ else
1540
+ name = Vips::nickname_find gtype
1484
1541
  end
1485
1542
 
1486
- generate_operation.(gtype, nickname, op) if op
1487
- end
1543
+ if name
1544
+ begin
1545
+ # can fail for abstract types
1546
+ introspect = Vips::Introspect.get_yard name
1547
+ rescue Vips::Error
1548
+ nil
1549
+ end
1488
1550
 
1489
- Vips::vips_type_map gtype, generate_class, nil
1490
- end
1551
+ generate_operation(introspect) if introspect
1552
+ end
1491
1553
 
1492
- puts "module Vips"
1493
- puts " class Image"
1494
- puts ""
1554
+ Vips::vips_type_map gtype, generate_class, nil
1555
+ end
1495
1556
 
1496
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1557
+ puts "module Vips"
1558
+ puts " class Image"
1559
+ puts ""
1497
1560
 
1498
- puts " end"
1499
- puts "end"
1500
- end
1561
+ generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1501
1562
 
1563
+ puts " end"
1564
+ puts "end"
1565
+ end
1566
+ end
1502
1567
  end