Skip to content

Commit

Permalink
Accept LUT with input range in [0,65535] set
Browse files Browse the repository at this point in the history
Before, if we wanted to use the LUT with 16 bit raster, we had necessary to
use SCALE before, so as to reduce the dynamics to 8 bits, since LUT were only
supported for [0,255]->[0,255] mapping. However, for example in the case of a
12 bit raster where the desired LUT [0,4095]->[0,255] would have segments with
a width of input values < 16, it is not possible to derive a proper 8 bit LUT.

For example, let's assume we want this LUT : 0:0,15:15,4080:240,4095:255
and that the input raster dynamics is effectively [0,4095]. Once we have
rescaled to 8 bits, we cannot "convert" this 12 bit LUT into a 8 bit LUT since
it would become this degenerated LUT: 0:0,0:15,255:240,255:255

Now a LUT like 1605:30,2569:128,3372:200,4095:255 is possible.

Note that the extended LUT is only supported when SCALE is not present. If SCALE
is present, the same behaviour as before will be applied, and input values above
255 will be clamped to 255.
  • Loading branch information
rouault committed Aug 26, 2015
1 parent 13d3fdf commit d675ea2
Showing 1 changed file with 217 additions and 29 deletions.
246 changes: 217 additions & 29 deletions mapdrawgdal.c
Expand Up @@ -858,7 +858,7 @@ int msDrawRasterLayerGDAL(mapObj *map, layerObj *layer, imageObj *image,
/* ParseDefaultLUT() */
/************************************************************************/

static int ParseDefaultLUT( const char *lut_def, GByte *lut )
static int ParseDefaultLUT( const char *lut_def, GByte *lut, int nMaxValIn )

{
const char *lut_read;
Expand All @@ -875,11 +875,11 @@ static int ParseDefaultLUT( const char *lut_def, GByte *lut )
while( isspace(*lut_read) )
lut_read++;

/* if we are at end, assum 255:255 */
/* if we are at end, assume nMaxValIn:255 */
if( *lut_read == '\0' ) {
all_done = TRUE;
if ( last_in != 255 ) {
this_in = 255;
if ( last_in != nMaxValIn ) {
this_in = nMaxValIn;
this_out = 255;
}
}
Expand All @@ -902,7 +902,7 @@ static int ParseDefaultLUT( const char *lut_def, GByte *lut )
lut_read++;
}

this_in = MAX(0,MIN(255,this_in));
this_in = MAX(0,MIN(nMaxValIn,this_in));
this_out = MAX(0,MIN(255,this_out));

/* apply linear values from last in:out to this in:out */
Expand Down Expand Up @@ -968,7 +968,7 @@ static int LutFromGimpLine( char *lut_line, GByte *lut )

CSLDestroy( tokens );

return ParseDefaultLUT( wrkLUTDef, lut );
return ParseDefaultLUT( wrkLUTDef, lut, 255 );
}

/************************************************************************/
Expand Down Expand Up @@ -1019,19 +1019,18 @@ static int ParseGimpLUT( const char *lut_def, GByte *lut, int iColorIndex )
}

/************************************************************************/
/* ApplyLUT() */
/* LoadLUT() */
/* */
/* Apply a LUT according to RFC 21. */
/* Load a LUT according to RFC 21. */
/************************************************************************/

static int ApplyLUT( int iColorIndex, layerObj *layer,
GByte *buffer, int buf_xsize, int buf_ysize )
static int LoadLUT( layerObj *layer, int iColorIndex, char** ppszLutDef )

{
const char *lut_def;
char key[20], lut_def_fromfile[2500];
GByte lut[256];
int err, i;

*ppszLutDef = NULL;

/* -------------------------------------------------------------------- */
/* Get lut specifier from processing directives. Do nothing if */
Expand Down Expand Up @@ -1076,25 +1075,159 @@ static int ApplyLUT( int iColorIndex, layerObj *layer,
lut_def = lut_def_fromfile;
}

*ppszLutDef = msStrdup(lut_def);

return 0;

}

/************************************************************************/
/* FreeLUTs() */
/************************************************************************/

static void FreeLUTs(char** apszLUTs)
{
int i;
for( i = 0; i < 4; i++ )
msFree(apszLUTs[i]);
msFree(apszLUTs);
}

/************************************************************************/
/* LoadLUTs() */
/* */
/* Return an array of 4 strings (some possibly NULL) with loaded LUTs */
/* or NULL in case of failure. */
/************************************************************************/

