vips 8.8.4 → 8.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e63e0ca10ce7e3c41bfda60bea2a13f12d13937c5ccab8be832648ed3d4aa6c
4
- data.tar.gz: 9e40c91344b418e24aed13952f335bb5b6a08dd015e675597e656650ce4bd2a9
3
+ metadata.gz: 5603f19612f6051e2ec146f82ef9ac652b0790a1f00499fcea3d27eb4141c144
4
+ data.tar.gz: 3ad98dc9631a08e364181f4edc10716059e997cb8a86c507e61d1d46a1cfe907
5
5
  SHA512:
6
- metadata.gz: 85aa8d1d02d8d91cdc33a2b012a2340f7b93aed72e3e6081ed3012ed7d0a8f605ec64a0a523d77a1add4cb6be15dfe2982d72d8770226c09547b39d3d6680145
7
- data.tar.gz: de62d6c3d576d3d0c34e0f391a6e720412152aafe672db9f2df148d34b948059f23402e2af4393421129efbfb2d414fb93b74bd1c50e0599fc367f73adf5fc83
6
+ metadata.gz: ccef89e9e98babf11c0b07073700c0406cc296c6b2a6f3f92ee87b1fbb89ac17edf32fc48f4366b3f776e1ca239174c1147fa457c1362eb2ca3883fb3b0acf2d
7
+ data.tar.gz: 9970c5801392bf405dc75f2b8e3edc174b597d90d3e1e184b08c7a9362ba0feea0c9d68bad012a924f390a776ef36a78160d7364da637e001f1660742052543f
@@ -41,6 +41,7 @@ matrix:
41
41
  - rvm: 2.6
42
42
  os: osx
43
43
  env: PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig"
44
+ - rvm: 2.7
44
45
  - rvm: truffleruby
45
46
  - rvm: jruby-head
46
47
  - rvm: ruby-head
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # Vips
2
2
 
3
+ Programs that use `vips` don't manipulate images directly, instead they create pipelines of image processing operations building on a source image. When the end of the pipe is connected to a destination, the whole pipeline executes at once, streaming the image in parallel from source to destination a section at a time. Because `ruby-vips` is parallel, it's quick, and because it doesn't need to keep entire images in memory, it's light.
4
+
3
5
  This gem is a backwards compatible fork of `ruby-vips` but also includes (and compiles) the [libvips] source code.
4
6
 
