Skip to content

Commit 7d6cd79

Browse files
committed
Add true streaming (initial commit)
Tests are still missing. It works with luajit, but produces slightly wrong images with Lua 5.2, 5.3 and crashes with Lua 5.4
1 parent 2f345ea commit 7d6cd79

9 files changed

+244
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to `lua-vips` will be documented in this file.
44

55
# master
66

7+
- add `vips.Connection`, `vips.Source` and `vips.Target` for true streaming support [rolandlo]
8+
79
# 1.1-11 - 2024-04-16
810

911
- add standard Lua support [rolandlo]

lua-vips-1.1-11.rockspec

+4-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ build = {
4747
["vips.voperation"] = "src/vips/voperation.lua",
4848
["vips.Image"] = "src/vips/Image.lua",
4949
["vips.Image_methods"] = "src/vips/Image_methods.lua",
50-
["vips.Interpolate"] = "src/vips/Interpolate.lua"
50+
["vips.Interpolate"] = "src/vips/Interpolate.lua",
51+
["vips.Connection"] = "src/vips/Connection.lua",
52+
["vips.Source"] = "src/vips/Source.lua",
53+
["vips.Target"] = "src/vips/Target.lua",
5154
}
5255
}

src/vips.lua

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ local vips = {
2323
voperation = require "vips.voperation",
2424
Image = require "vips.Image_methods",
2525
Interpolate = require "vips.Interpolate",
26+
Connection = require "vips.Connection",
27+
Source = require "vips.Source",
28+
Target = require "vips.Target",
2629
}
2730

2831
function vips.leak_set(leak)

src/vips/Connection.lua

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
-- abstract base Connection class
2+
3+
local ffi = require "ffi"
4+
5+
local vobject = require "vips.vobject"
6+
7+
local vips_lib = ffi.load(ffi.os == "Windows" and "libvips-42.dll" or "vips")
8+
9+
local Connection = {}
10+
11+
Connection.vobject = function(self)
12+
return ffi.cast(vobject.typeof, self)
13+
end
14+
15+
Connection.new = function(self)
16+
return vobject.new(self)
17+
end
18+
Connection.filename = function(self)
19+
-- Get the filename asscoiated with a connection. Return nil if there is no associated file.
20+
local so = ffi.cast('VipsConnection *', self.pointer)
21+
local filename = vips_lib.vips_connection_filename(so)
22+
if filename == ffi.NULL then
23+
return nil
24+
else
25+
return ffi.string(filename)
26+
end
27+
end
28+
29+
Connection.nick = function(self)
30+
-- Make a human-readable name for a connection suitable for error messages.
31+
32+
local so = ffi.cast('VipsConnection *', self.pointer)
33+
local nick = vips_lib.vips_connection_nick(so)
34+
if nick == ffi.NULL then
35+
return nil
36+
else
37+
return ffi.string(nick)
38+
end
39+
end
40+
41+
return ffi.metatype("VipsConnection", {
42+
__index = Connection
43+
})

src/vips/Image_methods.lua

+19
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,14 @@ function Image.new_from_image(base_image, value)
218218
return image
219219
end
220220

221+
function Image.new_from_source(source, options, ...)
222+
local name = vips_lib.vips_foreign_find_load_source(source.vobject)
223+
if name == ffi.NULL then
224+
error("Unable to load from source")
225+
end
226+
227+
return voperation.call(ffi.string(name), options, source, unpack { ... })
228+
end
221229
-- overloads
222230

223231
function Image.mt.__add(a, b)
@@ -413,6 +421,17 @@ function Image_method:write_to_memory_ptr()
413421
return ffi.gc(vips_memory, glib_lib.g_free), tonumber(psize[0])
414422
end
415423

424+
function Image_method:write_to_target(target, format_string, ...)
425+
collectgarbage("stop")
426+
local options = to_string_copy(vips_lib.vips_filename_get_options(format_string))
427+
local name = vips_lib.vips_foreign_find_save_target(format_string)
428+
collectgarbage("restart")
429+
if name == ffi.NULL then
430+
error(verror.get())
431+
end
432+
433+
return voperation.call(ffi.string(name), options, self, target, unpack { ... })
434+
end
416435
-- get/set metadata
417436

418437
function Image_method:get_typeof(name)

