ruby-vips 2.0.17 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +13 -0
  7. data/Gemfile +3 -1
  8. data/README.md +4 -4
  9. data/Rakefile +13 -21
  10. data/TODO +3 -6
  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 +35 -36
  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 +121 -75
  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 +232 -155
  34. data/lib/vips/interpolate.rb +3 -2
  35. data/lib/vips/methods.rb +165 -15
  36. data/lib/vips/mutableimage.rb +154 -0
  37. data/lib/vips/object.rb +84 -85
  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 +28 -48
  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
@@ -0,0 +1,154 @@
1
+ # This module provides an interface to the vips image processing library
2
+ # via ruby-ffi.
3
+ #
4
+ # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
+ # License:: MIT
6
+
7
+ require "ffi"
8
+ require "forwardable"
9
+
10
+ module Vips
11
+ # This class represents a libvips image which can be modified. See
12
+ # {Vips::Image#mutate}.
13
+ class MutableImage < Vips::Object
14
+ extend Forwardable
15
+ alias_method :parent_get_typeof, :get_typeof
16
+ def_instance_delegators :@image, :width, :height, :bands, :format,
17
+ :interpretation, :filename, :xoffset, :yoffset, :xres, :yres, :size,
18
+ :get, :get_typeof, :get_fields
19
+
20
+ # layout is exactly as {Image} (since we are also wrapping a VipsImage
21
+ # object)
22
+ module MutableImageLayout
23
+ def self.included base
24
+ base.class_eval do
25
+ layout :parent, Vips::Object::Struct
26
+ # rest opaque
27
+ end
28
+ end
29
+ end
30
+
31
+ class Struct < Vips::Object::Struct
32
+ include MutableImageLayout
33
+ end
34
+
35
+ class ManagedStruct < Vips::Object::ManagedStruct
36
+ include MutableImageLayout
37
+ end
38
+
39
+ # Get the {Image} this {MutableImage} is modifying. Only use this once you
40
+ # have finished all modifications.
41
+ #
42
+ # This is for internal use only. See {Vips::Image#mutate} for the
43
+ # user-facing interface.
44
+ attr_reader :image
45
+
46
+ # Make a {MutableImage} from a regular {Image}.
47
+ #
48
+ # This is for internal use only. See {Vips::Image#mutate} for the
49
+ # user-facing interface.
50
+ def initialize(image)
51
+ # We take a copy of the regular Image to ensure we have an unshared
52
+ # (unique) object. We forward things like #width and #height to this, and
53
+ # it's the thing we return at the end of the mutate block.
54
+ copy_image = image.copy
55
+
56
+ # use ptr since we need the raw unwrapped pointer inside the image ...
57
+ # and make the ref that gobject will unref when it finishes
58
+ pointer = copy_image.ptr
59
+ ::GObject.g_object_ref pointer
60
+ super pointer
61
+
62
+ # and save the copy ready for when we finish mutating
63
+ @image = copy_image
64
+ end
65
+
66
+ def inspect
67
+ "#<MutableImage #{width}x#{height} #{format}, #{bands} bands, #{interpretation}>"
68
+ end
69
+
70
+ def respond_to? name, include_all = false
71
+ # To support keyword args, we need to tell Ruby that final image
72
+ # arguments cannot be hashes of keywords.
73
+ #
74
+ # https://makandracards.com/makandra/
75
+ # 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
76
+ return false if name == :to_hash
77
+
78
+ super
79
+ end
80
+
81
+ def respond_to_missing? name, include_all = false
82
+ # respond to all vips operations by nickname
83
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
84
+
85
+ super
86
+ end
87
+
88
+ # Invoke a vips operation with {Vips::Operation#call}, using self as
89
+ # the first input argument. {Vips::Operation#call} will only allow
90
+ # operations that modify self when passed a {MutableImage}.
91
+ #
92
+ # @param name [String] vips operation to call
93
+ # @return result of vips operation
94
+ def method_missing name, *args, **options
95
+ Vips::Operation.call name.to_s, [self, *args], options
96
+ end
97
+
98
+ # Create a metadata item on an image of the specifed type. Ruby types
99
+ # are automatically transformed into the matching glib type (eg.
100
+ # {GObject::GINT_TYPE}), if possible.
101
+ #
102
+ # For example, you can use this to set an image's ICC profile:
103
+ #
104
+ # ```ruby
105
+ # x.set_type! Vips::BLOB_TYPE, "icc-profile-data", profile
106
+ # ```
107
+ #
108
+ # where `profile` is an ICC profile held as a binary string object.
109
+ #
110
+ # @see set!
111
+ # @param gtype [Integer] GType of item
112
+ # @param name [String] Metadata field to set
113
+ # @param value [Object] Value to set
114
+ def set_type! gtype, name, value
115
+ gvalue = GObject::GValue.alloc
116
+ gvalue.init gtype
117
+ gvalue.set value
118
+ Vips.vips_image_set self, name, gvalue
119
+ gvalue.unset
120
+ end
121
+
122
+ # Set the value of a metadata item on an image. The metadata item must
123
+ # already exist. Ruby types are automatically transformed into the
124
+ # matching {GObject::GValue}, if possible.
125
+ #
126
+ # For example, you can use this to set an image's ICC profile:
127
+ #
128
+ # ```
129
+ # x.set! "icc-profile-data", profile
130
+ # ```
131
+ #
132
+ # where `profile` is an ICC profile held as a binary string object.
133
+ #
134
+ # @see set_type!
135
+ # @param name [String] Metadata field to set
136
+ # @param value [Object] Value to set
137
+ def set! name, value
138
+ set_type! get_typeof(name), name, value
139
+ end
140
+
141
+ # Remove a metadata item from an image.
142
+ #
143
+ # For example:
144
+ #
145
+ # ```
146
+ # x.remove! "icc-profile-data"
147
+ # ```
148
+ #
149
+ # @param name [String] Metadata field to remove
150
+ def remove! name
151
+ Vips.vips_image_remove self, name
152
+ end
153
+ end
154
+ end
data/lib/vips/object.rb CHANGED
@@ -4,7 +4,7 @@
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
@@ -21,20 +21,20 @@ module Vips
21
21
  public
