ruby-vips 2.0.13 → 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 (69) 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 +44 -0
  7. data/Gemfile +3 -1
  8. data/README.md +45 -47
  9. data/Rakefile +13 -15
  10. data/TODO +19 -10
  11. data/VERSION +1 -1
  12. data/example/annotate.rb +7 -7
  13. data/example/connection.rb +26 -0
  14. data/example/daltonize8.rb +27 -29
  15. data/example/draw_lines.rb +30 -0
  16. data/example/example1.rb +5 -6
  17. data/example/example2.rb +11 -11
  18. data/example/example3.rb +9 -9
  19. data/example/example4.rb +8 -8
  20. data/example/example5.rb +8 -9
  21. data/example/inheritance_with_refcount.rb +203 -221
  22. data/example/progress.rb +30 -0
  23. data/example/thumb.rb +12 -14
  24. data/example/trim8.rb +7 -7
  25. data/example/watermark.rb +15 -36
  26. data/example/wobble.rb +25 -25
  27. data/lib/ruby-vips.rb +1 -1
  28. data/lib/vips.rb +473 -338
  29. data/lib/vips/access.rb +9 -9
  30. data/lib/vips/align.rb +7 -8
  31. data/lib/vips/angle.rb +8 -9
  32. data/lib/vips/angle45.rb +12 -13
  33. data/lib/vips/bandformat.rb +16 -18
  34. data/lib/vips/blend_mode.rb +36 -0
  35. data/lib/vips/coding.rb +11 -12
  36. data/lib/vips/compass_direction.rb +13 -14
  37. data/lib/vips/connection.rb +46 -0
  38. data/lib/vips/direction.rb +7 -8
  39. data/lib/vips/extend.rb +13 -14
  40. data/lib/vips/gobject.rb +111 -100
  41. data/lib/vips/gvalue.rb +243 -237
  42. data/lib/vips/image.rb +1501 -1338
  43. data/lib/vips/interesting.rb +10 -11
  44. data/lib/vips/interpolate.rb +50 -54
  45. data/lib/vips/interpretation.rb +25 -26
  46. data/lib/vips/kernel.rb +18 -19
  47. data/lib/vips/methods.rb +929 -309
  48. data/lib/vips/mutableimage.rb +154 -0
  49. data/lib/vips/object.rb +318 -208
  50. data/lib/vips/operation.rb +467 -320
  51. data/lib/vips/operationboolean.rb +10 -11
  52. data/lib/vips/operationcomplex.rb +8 -9
  53. data/lib/vips/operationcomplex2.rb +6 -7
  54. data/lib/vips/operationcomplexget.rb +7 -8
  55. data/lib/vips/operationmath.rb +14 -15
  56. data/lib/vips/operationmath2.rb +6 -7
  57. data/lib/vips/operationrelational.rb +11 -12
  58. data/lib/vips/operationround.rb +7 -8
  59. data/lib/vips/region.rb +73 -0
  60. data/lib/vips/size.rb +9 -10
  61. data/lib/vips/source.rb +88 -0
  62. data/lib/vips/sourcecustom.rb +89 -0
  63. data/lib/vips/target.rb +86 -0
  64. data/lib/vips/targetcustom.rb +77 -0
  65. data/lib/vips/version.rb +1 -2
  66. data/ruby-vips.gemspec +29 -20
  67. metadata +51 -40
  68. data/.travis.yml +0 -55
  69. data/install-vips.sh +0 -26
data/lib/vips/image.rb CHANGED
@@ -4,1505 +4,1668 @@
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
- private
10
+ private
11
11
 
12
- attach_function :vips_image_new_matrix_from_array,
13
- [:int, :int, :pointer, :int], :pointer
12
+ attach_function :vips_image_new_matrix_from_array,
13
+ [:int, :int, :pointer, :int], :pointer
14
14
 
15
- attach_function :vips_image_copy_memory, [:pointer], :pointer
15
+ attach_function :vips_image_copy_memory, [:pointer], :pointer
16
16
 
17
- attach_function :vips_filename_get_filename, [:string], :pointer
18
- attach_function :vips_filename_get_options, [:string], :pointer
17
+ attach_function :vips_image_set_progress, [:pointer, :bool], :void
18
+ attach_function :vips_image_set_kill, [:pointer, :bool], :void
19
19
 
20
- attach_function :vips_foreign_find_load, [:string], :string
21
- attach_function :vips_foreign_find_save, [:string], :string
22
- attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
23
- attach_function :vips_foreign_find_save_buffer, [:string], :string
20
+ attach_function :vips_filename_get_filename, [:string], :pointer
21
+ attach_function :vips_filename_get_options, [:string], :pointer
24
22
 
25
- attach_function :vips_image_write_to_memory,
26
- [:pointer, SizeStruct.ptr], :pointer
23
+ attach_function :vips_foreign_find_load, [:string], :string
24
+ attach_function :vips_foreign_find_save, [:string], :string
25
+ attach_function :vips_foreign_find_load_buffer, [:pointer, :size_t], :string
26
+ attach_function :vips_foreign_find_save_buffer, [:string], :string
27
27
 
28
- attach_function :vips_image_get_typeof, [:pointer, :string], :GType
29
- attach_function :vips_image_get,
30
- [:pointer, :string, GObject::GValue.ptr], :int
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
31
32
 
32
- # vips_image_get_fields was added in libvips 8.5
33
- begin
34
- attach_function :vips_image_get_fields, [:pointer], :pointer
35
- rescue FFI::NotFoundError
36
- end
33
+ attach_function :vips_image_write_to_memory,
34
+ [:pointer, SizeStruct.ptr], :pointer
37
35
 
38
- # vips_addalpha was added in libvips 8.6
39
- if Vips::at_least_libvips?(8, 6)
40
- attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
41
- end
42
- if Vips::at_least_libvips?(8, 5)
43
- attach_function :vips_image_hasalpha, [:pointer], :int
44
- end
36
+ attach_function :vips_image_get_typeof, [:pointer, :string], :GType
37
+ attach_function :vips_image_get,
38
+ [:pointer, :string, GObject::GValue.ptr], :int
45
39
 
46
- attach_function :vips_image_set,
47
- [:pointer, :string, GObject::GValue.ptr], :void
48
- attach_function :vips_image_remove, [:pointer, :string], :void
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
49
43
 
50
- attach_function :vips_band_format_iscomplex, [:int], :int
51
- attach_function :vips_band_format_isfloat, [:int], :int
44
+ if Vips.at_least_libvips?(8, 5)
45
+ attach_function :vips_image_get_fields, [:pointer], :pointer
46
+ attach_function :vips_image_hasalpha, [:pointer], :int
47
+ end
52
48
 
53
- attach_function :nickname_find, :vips_nickname_find, [:GType], :string
49
+ if Vips.at_least_libvips?(8, 6)
50
+ attach_function :vips_addalpha, [:pointer, :pointer, :varargs], :int
51
+ end
54
52
 
55
- # turn a raw pointer that must be freed into a self-freeing Ruby string
56
- def self.p2str(pointer)
57
- pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
58
- pointer.read_string
59
- end
53
+ # move these three lines to mutableimage when we finally remove set and
54
+ # remove in this class
55
+ attach_function :vips_image_set,
56
+ [:pointer, :string, GObject::GValue.ptr], :void
57
+ attach_function :vips_image_remove, [:pointer, :string], :void
60
58
 
61
- public
59
+ attach_function :vips_band_format_iscomplex, [:int], :int
60
+ attach_function :vips_band_format_isfloat, [:int], :int
62
61
 
63
- # This class represents a libvips image. See the {Vips} module documentation
64
- # for an introduction to using this class.
62
+ attach_function :nickname_find, :vips_nickname_find, [:GType], :string
65
63
 
66
- class Image < Vips::Object
67
- alias_method :parent_get_typeof, :get_typeof
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
68
66
 
69
- private
67
+ # turn a raw pointer that must be freed into a self-freeing Ruby string
68
+ def self.p2str(pointer)
69
+ pointer = FFI::AutoPointer.new(pointer, GLib::G_FREE)
70
+ pointer.read_string
71
+ end
70
72
 
71
- # the layout of the VipsImage struct
72
- module ImageLayout
73
- def self.included base
74
- base.class_eval do
75
- layout :parent, Vips::Object::Struct
76
- # rest opaque
77
- end
78
- end
79
- end
73
+ public
80
74
 
81
- class Struct < Vips::Object::Struct
82
- include ImageLayout
75
+ # This class represents a libvips image. See the {Vips} module documentation
76
+ # for an introduction to using this class.
83
77
 
84
- end
78
+ class Image < Vips::Object
79
+ alias_method :parent_get_typeof, :get_typeof
85
80
 
86
- class ManagedStruct < Vips::Object::ManagedStruct
87
- include ImageLayout
81
+ private
88
82
 
83
+ # the layout of the VipsImage struct
84
+ module ImageLayout
85
+ def self.included base
86
+ base.class_eval do
87
+ layout :parent, Vips::Object::Struct
88
+ # rest opaque
89
89
  end
90
+ end
91
+ end
90
92
 
91
- class GenericPtr < FFI::Struct
92
- layout :value, :pointer
93
- end
93
+ class Struct < Vips::Object::Struct
94
+ include ImageLayout
95
+ end
94
96
 
95
- # handy for overloads ... want to be able to apply a function to an
96
- # array or to a scalar
97
- def self.smap x, &block
98
- x.is_a?(Array) ? x.map {|y| smap(y, &block)} : block.(x)
99
- end
97
+ class ManagedStruct < Vips::Object::ManagedStruct
98
+ include ImageLayout
99
+ end
100
100
 
101
- def self.complex? format
102
- format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
103
- Vips::vips_band_format_iscomplex(format_number) != 0
101
+ class GenericPtr < FFI::Struct
102
+ layout :value, :pointer
103
+ end
104
+
105
+ # handy for overloads ... want to be able to apply a function to an
106
+ # array or to a scalar
107
+ def self.smap x, &block
108
+ x.is_a?(Array) ? x.map { |y| smap(y, &block) } : block.call(x)
109
+ end
110
+
111
+ def self.complex? format
112
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
113
+ Vips.vips_band_format_iscomplex(format_number) != 0
114
+ end
115
+
116
+ def self.float? format
117
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
118
+ Vips.vips_band_format_isfloat(format_number) != 0
119
+ end
120
+
121
+ # run a complex operation on a complex image, or an image with an even
122
+ # number of bands ... handy for things like running .polar on .index
123
+ # images
124
+ def self.run_cmplx image, &block
125
+ original_format = image.format
126
+
127
+ unless Image.complex? image.format
128
+ if image.bands % 2 != 0
129
+ raise Vips::Error, "not an even number of bands"
104
130
  end
105
131
 
106
- def self.float? format
107
- format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
108
- Vips::vips_band_format_isfloat(format_number) != 0
132
+ unless Image.float? image.format
133
+ image = image.cast :float
109
134
  end
110
135
 
111
- # run a complex operation on a complex image, or an image with an even
112
- # number of bands ... handy for things like running .polar on .index
113
- # images
114
- def self.run_cmplx image, &block
115
- original_format = image.format
136
+ new_format = image.format == :double ? :dpcomplex : :complex
137
+ image = image.copy format: new_format, bands: image.bands / 2
138
+ end
116
139
 
117
- unless Image::complex? image.format
118
- if image.bands % 2 != 0
119
- raise Error, "not an even number of bands"
120
- end
140
+ image = block.call(image)
121
141
 
122
- unless Image::float? image.format
123
- image = image.cast :float
124
- end
142
+ unless Image.complex? original_format
143
+ new_format = image.format == :dpcomplex ? :double : :float
144
+ image = image.copy format: new_format, bands: image.bands * 2
145
+ end
125
146
 
126
- new_format = image.format == :double ? :dpcomplex : :complex
127
- image = image.copy format: new_format, bands: image.bands / 2
128
- end
147
+ image
148
+ end
129
149
 
130
- image = block.(image)
150
+ # handy for expanding enum operations
151
+ def call_enum(name, other, enum)
152
+ if other.is_a?(Vips::Image)
153
+ Vips::Operation.call name.to_s, [self, other, enum]
154
+ else
155
+ Vips::Operation.call name.to_s + "_const", [self, enum, other]
156
+ end
157
+ end
131
158
 
132
- unless Image::complex? original_format
133
- new_format = image.format == :dpcomplex ? :double : :float
134
- image = image.copy format: new_format, bands: image.bands * 2
135
- end
159
+ # Write can fail due to no file descriptors and memory can fill if
160
+ # large objects are not collected fairly soon. We can't try a
161
+ # write and GC and retry on fail, since the write may take a
162
+ # long time and may not be repeatable.
163
+ #
164
+ # GCing before every write would have a horrible effect on
165
+ # performance, so as a compromise we GC every @@gc_interval writes.
166
+ #
167
+ # ruby2.1 introduced a generational GC which is fast enough to be
168
+ # able to GC on every write.
136
169
 
137
- image
138
- end
170
+ @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
139
171
 
140
- # handy for expanding enum operations
141
- def call_enum(name, other, enum)
142
- if other.is_a?(Vips::Image)
143
- Vips::Operation.call name.to_s, [self, other, enum]
144
- else
145
- Vips::Operation.call name.to_s + "_const", [self, enum, other]
146
- end
147
- end
172
+ @@gc_interval = 100
173
+ @@gc_countdown = @@gc_interval
148
174
 
