add thumbnail_stream

this works:

	$ cat k2.jpg | vips thumbnail_stream [descriptor=0] x.jpg 200
	$ vipsheader x.jpg
	temp-0: 141x200 uchar, 3 bands, srgb, jpegload
This commit is contained in:
John Cupitt 2019-10-12 17:37:31 +01:00
parent 4d3f66fe33
commit 5f6911d516
7 changed files with 248 additions and 36 deletions

View File

@ -79,6 +79,9 @@ int vips_thumbnail_buffer( void *buf, size_t len, VipsImage **out,
__attribute__((sentinel));
int vips_thumbnail_image( VipsImage *in, VipsImage **out, int width, ... )
__attribute__((sentinel));
int vips_thumbnail_stream( VipsStreamInput *input, VipsImage **out,
int width, ... )
__attribute__((sentinel));
int vips_similarity( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -171,6 +171,7 @@ VipsStreamInput *vips_stream_input_new_from_filename( const char *filename );
VipsStreamInput *vips_stream_input_new_from_blob( VipsBlob *blob );
VipsStreamInput *vips_stream_input_new_from_memory( const void *data,
size_t size );
VipsStreamInput *vips_stream_input_new_from_options( const char *options );
ssize_t vips_stream_input_read( VipsStreamInput *input,
unsigned char *data, size_t length );

View File

@ -1889,18 +1889,11 @@ vips_object_set_argument_from_string( VipsObject *object,
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
if( !value ) {
vips_object_no_value( object, name );
return( -1 );
}
flags = 0;
if( VIPS_IS_OPERATION( object ) )
flags = vips_operation_get_flags(
VIPS_OPERATION( object ) );
/* Read the filename.
*/
if( flags &
(VIPS_OPERATION_SEQUENTIAL_UNBUFFERED |
VIPS_OPERATION_SEQUENTIAL) )
@ -1908,6 +1901,10 @@ vips_object_set_argument_from_string( VipsObject *object,
else
access = VIPS_ACCESS_RANDOM;
if( !value ) {
vips_object_no_value( object, name );
return( -1 );
}
vips__filename_split8( value, filename, option_string );
if( strcmp( "stdin", filename ) == 0 ) {
@ -1940,6 +1937,25 @@ vips_object_set_argument_from_string( VipsObject *object,
*/
g_object_unref( out );
}
else if( g_type_is_a( otype, VIPS_TYPE_STREAM_INPUT ) ) {
VipsStreamInput *input;
if( !value ) {
vips_object_no_value( object, name );
return( -1 );
}
if( !(input = vips_stream_input_new_from_options( value )) )
return( -1 );
g_value_init( &gvalue, VIPS_TYPE_STREAM_INPUT );
g_value_set_object( &gvalue, input );
/* Setting gvalue will have upped @out's count again,
* go back to 1 so that gvalue has the only ref.
*/
g_object_unref( input );
}
else if( g_type_is_a( otype, VIPS_TYPE_ARRAY_IMAGE ) ) {
/* We have to have a special case for this, we can't just rely
* on transform_g_string_array_image(), since we need to be

View File

@ -1245,7 +1245,8 @@ typedef struct _VipsCall {
static const char *
vips_call_get_arg( VipsCall *call, int i )
{
if( i < 0 || i >= call->argc ) {
if( i < 0 ||
i >= call->argc ) {
vips_error( VIPS_OBJECT_GET_CLASS( call->operation )->nickname,
"%s", _( "too few arguments" ) );
return( NULL );

View File

@ -33,6 +33,7 @@
/* TODO
*
* - minimise support, and reenable jpg test_descriptors
* - filename encoding
* - memory output
* - add mmapable descriptors
@ -322,7 +323,7 @@ vips_stream_input_class_init( VipsStreamInputClass *class )
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "input";
object_class->nickname = "stream_input";
object_class->description = _( "input stream" );
object_class->build = vips_stream_input_build;
@ -416,67 +417,84 @@ vips_stream_input_new_from_filename( const char *filename )
*
* Create a stream attached to an area of memory.
*
* #VipsStream s start out empty, you need to call
* vips_stream_input_refill() to fill them with bytes.
*
* See also: vips_stream_input_refill().
*
* Returns: a new #VipsStream
*/
VipsStreamInput *
vips_stream_input_new_from_blob( VipsBlob *blob )
{
VipsStreamInput *stream;
VipsStreamInput *input;
VIPS_DEBUG_MSG( "vips_stream_input_new_from_blob: %p\n", blob );
stream = VIPS_STREAM_INPUT(
input = VIPS_STREAM_INPUT(
g_object_new( VIPS_TYPE_STREAM_INPUT,
"blob", blob,
NULL ) );
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
VIPS_UNREF( stream );
if( vips_object_build( VIPS_OBJECT( input ) ) ) {
VIPS_UNREF( input );
return( NULL );
}
return( stream );
return( input );
}
/**
* vips_stream_input_new_from_memory:
* @buf: memory area to load
* @len: size of memory area
* @data: memory area to load
* @length: size of memory area
*
* Create a stream attached to an area of memory.
*
* You must not free @buf while the stream is active.
*
* #VipsStream s start out empty, you need to call
* vips_stream_input_refill() to fill them with bytes.
*
* See also: vips_stream_input_refill().
* You must not free @data while the stream is active.
*
* Returns: a new #VipsStream
*/
VipsStreamInput *
vips_stream_input_new_from_memory( const void *data, size_t size )
vips_stream_input_new_from_memory( const void *data, size_t length )
{
VipsStreamInput *stream;
VipsStreamInput *input;
VipsBlob *blob;
VIPS_DEBUG_MSG( "vips_stream_input_new_from_buffer: %p, size = %zd\n",
data, size );
VIPS_DEBUG_MSG( "vips_stream_input_new_from_buffer: "
"%p, length = %zd\n", data, length );
/* We don't take a copy of the data or free it.
*/
blob = vips_blob_new( NULL, data, size );
blob = vips_blob_new( NULL, data, length );
stream = vips_stream_input_new_from_blob( blob );
input = vips_stream_input_new_from_blob( blob );
vips_area_unref( VIPS_AREA( blob ) );
return( stream );
return( input );
}
/**
* vips_stream_input_new_from_options:
* @options: option string
*
* Create a stream from an option string.
*
* Returns: a new #VipsStream
*/
VipsStreamInput *
vips_stream_input_new_from_options( const char *options )
{
VipsStreamInput *input;
VIPS_DEBUG_MSG( "vips_stream_input_new_from_options: %s\n", options );
input = VIPS_STREAM_INPUT(
g_object_new( VIPS_TYPE_STREAM_INPUT, NULL ) );
if( vips_object_set_from_string( VIPS_OBJECT( input ), options ) ||
vips_object_build( VIPS_OBJECT( input ) ) ) {
VIPS_UNREF( input );
return( NULL );
}
return( input );
}
ssize_t
@ -696,7 +714,7 @@ vips_stream_output_class_init( VipsStreamOutputClass *class )
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "output";
object_class->nickname = "stream_output";
object_class->description = _( "output stream" );
object_class->build = vips_stream_output_build;
@ -706,7 +724,7 @@ vips_stream_output_class_init( VipsStreamOutputClass *class )
/* SET_ALWAYS means that blob is set by C and the obj system is not
* involved in creation or destruction. It can be read at any time.
*/
VIPS_ARG_BOXED( class, "blob", 1,
VIPS_ARG_BOXED( class, "blob", 3,
_( "Blob" ),
_( "Blob to save to" ),
VIPS_ARGUMENT_SET_ALWAYS,

View File

@ -157,6 +157,7 @@ vips_resample_operation_init( void )
extern GType vips_thumbnail_file_get_type( void );
extern GType vips_thumbnail_buffer_get_type( void );
extern GType vips_thumbnail_image_get_type( void );
extern GType vips_thumbnail_stream_get_type( void );
extern GType vips_mapim_get_type( void );
extern GType vips_shrink_get_type( void );
extern GType vips_shrinkh_get_type( void );
@ -173,6 +174,7 @@ vips_resample_operation_init( void )
vips_thumbnail_file_get_type();
vips_thumbnail_buffer_get_type();
vips_thumbnail_image_get_type();
vips_thumbnail_stream_get_type();
vips_mapim_get_type();
vips_shrink_get_type();
vips_shrinkh_get_type();

View File

@ -26,6 +26,8 @@
* - prevent over-pre-shrink in thumbnail
* 30/9/19
* - smarter heif thumbnail selection
* 12/10/19
* - add thumbnail_stream
*/
/*
@ -1249,6 +1251,175 @@ vips_thumbnail_buffer( void *buf, size_t len, VipsImage **out, int width, ... )
return( result );
}
typedef struct _VipsThumbnailStream {
VipsThumbnail parent_object;
VipsStreamInput *input;
char *option_string;
} VipsThumbnailStream;
typedef VipsThumbnailClass VipsThumbnailStreamClass;
G_DEFINE_TYPE( VipsThumbnailStream, vips_thumbnail_stream,
vips_thumbnail_get_type() );
/* Get the info from a stream.
*/
static int
vips_thumbnail_stream_get_info( VipsThumbnail *thumbnail )
{
VipsThumbnailStream *stream = (VipsThumbnailStream *) thumbnail;
VipsImage *image;
g_info( "thumbnailing stream" );
if( !(thumbnail->loader = vips_foreign_find_load_stream(
stream->input )) ||
!(image = vips_image_new_from_stream( stream->input,
stream->option_string, NULL )) )
return( -1 );
vips_thumbnail_read_header( thumbnail, image );
g_object_unref( image );
return( 0 );
}
/* Open an image, scaling as appropriate.
*/
static VipsImage *
vips_thumbnail_stream_open( VipsThumbnail *thumbnail, double factor )
{
VipsThumbnailStream *stream = (VipsThumbnailStream *) thumbnail;
if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) {
return( vips_image_new_from_stream(
stream->input,
stream->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"shrink", (int) factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadOpenslide",
thumbnail->loader ) ) {
return( vips_image_new_from_stream(
stream->input,
stream->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"level", (int) factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ||
vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) {
return( vips_image_new_from_stream(
stream->input,
stream->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"scale", 1.0 / factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) {
return( vips_image_new_from_stream(
stream->input,
stream->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"page", (int) factor,
NULL ) );
}
else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) {
return( vips_image_new_from_stream(
stream->input,
stream->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
"thumbnail", (int) factor,
NULL ) );
}
else {
return( vips_image_new_from_stream(
stream->input,
stream->option_string,
"access", VIPS_ACCESS_SEQUENTIAL,
NULL ) );
}
}
static void
vips_thumbnail_stream_class_init( VipsThumbnailClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
VipsThumbnailClass *thumbnail_class = VIPS_THUMBNAIL_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "thumbnail_stream";
vobject_class->description = _( "generate thumbnail from stream" );
thumbnail_class->get_info = vips_thumbnail_stream_get_info;
thumbnail_class->open = vips_thumbnail_stream_open;
VIPS_ARG_OBJECT( class, "input", 1,
_( "Input" ),
_( "Stream to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsThumbnailStream, input ),
VIPS_TYPE_STREAM_INPUT );
VIPS_ARG_STRING( class, "option_string", 20,
_( "Extra options" ),
_( "Options that are passed on to the underlying loader" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsThumbnailStream, option_string ),
"" );
}
static void
vips_thumbnail_stream_init( VipsThumbnailStream *stream )
{
}
/**
* vips_thumbnail_stream:
* @input: stream to thumbnail
* @out: (out): output image
* @width: target width in pixels
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @height: %gint, target height in pixels
* * @size: #VipsSize, upsize, downsize, both or force
* * @no_rotate: %gboolean, don't rotate upright using orientation tag
* * @crop: #VipsInteresting, shrink and crop to fill target
* * @linear: %gboolean, perform shrink in linear light
* * @import_profile: %gchararray, fallback import ICC profile
* * @export_profile: %gchararray, export ICC profile
* * @intent: #VipsIntent, rendering intent
*
* Exacty as vips_thumbnail(), but read from a stream.
*
* See also: vips_thumbnail().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_thumbnail_stream( VipsStreamInput *input, VipsImage **out, int width, ... )
{
va_list ap;
int result;
va_start( ap, width );
result = vips_call_split( "thumbnail_stream", ap, input, out, width );
va_end( ap );
return( result );
}
typedef struct _VipsThumbnailImage {
VipsThumbnail parent_object;