From 98be88bf802814c71fa00be081bcc3129ab2b77e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 28 Dec 2020 15:14:36 +0000 Subject: [PATCH] add sourceginput This is a source which wraps a GInputStream. You can use this to eg. efficiently load a PNG file from a GFile object. --- ChangeLog | 1 + libvips/include/vips/connection.h | 43 ++++++- libvips/iofuncs/Makefile.am | 1 + libvips/iofuncs/source.c | 3 +- libvips/iofuncs/sourceginput.c | 198 ++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 libvips/iofuncs/sourceginput.c diff --git a/ChangeLog b/ChangeLog index 4f97cd40..cee2e141 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,7 @@ - get pdfium load building again [Projkt-James] - add _source load support for pdfium - add "seed" param to perlin, worley and gaussnoise +- add vips_source_g_input_stream_new() to load images from a GInputStream 22/12/20 start 8.10.6 - don't seek on bad file descriptors [kleisauke] diff --git a/libvips/include/vips/connection.h b/libvips/include/vips/connection.h index 3f5ab5e3..c22f4461 100644 --- a/libvips/include/vips/connection.h +++ b/libvips/include/vips/connection.h @@ -265,8 +265,8 @@ typedef struct _VipsSourceCustomClass { GType vips_source_custom_get_type( void ); VipsSourceCustom *vips_source_custom_new( void ); -/* A GInputStream that's actually a VipsSource under the hood. This lets us - * hook librsvg up to libvips using the GInputStream interface. +/* A GInputStream that wraps a VipsSource. This lets us eg. + * hook librsvg up to libvips using their GInputStream interface. */ #define VIPS_TYPE_G_INPUT_STREAM (vips_g_input_stream_get_type()) @@ -284,8 +284,6 @@ VipsSourceCustom *vips_source_custom_new( void ); (G_TYPE_INSTANCE_GET_CLASS( (obj), \ VIPS_TYPE_G_INPUT_STREAM, VipsGInputStreamClass )) -/* GInputStream <--> VipsSource - */ typedef struct _VipsGInputStream { GInputStream parent_instance; @@ -304,6 +302,43 @@ typedef struct _VipsGInputStreamClass { GInputStream *vips_g_input_stream_new_from_source( VipsSource *source ); +/* A VipsSource that wraps a GInputStream. This lets us eg. load PNGs from + * GFile objects. + */ + +#define VIPS_TYPE_SOURCE_G_INPUT_STREAM (vips_source_g_input_stream_get_type()) +#define VIPS_SOURCE_G_INPUT_STREAM( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_SOURCE_G_INPUT_STREAM, VipsSourceGInputStream )) +#define VIPS_SOURCE_G_INPUT_STREAM_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_SOURCE_G_INPUT_STREAM, VipsSourceGInputStreamClass)) +#define VIPS_IS_SOURCE_G_INPUT_STREAM( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_SOURCE_G_INPUT_STREAM )) +#define VIPS_IS_SOURCE_G_INPUT_STREAM_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_SOURCE_G_INPUT_STREAM )) +#define VIPS_SOURCE_G_INPUT_STREAM_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_SOURCE_G_INPUT_STREAM, VipsSourceGInputStreamClass )) + +typedef struct _VipsSourceGInputStream { + VipsSource parent_instance; + + /*< private >*/ + + /* The GInputStream we wrap. + */ + GInputStream *stream; + +} VipsSourceGInputStream; + +typedef struct _VipsSourceGInputStreamClass { + VipsSourceClass parent_class; + +} VipsSourceGInputStreamClass; + +VipsSourceGInputStream *vips_source_g_input_stream_new( GInputStream *stream ); + #define VIPS_TYPE_TARGET (vips_target_get_type()) #define VIPS_TARGET( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index 91e6ddd8..ec598845 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -2,6 +2,7 @@ noinst_LTLIBRARIES = libiofuncs.la libiofuncs_la_SOURCES = \ ginputsource.c \ + sourceginput.c \ connection.c \ source.c \ sourcecustom.c \ diff --git a/libvips/iofuncs/source.c b/libvips/iofuncs/source.c index cc3764de..a18d9be1 100644 --- a/libvips/iofuncs/source.c +++ b/libvips/iofuncs/source.c @@ -354,7 +354,8 @@ vips_source_seek_real( VipsSource *source, gint64 offset, int whence ) * vips__seek() wrapper so we can seek long files on Windows. */ if( connection->descriptor != -1 ) - return( vips__seek_no_error( connection->descriptor, offset, whence ) ); + return( vips__seek_no_error( connection->descriptor, + offset, whence ) ); return( -1 ); } diff --git a/libvips/iofuncs/sourceginput.c b/libvips/iofuncs/sourceginput.c new file mode 100644 index 00000000..b33f8c37 --- /dev/null +++ b/libvips/iofuncs/sourceginput.c @@ -0,0 +1,198 @@ +/* A Source subclass with signals you can easily hook up to other input + * sources. + * + * J.Cupitt, 21/11/19 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* + */ +#define VIPS_DEBUG + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "vipsmarshal.h" + +G_DEFINE_TYPE( VipsSourceGInputStream, vips_source_g_input_stream, + VIPS_TYPE_SOURCE ); + +/* TODO: + * - can get get a fileno out? it'd be useful if we could make + * vips_source_map() work for eg. vips or ppm images + */ + +static gint64 +vips_source_g_input_stream_read( VipsSource *source, + void *buffer, size_t length ) +{ + VipsSourceGInputStream *source_ginput = + VIPS_SOURCE_G_INPUT_STREAM( source ); + GError *error = NULL; + + gint64 bytes_read; + + VIPS_DEBUG_MSG( "vips_source_g_input_stream_read: %zd bytes\n", + length ); + + /* Do we need to loop on this call? The docs are unclear. + */ + if( (bytes_read = g_input_stream_read( source_ginput->stream, + buffer, length, NULL, &error )) < 0 ) { + VIPS_DEBUG_MSG( " %s\n", error->message ); + vips_g_error( &error ); + return( -1 ); + } + + VIPS_DEBUG_MSG( " (returned %zd bytes)\n", bytes_read ); + + return( bytes_read ); +} + +static GSeekType +lseek_to_seek_type( int whence ) +{ + switch( whence ) { + default: + case SEEK_CUR: + return( G_SEEK_CUR ); + case SEEK_SET: + return( G_SEEK_SET ); + case SEEK_END: + return( G_SEEK_END ); + } +} + +static gint64 +vips_source_g_input_stream_seek( VipsSource *source, gint64 offset, int whence ) +{ + VipsSourceGInputStream *source_ginput = + VIPS_SOURCE_G_INPUT_STREAM( source ); + GSeekType type = lseek_to_seek_type( whence ); + GError *error = NULL; + + GSeekable *seekable; + gint64 new_position; + + VIPS_DEBUG_MSG( "vips_source_g_input_stream_seek: " + "offset = %zd, whence = %d\n", offset, whence ); + + if( !G_IS_SEEKABLE( source_ginput->stream ) ) + return( -1 ); + seekable = G_SEEKABLE( source_ginput->stream ); + if( !g_seekable_can_seek( seekable ) ) + return( -1 ); + + if( !g_seekable_seek( seekable, offset, type, NULL, &error ) ) { + vips_g_error( &error ); + return( -1 ); + } + + new_position = g_seekable_tell( seekable ); + + VIPS_DEBUG_MSG( " (new position = %zd)\n", new_position ); + + return( new_position ); +} + +static void +vips_source_g_input_stream_class_init( VipsSourceGInputStreamClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsSourceClass *source_class = VIPS_SOURCE_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "source_g_input_stream"; + object_class->description = _( "GInputStream source" ); + + source_class->read = vips_source_g_input_stream_read; + source_class->seek = vips_source_g_input_stream_seek; + + VIPS_ARG_OBJECT( class, "stream", 3, + _( "stream" ), + _( "GInputStream to read from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSourceGInputStream, stream ), + G_TYPE_INPUT_STREAM ); + +} + +static void +vips_source_g_input_stream_init( VipsSourceGInputStream *source ) +{ +} + +/** + * vips_source_g_input_stream_new: + * @stream: read from this stream + * + * Create a #VipsSourceGInputStream which wraps @stream. + * + * Returns: the new source. + */ +VipsSourceGInputStream * +vips_source_g_input_stream_new( GInputStream *stream ) +{ + VipsSourceGInputStream *source; + + VIPS_DEBUG_MSG( "vips_source_g_input_stream_new:\n" ); + + source = VIPS_SOURCE_G_INPUT_STREAM( + g_object_new( VIPS_TYPE_SOURCE_G_INPUT_STREAM, + "stream", stream, + NULL ) ); + + if( vips_object_build( VIPS_OBJECT( source ) ) ) { + VIPS_UNREF( source ); + return( NULL ); + } + + return( source ); +}