149
- # Write can fail due to no file descriptors and memory can fill if
150
- # large objects are not collected fairly soon. We can't try a
151
- # write and GC and retry on fail, since the write may take a
152
- # long time and may not be repeatable.
153
- #
154
- # GCing before every write would have a horrible effect on
155
- # performance, so as a compromise we GC every @@gc_interval writes.
156
- #
157
- # ruby2.1 introduced a generational GC which is fast enough to be
158
- # able to GC on every write.
159
-
160
- @@generational_gc = RUBY_ENGINE == "ruby" && RUBY_VERSION.to_f >= 2.1
161
-
162
- @@gc_interval = 100
163
- @@gc_countdown = @@gc_interval
164
-
165
- def write_gc
166
- if @@generational_gc
167
- GC.start full_mark: false
168
- else
169
- @@gc_countdown -= 1
170
- if @@gc_countdown < 0
171
- @@gc_countdown = @@gc_interval
172
- GC.start
173
- end
174
- end
175
+ def write_gc
176
+ if @@generational_gc
177
+ GC.start full_mark: false
178
+ else
179
+ @@gc_countdown -= 1
180
+ if @@gc_countdown < 0
181
+ @@gc_countdown = @@gc_interval
182
+ GC.start
175
183
  end
184
+ end
185
+ end
176
186
 
177
- public
187
+ public
178
188
 
179
- def inspect
180
- "#<Image #{width}x#{height} #{format}, #{bands} bands, " +
181
- "#{interpretation}>"
182
- end
189
+ def inspect
190
+ "#<Image #{width}x#{height} #{format}, #{bands} bands, #{interpretation}>"
191
+ end
183
192
 
184
- def respond_to? name, include_all = false
185
- # To support keyword args, we need to tell Ruby that final image
186
- # arguments cannot be hashes of keywords.
187
- #
188
- # https://makandracards.com/makandra/36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
189
- return false if name == :to_hash
193
+ def respond_to? name, include_all = false
194
+ # To support keyword args, we need to tell Ruby that final image
195
+ # arguments cannot be hashes of keywords.
196
+ #
197
+ # https://makandracards.com/makandra/
198
+ # 36013-heads-up-ruby-implicitly-converts-a-hash-to-keyword-arguments
199
+ return false if name == :to_hash
190
200
 
191
- # respond to all vips operations by nickname
192
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
201
+ super
202
+ end
193
203
 
194
- super
195
- end
204
+ def respond_to_missing? name, include_all = false
205
+ # respond to all vips operations by nickname
206
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
196
207
 
197
- def self.respond_to? name, include_all = false
198
- # respond to all vips operations by nickname
199
- return true if Vips::type_find("VipsOperation", name.to_s) != 0
208
+ super
209
+ end
200
210
 
201
- super
202
- end
211
+ def self.respond_to_missing? name, include_all = false
212
+ # respond to all vips operations by nickname
213
+ return true if Vips.type_find("VipsOperation", name.to_s) != 0
203
214
 
204
- # Invoke a vips operation with {Vips::Operation.call}, using self as
205
- # the first input argument.
206
- #
207
- # @param name [String] vips operation to call
208
- # @return result of vips operation
209
- def method_missing name, *args, **options
210
- Vips::Operation.call name.to_s, [self, *args], options
211
- end
215
+ super
216
+ end
212
217
 
213
- # Invoke a vips operation with {Vips::Operation.call}.
214
- def self.method_missing name, *args, **options
215
- Vips::Operation.call name.to_s, args, options
216
- end
218
+ # Invoke a vips operation with {Vips::Operation.call}, using self as
219
+ # the first input argument.
220
+ #
221
+ # @param name [String] vips operation to call
222
+ # @return result of vips operation
223
+ def method_missing name, *args, **options
224
+ Vips::Operation.call name.to_s, [self, *args], options
225
+ end
217
226
 
218
- # Return a new {Image} for a file on disc. This method can load
219
- # images in any format supported by vips. The filename can include
220
- # load options, for example:
221
- #
222
- # ```
223
- # image = Vips::new_from_file "fred.jpg[shrink=2]"
224
- # ```
225
- #
226
- # You can also supply options as a hash, for example:
227
- #
228
- # ```
229
- # image = Vips::new_from_file "fred.jpg", shrink: 2
230
- # ```
231
- #
232
- # The full set of options available depend upon the load operation that
233
- # will be executed. Try something like:
234
- #
235
- # ```
236
- # $ vips jpegload
237
- # ```
238
- #
239
- # at the command-line to see a summary of the available options for the
240
- # JPEG loader.
241
- #
242
- # Loading is fast: only enough of the image is loaded to be able to fill
243
- # out the header. Pixels will only be decompressed when they are needed.
244
- #
245
- # @!macro [new] vips.loadopts
246
- # @param opts [Hash] set of options
247
- # @option opts [Boolean] :disc (true) Open large images via a
248
- # temporary disc file
249
- # @option opts [Vips::Access] :access (:random) Access mode for file
250
- #
251
- # @param name [String] the filename to load from
252
- # @macro vips.loadopts
253
- # @return [Image] the loaded image
254
- def self.new_from_file name, opts = {}
255
- # very common, and Vips::vips_filename_get_filename will segv if we
256
- # pass this
257
- raise Vips::Error, "filename is nil" if name == nil
258
-
259
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
260
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
261
- loader = Vips::vips_foreign_find_load filename
262
- raise Vips::Error if loader == nil
263
-
264
- Operation.call loader, [filename], opts, option_string
265
- end
227
+ # Invoke a vips operation with {Vips::Operation.call}.
228
+ def self.method_missing name, *args, **options
229
+ Vips::Operation.call name.to_s, args, options
230
+ end
266
231
 
267
- # Create a new {Image} for an image encoded, in a format such as
268
- # JPEG, in a memory string. Load options may be passed as
269
- # strings or appended as a hash. For example:
270
- #
271
- # ```
272
- # image = Vips::Image.new_from_buffer memory_buffer, "shrink=2"
273
- # ```
274
- #
275
- # or alternatively:
276
- #
277
- # ```
278
- # image = Vips::Image.new_from_buffer memory_buffer, "", shrink: 2
279
- # ```
280
- #
281
- # The options available depend on the file format. Try something like:
282
- #
283
- # ```
284
- # $ vips jpegload_buffer
285
- # ```
286
- #
287
- # at the command-line to see the available options. Not all loaders
288
- # support load from buffer, but at least JPEG, PNG and
289
- # TIFF images will work.
290
- #
291
- # Loading is fast: only enough of the image is loaded to be able to fill
292
- # out the header. Pixels will only be decompressed when they are needed.
293
- #
294
- # @param data [String] the data to load from
295
- # @param option_string [String] load options as a string
296
- # @macro vips.loadopts
297
- # @return [Image] the loaded image
298
- def self.new_from_buffer data, option_string, opts = {}
299
- loader = Vips::vips_foreign_find_load_buffer data, data.bytesize
300
- raise Vips::Error if loader == nil
301
-
302
- Vips::Operation.call loader, [data], opts, option_string
303
- end
232
+ # Return a new {Image} for a file on disc. This method can load
233
+ # images in any format supported by vips. The filename can include
234
+ # load options, for example:
235
+ #
236
+ # ```
237
+ # image = Vips::Image.new_from_file "fred.jpg[shrink=2]"
238
+ # ```
239
+ #
240
+ # You can also supply options as a hash, for example:
241
+ #
242
+ # ```
243
+ # image = Vips::Image.new_from_file "fred.jpg", shrink: 2
244
+ # ```
245
+ #
246
+ # The full set of options available depend upon the load operation that
247
+ # will be executed. Try something like:
248
+ #
249
+ # ```
250
+ # $ vips jpegload
251
+ # ```
252
+ #
253
+ # at the command-line to see a summary of the available options for the
254
+ # JPEG loader.
255
+ #
256
+ # Loading is fast: only enough of the image is loaded to be able to fill
257
+ # out the header. Pixels will only be decompressed when they are needed.
258
+ #
259
+ # @!macro [new] vips.loadopts
260
+ # @param opts [Hash] set of options
261
+ # @option opts [Boolean] :disc (true) Open large images via a
262
+ # temporary disc file
263
+ # @option opts [Vips::Access] :access (:random) Access mode for file
264
+ #
265
+ # @param name [String] the filename to load from
266
+ # @macro vips.loadopts
267
+ # @return [Image] the loaded image
268
+ def self.new_from_file name, **opts
269
+ # very common, and Vips::vips_filename_get_filename will segv if we
270
+ # pass this
271
+ raise Vips::Error, "filename is nil" if name.nil?
272
+
273
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
274
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
275
+ loader = Vips.vips_foreign_find_load filename
276
+ raise Vips::Error if loader.nil?
277
+
278
+ Operation.call loader, [filename], opts, option_string
279
+ end
304
280
 
305
- def self.matrix_from_array width, height, array
306
- ptr = FFI::MemoryPointer.new :double, array.length
307
- ptr.write_array_of_double array
308
- image = Vips::vips_image_new_matrix_from_array width, height,
309
- ptr, array.length
310
- Vips::Image.new image
311
- end
281
+ # Create a new {Image} for an image encoded in a format such as
282
+ # JPEG in a binary string. Load options may be passed as
283
+ # strings or appended as a hash. For example:
284
+ #
285
+ # ```
286
+ # image = Vips::Image.new_from_buffer memory_buffer, "shrink=2"
287
+ # ```
288
+ #
289
+ # or alternatively:
290
+ #
291
+ # ```
292
+ # image = Vips::Image.new_from_buffer memory_buffer, "", shrink: 2
293
+ # ```
294
+ #
295
+ # The options available depend on the file format. Try something like:
296
+ #
297
+ # ```
298
+ # $ vips jpegload_buffer
299
+ # ```
300
+ #
301
+ # at the command-line to see the available options. Not all loaders
302
+ # support load from buffer, but at least JPEG, PNG and
303
+ # TIFF images will work.
304
+ #
305
+ # Loading is fast: only enough of the image is loaded to be able to fill
306
+ # out the header. Pixels will only be decompressed when they are needed.
307
+ #
308
+ # @param data [String] the data to load from
309
+ # @param option_string [String] load options as a string
310
+ # @macro vips.loadopts
311
+ # @return [Image] the loaded image
312
+ def self.new_from_buffer data, option_string, **opts
313
+ loader = Vips.vips_foreign_find_load_buffer data, data.bytesize
314
+ raise Vips::Error if loader.nil?
315
+
316
+ Vips::Operation.call loader, [data], opts, option_string
317
+ end
312
318
 
313
- # Create a new Image from a 1D or 2D array. A 1D array becomes an
314
- # image with height 1. Use `scale` and `offset` to set the scale and
315
- # offset fields in the header. These are useful for integer
316
- # convolutions.
317
- #
318
- # For example:
319
- #
320
- # ```
321
- # image = Vips::new_from_array [1, 2, 3]
322
- # ```
323
- #
324
- # or
325
- #
326
- # ```
327
- # image = Vips::new_from_array [
328
- # [-1, -1, -1],
329
- # [-1, 16, -1],
330
- # [-1, -1, -1]], 8
331
- # ```
332
- #
333
- # for a simple sharpening mask.
334
- #
335
- # @param array [Array] the pixel data as an array of numbers
336
- # @param scale [Real] the convolution scale
337
- # @param offset [Real] the convolution offset
338
- # @return [Image] the image
339
- def self.new_from_array array, scale = 1, offset = 0
340
- # we accept a 1D array and assume height == 1, or a 2D array
341
- # and check all lines are the same length
342
- unless array.is_a? Array
343
- raise Vips::Error, "Argument is not an array."
344
- end
345
-
346
- if array[0].is_a? Array
347
- height = array.length
348
- width = array[0].length
349
- unless array.all? {|x| x.is_a? Array}
350
- raise Vips::Error, "Not a 2D array."
351
- end
352
- unless array.all? {|x| x.length == width}
353
- raise Vips::Error, "Array not rectangular."
354
- end
355
- array = array.flatten
356
- else
357
- height = 1
358
- width = array.length
359
- end
360
-
361
- unless array.all? {|x| x.is_a? Numeric}
362
- raise Vips::Error, "Not all array elements are Numeric."
363
- end
364
-
365
- image = Vips::Image.matrix_from_array width, height, array
366
- raise Vips::Error if image == nil
367
-
368
- # be careful to set them as double
369
- image.set_type GObject::GDOUBLE_TYPE, 'scale', scale.to_f
370
- image.set_type GObject::GDOUBLE_TYPE, 'offset', offset.to_f
371
-
372
- return image
373
- end
319
+ # Create a new {Image} from a C-style array held in memory. For example:
320
+ #
321
+ # ```
322
+ # image = Vips::Image.black(16, 16) + 128
323
+ # data = image.write_to_memory
324
+ #
325
+ # x = Vips::Image.new_from_memory data,
326
+ # image.width, image.height, image.bands, image.format
327
+ # ```
328
+ #
329
+ # {new_from_memory} keeps a reference to the array of pixels you pass in
330
+ # to try to prevent that memory from being freed by the Ruby GC while it
331
+ # is being used.
332
+ #
333
+ # See {new_from_memory_copy} for a version of this method which does not
334
+ # keep a reference.
335
+ #
336
+ # @param data [String, FFI::Pointer] the data to load from
337
+ # @param width [Integer] width in pixels
338
+ # @param height [Integer] height in pixels
339
+ # @param bands [Integer] number of bands
340
+ # @param format [Symbol] band format
341
+ # @return [Image] the loaded image
342
+ def self.new_from_memory data, width, height, bands, format
343
+ size = data.bytesize
344
+
345
+ # prevent data from being freed with JRuby FFI
346
+ if defined?(JRUBY_VERSION) && !data.is_a?(FFI::Pointer)
347
+ data = ::FFI::MemoryPointer.new(:char, data.bytesize).write_bytes data
348
+ end
349
+
350
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
351
+ vi = Vips.vips_image_new_from_memory data, size,
352
+ width, height, bands, format_number
353
+ raise Vips::Error if vi.null?
354
+ image = new(vi)
355
+
356
+ # keep a secret ref to the underlying object .. this reference will be
357
+ # inherited by things that in turn depend on us, so the memory we are
358
+ # using will not be freed
359
+ image.references << data
360
+
361
+ image
362
+ end
374
363
 
