ruby-vips 2.0.17 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
  3. data/.github/workflows/test.yml +80 -0
  4. data/.standard.yml +17 -0
  5. data/.yardopts +0 -1
  6. data/CHANGELOG.md +26 -0
  7. data/Gemfile +3 -1
  8. data/README.md +12 -11
  9. data/Rakefile +13 -21
  10. data/TODO +4 -8
  11. data/VERSION +1 -1
  12. data/example/annotate.rb +6 -6
  13. data/example/connection.rb +18 -9
  14. data/example/daltonize8.rb +6 -6
  15. data/example/draw_lines.rb +30 -0
  16. data/example/example1.rb +4 -4
  17. data/example/example2.rb +6 -6
  18. data/example/example3.rb +5 -5
  19. data/example/example4.rb +2 -2
  20. data/example/example5.rb +4 -4
  21. data/example/inheritance_with_refcount.rb +46 -39
  22. data/example/progress.rb +3 -3
  23. data/example/thumb.rb +6 -6
  24. data/example/trim8.rb +1 -1
  25. data/example/watermark.rb +2 -2
  26. data/example/wobble.rb +1 -1
  27. data/lib/ruby-vips.rb +1 -1
  28. data/lib/vips.rb +127 -76
  29. data/lib/vips/blend_mode.rb +29 -25
  30. data/lib/vips/connection.rb +4 -4
  31. data/lib/vips/gobject.rb +18 -11
  32. data/lib/vips/gvalue.rb +54 -54
  33. data/lib/vips/image.rb +289 -165
  34. data/lib/vips/interpolate.rb +3 -2
  35. data/lib/vips/methods.rb +484 -107
  36. data/lib/vips/mutableimage.rb +173 -0
  37. data/lib/vips/object.rb +86 -93
  38. data/lib/vips/operation.rb +161 -82
  39. data/lib/vips/region.rb +6 -6
  40. data/lib/vips/source.rb +11 -12
  41. data/lib/vips/sourcecustom.rb +7 -8
  42. data/lib/vips/target.rb +12 -13
  43. data/lib/vips/targetcustom.rb +9 -10
  44. data/lib/vips/version.rb +1 -1
  45. data/ruby-vips.gemspec +26 -22
  46. metadata +29 -49
  47. data/.rubocop.yml +0 -22
  48. data/.rubocop_todo.yml +0 -473
  49. data/.travis.yml +0 -57
  50. data/install-vips.sh +0 -26
data/lib/vips/image.rb CHANGED
@@ -4,13 +4,13 @@
4
4
  # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
5
  # License:: MIT
6
6
 
7
- require 'ffi'
7
+ require "ffi"
8
8
 
9
9
  module Vips
10
10
  private
11
11
 
12
12
  attach_function :vips_image_new_matrix_from_array,
13
- [:int, :int, :pointer, :int], :pointer
13
+ [:int, :int, :pointer, :int], :pointer
14
14
 
15
15
  attach_function :vips_image_copy_memory, [:pointer], :pointer
16
16
 
@@ -25,29 +25,35 @@ module Vips
25
25
  attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
26
26
  attach_function :vips_foreign_find_save_buffer, [:string], :string
27
27
 
28
- if Vips::at_least_libvips?(8, 9)
28
+ if Vips.at_least_libvips?(8, 9)
29
29
  attach_function :vips_foreign_find_load_source, [:pointer], :string
30
30
  attach_function :vips_foreign_find_save_target, [:string], :string
31
31
  end
32
32
 
33
33
  attach_function :vips_image_write_to_memory,
34
- [:pointer, SizeStruct.ptr], :pointer
34
+ [:pointer, SizeStruct.ptr], :pointer
35
35
 
36
36
  attach_function :vips_image_get_typeof, [:pointer, :string], :GType
37
37
  attach_function :vips_image_get,
38
- [:pointer, :string, GObject::GValue.ptr], :int
38
+ [:pointer, :string, GObject::GValue.ptr], :int
39
39
 
40
- if Vips::at_least_libvips?(8, 5)
40
+ attach_function :vips_image_get_width, [:pointer], :int
41
+ attach_function :vips_image_get_height, [:pointer], :int
42
+ attach_function :vips_image_get_bands, [:pointer], :int
43
+
44
+ if Vips.at_least_libvips?(8, 5)
41
45
  attach_function :vips_image_get_fields, [:pointer], :pointer
42
46
  attach_function :vips_image_hasalpha, [:pointer], :int
43
47
  end
44
48
 
45
- if Vips::at_least_libvips?(8, 6)
49
+ if Vips.at_least_libvips?(8, 6)
46
50
  attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
47
51
  end
48
52
 
53
+ # move these three lines to mutableimage when we finally remove set and
54
+ # remove in this class
49
55
  attach_function :vips_image_set,
50
- [:pointer, :string, GObject::GValue.ptr], :void
56
+ [:pointer, :string, GObject::GValue.ptr], :void
51
57
  attach_function :vips_image_remove, [:pointer, :string], :void
52
58
 
53
59
  attach_function :vips_band_format_iscomplex, [:int], :int
@@ -55,6 +61,9 @@ module Vips
55
61
 
56
62
  attach_function :nickname_find, :vips_nickname_find, [:GType], :string
57
63
 
64
+ attach_function :vips_image_new_from_memory, [:pointer, :size_t, :int, :int, :int, :int], :pointer
65
+ attach_function :vips_image_new_from_memory_copy, [:pointer, :size_t, :int, :int, :int, :int], :pointer
66
+
58
67
  # turn a raw pointer that must be freed into a self-freeing Ruby string
59
68
  def self.p2str(pointer)
60
69
  pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
@@ -69,6 +78,11 @@ module Vips
69
78
  class Image < Vips::Object
70
79
  alias_method :parent_get_typeof, :get_typeof
71
80
 
81
+ # FFI sets a pointer's size to this magic value if the size of the memory
82
+ # chunk the pointer points to is unknown to FFI.
83
+ UNKNOWN_POINTER_SIZE = FFI::Pointer.new(1).size
84
+ private_constant :UNKNOWN_POINTER_SIZE
85
+
72
86
  private
73
87
 
74
88
  # the layout of the VipsImage struct
@@ -96,17 +110,17 @@ module Vips
96
110
  # handy for overloads ... want to be able to apply a function to an
97
111
  # array or to a scalar
98
112
  def self.smap x, &block
99
- x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.(x)
113
+ x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.call(x)
100
114
  end
101
115
 
102
116
  def self.complex? format
103
117
  format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
104
- Vips::vips_band_format_iscomplex(format_number) != 0
118
+ Vips.vips_band_format_iscomplex(format_number) != 0
105
119
  end
106
120
 
107
121
  def self.float? format
108
122
  format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
109
- Vips::vips_band_format_isfloat(format_number) != 0
123
+ Vips.vips_band_format_isfloat(format_number) != 0
110
124
  end
111
125
 
112
126
  # run a complex operation on a complex image, or an image with an even
@@ -115,12 +129,12 @@ module Vips
115
129
  def self.run_cmplx image, &block
116
130
  original_format = image.format
117
131
 
118
- unless Image::complex? image.format
132
+ unless Image.complex? image.format
119
133
  if image.bands % 2 != 0
120
134
  raise Vips::Error, "not an even number of bands"
121
135
  end
122
136
 
123
- unless Image::float? image.format
137
+ unless Image.float? image.format
124
138
  image = image.cast :float
125
139
  end
126
140
 
@@ -128,9 +142,9 @@ module Vips
128
142
  image = image.copy format: new_format, bands: image.bands / 2
129
143
  end
130
144
 
131
- image = block.(image)
145
+ image = block.call(image)
132
146
 
133
- unless Image::complex? original_format
147
+ unless Image.complex? original_format
134
148
  new_format = image.format == :dpcomplex ? :double : :float
135
149
  image = image.copy format: new_format, bands: image.bands * 2
136
150
  end
@@ -189,15 +203,19 @@ module Vips
189
203
  # 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
190
204
  return false if name == :to_hash
191
205
 
206
+ super
207
+ end
208
+
209
+ def respond_to_missing? name, include_all = false
192
210
  # respond to all vips operations by nickname
193
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
211
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
194
212
 
195
213
  super
196
214
  end
197
215
 
198
- def self.respond_to? name, include_all = false
216
+ def self.respond_to_missing? name, include_all = false
199
217
  # respond to all vips operations by nickname
200
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
218
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
201
219
 
202
220
  super
203
221
  end
@@ -255,18 +273,18 @@ module Vips
255
273
  def self.new_from_file name, **opts
256
274
  # very common, and Vips::vips_filename_get_filename will segv if we
257
275
  # pass this
258
- raise Vips::Error, "filename is nil" if name == nil
276
+ raise Vips::Error, "filename is nil" if name.nil?
259
277
 
260
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
261
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
262
- loader = Vips::vips_foreign_find_load filename
263
- raise Vips::Error if loader == nil
278
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
279
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
280
+ loader = Vips.vips_foreign_find_load filename
281
+ raise Vips::Error if loader.nil?
264
282
 
265
283
  Operation.call loader, [filename], opts, option_string
266
284
  end
267
285
 
268
- # Create a new {Image} for an image encoded, in a format such as
269
- # JPEG, in a binary string. Load options may be passed as
286
+ # Create a new {Image} for an image encoded in a format such as
287
+ # JPEG in a binary string. Load options may be passed as
270
288
  # strings or appended as a hash. For example:
271
289
  #
272
290
  # ```
@@ -297,12 +315,113 @@ module Vips
297
315
  # @macro vips.loadopts
298
316
  # @return [Image] the loaded image
299
317
  def self.new_from_buffer data, option_string, **opts
300
- loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
318
+ loader = Vips.vips_foreign_find_load_buffer data, data.bytesize
301
319
  raise Vips::Error if loader.nil?
302
320
 
303
321
  Vips::Operation.call loader, [data], opts, option_string
304
322
  end
305
323
 
