diff --git a/ChangeLog b/ChangeLog index 6407f838..cca15af2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,10 +2,10 @@ - added vips_matrixload() and vips_matrixsave(), load and save vips mat format - rename image arrays as image matrices ... INTERPRETATION_ARRAY -> INTERPRETATION_MATRIX etc. -- rewrite im_buildlut(), im_identity*() to classes +- rewrite im_buildlut(), im_identity*(), im_maplut() as classes - added vips_error_freeze()/vips_error_thaw() - used freeze()/thaw() to stop file format sniffers logging spurious errors -- vipsthumbnail uses embedded jpg thumbnails +- vipsthumbnail uses embedded jpg thumbnails if it can 3/7/13 started 7.34.2 - lower priority for Matlab load to reduce segvs from Mat_Open(), thanks diff --git a/TODO b/TODO index c1d994fc..f330def8 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,11 @@ +- rename vips_diag() as vips_info(), make it work like a log thing ... + normally no output, can be turned on with a --vips-verbose flag + + handy with vipsthumbnail + +- what's the difference between buildlut and invertlut? can we make invertlut + by just swapping the channels before calling buildlut? + - do we always call copy_fields and demand_hint with ALL input images? what about the operators in conversion? @@ -22,10 +30,6 @@ we've moved all the no-input ones of to create now, I think -- move im_buildlut() to create package - - others too? identity? - - object construction is threadsafe, but class construction is not diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index d528ba80..2e08d7ef 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -3307,7 +3307,24 @@ im_cross_phase( IMAGE *in1, IMAGE *in2, IMAGE *out ) return( 0 ); } -/* This is used by vipsthumbnail and the carrierwave shrinker to cache the +int +im_maplut( IMAGE *in, IMAGE *out, IMAGE *lut ) +{ + VipsImage *x; + + if( vips_maplut( in, &x, lut, NULL ) ) + return( -1 ); + + if( im_copy( x, out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); + + return( 0 ); +} + +/* This is used by the carrierwave shrinker to cache the * output of shrink before doing the final affine. * * We use the vips8 threaded tilecache to avoid a deadlock: suppose thread1, @@ -3317,10 +3334,10 @@ im_cross_phase( IMAGE *in1, IMAGE *in2, IMAGE *out ) * * With an unthreaded tilecache (as we had before), thread2 will get * the cache lock and start evaling the second block of the shrink. When it - * reaches the png reader it will stall untilthe first block has been used ... + * reaches the png reader it will stall until the first block has been used ... * but it never will, since thread1 will block on this cache lock. * - * This function is only used in those two places (I think), so it's OK to + * This function is only used in this place (I think), so it's OK to * hard-wire this to be a sequential threaded cache. */ int diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index 8d479334..3a3d7b11 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libhistogram.la libhistogram_la_SOURCES = \ + maplut.c \ histogram.c \ phistogram.h \ hist_dispatch.c \ @@ -17,7 +18,6 @@ libhistogram_la_SOURCES = \ im_mpercent.c \ im_invertlut.c \ im_lhisteq.c \ - im_maplut.c \ im_project.c \ im_stdif.c \ tone.c diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index f21eef27..e137848a 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -136,7 +136,7 @@ vips_histogram_init( VipsHistogram *histogram ) void vips_histogram_operation_init( void ) { - extern GType vips_xyz_get_type( void ); + extern GType vips_maplut_get_type( void ); - vips_xyz_get_type(); + vips_maplut_get_type(); } diff --git a/libvips/histogram/im_maplut.c b/libvips/histogram/im_maplut.c deleted file mode 100644 index ac2e96ba..00000000 --- a/libvips/histogram/im_maplut.c +++ /dev/null @@ -1,627 +0,0 @@ -/* map though a LUT - * - * Modified: - * 18/6/93 JC - * - oops! im_incheck() added for LUT image - * - some ANSIfication - * 15/7/93 JC - * - adapted for partial v2 - * - ANSIfied - * - now does complex LUTs too - * 10/3/94 JC - * - more helpful error messages, slight reformatting - * 24/8/94 JC - * - now allows non-uchar image input - * 7/10/94 JC - * - uses im_malloc(), IM_NEW() etc. - * 13/3/95 JC - * - now takes a private copy of LUT, so user can im_close() LUT image - * after im_maplut() without fear of coredumps - * 23/6/95 JC - * - lut may now have many bands if image has just one band - * 3/3/01 JC - * - small speed ups - * 30/6/04 - * - heh, 1 band image + 3 band lut + >8bit output has been broken for 9 - * years :-) - * 7/11/07 - * - new eval start/end system - * 25/3/10 - * - gtkdoc - * - small cleanups - */ - -/* - - 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 - -/* Struct we carry for LUT operations. - */ -typedef struct { - int fmt; /* LUT image BandFmt */ - int nb; /* Number of bands in lut */ - int es; /* IM_IMAGE_SIZEOF_ELEMENT() for lut image */ - int sz; /* Number of elements in minor dimension */ - int clp; /* Value we clip against */ - VipsPel **table; /* Lut converted to 2d array */ - int overflow; /* Number of overflows for non-uchar lut */ -} LutInfo; - -static int -lut_start( LutInfo *st ) -{ - st->overflow = 0; - - return( 0 ); -} - -/* Print overflows, if any. - */ -static int -lut_end( LutInfo *st ) -{ - if( st->overflow ) - im_warn( "im_maplut", _( "%d overflows detected" ), - st->overflow ); - - return( 0 ); -} - -/* Build a lut table. - */ -static LutInfo * -build_luts( IMAGE *out, IMAGE *lut ) -{ - LutInfo *st; - int i, x; - VipsPel *q; - - if( !(st = IM_NEW( out, LutInfo )) ) - return( NULL ); - - /* Make luts. We unpack the LUT image into a C 2D array to speed - * processing. - */ - st->fmt = lut->BandFmt; - st->es = IM_IMAGE_SIZEOF_ELEMENT( lut ); - st->nb = lut->Bands; - st->sz = lut->Xsize * lut->Ysize; - st->clp = st->sz - 1; - st->overflow = 0; - st->table = NULL; - if( im_add_evalstart_callback( out, - (im_callback_fn) lut_start, st, NULL ) || - im_add_evalend_callback( out, - (im_callback_fn) lut_end, st, NULL ) ) - return( NULL ); - - /* Attach tables. - */ - if( !(st->table = IM_ARRAY( out, lut->Bands, VipsPel * )) ) - return( NULL ); - for( i = 0; i < lut->Bands; i++ ) - if( !(st->table[i] = IM_ARRAY( out, st->sz * st->es, VipsPel )) ) - return( NULL ); - - /* Scan LUT and fill table. - */ - q = (VipsPel *) lut->data; - for( x = 0; x < st->sz; x++ ) - for( i = 0; i < st->nb; i++ ) { - memcpy( st->table[i] + x * st->es, q, st->es ); - q += st->es; - } - - return( st ); -} - -/* Our sequence value: the region this sequence is using, and local stats. - */ -typedef struct { - REGION *ir; /* Input region */ - int overflow; /* Number of overflows */ -} Seq; - -/* Destroy a sequence value. - */ -static int -maplut_stop( void *vseq, void *a, void *b ) -{ - Seq *seq = (Seq *) vseq; - LutInfo *st = (LutInfo *) b; - - /* Add to global stats. - */ - st->overflow += seq->overflow; - - IM_FREEF( im_region_free, seq->ir ); - - return( 0 ); -} - -/* Our start function. - */ -static void * -maplut_start( IMAGE *out, void *a, void *b ) -{ - IMAGE *in = (IMAGE *) a; - Seq *seq; - - if( !(seq = IM_NEW( out, Seq )) ) - return( NULL ); - - /* Init! - */ - seq->ir = NULL; - seq->overflow = 0; - - if( !(seq->ir = im_region_create( in )) ) - return( NULL ); - - return( seq ); -} - -/* Map through n non-complex luts. - */ -#define loop(OUT) { \ - int b = st->nb; \ - \ - for( y = to; y < bo; y++ ) { \ - for( z = 0; z < b; z++ ) { \ - VipsPel *p = IM_REGION_ADDR( ir, le, y ); \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - OUT *tlut = (OUT *) st->table[z]; \ - \ - for( x = z; x < ne; x += b ) \ - q[x] = tlut[p[x]]; \ - } \ - } \ -} - -/* Map through n complex luts. - */ -#define loopc(OUT) { \ - int b = in->Bands; \ - \ - for( y = to; y < bo; y++ ) { \ - for( z = 0; z < b; z++ ) { \ - VipsPel *p = IM_REGION_ADDR( ir, le, y ) + z; \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z * 2; \ - OUT *tlut = (OUT *) st->table[z]; \ - \ - for( x = 0; x < ne; x += b ) { \ - int n = p[x] * 2; \ - \ - q[0] = tlut[n]; \ - q[1] = tlut[n + 1]; \ - q += b * 2; \ - } \ - } \ - } \ -} - -#define loopg(IN,OUT) { \ - int b = st->nb; \ - \ - for( y = to; y < bo; y++ ) { \ - for( z = 0; z < b; z++ ) { \ - IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - OUT *tlut = (OUT *) st->table[z]; \ - \ - for( x = z; x < ne; x += b ) { \ - int index = p[x]; \ - \ - if( index > st->clp ) { \ - index = st->clp; \ - seq->overflow++; \ - } \ - \ - q[x] = tlut[index]; \ - } \ - } \ - } \ -} - -#define loopcg(IN,OUT) { \ - int b = in->Bands; \ - \ - for( y = to; y < bo; y++ ) { \ - for( z = 0; z < b; z++ ) { \ - IN *p = (IN *) IM_REGION_ADDR( ir, le, y ) + z; \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z * 2; \ - OUT *tlut = (OUT *) st->table[z]; \ - \ - for( x = 0; x < ne; x += b ) { \ - int index = p[x]; \ - \ - if( index > st->clp ) { \ - index = st->clp; \ - seq->overflow++; \ - } \ - \ - q[0] = tlut[index * 2]; \ - q[1] = tlut[index * 2 + 1]; \ - \ - q += b * 2; \ - } \ - } \ - } \ -} - -/* Map image through one non-complex lut. - */ -#define loop1(OUT) { \ - OUT *tlut = (OUT *) st->table[0]; \ - \ - for( y = to; y < bo; y++ ) { \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - VipsPel *p = IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < ne; x++ ) \ - q[x] = tlut[p[x]]; \ - } \ -} - -/* Map image through one complex lut. - */ -#define loop1c(OUT) { \ - OUT *tlut = (OUT *) st->table[0]; \ - \ - for( y = to; y < bo; y++ ) { \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - VipsPel *p = IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < ne; x++ ) { \ - int n = p[x] * 2; \ - \ - q[0] = tlut[n]; \ - q[1] = tlut[n + 1]; \ - q += 2; \ - } \ - } \ -} - -/* As above, but the input image may be any unsigned integer type. We have to - * index the lut carefully, and record the number of overflows we detect. - */ -#define loop1g(IN,OUT) { \ - OUT *tlut = (OUT *) st->table[0]; \ - \ - for( y = to; y < bo; y++ ) { \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < ne; x++ ) { \ - int index = p[x]; \ - \ - if( index > st->clp ) { \ - index = st->clp; \ - seq->overflow++; \ - } \ - \ - q[x] = tlut[index]; \ - } \ - } \ -} - -#define loop1cg(IN,OUT) { \ - OUT *tlut = (OUT *) st->table[0]; \ - \ - for( y = to; y < bo; y++ ) { \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < ne; x++ ) { \ - int index = p[x]; \ - \ - if( index > st->clp ) { \ - index = st->clp; \ - seq->overflow++; \ - } \ - \ - q[0] = tlut[index * 2]; \ - q[1] = tlut[index * 2 + 1]; \ - q += 2; \ - } \ - } \ -} - -/* Map 1-band image through a many-band non-complex lut. - */ -#define loop1m(OUT) { \ - OUT **tlut = (OUT **) st->table; \ - \ - for( y = to; y < bo; y++ ) { \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - VipsPel *p = IM_REGION_ADDR( ir, le, y ); \ - \ - for( i = 0, x = 0; x < np; x++ ) { \ - int n = p[x]; \ - \ - for( z = 0; z < st->nb; z++, i++ ) \ - q[i] = tlut[z][n]; \ - } \ - } \ -} - -/* Map 1-band image through many-band complex lut. - */ -#define loop1cm(OUT) { \ - OUT **tlut = (OUT **) st->table; \ - \ - for( y = to; y < bo; y++ ) { \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - VipsPel *p = IM_REGION_ADDR( ir, le, y ); \ - \ - for( x = 0; x < np; x++ ) { \ - int n = p[x] * 2; \ - \ - for( z = 0; z < st->nb; z++ ) { \ - q[0] = tlut[z][n]; \ - q[1] = tlut[z][n+1]; \ - q += 2; \ - } \ - } \ - } \ -} - -/* Map 1-band uint or ushort image through a many-band non-complex LUT. - */ -#define loop1gm(IN,OUT) { \ - OUT **tlut = (OUT **) st->table; \ - \ - for( y = to; y < bo; y++ ) { \ - IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - \ - for( i = 0, x = 0; x < np; x++ ) { \ - int n = p[x]; \ - \ - if( n > st->clp ) { \ - n = st->clp; \ - seq->overflow++; \ - } \ - \ - for( z = 0; z < st->nb; z++, i++ ) \ - q[i] = tlut[z][n]; \ - } \ - } \ -} - -/* Map 1-band uint or ushort image through a many-band complex LUT. - */ -#define loop1cgm(IN,OUT) { \ - OUT **tlut = (OUT **) st->table; \ - \ - for( y = to; y < bo; y++ ) { \ - IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \ - OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \ - \ - for( x = 0; x < np; x++ ) { \ - int n = p[x]; \ - \ - if( n > st->clp ) { \ - n = st->clp; \ - seq->overflow++; \ - } \ - \ - for( z = 0; z < st->nb; z++ ) { \ - q[0] = tlut[z][n * 2]; \ - q[1] = tlut[z][n * 2 + 1]; \ - q += 2; \ - } \ - } \ - } \ -} - -/* Switch for input types. Has to be uint type! - */ -#define inner_switch( UCHAR, GEN, OUT ) \ - switch( ir->im->BandFmt ) { \ - case IM_BANDFMT_UCHAR: UCHAR( OUT ); break; \ - case IM_BANDFMT_USHORT: GEN( unsigned short, OUT ); break; \ - case IM_BANDFMT_UINT: GEN( unsigned int, OUT ); break; \ - default: \ - g_assert( 0 ); \ - } - -/* Switch for LUT types. One function for non-complex images, a - * variant for complex ones. Another pair as well, in case the input is not - * uchar. - */ -#define outer_switch( UCHAR_F, UCHAR_FC, GEN_F, GEN_FC ) \ - switch( st->fmt ) { \ - case IM_BANDFMT_UCHAR: inner_switch( UCHAR_F, GEN_F, \ - unsigned char ); break; \ - case IM_BANDFMT_CHAR: inner_switch( UCHAR_F, GEN_F, \ - char ); break; \ - case IM_BANDFMT_USHORT: inner_switch( UCHAR_F, GEN_F, \ - unsigned short ); break; \ - case IM_BANDFMT_SHORT: inner_switch( UCHAR_F, GEN_F, \ - short ); break; \ - case IM_BANDFMT_UINT: inner_switch( UCHAR_F, GEN_F, \ - unsigned int ); break; \ - case IM_BANDFMT_INT: inner_switch( UCHAR_F, GEN_F, \ - int ); break; \ - case IM_BANDFMT_FLOAT: inner_switch( UCHAR_F, GEN_F, \ - float ); break; \ - case IM_BANDFMT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \ - double ); break; \ - case IM_BANDFMT_COMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ - float ); break; \ - case IM_BANDFMT_DPCOMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ - double ); break; \ - default: \ - g_assert( 0 ); \ - } - -/* Do a map. - */ -static int -maplut_gen( REGION *or, void *vseq, void *a, void *b ) -{ - Seq *seq = (Seq *) vseq; - IMAGE *in = (IMAGE *) a; - LutInfo *st = (LutInfo *) b; - REGION *ir = seq->ir; - Rect *r = &or->valid; - int le = r->left; - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - int np = r->width; /* Pels across region */ - int ne = IM_REGION_N_ELEMENTS( or ); /* Number of elements */ - int x, y, z, i; - - /* Get input ready. - */ - if( im_prepare( ir, r ) ) - return( -1 ); - - /* Process! - */ - if( st->nb == 1 ) - /* One band lut. - */ - outer_switch( loop1, loop1c, loop1g, loop1cg ) - else - /* Many band lut. - */ - if( ir->im->Bands == 1 ) - /* ... but 1 band input. - */ - outer_switch( loop1m, loop1cm, loop1gm, loop1cgm ) - else - outer_switch( loop, loopc, loopg, loopcg ) - - return( 0 ); -} - -/* Save a bit of typing. - */ -#define UC IM_BANDFMT_UCHAR -#define US IM_BANDFMT_USHORT -#define UI IM_BANDFMT_UINT - -/* Type mapping: go to uchar or ushort. - */ -static int bandfmt_maplut[10] = { -/* UC C US S UI I F X D DX */ - UC, UC, US, US, UI, UI, UI, UI, UI, UI -}; - -/** - * im_maplut: - * @in: input image - * @out: output image - * @lut: look-up table - * - * Map an image through another image acting as a LUT (Look Up Table). - * The lut may have any type, and the output image will be that type. - * - * The input image will be cast to one of the unsigned integer types, that is, - * IM_BANDFMT_UCHAR, IM_BANDFMT_USHORT or IM_BANDFMT_UINT. - * - * If @lut is too small for the input type (for example, if @in is - * IM_BANDFMT_UCHAR but @lut only has 100 elements), the lut is padded out - * by copying the last element. Overflows are reported at the end of - * computation. - * If @lut is too large, extra values are ignored. - * - * If @lut has one band, then all bands of @in pass through it. If @lut - * has same number of bands as @in, then each band is mapped - * separately. If @in has one band, then @lut may have many bands and - * the output will have the same number of bands as @lut. - * - * See also: im_histgr(), im_identity(). - * - * Returns: 0 on success, -1 on error - */ -int -im_maplut( IMAGE *in, IMAGE *out, IMAGE *lut ) -{ - IMAGE *t; - LutInfo *st; - - /* Check input output. Old-style IO from lut, for simplicity. - */ - if( im_check_hist( "im_maplut", lut ) || - im_check_uncoded( "im_maplut", lut ) || - im_check_uncoded( "im_maplut", in ) || - im_check_bands_1orn( "im_maplut", in, lut ) || - im_piocheck( in, out ) || - im_incheck( lut ) ) - return( -1 ); - - /* Cast in to u8/u16/u32. - */ - if( !(t = im_open_local( out, "im_maplut", "p" )) || - im_clip2fmt( in, t, bandfmt_maplut[in->BandFmt] ) ) - return( -1 ); - - /* Prepare the output header. - */ - if( im_cp_descv( out, t, lut, NULL ) ) - return( -1 ); - - /* Force output to be the same type as lut. - */ - 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 ) - out->Bands = lut->Bands; - - /* Make tables. - */ - if( !(st = build_luts( out, lut )) ) - return( -1 ); - - /* Set demand hints. - */ - if( im_demand_hint( out, IM_THINSTRIP, t, NULL ) ) - return( -1 ); - - /* Process! - */ - if( im_generate( out, maplut_start, maplut_gen, maplut_stop, t, st ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/histogram/maplut.c b/libvips/histogram/maplut.c index 1e76c13f..63f1b848 100644 --- a/libvips/histogram/maplut.c +++ b/libvips/histogram/maplut.c @@ -74,11 +74,11 @@ #include "phistogram.h" typedef struct _VipsMaplut { - VipsHistogram parent_instance; + VipsOperation parent_instance; - /* Process this image (@in is the LUT). - */ - VipsImage *process; + VipsImage *in; + VipsImage *out; + VipsImage *lut; int fmt; /* LUT image BandFmt */ int nb; /* Number of bands in lut */ @@ -90,9 +90,9 @@ typedef struct _VipsMaplut { } VipsMaplut; -typedef VipsHistogramClass VipsMaplutClass; +typedef VipsOperationClass VipsMaplutClass; -G_DEFINE_TYPE( VipsMaplut, vips_maplut, VIPS_TYPE_HISTOGRAM ); +G_DEFINE_TYPE( VipsMaplut, vips_maplut, VIPS_TYPE_OPERATION ); static void vips_maplut_preeval( VipsImage *image, VipsProgress *progress, @@ -143,7 +143,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map through n non-complex luts. */ -#define loop(OUT) { \ +#define loop( OUT ) { \ int b = st->nb; \ \ for( y = to; y < bo; y++ ) { \ @@ -160,7 +160,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map through n complex luts. */ -#define loopc(OUT) { \ +#define loopc( OUT ) { \ int b = in->Bands; \ \ for( y = to; y < bo; y++ ) { \ @@ -180,7 +180,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) } \ } -#define loopg(IN,OUT) { \ +#define loopg( IN, OUT ) { \ int b = st->nb; \ \ for( y = to; y < bo; y++ ) { \ @@ -203,7 +203,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) } \ } -#define loopcg(IN,OUT) { \ +#define loopcg( IN, OUT ) { \ int b = in->Bands; \ \ for( y = to; y < bo; y++ ) { \ @@ -231,7 +231,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map image through one non-complex lut. */ -#define loop1(OUT) { \ +#define loop1( OUT ) { \ OUT *tlut = (OUT *) st->table[0]; \ \ for( y = to; y < bo; y++ ) { \ @@ -245,7 +245,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map image through one complex lut. */ -#define loop1c(OUT) { \ +#define loop1c( OUT ) { \ OUT *tlut = (OUT *) st->table[0]; \ \ for( y = to; y < bo; y++ ) { \ @@ -265,7 +265,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* As above, but the input image may be any unsigned integer type. We have to * index the lut carefully, and record the number of overflows we detect. */ -#define loop1g(IN,OUT) { \ +#define loop1g( IN, OUT ) { \ OUT *tlut = (OUT *) st->table[0]; \ \ for( y = to; y < bo; y++ ) { \ @@ -285,7 +285,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) } \ } -#define loop1cg(IN,OUT) { \ +#define loop1cg( IN, OUT ) { \ OUT *tlut = (OUT *) st->table[0]; \ \ for( y = to; y < bo; y++ ) { \ @@ -309,7 +309,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map 1-band image through a many-band non-complex lut. */ -#define loop1m(OUT) { \ +#define loop1m( OUT ) { \ OUT **tlut = (OUT **) st->table; \ \ for( y = to; y < bo; y++ ) { \ @@ -327,7 +327,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map 1-band image through many-band complex lut. */ -#define loop1cm(OUT) { \ +#define loop1cm( OUT ) { \ OUT **tlut = (OUT **) st->table; \ \ for( y = to; y < bo; y++ ) { \ @@ -348,7 +348,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map 1-band uint or ushort image through a many-band non-complex LUT. */ -#define loop1gm(IN,OUT) { \ +#define loop1gm( IN, OUT ) { \ OUT **tlut = (OUT **) st->table; \ \ for( y = to; y < bo; y++ ) { \ @@ -371,7 +371,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) /* Map 1-band uint or ushort image through a many-band complex LUT. */ -#define loop1cgm(IN,OUT) { \ +#define loop1cgm( IN, OUT ) { \ OUT **tlut = (OUT **) st->table; \ \ for( y = to; y < bo; y++ ) { \ @@ -400,7 +400,7 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) #define inner_switch( UCHAR, GEN, OUT ) \ switch( ir->im->BandFmt ) { \ case VIPS_FORMAT_UCHAR: UCHAR( OUT ); break; \ - case VIPS_FORMAT_USHORT: GEN( unsigned short, OUT ); break; \ + case VIPS_FORMAT_USHORT: GEN( unsigned short, OUT ); break; \ case VIPS_FORMAT_UINT: GEN( unsigned int, OUT ); break; \ default: \ g_assert( 0 ); \ @@ -413,25 +413,25 @@ vips_maplut_start( VipsImage *out, void *a, void *b ) #define outer_switch( UCHAR_F, UCHAR_FC, GEN_F, GEN_FC ) \ switch( st->fmt ) { \ case VIPS_FORMAT_UCHAR: inner_switch( UCHAR_F, GEN_F, \ - unsigned char ); break; \ + unsigned char ); break; \ case VIPS_FORMAT_CHAR: inner_switch( UCHAR_F, GEN_F, \ - char ); break; \ - case VIPS_FORMAT_USHORT: inner_switch( UCHAR_F, GEN_F, \ - unsigned short ); break; \ + char ); break; \ + case VIPS_FORMAT_USHORT: inner_switch( UCHAR_F, GEN_F, \ + unsigned short ); break; \ case VIPS_FORMAT_SHORT: inner_switch( UCHAR_F, GEN_F, \ - short ); break; \ + short ); break; \ case VIPS_FORMAT_UINT: inner_switch( UCHAR_F, GEN_F, \ - unsigned int ); break; \ + unsigned int ); break; \ case VIPS_FORMAT_INT: inner_switch( UCHAR_F, GEN_F, \ - int ); break; \ + int ); break; \ case VIPS_FORMAT_FLOAT: inner_switch( UCHAR_F, GEN_F, \ - float ); break; \ - case VIPS_FORMAT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \ - double ); break; \ + float ); break; \ + case VIPS_FORMAT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \ + double ); break; \ case VIPS_FORMAT_COMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ - float ); break; \ + float ); break; \ case VIPS_FORMAT_DPCOMPLEX: inner_switch( UCHAR_FC, GEN_FC, \ - double ); break; \ + double ); break; \ default: \ g_assert( 0 ); \ } @@ -446,17 +446,18 @@ vips_maplut_gen( VipsRegion *or, void *vseq, void *a, void *b, VipsImage *in = (VipsImage *) a; VipsMaplut *st = (VipsMaplut *) b; VipsRegion *ir = seq->ir; - Rect *r = &or->valid; + VipsRect *r = &or->valid; int le = r->left; int to = r->top; int bo = VIPS_RECT_BOTTOM(r); int np = r->width; /* Pels across region */ int ne = VIPS_REGION_N_ELEMENTS( or ); /* Number of elements */ + int x, y, z, i; /* Get input ready. */ - if( im_prepare( ir, r ) ) + if( vips_region_prepare( ir, r ) ) return( -1 ); /* Process! @@ -512,73 +513,76 @@ static int vips_maplut_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsHistogram *histogram = VIPS_HISTOGRAM( object ); VipsMaplut *maplut = (VipsMaplut *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); + VipsImage *in; + VipsImage *lut; int i, x; VipsPel *q; + g_object_set( maplut, "out", vips_image_new(), NULL ); + if( VIPS_OBJECT_CLASS( vips_maplut_parent_class )->build( object ) ) return( -1 ); - /* @in is the LUT. + in = maplut->in; + lut = maplut->lut; + + if( vips_check_hist( class->nickname, lut ) || + vips_check_uncoded( class->nickname, lut ) || + vips_image_wio_input( lut ) ) + return( -1 ); + + /* Cast @in to u8/u16/u32 to make the index image. */ - if( vips_check_uncoded( class->nickname, histogram->in ) || - vips_image_wio_input( histogram->in ) ) + if( vips_cast( in, &t[0], bandfmt_maplut[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 ); - /* Cast @process to u8/u16/u32. - */ - if( !(t = im_open_local( out, "im_maplut", "p" )) || - im_clip2fmt( in, t, bandfmt_maplut[in->BandFmt] ) ) + if( vips_image_copy_fieldsv( maplut->out, in, lut, NULL ) ) return( -1 ); - - if( vips_check_uncoded( class->nickname, maplut->process ) || - vips_check_bands_1orn( class->nickname, - maplut->process, histogram->in ) || - vips_image_pio_input( maplut->process ) ) - return( -1 ); - - if( vips_image_copy_fieldsv( histogram->out, - maplut->process, histogram->in, NULL ) ) - return( -1 ); - vips_demand_hint( histogram->out, VIPS_DEMAND_STYLE_THINSTRIP, - maplut->process, histogram->in, NULL ); - histogram->out->BandFmt = histogram->in->BandFmt; + vips_demand_hint( maplut->out, VIPS_DEMAND_STYLE_THINSTRIP, + in, lut, NULL ); + maplut->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( histogram->in->Bands != 1 ) - histogram->out->Bands = histogram->in->Bands; + if( lut->Bands != 1 ) + maplut->out->Bands = lut->Bands; - g_signal_connect( maplut->in, "preeval", + g_signal_connect( in, "preeval", G_CALLBACK( vips_maplut_preeval ), maplut ); - g_signal_connect( maplut->in, "posteval", + g_signal_connect( in, "posteval", G_CALLBACK( vips_maplut_posteval ), maplut ); - /* Make luts. We unpack the LUT image into a C 2D array to speed + /* Make luts. We unpack the LUT image into a 2D C array to speed * processing. */ - maplut->fmt = histogram->in->BandFmt; - maplut->es = VIPS_IMAGE_SIZEOF_ELEMENT( histogram->in ); - maplut->nb = histogram->in->Bands; - maplut->sz = histogram->in->Xsize * histogram->in->Ysize; + maplut->fmt = lut->BandFmt; + maplut->es = VIPS_IMAGE_SIZEOF_ELEMENT( lut ); + maplut->nb = lut->Bands; + maplut->sz = lut->Xsize * lut->Ysize; maplut->clp = maplut->sz - 1; /* Attach tables. */ - if( !(maplut->table = VIPS_ARRAY( maplut, - histogram->in->Bands, VipsPel * )) ) - return( NULL ); - for( i = 0; i < histogram->in->Bands; i++ ) + if( !(maplut->table = VIPS_ARRAY( maplut, lut->Bands, VipsPel * )) ) + return( -1 ); + for( i = 0; i < lut->Bands; i++ ) if( !(maplut->table[i] = VIPS_ARRAY( maplut, maplut->sz * maplut->es, VipsPel )) ) - return( NULL ); + return( -1 ); /* Scan LUT and fill table. */ - q = (VipsPel *) histogram->in->data; + q = (VipsPel *) lut->data; for( x = 0; x < maplut->sz; x++ ) for( i = 0; i < maplut->nb; i++ ) { memcpy( maplut->table[i] + x * maplut->es, q, @@ -586,23 +590,21 @@ vips_maplut_build( VipsObject *object ) q += maplut->es; } - if( vips_image_generate( histogram->out, + if( vips_image_generate( maplut->out, vips_maplut_start, vips_maplut_gen, vips_maplut_stop, - maplut->process, maplut ) ) + in, maplut ) ) return( -1 ); return( 0 ); } static void -vips_maplut_class_init( VipsCastClass *class ) +vips_maplut_class_init( VipsMaplutClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); - VIPS_DEBUG_MSG( "vips_maplut_class_init\n" ); - gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -612,27 +614,40 @@ vips_maplut_class_init( VipsCastClass *class ) operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - VIPS_ARG_IMAGE( class, "process", 2, - _( "Process" ), - _( "Image to pass through LUT" ), + VIPS_ARG_IMAGE( class, "in", 0, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsHistogram, in ) ); + + VIPS_ARG_IMAGE( class, "out", 1, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsHistogram, out ) ); + + VIPS_ARG_IMAGE( class, "lut", 2, + _( "LUT" ), + _( "Look-up table image" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsMaplut, process ) ); + G_STRUCT_OFFSET( VipsMaplut, lut ) ); } static void -vips_maplut_init( VipsCast *maplut ) +vips_maplut_init( VipsMaplut *maplut ) { } /** - * im_maplut: + * vips_maplut: * @in: input image * @out: output image * @lut: look-up table + * @...: %NULL-terminated list of optional named arguments * * Map an image through another image acting as a LUT (Look Up Table). - * The lut may have any type, and the output image will be that type. + * The lut may have any type and the output image will be that type. * * The input image will be cast to one of the unsigned integer types, that is, * VIPS_FORMAT_UCHAR, VIPS_FORMAT_USHORT or VIPS_FORMAT_UINT. @@ -648,61 +663,20 @@ vips_maplut_init( VipsCast *maplut ) * separately. If @in has one band, then @lut may have many bands and * the output will have the same number of bands as @lut. * - * See also: im_histgr(), im_identity(). + * See also: im_histgr(), vips_identity(). * * Returns: 0 on success, -1 on error */ -int -im_maplut( VipsImage *in, VipsImage *out, VipsImage *lut ) +int +vips_maplut( VipsImage *in, VipsImage **out, VipsImage *lut, ... ) { - VipsImage *t; - VipsMaplut *st; + va_list ap; + int result; - /* Check input output. Old-style IO from lut, for simplicity. - */ - if( im_check_hist( "im_maplut", lut ) || - im_check_uncoded( "im_maplut", lut ) || - im_check_uncoded( "im_maplut", in ) || - im_check_bands_1orn( "im_maplut", in, lut ) || - im_piocheck( in, out ) || - im_incheck( lut ) ) - return( -1 ); + va_start( ap, lut ); + result = vips_call_split( "maplut", ap, in, out, lut ); + va_end( ap ); - /* Cast in to u8/u16/u32. - */ - if( !(t = im_open_local( out, "im_maplut", "p" )) || - im_clip2fmt( in, t, bandfmt_maplut[in->BandFmt] ) ) - return( -1 ); - - /* Prepare the output header. - */ - if( im_cp_descv( out, t, lut, NULL ) ) - return( -1 ); - - /* Force output to be the same type as lut. - */ - 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 ) - out->Bands = lut->Bands; - - /* Make tables. - */ - if( !(st = build_luts( out, lut )) ) - return( -1 ); - - /* Set demand hints. - */ - if( im_demand_hint( out, VIPS_THINSTRIP, t, NULL ) ) - return( -1 ); - - /* Process! - */ - if( im_generate( out, maplut_start, maplut_gen, maplut_stop, t, st ) ) - return( -1 ); - - return( 0 ); + return( result ); } + diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index b3113163..e329fa3b 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -38,6 +38,9 @@ extern "C" { #endif /*__cplusplus*/ +int vips_maplut( VipsImage *in, VipsImage **out, VipsImage *lut, ... ) + __attribute__((sentinel)); + int im_histgr( VipsImage *in, VipsImage *out, int bandno ); int im_histnD( VipsImage *in, VipsImage *out, int bins ); int im_hist_indexed( VipsImage *index, VipsImage *value, VipsImage *out ); @@ -52,8 +55,6 @@ int im_histspec( VipsImage *in, VipsImage *ref, VipsImage *out ); int im_ismonotonic( VipsImage *lut, int *out ); int im_histplot( VipsImage *in, VipsImage *out ); -int im_maplut( VipsImage *in, VipsImage *out, VipsImage *lut ); - int im_hist( VipsImage *in, VipsImage *out, int bandno ); int im_hsp( VipsImage *in, VipsImage *ref, VipsImage *out ); int im_gammacorrect( VipsImage *in, VipsImage *out, double exponent ); diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index ddf7798e..6638d35f 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -280,6 +280,7 @@ void vips_conversion_operation_init( void ); void vips_resample_operation_init( void ); void vips_foreign_operation_init( void ); void vips_colour_operation_init( void ); +void vips_histogram_operation_init( void ); guint64 vips__parse_size( const char *size_string ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 4b5a6d15..6d0855e3 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -846,6 +846,8 @@ int im_lab_morph( VipsImage *in, VipsImage *out, int im_quadratic( IMAGE *in, IMAGE *out, IMAGE *coeff ); +int im_maplut( VipsImage *in, VipsImage *out, VipsImage *lut ); + /* ruby-vips uses this */ #define vips_class_map_concrete_all vips_class_map_all diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index a1467444..3f157279 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -249,6 +249,7 @@ vips_init( const char *argv0 ) vips_foreign_operation_init(); vips_resample_operation_init(); vips_colour_operation_init(); + vips_histogram_operation_init(); /* Load up any plugins in the vips libdir. We don't error on failure, * it's too annoying to have VIPS refuse to start because of a broken