diff --git a/vips/vips.c b/vips/vips.c index 4f4e8912..def5e6d6 100644 --- a/vips/vips.c +++ b/vips/vips.c @@ -1116,3 +1116,87 @@ vips_cleanup() vips_error_clear(); vips_thread_shutdown(); } + +// --- async source ---------------------------------------------------------------------- + +// define glib subtype for vips async source +#define VIPS_TYPE_ASYNC_SOURCE (vips_async_source_get_type()) +G_DEFINE_FINAL_TYPE(VipsAsyncSource, vips_async_source, VIPS_TYPE_SOURCE) + +extern void closeAsyncReader(uintptr_t handle); +extern gint64 asyncReaderSeek(uintptr_t handle, gint64 offset, int whence); +extern gint64 asyncReaderRead(uintptr_t handle, gpointer buffer, gint64 size); + +// loads jpeg from a source +int +vips_jpegloadsource_go(VipsAsyncSource *source, int shrink, VipsImage **out) +{ + if (shrink > 1) + return vips_jpegload_source(VIPS_SOURCE(source), out, "shrink", shrink, + NULL); + + return vips_jpegload_source(VIPS_SOURCE(source), out, NULL); +} + +// dereferences source +void +close_source(VipsImage **in, VipsAsyncSource *source) +{ + uintptr_t readerHandle = source->readerHandle; + VIPS_UNREF(source); + closeAsyncReader(readerHandle); +} + +// attaches close signals to the image. first signal closes it's source, second closes the reader. +void +vips_attach_image_close_signals(VipsImage **in, uintptr_t handle, VipsAsyncSource *source) +{ + g_signal_connect(*in, "close", G_CALLBACK(close_source), (void *) source); +} + +// read function for vips async source +static gint64 +vips_async_source_read(VipsSource *source, void *buffer, size_t length) +{ + VipsAsyncSource *self = (VipsAsyncSource *) source; + + return asyncReaderRead(self->readerHandle, buffer, length); +} + +// seek function for vips async source. whence can be SEEK_SET (0), SEEK_CUR (1), or SEEK_END (2). +static gint64 +vips_async_source_seek(VipsSource *source, gint64 offset, int whence) +{ + VipsAsyncSource *self = (VipsAsyncSource *) source; + + return asyncReaderSeek(self->readerHandle, offset, whence); +} + +// attaches seek/read handlers to the async source class +static void +vips_async_source_class_init(VipsAsyncSourceClass *klass) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS(klass); + VipsSourceClass *source_class = VIPS_SOURCE_CLASS(klass); + + object_class->nickname = "async_source"; + object_class->description = "async input source"; + + source_class->read = vips_async_source_read; + source_class->seek = vips_async_source_seek; +} + +// initializes the async source (nothing to do here yet) +static void +vips_async_source_init(VipsAsyncSource *source) +{ +} + +// creates a new async source with the given reader handle +VipsAsyncSource * +vips_new_async_source(uintptr_t readerHandle) +{ + VipsAsyncSource *source = g_object_new(vips_async_source_get_type(), NULL); + source->readerHandle = readerHandle; + return source; +} diff --git a/vips/vips.go b/vips/vips.go index 108c1794..9548d799 100644 --- a/vips/vips.go +++ b/vips/vips.go @@ -8,12 +8,14 @@ package vips */ import "C" import ( + "bytes" "context" "math" "net/http" "os" "regexp" "runtime" + "runtime/cgo" "strings" "sync" "time" @@ -329,6 +331,49 @@ func (img *Image) Pages() int { return p } +//export closeAsyncReader +func closeAsyncReader(handle C.uintptr_t) { + h := cgo.Handle(handle) + h.Delete() +} + +// calls seek() on the async reader via it's handle from the C side +// +//export asyncReaderSeek +func asyncReaderSeek(handle C.uintptr_t, offset C.int64_t, whence int) C.int64_t { + h := cgo.Handle(handle) + reader, ok := h.Value().(*bytes.Reader) + if !ok { + return -1 + } + + pos, err := reader.Seek(int64(offset), whence) + if err != nil { + return -1 + } + + return C.int64_t(pos) +} + +// calls read() on the async reader via it's handle from the C side +// +//export asyncReaderRead +func asyncReaderRead(handle C.uintptr_t, pointer unsafe.Pointer, size C.int64_t) C.int64_t { + h := cgo.Handle(handle) + reader, ok := h.Value().(*bytes.Reader) + if !ok { + return -1 + } + + buf := unsafe.Slice((*byte)(pointer), size) + n, err := reader.Read(buf) + if err != nil { + return -1 + } + + return C.int64_t(n) +} + func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64, pages int) error { if imgdata.Type == imagetype.ICO { return img.loadIco(imgdata.Data, shrink, scale, pages) @@ -344,9 +389,13 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64, dataSize := C.size_t(len(imgdata.Data)) err := C.int(0) + reader := bytes.NewReader(imgdata.Data) + handler := cgo.NewHandle(reader) + source := C.vips_new_async_source(C.uintptr_t(handler)) + switch imgdata.Type { case imagetype.JPEG: - err = C.vips_jpegload_go(data, dataSize, C.int(shrink), &tmp) + err = C.vips_jpegloadsource_go(source, C.int(shrink), &tmp) case imagetype.JXL: err = C.vips_jxlload_go(data, dataSize, C.int(pages), &tmp) case imagetype.PNG: @@ -362,12 +411,15 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64, case imagetype.TIFF: err = C.vips_tiffload_go(data, dataSize, &tmp) default: + C.close_source(nil, source) return newVipsError("Usupported image type to load") } if err != 0 { + C.close_source(nil, source) return Error() } + C.vips_attach_image_close_signals(&tmp, C.uintptr_t(handler), source) C.swap_and_clear(&img.VipsImage, tmp) if imgdata.Type == imagetype.TIFF { diff --git a/vips/vips.h b/vips/vips.h index c5149e13..945f4c0b 100644 --- a/vips/vips.h +++ b/vips/vips.h @@ -1,6 +1,8 @@ #include +#include // uintptr_t #include +#include #include #include @@ -10,6 +12,17 @@ typedef struct _RGB { double b; } RGB; +// vips async source +typedef struct _VipsAsyncSource { + VipsSourceCustom source; // class designator + uintptr_t readerHandle; // async reader handler +} VipsAsyncSource; + +// glib class for vips async source +typedef struct _VipsAsyncSourceClass { + VipsSourceCustomClass parent_class; +} VipsAsyncSourceClass; + int vips_initialize(); void clear_image(VipsImage **in); @@ -99,3 +112,15 @@ int vips_avifsave_go(VipsImage *in, void **buf, size_t *len, int quality, int sp int vips_tiffsave_go(VipsImage *in, void **buf, size_t *len, int quality); void vips_cleanup(); + +// vips async source read function +int vips_jpegloadsource_go(VipsAsyncSource *source, int shrink, VipsImage **out); + +// creates new vips async source from a reader handle +VipsAsyncSource *vips_new_async_source(uintptr_t readerHandle); + +// attaches "close" signal to the vips image: closes reader and unrefs vips source +void vips_attach_image_close_signals(VipsImage **in, uintptr_t handle, VipsAsyncSource *source); + +// closes source and corresponding reader +void close_source(VipsImage **in, VipsAsyncSource *source);