375
- # A new image is created with the same width, height, format,
376
- # interpretation, resolution and offset as self, but with every pixel
377
- # set to the specified value.
378
- #
379
- # You can pass an array to make a many-band image, or a single value to
380
- # make a one-band image.
381
- #
382
- # @param value [Real, Array<Real>] value to put in each pixel
383
- # @return [Image] constant image
384
- def new_from_image value
385
- pixel = (Vips::Image.black(1, 1) + value).cast(format)
386
- image = pixel.embed 0, 0, width, height, extend: :copy
387
- image.copy interpretation: interpretation,
388
- xres: xres, yres: yres, xoffset: xoffset, yoffset: yoffset
389
- end
364
+ # Create a new {Image} from memory and copies the memory area. See
365
+ # {new_from_memory} for a version of this method which does not copy the
366
+ # memory area.
367
+ #
368
+ # @param data [String, FFI::Pointer] the data to load from
369
+ # @param width [Integer] width in pixels
370
+ # @param height [Integer] height in pixels
371
+ # @param bands [Integer] number of bands
372
+ # @param format [Symbol] band format
373
+ # @return [Image] the loaded image
374
+ def self.new_from_memory_copy data, width, height, bands, format
375
+ format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
376
+ vi = Vips.vips_image_new_from_memory_copy data, data.bytesize,
377
+ width, height, bands, format_number
378
+ raise Vips::Error if vi.null?
379
+ new(vi)
380
+ end
390
381
 
391
- # Write this image to a file. Save options may be encoded in the
392
- # filename or given as a hash. For example:
393
- #
394
- # ```
395
- # image.write_to_file "fred.jpg[Q=90]"
396
- # ```
397
- #
398
- # or equivalently:
399
- #
400
- # ```
401
- # image.write_to_file "fred.jpg", Q: 90
402
- # ```
403
- #
404
- # The full set of save options depend on the selected saver. Try
405
- # something like:
406
- #
407
- # ```
408
- # $ vips jpegsave
409
- # ```
410
- #
411
- # to see all the available options for JPEG save.
412
- #
413
- # @!macro [new] vips.saveopts
414
- # @param opts [Hash] set of options
415
- # @option opts [Boolean] :strip (false) Strip all metadata from image
416
- # @option opts [Array<Float>] :background (0) Background colour to
417
- # flatten alpha against, if necessary
418
- #
419
- # @param name [String] filename to write to
420
- def write_to_file name, opts = {}
421
- filename = Vips::p2str(Vips::vips_filename_get_filename name)
422
- option_string = Vips::p2str(Vips::vips_filename_get_options name)
423
- saver = Vips::vips_foreign_find_save filename
424
- if saver == nil
425
- raise Vips::Error, "No known saver for '#{filename}'."
426
- end
427
-
428
- Vips::Operation.call saver, [self, filename], opts, option_string
429
-
430
- write_gc
431
- end
382
+ # Create a new {Image} from a source. Load options may be passed as
383
+ # strings or appended as a hash. For example:
384
+ #
385
+ # ```
386
+ # source = Vips::Source.new_from_file("k2.jpg")
387
+ # image = Vips::Image.new_from_source source, "shrink=2"
388
+ # ```
389
+ #
390
+ # or alternatively:
391
+ #
392
+ # ```
393
+ # image = Vips::Image.new_from_source source, "", shrink: 2
394
+ # ```
395
+ #
396
+ # The options available depend on the file format. Try something like:
397
+ #
398
+ # ```
399
+ # $ vips jpegload_source
400
+ # ```
401
+ #
402
+ # at the command-line to see the available options. Not all loaders
403
+ # support load from source, but at least JPEG, PNG and
404
+ # TIFF images will work.
405
+ #
406
+ # Loading is fast: only enough data is read to be able to fill
407
+ # out the header. Pixels will only be read and decompressed when they are
408
+ # needed.
409
+ #
410
+ # @param source [Vips::Source] the source to load from
411
+ # @param option_string [String] load options as a string
412
+ # @macro vips.loadopts
413
+ # @return [Image] the loaded image
414
+ def self.new_from_source source, option_string, **opts
415
+ loader = Vips.vips_foreign_find_load_source source
416
+ raise Vips::Error if loader.nil?
417
+
418
+ Vips::Operation.call loader, [source], opts, option_string
419
+ end
432
420
 
433
- # Write this image to a memory buffer. Save options may be encoded in
434
- # the format_string or given as a hash. For example:
435
- #
436
- # ```
437
- # buffer = image.write_to_buffer ".jpg[Q=90]"
438
- # ```
439
- #
440
- # or equivalently:
441
- #
442
- # ```
443
- # image.write_to_buffer ".jpg", Q: 90
444
- # ```
445
- #
446
- # The full set of save options depend on the selected saver. Try
447
- # something like:
448
- #
449
- # ```
450
- # $ vips jpegsave
451
- # ```
452
- #
453
- # to see all the available options for JPEG save.
454
- #
455
- # @param format_string [String] save format plus options
456
- # @macro vips.saveopts
457
- # @return [String] the image saved in the specified format
458
- def write_to_buffer format_string, opts = {}
459
- filename =
460
- Vips::p2str(Vips::vips_filename_get_filename format_string)
461
- option_string =
462
- Vips::p2str(Vips::vips_filename_get_options format_string)
463
- saver = Vips::vips_foreign_find_save_buffer filename
464
- if saver == nil
465
- raise Vips::Error, "No known saver for '#{filename}'."
466
- end
467
-
468
- buffer = Vips::Operation.call saver, [self], opts, option_string
469
- raise Vips::Error if buffer == nil
470
-
471
- write_gc
472
-
473
- return buffer
474
- end
421
+ def self.matrix_from_array width, height, array
422
+ ptr = FFI::MemoryPointer.new :double, array.length
423
+ ptr.write_array_of_double array
424
+ image = Vips.vips_image_new_matrix_from_array width, height,
425
+ ptr, array.length
426
+ Vips::Image.new image
427
+ end
475
428
 
476
- # Write this image to a large memory buffer.
477
- #
478
- # @return [String] the pixels as a huge binary string
479
- def write_to_memory
480
- len = Vips::SizeStruct.new
481
- ptr = Vips::vips_image_write_to_memory self, len
429
+ # Create a new Image from a 1D or 2D array. A 1D array becomes an
430
+ # image with height 1. Use `scale` and `offset` to set the scale and
431
+ # offset fields in the header. These are useful for integer
432
+ # convolutions.
433
+ #
434
+ # For example:
435
+ #
436
+ # ```
437
+ # image = Vips::Image.new_from_array [1, 2, 3]
438
+ # ```
439
+ #
440
+ # or
441
+ #
442
+ # ```
443
+ # image = Vips::Image.new_from_array [
444
+ # [-1, -1, -1],
445
+ # [-1, 16, -1],
446
+ # [-1, -1, -1]], 8
447
+ # ```
448
+ #
449
+ # for a simple sharpening mask.
450
+ #
451
+ # @param array [Array] the pixel data as an array of numbers
452
+ # @param scale [Real] the convolution scale
453
+ # @param offset [Real] the convolution offset
454
+ # @return [Image] the image
455
+ def self.new_from_array array, scale = 1, offset = 0
456
+ # we accept a 1D array and assume height == 1, or a 2D array
457
+ # and check all lines are the same length
458
+ unless array.is_a? Array
459
+ raise Vips::Error, "Argument is not an array."
460
+ end
461
+
462
+ if array[0].is_a? Array
463
+ height = array.length
464
+ width = array[0].length
465
+ unless array.all? { |x| x.is_a? Array }
466
+ raise Vips::Error, "Not a 2D array."
467
+ end
468
+ unless array.all? { |x| x.length == width }
469
+ raise Vips::Error, "Array not rectangular."
470
+ end
471
+
472
+ array = array.flatten
473
+ else
474
+ height = 1
475
+ width = array.length
476
+ end
477
+
478
+ unless array.length == width * height
479
+ raise Vips::Error, "Bad array dimensions."
480
+ end
481
+
482
+ unless array.all? { |x| x.is_a? Numeric }
483
+ raise Vips::Error, "Not all array elements are Numeric."
484
+ end
485
+
486
+ image = Vips::Image.matrix_from_array width, height, array
487
+ raise Vips::Error if image.nil?
488
+
489
+ image.mutate do |mutable|
490
+ # be careful to set them as double
491
+ mutable.set_type! GObject::GDOUBLE_TYPE, "scale", scale.to_f
492
+ mutable.set_type! GObject::GDOUBLE_TYPE, "offset", offset.to_f
493
+ end
494
+ end
482
495
 
483
- # wrap up as an autopointer
484
- ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
496
+ # A new image is created with the same width, height, format,
497
+ # interpretation, resolution and offset as self, but with every pixel
498
+ # set to the specified value.
499
+ #
500
+ # You can pass an array to make a many-band image, or a single value to
501
+ # make a one-band image.
502
+ #
503
+ # @param value [Real, Array<Real>] value to put in each pixel
504
+ # @return [Image] constant image
505
+ def new_from_image value
506
+ pixel = (Vips::Image.black(1, 1) + value).cast(format)
507
+ image = pixel.embed 0, 0, width, height, extend: :copy
508
+ image.copy interpretation: interpretation, xres: xres, yres: yres,
509
+ xoffset: xoffset, yoffset: yoffset
510
+ end
485
511
 
486
- ptr.get_bytes 0, len[:value]
487
- end
512
+ # Write this image to a file. Save options may be encoded in the
513
+ # filename or given as a hash. For example:
514
+ #
515
+ # ```
516
+ # image.write_to_file "fred.jpg[Q=90]"
517
+ # ```
518
+ #
519
+ # or equivalently:
520
+ #
521
+ # ```
522
+ # image.write_to_file "fred.jpg", Q: 90
523
+ # ```
524
+ #
525
+ # The full set of save options depend on the selected saver. Try
526
+ # something like:
527
+ #
528
+ # ```
529
+ # $ vips jpegsave
530
+ # ```
531
+ #
532
+ # to see all the available options for JPEG save.
533
+ #
534
+ # @!macro [new] vips.saveopts
535
+ # @param opts [Hash] set of options
536
+ # @option opts [Boolean] :strip (false) Strip all metadata from image
537
+ # @option opts [Array<Float>] :background (0) Background colour to
538
+ # flatten alpha against, if necessary
539
+ #
540
+ # @param name [String] filename to write to
541
+ def write_to_file name, **opts
542
+ raise Vips::Error, "filename is nil" if name.nil?
488
543
 
489
- # Fetch a `GType` from an image. `GType` will be 0 for no such field.
490
- #
491
- # @see get
492
- # @param name [String] Metadata field to fetch
493
- # @return [Integer] GType
494
- def get_typeof name
495
- # on libvips before 8.5, property types must be searched first,
496
- # since vips_image_get_typeof returned built-in enums as int
497
- unless Vips::at_least_libvips?(8, 5)
498
- gtype = parent_get_typeof name
499
- return gtype if gtype != 0
500
- end
501
-
502
- Vips::vips_image_get_typeof self, name
503
- end
544
+ filename = Vips.p2str(Vips.vips_filename_get_filename(name))
545
+ option_string = Vips.p2str(Vips.vips_filename_get_options(name))
546
+ saver = Vips.vips_foreign_find_save filename
547
+ raise Vips::Error if saver.nil?
504
548
 
505
- # Get a metadata item from an image. Ruby types are constructed
506
- # automatically from the `GValue`, if possible.
507
- #
508
- # For example, you can read the ICC profile from an image like this:
509
- #
510
- # ```
511
- # profile = image.get "icc-profile-data"
512
- # ```
513
- #
514
- # and profile will be an array containing the profile.
515
- #
516
- # @param name [String] Metadata field to get
517
- # @return [Object] Value of field
518
- def get name
519
- # with old libvips, we must fetch properties (as opposed to
520
- # metadata) via VipsObject
521
- unless Vips::at_least_libvips?(8, 5)
522
- return super if parent_get_typeof(name) != 0
523
- end
524
-
525
- gvalue = GObject::GValue.alloc
526
- result = Vips::vips_image_get self, name, gvalue
527
- raise Vips::Error if result != 0
528
-
529
- return gvalue.get
530
- end
549
+ Vips::Operation.call saver, [self, filename], opts, option_string
531
550
 
532
- # Get the names of all fields on an image. Use this to loop over all
533
- # image metadata.
534
- #
535
- # @return [[String]] array of field names
536
- def get_fields
537
- # vips_image_get_fields() was added in libvips 8.5
538
- return [] unless Vips.respond_to? :vips_image_get_fields
539
-
540
- array = Vips::vips_image_get_fields self
541
-
542
- names = []
543
- p = array
544
- until ((q = p.read_pointer).null?)
545
- names << q.read_string
546
- GLib::g_free q
547
- p += FFI::Type::POINTER.size
548
- end
549
- GLib::g_free array
550
-
551
- return names
552
- end
551
+ write_gc
552
+ end
553
553
 
554
- # Create a metadata item on an image, of the specifed type. Ruby types
555
- # are automatically
556
- # transformed into the matching `GType`, if possible.
557
- #
558
- # For example, you can use this to set an image's ICC profile:
559
- #
560
- # ```
561
- # x = y.set_type Vips::BLOB_TYPE, "icc-profile-data", profile
562
- # ```
563
- #
564
- # where `profile` is an ICC profile held as a binary string object.
565
- #
566
- # @see set
567
- # @param gtype [Integer] GType of item
568
- # @param name [String] Metadata field to set
569
- # @param value [Object] Value to set
570
- def set_type gtype, name, value
571
- gvalue = GObject::GValue.alloc
572
- gvalue.init gtype
573
- gvalue.set value
574
- Vips::vips_image_set self, name, gvalue
575
- end
554
+ # Write this image to a memory buffer. Save options may be encoded in
555
+ # the format_string or given as a hash. For example:
556
+ #
557
+ # ```
558
+ # buffer = image.write_to_buffer ".jpg[Q=90]"
559
+ # ```
560
+ #
561
+ # or equivalently:
562
+ #
563
+ # ```
564
+ # image.write_to_buffer ".jpg", Q: 90
565
+ # ```
566
+ #
567
+ # The full set of save options depend on the selected saver. Try
568
+ # something like:
569
+ #
570
+ # ```
571
+ # $ vips jpegsave
572
+ # ```
573
+ #
574
+ # to see all the available options for JPEG save.
575
+ #
576
+ # @param format_string [String] save format plus options
577
+ # @macro vips.saveopts
578
+ # @return [String] the image saved in the specified format
579
+ def write_to_buffer format_string, **opts
580
+ raise Vips::Error, "filename is nil" if format_string.nil?
581
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
582
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
583
+ saver = Vips.vips_foreign_find_save_buffer filename
584
+ raise Vips::Error if saver.nil?
585
+
586
+ buffer = Vips::Operation.call saver, [self], opts, option_string
587
+ raise Vips::Error if buffer.nil?
588
+
589
+ write_gc
590
+
591
+ buffer
592
+ end
576
593
 
577
- # Set the value of a metadata item on an image. The metadata item must
578
- # already exist. Ruby types are automatically
579
- # transformed into the matching `GValue`, if possible.
580
- #
581
- # For example, you can use this to set an image's ICC profile:
582
- #
583
- # ```
584
- # x = y.set "icc-profile-data", profile
585
- # ```
586
- #
587
- # where `profile` is an ICC profile held as a binary string object.
588
- #
589
- # @see set_type
590
- # @param name [String] Metadata field to set
591
- # @param value [Object] Value to set
592
- def set name, value
593
- set_type get_typeof(name), name, value
594
- end
594
+ # Write this image to a target. Save options may be encoded in
595
+ # the format_string or given as a hash. For example:
596
+ #
597
+ # ```ruby
598
+ # target = Vips::Target.new_to_file "k2.jpg"
599
+ # image.write_to_target target, ".jpg[Q=90]"
600
+ # ```
601
+ #
602
+ # or equivalently:
603
+ #
604
+ # ```ruby
605
+ # image.write_to_target target, ".jpg", Q: 90
606
+ # ```
607
+ #
608
+ # The full set of save options depend on the selected saver. Try
609
+ # something like:
610
+ #
611
+ # ```
612
+ # $ vips jpegsave_target
613
+ # ```
614
+ #
615
+ # to see all the available options for JPEG save.
616
+ #
617
+ # @param target [Vips::Target] the target to write to
618
+ # @param format_string [String] save format plus string options
619
+ # @macro vips.saveopts
620
+ def write_to_target target, format_string, **opts
621
+ raise Vips::Error, "filename is nil" if format_string.nil?
622
+ filename = Vips.p2str(Vips.vips_filename_get_filename(format_string))
623
+ option_string = Vips.p2str(Vips.vips_filename_get_options(format_string))
624
+ saver = Vips.vips_foreign_find_save_target filename
625
+ raise Vips::Error if saver.nil?
626
+
627
+ Vips::Operation.call saver, [self, target], opts, option_string
628
+ write_gc
629
+ end
595
630
 
596
- # Remove a metadata item from an image.
597
- #
598
- # @param name [String] Metadata field to remove
599
- def remove name
600
- Vips::vips_image_remove self, name
601
- end
631
+ # Write this image to a large memory buffer.
632
+ #
633
+ # @return [String] the pixels as a huge binary string
634
+ def write_to_memory
635
+ len = Vips::SizeStruct.new
636
+ ptr = Vips.vips_image_write_to_memory self, len
637
+ raise Vips::Error if ptr.nil?
602
638
 
603
- # compatibility: old name for get
604
- def get_value name
605
- get name
606
- end
639
+ # wrap up as an autopointer
640
+ ptr = FFI::AutoPointer.new(ptr, GLib::G_FREE)
607
641
 
608
- # compatibility: old name for set
609
- def set_value name, value
610
- set name, value
611
- end
642
+ ptr.get_bytes 0, len[:value]
643
+ end
612
644
 
613
- # Get image width, in pixels.
614
- #
615
- # @return [Integer] image width, in pixels
616
- def width
617
- get "width"
618
- end
645
+ # Turn progress signalling on and off.
646
+ #
647
+ # If this is on, the most-downstream image from this image will issue
648
+ # progress signals.
649
+ #
650
+ # @see Object#signal_connect
651
+ # @param state [Boolean] progress signalling state
652
+ def set_progress state
653
+ Vips.vips_image_set_progress self, state
654
+ end
619
655
 
620
- # Get image height, in pixels.
621
- #
622
- # @return [Integer] image height, in pixels
623
- def height
624
- get "height"
625
- end
656
+ # Kill computation of this time.
657
+ #
658
+ # Set true to stop computation of this image. You can call this from a
659
+ # progress handler, for example.
660
+ #
661
+ # @see Object#signal_connect
662
+ # @param kill [Boolean] stop computation
663
+ def set_kill kill
664
+ Vips.vips_image_set_kill self, kill
665
+ end
626
666
 
627
- # Get number of image bands.
628
- #
629
- # @return [Integer] number of image bands
630
- def bands
631
- get "bands"
632
- end
667
+ # Get the `GType` of a metadata field. The result is 0 if no such field
668
+ # exists.
669
+ #
670
+ # @see get
671
+ # @param name [String] Metadata field to fetch
672
+ # @return [Integer] GType
673
+ def get_typeof name
674
+ # on libvips before 8.5, property types must be searched first,
675
+ # since vips_image_get_typeof returned built-in enums as int
676
+ unless Vips.at_least_libvips?(8, 5)
677
+ gtype = parent_get_typeof name
678
+ return gtype if gtype != 0
679
+ end
680
+
681
+ Vips.vips_image_get_typeof self, name
682
+ end
633
683
 
634
- # Get image format.
635
- #
636
- # @return [Symbol] image format
637
- def format
638
- get "format"
639
- end
684
+ # Get a metadata item from an image. Ruby types are constructed
685
+ # automatically from the `GValue`, if possible.
686
+ #
687
+ # For example, you can read the ICC profile from an image like this:
688
+ #
689
+ # ```
690
+ # profile = image.get "icc-profile-data"
691
+ # ```
692
+ #
693
+ # and profile will be an array containing the profile.
694
+ #
695
+ # @param name [String] Metadata field to get
696
+ # @return [Object] Value of field
697
+ def get name
698
+ # with old libvips, we must fetch properties (as opposed to
699
+ # metadata) via VipsObject
700
+ unless Vips.at_least_libvips?(8, 5)
701
+ return super if parent_get_typeof(name) != 0
702
+ end
703
+
704
+ gvalue = GObject::GValue.alloc
705
+ raise Vips::Error if Vips.vips_image_get(self, name, gvalue) != 0
706
+ result = gvalue.get
707
+ gvalue.unset
708
+
709
+ result
710
+ end
640
711
 
641
- # Get image interpretation.
642
- #
643
- # @return [Symbol] image interpretation
644
- def interpretation
645
- get "interpretation"
646
- end
712
+ # Get the names of all fields on an image. Use this to loop over all
713
+ # image metadata.
714
+ #
715
+ # @return [[String]] array of field names
716
+ def get_fields
717
+ # vips_image_get_fields() was added in libvips 8.5
718
+ return [] unless Vips.respond_to? :vips_image_get_fields
719
+
720
+ array = Vips.vips_image_get_fields self
721
+
722
+ names = []
723
+ p = array
724
+ until (q = p.read_pointer).null?
725
+ names << q.read_string
726
+ GLib.g_free q
727
+ p += FFI::Type::POINTER.size
728
+ end
729
+ GLib.g_free array
730
+
731
+ names
732
+ end
647
733
 
648
- # Get image coding.
649
- #
650
- # @return [Symbol] image coding
651
- def coding
652
- get "coding"
653
- end
734
+ # Mutate an image with a block. Inside the block, you can call methods
735
+ # which modify the image, such as setting or removing metadata, or
736
+ # modifying pixels.
737
+ #
738
+ # For example:
739
+ #
740
+ # ```ruby
741
+ # image = image.mutate do |x|
742
+ # (0 ... 1).step(0.01) do |i|
743
+ # x.draw_line! 255, x.width * i, 0, 0, x.height * (1 - i)
744
+ # end
745
+ # end
746
+ # ```
747
+ #
748
+ # See {MutableImage}.
749
+ def mutate
750
+ mutable = Vips::MutableImage.new self
751
+ yield mutable
752
+ mutable.image
753
+ end
654
754
 
655
- # Get image filename, if any.
656
- #
657
- # @return [String] image filename
658
- def filename
659
- get "filename"
660
- end
755
+ # This method is deprecated.
756
+ #
757
+ # Please use {MutableImage#set_type!} instead.
758
+ def set_type gtype, name, value
759
+ gvalue = GObject::GValue.alloc
760
+ gvalue.init gtype
761
+ gvalue.set value
762
+ Vips.vips_image_set self, name, gvalue
763
+ gvalue.unset
764
+ end
661
765
 
662
- # Get image xoffset.
663
- #
664
- # @return [Integer] image xoffset
665
- def xoffset
666
- get "xoffset"
667
- end
766
+ # This method is deprecated.
767
+ #
768
+ # Please use {MutableImage#set!} instead.
769
+ def set name, value
770
+ set_type get_typeof(name), name, value
771
+ end
668
772
 
669
- # Get image yoffset.
670
- #
671
- # @return [Integer] image yoffset
672
- def yoffset
673
- get "yoffset"
674
- end
773
+ # This method is deprecated.
774
+ #
775
+ # Please use {MutableImage#remove!} instead.
776
+ def remove name
777
+ Vips.vips_image_remove self, name
778
+ end
675
779
 
676
- # Get image x resolution.
677
- #
678
- # @return [Float] image x resolution
679
- def xres
680
- get "xres"
681
- end
780
+ # compatibility: old name for get
781
+ def get_value name
782
+ get name
783
+ end
682
784
 
683
- # Get image y resolution.
684
- #
685
- # @return [Float] image y resolution
686
- def yres
687
- get "yres"
688
- end
785
+ # This method is deprecated.
786
+ #
787
+ # Please use {MutableImage#set!} instead.
788
+ def set_value name, value
789
+ set name, value
790
+ end
689
791
 
690
- # Get scale metadata.
691
- #
692
- # @return [Float] image scale
693
- def scale
694
- return 1 if get_typeof("scale") == 0
792
+ # Get image width, in pixels.
793
+ #
794
+ # @return [Integer] image width, in pixels
795
+ def width
796
+ Vips.vips_image_get_width self
797
+ end
695
798
 
696
- get "scale"
697
- end
799
+ # Get image height, in pixels.
800
+ #
801
+ # @return [Integer] image height, in pixels
802
+ def height
803
+ Vips.vips_image_get_height self
804
+ end
698
805
 
699
- # Get offset metadata.
700
- #
701
- # @return [Float] image offset
702
- def offset
703
- return 0 if get_typeof("offset") == 0
806
+ # Get number of image bands.
807
+ #
808
+ # @return [Integer] number of image bands
809
+ def bands
810
+ Vips.vips_image_get_bands self
811
+ end
704
812
 
705
- get "offset"
706
- end
813
+ # Get image format.
814
+ #
815
+ # @return [Symbol] image format
816
+ def format
817
+ get "format"
818
+ end
707
819
 
