Skip to content

Commit

Permalink
Add true streaming (initial commit)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rolandlo committed Dec 23, 2024
1 parent 2f345ea commit 590afda
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to `lua-vips` will be documented in this file.

# master

- add `vips.Connection`, `vips.Source` and `vips.Target` for true streaming support [rolandlo]

# 1.1-11 - 2024-04-16

- add standard Lua support [rolandlo]
Expand Down
5 changes: 4 additions & 1 deletion lua-vips-1.1-11.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ build = {
["vips.voperation"] = "src/vips/voperation.lua",
["vips.Image"] = "src/vips/Image.lua",
["vips.Image_methods"] = "src/vips/Image_methods.lua",
["vips.Interpolate"] = "src/vips/Interpolate.lua"
["vips.Interpolate"] = "src/vips/Interpolate.lua",
["vips.Connection"] = "src/vips/Connection.lua",
["vips.Source"] = "src/vips/Source.lua",
["vips.Target"] = "src/vips/Target.lua",
}
}
3 changes: 3 additions & 0 deletions src/vips.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ local vips = {
voperation = require "vips.voperation",
Image = require "vips.Image_methods",
Interpolate = require "vips.Interpolate",
Connection = require "vips.Connection",
Source = require "vips.Source",
Target = require "vips.Target",
}

function vips.leak_set(leak)
Expand Down
43 changes: 43 additions & 0 deletions src/vips/Connection.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-- abstract base Connection class

local ffi = require "ffi"

local vobject = require "vips.vobject"

local vips_lib = ffi.load(ffi.os == "Windows" and "libvips-42.dll" or "vips")

local Connection = {}

Connection.vobject = function(self)
return ffi.cast(vobject.typeof, self)
end

Connection.new = function(self)
return vobject.new(self)
end
Connection.filename = function(self)
-- Get the filename asscoiated with a connection. Return nil if there is no associated file.
local so = ffi.cast('VipsConnection *', self.pointer)
local filename = vips_lib.vips_connection_filename(so)
if filename == ffi.NULL then
return nil
else
return ffi.string(filename)
end
end

Connection.nick = function(self)
-- Make a human-readable name for a connection suitable for error messages.

local so = ffi.cast('VipsConnection *', self.pointer)
local nick = vips_lib.vips_connection_nick(so)
if nick == ffi.NULL then
return nil
else
return ffi.string(nick)
end
end

return ffi.metatype("VipsConnection", {
__index = Connection
})
20 changes: 20 additions & 0 deletions src/vips/Image_methods.lua
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ function Image.new_from_image(base_image, value)
return image
end

function Image.new_from_source(source, options, ...)
local name = vips_lib.vips_foreign_find_load_source(source.vobject)
if name == ffi.NULL then
error("Unable to load from source")
end

return voperation.call(ffi.string(name), options, source, unpack { ... })
end
-- overloads

function Image.mt.__add(a, b)
Expand Down Expand Up @@ -413,6 +421,18 @@ function Image_method:write_to_memory_ptr()
return ffi.gc(vips_memory, glib_lib.g_free), tonumber(psize[0])
end

function Image_method:write_to_target(target, format_string, ...)
collectgarbage("stop")
local options = to_string_copy(vips_lib.vips_filename_get_options(format_string))
local name = vips_lib.vips_foreign_find_save_target(format_string)
collectgarbage("restart")
if name == ffi.NULL then
error(verror.get())
end
print(ffi.string(name), options, target)

return voperation.call(ffi.string(name), options, self, target, unpack { ... })
end
-- get/set metadata

function Image_method:get_typeof(name)
Expand Down
46 changes: 46 additions & 0 deletions src/vips/Source.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-- An input connection

local ffi = require "ffi"

local verror = require "vips.verror"
local vobject = require "vips.vobject"
local Connection = require "vips.Connection"

local vips_lib = ffi.load(ffi.os == "Windows" and "libvips-42.dll" or "vips")