22
22
 
23
23
  # some handy gtypes
24
- IMAGE_TYPE = GObject::g_type_from_name "VipsImage"
25
- ARRAY_INT_TYPE = GObject::g_type_from_name "VipsArrayInt"
26
- ARRAY_DOUBLE_TYPE = GObject::g_type_from_name "VipsArrayDouble"
27
- ARRAY_IMAGE_TYPE = GObject::g_type_from_name "VipsArrayImage"
28
- REFSTR_TYPE = GObject::g_type_from_name "VipsRefString"
29
- BLOB_TYPE = GObject::g_type_from_name "VipsBlob"
30
-
31
- BAND_FORMAT_TYPE = Vips::vips_band_format_get_type
32
- INTERPRETATION_TYPE = Vips::vips_interpretation_get_type
33
- CODING_TYPE = Vips::vips_coding_get_type
34
-
35
- if Vips::at_least_libvips?(8, 6)
24
+ IMAGE_TYPE = GObject.g_type_from_name "VipsImage"
25
+ ARRAY_INT_TYPE = GObject.g_type_from_name "VipsArrayInt"
26
+ ARRAY_DOUBLE_TYPE = GObject.g_type_from_name "VipsArrayDouble"
27
+ ARRAY_IMAGE_TYPE = GObject.g_type_from_name "VipsArrayImage"
28
+ REFSTR_TYPE = GObject.g_type_from_name "VipsRefString"
29
+ BLOB_TYPE = GObject.g_type_from_name "VipsBlob"
30
+
31
+ BAND_FORMAT_TYPE = Vips.vips_band_format_get_type
32
+ INTERPRETATION_TYPE = Vips.vips_interpretation_get_type
33
+ CODING_TYPE = Vips.vips_coding_get_type
34
+
35
+ if Vips.at_least_libvips?(8, 6)
36
36
  attach_function :vips_blend_mode_get_type, [], :GType
37
- BLEND_MODE_TYPE = Vips::vips_blend_mode_get_type
37
+ BLEND_MODE_TYPE = Vips.vips_blend_mode_get_type
38
38
  else
39
39
  BLEND_MODE_TYPE = nil
40
40
  end
@@ -45,37 +45,37 @@ module Vips
45
45
  layout :im, :pointer,
46
46
  :run, :int,
47
47
  :eta, :int,