708
- # Get the image size.
709
- #
710
- # @return [Integer, Integer] image width and height
711
- def size
712
- [width, height]
713
- end
820
+ # Get image interpretation.
821
+ #
822
+ # @return [Symbol] image interpretation
823
+ def interpretation
824
+ get "interpretation"
825
+ end
714
826
 
715
- if Vips::at_least_libvips?(8, 5)
716
- # Detect if image has an alpha channel
717
- #
718
- # @return [Boolean] true if image has an alpha channel.
719
- def has_alpha?
720
- return Vips::vips_image_hasalpha(self) != 0
721
- end
722
- end
827
+ # Get image coding.
828
+ #
829
+ # @return [Symbol] image coding
830
+ def coding
831
+ get "coding"
832
+ end
723
833
 
724
- # vips_addalpha was added in libvips 8.6
725
- if Vips::at_least_libvips?(8, 6)
726
- # Append an alpha channel to an image.
727
- #
728
- # @return [Image] new image
729
- def add_alpha
730
- ptr = GenericPtr.new
731
- result = Vips::vips_addalpha self, ptr
732
- raise Vips::Error if result != 0
733
-
734
- Vips::Image.new ptr[:value]
735
- end
736
- end
834
+ # Get image filename, if any.
835
+ #
836
+ # @return [String] image filename
837
+ def filename
838
+ get "filename"
839
+ end
737
840
 
738
- # Copy an image to a memory area.
739
- #
740
- # This can be useful for reusing results, but can obviously use a lot of
741
- # memory for large images. See {Image#tilecache} for a way of caching
742
- # parts of an image.
743
- #
744
- # @return [Image] new memory image
745
- def copy_memory
746
- new_image = Vips::vips_image_copy_memory self
747
- Vips::Image.new new_image
748
- end
841
+ # Get image xoffset.
842
+ #
843
+ # @return [Integer] image xoffset
844
+ def xoffset
845
+ get "xoffset"
846
+ end
749
847
 
750
- # Draw a point on an image.
751
- #
752
- # See {Image#draw_rect}.
753
- #
754
- # @return [Image] modified image
755
- def draw_point ink, left, top, opts = {}
756
- draw_rect ink, left, top, 1, 1, opts
757
- end
848
+ # Get image yoffset.
849
+ #
850
+ # @return [Integer] image yoffset
851
+ def yoffset
852
+ get "yoffset"
853
+ end
758
854
 
759
- # Add an image, constant or array.
760
- #
761
- # @param other [Image, Real, Array<Real>] Thing to add to self
762
- # @return [Image] result of addition
763
- def + other
764
- other.is_a?(Vips::Image) ?
765
- add(other) : linear(1, other)
766
- end
855
+ # Get image x resolution.
856
+ #
857
+ # @return [Float] image x resolution
858
+ def xres
859
+ get "xres"
860
+ end
767
861
 
768
- # Subtract an image, constant or array.
769
- #
770
- # @param other [Image, Real, Array<Real>] Thing to subtract from self
771
- # @return [Image] result of subtraction
772
- def - other
773
- other.is_a?(Vips::Image) ?
774
- subtract(other) : linear(1, Image::smap(other) {|x| x * -1})
775
- end
862
+ # Get image y resolution.
863
+ #
864
+ # @return [Float] image y resolution
865
+ def yres
866
+ get "yres"
867
+ end
776
868
 
777
- # Multiply an image, constant or array.
778
- #
779
- # @param other [Image, Real, Array<Real>] Thing to multiply by self
780
- # @return [Image] result of multiplication
781
- def * other
782
- other.is_a?(Vips::Image) ?
783
- multiply(other) : linear(other, 0)
784
- end
869
+ # Get scale metadata.
870
+ #
871
+ # @return [Float] image scale
872
+ def scale
873
+ return 1 if get_typeof("scale") == 0
785
874
 
786
- # Divide an image, constant or array.
787
- #
788
- # @param other [Image, Real, Array<Real>] Thing to divide self by
789
- # @return [Image] result of division
790
- def / other
791
- other.is_a?(Vips::Image) ?
792
- divide(other) : linear(Image::smap(other) {|x| 1.0 / x}, 0)
793
- end
875
+ get "scale"
876
+ end
794
877
 
795
- # Remainder after integer division with an image, constant or array.
796
- #
797
- # @param other [Image, Real, Array<Real>] self modulo this
798
- # @return [Image] result of modulo
799
- def % other
800
- other.is_a?(Vips::Image) ?
801
- remainder(other) : remainder_const(other)
802
- end
878
+ # Get offset metadata.
879
+ #
880
+ # @return [Float] image offset
881
+ def offset
882
+ return 0 if get_typeof("offset") == 0
803
883
 
804
- # Raise to power of an image, constant or array.
805
- #
806
- # @param other [Image, Real, Array<Real>] self to the power of this
807
- # @return [Image] result of power
808
- def ** other
809
- call_enum "math2", other, :pow
810
- end
884
+ get "offset"
885
+ end
811
886
 
812
- # Integer left shift with an image, constant or array.
813
- #
814
- # @param other [Image, Real, Array<Real>] shift left by this much
815
- # @return [Image] result of left shift
816
- def << other
817
- call_enum "boolean", other, :lshift
818
- end
887
+ # Get the image size.
888
+ #
889
+ # @return [Integer, Integer] image width and height
890
+ def size
891
+ [width, height]
892
+ end
819
893
 
820
- # Integer right shift with an image, constant or array.
821
- #
822
- # @param other [Image, Real, Array<Real>] shift right by this much
823
- # @return [Image] result of right shift
824
- def >> other
825
- call_enum "boolean", other, :rshift
826
- end
894
+ if Vips.at_least_libvips?(8, 5)
895
+ # Detect if image has an alpha channel
896
+ #
897
+ # @return [Boolean] true if image has an alpha channel.
898
+ def has_alpha?
899
+ Vips.vips_image_hasalpha(self) != 0
900
+ end
901
+ end
827
902
 
828
- # Integer bitwise OR with an image, constant or array.
829
- #
830
- # @param other [Image, Real, Array<Real>] bitwise OR with this
831
- # @return [Image] result of bitwise OR
832
- def | other
833
- call_enum "boolean", other, :or
834
- end
903
+ # vips_addalpha was added in libvips 8.6
904
+ if Vips.at_least_libvips?(8, 6)
905
+ # Append an alpha channel to an image.
906
+ #
907
+ # @return [Image] new image
908
+ def add_alpha
909
+ ptr = GenericPtr.new
910
+ result = Vips.vips_addalpha self, ptr
911
+ raise Vips::Error if result != 0
912
+
913
+ Vips::Image.new ptr[:value]
914
+ end
915
+ end
835
916
 
836
- # Integer bitwise AND with an image, constant or array.
837
- #
838
- # @param other [Image, Real, Array<Real>] bitwise AND with this
839
- # @return [Image] result of bitwise AND
840
- def & other
841
- call_enum "boolean", other, :and
842
- end
917
+ # Copy an image to a memory area.
918
+ #
919
+ # This can be useful for reusing results, but can obviously use a lot of
920
+ # memory for large images. See {Image#tilecache} for a way of caching
921
+ # parts of an image.
922
+ #
923
+ # @return [Image] new memory image
924
+ def copy_memory
925
+ new_image = Vips.vips_image_copy_memory self
926
+ Vips::Image.new new_image
927
+ end
843
928
 
844
- # Integer bitwise EOR with an image, constant or array.
845
- #
846
- # @param other [Image, Real, Array<Real>] bitwise EOR with this
847
- # @return [Image] result of bitwise EOR
848
- def ^ other
849
- call_enum "boolean", other, :eor
850
- end
929
+ # Draw a point on an image.
930
+ #
931
+ # See {Image#draw_rect}.
932
+ #
933
+ # @return [Image] modified image
934
+ def draw_point ink, left, top, **opts
935
+ draw_rect ink, left, top, 1, 1, **opts
936
+ end
851
937
 
852
- # Equivalent to image ^ -1
853
- #
854
- # @return [Image] image with bits flipped
855
- def !
856
- self ^ -1
857
- end
938
+ # Add an image, constant or array.
939
+ #
940
+ # @param other [Image, Real, Array<Real>] Thing to add to self
941
+ # @return [Image] result of addition
942
+ def + other
943
+ other.is_a?(Vips::Image) ?
944
+ add(other) : linear(1, other)
945
+ end
858
946
 
859
- # Equivalent to image ^ -1
860
- #
861
- # @return [Image] image with bits flipped
862
- def ~
863
- self ^ -1
864
- end
947
+ # Subtract an image, constant or array.
948
+ #
949
+ # @param other [Image, Real, Array<Real>] Thing to subtract from self
950
+ # @return [Image] result of subtraction
951
+ def - other
952
+ other.is_a?(Vips::Image) ?
953
+ subtract(other) : linear(1, Image.smap(other) { |x| x * -1 })
954
+ end
865
955
 
866
- # @return [Image] image
867
- def +@
868
- self
869
- end
956
+ # Multiply an image, constant or array.
957
+ #
958
+ # @param other [Image, Real, Array<Real>] Thing to multiply by self
959
+ # @return [Image] result of multiplication
960
+ def * other
961
+ other.is_a?(Vips::Image) ?
962
+ multiply(other) : linear(other, 0)
963
+ end
870
964
 
871
- # Equivalent to image * -1
872
- #
873
- # @return [Image] negative of image
874
- def -@
875
- self * -1
876
- end
965
+ # Divide an image, constant or array.
966
+ #
967
+ # @param other [Image, Real, Array<Real>] Thing to divide self by
968
+ # @return [Image] result of division
969
+ def / other
970
+ other.is_a?(Vips::Image) ?
971
+ divide(other) : linear(Image.smap(other) { |x| 1.0 / x }, 0)
972
+ end
877
973
 
878
- # Relational less than with an image, constant or array.
879
- #
880
- # @param other [Image, Real, Array<Real>] relational less than with this
881
- # @return [Image] result of less than
882
- def < other
883
- call_enum "relational", other, :less
884
- end
974
+ # Remainder after integer division with an image, constant or array.
975
+ #
976
+ # @param other [Image, Real, Array<Real>] self modulo this
977
+ # @return [Image] result of modulo
978
+ def % other
979
+ other.is_a?(Vips::Image) ?
980
+ remainder(other) : remainder_const(other)
981
+ end
885
982
 
886
- # Relational less than or equal to with an image, constant or array.
887
- #
888
- # @param other [Image, Real, Array<Real>] relational less than or
889
- # equal to with this
890
- # @return [Image] result of less than or equal to
891
- def <= other
892
- call_enum "relational", other, :lesseq
893
- end
983
+ # Raise to power of an image, constant or array.
984
+ #
985
+ # @param other [Image, Real, Array<Real>] self to the power of this
986
+ # @return [Image] result of power
987
+ def ** other
988
+ call_enum "math2", other, :pow
989
+ end
894
990
 
895
- # Relational more than with an image, constant or array.
896
- #
897
- # @param other [Image, Real, Array<Real>] relational more than with this
898
- # @return [Image] result of more than
899
- def > other
900
- call_enum "relational", other, :more
901
- end
991
+ # Integer left shift with an image, constant or array.
992
+ #
993
+ # @param other [Image, Real, Array<Real>] shift left by this much
994
+ # @return [Image] result of left shift
995
+ def << other
996
+ call_enum "boolean", other, :lshift
997
+ end
902
998
 
903
- # Relational more than or equal to with an image, constant or array.
904
- #
905
- # @param other [Image, Real, Array<Real>] relational more than or
906
- # equal to with this
907
- # @return [Image] result of more than or equal to
908
- def >= other
909
- call_enum "relational", other, :moreeq
910
- end
999
+ # Integer right shift with an image, constant or array.
1000
+ #
1001
+ # @param other [Image, Real, Array<Real>] shift right by this much
1002
+ # @return [Image] result of right shift
1003
+ def >> other
1004
+ call_enum "boolean", other, :rshift
1005
+ end
911
1006
 
912
- # Compare equality to nil, an image, constant or array.
913
- #
914
- # @param other [nil, Image, Real, Array<Real>] test equality to this
915
- # @return [Image] result of equality
916
- def == other
917
- # for equality, we must allow tests against nil
918
- if other == nil
919
- false
920
- else
921
- call_enum "relational", other, :equal
922
- end
923
- end
1007
+ # Integer bitwise OR with an image, constant or array.
1008
+ #
1009
+ # @param other [Image, Real, Array<Real>] bitwise OR with this
1010
+ # @return [Image] result of bitwise OR
1011
+ def | other
1012
+ call_enum "boolean", other, :or
1013
+ end
924
1014
 
925
- # Compare inequality to nil, an image, constant or array.
926
- #
927
- # @param other [nil, Image, Real, Array<Real>] test inequality to this
928
- # @return [Image] result of inequality
929
- def != other
930
- # for equality, we must allow tests against nil
931
- if other == nil
932
- true
933
- else
934
- call_enum "relational", other, :noteq
935
- end
936
- end
1015
+ # Integer bitwise AND with an image, constant or array.
1016
+ #
1017
+ # @param other [Image, Real, Array<Real>] bitwise AND with this
1018
+ # @return [Image] result of bitwise AND
1019
+ def & other
1020
+ call_enum "boolean", other, :and
1021
+ end
937
1022
 
