ruby-vips 1.0.6 → 2.0.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.
@@ -0,0 +1,204 @@
1
+ # This module provides an interface to the top level bits of libvips
2
+ # via ruby-ffi.
3
+ #
4
+ # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
9
+ module Vips
10
+ private
11
+
12
+ # we must init these by hand, since they are usually made on first image
13
+ # create
14
+ attach_function :vips_band_format_get_type, [], :GType
15
+ attach_function :vips_interpretation_get_type, [], :GType
16
+ attach_function :vips_coding_get_type, [], :GType
17
+
18
+ public
19
+
20
+ # some handy gtypes
21
+ IMAGE_TYPE = GObject::g_type_from_name "VipsImage"
22
+ ARRAY_INT_TYPE = GObject::g_type_from_name "VipsArrayInt"
23
+ ARRAY_DOUBLE_TYPE = GObject::g_type_from_name "VipsArrayDouble"
24
+ ARRAY_IMAGE_TYPE = GObject::g_type_from_name "VipsArrayImage"
25
+ REFSTR_TYPE = GObject::g_type_from_name "VipsRefString"
26
+ BLOB_TYPE = GObject::g_type_from_name "VipsBlob"
27
+
28
+ BAND_FORMAT_TYPE = Vips::vips_band_format_get_type
29
+ INTERPRETATION_TYPE = Vips::vips_interpretation_get_type
30
+ CODING_TYPE = Vips::vips_coding_get_type
31
+
32
+ private
33
+
34
+ attach_function :vips_enum_from_nick, [:string, :GType, :string], :int
35
+ attach_function :vips_enum_nick, [:GType, :int], :string
36
+
37
+ attach_function :vips_value_set_array_double,
38
+ [GObject::GValue.ptr, :pointer, :int], :void
39
+ attach_function :vips_value_set_array_int,
40
+ [GObject::GValue.ptr, :pointer, :int], :void
41
+ attach_function :vips_value_set_array_image,
42
+ [GObject::GValue.ptr, :int], :void
43
+ callback :free_fn, [:pointer], :void
44
+ attach_function :vips_value_set_blob,
45
+ [GObject::GValue.ptr, :free_fn, :pointer, :size_t], :void
46
+
47
+ class SizeStruct < FFI::Struct
48
+ layout :value, :size_t
49
+ end
50
+
51
+ class IntStruct < FFI::Struct
52
+ layout :value, :int
53
+ end
54
+
55
+ attach_function :vips_value_get_ref_string,
56
+ [GObject::GValue.ptr, SizeStruct.ptr], :string
57
+ attach_function :vips_value_get_array_double,
58
+ [GObject::GValue.ptr, IntStruct.ptr], :pointer
59
+ attach_function :vips_value_get_array_int,
60
+ [GObject::GValue.ptr, IntStruct.ptr], :pointer
61
+ attach_function :vips_value_get_array_image,
62
+ [GObject::GValue.ptr, IntStruct.ptr], :pointer
63
+ attach_function :vips_value_get_blob,
64
+ [GObject::GValue.ptr, SizeStruct.ptr], :pointer
65
+
66
+ attach_function :type_find, :vips_type_find, [:string, :string], :GType
67
+
68
+ class Object < GObject::GObject
69
+
70
+ # the layout of the VipsObject struct
71
+ module ObjectLayout
72
+ def self.included base
73
+ base.class_eval do
74
+ # don't actually need most of these
75
+ layout :parent, GObject::GObject::Struct,
76
+ :constructed, :int,
77
+ :static_object, :int,
78
+ :argument_table, :pointer,
79
+ :nickname, :string,
80
+ :description, :string,
81
+ :preclose, :int,
82
+ :close, :int,
83
+ :postclose, :int,
84
+ :local_memory, :size_t
85
+ end
86
+ end
87
+ end
88
+
89
+ class Struct < GObject::GObject::Struct
90
+ include ObjectLayout
91
+
92
+ end
93
+
94
+ class ManagedStruct < GObject::GObject::ManagedStruct
95
+ include ObjectLayout
96
+
97
+ end
98
+
99
+ def get_typeof name
100
+ pspec = GObject::GParamSpecPtr.new
101
+ argument_class = Vips::ArgumentClassPtr.new
102
+ argument_instance = Vips::ArgumentInstancePtr.new
103
+
104
+ result = Vips::vips_object_get_argument self, name,
105
+ pspec, argument_class, argument_instance
106
+ raise Vips::Error if result != 0
107
+
108
+ pspec[:value][:value_type]
109
+ end
110
+
111
+ def get name
112
+ gtype = get_typeof name
113
+ gvalue = GObject::GValue.alloc
114
+ gvalue.init gtype
115
+ GObject::g_object_get_property self, name, gvalue
116
+ result = gvalue.get
117
+
118
+ # Vips::log "Vips::Object.get(\"#{name}\"): result = #{result}"
119
+
120
+ return result
121
+ end
122
+
123
+ def set name, value
124
+ # Vips::log "Vips::Object.set: #{name} = #{value}"
125
+
126
+ gtype = get_typeof name
127
+ gvalue = GObject::GValue.alloc
128
+ gvalue.init gtype
129
+ gvalue.set value
130
+ GObject::g_object_set_property self, name, gvalue
131
+ end
132
+
133
+ end
134
+
135
+ class ObjectClass < FFI::Struct
136
+ # opaque
137
+ end
138
+
139
+ class Argument < FFI::Struct
140
+ layout :pspec, GObject::GParamSpec.ptr
141
+ end
142
+
143
+ class ArgumentInstance < Argument
144
+ layout :parent, Argument
145
+ # rest opaque
146
+ end
147
+
148
+ # enum VipsArgumentFlags
149
+ ARGUMENT_REQUIRED = 1
150
+ ARGUMENT_CONSTRUCT = 2
151
+ ARGUMENT_SET_ONCE = 4
152
+ ARGUMENT_SET_ALWAYS = 8
153
+ ARGUMENT_INPUT = 16
154
+ ARGUMENT_OUTPUT = 32
155
+ ARGUMENT_DEPRECATED = 64
156
+ ARGUMENT_MODIFY = 128
157
+
158
+ ARGUMENT_FLAGS = {
159
+ :required => ARGUMENT_REQUIRED,
160
+ :construct => ARGUMENT_CONSTRUCT,
161
+ :set_once => ARGUMENT_SET_ONCE,
162
+ :set_always => ARGUMENT_SET_ALWAYS,
163
+ :input => ARGUMENT_INPUT,
164
+ :output => ARGUMENT_OUTPUT,
165
+ :deprecated => ARGUMENT_DEPRECATED,
166
+ :modify => ARGUMENT_MODIFY
167
+ }
168
+
169
+ class ArgumentClass < Argument
170
+ layout :parent, Argument,
171
+ :object_class, ObjectClass.ptr,
172
+ :flags, :uint,
173
+ :priority, :int,
174
+ :offset, :ulong_long
175
+ end
176
+
177
+ class ArgumentClassPtr < FFI::Struct
178
+ layout :value, ArgumentClass.ptr
179
+ end
180
+
181
+ class ArgumentInstancePtr < FFI::Struct
182
+ layout :value, ArgumentInstance.ptr
183
+ end
184
+
185
+ # just use :pointer, not VipsObject.ptr, to avoid casting gobject
186
+ # subclasses
187
+ attach_function :vips_object_get_argument,
188
+ [:pointer, :string,
189
+ GObject::GParamSpecPtr.ptr,
190
+ ArgumentClassPtr.ptr, ArgumentInstancePtr.ptr],
191
+ :int
192
+
193
+ attach_function :vips_object_print_all, [], :void
194
+
195
+ attach_function :vips_object_set_from_string, [:pointer, :string], :int
196
+
197
+ callback :type_map_fn, [:GType, :pointer], :pointer
198
+ attach_function :vips_type_map, [:GType, :type_map_fn, :pointer], :pointer
199
+
200
+ attach_function :vips_object_get_description, [:pointer], :string
201
+
202
+ end
203
+
204
+
@@ -1,19 +1,363 @@
1
+ # This module provides an interface to the top level bits of libvips
2
+ # via ruby-ffi.
3
+ #
4
+ # Author:: John Cupitt (mailto:jcupitt@gmail.com)
5
+ # License:: MIT
6
+
7
+ require 'ffi'
8
+
1
9
  module Vips