static char** LoadLUTs(layerObj *layer, int band_count)
{
int i;
char** apszLUTs;

assert(band_count <= 4);

apszLUTs = (char**) msSmallCalloc( 4, sizeof(char*) );
for( i = 0; i < band_count; i++ )
{
if( LoadLUT( layer, i+1, &apszLUTs[i] ) != 0 )
{
FreeLUTs(apszLUTs);
return NULL;
}
}

return apszLUTs;
}

/************************************************************************/
/* GetDataTypeAppropriateForLUTS() */
/* */
/* This does a quick examination of the LUT strings to determine */
/* if they have input values > 255, in which case the raster data */
/* must be queries on 16-bits. */
/************************************************************************/

static GDALDataType GetDataTypeAppropriateForLUTS(char** apszLUTs)
{
GDALDataType eDT = GDT_Byte;
int i;
for( i = 0; i < 4; i++ )
{
const char* pszLastTuple;
int nLastInValue;
if( apszLUTs[i] == NULL )
continue;
if( EQUALN(apszLUTs[i],"# GIMP",6) )
continue;
/* Find last in:out tuple in string */
pszLastTuple = strrchr( apszLUTs[i], ',' );
if( pszLastTuple == NULL )
pszLastTuple = apszLUTs[i];
else
pszLastTuple ++;
while( *pszLastTuple == ' ' )
pszLastTuple ++;
nLastInValue = atoi(pszLastTuple);
if( nLastInValue > 255 )
{
eDT = GDT_UInt16;
break;
}
}
return eDT;
}

/************************************************************************/
/* ApplyLUT() */
/************************************************************************/

static int ApplyLUT( int iColorIndex, const char* lut_def,
const void* pInBuffer, GDALDataType eDT,
GByte* pabyOutBuffer,
int nPixelCount )
{
int i, err;
GByte byteLut[256];
GByte* uint16Lut = NULL;

assert( eDT == GDT_Byte || eDT == GDT_UInt16 );

if( lut_def == NULL )
{
if( pInBuffer != pabyOutBuffer )
{
GDALCopyWords( (void*)pInBuffer, eDT, GDALGetDataTypeSize(eDT) / 8,
pabyOutBuffer, GDT_Byte, 1,
nPixelCount );
}
return 0;
}

/* -------------------------------------------------------------------- */
/* Parse the LUT description. */
/* -------------------------------------------------------------------- */
if( EQUALN(lut_def,"# GIMP",6) ) {
err = ParseGimpLUT( lut_def, lut, iColorIndex );
if( eDT != GDT_Byte ) {
msSetError(MS_MISCERR,
"Cannot apply a GIMP LUT on a 16-bit buffer",
"ApplyLUT()");
return -1;
}
err = ParseGimpLUT( lut_def, byteLut, iColorIndex );
} else {
err = ParseDefaultLUT( lut_def, lut );
if( eDT == GDT_Byte )
err = ParseDefaultLUT( lut_def, byteLut, 255 );
else
{
uint16Lut = (GByte*) malloc( 65536 );
if( uint16Lut == NULL )
{
msSetError(MS_MEMERR,
"Cannot allocate 16-bit LUT",
"ApplyLUT()" );
return -1;
}
err = ParseDefaultLUT( lut_def, uint16Lut, 65535 );
}
}

if( err != 0 )
return err;

/* -------------------------------------------------------------------- */
/* Apply LUT. */
/* -------------------------------------------------------------------- */
for( i = buf_xsize * buf_ysize - 1; i >= 0; i-- )
buffer[i] = lut[buffer[i]];

return 0;
if( eDT == GDT_Byte )
{
for( i = 0; i < nPixelCount; i++ )
pabyOutBuffer[i] = byteLut[((GByte*)pInBuffer)[i]];
}
else
{
for( i = 0; i < nPixelCount; i++ )
pabyOutBuffer[i] = uint16Lut[((GUInt16*)pInBuffer)[i]];
free(uint16Lut);
}

return err;
}