src/vips/Source.lua

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- An input connection
2+
3+
local ffi = require "ffi"
4+
5+
local verror = require "vips.verror"
6+
local vobject = require "vips.vobject"
7+
local Connection = require "vips.Connection"
8+
9+
local vips_lib = ffi.load(ffi.os == "Windows" and "libvips-42.dll" or "vips")
10+
11+
local Source = {}
12+
13+
Source.vobject = function(self)
14+
return ffi.cast(vobject.typeof, self)
15+
end
16+
17+
Source.new_from_descriptor = function(descriptor)
18+
local source = vips_lib.vips_source_new_from_descriptor(descriptor)
19+
if source == ffi.NULL then
20+
error("Can't create source from descriptor " .. descriptor .. "\n" .. verror.get())
21+
end
22+
23+
return Connection.new(source)
24+
end
25+
26+
Source.new_from_file = function(filename)
27+
local source = vips_lib.vips_source_new_from_file(filename)
28+
if source == ffi.NULL then
29+
error("Can't create source from filename " .. filename .. "\n" .. verror.get())
30+
end
31+
32+
return Connection.new(source)
33+
end
34+
35+
Source.new_from_memory = function(data) -- data is an FFI memory array formatted as a C-style array
36+
local source = vips_lib.vips_source_new_from_memory(data, ffi.sizeof(data))
37+
if source == ffi.NULL then
38+
error("Can't create input source from memory \n" .. verror.get())
39+
end
40+
41+
return Connection.new(source)
42+
end
43+
44+
return ffi.metatype("VipsSource", {
45+
__index = Source
46+
})

src/vips/Target.lua

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- An input connection
2+
3+
local ffi = require "ffi"
4+
5+
local vobject = require "vips.vobject"
6+
local Connection = require "vips.Connection"
7+
8+
local vips_lib = ffi.load(ffi.os == "Windows" and "libvips-42.dll" or "vips")
9+
10+
local Target = {}
11+
12+
Target.vobject = function(self)
13+
return ffi.cast(vobject.typeof, self)
14+
end
15+
16+
Target.new_to_descriptor = function(descriptor)
17+
local target = vips_lib.vips_target_new_to_descriptor(descriptor)
18+
if target == ffi.NULL then
19+
error("can't create output target from descriptor " .. descriptor)
20+
else
21+
return Connection.new(target)
22+
end
23+
end
24+
25+
Target.new_to_file = function(filename)
26+
local target = vips_lib.vips_target_new_to_file(filename)
27+
if target == ffi.NULL then
28+
error("can't create output target from filename " .. filename)
29+
else
30+
return Connection.new(target)
31+
end
32+
end
33+
34+
Target.new_to_memory = function()
35+
local target = vips_lib.vips_target_new_to_memory()
36+
if target == ffi.NULL then
37+
error("can't create output target from memory")
38+
else
39+
return Connection.new(target)
40+
end
41+
end
42+
43+
return ffi.metatype("VipsTarget", {
44+
__index = Target
45+
})

src/vips/cdefs.lua

