vips 8.8.0.3 → 8.10.5

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