/************************************************************************/
Expand All @@ -1118,6 +1251,7 @@ LoadGDALImages( GDALDatasetH hDS, int band_numbers[4], int band_count,
int iColorIndex, result_code=0;
CPLErr eErr;
float *pafWholeRawData;
char** papszLUTs;

/* -------------------------------------------------------------------- */
/* If we have no alpha band, but we do have three input */
Expand Down Expand Up @@ -1152,10 +1286,38 @@ LoadGDALImages( GDALDatasetH hDS, int band_numbers[4], int band_count,
&& CSLFetchNameValue( layer->processing, "SCALE_2" ) == NULL
&& CSLFetchNameValue( layer->processing, "SCALE_3" ) == NULL
&& CSLFetchNameValue( layer->processing, "SCALE_4" ) == NULL ) {

GDALDataType eDT;
GUInt16* panBuffer = NULL;
void* pBuffer;

papszLUTs = LoadLUTs(layer, band_count);
if( papszLUTs == NULL ) {
return -1;
}

eDT = GetDataTypeAppropriateForLUTS(papszLUTs);
if( eDT == GDT_UInt16 ) {
panBuffer =
(GUInt16 *) malloc(sizeof(GUInt16) * dst_xsize * dst_ysize * band_count );

if( panBuffer == NULL ) {
msSetError(MS_MEMERR,
"Allocating work uint16 image of size %dx%dx%d failed.",
"msDrawRasterLayerGDAL()",
dst_xsize, dst_ysize, band_count );
FreeLUTs(papszLUTs);
return -1;
}
pBuffer = panBuffer;
}
else
pBuffer = pabyWholeBuffer;

eErr = GDALDatasetRasterIO( hDS, GF_Read,
src_xoff, src_yoff, src_xsize, src_ysize,
pabyWholeBuffer,
dst_xsize, dst_ysize, GDT_Byte,
pBuffer,
dst_xsize, dst_ysize, eDT,
band_count, band_numbers,
0,0,0);

Expand All @@ -1164,17 +1326,24 @@ LoadGDALImages( GDALDatasetH hDS, int band_numbers[4], int band_count,
"GDALDatasetRasterIO() failed: %s",
"drawGDAL()",
CPLGetLastErrorMsg() );
FreeLUTs(papszLUTs);
msFree(panBuffer);
return -1;
}

for( iColorIndex = 0;
iColorIndex < band_count && result_code == 0; iColorIndex++ ) {
result_code = ApplyLUT( iColorIndex+1, layer,
pabyWholeBuffer
+ dst_xsize*dst_ysize*iColorIndex,
dst_xsize, dst_ysize );
result_code = ApplyLUT( iColorIndex+1,
papszLUTs[iColorIndex],
(GByte*)pBuffer
+ dst_xsize*dst_ysize*iColorIndex*(GDALGetDataTypeSize(eDT)/8),
eDT,
pabyWholeBuffer + dst_xsize*dst_ysize*iColorIndex,
dst_xsize * dst_ysize );
}

FreeLUTs(papszLUTs);
msFree(panBuffer);
return result_code;
}

Expand Down Expand Up @@ -1223,6 +1392,18 @@ LoadGDALImages( GDALDatasetH hDS, int band_numbers[4], int band_count,
return -1;
}

papszLUTs = LoadLUTs(layer, band_count);
if( papszLUTs == NULL ) {
free( pafWholeRawData );
return -1;
}

if( GetDataTypeAppropriateForLUTS(papszLUTs) != GDT_Byte ) {
msDebug( "LoadGDALImage(%s): One of the LUT contains a input value > 255.\n"
"This is not properly supported in combination with SCALE\n",
layer->name );
}

/* -------------------------------------------------------------------- */
/* Fetch the scale processing option. */
/* -------------------------------------------------------------------- */
Expand Down Expand Up @@ -1253,6 +1434,7 @@ LoadGDALImages( GDALDatasetH hDS, int band_numbers[4], int band_count,
} else if( CSLCount(papszTokens) != 2 ) {
free( pafWholeRawData );
CSLDestroy( papszTokens );
FreeLUTs( papszLUTs );
msSetError( MS_MISCERR,
"SCALE PROCESSING option unparsable for layer %s.",
"msDrawGDAL()",
Expand Down Expand Up @@ -1334,15 +1516,21 @@ LoadGDALImages( GDALDatasetH hDS, int band_numbers[4], int band_count,
/* -------------------------------------------------------------------- */
/* Apply LUT if there is one. */
/* -------------------------------------------------------------------- */
result_code = ApplyLUT( iColorIndex+1, layer,
pabyBuffer, dst_xsize, dst_ysize );;
result_code = ApplyLUT( iColorIndex+1,
papszLUTs[iColorIndex],
pabyBuffer,
GDT_Byte,
pabyBuffer,
dst_xsize * dst_ysize );
if( result_code == -1 ) {
free( pafWholeRawData );
FreeLUTs( papszLUTs );
return result_code;
}
}

free( pafWholeRawData );
FreeLUTs( papszLUTs );

return result_code;
}
Expand Down

0 comments on commit d675ea2

Please sign in to comment.