/vendor/cache/ruby/2.7.0/gems/ruby-vips-2.0.17/lib/vips/object.rb
Ruby | 355 lines | 256 code | 61 blank | 38 comment | 4 complexity | b6122f246e493603a85aa02352f7bdae MD5 | raw file
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
7require 'ffi'
8
9module Vips
10 private
11
12 # debugging support
13 attach_function :vips_object_print_all, [], :void
14
15 # we must init these by hand, since they are usually made on first image
16 # create
17 attach_function :vips_band_format_get_type, [], :GType
18 attach_function :vips_interpretation_get_type, [], :GType
19 attach_function :vips_coding_get_type, [], :GType
20
21 public
22
23 # some handy gtypes
24 IMAGE_TYPE = GObject::g_type_from_name "VipsImage"
25 ARRAY_INT_TYPE = GObject::g_type_from_name "VipsArrayInt"
26 ARRAY_DOUBLE_TYPE = GObject::g_type_from_name "VipsArrayDouble"
27 ARRAY_IMAGE_TYPE = GObject::g_type_from_name "VipsArrayImage"
28 REFSTR_TYPE = GObject::g_type_from_name "VipsRefString"
29 BLOB_TYPE = GObject::g_type_from_name "VipsBlob"
30
31 BAND_FORMAT_TYPE = Vips::vips_band_format_get_type
32 INTERPRETATION_TYPE = Vips::vips_interpretation_get_type
33 CODING_TYPE = Vips::vips_coding_get_type
34
35 if Vips::at_least_libvips?(8, 6)
36 attach_function :vips_blend_mode_get_type, [], :GType
37 BLEND_MODE_TYPE = Vips::vips_blend_mode_get_type
38 else
39 BLEND_MODE_TYPE = nil
40 end
41
42 private
43
44 class Progress < FFI::Struct
45 layout :im, :pointer,
46 :run, :int,
47 :eta, :int,
48 :tpels, :int64_t,
49 :npels, :int64_t,
50 :percent, :int,
51 :start, :pointer
52 end
53
54 # Our signal marshalers.
55 #
56 # These are functions which take the handler as a param and return a
57 # closure with the right FFI signature for g_signal_connect for this
58 # specific signal.
59 #
60 # ruby-ffi makes it hard to use the g_signal_connect user data param
61 # to pass the function pointer through, unfortunately.
62 #
63 # We can't throw exceptions across C, so we must catch everything.
64
65 MARSHAL_PROGRESS = Proc.new do |handler|
66 FFI::Function.new(:void, [:pointer, :pointer, :pointer]) do |vi, prog, cb|
67 begin
68 handler.(Progress.new(prog))
69 rescue Exception => e
70 puts "progress: #{e}"
71 end
72 end
73 end
74
75 MARSHAL_READ = Proc.new do |handler|
76 FFI::Function.new(:int64_t, [:pointer, :pointer, :int64_t]) do |i, p, len|
77 begin
78 result = handler.(p, len)
79 rescue Exception => e
80 puts "read: #{e}"
81 result = 0
82 end
83
84 result
85 end
86 end
87
88 MARSHAL_SEEK = Proc.new do |handler|
89 FFI::Function.new(:int64_t, [:pointer, :int64_t, :int]) do |i, off, whence|
90 begin
91 result = handler.(off, whence)
92 rescue Exception => e
93 puts "seek: #{e}"
94 result = -1
95 end
96
97 result
98 end
99 end
100
101 MARSHAL_WRITE = Proc.new do |handler|
102 FFI::Function.new(:int64_t, [:pointer, :pointer, :int64_t]) do |i, p, len|
103 begin
104 result = handler.(p, len)
105 rescue Exception => e
106 puts "write: #{e}"
107 result = 0
108 end
109
110 result
111 end
112 end
113
114 MARSHAL_FINISH = Proc.new do |handler|
115 FFI::Function.new(:void, [:pointer, :pointer]) do |i, cb|
116 begin
117 handler.()
118 rescue Exception => e
119 puts "finish: #{e}"
120 end
121 end
122 end
123
124 # map signal name to marshal proc
125 MARSHAL_ALL = {
126 :preeval => MARSHAL_PROGRESS,
127 :eval => MARSHAL_PROGRESS,
128 :posteval => MARSHAL_PROGRESS,
129 :read => MARSHAL_READ,
130 :seek => MARSHAL_SEEK,
131 :write => MARSHAL_WRITE,
132 :finish => MARSHAL_FINISH,
133 }
134
135 attach_function :vips_enum_from_nick, [:string, :GType, :string], :int
136 attach_function :vips_enum_nick, [:GType, :int], :string
137
138 attach_function :vips_value_set_ref_string,
139 [GObject::GValue.ptr, :string], :void
140 attach_function :vips_value_set_array_double,
141 [GObject::GValue.ptr, :pointer, :int], :void
142 attach_function :vips_value_set_array_int,
143 [GObject::GValue.ptr, :pointer, :int], :void
144 attach_function :vips_value_set_array_image,
145 [GObject::GValue.ptr, :int], :void
146 callback :free_fn, [:pointer], :void
147 attach_function :vips_value_set_blob,
148 [GObject::GValue.ptr, :free_fn, :pointer, :size_t], :void
149
150 class SizeStruct < FFI::Struct
151 layout :value, :size_t
152 end
153
154 class IntStruct < FFI::Struct
155 layout :value, :int
156 end
157
158 attach_function :vips_value_get_ref_string,
159 [GObject::GValue.ptr, SizeStruct.ptr], :string
160 attach_function :vips_value_get_array_double,
161 [GObject::GValue.ptr, IntStruct.ptr], :pointer
162 attach_function :vips_value_get_array_int,
163 [GObject::GValue.ptr, IntStruct.ptr], :pointer
164 attach_function :vips_value_get_array_image,
165 [GObject::GValue.ptr, IntStruct.ptr], :pointer
166 attach_function :vips_value_get_blob,
167 [GObject::GValue.ptr, SizeStruct.ptr], :pointer
168
169 attach_function :type_find, :vips_type_find, [:string, :string], :GType
170
171 class Object < GObject::GObject
172 # print all active VipsObjects, with their reference counts. Handy for
173 # debugging ruby-vips.
174 def self.print_all
175 GC.start
176 Vips::vips_object_print_all
177 end
178
179 # the layout of the VipsObject struct
180 module ObjectLayout
181 def self.included base
182 base.class_eval do
183 # don't actually need most of these
184 layout :parent, GObject::GObject::Struct,
185 :constructed, :int,
186 :static_object, :int,
187 :argument_table, :pointer,
188 :nickname, :string,
189 :description, :string,
190 :preclose, :int,
191 :close, :int,
192 :postclose, :int,
193 :local_memory, :size_t
194 end
195 end
196 end
197
198 class Struct < GObject::GObject::Struct
199 include ObjectLayout
200 end
201
202 class ManagedStruct < GObject::GObject::ManagedStruct
203 include ObjectLayout
204 end
205
206 # return a pspec, or nil ... nil wil leave a message in the error log
207 # which you must clear
208 def get_pspec name
209 ppspec = GObject::GParamSpecPtr.new
210 argument_class = Vips::ArgumentClassPtr.new
211 argument_instance = Vips::ArgumentInstancePtr.new
212
213 result = Vips::vips_object_get_argument self, name,
214 ppspec, argument_class, argument_instance
215 return nil if result != 0
216
217 ppspec[:value]
218 end
219
220 # return a gtype, raise an error on not found
221 def get_typeof_error name
222 pspec = get_pspec name
223 raise Vips::Error unless pspec
224
225 pspec[:value_type]
226 end
227
228 # return a gtype, 0 on not found
229 def get_typeof name
230 pspec = get_pspec name
231 unless pspec
232 Vips::vips_error_clear
233 return 0
234 end
235
236 pspec[:value_type]
237 end
238
239 def get name
240 gtype = get_typeof_error name
241 gvalue = GObject::GValue.alloc
242 gvalue.init gtype
243 GObject::g_object_get_property self, name, gvalue
244 result = gvalue.get
245 gvalue.unset
246
247 GLib::logger.debug("Vips::Object.get") { "#{name} == #{result}" }
248
249 return result
250 end
251
252 def set name, value
253 GLib::logger.debug("Vips::Object.set") { "#{name} = #{value}" }
254
255 gtype = get_typeof_error name
256 gvalue = GObject::GValue.alloc
257 gvalue.init gtype
258 gvalue.set value
259 GObject::g_object_set_property self, name, gvalue
260 gvalue.unset
261 end
262
263 def signal_connect name, handler=nil
264 marshal = MARSHAL_ALL[name.to_sym]
265 raise Vips::Error, "unsupported signal #{name}" if marshal == nil
266
267 if block_given?
268 # This will grab any block given to us and make it into a proc
269 prc = Proc.new
270 elsif handler
271 # We assume the hander is a proc (perhaps we should test)
272 prc = handler
273 else
274 raise Vips::Error, "must supply either block or handler"
275 end
276
277 # The marshal function will make a closure with the right type signature
278 # for the selected signal
279 callback = marshal.(prc)
280
281 # we need to make sure this is not GCd while self is alive
282 @references << callback
283
284 GObject::g_signal_connect_data(self, name.to_s, callback, nil, nil, 0)
285 end
286
287 end
288
289 class ObjectClass < FFI::Struct
290 # opaque
291 end
292
293 class Argument < FFI::Struct
294 layout :pspec, GObject::GParamSpec.ptr
295 end
296
297 class ArgumentInstance < Argument
298 layout :parent, Argument
299 # rest opaque
300 end
301
302 # enum VipsArgumentFlags
303 ARGUMENT_REQUIRED = 1
304 ARGUMENT_CONSTRUCT = 2
305 ARGUMENT_SET_ONCE = 4
306 ARGUMENT_SET_ALWAYS = 8
307 ARGUMENT_INPUT = 16
308 ARGUMENT_OUTPUT = 32
309 ARGUMENT_DEPRECATED = 64
310 ARGUMENT_MODIFY = 128
311
312 ARGUMENT_FLAGS = {
313 required: ARGUMENT_REQUIRED,
314 construct: ARGUMENT_CONSTRUCT,
315 set_once: ARGUMENT_SET_ONCE,
316 set_always: ARGUMENT_SET_ALWAYS,
317 input: ARGUMENT_INPUT,
318 output: ARGUMENT_OUTPUT,
319 deprecated: ARGUMENT_DEPRECATED,
320 modify: ARGUMENT_MODIFY
321 }
322
323 class ArgumentClass < Argument
324 layout :parent, Argument,
325 :object_class, ObjectClass.ptr,
326 :flags, :uint,
327 :priority, :int,
328 :offset, :ulong_long
329 end
330
331 class ArgumentClassPtr < FFI::Struct
332 layout :value, ArgumentClass.ptr
333 end
334
335 class ArgumentInstancePtr < FFI::Struct
336 layout :value, ArgumentInstance.ptr
337 end
338
339 # just use :pointer, not VipsObject.ptr, to avoid casting gobject
340 # subclasses
341 attach_function :vips_object_get_argument,
342 [:pointer, :string,
343 GObject::GParamSpecPtr.ptr,
344 ArgumentClassPtr.ptr, ArgumentInstancePtr.ptr],
345 :int
346
347 attach_function :vips_object_print_all, [], :void
348
349 attach_function :vips_object_set_from_string, [:pointer, :string], :int
350
351 callback :type_map_fn, [:GType, :pointer], :pointer
352 attach_function :vips_type_map, [:GType, :type_map_fn, :pointer], :pointer
353
354 attach_function :vips_object_get_description, [:pointer], :string
355end