938
- # Fetch bands using a number or a range
939
- #
940
- # @param index [Numeric, Range] extract these band(s)
941
- # @return [Image] extracted band(s)
942
- def [] index
943
- if index.is_a? Range
944
- n = index.size
945
- extract_band index.begin, n: n
946
- elsif index.is_a? Numeric
947
- extract_band index
948
- else
949
- raise Vips::Error, "[] index is not range or numeric."
950
- end
951
- end
1023
+ # Integer bitwise EOR with an image, constant or array.
1024
+ #
1025
+ # @param other [Image, Real, Array<Real>] bitwise EOR with this
1026
+ # @return [Image] result of bitwise EOR
1027
+ def ^ other
1028
+ call_enum "boolean", other, :eor
1029
+ end
952
1030
 
953
- # Convert to an Array. This will be slow for large images.
954
- #
955
- # @return [Array] array of Fixnum
956
- def to_a
957
- # we render the image to a big string, then unpack
958
- # as a Ruby array of the correct type
959
- memory = write_to_memory
960
-
961
- # make the template for unpack
962
- template = {
963
- :char => 'c',
964
- :uchar => 'C',
965
- :short => 's_',
966
- :ushort => 'S_',
967
- :int => 'i_',
968
- :uint => 'I_',
969
- :float => 'f',
970
- :double => 'd',
971
- :complex => 'f',
972
- :dpcomplex => 'd'
973
- }[format] + '*'
974
-
975
- # and unpack into something like [1, 2, 3, 4 ..]
976
- array = memory.unpack(template)
977
-
978
- # gather band elements together
979
- pixel_array = array.each_slice(bands).to_a
980
-
981
- # build rows
982
- row_array = pixel_array.each_slice(width).to_a
983
-
984
- return row_array
985
- end
1031
+ # Equivalent to image ^ -1
1032
+ #
1033
+ # @return [Image] image with bits flipped
1034
+ def !
1035
+ self ^ -1
1036
+ end
986
1037
 
987
- # Return the largest integral value not greater than the argument.
988
- #
989
- # @return [Image] floor of image
990
- def floor
991
- round :floor
992
- end
1038
+ # Equivalent to image ^ -1
1039
+ #
1040
+ # @return [Image] image with bits flipped
1041
+ def ~
1042
+ self ^ -1
1043
+ end
993
1044
 
994
- # Return the smallest integral value not less than the argument.
995
- #
996
- # @return [Image] ceil of image
997
- def ceil
998
- round :ceil
999
- end
1045
+ # @return [Image] image
1046
+ def +@
1047
+ self
1048
+ end
1000
1049
 
1001
- # Return the nearest integral value.
1002
- #
1003
- # @return [Image] rint of image
1004
- def rint
1005
- round :rint
1006
- end
1050
+ # Equivalent to image * -1
1051
+ #
1052
+ # @return [Image] negative of image
1053
+ def -@
1054
+ self * -1
1055
+ end
1007
1056
 
1008
- # AND the bands of an image together
1009
- #
1010
- # @return [Image] all bands ANDed together
1011
- def bandand
1012
- bandbool :and
1013
- end
1057
+ # Relational less than with an image, constant or array.
1058
+ #
1059
+ # @param other [Image, Real, Array<Real>] relational less than with this
1060
+ # @return [Image] result of less than
1061
+ def < other
1062
+ call_enum "relational", other, :less
1063
+ end
1014
1064
 
1015
- # OR the bands of an image together
1016
- #
1017
- # @return [Image] all bands ORed together
1018
- def bandor
1019
- bandbool :or
1020
- end
1065
+ # Relational less than or equal to with an image, constant or array.
1066
+ #
1067
+ # @param other [Image, Real, Array<Real>] relational less than or
1068
+ # equal to with this
1069
+ # @return [Image] result of less than or equal to
1070
+ def <= other
1071
+ call_enum "relational", other, :lesseq
1072
+ end
1021
1073
 
1022
- # EOR the bands of an image together
1023
- #
1024
- # @return [Image] all bands EORed together
1025
- def bandeor
1026
- bandbool :eor
1027
- end
1074
+ # Relational more than with an image, constant or array.
1075
+ #
1076
+ # @param other [Image, Real, Array<Real>] relational more than with this
1077
+ # @return [Image] result of more than
1078
+ def > other
1079
+ call_enum "relational", other, :more
1080
+ end
1028
1081
 
1029
- # Split an n-band image into n separate images.
1030
- #
1031
- # @return [Array<Image>] Array of n one-band images
1032
- def bandsplit
1033
- (0...bands).map {|i| extract_band i}
1034
- end
1082
+ # Relational more than or equal to with an image, constant or array.
1083
+ #
1084
+ # @param other [Image, Real, Array<Real>] relational more than or
1085
+ # equal to with this
1086
+ # @return [Image] result of more than or equal to
1087
+ def >= other
1088
+ call_enum "relational", other, :moreeq
1089
+ end
1035
1090
 
1036
- # Join a set of images bandwise.
1037
- #
1038
- # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1039
- # @return [Image] many band image
1040
- def bandjoin other
1041
- unless other.is_a? Array
1042
- other = [other]
1043
- end
1044
-
1045
- # if other is just Numeric, we can use bandjoin_const
1046
- not_all_real = !other.all?{|x| x.is_a? Numeric}
1047
-
1048
- if not_all_real
1049
- Vips::Image.bandjoin([self] + other)
1050
- else
1051
- bandjoin_const other
1052
- end
1053
- end
1091
+ # Compare equality to nil, an image, constant or array.
1092
+ #
1093
+ # @param other [nil, Image, Real, Array<Real>] test equality to this
1094
+ # @return [Image] result of equality
1095
+ def == other
1096
+ # for equality, we must allow tests against nil
1097
+ if other.nil?
1098
+ false
1099
+ else
1100
+ call_enum "relational", other, :equal
1101
+ end
1102
+ end
1054
1103
 
1055
- # Composite a set of images with a set of blend modes.
1056
- #
1057
- # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1058
- # @return [Image] many band image
1059
- def composite other, mode, opts = {}
1060
- unless other.is_a? Array
1061
- other = [other]
1062
- end
1063
- unless mode.is_a? Array
1064
- mode = [mode]
1065
- end
1066
-
1067
- mode = mode.map do |x|
1068
- GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1069
- end
1070
-
1071
- Vips::Image.composite([self] + other, mode, opts)
1072
- end
1104
+ # Compare inequality to nil, an image, constant or array.
1105
+ #
1106
+ # @param other [nil, Image, Real, Array<Real>] test inequality to this
1107
+ # @return [Image] result of inequality
1108
+ def != other
1109
+ # for equality, we must allow tests against nil
1110
+ if other.nil?
1111
+ true
1112
+ else
1113
+ call_enum "relational", other, :noteq
1114
+ end
1115
+ end
1073
1116
 
1074
- # Return the coordinates of the image maximum.
1075
- #
1076
- # @return [Real, Real, Real] maximum value, x coordinate of maximum, y
1077
- # coordinate of maximum
1078
- def maxpos
1079
- v, opts = max x: true, y: true
1080
- x = opts['x']
1081
- y = opts['y']
1082
- return v, x, y
1083
- end
1117
+ # Fetch bands using a number or a range
1118
+ #
1119
+ # @param index [Numeric, Range] extract these band(s)
1120
+ # @return [Image] extracted band(s)
1121
+ def [] index
1122
+ if index.is_a? Range
1123
+ n = index.size
1124
+ extract_band index.begin, n: n
1125
+ elsif index.is_a? Numeric
1126
+ extract_band index
1127
+ else
1128
+ raise Vips::Error, "[] index is not range or numeric."
1129
+ end
1130
+ end
1084
1131
 
1085
- # Return the coordinates of the image minimum.
1086
- #
1087
- # @return [Real, Real, Real] minimum value, x coordinate of minimum, y
1088
- # coordinate of minimum
1089
- def minpos
1090
- v, opts = min x: true, y: true
1091
- x = opts['x']
1092
- y = opts['y']
1093
- return v, x, y
1094
- end
1132
+ # Convert to an Array. This will be slow for large images.
1133
+ #
1134
+ # @return [Array] array of Fixnum
1135
+ def to_a
1136
+ # we render the image to a big string, then unpack
1137
+ # as a Ruby array of the correct type
1138
+ memory = write_to_memory
1139
+
1140
+ # make the template for unpack
1141
+ template = {
1142
+ char: "c",
1143
+ uchar: "C",
1144
+ short: "s_",
1145
+ ushort: "S_",
1146
+ int: "i_",
1147
+ uint: "I_",
1148
+ float: "f",
1149
+ double: "d",
1150
+ complex: "f",
1151
+ dpcomplex: "d"
1152
+ }[format] + "*"
1153
+
1154
+ # and unpack into something like [1, 2, 3, 4 ..]
1155
+ array = memory.unpack(template)
1156
+
1157
+ # gather band elements together
1158
+ pixel_array = array.each_slice(bands).to_a
1159
+
1160
+ # build rows
1161
+ pixel_array.each_slice(width).to_a
1162
+ end
1095
1163
 
1096
- # get the value of a pixel as an array
1097
- #
1098
- # @param x [Integer] x coordinate to sample
1099
- # @param y [Integer] y coordinate to sample
1100
- # @return [Array<Float>] the pixel values as an array
1101
- def getpoint x, y
1102
- # vips has an operation that does this, but we can't call it via
1103
- # gobject-introspection 3.1 since it's missing array double
1104
- # get
1105
- #
1106
- # remove this def when gobject-introspection updates
1107
- crop(x, y, 1, 1).bandsplit.map(&:avg)
1108
- end
1164
+ # Return the largest integral value not greater than the argument.
1165
+ #
1166
+ # @return [Image] floor of image
1167
+ def floor
1168
+ round :floor
1169
+ end
1109
1170
 
1110
- # a median filter
1111
- #
1112
- # @param size [Integer] size of filter window
1113
- # @return [Image] result of median filter
1114
- def median size = 3
1115
- rank size, size, (size * size) / 2
1116
- end
1171
+ # Return the smallest integral value not less than the argument.
1172
+ #
1173
+ # @return [Image] ceil of image
1174
+ def ceil
1175
+ round :ceil
1176
+ end
1117
1177
 
1118
- # Return the real part of a complex image.
1119
- #
1120
- # @return [Image] real part of complex image
1121
- def real
1122
- complexget :real
1123
- end
1178
+ # Return the nearest integral value.
1179
+ #
1180
+ # @return [Image] rint of image
1181
+ def rint
1182
+ round :rint
1183
+ end
1124
1184
 
1125
- # Return the imaginary part of a complex image.
1126
- #
1127
- # @return [Image] imaginary part of complex image
1128
- def imag
1129
- complexget :imag
1130
- end
1185
+ # AND the bands of an image together
1186
+ #
1187
+ # @return [Image] all bands ANDed together
1188
+ def bandand
1189
+ bandbool :and
1190
+ end
1131
1191
 
1132
- # Return an image with rectangular pixels converted to polar.
1133
- #
1134
- # The image
1135
- # can be complex, in which case the return image will also be complex,
1136
- # or must have an even number of bands, in which case pairs of
1137
- # bands are treated as (x, y) coordinates.
1138
- #
1139
- # @see xyz
1140
- # @return [Image] image converted to polar coordinates
1141
- def polar
1142
- Image::run_cmplx(self) {|x| x.complex :polar}
1143
- end
1192
+ # OR the bands of an image together
1193
+ #
1194
+ # @return [Image] all bands ORed together
1195
+ def bandor
1196
+ bandbool :or
1197
+ end
1144
1198
 
1145
- # Return an image with polar pixels converted to rectangular.
1146
- #
1147
- # The image
1148
- # can be complex, in which case the return image will also be complex,
1149
- # or must have an even number of bands, in which case pairs of
1150
- # bands are treated as (x, y) coordinates.
1151
- #
1152
- # @see xyz
1153
- # @return [Image] image converted to rectangular coordinates
1154
- def rect
1155
- Image::run_cmplx(self) {|x| x.complex :rect}
1156
- end
1199
+ # EOR the bands of an image together
1200
+ #
1201
+ # @return [Image] all bands EORed together
1202
+ def bandeor
1203
+ bandbool :eor
1204
+ end
1157
1205
 
1158
- # Return the complex conjugate of an image.
1159
- #
1160
- # The image
1161
- # can be complex, in which case the return image will also be complex,
1162
- # or must have an even number of bands, in which case pairs of
1163
- # bands are treated as (x, y) coordinates.
1164
- #
1165
- # @return [Image] complex conjugate
1166
- def conj
1167
- Image::run_cmplx(self) {|x| x.complex :conj}
1168
- end
1206
+ # Split an n-band image into n separate images.
1207
+ #
1208
+ # @return [Array<Image>] Array of n one-band images
1209
+ def bandsplit
1210
+ (0...bands).map { |i| extract_band i }
1211
+ end
1169
1212
 
1170
- # Calculate the cross phase of two images.
1171
- #
1172
- # @param other [Image, Real, Array<Real>] cross phase with this
1173
- # @return [Image] cross phase
1174
- def cross_phase other
1175
- complex2 other, :cross_phase
1176
- end
1213
+ # Join a set of images bandwise.
1214
+ #
1215
+ # @param other [Image, Array<Image>, Real, Array<Real>] bands to append
1216
+ # @return [Image] many band image
1217
+ def bandjoin other
1218
+ unless other.is_a? Array
1219
+ other = [other]
1220
+ end
1221
+
1222
+ # if other is just Numeric, we can use bandjoin_const
1223
+ not_all_real = !other.all? { |x| x.is_a? Numeric }
1224
+
1225
+ if not_all_real
1226
+ Vips::Image.bandjoin([self] + other)
1227
+ else
1228
+ bandjoin_const other
1229
+ end
1230
+ end
1177
1231
 
