diff --git a/ChangeLog b/ChangeLog index a59dfe57..b8bcec45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 31/8/12 started 7.31.0 -- redone im_Lab2XYZ(), im_Lab2LCh(), im_LCh2Lab(), im_UCS2LCh, im_LCh2UCS() - as classes +- redone im_Lab2XYZ(), im_XYZ2Lab(), im_Lab2LCh(), im_LCh2Lab(), im_UCS2LCh, + im_LCh2UCS() as classes 13/9/12 started 7.30.3 - linecache sized itself too large diff --git a/libvips/colour/Lab2XYZ.c b/libvips/colour/Lab2XYZ.c index d6f6e4e8..e0e110a4 100644 --- a/libvips/colour/Lab2XYZ.c +++ b/libvips/colour/Lab2XYZ.c @@ -236,9 +236,9 @@ vips_col_Lab2XYZ( float L, float a, float b, float *X, float *Y, float *Z ) in[0] = L; in[1] = a; in[2] = b; - Lab2XYZ.X0 = IM_D65_X0; - Lab2XYZ.Y0 = IM_D65_Y0; - Lab2XYZ.Z0 = IM_D65_Z0; + Lab2XYZ.X0 = VIPS_D65_X0; + Lab2XYZ.Y0 = VIPS_D65_Y0; + Lab2XYZ.Z0 = VIPS_D65_Z0; vips_Lab2XYZ_line( (VipsColour *) &Lab2XYZ, (VipsPel *) out, (VipsPel **) &in, 1 ); *X = out[0]; diff --git a/libvips/colour/Makefile.am b/libvips/colour/Makefile.am index 073f50e0..e8e1be92 100644 --- a/libvips/colour/Makefile.am +++ b/libvips/colour/Makefile.am @@ -11,6 +11,7 @@ libcolour_la_SOURCES = \ LCh2Lab.c \ LCh2UCS.c \ UCS2LCh.c \ + XYZ2Lab.c \ im_icc_transform.c \ im_Lab2LabQ.c \ im_Lab2LabS.c \ @@ -20,7 +21,6 @@ libcolour_la_SOURCES = \ im_LabS2LabQ.c \ im_LabS2Lab.c \ im_lab_morph.c \ - im_XYZ2Lab.c \ im_XYZ2Yxy.c \ im_float2rad.c \ im_Yxy2XYZ.c \ diff --git a/libvips/colour/XYZ2Lab.c b/libvips/colour/XYZ2Lab.c new file mode 100644 index 00000000..38539a4f --- /dev/null +++ b/libvips/colour/XYZ2Lab.c @@ -0,0 +1,265 @@ +/* Turn XYZ to Lab colourspace. + * + * Modifed: + * 16/11/94 JC + * - uses im_wrapone() + * - in-line conversion + * 27/1/03 JC + * - swapped cbrt() for pow(), more portable + * 12/11/04 + * - swapped pow() for cbrt() again, pow() is insanely slow on win32 + * - added a configure test for cbrt(). + * 23/11/04 + * - use a large LUT instead, about 5x faster + * 23/11/06 + * - ahem, build the LUT outside the eval thread + * 2/11/09 + * - gtkdoc + * 3/8/11 + * - fix a race in the table build + * 19/9/12 + * - redone as a class + */ + +/* + + 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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "colour.h" + +#ifndef HAVE_CBRT +#define cbrt( X ) pow( (X), 1.0 / 3.0 ) +#endif /*!HAVE_CBRT*/ + +/* Lookup table size. + */ +#define QUANT_ELEMENTS (100000) + +float cbrt_table[QUANT_ELEMENTS]; + +typedef struct _VipsXYZ2Lab { + VipsColorimetric parent_instance; + + /* The colour temperature -- default to D65. + */ + VipsArea *temp; + + /* Broken out as xyz. + */ + double X0; + double Y0; + double Z0; + +} VipsXYZ2Lab; + +typedef VipsColorimetricClass VipsXYZ2LabClass; + +G_DEFINE_TYPE( VipsXYZ2Lab, vips_XYZ2Lab, VIPS_TYPE_COLORIMETRIC ); + +static void * +table_init( void *client ) +{ + int i; + + for( i = 0; i < QUANT_ELEMENTS; i++ ) { + float Y = (double) i / QUANT_ELEMENTS; + + if( Y < 0.008856 ) + cbrt_table[i] = 7.787 * Y + (16.0 / 116.0); + else + cbrt_table[i] = cbrt( Y ); + } + + return( NULL ); +} + +static void +vips_XYZ2Lab_make_tables( void ) +{ + static GOnce once = G_ONCE_INIT; + + (void) g_once( &once, table_init, NULL ); +} + +/* Process a buffer of data. + */ +static void +vips_XYZ2Lab_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width ) +{ + VipsXYZ2Lab *XYZ2Lab = (VipsXYZ2Lab *) colour; + float *p = (float *) in[0]; + float *q = (float *) out; + + int x; + + vips_XYZ2Lab_make_tables(); + + for( x = 0; x < width; x++ ) { + float nX, nY, nZ; + int i; + float f; + float cbx, cby, cbz; + + nX = QUANT_ELEMENTS * p[0] / XYZ2Lab->X0; + nY = QUANT_ELEMENTS * p[1] / XYZ2Lab->Y0; + nZ = QUANT_ELEMENTS * p[2] / XYZ2Lab->Z0; + p += 3; + + i = VIPS_CLIP( 0, (int) nX, QUANT_ELEMENTS - 2 ); + f = nX - i; + cbx = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + i = VIPS_CLIP( 0, (int) nY, QUANT_ELEMENTS - 2 ); + f = nY - i; + cby = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + i = VIPS_CLIP( 0, (int) nZ, QUANT_ELEMENTS - 2 ); + f = nZ - i; + cbz = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); + + q[0] = 116.0 * cby - 16.0; + q[1] = 500.0 * (cbx - cby); + q[2] = 200.0 * (cby - cbz); + q += 3; + } +} + +/** + * vips_col_XYZ2Lab: + * @X: Input CIE XYZ colour + * @Y: Input CIE XYZ colour + * @Z: Input CIE XYZ colour + * @L: Return CIE Lab value + * @a: Return CIE Lab value + * @b: Return CIE Lab value + * + * Calculate XYZ from Lab, D65. + * + * See also: vips_XYZ2Lab(). + */ +void +vips_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ) +{ + float in[3], out[3]; + VipsXYZ2Lab XYZ2Lab; + + in[0] = X; + in[1] = Y; + in[2] = Z; + XYZ2Lab.X0 = VIPS_D65_X0; + XYZ2Lab.Y0 = VIPS_D65_Y0; + XYZ2Lab.Z0 = VIPS_D65_Z0; + vips_XYZ2Lab_line( (VipsColour *) &XYZ2Lab, + (VipsPel *) out, (VipsPel **) &in, 1 ); + *L = out[0]; + *a = out[1]; + *b = out[2]; +} + +static int +vips_XYZ2Lab_build( VipsObject *object ) +{ + VipsXYZ2Lab *XYZ2Lab = (VipsXYZ2Lab *) object; + + if( XYZ2Lab->temp ) { + if( vips_check_vector_length( "VipsXYZ2Lab", + XYZ2Lab->temp->n, 3 ) ) + return( -1 ); + XYZ2Lab->X0 = ((double *) XYZ2Lab->temp->data)[0]; + XYZ2Lab->Y0 = ((double *) XYZ2Lab->temp->data)[1]; + XYZ2Lab->Z0 = ((double *) XYZ2Lab->temp->data)[2]; + } + + if( VIPS_OBJECT_CLASS( vips_XYZ2Lab_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_XYZ2Lab_class_init( VipsXYZ2LabClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsColourClass *colour_class = VIPS_COLOUR_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "XYZ2Lab"; + object_class->description = _( "transform XYZ to Lab" ); + object_class->build = vips_XYZ2Lab_build; + + colour_class->process_line = vips_XYZ2Lab_line; + colour_class->interpretation = VIPS_INTERPRETATION_LAB; + + VIPS_ARG_BOXED( class, "temp", 110, + _( "Temperature" ), + _( "Colour temperature" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsXYZ2Lab, temp ), + VIPS_TYPE_ARRAY_DOUBLE ); +} + +static void +vips_XYZ2Lab_init( VipsXYZ2Lab *XYZ2Lab ) +{ + XYZ2Lab->X0 = VIPS_D65_X0; + XYZ2Lab->Y0 = VIPS_D65_Y0; + XYZ2Lab->Z0 = VIPS_D65_Z0; +} + +/** + * vips_XYZ2Lab: + * @in: input image + * @out: output image + * + * Turn XYZ to D65 Lab. + * + * Returns: 0 on success, -1 on error. + */ +int +vips_XYZ2Lab( VipsImage *in, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "XYZ2Lab", ap, in, out ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/colour/colour_funcs.c b/libvips/colour/colour_funcs.c index edabbeb1..7b689af1 100644 --- a/libvips/colour/colour_funcs.c +++ b/libvips/colour/colour_funcs.c @@ -67,37 +67,6 @@ #include #include -/** - * im_col_XYZ2Lab: - * @X: Input CIE XYZ colour - * @Y: Input CIE XYZ colour - * @Z: Input CIE XYZ colour - * @L: return CIE Lab value - * @a: return CIE Lab value - * @b: return CIE Lab value - * - * Calculate Lab from XYZ, D65. - * - * See also: im_XYZ2Lab_temp(). - */ -void -im_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ) -{ - float in[3], out[3]; - im_colour_temperature temp; - - in[0] = X; - in[1] = Y; - in[2] = Z; - temp.X0 = IM_D65_X0; - temp.Y0 = IM_D65_Y0; - temp.Z0 = IM_D65_Z0; - imb_XYZ2Lab( in, out, 1, &temp ); - *L = out[0]; - *a = out[1]; - *b = out[2]; -} - /** * im_col_pythagoras: * @L1: Input coordinate 1 diff --git a/libvips/colour/im_XYZ2Lab.c b/libvips/colour/im_XYZ2Lab.c deleted file mode 100644 index ad7f42e4..00000000 --- a/libvips/colour/im_XYZ2Lab.c +++ /dev/null @@ -1,189 +0,0 @@ -/* Turn XYZ to Lab colourspace. - * - * Modifed: - * 16/11/94 JC - * - uses im_wrapone() - * - in-line conversion - * 27/1/03 JC - * - swapped cbrt() for pow(), more portable - * 12/11/04 - * - swapped pow() for cbrt() again, pow() is insanely slow on win32 - * - added a configure test for cbrt(). - * 23/11/04 - * - use a large LUT instead, about 5x faster - * 23/11/06 - * - ahem, build the LUT outside the eval thread - * 2/11/09 - * - gtkdoc - * 3/8/11 - * - fix a race in the table build - */ - -/* - - 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 -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include -#include -#include - -#ifndef HAVE_CBRT -#define cbrt( X ) pow( (X), 1.0 / 3.0 ) -#endif /*!HAVE_CBRT*/ - -/* Lookup table size. - */ -#define QUANT_ELEMENTS (100000) - -float cbrt_table[QUANT_ELEMENTS]; - -static void -imb_XYZ2Lab_tables( void ) -{ - static int built_tables = 0; - - int i; - - g_mutex_lock( im__global_lock ); - - if( built_tables ) { - g_mutex_unlock( im__global_lock ); - return; - } - - for( i = 0; i < QUANT_ELEMENTS; i++ ) { - float Y = (double) i / QUANT_ELEMENTS; - - if( Y < 0.008856 ) - cbrt_table[i] = 7.787 * Y + (16.0 / 116.0); - else - cbrt_table[i] = cbrt( Y ); - } - - built_tables = 1; - - g_mutex_unlock( im__global_lock ); -} - -/* Process a buffer of data. - */ -void -imb_XYZ2Lab( float *p, float *q, int n, im_colour_temperature *temp ) -{ - int x; - - imb_XYZ2Lab_tables(); - - for( x = 0; x < n; x++ ) { - float nX, nY, nZ; - int i; - float f; - float cbx, cby, cbz; - - nX = QUANT_ELEMENTS * p[0] / temp->X0; - nY = QUANT_ELEMENTS * p[1] / temp->Y0; - nZ = QUANT_ELEMENTS * p[2] / temp->Z0; - p += 3; - - i = (int) nX; - if( i < 0 ) - i = 0; - if( i > QUANT_ELEMENTS - 2 ) - i = QUANT_ELEMENTS - 2; - f = nX - i; - cbx = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); - - i = (int) nY; - if( i < 0 ) - i = 0; - if( i > QUANT_ELEMENTS - 2 ) - i = QUANT_ELEMENTS - 2; - f = nY - i; - cby = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); - - i = (int) nZ; - if( i < 0 ) - i = 0; - if( i > QUANT_ELEMENTS - 2 ) - i = QUANT_ELEMENTS - 2; - f = nZ - i; - cbz = cbrt_table[i] + f * (cbrt_table[i + 1] - cbrt_table[i]); - - q[0] = 116.0 * cby - 16.0; - q[1] = 500.0 * (cbx - cby); - q[2] = 200.0 * (cby - cbz); - q += 3; - } -} - -/** - * im_XYZ2Lab_temp: - * @in: input image - * @out: output image - * @X0: colour temperature - * @Y0: colour temperature - * @Z0: colour temperature - * - * Turn XYZ to Lab. @X0, @y0, @Z0 give the Lab colour temperature. - * - * Returns: 0 on success, -1 on error. - */ -int -im_XYZ2Lab_temp( IMAGE *in, IMAGE *out, double X0, double Y0, double Z0 ) -{ - im_colour_temperature *temp; - - if( !(temp = IM_NEW( out, im_colour_temperature )) ) - return( -1 ); - temp->X0 = X0; - temp->Y0 = Y0; - temp->Z0 = Z0; - - return( im__colour_unary( "im_XYZ2Lab", in, out, IM_TYPE_LAB, - (im_wrapone_fn) imb_XYZ2Lab, temp, NULL ) ); -} - -/** - * im_XYZ2Lab: - * @in: input image - * @out: output image - * - * Turn XYZ to D65 Lab. - * - * Returns: 0 on success, -1 on error. - */ -int -im_XYZ2Lab( IMAGE *in, IMAGE *out ) -{ - return( im_XYZ2Lab_temp( in, out, IM_D65_X0, IM_D65_Y0, IM_D65_Z0 ) ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 85a6386c..ee285b15 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2186,6 +2186,48 @@ im_Lab2XYZ( IMAGE *in, IMAGE *out ) return( 0 ); } +int +im_XYZ2Lab_temp( IMAGE *in, IMAGE *out, double X0, double Y0, double Z0 ) +{ + double ary[3]; + VipsArea *temp; + VipsImage *x; + + ary[0] = X0; + ary[1] = Y0; + ary[2] = Z0; + temp = (VipsArea *) vips_array_double_new( ary, 3 ); + if( vips_XYZ2Lab( in, &x, "temp", temp, NULL ) ) { + vips_area_unref( temp ); + return( -1 ); + } + vips_area_unref( temp ); + + if( im_copy( x, out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); + + return( 0 ); +} + +int +im_XYZ2Lab( IMAGE *in, IMAGE *out ) +{ + VipsImage *x; + + if( vips_XYZ2Lab( in, &x, NULL ) ) + return( -1 ); + if( im_copy( x, out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); + + return( 0 ); +} + int im_Lab2LCh( IMAGE *in, IMAGE *out ) { diff --git a/libvips/include/vips/colour.h b/libvips/include/vips/colour.h index 0d0400b6..63276cb2 100644 --- a/libvips/include/vips/colour.h +++ b/libvips/include/vips/colour.h @@ -117,6 +117,8 @@ int vips_UCS2XYZ( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_Lab2XYZ( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_XYZ2Lab( VipsImage *in, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_XYZ2disp( VipsImage *in, VipsImage **out, struct im_col_display *disp, ... ) __attribute__((sentinel)); @@ -127,6 +129,8 @@ int vips_UCS2LCh( VipsImage *in, VipsImage **out, ... ) void vips_col_Lab2XYZ( float L, float a, float b, float *X, float *Y, float *Z ); +void vips_col_XYZ2Lab( float X, float Y, float Z, + float *L, float *a, float *b ); double vips_col_ab2h( double a, double b ); void vips_col_ab2Ch( float a, float b, float *C, float *h ); void vips_col_Ch2ab( float C, float h, float *a, float *b ); @@ -146,8 +150,6 @@ float vips_col_Chucs2h( float C, float hucs ); /* Colour loading and conversion functions. */ -void im_col_ab2Ch( float a, float b, float *C, float *h ); -void im_col_XYZ2Lab( float X, float Y, float Z, float *L, float *a, float *b ); float im_col_pythagoras( float L1, float a1, float b1, float L2, float a2, float b2 ); @@ -169,9 +171,6 @@ int im_LabS2Lab( VipsImage *in, VipsImage *out ); int im_UCS2XYZ( VipsImage *in, VipsImage *out ); int im_UCS2Lab( VipsImage *in, VipsImage *out ); int im_Lab2UCS( VipsImage *in, VipsImage *out ); -int im_XYZ2Lab( VipsImage *in, VipsImage *out ); -int im_XYZ2Lab_temp( VipsImage *in, VipsImage *out, - double X0, double Y0, double Z0 ); int im_XYZ2UCS( VipsImage *in, VipsImage *out ); int im_sRGB2XYZ( VipsImage *in, VipsImage *out ); int im_XYZ2sRGB( VipsImage *in, VipsImage *out ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 088b0666..d37f8860 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -81,6 +81,7 @@ extern "C" { #define IM_D3250_Z0 VIPS_D3250_Z0 #define im_col_Lab2XYZ vips_col_Lab2XYZ +#define im_col_XYZ2Lab vips_col_XYZ2Lab #define im_col_ab2h vips_col_ab2h #define im_col_ab2Ch vips_col_ab2Ch #define im_col_Ch2ab vips_col_Ch2ab @@ -708,6 +709,9 @@ int im_shrink( VipsImage *in, VipsImage *out, double xshrink, double yshrink ); int im_Lab2XYZ_temp( IMAGE *in, IMAGE *out, double X0, double Y0, double Z0 ); int im_Lab2XYZ( IMAGE *in, IMAGE *out ); +int im_XYZ2Lab( VipsImage *in, VipsImage *out ); +int im_XYZ2Lab_temp( VipsImage *in, VipsImage *out, + double X0, double Y0, double Z0 ); int im_Lab2LCh( VipsImage *in, VipsImage *out ); int im_LCh2Lab( VipsImage *in, VipsImage *out ); int im_LCh2UCS( VipsImage *in, VipsImage *out );