324
+ # Create a new {Image} from a C-style array held in memory. For example:
325
+ #
326
+ # ```
327
+ # image = Vips::Image.black(16, 16) + 128
328
+ # data = image.write_to_memory
329
+ #
330
+ # x = Vips::Image.new_from_memory data,
331
+ # image.width, image.height, image.bands, image.format
332
+ # ```
333
+ #
334
+ # Creating a new image from a memory pointer:
335
+ #
336
+ # ```
337
+ # ptr = FFI::MemoryPointer.new(:uchar, 10*10)
338
+ # # => #<FFI::MemoryPointer address=0x00007fc236db31d0 size=100>
339
+ # x = Vips::Image.new_from_memory(ptr, 10, 10, 1, :uchar)
340
+ # ```
341
+ #
342
+ # Creating a new image from an address only pointer:
343
+ #
344
+ # ```
345
+ # ptr = call_to_external_c_library(w: 10, h: 10)
346
+ # # => #<FFI::Pointer address=0x00007f9780813a00>
347
+ # ptr_slice = ptr.slice(0, 10*10)
348
+ # # => #<FFI::Pointer address=0x00007f9780813a00 size=100>
349
+ # x = Vips::Image.new_from_memory(ptr_slice, 10, 10, 1, :uchar)
350
+ # ```
351
+ #
352
+ # {new_from_memory} keeps a reference to the array of pixels you pass in
353
+ # to try to prevent that memory from being freed by the Ruby GC while it
354
+ # is being used.
355
+ #
356
+ # See {new_from_memory_copy} for a version of this method which does not
357
+ # keep a reference.
358
+ #
359
+ # @param data [String, FFI::Pointer] the data to load from
360
+ # @param width [Integer] width in pixels
361
+ # @param height [Integer] height in pixels
362
+ # @param bands [Integer] number of bands
363
+ # @param format [Symbol] band format
364
+ # @return [Image] the loaded image
365
+ def self.new_from_memory data, width, height, bands, format
366
+ # prevent data from being freed with JRuby FFI
367
+ if defined?(JRUBY_VERSION) && !data.is_a?(FFI::Pointer)
368
+ data = ::FFI::MemoryPointer.new(:char, data.bytesize).write_bytes data
369
+ end
370
+
371
+ if data.is_a?(FFI::Pointer)
372
+ # A pointer needs to know about the size of the memory it points to.
373
+ # If you have an address-only pointer, use the .slice method to wrap
374
+ # the pointer in a size aware pointer.
375
+ if data.size == UNKNOWN_POINTER_SIZE
376
+ raise Vips::Error, "size of memory is unknown"
377
+ end
378
+ size = data.size
379
+ else
380
+ size = data.bytesize
381
+ end
382
+
383
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
384
+ vi = Vips.vips_image_new_from_memory data, size,
385
+ width, height, bands, format_number
386
+ raise Vips::Error if vi.null?
387
+ image = new(vi)
388
+
389
+ # keep a secret ref to the underlying object .. this reference will be
390
+ # inherited by things that in turn depend on us, so the memory we are
391
+ # using will not be freed
392
+ image.references << data
393
+
394
+ image
395
+ end
396
+
397
+ # Create a new {Image} from memory and copies the memory area. See
398
+ # {new_from_memory} for a version of this method which does not copy the
399
+ # memory area.
400
+ #
401
+ # @param data [String, FFI::Pointer] the data to load from
402
+ # @param width [Integer] width in pixels
403
+ # @param height [Integer] height in pixels
404
+ # @param bands [Integer] number of bands
405
+ # @param format [Symbol] band format
406
+ # @return [Image] the loaded image
407
+ def self.new_from_memory_copy data, width, height, bands, format
408
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
409
+
410
+ if data.is_a?(FFI::Pointer)
411
+ if data.size == UNKNOWN_POINTER_SIZE
412
+ raise Vips::Error, "size of memory is unknown"
413
+ end
414
+ size = data.size
415
+ else
416
+ size = data.bytesize
417
+ end
418
+
419
+ vi = Vips.vips_image_new_from_memory_copy data, size,
420
+ width, height, bands, format_number
421
+ raise Vips::Error if vi.null?
422
+ new(vi)
423
+ end
424
+
306
425
  # Create a new {Image} from a source. Load options may be passed as
307
426
  # strings or appended as a hash. For example:
308
427
  #
@@ -328,7 +447,7 @@ module Vips
328
447
  # TIFF images will work.
329
448
  #
330
449
  # Loading is fast: only enough data is read to be able to fill
331
- # out the header. Pixels will only be read and decompressed when they are
450
+ # out the header. Pixels will only be read and decompressed when they are
332
451
  # needed.
333
452
  #
334
453
  # @param source [Vips::Source] the source to load from
@@ -336,7 +455,7 @@ module Vips
336
455
  # @macro vips.loadopts
337
456
  # @return [Image] the loaded image
338
457
  def self.new_from_source source, option_string, **opts
339
- loader = Vips::vips_foreign_find_load_source source
458
+ loader = Vips.vips_foreign_find_load_source source
340
459
  raise Vips::Error if loader.nil?
341
460
 
342
461
  Vips::Operation.call loader, [source], opts, option_string
@@ -345,8 +464,8 @@ module Vips
345
464
  def self.matrix_from_array width, height, array