1178
- # Return the sine of an image in degrees.
1179
- #
1180
- # @return [Image] sine of each pixel
1181
- def sin
1182
- math :sin
1183
- end
1232
+ # Composite a set of images with a set of blend modes.
1233
+ #
1234
+ # @param overlay [Image, Array<Image>] images to composite
1235
+ # @param mode [BlendMode, Array<BlendMode>] blend modes to use
1236
+ # @param opts [Hash] Set of options
1237
+ # @option opts [Array<Integer>] :x x positions of overlay
1238
+ # @option opts [Array<Integer>] :y y positions of overlay
1239
+ # @option opts [Vips::Interpretation] :compositing_space Composite images
1240
+ # in this colour space
1241
+ # @option opts [Boolean] :premultiplied Images have premultiplied alpha
1242
+ # @return [Image] blended image
1243
+ def composite overlay, mode, **opts
1244
+ unless overlay.is_a? Array
1245
+ overlay = [overlay]
1246
+ end
1247
+ unless mode.is_a? Array
1248
+ mode = [mode]
1249
+ end
1250
+
1251
+ mode = mode.map do |x|
1252
+ GObject::GValue.from_nick Vips::BLEND_MODE_TYPE, x
1253
+ end
1254
+
1255
+ Vips::Image.composite([self] + overlay, mode, **opts)
1256
+ end
1184
1257
 
1185
- # Return the cosine of an image in degrees.
1186
- #
1187
- # @return [Image] cosine of each pixel
1188
- def cos
1189
- math :cos
1190
- end
1258
+ # Return the coordinates of the image maximum.
1259
+ #
1260
+ # @return [Real, Real, Real] maximum value, x coordinate of maximum, y
1261
+ # coordinate of maximum
1262
+ def maxpos
1263
+ v, opts = max x: true, y: true
1264
+ x = opts["x"]
1265
+ y = opts["y"]
1266
+ [v, x, y]
1267
+ end
1191
1268
 
1192
- # Return the tangent of an image in degrees.
1193
- #
1194
- # @return [Image] tangent of each pixel
1195
- def tan
1196
- math :tan
1197
- end
1269
+ # Return the coordinates of the image minimum.
1270
+ #
1271
+ # @return [Real, Real, Real] minimum value, x coordinate of minimum, y
1272
+ # coordinate of minimum
1273
+ def minpos
1274
+ v, opts = min x: true, y: true
1275
+ x = opts["x"]
1276
+ y = opts["y"]
1277
+ [v, x, y]
1278
+ end
1198
1279
 
1199
- # Return the inverse sine of an image in degrees.
1200
- #
1201
- # @return [Image] inverse sine of each pixel
1202
- def asin
1203
- math :asin
1204
- end
1280
+ # a median filter
1281
+ #
1282
+ # @param size [Integer] size of filter window
1283
+ # @return [Image] result of median filter
1284
+ def median size = 3
1285
+ rank size, size, size**2 / 2
1286
+ end
1205
1287
 
1206
- # Return the inverse cosine of an image in degrees.
1207
- #
1208
- # @return [Image] inverse cosine of each pixel
1209
- def acos
1210
- math :acos
1211
- end
1288
+ # Return the real part of a complex image.
1289
+ #
1290
+ # @return [Image] real part of complex image
1291
+ def real
1292
+ complexget :real
1293
+ end
1212
1294
 
1213
- # Return the inverse tangent of an image in degrees.
1214
- #
1215
- # @return [Image] inverse tangent of each pixel
1216
- def atan
1217
- math :atan
1218
- end
1295
+ # Return the imaginary part of a complex image.
1296
+ #
1297
+ # @return [Image] imaginary part of complex image
1298
+ def imag
1299
+ complexget :imag
1300
+ end
1219
1301
 
1220
- # Return the natural log of an image.
1221
- #
1222
- # @return [Image] natural log of each pixel
1223
- def log
1224
- math :log
1225
- end
1302
+ # Return an image with rectangular pixels converted to polar.
1303
+ #
1304
+ # The image
1305
+ # can be complex, in which case the return image will also be complex,
1306
+ # or must have an even number of bands, in which case pairs of
1307
+ # bands are treated as (x, y) coordinates.
1308
+ #
1309
+ # @see xyz
1310
+ # @return [Image] image converted to polar coordinates
1311
+ def polar
1312
+ Image.run_cmplx(self) { |x| x.complex :polar }
1313
+ end
1226
1314
 
1227
- # Return the log base 10 of an image.
1228
- #
1229
- # @return [Image] base 10 log of each pixel
1230
- def log10
1231
- math :log10
1232
- end
1315
+ # Return an image with polar pixels converted to rectangular.
1316
+ #
1317
+ # The image
1318
+ # can be complex, in which case the return image will also be complex,
1319
+ # or must have an even number of bands, in which case pairs of
1320
+ # bands are treated as (x, y) coordinates.
1321
+ #
1322
+ # @see xyz
1323
+ # @return [Image] image converted to rectangular coordinates
1324
+ def rect
1325
+ Image.run_cmplx(self) { |x| x.complex :rect }
1326
+ end
1233
1327
 
1234
- # Return e ** pixel.
1235
- #
1236
- # @return [Image] e ** pixel
1237
- def exp
1238
- math :exp
1239
- end
1328
+ # Return the complex conjugate of an image.
1329
+ #
1330
+ # The image
1331
+ # can be complex, in which case the return image will also be complex,
1332
+ # or must have an even number of bands, in which case pairs of
1333
+ # bands are treated as (x, y) coordinates.
1334
+ #
1335
+ # @return [Image] complex conjugate
1336
+ def conj
1337
+ Image.run_cmplx(self) { |x| x.complex :conj }
1338
+ end
1240
1339
 
1241
- # Return 10 ** pixel.
1242
- #
1243
- # @return [Image] 10 ** pixel
1244
- def exp10
1245
- math :exp10
1246
- end
1340
+ # Calculate the cross phase of two images.
1341
+ #
1342
+ # @param other [Image, Real, Array<Real>] cross phase with this
1343
+ # @return [Image] cross phase
1344
+ def cross_phase other
1345
+ complex2 other, :cross_phase
1346
+ end
1247
1347
 
1248
- # Flip horizontally.
1249
- #
1250
- # @return [Image] image flipped horizontally
1251
- def fliphor
1252
- flip :horizontal
1253
- end
1348
+ # Return the sine of an image in degrees.
1349
+ #
1350
+ # @return [Image] sine of each pixel
1351
+ def sin
1352
+ math :sin
1353
+ end
1254
1354
 
1255
- # Flip vertically.
1256
- #
1257
- # @return [Image] image flipped vertically
1258
- def flipver
1259
- flip :vertical
1260
- end
1355
+ # Return the cosine of an image in degrees.
1356
+ #
1357
+ # @return [Image] cosine of each pixel
1358
+ def cos
1359
+ math :cos
1360
+ end
1261
1361
 
1262
- # Erode with a structuring element.
1263
- #
1264
- # The structuring element must be an array with 0 for black, 255 for
1265
- # white and 128 for don't care.
1266
- #
1267
- # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1268
- # element
1269
- # @return [Image] eroded image
1270
- def erode mask
1271
- morph mask, :erode
1272
- end
1362
+ # Return the tangent of an image in degrees.
1363
+ #
1364
+ # @return [Image] tangent of each pixel
1365
+ def tan
1366
+ math :tan
1367
+ end
1273
1368
 
1274
- # Dilate with a structuring element.
1275
- #
1276
- # The structuring element must be an array with 0 for black, 255 for
1277
- # white and 128 for don't care.
1278
- #
1279
- # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1280
- # element
1281
- # @return [Image] dilated image
1282
- def dilate mask
1283
- morph mask, :dilate
1284
- end
1369
+ # Return the inverse sine of an image in degrees.
1370
+ #
1371
+ # @return [Image] inverse sine of each pixel
1372
+ def asin
1373
+ math :asin
1374
+ end
1285
1375
 
1286
- # Rotate by 90 degrees clockwise.
1287
- #
1288
- # @return [Image] rotated image
1289
- def rot90
1290
- rot :d90
1291
- end
1376
+ # Return the inverse cosine of an image in degrees.
1377
+ #
1378
+ # @return [Image] inverse cosine of each pixel
1379
+ def acos
1380
+ math :acos
1381
+ end
1292
1382
 
1293
- # Rotate by 180 degrees clockwise.
1294
- #
1295
- # @return [Image] rotated image
1296
- def rot180
1297
- rot :d180
1298
- end
1383
+ # Return the inverse tangent of an image in degrees.
1384
+ #
1385
+ # @return [Image] inverse tangent of each pixel
1386
+ def atan
1387
+ math :atan
1388
+ end
1299
1389
 
1300
- # Rotate by 270 degrees clockwise.
1301
- #
1302
- # @return [Image] rotated image
1303
- def rot270
1304
- rot :d270
1305
- end
1390
+ # Return the natural log of an image.
1391
+ #
1392
+ # @return [Image] natural log of each pixel
1393
+ def log
1394
+ math :log
1395
+ end
1306
1396
 
1307
- # Select pixels from `th` if `self` is non-zero and from `el` if
1308
- # `self` is zero. Use the `:blend` option to fade smoothly
1309
- # between `th` and `el`.
1310
- #
1311
- # @param th [Image, Real, Array<Real>] true values
1312
- # @param el [Image, Real, Array<Real>] false values
1313
- # @param opts [Hash] set of options
1314
- # @option opts [Boolean] :blend (false) Blend smoothly between th and el
1315
- # @return [Image] merged image
1316
- def ifthenelse(th, el, opts = {})
1317
- match_image = [th, el, self].find {|x| x.is_a? Vips::Image}
1318
-
1319
- unless th.is_a? Vips::Image
1320
- th = Operation.imageize match_image, th
1321
- end
1322
- unless el.is_a? Vips::Image
1323
- el = Operation.imageize match_image, el
1324
- end
1325
-
1326
- Vips::Operation.call "ifthenelse", [self, th, el], opts
1327
- end
1397
+ # Return the log base 10 of an image.
1398
+ #
1399
+ # @return [Image] base 10 log of each pixel
1400
+ def log10
1401
+ math :log10
1402
+ end
1328
1403
 
1329
- # Scale an image to uchar. This is the vips `scale` operation, but
1330
- # renamed to avoid a clash with the `.scale` property.
1331
- #
1332
- # @param opts [Hash] Set of options
1333
- # @return [Vips::Image] Output image
1334
- def scaleimage opts = {}
1335
- Vips::Image.scale self, opts
1336
- end
1404
+ # Return e ** pixel.
1405
+ #
1406
+ # @return [Image] e ** pixel
1407
+ def exp
1408
+ math :exp
1409
+ end
1337
1410
 
1411
+ # Return 10 ** pixel.
1412
+ #
1413
+ # @return [Image] 10 ** pixel
1414
+ def exp10
1415
+ math :exp10
1338
1416
  end
1339
1417
 
1340
- # This method generates yard comments for all the dynamically bound
1341
- # vips operations.
1418
+ # Flip horizontally.
1342
1419
  #
1343
- # Regenerate with something like:
1420
+ # @return [Image] image flipped horizontally
1421
+ def fliphor
1422
+ flip :horizontal
1423
+ end
1424
+
1425
+ # Flip vertically.
1344
1426
  #
1345
- # ```
1346
- # $ ruby > methods.rb
1347
- # require 'vips'; Vips::generate_yard
1348
- # ^D
1349
- # ```
1427
+ # @return [Image] image flipped vertically
1428
+ def flipver
1429
+ flip :vertical
1430
+ end
1350
1431
 
