blob: f0e43a9e936d764c0485bfa3b2eec71de2b5cf6d [file] [log] [blame]
/* 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 );
}