48
- :tpels, :int64_t,
49
- :npels, :int64_t,
50
- :percent, :int,
51
- :start, :pointer
48
+ :tpels, :int64_t,
49
+ :npels, :int64_t,
50
+ :percent, :int,
51
+ :start, :pointer
52
52
  end
53
53
 
54
- # Our signal marshalers.
54
+ # Our signal marshalers.
55
55
  #
56
- # These are functions which take the handler as a param and return a
56
+ # These are functions which take the handler as a param and return a
57
57
  # closure with the right FFI signature for g_signal_connect for this
58
- # specific signal.
58
+ # specific signal.
59
59
  #
60
- # ruby-ffi makes it hard to use the g_signal_connect user data param
60
+ # ruby-ffi makes it hard to use the g_signal_connect user data param
61
61
  # to pass the function pointer through, unfortunately.
62
62
  #
63
63
  # We can't throw exceptions across C, so we must catch everything.
64
64
 
65
- MARSHAL_PROGRESS = Proc.new do |handler|
65
+ MARSHAL_PROGRESS = proc do |handler|
66
66
  FFI::Function.new(:void, [:pointer, :pointer, :pointer]) do |vi, prog, cb|
67
67
  begin
68
- handler.(Progress.new(prog))
68
+ handler.call(Progress.new(prog))
69
69
  rescue Exception => e
70
70
  puts "progress: #{e}"
71
71
  end
72
72
  end
73
73
  end
74
74
 
75
- MARSHAL_READ = Proc.new do |handler|
75
+ MARSHAL_READ = proc do |handler|
76
76
  FFI::Function.new(:int64_t, [:pointer, :pointer, :int64_t]) do |i, p, len|
77
77
  begin
78
- result = handler.(p, len)
78
+ result = handler.call(p, len)
79
79
  rescue Exception => e
80
80
  puts "read: #{e}"
81
81
  result = 0
@@ -85,10 +85,10 @@ module Vips
85
85
  end
86
86
  end
87
87
 
88
- MARSHAL_SEEK = Proc.new do |handler|
88
+ MARSHAL_SEEK = proc do |handler|
89
89
  FFI::Function.new(:int64_t, [:pointer, :int64_t, :int]) do |i, off, whence|
90
90
  begin
91
- result = handler.(off, whence)
91
+ result = handler.call(off, whence)
92
92
  rescue Exception => e
93
93
  puts "seek: #{e}"
94
94
  result = -1
@@ -98,10 +98,10 @@ module Vips
98
98
  end
99
99
  end
100
100
 
101
- MARSHAL_WRITE = Proc.new do |handler|
101
+ MARSHAL_WRITE = proc do |handler|
102
102
  FFI::Function.new(:int64_t, [:pointer, :pointer, :int64_t]) do |i, p, len|
103
103
  begin
104
- result = handler.(p, len)
104
+ result = handler.call(p, len)
105
105
  rescue Exception => e
106
106
  puts "write: #{e}"
107
107
  result = 0
@@ -111,10 +111,10 @@ module Vips
111
111
  end
112
112
  end
113
113
 
114
- MARSHAL_FINISH = Proc.new do |handler|
114
+ MARSHAL_FINISH = proc do |handler|
115
115
  FFI::Function.new(:void, [:pointer, :pointer]) do |i, cb|
116
116
  begin
117
- handler.()
117
+ handler.call
118
118
  rescue Exception => e
119
119
  puts "finish: #{e}"
120
120
  end
@@ -123,29 +123,29 @@ module Vips
123
123
 
124
124
  # map signal name to marshal proc
125
125
  MARSHAL_ALL = {
126
- :preeval => MARSHAL_PROGRESS,
127
- :eval => MARSHAL_PROGRESS,
128
- :posteval => MARSHAL_PROGRESS,
129
- :read => MARSHAL_READ,
130
- :seek => MARSHAL_SEEK,
131
- :write => MARSHAL_WRITE,
132
- :finish => MARSHAL_FINISH,
126
+ preeval: MARSHAL_PROGRESS,
127
+ eval: MARSHAL_PROGRESS,
128
+ posteval: MARSHAL_PROGRESS,
129
+ read: MARSHAL_READ,
130
+ seek: MARSHAL_SEEK,
131
+ write: MARSHAL_WRITE,
132
+ finish: MARSHAL_FINISH
133
133
  }
