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