346
465
  ptr = FFI::MemoryPointer.new :double, array.length
347
466
  ptr.write_array_of_double array
348
- image = Vips::vips_image_new_matrix_from_array width, height,
349
- ptr, array.length
467
+ image = Vips.vips_image_new_matrix_from_array width, height,
468
+ ptr, array.length
350
469
  Vips::Image.new image
351
470
  end
352
471
 
@@ -399,18 +518,22 @@ module Vips
399
518
  width = array.length
400
519
  end
401
520
 
521
+ unless array.length == width * height
522
+ raise Vips::Error, "Bad array dimensions."
523
+ end
524
+
402
525
  unless array.all? { |x| x.is_a? Numeric }
403
526
  raise Vips::Error, "Not all array elements are Numeric."
404
527
  end
405
528
 
406
529
  image = Vips::Image.matrix_from_array width, height, array
407
- raise Vips::Error if image == nil
408
-
409
- # be careful to set them as double
410
- image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
411
- image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
530
+ raise Vips::Error if image.nil?
412
531
 
413
- return image
532
+ image.mutate do |mutable|
533
+ # be careful to set them as double
534
+ mutable.set_type! GObject::GDOUBLE_TYPE, "scale", scale.to_f
535
+ mutable.set_type! GObject::GDOUBLE_TYPE, "offset", offset.to_f
536
+ end
414
537
  end
415
538
 
416
539
  # A new image is created with the same width, height, format,
@@ -459,12 +582,12 @@ module Vips
459
582
  #
460
583
  # @param name [String] filename to write to
461
584
  def write_to_file name, **opts
462
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
463
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
464
- saver = Vips::vips_foreign_find_save filename
465
- if saver == nil
466
- raise Vips::Error, "No known saver for '#{filename}'."
467
- end
585
+ raise Vips::Error, "filename is nil" if name.nil?
586
+
587
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
588
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
589
+ saver = Vips.vips_foreign_find_save filename
590
+ raise Vips::Error if saver.nil?
468
591
 
469
592
  Vips::Operation.call saver, [self, filename], opts, option_string
470
593
 
@@ -497,19 +620,18 @@ module Vips
497
620
  # @macro vips.saveopts
498
621
  # @return [String] the image saved in the specified format
499
622
  def write_to_buffer format_string, **opts
500
- filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
501
- option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
502
- saver = Vips::vips_foreign_find_save_buffer filename
503
- if saver == nil
504
- raise Vips::Error, "No known buffer saver for '#{filename}'."
505
- end
623
+ raise Vips::Error, "filename is nil" if format_string.nil?
624
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
625
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
626
+ saver = Vips.vips_foreign_find_save_buffer filename
627
+ raise Vips::Error if saver.nil?
506
628
 
507
629
  buffer = Vips::Operation.call saver, [self], opts, option_string
508
- raise Vips::Error if buffer == nil
630
+ raise Vips::Error if buffer.nil?
509
631
 
510
632
  write_gc
511
633
 
512
- return buffer
634
+ buffer
513
635
  end
514
636
 
515
637
  # Write this image to a target. Save options may be encoded in
@@ -539,12 +661,11 @@ module Vips
539
661
  # @param format_string [String] save format plus string options
540
662
  # @macro vips.saveopts
541
663
  def write_to_target target, format_string, **opts
542
- filename = Vips::p2str(Vips::vips_filename_get_filename format_string)
543
- option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
544
- saver = Vips::vips_foreign_find_save_target filename
545
- if saver == nil
546
- raise Vips::Error, "No known target saver for '#{filename}'."
547
- end
664
+ raise Vips::Error, "filename is nil" if format_string.nil?
665
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
666
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
667
+ saver = Vips.vips_foreign_find_save_target filename
668
+ raise Vips::Error if saver.nil?
548
669
 
549
670
  Vips::Operation.call saver, [self, target], opts, option_string
550
671
  write_gc
@@ -555,8 +676,8 @@ module Vips
555
676
  # @return [String] the pixels as a huge binary string
556
677
  def write_to_memory
557
678
  len = Vips::SizeStruct.new
558
- ptr = Vips::vips_image_write_to_memory self, len
559
- raise Vips::Error if ptr == nil
679
+ ptr = Vips.vips_image_write_to_memory self, len
680
+ raise Vips::Error if ptr.nil?
560
681
 
561
682
  # wrap up as an autopointer
562
683
  ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
@@ -564,15 +685,15 @@ module Vips
564
685
  ptr.get_bytes 0, len[:value]
565
686
  end
566
687
 
567
- # Turn progress signalling on and off.
688
+ # Turn progress signalling on and off.
568
689
  #
569
690
  # If this is on, the most-downstream image from this image will issue
570
- # progress signals.
691
+ # progress signals.
571
692
  #
572
693
  # @see Object#signal_connect
573
694
  # @param state [Boolean] progress signalling state
574
695
  def set_progress state
575
- Vips::vips_image_set_progress self, state
696
+ Vips.vips_image_set_progress self, state
576
697
  end
577
698
 
578
699
  # Kill computation of this time.
@@ -583,7 +704,7 @@ module Vips
583
704
  # @see Object#signal_connect
