Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c9c7b56

Browse files
authored
Merge pull request #296 from sled/bugfix/sled-295-ffi-pointer-size
Fix FFI::Pointer handling in Image#new_from_memory and Image#new_from_memory_copy
2 parents fe124c4 + fed73ec commit c9c7b56

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

‎lib/vips/image.rb‎

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ def self.p2str(pointer)
7878
class Image < Vips::Object
7979
alias_method :parent_get_typeof, :get_typeof
8080

81+
# FFI sets a pointer's size to this magic value if the size of the memory
82+
# chunk the pointer points to is unknown to FFI.
83+
UNKNOWN_POINTER_SIZE = FFI::Pointer.new(1).size
84+
private_constant :UNKNOWN_POINTER_SIZE
85+
8186
private
8287

8388
# the layout of the VipsImage struct
@@ -326,6 +331,24 @@ def self.new_from_buffer data, option_string, **opts
326331
# image.width, image.height, image.bands, image.format
327332
# ```
328333
#
334+
# Creating a new image from a memory pointer:
335+
#
336+
# ```
337+
# ptr = FFI::MemoryPointer.new(:uchar, 10*10)
338+
# # => #<FFI::MemoryPointer address=0x00007fc236db31d0 size=100>
339+
# x = Vips::Image.new_from_memory(ptr, 10, 10, 1, :uchar)
340+
# ```
341+
#
342+
# Creating a new image from an address only pointer:
343+
#
344+
# ```
345+
# ptr = call_to_external_c_library(w: 10, h: 10)
346+
# # => #<FFI::Pointer address=0x00007f9780813a00>
347+
# ptr_slice = ptr.slice(0, 10*10)
348+
# # => #<FFI::Pointer address=0x00007f9780813a00 size=100>
349+
# x = Vips::Image.new_from_memory(ptr_slice, 10, 10, 1, :uchar)
350+
# ```
351+
#
329352
# {new_from_memory} keeps a reference to the array of pixels you pass in
330353
# to try to prevent that memory from being freed by the Ruby GC while it
331354
# is being used.
@@ -340,13 +363,23 @@ def self.new_from_buffer data, option_string, **opts
340363
# @param format [Symbol] band format
341364
# @return [Image] the loaded image
342365
def self.new_from_memory data, width, height, bands, format
343-
size = data.bytesize
344-
345366
# prevent data from being freed with JRuby FFI
346367
if defined?(JRUBY_VERSION) && !data.is_a?(FFI::Pointer)
347368
data = ::FFI::MemoryPointer.new(:char, data.bytesize).write_bytes data
348369
end
349370

371+
if data.is_a?(FFI::Pointer)
372+
# A pointer needs to know about the size of the memory it points to.
373+
# If you have an address-only pointer, use the .slice method to wrap
374+
# the pointer in a size aware pointer.
375+
if data.size == UNKNOWN_POINTER_SIZE
376+
raise Vips::Error, "size of memory is unknown"
377+
end
378+
size = data.size
379+
else
380+
size = data.bytesize
381+
end
382+
350383
format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
351384
vi = Vips.vips_image_new_from_memory data, size,
352385
width, height, bands, format_number
@@ -373,7 +406,17 @@ def self.new_from_memory data, width, height, bands, format
373406
# @return [Image] the loaded image
374407
def self.new_from_memory_copy data, width, height, bands, format
375408
format_number = GObject::GValue.from_nick BAND_FORMAT_TYPE, format
376-
vi = Vips.vips_image_new_from_memory_copy data, data.bytesize,
409+
410+
if data.is_a?(FFI::Pointer)
411+
if data.size == UNKNOWN_POINTER_SIZE
412+
raise Vips::Error, "size of memory is unknown"
413+
end
414+
size = data.size
415+
else
416+
size = data.bytesize
417+
end
418+
419+
vi = Vips.vips_image_new_from_memory_copy data, size,
377420
width, height, bands, format_number
378421
raise Vips::Error if vi.null?
379422
new(vi)

