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