-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathsourcecustom.rb
89 lines (79 loc) · 2.51 KB
/
sourcecustom.rb
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