+61
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,71 @@ ffi.cdef [[
166166
// opaque
167167
} VipsImage;
168168

169+
typedef struct _VipsConnection {
170+
VipsObject parent_instance;
171+
172+
// opaque
173+
} VipsConnection;
174+
175+
const char *vips_connection_filename (VipsConnection *connection);
176+
const char *vips_connection_nick (VipsConnection *connection);
177+
178+
typedef struct _VipsSource {
179+
VipsConnection parent_instance;
180+
181+
// opaque
182+
} VipsSource;
183+
184+
typedef struct _VipsTarget {
185+
VipsConnection parent_instance;
186+
187+
// opaque
188+
} VipsTarget;
189+
190+
VipsSource *vips_source_new_from_descriptor (int descriptor);
191+
VipsSource *vips_source_new_from_file (const char *filename);
192+
// VipsSource *vips_source_new_from_blob (VipsBlob *blob);
193+
// VipsSource *vips_source_new_from_target (VipsTarget *target);
194+
VipsSource *vips_source_new_from_memory (const void *data, size_t size);
195+
// VipsSource *vips_source_new_from_options (const char *options);
196+
// void vips_source_minimise (VipsSource *source);
197+
// int vips_source_decode (VipsSource *source);
198+
// gint64 vips_source_read (VipsSource *source, void *data, size_t length);
199+
// gboolean vips_source_is_mappable (VipsSource *source);
200+
// gboolean vips_source_is_file (VipsSource *source);
201+
// const void *vips_source_map (VipsSource *source, size_t *length);
202+
// VipsBlob *vips_source_map_blob (VipsSource *source);
203+
// gint64 vips_source_seek (VipsSource *source, gint64 offset, int whence);
204+
// int vips_source_rewind (VipsSource *source);
205+
// gint64 vips_source_sniff_at_most (VipsSource *source, unsigned char **data, size_t length);
206+
// unsigned char *vips_source_sniff (VipsSource *source, size_t length);
207+
// gint64 vips_source_length (VipsSource *source);
208+
// VipsSourceCustom *vips_source_custom_new (void);
209+
// GInputStream *vips_g_input_stream_new_from_source (VipsSource *source);
210+
// VipsSourceGInputStream *vips_source_g_input_stream_new (GInputStream *stream);
211+
212+
VipsTarget *vips_target_new_to_descriptor (int descriptor);
213+
VipsTarget *vips_target_new_to_file (const char *filename);
214+
VipsTarget *vips_target_new_to_memory (void);
215+
// VipsTarget *vips_target_new_temp (VipsTarget *target);
216+
// int vips_target_write (VipsTarget *target, const void *data, size_t length);
217+
// gint64 vips_target_read (VipsTarget *target, void *buffer, size_t length);
218+
// gint64 vips_target_seek (VipsTarget *target, gint64 offset, int whence);
219+
// int vips_target_end (VipsTarget *target);
220+
// unsigned char *vips_target_steal (VipsTarget *target, size_t *length);
221+
// char *vips_target_steal_text (VipsTarget *target);
222+
// int vips_target_putc (VipsTarget *target, int ch);
223+
// int vips_target_writes (VipsTarget *target, const char *str);
224+
// int vips_target_writef (VipsTarget *target, const char *fmt, ...);
225+
// int vips_target_write_amp (VipsTarget *target, const char *str);
226+
// VipsTargetCustom *vips_target_custom_new (void);
227+
169228
const char *vips_foreign_find_load (const char *name);
170229
const char *vips_foreign_find_load_buffer (const void *data, size_t size);
171230
const char *vips_foreign_find_save (const char *name);
172231
const char *vips_foreign_find_save_buffer (const char *suffix);
232+
const char* vips_foreign_find_load_source (VipsSource *source);
233+
const char* vips_foreign_find_save_target (const char* suffix);
173234

174235
VipsImage *vips_image_new_matrix_from_array (int width, int height,
175236
const double *array, int size);

src/vips/gvalue.lua

+21
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ gvalue.double_arr_typeof = ffi.typeof("const double[?]")
4040
gvalue.psize_typeof = ffi.typeof("size_t[?]")
4141
gvalue.mem_typeof = ffi.typeof("unsigned char[?]")
4242
gvalue.interpolate_typeof = ffi.typeof("VipsInterpolate*")
43+
gvalue.connection_typeof = ffi.typeof("VipsConnection*")
44+
gvalue.source_typeof = ffi.typeof("VipsSource*")
45+
gvalue.target_typeof = ffi.typeof("VipsTarget*")
4346

4447
-- look up some common gtypes at init for speed
4548
gvalue.gbool_type = gobject_lib.g_type_from_name("gboolean")
@@ -57,6 +60,9 @@ gvalue.blob_type = gobject_lib.g_type_from_name("VipsBlob")
5760
gvalue.band_format_type = gobject_lib.g_type_from_name("VipsBandFormat")
5861
gvalue.blend_mode_type = version.at_least(8, 6) and gobject_lib.g_type_from_name("VipsBlendMode") or 0
5962
gvalue.interpolate_type = gobject_lib.g_type_from_name("VipsInterpolate")
63+
gvalue.connection_type = gobject_lib.g_type_from_name("VipsConnection")
64+
gvalue.source_type = gobject_lib.g_type_from_name("VipsSource")
65+
gvalue.target_type = gobject_lib.g_type_from_name("VipsTarget")
6066

6167
-- gvalue.*_type can be of type cdata or number depending on the OS and Lua version
6268
-- gtypes as returned by vips_lib can also be of type cdata or number
@@ -159,6 +165,12 @@ gvalue.set = function(gv, value)
159165
end
160166
elseif gtype_comp == gvalue.interpolate_type then
161167
gobject_lib.g_value_set_object(gv, value)
168+
elseif gtype_comp == gvalue.connection_type then
169+
gobject_lib.g_value_set_object(gv, value)
170+
elseif gtype_comp == gvalue.source_type then
171+
gobject_lib.g_value_set_object(gv, value)
172+
elseif gtype_comp == gvalue.target_type then
173+
gobject_lib.g_value_set_object(gv, value)
162174
else
163175
error("unsupported gtype for set " .. gvalue.type_name(gtype))
164176
end
@@ -256,6 +268,15 @@ gvalue.get = function(gv)
256268
elseif gtype_comp == gvalue.interpolate_type then
257269
local vo = gobject_lib.g_value_get_object(gv)
258270
result = ffi.cast(gvalue.interpolate_typeof, vo)
271+
elseif gtype_comp == gvalue.connection_type then
272+
local vo = gobject_lib.g_value_get_object(gv)
273+
result = ffi.cast(gvalue.connection_typeof, vo)
274+
elseif gtype_comp == gvalue.source_type then
275+
local vo = gobject_lib.g_value_get_object(gv)
276+
result = ffi.cast(gvalue.source_typeof, vo)
277+
elseif gtype_comp == gvalue.target_type then
278+
local vo = gobject_lib.g_value_get_object(gv)
279+
result = ffi.cast(gvalue.target_typeof, vo)
259280
else
260281
error("unsupported gtype for get " .. gvalue.type_name(gtype))
261282
end

0 commit comments

Comments
 (0)