‎spec/image_spec.rb‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,47 @@ def has_jpeg?
4444
expect(x.avg).to eq(128)
4545
end
4646

47+
it "can load an image from memory by memory pointer" do
48+
data = FFI::MemoryPointer.new(:uchar, 16 * 16)
49+
data.put_array_of_uchar(0, Array.new(16 * 16, 128))
50+
51+
x = Vips::Image.new_from_memory data, 16, 16, 1, :uchar
52+
53+
# GC to try to trigger a segv if data hasn't been reffed by
54+
# new_from_memory
55+
GC.start
56+
57+
expect(x.width).to eq(16)
58+
expect(x.height).to eq(16)
59+
expect(x.bands).to eq(1)
60+
expect(x.avg).to eq(128)
61+
end
62+
63+
it "can load an image from memory by size aware address pointer" do
64+
memory = FFI::MemoryPointer.new(:uchar, 16 * 16)
65+
memory.put_array_of_uchar(0, Array.new(16 * 16, 128))
66+
67+
data = FFI::Pointer.new(memory)
68+
# JRuby's FFI implementation looses the size information
69+
data = data.slice(0, 16 * 16) if defined?(JRUBY_VERSION)
70+
71+
x = Vips::Image.new_from_memory data, 16, 16, 1, :uchar
72+
73+
# GC to try to trigger a segv if data hasn't been reffed by
74+
# new_from_memory
75+
GC.start
76+
77+
expect(x.width).to eq(16)
78+
expect(x.height).to eq(16)
79+
expect(x.bands).to eq(1)
80+
expect(x.avg).to eq(128)
81+
end
82+
83+
it "throws an error when trying to load an image from memory with unknown size" do
84+
data = FFI::Pointer.new(1)
85+
expect { Vips::Image.new_from_memory(data, 16, 16, 1, :uchar) }.to raise_error(Vips::Error)
86+
end
87+
4788
it "can load an image from memory and copy" do
4889
image = Vips::Image.black(16, 16) + 128
4990
data = image.write_to_memory
@@ -57,6 +98,39 @@ def has_jpeg?
5798
expect(x.avg).to eq(128)
5899
end
59100

101+
it "can load and copy an image from memory by memory pointer" do
102+
data = FFI::MemoryPointer.new(:uchar, 16 * 16)
103+
data.put_array_of_uchar(0, Array.new(16 * 16, 128))
104+
105+
x = Vips::Image.new_from_memory_copy data, 16, 16, 1, :uchar
106+
107+
expect(x.width).to eq(16)
108+
expect(x.height).to eq(16)
109+
expect(x.bands).to eq(1)
110+
expect(x.avg).to eq(128)
111+
end
112+
113+
it "can load and copy an image from memory by size aware address pointer" do
114+
memory = FFI::MemoryPointer.new(:uchar, 16 * 16)
115+
memory.put_array_of_uchar(0, Array.new(16 * 16, 128))
116+
117+
data = FFI::Pointer.new(memory)
118+
# JRuby's FFI implementation looses the size information
119+
data = data.slice(0, 16 * 16) if defined?(JRUBY_VERSION)
120+
121+
x = Vips::Image.new_from_memory_copy data, 16, 16, 1, :uchar
122+
123+
expect(x.width).to eq(16)
124+
expect(x.height).to eq(16)
125+
expect(x.bands).to eq(1)
126+
expect(x.avg).to eq(128)
127+
end
128+
129+
it "throws an error when trying to load and copy from memory with unknown size" do
130+
data = FFI::Pointer.new(1)
131+
expect { Vips::Image.new_from_memory_copy(data, 16, 16, 1, :uchar) }.to raise_error(Vips::Error)
132+
end
133+
60134
if has_jpeg?
61135
it "can save an image to a buffer" do
62136
image = Vips::Image.black(16, 16) + 128

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /