Skip to content

Commit 0470922

Browse files
committed
add extended targetcustom support
but TIFF write fails for some reason argh
1 parent 932c31a commit 0470922

File tree

5 files changed

+144
-34
lines changed

5 files changed

+144
-34
lines changed

example/connection.rb

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,42 @@
11
#!/usr/bin/ruby
22

33
require "vips"
4-
require "down/http"
4+
# gem install down
5+
require "down"
56

67
# byte_source = File.open ARGV[0], "rb"
78
# eg. https://images.unsplash.com/photo-1491933382434-500287f9b54b
8-
byte_source = Down::Http.open(ARGV[0])
9+
byte_source = Down::open(ARGV[0])
910

1011
source = Vips::SourceCustom.new
1112
source.on_read do |length|
12-
puts "reading #{length} bytes ..."
13+
puts "source: reading #{length} bytes ..."
1314
byte_source.read length
1415
end
1516
source.on_seek do |offset, whence|
16-
puts "seeking to #{offset}, #{whence}"
17+
puts "source: seeking to #{offset}, #{whence}"
1718
byte_source.seek(offset, whence)
1819
end
1920

20-
byte_target = File.open ARGV[1], "wb"
21+
byte_target = File.open ARGV[1], "w+b"
22+
2123
target = Vips::TargetCustom.new
22-
target.on_write { |chunk| byte_target.write(chunk) }
23-
target.on_finish { byte_target.close }
24+
target.on_write do |chunk|
25+
puts "target: writing #{chunk.length} bytes ..."
26+
byte_target.write(chunk)
27+
end
28+
target.on_read do |length|
29+
puts "target: reading #{length} bytes ..."
30+
byte_target.read length
31+
end
32+
target.on_seek do |offset, whence|
33+
puts "target: seeking to #{offset}, #{whence}"
34+
byte_target.seek(offset, whence)
35+
end
36+
target.on_end do
37+
puts "target: ending"
38+
byte_target.close
39+
end
2440

2541
image = Vips::Image.new_from_source source, "", access: :sequential
26-
image.write_to_target target, ".jpg"
42+
image.write_to_target target, ARGV[2]

lib/vips/image.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,8 +548,12 @@ def self.new_from_array array, scale = 1, offset = 0
548548
def new_from_image value
549549
pixel = (Vips::Image.black(1, 1) + value).cast(format)
550550
image = pixel.embed 0, 0, width, height, extend: :copy
551-
image.copy interpretation: interpretation, xres: xres, yres: yres,
552-
xoffset: xoffset, yoffset: yoffset
551+
image.copy \
552+
interpretation: interpretation,
553+
xres: xres,
554+
yres: yres,
555+
xoffset: xoffset,
556+
yoffset: yoffset
553557
end
554558

555559
# Write this image to a file. Save options may be encoded in the

lib/vips/object.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ class Progress < FFI::Struct
108108
end
109109
end
110110

111+
MARSHAL_END = proc do |handler|
112+
FFI::Function.new(:int, [:pointer, :pointer]) do |i, cb|
113+
# this can't throw an exception, so no catch is necessary
114+
handler.call
115+
end
116+
end
117+
111118
MARSHAL_FINISH = proc do |handler|
112119
FFI::Function.new(:void, [:pointer, :pointer]) do |i, cb|
113120
# this can't throw an exception, so no catch is necessary
@@ -123,6 +130,7 @@ class Progress < FFI::Struct
123130
read: MARSHAL_READ,
124131
seek: MARSHAL_SEEK,
125132
write: MARSHAL_WRITE,
133+
end: MARSHAL_END,
126134
finish: MARSHAL_FINISH
127135
}
128136

lib/vips/targetcustom.rb

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,63 @@ def on_write &block
6666
end
6767
end
6868

69+
# The block is executed to read data from the target. The interface is
70+
# exactly as IO::read, ie. it takes a maximum number of bytes to read and
71+
# returns a string of bytes from the target, or nil if the target is already
72+
# at end of file.
73+
#
74+
# This handler is optional and only needed for image formats which have
75+
# to be able to read their own output, like TIFF.
76+
#
77+
# @yieldparam length [Integer] Read and return up to this many bytes
78+
# @yieldreturn [String] Up to length bytes of data, or nil for EOF
79+
def on_read &block
80+
# target read added in 8.13
81+
if Vips.at_least_libvips?(8, 13)
82+
signal_connect "read" do |buf, len|
83+
chunk = block.call len
84+
return 0 if chunk.nil?
85+
bytes_read = chunk.bytesize
86+
buf.put_bytes(0, chunk, 0, bytes_read)
87+
chunk.clear
88+
89+
bytes_read
90+
end
91+
end
92+
end
93+
94+
# The block is executed to seek the target. The interface is exactly as
95+
# IO::seek, ie. it should take an offset and whence, and return the
96+
# new read position.
97+
#
98+
# This handler is optional and only needed for image formats which have
99+
# to be able to read their own output, like TIFF.
100+
#
101+
# @yieldparam offset [Integer] Seek offset
102+
# @yieldparam whence [Integer] Seek whence
103+
# @yieldreturn [Integer] the new read position, or -1 on error
104+
def on_seek &block
105+
# target seek added in 8.13
106+
if Vips.at_least_libvips?(8, 13)
107+
signal_connect "seek" do |offset, whence|
108+
block.call offset, whence
109+
end
110+
end
111+
end
112+
69113
# The block is executed at the end of write. It should do any necessary
70-
# finishing action, such as closing a file.
114+
# finishing action, such as closing a file, and return 0 on sucess and -1
115+
# on error
116+
#
117+
# @yieldreturn [Integer] 0 on sucess, or -1 on error
118+
def on_end &block
119+
signal_name = Vips.at_least_libvips?(8, 13) ? "end" : "finish"
120+
signal_connect signal_name do
121+
block.call
122+
end
123+
end
124+
125+
# Deprecated name for libvips before 8.13
71126
def on_finish &block
72127
signal_connect "finish" do
73128
block.call

