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