Module: SafeImage::VipsGlue
- Defined in:
- lib/safe_image/vips_glue.rb
Overview
Minimal Fiddle binding for libvips. Instead of libvips’ variadic C convenience API, operations are invoked through the fixed-signature GObject layer: vips_operation_new -> set properties as GValues -> vips_cache_operation_build -> read outputs -> unref. The invocation pattern is modelled on ruby-vips (MIT, Copyright © 2016 John Cupitt, github.com/libvips/ruby-vips), trimmed to exactly the operations SafeImage::Native performs — the function table below doubles as an operation allowlist.
Constant Summary collapse
- GVALUE_SIZE =
24- GVALUE_ZERO =
("\0" * GVALUE_SIZE).freeze
- PSPEC_VALUE_TYPE_OFFSET =
Public, decades-stable GParamSpec ABI: GTypeInstance(8) + name(8) + flags(4 + padding) puts value_type at byte 24.
24- LIBRARY_CANDIDATES =
%w[libvips.so.42 libvips.42.dylib libvips.dylib libvips.so].freeze
- TYPE =
{ void: Fiddle::TYPE_VOID, int: Fiddle::TYPE_INT, double: Fiddle::TYPE_DOUBLE, size_t: Fiddle::TYPE_SIZE_T, ptr: Fiddle::TYPE_VOIDP }.freeze
- SIGNATURES =
Fixed-signature entry points only; no varargs anywhere. The g_* symbols resolve through the libvips handle via its GLib dependency.
{ vips_init: [%i[ptr], :int], vips_version: [%i[int], :int], vips_error_buffer: [[], :ptr], vips_error_clear: [[], :void], vips_block_untrusted_set: [%i[int], :void], vips_operation_block_set: [%i[ptr int], :void], vips_concurrency_set: [%i[int], :void], vips_cache_set_max: [%i[int], :void], vips_cache_set_max_mem: [%i[size_t], :void], vips_cache_set_max_files: [%i[int], :void], vips_type_find: [%i[ptr ptr], :size_t], vips_enum_from_nick: [%i[ptr size_t ptr], :int], vips_operation_new: [%i[ptr], :ptr], vips_cache_operation_build: [%i[ptr], :ptr], vips_object_unref_outputs: [%i[ptr], :void], vips_value_set_array_double: [%i[ptr ptr int], :void], vips_image_get_width: [%i[ptr], :int], vips_image_get_height: [%i[ptr], :int], vips_image_get_bands: [%i[ptr], :int], vips_image_get_n_pages: [%i[ptr], :int], vips_image_get_orientation: [%i[ptr], :int], vips_image_hasalpha: [%i[ptr], :int], vips_colourspace_issupported: [%i[ptr], :int], vips_image_new_from_memory_copy: [%i[ptr size_t int int int int], :ptr], vips_image_write_to_memory: [%i[ptr ptr], :ptr], g_object_ref: [%i[ptr], :ptr], g_object_unref: [%i[ptr], :void], g_object_set_property: [%i[ptr ptr ptr], :void], g_object_get_property: [%i[ptr ptr ptr], :void], g_object_class_find_property: [%i[ptr ptr], :ptr], g_value_init: [%i[ptr size_t], :ptr], g_value_unset: [%i[ptr], :void], g_value_set_boolean: [%i[ptr int], :void], g_value_set_int: [%i[ptr int], :void], g_value_set_double: [%i[ptr double], :void], g_value_set_string: [%i[ptr ptr], :void], g_value_set_enum: [%i[ptr int], :void], g_value_set_flags: [%i[ptr int], :void], g_value_set_object: [%i[ptr ptr], :void], g_value_get_object: [%i[ptr], :ptr], g_type_fundamental: [%i[size_t], :size_t], g_type_from_name: [%i[ptr], :size_t], g_free: [%i[ptr], :void] }.freeze
Class Method Summary collapse
- .alpha?(image_ptr) ⇒ Boolean
-
.available? ⇒ Boolean
True when libvips loaded (or loads) successfully.
- .bands(image_ptr) ⇒ Object
-
.c(name, *args) ⇒ Object
Calls a bound C function by name.
- .colourspace_supported?(image_ptr) ⇒ Boolean
- .error! ⇒ Object
- .error_message ⇒ Object
- .height(image_ptr) ⇒ Object
-
.image_bytes(image_ptr) ⇒ Object
Copies the image’s pixel data out as a binary string (used to read the tiny vips_stats matrix without binding the variadic getpoint).
- .image_from_memory(bytes, width, height, bands, format_number) ⇒ Object
- .init! ⇒ Object
-
.operation(nickname, inputs, output: "out") ⇒ Object
Invokes one vips operation.
- .orientation(image_ptr) ⇒ Object
- .pages(image_ptr) ⇒ Object
- .type_find?(nickname) ⇒ Boolean
- .unref(image_ptr) ⇒ Object
- .version ⇒ Object
- .width(image_ptr) ⇒ Object
-
.with_images ⇒ Object
Tracks every acquired VipsImage pointer and releases all of them when the block exits, success or failure.
Class Method Details
.alpha?(image_ptr) ⇒ Boolean
201 |
# File 'lib/safe_image/vips_glue.rb', line 201 def alpha?(image_ptr) = !c(:vips_image_hasalpha, image_ptr).zero? |
.available? ⇒ Boolean
True when libvips loaded (or loads) successfully. A load failure is memoized; the gem keeps working through the ImageMagick paths.
119 120 121 122 123 124 |
# File 'lib/safe_image/vips_glue.rb', line 119 def available? init! true rescue VipsUnavailableError false end |
.bands(image_ptr) ⇒ Object
198 |
# File 'lib/safe_image/vips_glue.rb', line 198 def bands(image_ptr) = c(:vips_image_get_bands, image_ptr) |
.c(name, *args) ⇒ Object
Calls a bound C function by name.
127 128 129 |
# File 'lib/safe_image/vips_glue.rb', line 127 def c(name, *args) @functions.fetch(name).call(*args) end |
.colourspace_supported?(image_ptr) ⇒ Boolean
202 |
# File 'lib/safe_image/vips_glue.rb', line 202 def colourspace_supported?(image_ptr) = !c(:vips_colourspace_issupported, image_ptr).zero? |
.error! ⇒ Object
131 132 133 134 135 |
# File 'lib/safe_image/vips_glue.rb', line 131 def error! = c(:vips_error_clear) raise InvalidImageError, end |
.error_message ⇒ Object
137 138 139 140 141 |
# File 'lib/safe_image/vips_glue.rb', line 137 def ptr = c(:vips_error_buffer) = ptr.null? ? "" : ptr.to_s .empty? ? "libvips error" : .strip end |
.height(image_ptr) ⇒ Object
197 |
# File 'lib/safe_image/vips_glue.rb', line 197 def height(image_ptr) = c(:vips_image_get_height, image_ptr) |
.image_bytes(image_ptr) ⇒ Object
Copies the image’s pixel data out as a binary string (used to read the tiny vips_stats matrix without binding the variadic getpoint).
213 214 215 216 217 218 219 220 221 222 |
# File 'lib/safe_image/vips_glue.rb', line 213 def image_bytes(image_ptr) size_out = Fiddle::Pointer.malloc(Fiddle::SIZEOF_SIZE_T, Fiddle::RUBY_FREE) buffer = c(:vips_image_write_to_memory, image_ptr, size_out) error! if buffer.null? begin buffer[0, size_out[0, Fiddle::SIZEOF_SIZE_T].unpack1("J")] ensure c(:g_free, buffer) end end |
.image_from_memory(bytes, width, height, bands, format_number) ⇒ Object
204 205 206 207 208 209 |
# File 'lib/safe_image/vips_glue.rb', line 204 def image_from_memory(bytes, width, height, bands, format_number) init! ptr = c(:vips_image_new_from_memory_copy, bytes, bytes.bytesize, width, height, bands, format_number) error! if ptr.null? ptr end |
.init! ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/safe_image/vips_glue.rb', line 84 def init! return if @initialized raise @load_error if @load_error @init_mutex.synchronize do next if @initialized raise @load_error if @load_error handle = open_library @functions = SIGNATURES.to_h do |name, (args, ret)| address = handle[name.to_s] [name, Fiddle::Function.new(address, args.map { |t| TYPE.fetch(t) }, TYPE.fetch(ret))] end silence_vips_log! raise Error, "vips_init failed: #{}" if c(:vips_init, "safe_image") != 0 major = c(:vips_version, 0) minor = c(:vips_version, 1) @version = [major, minor] raise Error, "libvips >= 8.13 is required (found #{major}.#{minor})" if (@version <=> [8, 13]).negative? harden! resolve_gtypes! @initialized = true end end |
.operation(nickname, inputs, output: "out") ⇒ Object
Invokes one vips operation. Property values are converted according to the property’s GType: booleans, ints, doubles, strings, enums (given as nick strings), flags, double arrays and VipsImage pointers. Returns the named output image pointer (caller owns one reference), or nil when output is nil (savers).
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/safe_image/vips_glue.rb', line 172 def operation(nickname, inputs, output: "out") op = c(:vips_operation_new, nickname) raise UnsupportedFormatError, "unknown vips operation: #{nickname}" if op.null? begin inputs.each { |name, value| set_property(op, name.to_s, value) } built = c(:vips_cache_operation_build, op) if built.null? c(:vips_object_unref_outputs, op) error! end begin output ? image_output(built, output) : nil ensure c(:vips_object_unref_outputs, built) c(:g_object_unref, built) end ensure c(:g_object_unref, op) end end |
.orientation(image_ptr) ⇒ Object
200 |
# File 'lib/safe_image/vips_glue.rb', line 200 def orientation(image_ptr) = c(:vips_image_get_orientation, image_ptr) |
.pages(image_ptr) ⇒ Object
199 |
# File 'lib/safe_image/vips_glue.rb', line 199 def pages(image_ptr) = c(:vips_image_get_n_pages, image_ptr) |
.type_find?(nickname) ⇒ Boolean
143 144 145 146 |
# File 'lib/safe_image/vips_glue.rb', line 143 def type_find?(nickname) init! !c(:vips_type_find, "VipsOperation", nickname).zero? end |
.unref(image_ptr) ⇒ Object
148 149 150 |
# File 'lib/safe_image/vips_glue.rb', line 148 def unref(image_ptr) c(:g_object_unref, image_ptr) if image_ptr && !image_ptr.null? end |
.version ⇒ Object
112 113 114 115 |
# File 'lib/safe_image/vips_glue.rb', line 112 def version init! @version end |
.width(image_ptr) ⇒ Object
196 |
# File 'lib/safe_image/vips_glue.rb', line 196 def width(image_ptr) = c(:vips_image_get_width, image_ptr) |
.with_images ⇒ Object
Tracks every acquired VipsImage pointer and releases all of them when the block exits, success or failure. Pipelines are strictly linear, so deterministic unref in reverse order is sufficient.
155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/safe_image/vips_glue.rb', line 155 def with_images acquired = [] track = lambda do |ptr| acquired << ptr ptr end init! yield track ensure acquired.reverse_each { |ptr| unref(ptr) } end |