1351
- def self.generate_yard
1352
- # these have hand-written methods, see above
1353
- no_generate = ["scale", "bandjoin", "composite", "ifthenelse"]
1354
-
1355
- # map gobject's type names to Ruby
1356
- map_go_to_ruby = {
1357
- "gboolean" => "Boolean",
1358
- "gint" => "Integer",
1359
- "gdouble" => "Float",
1360
- "gfloat" => "Float",
1361
- "gchararray" => "String",
1362
- "VipsImage" => "Vips::Image",
1363
- "VipsInterpolate" => "Vips::Interpolate",
1364
- "VipsArrayDouble" => "Array<Double>",
1365
- "VipsArrayInt" => "Array<Integer>",
1366
- "VipsArrayImage" => "Array<Image>",
1367
- "VipsArrayString" => "Array<String>",
1368
- }
1369
-
1370
- generate_operation = lambda do |gtype, nickname, op|
1371
- op_flags = op.get_flags
1372
- return if (op_flags & OPERATION_DEPRECATED) != 0
1373
- return if no_generate.include? nickname
1374
- description = Vips::vips_object_get_description op
1375
-
1376
- # find and classify all the arguments the operator can take
1377
- required_input = []
1378
- optional_input = []
1379
- required_output = []
1380
- optional_output = []
1381
- member_x = nil
1382
- op.argument_map do |pspec, argument_class, argument_instance|
1383
- arg_flags = argument_class[:flags]
1384
- next if (arg_flags & ARGUMENT_CONSTRUCT) == 0
1385
- next if (arg_flags & ARGUMENT_DEPRECATED) != 0
1386
-
1387
- name = pspec[:name].tr("-", "_")
1388
- # 'in' as a param name confuses yard
1389
- name = "im" if name == "in"
1390
- gtype = pspec[:value_type]
1391
- fundamental = GObject::g_type_fundamental gtype
1392
- type_name = GObject::g_type_name gtype
1393
- if map_go_to_ruby.include? type_name
1394
- type_name = map_go_to_ruby[type_name]
1395
- end
1396
- if fundamental == GObject::GFLAGS_TYPE ||
1397
- fundamental == GObject::GENUM_TYPE
1398
- type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1399
- end
1400
- blurb = GObject::g_param_spec_get_blurb pspec
1401
- value = {:name => name,
1402
- :flags => arg_flags,
1403
- :gtype => gtype,
1404
- :type_name => type_name,
1405
- :blurb => blurb}
1406
-
1407
- if (arg_flags & ARGUMENT_INPUT) != 0
1408
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1409
- # note the first required input image, if any ... we
1410
- # will be a method of this instance
1411
- if !member_x && gtype == Vips::IMAGE_TYPE
1412
- member_x = value
1413
- else
1414
- required_input << value
1415
- end
1416
- else
1417
- optional_input << value
1418
- end
1419
- end
1420
-
1421
- # MODIFY INPUT args count as OUTPUT as well
1422
- if (arg_flags & ARGUMENT_OUTPUT) != 0 ||
1423
- ((arg_flags & ARGUMENT_INPUT) != 0 &&
1424
- (arg_flags & ARGUMENT_MODIFY) != 0)
1425
- if (arg_flags & ARGUMENT_REQUIRED) != 0
1426
- required_output << value
1427
- else
1428
- optional_output << value
1429
- end
1430
- end
1431
-
1432
- end
1433
-
1434
- print "# @!method "
1435
- print "self." unless member_x
1436
- print "#{nickname}("
1437
- print required_input.map{|x| x[:name]}.join(", ")
1438
- print ", " if required_input.length > 0
1439
- puts "opts = {})"
1440
-
1441
- puts "# #{description.capitalize}."
1442
-
1443
- required_input.each do |arg|
1444
- puts "# @param #{arg[:name]} [#{arg[:type_name]}] " +
1445
- "#{arg[:blurb]}"
1446
- end
1447
-
1448
- puts "# @param opts [Hash] Set of options"
1449
- optional_input.each do |arg|
1450
- puts "# @option opts [#{arg[:type_name]}] :#{arg[:name]} " +
1451
- "#{arg[:blurb]}"
1452
- end
1453
- optional_output.each do |arg|
1454
- print "# @option opts [#{arg[:type_name]}] :#{arg[:name]}"
1455
- puts " Output #{arg[:blurb]}"
1456
- end
1457
-
1458
- print "# @return ["
1459
- if required_output.length == 0
1460
- print "nil"
1461
- elsif required_output.length == 1
1462
- print required_output.first[:type_name]
1463
- elsif
1464
- print "Array<"
1465
- print required_output.map{|x| x[:type_name]}.join(", ")
1466
- print ">"
1467
- end
1468
- if optional_output.length > 0
1469
- print ", Hash<Symbol => Object>"
1470
- end
1471
- print "] "
1472
- print required_output.map{|x| x[:blurb]}.join(", ")
1473
- if optional_output.length > 0
1474
- print ", " if required_output.length > 0
1475
- print "Hash of optional output items"
1476
- end
1477
- puts ""
1478
-
1479
- puts ""
1480
- end
1432
+ # Erode with a structuring element.
1433
+ #
1434
+ # The structuring element must be an array with 0 for black, 255 for
1435
+ # white and 128 for don't care.
1436
+ #
1437
+ # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1438
+ # element
1439
+ # @return [Image] eroded image
1440
+ def erode mask
1441
+ morph mask, :erode
1442
+ end
1481
1443
 
1482
- generate_class = lambda do |gtype, a|
1483
- nickname = Vips::nickname_find gtype
1444
+ # Dilate with a structuring element.
1445
+ #
1446
+ # The structuring element must be an array with 0 for black, 255 for
1447
+ # white and 128 for don't care.
1448
+ #
1449
+ # @param mask [Image, Array<Real>, Array<Array<Real>>] structuring
1450
+ # element
1451
+ # @return [Image] dilated image
1452
+ def dilate mask
1453
+ morph mask, :dilate
1454
+ end
1484
1455
 
1485
- if nickname
1486
- begin
1487
- # can fail for abstract types
1488
- op = Vips::Operation.new nickname
1489
- rescue
1490
- end
1456
+ # Rotate by 90 degrees clockwise.
1457
+ #
1458
+ # @return [Image] rotated image
1459
+ def rot90
1460
+ rot :d90
1461
+ end
1462
+
1463
+ # Rotate by 180 degrees clockwise.
1464
+ #
1465
+ # @return [Image] rotated image
1466
+ def rot180
1467
+ rot :d180
1468
+ end
1491
1469
 
1492
- generate_operation.(gtype, nickname, op) if op
1493
- end
1470
+ # Rotate by 270 degrees clockwise.
1471
+ #
1472
+ # @return [Image] rotated image
1473
+ def rot270
1474
+ rot :d270
1475
+ end
1494
1476
 
1495
- Vips::vips_type_map gtype, generate_class, nil
1496
- end
1477
+ # Select pixels from `th` if `self` is non-zero and from `el` if
1478
+ # `self` is zero. Use the `:blend` option to fade smoothly
1479
+ # between `th` and `el`.
1480
+ #
1481
+ # @param th [Image, Real, Array<Real>] true values
1482
+ # @param el [Image, Real, Array<Real>] false values
1483
+ # @param opts [Hash] set of options
1484
+ # @option opts [Boolean] :blend (false) Blend smoothly between th and el
1485
+ # @return [Image] merged image
1486
+ def ifthenelse(th, el, **opts)
1487
+ match_image = [th, el, self].find { |x| x.is_a? Vips::Image }
1488
+
1489
+ unless th.is_a? Vips::Image
1490
+ th = Operation.imageize match_image, th
1491
+ end
1492
+ unless el.is_a? Vips::Image
1493
+ el = Operation.imageize match_image, el
1494
+ end
1495
+
1496
+ Vips::Operation.call "ifthenelse", [self, th, el], opts
1497
+ end
1497
1498
 
1498
- puts "module Vips"
1499
- puts " class Image"
1500
- puts ""
1499
+ # Scale an image to uchar. This is the vips `scale` operation, but
1500
+ # renamed to avoid a clash with the `.scale` property.
1501
+ #
1502
+ # @param opts [Hash] Set of options
1503
+ # @return [Vips::Image] Output image
1504
+ def scaleimage **opts
1505
+ Vips::Image.scale self, **opts
1506
+ end
1507
+ end
1508
+ end
1501
1509
 
1502
- generate_class.(GObject::g_type_from_name("VipsOperation"), nil)
1510
+ module Vips
1511
+ # This module generates yard comments for all the dynamically bound
1512
+ # vips operations.
1513
+ #
1514
+ # Regenerate with something like:
1515
+ #
1516
+ # ```
1517
+ # $ ruby > methods.rb
1518
+ # require "vips"; Vips::Yard.generate
1519
+ # ^D
1520
+ # ```
1521
+
1522
+ module Yard
1523
+ # map gobject's type names to Ruby
1524
+ MAP_GO_TO_RUBY = {
1525
+ "gboolean" => "Boolean",
1526
+ "gint" => "Integer",
1527
+ "gdouble" => "Float",
1528
+ "gfloat" => "Float",
1529
+ "gchararray" => "String",
1530
+ "VipsImage" => "Vips::Image",
1531
+ "VipsInterpolate" => "Vips::Interpolate",
1532
+ "VipsConnection" => "Vips::Connection",
1533
+ "VipsSource" => "Vips::Source",
1534
+ "VipsTarget" => "Vips::Target",
1535
+ "VipsSourceCustom" => "Vips::SourceCustom",
1536
+ "VipsTargetCustom" => "Vips::TargetCustom",
1537
+ "VipsArrayDouble" => "Array<Double>",
1538
+ "VipsArrayInt" => "Array<Integer>",
1539
+ "VipsArrayImage" => "Array<Image>",
1540
+ "VipsArrayString" => "Array<String>"
1541
+ }
1542
+
1543
+ # these have hand-written methods, see above
1544
+ NO_GENERATE = ["scale", "bandjoin", "composite", "ifthenelse"]
1545
+
1546
+ # these are aliased (appear under several names)
1547
+ ALIAS = ["crop"]
1548
+
1549
+ # turn a gtype into a ruby type name
1550
+ def self.gtype_to_ruby gtype
1551
+ fundamental = GObject.g_type_fundamental gtype
1552
+ type_name = GObject.g_type_name gtype
1553
+
1554
+ if MAP_GO_TO_RUBY.include? type_name
1555
+ type_name = MAP_GO_TO_RUBY[type_name]
1556
+ end
1557
+
1558
+ if fundamental == GObject::GFLAGS_TYPE ||
1559
+ fundamental == GObject::GENUM_TYPE
1560
+ type_name = "Vips::" + type_name[/Vips(.*)/, 1]
1561
+ end
1562
+
1563
+ type_name
1564
+ end
1503
1565
 
1504
- puts " end"
1505
- puts "end"
1566
+ def self.generate_operation introspect
1567
+ return if (introspect.flags & OPERATION_DEPRECATED) != 0
1568
+ return if NO_GENERATE.include? introspect.name
1569
+
1570
+ method_args = introspect.method_args
1571
+ required_output = introspect.required_output
1572
+ optional_input = introspect.optional_input
1573
+ optional_output = introspect.optional_output
1574
+
1575
+ print "# @!method "
1576
+ print "self." unless introspect.member_x
1577
+ print "#{introspect.name}("
1578
+ print method_args.map { |x| x[:yard_name] }.join(", ")
1579
+ print ", " if method_args.length > 0
1580
+ puts "**opts)"
1581
+
1582
+ puts "# #{introspect.description.capitalize}."
1583
+
1584
+ method_args.each do |details|
1585
+ yard_name = details[:yard_name]
1586
+ gtype = details[:gtype]
1587
+ blurb = details[:blurb]
1588
+
1589
+ puts "# @param #{yard_name} [#{gtype_to_ruby(gtype)}] #{blurb}"
1590
+ end
1591
+
1592
+ puts "# @param opts [Hash] Set of options"
1593
+ optional_input.each do |arg_name, details|
1594
+ yard_name = details[:yard_name]
1595
+ gtype = details[:gtype]
1596
+ blurb = details[:blurb]
1597
+
1598
+ puts "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name} #{blurb}"
1599
+ end
1600
+ optional_output.each do |arg_name, details|
1601
+ yard_name = details[:yard_name]
1602
+ gtype = details[:gtype]
1603
+ blurb = details[:blurb]
1604
+
1605
+ print "# @option opts [#{gtype_to_ruby(gtype)}] :#{yard_name}"
1606
+ puts " Output #{blurb}"
1607
+ end
1608
+
1609
+ print "# @return ["
1610
+ if required_output.length == 0
1611
+ print "nil"
1612
+ elsif required_output.length == 1
1613
+ print gtype_to_ruby(required_output.first[:gtype])
1614
+ else
1615
+ print "Array<"
1616
+ print required_output.map { |x| gtype_to_ruby(x[:gtype]) }.join(", ")
1617
+ print ">"
1618
+ end
1619
+ if optional_output.length > 0
1620
+ print ", Hash<Symbol => Object>"
1621
+ end
1622
+ print "] "
1623
+ print required_output.map { |x| x[:blurb] }.join(", ")
1624
+ if optional_output.length > 0
1625
+ print ", " if required_output.length > 0
1626
+ print "Hash of optional output items"
1627
+ end
1628
+ puts ""
1629
+
1630
+ puts ""
1506
1631
  end
1507
1632
 
1633
+ def self.generate
1634
+ alias_gtypes = {}
1635
+ ALIAS.each do |name|
1636
+ gtype = Vips.type_find "VipsOperation", name
1637
+ alias_gtypes[gtype] = name
1638
+ end
1639
+
1640
+ generate_class = lambda do |gtype, _|
1641
+ name = if alias_gtypes.key? gtype
1642
+ alias_gtypes[gtype]
1643
+ else
1644
+ Vips.nickname_find gtype
1645
+ end
1646
+
1647
+ if name
1648
+ begin
1649
+ # can fail for abstract types
1650
+ introspect = Vips::Introspect.get_yard name
1651
+ rescue Vips::Error
1652
+ nil
1653
+ end
1654
+
1655
+ generate_operation(introspect) if introspect
1656
+ end
1657
+
1658
+ Vips.vips_type_map gtype, generate_class, nil
1659
+ end
1660
+
1661
+ puts "module Vips"
1662
+ puts " class Image"
1663
+ puts ""
1664
+
1665
+ generate_class.call(GObject.g_type_from_name("VipsOperation"), nil)
1666
+
1667
+ puts " end"
1668
+ puts "end"
1669
+ end
1670
+ end
1508
1671
  end