local Source = {}

Source.vobject = function(self)
return ffi.cast(vobject.typeof, self)
end

Source.new_from_descriptor = function(descriptor)
local source = vips_lib.vips_source_new_from_descriptor(descriptor)
if source == ffi.NULL then
error("Can't create source from descriptor " .. descriptor .. "\n" .. verror.get())
end

return Connection.new(source)
end

Source.new_from_file = function(filename)
local source = vips_lib.vips_source_new_from_file(filename)
if source == ffi.NULL then
error("Can't create source from filename " .. filename .. "\n" .. verror.get())
end

return Connection.new(source)
end

Source.new_from_memory = function(data) -- data is an FFI memory array formatted as a C-style array
local source = vips_lib.vips_source_new_from_memory(data, ffi.sizeof(data))
if source == ffi.NULL then
error("Can't create input source from memory \n" .. verror.get())
end

return Connection.new(source)
end

return ffi.metatype("VipsSource", {
__index = Source
})
45 changes: 45 additions & 0 deletions src/vips/Target.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
-- An input connection

local ffi = require "ffi"

local vobject = require "vips.vobject"
local Connection = require "vips.Connection"

local vips_lib = ffi.load(ffi.os == "Windows" and "libvips-42.dll" or "vips")

local Target = {}

Target.vobject = function(self)
return ffi.cast(vobject.typeof, self)
end

Target.new_to_descriptor = function(descriptor)
local target = vips_lib.vips_target_new_to_descriptor(descriptor)
if target == ffi.NULL then
error("can't create output target from descriptor " .. descriptor)
else
return Connection.new(target)
end
end

Target.new_to_file = function(filename)
local target = vips_lib.vips_target_new_to_file(filename)
if target == ffi.NULL then
error("can't create output target from filename " .. filename)
else
return Connection.new(target)
end
end

Target.new_to_memory = function()
local target = vips_lib.vips_target_new_to_memory()
if target == ffi.NULL then
error("can't create output target from memory")
else
return Connection.new(target)
end
end