584
705
  # @param kill [Boolean] stop computation
585
706
  def set_kill kill
586
- Vips::vips_image_set_kill self, kill
707
+ Vips.vips_image_set_kill self, kill
587
708
  end
588
709
 
589
710
  # Get the `GType` of a metadata field. The result is 0 if no such field
@@ -595,12 +716,12 @@ module Vips
595
716
  def get_typeof name
596
717
  # on libvips before 8.5, property types must be searched first,
597
718
  # since vips_image_get_typeof returned built-in enums as int
598
- unless Vips::at_least_libvips?(8, 5)
719
+ unless Vips.at_least_libvips?(8, 5)
599
720
  gtype = parent_get_typeof name
600
721
  return gtype if gtype != 0
601
722
  end
602
723
 
603
- Vips::vips_image_get_typeof self, name
724
+ Vips.vips_image_get_typeof self, name
604
725
  end
605
726
 
606
727
  # Get a metadata item from an image. Ruby types are constructed
@@ -619,12 +740,12 @@ module Vips
619
740
  def get name
620
741
  # with old libvips, we must fetch properties (as opposed to
621
742
  # metadata) via VipsObject
622
- unless Vips::at_least_libvips?(8, 5)
743
+ unless Vips.at_least_libvips?(8, 5)
623
744
  return super if parent_get_typeof(name) != 0
624
745
  end
625
746
 
626
747
  gvalue = GObject::GValue.alloc
627
- raise Vips::Error if Vips::vips_image_get(self, name, gvalue) != 0
748
+ raise Vips::Error if Vips.vips_image_get(self, name, gvalue) != 0
628
749
  result = gvalue.get
629
750
  gvalue.unset
630
751
 
@@ -639,67 +760,64 @@ module Vips
639
760
  # vips_image_get_fields() was added in libvips 8.5
640
761
  return [] unless Vips.respond_to? :vips_image_get_fields
641
762
 
642
- array = Vips::vips_image_get_fields self
763
+ array = Vips.vips_image_get_fields self
643
764
 
644
765
  names = []
645
766
  p = array
646
767
  until (q = p.read_pointer).null?
647
768
  names << q.read_string
648
- GLib::g_free q
769
+ GLib.g_free q
649
770
  p += FFI::Type::POINTER.size
650
771
  end
651
- GLib::g_free array
772
+ GLib.g_free array
652
773
 
653
774
  names
654
775
  end
655
776
 
656
- # Create a metadata item on an image of the specifed type. Ruby types
657
- # are automatically transformed into the matching `GType`, if possible.
777
+ # Mutate an image with a block. Inside the block, you can call methods
778
+ # which modify the image, such as setting or removing metadata, or
779
+ # modifying pixels.
658
780
  #
659
- # For example, you can use this to set an image's ICC profile:
781
+ # For example:
660
782
  #
661
- # ```
662
- # x = y.set_type Vips::BLOB_TYPE, "icc-profile-data", profile
783
+ # ```ruby
784
+ # image = image.mutate do |x|
785
+ # (0 ... 1).step(0.01) do |i|
786
+ # x.draw_line! 255, x.width * i, 0, 0, x.height * (1 - i)
787
+ # end
788
+ # end
663
789
  # ```
664
790
  #
665
- # where `profile` is an ICC profile held as a binary string object.
791
+ # See {MutableImage}.
792
+ def mutate
793
+ mutable = Vips::MutableImage.new self
794
+ yield mutable
795
+ mutable.image
796
+ end
797
+
798
+ # This method is deprecated.
666
799
  #
667
- # @see set
668
- # @param gtype [Integer] GType of item
669
- # @param name [String] Metadata field to set
670
- # @param value [Object] Value to set
800
+ # Please use {MutableImage#set_type!} instead.
671
801
  def set_type gtype, name, value
672
802
  gvalue = GObject::GValue.alloc
673
803
  gvalue.init gtype
674
804
  gvalue.set value
675
- Vips::vips_image_set self, name, gvalue
805
+ Vips.vips_image_set self, name, gvalue
676
806
  gvalue.unset
677
807
  end
678
808
 
679
- # Set the value of a metadata item on an image. The metadata item must
680
- # already exist. Ruby types are automatically transformed into the
681
- # matching `GValue`, if possible.
682
- #
683
- # For example, you can use this to set an image's ICC profile:
684
- #
685
- # ```
686
- # x = y.set "icc-profile-data", profile
687
- # ```
688
- #
689
- # where `profile` is an ICC profile held as a binary string object.
809
+ # This method is deprecated.
690
810
  #
691
- # @see set_type
692
- # @param name [String] Metadata field to set
693
- # @param value [Object] Value to set
811
+ # Please use {MutableImage#set!} instead.
694
812
  def set name, value
695
813
  set_type get_typeof(name), name, value
696
814
  end
697
815
 
698
- # Remove a metadata item from an image.
816
+ # This method is deprecated.
699
817
  #
700
- # @param name [String] Metadata field to remove
818
+ # Please use {MutableImage#remove!} instead.
701
819
  def remove name
702
- Vips::vips_image_remove self, name
820
+ Vips.vips_image_remove self, name
703
821
  end