134
134
 
135
135
  attach_function :vips_enum_from_nick, [:string, :GType, :string], :int
136
136
  attach_function :vips_enum_nick, [:GType, :int], :string
137
137
 
138
138
  attach_function :vips_value_set_ref_string,
139
- [GObject::GValue.ptr, :string], :void
139
+ [GObject::GValue.ptr, :string], :void
140
140
  attach_function :vips_value_set_array_double,
141
- [GObject::GValue.ptr, :pointer, :int], :void
141
+ [GObject::GValue.ptr, :pointer, :int], :void
142
142
  attach_function :vips_value_set_array_int,
143
- [GObject::GValue.ptr, :pointer, :int], :void
143
+ [GObject::GValue.ptr, :pointer, :int], :void
144
144
  attach_function :vips_value_set_array_image,
145
- [GObject::GValue.ptr, :int], :void
145
+ [GObject::GValue.ptr, :int], :void
146
146
  callback :free_fn, [:pointer], :void
147
147
  attach_function :vips_value_set_blob,
148
- [GObject::GValue.ptr, :free_fn, :pointer, :size_t], :void
148
+ [GObject::GValue.ptr, :free_fn, :pointer, :size_t], :void
149
149
 
150
150
  class SizeStruct < FFI::Struct
151
151
  layout :value, :size_t
@@ -156,15 +156,15 @@ module Vips
156
156
  end
157
157
 
158
158
  attach_function :vips_value_get_ref_string,
159
- [GObject::GValue.ptr, SizeStruct.ptr], :string
159
+ [GObject::GValue.ptr, SizeStruct.ptr], :string
160
160
  attach_function :vips_value_get_array_double,
161
- [GObject::GValue.ptr, IntStruct.ptr], :pointer
161
+ [GObject::GValue.ptr, IntStruct.ptr], :pointer
162
162
  attach_function :vips_value_get_array_int,
163
- [GObject::GValue.ptr, IntStruct.ptr], :pointer
163
+ [GObject::GValue.ptr, IntStruct.ptr], :pointer
164
164
  attach_function :vips_value_get_array_image,
165
- [GObject::GValue.ptr, IntStruct.ptr], :pointer
165
+ [GObject::GValue.ptr, IntStruct.ptr], :pointer
166
166
  attach_function :vips_value_get_blob,
167
- [GObject::GValue.ptr, SizeStruct.ptr], :pointer
167
+ [GObject::GValue.ptr, SizeStruct.ptr], :pointer
168
168
 
169
169
  attach_function :type_find, :vips_type_find, [:string, :string], :GType
170
170
 
@@ -173,7 +173,7 @@ module Vips
173
173
  # debugging ruby-vips.
174
174
  def self.print_all
175
175
  GC.start
176
- Vips::vips_object_print_all
176
+ Vips.vips_object_print_all
177
177
  end
178
178
 
179
179
  # the layout of the VipsObject struct
@@ -182,15 +182,15 @@ module Vips
182
182
  base.class_eval do
183
183
  # don't actually need most of these
184
184
  layout :parent, GObject::GObject::Struct,
185
- :constructed, :int,
186
- :static_object, :int,
187
- :argument_table, :pointer,
188
- :nickname, :string,
189
- :description, :string,
190
- :preclose, :int,
191
- :close, :int,
192
- :postclose, :int,
193
- :local_memory, :size_t
185
+ :constructed, :int,
186
+ :static_object, :int,
187
+ :argument_table, :pointer,
188
+ :nickname, :string,
189
+ :description, :string,
190
+ :preclose, :int,
191
+ :close, :int,
192
+ :postclose, :int,
193
+ :local_memory, :size_t
194
194
  end
195
195
  end
196
196
  end
@@ -210,8 +210,8 @@ module Vips
210
210
  argument_class = Vips::ArgumentClassPtr.new
211
211
  argument_instance = Vips::ArgumentInstancePtr.new
212
212
 
213
- result = Vips::vips_object_get_argument self, name,
214
- ppspec, argument_class, argument_instance
213
+ result = Vips.vips_object_get_argument self, name,
214
+ ppspec, argument_class, argument_instance
215
215
  return nil if result != 0
216
216
 
