/* babl - dynamically extendable universal pixel conversion library. * Copyright (C) 2005, 2014, 2019 Øyvind Kolås. * Copyright (C) 2009, Martin Nordholts * Copyright (C) 2014, 2019 Elle Stone * Copyright (C) 2017, 2018 Red Hat, Inc. * * This library 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 3 of the License, or (at your option) any later version. * * This library 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 library; if not, see * . */ #include "config.h" #include #include #include #if defined(USE_SSE2) #include #endif /* defined(USE_SSE2) */ #include "babl-internal.h" #include "extensions/util.h" #define DEGREES_PER_RADIAN (180 / 3.14159265358979323846) #define RADIANS_PER_DEGREE (1 / DEGREES_PER_RADIAN) #define DEGREES_PER_RADIANf (180 / 3.14159265358979323846f) #define RADIANS_PER_DEGREEf (1 / DEGREES_PER_RADIANf) #define LAB_EPSILON (216.0 / 24389.0) #define LAB_EPSILONf (216.0f / 24389.0f) #define LAB_KAPPA (24389.0 / 27.0) #define LAB_KAPPAf (24389.0f / 27.0f) /* The constants below hard-code the D50-adapted sRGB ICC profile * reference white, aka the ICC profile D50 illuminant. * * In a properly ICC profile color-managed application, the profile * illuminant values should be retrieved from the image's * ICC profile's illuminant. * * At present, the ICC profile illuminant is always D50. This might * change when the next version of the ICC specs is released. * * As encoded in an actual V2 or V4 ICC profile, * the illuminant values are hexadecimal-rounded, as are the following * hard-coded D50 ICC profile illuminant values: */ #define D50_WHITE_REF_X 0.964202880 #define D50_WHITE_REF_Y 1.000000000 #define D50_WHITE_REF_Z 0.824905400 #define D50_WHITE_REF_Xf 0.964202880f #define D50_WHITE_REF_Yf 1.000000000f #define D50_WHITE_REF_Zf 0.824905400f #define NEAR_ZERO 0.0000000001 #define NEAR_ZEROf 0.0000000001f #define near_zero(a) ((a) < NEAR_ZERO && (a) > -NEAR_ZERO) #define near_zerof(a) ((a) < NEAR_ZEROf && (a) > -NEAR_ZEROf) #define D50_WHITE_REF_x 0.345702921222f #define D50_WHITE_REF_y 0.358537532290f static void types (void); static void components (void); static void models (void); static void conversions (void); static void formats (void); int init (void); #include "babl-verify-cpu.inc" int init (void) { BABL_VERIFY_CPU(); types (); components (); models (); formats (); conversions (); return 0; } static void components (void) { babl_component_new ("CIE L", "doc", "Luminance, range 0.0-100.0 in float", NULL); babl_component_new ("CIE a", "chroma", "doc", "chroma component 0.0 is no saturation", NULL); babl_component_new ("CIE b", "chroma", "doc", "chroma component 0.0 is no saturation", NULL); babl_component_new ("CIE C(ab)", "chroma", "doc", "chrominance/saturation", NULL); babl_component_new ("CIE H(ab)", "chroma", "doc", "hue value range 0.0-360.0", NULL); babl_component_new ("CIE X", NULL); babl_component_new ("CIE Y", NULL); babl_component_new ("CIE Z", NULL); babl_component_new ("CIE x", NULL); babl_component_new ("CIE y", NULL); /* CIE 1976 UCS */ babl_component_new ("CIE u", NULL); babl_component_new ("CIE v", NULL); /* babl_component_new ("CIE z", NULL);*/ } static void models (void) { babl_model_new ( "name", "CIE Lab", "doc", "CIE Lab color model, a perceptually uniform space, euclidian distance in this space represents delta E.", babl_component ("CIE L"), babl_component ("CIE a"), babl_component ("CIE b"), "CIE", NULL); babl_model_new ( "name", "CIE Lab alpha", "doc", "CIE Lab color model, with separate alpha", babl_component ("CIE L"), babl_component ("CIE a"), babl_component ("CIE b"), babl_component ("A"), "CIE", "alpha", NULL); babl_model_new ( "name", "CIE LCH(ab)", "doc", "CIE LCH color model, using cylindrical coordinates", babl_component ("CIE L"), babl_component ("CIE C(ab)"), babl_component ("CIE H(ab)"), "CIE", NULL); babl_model_new ( "name", "CIE LCH(ab) alpha", "doc", "CIE LCH color model, using cylindrical coordinates, with separate alpha", babl_component ("CIE L"), babl_component ("CIE C(ab)"), babl_component ("CIE H(ab)"), babl_component ("A"), "CIE", "alpha", NULL); babl_model_new ( "name", "CIE XYZ", babl_component ("CIE X"), babl_component ("CIE Y"), babl_component ("CIE Z"), "CIE", NULL); babl_model_new ( "name", "CIE XYZ alpha", babl_component ("CIE X"), babl_component ("CIE Y"), babl_component ("CIE Z"), babl_component ("A"), "CIE", "alpha", NULL); babl_model_new ( "name", "CIE xyY", "doc", "the coordinate system often used for drawing chromaticity diagrams. Y is luminance.", babl_component ("CIE x"), babl_component ("CIE y"), babl_component ("CIE Y"), "CIE", NULL); babl_model_new ( "name", "CIE xyY alpha", "doc", "the coordinate system often used for drawing chromaticity diagrams. Y is luminance, with separate alpha", babl_component ("CIE x"), babl_component ("CIE y"), babl_component ("CIE Y"), babl_component ("A"), "CIE", "alpha", NULL); /* CIE 1976 UCS */ babl_model_new ( "name", "CIE Yuv", "doc", "A newer more perceptually uniform space than xyY for chromaticity diagrams.", babl_component ("CIE Y"), babl_component ("CIE u"), babl_component ("CIE v"), NULL); babl_model_new ( "name", "CIE Yuv alpha", "doc", "A newer more perceptually uniform space than xyY for chromaticity diagrams, with separate alpha.", babl_component ("CIE Y"), babl_component ("CIE u"), babl_component ("CIE v"), babl_component ("A"), NULL); } static void rgbcie_init (void); /******** begin double RGB/CIE color space conversions ****************/ static inline void ab_to_CHab (double a, double b, double *to_C, double *to_H); static inline void CHab_to_ab (double C, double H, double *to_a, double *to_b); static inline void XYZ_to_LAB (double X, double Y, double Z, double *to_L, double *to_a, double *to_b ); static inline void LAB_to_XYZ (double L, double a, double b, double *to_X, double *to_Y, double *to_Z ); static inline void XYZ_to_xyY (double X, double Y, double Z, double *to_x, double *to_y, double *to_Y ); static inline void xyY_to_XYZ (double x, double y, double Y, double *to_X, double *to_Y, double *to_Z ); /* CIE 1976 UCS */ static inline void XYZ_to_Yuv (double X, double Y, double Z, double *to_Y, double *to_u, double *to_v ); static inline void Yuv_to_XYZ (double Y, double u, double v, double *to_X, double *to_Y, double *to_Z ); static inline void XYZ_to_LAB (double X, double Y, double Z, double *to_L, double *to_a, double *to_b) { double xr = X / D50_WHITE_REF_X; double yr = Y / D50_WHITE_REF_Y; double zr = Z / D50_WHITE_REF_Z; double fx = xr > LAB_EPSILON ? cbrt (xr) : (LAB_KAPPA * xr + 16.0) / 116.0; double fy = yr > LAB_EPSILON ? cbrt (yr) : (LAB_KAPPA * yr + 16.0) / 116.0; double fz = zr > LAB_EPSILON ? cbrt (zr) : (LAB_KAPPA * zr + 16.0) / 116.0; *to_L = 116.0 * fy - 16.0; *to_a = 500.0 * (fx - fy); *to_b = 200.0 * (fy - fz); } static inline void LAB_to_XYZ (double L, double a, double b, double *to_X, double *to_Y, double *to_Z) { double fy = (L + 16.0) / 116.0; double fy_cubed = fy * fy * fy; double fx = fy + a / 500.0; double fx_cubed = fx * fx * fx; double fz = fy - b / 200.0; double fz_cubed = fz * fz * fz; double yr = L > LAB_KAPPA * LAB_EPSILON ? fy_cubed : L / LAB_KAPPA; double xr = fx_cubed > LAB_EPSILON ? fx_cubed : (fx * 116.0 - 16.0) / LAB_KAPPA; double zr = fz_cubed > LAB_EPSILON ? fz_cubed : (fz * 116.0 - 16.0) / LAB_KAPPA; *to_X = xr * D50_WHITE_REF_X; *to_Y = yr * D50_WHITE_REF_Y; *to_Z = zr * D50_WHITE_REF_Z; } /* CIE xyY */ static inline void XYZ_to_xyY (double X, double Y, double Z, double *to_x, double *to_y, double *to_Y) { double sum = X + Y + Z; if (near_zero (sum)) { *to_Y = 0.0; *to_x = D50_WHITE_REF_x; *to_y = D50_WHITE_REF_y; } else { *to_x = X / sum; *to_y = Y / sum; *to_Y = Y; } } static inline void xyY_to_XYZ (double x, double y, double Y, double *to_X, double *to_Y, double *to_Z) { if (near_zero (Y)) { *to_X = 0.0; *to_Y = 0.0; *to_Z = 0.0; } else { *to_X = (x * Y) / y; *to_Y = Y; *to_Z = ((1 - x - y) * Y) / y; } } /* CIE 1976 UCS */ /* Code for near-zero XYZ and RGB for CIE 1976 UCS patterned * after code written by and used with kind permission of Graeme Gill. * Graeme Gill's code is in "icc.c" * downloadable from http://argyllcms.com/ */ static inline void XYZ_to_Yuv (double X, double Y, double Z, double *to_Y, double *to_u, double *to_v) { double sum = X + (15.0 * Y) + (3.0 * Z); if (near_zero (sum)) { *to_Y = 0.0; *to_u = 4.0/19.0; *to_v = 9.0/19.0; } else { *to_Y = Y; *to_u = (4.0 * X) / sum; *to_v = (9.0 * Y) / sum; } } static inline void Yuv_to_XYZ (double Y, double u, double v, double *to_X, double *to_Y, double *to_Z) { if (near_zero (v)) { *to_X = 0.0; *to_Y = 0.0; *to_Z = 0.0; } else { *to_X = ((9.0 * u * Y)/(4.0 * v)); *to_Y = Y; *to_Z = -(((20.0 * v + 3.0 * u - 12.0) * Y)/(4.0 * v)); } } /* rgb <-> XYZ */ static void rgba_to_xyz (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double RGB[3] = {((double *) src)[0], ((double *) src)[1], ((double *) src)[2] }; babl_space_to_xyz (space, RGB, (double*)dst); src += sizeof (double) * 4; dst += sizeof (double) * 3; } } static void xyz_to_rgba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { babl_space_from_xyz (space, (double*)src, (double*) dst); ((double *) dst)[3] = 1.0; src += sizeof (double) * 3; dst += sizeof (double) * 4; } } static void rgba_to_xyza (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { babl_space_to_xyz (space, (double*)src, (double*)dst); ((double *) dst)[3] = ((double *) src)[3]; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } static void xyza_to_rgba (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { babl_space_from_xyz (space, (double*)src, (double*) dst); ((double *) dst)[3] = ((double *) src)[3]; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /* rgb -> xyY */ static void rgba_to_xyY (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double XYZ[3], x, y, Y; babl_space_to_xyz (space, (double*)src, XYZ); XYZ_to_xyY (XYZ[0], XYZ[1], XYZ[2], &x, &y, &Y); ((double *) dst)[0] = x; ((double *) dst)[1] = y; ((double *) dst)[2] = Y; src += sizeof (double) * 4; dst += sizeof (double) * 3; } } static void rgba_to_xyYa (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double alpha = ((double *) src)[3]; double XYZ[3], x, y, Y; //convert RGB to XYZ babl_space_to_xyz (space, (double*)src, XYZ); //convert XYZ to xyY XYZ_to_xyY (XYZ[0], XYZ[1], XYZ[2], &x, &y, &Y); ((double *) dst)[0] = x; ((double *) dst)[1] = y; ((double *) dst)[2] = Y; ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /* rgb -> Yuv */ static void rgba_to_Yuv (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double XYZ[3], Y, u, v; babl_space_to_xyz (space, (double*)src, XYZ); XYZ_to_Yuv (XYZ[0], XYZ[1], XYZ[2], &Y, &u, &v); ((double *) dst)[0] = Y; ((double *) dst)[1] = u; ((double *) dst)[2] = v; src += sizeof (double) * 4; dst += sizeof (double) * 3; } } static void rgba_to_Yuva (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double alpha = ((double *) src)[3]; double XYZ[3], Y, u, v; //convert RGB to XYZ babl_space_to_xyz (space, (double*)src, XYZ); //convert XYZ to Yuv XYZ_to_Yuv (XYZ[0], XYZ[1], XYZ[2], &Y, &u, &v); ((double *) dst)[0] = Y; ((double *) dst)[1] = u; ((double *) dst)[2] = v; ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /* rgbf <->xyYf */ static void rgbaf_to_xyYaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float x, y, X, Y, Z, r, g, b, a; r = src[0]; g = src[1]; b = src[2]; a = src[3]; if (near_zerof(r) && near_zerof(g) && near_zerof(b)) { Y = 0.0f; x = D50_WHITE_REF_x; y = D50_WHITE_REF_y; } else { X = m_0_0 * r + m_0_1 * g + m_0_2 * b; Y = m_1_0 * r + m_1_1 * g + m_1_2 * b; Z = m_2_0 * r + m_2_1 * g + m_2_2 * b; x = X / (X + Y + Z); y = Y / (X + Y + Z); } dst[0] = x; dst[1] = y; dst[2] = Y; dst[3] = a; src += 4; dst += 4; } } static void rgbf_to_xyYf (const Babl *conversion,float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float x, y, X, Y, Z, r, g, b; r = src[0]; g = src[1]; b = src[2]; if (near_zerof(r) && near_zerof(g) && near_zerof(b)) { Y = 0.0f; x = D50_WHITE_REF_x; y = D50_WHITE_REF_y; } else { X = m_0_0 * r + m_0_1 * g + m_0_2 * b; Y = m_1_0 * r + m_1_1 * g + m_1_2 * b; Z = m_2_0 * r + m_2_1 * g + m_2_2 * b; x = X / (X + Y + Z); y = Y / (X + Y + Z); } dst[0] = x; dst[1] = y; dst[2] = Y; src += 3; dst += 3; } } static void rgbaf_to_xyYf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float x, y, X, Y, Z, r, g, b; r = src[0]; g = src[1]; b = src[2]; if (near_zerof(r) && near_zerof(g) && near_zerof(b)) { Y = 0.0f; x = D50_WHITE_REF_x; y = D50_WHITE_REF_y; } else { X = m_0_0 * r + m_0_1 * g + m_0_2 * b; Y = m_1_0 * r + m_1_1 * g + m_1_2 * b; Z = m_2_0 * r + m_2_1 * g + m_2_2 * b; x = X / (X + Y + Z); y = Y / (X + Y + Z); } dst[0] = x; dst[1] = y; dst[2] = Y; src += 4; dst += 3; } } /* rgbf -> Yuv */ static void rgbaf_to_Yuvaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float u, v, X, Y, Z, r, g, b, a, sum; r = src[0]; g = src[1]; b = src[2]; a = src[3]; if (near_zerof(r) && near_zerof(g) && near_zerof(b)) { Y = 0.0f; u = 4.0f/19.0f; v = 9.0f/19.0f; } else { X = m_0_0 * r + m_0_1 * g + m_0_2 * b; Y = m_1_0 * r + m_1_1 * g + m_1_2 * b; Z = m_2_0 * r + m_2_1 * g + m_2_2 * b; sum = (X + 15.0f * Y + 3.0f * Z); u = (4.0f * X) / sum; v = (9.0f * Y) / sum; } dst[0] = Y; dst[1] = u; dst[2] = v; dst[3] = a; src += 4; dst += 4; } } static void rgbf_to_Yuvf (const Babl *conversion,float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float u, v, X, Y, Z, r, g, b, sum; r = src[0]; g = src[1]; b = src[2]; if (near_zerof(r) && near_zerof(g) && near_zerof(b)) { Y = 0.0f; u = 4.0/19.0; v = 9.0/19.0; } else { X = m_0_0 * r + m_0_1 * g + m_0_2 * b; Y = m_1_0 * r + m_1_1 * g + m_1_2 * b; Z = m_2_0 * r + m_2_1 * g + m_2_2 * b; sum = (X + 15.0f * Y + 3.0f * Z); u = (4.0f * X) / sum; v = (9.0f * Y) / sum; } dst[0] = Y; dst[1] = u; dst[2] = v; src += 3; dst += 3; } } static void rgbaf_to_Yuvf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float u, v, X, Y, Z, r, g, b, sum; r = src[0]; g = src[1]; b = src[2]; if (near_zerof(r) && near_zerof(g) && near_zerof(b)) { Y = 0.0f; u = 4.0f/19.0f; v = 9.0f/19.0f; } else { X = m_0_0 * r + m_0_1 * g + m_0_2 * b; Y = m_1_0 * r + m_1_1 * g + m_1_2 * b; Z = m_2_0 * r + m_2_1 * g + m_2_2 * b; sum = (X + 15.0f * Y + 3.0f * Z); u = (4.0f * X) / sum; v = (9.0f * Y) / sum; } dst[0] = Y; dst[1] = u; dst[2] = v; src += 4; dst += 3; } } /* xyY -> rgb */ static void xyY_to_rgba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double x = ((double *) src)[0]; double y = ((double *) src)[1]; double Y = ((double *) src)[2]; double R, G, B, X, Z; //convert xyY to XYZ xyY_to_XYZ (x, y, Y, &X, &Y, &Z); //convert XYZ to RGB { double XYZ[3] = {X,Y,Z}; double RGB[3]; babl_space_from_xyz (space, XYZ, RGB); R = RGB[0]; G = RGB[1]; B = RGB[2]; } ((double *) dst)[0] = R; ((double *) dst)[1] = G; ((double *) dst)[2] = B; ((double *) dst)[3] = 1.0; src += sizeof (double) * 3; dst += sizeof (double) * 4; } } static void xyYa_to_rgba (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double x = ((double *) src)[0]; double y = ((double *) src)[1]; double Y = ((double *) src)[2]; double alpha = ((double *) src)[3]; double X, Z; //convert xyY to XYZ xyY_to_XYZ (x, y, Y, &X, &Y, &Z); { //convert XYZ to RGB double XYZ[3] = {X,Y,Z}; babl_space_from_xyz (space, XYZ, (double*)dst); } ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /* Yuv -> rgb */ static void Yuv_to_rgba (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double Y = ((double *) src)[0]; double u = ((double *) src)[1]; double v = ((double *) src)[2]; double R, G, B, X, Z; //convert Yuv to XYZ Yuv_to_XYZ (Y, u, v, &X, &Y, &Z); //convert XYZ to RGB { double XYZ[3] = {X,Y,Z}; double RGB[3]; babl_space_from_xyz (space, XYZ, RGB); R = RGB[0]; G = RGB[1]; B = RGB[2]; } ((double *) dst)[0] = R; ((double *) dst)[1] = G; ((double *) dst)[2] = B; ((double *) dst)[3] = 1.0; src += sizeof (double) * 3; dst += sizeof (double) * 4; } } static void Yuva_to_rgba (const Babl *conversion,char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double Y = ((double *) src)[0]; double u = ((double *) src)[1]; double v = ((double *) src)[2]; double alpha = ((double *) src)[3]; double X, Z; //convert Yuv to XYZ Yuv_to_XYZ (Y, u, v, &X, &Y, &Z); { //convert XYZ to RGB double XYZ[3] = {X,Y,Z}; babl_space_from_xyz (space, XYZ, (double*)dst); } ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /* xyYf -> rgbf */ static void xyYf_to_rgbf (const Babl *conversion,float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float X, Z, r, g, b; float x = src[0]; float y = src[1]; float Y = src[2]; if (near_zerof (y)) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { X = (x * Y) / y; //Y = Y; Z = ((1 - x - y) * Y) / y; } r = m_0_0 * X + m_0_1 * Y + m_0_2 * Z; g = m_1_0 * X + m_1_1 * Y + m_1_2 * Z; b = m_2_0 * X + m_2_1 * Y + m_2_2 * Z; dst[0] = r; dst[1] = g; dst[2] = b; src += 3; dst += 3; } } static void xyYf_to_rgbaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float X, Z, r, g, b; float x = src[0]; float y = src[1]; float Y = src[2]; if (near_zerof (Y)) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { X = (x * Y) / y; //Y = Y; Z = ((1 - x - y) * Y) / y; } r = m_0_0 * X + m_0_1 * Y + m_0_2 * Z; g = m_1_0 * X + m_1_1 * Y + m_1_2 * Z; b = m_2_0 * X + m_2_1 * Y + m_2_2 * Z; dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 1.0f; src += 3; dst += 4; } } static void xyYaf_to_rgbaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float X, Z, r, g, b; float x = src[0]; float y = src[1]; float Y = src[2]; float a = src[3]; if (near_zerof (Y)) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { X = (x * Y) / y; //Y = Y; Z = ((1 - x - y) * Y) / y; } r = m_0_0 * X + m_0_1 * Y + m_0_2 * Z; g = m_1_0 * X + m_1_1 * Y + m_1_2 * Z; b = m_2_0 * X + m_2_1 * Y + m_2_2 * Z; dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = a; src += 4; dst += 4; } } /* Yuvf -> rgbf */ static void Yuvf_to_rgbf (const Babl *conversion,float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float X, Z, r, g, b; float Y = src[0]; float u = src[1]; float v = src[2]; if (near_zerof (v)) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { X = ((9.0f * u * Y)/(4.0f * v)); //Y = Y; Z = -(((20.0f * v + 3.0f * u - 12.0f) * Y)/(4.0f * v)); } r = m_0_0 * X + m_0_1 * Y + m_0_2 * Z; g = m_1_0 * X + m_1_1 * Y + m_1_2 * Z; b = m_2_0 * X + m_2_1 * Y + m_2_2 * Z; dst[0] = r; dst[1] = g; dst[2] = b; src += 3; dst += 3; } } static void Yuvf_to_rgbaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float X, Z, r, g, b; float Y = src[0]; float u = src[1]; float v = src[2]; if (near_zerof (v)) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { X = ((9.0f * u * Y)/(4.0f * v)); //Y = Y; Z = -(((20.0f * v + 3.0f * u - 12.0f) * Y)/(4.0f * v)); } r = m_0_0 * X + m_0_1 * Y + m_0_2 * Z; g = m_1_0 * X + m_1_1 * Y + m_1_2 * Z; b = m_2_0 * X + m_2_1 * Y + m_2_2 * Z; dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 1.0f; src += 3; dst += 4; } } static void Yuvaf_to_rgbaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float X, Z, r, g, b; float Y = src[0]; float u = src[1]; float v = src[2]; float a = src[3]; if (near_zerof (v)) { X = 0.0f; Y = 0.0f; Z = 0.0f; } else { X = ((9.0f * u * Y)/(4.0f * v)); //Y = Y; Z = -(((20.0f * v + 3.0f * u - 12.0f) * Y)/(4.0f * v)); } r = m_0_0 * X + m_0_1 * Y + m_0_2 * Z; g = m_1_0 * X + m_1_1 * Y + m_1_2 * Z; b = m_2_0 * X + m_2_1 * Y + m_2_2 * Z; dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = a; src += 4; dst += 4; } } /* rgb <-> LAB */ static void rgba_to_lab (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double XYZ[3], L, a, b; babl_space_to_xyz (space, (double*)src, XYZ); XYZ_to_LAB (XYZ[0], XYZ[1], XYZ[2], &L, &a, &b); ((double *) dst)[0] = L; ((double *) dst)[1] = a; ((double *) dst)[2] = b; src += sizeof (double) * 4; dst += sizeof (double) * 3; } } static void lab_to_rgba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double L = ((double *) src)[0]; double a = ((double *) src)[1]; double b = ((double *) src)[2]; double X, Y, Z, R, G, B; //convert Lab to XYZ LAB_to_XYZ (L, a, b, &X, &Y, &Z); //convert XYZ to RGB { double XYZ[3] = {X,Y,Z}; double RGB[3]; babl_space_from_xyz (space, XYZ, RGB); R = RGB[0]; G = RGB[1]; B = RGB[2]; } ((double *) dst)[0] = R; ((double *) dst)[1] = G; ((double *) dst)[2] = B; ((double *) dst)[3] = 1.0; src += sizeof (double) * 3; dst += sizeof (double) * 4; } } static void rgba_to_laba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double alpha = ((double *) src)[3]; double XYZ[3], L, a, b; //convert RGB to XYZ babl_space_to_xyz (space, (double*)src, XYZ); //convert XYZ to Lab XYZ_to_LAB (XYZ[0], XYZ[1], XYZ[2], &L, &a, &b); ((double *) dst)[0] = L; ((double *) dst)[1] = a; ((double *) dst)[2] = b; ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } static void laba_to_rgba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double L = ((double *) src)[0]; double a = ((double *) src)[1]; double b = ((double *) src)[2]; double alpha = ((double *) src)[3]; double X, Y, Z; //convert Lab to XYZ LAB_to_XYZ (L, a, b, &X, &Y, &Z); { //convert XYZ to RGB double XYZ[3] = {X,Y,Z}; babl_space_from_xyz (space, XYZ, (double*)dst); } ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /* rgb <-> LCh */ static inline void CHab_to_ab (double C, double H, double *to_a, double *to_b) { *to_a = cos (H * RADIANS_PER_DEGREE) * C; *to_b = sin (H * RADIANS_PER_DEGREE) * C; } static inline void ab_to_CHab (double a, double b, double *to_C, double *to_H) { *to_C = sqrt ( (a * a) + (b * b) ); *to_H = atan2 (b, a) * DEGREES_PER_RADIAN; // Keep H within the range 0-360 if (*to_H < 0.0) *to_H += 360; } static void rgba_to_lchab (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double XYZ[3], L, a, b, C, H; //convert RGB to XYZ babl_space_to_xyz (space, (double *)src, XYZ); //convert XYZ to Lab XYZ_to_LAB (XYZ[0], XYZ[1], XYZ[2], &L, &a, &b); //convert Lab to LCH(ab) ab_to_CHab (a, b, &C, &H); ((double *) dst)[0] = L; ((double *) dst)[1] = C; ((double *) dst)[2] = H; src += sizeof (double) * 4; dst += sizeof (double) * 3; } } static void lchab_to_rgba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double L = ((double *) src)[0]; double C = ((double *) src)[1]; double H = ((double *) src)[2]; double a, b, X, Y, Z; //Convert LCH(ab) to Lab CHab_to_ab (C, H, &a, &b); //Convert LAB to XYZ LAB_to_XYZ (L, a, b, &X, &Y, &Z); //Convert XYZ to RGB { double XYZ[3] = {X,Y,Z}; babl_space_from_xyz (space, XYZ, (double*)dst); } ((double *) dst)[3] = 1.0; src += sizeof (double) * 3; dst += sizeof (double) * 4; } } static void rgba_to_lchaba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_source_space (conversion); while (n--) { double alpha = ((double *) src)[3]; double XYZ[3], L, a, b, C, H; //convert RGB to XYZ babl_space_to_xyz (space, (double*)src, XYZ); //convert XYZ to Lab XYZ_to_LAB (XYZ[0], XYZ[1], XYZ[2], &L, &a, &b); //convert Lab to LCH(ab) ab_to_CHab (a, b, &C, &H); ((double *) dst)[0] = L; ((double *) dst)[1] = C; ((double *) dst)[2] = H; ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } static void lchaba_to_rgba (const Babl *conversion, char *src, char *dst, long n) { const Babl *space = babl_conversion_get_destination_space (conversion); while (n--) { double L = ((double *) src)[0]; double C = ((double *) src)[1]; double H = ((double *) src)[2]; double alpha = ((double *) src)[3]; double a, b, X, Y, Z; //Convert LCH(ab) to Lab CHab_to_ab (C, H, &a, &b); //Convert Lab to XYZ LAB_to_XYZ (L, a, b, &X, &Y, &Z); //Convert XYZ to RGB { double XYZ[3] = {X,Y,Z}; babl_space_from_xyz (space, XYZ, (double*)dst); } ((double *) dst)[3] = alpha; src += sizeof (double) * 4; dst += sizeof (double) * 4; } } /******** end double RGB/CIE color space conversions ******************/ /******** begin floating point RGB/CIE color space conversions ********/ /* origin: http://www.hackersdelight.org/hdcodetxt/acbrt.c.txt * permissions: http://www.hackersdelight.org/permissions.htm */ /* _cbrtf(x) * Return cube root of x */ static inline float _cbrtf (float x) { union { float f; uint32_t i; } u = { x }; u.i = u.i / 4 + u.i / 16; u.i = u.i + u.i / 16; u.i = u.i + u.i / 256; u.i = 0x2a5137a0 + u.i; u.f = 0.33333333f * (2.0f * u.f + x / (u.f * u.f)); u.f = 0.33333333f * (2.0f * u.f + x / (u.f * u.f)); return u.f; } static inline float cubef (float f) { return f * f * f; } static void Yf_to_Lf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float yr = src[0]; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; src++; dst++; } } static void Yaf_to_Lf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float yr = src[0]; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; src += 2; dst += 1; } } static void Yaf_to_Laf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float yr = src[0]; float a = src[1]; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; dst[1] = a; src += 2; dst += 2; } } static void rgbf_to_Labf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float r = src[0]; float g = src[1]; float b = src[2]; float xr = m_0_0 * r + m_0_1 * g + m_0_2 * b; float yr = m_1_0 * r + m_1_1 * g + m_1_2 * b; float zr = m_2_0 * r + m_2_1 * g + m_2_2 * b; float fx = xr > LAB_EPSILONf ? _cbrtf (xr) : (LAB_KAPPAf * xr + 16.0f) / 116.0f; float fy = yr > LAB_EPSILONf ? _cbrtf (yr) : (LAB_KAPPAf * yr + 16.0f) / 116.0f; float fz = zr > LAB_EPSILONf ? _cbrtf (zr) : (LAB_KAPPAf * zr + 16.0f) / 116.0f; float L = 116.0f * fy - 16.0f; float A = 500.0f * (fx - fy); float B = 200.0f * (fy - fz); dst[0] = L; dst[1] = A; dst[2] = B; src += 3; dst += 3; } } static void rgbaf_to_Lf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; long n = samples; while (n--) { float r = src[0]; float g = src[1]; float b = src[2]; float yr = m_1_0 * r + m_1_1 * g + m_1_2 * b; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; src += 4; dst += 1; } } static void rgbaf_to_Labf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float r = src[0]; float g = src[1]; float b = src[2]; float xr = m_0_0 * r + m_0_1 * g + m_0_2 * b; float yr = m_1_0 * r + m_1_1 * g + m_1_2 * b; float zr = m_2_0 * r + m_2_1 * g + m_2_2 * b; float fx = xr > LAB_EPSILONf ? _cbrtf (xr) : (LAB_KAPPAf * xr + 16.0f) / 116.0f; float fy = yr > LAB_EPSILONf ? _cbrtf (yr) : (LAB_KAPPAf * yr + 16.0f) / 116.0f; float fz = zr > LAB_EPSILONf ? _cbrtf (zr) : (LAB_KAPPAf * zr + 16.0f) / 116.0f; float L = 116.0f * fy - 16.0f; float A = 500.0f * (fx - fy); float B = 200.0f * (fy - fz); dst[0] = L; dst[1] = A; dst[2] = B; src += 4; dst += 3; } } static void rgbaf_to_Labaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long n = samples; while (n--) { float r = src[0]; float g = src[1]; float b = src[2]; float a = src[3]; float xr = m_0_0 * r + m_0_1 * g + m_0_2 * b; float yr = m_1_0 * r + m_1_1 * g + m_1_2 * b; float zr = m_2_0 * r + m_2_1 * g + m_2_2 * b; float fx = xr > LAB_EPSILONf ? _cbrtf (xr) : (LAB_KAPPAf * xr + 16.0f) / 116.0f; float fy = yr > LAB_EPSILONf ? _cbrtf (yr) : (LAB_KAPPAf * yr + 16.0f) / 116.0f; float fz = zr > LAB_EPSILONf ? _cbrtf (zr) : (LAB_KAPPAf * zr + 16.0f) / 116.0f; float L = 116.0f * fy - 16.0f; float A = 500.0f * (fx - fy); float B = 200.0f * (fy - fz); dst[0] = L; dst[1] = A; dst[2] = B; dst[3] = a; src += 4; dst += 4; } } static void Labf_to_Lf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { dst[0] = src[0]; src += 3; dst += 1; } } static void Labaf_to_Lf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { dst[0] = src[0]; src += 4; dst += 1; } } static void Labf_to_rgbf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float L = src[0]; float A = src[1]; float B = src[2]; float fy = (L + 16.0f) / 116.0f; float fx = fy + A / 500.0f; float fz = fy - B / 200.0f; float yr = L > LAB_KAPPAf * LAB_EPSILONf ? cubef (fy) : L / LAB_KAPPAf; float xr = cubef (fx) > LAB_EPSILONf ? cubef (fx) : (fx * 116.0f - 16.0f) / LAB_KAPPAf; float zr = cubef (fz) > LAB_EPSILONf ? cubef (fz) : (fz * 116.0f - 16.0f) / LAB_KAPPAf; float r = m_0_0 * xr + m_0_1 * yr + m_0_2 * zr; float g = m_1_0 * xr + m_1_1 * yr + m_1_2 * zr; float b = m_2_0 * xr + m_2_1 * yr + m_2_2 * zr; dst[0] = r; dst[1] = g; dst[2] = b; src += 3; dst += 3; } } static void Labf_to_rgbaf (const Babl *conversion,float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float L = src[0]; float A = src[1]; float B = src[2]; float fy = (L + 16.0f) / 116.0f; float fx = fy + A / 500.0f; float fz = fy - B / 200.0f; float yr = L > LAB_KAPPAf * LAB_EPSILONf ? cubef (fy) : L / LAB_KAPPAf; float xr = cubef (fx) > LAB_EPSILONf ? cubef (fx) : (fx * 116.0f - 16.0f) / LAB_KAPPAf; float zr = cubef (fz) > LAB_EPSILONf ? cubef (fz) : (fz * 116.0f - 16.0f) / LAB_KAPPAf; float r = m_0_0 * xr + m_0_1 * yr + m_0_2 * zr; float g = m_1_0 * xr + m_1_1 * yr + m_1_2 * zr; float b = m_2_0 * xr + m_2_1 * yr + m_2_2 * zr; dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = 1.0f; src += 3; dst += 4; } } static void Labaf_to_rgbaf (const Babl *conversion, float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); float m_0_0 = space->space.XYZtoRGBf[0] * D50_WHITE_REF_Xf; float m_0_1 = space->space.XYZtoRGBf[1] * D50_WHITE_REF_Yf; float m_0_2 = space->space.XYZtoRGBf[2] * D50_WHITE_REF_Zf; float m_1_0 = space->space.XYZtoRGBf[3] * D50_WHITE_REF_Xf; float m_1_1 = space->space.XYZtoRGBf[4] * D50_WHITE_REF_Yf; float m_1_2 = space->space.XYZtoRGBf[5] * D50_WHITE_REF_Zf; float m_2_0 = space->space.XYZtoRGBf[6] * D50_WHITE_REF_Xf; float m_2_1 = space->space.XYZtoRGBf[7] * D50_WHITE_REF_Yf; float m_2_2 = space->space.XYZtoRGBf[8] * D50_WHITE_REF_Zf; long n = samples; while (n--) { float L = src[0]; float A = src[1]; float B = src[2]; float a = src[3]; float fy = (L + 16.0f) / 116.0f; float fx = fy + A / 500.0f; float fz = fy - B / 200.0f; float yr = L > LAB_KAPPAf * LAB_EPSILONf ? cubef (fy) : L / LAB_KAPPAf; float xr = cubef (fx) > LAB_EPSILONf ? cubef (fx) : (fx * 116.0f - 16.0f) / LAB_KAPPAf; float zr = cubef (fz) > LAB_EPSILONf ? cubef (fz) : (fz * 116.0f - 16.0f) / LAB_KAPPAf; float r = m_0_0 * xr + m_0_1 * yr + m_0_2 * zr; float g = m_1_0 * xr + m_1_1 * yr + m_1_2 * zr; float b = m_2_0 * xr + m_2_1 * yr + m_2_2 * zr; dst[0] = r; dst[1] = g; dst[2] = b; dst[3] = a; src += 4; dst += 4; } } static void Labf_to_Lchabf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float L = src[0]; float A = src[1]; float B = src[2]; float C = sqrtf (A * A + B * B); float H = atan2f (B, A) * DEGREES_PER_RADIANf; // Keep H within the range 0-360 if (H < 0.0f) H += 360.0f; dst[0] = L; dst[1] = C; dst[2] = H; src += 3; dst += 3; } } static void Lchabf_to_Labf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float L = src[0]; float C = src[1]; float H = src[2]; float A = C * cosf (H * RADIANS_PER_DEGREEf); float B = C * sinf (H * RADIANS_PER_DEGREEf); dst[0] = L; dst[1] = A; dst[2] = B; src += 3; dst += 3; } } static void Labaf_to_Lchabaf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float L = src[0]; float A = src[1]; float B = src[2]; float a = src[3]; float C = sqrtf (A * A + B * B); float H = atan2f (B, A) * DEGREES_PER_RADIANf; // Keep H within the range 0-360 if (H < 0.0f) H += 360.0f; dst[0] = L; dst[1] = C; dst[2] = H; dst[3] = a; src += 4; dst += 4; } } static void Lchabaf_to_Labaf (const Babl *conversion, float *src, float *dst, long samples) { long n = samples; while (n--) { float L = src[0]; float C = src[1]; float H = src[2]; float a = src[3]; float A = C * cosf (H * RADIANS_PER_DEGREEf); float B = C * sinf (H * RADIANS_PER_DEGREEf); dst[0] = L; dst[1] = A; dst[2] = B; dst[3] = a; src += 4; dst += 4; } } #if defined(USE_SSE2) /* This is an SSE2 version of Halley's method for approximating the * cube root of an IEEE float implementation. * * The scalar version is as follows: * * static inline float * _cbrt_5f (float x) * { * union { float f; uint32_t i; } u = { x }; * * u.i = u.i / 3 + 709921077; * return u.f; * } * * static inline float * _cbrta_halleyf (float a, float R) * { * float a3 = a * a * a; * float b = a * (a3 + R + R) / (a3 + a3 + R); * return b; * } * * static inline float * _cbrtf (float x) * { * float a; * * a = _cbrt_5f (x); * a = _cbrta_halleyf (a, x); * a = _cbrta_halleyf (a, x); * return a; * } * * The above scalar version seems to have originated from * http://metamerist.com/cbrt/cbrt.htm but that's not accessible * anymore. At present there's a copy in CubeRoot.cpp in the Skia * sources that's licensed under a BSD-style license. There's some * discussion on the implementation at * http://www.voidcn.com/article/p-gpwztojr-wt.html. * * Note that Darktable also has an SSE2 version of the same algorithm, * but uses only a single iteration of Halley's method, which is too * coarse. */ /* Return cube roots of the four single-precision floating point * components of x. */ static inline __m128 _cbrtf_ps_sse2 (__m128 x) { const __m128i magic = _mm_set1_epi32 (709921077); __m128i xi = _mm_castps_si128 (x); __m128 xi_3 = _mm_div_ps (_mm_cvtepi32_ps (xi), _mm_set1_ps (3.0f)); __m128i ai = _mm_add_epi32 (_mm_cvtps_epi32 (xi_3), magic); __m128 a = _mm_castsi128_ps (ai); __m128 a3 = _mm_mul_ps (_mm_mul_ps (a, a), a); __m128 divisor = _mm_add_ps (_mm_add_ps (a3, a3), x); a = _mm_div_ps (_mm_mul_ps (a, _mm_add_ps (a3, _mm_add_ps (x, x))), divisor); a3 = _mm_mul_ps (_mm_mul_ps (a, a), a); divisor = _mm_add_ps (_mm_add_ps (a3, a3), x); a = _mm_div_ps (_mm_mul_ps (a, _mm_add_ps (a3, _mm_add_ps (x, x))), divisor); return a; } static inline __m128 lab_r_to_f_sse2 (__m128 r) { const __m128 epsilon = _mm_set1_ps (LAB_EPSILON); const __m128 kappa = _mm_set1_ps (LAB_KAPPA); const __m128 f_big = _cbrtf_ps_sse2 (r); const __m128 f_small = _mm_div_ps (_mm_add_ps (_mm_mul_ps (kappa, r), _mm_set1_ps (16.0f)), _mm_set1_ps (116.0f)); const __m128 mask = _mm_cmpgt_ps (r, epsilon); const __m128 f = _mm_or_ps (_mm_and_ps (mask, f_big), _mm_andnot_ps (mask, f_small)); return f; } static void Yf_to_Lf_sse2 (const Babl *conversion, const float *src, float *dst, long samples) { long i = 0; long remainder; if (((uintptr_t) src % 16) + ((uintptr_t) dst % 16) == 0) { const long n = (samples / 4) * 4; for ( ; i < n; i += 4) { __m128 Y = _mm_load_ps (src); __m128 fy = lab_r_to_f_sse2 (Y); __m128 L = _mm_sub_ps (_mm_mul_ps (_mm_set1_ps (116.0f), fy), _mm_set1_ps (16.0f)); _mm_store_ps (dst, L); src += 4; dst += 4; } } remainder = samples - i; while (remainder--) { float yr = src[0]; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; src++; dst++; } } static void Yaf_to_Lf_sse2 (const Babl *conversion, const float *src, float *dst, long samples) { long i = 0; long remainder; if (((uintptr_t) src % 16) + ((uintptr_t) dst % 16) == 0) { const long n = (samples / 4) * 4; for ( ; i < n; i += 4) { __m128 YaYa0 = _mm_load_ps (src); __m128 YaYa1 = _mm_load_ps (src + 4); __m128 Y = _mm_shuffle_ps (YaYa0, YaYa1, _MM_SHUFFLE (2, 0, 2, 0)); __m128 fy = lab_r_to_f_sse2 (Y); __m128 L = _mm_sub_ps (_mm_mul_ps (_mm_set1_ps (116.0f), fy), _mm_set1_ps (16.0f)); _mm_store_ps (dst, L); src += 8; dst += 4; } } remainder = samples - i; while (remainder--) { float yr = src[0]; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; src += 2; dst += 1; } } static void rgbaf_to_Lf_sse2 (const Babl *conversion, const float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); const float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; const float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; const float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; long i = 0; long remainder; if (((uintptr_t) src % 16) + ((uintptr_t) dst % 16) == 0) { const long n = (samples / 4) * 4; const __m128 m_1_0_v = _mm_set1_ps (m_1_0); const __m128 m_1_1_v = _mm_set1_ps (m_1_1); const __m128 m_1_2_v = _mm_set1_ps (m_1_2); for ( ; i < n; i += 4) { __m128 rgba0 = _mm_load_ps (src); __m128 rgba1 = _mm_load_ps (src + 4); __m128 rgba2 = _mm_load_ps (src + 8); __m128 rgba3 = _mm_load_ps (src + 12); __m128 r = rgba0; __m128 g = rgba1; __m128 b = rgba2; __m128 a = rgba3; _MM_TRANSPOSE4_PS (r, g, b, a); { __m128 yr = _mm_add_ps (_mm_add_ps (_mm_mul_ps (m_1_0_v, r), _mm_mul_ps (m_1_1_v, g)), _mm_mul_ps (m_1_2_v, b)); __m128 fy = lab_r_to_f_sse2 (yr); __m128 L = _mm_sub_ps (_mm_mul_ps (_mm_set1_ps (116.0f), fy), _mm_set1_ps (16.0f)); _mm_store_ps (dst, L); } src += 16; dst += 4; } } remainder = samples - i; while (remainder--) { float r = src[0]; float g = src[1]; float b = src[2]; float yr = m_1_0 * r + m_1_1 * g + m_1_2 * b; float L = yr > LAB_EPSILONf ? 116.0f * _cbrtf (yr) - 16 : LAB_KAPPAf * yr; dst[0] = L; src += 4; dst += 1; } } static void rgbaf_to_Labaf_sse2 (const Babl *conversion, const float *src, float *dst, long samples) { const Babl *space = babl_conversion_get_source_space (conversion); const float m_0_0 = space->space.RGBtoXYZf[0] / D50_WHITE_REF_Xf; const float m_0_1 = space->space.RGBtoXYZf[1] / D50_WHITE_REF_Xf; const float m_0_2 = space->space.RGBtoXYZf[2] / D50_WHITE_REF_Xf; const float m_1_0 = space->space.RGBtoXYZf[3] / D50_WHITE_REF_Yf; const float m_1_1 = space->space.RGBtoXYZf[4] / D50_WHITE_REF_Yf; const float m_1_2 = space->space.RGBtoXYZf[5] / D50_WHITE_REF_Yf; const float m_2_0 = space->space.RGBtoXYZf[6] / D50_WHITE_REF_Zf; const float m_2_1 = space->space.RGBtoXYZf[7] / D50_WHITE_REF_Zf; const float m_2_2 = space->space.RGBtoXYZf[8] / D50_WHITE_REF_Zf; long i = 0; long remainder; if (((uintptr_t) src % 16) + ((uintptr_t) dst % 16) == 0) { const long n = (samples / 4) * 4; const __m128 m_0_0_v = _mm_set1_ps (m_0_0); const __m128 m_0_1_v = _mm_set1_ps (m_0_1); const __m128 m_0_2_v = _mm_set1_ps (m_0_2); const __m128 m_1_0_v = _mm_set1_ps (m_1_0); const __m128 m_1_1_v = _mm_set1_ps (m_1_1); const __m128 m_1_2_v = _mm_set1_ps (m_1_2); const __m128 m_2_0_v = _mm_set1_ps (m_2_0); const __m128 m_2_1_v = _mm_set1_ps (m_2_1); const __m128 m_2_2_v = _mm_set1_ps (m_2_2); for ( ; i < n; i += 4) { __m128 Laba0; __m128 Laba1; __m128 Laba2; __m128 Laba3; __m128 rgba0 = _mm_load_ps (src); __m128 rgba1 = _mm_load_ps (src + 4); __m128 rgba2 = _mm_load_ps (src + 8); __m128 rgba3 = _mm_load_ps (src + 12); __m128 r = rgba0; __m128 g = rgba1; __m128 b = rgba2; __m128 a = rgba3; _MM_TRANSPOSE4_PS (r, g, b, a); { __m128 xr = _mm_add_ps (_mm_add_ps (_mm_mul_ps (m_0_0_v, r), _mm_mul_ps (m_0_1_v, g)), _mm_mul_ps (m_0_2_v, b)); __m128 yr = _mm_add_ps (_mm_add_ps (_mm_mul_ps (m_1_0_v, r), _mm_mul_ps (m_1_1_v, g)), _mm_mul_ps (m_1_2_v, b)); __m128 zr = _mm_add_ps (_mm_add_ps (_mm_mul_ps (m_2_0_v, r), _mm_mul_ps (m_2_1_v, g)), _mm_mul_ps (m_2_2_v, b)); __m128 fx = lab_r_to_f_sse2 (xr); __m128 fy = lab_r_to_f_sse2 (yr); __m128 fz = lab_r_to_f_sse2 (zr); __m128 L = _mm_sub_ps (_mm_mul_ps (_mm_set1_ps (116.0f), fy), _mm_set1_ps (16.0f)); __m128 A = _mm_mul_ps (_mm_set1_ps (500.0f), _mm_sub_ps (fx, fy)); __m128 B = _mm_mul_ps (_mm_set1_ps (200.0f), _mm_sub_ps (fy, fz)); Laba0 = L; Laba1 = A; Laba2 = B; Laba3 = a; _MM_TRANSPOSE4_PS (Laba0, Laba1, Laba2, Laba3); } _mm_store_ps (dst, Laba0); _mm_store_ps (dst + 4, Laba1); _mm_store_ps (dst + 8, Laba2); _mm_store_ps (dst + 12, Laba3); src += 16; dst += 16; } } remainder = samples - i; while (remainder--) { float r = src[0]; float g = src[1]; float b = src[2]; float a = src[3]; float xr = m_0_0 * r + m_0_1 * g + m_0_2 * b; float yr = m_1_0 * r + m_1_1 * g + m_1_2 * b; float zr = m_2_0 * r + m_2_1 * g + m_2_2 * b; float fx = xr > LAB_EPSILONf ? _cbrtf (xr) : (LAB_KAPPAf * xr + 16.0f) / 116.0f; float fy = yr > LAB_EPSILONf ? _cbrtf (yr) : (LAB_KAPPAf * yr + 16.0f) / 116.0f; float fz = zr > LAB_EPSILONf ? _cbrtf (zr) : (LAB_KAPPAf * zr + 16.0f) / 116.0f; float L = 116.0f * fy - 16.0f; float A = 500.0f * (fx - fy); float B = 200.0f * (fy - fz); dst[0] = L; dst[1] = A; dst[2] = B; dst[3] = a; src += 4; dst += 4; } } #endif /* defined(USE_SSE2) */ static void conversions (void) { /* babl_model */ babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE Lab"), "linear", rgba_to_lab, NULL ); babl_conversion_new ( babl_model ("CIE Lab"), babl_model ("RGBA"), "linear", lab_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE Lab alpha"), "linear", rgba_to_laba, NULL ); babl_conversion_new ( babl_model ("CIE Lab alpha"), babl_model ("RGBA"), "linear", laba_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE LCH(ab)"), "linear", rgba_to_lchab, NULL ); babl_conversion_new ( babl_model ("CIE LCH(ab)"), babl_model ("RGBA"), "linear", lchab_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE LCH(ab) alpha"), "linear", rgba_to_lchaba, NULL ); babl_conversion_new ( babl_model ("CIE LCH(ab) alpha"), babl_model ("RGBA"), "linear", lchaba_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE XYZ"), "linear", rgba_to_xyz, NULL ); babl_conversion_new ( babl_model ("CIE XYZ"), babl_model ("RGBA"), "linear", xyz_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE XYZ alpha"), "linear", rgba_to_xyza, NULL ); babl_conversion_new ( babl_model ("CIE XYZ alpha"), babl_model ("RGBA"), "linear", xyza_to_rgba, NULL ); /* CIE xyY */ babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE xyY"), "linear", rgba_to_xyY, NULL ); babl_conversion_new ( babl_model ("CIE xyY"), babl_model ("RGBA"), "linear", xyY_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE xyY alpha"), "linear", rgba_to_xyYa, NULL ); babl_conversion_new ( babl_model ("CIE xyY alpha"), babl_model ("RGBA"), "linear", xyYa_to_rgba, NULL ); /* CIE 1976 UCS */ babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE Yuv"), "linear", rgba_to_Yuv, NULL ); babl_conversion_new ( babl_model ("CIE Yuv"), babl_model ("RGBA"), "linear", Yuv_to_rgba, NULL ); babl_conversion_new ( babl_model ("RGBA"), babl_model ("CIE Yuv alpha"), "linear", rgba_to_Yuva, NULL ); babl_conversion_new ( babl_model ("CIE Yuv alpha"), babl_model ("RGBA"), "linear", Yuva_to_rgba, NULL ); /* babl_format */ babl_conversion_new ( babl_format ("RGB float"), babl_format ("CIE Lab float"), "linear", rgbf_to_Labf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE Lab float"), "linear", rgbaf_to_Labf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE Lab alpha float"), "linear", rgbaf_to_Labaf, NULL ); babl_conversion_new ( babl_format ("CIE Lab float"), babl_format ("RGB float"), "linear", Labf_to_rgbf, NULL ); babl_conversion_new ( babl_format ("CIE Lab float"), babl_format ("RGBA float"), "linear", Labf_to_rgbaf, NULL ); babl_conversion_new ( babl_format ("CIE Lab alpha float"), babl_format ("RGBA float"), "linear", Labaf_to_rgbaf, NULL ); babl_conversion_new ( babl_format ("Y float"), babl_format ("CIE L float"), "linear", Yf_to_Lf, NULL ); babl_conversion_new ( babl_format ("YA float"), babl_format ("CIE L float"), "linear", Yaf_to_Lf, NULL ); babl_conversion_new ( babl_format ("YA float"), babl_format ("CIE L alpha float"), "linear", Yaf_to_Laf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE L float"), "linear", rgbaf_to_Lf, NULL ); babl_conversion_new ( babl_format ("CIE Lab float"), babl_format ("CIE L float"), "linear", Labf_to_Lf, NULL ); babl_conversion_new ( babl_format ("CIE Lab alpha float"), babl_format ("CIE L float"), "linear", Labaf_to_Lf, NULL ); babl_conversion_new ( babl_format ("CIE Lab float"), babl_format ("CIE LCH(ab) float"), "linear", Labf_to_Lchabf, NULL ); babl_conversion_new ( babl_format ("CIE LCH(ab) float"), babl_format ("CIE Lab float"), "linear", Lchabf_to_Labf, NULL ); babl_conversion_new ( babl_format ("CIE Lab alpha float"), babl_format ("CIE LCH(ab) alpha float"), "linear", Labaf_to_Lchabaf, NULL ); babl_conversion_new ( babl_format ("CIE LCH(ab) alpha float"), babl_format ("CIE Lab alpha float"), "linear", Lchabaf_to_Labaf, NULL ); /* CIE xyY */ babl_conversion_new ( babl_format ("RGB float"), babl_format ("CIE xyY float"), "linear", rgbf_to_xyYf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE xyY alpha float"), "linear", rgbaf_to_xyYaf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE xyY float"), "linear", rgbaf_to_xyYf, NULL ); babl_conversion_new ( babl_format ("CIE xyY float"), babl_format ("RGB float"), "linear", xyYf_to_rgbf, NULL ); babl_conversion_new ( babl_format ("CIE xyY float"), babl_format ("RGBA float"), "linear", xyYf_to_rgbaf, NULL ); babl_conversion_new ( babl_format ("CIE xyY alpha float"), babl_format ("RGBA float"), "linear", xyYaf_to_rgbaf, NULL ); /* CIE 1976 UCS */ babl_conversion_new ( babl_format ("RGB float"), babl_format ("CIE Yuv float"), "linear", rgbf_to_Yuvf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE Yuv alpha float"), "linear", rgbaf_to_Yuvaf, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE Yuv float"), "linear", rgbaf_to_Yuvf, NULL ); babl_conversion_new ( babl_format ("CIE Yuv float"), babl_format ("RGB float"), "linear", Yuvf_to_rgbf, NULL ); babl_conversion_new ( babl_format ("CIE Yuv float"), babl_format ("RGBA float"), "linear", Yuvf_to_rgbaf, NULL ); babl_conversion_new ( babl_format ("CIE Yuv alpha float"), babl_format ("RGBA float"), "linear", Yuvaf_to_rgbaf, NULL ); #if defined(USE_SSE2) if (babl_cpu_accel_get_support () & BABL_CPU_ACCEL_X86_SSE2) { babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE Lab alpha float"), "linear", rgbaf_to_Labaf_sse2, NULL ); babl_conversion_new ( babl_format ("Y float"), babl_format ("CIE L float"), "linear", Yf_to_Lf_sse2, NULL ); babl_conversion_new ( babl_format ("YA float"), babl_format ("CIE L float"), "linear", Yaf_to_Lf_sse2, NULL ); babl_conversion_new ( babl_format ("RGBA float"), babl_format ("CIE L float"), "linear", rgbaf_to_Lf_sse2, NULL ); } #endif /* defined(USE_SSE2) */ rgbcie_init (); } static void formats (void) { babl_format_new ( "name", "CIE Lab float", babl_model ("CIE Lab"), babl_type ("float"), babl_component ("CIE L"), babl_component ("CIE a"), babl_component ("CIE b"), NULL); babl_format_new ( "name", "CIE XYZ float", babl_model ("CIE XYZ"), babl_type ("float"), babl_component ("CIE X"), babl_component ("CIE Y"), babl_component ("CIE Z"), NULL); babl_format_new ( "name", "CIE XYZ alpha float", babl_model ("CIE XYZ"), babl_type ("float"), babl_component ("CIE X"), babl_component ("CIE Y"), babl_component ("CIE Z"), babl_component ("A"), NULL); babl_format_new ( "name", "CIE Lab alpha float", babl_model ("CIE Lab alpha"), babl_type ("float"), babl_component ("CIE L"), babl_component ("CIE a"), babl_component ("CIE b"), babl_component ("A"), NULL); babl_format_new ( "name", "CIE LCH(ab) float", babl_model ("CIE LCH(ab)"), babl_type ("float"), babl_component ("CIE L"), babl_component ("CIE C(ab)"), babl_component ("CIE H(ab)"), NULL); babl_format_new ( "name", "CIE LCH(ab) alpha float", babl_model ("CIE LCH(ab) alpha"), babl_type ("float"), babl_component ("CIE L"), babl_component ("CIE C(ab)"), babl_component ("CIE H(ab)"), babl_component ("A"), NULL); babl_format_new ( "name", "CIE L float", babl_model ("CIE Lab"), babl_type ("float"), babl_component ("CIE L"), NULL); babl_format_new ( "name", "CIE L alpha float", babl_model ("CIE Lab alpha"), babl_type ("float"), babl_component ("CIE L"), babl_component ("A"), NULL); babl_format_new ( "name", "CIE Lab u8", babl_model ("CIE Lab"), babl_type ("CIE u8 L"), babl_component ("CIE L"), babl_type ("CIE u8 ab"), babl_component ("CIE a"), babl_type ("CIE u8 ab"), babl_component ("CIE b"), NULL); babl_format_new ( "name", "CIE Lab alpha u8", babl_model ("CIE Lab alpha"), babl_type ("CIE u8 L"), babl_component ("CIE L"), babl_type ("CIE u8 ab"), babl_component ("CIE a"), babl_type ("CIE u8 ab"), babl_component ("CIE b"), babl_type ("u8"), babl_component ("A"), NULL); babl_format_new ( "name", "CIE Lab u16", babl_model ("CIE Lab"), babl_type ("CIE u16 L"), babl_component ("CIE L"), babl_type ("CIE u16 ab"), babl_component ("CIE a"), babl_type ("CIE u16 ab"), babl_component ("CIE b"), NULL); babl_format_new ( "name", "CIE Lab alpha u16", babl_model ("CIE Lab alpha"), babl_type ("CIE u16 L"), babl_component ("CIE L"), babl_type ("CIE u16 ab"), babl_component ("CIE a"), babl_type ("CIE u16 ab"), babl_component ("CIE b"), babl_type ("u16"), babl_component ("A"), NULL); babl_format_new ( "name", "CIE xyY float", babl_model ("CIE xyY"), babl_type ("float"), babl_component ("CIE x"), babl_component ("CIE y"), babl_component ("CIE Y"), NULL); babl_format_new ( "name", "CIE xyY alpha float", babl_model ("CIE xyY alpha"), babl_type ("float"), babl_component ("CIE x"), babl_component ("CIE y"), babl_component ("CIE Y"), babl_component ("A"), NULL); /* CIE 1976 UCS */ babl_format_new ( "name", "CIE Yuv float", babl_model ("CIE Yuv"), babl_type ("float"), babl_component ("CIE Y"), babl_component ("CIE u"), babl_component ("CIE v"), NULL); babl_format_new ( "name", "CIE Yuv alpha float", babl_model ("CIE Yuv alpha"), babl_type ("float"), babl_component ("CIE Y"), babl_component ("CIE u"), babl_component ("CIE v"), babl_component ("A"), NULL); } /******** end floating point RGB/CIE color space conversions **********/ /******** begin integer RGB/CIE color space conversions **************/ static inline void convert_double_u8_scaled (const Babl *conversion, double min_val, double max_val, unsigned char min, unsigned char max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { double dval = *(double *) src; unsigned char u8val; if (dval < min_val) u8val = min; else if (dval > max_val) u8val = max; else u8val = rint ((dval - min_val) / (max_val - min_val) * (max - min) + min); *(unsigned char *) dst = u8val; src += src_pitch; dst += dst_pitch; } } static inline void convert_u8_double_scaled (const Babl *conversion, double min_val, double max_val, unsigned char min, unsigned char max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { int u8val = *(unsigned char *) src; double dval; if (u8val < min) dval = min_val; else if (u8val > max) dval = max_val; else dval = (u8val - min) / (double) (max - min) * (max_val - min_val) + min_val; (*(double *) dst) = dval; dst += dst_pitch; src += src_pitch; } } #define MAKE_CONVERSIONS(name, min_val, max_val, min, max) \ static void \ convert_ ## name ## _double (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_u8_double_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } \ static void \ convert_double_ ## name (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_double_u8_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } /* source ICC.1:2004-10 */ MAKE_CONVERSIONS (u8_l, 0.0, 100.0, 0x00, 0xff) MAKE_CONVERSIONS (u8_ab, -128.0, 127.0, 0x00, 0xff) #undef MAKE_CONVERSIONS static inline void convert_float_u8_scaled (const Babl *conversion, float min_val, float max_val, unsigned char min, unsigned char max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { float dval = *(float *) src; unsigned char u8val; if (dval < min_val) u8val = min; else if (dval > max_val) u8val = max; else u8val = rint ((dval - min_val) / (max_val - min_val) * (max - min) + min); *(unsigned char *) dst = u8val; src += src_pitch; dst += dst_pitch; } } static inline void convert_u8_float_scaled (const Babl *conversion, float min_val, float max_val, unsigned char min, unsigned char max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { int u8val = *(unsigned char *) src; float dval; if (u8val < min) dval = min_val; else if (u8val > max) dval = max_val; else dval = (u8val - min) / (float) (max - min) * (max_val - min_val) + min_val; (*(float *) dst) = dval; dst += dst_pitch; src += src_pitch; } } #define MAKE_CONVERSIONS(name, min_val, max_val, min, max) \ static void \ convert_ ## name ## _float (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_u8_float_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } \ static void \ convert_float_ ## name (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_float_u8_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } /* source ICC.1:2004-10 */ MAKE_CONVERSIONS (u8_l, 0.0, 100.0, 0x00, 0xff) MAKE_CONVERSIONS (u8_ab, -128.0, 127.0, 0x00, 0xff) #undef MAKE_CONVERSIONS static void types_u8 (void) { babl_type_new ( "CIE u8 L", "integer", "unsigned", "bits", 8, "min_val", 0.0, "max_val", 100.0, NULL ); babl_type_new ( "CIE u8 ab", "integer", "unsigned", "bits", 8, "min_val", -128.0, "max_val", 127.0, NULL ); babl_conversion_new ( babl_type ("CIE u8 L"), babl_type ("double"), "plane", convert_u8_l_double, NULL ); babl_conversion_new ( babl_type ("double"), babl_type ("CIE u8 L"), "plane", convert_double_u8_l, NULL ); babl_conversion_new ( babl_type ("CIE u8 ab"), babl_type ("double"), "plane", convert_u8_ab_double, NULL ); babl_conversion_new ( babl_type ("double"), babl_type ("CIE u8 ab"), "plane", convert_double_u8_ab, NULL ); babl_conversion_new ( babl_type ("CIE u8 L"), babl_type ("float"), "plane", convert_u8_l_float, NULL ); babl_conversion_new ( babl_type ("float"), babl_type ("CIE u8 L"), "plane", convert_float_u8_l, NULL ); babl_conversion_new ( babl_type ("CIE u8 ab"), babl_type ("float"), "plane", convert_u8_ab_float, NULL ); babl_conversion_new ( babl_type ("float"), babl_type ("CIE u8 ab"), "plane", convert_float_u8_ab, NULL ); } static inline void convert_double_u16_scaled (const Babl *conversion, double min_val, double max_val, unsigned short min, unsigned short max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { double dval = *(double *) src; unsigned short u16val; if (dval < min_val) u16val = min; else if (dval > max_val) u16val = max; else u16val = rint ((dval - min_val) / (max_val - min_val) * (max - min) + min); *(unsigned short *) dst = u16val; dst += dst_pitch; src += src_pitch; } } static inline void convert_u16_double_scaled (const Babl *conversion, double min_val, double max_val, unsigned short min, unsigned short max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { int u16val = *(unsigned short *) src; double dval; if (u16val < min) dval = min_val; else if (u16val > max) dval = max_val; else dval = (u16val - min) / (double) (max - min) * (max_val - min_val) + min_val; (*(double *) dst) = dval; dst += dst_pitch; src += src_pitch; } } #define MAKE_CONVERSIONS(name, min_val, max_val, min, max) \ static void \ convert_ ## name ## _double (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_u16_double_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } \ static void \ convert_double_ ## name (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_double_u16_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } MAKE_CONVERSIONS (u16_l, 0.0, 100.0, 0x00, 0xffff) MAKE_CONVERSIONS (u16_ab, -128.0, 127.0, 0x00, 0xffff) #undef MAKE_CONVERSIONS static inline void convert_float_u16_scaled (const Babl *conversion, float min_val, float max_val, unsigned short min, unsigned short max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { float dval = *(float *) src; unsigned short u16val; if (dval < min_val) u16val = min; else if (dval > max_val) u16val = max; else u16val = rint ((dval - min_val) / (max_val - min_val) * (max - min) + min); *(unsigned short *) dst = u16val; dst += dst_pitch; src += src_pitch; } } static inline void convert_u16_float_scaled (const Babl *conversion, float min_val, float max_val, unsigned short min, unsigned short max, char *src, char *dst, int src_pitch, int dst_pitch, long n) { while (n--) { int u16val = *(unsigned short *) src; float dval; if (u16val < min) dval = min_val; else if (u16val > max) dval = max_val; else dval = (u16val - min) / (float) (max - min) * (max_val - min_val) + min_val; (*(float *) dst) = dval; dst += dst_pitch; src += src_pitch; } } #define MAKE_CONVERSIONS(name, min_val, max_val, min, max) \ static void \ convert_ ## name ## _float (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_u16_float_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } \ static void \ convert_float_ ## name (const Babl *c, char *src, \ char *dst, \ int src_pitch, \ int dst_pitch, \ long n) \ { \ convert_float_u16_scaled (c, min_val, max_val, min, max, \ src, dst, src_pitch, dst_pitch, n); \ } MAKE_CONVERSIONS (u16_l, 0.0, 100.0, 0x00, 0xffff) MAKE_CONVERSIONS (u16_ab, -128.0, 127.0, 0x00, 0xffff) #undef MAKE_CONVERSIONS static void types_u16 (void) { babl_type_new ( "CIE u16 L", "integer", "unsigned", "bits", 16, "min_val", 0.0, "max_val", 100.0, NULL ); babl_type_new ( "CIE u16 ab", "integer", "unsigned", "bits", 16, "min_val", -128.0, "max_val", 127.0, NULL ); babl_conversion_new ( babl_type ("CIE u16 L"), babl_type ("double"), "plane", convert_u16_l_double, NULL ); babl_conversion_new ( babl_type ("double"), babl_type ("CIE u16 L"), "plane", convert_double_u16_l, NULL ); babl_conversion_new ( babl_type ("CIE u16 ab"), babl_type ("double"), "plane", convert_u16_ab_double, NULL ); babl_conversion_new ( babl_type ("double"), babl_type ("CIE u16 ab"), "plane", convert_double_u16_ab, NULL ); babl_conversion_new ( babl_type ("CIE u16 L"), babl_type ("float"), "plane", convert_u16_l_float, NULL ); babl_conversion_new ( babl_type ("float"), babl_type ("CIE u16 L"), "plane", convert_float_u16_l, NULL ); babl_conversion_new ( babl_type ("CIE u16 ab"), babl_type ("float"), "plane", convert_u16_ab_float, NULL ); babl_conversion_new ( babl_type ("float"), babl_type ("CIE u16 ab"), "plane", convert_float_u16_ab, NULL ); } static void types (void) { types_u8 (); types_u16 (); } /******** end integer RGB/CIE color space conversions ****************/ static void rgbxyzrgb_init (void) { } static void rgbcie_init (void) { static int initialized = 0; if (!initialized) { rgbxyzrgb_init (); initialized = 1; } }