10
+ private
11
+
12
+ attach_function :vips_operation_new, [:string], :pointer
13
+
14
+ attach_function :vips_cache_operation_build, [:pointer], :pointer
15
+ attach_function :vips_object_unref_outputs, [:pointer], :void
16
+
17
+ callback :argument_map_fn, [:pointer,
18
+ GObject::GParamSpec.ptr,
19
+ ArgumentClass.ptr,
20
+ ArgumentInstance.ptr,
21
+ :pointer, :pointer], :pointer
22
+ attach_function :vips_argument_map, [:pointer,
23
+ :argument_map_fn,
24
+ :pointer, :pointer], :pointer
25
+
26
+ OPERATION_SEQUENTIAL = 1
27
+ OPERATION_NOCACHE = 4
28
+ OPERATION_DEPRECATED = 8
29
+
30
+ OPERATION_FLAGS = {
31
+ :sequential => OPERATION_SEQUENTIAL,
32
+ :nocache => OPERATION_NOCACHE,
33
+ :deprecated => OPERATION_DEPRECATED
34
+ }
35
+
36
+ attach_function :vips_operation_get_flags, [:pointer], :int
37
+
38
+ class Operation < Object
39
+
40
+ # the layout of the VipsOperation struct
41
+ module OperationLayout
42
+ def self.included base
43
+ base.class_eval do
44
+ layout :parent, Object::Struct
45
+ # rest opaque
46
+ end
47
+ end
48
+ end
49
+
50
+ class Struct < Object::Struct
51
+ include OperationLayout
52
+
53
+ end
54
+
55
+ class ManagedStruct < Object::ManagedStruct
56
+ include OperationLayout
57
+
58
+ end
59
+
60
+ def initialize value
61
+ # allow init with a pointer so we can wrap the return values from
62
+ # things like _build
63
+ if value.is_a? String
64
+ value = Vips::vips_operation_new value
65
+ raise Vips::Error if value == nil
66
+ end
67
+
68
+ super value
69
+ end
70
+
71
+ def build
72
+ op = Vips::vips_cache_operation_build self
73
+ if op == nil
74
+ raise Vips::Error
75
+ end
76
+
77
+ return Operation.new op
78
+ end
79
+
80
+ def argument_map &block
81
+ fn = Proc.new do |op, pspec, argument_class, argument_instance, a, b|
82
+ block.call pspec, argument_class, argument_instance
83
+ end
84
+
85
+ Vips::vips_argument_map self, fn, nil, nil
86
+ end
87
+
88
+ def get_flags
89
+ Vips::vips_operation_get_flags self
90
+ end
91
+
92
+ # not quick! try to call this infrequently
93
+ def get_construct_args
94
+ args = []
95
+
96
+ argument_map do |pspec, argument_class, argument_instance|
97
+ flags = argument_class[:flags]
98
+ if (flags & ARGUMENT_CONSTRUCT) != 0
99
+ # names can include - as punctuation, but we always use _ in
100
+ # Ruby
101
+ name = pspec[:name].gsub("-", "_")
102
+
103
+ args << [name, flags]
104
+ end
105
+ end
106
+
107
+ return args
108
+ end
109
+
110
+ # search array for the first element to match a predicate ...
111
+ # search inside subarrays and sub-hashes
112
+ def self.find_inside object, &block
113
+ return object if block.call object
114
+
115
+ if object.is_a? Enumerable
116
+ object.find {|value| block.call value, block}
117
+ end
118
+
119
+ return nil
120
+ end
121
+
122
+ # expand a constant into an image
123
+ def self.imageize match_image, value
124
+ return value if value.is_a? Image
125
+
126
+ # 2D array values become tiny 2D images
127
+ # if there's nothing to match to, we also make a 2D image
128
+ if (value.is_a? Array and value[0].is_a? Array) or match_image == nil
129
+ return Image.new_from_array value
130
+ else
131
+ # we have a 1D array ... use that as a pixel constant and
132
+ # expand to match match_image
133
+ return match_image.new_from_image value
134
+ end
135
+ end
2
136
 