spec/connection_spec.rb

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@
3636
it "can load an image from filename source" do
3737
source = Vips::Source.new_from_file simg("wagon.jpg")
3838
image = Vips::Image.new_from_source source, ""
39+
real = Vips::Image.new_from_file simg("wagon.jpg")
3940

4041
expect(image)
41-
expect(image.width).to eq(685)
42-
expect(image.height).to eq(478)
43-
expect(image.bands).to eq(3)
44-
expect(image.avg).to be_within(0.001).of(109.789)
42+
expect(image.width).to eq(real.width)
43+
expect(image.height).to eq(real.height)
44+
expect(image.bands).to eq(real.bands)
45+
expect(image.avg).to eq(real.avg)
4546
end
4647
end
4748

@@ -76,13 +77,14 @@
7677
filename = timg("x4.png")
7778
target = Vips::Target.new_to_file filename
7879
image.write_to_target target, ".png"
80+
real = Vips::Image.new_from_file simg("wagon.jpg")
7981

8082
image = Vips::Image.new_from_file filename
8183
expect(image)
82-
expect(image.width).to eq(685)
83-
expect(image.height).to eq(478)
84-
expect(image.bands).to eq(3)
85-
expect(image.avg).to be_within(0.001).of(109.789)
84+
expect(image.width).to eq(real.width)
85+
expect(image.height).to eq(real.height)
86+
expect(image.bands).to eq(real.bands)
87+
expect(image.avg).to eq(real.avg)
8688
end
8789

8890
it "can save an image to a memory target" do
@@ -114,47 +116,72 @@
114116
source.on_read { |length| file.read length }
115117
source.on_seek { |offset, whence| file.seek(offset, whence) }
116118
image = Vips::Image.new_from_source source, ""
119+
real = Vips::Image.new_from_file simg("wagon.jpg")
117120

118121
expect(image)
119-
expect(image.width).to eq(685)
120-
expect(image.height).to eq(478)
121-
expect(image.bands).to eq(3)
122-
expect(image.avg).to be_within(0.001).of(109.789)
122+
expect(image.width).to eq(real.width)
123+
expect(image.height).to eq(real.height)
124+
expect(image.bands).to eq(real.bands)
125+
expect(image.avg).to eq(real.avg)
123126
end
124127

125128
it "on_seek is optional" do
126129
file = File.open simg("wagon.jpg"), "rb"
127130
source = Vips::SourceCustom.new
128131
source.on_read { |length| file.read length }
129132
image = Vips::Image.new_from_source source, ""
133+
real = Vips::Image.new_from_file simg("wagon.jpg")
130134

131135
expect(image)
132-
expect(image.width).to eq(685)
133-
expect(image.height).to eq(478)
134-
expect(image.bands).to eq(3)
135-
expect(image.avg).to be_within(0.001).of(109.789)
136+
expect(image.width).to eq(real.width)
137+
expect(image.height).to eq(real.height)
138+
expect(image.bands).to eq(real.bands)
139+
expect(image.avg).to eq(real.avg)
136140
end
137141

138-
it "can create a user output stream" do
142+
it "can create a custom target" do
139143
target = Vips::TargetCustom.new
140144

141145
expect(target)
142146
end
143147

144-
it "can write an image to a user output stream" do
148+
it "can write an image to a custom target" do
145149
filename = timg("x5.png")
146150
file = File.open filename, "wb"
147151
target = Vips::TargetCustom.new
148152
target.on_write { |chunk| file.write(chunk) }
149-
target.on_finish { file.close }
153+
target.on_end { file.close }
150154
image = Vips::Image.new_from_file simg("wagon.jpg")
151155
image.write_to_target target, ".png"
152156

153157
image = Vips::Image.new_from_file filename
158+
real = Vips::Image.new_from_file simg("wagon.jpg")
154159
expect(image)
155-
expect(image.width).to eq(685)
156-
expect(image.height).to eq(478)
157-
expect(image.bands).to eq(3)
158-
expect(image.avg).to be_within(0.001).of(109.789)
160+
expect(image.width).to eq(real.width)
161+
expect(image.height).to eq(real.height)
162+
expect(image.bands).to eq(real.bands)
163+
expect(image.avg).to eq(real.avg)
164+
end
165+
end
166+
167+
RSpec.describe Vips::TargetCustom, version: [8, 13] do
168+
it "can write to a custom target as TIFF" do
169+
filename = timg("x6.tif")
170+
file = File.open filename, "w+b"
171+
target = Vips::TargetCustom.new
172+
target.on_write { |chunk| file.write(chunk) }
173+
target.on_read { |length| file.read length }
174+
target.on_seek { |offset, whence| file.seek(offset, whence) }
175+
target.on_end { file.close; 0 }
176+
image = Vips::Image.new_from_file simg("wagon.jpg")
177+
image.write_to_target target, ".tif"
178+
179+
image = Vips::Image.new_from_file filename
180+
real = Vips::Image.new_from_file simg("wagon.jpg")
181+
expect(image)
182+
expect(image.width).to eq(real.width)
183+
expect(image.height).to eq(real.height)
184+
expect(image.bands).to eq(real.bands)
185+
expect(image.avg).to eq(real.avg)
159186
end
160187
end

0 commit comments

Comments
 (0)