From c14d7c254b87359ac73ec32a4c8614f39fc45ed7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 24 Jan 2020 17:55:11 +0000 Subject: [PATCH] add max and min to region_shrink Add max and min to region_shrink. Useful with tiffsave and dzsave when the image is (for example) line art. Thanks rgluskin. See: https://github.com/libvips/libvips/issues/1490 --- ChangeLog | 1 + libvips/include/vips/region.h | 4 + libvips/iofuncs/enumtypes.c | 2 + libvips/iofuncs/operation.c | 2 +- libvips/iofuncs/region.c | 186 ++++++++++++++++++---------------- 5 files changed, 106 insertions(+), 89 deletions(-) diff --git a/ChangeLog b/ChangeLog index 95ab55fe..156ace5b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 24/1/20 started 8.10.0 - more conformat IIIF output from dzsave [regisrob] - add @id to dzsave to set IIIF id property [regisrob] +- add max and min to region shrink [rgluskin] 20/6/19 started 8.9.1 - don't use the new source loaders for new_from_file or new_from_buffer, it diff --git a/libvips/include/vips/region.h b/libvips/include/vips/region.h index 2a68bb17..bcce3c47 100644 --- a/libvips/include/vips/region.h +++ b/libvips/include/vips/region.h @@ -60,6 +60,8 @@ extern "C" { * @VIPS_REGION_SHRINK_MEAN: use the average * @VIPS_REGION_SHRINK_MEDIAN: use the median * @VIPS_REGION_SHRINK_MODE: use the mode + * @VIPS_REGION_SHRINK_MAX: use the maximum + * @VIPS_REGION_SHRINK_MIN: use the minimum * * How to calculate the output pixels when shrinking a 2x2 region. */ @@ -67,6 +69,8 @@ typedef enum { VIPS_REGION_SHRINK_MEAN, VIPS_REGION_SHRINK_MEDIAN, VIPS_REGION_SHRINK_MODE, + VIPS_REGION_SHRINK_MAX, + VIPS_REGION_SHRINK_MIN, VIPS_REGION_SHRINK_LAST } VipsRegionShrink; diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index c22f1ff7..2f571331 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -903,6 +903,8 @@ vips_region_shrink_get_type( void ) {VIPS_REGION_SHRINK_MEAN, "VIPS_REGION_SHRINK_MEAN", "mean"}, {VIPS_REGION_SHRINK_MEDIAN, "VIPS_REGION_SHRINK_MEDIAN", "median"}, {VIPS_REGION_SHRINK_MODE, "VIPS_REGION_SHRINK_MODE", "mode"}, + {VIPS_REGION_SHRINK_MAX, "VIPS_REGION_SHRINK_MAX", "max"}, + {VIPS_REGION_SHRINK_MIN, "VIPS_REGION_SHRINK_MIN", "min"}, {VIPS_REGION_SHRINK_LAST, "VIPS_REGION_SHRINK_LAST", "last"}, {0, NULL, NULL} }; diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index da8f720e..b93a45f9 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -611,7 +611,7 @@ vips_operation_get_flags( VipsOperation *operation ) void vips_operation_class_print_usage( VipsOperationClass *operation_class ) { - char str[2048]; + char str[4096]; VipsBuf buf = VIPS_BUF_STATIC( str ); operation_class->usage( operation_class, &buf ); diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index fb9f7386..ab201ff5 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -1254,50 +1254,6 @@ vips_region_shrink_uncoded_mean( VipsRegion *from, } \ } -/* Generate area @target in @to using pixels in @from. Non-complex. - */ -static void -vips_region_shrink_uncoded_median( VipsRegion *from, - VipsRegion *to, const VipsRect *target ) -{ - int ls = VIPS_REGION_LSKIP( from ); - int ps = VIPS_IMAGE_SIZEOF_PEL( from->im ); - int nb = from->im->Bands; - - int x, y, z; - - for( y = 0; y < target->height; y++ ) { - VipsPel *p = VIPS_REGION_ADDR( from, - target->left * 2, (target->top + y) * 2 ); - VipsPel *q = VIPS_REGION_ADDR( to, - target->left, target->top + y ); - - /* Process this line of pels. - */ - switch( from->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - SHRINK_TYPE_MEDIAN( unsigned char ); break; - case VIPS_FORMAT_CHAR: - SHRINK_TYPE_MEDIAN( signed char ); break; - case VIPS_FORMAT_USHORT: - SHRINK_TYPE_MEDIAN( unsigned short ); break; - case VIPS_FORMAT_SHORT: - SHRINK_TYPE_MEDIAN( signed short ); break; - case VIPS_FORMAT_UINT: - SHRINK_TYPE_MEDIAN( unsigned int ); break; - case VIPS_FORMAT_INT: - SHRINK_TYPE_MEDIAN( signed int ); break; - case VIPS_FORMAT_FLOAT: - SHRINK_TYPE_MEDIAN( float ); break; - case VIPS_FORMAT_DOUBLE: - SHRINK_TYPE_MEDIAN( double ); break; - - default: - g_assert_not_reached(); - } - } -} - /* This method is implemented so as to perform well and to always select an * output pixel from one of the input pixels. As such we make only the * following guarantees: @@ -1334,50 +1290,94 @@ vips_region_shrink_uncoded_median( VipsRegion *from, } \ } -/* Generate area @target in @to using pixels in @from. Non-complex. - */ -static void -vips_region_shrink_uncoded_mode( VipsRegion *from, - VipsRegion *to, const VipsRect *target ) -{ - int ls = VIPS_REGION_LSKIP( from ); - int ps = VIPS_IMAGE_SIZEOF_PEL( from->im ); - int nb = from->im->Bands; - - int x, y, z; - - for( y = 0; y < target->height; y++ ) { - VipsPel *p = VIPS_REGION_ADDR( from, - target->left * 2, (target->top + y) * 2 ); - VipsPel *q = VIPS_REGION_ADDR( to, - target->left, target->top + y ); - - /* Process this line of pels. - */ - switch( from->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - SHRINK_TYPE_MODE( unsigned char ); break; - case VIPS_FORMAT_CHAR: - SHRINK_TYPE_MODE( signed char ); break; - case VIPS_FORMAT_USHORT: - SHRINK_TYPE_MODE( unsigned short ); break; - case VIPS_FORMAT_SHORT: - SHRINK_TYPE_MODE( signed short ); break; - case VIPS_FORMAT_UINT: - SHRINK_TYPE_MODE( unsigned int ); break; - case VIPS_FORMAT_INT: - SHRINK_TYPE_MODE( signed int ); break; - case VIPS_FORMAT_FLOAT: - SHRINK_TYPE_MODE( float ); break; - case VIPS_FORMAT_DOUBLE: - SHRINK_TYPE_MODE( double ); break; - - default: - g_assert_not_reached(); - } - } +#define SHRINK_TYPE_MAX( TYPE ) { \ + for( x = 0; x < target->width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + tq[z] = VIPS_MAX( \ + VIPS_MAX( tp[z], tp[z + nb] ), \ + VIPS_MAX( tp1[z], tp1[z + nb] ) \ + ); \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } \ } +#define SHRINK_TYPE_MIN( TYPE ) { \ + for( x = 0; x < target->width; x++ ) { \ + TYPE *tp = (TYPE *) p; \ + TYPE *tp1 = (TYPE *) (p + ls); \ + TYPE *tq = (TYPE *) q; \ + \ + for( z = 0; z < nb; z++ ) { \ + tq[z] = VIPS_MIN( \ + VIPS_MIN( tp[z], tp[z + nb] ), \ + VIPS_MIN( tp1[z], tp1[z + nb] ) \ + ); \ + } \ + \ + /* Move on two pels in input. \ + */ \ + p += ps << 1; \ + q += ps; \ + } \ +} + +#define VIPS_REGION_SHRINK( OP ) \ +static void \ +vips_region_shrink_uncoded_ ## OP( VipsRegion *from, \ + VipsRegion *to, const VipsRect *target ) \ +{ \ + int ls = VIPS_REGION_LSKIP( from ); \ + int ps = VIPS_IMAGE_SIZEOF_PEL( from->im ); \ + int nb = from->im->Bands; \ + \ + int x, y, z; \ + \ + for( y = 0; y < target->height; y++ ) { \ + VipsPel *p = VIPS_REGION_ADDR( from, \ + target->left * 2, (target->top + y) * 2 ); \ + VipsPel *q = VIPS_REGION_ADDR( to, \ + target->left, target->top + y ); \ + \ + /* Process this line of pels. \ + */ \ + switch( from->im->BandFmt ) { \ + case VIPS_FORMAT_UCHAR: \ + SHRINK_TYPE_ ## OP( unsigned char ); break; \ + case VIPS_FORMAT_CHAR: \ + SHRINK_TYPE_ ## OP( signed char ); break; \ + case VIPS_FORMAT_USHORT: \ + SHRINK_TYPE_ ## OP( unsigned short ); break; \ + case VIPS_FORMAT_SHORT: \ + SHRINK_TYPE_ ## OP( signed short ); break; \ + case VIPS_FORMAT_UINT: \ + SHRINK_TYPE_ ## OP( unsigned int ); break; \ + case VIPS_FORMAT_INT: \ + SHRINK_TYPE_ ## OP( signed int ); break; \ + case VIPS_FORMAT_FLOAT: \ + SHRINK_TYPE_ ## OP( float ); break; \ + case VIPS_FORMAT_DOUBLE: \ + SHRINK_TYPE_ ## OP( double ); break; \ + \ + default: \ + g_assert_not_reached(); \ + } \ + } \ +} + +VIPS_REGION_SHRINK( MAX ); +VIPS_REGION_SHRINK( MIN ); +VIPS_REGION_SHRINK( MODE ); +VIPS_REGION_SHRINK( MEDIAN ); + /* Generate area @target in @to using pixels in @from. Non-complex. */ static void @@ -1388,11 +1388,21 @@ vips_region_shrink_uncoded( VipsRegion *from, case VIPS_REGION_SHRINK_MEAN: vips_region_shrink_uncoded_mean( from, to, target ); break; + case VIPS_REGION_SHRINK_MEDIAN: - vips_region_shrink_uncoded_median( from, to, target ); + vips_region_shrink_uncoded_MEDIAN( from, to, target ); break; + case VIPS_REGION_SHRINK_MODE: - vips_region_shrink_uncoded_mode( from, to, target ); + vips_region_shrink_uncoded_MODE( from, to, target ); + break; + + case VIPS_REGION_SHRINK_MAX: + vips_region_shrink_uncoded_MAX( from, to, target ); + break; + + case VIPS_REGION_SHRINK_MIN: + vips_region_shrink_uncoded_MIN( from, to, target ); break; default: