vips 8.11.3 → 8.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,8 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
+ require "set"
8
9
 
9
10
  module Vips
10
11
  private
@@ -12,22 +13,22 @@ module Vips
12
13
  attach_function :vips_operation_new, [:string], :pointer
13
14
 
14
15
  # We may well block during this (eg. if it's avg, or perhaps jpegsave), and
15
- # libvips might trigger some signals which ruby has handles for.
16
+ # libvips might trigger some signals which ruby has handles for.
16
17
  #
17
- # We need FFI to drop the GIL lock during this call and reacquire it when
18
+ # We need FFI to drop the GIL lock during this call and reacquire it when
18
19
  # the call ends, or we'll deadlock.
19
- attach_function :vips_cache_operation_build, [:pointer], :pointer,
20
+ attach_function :vips_cache_operation_build, [:pointer], :pointer,
20
21
  blocking: true
21
22
  attach_function :vips_object_unref_outputs, [:pointer], :void
22
23
 
23
24
  callback :argument_map_fn, [:pointer,
24
- GObject::GParamSpec.ptr,
25
- ArgumentClass.ptr,
26
- ArgumentInstance.ptr,
27
- :pointer, :pointer], :pointer
25
+ GObject::GParamSpec.ptr,
26
+ ArgumentClass.ptr,
27
+ ArgumentInstance.ptr,
28
+ :pointer, :pointer], :pointer
28
29
  attach_function :vips_argument_map, [:pointer,
29
- :argument_map_fn,
30
- :pointer, :pointer], :pointer
30
+ :argument_map_fn,
31
+ :pointer, :pointer], :pointer
31
32
 
32
33
  OPERATION_SEQUENTIAL = 1
33
34
  OPERATION_NOCACHE = 4
@@ -45,14 +46,26 @@ module Vips
45
46
  # everything we know about it. This is used for doc generation as well as
46
47
  # call.
47
48
  class Introspect
48
- attr_reader :name, :description, :flags, :args, :required_input,
49
- :optional_input, :required_output, :optional_output, :member_x,
50
- :method_args
49
+ attr_reader :name, :description, :flags, :args, :required_input,
50
+ :optional_input, :required_output, :optional_output, :member_x,
51
+ :method_args, :vips_name, :destructive, :doc_optional_input,
52
+ :doc_optional_output
51
53
 
52
54
  @@introspect_cache = {}
53
55
 
54
56
  def initialize name
55
- @op = Operation.new name
57
+ # if there's a trailing "!", this is a destructive version of an
58
+ # operation
59
+ if name[-1] == "!"
60
+ @destructive = true
61
+ # strip the trailing "!"
62
+ @vips_name = name[0...-1]
63
+ else
64
+ @destructive = false
65
+ @vips_name = name
66
+ end
67
+
68
+ @op = Operation.new @vips_name
56
69
  @args = []
57
70
  @required_input = []
58
71
  @optional_input = {}
@@ -66,12 +79,14 @@ module Vips
66
79
  # names can include - as punctuation, but we always use _ in
67
80
  # Ruby
68
81
  arg_name = pspec[:name].tr("-", "_")
