From 474ab9ca0dce14a8db1260d90f56f8a7209eec2a Mon Sep 17 00:00:00 2001 From: Andrey Kiselev Date: Thu, 13 Nov 2003 19:46:39 +0000 Subject: [PATCH] New color space conversion code: CIE L*a*b* 1976 images now supported by the TIFFRGBAImage interface. All introduced routines go to new module tif_color.c. Eventually all color conversion functions should be moved there. --- libtiff/Makefile.in | 6 +- libtiff/tif_color.c | 233 +++++++++++++++++++++++++++++++++++++++++ libtiff/tif_getimage.c | 89 ++++++++++++---- libtiff/tiffio.h | 70 +++++++++++-- 4 files changed, 365 insertions(+), 33 deletions(-) create mode 100644 libtiff/tif_color.c diff --git a/libtiff/Makefile.in b/libtiff/Makefile.in index b00f6e53..7284650b 100644 --- a/libtiff/Makefile.in +++ b/libtiff/Makefile.in @@ -1,4 +1,4 @@ -# $Header: /cvs/maptools/cvsroot/libtiff/libtiff/Makefile.in,v 1.24 2003-10-09 08:36:24 dron Exp $ +# $Header: /cvs/maptools/cvsroot/libtiff/libtiff/Makefile.in,v 1.25 2003-11-13 19:46:39 dron Exp $ # # Tag Image File Format Library # @@ -72,6 +72,7 @@ SRCS = \ tif_close.c \ tif_codec.c \ tif_compress.c \ + tif_color.c \ tif_dir.c \ tif_dirinfo.c \ tif_dirread.c \ @@ -109,6 +110,7 @@ OBJS = \ tif_close.o \ tif_codec.o \ tif_compress.o \ + tif_color.o\ tif_dir.o \ tif_dirinfo.o \ tif_dirread.o \ @@ -286,6 +288,8 @@ tif_codec.o: ${SRCDIR}/tif_codec.c ${CC} -c ${CFLAGS} ${SRCDIR}/tif_codec.c tif_compress.o: ${SRCDIR}/tif_compress.c ${CC} -c ${CFLAGS} ${SRCDIR}/tif_compress.c +tif_color.o: ${SRCDIR}/tif_color.c + ${CC} -c ${CFLAGS} ${SRCDIR}/tif_color.c tif_dir.o: ${SRCDIR}/tif_dir.c ${CC} -c ${CFLAGS} ${SRCDIR}/tif_dir.c tif_dirinfo.o: ${SRCDIR}/tif_dirinfo.c diff --git a/libtiff/tif_color.c b/libtiff/tif_color.c new file mode 100644 index 00000000..f6c0e60d --- /dev/null +++ b/libtiff/tif_color.c @@ -0,0 +1,233 @@ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_color.c,v 1.1 2003-11-13 19:46:39 dron Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * CIE L*a*b* to CIE XYZ and CIE XYZ to RGB conversion routines are taken + * from the VIPS library (http://www.vips.ecs.soton.ac.uk) with + * the permission of John Cupitt, the author. + */ + +/* + * TIFF Library. + * + * Color space conversion routines. + */ + +#include "tiffiop.h" +#include + +/* + * Convert color value from the CIE L*a*b* 1976 space to CIE XYZ. Different + * reference white tristimuli can be specified. + */ +void +TIFFCIELabToXYZ(uint32 l, int32 a, int32 b, float *X, float *Y, float *Z, + float X0, float Y0, float Z0) +{ + float L = (float)l * 100.0 / 255.0; + float cby, tmp; + + if( L < 8.856 ) { + *Y = (L * Y0) / 903.292; + cby = 7.787 * (*Y / Y0) + 16.0 / 116.0; + } else { + cby = (L + 16.0) / 116.0; + *Y = Y0 * cby * cby * cby; + } + + tmp = (double)a / 500.0 + cby; + if( tmp < 0.2069 ) + *X = X0 * (tmp - 0.13793) / 7.787; + else + *X = X0 * tmp * tmp * tmp; + + tmp = cby - (double)b / 200.0; + if( tmp < 0.2069 ) + *Z = Z0 * (tmp - 0.13793) / 7.787; + else + *Z = Z0 * tmp * tmp * tmp; +} + +/* + * Convert color value from the XYZ space to RGB. + */ +void +TIFFXYZToRGB(TIFFCIELabToRGB *cielab, float X, float Y, float Z, + uint32 *r, uint32 *g, uint32 *b) +{ + int i; + float Yr, Yg, Yb; + float *matrix = &cielab->display->d_mat[0][0]; + + /* Multiply through the matrix to get luminosity values. */ + Yr = matrix[0] * X + matrix[1] * Y + matrix[2] * Z; + Yg = matrix[3] * X + matrix[4] * Y + matrix[5] * Z; + Yb = matrix[6] * X + matrix[7] * Y + matrix[8] * Z; + + /* Clip input */ + Yr = TIFFmax( Yr, cielab->display->d_Y0R ); + Yg = TIFFmax( Yg, cielab->display->d_Y0G ); + Yb = TIFFmax( Yb, cielab->display->d_Y0B ); + + /* Turn luminosity to colour value. */ + i = TIFFmin(cielab->range, + (Yr - cielab->display->d_Y0R) / cielab->rstep); + *r = TIFFrint(cielab->Yr2r[i]); + + i = TIFFmin(cielab->range, + (Yg - cielab->display->d_Y0G) / cielab->gstep); + *g = TIFFrint(cielab->Yg2g[i]); + + i = TIFFmin(cielab->range, + (Yb - cielab->display->d_Y0B) / cielab->bstep); + *b = TIFFrint(cielab->Yb2b[i]); + + /* Clip output. */ + *r = TIFFmin( *r, cielab->display->d_Vrwr ); + *g = TIFFmin( *g, cielab->display->d_Vrwg ); + *b = TIFFmin( *b, cielab->display->d_Vrwb ); +} + +/* + * Allocate conversion state structures and make look_up tables for + * the Yr,Yb,Yg <=> r,g,b conversions. + */ +int +TIFFCIELabToRGBInit(TIFFCIELabToRGB** cielab) +{ + static char module[] = "TIFFCIELabToRGBInit"; + + int i; + float gamma; + + TIFFDisplay sRGB_display = { + { /* XYZ -> luminance matrix */ + { 3.2410, -1.5374, -0.4986 }, + { -0.9692, 1.8760, 0.0416 }, + { 0.0556, -0.2040, 1.0570 } + }, + 100, 100, 100, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 1, 1, 1, /* Residual light o/p for black pixel */ + 2.4, 2.4, 2.4, /* Gamma values for the three guns */ + }; + + + if (!(*cielab)) { + *cielab = (TIFFCIELabToRGB *) + _TIFFmalloc(sizeof(TIFFCIELabToRGB)); + if (*cielab == NULL) { + TIFFError(module, + "No space for CIE L*a*b* control structure"); + return -1; + } + + (*cielab)->range = 1500; + + (*cielab)->display = + (TIFFDisplay *)_TIFFmalloc(sizeof(TIFFDisplay)); + if ((*cielab) == NULL) { + TIFFError(module, "No space for display structure"); + _TIFFfree(*cielab); + *cielab = 0; + return -1; + } + + (*cielab)->Yr2r = (float *) + _TIFFmalloc(((*cielab)->range + 1) * sizeof(float)); + if ((*cielab)->Yr2r == NULL) { + TIFFError(module, "No space for Red conversion array"); + _TIFFfree((*cielab)->display); + _TIFFfree(*cielab); + *cielab = 0; + return -1; + } + + (*cielab)->Yg2g = (float *) + _TIFFmalloc(((*cielab)->range + 1) * sizeof(float)); + if ((*cielab)->Yg2g == NULL) { + TIFFError(module, + "No space for Green conversion array"); + _TIFFfree((*cielab)->Yr2r); + _TIFFfree((*cielab)->display); + _TIFFfree(*cielab); + *cielab = 0; + return -1; + } + + (*cielab)->Yb2b = (float *) + _TIFFmalloc(((*cielab)->range + 1) * sizeof(float)); + if ((*cielab)->Yb2b == NULL) { + TIFFError(module, "No space for Blue conversion array"); + _TIFFfree((*cielab)->Yb2b); + _TIFFfree((*cielab)->Yr2r); + _TIFFfree((*cielab)->display); + _TIFFfree(*cielab); + *cielab = 0; + return -1; + } + + _TIFFmemcpy((*cielab)->display, &sRGB_display, + sizeof(TIFFDisplay)); + } + + /* Red */ + gamma = 1.0 / (*cielab)->display->d_gammaR ; + (*cielab)->rstep = + ((*cielab)->display->d_YCR - (*cielab)->display->d_Y0R) + / (*cielab)->range; + for(i = 0; i <= (*cielab)->range; i++) { + (*cielab)->Yr2r[i] = + (*cielab)->display->d_Vrwr + * (pow((double)i / (*cielab)->range, gamma)); + } + + /* Green */ + gamma = 1.0 / (*cielab)->display->d_gammaG ; + (*cielab)->gstep = + ((*cielab)->display->d_YCR - (*cielab)->display->d_Y0R) + / (*cielab)->range; + for(i = 0; i <= (*cielab)->range; i++) { + (*cielab)->Yg2g[i] = + (*cielab)->display->d_Vrwg + * (pow((double)i / (*cielab)->range, gamma)); + } + + /* Blue */ + gamma = 1.0 / (*cielab)->display->d_gammaB ; + (*cielab)->bstep = + ((*cielab)->display->d_YCR - (*cielab)->display->d_Y0R) + / (*cielab)->range; + for(i = 0; i <= (*cielab)->range; i++) { + (*cielab)->Yb2b[i] = + (*cielab)->display->d_Vrwb + * (pow((double)i / (*cielab)->range, gamma)); + } + + return 0; +} + + diff --git a/libtiff/tif_getimage.c b/libtiff/tif_getimage.c index 81949de4..63141a9d 100644 --- a/libtiff/tif_getimage.c +++ b/libtiff/tif_getimage.c @@ -1,4 +1,4 @@ -/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_getimage.c,v 1.28 2003-10-18 15:42:50 dron Exp $ */ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_getimage.c,v 1.29 2003-11-13 19:46:39 dron Exp $ */ /* * Copyright (c) 1991-1997 Sam Leffler @@ -151,6 +151,8 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024]) return (0); } break; + case PHOTOMETRIC_CIELAB: + break; default: sprintf(emsg, "Sorry, can not handle image with %s=%d", photoTag, photometric); @@ -162,20 +164,27 @@ TIFFRGBAImageOK(TIFF* tif, char emsg[1024]) void TIFFRGBAImageEnd(TIFFRGBAImage* img) { - if (img->Map) - _TIFFfree(img->Map), img->Map = NULL; - if (img->BWmap) - _TIFFfree(img->BWmap), img->BWmap = NULL; - if (img->PALmap) - _TIFFfree(img->PALmap), img->PALmap = NULL; - if (img->ycbcr) - _TIFFfree(img->ycbcr), img->ycbcr = NULL; + if (img->Map) + _TIFFfree(img->Map), img->Map = NULL; + if (img->BWmap) + _TIFFfree(img->BWmap), img->BWmap = NULL; + if (img->PALmap) + _TIFFfree(img->PALmap), img->PALmap = NULL; + if (img->ycbcr) + _TIFFfree(img->ycbcr), img->ycbcr = NULL; + if (img->cielab) { + _TIFFfree(img->cielab->Yr2r); + _TIFFfree(img->cielab->Yg2g); + _TIFFfree(img->cielab->Yb2b); + _TIFFfree(img->cielab->display); + _TIFFfree(img->cielab), img->cielab = NULL; + } - if( img->redcmap ) { - _TIFFfree( img->redcmap ); - _TIFFfree( img->greencmap ); - _TIFFfree( img->bluecmap ); - } + if( img->redcmap ) { + _TIFFfree( img->redcmap ); + _TIFFfree( img->greencmap ); + _TIFFfree( img->bluecmap ); + } } static int @@ -375,6 +384,8 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024]) img->photometric = PHOTOMETRIC_RGB; /* little white lie */ img->bitspersample = 8; break; + case PHOTOMETRIC_CIELAB: + break; default: sprintf(emsg, "Sorry, can not handle image with %s=%d", photoTag, img->photometric); @@ -384,6 +395,7 @@ TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024]) img->BWmap = NULL; img->PALmap = NULL; img->ycbcr = NULL; + img->cielab = NULL; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height); TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation); @@ -1471,6 +1483,31 @@ DECLARESepPutFunc(putRGBUAseparate16bittile) } } +/* + * 8-bit packed CIE L*a*b 1976 samples => RGB + */ +DECLAREContigPutFunc(putcontig8bitCIELab) +{ + float X, Y, Z; + uint32 r, g, b; + (void) y; + fromskew *= 3; + while (h-- > 0) { + for (x = w; x-- > 0;) { + TIFFCIELabToXYZ((u_char)pp[0], + (signed char)pp[1], + (signed char)pp[2], + &X, &Y, &Z, + D50_X0, D50_Y0, D50_Z0); + TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b); + *cp++ = PACK(r, g, b); + pp += 3; + } + cp += toskew; + pp += fromskew; + } +} + /* * YCbCr -> RGB conversion and packing routines. The colorspace * conversion algorithm comes from the IJG v5a code; see below @@ -2040,6 +2077,13 @@ initYCbCrConversion(TIFFRGBAImage* img) return (NULL); } +static tileContigRoutine +initCIELabToRGBConversion(TIFFRGBAImage* img) +{ + TIFFCIELabToRGBInit(&img->cielab); + return putcontig8bitCIELab; +} + /* * Greyscale images with less than 8 bits/sample are handled * with a table to avoid lots of shifts and masks. The table @@ -2338,6 +2382,10 @@ pickTileContigCase(TIFFRGBAImage* img) if (img->bitspersample == 8) put = initYCbCrConversion(img); break; + case PHOTOMETRIC_CIELAB: + if (img->bitspersample == 8) + put = initCIELabToRGBConversion(img); + break; } } return ((img->put.contig = put) != 0); @@ -2481,9 +2529,10 @@ TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster) * Setup the RGBA reader. */ - if (!TIFFRGBAImageOK(tif, emsg) || !TIFFRGBAImageBegin(&img, tif, 0, emsg)) { - TIFFError(TIFFFileName(tif), emsg); - return( 0 ); + if (!TIFFRGBAImageOK(tif, emsg) + || !TIFFRGBAImageBegin(&img, tif, 0, emsg)) { + TIFFError(TIFFFileName(tif), emsg); + return( 0 ); } /* @@ -2525,8 +2574,7 @@ TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster) if( read_xsize == tile_xsize && read_ysize == tile_ysize ) return( ok ); - for( i_row = 0; i_row < read_ysize; i_row++ ) - { + for( i_row = 0; i_row < read_ysize; i_row++ ) { memmove( raster + (tile_ysize - i_row - 1) * tile_xsize, raster + (read_ysize - i_row - 1) * read_xsize, read_xsize * sizeof(uint32) ); @@ -2534,8 +2582,7 @@ TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster) 0, sizeof(uint32) * (tile_xsize - read_xsize) ); } - for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) - { + for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) { _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize, 0, sizeof(uint32) * tile_xsize ); } diff --git a/libtiff/tiffio.h b/libtiff/tiffio.h index ed179b7e..2658765f 100644 --- a/libtiff/tiffio.h +++ b/libtiff/tiffio.h @@ -1,4 +1,4 @@ -/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tiffio.h,v 1.18 2003-11-07 11:26:38 dron Exp $ */ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tiffio.h,v 1.19 2003-11-13 19:46:39 dron Exp $ */ /* * Copyright (c) 1988-1997 Sam Leffler @@ -114,10 +114,66 @@ typedef void* thandle_t; /* client data handle */ #define TIFFPRINT_JPEGACTABLES 0x200 /* JPEG AC tables */ #define TIFFPRINT_JPEGDCTABLES 0x200 /* JPEG DC tables */ +/* + * Colour conversion stuff + */ + +/* reference white */ +#define D65_X0 (95.0470) +#define D65_Y0 (100.0) +#define D65_Z0 (108.8827) + +#define D50_X0 (96.4250) +#define D50_Y0 (100.0) +#define D50_Z0 (82.4680) + +/* Structure for holding information about a display device. */ + +typedef unsigned char TIFFRGBValue; /* 8-bit samples */ + +typedef struct { + float d_mat[3][3]; /* XYZ -> luminance matrix */ + float d_YCR; /* Light o/p for reference white */ + float d_YCG; + float d_YCB; + int d_Vrwr; /* Pixel values for ref. white */ + int d_Vrwg; + int d_Vrwb; + float d_Y0R; /* Residual light for black pixel */ + float d_Y0G; + float d_Y0B; + float d_gammaR; /* Gamma values for the three guns */ + float d_gammaG; + float d_gammaB; +} TIFFDisplay; + +typedef struct { /* YCbCr->RGB support */ + TIFFRGBValue* clamptab; /* range clamping table */ + int* Cr_r_tab; + int* Cb_b_tab; + int32* Cr_g_tab; + int32* Cb_g_tab; + float coeffs[3]; /* cached for repeated use */ +} TIFFYCbCrToRGB; + +typedef struct { /* CIE Lab 1976->RGB support */ + TIFFDisplay *display; + int range; /* Size of conversion table*/ + float* Yr2r; /* Conversion of Yr to r */ + float* Yg2g; /* Conversion of Yg to g */ + float* Yb2b; /* Conversion of Yb to b */ + float rstep, gstep, bstep; +} TIFFCIELabToRGB; + +extern int TIFFCIELabToRGBInit(TIFFCIELabToRGB**); +extern void TIFFCIELabToXYZ(uint32, int32, int32, float *, float *, float *, + float, float, float); +extern void TIFFXYZToRGB(TIFFCIELabToRGB *, float, float, float, + uint32 *, uint32 *, uint32 *); + /* * RGBA-style image support. */ -typedef unsigned char TIFFRGBValue; /* 8-bit samples */ typedef struct _TIFFRGBAImage TIFFRGBAImage; /* * The image reading and conversion routines invoke @@ -138,15 +194,6 @@ typedef void (*tileSeparateRoutine) /* * RGBA-reader state. */ -typedef struct { /* YCbCr->RGB support */ - TIFFRGBValue* clamptab; /* range clamping table */ - int* Cr_r_tab; - int* Cb_b_tab; - int32* Cr_g_tab; - int32* Cb_g_tab; - float coeffs[3]; /* cached for repeated use */ -} TIFFYCbCrToRGB; - struct _TIFFRGBAImage { TIFF* tif; /* image handle */ int stoponerr; /* stop on read error */ @@ -172,6 +219,7 @@ struct _TIFFRGBAImage { uint32** BWmap; /* black&white map */ uint32** PALmap; /* palette image map */ TIFFYCbCrToRGB* ycbcr; /* YCbCr conversion state */ + TIFFCIELabToRGB* cielab; /* CIE L*a*b conversion state */ int row_offset; int col_offset;