704
822
 
705
823
  # compatibility: old name for get
@@ -707,7 +825,9 @@ module Vips
707
825
  get name
708
826
  end
709
827
 
710
- # compatibility: old name for set
828
+ # This method is deprecated.
829
+ #
830
+ # Please use {MutableImage#set!} instead.
711
831
  def set_value name, value
712
832
  set name, value
713
833
  end
@@ -716,21 +836,21 @@ module Vips
716
836
  #
717
837
  # @return [Integer] image width, in pixels
718
838
  def width
719
- get "width"
839
+ Vips.vips_image_get_width self
720
840
  end
721
841
 
722
842
  # Get image height, in pixels.
723
843
  #
724
844
  # @return [Integer] image height, in pixels
725
845
  def height
726
- get "height"
846
+ Vips.vips_image_get_height self
727
847
  end
728
848
 
729
849
  # Get number of image bands.
730
850
  #
731
851
  # @return [Integer] number of image bands
732
852
  def bands
733
- get "bands"
853
+ Vips.vips_image_get_bands self
734
854
  end
735
855
 
736
856
  # Get image format.
@@ -814,23 +934,23 @@ module Vips
814
934
  [width, height]
815
935
  end
816
936
 
817
- if Vips::at_least_libvips?(8, 5)
937
+ if Vips.at_least_libvips?(8, 5)
818
938
  # Detect if image has an alpha channel
819
939
  #
820
940
  # @return [Boolean] true if image has an alpha channel.
821
941
  def has_alpha?
822
- return Vips::vips_image_hasalpha(self) != 0
942
+ Vips.vips_image_hasalpha(self) != 0
823
943
  end
824
944
  end
825
945
 
826
946
  # vips_addalpha was added in libvips 8.6
827
- if Vips::at_least_libvips?(8, 6)
947
+ if Vips.at_least_libvips?(8, 6)
828
948
  # Append an alpha channel to an image.
829
949
  #
830
950
  # @return [Image] new image
831
951
  def add_alpha
832
952
  ptr = GenericPtr.new
833
- result = Vips::vips_addalpha self, ptr
953
+ result = Vips.vips_addalpha self, ptr
834
954
  raise Vips::Error if result != 0
835
955
 
836
956
  Vips::Image.new ptr[:value]
@@ -845,7 +965,7 @@ module Vips
845
965
  #
846
966
  # @return [Image] new memory image
847
967
  def copy_memory
848
- new_image = Vips::vips_image_copy_memory self
968
+ new_image = Vips.vips_image_copy_memory self
849
969
  Vips::Image.new new_image
850
970
  end
851
971
 
@@ -855,7 +975,7 @@ module Vips
855
975
  #
856
976
  # @return [Image] modified image
857
977
  def draw_point ink, left, top, **opts
858
- draw_rect ink, left, top, 1, 1, opts
978
+ draw_rect ink, left, top, 1, 1, **opts
859
979
  end
860
980
 
861
981
  # Add an image, constant or array.
@@ -873,7 +993,7 @@ module Vips
873
993
  # @return [Image] result of subtraction
874
994
  def - other
875
995
  other.is_a?(Vips::Image) ?
876
- subtract(other) : linear(1, Image::smap(other) { |x| x * -1 })
996
+ subtract(other) : linear(1, Image.smap(other) { |x| x * -1 })
877
997
  end
878
998
 
879
999
  # Multiply an image, constant or array.
@@ -891,7 +1011,7 @@ module Vips
891
1011
  # @return [Image] result of division
892
1012
  def / other
893
1013
  other.is_a?(Vips::Image) ?
894
- divide(other) : linear(Image::smap(other) { |x| 1.0 / x }, 0)
1014
+ divide(other) : linear(Image.smap(other) { |x| 1.0 / x }, 0)
895
1015
  end
896
1016
 
897
1017
  # Remainder after integer division with an image, constant or array.
@@ -1017,7 +1137,7 @@ module Vips
1017
1137
  # @return [Image] result of equality
1018
1138
  def == other
1019
1139
  # for equality, we must allow tests against nil
1020
- if other == nil
1140
+ if other.nil?
1021
1141
  false
1022
1142
  else
1023
1143
  call_enum "relational", other, :equal
@@ -1030,7 +1150,7 @@ module Vips
1030
1150
  # @return [Image] result of inequality
1031
1151
  def != other
1032
1152
  # for equality, we must allow tests against nil
1033
- if other == nil
1153
+ if other.nil?
1034
1154
  true
1035
1155
  else
1036
1156
  call_enum "relational", other, :noteq
@@ -1052,38 +1172,40 @@ module Vips
1052
1172
  end
1053
1173
  end
1054
1174
 
1055
- # Convert to an Array. This will be slow for large images.
1175
+ # Convert to an Enumerator. Similar to `#to_a` but lazier.
1056
1176
  #
1057
- # @return [Array] array of Fixnum
1058
- def to_a
1059
- # we render the image to a big string, then unpack
1060
- # as a Ruby array of the correct type
1061
- memory = write_to_memory
1062
-
1177
+ # @return [Enumerator] Enumerator of Enumerators of Arrays of Numerics
1178
+ def to_enum
1063
1179
  # make the template for unpack
