| /* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| */ |
| |
| /* |
| |
| These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
| |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif /*HAVE_CONFIG_H*/ |
| #include <vips/intl.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <vips/vips.h> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC*/ |
| |
| /* 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 */ |
| PEL **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; |
| PEL *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, PEL * )) ) |
| return( NULL ); |
| for( i = 0; i < lut->Bands; i++ ) |
| if( !(st->table[i] = IM_ARRAY( out, st->sz * st->es, PEL )) ) |
| return( NULL ); |
| |
| /* Scan LUT and fill table. |
| */ |
| q = (PEL *) 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++ ) { \ |
| PEL *p = (PEL *) 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++ ) { \ |
| PEL *p = (PEL *) 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 ); \ |
| PEL *p = (PEL *) 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 ); \ |
| PEL *p = (PEL *) 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 ); \ |
| PEL *p = (PEL *) 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 ); \ |
| PEL *p = (PEL *) 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. |
| */ |
| 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 ); |
| } |