217
217
  ppspec[:value]
@@ -229,7 +229,7 @@ module Vips
229
229
  def get_typeof name
230
230
  pspec = get_pspec name
231
231
  unless pspec
232
- Vips::vips_error_clear
232
+ Vips.vips_error_clear
233
233
  return 0
234
234
  end
235
235
 
@@ -240,50 +240,49 @@ module Vips
240
240
  gtype = get_typeof_error name
241
241
  gvalue = GObject::GValue.alloc
242
242
  gvalue.init gtype
243
- GObject::g_object_get_property self, name, gvalue
243
+ GObject.g_object_get_property self, name, gvalue
244
244
  result = gvalue.get
245
245
  gvalue.unset
246
246
 
247
- GLib::logger.debug("Vips::Object.get") { "#{name} == #{result}" }
247
+ GLib.logger.debug("Vips::Object.get") { "#{name} == #{result}" }
248
248
 
249
- return result
249
+ result
250
250
  end
251
251
 
252
252
  def set name, value
253
- GLib::logger.debug("Vips::Object.set") { "#{name} = #{value}" }
253
+ GLib.logger.debug("Vips::Object.set") { "#{name} = #{value}" }
254
254
 
255
255
  gtype = get_typeof_error name
256
256
  gvalue = GObject::GValue.alloc
257
257
  gvalue.init gtype
258
258
  gvalue.set value
259
- GObject::g_object_set_property self, name, gvalue
259
+ GObject.g_object_set_property self, name, gvalue
260
260
  gvalue.unset
261
261
  end
262
262
 
263
- def signal_connect name, handler=nil
263
+ def signal_connect name, handler = nil, &block
264
264
  marshal = MARSHAL_ALL[name.to_sym]
265
- raise Vips::Error, "unsupported signal #{name}" if marshal == nil
265
+ raise Vips::Error, "unsupported signal #{name}" if marshal.nil?
266
266
 
267
- if block_given?
268
- # This will grab any block given to us and make it into a proc
269
- prc = Proc.new
267
+ if block
268
+ # our block as a Proc
269
+ prc = block
270
270
  elsif handler
271
- # We assume the hander is a proc (perhaps we should test)
271
+ # We assume the hander is a Proc (perhaps we should test)
272
272
  prc = handler
273
273
  else
274
274
  raise Vips::Error, "must supply either block or handler"
275
275
  end
276
276
 
277
- # The marshal function will make a closure with the right type signature
277
+ # The marshal function will make a closure with the right type signature
278
278
  # for the selected signal
279
- callback = marshal.(prc)
279
+ callback = marshal.call(prc)
280
280
 
281
281
  # we need to make sure this is not GCd while self is alive
282
282
  @references << callback
283
283
 
284
- GObject::g_signal_connect_data(self, name.to_s, callback, nil, nil, 0)
284
+ GObject.g_signal_connect_data(self, name.to_s, callback, nil, nil, 0)
285
285
  end
286
-
287
286
  end
288
287
 
289
288
  class ObjectClass < FFI::Struct
@@ -322,10 +321,10 @@ module Vips
322
321
 
323
322
  class ArgumentClass < Argument
324
323
  layout :parent, Argument,
325
- :object_class, ObjectClass.ptr,
326
- :flags, :uint,
327
- :priority, :int,
328
- :offset, :ulong_long
324
+ :object_class, ObjectClass.ptr,
325
+ :flags, :uint,
326
+ :priority, :int,
327
+ :offset, :ulong_long
329
328
  end
330
329
 
331
330
  class ArgumentClassPtr < FFI::Struct
@@ -339,10 +338,10 @@ module Vips
339
338
  # just use :pointer, not VipsObject.ptr, to avoid casting gobject
340
339
  # subclasses
341
340
  attach_function :vips_object_get_argument,
342
- [:pointer, :string,
343
- GObject::GParamSpecPtr.ptr,
344
- ArgumentClassPtr.ptr, ArgumentInstancePtr.ptr],
345
- :int
341
+ [:pointer, :string,
342
+ GObject::GParamSpecPtr.ptr,
343
+ ArgumentClassPtr.ptr, ArgumentInstancePtr.ptr],
344
+ :int
346
345
 
347
346
  attach_function :vips_object_print_all, [], :void
348
347