1064
1180
  template = {
1065
- char: 'c',
1066
- uchar: 'C',
1067
- short: 's_',
1068
- ushort: 'S_',
1069
- int: 'i_',
1070
- uint: 'I_',
1071
- float: 'f',
1072
- double: 'd',
1073
- complex: 'f',
1074
- dpcomplex: 'd'
1075
- }[format] + '*'
1181
+ char: "c",
1182
+ uchar: "C",
1183
+ short: "s_",
1184
+ ushort: "S_",
1185
+ int: "i_",
1186
+ uint: "I_",
1187
+ float: "f",
1188
+ double: "d",
1189
+ complex: "f",
1190
+ dpcomplex: "d"
1191
+ }[format] + "*"
1076
1192
 
1077
- # and unpack into something like [1, 2, 3, 4 ..]
1078
- array = memory.unpack(template)
1193
+ # we render the image to a big string, then unpack into
1194
+ # one-dimensional array as a Ruby array of the correct type
1195
+ array = write_to_memory.unpack template
1079
1196
 
1080
- # gather band elements together
1081
- pixel_array = array.each_slice(bands).to_a
1197
+ # gather bands of a pixel together
1198
+ pixel_array = array.each_slice bands
1082
1199
 
1083
- # build rows
1084
- row_array = pixel_array.each_slice(width).to_a
1200
+ # gather pixels of a row together
1201
+ pixel_array.each_slice width
1202
+ end
1085
1203
 
1086
- return row_array
1204
+ # Convert to an Array. This will be slow for large images.
1205
+ #
1206
+ # @return [Array] Array of Arrays of Arrays of Numerics
1207
+ def to_a
1208
+ to_enum.to_a
1087
1209
  end
1088
1210
 
1089
1211
  # Return the largest integral value not greater than the argument.
@@ -1159,7 +1281,10 @@ module Vips
1159
1281
  # @param overlay [Image, Array<Image>] images to composite
1160
1282
  # @param mode [BlendMode, Array<BlendMode>] blend modes to use
1161
1283
  # @param opts [Hash] Set of options
1162
- # @option opts [Vips::Interpretation] :compositing_space Composite images in this colour space
1284
+ # @option opts [Array<Integer>] :x x positions of overlay
1285
+ # @option opts [Array<Integer>] :y y positions of overlay
1286
+ # @option opts [Vips::Interpretation] :compositing_space Composite images
1287
+ # in this colour space
1163
1288
  # @option opts [Boolean] :premultiplied Images have premultiplied alpha
1164
1289
  # @return [Image] blended image
1165
1290
  def composite overlay, mode, **opts
@@ -1183,9 +1308,9 @@ module Vips
1183
1308
  # coordinate of maximum
1184
1309
  def maxpos
1185
1310
  v, opts = max x: true, y: true
1186
- x = opts['x']
1187
- y = opts['y']
1188
- return v, x, y
1311
+ x = opts["x"]
1312
+ y = opts["y"]
1313
+ [v, x, y]
1189
1314
  end
1190
1315
 
1191
1316
  # Return the coordinates of the image minimum.
@@ -1194,9 +1319,9 @@ module Vips
1194
1319
  # coordinate of minimum
1195
1320
  def minpos
1196
1321
  v, opts = min x: true, y: true
1197
- x = opts['x']
1198
- y = opts['y']
1199
- return v, x, y
1322
+ x = opts["x"]
1323
+ y = opts["y"]
1324
+ [v, x, y]
1200
1325
  end
1201
1326
 
1202
1327
  # a median filter
@@ -1204,7 +1329,7 @@ module Vips
1204
1329
  # @param size [Integer] size of filter window
1205
1330
  # @return [Image] result of median filter
1206
1331
  def median size = 3
1207
- rank size, size, (size * size) / 2
1332
+ rank size, size, size**2 / 2
1208
1333
  end
1209
1334
 
1210
1335
  # Return the real part of a complex image.
@@ -1231,7 +1356,7 @@ module Vips
1231
1356
  # @see xyz
1232
1357
  # @return [Image] image converted to polar coordinates
1233
1358
  def polar
1234
- Image::run_cmplx(self) { |x| x.complex :polar }
1359
+ Image.run_cmplx(self) { |x| x.complex :polar }
1235
1360
  end
1236
1361
 
1237
1362
  # Return an image with polar pixels converted to rectangular.
@@ -1244,7 +1369,7 @@ module Vips
1244
1369
  # @see xyz
1245
1370
  # @return [Image] image converted to rectangular coordinates
1246
1371
  def rect
1247
- Image::run_cmplx(self) { |x| x.complex :rect }
1372
+ Image.run_cmplx(self) { |x| x.complex :rect }
1248
1373
  end
1249
1374
 
1250
1375
  # Return the complex conjugate of an image.
@@ -1256,7 +1381,7 @@ module Vips
1256
1381
  #
1257
1382
  # @return [Image] complex conjugate
1258
1383
  def conj
