From 1220cc9667f4172d76ba6c6f11388d6b6bb6243e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 28 Jul 2019 19:40:37 +0100 Subject: [PATCH 01/63] start mapimage --- libvips/histogram/Makefile.am | 1 + libvips/histogram/histogram.c | 22 +-- libvips/histogram/mapimage.c | 288 +++++++++++++++++++++++++++++++ libvips/include/vips/histogram.h | 2 + 4 files changed, 303 insertions(+), 10 deletions(-) create mode 100644 libvips/histogram/mapimage.c diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index bd903644..65292b9c 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -4,6 +4,7 @@ libhistogram_la_SOURCES = \ histogram.c \ phistogram.h \ maplut.c \ + mapimage.c \ hist_unary.c \ hist_unary.h \ hist_cum.c \ diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index 13ef1ce8..5df56fef 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -251,6 +251,7 @@ void vips_histogram_operation_init( void ) { extern GType vips_maplut_get_type( void ); + extern GType vips_mapimage_get_type( void ); extern GType vips_percent_get_type( void ); extern GType vips_hist_cum_get_type( void ); extern GType vips_hist_norm_get_type( void ); @@ -262,15 +263,16 @@ vips_histogram_operation_init( void ) extern GType vips_hist_entropy_get_type( void ); extern GType vips_stdif_get_type( void ); - vips_maplut_get_type(); - vips_percent_get_type(); - vips_stdif_get_type(); - vips_hist_cum_get_type(); - vips_hist_norm_get_type(); - vips_hist_equal_get_type(); - vips_hist_plot_get_type(); - vips_hist_match_get_type(); - vips_hist_local_get_type(); - vips_hist_ismonotonic_get_type(); + vips_maplut_get_type(); + vips_mapimage_get_type(); + vips_percent_get_type(); + vips_stdif_get_type(); + vips_hist_cum_get_type(); + vips_hist_norm_get_type(); + vips_hist_equal_get_type(); + vips_hist_plot_get_type(); + vips_hist_match_get_type(); + vips_hist_local_get_type(); + vips_hist_ismonotonic_get_type(); vips_hist_entropy_get_type(); } diff --git a/libvips/histogram/mapimage.c b/libvips/histogram/mapimage.c new file mode 100644 index 00000000..3bcb8325 --- /dev/null +++ b/libvips/histogram/mapimage.c @@ -0,0 +1,288 @@ +/* map though an array of images + * + * 28/7/19 + * - from mapimage.c + */ + +/* + + 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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +typedef struct _VipsMapimage { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; + VipsArrayImage *lut; + +} VipsMapimage; + +typedef VipsOperationClass VipsMapimageClass; + +G_DEFINE_TYPE( VipsMapimage, vips_mapimage, VIPS_TYPE_OPERATION ); + +/* Our sequence value: the region this sequence is using, and local stats. + */ +typedef struct { + VipsRegion *ir; /* Input region */ +} VipsMapimageSequence; + +/* Our start function. + */ +static void * +vips_mapimage_start( VipsImage *out, void *a, void *b ) +{ + VipsImage *in = (VipsImage *) a; + VipsMapimageSequence *seq; + + if( !(seq = VIPS_NEW( out, VipsMapimageSequence )) ) + return( NULL ); + + /* Init! + */ + + if( !(seq->ir = vips_region_new( in )) ) + return( NULL ); + + return( seq ); +} + +/* Do a map. + */ +static int +vips_mapimage_gen( VipsRegion *or, void *vseq, void *a, void *b, + gboolean *stop ) +{ + VipsMapimageSequence *seq = (VipsMapimageSequence *) vseq; + VipsImage *in = (VipsImage *) a; + VipsMapimage *mapimage = (VipsMapimage *) b; + VipsRegion *ir = seq->ir; + VipsRect *r = &or->valid; + + return( 0 ); +} + +/* Destroy a sequence value. + */ +static int +vips_mapimage_stop( void *vseq, void *a, void *b ) +{ + VipsMapimageSequence *seq = (VipsMapimageSequence *) vseq; + VipsMapimage *mapimage = (VipsMapimage *) b; + + VIPS_UNREF( seq->ir ); + + return( 0 ); +} + +static int +vips_mapimage_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsMapimage *mapimage = (VipsMapimage *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + VipsImage *lut; + int i; + + g_object_set( object, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_mapimage_parent_class )->build( object ) ) + return( -1 ); + + /* Cast @in to u8/u16/u32 to make the index image. + */ + if( vips_cast( in, &t[0], bandfmt_mapimage[in->BandFmt], NULL ) ) + return( -1 ); + in = t[0]; + + if( vips_check_uncoded( class->nickname, in ) || + vips_check_bands_1orn( class->nickname, in, lut ) || + vips_image_pio_input( in ) ) + return( -1 ); + + if( vips_image_pipelinev( mapimage->out, + VIPS_DEMAND_STYLE_THINSTRIP, in, lut, NULL ) ) + return( -1 ); + mapimage->out->BandFmt = lut->BandFmt; + + /* Output has same number of bands as LUT, unless LUT has 1 band, in + * which case output has same number of bands as input. + */ + if( lut->Bands != 1 ) + mapimage->out->Bands = lut->Bands; + + /* The Type comes from the image with many bands. A B_W index image, + * for example, needs to become an RGB image when it goes through a + * three-band LUT. + */ + if( lut->Bands != 1 ) + mapimage->out->Type = lut->Type; + + g_signal_connect( in, "preeval", + G_CALLBACK( vips_mapimage_preeval ), mapimage ); + g_signal_connect( in, "posteval", + G_CALLBACK( vips_mapimage_posteval ), mapimage ); + + /* Make luts. We unpack the LUT image into a 2D C array to speed + * processing. + */ + if( !(t[1] = vips_image_copy_memory( lut )) ) + return( -1 ); + lut = t[1]; + mapimage->fmt = lut->BandFmt; + mapimage->es = VIPS_IMAGE_SIZEOF_ELEMENT( lut ); + mapimage->sz = lut->Xsize * lut->Ysize; + mapimage->clp = mapimage->sz - 1; + + /* If @bands is >= 0, we need to expand the lut to the number of bands + * in the input image. + */ + if( mapimage->band >= 0 && + lut->Bands == 1 ) + mapimage->nb = in->Bands; + else + mapimage->nb = lut->Bands; + + /* Attach tables. + */ + if( !(mapimage->table = VIPS_ARRAY( mapimage, mapimage->nb, VipsPel * )) ) + return( -1 ); + for( i = 0; i < mapimage->nb; i++ ) + if( !(mapimage->table[i] = VIPS_ARRAY( mapimage, + mapimage->sz * mapimage->es, VipsPel )) ) + return( -1 ); + + if( vips_image_generate( mapimage->out, + vips_mapimage_start, vips_mapimage_gen, vips_mapimage_stop, + in, mapimage ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_mapimage_class_init( VipsMapimageClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "mapimage"; + object_class->description = _( "map an image though a lut" ); + object_class->build = vips_mapimage_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsMapimage, in ) ); + + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsMapimage, out ) ); + + VIPS_ARG_BOXED( class, "lut", 3, + _( "LUT" ), + _( "Look-up table of images" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsMapimage, lut ), + VIPS_TYPE_ARRAY_IMAGE ); + +} + +static void +vips_mapimage_init( VipsMapimage *mapimage ) +{ +} + +static int +vips_mapimagev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, + va_list ap ) +{ + VipsArrayImage *array; + int result; + + array = vips_array_image_new( lut, n ); + result = vips_call_split( "mapimage", ap, in, out, array ); + vips_area_unref( VIPS_AREA( array ) ); + + return( result ); +} + +/** + * vips_mapimage: (method) + * @in: input image + * @out: (out): output image + * @lut: (array length=n): LUT of input images + * @n: number of input images + * @...: %NULL-terminated list of optional named arguments + * + * Map index image @in through a LUT of images. + * + * Each value in @in is used to select an image from @lut, and the + * corresponding pixel is copied to the output. + * + * @in must have one band. @lut can have up to 256 elements. Values in @in + * greater than or equal to @n use the final image in @lut. The images in @lut + * must have either one band or the same number of bands. + * + * See also: vips_maplut(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_mapimage( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_mapimagev( in, out, lut, n, ap ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index 53d46b15..3d5edb46 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -40,6 +40,8 @@ extern "C" { int vips_maplut( VipsImage *in, VipsImage **out, VipsImage *lut, ... ) __attribute__((sentinel)); +int vips_mapimage( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) + __attribute__((sentinel)); int vips_percent( VipsImage *in, double percent, int *threshold, ... ) __attribute__((sentinel)); int vips_stdif( VipsImage *in, VipsImage **out, int width, int height, ... ) From 6078ba744cfe43ff236e8340ed7680cfeee51b5e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 29 Jul 2019 09:56:22 +0100 Subject: [PATCH 02/63] added more --- libvips/histogram/mapimage.c | 45 +++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/libvips/histogram/mapimage.c b/libvips/histogram/mapimage.c index 3bcb8325..4a3ae03d 100644 --- a/libvips/histogram/mapimage.c +++ b/libvips/histogram/mapimage.c @@ -117,7 +117,12 @@ vips_mapimage_build( VipsObject *object ) VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *in; - VipsImage *lut; + VipsImage **lut; + int n; + VipsImage **decode; + VipsImage **format; + VipsImage **band; + VipsImage **size; int i; g_object_set( object, "out", vips_image_new(), NULL ); @@ -125,12 +130,46 @@ vips_mapimage_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_mapimage_parent_class )->build( object ) ) return( -1 ); - /* Cast @in to u8/u16/u32 to make the index image. + in = mapimage->in; + lut = vips_area_get_data( &mapimage->lut.area, NULL, &n, NULL, NULL ); + if( n > 256 ) { + vips_error( class->nickname, _( "LUT too large" ) ); + return( -1 ); + } + if( in->Bands > 1 ) { + vips_error( class->nickname, _( "index image not 1-band" ) ); + return( -1 ); + } + + /* Cast @in to u8 to make the index image. */ - if( vips_cast( in, &t[0], bandfmt_mapimage[in->BandFmt], NULL ) ) + if( vips_cast( in, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) return( -1 ); in = t[0]; + decode = (VipsImage **) vips_object_local_array( object, n ); + format = (VipsImage **) vips_object_local_array( object, n ); + band = (VipsImage **) vips_object_local_array( object, n ); + size = (VipsImage **) vips_object_local_array( object, n ); + + /* Decode RAD/LABQ etc. + */ + for( i = 0; i < arithmetic->n; i++ ) + if( vips_image_decode( lut[i], &decode[i] ) ) + return( -1 ); + lut = decode; + + /* LUT images must match in format, size and bands. + */ + if( vips__formatalike_vec( lut, format, n ) || + vips__bandalike_vec( class->nickname, format, band, n, max_bands ) || + vips__sizealike_vec( band, size, n ) ) + return( -1 ); + + + + + if( vips_check_uncoded( class->nickname, in ) || vips_check_bands_1orn( class->nickname, in, lut ) || vips_image_pio_input( in ) ) From 0a51ca96c927ccb6748f6f804c1eb3c89777e896 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 1 Aug 2019 20:57:46 +0100 Subject: [PATCH 03/63] mostly working still fails to exit cleanly --- libvips/histogram/Makefile.am | 2 +- libvips/histogram/histogram.c | 4 +- libvips/histogram/mapimage.c | 327 ---------------------------------- libvips/histogram/switch.c | 307 +++++++++++++++++++++++++++++++ 4 files changed, 310 insertions(+), 330 deletions(-) delete mode 100644 libvips/histogram/mapimage.c create mode 100644 libvips/histogram/switch.c diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index 65292b9c..9703a8d0 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -4,7 +4,7 @@ libhistogram_la_SOURCES = \ histogram.c \ phistogram.h \ maplut.c \ - mapimage.c \ + switch.c \ hist_unary.c \ hist_unary.h \ hist_cum.c \ diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index 5df56fef..c0c97ea1 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -251,7 +251,7 @@ void vips_histogram_operation_init( void ) { extern GType vips_maplut_get_type( void ); - extern GType vips_mapimage_get_type( void ); + extern GType vips_switch_get_type( void ); extern GType vips_percent_get_type( void ); extern GType vips_hist_cum_get_type( void ); extern GType vips_hist_norm_get_type( void ); @@ -264,7 +264,7 @@ vips_histogram_operation_init( void ) extern GType vips_stdif_get_type( void ); vips_maplut_get_type(); - vips_mapimage_get_type(); + vips_switch_get_type(); vips_percent_get_type(); vips_stdif_get_type(); vips_hist_cum_get_type(); diff --git a/libvips/histogram/mapimage.c b/libvips/histogram/mapimage.c deleted file mode 100644 index 4a3ae03d..00000000 --- a/libvips/histogram/mapimage.c +++ /dev/null @@ -1,327 +0,0 @@ -/* map though an array of images - * - * 28/7/19 - * - from mapimage.c - */ - -/* - - 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 - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include - -typedef struct _VipsMapimage { - VipsOperation parent_instance; - - VipsImage *in; - VipsImage *out; - VipsArrayImage *lut; - -} VipsMapimage; - -typedef VipsOperationClass VipsMapimageClass; - -G_DEFINE_TYPE( VipsMapimage, vips_mapimage, VIPS_TYPE_OPERATION ); - -/* Our sequence value: the region this sequence is using, and local stats. - */ -typedef struct { - VipsRegion *ir; /* Input region */ -} VipsMapimageSequence; - -/* Our start function. - */ -static void * -vips_mapimage_start( VipsImage *out, void *a, void *b ) -{ - VipsImage *in = (VipsImage *) a; - VipsMapimageSequence *seq; - - if( !(seq = VIPS_NEW( out, VipsMapimageSequence )) ) - return( NULL ); - - /* Init! - */ - - if( !(seq->ir = vips_region_new( in )) ) - return( NULL ); - - return( seq ); -} - -/* Do a map. - */ -static int -vips_mapimage_gen( VipsRegion *or, void *vseq, void *a, void *b, - gboolean *stop ) -{ - VipsMapimageSequence *seq = (VipsMapimageSequence *) vseq; - VipsImage *in = (VipsImage *) a; - VipsMapimage *mapimage = (VipsMapimage *) b; - VipsRegion *ir = seq->ir; - VipsRect *r = &or->valid; - - return( 0 ); -} - -/* Destroy a sequence value. - */ -static int -vips_mapimage_stop( void *vseq, void *a, void *b ) -{ - VipsMapimageSequence *seq = (VipsMapimageSequence *) vseq; - VipsMapimage *mapimage = (VipsMapimage *) b; - - VIPS_UNREF( seq->ir ); - - return( 0 ); -} - -static int -vips_mapimage_build( VipsObject *object ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsMapimage *mapimage = (VipsMapimage *) object; - VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); - - VipsImage *in; - VipsImage **lut; - int n; - VipsImage **decode; - VipsImage **format; - VipsImage **band; - VipsImage **size; - int i; - - g_object_set( object, "out", vips_image_new(), NULL ); - - if( VIPS_OBJECT_CLASS( vips_mapimage_parent_class )->build( object ) ) - return( -1 ); - - in = mapimage->in; - lut = vips_area_get_data( &mapimage->lut.area, NULL, &n, NULL, NULL ); - if( n > 256 ) { - vips_error( class->nickname, _( "LUT too large" ) ); - return( -1 ); - } - if( in->Bands > 1 ) { - vips_error( class->nickname, _( "index image not 1-band" ) ); - return( -1 ); - } - - /* Cast @in to u8 to make the index image. - */ - if( vips_cast( in, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) - return( -1 ); - in = t[0]; - - decode = (VipsImage **) vips_object_local_array( object, n ); - format = (VipsImage **) vips_object_local_array( object, n ); - band = (VipsImage **) vips_object_local_array( object, n ); - size = (VipsImage **) vips_object_local_array( object, n ); - - /* Decode RAD/LABQ etc. - */ - for( i = 0; i < arithmetic->n; i++ ) - if( vips_image_decode( lut[i], &decode[i] ) ) - return( -1 ); - lut = decode; - - /* LUT images must match in format, size and bands. - */ - if( vips__formatalike_vec( lut, format, n ) || - vips__bandalike_vec( class->nickname, format, band, n, max_bands ) || - vips__sizealike_vec( band, size, n ) ) - return( -1 ); - - - - - - if( vips_check_uncoded( class->nickname, in ) || - vips_check_bands_1orn( class->nickname, in, lut ) || - vips_image_pio_input( in ) ) - return( -1 ); - - if( vips_image_pipelinev( mapimage->out, - VIPS_DEMAND_STYLE_THINSTRIP, in, lut, NULL ) ) - return( -1 ); - mapimage->out->BandFmt = lut->BandFmt; - - /* Output has same number of bands as LUT, unless LUT has 1 band, in - * which case output has same number of bands as input. - */ - if( lut->Bands != 1 ) - mapimage->out->Bands = lut->Bands; - - /* The Type comes from the image with many bands. A B_W index image, - * for example, needs to become an RGB image when it goes through a - * three-band LUT. - */ - if( lut->Bands != 1 ) - mapimage->out->Type = lut->Type; - - g_signal_connect( in, "preeval", - G_CALLBACK( vips_mapimage_preeval ), mapimage ); - g_signal_connect( in, "posteval", - G_CALLBACK( vips_mapimage_posteval ), mapimage ); - - /* Make luts. We unpack the LUT image into a 2D C array to speed - * processing. - */ - if( !(t[1] = vips_image_copy_memory( lut )) ) - return( -1 ); - lut = t[1]; - mapimage->fmt = lut->BandFmt; - mapimage->es = VIPS_IMAGE_SIZEOF_ELEMENT( lut ); - mapimage->sz = lut->Xsize * lut->Ysize; - mapimage->clp = mapimage->sz - 1; - - /* If @bands is >= 0, we need to expand the lut to the number of bands - * in the input image. - */ - if( mapimage->band >= 0 && - lut->Bands == 1 ) - mapimage->nb = in->Bands; - else - mapimage->nb = lut->Bands; - - /* Attach tables. - */ - if( !(mapimage->table = VIPS_ARRAY( mapimage, mapimage->nb, VipsPel * )) ) - return( -1 ); - for( i = 0; i < mapimage->nb; i++ ) - if( !(mapimage->table[i] = VIPS_ARRAY( mapimage, - mapimage->sz * mapimage->es, VipsPel )) ) - return( -1 ); - - if( vips_image_generate( mapimage->out, - vips_mapimage_start, vips_mapimage_gen, vips_mapimage_stop, - in, mapimage ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_mapimage_class_init( VipsMapimageClass *class ) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS( class ); - VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); - VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); - - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - object_class->nickname = "mapimage"; - object_class->description = _( "map an image though a lut" ); - object_class->build = vips_mapimage_build; - - operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - - VIPS_ARG_IMAGE( class, "in", 1, - _( "Input" ), - _( "Input image" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsMapimage, in ) ); - - VIPS_ARG_IMAGE( class, "out", 2, - _( "Output" ), - _( "Output image" ), - VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsMapimage, out ) ); - - VIPS_ARG_BOXED( class, "lut", 3, - _( "LUT" ), - _( "Look-up table of images" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsMapimage, lut ), - VIPS_TYPE_ARRAY_IMAGE ); - -} - -static void -vips_mapimage_init( VipsMapimage *mapimage ) -{ -} - -static int -vips_mapimagev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, - va_list ap ) -{ - VipsArrayImage *array; - int result; - - array = vips_array_image_new( lut, n ); - result = vips_call_split( "mapimage", ap, in, out, array ); - vips_area_unref( VIPS_AREA( array ) ); - - return( result ); -} - -/** - * vips_mapimage: (method) - * @in: input image - * @out: (out): output image - * @lut: (array length=n): LUT of input images - * @n: number of input images - * @...: %NULL-terminated list of optional named arguments - * - * Map index image @in through a LUT of images. - * - * Each value in @in is used to select an image from @lut, and the - * corresponding pixel is copied to the output. - * - * @in must have one band. @lut can have up to 256 elements. Values in @in - * greater than or equal to @n use the final image in @lut. The images in @lut - * must have either one band or the same number of bands. - * - * See also: vips_maplut(). - * - * Returns: 0 on success, -1 on error - */ -int -vips_mapimage( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) -{ - va_list ap; - int result; - - va_start( ap, n ); - result = vips_mapimagev( in, out, lut, n, ap ); - va_end( ap ); - - return( result ); -} - diff --git a/libvips/histogram/switch.c b/libvips/histogram/switch.c new file mode 100644 index 00000000..cb1cff6f --- /dev/null +++ b/libvips/histogram/switch.c @@ -0,0 +1,307 @@ +/* use pixel values to switch between an array of images + * + * 28/7/19 + * - from maplut.c + */ + +/* + + 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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +typedef struct _VipsSwitch { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; + VipsArrayImage *lut; + int n; + +} VipsSwitch; + +typedef VipsOperationClass VipsSwitchClass; + +G_DEFINE_TYPE( VipsSwitch, vips_switch, VIPS_TYPE_OPERATION ); + +/* Do a map. + */ +static int +vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b, + gboolean *stop ) +{ + VipsRegion **ar = (VipsRegion **) seq; + VipsSwitch *swit = (VipsSwitch *) b; + VipsRect *r = &or->valid; + VipsRegion *index = ar[swit->n]; + + int x, y, i, j; + VipsPel *ip; + VipsPel *q; + size_t ils; + size_t qls; + int hist[256]; + VipsPel *p[256]; + size_t ls[256]; + size_t ps; + + if( vips_region_prepare( index, r ) ) + return( -1 ); + + g_assert( index->im->BandFmt == VIPS_FORMAT_UCHAR ); + g_assert( index->im->Bands == 1 ); + + /* Histogram of input region, so we know which of our inputs we will + * need to prepare. + */ + memset( hist, 0, 256 * sizeof( int ) ); + ip = VIPS_REGION_ADDR( index, r->left, r->top ); + ils = VIPS_REGION_LSKIP( index ); + for( y = 0; y < r->height; y++ ) { + for( x = 0; x < r->width; x++ ) + hist[ip[x]] += 1; + + ip += ils; + } + + for( i = 0; i < 256; i++ ) + if( hist[i] ) { + if( vips_region_prepare( ar[i], r ) ) + return( -1 ); + p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top ); + ls[i] = VIPS_REGION_LSKIP( ar[i] ); + } + + ip = VIPS_REGION_ADDR( index, r->left, r->top ); + q = VIPS_REGION_ADDR( or, r->left, r->top ); + qls = VIPS_REGION_LSKIP( or ); + ps = VIPS_IMAGE_SIZEOF_PEL( or->im ); + for( y = 0; y < r->height; y++ ) { + i = 0; + for( x = 0; x < r->width; x++ ) { + int v = ip[x]; + + for( j = 0; j < ps; j++ ) { + q[i] = p[v][i]; + i += 1; + } + } + + ip += ils; + q += qls; + for( i = 0; i < 256; i++ ) + if( hist[i] ) + p[i] += ls[i]; + } + + return( 0 ); +} + +static int +vips_switch_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsSwitch *swit = (VipsSwitch *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + VipsImage **lut; + VipsImage **decode; + VipsImage **format; + VipsImage **band; + VipsImage **size; + int i; + + g_object_set( object, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) ) + return( -1 ); + + in = swit->in; + lut = vips_area_get_data( &swit->lut->area, + NULL, &swit->n, NULL, NULL ); + if( swit->n > 256 ) { + vips_error( class->nickname, "%s", _( "LUT too large" ) ); + return( -1 ); + } + if( in->Bands > 1 ) { + vips_error( class->nickname, + "%s", _( "index image not 1-band" ) ); + return( -1 ); + } + + /* Cast @in to u8 to make the index image. + */ + if( vips_cast( in, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) + return( -1 ); + in = t[0]; + + decode = (VipsImage **) vips_object_local_array( object, swit->n ); + format = (VipsImage **) vips_object_local_array( object, swit->n ); + band = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + size = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + + /* Decode RAD/LABQ etc. + */ + for( i = 0; i < swit->n; i++ ) + if( vips_image_decode( lut[i], &decode[i] ) ) + return( -1 ); + lut = decode; + + /* LUT images must match in format, size and bands. + * + * We want everything sized up to the size of the index image, so add + * that to the end of the set of images for sizealike. + */ + band[swit->n] = in; + if( vips__formatalike_vec( lut, format, swit->n ) || + vips__bandalike_vec( class->nickname, + format, band, swit->n, 1 ) || + vips__sizealike_vec( band, size, swit->n + 1 ) ) + return( -1 ); + lut = size; + + swit->out->BandFmt = lut[0]->BandFmt; + swit->out->Bands = lut[0]->Bands; + swit->out->Type = lut[0]->Type; + swit->out->Xsize = in->Xsize; + swit->out->Ysize = in->Ysize; + + if( vips_image_pipeline_array( swit->out, + VIPS_DEMAND_STYLE_THINSTRIP, lut ) ) + return( -1 ); + + if( vips_image_generate( swit->out, + vips_start_many, vips_switch_gen, vips_stop_many, + lut, swit ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_switch_class_init( VipsSwitchClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "switch"; + object_class->description = + _( "use pixel values to switch between a set of images" ); + object_class->build = vips_switch_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSwitch, in ) ); + + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsSwitch, out ) ); + + VIPS_ARG_BOXED( class, "lut", 3, + _( "LUT" ), + _( "Look-up table of images" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSwitch, lut ), + VIPS_TYPE_ARRAY_IMAGE ); + +} + +static void +vips_switch_init( VipsSwitch *swit ) +{ +} + +static int +vips_switchv( VipsImage *in, VipsImage **out, VipsImage **lut, int n, + va_list ap ) +{ + VipsArrayImage *array; + int result; + + array = vips_array_image_new( lut, n ); + result = vips_call_split( "switch", ap, in, out, array ); + vips_area_unref( VIPS_AREA( array ) ); + + return( result ); +} + +/** + * vips_switch: (method) + * @in: input image + * @out: (out): output image + * @lut: (array length=n): LUT of input images + * @n: number of input images + * @...: %NULL-terminated list of optional named arguments + * + * Use pixel values to switch between an array of images. + * + * Each value in @in is used to select an image from @lut, and the + * corresponding pixel is copied to the output. + * + * @in must have one band. @lut can have up to 256 elements. Values in @in + * greater than or equal to @n use the final image in @lut. The images in @lut + * must have either one band or the same number of bands. The output image is + * the same size as @in. Images in @lut are expanded to the smallest common + * format and number of bands. + * + * See also: vips_maplut(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_switch( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_switchv( in, out, lut, n, ap ); + va_end( ap ); + + return( result ); +} + From 42cdc8177c0e23fa0c4d73f8866d0e4b59826de2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 2 Aug 2019 20:19:28 +0100 Subject: [PATCH 04/63] git switch working --- libvips/histogram/switch.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libvips/histogram/switch.c b/libvips/histogram/switch.c index cb1cff6f..2054dce0 100644 --- a/libvips/histogram/switch.c +++ b/libvips/histogram/switch.c @@ -69,12 +69,12 @@ vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b, VipsRegion *index = ar[swit->n]; int x, y, i, j; - VipsPel *ip; - VipsPel *q; + VipsPel * restrict ip; + VipsPel * restrict q; size_t ils; size_t qls; int hist[256]; - VipsPel *p[256]; + VipsPel * restrict p[256]; size_t ls[256]; size_t ps; @@ -112,10 +112,10 @@ vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b, for( y = 0; y < r->height; y++ ) { i = 0; for( x = 0; x < r->width; x++ ) { - int v = ip[x]; + VipsPel * restrict pv = p[ip[x]]; for( j = 0; j < ps; j++ ) { - q[i] = p[v][i]; + q[i] = pv[i]; i += 1; } } @@ -187,6 +187,7 @@ vips_switch_build( VipsObject *object ) * that to the end of the set of images for sizealike. */ band[swit->n] = in; + g_object_ref( in ); if( vips__formatalike_vec( lut, format, swit->n ) || vips__bandalike_vec( class->nickname, format, band, swit->n, 1 ) || From d5634a918b1fb55b64433af1e9341b8ae8417703 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 11 Aug 2019 19:11:17 +0100 Subject: [PATCH 05/63] start vips_case() --- libvips/histogram/Makefile.am | 1 + libvips/histogram/case.c | 308 +++++++++++++++++++++++++++++++ libvips/include/vips/histogram.h | 5 + 3 files changed, 314 insertions(+) create mode 100644 libvips/histogram/case.c diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index 9703a8d0..28bf8940 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -5,6 +5,7 @@ libhistogram_la_SOURCES = \ phistogram.h \ maplut.c \ switch.c \ + case.c \ hist_unary.c \ hist_unary.h \ hist_cum.c \ diff --git a/libvips/histogram/case.c b/libvips/histogram/case.c new file mode 100644 index 00000000..b4fe0b76 --- /dev/null +++ b/libvips/histogram/case.c @@ -0,0 +1,308 @@ +/* use pixel values to case between an array of images + * + * 28/7/19 + * - from maplut.c + */ + +/* + + 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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +typedef struct _VipsCase { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; + VipsArrayImage *lut; + int n; + +} VipsCase; + +typedef VipsOperationClass VipsCaseClass; + +G_DEFINE_TYPE( VipsCase, vips_case, VIPS_TYPE_OPERATION ); + +/* Do a map. + */ +static int +vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, + gboolean *stop ) +{ + VipsRegion **ar = (VipsRegion **) seq; + VipsCase *swit = (VipsCase *) b; + VipsRect *r = &or->valid; + VipsRegion *index = ar[swit->n]; + + int x, y, i, j; + VipsPel * restrict ip; + VipsPel * restrict q; + size_t ils; + size_t qls; + int hist[256]; + VipsPel * restrict p[256]; + size_t ls[256]; + size_t ps; + + if( vips_region_prepare( index, r ) ) + return( -1 ); + + g_assert( index->im->BandFmt == VIPS_FORMAT_UCHAR ); + g_assert( index->im->Bands == 1 ); + + /* Histogram of input region, so we know which of our inputs we will + * need to prepare. + */ + memset( hist, 0, 256 * sizeof( int ) ); + ip = VIPS_REGION_ADDR( index, r->left, r->top ); + ils = VIPS_REGION_LSKIP( index ); + for( y = 0; y < r->height; y++ ) { + for( x = 0; x < r->width; x++ ) + hist[ip[x]] += 1; + + ip += ils; + } + + for( i = 0; i < 256; i++ ) + if( hist[i] ) { + if( vips_region_prepare( ar[i], r ) ) + return( -1 ); + p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top ); + ls[i] = VIPS_REGION_LSKIP( ar[i] ); + } + + ip = VIPS_REGION_ADDR( index, r->left, r->top ); + q = VIPS_REGION_ADDR( or, r->left, r->top ); + qls = VIPS_REGION_LSKIP( or ); + ps = VIPS_IMAGE_SIZEOF_PEL( or->im ); + for( y = 0; y < r->height; y++ ) { + i = 0; + for( x = 0; x < r->width; x++ ) { + VipsPel * restrict pv = p[ip[x]]; + + for( j = 0; j < ps; j++ ) { + q[i] = pv[i]; + i += 1; + } + } + + ip += ils; + q += qls; + for( i = 0; i < 256; i++ ) + if( hist[i] ) + p[i] += ls[i]; + } + + return( 0 ); +} + +static int +vips_case_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCase *swit = (VipsCase *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + VipsImage **lut; + VipsImage **decode; + VipsImage **format; + VipsImage **band; + VipsImage **size; + int i; + + g_object_set( object, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_case_parent_class )->build( object ) ) + return( -1 ); + + in = swit->in; + lut = vips_area_get_data( &swit->lut->area, + NULL, &swit->n, NULL, NULL ); + if( swit->n > 256 ) { + vips_error( class->nickname, "%s", _( "LUT too large" ) ); + return( -1 ); + } + if( in->Bands > 1 ) { + vips_error( class->nickname, + "%s", _( "index image not 1-band" ) ); + return( -1 ); + } + + /* Cast @in to u8 to make the index image. + */ + if( vips_cast( in, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) + return( -1 ); + in = t[0]; + + decode = (VipsImage **) vips_object_local_array( object, swit->n ); + format = (VipsImage **) vips_object_local_array( object, swit->n ); + band = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + size = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + + /* Decode RAD/LABQ etc. + */ + for( i = 0; i < swit->n; i++ ) + if( vips_image_decode( lut[i], &decode[i] ) ) + return( -1 ); + lut = decode; + + /* LUT images must match in format, size and bands. + * + * We want everything sized up to the size of the index image, so add + * that to the end of the set of images for sizealike. + */ + band[swit->n] = in; + g_object_ref( in ); + if( vips__formatalike_vec( lut, format, swit->n ) || + vips__bandalike_vec( class->nickname, + format, band, swit->n, 1 ) || + vips__sizealike_vec( band, size, swit->n + 1 ) ) + return( -1 ); + lut = size; + + swit->out->BandFmt = lut[0]->BandFmt; + swit->out->Bands = lut[0]->Bands; + swit->out->Type = lut[0]->Type; + swit->out->Xsize = in->Xsize; + swit->out->Ysize = in->Ysize; + + if( vips_image_pipeline_array( swit->out, + VIPS_DEMAND_STYLE_THINSTRIP, lut ) ) + return( -1 ); + + if( vips_image_generate( swit->out, + vips_start_many, vips_case_gen, vips_stop_many, + lut, swit ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_case_class_init( VipsCaseClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "case"; + object_class->description = + _( "use pixel values to case between a set of images" ); + object_class->build = vips_case_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsCase, in ) ); + + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsCase, out ) ); + + VIPS_ARG_BOXED( class, "lut", 3, + _( "LUT" ), + _( "Look-up table of images" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsCase, lut ), + VIPS_TYPE_ARRAY_IMAGE ); + +} + +static void +vips_case_init( VipsCase *swit ) +{ +} + +static int +vips_casev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, + va_list ap ) +{ + VipsArrayImage *array; + int result; + + array = vips_array_image_new( lut, n ); + result = vips_call_split( "case", ap, in, out, array ); + vips_area_unref( VIPS_AREA( array ) ); + + return( result ); +} + +/** + * vips_case: (method) + * @in: input image + * @out: (out): output image + * @lut: (array length=n): LUT of input images + * @n: number of input images + * @...: %NULL-terminated list of optional named arguments + * + * Use pixel values to case between an array of images. + * + * Each value in @in is used to select an image from @lut, and the + * corresponding pixel is copied to the output. + * + * @in must have one band. @lut can have up to 256 elements. Values in @in + * greater than or equal to @n use the final image in @lut. The images in @lut + * must have either one band or the same number of bands. The output image is + * the same size as @in. Images in @lut are expanded to the smallest common + * format and number of bands. + * + * See also: vips_maplut(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_casev( in, out, lut, n, ap ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index 3d5edb46..ff841681 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -64,6 +64,11 @@ int vips_hist_ismonotonic( VipsImage *in, gboolean *out, ... ) int vips_hist_entropy( VipsImage *in, double *out, ... ) __attribute__((sentinel)); +int vips_switch( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) + __attribute__((sentinel)); +int vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) + __attribute__((sentinel)); + #ifdef __cplusplus } #endif /*__cplusplus*/ From 12371e68c25c1fb2acd82f547da2231e9e8ff7e3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 13 Aug 2019 03:04:33 +0100 Subject: [PATCH 06/63] start adding select --- libvips/conversion/Makefile.am | 1 + .../{histogram/case.c => conversion/select.c} | 173 +++++++++++++----- libvips/histogram/Makefile.am | 1 - libvips/include/vips/conversion.h | 3 + libvips/include/vips/histogram.h | 2 - 5 files changed, 128 insertions(+), 52 deletions(-) rename libvips/{histogram/case.c => conversion/select.c} (60%) diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index 95b8d4fc..752b277f 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libconversion.la libconversion_la_SOURCES = \ + select.c \ transpose3d.c \ composite.cpp \ smartcrop.c \ diff --git a/libvips/histogram/case.c b/libvips/conversion/select.c similarity index 60% rename from libvips/histogram/case.c rename to libvips/conversion/select.c index b4fe0b76..c8ff4a3f 100644 --- a/libvips/histogram/case.c +++ b/libvips/conversion/select.c @@ -1,4 +1,4 @@ -/* use pixel values to case between an array of images +/* use pixel values to select between an array of images * * 28/7/19 * - from maplut.c @@ -43,28 +43,99 @@ #include #include -typedef struct _VipsCase { +typedef struct _VipsSelect { VipsOperation parent_instance; - VipsImage *in; + VipsArrayImage *tests; + VipsArrayImage *cases; VipsImage *out; - VipsArrayImage *lut; + int n; -} VipsCase; +} VipsSelect; -typedef VipsOperationClass VipsCaseClass; +typedef VipsOperationClass VipsSelectClass; -G_DEFINE_TYPE( VipsCase, vips_case, VIPS_TYPE_OPERATION ); +G_DEFINE_TYPE( VipsSelect, vips_select, VIPS_TYPE_OPERATION ); + +/* Our sequence value. + */ +typedef struct _VipsSelectSeq { + VipsSelect *select; + + /* Set of input regions. + */ + VipsRegion **tests; + VipsRegion **cases; + +} VipsSelectSeq; + +static void * +vips_select_start( VipsImage *out, void *a, void *b ) +{ + VipsImage **in = (VipsImage **) a; + VipsSelect *select = (VipsSelect *) b; + + VipsSelectSeq *seq; + int i, n; + + if( !(seq = VIPS_NEW( NULL, VipsSelectSeq )) ) + return( NULL ); + + seq->select = select; + seq->tests = NULL; + seq->cases = NULL; + + /* How many images? + */ + for( n = 0; in[n]; n++ ) + ; + + /* Alocate space for region array. + */ + if( !(seq->ir = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) { + vips_bandary_stop( seq, NULL, NULL ); + return( NULL ); + } + + /* Create a set of regions. + */ + for( i = 0; i < n; i++ ) + if( !(seq->ir[i] = vips_region_new( in[i] )) ) { + vips_bandary_stop( seq, NULL, NULL ); + return( NULL ); + } + seq->ir[n] = NULL; + + /* Input pointers. + */ + if( !(seq->p = VIPS_ARRAY( NULL, n + 1, VipsPel * )) ) { + vips_bandary_stop( seq, NULL, NULL ); + return( NULL ); + } + + /* Pixel buffer. This is used as working space by some subclasses. + */ + if( !(seq->pixels = VIPS_ARRAY( NULL, + n * VIPS_IMAGE_SIZEOF_PEL( bandary->ready[0] ), VipsPel )) ) { + vips_bandary_stop( seq, NULL, NULL ); + return( NULL ); + } + + return( seq ); +} + + +need start / stop and a seq typedef for our two arrays of regions /* Do a map. */ static int -vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, +vips_select_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion **ar = (VipsRegion **) seq; - VipsCase *swit = (VipsCase *) b; + VipsSelect *swit = (VipsSelect *) b; VipsRect *r = &or->valid; VipsRegion *index = ar[swit->n]; @@ -131,10 +202,10 @@ vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, } static int -vips_case_build( VipsObject *object ) +vips_select_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsCase *swit = (VipsCase *) object; + VipsSelect *swit = (VipsSelect *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *in; @@ -147,7 +218,7 @@ vips_case_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - if( VIPS_OBJECT_CLASS( vips_case_parent_class )->build( object ) ) + if( VIPS_OBJECT_CLASS( vips_select_parent_class )->build( object ) ) return( -1 ); in = swit->in; @@ -206,7 +277,7 @@ vips_case_build( VipsObject *object ) return( -1 ); if( vips_image_generate( swit->out, - vips_start_many, vips_case_gen, vips_stop_many, + vips_start_many, vips_select_gen, vips_stop_many, lut, swit ) ) return( -1 ); @@ -214,7 +285,7 @@ vips_case_build( VipsObject *object ) } static void -vips_case_class_init( VipsCaseClass *class ) +vips_select_class_init( VipsSelectClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); @@ -223,84 +294,88 @@ vips_case_class_init( VipsCaseClass *class ) gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "case"; + object_class->nickname = "select"; object_class->description = - _( "use pixel values to case between a set of images" ); - object_class->build = vips_case_build; + _( "test images pick pixels from a set of case images" ); + object_class->build = vips_select_build; operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - VIPS_ARG_IMAGE( class, "in", 1, - _( "Input" ), - _( "Input image" ), + VIPS_ARG_BOXED( class, "tests", 1, + _( "Tests" ), + _( "Table of images to test" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsCase, in ) ); + G_STRUCT_OFFSET( VipsSelect, tests ), + VIPS_TYPE_ARRAY_IMAGE ); - VIPS_ARG_IMAGE( class, "out", 2, + VIPS_ARG_BOXED( class, "cases", 2, + _( "Cases" ), + _( "Table of image cases" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSelect, cases ), + VIPS_TYPE_ARRAY_IMAGE ); + + VIPS_ARG_IMAGE( class, "out", 3, _( "Output" ), _( "Output image" ), VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsCase, out ) ); - - VIPS_ARG_BOXED( class, "lut", 3, - _( "LUT" ), - _( "Look-up table of images" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsCase, lut ), - VIPS_TYPE_ARRAY_IMAGE ); + G_STRUCT_OFFSET( VipsSelect, out ) ); } static void -vips_case_init( VipsCase *swit ) +vips_select_init( VipsSelect *swit ) { } static int -vips_casev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, +vips_selectv( VipsImage **tests, VipsImage **cases, VipsImage **out, int n, va_list ap ) { - VipsArrayImage *array; + VipsArrayImage *tests_array; + VipsArrayImage *cases_array; int result; - array = vips_array_image_new( lut, n ); - result = vips_call_split( "case", ap, in, out, array ); - vips_area_unref( VIPS_AREA( array ) ); + tests_array = vips_array_image_new( tests n ); + cases_array = vips_array_image_new( selects, n ); + result = vips_call_split( "select", ap, + tests_array, cases_array, out ); + vips_area_unref( VIPS_AREA( tests_array ) ); + vips_area_unref( VIPS_AREA( cases_array ) ); return( result ); } /** - * vips_case: (method) - * @in: input image + * vips_select: (method) + * @tests: (array length=n): test these images + * @cases: (array length=n): to pick between these images * @out: (out): output image - * @lut: (array length=n): LUT of input images * @n: number of input images * @...: %NULL-terminated list of optional named arguments * - * Use pixel values to case between an array of images. + * The @tests images are evaluated and the index of the first non-zero value + * in @tests is used to pick a pixel from @cases. If all @tests are false, the + * pixel from the final image is @cases is used. * - * Each value in @in is used to select an image from @lut, and the - * corresponding pixel is copied to the output. - * - * @in must have one band. @lut can have up to 256 elements. Values in @in - * greater than or equal to @n use the final image in @lut. The images in @lut + * Images in @tests images must have one uchar band. @cases and @tests can + * have up to 256 elements. The images in @tests and @cases * must have either one band or the same number of bands. The output image is - * the same size as @in. Images in @lut are expanded to the smallest common - * format and number of bands. + * the same size as @tests. Images in @cases are expanded to the smallest + * common format and number of bands. * * See also: vips_maplut(). * * Returns: 0 on success, -1 on error */ int -vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +vips_select( VipsImage **tests, VipsImage **cases, VipsImage **out, int n, ... ) { va_list ap; int result; va_start( ap, n ); - result = vips_casev( in, out, lut, n, ap ); + result = vips_selectv( tests, cases, out, n, ap ); va_end( ap ); return( result ); diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index 28bf8940..9703a8d0 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -5,7 +5,6 @@ libhistogram_la_SOURCES = \ phistogram.h \ maplut.c \ switch.c \ - case.c \ hist_unary.c \ hist_unary.h \ hist_cum.c \ diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index eaa7ff22..4e893840 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -262,6 +262,9 @@ int vips_recomb( VipsImage *in, VipsImage **out, VipsImage *m, ... ) int vips_ifthenelse( VipsImage *cond, VipsImage *in1, VipsImage *in2, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_select( VipsImage **tests, VipsImage **cases, + VipsImage **out, int n, ... ) + __attribute__((sentinel)); int vips_flatten( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index ff841681..573c3e2d 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -66,8 +66,6 @@ int vips_hist_entropy( VipsImage *in, double *out, ... ) int vips_switch( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) __attribute__((sentinel)); -int vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) - __attribute__((sentinel)); #ifdef __cplusplus } From d80ce4bf15936d7d6c35159b0eb3fe3207b8b89a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 13 Aug 2019 11:21:01 +0100 Subject: [PATCH 07/63] fix a problem with shrinkv tail processing Tail processing in shrinkv had an implicit assumption of round-down, but of course we round to nearest. Thanks angelmixu. see https://github.com/libvips/libvips/issues/1396 --- ChangeLog | 1 + libvips/resample/shrinkv.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 46b42970..02cf7e4c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ - fix build with GM - add locks for pdfium load - fix build with MSVC +- fix a problem with shinkv tail processing [angelmixu] 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/resample/shrinkv.c b/libvips/resample/shrinkv.c index 0dfac96b..19925c32 100644 --- a/libvips/resample/shrinkv.c +++ b/libvips/resample/shrinkv.c @@ -329,12 +329,12 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq, r->top + r->height >= or->im->Ysize ) { /* First unused scanline. resample->in->Ysize because we want * the height before the embed. + * + * Because we round to nearest, unused can be negative. */ int first = or->im->Ysize * shrink->vshrink; int unused = resample->in->Ysize - first; - g_assert( unused >= 0 ); - for( y = 0; y < unused; y++ ) { VipsRect s; From 6510e1ff306116f0c3d003a8efcf7589fcb6bf5d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 16 Aug 2019 13:24:25 +0100 Subject: [PATCH 08/63] hide a gcc warning gcc has an "unknown pragma" warning which triggers for clang pragmas we need to hide clang compiler warnings (!!) --- configure.ac | 16 ---------------- libvips/conversion/Makefile.am | 8 +++++++- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index 7f2b6b11..818c7605 100644 --- a/configure.ac +++ b/configure.ac @@ -1441,22 +1441,6 @@ image pyramid export: $with_gsf use libexif to load/save JPEG metadata: $with_libexif ]) -if test x"$found_introspection" = x"yes" -a "$VIPS_LIBDIR/girepository-1.0" != "$INTROSPECTION_TYPELIBDIR"; then - case "$VIPS_LIBDIR" in - /usr/local/Cellar/vips/*) - ;; # ignore for homebrew - *) - AC_MSG_RESULT([dnl -Vips-8.0.typelib will be installed to $VIPS_LIBDIR/girepository-1.0, but -your system repository seems to be $INTROSPECTION_TYPELIBDIR. -You may need to add this directory to your typelib path, for example: - - export GI_TYPELIB_PATH="$VIPS_LIBDIR/girepository-1.0" - ]) - ;; - esac -fi - if test x"$vips_os_win32" = x"yes"; then if test x"$have_g_win32_get_command_line" != x"yes"; then AC_MSG_RESULT([dnl diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index 95b8d4fc..da804a11 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -44,4 +44,10 @@ libconversion_la_SOURCES = \ subsample.c \ zoom.c -AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ +# gcc annoyingly warns about clang pragmas, and does not support suppressing +# the warning with a gcc pragma +AM_CPPFLAGS = \ + -I${top_srcdir}/libvips/include \ + @VIPS_CFLAGS@ @VIPS_INCLUDES@ \ + -Wno-unknown-pragmas + From 45c847a96cc701ca88f061041d75f7d4473ae23e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 17 Aug 2019 12:57:01 +0100 Subject: [PATCH 09/63] more hacking --- libvips/conversion/select.c | 66 +++++++++++++++---------------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/libvips/conversion/select.c b/libvips/conversion/select.c index c8ff4a3f..ef495328 100644 --- a/libvips/conversion/select.c +++ b/libvips/conversion/select.c @@ -70,6 +70,24 @@ typedef struct _VipsSelectSeq { } VipsSelectSeq; +int +vips_select_stop( void *vseq, void *a, void *b ) +{ + VipsSelectSeq *seq = (VipsSelectSeq *) vseq; + + if( seq->tests ) { + vips_stop_many( (void *) seq->tests, NULL, NULL ); + seq->tests = NULL; + } + if( seq->cases ) { + vips_stop_many( (void *) seq->cases, NULL, NULL ); + seq->cases = NULL; + } + VIPS_FREE( seq ); + + return( 0 ): +} + static void * vips_select_start( VipsImage *out, void *a, void *b ) { @@ -86,58 +104,26 @@ vips_select_start( VipsImage *out, void *a, void *b ) seq->tests = NULL; seq->cases = NULL; - /* How many images? - */ - for( n = 0; in[n]; n++ ) - ; - - /* Alocate space for region array. - */ - if( !(seq->ir = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) ) { - vips_bandary_stop( seq, NULL, NULL ); - return( NULL ); - } - - /* Create a set of regions. - */ - for( i = 0; i < n; i++ ) - if( !(seq->ir[i] = vips_region_new( in[i] )) ) { - vips_bandary_stop( seq, NULL, NULL ); - return( NULL ); - } - seq->ir[n] = NULL; - - /* Input pointers. - */ - if( !(seq->p = VIPS_ARRAY( NULL, n + 1, VipsPel * )) ) { - vips_bandary_stop( seq, NULL, NULL ); - return( NULL ); - } - - /* Pixel buffer. This is used as working space by some subclasses. - */ - if( !(seq->pixels = VIPS_ARRAY( NULL, - n * VIPS_IMAGE_SIZEOF_PEL( bandary->ready[0] ), VipsPel )) ) { - vips_bandary_stop( seq, NULL, NULL ); + seq->tests = vips_start_many( NULL, (void *) select->tests, NULL ); + seq->cases = vips_start_many( NULL, (void *) select->cases, NULL ); + if( !seq->tests || + !seq->cases ) { + vips_select_stop( (void *) seq, NULL, NULL ); return( NULL ); } return( seq ); } - -need start / stop and a seq typedef for our two arrays of regions - /* Do a map. */ static int -vips_select_gen( VipsRegion *or, void *seq, void *a, void *b, +vips_select_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) { - VipsRegion **ar = (VipsRegion **) seq; - VipsSelect *swit = (VipsSelect *) b; + VipsSelectSeq *seq = (VipsSelectSeq *) vseq; + VipsSelect *select = seq->select; VipsRect *r = &or->valid; - VipsRegion *index = ar[swit->n]; int x, y, i, j; VipsPel * restrict ip; From fc17a797679c48562b60f29ceddce53e0d4d0f9b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 17 Aug 2019 14:51:55 +0100 Subject: [PATCH 10/63] split into switch/case A neater solution! In Python it should be something like: merge = pyvips.Image.switch([tests]).case([cases]) and should be efficient --- libvips/conversion/Makefile.am | 2 +- libvips/conversion/conversion.c | 2 + libvips/conversion/select.c | 369 ------------------------- libvips/conversion/switch.c | 257 +++++++++++++++++ libvips/histogram/Makefile.am | 2 +- libvips/histogram/{switch.c => case.c} | 99 +++---- libvips/include/vips/conversion.h | 3 +- libvips/include/vips/histogram.h | 2 +- 8 files changed, 314 insertions(+), 422 deletions(-) delete mode 100644 libvips/conversion/select.c create mode 100644 libvips/conversion/switch.c rename libvips/histogram/{switch.c => case.c} (71%) diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index 752b277f..bd3e790b 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -1,7 +1,7 @@ noinst_LTLIBRARIES = libconversion.la libconversion_la_SOURCES = \ - select.c \ + switch.c \ transpose3d.c \ composite.cpp \ smartcrop.c \ diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 465c791d..51dcfb97 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -383,6 +383,7 @@ vips_conversion_operation_init( void ) extern GType vips_rot45_get_type( void ); extern GType vips_autorot_get_type( void ); extern GType vips_ifthenelse_get_type( void ); + extern GType vips_switch_get_type( void ); extern GType vips_recomb_get_type( void ); extern GType vips_bandmean_get_type( void ); extern GType vips_bandfold_get_type( void ); @@ -434,6 +435,7 @@ vips_conversion_operation_init( void ) vips_rot45_get_type(); vips_autorot_get_type(); vips_ifthenelse_get_type(); + vips_switch_get_type(); vips_recomb_get_type(); vips_bandmean_get_type(); vips_bandfold_get_type(); diff --git a/libvips/conversion/select.c b/libvips/conversion/select.c deleted file mode 100644 index ef495328..00000000 --- a/libvips/conversion/select.c +++ /dev/null @@ -1,369 +0,0 @@ -/* use pixel values to select between an array of images - * - * 28/7/19 - * - from maplut.c - */ - -/* - - 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 - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include -#include - -typedef struct _VipsSelect { - VipsOperation parent_instance; - - VipsArrayImage *tests; - VipsArrayImage *cases; - VipsImage *out; - - int n; - -} VipsSelect; - -typedef VipsOperationClass VipsSelectClass; - -G_DEFINE_TYPE( VipsSelect, vips_select, VIPS_TYPE_OPERATION ); - -/* Our sequence value. - */ -typedef struct _VipsSelectSeq { - VipsSelect *select; - - /* Set of input regions. - */ - VipsRegion **tests; - VipsRegion **cases; - -} VipsSelectSeq; - -int -vips_select_stop( void *vseq, void *a, void *b ) -{ - VipsSelectSeq *seq = (VipsSelectSeq *) vseq; - - if( seq->tests ) { - vips_stop_many( (void *) seq->tests, NULL, NULL ); - seq->tests = NULL; - } - if( seq->cases ) { - vips_stop_many( (void *) seq->cases, NULL, NULL ); - seq->cases = NULL; - } - VIPS_FREE( seq ); - - return( 0 ): -} - -static void * -vips_select_start( VipsImage *out, void *a, void *b ) -{ - VipsImage **in = (VipsImage **) a; - VipsSelect *select = (VipsSelect *) b; - - VipsSelectSeq *seq; - int i, n; - - if( !(seq = VIPS_NEW( NULL, VipsSelectSeq )) ) - return( NULL ); - - seq->select = select; - seq->tests = NULL; - seq->cases = NULL; - - seq->tests = vips_start_many( NULL, (void *) select->tests, NULL ); - seq->cases = vips_start_many( NULL, (void *) select->cases, NULL ); - if( !seq->tests || - !seq->cases ) { - vips_select_stop( (void *) seq, NULL, NULL ); - return( NULL ); - } - - return( seq ); -} - -/* Do a map. - */ -static int -vips_select_gen( VipsRegion *or, void *vseq, void *a, void *b, - gboolean *stop ) -{ - VipsSelectSeq *seq = (VipsSelectSeq *) vseq; - VipsSelect *select = seq->select; - VipsRect *r = &or->valid; - - int x, y, i, j; - VipsPel * restrict ip; - VipsPel * restrict q; - size_t ils; - size_t qls; - int hist[256]; - VipsPel * restrict p[256]; - size_t ls[256]; - size_t ps; - - if( vips_region_prepare( index, r ) ) - return( -1 ); - - g_assert( index->im->BandFmt == VIPS_FORMAT_UCHAR ); - g_assert( index->im->Bands == 1 ); - - /* Histogram of input region, so we know which of our inputs we will - * need to prepare. - */ - memset( hist, 0, 256 * sizeof( int ) ); - ip = VIPS_REGION_ADDR( index, r->left, r->top ); - ils = VIPS_REGION_LSKIP( index ); - for( y = 0; y < r->height; y++ ) { - for( x = 0; x < r->width; x++ ) - hist[ip[x]] += 1; - - ip += ils; - } - - for( i = 0; i < 256; i++ ) - if( hist[i] ) { - if( vips_region_prepare( ar[i], r ) ) - return( -1 ); - p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top ); - ls[i] = VIPS_REGION_LSKIP( ar[i] ); - } - - ip = VIPS_REGION_ADDR( index, r->left, r->top ); - q = VIPS_REGION_ADDR( or, r->left, r->top ); - qls = VIPS_REGION_LSKIP( or ); - ps = VIPS_IMAGE_SIZEOF_PEL( or->im ); - for( y = 0; y < r->height; y++ ) { - i = 0; - for( x = 0; x < r->width; x++ ) { - VipsPel * restrict pv = p[ip[x]]; - - for( j = 0; j < ps; j++ ) { - q[i] = pv[i]; - i += 1; - } - } - - ip += ils; - q += qls; - for( i = 0; i < 256; i++ ) - if( hist[i] ) - p[i] += ls[i]; - } - - return( 0 ); -} - -static int -vips_select_build( VipsObject *object ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsSelect *swit = (VipsSelect *) object; - VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); - - VipsImage *in; - VipsImage **lut; - VipsImage **decode; - VipsImage **format; - VipsImage **band; - VipsImage **size; - int i; - - g_object_set( object, "out", vips_image_new(), NULL ); - - if( VIPS_OBJECT_CLASS( vips_select_parent_class )->build( object ) ) - return( -1 ); - - in = swit->in; - lut = vips_area_get_data( &swit->lut->area, - NULL, &swit->n, NULL, NULL ); - if( swit->n > 256 ) { - vips_error( class->nickname, "%s", _( "LUT too large" ) ); - return( -1 ); - } - if( in->Bands > 1 ) { - vips_error( class->nickname, - "%s", _( "index image not 1-band" ) ); - return( -1 ); - } - - /* Cast @in to u8 to make the index image. - */ - if( vips_cast( in, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) - return( -1 ); - in = t[0]; - - decode = (VipsImage **) vips_object_local_array( object, swit->n ); - format = (VipsImage **) vips_object_local_array( object, swit->n ); - band = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); - size = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); - - /* Decode RAD/LABQ etc. - */ - for( i = 0; i < swit->n; i++ ) - if( vips_image_decode( lut[i], &decode[i] ) ) - return( -1 ); - lut = decode; - - /* LUT images must match in format, size and bands. - * - * We want everything sized up to the size of the index image, so add - * that to the end of the set of images for sizealike. - */ - band[swit->n] = in; - g_object_ref( in ); - if( vips__formatalike_vec( lut, format, swit->n ) || - vips__bandalike_vec( class->nickname, - format, band, swit->n, 1 ) || - vips__sizealike_vec( band, size, swit->n + 1 ) ) - return( -1 ); - lut = size; - - swit->out->BandFmt = lut[0]->BandFmt; - swit->out->Bands = lut[0]->Bands; - swit->out->Type = lut[0]->Type; - swit->out->Xsize = in->Xsize; - swit->out->Ysize = in->Ysize; - - if( vips_image_pipeline_array( swit->out, - VIPS_DEMAND_STYLE_THINSTRIP, lut ) ) - return( -1 ); - - if( vips_image_generate( swit->out, - vips_start_many, vips_select_gen, vips_stop_many, - lut, swit ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_select_class_init( VipsSelectClass *class ) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS( class ); - VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); - VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); - - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - object_class->nickname = "select"; - object_class->description = - _( "test images pick pixels from a set of case images" ); - object_class->build = vips_select_build; - - operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - - VIPS_ARG_BOXED( class, "tests", 1, - _( "Tests" ), - _( "Table of images to test" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsSelect, tests ), - VIPS_TYPE_ARRAY_IMAGE ); - - VIPS_ARG_BOXED( class, "cases", 2, - _( "Cases" ), - _( "Table of image cases" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsSelect, cases ), - VIPS_TYPE_ARRAY_IMAGE ); - - VIPS_ARG_IMAGE( class, "out", 3, - _( "Output" ), - _( "Output image" ), - VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsSelect, out ) ); - -} - -static void -vips_select_init( VipsSelect *swit ) -{ -} - -static int -vips_selectv( VipsImage **tests, VipsImage **cases, VipsImage **out, int n, - va_list ap ) -{ - VipsArrayImage *tests_array; - VipsArrayImage *cases_array; - int result; - - tests_array = vips_array_image_new( tests n ); - cases_array = vips_array_image_new( selects, n ); - result = vips_call_split( "select", ap, - tests_array, cases_array, out ); - vips_area_unref( VIPS_AREA( tests_array ) ); - vips_area_unref( VIPS_AREA( cases_array ) ); - - return( result ); -} - -/** - * vips_select: (method) - * @tests: (array length=n): test these images - * @cases: (array length=n): to pick between these images - * @out: (out): output image - * @n: number of input images - * @...: %NULL-terminated list of optional named arguments - * - * The @tests images are evaluated and the index of the first non-zero value - * in @tests is used to pick a pixel from @cases. If all @tests are false, the - * pixel from the final image is @cases is used. - * - * Images in @tests images must have one uchar band. @cases and @tests can - * have up to 256 elements. The images in @tests and @cases - * must have either one band or the same number of bands. The output image is - * the same size as @tests. Images in @cases are expanded to the smallest - * common format and number of bands. - * - * See also: vips_maplut(). - * - * Returns: 0 on success, -1 on error - */ -int -vips_select( VipsImage **tests, VipsImage **cases, VipsImage **out, int n, ... ) -{ - va_list ap; - int result; - - va_start( ap, n ); - result = vips_selectv( tests, cases, out, n, ap ); - va_end( ap ); - - return( result ); -} - diff --git a/libvips/conversion/switch.c b/libvips/conversion/switch.c new file mode 100644 index 00000000..ceec8c88 --- /dev/null +++ b/libvips/conversion/switch.c @@ -0,0 +1,257 @@ +/* switch between an array of images + * + * 28/7/19 + * - from maplut.c + */ + +/* + + 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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +typedef struct _VipsSwitch { + VipsOperation parent_instance; + + VipsArrayImage *tests; + VipsImage *out; + + int n; + +} VipsSwitch; + +typedef VipsOperationClass VipsSwitchClass; + +G_DEFINE_TYPE( VipsSwitch, vips_switch, VIPS_TYPE_OPERATION ); + +static int +vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b, + gboolean *stop ) +{ + VipsRegion **ar = (VipsRegion **) seq; + VipsSwitch *swit = (VipsSwitch *) b; + VipsRect *r = &or->valid; + + int x, y, i; + VipsPel * restrict q; + size_t qls; + VipsPel * restrict p[256]; + size_t ls[256]; + + if( vips_reorder_prepare_many( or->im, ar, r ) ) + return( -1 ); + + g_assert( ar->im->BandFmt == VIPS_FORMAT_UCHAR ); + g_assert( ar->im->Bands == 1 ); + + for( i = 0; i < swit->n; i++ ) { + p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top ); + ls[i] = VIPS_REGION_LSKIP( ar[i] ); + } + + q = VIPS_REGION_ADDR( or, r->left, r->top ); + qls = VIPS_REGION_LSKIP( or ); + for( y = 0; y < r->height; y++ ) { + for( x = 0; x < r->width; x++ ) { + for( i = 0; i < swit->n; i++ ) + if( p[i][x] ) + break; + + q[x] = i; + } + + q += qls; + for( i = 0; i < swit->n; i++ ) + p[i] += ls[i]; + } + + return( 0 ); +} + +static int +vips_switch_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsSwitch *swit = (VipsSwitch *) object; + + VipsImage **tests; + VipsImage **decode; + VipsImage **format; + VipsImage **band; + VipsImage **size; + int i; + + g_object_set( object, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) ) + return( -1 ); + + tests = vips_area_get_data( &swit->tests->area, + NULL, &swit->n, NULL, NULL ); + if( swit->n > 255 || + swit->n < 1 ) { + vips_error( class->nickname, "%s", _( "bad number of tests" ) ); + return( -1 ); + } + + decode = (VipsImage **) vips_object_local_array( object, swit->n ); + format = (VipsImage **) vips_object_local_array( object, swit->n ); + band = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + size = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + + /* Decode RAD/LABQ etc. + */ + for( i = 0; i < swit->n; i++ ) + if( vips_image_decode( tests[i], &decode[i] ) ) + return( -1 ); + tests = decode; + + /* Must be uchar. + */ + for( i = 0; i < swit->n; i++ ) + if( vips_cast_uchar( tests[i], &format[i], NULL ) ) + return( -1 ); + tests = format; + + /* Images must match in size and bands. + */ + if( vips__bandalike_vec( class->nickname, tests, band, swit->n, 1 ) || + vips__sizealike_vec( band, size, swit->n + 1 ) ) + return( -1 ); + tests = size; + + if( tests[0]->Bands > 1 ) { + vips_error( class->nickname, + "%s", _( "test images not 1-band" ) ); + return( -1 ); + } + + if( vips_image_pipeline_array( swit->out, + VIPS_DEMAND_STYLE_THINSTRIP, tests ) ) + return( -1 ); + + if( vips_image_generate( swit->out, + vips_start_many, vips_switch_gen, vips_stop_many, + tests, swit ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_switch_class_init( VipsSwitchClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "switch"; + object_class->description = + _( "find the index of the first non-zero pixel in tests" ); + object_class->build = vips_switch_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_BOXED( class, "tests", 1, + _( "Tests" ), + _( "Table of images to test" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSwitch, tests ), + VIPS_TYPE_ARRAY_IMAGE ); + + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsSwitch, out ) ); + +} + +static void +vips_switch_init( VipsSwitch *swit ) +{ +} + +static int +vips_switchv( VipsImage **tests, VipsImage **out, int n, va_list ap ) +{ + VipsArrayImage *tests_array; + int result; + + tests_array = vips_array_image_new( tests, n ); + result = vips_call_split( "switch", ap, tests_array, out ); + vips_area_unref( VIPS_AREA( tests_array ) ); + + return( result ); +} + +/** + * vips_switch: (method) + * @tests: (array length=n): test these images + * @out: (out): output index image + * @n: number of input images + * @...: %NULL-terminated list of optional named arguments + * + * The @tests images are evaluated and at each point the index of the first + * non-zero value is written to @out. If all @tests are false, the value + * (@n + 1) is written. + * + * Images in @tests must have one band. They are expanded to the + * bounding box of the set of images in @tests, and that size is used for + * @out. @tests can have up to 255 elements. + * + * Combine with vips_case() to make an efficient multi-way vips_ifthenelse(). + * + * See also: vips_maplut(), vips_case(), vips_ifthenelse(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_switch( VipsImage **tests, VipsImage **out, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_switchv( tests, out, n, ap ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index 9703a8d0..2e901388 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -4,7 +4,7 @@ libhistogram_la_SOURCES = \ histogram.c \ phistogram.h \ maplut.c \ - switch.c \ + case.c \ hist_unary.c \ hist_unary.h \ hist_cum.c \ diff --git a/libvips/histogram/switch.c b/libvips/histogram/case.c similarity index 71% rename from libvips/histogram/switch.c rename to libvips/histogram/case.c index 2054dce0..ee7cf37b 100644 --- a/libvips/histogram/switch.c +++ b/libvips/histogram/case.c @@ -1,4 +1,4 @@ -/* use pixel values to switch between an array of images +/* use pixel values to pick cases from an array of images * * 28/7/19 * - from maplut.c @@ -43,7 +43,7 @@ #include #include -typedef struct _VipsSwitch { +typedef struct _VipsCase { VipsOperation parent_instance; VipsImage *in; @@ -51,22 +51,22 @@ typedef struct _VipsSwitch { VipsArrayImage *lut; int n; -} VipsSwitch; +} VipsCase; -typedef VipsOperationClass VipsSwitchClass; +typedef VipsOperationClass VipsCaseClass; -G_DEFINE_TYPE( VipsSwitch, vips_switch, VIPS_TYPE_OPERATION ); +G_DEFINE_TYPE( VipsCase, vips_case, VIPS_TYPE_OPERATION ); /* Do a map. */ static int -vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b, +vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion **ar = (VipsRegion **) seq; - VipsSwitch *swit = (VipsSwitch *) b; + VipsCase *cas = (VipsCase *) b; VipsRect *r = &or->valid; - VipsRegion *index = ar[swit->n]; + VipsRegion *index = ar[cas->n]; int x, y, i, j; VipsPel * restrict ip; @@ -131,10 +131,10 @@ vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b, } static int -vips_switch_build( VipsObject *object ) +vips_case_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsSwitch *swit = (VipsSwitch *) object; + VipsCase *cas = (VipsCase *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *in; @@ -147,13 +147,13 @@ vips_switch_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) ) + if( VIPS_OBJECT_CLASS( vips_case_parent_class )->build( object ) ) return( -1 ); - in = swit->in; - lut = vips_area_get_data( &swit->lut->area, - NULL, &swit->n, NULL, NULL ); - if( swit->n > 256 ) { + in = cas->in; + lut = vips_area_get_data( &cas->lut->area, + NULL, &cas->n, NULL, NULL ); + if( cas->n > 256 ) { vips_error( class->nickname, "%s", _( "LUT too large" ) ); return( -1 ); } @@ -169,14 +169,14 @@ vips_switch_build( VipsObject *object ) return( -1 ); in = t[0]; - decode = (VipsImage **) vips_object_local_array( object, swit->n ); - format = (VipsImage **) vips_object_local_array( object, swit->n ); - band = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); - size = (VipsImage **) vips_object_local_array( object, swit->n + 1 ); + decode = (VipsImage **) vips_object_local_array( object, cas->n ); + format = (VipsImage **) vips_object_local_array( object, cas->n ); + band = (VipsImage **) vips_object_local_array( object, cas->n + 1 ); + size = (VipsImage **) vips_object_local_array( object, cas->n + 1 ); /* Decode RAD/LABQ etc. */ - for( i = 0; i < swit->n; i++ ) + for( i = 0; i < cas->n; i++ ) if( vips_image_decode( lut[i], &decode[i] ) ) return( -1 ); lut = decode; @@ -186,35 +186,35 @@ vips_switch_build( VipsObject *object ) * We want everything sized up to the size of the index image, so add * that to the end of the set of images for sizealike. */ - band[swit->n] = in; + band[cas->n] = in; g_object_ref( in ); - if( vips__formatalike_vec( lut, format, swit->n ) || + if( vips__formatalike_vec( lut, format, cas->n ) || vips__bandalike_vec( class->nickname, - format, band, swit->n, 1 ) || - vips__sizealike_vec( band, size, swit->n + 1 ) ) + format, band, cas->n, 1 ) || + vips__sizealike_vec( band, size, cas->n + 1 ) ) return( -1 ); lut = size; - swit->out->BandFmt = lut[0]->BandFmt; - swit->out->Bands = lut[0]->Bands; - swit->out->Type = lut[0]->Type; - swit->out->Xsize = in->Xsize; - swit->out->Ysize = in->Ysize; + cas->out->BandFmt = lut[0]->BandFmt; + cas->out->Bands = lut[0]->Bands; + cas->out->Type = lut[0]->Type; + cas->out->Xsize = in->Xsize; + cas->out->Ysize = in->Ysize; - if( vips_image_pipeline_array( swit->out, + if( vips_image_pipeline_array( cas->out, VIPS_DEMAND_STYLE_THINSTRIP, lut ) ) return( -1 ); - if( vips_image_generate( swit->out, - vips_start_many, vips_switch_gen, vips_stop_many, - lut, swit ) ) + if( vips_image_generate( cas->out, + vips_start_many, vips_case_gen, vips_stop_many, + lut, cas ) ) return( -1 ); return( 0 ); } static void -vips_switch_class_init( VipsSwitchClass *class ) +vips_case_class_init( VipsCaseClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); @@ -223,10 +223,10 @@ vips_switch_class_init( VipsSwitchClass *class ) gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "switch"; + object_class->nickname = "case"; object_class->description = - _( "use pixel values to switch between a set of images" ); - object_class->build = vips_switch_build; + _( "use pixel values to case between a set of images" ); + object_class->build = vips_case_build; operation_class->flags = VIPS_OPERATION_SEQUENTIAL; @@ -234,51 +234,51 @@ vips_switch_class_init( VipsSwitchClass *class ) _( "Input" ), _( "Input image" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsSwitch, in ) ); + G_STRUCT_OFFSET( VipsCase, in ) ); VIPS_ARG_IMAGE( class, "out", 2, _( "Output" ), _( "Output image" ), VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsSwitch, out ) ); + G_STRUCT_OFFSET( VipsCase, out ) ); VIPS_ARG_BOXED( class, "lut", 3, _( "LUT" ), _( "Look-up table of images" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsSwitch, lut ), + G_STRUCT_OFFSET( VipsCase, lut ), VIPS_TYPE_ARRAY_IMAGE ); } static void -vips_switch_init( VipsSwitch *swit ) +vips_case_init( VipsCase *cas ) { } static int -vips_switchv( VipsImage *in, VipsImage **out, VipsImage **lut, int n, +vips_casev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, va_list ap ) { VipsArrayImage *array; int result; array = vips_array_image_new( lut, n ); - result = vips_call_split( "switch", ap, in, out, array ); + result = vips_call_split( "case", ap, in, out, array ); vips_area_unref( VIPS_AREA( array ) ); return( result ); } /** - * vips_switch: (method) + * vips_case: (method) * @in: input image * @out: (out): output image * @lut: (array length=n): LUT of input images * @n: number of input images * @...: %NULL-terminated list of optional named arguments * - * Use pixel values to switch between an array of images. + * Use pixel values to pick cases from an array of images. * * Each value in @in is used to select an image from @lut, and the * corresponding pixel is copied to the output. @@ -289,18 +289,21 @@ vips_switchv( VipsImage *in, VipsImage **out, VipsImage **lut, int n, * the same size as @in. Images in @lut are expanded to the smallest common * format and number of bands. * - * See also: vips_maplut(). + * Combine this with vips_switch() to make something like a case statement, or + * a multi-way vips_ifthenelse(). + * + * See also: vips_maplut(), vips_switch(), vips_ifthenelse(). * * Returns: 0 on success, -1 on error */ int -vips_switch( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) { va_list ap; int result; va_start( ap, n ); - result = vips_switchv( in, out, lut, n, ap ); + result = vips_casev( in, out, lut, n, ap ); va_end( ap ); return( result ); diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 4e893840..109b456e 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -262,8 +262,7 @@ int vips_recomb( VipsImage *in, VipsImage **out, VipsImage *m, ... ) int vips_ifthenelse( VipsImage *cond, VipsImage *in1, VipsImage *in2, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_select( VipsImage **tests, VipsImage **cases, - VipsImage **out, int n, ... ) +int vips_switch( VipsImage **tests, VipsImage **out, int n, ... ) __attribute__((sentinel)); int vips_flatten( VipsImage *in, VipsImage **out, ... ) diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index 573c3e2d..c7a2201d 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -64,7 +64,7 @@ int vips_hist_ismonotonic( VipsImage *in, gboolean *out, ... ) int vips_hist_entropy( VipsImage *in, double *out, ... ) __attribute__((sentinel)); -int vips_switch( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +int vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) __attribute__((sentinel)); #ifdef __cplusplus From daf578ca421e76ad390bccea080be52419f9146c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 18 Aug 2019 16:35:28 +0100 Subject: [PATCH 11/63] seems to work now a switch/case pair --- libvips/conversion/switch.c | 5 +- libvips/histogram/case.c | 144 ++++++++++++++++--------------- libvips/histogram/histogram.c | 4 +- libvips/include/vips/histogram.h | 3 +- 4 files changed, 81 insertions(+), 75 deletions(-) diff --git a/libvips/conversion/switch.c b/libvips/conversion/switch.c index ceec8c88..055b372b 100644 --- a/libvips/conversion/switch.c +++ b/libvips/conversion/switch.c @@ -119,6 +119,9 @@ vips_switch_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) ) return( -1 ); + /* 255 rather than 256, since we want to reserve +1 as the no + * match value. + */ tests = vips_area_get_data( &swit->tests->area, NULL, &swit->n, NULL, NULL ); if( swit->n > 255 || @@ -149,7 +152,7 @@ vips_switch_build( VipsObject *object ) /* Images must match in size and bands. */ if( vips__bandalike_vec( class->nickname, tests, band, swit->n, 1 ) || - vips__sizealike_vec( band, size, swit->n + 1 ) ) + vips__sizealike_vec( band, size, swit->n ) ) return( -1 ); tests = size; diff --git a/libvips/histogram/case.c b/libvips/histogram/case.c index ee7cf37b..8ed97a97 100644 --- a/libvips/histogram/case.c +++ b/libvips/histogram/case.c @@ -46,9 +46,9 @@ typedef struct _VipsCase { VipsOperation parent_instance; - VipsImage *in; + VipsImage *index; + VipsArrayImage *cases; VipsImage *out; - VipsArrayImage *lut; int n; } VipsCase; @@ -57,8 +57,6 @@ typedef VipsOperationClass VipsCaseClass; G_DEFINE_TYPE( VipsCase, vips_case, VIPS_TYPE_OPERATION ); -/* Do a map. - */ static int vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) @@ -68,7 +66,7 @@ vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, VipsRect *r = &or->valid; VipsRegion *index = ar[cas->n]; - int x, y, i, j; + int x, y, i; VipsPel * restrict ip; VipsPel * restrict q; size_t ils; @@ -84,20 +82,23 @@ vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, g_assert( index->im->BandFmt == VIPS_FORMAT_UCHAR ); g_assert( index->im->Bands == 1 ); - /* Histogram of input region, so we know which of our inputs we will + /* Histogram of index region, so we know which of our inputs we will * need to prepare. */ - memset( hist, 0, 256 * sizeof( int ) ); + memset( hist, 0, cas->n * sizeof( int ) ); ip = VIPS_REGION_ADDR( index, r->left, r->top ); ils = VIPS_REGION_LSKIP( index ); for( y = 0; y < r->height; y++ ) { - for( x = 0; x < r->width; x++ ) - hist[ip[x]] += 1; + for( x = 0; x < r->width; x++ ) { + int v = VIPS_MIN( ip[x], cas->n - 1 ); + + hist[v] += 1; + } ip += ils; } - for( i = 0; i < 256; i++ ) + for( i = 0; i < cas->n; i++ ) if( hist[i] ) { if( vips_region_prepare( ar[i], r ) ) return( -1 ); @@ -110,19 +111,24 @@ vips_case_gen( VipsRegion *or, void *seq, void *a, void *b, qls = VIPS_REGION_LSKIP( or ); ps = VIPS_IMAGE_SIZEOF_PEL( or->im ); for( y = 0; y < r->height; y++ ) { - i = 0; + int k; + + k = 0; for( x = 0; x < r->width; x++ ) { - VipsPel * restrict pv = p[ip[x]]; + int v = VIPS_MIN( ip[x], cas->n - 1 ); + VipsPel * restrict pv = p[v]; + + int j; for( j = 0; j < ps; j++ ) { - q[i] = pv[i]; - i += 1; + q[k] = pv[k]; + k += 1; } } ip += ils; q += qls; - for( i = 0; i < 256; i++ ) + for( i = 0; i < cas->n; i++ ) if( hist[i] ) p[i] += ls[i]; } @@ -137,8 +143,8 @@ vips_case_build( VipsObject *object ) VipsCase *cas = (VipsCase *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); - VipsImage *in; - VipsImage **lut; + VipsImage *index; + VipsImage **cases; VipsImage **decode; VipsImage **format; VipsImage **band; @@ -150,24 +156,25 @@ vips_case_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_case_parent_class )->build( object ) ) return( -1 ); - in = cas->in; - lut = vips_area_get_data( &cas->lut->area, + index = cas->index; + cases = vips_area_get_data( &cas->cases->area, NULL, &cas->n, NULL, NULL ); - if( cas->n > 256 ) { - vips_error( class->nickname, "%s", _( "LUT too large" ) ); + if( cas->n > 256 || + cas->n < 1 ) { + vips_error( class->nickname, "%s", _( "bad number of cases" ) ); return( -1 ); } - if( in->Bands > 1 ) { + if( index->Bands > 1 ) { vips_error( class->nickname, "%s", _( "index image not 1-band" ) ); return( -1 ); } - /* Cast @in to u8 to make the index image. + /* Cast @index to u8 to make the index image. */ - if( vips_cast( in, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) + if( vips_cast( index, &t[0], VIPS_FORMAT_UCHAR, NULL ) ) return( -1 ); - in = t[0]; + index = t[0]; decode = (VipsImage **) vips_object_local_array( object, cas->n ); format = (VipsImage **) vips_object_local_array( object, cas->n ); @@ -177,37 +184,35 @@ vips_case_build( VipsObject *object ) /* Decode RAD/LABQ etc. */ for( i = 0; i < cas->n; i++ ) - if( vips_image_decode( lut[i], &decode[i] ) ) + if( vips_image_decode( cases[i], &decode[i] ) ) return( -1 ); - lut = decode; + cases = decode; - /* LUT images must match in format, size and bands. + /* case images must match in format, size and bands. * * We want everything sized up to the size of the index image, so add * that to the end of the set of images for sizealike. */ - band[cas->n] = in; - g_object_ref( in ); - if( vips__formatalike_vec( lut, format, cas->n ) || + band[cas->n] = index; + g_object_ref( index ); + if( vips__formatalike_vec( cases, format, cas->n ) || vips__bandalike_vec( class->nickname, format, band, cas->n, 1 ) || vips__sizealike_vec( band, size, cas->n + 1 ) ) return( -1 ); - lut = size; - - cas->out->BandFmt = lut[0]->BandFmt; - cas->out->Bands = lut[0]->Bands; - cas->out->Type = lut[0]->Type; - cas->out->Xsize = in->Xsize; - cas->out->Ysize = in->Ysize; + cases = size; if( vips_image_pipeline_array( cas->out, - VIPS_DEMAND_STYLE_THINSTRIP, lut ) ) + VIPS_DEMAND_STYLE_THINSTRIP, cases ) ) return( -1 ); + cas->out->BandFmt = cases[0]->BandFmt; + cas->out->Bands = cases[0]->Bands; + cas->out->Type = cases[0]->Type; + if( vips_image_generate( cas->out, vips_start_many, vips_case_gen, vips_stop_many, - lut, cas ) ) + cases, cas ) ) return( -1 ); return( 0 ); @@ -225,30 +230,30 @@ vips_case_class_init( VipsCaseClass *class ) object_class->nickname = "case"; object_class->description = - _( "use pixel values to case between a set of images" ); + _( "use pixel values to pick cases from an array of images" ); object_class->build = vips_case_build; operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - VIPS_ARG_IMAGE( class, "in", 1, - _( "Input" ), - _( "Input image" ), + VIPS_ARG_IMAGE( class, "index", 1, + _( "index" ), + _( "Index image" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsCase, in ) ); + G_STRUCT_OFFSET( VipsCase, index ) ); - VIPS_ARG_IMAGE( class, "out", 2, + VIPS_ARG_BOXED( class, "cases", 2, + _( "cases" ), + _( "Array of case images" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsCase, cases ), + VIPS_TYPE_ARRAY_IMAGE ); + + VIPS_ARG_IMAGE( class, "out", 3, _( "Output" ), _( "Output image" ), VIPS_ARGUMENT_REQUIRED_OUTPUT, G_STRUCT_OFFSET( VipsCase, out ) ); - VIPS_ARG_BOXED( class, "lut", 3, - _( "LUT" ), - _( "Look-up table of images" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsCase, lut ), - VIPS_TYPE_ARRAY_IMAGE ); - } static void @@ -257,14 +262,14 @@ vips_case_init( VipsCase *cas ) } static int -vips_casev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, +vips_casev( VipsImage *index, VipsImage **cases, VipsImage **out, int n, va_list ap ) { VipsArrayImage *array; int result; - array = vips_array_image_new( lut, n ); - result = vips_call_split( "case", ap, in, out, array ); + array = vips_array_image_new( cases, n ); + result = vips_call_split( "case", ap, index, array, out ); vips_area_unref( VIPS_AREA( array ) ); return( result ); @@ -272,24 +277,21 @@ vips_casev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, /** * vips_case: (method) - * @in: input image + * @index: index image + * @cases: (array length=n): array of case images * @out: (out): output image - * @lut: (array length=n): LUT of input images - * @n: number of input images + * @n: number of case images * @...: %NULL-terminated list of optional named arguments * - * Use pixel values to pick cases from an array of images. + * Use values in @index to select pixels from @cases. * - * Each value in @in is used to select an image from @lut, and the - * corresponding pixel is copied to the output. + * @index must have one band. @cases can have up to 256 elements. Values in + * @index greater than or equal to @n use the final image in @cases. The + * images in @cases must have either one band or the same number of bands. + * The output image is the same size as @index. Images in @cases are + * expanded to the smallest common format and number of bands. * - * @in must have one band. @lut can have up to 256 elements. Values in @in - * greater than or equal to @n use the final image in @lut. The images in @lut - * must have either one band or the same number of bands. The output image is - * the same size as @in. Images in @lut are expanded to the smallest common - * format and number of bands. - * - * Combine this with vips_switch() to make something like a case statement, or + * Combine this with vips_switch() to make something like a case statement or * a multi-way vips_ifthenelse(). * * See also: vips_maplut(), vips_switch(), vips_ifthenelse(). @@ -297,13 +299,13 @@ vips_casev( VipsImage *in, VipsImage **out, VipsImage **lut, int n, * Returns: 0 on success, -1 on error */ int -vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +vips_case( VipsImage *index, VipsImage **cases, VipsImage **out, int n, ... ) { va_list ap; int result; va_start( ap, n ); - result = vips_casev( in, out, lut, n, ap ); + result = vips_casev( index, cases, out, n, ap ); va_end( ap ); return( result ); diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index c0c97ea1..a7c51b15 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -251,7 +251,7 @@ void vips_histogram_operation_init( void ) { extern GType vips_maplut_get_type( void ); - extern GType vips_switch_get_type( void ); + extern GType vips_case_get_type( void ); extern GType vips_percent_get_type( void ); extern GType vips_hist_cum_get_type( void ); extern GType vips_hist_norm_get_type( void ); @@ -264,7 +264,7 @@ vips_histogram_operation_init( void ) extern GType vips_stdif_get_type( void ); vips_maplut_get_type(); - vips_switch_get_type(); + vips_case_get_type(); vips_percent_get_type(); vips_stdif_get_type(); vips_hist_cum_get_type(); diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index c7a2201d..039b9d94 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -64,7 +64,8 @@ int vips_hist_ismonotonic( VipsImage *in, gboolean *out, ... ) int vips_hist_entropy( VipsImage *in, double *out, ... ) __attribute__((sentinel)); -int vips_case( VipsImage *in, VipsImage **out, VipsImage **lut, int n, ... ) +int vips_case( VipsImage *index, VipsImage **cases, VipsImage **out, int n, + ... ) __attribute__((sentinel)); #ifdef __cplusplus From 2d0c21279a193980156b82be6afa396b3a79feca Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 19 Aug 2019 16:27:44 +0100 Subject: [PATCH 12/63] add tests --- ChangeLog | 1 + test/test-suite/test_conversion.py | 21 +++++++++++++++++++++ test/test-suite/test_histogram.py | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/ChangeLog b/ChangeLog index e3a0ef8b..871fc395 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ - support webp and zstd compression in tiff - loaders use "minimise" to close input files earlier - integrate support for oss-fuzz [omira-sch] +- add vips_switch() / vips_case() ... fast many-way ifthenelse 9/7/19 started 8.8.2 - better early shutdown in readers diff --git a/test/test-suite/test_conversion.py b/test/test-suite/test_conversion.py index 73409ee3..afd29b22 100644 --- a/test/test-suite/test_conversion.py +++ b/test/test-suite/test_conversion.py @@ -539,6 +539,27 @@ class TestConversion: result = r(50, 50) assert_almost_equal_objects(result, [3.0, 4.9, 6.9], threshold=0.1) + def test_switch(self): + x = pyvips.Image.grey(256, 256, uchar=True) + + # slice into two at 128, we should get 50% of pixels in each half + index = pyvips.Image.switch([x < 128, x >= 128]) + assert index.avg() == 0.5 + + # slice into four + index = pyvips.Image.switch([ + x < 64, + x >= 64 and x < 128, + x >= 128 and x < 192, + x >= 192 + ]) + assert index.avg() == 1.5 + + # no match should return n + 1 + # FIXME uncomment when we fix relational const + #index = pyvips.Image.switch([x == 1000, x == 2000]) + #assert index.avg() == 2 + def test_insert(self): for x in all_formats: for y in all_formats: diff --git a/test/test-suite/test_histogram.py b/test/test-suite/test_histogram.py index 765b33e1..a709dcfa 100644 --- a/test/test-suite/test_histogram.py +++ b/test/test-suite/test_histogram.py @@ -109,6 +109,25 @@ class TestHistogram: # new mean should be closer to target mean assert abs(im.avg() - 128) > abs(im2.avg() - 128) + def test_case(self): + # slice into two at 128, we should get 50% of pixels in each half + x = pyvips.Image.grey(256, 256, uchar=True) + index = pyvips.Image.switch([x < 128, x >= 128]) + + y = index.case([10, 20]) + assert y.avg() == 15 + + # slice into four + index = pyvips.Image.switch([ + x < 64, + x >= 64 and x < 128, + x >= 128 and x < 192, + x >= 192 + ]) + assert index.case([10, 20, 30, 40]).avg() == 25 + + # values over N should use the last value + assert index.case([10, 20, 30]).avg() == 22.5 if __name__ == '__main__': pytest.main() From d70c4319615417390b12aef5e90546bbf16fb78d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 19 Aug 2019 16:44:32 +0100 Subject: [PATCH 13/63] relax HEIC threshold argh rounding with different lib versions --- test/test-suite/test_foreign.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index 89bb8f34..6856418c 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -842,7 +842,9 @@ class TestForeign: def test_heifload(self): def heif_valid(im): a = im(10, 10) - assert_almost_equal_objects(a, [75.0, 86.0, 81.0]) + # different versions of HEIC decode have slightly different + # rounding + assert_almost_equal_objects(a, [75.0, 86.0, 81.0], threshold=2) assert im.width == 4032 assert im.height == 3024 assert im.bands == 3 From 16a5cac2e352cfd661ee56f113103d451afd8b64 Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Mon, 19 Aug 2019 15:59:55 +0200 Subject: [PATCH 14/63] add fuzzers for vips_smartcrop and vip_mosaic --- fuzz/Makefile.am | 4 +- fuzz/mosaic_fuzzer.cc | 63 ++++++++++++++++++++++++++++++ fuzz/mosaic_fuzzer_corpus/.keep | 0 fuzz/smartcrop_fuzzer.cc | 39 ++++++++++++++++++ fuzz/smartcrop_fuzzer_corpus/.keep | 0 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 fuzz/mosaic_fuzzer.cc create mode 100644 fuzz/mosaic_fuzzer_corpus/.keep create mode 100644 fuzz/smartcrop_fuzzer.cc create mode 100644 fuzz/smartcrop_fuzzer_corpus/.keep diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am index 6169da14..64f75318 100644 --- a/fuzz/Makefile.am +++ b/fuzz/Makefile.am @@ -6,7 +6,9 @@ FUZZPROGS = \ pngsave_buffer_fuzzer \ webpsave_buffer_fuzzer \ sharpen_fuzzer \ - thumbnail_fuzzer + thumbnail_fuzzer \ + smartcrop_fuzzer \ + mosaic_fuzzer AM_DEFAULT_SOURCE_EXT = .cc diff --git a/fuzz/mosaic_fuzzer.cc b/fuzz/mosaic_fuzzer.cc new file mode 100644 index 00000000..a7b7a12d --- /dev/null +++ b/fuzz/mosaic_fuzzer.cc @@ -0,0 +1,63 @@ +#include + +struct mosaic_opt { + guint8 dir : 1; + guint16 xref; + guint16 yref; + guint16 xsec; + guint16 ysec; +}; + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *ref, *sec, *out; + struct mosaic_opt *opt; + double d; + + if( size < sizeof(struct mosaic_opt) ) + return( 0 ); + + if( !(ref = vips_image_new_from_buffer( data, size, "", NULL )) ) + return( 0 ); + + /* Skip big images. They are likely to timeout. + */ + if( ref->Xsize > 1024 || + ref->Ysize > 1024 || + ref->Bands > 10 ) { + g_object_unref( ref ); + return( 0 ); + } + + if( vips_rot180( ref, &sec, NULL ) ) { + g_object_unref( ref ); + return( 0 ); + } + + /* Extract some bytes from the tail to fuzz the arguments of the API. + */ + opt = (struct mosaic_opt *) (data + size - sizeof(struct mosaic_opt)); + + if( vips_mosaic( ref, sec, &out, (VipsDirection) opt->dir, + opt->xref, opt->yref, opt->xsec, opt->ysec, NULL ) ) { + g_object_unref( sec ); + g_object_unref( ref ); + return( 0 ); + } + + vips_max( out, &d, NULL ); + + g_object_unref( out ); + g_object_unref( sec ); + g_object_unref( ref ); + + return( 0 ); +} diff --git a/fuzz/mosaic_fuzzer_corpus/.keep b/fuzz/mosaic_fuzzer_corpus/.keep new file mode 100644 index 00000000..e69de29b diff --git a/fuzz/smartcrop_fuzzer.cc b/fuzz/smartcrop_fuzzer.cc new file mode 100644 index 00000000..30e349d1 --- /dev/null +++ b/fuzz/smartcrop_fuzzer.cc @@ -0,0 +1,39 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + VipsImage *image, *out; + double d; + + if( !(image = vips_image_new_from_buffer( data, size, "", NULL )) ) + return( 0 ); + + /* Skip big images. They are likely to timeout. + */ + if( image->Xsize > 1024 || + image->Ysize > 1024 || + image->Bands > 10 ) { + g_object_unref( image ); + return( 0 ); + } + + if( vips_smartcrop( image, &out, 32, 32, NULL ) ) { + g_object_unref( image ); + return( 0 ); + } + + vips_min( out, &d, NULL ); + + g_object_unref( out ); + g_object_unref( image ); + + return( 0 ); +} diff --git a/fuzz/smartcrop_fuzzer_corpus/.keep b/fuzz/smartcrop_fuzzer_corpus/.keep new file mode 100644 index 00000000..e69de29b From a3466f305c5be56de2076b682b249a0ee578726e Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Mon, 19 Aug 2019 18:45:03 +0200 Subject: [PATCH 15/63] move fuzzing corpus into a single dir --- .../.keep | 0 ...inimized-jpegsave_buffer_fuzzer-5658586599915520 | Bin ...minimized-pngsave_buffer_fuzzer-5078454764044288 | Bin ...stcase-minimized-sharpen_fuzzer-5678720198639616 | Bin ...stcase-minimized-sharpen_fuzzer-5691855517253632 | Bin ...case-minimized-thumbnail_fuzzer-5676300823429120 | Bin ...case-minimized-thumbnail_fuzzer-5718717719117824 | Bin ...case-minimized-thumbnail_fuzzer-5741423734816768 | Bin ...inimized-webpsave_buffer_fuzzer-5207224829345792 | Bin ...inimized-webpsave_buffer_fuzzer-5674696418263040 | Bin fuzz/pngsave_buffer_fuzzer_corpus/.keep | 0 fuzz/sharpen_fuzzer_corpus/.keep | 0 fuzz/test_fuzz.sh | 2 +- fuzz/thumbnail_fuzzer_corpus/.keep | 0 fuzz/webpsave_buffer_fuzzer_corpus/.keep | 0 15 files changed, 1 insertion(+), 1 deletion(-) rename fuzz/{jpegsave_buffer_fuzzer_corpus => common_fuzzer_corpus}/.keep (100%) rename fuzz/{jpegsave_buffer_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5658586599915520 (100%) rename fuzz/{pngsave_buffer_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-pngsave_buffer_fuzzer-5078454764044288 (100%) rename fuzz/{sharpen_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-sharpen_fuzzer-5678720198639616 (100%) rename fuzz/{sharpen_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-sharpen_fuzzer-5691855517253632 (100%) rename fuzz/{thumbnail_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5676300823429120 (100%) rename fuzz/{thumbnail_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5718717719117824 (100%) rename fuzz/{thumbnail_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5741423734816768 (100%) rename fuzz/{webpsave_buffer_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5207224829345792 (100%) rename fuzz/{webpsave_buffer_fuzzer_corpus => common_fuzzer_corpus}/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5674696418263040 (100%) delete mode 100644 fuzz/pngsave_buffer_fuzzer_corpus/.keep delete mode 100644 fuzz/sharpen_fuzzer_corpus/.keep delete mode 100644 fuzz/thumbnail_fuzzer_corpus/.keep delete mode 100644 fuzz/webpsave_buffer_fuzzer_corpus/.keep diff --git a/fuzz/jpegsave_buffer_fuzzer_corpus/.keep b/fuzz/common_fuzzer_corpus/.keep similarity index 100% rename from fuzz/jpegsave_buffer_fuzzer_corpus/.keep rename to fuzz/common_fuzzer_corpus/.keep diff --git a/fuzz/jpegsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5658586599915520 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5658586599915520 similarity index 100% rename from fuzz/jpegsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5658586599915520 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5658586599915520 diff --git a/fuzz/pngsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-pngsave_buffer_fuzzer-5078454764044288 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-pngsave_buffer_fuzzer-5078454764044288 similarity index 100% rename from fuzz/pngsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-pngsave_buffer_fuzzer-5078454764044288 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-pngsave_buffer_fuzzer-5078454764044288 diff --git a/fuzz/sharpen_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5678720198639616 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5678720198639616 similarity index 100% rename from fuzz/sharpen_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5678720198639616 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5678720198639616 diff --git a/fuzz/sharpen_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5691855517253632 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5691855517253632 similarity index 100% rename from fuzz/sharpen_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5691855517253632 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5691855517253632 diff --git a/fuzz/thumbnail_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5676300823429120 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5676300823429120 similarity index 100% rename from fuzz/thumbnail_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5676300823429120 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5676300823429120 diff --git a/fuzz/thumbnail_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5718717719117824 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5718717719117824 similarity index 100% rename from fuzz/thumbnail_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5718717719117824 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5718717719117824 diff --git a/fuzz/thumbnail_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5741423734816768 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5741423734816768 similarity index 100% rename from fuzz/thumbnail_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5741423734816768 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-thumbnail_fuzzer-5741423734816768 diff --git a/fuzz/webpsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5207224829345792 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5207224829345792 similarity index 100% rename from fuzz/webpsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5207224829345792 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5207224829345792 diff --git a/fuzz/webpsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5674696418263040 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5674696418263040 similarity index 100% rename from fuzz/webpsave_buffer_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5674696418263040 rename to fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-webpsave_buffer_fuzzer-5674696418263040 diff --git a/fuzz/pngsave_buffer_fuzzer_corpus/.keep b/fuzz/pngsave_buffer_fuzzer_corpus/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/fuzz/sharpen_fuzzer_corpus/.keep b/fuzz/sharpen_fuzzer_corpus/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/fuzz/test_fuzz.sh b/fuzz/test_fuzz.sh index dd4decb5..e536e113 100755 --- a/fuzz/test_fuzz.sh +++ b/fuzz/test_fuzz.sh @@ -14,7 +14,7 @@ export VIPS_WARNING=0 ret=0 for fuzzer in *_fuzzer; do - find "${fuzzer}_corpus" -type f -not -empty -print0 \ + find "common_fuzzer_corpus" -type f -not -empty -print0 \ | xargs -0 -n1 "./$fuzzer" || ret=1 done diff --git a/fuzz/thumbnail_fuzzer_corpus/.keep b/fuzz/thumbnail_fuzzer_corpus/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/fuzz/webpsave_buffer_fuzzer_corpus/.keep b/fuzz/webpsave_buffer_fuzzer_corpus/.keep deleted file mode 100644 index e69de29b..00000000 From 8d028420d50a983071dfaaf21523459dd3059c28 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Mon, 19 Aug 2019 19:32:59 +0100 Subject: [PATCH 16/63] WebP loader: verify upper limit on dimensions in header --- libvips/foreign/webp2vips.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index c12cdc46..90bf3a04 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -547,7 +547,9 @@ read_header( Read *read, VipsImage *out ) } if( read->width <= 0 || - read->height <= 0 ) { + read->height <= 0 || + read->width > 0x3FFF || + read->height > 0x3FFF ) { vips_error( "webp", "%s", _( "bad image dimensions" ) ); return( -1 ); } From 593b6f0d935becfe8e2163d6fcf70d1045514d80 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 19 Aug 2019 23:05:59 +0100 Subject: [PATCH 17/63] better /0 protection --- libvips/foreign/exif.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libvips/foreign/exif.c b/libvips/foreign/exif.c index 759e0fdc..fd2e07ee 100644 --- a/libvips/foreign/exif.c +++ b/libvips/foreign/exif.c @@ -681,7 +681,11 @@ vips_exif_set_double( ExifData *ed, ExifRational rv; rv = exif_get_rational( entry->data + offset, bo ); - old_value = (double) rv.numerator / rv.denominator; + if( rv.denominator == 0 ) + old_value = 0; + else + old_value = (double) rv.numerator / rv.denominator; + if( VIPS_FABS( old_value - value ) > 0.0001 ) { vips_exif_double_to_rational( value, &rv ); @@ -696,7 +700,11 @@ vips_exif_set_double( ExifData *ed, ExifSRational srv; srv = exif_get_srational( entry->data + offset, bo ); - old_value = (double) srv.numerator / srv.denominator; + if( rv.denominator == 0 ) + old_value = 0; + else + old_value = (double) srv.numerator / srv.denominator; + if( VIPS_FABS( old_value - value ) > 0.0001 ) { vips_exif_double_to_srational( value, &srv ); From db49e82bda6146a3504a45fe49e04bac9fba5ebe Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 19 Aug 2019 23:31:25 +0100 Subject: [PATCH 18/63] fix typo --- libvips/foreign/exif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/foreign/exif.c b/libvips/foreign/exif.c index fd2e07ee..c69cfb18 100644 --- a/libvips/foreign/exif.c +++ b/libvips/foreign/exif.c @@ -700,7 +700,7 @@ vips_exif_set_double( ExifData *ed, ExifSRational srv; srv = exif_get_srational( entry->data + offset, bo ); - if( rv.denominator == 0 ) + if( srv.denominator == 0 ) old_value = 0; else old_value = (double) srv.numerator / srv.denominator; From ed2054dbbcab5e31c816a229f509e086ab9b7157 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 21 Aug 2019 10:35:48 +0100 Subject: [PATCH 19/63] revise arithmetic with const there's a problem with out of bounds values, for example: vips relational_const k2.jpg x.v equal 1000 actually finds pixels == 255, since 1000 is saturated converted to 255 before the test starts. This patch reworks arithmetic against const values to fix this. --- ChangeLog | 2 + libvips/arithmetic/boolean.c | 15 ++-- libvips/arithmetic/math2.c | 5 +- libvips/arithmetic/relational.c | 82 ++++++++++++------- libvips/arithmetic/remainder.c | 8 +- libvips/arithmetic/unaryconst.c | 136 +++++++------------------------- libvips/arithmetic/unaryconst.h | 15 ++-- 7 files changed, 100 insertions(+), 163 deletions(-) diff --git a/ChangeLog b/ChangeLog index 871fc395..162ba56f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,8 @@ - loaders use "minimise" to close input files earlier - integrate support for oss-fuzz [omira-sch] - add vips_switch() / vips_case() ... fast many-way ifthenelse +- better const handling for arithmetic operators fixes comparisons against out + of range values 9/7/19 started 8.8.2 - better early shutdown in readers diff --git a/libvips/arithmetic/boolean.c b/libvips/arithmetic/boolean.c index 1df7dab3..c4fcf18d 100644 --- a/libvips/arithmetic/boolean.c +++ b/libvips/arithmetic/boolean.c @@ -458,14 +458,11 @@ vips_boolean_const_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsUnary *unary = (VipsUnary *) object; - VipsUnaryConst *uconst = (VipsUnaryConst *) object; if( unary->in && vips_check_noncomplex( class->nickname, unary->in ) ) return( -1 ); - uconst->const_format = VIPS_FORMAT_INT; - if( VIPS_OBJECT_CLASS( vips_boolean_const_parent_class )-> build( object ) ) return( -1 ); @@ -474,9 +471,9 @@ vips_boolean_const_build( VipsObject *object ) } #define LOOPC( TYPE, OP ) { \ - TYPE *p = (TYPE *) in[0]; \ - TYPE *q = (TYPE *) out; \ - int *c = (int *) uconst->c_ready; \ + TYPE * restrict p = (TYPE *) in[0]; \ + TYPE * restrict q = (TYPE *) out; \ + int * restrict c = uconst->c_int; \ \ for( i = 0, x = 0; x < width; x++ ) \ for( b = 0; b < bands; b++, i++ ) \ @@ -484,9 +481,9 @@ vips_boolean_const_build( VipsObject *object ) } #define FLOOPC( TYPE, OP ) { \ - TYPE *p = (TYPE *) in[0]; \ - int *q = (int *) out; \ - int *c = (int *) uconst->c_ready; \ + TYPE * restrict p = (TYPE *) in[0]; \ + int * restrict q = (int *) out; \ + int * restrict c = uconst->c_int; \ \ for( i = 0, x = 0; x < width; x++ ) \ for( b = 0; b < bands; b++, i++ ) \ diff --git a/libvips/arithmetic/math2.c b/libvips/arithmetic/math2.c index 35ed16d0..cbcd2b99 100644 --- a/libvips/arithmetic/math2.c +++ b/libvips/arithmetic/math2.c @@ -338,14 +338,11 @@ vips_math2_const_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsUnary *unary = (VipsUnary *) object; - VipsUnaryConst *uconst = (VipsUnaryConst *) object; if( unary->in && vips_check_noncomplex( class->nickname, unary->in ) ) return( -1 ); - uconst->const_format = VIPS_FORMAT_DOUBLE; - if( VIPS_OBJECT_CLASS( vips_math2_const_parent_class )-> build( object ) ) return( -1 ); @@ -356,7 +353,7 @@ vips_math2_const_build( VipsObject *object ) #define LOOPC( IN, OUT, OP ) { \ IN * restrict p = (IN *) in[0]; \ OUT * restrict q = (OUT *) out; \ - double * restrict c = (double *) uconst->c_ready; \ + double * restrict c = uconst->c_double; \ \ for( i = 0, x = 0; x < width; x++ ) \ for( b = 0; b < bands; b++, i++ ) \ diff --git a/libvips/arithmetic/relational.c b/libvips/arithmetic/relational.c index c975f2af..7d18e6ab 100644 --- a/libvips/arithmetic/relational.c +++ b/libvips/arithmetic/relational.c @@ -457,25 +457,18 @@ typedef VipsUnaryConstClass VipsRelationalConstClass; G_DEFINE_TYPE( VipsRelationalConst, vips_relational_const, VIPS_TYPE_UNARY_CONST ); -static int -vips_relational_const_build( VipsObject *object ) -{ - VipsUnary *unary = (VipsUnary *) object; - VipsUnaryConst *uconst = (VipsUnaryConst *) object; - - if( unary->in ) - uconst->const_format = unary->in->BandFmt; - - if( VIPS_OBJECT_CLASS( vips_relational_const_parent_class )-> - build( object ) ) - return( -1 ); - - return( 0 ); +#define RLOOPCI( TYPE, OP ) { \ + TYPE * restrict p = (TYPE *) in[0]; \ + int * restrict c = uconst->c_int; \ + \ + for( i = 0, x = 0; x < width; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + out[i] = (p[i] OP c[b]) ? 255 : 0; \ } -#define RLOOPC( TYPE, OP ) { \ +#define RLOOPCF( TYPE, OP ) { \ TYPE * restrict p = (TYPE *) in[0]; \ - TYPE * restrict c = (TYPE *) uconst->c_ready; \ + double * restrict c = uconst->c_double; \ \ for( i = 0, x = 0; x < width; x++ ) \ for( b = 0; b < bands; b++, i++ ) \ @@ -486,7 +479,7 @@ vips_relational_const_build( VipsObject *object ) TYPE * restrict p = (TYPE *) in[0]; \ \ for( i = 0, x = 0; x < width; x++ ) { \ - TYPE * restrict c = (TYPE *) uconst->c_ready; \ + double * restrict c = uconst->c_double; \ \ for( b = 0; b < bands; b++, i++ ) { \ out[i] = OP( p[0], p[1], c[0], c[1]) ? 255 : 0; \ @@ -505,32 +498,64 @@ vips_relational_const_buffer( VipsArithmetic *arithmetic, VipsRelationalConst *rconst = (VipsRelationalConst *) arithmetic; VipsImage *im = arithmetic->ready[0]; int bands = im->Bands; + gboolean is_int = uconst->is_int && + vips_band_format_isint( im->BandFmt ); int i, x, b; switch( rconst->relational ) { - case VIPS_OPERATION_RELATIONAL_EQUAL: - SWITCH( RLOOPC, CLOOPC, ==, CEQUAL ); + case VIPS_OPERATION_RELATIONAL_EQUAL: + if( is_int ) { + SWITCH( RLOOPCI, CLOOPC, ==, CEQUAL ); + } + else { + SWITCH( RLOOPCF, CLOOPC, ==, CEQUAL ); + } break; case VIPS_OPERATION_RELATIONAL_NOTEQ: - SWITCH( RLOOPC, CLOOPC, !=, CNOTEQ ); + if( is_int ) { + SWITCH( RLOOPCI, CLOOPC, !=, CNOTEQ ); + } + else { + SWITCH( RLOOPCF, CLOOPC, !=, CNOTEQ ); + } break; - case VIPS_OPERATION_RELATIONAL_LESS: - SWITCH( RLOOPC, CLOOPC, <, CLESS ); + case VIPS_OPERATION_RELATIONAL_LESS: + if( is_int ) { + SWITCH( RLOOPCI, CLOOPC, <, CLESS ); + } + else { + SWITCH( RLOOPCF, CLOOPC, <, CLESS ); + } break; - case VIPS_OPERATION_RELATIONAL_LESSEQ: - SWITCH( RLOOPC, CLOOPC, <=, CLESSEQ ); + case VIPS_OPERATION_RELATIONAL_LESSEQ: + if( is_int ) { + SWITCH( RLOOPCI, CLOOPC, <=, CLESSEQ ); + } + else { + SWITCH( RLOOPCF, CLOOPC, <=, CLESSEQ ); + } break; - case VIPS_OPERATION_RELATIONAL_MORE: - SWITCH( RLOOPC, CLOOPC, >, CMORE ); + case VIPS_OPERATION_RELATIONAL_MORE: + if( is_int ) { + SWITCH( RLOOPCI, CLOOPC, >, CMORE ); + } + else { + SWITCH( RLOOPCF, CLOOPC, >, CMORE ); + } break; - case VIPS_OPERATION_RELATIONAL_MOREEQ: - SWITCH( RLOOPC, CLOOPC, >=, CMOREEQ ); + case VIPS_OPERATION_RELATIONAL_MOREEQ: + if( is_int ) { + SWITCH( RLOOPCI, CLOOPC, >=, CMOREEQ ); + } + else { + SWITCH( RLOOPCF, CLOOPC, >=, CMOREEQ ); + } break; default: @@ -551,7 +576,6 @@ vips_relational_const_class_init( VipsRelationalConstClass *class ) object_class->nickname = "relational_const"; object_class->description = _( "relational operations against a constant" ); - object_class->build = vips_relational_const_build; aclass->process_line = vips_relational_const_buffer; diff --git a/libvips/arithmetic/remainder.c b/libvips/arithmetic/remainder.c index b845b37e..75df8f4d 100644 --- a/libvips/arithmetic/remainder.c +++ b/libvips/arithmetic/remainder.c @@ -238,15 +238,11 @@ vips_remainder_const_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsUnary *unary = (VipsUnary *) object; - VipsUnaryConst *uconst = (VipsUnaryConst *) object; if( unary->in && vips_check_noncomplex( class->nickname, unary->in ) ) return( -1 ); - if( unary->in ) - uconst->const_format = unary->in->BandFmt; - if( VIPS_OBJECT_CLASS( vips_remainder_const_parent_class )-> build( object ) ) return( -1 ); @@ -259,7 +255,7 @@ vips_remainder_const_build( VipsObject *object ) #define IREMAINDERCONST( TYPE ) { \ TYPE * restrict p = (TYPE *) in[0]; \ TYPE * restrict q = (TYPE *) out; \ - TYPE * restrict c = (TYPE *) uconst->c_ready; \ + int * restrict c = uconst->c_int; \ \ for( i = 0, x = 0; x < width; x++ ) \ for( b = 0; b < bands; b++, i++ ) \ @@ -271,7 +267,7 @@ vips_remainder_const_build( VipsObject *object ) #define FREMAINDERCONST( TYPE ) { \ TYPE * restrict p = (TYPE *) in[0]; \ TYPE * restrict q = (TYPE *) out; \ - TYPE * restrict c = (TYPE *) uconst->c_ready; \ + int * restrict c = uconst->c_int; \ \ for( i = 0, x = 0; x < width; x++ ) \ for( b = 0; b < bands; b++, i++ ) { \ diff --git a/libvips/arithmetic/unaryconst.c b/libvips/arithmetic/unaryconst.c index b5cbf797..133ede58 100644 --- a/libvips/arithmetic/unaryconst.c +++ b/libvips/arithmetic/unaryconst.c @@ -49,100 +49,6 @@ G_DEFINE_ABSTRACT_TYPE( VipsUnaryConst, vips_unary_const, VIPS_TYPE_UNARY ); -/* Cast a vector of double to a vector of TYPE, clipping to a range. - */ -#define CAST_CLIP( TYPE, N, X ) { \ - TYPE * restrict tq = (TYPE *) q; \ - \ - for( i = 0; i < m; i++ ) { \ - double v = p[VIPS_MIN( n - 1, i )]; \ - \ - tq[i] = (TYPE) VIPS_FCLIP( N, v, X ); \ - } \ -} - -/* Cast a vector of double to a vector of TYPE. - */ -#define CAST( TYPE ) { \ - TYPE * restrict tq = (TYPE *) q; \ - \ - for( i = 0; i < m; i++ ) \ - tq[i] = (TYPE) p[VIPS_MIN( n - 1, i )]; \ -} - -/* Cast a vector of double to a complex vector of TYPE. - */ -#define CASTC( TYPE ) { \ - TYPE * restrict tq = (TYPE *) q; \ - \ - for( i = 0; i < m; i++ ) { \ - tq[0] = (TYPE) p[VIPS_MIN( n - 1, i )]; \ - tq[1] = 0; \ - \ - tq += 2; \ - } \ -} - -/* Cast a n-band vector of double to a m-band vector in another format. - */ -static VipsPel * -make_pixel( VipsObject *obj, - int m, VipsBandFormat fmt, int n, double * restrict p ) -{ - VipsPel *q; - int i; - - if( !(q = VIPS_ARRAY( obj, m * vips_format_sizeof( fmt ), VipsPel )) ) - return( NULL ); - - switch( fmt ) { - case VIPS_FORMAT_CHAR: - CAST_CLIP( signed char, SCHAR_MIN, SCHAR_MAX ); - break; - - case VIPS_FORMAT_UCHAR: - CAST_CLIP( unsigned char, 0, UCHAR_MAX ); - break; - - case VIPS_FORMAT_SHORT: - CAST_CLIP( signed short, SCHAR_MIN, SCHAR_MAX ); - break; - - case VIPS_FORMAT_USHORT: - CAST_CLIP( unsigned short, 0, USHRT_MAX ); - break; - - case VIPS_FORMAT_INT: - CAST_CLIP( signed int, INT_MIN, INT_MAX ); - break; - - case VIPS_FORMAT_UINT: - CAST_CLIP( unsigned int, 0, UINT_MAX ); - break; - - case VIPS_FORMAT_FLOAT: - CAST( float ); - break; - - case VIPS_FORMAT_DOUBLE: - CAST( double ); - break; - - case VIPS_FORMAT_COMPLEX: - CASTC( float ); - break; - - case VIPS_FORMAT_DPCOMPLEX: - CASTC( double ); - break; - - default: - g_assert_not_reached(); - } - - return( q ); -} - static int vips_unary_const_build( VipsObject *object ) { @@ -161,27 +67,43 @@ vips_unary_const_build( VipsObject *object ) uconst->n = VIPS_MAX( uconst->n, unary->in->Bands ); arithmetic->base_bands = uconst->n; - if( unary->in && uconst->c ) { + if( unary->in && + uconst->c ) { if( vips_check_vector( class->nickname, uconst->c->n, unary->in ) ) return( -1 ); } - /* Some operations need the vector in the input type (eg. - * im_equal_vec() where the output type is always uchar and is useless - * for comparisons), some need it in the output type (eg. - * im_andimage_vec() where we want to get the double to an int so we - * can do bitwise-and without having to cast for each pixel), some - * need a fixed type (eg. im_powtra_vec(), where we want to keep it as - * double). + /* Some operations need int constants, for example boolean AND, SHIFT + * etc. * - * Therefore pass in the desired vector type as a param. + * Some can use int constants as an optimisation, for example (x < + * 12). It depends on the value though: obviously (x < 12.5) should + * not use the int form. */ + if( uconst->c ) { + int i; - if( uconst->c ) - uconst->c_ready = make_pixel( (VipsObject *) uconst, - uconst->n, uconst->const_format, - uconst->c->n, (double *) uconst->c->data ); + uconst->c_int = VIPS_ARRAY( object, uconst->n, int ); + uconst->c_double = VIPS_ARRAY( object, uconst->n, double ); + if( !uconst->c_int || + !uconst->c_double ) + return( -1 ); + + for( i = 0; i < uconst->n; i++ ) + uconst->c_double[i] = ((double *) uconst->c->data) + [VIPS_MIN( i, uconst->c->n - 1)]; + + for( i = 0; i < uconst->n; i++ ) + uconst->c_int[i] = uconst->c_double[i]; + + uconst->is_int = TRUE; + for( i = 0; i < uconst->n; i++ ) + if( uconst->c_int[i] != uconst->c_double[i] ) { + uconst->is_int = FALSE; + break; + } + } if( VIPS_OBJECT_CLASS( vips_unary_const_parent_class )-> build( object ) ) diff --git a/libvips/arithmetic/unaryconst.h b/libvips/arithmetic/unaryconst.h index 2d204426..b2c26135 100644 --- a/libvips/arithmetic/unaryconst.h +++ b/libvips/arithmetic/unaryconst.h @@ -59,16 +59,15 @@ typedef struct _VipsUnaryConst { */ VipsArea *c; - /* The format the constant should be cast to. Subclasses set this - * ready for unaryconst's build method. - */ - VipsBandFormat const_format; - - /* Our constant expanded to match arith->ready in size and - * const_format in type. + /* Our constant expanded to match arith->ready in size. We need int + * and double versions. + * + * is_int is TRUE if the two arrays are equal for every element. */ int n; - VipsPel *c_ready; + int *c_int; + double *c_double; + gboolean is_int; } VipsUnaryConst; From 0b3ece5b42e15edbbd82c040f8eaabd0be1f97f5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 21 Aug 2019 16:36:18 +0100 Subject: [PATCH 20/63] new constants code now passes all tests --- libvips/arithmetic/relational.c | 2 +- libvips/arithmetic/unaryconst.c | 27 ++++++++++++++++++++------- test/test-suite/helpers/helpers.py | 3 ++- test/test-suite/test_arithmetic.py | 11 +++++++---- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/libvips/arithmetic/relational.c b/libvips/arithmetic/relational.c index 7d18e6ab..f2f8f817 100644 --- a/libvips/arithmetic/relational.c +++ b/libvips/arithmetic/relational.c @@ -499,7 +499,7 @@ vips_relational_const_buffer( VipsArithmetic *arithmetic, VipsImage *im = arithmetic->ready[0]; int bands = im->Bands; gboolean is_int = uconst->is_int && - vips_band_format_isint( im->BandFmt ); + vips_band_format_isint( im->BandFmt ); int i, x, b; diff --git a/libvips/arithmetic/unaryconst.c b/libvips/arithmetic/unaryconst.c index 133ede58..f098f169 100644 --- a/libvips/arithmetic/unaryconst.c +++ b/libvips/arithmetic/unaryconst.c @@ -2,6 +2,8 @@ * * 11/11/11 * - from arith_binary_const + * 21/8/19 + * - revise to fix out of range comparisons */ /* @@ -80,25 +82,36 @@ vips_unary_const_build( VipsObject *object ) * Some can use int constants as an optimisation, for example (x < * 12). It depends on the value though: obviously (x < 12.5) should * not use the int form. + * + * For complex images, we double the vector length and set the + * imaginary part to 0. */ if( uconst->c ) { + gboolean is_complex = + vips_band_format_iscomplex( unary->in->BandFmt ); + int step = is_complex ? 2 : 1; + int n = step * uconst->n; + double *c = (double *) uconst->c->data; + int i; - uconst->c_int = VIPS_ARRAY( object, uconst->n, int ); - uconst->c_double = VIPS_ARRAY( object, uconst->n, double ); + uconst->c_int = VIPS_ARRAY( object, n, int ); + uconst->c_double = VIPS_ARRAY( object, n, double ); if( !uconst->c_int || !uconst->c_double ) return( -1 ); + memset( uconst->c_int, 0, n * sizeof( int ) ); + memset( uconst->c_double, 0, n * sizeof( double ) ); - for( i = 0; i < uconst->n; i++ ) - uconst->c_double[i] = ((double *) uconst->c->data) - [VIPS_MIN( i, uconst->c->n - 1)]; + for( i = 0; i < n; i += step ) + uconst->c_double[i] = + c[VIPS_MIN( i / step, uconst->c->n - 1)]; - for( i = 0; i < uconst->n; i++ ) + for( i = 0; i < n; i += step ) uconst->c_int[i] = uconst->c_double[i]; uconst->is_int = TRUE; - for( i = 0; i < uconst->n; i++ ) + for( i = 0; i < n; i += step ) if( uconst->c_int[i] != uconst->c_double[i] ) { uconst->is_int = FALSE; break; diff --git a/test/test-suite/helpers/helpers.py b/test/test-suite/helpers/helpers.py index f254bd16..00d8caf6 100644 --- a/test/test-suite/helpers/helpers.py +++ b/test/test-suite/helpers/helpers.py @@ -1,5 +1,6 @@ # vim: set fileencoding=utf-8 : # test helpers + import os import tempfile import pytest @@ -174,7 +175,7 @@ def run_fn2(fn, x, y): # test a pair of things which can be lists for approx. equality def assert_almost_equal_objects(a, b, threshold=0.0001, msg=''): - # print 'assertAlmostEqualObjects %s = %s' % (a, b) + # print('assertAlmostEqualObjects %s = %s' % (a, b)) assert all([pytest.approx(x, abs=threshold) == y for x, y in zip_expand(a, b)]), msg diff --git a/test/test-suite/test_arithmetic.py b/test/test-suite/test_arithmetic.py index 8e6f3e46..eb3c15d8 100644 --- a/test/test-suite/test_arithmetic.py +++ b/test/test-suite/test_arithmetic.py @@ -1,4 +1,5 @@ # vim: set fileencoding=utf-8 : + import math import pytest @@ -10,14 +11,16 @@ from helpers import unsigned_formats, float_formats, noncomplex_formats, \ class TestArithmetic: def run_arith(self, fn, fmt=all_formats): - [run_image2(fn.__name__ + ' image', x.cast(y), x.cast(z), fn) + [run_image2('%s image %s %s %s' % (fn.__name__, x, y, z), + x.cast(y), x.cast(z), fn) for x in self.all_images for y in fmt for z in fmt] def run_arith_const(self, fn, fmt=all_formats): - [run_const(fn.__name__ + ' scalar', fn, x.cast(y), 2) + [run_const('%s scalar %s %s' % (fn.__name__, x, y), + fn, x.cast(y), 2) for x in self.all_images for y in fmt] - [run_const(fn.__name__ + ' vector', fn, self.colour.cast(y), - [1, 2, 3]) + [run_const('%s vector %s' % (fn.__name__, y), + fn, self.colour.cast(y), [1, 2, 3]) for y in fmt] # run a function on an image, From fd25f13a2d424ca710e0458ba245ccdc919ac153 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 21 Aug 2019 16:47:13 +0100 Subject: [PATCH 21/63] add a test for the fix --- test/test-suite/test_arithmetic.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test-suite/test_arithmetic.py b/test/test-suite/test_arithmetic.py index eb3c15d8..b965991c 100644 --- a/test/test-suite/test_arithmetic.py +++ b/test/test-suite/test_arithmetic.py @@ -202,6 +202,13 @@ class TestArithmetic: self.run_arith_const(noteq) self.run_arith(noteq) + # comparisons against out of range values should always fail, and + # comparisons to fractional values should always fail + x = pyvips.Image.grey(256, 256, uchar=True) + assert (x == 1000).max() == 0 + assert (x == 12).max() == 255 + assert (x == 12.5).max() == 0 + def test_abs(self): def my_abs(x): return abs(x) From 8f28fa625e33c9e1c9ab4d6b2a0f96c60fff52f8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 21 Aug 2019 16:51:01 +0100 Subject: [PATCH 22/63] restore switch/case test now that relational const is fixed up --- test/test-suite/test_conversion.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test-suite/test_conversion.py b/test/test-suite/test_conversion.py index afd29b22..0ee88574 100644 --- a/test/test-suite/test_conversion.py +++ b/test/test-suite/test_conversion.py @@ -556,9 +556,8 @@ class TestConversion: assert index.avg() == 1.5 # no match should return n + 1 - # FIXME uncomment when we fix relational const - #index = pyvips.Image.switch([x == 1000, x == 2000]) - #assert index.avg() == 2 + index = pyvips.Image.switch([x == 1000, x == 2000]) + assert index.avg() == 2 def test_insert(self): for x in all_formats: From b5e8e99746ff1798ca54464c197b9c4c55db38ae Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 21 Aug 2019 17:17:54 +0100 Subject: [PATCH 23/63] fix a read-one-byte-beyond issue in jpeg load libvips could harmlessly read beyond the end of a string with a crafted jpg file --- ChangeLog | 1 + libvips/foreign/jpeg2vips.c | 7 +++++-- libvips/iofuncs/header.c | 7 +++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 02cf7e4c..42848727 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ - add locks for pdfium load - fix build with MSVC - fix a problem with shinkv tail processing [angelmixu] +- fix a read one byte beyond buffer bug in jpegload 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 8240b164..1060bc9e 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -338,13 +338,16 @@ attach_xmp_blob( VipsImage *im, void *data, int data_length ) char *p = (char *) data; int i; - if( !vips_isprefix( "http", p ) ) + if( data_length < 4 || + !vips_isprefix( "http", p ) ) return( 0 ); /* Search for a null char within the first few characters. 80 * should be plenty for a basic URL. + * + * -2 for the extra null. */ - for( i = 0; i < 80; i++ ) + for( i = 0; i < VIPS_MIN( 80, data_length - 2 ); i++ ) if( !p[i] ) break; if( p[i] ) diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index 2af3752d..62df5e9b 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -1451,12 +1451,15 @@ vips_image_set_blob_copy( VipsImage *image, { void *data_copy; + /* Cap at 100mb for sanity. + */ if( !data || - length == 0 ) + length == 0 || + length > 100 * 1024 * 1024 ) return; /* We add an extra, secret null byte at the end, just in case this blob - * is read as a C string. The libtiff reader (for example) attaches + * is read as a C string. The libtiff reader attaches * XMP XML as a blob, for example. */ if( !(data_copy = vips_malloc( NULL, length + 1 )) ) From 81c92242d8e6cdde1c8f2b4814b0a9ab0b3f1d7f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 21 Aug 2019 17:42:25 +0100 Subject: [PATCH 24/63] relax HEIC threshold libheic versions change rounding behaviour --- test/test-suite/test_foreign.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index 4d8501b6..0ef1e2b6 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -830,7 +830,7 @@ class TestForeign: def test_heifload(self): def heif_valid(im): a = im(10, 10) - assert_almost_equal_objects(a, [75.0, 86.0, 81.0]) + assert_almost_equal_objects(a, [75.0, 86.0, 81.0], threshold=2) assert im.width == 4032 assert im.height == 3024 assert im.bands == 3 From 1de458556a27af735644623e0ee0dad6ea532e0b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 23 Aug 2019 12:43:59 +0100 Subject: [PATCH 25/63] try adding a file fuzzer oss-fuzz normally fuzzes with memory buffers, but a lot of the libvips loadres, especially CSV and PPM, only work from the filesystem. This fuzzer writes the oss-fuzz buffer to the FS, then fuzzes that. --- fuzz/Makefile.am | 1 + fuzz/jpegsave_file_fuzzer.cc | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 fuzz/jpegsave_file_fuzzer.cc diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am index 64f75318..edee8cbd 100644 --- a/fuzz/Makefile.am +++ b/fuzz/Makefile.am @@ -2,6 +2,7 @@ TESTS = \ test_fuzz.sh FUZZPROGS = \ + jpegsave_file_fuzzer \ jpegsave_buffer_fuzzer \ pngsave_buffer_fuzzer \ webpsave_buffer_fuzzer \ diff --git a/fuzz/jpegsave_file_fuzzer.cc b/fuzz/jpegsave_file_fuzzer.cc new file mode 100644 index 00000000..878dd9e6 --- /dev/null +++ b/fuzz/jpegsave_file_fuzzer.cc @@ -0,0 +1,62 @@ +#include + +extern "C" int +LLVMFuzzerInitialize( int *argc, char ***argv ) +{ + vips_concurrency_set( 1 ); + return( 0 ); +} + +static int +test_one_file( const char *name ) +{ + VipsImage *image; + void *buf; + size_t len; + + if( !(image = vips_image_new_from_file( name, + "access", VIPS_ACCESS_SEQUENTIAL, + NULL )) ) + return( 0 ); + + /* Skip big images. They are likely to timeout. + */ + if( image->Xsize > 1024 || + image->Ysize > 1024 || + image->Bands > 10 ) { + g_object_unref( image ); + return( 0 ); + } + + if( vips_jpegsave_buffer( image, &buf, &len, NULL ) ) { + g_object_unref( image ); + return( 0 ); + } + + g_free( buf ); + g_object_unref( image ); + + return( 0 ); +} + +extern "C" int +LLVMFuzzerTestOneInput( const guint8 *data, size_t size ) +{ + char *name; + + if( !(name = vips__temp_name( "%s" )) ) + return( 0 ); + + if( !g_file_set_contents( name, (const char *) data, size, NULL ) || + test_one_file( name ) ) { + g_unlink( name ); + g_free( name ); + + return( 0 ); + } + + g_unlink( name ); + g_free( name ); + + return( 0 ); +} From 54ce3513fd7f3fabf6513739a6d11da9fe817b3f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 23 Aug 2019 12:50:33 +0100 Subject: [PATCH 26/63] add some corpus ascii files some sample images for the file loader --- fuzz/common_fuzzer_corpus/x-ascii.ppm | 15 +++++++++++++++ fuzz/common_fuzzer_corpus/x.csv | 10 ++++++++++ fuzz/common_fuzzer_corpus/x.mat | 11 +++++++++++ fuzz/common_fuzzer_corpus/x.ppm | 6 ++++++ fuzz/common_fuzzer_corpus/x3.ppm | 6 ++++++ 5 files changed, 48 insertions(+) create mode 100644 fuzz/common_fuzzer_corpus/x-ascii.ppm create mode 100644 fuzz/common_fuzzer_corpus/x.csv create mode 100644 fuzz/common_fuzzer_corpus/x.mat create mode 100644 fuzz/common_fuzzer_corpus/x.ppm create mode 100644 fuzz/common_fuzzer_corpus/x3.ppm diff --git a/fuzz/common_fuzzer_corpus/x-ascii.ppm b/fuzz/common_fuzzer_corpus/x-ascii.ppm new file mode 100644 index 00000000..aa81a14b --- /dev/null +++ b/fuzz/common_fuzzer_corpus/x-ascii.ppm @@ -0,0 +1,15 @@ +P2 +#vips2ppm - Fri Aug 23 12:48:07 2019 + +10 10 +255 +96 101 113 118 124 130 136 141 147 150 +81 87 98 101 107 112 117 123 130 135 +73 78 85 90 95 99 103 110 118 124 +46 51 60 67 73 81 87 94 103 109 +34 35 40 48 60 69 77 81 85 88 +28 26 31 36 45 54 59 64 69 72 +32 31 41 39 39 40 45 52 61 66 +38 38 47 42 38 36 38 43 49 53 +37 38 39 39 37 37 37 36 34 32 +36 36 38 36 35 34 35 32 28 25 diff --git a/fuzz/common_fuzzer_corpus/x.csv b/fuzz/common_fuzzer_corpus/x.csv new file mode 100644 index 00000000..640265ec --- /dev/null +++ b/fuzz/common_fuzzer_corpus/x.csv @@ -0,0 +1,10 @@ +96 101 113 118 124 130 136 141 147 150 +81 87 98 101 107 112 117 123 130 135 +73 78 85 90 95 99 103 110 118 124 +46 51 60 67 73 81 87 94 103 109 +34 35 40 48 60 69 77 81 85 88 +28 26 31 36 45 54 59 64 69 72 +32 31 41 39 39 40 45 52 61 66 +38 38 47 42 38 36 38 43 49 53 +37 38 39 39 37 37 37 36 34 32 +36 36 38 36 35 34 35 32 28 25 diff --git a/fuzz/common_fuzzer_corpus/x.mat b/fuzz/common_fuzzer_corpus/x.mat new file mode 100644 index 00000000..bed7279e --- /dev/null +++ b/fuzz/common_fuzzer_corpus/x.mat @@ -0,0 +1,11 @@ +10 10 +96 101 113 118 124 130 136 141 147 150 +81 87 98 101 107 112 117 123 130 135 +73 78 85 90 95 99 103 110 118 124 +46 51 60 67 73 81 87 94 103 109 +34 35 40 48 60 69 77 81 85 88 +28 26 31 36 45 54 59 64 69 72 +32 31 41 39 39 40 45 52 61 66 +38 38 47 42 38 36 38 43 49 53 +37 38 39 39 37 37 37 36 34 32 +36 36 38 36 35 34 35 32 28 25 diff --git a/fuzz/common_fuzzer_corpus/x.ppm b/fuzz/common_fuzzer_corpus/x.ppm new file mode 100644 index 00000000..cc0b6072 --- /dev/null +++ b/fuzz/common_fuzzer_corpus/x.ppm @@ -0,0 +1,6 @@ +P5 +#vips2ppm - Fri Aug 23 12:47:38 2019 + +10 10 +255 +`eqv|‚ˆ“–QWbekpu{‚‡INUZ_cgnv|.3LQDUWL]\SfcZolbzri|.(/)3#.9+8E7FM@QSHYVM`ZQf]Tg' %*$- *6(5=1?C6HG;OL@VOCW,!)!3$+2",0#-1#05(9>/B.""/##0$&/#'.!(.!*. --.+.)-," ,"!/##-!#,&+'-*+*'(#% \ No newline at end of file From 4b677fb16a95f11ee1fe02502b1565cfc3d2ad19 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 23 Aug 2019 15:40:48 +0100 Subject: [PATCH 27/63] make GIF parse less strict Some GIFs don't follow the standard very closely. Let them through. See https://github.com/libvips/libvips/issues/1404 --- ChangeLog | 1 + libvips/foreign/gifload.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 42848727..870ff49b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,7 @@ - fix build with MSVC - fix a problem with shinkv tail processing [angelmixu] - fix a read one byte beyond buffer bug in jpegload +- make GIF parsing less strict 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 122e5f40..a76ba040 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -22,6 +22,8 @@ * - init pages to 0 before load * 14/2/19 * - rework as a sequential loader ... simpler, much lower mem use + * 23/8/18 + * - allow GIF read errors during header scan */ /* @@ -601,11 +603,12 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) gif->n_pages = 0; - do { - if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } + do { + /* Don't flag errors during header scan. Some corrupt GIFs + * will fail. + */ + if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) + continue; switch( record ) { case IMAGE_DESC_RECORD_TYPE: From 038409093faa55046972f632d9c6135f11572f7a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Aug 2019 11:20:45 +0100 Subject: [PATCH 28/63] clip coding and interpretation on image read They could be out of bounds enums. This used not to matter, but we use them more now, so out of bounds values can cause coredumps. --- ChangeLog | 1 + libvips/iofuncs/vips.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 870ff49b..e9024987 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ - fix a problem with shinkv tail processing [angelmixu] - fix a read one byte beyond buffer bug in jpegload - make GIF parsing less strict +- clip coding and interpretation on vips image read 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 83988c78..4e35023b 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -382,8 +382,10 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from ) im->Ysize = VIPS_CLIP( 1, im->Ysize, VIPS_MAX_COORD ); im->Bands = VIPS_CLIP( 1, im->Bands, VIPS_MAX_COORD ); im->BandFmt = VIPS_CLIP( 0, im->BandFmt, VIPS_FORMAT_LAST - 1 ); + im->Type = VIPS_CLIP( 0, im->Type, VIPS_INTERPRETATION_LAST - 1 ); + im->Coding = VIPS_CLIP( 0, im->Coding, VIPS_CODING_LAST - 1 ); - /* Type, Coding, Offset, Res, etc. don't affect vips file layout, just + /* Offset, Res, etc. don't affect vips file layout, just * pixel interpretation, don't clip them. */ From 4691260540dbc2bf3828d8fbf28e9ac1e92b9b2e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Aug 2019 17:14:10 +0100 Subject: [PATCH 29/63] better feof() handling in gif load Relaxing the read error rules made looping possible .. make sure we always stop explicitly on eof. --- ChangeLog | 1 + libvips/foreign/gifload.c | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ChangeLog b/ChangeLog index e9024987..6f3cb5ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ - fix a problem with shinkv tail processing [angelmixu] - fix a read one byte beyond buffer bug in jpegload - make GIF parsing less strict +- better feof() handling in GIF load - clip coding and interpretation on vips image read 24/5/19 started 8.8.1 diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index a76ba040..b45aa059 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -191,7 +191,6 @@ typedef struct _VipsForeignLoadGif { /* Params for DGifOpen(). Set by subclasses, called by base class in * _open(). */ - void *userPtr; InputFunc read_func; } VipsForeignLoadGif; @@ -1052,13 +1051,13 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) { int error; - if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func, &error )) ) { + if( !(gif->file = DGifOpen( gif, gif->read_func, &error )) ) { vips_foreign_load_gif_error_vips( gif, error ); return( -1 ); } } #else - if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func )) ) { + if( !(gif->file = DGifOpen( gif, gif->read_func )) ) { vips_foreign_load_gif_error_vips( gif, GifLastError() ); return( -1 ); } @@ -1161,11 +1160,15 @@ vips_foreign_load_gif_file_dispose( GObject *gobject ) * across DLL boundaries on Windows. */ static int -vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n ) +vips_giflib_file_read( GifFileType *gfile, GifByteType *buffer, int n ) { - FILE *fp = (FILE *) file->UserData; + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gfile->UserData; + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; - return( (int) fread( (void *) buffer, 1, n, fp ) ); + if( feof( file->fp ) ) + gif->eof = TRUE; + + return( (int) fread( (void *) buffer, 1, n, file->fp ) ); } static int @@ -1185,7 +1188,6 @@ vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) rewind( file->fp ); vips_foreign_load_gif_close( gif ); - gif->userPtr = file->fp; gif->read_func = vips_giflib_file_read; return( VIPS_FOREIGN_LOAD_GIF_CLASS( @@ -1279,7 +1281,6 @@ vips_foreign_load_gif_buffer_open( VipsForeignLoadGif *gif ) vips_foreign_load_gif_close( gif ); buffer->p = buffer->buf->data; buffer->bytes_to_go = buffer->buf->length; - gif->userPtr = gif; gif->read_func = vips_giflib_buffer_read;; return( VIPS_FOREIGN_LOAD_GIF_CLASS( From 1ac96313cca88c4a69019f66c7f625e15815a29f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Aug 2019 17:16:28 +0100 Subject: [PATCH 30/63] note change in gifload.c --- libvips/foreign/gifload.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index b45aa059..33c45f65 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -24,6 +24,7 @@ * - rework as a sequential loader ... simpler, much lower mem use * 23/8/18 * - allow GIF read errors during header scan + * - better feof() handling */ /* From 0dda54b1b5dd97ac1358fb7414b8a6e117029473 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Aug 2019 17:17:45 +0100 Subject: [PATCH 31/63] merge --- libvips/foreign/gifload.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index a23788bb..1bab44db 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -1093,17 +1093,10 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) return( -1 ); } } -<<<<<<< HEAD -#else - if( !(gif->file = DGifOpen( gif->userPtr, gif->read_func )) ) { - vips_foreign_load_gif_error_vips( gif, GifLastError() ); - return( -1 ); -======= #else if( !(gif->file = DGifOpen( gif, gif->read_func )) ) { vips_foreign_load_gif_error_vips( gif, GifLastError() ); return( -1 ); ->>>>>>> 8.8 } #endif @@ -1204,13 +1197,8 @@ vips_foreign_load_gif_file_dispose( GObject *gobject ) * DGifOpenFileHandle() since that's an fd from open() and you can't pass those * across DLL boundaries on Windows. */ -<<<<<<< HEAD -static int -vips_giflib_file_read( GifFileType *file, GifByteType *buffer, int n ) -======= static int vips_giflib_file_read( GifFileType *gfile, GifByteType *buffer, int n ) ->>>>>>> 8.8 { VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gfile->UserData; VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; From 9941490d9e14b13a0bb1df21a8029d737155d7be Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Aug 2019 17:24:59 +0100 Subject: [PATCH 32/63] add fuzz file for gif loop --- ...lusterfuzz-testcase-minimized-sharpen_fuzzer-5203581631725568 | 1 + 1 file changed, 1 insertion(+) create mode 100644 fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5203581631725568 diff --git a/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5203581631725568 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5203581631725568 new file mode 100644 index 00000000..af58a693 --- /dev/null +++ b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-sharpen_fuzzer-5203581631725568 @@ -0,0 +1 @@ +GIF8 =¥ \ No newline at end of file From 6c525e144b13774593faeb58279bbc9a66cdc4e7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 24 Aug 2019 18:49:56 +0100 Subject: [PATCH 33/63] handle eof better for buffer GIF Add the new eof handling to the memory reader. --- libvips/foreign/gifload.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 33c45f65..42b9d840 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -1263,14 +1263,17 @@ G_DEFINE_TYPE( VipsForeignLoadGifBuffer, vips_foreign_load_gif_buffer, static int vips_giflib_buffer_read( GifFileType *file, GifByteType *buf, int n ) { - VipsForeignLoadGifBuffer *buffer = - (VipsForeignLoadGifBuffer *) file->UserData; + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) file->UserData; + VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) gif; size_t will_read = VIPS_MIN( n, buffer->bytes_to_go ); memcpy( buf, buffer->p, will_read ); buffer->p += will_read; buffer->bytes_to_go -= will_read; + if( will_read == 0 ) + gif->eof = TRUE; + return( will_read ); } From db6e6a00761d62fa85ff68af8034a48b1b910d6a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 25 Aug 2019 11:29:37 +0100 Subject: [PATCH 34/63] catch /0 in exif read --- libvips/foreign/exif.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libvips/foreign/exif.c b/libvips/foreign/exif.c index 759e0fdc..971186e0 100644 --- a/libvips/foreign/exif.c +++ b/libvips/foreign/exif.c @@ -237,14 +237,25 @@ vips_exif_get_double( ExifData *ed, { ExifRational rv; ExifSRational srv; + double value; - if( !vips_exif_get_rational( ed, entry, component, &rv ) ) - *out = (double) rv.numerator / rv.denominator; - else if( !vips_exif_get_srational( ed, entry, component, &srv ) ) - *out = (double) srv.numerator / srv.denominator; + if( !vips_exif_get_rational( ed, entry, component, &rv ) ) { + if( rv.denominator == 0 ) + value = 0; + else + value = (double) rv.numerator / rv.denominator; + } + else if( !vips_exif_get_srational( ed, entry, component, &srv ) ) { + if( srv.denominator == 0 ) + value = 0; + else + value = (double) srv.numerator / srv.denominator; + } else return( -1 ); + *out = value; + return( 0 ); } From 5749d60425c0e58346da0ba17398217f7c576ce8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 25 Aug 2019 11:31:21 +0100 Subject: [PATCH 35/63] add a test case for /0 in exif read --- ...imized-jpegsave_buffer_fuzzer-5759265708441600 | Bin 0 -> 289 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5759265708441600 diff --git a/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5759265708441600 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_buffer_fuzzer-5759265708441600 new file mode 100644 index 0000000000000000000000000000000000000000..5c4e7523e2a0b67aa35526a31c4985c68e7bb606 GIT binary patch literal 289 zcmex==iAP0jr!vbbT jB?cxzMrJ|N|F;-;m>Gc_W*$sg#{YL22%3ti4aradZNedS literal 0 HcmV?d00001 From 25e457736173369dcb0f7c09d07af68aedbdc175 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 26 Aug 2019 10:54:45 +0100 Subject: [PATCH 36/63] relax GIF parse rules again lots of GIFs are very badly structured :( --- libvips/foreign/gifload.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 42b9d840..67fe4c80 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -404,7 +404,7 @@ vips_foreign_load_gif_code_next( VipsForeignLoadGif *gif, /* Quickly scan an image record. */ static int -vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif ) +vips_foreign_load_gif_scan_image( VipsForeignLoadGif *gif ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); GifFileType *file = gif->file; @@ -413,6 +413,11 @@ vips_foreign_load_gif_scan_image_record( VipsForeignLoadGif *gif ) GifByteType *extension; + if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) { + vips_foreign_load_gif_error( gif ); + return( -1 ); + } + /* Check that the frame looks sane. Perhaps giflib checks * this for us. */ @@ -587,6 +592,8 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) /* Attempt to quickly scan a GIF and discover what we need for our header. We * need to scan the whole file to get n_pages, transparency and colour. + * + * Don't flag errors during header scan. Many GIFs do not follow spec. */ static int vips_foreign_load_gif_header( VipsForeignLoad *load ) @@ -604,23 +611,12 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) gif->n_pages = 0; do { - /* Don't flag errors during header scan. Some corrupt GIFs - * will fail. - */ if( DGifGetRecordType( gif->file, &record ) == GIF_ERROR ) continue; switch( record ) { case IMAGE_DESC_RECORD_TYPE: - if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - - /* Read in the image record. - */ - if( vips_foreign_load_gif_scan_image_record( gif ) ) - return( -1 ); + (void) vips_foreign_load_gif_scan_image( gif ); gif->n_pages += 1; @@ -630,8 +626,7 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) /* We will need to fetch the extensions to check for * cmaps and transparency. */ - if( vips_foreign_load_gif_scan_extension( gif ) ) - return( -1 ); + (void) vips_foreign_load_gif_scan_extension( gif ); break; case TERMINATE_RECORD_TYPE: From f6cc2a9055809f9cdda2eec6ab306b8156bbf8d5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 26 Aug 2019 11:35:26 +0100 Subject: [PATCH 37/63] revise gifload delays array handling so pages and delay_length can't get out of sync, even with corrupt GIFs --- libvips/foreign/gifload.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index e36876f4..d87c4392 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -22,7 +22,6 @@ * - init pages to 0 before load * 14/2/19 * - rework as a sequential loader ... simpler, much lower mem use -<<<<<<< HEAD * 6/7/19 [deftomat] * - support array of delays * 24/7/19 @@ -376,6 +375,23 @@ vips_foreign_load_gif_is_a( const char *filename ) return( 0 ); } +/* Make sure delays is allocated and large enough. + */ +static void +vips_foreign_load_gif_allocate_delays( VipsForeignLoadGif *gif ) +{ + if( gif->n_pages >= gif->delays_length ) { + int old = gif->delays_length; + int i; + + gif->delays_length = gif->delays_length + gif->n_pages + 64; + gif->delays = (int *) g_realloc( gif->delays, + gif->delays_length * sizeof( int ) ); + for( i = old; i < gif->delays_length; i++ ) + gif->delays[i] = 40; + } +} + static int vips_foreign_load_gif_ext_next( VipsForeignLoadGif *gif, GifByteType **extension ) @@ -533,18 +549,6 @@ vips_foreign_load_gif_scan_extension( VipsForeignLoadGif *gif ) gif->has_transparency = TRUE; } - if( gif->n_pages >= gif->delays_length ) { - int old = gif->delays_length; - int i; - - gif->delays_length = - gif->delays_length + gif->n_pages + 64; - gif->delays = (int *) g_realloc( gif->delays, - gif->delays_length * sizeof( int ) ); - for( i = old; i < gif->delays_length; i++ ) - gif->delays[i] = 40; - } - /* giflib uses centiseconds, we use ms. */ gif->delays[gif->n_pages] = @@ -644,9 +648,8 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) switch( record ) { case IMAGE_DESC_RECORD_TYPE: (void) vips_foreign_load_gif_scan_image( gif ); - gif->n_pages += 1; - + vips_foreign_load_gif_allocate_delays( gif ); break; case EXTENSION_RECORD_TYPE: @@ -1158,6 +1161,8 @@ vips_foreign_load_gif_init( VipsForeignLoadGif *gif ) gif->loop = 0; gif->comment = NULL; gif->dispose = 0; + + vips_foreign_load_gif_allocate_delays( gif ); } typedef struct _VipsForeignLoadGifFile { From c8ff970476fa9747963029dc753ce70ae0f5f0c7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 09:14:59 +0100 Subject: [PATCH 38/63] make jpeg buffer free safer possible race in free --- libvips/foreign/vips2jpeg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index d941b961..03b97de5 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -776,9 +776,6 @@ empty_output_buffer( j_compress_ptr cinfo ) METHODDEF(void) init_destination( j_compress_ptr cinfo ) { - OutputBuffer *buf = (OutputBuffer *) cinfo->dest; - - vips_dbuf_init( &buf->dbuf ); empty_output_buffer( cinfo ); } @@ -845,6 +842,7 @@ buf_dest( j_compress_ptr cinfo, void **obuf, size_t *olen ) /* Save output parameters. */ + vips_dbuf_init( &buf->dbuf ); buf->obuf = obuf; buf->olen = olen; } From 3e89944bf92dd63b60c8d224aed0d45ea8a9b88e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 09:23:16 +0100 Subject: [PATCH 39/63] remove a pointless optimisation from rad2float Some old rad code could cause a read beyond end of buffer. --- libvips/colour/rad2float.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/libvips/colour/rad2float.c b/libvips/colour/rad2float.c index cecbb38c..7089d2d9 100644 --- a/libvips/colour/rad2float.c +++ b/libvips/colour/rad2float.c @@ -164,17 +164,10 @@ vips_rad2float_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width ) COLR *inp = (COLR *) in[0]; COLOR *outbuf = (COLOR *) out; - colr_color(outbuf[0], inp[0]); - while (--width > 0) { - outbuf++; inp++; - if (inp[0][RED] == inp[-1][RED] && - inp[0][GRN] == inp[-1][GRN] && - inp[0][BLU] == inp[-1][BLU] && - inp[0][EXP] == inp[-1][EXP]) - copycolor(outbuf[0], outbuf[-1]); - else - colr_color(outbuf[0], inp[0]); - } + int i; + + for( i = 0; i < width; i++ ) + colr_color( outbuf[i], inp[i] ); } static void From 0895d120d80f3b3ba6defb1b129782645e369562 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 10:49:37 +0100 Subject: [PATCH 40/63] better enum validation in vips load --- libvips/iofuncs/vips.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 4e35023b..9924587d 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -382,8 +382,17 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from ) im->Ysize = VIPS_CLIP( 1, im->Ysize, VIPS_MAX_COORD ); im->Bands = VIPS_CLIP( 1, im->Bands, VIPS_MAX_COORD ); im->BandFmt = VIPS_CLIP( 0, im->BandFmt, VIPS_FORMAT_LAST - 1 ); - im->Type = VIPS_CLIP( 0, im->Type, VIPS_INTERPRETATION_LAST - 1 ); - im->Coding = VIPS_CLIP( 0, im->Coding, VIPS_CODING_LAST - 1 ); + + /* Coding and Type have missing values, so we look up in the enum. + */ + im->Type = g_enum_get_value( + g_type_class_ref( VIPS_TYPE_INTERPRETATION ), + im->Type ) ? + im->Type : VIPS_INTERPRETATION_ERROR; + im->Coding = g_enum_get_value( + g_type_class_ref( VIPS_TYPE_CODING ), + im->Coding ) ? + im->Coding : VIPS_CODING_ERROR; /* Offset, Res, etc. don't affect vips file layout, just * pixel interpretation, don't clip them. From ce684dd008532ea0bf9d4a1d89bacb35f4a83f4d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 12:50:52 +0100 Subject: [PATCH 41/63] fetch map after DGifGetImageDesc() Earlier refactoring broke GIF map fetch. --- libvips/foreign/gifload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 67fe4c80..a394da7a 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -408,9 +408,8 @@ vips_foreign_load_gif_scan_image( VipsForeignLoadGif *gif ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); GifFileType *file = gif->file; - ColorMapObject *map = file->Image.ColorMap ? - file->Image.ColorMap : file->SColorMap; + ColorMapObject *map; GifByteType *extension; if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) { @@ -435,6 +434,7 @@ vips_foreign_load_gif_scan_image( VipsForeignLoadGif *gif ) /* Test for a non-greyscale colourmap for this frame. */ + map = file->Image.ColorMap ? file->Image.ColorMap : file->SColorMap; if( !gif->has_colour && map ) { int i; From 5cce83a2941f66fe50a903e673272cc0eceb56d8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 13:04:28 +0100 Subject: [PATCH 42/63] check image bounds for GIF load It seems giflib does no checking of image dimensions at all, not even height == 0. --- ChangeLog | 1 + libvips/foreign/gifload.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6f3cb5ae..61d84138 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,7 @@ - make GIF parsing less strict - better feof() handling in GIF load - clip coding and interpretation on vips image read +- check image bounds for GIF load 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index a394da7a..eeb08b9c 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -1043,6 +1043,8 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) static int vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) { + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); + #ifdef HAVE_GIFLIB_5 { int error; @@ -1062,6 +1064,17 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) gif->eof = FALSE; gif->current_page = 0; + /* giflib does no checking of image dimensions, not even for 0. + */ + if( gif->file->SWidth <= 0 || + gif->file->SWidth > VIPS_MAX_COORD || + gif->file->SHeight <= 0 || + gif->file->SHeight > VIPS_MAX_COORD ) { + vips_error( class->nickname, + "%s", _( "image size out of bounds" ) ); + return( -1 ); + } + /* Allocate a line buffer now that we have the GIF width. */ VIPS_FREE( gif->line ) From 848df69a9cbf2f38847f6ca7cf0b21fa3b2bc169 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 10:49:37 +0100 Subject: [PATCH 43/63] better enum validation in vips load --- libvips/iofuncs/vips.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 4e35023b..9924587d 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -382,8 +382,17 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from ) im->Ysize = VIPS_CLIP( 1, im->Ysize, VIPS_MAX_COORD ); im->Bands = VIPS_CLIP( 1, im->Bands, VIPS_MAX_COORD ); im->BandFmt = VIPS_CLIP( 0, im->BandFmt, VIPS_FORMAT_LAST - 1 ); - im->Type = VIPS_CLIP( 0, im->Type, VIPS_INTERPRETATION_LAST - 1 ); - im->Coding = VIPS_CLIP( 0, im->Coding, VIPS_CODING_LAST - 1 ); + + /* Coding and Type have missing values, so we look up in the enum. + */ + im->Type = g_enum_get_value( + g_type_class_ref( VIPS_TYPE_INTERPRETATION ), + im->Type ) ? + im->Type : VIPS_INTERPRETATION_ERROR; + im->Coding = g_enum_get_value( + g_type_class_ref( VIPS_TYPE_CODING ), + im->Coding ) ? + im->Coding : VIPS_CODING_ERROR; /* Offset, Res, etc. don't affect vips file layout, just * pixel interpretation, don't clip them. From 32e944349d9d16b0d63d78516dda68278f0c823c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 14:08:12 +0100 Subject: [PATCH 44/63] check frame bounds for gif load giflib does not check this either :( --- libvips/foreign/gifload.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index eeb08b9c..c3d0fe11 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -723,6 +723,11 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) { GifFileType *file = gif->file; + if( DGifGetImageDesc( file ) == GIF_ERROR ) { + vips_foreign_load_gif_error( gif ); + return( -1 ); + } + /* Update the colour map for this frame. */ vips_foreign_load_gif_build_cmap( gif ); @@ -748,7 +753,28 @@ vips_foreign_load_gif_render( VipsForeignLoadGif *gif ) VIPS_IMAGE_ADDR( gif->frame, 0, 0 ), VIPS_IMAGE_SIZEOF_IMAGE( gif->frame ) ); - if( file->Image.Interlace ) { + /* giflib does not check that the Left / Top / Width / Height for this + * Image is inside the canvas. + * + * We could clip against the canvas, but for now, just ignore out of + * bounds frames. Watch for int overflow too. + */ + if( file->Image.Left < 0 || + file->Image.Left > VIPS_MAX_COORD || + file->Image.Width <= 0 || + file->Image.Width > VIPS_MAX_COORD || + file->Image.Left + file->Image.Width > file->SWidth || + file->Image.Top < 0 || + file->Image.Top > VIPS_MAX_COORD || + file->Image.Height <= 0 || + file->Image.Height > VIPS_MAX_COORD || + file->Image.Top + file->Image.Height > file->SHeight ) { + VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: " + "out of bounds frame of %d x %d pixels at %d x %d\n", + file->Image.Width, file->Image.Height, + file->Image.Left, file->Image.Top ); + } + else if( file->Image.Interlace ) { int i; VIPS_DEBUG_MSG( "vips_foreign_load_gif_render: " @@ -862,11 +888,6 @@ vips_foreign_load_gif_next_page( VipsForeignLoadGif *gif ) VIPS_DEBUG_MSG( "vips_foreign_load_gif_next_page: " "IMAGE_DESC_RECORD_TYPE\n" ); - if( DGifGetImageDesc( gif->file ) == GIF_ERROR ) { - vips_foreign_load_gif_error( gif ); - return( -1 ); - } - if( vips_foreign_load_gif_render( gif ) ) return( -1 ); From 6fafbce81a63a0f09527a7305a98ed3e17c042b0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 14:10:57 +0100 Subject: [PATCH 45/63] add test case for out of bounds gif frame --- ...se-minimized-smartcrop_fuzzer-5687924892368896 | Bin 0 -> 109 bytes fuzz/x.png | Bin 96 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-smartcrop_fuzzer-5687924892368896 delete mode 100644 fuzz/x.png diff --git a/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-smartcrop_fuzzer-5687924892368896 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-smartcrop_fuzzer-5687924892368896 new file mode 100644 index 0000000000000000000000000000000000000000..b4f3de626a4fcdd75f3b4fa9c5fb27a85bffe983 GIT binary patch literal 109 rcmZ?wbhA)UP+(AC_z0vF6#fIze<1p=_@4#DXV3wWU;yUBIAqZPVRICY literal 0 HcmV?d00001 diff --git a/fuzz/x.png b/fuzz/x.png deleted file mode 100644 index c8ad1f99de435cd1356af09ede98807b23a202eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^3Lwk@BpAX3RW*PVXMsm#F#`kh3lL`9S{;-P6cqAw oaSXBOO-_(tU7R4&!}NxMfklCVam`72DWEKar>mdKI;Vst02yHuS^xk5 From d61a85f6c9c5400d8fbdd8f3ff67d5f82a9d6604 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 14:47:36 +0100 Subject: [PATCH 46/63] prevent over-pre-shrink in thumbnail We could pre-shrink so much that an axis went to 0. See https://github.com/lovell/sharp/issues/1782#issuecomment-525249430 --- ChangeLog | 1 + libvips/resample/thumbnail.c | 33 ++++++++++++++------------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index 61d84138..edb52682 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,7 @@ - better feof() handling in GIF load - clip coding and interpretation on vips image read - check image bounds for GIF load +- prevent over-pre-shrink in thumbnail [kleisauke] 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 89fbf48a..22838ae4 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -22,6 +22,8 @@ * - don't force import CMYK, since colourspace knows about it now * 24/4/19 * - support multi-page (animated) images + * 27/8/19 kleisauke + * - prevent over-pre-shrink in thumbnail */ /* @@ -464,36 +466,24 @@ vips_thumbnail_open( VipsThumbnail *thumbnail ) factor = 1.0; - if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) { + if( vips_isprefix( "VipsForeignLoadJpeg", thumbnail->loader ) ) factor = vips_thumbnail_find_jpegshrink( thumbnail, thumbnail->input_width, thumbnail->input_height ); - - g_info( "loading jpeg with factor %g pre-shrink", factor ); - } else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) || vips_isprefix( "VipsForeignLoadOpenslide", - thumbnail->loader ) ) { + thumbnail->loader ) ) factor = vips_thumbnail_find_pyrlevel( thumbnail, thumbnail->input_width, thumbnail->input_height ); - - g_info( "loading pyr level %g", factor ); - } - else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ) { + else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ) factor = 1.0 / vips_thumbnail_calculate_common_shrink( thumbnail, thumbnail->input_width, thumbnail->page_height ); - - g_info( "loading PDF with factor %g pre-scale", factor ); - } - else if( vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) { + else if( vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) factor = 1.0 / vips_thumbnail_calculate_common_shrink( thumbnail, thumbnail->input_width, thumbnail->input_height ); - - g_info( "loading SVG with factor %g pre-scale", factor ); - } else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) { /* 'factor' is a gboolean which enables thumbnail load instead * of image load. @@ -507,14 +497,19 @@ vips_thumbnail_open( VipsThumbnail *thumbnail ) factor = 0.0; } - else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) { + else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) factor = 1.0 / vips_thumbnail_calculate_common_shrink( thumbnail, thumbnail->input_width, thumbnail->page_height ); - g_info( "loading webp with factor %g pre-scale", factor ); - } + /* We don't want to pre-shrink so much that we send an axis to 0. + */ + if( factor > thumbnail->input_width || + factor > thumbnail->input_height ) + factor = 1.0; + + g_info( "loading with factor %g pre-shrink", factor ); if( !(im = class->open( thumbnail, factor )) ) return( NULL ); From 378537121f62a487644c94c2cb0883c03c793d5a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 27 Aug 2019 18:00:26 +0100 Subject: [PATCH 47/63] revise thumbnail preshrink again --- libvips/resample/thumbnail.c | 45 +++++++++++++++--------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 22838ae4..c38be69b 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -370,11 +370,20 @@ vips_thumbnail_calculate_common_shrink( VipsThumbnail *thumbnail, { double hshrink; double vshrink; + double shrink; vips_thumbnail_calculate_shrink( thumbnail, width, height, &hshrink, &vshrink ); - return( VIPS_MIN( hshrink, vshrink ) ); + shrink = VIPS_MIN( hshrink, vshrink ); + + /* We don't want to shrink so much that we send an axis to 0. + */ + if( shrink > thumbnail->input_width || + shrink > thumbnail->input_height ) + shrink = 1.0; + + return( shrink ); } /* Find the best jpeg preload shrink. @@ -474,17 +483,13 @@ vips_thumbnail_open( VipsThumbnail *thumbnail ) thumbnail->loader ) ) factor = vips_thumbnail_find_pyrlevel( thumbnail, thumbnail->input_width, thumbnail->input_height ); - else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) ) - factor = 1.0 / - vips_thumbnail_calculate_common_shrink( thumbnail, - thumbnail->input_width, - thumbnail->page_height ); - else if( vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) - factor = 1.0 / - vips_thumbnail_calculate_common_shrink( thumbnail, - thumbnail->input_width, - thumbnail->input_height ); - else if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) { + else if( vips_isprefix( "VipsForeignLoadPdf", thumbnail->loader ) || + vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) || + vips_isprefix( "VipsForeignLoadSvg", thumbnail->loader ) ) + factor = vips_thumbnail_calculate_common_shrink( thumbnail, + thumbnail->input_width, + thumbnail->page_height ); + if( vips_isprefix( "VipsForeignLoadHeif", thumbnail->loader ) ) { /* 'factor' is a gboolean which enables thumbnail load instead * of image load. * @@ -495,19 +500,7 @@ vips_thumbnail_open( VipsThumbnail *thumbnail ) factor = 1.0; else factor = 0.0; - } - else if( vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) - factor = 1.0 / - vips_thumbnail_calculate_common_shrink( thumbnail, - thumbnail->input_width, - thumbnail->page_height ); - - /* We don't want to pre-shrink so much that we send an axis to 0. - */ - if( factor > thumbnail->input_width || - factor > thumbnail->input_height ) - factor = 1.0; g_info( "loading with factor %g pre-shrink", factor ); @@ -947,7 +940,7 @@ vips_thumbnail_file_open( VipsThumbnail *thumbnail, double factor ) vips_isprefix( "VipsForeignLoadWebp", thumbnail->loader ) ) { return( vips_image_new_from_file( file->filename, "access", VIPS_ACCESS_SEQUENTIAL, - "scale", factor, + "scale", 1.0 / factor, NULL ) ); } else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) { @@ -1145,7 +1138,7 @@ vips_thumbnail_buffer_open( VipsThumbnail *thumbnail, double factor ) buffer->buf->data, buffer->buf->length, buffer->option_string, "access", VIPS_ACCESS_SEQUENTIAL, - "scale", factor, + "scale", 1.0 / factor, NULL ) ); } else if( vips_isprefix( "VipsForeignLoadTiff", thumbnail->loader ) ) { From c1e46c53b5b25b6f0c3c5efe4b7c337bccb2af6b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 28 Aug 2019 06:47:48 +0100 Subject: [PATCH 48/63] clarify docs for pdfload Expand note on @background for pdfload. See https://github.com/libvips/libvips/issues/1412 --- libvips/foreign/pdfload.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libvips/foreign/pdfload.c b/libvips/foreign/pdfload.c index bf2dffde..888f9d50 100644 --- a/libvips/foreign/pdfload.c +++ b/libvips/foreign/pdfload.c @@ -323,7 +323,10 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) vips_foreign_load_pdf_set_image( pdf, load->out ); - /* Convert the background to the image format. + /* Convert the background to the image format. + * + * FIXME ... we probably should convert this to pre-multiplied BGRA + * to match the Cairo convention. See vips__cairo2rgba(). */ if( !(pdf->ink = vips__vector_to_ink( class->nickname, load->out, @@ -747,8 +750,8 @@ vips_foreign_load_pdf_is_a( const char *filename ) * you can scale the rendering from the default 1 point == 1 pixel by * setting @scale. * - * Use @background to set the background colour, including transparency. The - * default is 255 (solid white). + * Use @background to set the background RGBA colour. The default is 255 + * (solid white), use eg. 0 for a transparent background. * * The operation fills a number of header fields with metadata, for example * "pdf-author". They may be useful. From 46212e92b1f943e6852e807db1ee6e5ca66b6ccf Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 28 Aug 2019 09:16:40 +0100 Subject: [PATCH 49/63] fix default sharpen sharpen with sigma 0.5 was doing nothing, since the new int precision rules meant that we generated a point-point convolution. This patch increases the int precision for sharpen, so we now work with sigma down to 0.5. Also: restore input colourspace. Previously, the output image was always LabS (the computation space for sharpen). Now, it transforms back to the input space. This is more in line with how other operators work. Thanks 2h4dl for pointing this out. See https://github.com/libvips/pyvips/issues/123 --- ChangeLog | 2 ++ libvips/convolution/sharpen.c | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index edb52682..1374ffad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,8 @@ - clip coding and interpretation on vips image read - check image bounds for GIF load - prevent over-pre-shrink in thumbnail [kleisauke] +- fix sharpen with sigma 0.5 [2h4dl] +- sharpen restores input colourspace 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/convolution/sharpen.c b/libvips/convolution/sharpen.c index dc817575..38df2e48 100644 --- a/libvips/convolution/sharpen.c +++ b/libvips/convolution/sharpen.c @@ -39,6 +39,9 @@ * - swap "radius" for "sigma", allows finer control * - allow a much greater range of parameters * - move to defaults suitable for screen output + * 28/8/19 + * - fix sigma 0.5 case (thanks 2h4dl) + * - restore input colourspace */ /* @@ -170,11 +173,12 @@ vips_sharpen_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsSharpen *sharpen = (VipsSharpen *) object; - VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 ); + VipsImage **t = (VipsImage **) vips_object_local_array( object, 8 ); VipsImage **args = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *in; int i; + VipsInterpretation old_interpretation; VIPS_GATE_START( "vips_sharpen_build: build" ); @@ -190,6 +194,7 @@ vips_sharpen_build( VipsObject *object ) in = sharpen->in; + old_interpretation = in->Type; if( vips_colourspace( in, &t[0], VIPS_INTERPRETATION_LABS, NULL ) ) return( -1 ); in = t[0]; @@ -199,10 +204,10 @@ vips_sharpen_build( VipsObject *object ) vips_check_format( class->nickname, in, VIPS_FORMAT_SHORT ) ) return( -1 ); - /* Stop at 20% of max ... a bit mean. We always sharpen a short, + /* Stop at 10% of max ... a bit mean. We always sharpen a short, * so there's no point using a float mask. */ - if( vips_gaussmat( &t[1], sharpen->sigma, 0.2, + if( vips_gaussmat( &t[1], sharpen->sigma, 0.1, "separable", TRUE, "precision", VIPS_PRECISION_INTEGER, NULL ) ) @@ -284,10 +289,11 @@ vips_sharpen_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - /* Reattach the rest. + /* Reattach the rest, back to the start colourspace. */ if( vips_bandjoin2( t[5], t[3], &t[6], NULL ) || - vips_image_write( t[6], sharpen->out ) ) + vips_colourspace( t[6], &t[7], old_interpretation, NULL ) || + vips_image_write( t[7], sharpen->out ) ) return( -1 ); VIPS_GATE_STOP( "vips_sharpen_build: build" ); From f8bdc0086695dbbeb2d3f97b69d240d38f49258c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 29 Aug 2019 14:20:30 +0100 Subject: [PATCH 50/63] void /0 in Yxy2XYZ Now sets 0 rather than inf. --- libvips/colour/Yxy2XYZ.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/libvips/colour/Yxy2XYZ.c b/libvips/colour/Yxy2XYZ.c index b15e24e3..04b2e135 100644 --- a/libvips/colour/Yxy2XYZ.c +++ b/libvips/colour/Yxy2XYZ.c @@ -7,7 +7,9 @@ * - gtkdoc * - cleanups * 20/9/12 - * redo as a class + * - redo as a class + * 29/8/19 + * - avoid /0 */ /* @@ -67,18 +69,26 @@ vips_Yxy2XYZ_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width ) float x = p[1]; float y = p[2]; - double total; float X, Z; - p += 3; + if( x == 0.0 || + y == 0.0 ) { + X = 0.0; + Z = 0.0; + } + else { + double total; - total = Y / y; - X = x * total; - Z = (X - x * X - x * Y) / x; + total = Y / y; + X = x * total; + Z = (X - x * X - x * Y) / x; + } q[0] = X; q[1] = Y; q[2] = Z; + + p += 3; q += 3; } } From 3161de3b526d77d91f3c52f6b7597d786796ce0a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 29 Aug 2019 15:28:55 +0100 Subject: [PATCH 51/63] verify bands/format for coded vips images WHen loading a vips image with Coding set, make sure that Bands and BandFmt are correct. --- ChangeLog | 1 + libvips/iofuncs/vips.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1374ffad..b7fad021 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,7 @@ - prevent over-pre-shrink in thumbnail [kleisauke] - fix sharpen with sigma 0.5 [2h4dl] - sharpen restores input colourspace +- verify bands/format for coded images 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 9924587d..5b43f101 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -25,6 +25,8 @@ * - use O_TMPFILE, if available * 23/7/18 * - escape ASCII control characters in XML + * 29/8/19 + * - verify bands/format for coded images */ /* @@ -348,8 +350,8 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from ) from += 4; if( im->magic != VIPS_MAGIC_INTEL && im->magic != VIPS_MAGIC_SPARC ) { - vips_error( "VipsImage", _( "\"%s\" is not a VIPS image" ), - im->filename ); + vips_error( "VipsImage", + _( "\"%s\" is not a VIPS image" ), im->filename ); return( -1 ); } @@ -398,6 +400,36 @@ vips__read_header_bytes( VipsImage *im, unsigned char *from ) * pixel interpretation, don't clip them. */ + /* Coding values imply Bands and BandFmt settings --- make sure they + * are sane. + */ + switch( im->Coding ) { + case VIPS_CODING_NONE: + break; + + case VIPS_CODING_LABQ: + if( im->Bands != 4 || + im->BandFmt != VIPS_FORMAT_UCHAR ) { + vips_error( "VipsImage", + "%s", _( "malformed LABQ image" ) ); + return( -1 ); + } + break; + + case VIPS_CODING_RAD: + if( im->Bands != 4 || + im->BandFmt != VIPS_FORMAT_UCHAR ) { + vips_error( "VipsImage", + "%s", _( "malformed RAD image" ) ); + return( -1 ); + } + break; + + default: + g_assert_not_reached(); + break; + } + return( 0 ); } From f135a6e292ce9e3b80a2f69ca3cd73912e5359ee Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 29 Aug 2019 15:30:49 +0100 Subject: [PATCH 52/63] add corpus for Coding verification --- ...nimized-jpegsave_file_fuzzer-5662041322291200 | Bin 0 -> 4078 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_file_fuzzer-5662041322291200 diff --git a/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_file_fuzzer-5662041322291200 b/fuzz/common_fuzzer_corpus/clusterfuzz-testcase-minimized-jpegsave_file_fuzzer-5662041322291200 new file mode 100644 index 0000000000000000000000000000000000000000..6e95e9ec803ad7e2db5f62e3a005462423158ecb GIT binary patch literal 4078 zcmeHJu}T9$5S_aijbd>TMPhXbenB`}4M-q@p#DLAK`aCdgN41|57^`rQd?MwO{xSd z1xvB83O4IrbmlHQdv|-D8=DM-nSJx-?d^vcx z@>w&O@()l}JCk$IvocU73I(_U12Ccb6rw(*M^#zlwSgQ_C_r@vV4|=$P$mimxGMuN zk>%-olR!)n)~EDnf_gS=AV(AmP?iChVBUa96bev<0hloN2kq07hyK`(d)=$M%WC7$ zfgADoW3W+mz%4oQUnrU5R#!F~;t48_>#eQ{&+{->E`c*A?qSP=>!YQ&_1bn2)Pvet zN6Ni6Tg?FsBI}4Se)$IAB1#P5T|9%g-)!y0`Gdp)S&9v-9-y+YJc^EK{glx$a&*wAo}Cf~*`KzXO$z7^r4kz*NthSz&Odl2+m)n{t|vS!2o$Kihg1B+rx literal 0 HcmV?d00001 From 57d1d86885b5da8c5a9e3f3a6dca584c8a6a7188 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 29 Aug 2019 15:42:05 +0100 Subject: [PATCH 53/63] avoid a harmless out of bounds read in UCS table building --- libvips/colour/UCS2LCh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/colour/UCS2LCh.c b/libvips/colour/UCS2LCh.c index e6b46530..6fe2e6fe 100644 --- a/libvips/colour/UCS2LCh.c +++ b/libvips/colour/UCS2LCh.c @@ -127,7 +127,7 @@ make_hI( void ) for( i = 0; i < 361; i++ ) { int k; - for( k = 0; k < 360 && hl[j][k] <= i; k++ ) + for( k = 1; k < 360 && hl[j][k] <= i; k++ ) ; hI[j][i] = k - 1 + (i - hl[j][k - 1]) / From 6ea76f9632edd93a716533acb78e7f6bd7089fe4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 30 Aug 2019 11:00:25 +0100 Subject: [PATCH 54/63] improve data_length handling in jpg load libjpeg uses unsigned ints for data length, so we must use size_t everywhere. --- ChangeLog | 1 + libvips/foreign/jpeg2vips.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index b7fad021..925eb6ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,7 @@ - fix sharpen with sigma 0.5 [2h4dl] - sharpen restores input colourspace - verify bands/format for coded images +- improve data_length handling for jpeg metadata 24/5/19 started 8.8.1 - improve realpath() use on older libc diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 1060bc9e..d4af258f 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -307,7 +307,7 @@ find_chroma_subsample( struct jpeg_decompress_struct *cinfo ) } static int -attach_blob( VipsImage *im, const char *field, void *data, int data_length ) +attach_blob( VipsImage *im, const char *field, void *data, size_t data_length ) { /* Only use the first one. */ @@ -333,7 +333,7 @@ attach_blob( VipsImage *im, const char *field, void *data, int data_length ) * the real XMP. */ static int -attach_xmp_blob( VipsImage *im, void *data, int data_length ) +attach_xmp_blob( VipsImage *im, void *data, size_t data_length ) { char *p = (char *) data; int i; @@ -496,7 +496,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) for( p = cinfo->marker_list; p; p = p->next ) { #ifdef DEBUG { - printf( "read_jpeg_header: seen %d bytes of APP%d\n", + printf( "read_jpeg_header: seen %u bytes of APP%d\n", p->data_length, p->marker - JPEG_APP0 ); @@ -593,7 +593,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) default: #ifdef DEBUG printf( "read_jpeg_header: " - "ignoring %d byte APP%d block\n", + "ignoring %u byte APP%d block\n", p->data_length, p->marker - JPEG_APP0 ); #endif /*DEBUG*/ break; From 4b5b982711f2e3bc372235756c89428ad20c6b4c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 31 Aug 2019 12:29:50 +0100 Subject: [PATCH 55/63] merge 8.8 --- ChangeLog | 3 +++ libvips/convolution/sharpen.c | 11 ++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index ebdac296..534fde5d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,9 @@ - better const handling for arithmetic operators fixes comparisons against out of range values +31/8/19 started 8.8.3 +- revert sharpen restoring the input colourspace + 9/7/19 started 8.8.2 - better early shutdown in readers - don't attempt to save large XMP to jpeg [tnextday] diff --git a/libvips/convolution/sharpen.c b/libvips/convolution/sharpen.c index 38df2e48..214d52ee 100644 --- a/libvips/convolution/sharpen.c +++ b/libvips/convolution/sharpen.c @@ -41,7 +41,6 @@ * - move to defaults suitable for screen output * 28/8/19 * - fix sigma 0.5 case (thanks 2h4dl) - * - restore input colourspace */ /* @@ -178,7 +177,6 @@ vips_sharpen_build( VipsObject *object ) VipsImage *in; int i; - VipsInterpretation old_interpretation; VIPS_GATE_START( "vips_sharpen_build: build" ); @@ -194,7 +192,6 @@ vips_sharpen_build( VipsObject *object ) in = sharpen->in; - old_interpretation = in->Type; if( vips_colourspace( in, &t[0], VIPS_INTERPRETATION_LABS, NULL ) ) return( -1 ); in = t[0]; @@ -274,9 +271,6 @@ vips_sharpen_build( VipsObject *object ) NULL ) ) return( -1 ); - /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause - * too many recalculations on overlaps. - */ t[5] = vips_image_new(); if( vips_image_pipeline_array( t[5], VIPS_DEMAND_STYLE_FATSTRIP, args ) ) @@ -289,11 +283,10 @@ vips_sharpen_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - /* Reattach the rest, back to the start colourspace. + /* Reattach the rest. */ if( vips_bandjoin2( t[5], t[3], &t[6], NULL ) || - vips_colourspace( t[6], &t[7], old_interpretation, NULL ) || - vips_image_write( t[7], sharpen->out ) ) + vips_image_write( t[6], sharpen->out ) ) return( -1 ); VIPS_GATE_STOP( "vips_sharpen_build: build" ); From a3d25449349802e4b743775d98f345e3a5e4f32a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 31 Aug 2019 12:30:06 +0100 Subject: [PATCH 56/63] sharpen restores colourspace --- libvips/convolution/sharpen.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libvips/convolution/sharpen.c b/libvips/convolution/sharpen.c index 214d52ee..795299a6 100644 --- a/libvips/convolution/sharpen.c +++ b/libvips/convolution/sharpen.c @@ -41,6 +41,7 @@ * - move to defaults suitable for screen output * 28/8/19 * - fix sigma 0.5 case (thanks 2h4dl) + * - restore input colourspace */ /* @@ -176,6 +177,7 @@ vips_sharpen_build( VipsObject *object ) VipsImage **args = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *in; + VipsInterpretation old_interpretation; int i; VIPS_GATE_START( "vips_sharpen_build: build" ); @@ -192,6 +194,7 @@ vips_sharpen_build( VipsObject *object ) in = sharpen->in; + old_interpretation = in->Type; if( vips_colourspace( in, &t[0], VIPS_INTERPRETATION_LABS, NULL ) ) return( -1 ); in = t[0]; @@ -286,7 +289,8 @@ vips_sharpen_build( VipsObject *object ) /* Reattach the rest. */ if( vips_bandjoin2( t[5], t[3], &t[6], NULL ) || - vips_image_write( t[6], sharpen->out ) ) + vips_colourspace( t[6], &t[7], old_interpretation, NULL ) || + vips_image_write( t[7], sharpen->out ) ) return( -1 ); VIPS_GATE_STOP( "vips_sharpen_build: build" ); From b3bbf47a467bc4b3279f690fb0c879e1319c3723 Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sat, 31 Aug 2019 14:29:09 -0400 Subject: [PATCH 57/63] configure.ac: drop --enable-cpp7 option It's no longer connected to anything. --- configure.ac | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/configure.ac b/configure.ac index 818c7605..c9827fe3 100644 --- a/configure.ac +++ b/configure.ac @@ -560,18 +560,6 @@ if test x"$expat_found" = x"no"; then exit 1 fi -# enable vips7 C++ binding ... this defaults off, the vips8 C++ binding -# defaults on -AC_ARG_ENABLE([cpp7], - AS_HELP_STRING([--enable-cpp7], - [enable deprecated vips7 C++ binding (default: no)]), - [enable_cpp7=$enableval - ], - [enable_cpp7="no (default)" - ] -) -AM_CONDITIONAL(ENABLE_CPP7, [test x"$enable_cpp7" = x"yes"]) - # optional supporting libraries AC_ARG_WITH([gsf], From 8733743642c4228ea66af169e8c2bc6d0c6e7d9c Mon Sep 17 00:00:00 2001 From: Benjamin Gilbert Date: Sun, 1 Sep 2019 02:26:34 -0400 Subject: [PATCH 58/63] configure.ac: drop --enable-pyvips8 option It doesn't do anything either. --- configure.ac | 34 -------- m4/python.m4 | 239 --------------------------------------------------- 2 files changed, 273 deletions(-) delete mode 100644 m4/python.m4 diff --git a/configure.ac b/configure.ac index c9827fe3..f61de972 100644 --- a/configure.ac +++ b/configure.ac @@ -1112,40 +1112,6 @@ if test x"$with_pangoft2" != x"no"; then ) fi -# install vips8 python -AC_ARG_ENABLE([pyvips8], - AS_HELP_STRING([--enable-pyvips8], - [install vips8 Python overrides (default: no)]), - [enable_pyvips8=$enableval - ], - [enable_pyvips8="no (default)" - ] -) - -if test x"$enable_pyvips8" = x"auto"; then - PKG_CHECK_EXISTS([pygobject-3.0 >= 3.13.0], - [enable_pyvips8=yes - ], - [AC_MSG_WARN([pygobject-3.0 not found; disabling vips8 python support]) - enable_pyvips8=no - ] - ) -fi - -if test x"$enable_pyvips8" = x"yes"; then - JD_PATH_PYTHON(2.7,, - [enable_pyvips8=no - AC_MSG_WARN([Python not found; disabling vips8 Python binding]) - ] - ) -fi - -if test x"$enable_pyvips8" = x"yes"; then - PKG_CHECK_MODULES(PYGOBJECT, [pygobject-3.0 >= 3.13.0]) -fi - -AM_CONDITIONAL(ENABLE_PYVIPS8, [test x"$enable_pyvips8" = x"yes"]) - # look for TIFF with pkg-config ... fall back to our tester # pkgconfig support for libtiff starts with libtiff-4 AC_ARG_WITH([tiff], diff --git a/m4/python.m4 b/m4/python.m4 deleted file mode 100644 index 4c3210e2..00000000 --- a/m4/python.m4 +++ /dev/null @@ -1,239 +0,0 @@ -## Imported from pygobject at commit 5737a9ec4bf4d9d07a7e3994d91abf9077b342cc. -## Automake's built-in version has problems on multiarch systems. -## this one is commonly used with AM_PATH_PYTHONDIR ... -dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]]) -dnl Check if a module containing a given symbol is visible to python. -AC_DEFUN([AM_CHECK_PYMOD], -[AC_REQUIRE([AM_PATH_PYTHON]) -py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` -AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1) -AC_CACHE_VAL(py_cv_mod_$py_mod_var, [ -ifelse([$2],[], [prog=" -import sys -try: - import $1 -except ImportError: - sys.exit(1) -except: - sys.exit(0) -sys.exit(0)"], [prog=" -import $1 -$1.$2"]) -if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC - then - eval "py_cv_mod_$py_mod_var=yes" - else - eval "py_cv_mod_$py_mod_var=no" - fi -]) -py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"` -if test "x$py_val" != xno; then - AC_MSG_RESULT(yes) - ifelse([$3], [],, [$3 -])dnl -else - AC_MSG_RESULT(no) - ifelse([$4], [],, [$4 -])dnl -fi -]) - -dnl a macro to check for ability to create python extensions -dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) -dnl function also defines PYTHON_INCLUDES -AC_DEFUN([AM_CHECK_PYTHON_HEADERS], -[AC_REQUIRE([AM_PATH_PYTHON]) -AC_MSG_CHECKING(for headers required to compile python extensions) -dnl deduce PYTHON_INCLUDES -if test "x$PYTHON_INCLUDES" = x; then - PYTHON_CONFIG=`which $PYTHON`-config - if test -x "$PYTHON_CONFIG"; then - PYTHON_INCLUDES=`$PYTHON_CONFIG --includes 2>/dev/null` - else - PYTHON_INCLUDES=`$PYTHON -c "import distutils.sysconfig, sys; sys.stdout.write(distutils.sysconfig.get_python_inc(True))"` - PYTHON_INCLUDES="-I$PYTHON_INCLUDES" - fi -fi -AC_SUBST(PYTHON_INCLUDES) -dnl check if the headers exist: -save_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" -AC_TRY_CPP([#include ],dnl -[AC_MSG_RESULT(found) -$1],dnl -[AC_MSG_RESULT(not found) -$2]) -CPPFLAGS="$save_CPPFLAGS" -]) - -dnl a macro to check for ability to embed python -dnl AM_CHECK_PYTHON_LIBS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) -dnl function also defines PYTHON_LIBS -AC_DEFUN([AM_CHECK_PYTHON_LIBS], -[AC_REQUIRE([AM_PATH_PYTHON]) -AC_MSG_CHECKING(for libraries required to embed python) -dnl deduce PYTHON_LIBS -py_prefix=`$PYTHON -c "import sys; sys.stdout.write(sys.prefix)"` -if test "x$PYTHON_LIBS" = x; then - PYTHON_CONFIG=`which $PYTHON`-config - if test -x "$PYTHON_CONFIG"; then - PYTHON_LIBS=`$PYTHON_CONFIG --ldflags 2>/dev/null` - else - PYTHON_LIBS="-L${py_prefix}/lib -lpython${PYTHON_VERSION}" - fi -fi -if test "x$PYTHON_LIB_LOC" = x; then - PYTHON_LIB_LOC="${py_prefix}/lib" -fi -AC_SUBST(PYTHON_LIBS) -AC_SUBST(PYTHON_LIB_LOC) -dnl check if the headers exist: -save_LIBS="$LIBS" -LIBS="$LIBS $PYTHON_LIBS" -AC_TRY_LINK_FUNC(Py_Initialize, dnl - [LIBS="$save_LIBS"; AC_MSG_RESULT(yes); $1], dnl - [LIBS="$save_LIBS"; AC_MSG_RESULT(no); $2]) - -]) - -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 -# Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# JD_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# --------------------------------------------------------------------------- -# Adds support for distributing Python modules and packages. To -# install modules, copy them to $(pythondir), using the python_PYTHON -# automake variable. To install a package with the same name as the -# automake package, install to $(pkgpythondir), or use the -# pkgpython_PYTHON automake variable. -# -# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as -# locations to install python extension modules (shared libraries). -# Another macro is required to find the appropriate flags to compile -# extension modules. -# -# If your package is configured with a different prefix to python, -# users will have to add the install directory to the PYTHONPATH -# environment variable, or create a .pth file (see the python -# documentation for details). -# -# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will -# cause an error if the version of python installed on the system -# doesn't meet the requirement. MINIMUM-VERSION should consist of -# numbers and dots only. -AC_DEFUN([JD_PATH_PYTHON], - [ - dnl Find a Python interpreter. Python versions prior to 2.0 are not - dnl supported - m4_define_default([_AM_PYTHON_INTERPRETER_LIST], - [python python2 python2.7 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0]) - - m4_if([$1],[],[ - dnl No version check is needed. - # Find any Python interpreter. - if test -z "$PYTHON"; then - AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) - fi - am_display_PYTHON=python - ], [ - dnl A version check is needed. - if test -n "$PYTHON"; then - # If the user set $PYTHON, use it and don't search something else. - AC_MSG_CHECKING([whether $PYTHON version >= $1]) - AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], - [AC_MSG_RESULT(yes)], - [AC_MSG_ERROR(too old)]) - am_display_PYTHON=$PYTHON - else - # Otherwise, try each interpreter until we find one that satisfies - # VERSION. - AC_CACHE_CHECK([for a Python interpreter with version >= $1], - [am_cv_pathless_PYTHON],[ - for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do - test "$am_cv_pathless_PYTHON" = none && break - AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) - done]) - # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. - if test "$am_cv_pathless_PYTHON" = none; then - PYTHON=: - else - AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) - fi - am_display_PYTHON=$am_cv_pathless_PYTHON - fi - ]) - - if test "$PYTHON" = :; then - dnl Run any user-specified action, or abort. - m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) - else - - dnl Query Python for its version number. Getting [:3] seems to be - dnl the best way to do this; it's what "site.py" does in the standard - dnl library. - - AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], - [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) - AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) - - dnl Use the values of $prefix and $exec_prefix for the corresponding - dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made - dnl distinct variables so they can be overridden if need be. However, - dnl general consensus is that you shouldn't need this ability. - - AC_SUBST([PYTHON_PREFIX], ['${prefix}']) - AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) - - dnl At times (like when building shared libraries) you may want - dnl to know which OS platform Python thinks this is. - - AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], - [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) - AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) - - - dnl Set up 4 directories: - - dnl pythondir -- where to install python scripts. This is the - dnl site-packages directory, not the python standard library - dnl directory like in previous automake betas. This behavior - dnl is more consistent with lispdir.m4 for example. - dnl Query distutils for this directory. distutils does not exist in - dnl Python 1.5, so we fall back to the hardcoded directory if it - dnl doesn't work. - AC_CACHE_CHECK([for $am_display_PYTHON script directory], - [am_cv_python_pythondir], - [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print(sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX'))" 2>/dev/null || - echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`]) - AC_SUBST([pythondir], [$am_cv_python_pythondir]) - - dnl pkgpythondir -- $PACKAGE directory under pythondir. Was - dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is - dnl more consistent with the rest of automake. - - AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) - - dnl pyexecdir -- directory for installing python extension modules - dnl (shared libraries) - dnl Query distutils for this directory. distutils does not exist in - dnl Python 1.5, so we fall back to the hardcoded directory if it - dnl doesn't work. - AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], - [am_cv_python_pyexecdir], - [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print(sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX'))" 2>/dev/null || - echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`]) - AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) - - dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) - - AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) - - dnl Run any user-specified action. - $2 - fi - -]) From 37eb4c73c88a924c2e94d512fc14fc097b3378db Mon Sep 17 00:00:00 2001 From: Oscar Mira Date: Sun, 1 Sep 2019 12:00:08 +0200 Subject: [PATCH 59/63] remove stale corpus dirs After #1398 these directories are not needed anymore. --- fuzz/mosaic_fuzzer_corpus/.keep | 0 fuzz/smartcrop_fuzzer_corpus/.keep | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 fuzz/mosaic_fuzzer_corpus/.keep delete mode 100644 fuzz/smartcrop_fuzzer_corpus/.keep diff --git a/fuzz/mosaic_fuzzer_corpus/.keep b/fuzz/mosaic_fuzzer_corpus/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/fuzz/smartcrop_fuzzer_corpus/.keep b/fuzz/smartcrop_fuzzer_corpus/.keep deleted file mode 100644 index e69de29b..00000000 From a1ed6c7f6c53d10b6b934d6b7a25dfadcfebd66f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 1 Sep 2019 12:54:47 +0100 Subject: [PATCH 60/63] improve GIF edarly close again We were trying to keep the FILE open for gifload between header and load, but this meant some corrupt GIFs could keep the file open longer than they should. Instead, make close into a vfunc and always close between header and load. see https://github.com/libvips/libvips/issues/1370#issuecomment-526829415 --- libvips/foreign/gifload.c | 151 +++++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 60 deletions(-) diff --git a/libvips/foreign/gifload.c b/libvips/foreign/gifload.c index 0242a08d..68ef31a2 100644 --- a/libvips/foreign/gifload.c +++ b/libvips/foreign/gifload.c @@ -32,6 +32,8 @@ * - better feof() handling * 27/8/19 * - check image and frame bounds, since giflib does not + * 1/9/19 + * - improve early close again */ /* @@ -207,6 +209,10 @@ typedef struct _VipsForeignLoadGifClass { /* Close and reopen gif->file. */ int (*open)( VipsForeignLoadGif *gif ); + + /* Close any underlying file resource. + */ + void (*close)( VipsForeignLoadGif *gif ); } VipsForeignLoadGifClass; G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadGif, vips_foreign_load_gif, @@ -301,32 +307,14 @@ vips_foreign_load_gif_error( VipsForeignLoadGif *gif ) vips_foreign_load_gif_error_vips( gif, error ); } -static void -vips_foreign_load_gif_close( VipsForeignLoadGif *gif ) -{ -#ifdef HAVE_GIFLIB_5 - if( gif->file ) { - int error; - - if( DGifCloseFile( gif->file, &error ) == GIF_ERROR ) - vips_foreign_load_gif_error_vips( gif, error ); - gif->file = NULL; - } -#else - if( gif->file ) { - if( DGifCloseFile( gif->file ) == GIF_ERROR ) - vips_foreign_load_gif_error_vips( gif, GifLastError() ); - gif->file = NULL; - } -#endif -} - static void vips_foreign_load_gif_dispose( GObject *gobject ) { VipsForeignLoadGif *gif = (VipsForeignLoadGif *) gobject; + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( gif ); - vips_foreign_load_gif_close( gif ); + class->close( gif ); VIPS_UNREF( gif->frame ); VIPS_UNREF( gif->previous ); @@ -629,18 +617,12 @@ vips_foreign_load_gif_set_header( VipsForeignLoadGif *gif, VipsImage *image ) * Don't flag errors during header scan. Many GIFs do not follow spec. */ static int -vips_foreign_load_gif_header( VipsForeignLoad *load ) +vips_foreign_load_gif_scan( VipsForeignLoadGif *gif ) { - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); - VipsForeignLoadGifClass *gif_class = - (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); - VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( gif ); GifRecordType record; - if( gif_class->open( gif ) ) - return( -1 ); - gif->n_pages = 0; do { @@ -684,11 +666,34 @@ vips_foreign_load_gif_header( VipsForeignLoad *load ) return( -1 ); } - /* And set the output vips header from what we've learned. - */ - if( vips_foreign_load_gif_set_header( gif, load->out ) ) + return( 0 ); +} + +/* Scan the GIF and set the libvips header. We always close after scan, even + * on an error. + */ +static int +vips_foreign_load_gif_header( VipsForeignLoad *load ) +{ + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + + if( class->open( gif ) ) return( -1 ); + if( vips_foreign_load_gif_scan( gif ) ) { + class->close( gif ); + return( -1 ); + } + + if( vips_foreign_load_gif_set_header( gif, load->out ) ) { + class->close( gif ); + return( -1 ); + } + + class->close( gif ); + return( 0 ); } @@ -1040,7 +1045,10 @@ vips_foreign_load_gif_generate( VipsRegion *or, static void vips_foreign_load_gif_minimise( VipsObject *object, VipsForeignLoadGif *gif ) { - vips_foreign_load_gif_close( gif ); + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( gif ); + + class->close( gif ); } static int @@ -1052,8 +1060,6 @@ vips_foreign_load_gif_load( VipsForeignLoad *load ) VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 4 ); - /* Rewind. - */ if( class->open( gif ) ) return( -1 ); @@ -1148,6 +1154,26 @@ vips_foreign_load_gif_open( VipsForeignLoadGif *gif ) return( 0 ); } +static void +vips_foreign_load_gif_close( VipsForeignLoadGif *gif ) +{ +#ifdef HAVE_GIFLIB_5 + if( gif->file ) { + int error; + + if( DGifCloseFile( gif->file, &error ) == GIF_ERROR ) + vips_foreign_load_gif_error_vips( gif, error ); + gif->file = NULL; + } +#else + if( gif->file ) { + if( DGifCloseFile( gif->file ) == GIF_ERROR ) + vips_foreign_load_gif_error_vips( gif, GifLastError() ); + gif->file = NULL; + } +#endif +} + static void vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) { @@ -1161,6 +1187,7 @@ vips_foreign_load_gif_class_init( VipsForeignLoadGifClass *class ) gobject_class->get_property = vips_object_get_property; gif_class->open = vips_foreign_load_gif_open; + gif_class->close = vips_foreign_load_gif_close; load_class->header = vips_foreign_load_gif_header; load_class->load = vips_foreign_load_gif_load; @@ -1219,17 +1246,6 @@ typedef VipsForeignLoadGifClass VipsForeignLoadGifFileClass; G_DEFINE_TYPE( VipsForeignLoadGifFile, vips_foreign_load_gif_file, vips_foreign_load_gif_get_type() ); -static void -vips_foreign_load_gif_file_dispose( GObject *gobject ) -{ - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gobject; - - VIPS_FREEF( fclose, file->fp ); - - G_OBJECT_CLASS( vips_foreign_load_gif_file_parent_class )-> - dispose( gobject ); -} - /* Our input function for file open. We can't use DGifOpenFileName(), since * that just calls open() and won't work with unicode on win32. We can't use * DGifOpenFileHandle() since that's an fd from open() and you can't pass those @@ -1250,13 +1266,15 @@ vips_giflib_file_read( GifFileType *gfile, GifByteType *buffer, int n ) static int vips_foreign_load_gif_file_header( VipsForeignLoad *load ) { - VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) load; + VipsForeignLoadGif *gif = (VipsForeignLoadGif *) load; + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); if( VIPS_FOREIGN_LOAD_CLASS( vips_foreign_load_gif_file_parent_class )->header( load ) ) { /* Close early if header read fails in our base class. */ - VIPS_FREEF( fclose, file->fp ); + class->close( gif ); return( -1 ); } @@ -1265,31 +1283,41 @@ vips_foreign_load_gif_file_header( VipsForeignLoad *load ) /* We have to have _open() as a vfunc since our base class needs to be able to * make two scans of the gif (scan for header, then scan for pixels), so we - * must be able to close and reopen (or rewind). + * must be able to close and reopen. */ static int vips_foreign_load_gif_file_open( VipsForeignLoadGif *gif ) { VipsForeignLoad *load = (VipsForeignLoad *) gif; VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( load ); - if( !file->fp ) { - if( !(file->fp = - vips__file_open_read( file->filename, NULL, FALSE )) ) - return( -1 ); + class->close( gif ); - VIPS_SETSTR( load->out->filename, file->filename ); - } - else - rewind( file->fp ); + if( !(file->fp = + vips__file_open_read( file->filename, NULL, FALSE )) ) + return( -1 ); + + VIPS_SETSTR( load->out->filename, file->filename ); - vips_foreign_load_gif_close( gif ); gif->read_func = vips_giflib_file_read; return( VIPS_FOREIGN_LOAD_GIF_CLASS( vips_foreign_load_gif_file_parent_class )->open( gif ) ); } +static void +vips_foreign_load_gif_file_close( VipsForeignLoadGif *gif ) +{ + VipsForeignLoadGifFile *file = (VipsForeignLoadGifFile *) gif; + + VIPS_FOREIGN_LOAD_GIF_CLASS( + vips_foreign_load_gif_file_parent_class )->close( gif ); + + VIPS_FREEF( fclose, file->fp ); +} + static const char *vips_foreign_gif_suffs[] = { ".gif", NULL @@ -1305,7 +1333,6 @@ vips_foreign_load_gif_file_class_init( VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; VipsForeignLoadGifClass *gif_class = (VipsForeignLoadGifClass *) class; - gobject_class->dispose = vips_foreign_load_gif_file_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -1318,6 +1345,7 @@ vips_foreign_load_gif_file_class_init( load_class->header = vips_foreign_load_gif_file_header; gif_class->open = vips_foreign_load_gif_file_open; + gif_class->close = vips_foreign_load_gif_file_close; VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), @@ -1377,8 +1405,11 @@ static int vips_foreign_load_gif_buffer_open( VipsForeignLoadGif *gif ) { VipsForeignLoadGifBuffer *buffer = (VipsForeignLoadGifBuffer *) gif; + VipsForeignLoadGifClass *class = + (VipsForeignLoadGifClass *) VIPS_OBJECT_GET_CLASS( gif ); + + class->close( gif ); - vips_foreign_load_gif_close( gif ); buffer->p = buffer->buf->data; buffer->bytes_to_go = buffer->buf->length; gif->read_func = vips_giflib_buffer_read;; From 75b45cc2ef66dd2fde565e32142c4c0fdd48dfb9 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 1 Sep 2019 16:37:43 +0100 Subject: [PATCH 61/63] enable alpha handling in heic load use RGBA decoding, when appropriate see https://github.com/libvips/libvips/issues/1411 --- ChangeLog | 1 + libvips/foreign/heifload.c | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 534fde5d..e187cb1e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,7 @@ - add vips_switch() / vips_case() ... fast many-way ifthenelse - better const handling for arithmetic operators fixes comparisons against out of range values +- handle alpha in heifload [meyermarcel] 31/8/19 started 8.8.3 - revert sharpen restoring the input colourspace diff --git a/libvips/foreign/heifload.c b/libvips/foreign/heifload.c index f468b1ed..2b8ed2f3 100644 --- a/libvips/foreign/heifload.c +++ b/libvips/foreign/heifload.c @@ -7,6 +7,8 @@ * 24/7/19 * - close early on minimise * - close early on error + * 1/9/19 [meyermarcel] + * - handle alpha */ /* @@ -86,6 +88,10 @@ typedef struct _VipsForeignLoadHeif { */ int n_top; + /* TRUE for RGBA ... otherwise, RGB. + */ + gboolean has_alpha; + /* Size of final output image. */ int width; @@ -260,7 +266,6 @@ vips_foreign_load_heif_set_page( VipsForeignLoadHeif *heif, static int vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) { - gboolean has_alpha; int bands; int i; /* Surely, 16 metadata items will be enough for anyone. @@ -275,10 +280,12 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) if( vips_foreign_load_heif_set_page( heif, heif->page, FALSE ) ) return( -1 ); - /* FIXME ... never seen this return TRUE on any image, strangely. - */ - has_alpha = heif_image_handle_has_alpha_channel( heif->handle ); - bands = has_alpha ? 4 : 3; + heif->has_alpha = heif_image_handle_has_alpha_channel( heif->handle ); +#ifdef DEBUG + printf( "heif_image_handle_has_alpha_channel() = %d\n", + heif->has_alpha ); +#endif /*DEBUG*/ + bands = heif->has_alpha ? 4 : 3; /* FIXME .. need to test XMP and IPCT. */ @@ -617,13 +624,11 @@ vips_foreign_load_heif_generate( VipsRegion *or, if( !heif->img ) { struct heif_error error; struct heif_decoding_options *options; + enum heif_chroma chroma = heif->has_alpha ? + heif_chroma_interleaved_RGBA : + heif_chroma_interleaved_RGB; - /* Decode the image to 24bit interleaved. - * - * FIXME What will this do for RGBA? Or is alpha always - * separate? - * - * Only disable transforms if we have been able to fetch the + /* Only disable transforms if we have been able to fetch the * untransformed dimensions. */ options = heif_decoding_options_alloc(); @@ -631,7 +636,7 @@ vips_foreign_load_heif_generate( VipsRegion *or, options->ignore_transformations = !heif->autorotate; #endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/ error = heif_decode_image( heif->handle, &heif->img, - heif_colorspace_RGB, heif_chroma_interleaved_RGB, + heif_colorspace_RGB, chroma, options ); heif_decoding_options_free( options ); if( error.code ) { From da8cee048ff0e22d9f8b4277b7fc1f0e9a98c9d3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 1 Sep 2019 16:58:50 +0100 Subject: [PATCH 62/63] enable alpha in heifsave Check for image alpha and enable it. There seem to be some non-transparent tiles, curiously. See https://github.com/libvips/libvips/issues/1411 --- ChangeLog | 2 +- libvips/foreign/heifsave.c | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index e187cb1e..3c99096a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,7 +11,7 @@ - add vips_switch() / vips_case() ... fast many-way ifthenelse - better const handling for arithmetic operators fixes comparisons against out of range values -- handle alpha in heifload [meyermarcel] +- handle alpha in heifload / heifsave [meyermarcel] 31/8/19 started 8.8.3 - revert sharpen restoring the input colourspace diff --git a/libvips/foreign/heifsave.c b/libvips/foreign/heifsave.c index 05a7a516..7fe44bf8 100644 --- a/libvips/foreign/heifsave.c +++ b/libvips/foreign/heifsave.c @@ -4,6 +4,8 @@ * - from niftisave.c * 3/7/19 [lovell] * - add "compression" option + * 1/9/19 [meyermarcel] + * - save alpha when necessary */ /* @@ -199,10 +201,8 @@ vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page ) #ifdef HAVE_HEIF_ENCODING_OPTIONS_ALLOC options = heif_encoding_options_alloc(); - /* FIXME .. should be an option, though I don't know of any way to - * test it - */ - options->save_alpha_channel = 1; + if( vips_image_hasalpha( save->ready ) ) + options->save_alpha_channel = 1; #else /*!HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/ options = NULL; #endif /*HAVE_HEIF_ENCODING_OPTIONS_ALLOC*/ @@ -294,6 +294,7 @@ vips_foreign_save_heif_build( VipsObject *object ) VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object; struct heif_error error; + enum heif_chroma chroma; if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )-> build( object ) ) @@ -335,8 +336,10 @@ vips_foreign_save_heif_build( VipsObject *object ) /* Make a heif image the size of a page. We send sink_disc() output * here and write a frame each time it fills. */ + chroma = vips_image_hasalpha( save->ready ) ? + heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB; error = heif_image_create( heif->page_width, heif->page_height, - heif_colorspace_RGB, heif_chroma_interleaved_RGB, &heif->img ); + heif_colorspace_RGB, chroma, &heif->img ); if( error.code ) { vips__heif_error( &error ); return( -1 ); @@ -394,7 +397,7 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class ) foreign_class->suffs = vips__heif_suffs; - save_class->saveable = VIPS_SAVEABLE_RGB; + save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY; save_class->format_table = vips_heif_bandfmt; VIPS_ARG_INT( class, "Q", 10, From 8ddbfbaf0c7481a2da53f745458bf7037e0f937c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 1 Sep 2019 20:55:25 +0100 Subject: [PATCH 63/63] fix RGBA heifsave we need to set plane bits as well see https://github.com/libvips/libvips/issues/1411 --- libvips/foreign/heifsave.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libvips/foreign/heifsave.c b/libvips/foreign/heifsave.c index 7fe44bf8..be425d72 100644 --- a/libvips/foreign/heifsave.c +++ b/libvips/foreign/heifsave.c @@ -294,7 +294,6 @@ vips_foreign_save_heif_build( VipsObject *object ) VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object; struct heif_error error; - enum heif_chroma chroma; if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )-> build( object ) ) @@ -336,17 +335,20 @@ vips_foreign_save_heif_build( VipsObject *object ) /* Make a heif image the size of a page. We send sink_disc() output * here and write a frame each time it fills. */ - chroma = vips_image_hasalpha( save->ready ) ? - heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB; error = heif_image_create( heif->page_width, heif->page_height, - heif_colorspace_RGB, chroma, &heif->img ); + heif_colorspace_RGB, + vips_image_hasalpha( save->ready ) ? + heif_chroma_interleaved_RGBA : + heif_chroma_interleaved_RGB, + &heif->img ); if( error.code ) { vips__heif_error( &error ); return( -1 ); } error = heif_image_add_plane( heif->img, heif_channel_interleaved, - heif->page_width, heif->page_height, 24 ); + heif->page_width, heif->page_height, + vips_image_hasalpha( save->ready ) ? 32 : 24 ); if( error.code ) { vips__heif_error( &error ); return( -1 );