69
- args << {
70
- :arg_name => arg_name,
71
- :flags => flags,
72
- :gtype => pspec[:value_type]
82
+ @args << {
83
+ arg_name: arg_name,
84
+ flags: flags,
85
+ gtype: pspec[:value_type]
73
86
  }
74
87
  end
88
+
89
+ nil
75
90
  end
76
91
 
77
92
  @args.each do |details|
@@ -79,26 +94,27 @@ module Vips
79
94
  flags = details[:flags]
80
95
 
81
96
  if (flags & ARGUMENT_INPUT) != 0
82
- if (flags & ARGUMENT_REQUIRED) != 0 &&
83
- (flags & ARGUMENT_DEPRECATED) == 0
97
+ if (flags & ARGUMENT_REQUIRED) != 0 &&
98
+ (flags & ARGUMENT_DEPRECATED) == 0
84
99
  @required_input << details
85
100
  else
86
101
  # we allow deprecated optional args
87
102
  @optional_input[arg_name] = details
88
103
  end
89
104
 
90
- # MODIFY INPUT args count as OUTPUT as well
91
- if (flags & ARGUMENT_MODIFY) != 0
92
- if (flags & ARGUMENT_REQUIRED) != 0 &&
93
- (flags & ARGUMENT_DEPRECATED) == 0
105
+ # MODIFY INPUT args count as OUTPUT as well in non-destructive mode
106
+ if (flags & ARGUMENT_MODIFY) != 0 &&
107
+ !@destructive
108
+ if (flags & ARGUMENT_REQUIRED) != 0 &&
109
+ (flags & ARGUMENT_DEPRECATED) == 0
94
110
  @required_output << details
95
111
  else
96
112
  @optional_output[arg_name] = details
97
113
  end
98
114
  end
99
115
  elsif (flags & ARGUMENT_OUTPUT) != 0
100
- if (flags & ARGUMENT_REQUIRED) != 0 &&
101
- (flags & ARGUMENT_DEPRECATED) == 0
116
+ if (flags & ARGUMENT_REQUIRED) != 0 &&
117
+ (flags & ARGUMENT_DEPRECATED) == 0
102
118
  @required_output << details
103
119
  else
104
120
  # again, allow deprecated optional args
@@ -106,6 +122,16 @@ module Vips
106
122
  end
107
123
  end
108
124
  end
125
+
126
+ # in destructive mode, the first required input arg must be MODIFY and
127
+ # must be an image
128
+ if @destructive
129
+ if @required_input.length < 1 ||
130
+ @required_input[0][:flags] & ARGUMENT_MODIFY == 0 ||
131
+ @required_input[0][:gtype] != IMAGE_TYPE
132
+ raise Vips::Error, "operation #{@vips_name} is not destructive"
133
+ end
134
+ end
109
135
  end
110
136
 
111
137
  # Yard comment generation needs a little more introspection. We add this
@@ -113,10 +139,12 @@ module Vips
113
139
  # we can.
114
140
  def add_yard_introspection name
115
141
  @name = name
116
- @description = Vips::vips_object_get_description @op
117
- @flags = Vips::vips_operation_get_flags @op
142
+ @description = Vips.vips_object_get_description @op
143
+ @flags = Vips.vips_operation_get_flags @op
118
144
  @member_x = nil
119
145
  @method_args = []
146
+ @doc_optional_input = {}
147
+ @doc_optional_output = {}
120
148
 
121
149
  @args.each do |details|
122
150
  arg_name = details[:arg_name]
@@ -125,20 +153,31 @@ module Vips
125
153
 
126
154
  details[:yard_name] = arg_name == "in" ? "im" : arg_name
127
155
  pspec = @op.get_pspec arg_name
128
- details[:blurb] = GObject::g_param_spec_get_blurb pspec
156
+ details[:blurb] = GObject.g_param_spec_get_blurb pspec
129
157
 
130
- if (flags & ARGUMENT_INPUT) != 0 &&
131
- (flags & ARGUMENT_REQUIRED) != 0 &&
132
- (flags & ARGUMENT_DEPRECATED) == 0
133
- # the first required input image is the thing we will be a method
158
+ if (flags & ARGUMENT_INPUT) != 0 &&
159
+ (flags & ARGUMENT_REQUIRED) != 0 &&
160
+ (flags & ARGUMENT_DEPRECATED) == 0
161
+ # the first required input image is the thing we will be a method
134
162
  # of
135
- if @member_x == nil && gtype == IMAGE_TYPE
136
- @member_x = details
163
+ if @member_x.nil? && gtype == IMAGE_TYPE
164
+ @member_x = details
137
165
  else
138
166
  @method_args << details
139
167
  end
140
168
  end
141
169
  end
170
+
171
+ # and make the arg sets to document by filtering out deprecated args
172
+ @optional_input.each do |arg_name, details|
173
+ next if (details[:flags] & ARGUMENT_DEPRECATED) != 0
174
+ @doc_optional_input[details[:arg_name]] = details
175
+ end
176
+
177
+ @optional_output.each do |arg_name, details|
178
+ next if (details[:flags] & ARGUMENT_DEPRECATED) != 0
179
+ @doc_optional_output[details[:arg_name]] = details
180
+ end
142
181
  end
143
182
 
144
183
  def self.get name
@@ -150,7 +189,6 @@ module Vips
150
189
  introspect.add_yard_introspection name
151
190
  introspect
152
191
  end
153
-
154
192
  end
155
193
 
156
194
  class Operation < Object
@@ -176,7 +214,7 @@ module Vips
176
214
  # allow init with a pointer so we can wrap the return values from
177
215
  # things like _build
178
216
  if value.is_a? String
179
- value = Vips::vips_operation_new value
217
+ value = Vips.vips_operation_new value
180
218
  raise Vips::Error if value.null?
181
219
  end
182
220
 
@@ -184,66 +222,65 @@ module Vips
184
222
  end
185
223
 
186
224
  def build
187
- op = Vips::vips_cache_operation_build self
225
+ op = Vips.vips_cache_operation_build self
188
226
  if op.null?
189
- Vips::vips_object_unref_outputs self
227
+ Vips.vips_object_unref_outputs self
190
228
  raise Vips::Error
191
229
  end
192
230
 
193
- return Operation.new op
231
+ Operation.new op
194
232
  end
195
233
 
196
234
  def argument_map &block
197
- fn = Proc.new do |_op, pspec, argument_class, argument_instance, _a, _b|
235
+ fn = proc do |_op, pspec, argument_class, argument_instance, _a, _b|
198
236
  block.call pspec, argument_class, argument_instance
199
237
  end
200
-
201
- Vips::vips_argument_map self, fn, nil, nil
238
+ Vips.vips_argument_map self, fn, nil, nil
202
239
  end
203
240
 
204
- # Search an object for the first element to match a predicate. Search
241
+ # Search an object for the first element to match a predicate. Search
205
242
  # inside subarrays and sub-hashes. Equlvalent to x.flatten.find{}.
206
243
  def self.flat_find object, &block
207
244
  if object.respond_to? :each
208
- object.each do |x|
209
- result = flat_find x, &block
210
- return result if result != nil
245
+ object.each do |x|
246
+ result = flat_find x, &block
247
+ return result unless result.nil?
211
248
  end
212
- else
213
- return object if yield object
249
+ elsif yield object
250
+ return object
214
251
  end
215
252
 
216
- return nil
253
+ nil
217
254
  end
218
255
 
219
256
  # expand a constant into an image
220
257
  def self.imageize match_image, value
221
- return value if value.is_a? Image
258
+ return value if value.is_a?(Image) || value.is_a?(MutableImage)
222
259
 
223
260
  # 2D array values become tiny 2D images
224
261
  # if there's nothing to match to, we also make a 2D image
225
- if (value.is_a?(Array) && value[0].is_a?(Array)) ||
226
- match_image == nil
227
- return Image.new_from_array value
262
+ if (value.is_a?(Array) && value[0].is_a?(Array)) || match_image.nil?
263
+ Image.new_from_array value
228
264
  else
229
265
  # we have a 1D array ... use that as a pixel constant and
230
266
  # expand to match match_image
231
- return match_image.new_from_image value
267
+ match_image.new_from_image value
232
268
  end
233
269
  end
234
270
 
235
271
  # set an operation argument, expanding constants and copying images as
236
272
  # required
237
- def set name, value, match_image, flags, gtype
273
+ def set name, value, match_image, flags, gtype, destructive
238
274
  if gtype == IMAGE_TYPE
239
- value = Operation::imageize match_image, value
275
+ value = Operation.imageize match_image, value
240
276
 
241
- if (flags & ARGUMENT_MODIFY) != 0
242
- # make sure we have a unique copy
277
+ # in non-destructive mode, make sure we have a unique copy
278
+ if (flags & ARGUMENT_MODIFY) != 0 &&
279
+ !destructive
243
280
  value = value.copy.copy_memory
244
281
  end
245
282
  elsif gtype == ARRAY_IMAGE_TYPE
246
- value = value.map { |x| Operation::imageize match_image, x }
283
+ value = value.map { |x| Operation.imageize match_image, x }
247
284
  end
248
285
 
249
286
  super name, value
@@ -319,8 +356,8 @@ module Vips
319
356
  # the constant value 255.
320
357
 
321
358
  def self.call name, supplied, optional = {}, option_string = ""
322
- GLib::logger.debug("Vips::VipsOperation.call") {
323
- "name = #{name}, supplied = #{supplied}, " +
359
+ GLib.logger.debug("Vips::VipsOperation.call") {
360
+ "name = #{name}, supplied = #{supplied}, " \
324
361
  "optional = #{optional}, option_string = #{option_string}"
325
362
  }
326
363
 
@@ -329,20 +366,21 @@ module Vips
329
366
  required_output = introspect.required_output
330
367
  optional_input = introspect.optional_input
331
368
  optional_output = introspect.optional_output
369
+ destructive = introspect.destructive
332
370
 
333
371
  unless supplied.is_a? Array
334
- raise Vips::Error, "unable to call #{name}: " +
335
- "argument array is not an array"
372
+ raise Vips::Error, "unable to call #{name}: " \
373
+ "argument array is not an array"
336
374
  end
337
375
  unless optional.is_a? Hash
338
- raise Vips::Error, "unable to call #{name}: " +
339
- "optional arguments are not a hash"
376
+ raise Vips::Error, "unable to call #{name}: " \
377
+ "optional arguments are not a hash"
340
378
  end
341
379
 
342
380
  if supplied.length != required_input.length
343
- raise Vips::Error, "unable to call #{name}: " +
344
- "you supplied #{supplied.length} arguments, " +
345
- "but operation needs " + "#{required_input.length}."
381
+ raise Vips::Error, "unable to call #{name}: " \
382
+ "you supplied #{supplied.length} arguments, " \
383
+ "but operation needs #{required_input.length}."
346
384
  end
347
385
 
348
386
  # all supplied_optional keys should be in optional_input or
@@ -352,8 +390,8 @@ module Vips
352
390
 
353
391
  unless optional_input.has_key?(arg_name) ||
354
392
  optional_output.has_key?(arg_name)
355
- raise Vips::Error, "unable to call #{name}: " +
356
- "unknown option #{arg_name}"
393
+ raise Vips::Error, "unable to call #{name}: " \
394
+ "unknown option #{arg_name}"
357
395
  end
358
396
  end
359
397
 
@@ -362,17 +400,58 @@ module Vips
362
400
  #
363
401
  # look inside array and hash arguments, since we may be passing an
364
402
  # array of images
365
- match_image = flat_find(supplied) { |value| value.is_a? Image }
403
+ #
404
+ # also enforce the rules around mutable and non-mutable images
405
+ match_image = nil
406
+ flat_find(supplied) do |value|
407
+ if match_image
408
+ # no non-first image arg can ever be mutable
409
+ if value.is_a?(MutableImage)
410
+ raise Vips::Error, "unable to call #{name}: " \
411
+ "only the first image argument can be mutable"
412
+ end
413
+ elsif destructive
414
+ if value.is_a?(Image)
415
+ raise Vips::Error, "unable to call #{name}: " \
416
+ "first image argument to a destructive " \
417
+ "operation must be mutable"
418
+ elsif value.is_a?(MutableImage)
419
+ match_image = value
420
+ end
421
+ elsif value.is_a?(MutableImage)
422
+ # non destructive operation, so no mutable images
423
+ raise Vips::Error, "unable to call #{name}: " \
424
+ "must not pass mutable images to " \
425
+ "non-destructive operations"
426
+ elsif value.is_a?(Image)
427
+ match_image = value
428
+ end
366
429
 
367
- op = Operation.new name
430
+ # keep looping
431
+ false
432
+ end
433
+
434
+ op = Operation.new introspect.vips_name
368
435
 
369
436
  # set any string args first so they can't be overridden
370
- if option_string != nil
371
- if Vips::vips_object_set_from_string(op, option_string) != 0
437
+ unless option_string.nil?
438
+ if Vips.vips_object_set_from_string(op, option_string) != 0
372
439
  raise Vips::Error
373
440
  end
374
441
  end
375
442
 
443
+ # collect a list of all input references here
444
+ references = Set.new
445
+
446
+ add_reference = lambda do |x|
447
+ if x.is_a?(Vips::Image)
448
+ x.references.each do |i|
449
+ references << i
450
+ end
451
+ end
452
+ false
453
+ end
454
+
376
455
  # set all required inputs
377
456
  required_input.each_index do |i|
378
457
  details = required_input[i]
@@ -381,7 +460,8 @@ module Vips
381
460
  gtype = details[:gtype]
382
461
  value = supplied[i]
383
462
 
384
- op.set arg_name, value, match_image, flags, gtype
463
+ flat_find value, &add_reference
464
+ op.set arg_name, value, match_image, flags, gtype, destructive
385
465
  end
386
466
 
387
467
  # set all optional inputs
@@ -395,16 +475,27 @@ module Vips
395
475
  flags = details[:flags]
396
476
  gtype = details[:gtype]
397
477
 
398
- op.set arg_name, value, match_image, flags, gtype
478
+ flat_find value, &add_reference
479
+ op.set arg_name, value, match_image, flags, gtype, destructive
399
480
  end
400
481
  end
401
482
 
402
483
  op = op.build
403
484
 
485
+ # attach all input refs to output x
486
+ set_reference = lambda do |x|
487
+ if x.is_a? Vips::Image
488
+ x.references += references
489
+ end
490
+ false
491
+ end
492
+
404
493
  # get all required results
405
494
  result = []
406
495
  required_output.each do |details|
407
- result << op.get(details[:arg_name])
496
+ value = details[:arg_name]
497
+ flat_find value, &set_reference
498
+ result << op.get(value)
408
499
  end
409
500
 
410
501
  # fetch all optional ones
@@ -413,7 +504,9 @@ module Vips
413
504
  arg_name = key.to_s
414
505
 
415
506
  if optional_output.has_key? arg_name
416
- optional_results[arg_name] = op.get arg_name
507
+ value = op.get arg_name
508
+ flat_find value, &set_reference
509
+ optional_results[arg_name] = value
417
510
  end
418
511
  end
419
512
 
@@ -425,11 +518,11 @@ module Vips
425
518
  result = nil
426
519
  end
427
520
 
428
- GLib::logger.debug("Vips::Operation.call") { "result = #{result}" }
521
+ GLib.logger.debug("Vips::Operation.call") { "result = #{result}" }
429
522
 
430
- Vips::vips_object_unref_outputs op
523
+ Vips.vips_object_unref_outputs op
431
524
 
432
- return result
525
+ result
433
526
  end
434
527
  end
435
528
  end
data/lib/vips/region.rb CHANGED
@@ -4,12 +4,12 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
8
 
9
9
  module Vips
10
10
  attach_function :vips_region_new, [:pointer], :pointer
11
11
 
12
- if Vips::at_least_libvips?(8, 8)
12
+ if Vips.at_least_libvips?(8, 8)
13
13
  attach_function :vips_region_fetch, [:pointer, :int, :int, :int, :int, SizeStruct.ptr], :pointer
14
14
  attach_function :vips_region_width, [:pointer], :int
15
15
  attach_function :vips_region_height, [:pointer], :int
@@ -44,24 +44,24 @@ module Vips
44
44
  end
45
45
 
46
46
  def initialize(name)
47
- ptr = Vips::vips_region_new name
47
+ ptr = Vips.vips_region_new name
48
48
  raise Vips::Error if ptr.null?
49
49
 
50
50
  super ptr
51
51
  end
52
52
 
53
53
  def width
54
- Vips::vips_region_width self
54
+ Vips.vips_region_width self
55
55
  end
56
56
 
57
57
  def height
58
- Vips::vips_region_height self
58
+ Vips.vips_region_height self
59
59
  end
60
60
 
61
61
  # Fetch a region filled with pixel data.
62
62
  def fetch(left, top, width, height)
63
63
  len = Vips::SizeStruct.new
64
- ptr = Vips::vips_region_fetch self, left, top, width, height, len
64
+ ptr = Vips.vips_region_fetch self, left, top, width, height, len
65
65
  raise Vips::Error if ptr.null?
66
66
 
67
67
  # wrap up as an autopointer
data/lib/vips/source.rb CHANGED
@@ -4,10 +4,10 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
8
 
9
9
  module Vips
10
- if Vips::at_least_libvips?(8, 9)
10
+ if Vips.at_least_libvips?(8, 9)
11
11
  attach_function :vips_source_new_from_descriptor, [:int], :pointer
12
12
  attach_function :vips_source_new_from_file, [:pointer], :pointer
13
13
  attach_function :vips_source_new_from_memory, [:pointer, :size_t], :pointer
@@ -42,26 +42,26 @@ module Vips
42
42
  #
43
43
  # Pass sources to {Image.new_from_source} to load images from
44
44
  # them.
45
- #
45
+ #
46
46
  # @param descriptor [Integer] the file descriptor
47
47
  # @return [Source] the new Vips::Source
48
48
  def self.new_from_descriptor(descriptor)
49
- ptr = Vips::vips_source_new_from_descriptor descriptor
49
+ ptr = Vips.vips_source_new_from_descriptor descriptor
50
50
  raise Vips::Error if ptr.null?
51
51
 
52
52
  Vips::Source.new ptr
53
53
  end
54
54
 
55
- # Create a new source from a file name.
55
+ # Create a new source from a file name.
56
56
  #
57
57
  # Pass sources to {Image.new_from_source} to load images from
58
58
  # them.
59
- #
59
+ #
60
60
  # @param filename [String] the name of the file
61
61
  # @return [Source] the new Vips::Source
62
62
  def self.new_from_file(filename)
63
63
  raise Vips::Error, "filename is nil" if filename.nil?
64
- ptr = Vips::vips_source_new_from_file filename
64
+ ptr = Vips.vips_source_new_from_file filename
65
65
  raise Vips::Error if ptr.null?
66
66
 
67
67
  Vips::Source.new ptr
@@ -72,18 +72,17 @@ module Vips
72
72
  #
73
73
  # Pass sources to {Image.new_from_source} to load images from
74
74
  # them.
75
- #
76
- # @param data [String] memory area
75
+ #
76
+ # @param data [String] memory area
77
77
  # @return [Source] the new Vips::Source
78
78
  def self.new_from_memory(data)
79
- ptr = Vips::vips_source_new_from_memory data, data.bytesize
79
+ ptr = Vips.vips_source_new_from_memory data, data.bytesize
80
80
  raise Vips::Error if ptr.null?
81
81
 
82
- # FIXME do we need to keep a ref to the underlying memory area? what
82
+ # FIXME do we need to keep a ref to the underlying memory area? what
83
83
  # about Image.new_from_buffer? Does that need a secret ref too?
84
84
 
85
85
  Vips::Source.new ptr
86
86
  end
87
-
88
87
  end
89
88
  end
@@ -4,14 +4,14 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
8
 
9
9
  module Vips
10
- if Vips::at_least_libvips?(8, 9)
10
+ if Vips.at_least_libvips?(8, 9)
11
11
  attach_function :vips_source_custom_new, [], :pointer
12
12
  end
13
13
 
14
- # A source you can attach action signal handlers to to implement
14
+ # A source you can attach action signal handlers to to implement
15
15
  # custom input types.
16
16
  #
17
17
  # For example:
@@ -23,7 +23,7 @@ module Vips
23
23
  # image = Vips::Image.new_from_source source
24
24
  # ```
25
25
  #
26
- # (just an example -- of course in practice you'd use {Source#new_from_file}
26
+ # (just an example -- of course in practice you'd use {Source#new_from_file}
27
27
  # to read from a named file)
28
28
  class SourceCustom < Vips::Source
29
29
  module SourceCustomLayout
@@ -44,7 +44,7 @@ module Vips
44
44
  end
45
45
 
46
46
  def initialize
47
- pointer = Vips::vips_source_custom_new
47
+ pointer = Vips.vips_source_custom_new
48
48
  raise Vips::Error if pointer.null?
49
49
 
50
50
  super pointer
@@ -60,7 +60,7 @@ module Vips
60
60
  def on_read &block
61
61
  signal_connect "read" do |buf, len|
62
62
  chunk = block.call len
63
- return 0 if chunk == nil
63
+ return 0 if chunk.nil?
64
64
  bytes_read = chunk.bytesize
65
65
  buf.put_bytes(0, chunk, 0, bytes_read)
66
66
  chunk.clear
@@ -70,7 +70,7 @@ module Vips
70
70
  end
71
71
 
72
72
  # The block is executed to seek the source. The interface is exactly as
73
- # IO::seek, ie. it should take an offset and whence, and return the
73
+ # IO::seek, ie. it should take an offset and whence, and return the
74
74
  # new read position.
75
75
  #
76
76
  # This handler is optional -- if you do not attach a seek handler,
@@ -85,6 +85,5 @@ module Vips
85
85
  block.call offset, whence
86
86
  end
87
87
  end
88
-
89
88
  end
90
89
  end