1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
|
# This module provides an interface to the top level bits of libvips
# via ruby-ffi.
#
# Author:: John Cupitt (mailto:jcupitt@gmail.com)
# License:: MIT
require "ffi"
module Vips
if Vips.at_least_libvips?(8, 9)
attach_function :vips_source_custom_new, [], :pointer
end
# A source you can attach action signal handlers to to implement
# custom input types.
#
# For example:
#
# ```ruby
# file = File.open "some/file/name", "rb"
# source = Vips::SourceCustom.new
# source.on_read { |length| file.read length }
# image = Vips::Image.new_from_source source
# ```
#
# (just an example -- of course in practice you'd use {Source#new_from_file}
# to read from a named file)
class SourceCustom < Vips::Source
module SourceCustomLayout
def self.included(base)
base.class_eval do
layout :parent, Vips::Source::Struct
# rest opaque
end
end
end
class Struct < Vips::Source::Struct
include SourceCustomLayout
end
class ManagedStruct < Vips::Source::ManagedStruct
include SourceCustomLayout
end
def initialize
pointer = Vips.vips_source_custom_new
raise Vips::Error if pointer.null?
super pointer
end
# The block is executed to read data from the source. The interface is
# exactly as IO::read, ie. it takes a maximum number of bytes to read and
# returns a string of bytes from the source, or nil if the source is already
# at end of file.
#
# @yieldparam length [Integer] Read and return up to this many bytes
# @yieldreturn [String] Up to length bytes of data, or nil for EOF
def on_read &block
signal_connect "read" do |buf, len|
chunk = block.call len
return 0 if chunk.nil?
bytes_read = chunk.bytesize
buf.put_bytes(0, chunk, 0, bytes_read)
chunk.clear
bytes_read
end
end
# The block is executed to seek the source. The interface is exactly as
# IO::seek, ie. it should take an offset and whence, and return the
# new read position.
#
# This handler is optional -- if you do not attach a seek handler,
# {Source} will treat your source like an unseekable pipe object and
# do extra caching.
#
# @yieldparam offset [Integer] Seek offset
# @yieldparam whence [Integer] Seek whence
# @yieldreturn [Integer] the new read position, or -1 on error
def on_seek &block
signal_connect "seek" do |offset, whence|
block.call offset, whence
end
end
end
end
|