5
7
  [![Build Status](https://secure.travis-ci.org/ioquatix/vips.svg)](http://travis-ci.org/ioquatix/vips)
6
8
 
7
- [libvips]: https://jcupitt.github.io/libvips
9
+ [libvips]: https://libvips.github.io/libvips
8
10
 
9
11
  ## Installation
10
12
 
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'vips'
4
+
5
+ file = File.open ARGV[0], "rb"
6
+ source = Vips::SourceCustom.new
7
+ source.on_read { |length| file.read length }
8
+ # this method is optional
9
+ # source.on_seek { |offset, whence| file.seek(offset, whence) }
10
+
11
+ dest = File.open ARGV[1], "wb"
12
+ target = Vips::TargetCustom.new
13
+ target.on_write { |chunk| dest.write(chunk) }
14
+ target.on_finish { dest.close }
15
+
16
+ image = Vips::Image.new_from_source source, "", access: :sequential
17
+ image.write_to_target target, ".png"
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'vips'
4
+
5
+ image = Vips::Image.black 1, 100000
6
+ image.set_progress true
7
+
8
+ def progress_to_s(name, progress)
9
+ puts "#{name}:"
10
+ puts " progress.run = #{progress[:run]}"
11
+ puts " progress.eta = #{progress[:eta]}"
12
+ puts " progress.tpels = #{progress[:tpels]}"
13
+ puts " progress.npels = #{progress[:npels]}"
14
+ puts " progress.percent = #{progress[:percent]}"
15
+ end
16
+
17
+ image.signal_connect :preeval do |progress|
18
+ progress_to_s("preeval", progress)
19
+ end
20
+
21
+ image.signal_connect :eval do |progress|
22
+ progress_to_s("eval", progress)
23
+ image.set_kill(true) if progress[:percent] > 50
24
+ end
25
+
26
+ image.signal_connect :posteval do |progress|
27
+ progress_to_s("posteval", progress)
28
+ end
29
+
30
+ image.avg
@@ -10,6 +10,29 @@ require 'logger'
10
10
  # This module uses FFI to make a simple layer over the glib and gobject
11
11
  # libraries.
12
12
 
13
+ # Generate a library name for ffi.
14
+ #
15
+ # Platform notes:
16
+ # linux:
17
+ # Some distros allow "libvips.so", but only if the -dev headers have been
18
+ # installed. To work everywhere, you must include the ABI number.
19
+ # Confusingly, the file extension is not at the end. ffi adds the "lib"
20
+ # prefix.
21
+ # mac:
22
+ # As linux, but the extension is at the end and is added by ffi.
23
+ # windows:
24
+ # The ABI number must be included, but with a hyphen. ffi does not add a
25
+ # "lib" prefix or a ".dll" suffix.
26
+ def library_name(name, abi_number)
27
+ if FFI::Platform.windows?
28
+ "lib#{name}-#{abi_number}.dll"
29
+ elsif FFI::Platform.mac?
30
+ "#{name}.#{abi_number}"
31
+ else
32
+ "#{name}.so.#{abi_number}"
33
+ end
34
+ end
35
+
13
36
  module GLib
14
37
  class << self
15
38
  attr_accessor :logger
@@ -19,13 +42,7 @@ module GLib
19
42
 
20
43
  extend FFI::Library
21
44
 
22
- if FFI::Platform.windows?
23
- glib_libname = 'libglib-2.0-0.dll'
24
- else
25
- glib_libname = 'glib-2.0'
26
- end
27
-
28
- ffi_lib glib_libname
45
+ ffi_lib library_name('glib-2.0', 0)
29
46
 
30
47
  attach_function :g_malloc, [:size_t], :pointer
31
48
 
@@ -117,13 +134,7 @@ end
117
134
  module GObject
118
135
  extend FFI::Library
119
136
 
120
- if FFI::Platform.windows?
121
- gobject_libname = 'libgobject-2.0-0.dll'
122
- else
123
- gobject_libname = 'gobject-2.0'
124
- end
125
-
126
- ffi_lib gobject_libname
137
+ ffi_lib library_name('gobject-2.0', 0)
127
138
 
128
139
  # we can't just use ulong, windows has different int sizing rules
129
140
  if FFI::Platform::ADDRESS_SIZE == 64
@@ -155,7 +166,7 @@ require 'vips/gobject'
155
166
  require 'vips/gvalue'
156
167
 
157
168
  # This module provides a binding for the [libvips image processing
158
- # library](https://jcupitt.github.io/libvips/).
169
+ # library](https://libvips.github.io/libvips/).
159
170
  #
160
171
  # # Example
161
172
  #
@@ -201,6 +212,9 @@ require 'vips/gvalue'
201
212
  # memory buffers, create images that wrap C-style memory arrays, or make images
202
213
  # from constants.
203
214
  #
215
+ # Use {Source} and {Image.new_from_source} to load images from any data
216
+ # source, for example URIs.
217
+ #
204
218
  # The next line:
205
219
  #
206
220
  # ```ruby
@@ -242,6 +256,9 @@ require 'vips/gvalue'
242
256
  # suffix. You can also write formatted images to memory buffers, or dump
243
257
  # image data to a raw memory array.
244
258
  #
259
+ # Use {Target} and {Image#write_to_target} to write formatted images to
260
+ # any data sink, for example URIs.
261
+ #
245
262
  # # How it works
246
263
  #
247
264
  # The binding uses [ruby-ffi](https://github.com/ffi/ffi) to open the libvips
@@ -393,12 +410,12 @@ require 'vips/gvalue'
393
410
  # # Automatic YARD documentation
394
411
  #
395
412
  # The bulk of these API docs are generated automatically by
396
- # {Vips::generate_yard}. It examines
413
+ # {Vips::Yard::generate}. It examines
397
414
  # libvips and writes a summary of each operation and the arguments and options
398
415
  # that that operation expects.
399
416
  #
400
417
  # Use the [C API
401
- # docs](https://jcupitt.github.io/libvips/API/current)
418
+ # docs](https://libvips.github.io/libvips/API/current)
402
419
  # for more detail.
403
420
  #
404
421
  # # Enums
@@ -423,6 +440,55 @@ require 'vips/gvalue'
423
440
  # If you want to avoid the copies, you'll need to call drawing operations
424
441
  # yourself.
425
442
  #
443
+ # # Progress
444
+ #
445
+ # You can attach signal handlers to images to watch computation progress. For
446
+ # example:
447
+ #
448
+ # ```ruby
449
+ # image = Vips::Image.black 1, 100000
450
+ # image.set_progress true
451
+ #
452
+ # def progress_to_s(name, progress)
453
+ # puts "#{name}:"
454
+ # puts " run = #{progress[:run]}"
455
+ # puts " eta = #{progress[:eta]}"
456
+ # puts " tpels = #{progress[:tpels]}"
457
+ # puts " npels = #{progress[:npels]}"
458
+ # puts " percent = #{progress[:percent]}"
459
+ # end
460
+ #
461
+ # image.signal_connect :preeval do |progress|
462
+ # progress_to_s("preeval", progress)
463
+ # end
464
+ #
465
+ # image.signal_connect :eval do |progress|
466
+ # progress_to_s("eval", progress)
467
+ # image.set_kill(true) if progress[:percent] > 50
468
+ # end
469
+ #
470
+ # image.signal_connect :posteval do |progress|
471
+ # progress_to_s("posteval", progress)
472
+ # end
473
+ #
474
+ # image.avg
475
+ # ```
476
+ #
477
+ # The `:eval` signal will fire for every tile that is processed. You can stop
478
+ # progress with {Image#set_kill} and processing will end with an exception.
479
+ #
480
+ # User streams
481
+ #
482
+ # You can make your own input and output stream objects with {SourceCustom} and
483
+ # {TargetCustom}. For example:
484
+ #
485
+ # ```ruby
486
+ # file = File.open "some/file", "rb"
487
+ # source = Vips::SourceCustom.new
488
+ # source.on_read { |length| file.read length }
489
+ # image = Vips::Image.new_from_source source, "", access: "sequential"
490
+ # ```
491
+ #
426
492
  # # Overloads
427
493
  #
428
494
  # The wrapper defines the usual set of arithmetic, boolean and relational
@@ -462,7 +528,7 @@ module Vips
462
528
  if FFI::Platform.windows?
463
529
  vips_libname = 'libvips-42.dll'
464
530
  else
465
- vips_libname = File.expand_path(FFI::map_library_name('vips'), __dir__)
531
+ vips_libname = File.expand_path(FFI.map_library_name('vips'), __dir__)
466
532
  end
467
533
 
468
534
  ffi_lib vips_libname
@@ -609,7 +675,7 @@ module Vips
609
675
  LIBRARY_VERSION = Vips::version_string
610
676
 
611
677
  # libvips has this arbitrary number as a sanity-check upper bound on image
612
- # size. It's sometimes useful for know whan calculating image ratios.
678
+ # size. It's sometimes useful to know when calculating scale factors.
613
679
  MAX_COORD = 10000000
614
680
  end
615
681
 
@@ -617,4 +683,10 @@ require 'vips/object'
617
683
  require 'vips/operation'
618
684
  require 'vips/image'
619
685
  require 'vips/interpolate'
686
+ require 'vips/region'
620
687
  require 'vips/version'
688
+ require 'vips/connection'
689
+ require 'vips/source'
690
+ require 'vips/sourcecustom'
691
+ require 'vips/target'
692
+ require 'vips/targetcustom'
@@ -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
@@ -73,6 +73,9 @@ module GObject
73
73
  def initialize ptr
74
74
  # GLib::logger.debug("GObject::GObject.initialize") {"ptr = #{ptr}"}
75
75
  @struct = ffi_managed_struct.new ptr
76
+
77
+ # sometimes we need to keep refs across C calls ... hide them here
78
+ @references = []
76
79
  end
77
80
 
78
81
  # access to the casting struct for this class
@@ -111,8 +114,13 @@ module GObject
111
114
  layout :value, GParamSpec.ptr
112
115
  end
113
116
 
114
- attach_function :g_param_spec_get_blurb, [GParamSpec.ptr], :string
117
+ attach_function :g_param_spec_get_blurb, [:pointer], :string
115
118
 
116
119
  attach_function :g_object_ref, [:pointer], :void
117
120
  attach_function :g_object_unref, [:pointer], :void
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
118
126
  end
@@ -13,6 +13,8 @@ module GObject
13
13
  # gvalue.init GObject::GDOUBLE_TYPE
14
14
  # gvalue.set 3.1415
15
15
  # value = gvalue.get
16
+ # # optional -- drop any ref the gvalue had
17
+ # gvalue.unset
16
18
  # ```
17
19
  #
18
20
  # Lifetime is managed automatically. It doesn't know about all GType values,
@@ -27,7 +29,9 @@ module GObject
27
29
  value = value.to_s if value.is_a? Symbol
28
30
 
29
31
  if value.is_a? String
30
- 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("_", "-")
31
35
  if value == -1
32
36
  raise Vips::Error
33
37
  end
@@ -241,6 +245,14 @@ module GObject
241
245
 
242
246
  return result
243
247
  end
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
244
256
  end
245
257
 
246
258
  attach_function :g_value_init, [GValue.ptr, :GType], :void
@@ -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
@@ -115,7 +123,7 @@ module Vips
115
123
 
116
124
  unless Image::complex? image.format
117
125
  if image.bands % 2 != 0
118
- raise Error, "not an even number of bands"
126
+ raise Vips::Error, "not an even number of bands"
119
127
  end
120
128
 
121
129
  unless Image::float? image.format
@@ -145,34 +153,6 @@ module Vips
145
153
  end
146
154
  end
147
155
 
148
- # Write can fail due to no file descriptors and memory can fill if
149
- # large objects are not collected fairly soon. We can't try a
150
- # write and GC and retry on fail, since the write may take a
151
- # long time and may not be repeatable.
152
- #
153
- # GCing before every write would have a horrible effect on
154
- # performance, so as a compromise we GC every @@gc_interval writes.
155
- #
156
- # ruby2.1 introduced a generational GC which is fast enough to be
157
- # able to GC on every write.
158
-
159
- @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
160
-
161
- @@gc_interval = 100
162
- @@gc_countdown = @@gc_interval
163
-
164
- def write_gc
165
- if @@generational_gc
166
- GC.start full_mark: false
167
- else
168
- @@gc_countdown -= 1
169
- if @@gc_countdown < 0
170
- @@gc_countdown = @@gc_interval
171
- GC.start
172
- end
173
- end
174
- end
175
-
176
156
  public
177
157
 
178
158
  def inspect
@@ -219,13 +199,13 @@ module Vips
219
199
  # load options, for example:
220
200
  #
221
201
  # ```
222
- # image = Vips::new_from_file "fred.jpg[shrink=2]"
202
+ # image = Vips::Image.new_from_file "fred.jpg[shrink=2]"
223
203
  # ```
224
204
  #
225
205
  # You can also supply options as a hash, for example:
226
206
  #
227
207
  # ```
228
- # image = Vips::new_from_file "fred.jpg", shrink: 2
208
+ # image = Vips::Image.new_from_file "fred.jpg", shrink: 2
229
209
  # ```
230
210
  #
231
211
  # The full set of options available depend upon the load operation that
@@ -296,11 +276,50 @@ module Vips
296
276
  # @return [Image] the loaded image
297
277
  def self.new_from_buffer data, option_string, **opts
298
278
  loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
299
- raise Vips::Error if loader == nil
279
+ raise Vips::Error if loader.nil?
300
280
 
301
281
  Vips::Operation.call loader, [data], opts, option_string
302
282
  end
303
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
+
304
323
  def self.matrix_from_array width, height, array
305
324
  ptr = FFI::MemoryPointer.new :double, array.length
306
325
  ptr.write_array_of_double array
@@ -317,13 +336,13 @@ module Vips
317
336
  # For example:
318
337
  #
319
338
  # ```
320
- # image = Vips::new_from_array [1, 2, 3]
339
+ # image = Vips::Image.new_from_array [1, 2, 3]
321
340
  # ```
322
341
  #
323
342
  # or
324
343
  #
325
344
  # ```
326
- # image = Vips::new_from_array [
345
+ # image = Vips::Image.new_from_array [
327
346
  # [-1, -1, -1],
328
347
  # [-1, 16, -1],
329
348
  # [-1, -1, -1]], 8
@@ -426,8 +445,6 @@ module Vips
426
445
  end
427
446
 
428
447
  Vips::Operation.call saver, [self, filename], opts, option_string
429
-
430
- write_gc
431
448
  end
432
449
 
433
450
  # Write this image to a memory buffer. Save options may be encoded in
@@ -460,17 +477,52 @@ module Vips
460
477
  option_string = Vips::p2str(Vips::vips_filename_get_options format_string)
461
478
  saver = Vips::vips_foreign_find_save_buffer filename
462
479
  if saver == nil
463
- raise Vips::Error, "No known saver for '#{filename}'."
480
+ raise Vips::Error, "No known buffer saver for '#{filename}'."
464
481
  end
465
482
 
466
483
  buffer = Vips::Operation.call saver, [self], opts, option_string
467
484
  raise Vips::Error if buffer == nil
468
485
 
469
- write_gc
470
-
471
486
  return buffer
472
487
  end
473
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
+
474
526
  # Write this image to a large memory buffer.
475
527
  #
476
528
  # @return [String] the pixels as a huge binary string
@@ -485,6 +537,28 @@ module Vips
485
537
  ptr.get_bytes 0, len[:value]
486
538
  end
487
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
+
488
562
  # Get the `GType` of a metadata field. The result is 0 if no such field
489
563
  # exists.
490
564
  #
@@ -523,10 +597,11 @@ module Vips
523
597
  end
524
598
 
525
599
  gvalue = GObject::GValue.alloc
526
- result = Vips::vips_image_get self, name, gvalue
527
- 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
528
603
 
529
- gvalue.get
604
+ result
530
605
  end
531
606
 
532
607
  # Get the names of all fields on an image. Use this to loop over all
@@ -571,6 +646,7 @@ module Vips
571
646
  gvalue.init gtype
572
647
  gvalue.set value
573
648
  Vips::vips_image_set self, name, gvalue
649
+ gvalue.unset
574
650
  end
575
651
 
576
652
  # Set the value of a metadata item on an image. The metadata item must
@@ -1059,7 +1135,7 @@ module Vips
1059
1135
  # @option opts [Vips::Interpretation] :compositing_space Composite images in this colour space
1060
1136
  # @option opts [Boolean] :premultiplied Images have premultiplied alpha
1061
1137
  # @return [Image] blended image
1062
- def composite overlay, mode, **opts
1138
+ def composite overlay, mode, **options
1063
1139
  unless overlay.is_a? Array
1064
1140
  overlay = [overlay]
1065
1141
  end
@@ -1071,7 +1147,7 @@ module Vips
1071
1147
  GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1072
1148
  end
1073
1149
 
1074
- Vips::Image.composite([self] + overlay, mode, opts)
1150
+ Vips::Image.composite([self] + overlay, mode, **options)
1075
1151
  end
1076
1152
 
1077
1153
  # Return the coordinates of the image maximum.
@@ -1320,30 +1396,27 @@ module Vips
1320
1396
  #
1321
1397
  # @param opts [Hash] Set of options
1322
1398
  # @return [Vips::Image] Output image
1323
- def scaleimage **opts
1324
- Vips::Image.scale self, opts
1399
+ def scaleimage **options
1400
+ Vips::Image.scale self, **options
1325
1401
  end
1326
1402
  end
1327
1403
  end
1328
1404
 
1329
1405
  module Vips
1330
- # This method generates yard comments for all the dynamically bound
1406
+ # This module generates yard comments for all the dynamically bound
1331
1407
  # vips operations.
1332
1408
  #
1333
1409
  # Regenerate with something like:
1334
1410
  #
1335
1411
  # ```
1336
1412
  # $ ruby > methods.rb
1337
- # require 'vips'; Vips::generate_yard
1413
+ # require 'vips'; Vips::Yard.generate
1338
1414
  # ^D
1339
1415
  # ```
1340
1416
 
1341
- def self.generate_yard
1342
- # these have hand-written methods, see above
1343
- no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
1344
-
1417
+ module Yard
1345
1418
  # map gobject's type names to Ruby
1346
- map_go_to_ruby = {
1419
+ MAP_GO_TO_RUBY = {
1347
1420
  "gboolean" => "Boolean",
1348
1421
  "gint" => "Integer",
1349
1422
  "gdouble" => "Float",
@@ -1351,116 +1424,99 @@ module Vips
1351
1424
  "gchararray" => "String",
1352
1425
  "VipsImage" => "Vips::Image",
1353
1426
  "VipsInterpolate" => "Vips::Interpolate",
1427
+ "VipsConnection" => "Vips::Connection",
1428
+ "VipsSource" => "Vips::Source",
1429
+ "VipsTarget" => "Vips::Target",
1430
+ "VipsSourceCustom" => "Vips::SourceCustom",
1431
+ "VipsTargetCustom" => "Vips::TargetCustom",
1354
1432
  "VipsArrayDouble" => "Array<Double>",
1355
1433
  "VipsArrayInt" => "Array<Integer>",
1356
1434
  "VipsArrayImage" => "Array<Image>",
1357
1435
  "VipsArrayString" => "Array<String>",
1358
1436
  }
1359
1437
 
1360
- generate_operation = lambda do |gtype, nickname, op|
1361
- op_flags = op.get_flags
1362
- return if (op_flags & OPERATION_DEPRECATED) != 0
1363
- return if no_generate.include? nickname
1364
-
1365
- description = Vips::vips_object_get_description op
1366
-
1367
- # find and classify all the arguments the operator can take
1368
- required_input = []
1369
- optional_input = []
1370
- required_output = []
1371
- optional_output = []
1372
- member_x = nil
1373
- op.argument_map do |pspec, argument_class, _argument_instance|
1374
- arg_flags = argument_class[:flags]
1375
- next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1376
- next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1377
-
1378
- name = pspec[:name].tr("-", "_")
1379
- # 'in' as a param name confuses yard
1380
- name = "im" if name == "in"
1381
- gtype = pspec[:value_type]
1382
- fundamental = GObject::g_type_fundamental gtype
1383
- type_name = GObject::g_type_name gtype
1384
- if map_go_to_ruby.include? type_name
1385
- type_name = map_go_to_ruby[type_name]
1386
- end
1387
- if fundamental == GObject::GFLAGS_TYPE ||
1388
- fundamental == GObject::GENUM_TYPE
1389
- type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1390
- end
1391
- blurb = GObject::g_param_spec_get_blurb pspec
1392
- value = {
1393
- name: name,
1394
- flags: arg_flags,
1395
- gtype: gtype,
1396
- type_name: type_name,
1397
- blurb: blurb
1398
- }
1399
-
1400
- if (arg_flags & ARGUMENT_INPUT) != 0
1401
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1402
- # note the first required input image, if any ... we
1403
- # will be a method of this instance
1404
- if !member_x && gtype == Vips::IMAGE_TYPE
1405
- member_x = value
1406
- else
1407
- required_input << value
1408
- end
1409
- else
1410
- optional_input << value
1411
- end
1412
- end
1438
+ # these have hand-written methods, see above
1439
+ NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
1413
1440
 
1414
- # MODIFY INPUT args count as OUTPUT as well
1415
- if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
1416
- ((arg_flags & ARGUMENT_INPUT) != 0 &&
1417
- (arg_flags & ARGUMENT_MODIFY) != 0)
1418
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1419
- required_output << value
1420
- else
1421
- optional_output << value
1422
- end
1423
- end
1441
+ # these are aliased (appear under several names)
1442
+ ALIAS = ["crop"]
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]
1424
1456
  end
1425
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
+
1426
1470
  print "# @!method "
1427
- print "self." unless member_x
1428
- print "#{nickname}("
1429
- print required_input.map { |x| x[:name] }.join(", ")
1430
- 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
1431
1475
  puts "**opts)"
1432
1476
 
1433
- puts "# #{description.capitalize}."
1477
+ puts "# #{introspect.description.capitalize}."
1478
+
1479
+ method_args.each do |details|
1480
+ yard_name = details[:yard_name]
1481
+ gtype = details[:gtype]
1482
+ blurb = details[:blurb]
1434
1483
 
1435
- required_input.each do |arg|
1436
- puts "# @param #{arg[:name]} [#{arg[:type_name]}] #{arg[:blurb]}"
1484
+ puts "# @param #{yard_name} [#{gtype_to_ruby(gtype)}] #{blurb}"
1437
1485
  end
1438
1486
 
1439
1487
  puts "# @param opts [Hash] Set of options"
1440
- optional_input.each do |arg|
1441
- puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
1442
- "#{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}"
1443
1495
  end
1444
- optional_output.each do |arg|
1445
- print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
1446
- 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}"
1447
1503
  end
1448
1504
 
1449
1505
  print "# @return ["
1450
1506
  if required_output.length == 0
1451
1507
  print "nil"
1452
1508
  elsif required_output.length == 1
1453
- print required_output.first[:type_name]
1509
+ print gtype_to_ruby(required_output.first[:gtype])
1454
1510
  else
1455
1511
  print "Array<"
1456
- print required_output.map { |x| x[:type_name] }.join(", ")
1512
+ print required_output.map{ |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1457
1513
  print ">"
1458
1514
  end
1459
1515
  if optional_output.length > 0
1460
1516
  print ", Hash<Symbol => Object>"
1461
1517
  end
1462
1518
  print "] "
1463
- print required_output.map { |x| x[:blurb] }.join(", ")
1519
+ print required_output.map{ |x| x[:blurb] }.join(", ")
1464
1520
  if optional_output.length > 0
1465
1521
  print ", " if required_output.length > 0
1466
1522
  print "Hash of optional output items"
@@ -1470,30 +1526,42 @@ module Vips
1470
1526
  puts ""
1471
1527
  end
1472
1528
 
1473
- generate_class = lambda do |gtype, _|
1474
- 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
1475
1535
 
1476
- if nickname
1477
- begin
1478
- # can fail for abstract types
1479
- op = Vips::Operation.new nickname
1480
- rescue Vips::Error
1481
- nil
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
1482
1541
  end
1483
1542
 
1484
- generate_operation.(gtype, nickname, op) if op
1485
- 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
1486
1550
 
1487
- Vips::vips_type_map gtype, generate_class, nil
1488
- end
1551
+ generate_operation(introspect) if introspect
1552
+ end
1553
+
1554
+ Vips::vips_type_map gtype, generate_class, nil
1555
+ end
1489
1556
 
1490
- puts "module Vips"
1491
- puts " class Image"
1492
- puts ""
1557
+ puts "module Vips"
1558
+ puts " class Image"
1559
+ puts ""
1493
1560
 
1494
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1561
+ generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1495
1562
 
1496
- puts " end"
1497
- puts "end"
1563
+ puts " end"
1564
+ puts "end"
1565
+ end
1498
1566
  end
1499
1567
  end