1259
- Image::run_cmplx(self) { |x| x.complex :conj }
1384
+ Image.run_cmplx(self) { |x| x.complex :conj }
1260
1385
  end
1261
1386
 
1262
1387
  # Calculate the cross phase of two images.
@@ -1437,7 +1562,7 @@ module Vips
1437
1562
  #
1438
1563
  # ```
1439
1564
  # $ ruby > methods.rb
1440
- # require 'vips'; Vips::Yard.generate
1565
+ # require "vips"; Vips::Yard.generate
1441
1566
  # ^D
1442
1567
  # ```
1443
1568
 
@@ -1459,7 +1584,7 @@ module Vips
1459
1584
  "VipsArrayDouble" => "Array<Double>",
1460
1585
  "VipsArrayInt" => "Array<Integer>",
1461
1586
  "VipsArrayImage" => "Array<Image>",
1462
- "VipsArrayString" => "Array<String>",
1587
+ "VipsArrayString" => "Array<String>"
1463
1588
  }
1464
1589
 
1465
1590
  # these have hand-written methods, see above
@@ -1470,15 +1595,15 @@ module Vips
1470
1595
 
1471
1596
  # turn a gtype into a ruby type name
1472
1597
  def self.gtype_to_ruby gtype
1473
- fundamental = GObject::g_type_fundamental gtype
1474
- type_name = GObject::g_type_name gtype
1598
+ fundamental = GObject.g_type_fundamental gtype
1599
+ type_name = GObject.g_type_name gtype
1475
1600
 
1476
1601
  if MAP_GO_TO_RUBY.include? type_name
1477
1602
  type_name = MAP_GO_TO_RUBY[type_name]
1478
1603
  end
1479
1604
 
1480
1605
  if fundamental == GObject::GFLAGS_TYPE ||
1481
- fundamental == GObject::GENUM_TYPE
1606
+ fundamental == GObject::GENUM_TYPE
1482
1607
  type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1483
1608
  end
1484
1609
 
@@ -1497,7 +1622,7 @@ module Vips
1497
1622
  print "# @!method "
1498
1623
  print "self." unless introspect.member_x
1499
1624
  print "#{introspect.name}("
1500
- print method_args.map{ |x| x[:yard_name] }.join(", ")
1625
+ print method_args.map { |x| x[:yard_name] }.join(", ")
1501
1626
  print ", " if method_args.length > 0
1502
1627
  puts "**opts)"
1503
1628
 
@@ -1517,8 +1642,7 @@ module Vips
1517
1642
  gtype = details[:gtype]
1518
1643
  blurb = details[:blurb]
1519
1644
 
1520
- puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} " +
1521
- "#{blurb}"
1645
+ puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} #{blurb}"
1522
1646
  end
1523
1647
  optional_output.each do |arg_name, details|
1524
1648
  yard_name = details[:yard_name]
@@ -1536,14 +1660,14 @@ module Vips
1536
1660
  print gtype_to_ruby(required_output.first[:gtype])
1537
1661
  else
1538
1662
  print "Array<"
1539
- print required_output.map{ |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1663
+ print required_output.map { |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1540
1664
  print ">"
1541
1665
  end
1542
1666
  if optional_output.length > 0
1543
1667
  print ", Hash<Symbol => Object>"
1544
1668
  end
1545
1669
  print "] "
1546
- print required_output.map{ |x| x[:blurb] }.join(", ")
1670
+ print required_output.map { |x| x[:blurb] }.join(", ")
1547
1671
  if optional_output.length > 0
1548
1672
  print ", " if required_output.length > 0
1549
1673
  print "Hash of optional output items"
@@ -1555,16 +1679,16 @@ module Vips
1555
1679
 
1556
1680
  def self.generate
1557
1681
  alias_gtypes = {}
1558
- ALIAS.each do |name|
1559
- gtype = Vips::type_find "VipsOperation", name
1560
- alias_gtypes[gtype] = name
1682
+ ALIAS.each do |name|
1683
+ gtype = Vips.type_find "VipsOperation", name
1684
+ alias_gtypes[gtype] = name
1561
1685
  end
1562
1686
 
1563
1687
  generate_class = lambda do |gtype, _|
1564
- if alias_gtypes.key? gtype
1565
- name = alias_gtypes[gtype]
1688
+ name = if alias_gtypes.key? gtype
1689
+ alias_gtypes[gtype]
1566
1690
  else
1567
- name = Vips::nickname_find gtype
1691
+ Vips.nickname_find gtype
1568
1692
  end
1569
1693
 
1570
1694
  if name
@@ -1578,14 +1702,14 @@ module Vips
1578
1702
  generate_operation(introspect) if introspect
1579
1703
  end
1580
1704
 
1581
- Vips::vips_type_map gtype, generate_class, nil
1705
+ Vips.vips_type_map gtype, generate_class, nil
1582
1706
  end
1583
1707
 
1584
1708
  puts "module Vips"
1585
1709
  puts " class Image"
1586
1710
  puts ""
1587
1711
 
1588
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1712
+ generate_class.call(GObject.g_type_from_name("VipsOperation"), nil)
1589
1713
 
1590
1714
  puts " end"
1591
1715
  puts "end"