return ffi.metatype("VipsTarget", {
__index = Target
})
61 changes: 61 additions & 0 deletions src/vips/cdefs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,71 @@ ffi.cdef [[
// opaque
} VipsImage;

typedef struct _VipsConnection {
VipsObject parent_instance;

// opaque
} VipsConnection;

const char *vips_connection_filename (VipsConnection *connection);
const char *vips_connection_nick (VipsConnection *connection);

typedef struct _VipsSource {
VipsConnection parent_instance;

// opaque
} VipsSource;

typedef struct _VipsTarget {
VipsConnection parent_instance;

// opaque
} VipsTarget;

VipsSource *vips_source_new_from_descriptor (int descriptor);
VipsSource *vips_source_new_from_file (const char *filename);
// VipsSource *vips_source_new_from_blob (VipsBlob *blob);
// VipsSource *vips_source_new_from_target (VipsTarget *target);
VipsSource *vips_source_new_from_memory (const void *data, size_t size);
// VipsSource *vips_source_new_from_options (const char *options);
// void vips_source_minimise (VipsSource *source);
// int vips_source_decode (VipsSource *source);
// gint64 vips_source_read (VipsSource *source, void *data, size_t length);
// gboolean vips_source_is_mappable (VipsSource *source);
// gboolean vips_source_is_file (VipsSource *source);
// const void *vips_source_map (VipsSource *source, size_t *length);
// VipsBlob *vips_source_map_blob (VipsSource *source);
// gint64 vips_source_seek (VipsSource *source, gint64 offset, int whence);
// int vips_source_rewind (VipsSource *source);
// gint64 vips_source_sniff_at_most (VipsSource *source, unsigned char **data, size_t length);
// unsigned char *vips_source_sniff (VipsSource *source, size_t length);
// gint64 vips_source_length (VipsSource *source);
// VipsSourceCustom *vips_source_custom_new (void);
// GInputStream *vips_g_input_stream_new_from_source (VipsSource *source);
// VipsSourceGInputStream *vips_source_g_input_stream_new (GInputStream *stream);

VipsTarget *vips_target_new_to_descriptor (int descriptor);
VipsTarget *vips_target_new_to_file (const char *filename);
VipsTarget *vips_target_new_to_memory (void);
// VipsTarget *vips_target_new_temp (VipsTarget *target);
// int vips_target_write (VipsTarget *target, const void *data, size_t length);
// gint64 vips_target_read (VipsTarget *target, void *buffer, size_t length);
// gint64 vips_target_seek (VipsTarget *target, gint64 offset, int whence);
// int vips_target_end (VipsTarget *target);
// unsigned char *vips_target_steal (VipsTarget *target, size_t *length);
// char *vips_target_steal_text (VipsTarget *target);
// int vips_target_putc (VipsTarget *target, int ch);
// int vips_target_writes (VipsTarget *target, const char *str);
// int vips_target_writef (VipsTarget *target, const char *fmt, ...);
// int vips_target_write_amp (VipsTarget *target, const char *str);
// VipsTargetCustom *vips_target_custom_new (void);

const char *vips_foreign_find_load (const char *name);
const char *vips_foreign_find_load_buffer (const void *data, size_t size);
const char *vips_foreign_find_save (const char *name);
const char *vips_foreign_find_save_buffer (const char *suffix);
const char* vips_foreign_find_load_source (VipsSource *source);
const char* vips_foreign_find_save_target (const char* suffix);

VipsImage *vips_image_new_matrix_from_array (int width, int height,
const double *array, int size);
Expand Down
21 changes: 21 additions & 0 deletions src/vips/gvalue.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ gvalue.double_arr_typeof = ffi.typeof("const double[?]")
gvalue.psize_typeof = ffi.typeof("size_t[?]")
gvalue.mem_typeof = ffi.typeof("unsigned char[?]")
gvalue.interpolate_typeof = ffi.typeof("VipsInterpolate*")
gvalue.connection_typeof = ffi.typeof("VipsConnection*")
gvalue.source_typeof = ffi.typeof("VipsSource*")
gvalue.target_typeof = ffi.typeof("VipsTarget*")

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

-- gvalue.*_type can be of type cdata or number depending on the OS and Lua version
-- gtypes as returned by vips_lib can also be of type cdata or number
Expand Down Expand Up @@ -159,6 +165,12 @@ gvalue.set = function(gv, value)
end
elseif gtype_comp == gvalue.interpolate_type then
gobject_lib.g_value_set_object(gv, value)
elseif gtype_comp == gvalue.connection_type then
gobject_lib.g_value_set_object(gv, value)
elseif gtype_comp == gvalue.source_type then
gobject_lib.g_value_set_object(gv, value)
elseif gtype_comp == gvalue.target_type then
gobject_lib.g_value_set_object(gv, value)
else
error("unsupported gtype for set " .. gvalue.type_name(gtype))
end
Expand Down Expand Up @@ -256,6 +268,15 @@ gvalue.get = function(gv)
elseif gtype_comp == gvalue.interpolate_type then
local vo = gobject_lib.g_value_get_object(gv)
result = ffi.cast(gvalue.interpolate_typeof, vo)
elseif gtype_comp == gvalue.connection_type then
local vo = gobject_lib.g_value_get_object(gv)
result = ffi.cast(gvalue.connection_typeof, vo)
elseif gtype_comp == gvalue.source_type then
local vo = gobject_lib.g_value_get_object(gv)
result = ffi.cast(gvalue.source_typeof, vo)
elseif gtype_comp == gvalue.target_type then
local vo = gobject_lib.g_value_get_object(gv)
result = ffi.cast(gvalue.target_typeof, vo)
else
error("unsupported gtype for get " .. gvalue.type_name(gtype))
end
Expand Down

0 comments on commit 590afda

Please sign in to comment.