Permalink
Browse files

Accept LUT with input range in [0,65535] set

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 d675ea2a5c10644f1de557deb2531af2c7fc30e3
Showing with 217 additions and 29 deletions.
  1. +217 −29 mapdrawgdal.c
@@ -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;
@@ -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;
}
}
@@ -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 */
@@ -968,7 +968,7 @@ static int LutFromGimpLine( char *lut_line, GByte *lut )

CSLDestroy( tokens );

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

/************************************************************************/
@@ -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 */
@@ -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;
}

/************************************************************************/
@@ -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 */
@@ -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);

@@ -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;
}

@@ -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. */
/* -------------------------------------------------------------------- */
@@ -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()",
@@ -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;
}

0 comments on commit d675ea2

Please sign in to comment.