Load JPEG from source instead of memory

This commit is contained in:
Viktor Sokolov
2025-06-27 13:18:01 +02:00
parent 22305da24e
commit ec5e4b2aae
3 changed files with 162 additions and 1 deletions

View File

@@ -1116,3 +1116,87 @@ vips_cleanup()
vips_error_clear(); vips_error_clear();
vips_thread_shutdown(); 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;
}

View File

@@ -8,12 +8,14 @@ package vips
*/ */
import "C" import "C"
import ( import (
"bytes"
"context" "context"
"math" "math"
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
"runtime" "runtime"
"runtime/cgo"
"strings" "strings"
"sync" "sync"
"time" "time"
@@ -329,6 +331,49 @@ func (img *Image) Pages() int {
return p 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 { func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64, pages int) error {
if imgdata.Type == imagetype.ICO { if imgdata.Type == imagetype.ICO {
return img.loadIco(imgdata.Data, shrink, scale, pages) 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)) dataSize := C.size_t(len(imgdata.Data))
err := C.int(0) 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 { switch imgdata.Type {
case imagetype.JPEG: 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: case imagetype.JXL:
err = C.vips_jxlload_go(data, dataSize, C.int(pages), &tmp) err = C.vips_jxlload_go(data, dataSize, C.int(pages), &tmp)
case imagetype.PNG: case imagetype.PNG:
@@ -362,12 +411,15 @@ func (img *Image) Load(imgdata *imagedata.ImageData, shrink int, scale float64,
case imagetype.TIFF: case imagetype.TIFF:
err = C.vips_tiffload_go(data, dataSize, &tmp) err = C.vips_tiffload_go(data, dataSize, &tmp)
default: default:
C.close_source(nil, source)
return newVipsError("Usupported image type to load") return newVipsError("Usupported image type to load")
} }
if err != 0 { if err != 0 {
C.close_source(nil, source)
return Error() return Error()
} }
C.vips_attach_image_close_signals(&tmp, C.uintptr_t(handler), source)
C.swap_and_clear(&img.VipsImage, tmp) C.swap_and_clear(&img.VipsImage, tmp)
if imgdata.Type == imagetype.TIFF { if imgdata.Type == imagetype.TIFF {

View File

@@ -1,6 +1,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> // uintptr_t
#include <vips/vips.h> #include <vips/vips.h>
#include <vips/connection.h>
#include <vips/vips7compat.h> #include <vips/vips7compat.h>
#include <vips/vector.h> #include <vips/vector.h>
@@ -10,6 +12,17 @@ typedef struct _RGB {
double b; double b;
} RGB; } 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(); int vips_initialize();
void clear_image(VipsImage **in); 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); int vips_tiffsave_go(VipsImage *in, void **buf, size_t *len, int quality);
void vips_cleanup(); 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);