split sRGB <-> XYZ up
split sRGB <-> XYZ into sRGB <-> scRGB <-> XYZ so we can support scRGB as a colourspace
This commit is contained in:
parent
77ec00b495
commit
1bddb78f6a
@ -18,6 +18,10 @@
|
|||||||
* - faster and more accurate sRGB <-> XYZ conversion
|
* - faster and more accurate sRGB <-> XYZ conversion
|
||||||
* 6/11/12
|
* 6/11/12
|
||||||
* - added a 16-bit path
|
* - added a 16-bit path
|
||||||
|
* 11/12/12
|
||||||
|
* - spot NaN, Inf in XYZ2RGB, they break LUT indexing
|
||||||
|
* - split sRGB <-> XYZ into sRGB <-> scRGB <-> XYZ so we can support
|
||||||
|
* scRGB as a colourspace
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -84,30 +88,6 @@ static int vips_Y2v_16[65536 + 1];
|
|||||||
*/
|
*/
|
||||||
static float vips_v2Y_16[65536];
|
static float vips_v2Y_16[65536];
|
||||||
|
|
||||||
/* We need Y in 0 - 100. We can save three muls later if we pre-scale the
|
|
||||||
* matrix.
|
|
||||||
*
|
|
||||||
* The matrix already includes the D65 channel weighting, so we just scale by
|
|
||||||
* Y.
|
|
||||||
*/
|
|
||||||
#define SCALE (VIPS_D65_Y0)
|
|
||||||
|
|
||||||
/* linear RGB -> XYZ matrix.
|
|
||||||
*/
|
|
||||||
static float vips_mat_RGB2XYZ[3][3] = {
|
|
||||||
{ SCALE * 0.4124, SCALE * 0.3576, SCALE * 0.18056 },
|
|
||||||
{ SCALE * 0.2126, SCALE * 0.7152, SCALE * 0.0722 },
|
|
||||||
{ SCALE * 0.0193, SCALE * 0.1192, SCALE * 0.9505 }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* XYZ -> linear RGB matrix.
|
|
||||||
*/
|
|
||||||
static float vips_mat_XYZ2RGB[3][3] = {
|
|
||||||
{ 3.2406 / SCALE, -1.5372 / SCALE, -0.4986 / SCALE },
|
|
||||||
{ -0.9689 / SCALE, 1.8758 / SCALE, 0.0415 / SCALE },
|
|
||||||
{ 0.0557 / SCALE, -0.2040 / SCALE, 1.0570 / SCALE }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Do our own indexing of the arrays below to make sure we get efficient mults.
|
/* Do our own indexing of the arrays below to make sure we get efficient mults.
|
||||||
*/
|
*/
|
||||||
#define INDEX( L, A, B ) (L + (A << 6) + (B << 12))
|
#define INDEX( L, A, B ) (L + (A << 6) + (B << 12))
|
||||||
@ -176,33 +156,45 @@ vips_col_make_tables_RGB_8( void )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RGB to XYZ.
|
/* sRGB to scRGB.
|
||||||
*
|
*
|
||||||
* @range is eg. 256 for 8-bit data,
|
* @range is eg. 256 for 8-bit data.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
vips_col_sRGB2XYZ( int range, float *lut,
|
vips_col_sRGB2scRGB( int range, float *lut,
|
||||||
int r, int g, int b, float *X, float *Y, float *Z )
|
int r, int g, int b, float *R, float *G, float *B )
|
||||||
{
|
{
|
||||||
int maxval = range - 1;
|
int maxval = range - 1;
|
||||||
float Yr, Yg, Yb;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
i = VIPS_CLIP( 0, r, maxval );
|
i = VIPS_CLIP( 0, r, maxval );
|
||||||
Yr = lut[i];
|
*R = lut[i];
|
||||||
|
|
||||||
i = VIPS_CLIP( 0, g, maxval );
|
i = VIPS_CLIP( 0, g, maxval );
|
||||||
Yg = lut[i];
|
*G = lut[i];
|
||||||
|
|
||||||
i = VIPS_CLIP( 0, b, maxval );
|
i = VIPS_CLIP( 0, b, maxval );
|
||||||
Yb = lut[i];
|
*B = lut[i];
|
||||||
|
|
||||||
*X = vips_mat_RGB2XYZ[0][0] * Yr +
|
return( 0 );
|
||||||
vips_mat_RGB2XYZ[0][1] * Yg + vips_mat_RGB2XYZ[0][2] * Yb;
|
}
|
||||||
*Y = vips_mat_RGB2XYZ[1][0] * Yr +
|
|
||||||
vips_mat_RGB2XYZ[1][1] * Yg + vips_mat_RGB2XYZ[1][2] * Yb;
|
/* We need Y in 0 - 100. We can save three muls later if we pre-scale the
|
||||||
*Z = vips_mat_RGB2XYZ[2][0] * Yr +
|
* matrix.
|
||||||
vips_mat_RGB2XYZ[2][1] * Yg + vips_mat_RGB2XYZ[2][2] * Yb;
|
*
|
||||||
|
* The matrix already includes the D65 channel weighting, so we just scale by
|
||||||
|
* Y.
|
||||||
|
*/
|
||||||
|
#define SCALE (VIPS_D65_Y0)
|
||||||
|
|
||||||
|
/* scRGB to XYZ.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vips_col_scRGB2XYZ( float R, float G, float B, float *X, float *Y, float *Z )
|
||||||
|
{
|
||||||
|
*X = SCALE * 0.4124 * R + SCALE * 0.3576 * G + SCALE * 0.18056 * B;
|
||||||
|
*Y = SCALE * 0.2126 * R + SCALE * 0.7152 * G + SCALE * 0.07220 * B;
|
||||||
|
*Z = SCALE * 0.0193 * R + SCALE * 0.1192 * G + SCALE * 0.9505 * B;
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
@ -210,9 +202,12 @@ vips_col_sRGB2XYZ( int range, float *lut,
|
|||||||
int
|
int
|
||||||
vips_col_sRGB2XYZ_8( int r, int g, int b, float *X, float *Y, float *Z )
|
vips_col_sRGB2XYZ_8( int r, int g, int b, float *X, float *Y, float *Z )
|
||||||
{
|
{
|
||||||
|
float R, G, B;
|
||||||
|
|
||||||
vips_col_make_tables_RGB_8();
|
vips_col_make_tables_RGB_8();
|
||||||
|
|
||||||
return( vips_col_sRGB2XYZ( 256, vips_v2Y_8, r, g, b, X, Y, Z ) );
|
return( vips_col_sRGB2scRGB( 256, vips_v2Y_8, r, g, b, &R, &G, &B ) ||
|
||||||
|
vips_col_scRGB2XYZ( R, G, B, X, Y, Z ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
@ -242,23 +237,40 @@ vips_col_make_tables_RGB_16( void )
|
|||||||
int
|
int
|
||||||
vips_col_sRGB2XYZ_16( int r, int g, int b, float *X, float *Y, float *Z )
|
vips_col_sRGB2XYZ_16( int r, int g, int b, float *X, float *Y, float *Z )
|
||||||
{
|
{
|
||||||
|
float R, G, B;
|
||||||
|
|
||||||
vips_col_make_tables_RGB_16();
|
vips_col_make_tables_RGB_16();
|
||||||
|
|
||||||
return( vips_col_sRGB2XYZ( 65536, vips_v2Y_16, r, g, b, X, Y, Z ) );
|
return( vips_col_sRGB2scRGB( 65536, vips_v2Y_16,
|
||||||
|
r, g, b, &R, &G, &B ) ||
|
||||||
|
vips_col_scRGB2XYZ( R, G, B, X, Y, Z ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turn XYZ into display colour. Return or=1 for out of gamut - rgb will
|
/* Turn XYZ into scRGB.
|
||||||
* contain an approximation of the right colour.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
vips_col_XYZ2sRGB( int range, int *lut,
|
vips_col_XYZ2scRGB( float X, float Y, float Z, float *R, float *G, float *B )
|
||||||
float X, float Y, float Z,
|
{
|
||||||
|
*R = 3.2406 / SCALE * X + -1.5372 / SCALE * Y + -0.4986 / SCALE * Z;
|
||||||
|
*G = -0.9689 / SCALE * X + 1.8758 / SCALE * Y + 0.0415 / SCALE * Z;
|
||||||
|
*B = 0.0557 / SCALE * X + -0.2040 / SCALE * Y + 1.0570 / SCALE * Z;
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn scRGB into sRGB. Return or=1 for out of gamut - rgb will contain an
|
||||||
|
* approximation of the right colour.
|
||||||
|
*
|
||||||
|
* Return -1 for NaN, Inf etc.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vips_col_scRGB2sRGB( int range, int *lut,
|
||||||
|
float R, float G, float B,
|
||||||
int *r, int *g, int *b,
|
int *r, int *g, int *b,
|
||||||
int *or_ret )
|
int *or_ret )
|
||||||
{
|
{
|
||||||
int maxval = range - 1;
|
int maxval = range - 1;
|
||||||
|
|
||||||
float Yr, Yg, Yb;
|
|
||||||
int or;
|
int or;
|
||||||
float Yf;
|
float Yf;
|
||||||
int Yi;
|
int Yi;
|
||||||
@ -267,9 +279,9 @@ vips_col_XYZ2sRGB( int range, int *lut,
|
|||||||
/* XYZ can be Nan, Inf etc. Throw those values out, they will break
|
/* XYZ can be Nan, Inf etc. Throw those values out, they will break
|
||||||
* our clipping.
|
* our clipping.
|
||||||
*/
|
*/
|
||||||
if( !isnormal( X ) ||
|
if( !isnormal( R ) ||
|
||||||
!isnormal( Y ) ||
|
!isnormal( G ) ||
|
||||||
!isnormal( Z ) ) {
|
!isnormal( B ) ) {
|
||||||
*r = 0;
|
*r = 0;
|
||||||
*g = 0;
|
*g = 0;
|
||||||
*b = 0;
|
*b = 0;
|
||||||
@ -277,15 +289,6 @@ vips_col_XYZ2sRGB( int range, int *lut,
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Multiply through the matrix to get luminosity values.
|
|
||||||
*/
|
|
||||||
Yr = vips_mat_XYZ2RGB[0][0] * X +
|
|
||||||
vips_mat_XYZ2RGB[0][1] * Y + vips_mat_XYZ2RGB[0][2] * Z;
|
|
||||||
Yg = vips_mat_XYZ2RGB[1][0] * X +
|
|
||||||
vips_mat_XYZ2RGB[1][1] * Y + vips_mat_XYZ2RGB[1][2] * Z;
|
|
||||||
Yb = vips_mat_XYZ2RGB[2][0] * X +
|
|
||||||
vips_mat_XYZ2RGB[2][1] * Y + vips_mat_XYZ2RGB[2][2] * Z;
|
|
||||||
|
|
||||||
/* Clip range, set the out-of-range flag.
|
/* Clip range, set the out-of-range flag.
|
||||||
*/
|
*/
|
||||||
#define CLIP( L, V, H ) { \
|
#define CLIP( L, V, H ) { \
|
||||||
@ -307,20 +310,20 @@ vips_col_XYZ2sRGB( int range, int *lut,
|
|||||||
|
|
||||||
or = 0;
|
or = 0;
|
||||||
|
|
||||||
Yf = Yr * maxval;
|
Yf = R * maxval;
|
||||||
CLIP( 0, Yf, maxval);
|
CLIP( 0, Yf, maxval );
|
||||||
Yi = (int) Yf;
|
Yi = (int) Yf;
|
||||||
v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi);
|
v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi);
|
||||||
*r = VIPS_RINT( v );
|
*r = VIPS_RINT( v );
|
||||||
|
|
||||||
Yf = Yg * maxval;
|
Yf = G * maxval;
|
||||||
CLIP( 0, Yf, maxval);
|
CLIP( 0, Yf, maxval );
|
||||||
Yi = (int) Yf;
|
Yi = (int) Yf;
|
||||||
v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi);
|
v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi);
|
||||||
*g = VIPS_RINT( v );
|
*g = VIPS_RINT( v );
|
||||||
|
|
||||||
Yf = Yb * maxval;
|
Yf = B * maxval;
|
||||||
CLIP( 0, Yf, maxval);
|
CLIP( 0, Yf, maxval );
|
||||||
Yi = (int) Yf;
|
Yi = (int) Yf;
|
||||||
v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi);
|
v = lut[Yi] + (lut[Yi + 1] - lut[Yi]) * (Yf - Yi);
|
||||||
*b = VIPS_RINT( v );
|
*b = VIPS_RINT( v );
|
||||||
@ -336,9 +339,13 @@ vips_col_XYZ2sRGB_8( float X, float Y, float Z,
|
|||||||
int *r, int *g, int *b,
|
int *r, int *g, int *b,
|
||||||
int *or )
|
int *or )
|
||||||
{
|
{
|
||||||
|
float R, G, B;
|
||||||
|
|
||||||
vips_col_make_tables_RGB_8();
|
vips_col_make_tables_RGB_8();
|
||||||
|
|
||||||
return( vips_col_XYZ2sRGB( 256, vips_Y2v_8, X, Y, Z, r, g, b, or ) );
|
return( vips_col_XYZ2scRGB( X, Y, Z, &R, &G, &B ) ||
|
||||||
|
vips_col_scRGB2sRGB( 256, vips_Y2v_8,
|
||||||
|
R, G, B, r, g, b, or ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -346,10 +353,13 @@ vips_col_XYZ2sRGB_16( float X, float Y, float Z,
|
|||||||
int *r, int *g, int *b,
|
int *r, int *g, int *b,
|
||||||
int *or )
|
int *or )
|
||||||
{
|
{
|
||||||
|
float R, G, B;
|
||||||
|
|
||||||
vips_col_make_tables_RGB_16();
|
vips_col_make_tables_RGB_16();
|
||||||
|
|
||||||
return( vips_col_XYZ2sRGB( 65536, vips_Y2v_16,
|
return( vips_col_XYZ2scRGB( X, Y, Z, &R, &G, &B ) ||
|
||||||
X, Y, Z, r, g, b, or ) );
|
vips_col_scRGB2sRGB( 65536, vips_Y2v_16,
|
||||||
|
R, G, B, r, g, b, or ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build Lab->disp dither tables.
|
/* Build Lab->disp dither tables.
|
||||||
|
Loading…
Reference in New Issue
Block a user