3
- # add a conventience method to Operation
4
- # @private
5
- class Operation
6
- # Fetch arg list, remove boring ones, sort into priority order.
7
- def get_args
8
- gobject_class = gtype.to_class
9
- props = gobject_class.properties.select do |name|
10
- flags = get_argument_flags name
11
- io = ((flags & :input) | (flags & :output)) != 0
12
- dep = (flags & :deprecated) != 0
13
- io & (not dep)
14
- end
15
- args = props.map {|name| Argument.new self, name}
16
- args.sort! {|a, b| a.priority - b.priority}
137
+ # set an operation argument, expanding constants and copying images as
138
+ # required
139
+ def set name, value, match_image = nil, flags = 0
140
+ gtype = get_typeof name
141
+
142
+ if gtype == IMAGE_TYPE
143
+ value = Operation::imageize match_image, value
144
+
145
+ if (flags & ARGUMENT_MODIFY) != 0
146
+ # make sure we have a unique copy
147
+ value = value.copy.copy_memory
148
+ end
149
+ elsif gtype == ARRAY_IMAGE_TYPE
150
+ value = value.map {|x| Operation::imageize match_image, x}
151
+ end
152
+
153
+ super name, value
17
154
  end
155
+
156
+ public
157
+
158
+ # This is the public entry point for the vips binding. {call} will run
159
+ # any vips operation, for example:
160
+ #
161
+ # ```ruby
162
+ # out = Vips::Operation.call "black", [100, 100], {:bands => 12}
163
+ # ```
164
+ #
165
+ # will call the C function
166
+ #
167
+ # ```C
168
+ # vips_black( &out, 100, 100, "bands", 12, NULL );
169
+ # ```
170
+ #
171
+ # There are {Image#method_missing} hooks which will run {call} for you
172
+ # on {Image} for undefined instance or class methods. So you can also
173
+ # write:
174
+ #
175
+ # ```ruby
176
+ # out = Vips::Image.black 100, 100, bands: 12
177
+ # ```
178
+ #
179
+ # Or perhaps:
180
+ #
181
+ # ```ruby
182
+ # x = Vips::Image.black 100, 100
183
+ # y = x.invert
184
+ # ```
185
+ #
186
+ # to run the `vips_invert()` operator.
187
+ #
188
+ # There are also a set of operator overloads and some convenience
189
+ # functions, see {Image}.
190
+ #
191
+ # If the operator needs a vector constant, {call} will turn a scalar
192
+ # into a
193
+ # vector for you. So for `x.linear a, b`, which calculates
194
+ # `x * a + b` where `a` and `b` are vector constants, you can write:
195
+ #
196
+ # ```ruby
197
+ # x = Vips::Image.black 100, 100, bands: 3
198
+ # y = x.linear 1, 2
199
+ # y = x.linear [1], 4
200
+ # y = x.linear [1, 2, 3], 4
201
+ # ```
202
+ #
203
+ # or any other combination. The operator overloads use this facility to
204
+ # support all the variations on:
205
+ #
206
+ # ```ruby
207
+ # x = Vips::Image.black 100, 100, bands: 3
208
+ # y = x * 2
209
+ # y = x + [1,2,3]
210
+ # y = x % [1]
211
+ # ```
212
+ #
213
+ # Similarly, wherever an image is required, you can use a constant. The
214
+ # constant will be expanded to an image matching the first input image
215
+ # argument. For example, you can write:
216
+ #
217
+ # ```
218
+ # x = Vips::Image.black 100, 100, bands: 3
219
+ # y = x.bandjoin 255
220
+ # ```
221
+ #
222
+ # to add an extra band to the image where each pixel in the new band has
223
+ # the constant value 255.
224
+
225
+ def self.call name, supplied, optional = {}, option_string = ""
226
+ #Vips::log "Vips::VipsOperation.call: name = #{name}, " +
227
+ # "supplied = #{supplied} optional = #{optional} " +
228
+ # "option_string = #{option_string}"
229
+
230
+ op = Operation.new name
231
+
232
+ # find and classify all the arguments the operator can take
233
+ args = op.get_construct_args
234
+ required_input = []
235
+ optional_input = {}
236
+ required_output = []
237
+ optional_output = {}
238
+ args.each do |name, flags|
239
+ next if (flags & ARGUMENT_DEPRECATED) != 0
240
+
241
+ if (flags & ARGUMENT_INPUT) != 0
242
+ if (flags & ARGUMENT_REQUIRED) != 0 and
243
+ required_input << [name, flags]
244
+ else
245
+ optional_input[name] = flags
246
+ end
247
+ end
248
+
249
+ # MODIFY INPUT args count as OUTPUT as well
250
+ if (flags & ARGUMENT_OUTPUT) != 0 or
251
+ ((flags & ARGUMENT_INPUT) != 0 and
252
+ (flags & ARGUMENT_MODIFY) != 0)
253
+ if (flags & ARGUMENT_REQUIRED) != 0 and
254
+ required_output << [name, flags]
255
+ else
256
+ optional_output[name] = flags
257
+ end
258
+ end
259
+
260
+ end
261
+
262
+ # so we should have been supplied with n_required_input values, or
263
+ # n_required_input + 1 if there's a hash of options at the end
264
+ if not supplied.is_a? Array
265
+ raise Vips::Error, "unable to call #{name}: " +
266
+ "argument array is not an array"
267
+ end
268
+ if not optional.is_a? Hash
269
+ raise Vips::Error, "unable to call #{name}: " +
270
+ "optional arguments are not a hash"
271
+ end
272
+ if supplied.length != required_input.length
273
+ raise Vips::Error, "unable to call #{name}: " +
274
+ "you supplied #{supplied.length} arguments, " +
275
+ "but operation needs #{required_input.length}."
276
+ end
277
+
278
+ # very that all supplied_optional keys are in optional_input or
279
+ # optional_output
280
+ optional.each do |key, value|
281
+ arg_name = key.to_s
282
+
283
+ if not optional_input.has_key? arg_name and
284
+ not optional_output.has_key? arg_name
285
+ raise Vips::Error, "unable to call #{name}: " +
286
+ "unknown option #{arg_name}"
287
+ end
288
+ end
289
+
290
+ # the first image arg is the thing we expand constants to match ...
291
+ # we need to find it
292
+ #
293
+ # look inside array and hash arguments, since we may be passing an
294
+ # array of images
295
+ match_image = find_inside(supplied) do |value|
296
+ value.is_a? Image
297
+ end
298
+
299
+ # set any string args first so they can't be overridden
300
+ if option_string != nil
301
+ if Vips::vips_object_set_from_string(op, option_string) != 0
302
+ raise Vips::Error
303
+ end
304
+ end
305
+
306
+ # set all required inputs
307
+ required_input.each_index do |i|
308
+ arg_name = required_input[i][0]
309
+ flags = required_input[i][1]
310
+ value = supplied[i]
311
+
312
+ op.set arg_name, value, match_image, flags
313
+ end
314
+
315
+ # set all optional inputs
316
+ optional.each do |key, value|
317
+ arg_name = key.to_s
318
+
319
+ if optional_input.has_key? arg_name
320
+ flags = optional_input[arg_name]
321
+
322
+ op.set arg_name, value, match_image, flags
323
+ end
324
+ end
325
+
326
+ op = op.build
327
+
328
+ # get all required results
329
+ result = []
330
+ required_output.each do |arg_name, flags|
331
+ result << op.get(arg_name)
332
+ end
333
+
334
+ # fetch all optional ones
335
+ optional_results = {}
336
+ optional.each do |key, value|
337
+ arg_name = key.to_s
338
+
339
+ if optional_output.has_key? arg_name
340
+ flags = optional_output[arg_name]
341
+
342
+ optional_results[arg_name] = op.get arg_name
343
+ end
344
+ end
345
+
346
+ result << optional_results if optional_results != {}
347
+
348
+ if result.length == 1
349
+ result = result.first
350
+ elsif result.length == 0
351
+ result = nil
352
+ end
353
+
354
+ # Vips::log "Vips::Operation.call: result = #{result}"
355
+
356
+ Vips::vips_object_unref_outputs op
357
+
358
+ return result
359
+ end
360
+
18
361
  end
362
+
19
363
  end