LCOV - code coverage report
Current view: top level - gfx/qcms - transform.c (source / functions) Hit Total Coverage
Test: output.info Lines: 190 637 29.8 %
Date: 2017-07-14 16:53:18 Functions: 17 31 54.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim: set ts=8 sw=8 noexpandtab: */
       2             : //  qcms
       3             : //  Copyright (C) 2009 Mozilla Corporation
       4             : //  Copyright (C) 1998-2007 Marti Maria
       5             : //
       6             : // Permission is hereby granted, free of charge, to any person obtaining 
       7             : // a copy of this software and associated documentation files (the "Software"), 
       8             : // to deal in the Software without restriction, including without limitation 
       9             : // the rights to use, copy, modify, merge, publish, distribute, sublicense, 
      10             : // and/or sell copies of the Software, and to permit persons to whom the Software 
      11             : // is furnished to do so, subject to the following conditions:
      12             : //
      13             : // The above copyright notice and this permission notice shall be included in 
      14             : // all copies or substantial portions of the Software.
      15             : //
      16             : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
      17             : // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
      18             : // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
      19             : // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
      20             : // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
      21             : // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
      22             : // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      23             : 
      24             : #include <stdlib.h>
      25             : #include <math.h>
      26             : #include <assert.h>
      27             : #include <string.h> //memcpy
      28             : #include "qcmsint.h"
      29             : #include "chain.h"
      30             : #include "matrix.h"
      31             : #include "transform_util.h"
      32             : 
      33             : /* for MSVC, GCC, Intel, and Sun compilers */
      34             : #if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_AMD64) || defined(__x86_64__) || defined(__x86_64)
      35             : #define X86
      36             : #endif /* _M_IX86 || __i386__ || __i386 || _M_AMD64 || __x86_64__ || __x86_64 */
      37             : 
      38             : /**
      39             :  * AltiVec detection for PowerPC CPUs
      40             :  * In case we have a method of detecting do the runtime detection.
      41             :  * Otherwise statically choose the AltiVec path in case the compiler
      42             :  * was told to build with AltiVec support.
      43             :  */
      44             : #if (defined(__POWERPC__) || defined(__powerpc__))
      45             : #if defined(__linux__)
      46             : #include <unistd.h>
      47             : #include <fcntl.h>
      48             : #include <stdio.h>
      49             : #include <elf.h>
      50             : #include <linux/auxvec.h>
      51             : #include <asm/cputable.h>
      52             : #include <link.h>
      53             : 
      54             : static inline qcms_bool have_altivec() {
      55             :         static int available = -1;
      56             :         int new_avail = 0;
      57             :         ElfW(auxv_t) auxv;
      58             :         ssize_t count;
      59             :         int fd, i;
      60             : 
      61             :         if (available != -1)
      62             :                 return (available != 0 ? true : false);
      63             : 
      64             :         fd = open("/proc/self/auxv", O_RDONLY);
      65             :         if (fd < 0)
      66             :                 goto out;
      67             :         do {
      68             :                 count = read(fd, &auxv, sizeof(auxv));
      69             :                 if (count < 0)
      70             :                         goto out_close;
      71             : 
      72             :                 if (auxv.a_type == AT_HWCAP) {
      73             :                         new_avail = !!(auxv.a_un.a_val & PPC_FEATURE_HAS_ALTIVEC);
      74             :                         goto out_close;
      75             :                 }
      76             :         } while (auxv.a_type != AT_NULL);
      77             : 
      78             : out_close:
      79             :         close(fd);
      80             : out:
      81             :         available = new_avail;
      82             :         return (available != 0 ? true : false);
      83             : }
      84             : #elif defined(__APPLE__) && defined(__MACH__)
      85             : #include <sys/sysctl.h>
      86             : 
      87             : /**
      88             :  * rip-off from ffmpeg AltiVec detection code.
      89             :  * this code also appears on Apple's AltiVec pages.
      90             :  */
      91             : static inline qcms_bool have_altivec() {
      92             :         int sels[2] = {CTL_HW, HW_VECTORUNIT};
      93             :         static int available = -1;
      94             :         size_t len = sizeof(available);
      95             :         int err;
      96             : 
      97             :         if (available != -1)
      98             :                 return (available != 0 ? true : false);
      99             : 
     100             :         err = sysctl(sels, 2, &available, &len, NULL, 0);
     101             : 
     102             :         if (err == 0)
     103             :                 if (available != 0)
     104             :                         return true;
     105             : 
     106             :         return false;
     107             : }
     108             : #elif defined(__ALTIVEC__) || defined(__APPLE_ALTIVEC__)
     109             : #define have_altivec() true
     110             : #else
     111             : #define have_altivec() false
     112             : #endif
     113             : #endif // (defined(__POWERPC__) || defined(__powerpc__))
     114             : 
     115             : // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
     116             : // This is just an approximation, I am not handling all the non-linear
     117             : // aspects of the RGB to XYZ process, and assumming that the gamma correction
     118             : // has transitive property in the tranformation chain.
     119             : //
     120             : // the alghoritm:
     121             : //
     122             : //            - First I build the absolute conversion matrix using
     123             : //              primaries in XYZ. This matrix is next inverted
     124             : //            - Then I eval the source white point across this matrix
     125             : //              obtaining the coeficients of the transformation
     126             : //            - Then, I apply these coeficients to the original matrix
     127          14 : static struct matrix build_RGB_to_XYZ_transfer_matrix(qcms_CIE_xyY white, qcms_CIE_xyYTRIPLE primrs)
     128             : {
     129             :         struct matrix primaries;
     130             :         struct matrix primaries_invert;
     131             :         struct matrix result;
     132             :         struct vector white_point;
     133             :         struct vector coefs;
     134             : 
     135             :         double xn, yn;
     136             :         double xr, yr;
     137             :         double xg, yg;
     138             :         double xb, yb;
     139             : 
     140          14 :         xn = white.x;
     141          14 :         yn = white.y;
     142             : 
     143          14 :         if (yn == 0.0)
     144           0 :                 return matrix_invalid();
     145             : 
     146          14 :         xr = primrs.red.x;
     147          14 :         yr = primrs.red.y;
     148          14 :         xg = primrs.green.x;
     149          14 :         yg = primrs.green.y;
     150          14 :         xb = primrs.blue.x;
     151          14 :         yb = primrs.blue.y;
     152             : 
     153          14 :         primaries.m[0][0] = xr;
     154          14 :         primaries.m[0][1] = xg;
     155          14 :         primaries.m[0][2] = xb;
     156             : 
     157          14 :         primaries.m[1][0] = yr;
     158          14 :         primaries.m[1][1] = yg;
     159          14 :         primaries.m[1][2] = yb;
     160             : 
     161          14 :         primaries.m[2][0] = 1 - xr - yr;
     162          14 :         primaries.m[2][1] = 1 - xg - yg;
     163          14 :         primaries.m[2][2] = 1 - xb - yb;
     164          14 :         primaries.invalid = false;
     165             : 
     166          14 :         white_point.v[0] = xn/yn;
     167          14 :         white_point.v[1] = 1.;
     168          14 :         white_point.v[2] = (1.0-xn-yn)/yn;
     169             : 
     170          14 :         primaries_invert = matrix_invert(primaries);
     171             : 
     172          14 :         coefs = matrix_eval(primaries_invert, white_point);
     173             : 
     174          14 :         result.m[0][0] = coefs.v[0]*xr;
     175          14 :         result.m[0][1] = coefs.v[1]*xg;
     176          14 :         result.m[0][2] = coefs.v[2]*xb;
     177             : 
     178          14 :         result.m[1][0] = coefs.v[0]*yr;
     179          14 :         result.m[1][1] = coefs.v[1]*yg;
     180          14 :         result.m[1][2] = coefs.v[2]*yb;
     181             : 
     182          14 :         result.m[2][0] = coefs.v[0]*(1.-xr-yr);
     183          14 :         result.m[2][1] = coefs.v[1]*(1.-xg-yg);
     184          14 :         result.m[2][2] = coefs.v[2]*(1.-xb-yb);
     185          14 :         result.invalid = primaries_invert.invalid;
     186             : 
     187          14 :         return result;
     188             : }
     189             : 
     190             : struct CIE_XYZ {
     191             :         double X;
     192             :         double Y;
     193             :         double Z;
     194             : };
     195             : 
     196             : /* CIE Illuminant D50 */
     197             : static const struct CIE_XYZ D50_XYZ = {
     198             :         0.9642,
     199             :         1.0000,
     200             :         0.8249
     201             : };
     202             : 
     203             : /* from lcms: xyY2XYZ()
     204             :  * corresponds to argyll: icmYxy2XYZ() */
     205          14 : static struct CIE_XYZ xyY2XYZ(qcms_CIE_xyY source)
     206             : {
     207             :         struct CIE_XYZ dest;
     208          14 :         dest.X = (source.x / source.y) * source.Y;
     209          14 :         dest.Y = source.Y;
     210          14 :         dest.Z = ((1 - source.x - source.y) / source.y) * source.Y;
     211          14 :         return dest;
     212             : }
     213             : 
     214             : /* from lcms: ComputeChromaticAdaption */
     215             : // Compute chromatic adaption matrix using chad as cone matrix
     216             : static struct matrix
     217          14 : compute_chromatic_adaption(struct CIE_XYZ source_white_point,
     218             :                            struct CIE_XYZ dest_white_point,
     219             :                            struct matrix chad)
     220             : {
     221             :         struct matrix chad_inv;
     222             :         struct vector cone_source_XYZ, cone_source_rgb;
     223             :         struct vector cone_dest_XYZ, cone_dest_rgb;
     224             :         struct matrix cone, tmp;
     225             : 
     226          14 :         tmp = chad;
     227          14 :         chad_inv = matrix_invert(tmp);
     228             : 
     229          14 :         cone_source_XYZ.v[0] = source_white_point.X;
     230          14 :         cone_source_XYZ.v[1] = source_white_point.Y;
     231          14 :         cone_source_XYZ.v[2] = source_white_point.Z;
     232             : 
     233          14 :         cone_dest_XYZ.v[0] = dest_white_point.X;
     234          14 :         cone_dest_XYZ.v[1] = dest_white_point.Y;
     235          14 :         cone_dest_XYZ.v[2] = dest_white_point.Z;
     236             : 
     237          14 :         cone_source_rgb = matrix_eval(chad, cone_source_XYZ);
     238          14 :         cone_dest_rgb   = matrix_eval(chad, cone_dest_XYZ);
     239             : 
     240          14 :         cone.m[0][0] = cone_dest_rgb.v[0]/cone_source_rgb.v[0];
     241          14 :         cone.m[0][1] = 0;
     242          14 :         cone.m[0][2] = 0;
     243          14 :         cone.m[1][0] = 0;
     244          14 :         cone.m[1][1] = cone_dest_rgb.v[1]/cone_source_rgb.v[1];
     245          14 :         cone.m[1][2] = 0;
     246          14 :         cone.m[2][0] = 0;
     247          14 :         cone.m[2][1] = 0;
     248          14 :         cone.m[2][2] = cone_dest_rgb.v[2]/cone_source_rgb.v[2];
     249          14 :         cone.invalid = false;
     250             : 
     251             :         // Normalize
     252          14 :         return matrix_multiply(chad_inv, matrix_multiply(cone, chad));
     253             : }
     254             : 
     255             : /* from lcms: cmsAdaptionMatrix */
     256             : // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
     257             : // Bradford is assumed
     258             : static struct matrix
     259          14 : adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumination)
     260             : {
     261          14 :         struct matrix lam_rigg = {{ // Bradford matrix
     262             :                                  {  0.8951f,  0.2664f, -0.1614f },
     263             :                                  { -0.7502f,  1.7135f,  0.0367f },
     264             :                                  {  0.0389f, -0.0685f,  1.0296f }
     265             :                                  }};
     266          14 :         return compute_chromatic_adaption(source_illumination, target_illumination, lam_rigg);
     267             : }
     268             : 
     269             : /* from lcms: cmsAdaptMatrixToD50 */
     270          14 : static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_white_pt)
     271             : {
     272             :         struct CIE_XYZ Dn;
     273             :         struct matrix Bradford;
     274             : 
     275          14 :         if (source_white_pt.y == 0.0)
     276           0 :                 return matrix_invalid();
     277             : 
     278          14 :         Dn = xyY2XYZ(source_white_pt);
     279             : 
     280          14 :         Bradford = adaption_matrix(Dn, D50_XYZ);
     281          14 :         return matrix_multiply(Bradford, r);
     282             : }
     283             : 
     284          11 : qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries)
     285             : {
     286             :         struct matrix colorants;
     287          11 :         colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries);
     288          11 :         colorants = adapt_matrix_to_D50(colorants, white_point);
     289             : 
     290          11 :         if (colorants.invalid)
     291           0 :                 return false;
     292             : 
     293             :         /* note: there's a transpose type of operation going on here */
     294          11 :         profile->redColorant.X = double_to_s15Fixed16Number(colorants.m[0][0]);
     295          11 :         profile->redColorant.Y = double_to_s15Fixed16Number(colorants.m[1][0]);
     296          11 :         profile->redColorant.Z = double_to_s15Fixed16Number(colorants.m[2][0]);
     297             : 
     298          11 :         profile->greenColorant.X = double_to_s15Fixed16Number(colorants.m[0][1]);
     299          11 :         profile->greenColorant.Y = double_to_s15Fixed16Number(colorants.m[1][1]);
     300          11 :         profile->greenColorant.Z = double_to_s15Fixed16Number(colorants.m[2][1]);
     301             : 
     302          11 :         profile->blueColorant.X = double_to_s15Fixed16Number(colorants.m[0][2]);
     303          11 :         profile->blueColorant.Y = double_to_s15Fixed16Number(colorants.m[1][2]);
     304          11 :         profile->blueColorant.Z = double_to_s15Fixed16Number(colorants.m[2][2]);
     305             : 
     306          11 :         return true;
     307             : }
     308             : 
     309           3 : qcms_bool get_rgb_colorants(struct matrix *colorants, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries)
     310             : {
     311           3 :         *colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries);
     312           3 :         *colorants = adapt_matrix_to_D50(*colorants, white_point);
     313             : 
     314           3 :         return (colorants->invalid ? true : false);
     315             : }
     316             : 
     317             : #if 0
     318             : static void qcms_transform_data_rgb_out_pow(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     319             : {
     320             :         int i;
     321             :         float (*mat)[4] = transform->matrix;
     322             :         for (i=0; i<length; i++) {
     323             :                 unsigned char device_r = *src++;
     324             :                 unsigned char device_g = *src++;
     325             :                 unsigned char device_b = *src++;
     326             : 
     327             :                 float linear_r = transform->input_gamma_table_r[device_r];
     328             :                 float linear_g = transform->input_gamma_table_g[device_g];
     329             :                 float linear_b = transform->input_gamma_table_b[device_b];
     330             : 
     331             :                 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
     332             :                 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
     333             :                 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
     334             : 
     335             :                 float out_device_r = pow(out_linear_r, transform->out_gamma_r);
     336             :                 float out_device_g = pow(out_linear_g, transform->out_gamma_g);
     337             :                 float out_device_b = pow(out_linear_b, transform->out_gamma_b);
     338             : 
     339             :                 dest[OUTPUT_R_INDEX] = clamp_u8(255*out_device_r);
     340             :                 dest[OUTPUT_G_INDEX] = clamp_u8(255*out_device_g);
     341             :                 dest[OUTPUT_B_INDEX] = clamp_u8(255*out_device_b);
     342             :                 dest += RGB_OUTPUT_COMPONENTS;
     343             :         }
     344             : }
     345             : #endif
     346             : 
     347           0 : static void qcms_transform_data_gray_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     348             : {
     349             :         unsigned int i;
     350           0 :         for (i = 0; i < length; i++) {
     351             :                 float out_device_r, out_device_g, out_device_b;
     352           0 :                 unsigned char device = *src++;
     353             : 
     354           0 :                 float linear = transform->input_gamma_table_gray[device];
     355             : 
     356           0 :                 out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
     357           0 :                 out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
     358           0 :                 out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
     359             : 
     360           0 :                 dest[OUTPUT_R_INDEX] = clamp_u8(out_device_r*255);
     361           0 :                 dest[OUTPUT_G_INDEX] = clamp_u8(out_device_g*255);
     362           0 :                 dest[OUTPUT_B_INDEX] = clamp_u8(out_device_b*255);
     363           0 :                 dest += RGB_OUTPUT_COMPONENTS;
     364             :         }
     365           0 : }
     366             : 
     367             : /* Alpha is not corrected.
     368             :    A rationale for this is found in Alvy Ray's "Should Alpha Be Nonlinear If
     369             :    RGB Is?" Tech Memo 17 (December 14, 1998).
     370             :         See: ftp://ftp.alvyray.com/Acrobat/17_Nonln.pdf
     371             : */
     372             : 
     373           0 : static void qcms_transform_data_graya_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     374             : {
     375             :         unsigned int i;
     376           0 :         for (i = 0; i < length; i++) {
     377             :                 float out_device_r, out_device_g, out_device_b;
     378           0 :                 unsigned char device = *src++;
     379           0 :                 unsigned char alpha = *src++;
     380             : 
     381           0 :                 float linear = transform->input_gamma_table_gray[device];
     382             : 
     383           0 :                 out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
     384           0 :                 out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
     385           0 :                 out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
     386             : 
     387           0 :                 dest[OUTPUT_R_INDEX] = clamp_u8(out_device_r*255);
     388           0 :                 dest[OUTPUT_G_INDEX] = clamp_u8(out_device_g*255);
     389           0 :                 dest[OUTPUT_B_INDEX] = clamp_u8(out_device_b*255);
     390           0 :                 dest[OUTPUT_A_INDEX] = alpha;
     391           0 :                 dest += RGBA_OUTPUT_COMPONENTS;
     392             :         }
     393           0 : }
     394             : 
     395             : 
     396           0 : static void qcms_transform_data_gray_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     397             : {
     398             :         unsigned int i;
     399           0 :         for (i = 0; i < length; i++) {
     400           0 :                 unsigned char device = *src++;
     401             :                 uint16_t gray;
     402             : 
     403           0 :                 float linear = transform->input_gamma_table_gray[device];
     404             : 
     405             :                 /* we could round here... */
     406           0 :                 gray = linear * PRECACHE_OUTPUT_MAX;
     407             : 
     408           0 :                 dest[OUTPUT_R_INDEX] = transform->output_table_r->data[gray];
     409           0 :                 dest[OUTPUT_G_INDEX] = transform->output_table_g->data[gray];
     410           0 :                 dest[OUTPUT_B_INDEX] = transform->output_table_b->data[gray];
     411           0 :                 dest += RGB_OUTPUT_COMPONENTS;
     412             :         }
     413           0 : }
     414             : 
     415           0 : static void qcms_transform_data_graya_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     416             : {
     417             :         unsigned int i;
     418           0 :         for (i = 0; i < length; i++) {
     419           0 :                 unsigned char device = *src++;
     420           0 :                 unsigned char alpha = *src++;
     421             :                 uint16_t gray;
     422             : 
     423           0 :                 float linear = transform->input_gamma_table_gray[device];
     424             : 
     425             :                 /* we could round here... */
     426           0 :                 gray = linear * PRECACHE_OUTPUT_MAX;
     427             : 
     428           0 :                 dest[OUTPUT_R_INDEX] = transform->output_table_r->data[gray];
     429           0 :                 dest[OUTPUT_G_INDEX] = transform->output_table_g->data[gray];
     430           0 :                 dest[OUTPUT_B_INDEX] = transform->output_table_b->data[gray];
     431           0 :                 dest[OUTPUT_A_INDEX] = alpha;
     432           0 :                 dest += RGBA_OUTPUT_COMPONENTS;
     433             :         }
     434           0 : }
     435             : 
     436           0 : static void qcms_transform_data_rgb_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     437             : {
     438             :         unsigned int i;
     439           0 :         float (*mat)[4] = transform->matrix;
     440           0 :         for (i = 0; i < length; i++) {
     441           0 :                 unsigned char device_r = *src++;
     442           0 :                 unsigned char device_g = *src++;
     443           0 :                 unsigned char device_b = *src++;
     444             :                 uint16_t r, g, b;
     445             : 
     446           0 :                 float linear_r = transform->input_gamma_table_r[device_r];
     447           0 :                 float linear_g = transform->input_gamma_table_g[device_g];
     448           0 :                 float linear_b = transform->input_gamma_table_b[device_b];
     449             : 
     450           0 :                 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
     451           0 :                 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
     452           0 :                 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
     453             : 
     454           0 :                 out_linear_r = clamp_float(out_linear_r);
     455           0 :                 out_linear_g = clamp_float(out_linear_g);
     456           0 :                 out_linear_b = clamp_float(out_linear_b);
     457             : 
     458             :                 /* we could round here... */
     459           0 :                 r = out_linear_r * PRECACHE_OUTPUT_MAX;
     460           0 :                 g = out_linear_g * PRECACHE_OUTPUT_MAX;
     461           0 :                 b = out_linear_b * PRECACHE_OUTPUT_MAX;
     462             : 
     463           0 :                 dest[OUTPUT_R_INDEX] = transform->output_table_r->data[r];
     464           0 :                 dest[OUTPUT_G_INDEX] = transform->output_table_g->data[g];
     465           0 :                 dest[OUTPUT_B_INDEX] = transform->output_table_b->data[b];
     466           0 :                 dest += RGB_OUTPUT_COMPONENTS;
     467             :         }
     468           0 : }
     469             : 
     470           0 : static void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     471             : {
     472             :         unsigned int i;
     473           0 :         float (*mat)[4] = transform->matrix;
     474           0 :         for (i = 0; i < length; i++) {
     475           0 :                 unsigned char device_r = *src++;
     476           0 :                 unsigned char device_g = *src++;
     477           0 :                 unsigned char device_b = *src++;
     478           0 :                 unsigned char alpha = *src++;
     479             :                 uint16_t r, g, b;
     480             : 
     481           0 :                 float linear_r = transform->input_gamma_table_r[device_r];
     482           0 :                 float linear_g = transform->input_gamma_table_g[device_g];
     483           0 :                 float linear_b = transform->input_gamma_table_b[device_b];
     484             : 
     485           0 :                 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
     486           0 :                 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
     487           0 :                 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
     488             : 
     489           0 :                 out_linear_r = clamp_float(out_linear_r);
     490           0 :                 out_linear_g = clamp_float(out_linear_g);
     491           0 :                 out_linear_b = clamp_float(out_linear_b);
     492             : 
     493             :                 /* we could round here... */
     494           0 :                 r = out_linear_r * PRECACHE_OUTPUT_MAX;
     495           0 :                 g = out_linear_g * PRECACHE_OUTPUT_MAX;
     496           0 :                 b = out_linear_b * PRECACHE_OUTPUT_MAX;
     497             : 
     498           0 :                 dest[OUTPUT_R_INDEX] = transform->output_table_r->data[r];
     499           0 :                 dest[OUTPUT_G_INDEX] = transform->output_table_g->data[g];
     500           0 :                 dest[OUTPUT_B_INDEX] = transform->output_table_b->data[b];
     501           0 :                 dest[OUTPUT_A_INDEX] = alpha;
     502           0 :                 dest += RGBA_OUTPUT_COMPONENTS;
     503             :         }
     504           0 : }
     505             : 
     506             : // Not used
     507             : /* 
     508             : static void qcms_transform_data_clut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) {
     509             :         unsigned int i;
     510             :         int xy_len = 1;
     511             :         int x_len = transform->grid_size;
     512             :         int len = x_len * x_len;
     513             :         float* r_table = transform->r_clut;
     514             :         float* g_table = transform->g_clut;
     515             :         float* b_table = transform->b_clut;
     516             :   
     517             :         for (i = 0; i < length; i++) {
     518             :                 unsigned char in_r = *src++;
     519             :                 unsigned char in_g = *src++;
     520             :                 unsigned char in_b = *src++;
     521             :                 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
     522             : 
     523             :                 int x = floorf(linear_r * (transform->grid_size-1));
     524             :                 int y = floorf(linear_g * (transform->grid_size-1));
     525             :                 int z = floorf(linear_b * (transform->grid_size-1));
     526             :                 int x_n = ceilf(linear_r * (transform->grid_size-1));
     527             :                 int y_n = ceilf(linear_g * (transform->grid_size-1));
     528             :                 int z_n = ceilf(linear_b * (transform->grid_size-1));
     529             :                 float x_d = linear_r * (transform->grid_size-1) - x; 
     530             :                 float y_d = linear_g * (transform->grid_size-1) - y;
     531             :                 float z_d = linear_b * (transform->grid_size-1) - z; 
     532             : 
     533             :                 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
     534             :                 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
     535             :                 float r_y1 = lerp(r_x1, r_x2, y_d);
     536             :                 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
     537             :                 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
     538             :                 float r_y2 = lerp(r_x3, r_x4, y_d);
     539             :                 float clut_r = lerp(r_y1, r_y2, z_d);
     540             : 
     541             :                 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
     542             :                 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
     543             :                 float g_y1 = lerp(g_x1, g_x2, y_d);
     544             :                 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
     545             :                 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
     546             :                 float g_y2 = lerp(g_x3, g_x4, y_d);
     547             :                 float clut_g = lerp(g_y1, g_y2, z_d);
     548             : 
     549             :                 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
     550             :                 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
     551             :                 float b_y1 = lerp(b_x1, b_x2, y_d);
     552             :                 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
     553             :                 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
     554             :                 float b_y2 = lerp(b_x3, b_x4, y_d);
     555             :                 float clut_b = lerp(b_y1, b_y2, z_d);
     556             : 
     557             :                 *dest++ = clamp_u8(clut_r*255.0f);
     558             :                 *dest++ = clamp_u8(clut_g*255.0f);
     559             :                 *dest++ = clamp_u8(clut_b*255.0f);
     560             :         }       
     561             : }
     562             : */
     563             : 
     564           0 : static int int_div_ceil(int value, int div) {
     565           0 :         return ((value  + div - 1) / div);
     566             : }
     567             : 
     568             : // Using lcms' tetra interpolation algorithm.
     569           0 : static void qcms_transform_data_tetra_clut_rgba(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) {
     570             :         unsigned int i;
     571           0 :         int xy_len = 1;
     572           0 :         int x_len = transform->grid_size;
     573           0 :         int len = x_len * x_len;
     574           0 :         float* r_table = transform->r_clut;
     575           0 :         float* g_table = transform->g_clut;
     576           0 :         float* b_table = transform->b_clut;
     577             :         float c0_r, c1_r, c2_r, c3_r;
     578             :         float c0_g, c1_g, c2_g, c3_g;
     579             :         float c0_b, c1_b, c2_b, c3_b;
     580             :         float clut_r, clut_g, clut_b;
     581           0 :         for (i = 0; i < length; i++) {
     582           0 :                 unsigned char in_r = *src++;
     583           0 :                 unsigned char in_g = *src++;
     584           0 :                 unsigned char in_b = *src++;
     585           0 :                 unsigned char in_a = *src++;
     586           0 :                 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
     587             : 
     588           0 :                 int x = in_r * (transform->grid_size-1) / 255;
     589           0 :                 int y = in_g * (transform->grid_size-1) / 255;
     590           0 :                 int z = in_b * (transform->grid_size-1) / 255;
     591           0 :                 int x_n = int_div_ceil(in_r * (transform->grid_size-1), 255);
     592           0 :                 int y_n = int_div_ceil(in_g * (transform->grid_size-1), 255);
     593           0 :                 int z_n = int_div_ceil(in_b * (transform->grid_size-1), 255);
     594           0 :                 float rx = linear_r * (transform->grid_size-1) - x; 
     595           0 :                 float ry = linear_g * (transform->grid_size-1) - y;
     596           0 :                 float rz = linear_b * (transform->grid_size-1) - z; 
     597             : 
     598           0 :                 c0_r = CLU(r_table, x, y, z);
     599           0 :                 c0_g = CLU(g_table, x, y, z);
     600           0 :                 c0_b = CLU(b_table, x, y, z);
     601             : 
     602           0 :                 if( rx >= ry ) {
     603           0 :                         if (ry >= rz) { //rx >= ry && ry >= rz
     604           0 :                                 c1_r = CLU(r_table, x_n, y, z) - c0_r;
     605           0 :                                 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
     606           0 :                                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     607           0 :                                 c1_g = CLU(g_table, x_n, y, z) - c0_g;
     608           0 :                                 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
     609           0 :                                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     610           0 :                                 c1_b = CLU(b_table, x_n, y, z) - c0_b;
     611           0 :                                 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
     612           0 :                                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     613             :                         } else { 
     614           0 :                                 if (rx >= rz) { //rx >= rz && rz >= ry
     615           0 :                                         c1_r = CLU(r_table, x_n, y, z) - c0_r;
     616           0 :                                         c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
     617           0 :                                         c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
     618           0 :                                         c1_g = CLU(g_table, x_n, y, z) - c0_g;
     619           0 :                                         c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
     620           0 :                                         c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
     621           0 :                                         c1_b = CLU(b_table, x_n, y, z) - c0_b;
     622           0 :                                         c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
     623           0 :                                         c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
     624             :                                 } else { //rz > rx && rx >= ry
     625           0 :                                         c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
     626           0 :                                         c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
     627           0 :                                         c3_r = CLU(r_table, x, y, z_n) - c0_r;
     628           0 :                                         c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
     629           0 :                                         c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
     630           0 :                                         c3_g = CLU(g_table, x, y, z_n) - c0_g;
     631           0 :                                         c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
     632           0 :                                         c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
     633           0 :                                         c3_b = CLU(b_table, x, y, z_n) - c0_b;
     634             :                                 }
     635             :                         }
     636             :                 } else {
     637           0 :                         if (rx >= rz) { //ry > rx && rx >= rz
     638           0 :                                 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
     639           0 :                                 c2_r = CLU(r_table, x, y_n, z) - c0_r;
     640           0 :                                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     641           0 :                                 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
     642           0 :                                 c2_g = CLU(g_table, x, y_n, z) - c0_g;
     643           0 :                                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     644           0 :                                 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
     645           0 :                                 c2_b = CLU(b_table, x, y_n, z) - c0_b;
     646           0 :                                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     647             :                         } else {
     648           0 :                                 if (ry >= rz) { //ry >= rz && rz > rx 
     649           0 :                                         c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
     650           0 :                                         c2_r = CLU(r_table, x, y_n, z) - c0_r;
     651           0 :                                         c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
     652           0 :                                         c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
     653           0 :                                         c2_g = CLU(g_table, x, y_n, z) - c0_g;
     654           0 :                                         c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
     655           0 :                                         c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
     656           0 :                                         c2_b = CLU(b_table, x, y_n, z) - c0_b;
     657           0 :                                         c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
     658             :                                 } else { //rz > ry && ry > rx
     659           0 :                                         c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
     660           0 :                                         c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
     661           0 :                                         c3_r = CLU(r_table, x, y, z_n) - c0_r;
     662           0 :                                         c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
     663           0 :                                         c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
     664           0 :                                         c3_g = CLU(g_table, x, y, z_n) - c0_g;
     665           0 :                                         c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
     666           0 :                                         c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
     667           0 :                                         c3_b = CLU(b_table, x, y, z_n) - c0_b;
     668             :                                 }
     669             :                         }
     670             :                 }
     671             :                                 
     672           0 :                 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
     673           0 :                 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
     674           0 :                 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
     675             : 
     676           0 :                 dest[OUTPUT_R_INDEX] = clamp_u8(clut_r*255.0f);
     677           0 :                 dest[OUTPUT_G_INDEX] = clamp_u8(clut_g*255.0f);
     678           0 :                 dest[OUTPUT_B_INDEX] = clamp_u8(clut_b*255.0f);
     679           0 :                 dest[OUTPUT_A_INDEX] = in_a;
     680           0 :                 dest += RGBA_OUTPUT_COMPONENTS;
     681             :         }       
     682           0 : }
     683             : 
     684             : // Using lcms' tetra interpolation code.
     685           0 : static void qcms_transform_data_tetra_clut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) {
     686             :         unsigned int i;
     687           0 :         int xy_len = 1;
     688           0 :         int x_len = transform->grid_size;
     689           0 :         int len = x_len * x_len;
     690           0 :         float* r_table = transform->r_clut;
     691           0 :         float* g_table = transform->g_clut;
     692           0 :         float* b_table = transform->b_clut;
     693             :         float c0_r, c1_r, c2_r, c3_r;
     694             :         float c0_g, c1_g, c2_g, c3_g;
     695             :         float c0_b, c1_b, c2_b, c3_b;
     696             :         float clut_r, clut_g, clut_b;
     697           0 :         for (i = 0; i < length; i++) {
     698           0 :                 unsigned char in_r = *src++;
     699           0 :                 unsigned char in_g = *src++;
     700           0 :                 unsigned char in_b = *src++;
     701           0 :                 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
     702             : 
     703           0 :                 int x = in_r * (transform->grid_size-1) / 255;
     704           0 :                 int y = in_g * (transform->grid_size-1) / 255;
     705           0 :                 int z = in_b * (transform->grid_size-1) / 255;
     706           0 :                 int x_n = int_div_ceil(in_r * (transform->grid_size-1), 255);
     707           0 :                 int y_n = int_div_ceil(in_g * (transform->grid_size-1), 255);
     708           0 :                 int z_n = int_div_ceil(in_b * (transform->grid_size-1), 255);
     709           0 :                 float rx = linear_r * (transform->grid_size-1) - x;
     710           0 :                 float ry = linear_g * (transform->grid_size-1) - y;
     711           0 :                 float rz = linear_b * (transform->grid_size-1) - z;
     712             : 
     713           0 :                 c0_r = CLU(r_table, x, y, z);
     714           0 :                 c0_g = CLU(g_table, x, y, z);
     715           0 :                 c0_b = CLU(b_table, x, y, z);
     716             : 
     717           0 :                 if( rx >= ry ) {
     718           0 :                         if (ry >= rz) { //rx >= ry && ry >= rz
     719           0 :                                 c1_r = CLU(r_table, x_n, y, z) - c0_r;
     720           0 :                                 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
     721           0 :                                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     722           0 :                                 c1_g = CLU(g_table, x_n, y, z) - c0_g;
     723           0 :                                 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
     724           0 :                                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     725           0 :                                 c1_b = CLU(b_table, x_n, y, z) - c0_b;
     726           0 :                                 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
     727           0 :                                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     728             :                         } else { 
     729           0 :                                 if (rx >= rz) { //rx >= rz && rz >= ry
     730           0 :                                         c1_r = CLU(r_table, x_n, y, z) - c0_r;
     731           0 :                                         c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
     732           0 :                                         c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
     733           0 :                                         c1_g = CLU(g_table, x_n, y, z) - c0_g;
     734           0 :                                         c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
     735           0 :                                         c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
     736           0 :                                         c1_b = CLU(b_table, x_n, y, z) - c0_b;
     737           0 :                                         c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
     738           0 :                                         c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
     739             :                                 } else { //rz > rx && rx >= ry
     740           0 :                                         c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
     741           0 :                                         c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
     742           0 :                                         c3_r = CLU(r_table, x, y, z_n) - c0_r;
     743           0 :                                         c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
     744           0 :                                         c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
     745           0 :                                         c3_g = CLU(g_table, x, y, z_n) - c0_g;
     746           0 :                                         c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
     747           0 :                                         c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
     748           0 :                                         c3_b = CLU(b_table, x, y, z_n) - c0_b;
     749             :                                 }
     750             :                         }
     751             :                 } else {
     752           0 :                         if (rx >= rz) { //ry > rx && rx >= rz
     753           0 :                                 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
     754           0 :                                 c2_r = CLU(r_table, x, y_n, z) - c0_r;
     755           0 :                                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     756           0 :                                 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
     757           0 :                                 c2_g = CLU(g_table, x, y_n, z) - c0_g;
     758           0 :                                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     759           0 :                                 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
     760           0 :                                 c2_b = CLU(b_table, x, y_n, z) - c0_b;
     761           0 :                                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     762             :                         } else {
     763           0 :                                 if (ry >= rz) { //ry >= rz && rz > rx 
     764           0 :                                         c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
     765           0 :                                         c2_r = CLU(r_table, x, y_n, z) - c0_r;
     766           0 :                                         c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
     767           0 :                                         c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
     768           0 :                                         c2_g = CLU(g_table, x, y_n, z) - c0_g;
     769           0 :                                         c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
     770           0 :                                         c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
     771           0 :                                         c2_b = CLU(b_table, x, y_n, z) - c0_b;
     772           0 :                                         c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
     773             :                                 } else { //rz > ry && ry > rx
     774           0 :                                         c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
     775           0 :                                         c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
     776           0 :                                         c3_r = CLU(r_table, x, y, z_n) - c0_r;
     777           0 :                                         c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
     778           0 :                                         c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
     779           0 :                                         c3_g = CLU(g_table, x, y, z_n) - c0_g;
     780           0 :                                         c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
     781           0 :                                         c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
     782           0 :                                         c3_b = CLU(b_table, x, y, z_n) - c0_b;
     783             :                                 }
     784             :                         }
     785             :                 }
     786             :                                 
     787           0 :                 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
     788           0 :                 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
     789           0 :                 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
     790             : 
     791           0 :                 dest[OUTPUT_R_INDEX] = clamp_u8(clut_r*255.0f);
     792           0 :                 dest[OUTPUT_G_INDEX] = clamp_u8(clut_g*255.0f);
     793           0 :                 dest[OUTPUT_B_INDEX] = clamp_u8(clut_b*255.0f);
     794           0 :                 dest += RGB_OUTPUT_COMPONENTS;
     795             :         }       
     796           0 : }
     797             : 
     798           0 : static void qcms_transform_data_rgb_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     799             : {
     800             :         unsigned int i;
     801           0 :         float (*mat)[4] = transform->matrix;
     802           0 :         for (i = 0; i < length; i++) {
     803           0 :                 unsigned char device_r = *src++;
     804           0 :                 unsigned char device_g = *src++;
     805           0 :                 unsigned char device_b = *src++;
     806             :                 float out_device_r, out_device_g, out_device_b;
     807             : 
     808           0 :                 float linear_r = transform->input_gamma_table_r[device_r];
     809           0 :                 float linear_g = transform->input_gamma_table_g[device_g];
     810           0 :                 float linear_b = transform->input_gamma_table_b[device_b];
     811             : 
     812           0 :                 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
     813           0 :                 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
     814           0 :                 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
     815             : 
     816           0 :                 out_linear_r = clamp_float(out_linear_r);
     817           0 :                 out_linear_g = clamp_float(out_linear_g);
     818           0 :                 out_linear_b = clamp_float(out_linear_b);
     819             : 
     820           0 :                 out_device_r = lut_interp_linear(out_linear_r, 
     821           0 :                                 transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
     822           0 :                 out_device_g = lut_interp_linear(out_linear_g, 
     823           0 :                                 transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
     824           0 :                 out_device_b = lut_interp_linear(out_linear_b, 
     825           0 :                                 transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
     826             : 
     827           0 :                 dest[OUTPUT_R_INDEX] = clamp_u8(out_device_r*255);
     828           0 :                 dest[OUTPUT_G_INDEX] = clamp_u8(out_device_g*255);
     829           0 :                 dest[OUTPUT_B_INDEX] = clamp_u8(out_device_b*255);
     830           0 :                 dest += RGB_OUTPUT_COMPONENTS;
     831             :         }
     832           0 : }
     833             : 
     834           0 : static void qcms_transform_data_rgba_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     835             : {
     836             :         unsigned int i;
     837           0 :         float (*mat)[4] = transform->matrix;
     838           0 :         for (i = 0; i < length; i++) {
     839           0 :                 unsigned char device_r = *src++;
     840           0 :                 unsigned char device_g = *src++;
     841           0 :                 unsigned char device_b = *src++;
     842           0 :                 unsigned char alpha = *src++;
     843             :                 float out_device_r, out_device_g, out_device_b;
     844             : 
     845           0 :                 float linear_r = transform->input_gamma_table_r[device_r];
     846           0 :                 float linear_g = transform->input_gamma_table_g[device_g];
     847           0 :                 float linear_b = transform->input_gamma_table_b[device_b];
     848             : 
     849           0 :                 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
     850           0 :                 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
     851           0 :                 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
     852             : 
     853           0 :                 out_linear_r = clamp_float(out_linear_r);
     854           0 :                 out_linear_g = clamp_float(out_linear_g);
     855           0 :                 out_linear_b = clamp_float(out_linear_b);
     856             : 
     857           0 :                 out_device_r = lut_interp_linear(out_linear_r, 
     858           0 :                                 transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
     859           0 :                 out_device_g = lut_interp_linear(out_linear_g, 
     860           0 :                                 transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
     861           0 :                 out_device_b = lut_interp_linear(out_linear_b, 
     862           0 :                                 transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
     863             : 
     864           0 :                 dest[OUTPUT_R_INDEX] = clamp_u8(out_device_r*255);
     865           0 :                 dest[OUTPUT_G_INDEX] = clamp_u8(out_device_g*255);
     866           0 :                 dest[OUTPUT_B_INDEX] = clamp_u8(out_device_b*255);
     867           0 :                 dest[OUTPUT_A_INDEX] = alpha;
     868           0 :                 dest += RGBA_OUTPUT_COMPONENTS;
     869             :         }
     870           0 : }
     871             : 
     872             : #if 0
     873             : static void qcms_transform_data_rgb_out_linear(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
     874             : {
     875             :         int i;
     876             :         float (*mat)[4] = transform->matrix;
     877             :         for (i = 0; i < length; i++) {
     878             :                 unsigned char device_r = *src++;
     879             :                 unsigned char device_g = *src++;
     880             :                 unsigned char device_b = *src++;
     881             : 
     882             :                 float linear_r = transform->input_gamma_table_r[device_r];
     883             :                 float linear_g = transform->input_gamma_table_g[device_g];
     884             :                 float linear_b = transform->input_gamma_table_b[device_b];
     885             : 
     886             :                 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
     887             :                 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
     888             :                 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
     889             : 
     890             :                 *dest++ = clamp_u8(out_linear_r*255);
     891             :                 *dest++ = clamp_u8(out_linear_g*255);
     892             :                 *dest++ = clamp_u8(out_linear_b*255);
     893             :         }
     894             : }
     895             : #endif
     896             : 
     897             : /*
     898             :  * If users create and destroy objects on different threads, even if the same
     899             :  * objects aren't used on different threads at the same time, we can still run
     900             :  * in to trouble with refcounts if they aren't atomic.
     901             :  *
     902             :  * This can lead to us prematurely deleting the precache if threads get unlucky
     903             :  * and write the wrong value to the ref count.
     904             :  */
     905          24 : static struct precache_output *precache_reference(struct precache_output *p)
     906             : {
     907          24 :         qcms_atomic_increment(p->ref_count);
     908          24 :         return p;
     909             : }
     910             : 
     911           9 : static struct precache_output *precache_create()
     912             : {
     913           9 :         struct precache_output *p = malloc(sizeof(struct precache_output));
     914           9 :         if (p)
     915           9 :                 p->ref_count = 1;
     916           9 :         return p;
     917             : }
     918             : 
     919          24 : void precache_release(struct precache_output *p)
     920             : {
     921          24 :         if (qcms_atomic_decrement(p->ref_count) == 0) {
     922           0 :                 free(p);
     923             :         }
     924          24 : }
     925             : 
     926             : #ifdef HAVE_POSIX_MEMALIGN
     927           8 : static qcms_transform *transform_alloc(void)
     928             : {
     929             :         qcms_transform *t;
     930             : 
     931             :         void *allocated_memory;
     932           8 :         if (!posix_memalign(&allocated_memory, 16, sizeof(qcms_transform))) {
     933             :                 /* Doing a memset to initialise all bits to 'zero'*/
     934           8 :                 memset(allocated_memory, 0, sizeof(qcms_transform));
     935           8 :                 t = allocated_memory;
     936           8 :                 return t;
     937             :         } else {
     938           0 :                 return NULL;
     939             :         }
     940             : }
     941           8 : static void transform_free(qcms_transform *t)
     942             : {
     943           8 :         free(t);
     944           8 : }
     945             : #else
     946             : static qcms_transform *transform_alloc(void)
     947             : {
     948             :         /* transform needs to be aligned on a 16byte boundrary */
     949             :         char *original_block = calloc(sizeof(qcms_transform) + sizeof(void*) + 16, 1);
     950             :         /* make room for a pointer to the block returned by calloc */
     951             :         void *transform_start = original_block + sizeof(void*);
     952             :         /* align transform_start */
     953             :         qcms_transform *transform_aligned = (qcms_transform*)(((uintptr_t)transform_start + 15) & ~0xf);
     954             : 
     955             :         /* store a pointer to the block returned by calloc so that we can free it later */
     956             :         void **(original_block_ptr) = (void**)transform_aligned;
     957             :         if (!original_block)
     958             :                 return NULL;
     959             :         original_block_ptr--;
     960             :         *original_block_ptr = original_block;
     961             : 
     962             :         return transform_aligned;
     963             : }
     964             : static void transform_free(qcms_transform *t)
     965             : {
     966             :         /* get at the pointer to the unaligned block returned by calloc */
     967             :         void **p = (void**)t;
     968             :         p--;
     969             :         free(*p);
     970             : }
     971             : #endif
     972             : 
     973           8 : void qcms_transform_release(qcms_transform *t)
     974             : {
     975             :         /* ensure we only free the gamma tables once even if there are
     976             :          * multiple references to the same data */
     977             : 
     978           8 :         if (t->output_table_r)
     979           8 :                 precache_release(t->output_table_r);
     980           8 :         if (t->output_table_g)
     981           8 :                 precache_release(t->output_table_g);
     982           8 :         if (t->output_table_b)
     983           8 :                 precache_release(t->output_table_b);
     984             : 
     985           8 :         free(t->input_gamma_table_r);
     986           8 :         if (t->input_gamma_table_g != t->input_gamma_table_r)
     987           8 :                 free(t->input_gamma_table_g);
     988          16 :         if (t->input_gamma_table_g != t->input_gamma_table_r &&
     989           8 :             t->input_gamma_table_g != t->input_gamma_table_b)
     990           8 :                 free(t->input_gamma_table_b);
     991             : 
     992           8 :         free(t->input_gamma_table_gray);
     993             : 
     994           8 :         free(t->output_gamma_lut_r);
     995           8 :         free(t->output_gamma_lut_g);
     996           8 :         free(t->output_gamma_lut_b);
     997             : 
     998           8 :         transform_free(t);
     999           8 : }
    1000             : 
    1001             : #ifdef X86
    1002             : // Determine if we can build with SSE2 (this was partly copied from jmorecfg.h in
    1003             : // mozilla/jpeg)
    1004             :  // -------------------------------------------------------------------------
    1005             : #if defined(_M_IX86) && defined(_MSC_VER)
    1006             : #define HAS_CPUID
    1007             : /* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC
    1008             :    register - I'm not sure if that ever happens on windows, but cpuid isn't
    1009             :    on the critical path so we just preserve the register to be safe and to be
    1010             :    consistent with the non-windows version. */
    1011             : static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
    1012             :        uint32_t a_, b_, c_, d_;
    1013             :        __asm {
    1014             :               xchg   ebx, esi
    1015             :               mov    eax, fxn
    1016             :               cpuid
    1017             :               mov    a_, eax
    1018             :               mov    b_, ebx
    1019             :               mov    c_, ecx
    1020             :               mov    d_, edx
    1021             :               xchg   ebx, esi
    1022             :        }
    1023             :        *a = a_;
    1024             :        *b = b_;
    1025             :        *c = c_;
    1026             :        *d = d_;
    1027             : }
    1028             : #elif (defined(__GNUC__) || defined(__SUNPRO_C)) && (defined(__i386__) || defined(__i386))
    1029             : #define HAS_CPUID
    1030             : /* Get us a CPUID function. We can't use ebx because it's the PIC register on
    1031             :    some platforms, so we use ESI instead and save ebx to avoid clobbering it. */
    1032             : static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
    1033             : 
    1034             :         uint32_t a_, b_, c_, d_;
    1035             :        __asm__ __volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;" 
    1036             :                              : "=a" (a_), "=S" (b_), "=c" (c_), "=d" (d_) : "a" (fxn));
    1037             :            *a = a_;
    1038             :            *b = b_;
    1039             :            *c = c_;
    1040             :            *d = d_;
    1041             : }
    1042             : #endif
    1043             : 
    1044             : // -------------------------Runtime SSEx Detection-----------------------------
    1045             : 
    1046             : /* MMX is always supported per
    1047             :  *  Gecko v1.9.1 minimum CPU requirements */
    1048             : #define SSE1_EDX_MASK (1UL << 25)
    1049             : #define SSE2_EDX_MASK (1UL << 26)
    1050             : #define SSE3_ECX_MASK (1UL <<  0)
    1051             : 
    1052           8 : static int sse_version_available(void)
    1053             : {
    1054             : #if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
    1055             :         /* we know at build time that 64-bit CPUs always have SSE2
    1056             :          * this tells the compiler that non-SSE2 branches will never be
    1057             :          * taken (i.e. OK to optimze away the SSE1 and non-SIMD code */
    1058           8 :         return 2;
    1059             : #elif defined(HAS_CPUID)
    1060             :         static int sse_version = -1;
    1061             :         uint32_t a, b, c, d;
    1062             :         uint32_t function = 0x00000001;
    1063             : 
    1064             :         if (sse_version == -1) {
    1065             :                 sse_version = 0;
    1066             :                 cpuid(function, &a, &b, &c, &d);
    1067             :                 if (c & SSE3_ECX_MASK)
    1068             :                         sse_version = 3;
    1069             :                 else if (d & SSE2_EDX_MASK)
    1070             :                         sse_version = 2;
    1071             :                 else if (d & SSE1_EDX_MASK)
    1072             :                         sse_version = 1;
    1073             :         }
    1074             : 
    1075             :         return sse_version;
    1076             : #else
    1077             :         return 0;
    1078             : #endif
    1079             : }
    1080             : #endif
    1081             : 
    1082             : static const struct matrix bradford_matrix = {{ { 0.8951f, 0.2664f,-0.1614f},
    1083             :                                                 {-0.7502f, 1.7135f, 0.0367f},
    1084             :                                                 { 0.0389f,-0.0685f, 1.0296f}}, 
    1085             :                                                 false};
    1086             : 
    1087             : static const struct matrix bradford_matrix_inv = {{ { 0.9869929f,-0.1470543f, 0.1599627f},
    1088             :                                                     { 0.4323053f, 0.5183603f, 0.0492912f},
    1089             :                                                     {-0.0085287f, 0.0400428f, 0.9684867f}}, 
    1090             :                                                     false};
    1091             : 
    1092             : // See ICCv4 E.3
    1093           0 : struct matrix compute_whitepoint_adaption(float X, float Y, float Z) {
    1094           0 :         float p = (0.96422f*bradford_matrix.m[0][0] + 1.000f*bradford_matrix.m[1][0] + 0.82521f*bradford_matrix.m[2][0]) /
    1095           0 :                   (X*bradford_matrix.m[0][0]      + Y*bradford_matrix.m[1][0]      + Z*bradford_matrix.m[2][0]     );
    1096           0 :         float y = (0.96422f*bradford_matrix.m[0][1] + 1.000f*bradford_matrix.m[1][1] + 0.82521f*bradford_matrix.m[2][1]) /
    1097           0 :                   (X*bradford_matrix.m[0][1]      + Y*bradford_matrix.m[1][1]      + Z*bradford_matrix.m[2][1]     );
    1098           0 :         float b = (0.96422f*bradford_matrix.m[0][2] + 1.000f*bradford_matrix.m[1][2] + 0.82521f*bradford_matrix.m[2][2]) /
    1099           0 :                   (X*bradford_matrix.m[0][2]      + Y*bradford_matrix.m[1][2]      + Z*bradford_matrix.m[2][2]     );
    1100           0 :         struct matrix white_adaption = {{ {p,0,0}, {0,y,0}, {0,0,b}}, false};
    1101           0 :         return matrix_multiply( bradford_matrix_inv, matrix_multiply(white_adaption, bradford_matrix) );
    1102             : }
    1103             : 
    1104           3 : void qcms_profile_precache_output_transform(qcms_profile *profile)
    1105             : {
    1106             :         /* we only support precaching on rgb profiles */
    1107           3 :         if (profile->color_space != RGB_SIGNATURE)
    1108           0 :                 return;
    1109             : 
    1110           3 :         if (qcms_supports_iccv4) {
    1111             :                 /* don't precache since we will use the B2A LUT */
    1112           0 :                 if (profile->B2A0)
    1113           0 :                         return;
    1114             : 
    1115             :                 /* don't precache since we will use the mBA LUT */
    1116           0 :                 if (profile->mBA)
    1117           0 :                         return;
    1118             :         }
    1119             : 
    1120             :         /* don't precache if we do not have the TRC curves */
    1121           3 :         if (!profile->redTRC || !profile->greenTRC || !profile->blueTRC)
    1122           0 :                 return;
    1123             : 
    1124           3 :         if (!profile->output_table_r) {
    1125           3 :                 profile->output_table_r = precache_create();
    1126           6 :                 if (profile->output_table_r &&
    1127           3 :                                 !compute_precache(profile->redTRC, profile->output_table_r->data)) {
    1128           0 :                         precache_release(profile->output_table_r);
    1129           0 :                         profile->output_table_r = NULL;
    1130             :                 }
    1131             :         }
    1132           3 :         if (!profile->output_table_g) {
    1133           3 :                 profile->output_table_g = precache_create();
    1134           6 :                 if (profile->output_table_g &&
    1135           3 :                                 !compute_precache(profile->greenTRC, profile->output_table_g->data)) {
    1136           0 :                         precache_release(profile->output_table_g);
    1137           0 :                         profile->output_table_g = NULL;
    1138             :                 }
    1139             :         }
    1140           3 :         if (!profile->output_table_b) {
    1141           3 :                 profile->output_table_b = precache_create();
    1142           6 :                 if (profile->output_table_b &&
    1143           3 :                                 !compute_precache(profile->blueTRC, profile->output_table_b->data)) {
    1144           0 :                         precache_release(profile->output_table_b);
    1145           0 :                         profile->output_table_b = NULL;
    1146             :                 }
    1147             :         }
    1148             : }
    1149             : 
    1150             : /* Replace the current transformation with a LUT transformation using a given number of sample points */
    1151           0 : qcms_transform* qcms_transform_precacheLUT_float(qcms_transform *transform, qcms_profile *in, qcms_profile *out, 
    1152             :                                                  int samples, qcms_data_type in_type)
    1153             : {
    1154             :         /* The range between which 2 consecutive sample points can be used to interpolate */
    1155             :         uint16_t x,y,z;
    1156             :         uint32_t l;
    1157           0 :         uint32_t lutSize = 3 * samples * samples * samples;
    1158           0 :         float* src = NULL;
    1159           0 :         float* dest = NULL;
    1160           0 :         float* lut = NULL;
    1161             : 
    1162           0 :         src = malloc(lutSize*sizeof(float));
    1163           0 :         dest = malloc(lutSize*sizeof(float));
    1164             : 
    1165           0 :         if (src && dest) {
    1166             :                 /* Prepare a list of points we want to sample */
    1167           0 :                 l = 0;
    1168           0 :                 for (x = 0; x < samples; x++) {
    1169           0 :                         for (y = 0; y < samples; y++) {
    1170           0 :                                 for (z = 0; z < samples; z++) {
    1171           0 :                                         src[l++] = x / (float)(samples-1);
    1172           0 :                                         src[l++] = y / (float)(samples-1);
    1173           0 :                                         src[l++] = z / (float)(samples-1);
    1174             :                                 }
    1175             :                         }
    1176             :                 }
    1177             : 
    1178           0 :                 lut = qcms_chain_transform(in, out, src, dest, lutSize);
    1179           0 :                 if (lut) {
    1180           0 :                         transform->r_clut = &lut[0];
    1181           0 :                         transform->g_clut = &lut[1];
    1182           0 :                         transform->b_clut = &lut[2];
    1183           0 :                         transform->grid_size = samples;
    1184           0 :                         if (in_type == QCMS_DATA_RGBA_8) {
    1185           0 :                                 transform->transform_fn = qcms_transform_data_tetra_clut_rgba;
    1186             :                         } else {
    1187           0 :                                 transform->transform_fn = qcms_transform_data_tetra_clut;
    1188             :                         }
    1189             :                 }
    1190             :         }
    1191             : 
    1192             : 
    1193             :         //XXX: qcms_modular_transform_data may return either the src or dest buffer. If so it must not be free-ed
    1194           0 :         if (src && lut != src) {
    1195           0 :                 free(src);
    1196             :         }
    1197           0 :         if (dest && lut != dest) {
    1198           0 :                 free(dest);
    1199             :         }
    1200             : 
    1201           0 :         if (lut == NULL) {
    1202           0 :                 return NULL;
    1203             :         }
    1204           0 :         return transform;
    1205             : }
    1206             : 
    1207             : #define NO_MEM_TRANSFORM NULL
    1208             : 
    1209           8 : qcms_transform* qcms_transform_create(
    1210             :                 qcms_profile *in, qcms_data_type in_type,
    1211             :                 qcms_profile *out, qcms_data_type out_type,
    1212             :                 qcms_intent intent)
    1213             : {
    1214           8 :         bool precache = false;
    1215             : 
    1216           8 :         qcms_transform *transform = transform_alloc();
    1217           8 :         if (!transform) {
    1218           0 :                 return NULL;
    1219             :         }
    1220           8 :         if (out_type != QCMS_DATA_RGB_8 &&
    1221             :                 out_type != QCMS_DATA_RGBA_8) {
    1222           0 :             assert(0 && "output type");
    1223             :             qcms_transform_release(transform);
    1224             :             return NULL;
    1225             :         }
    1226             : 
    1227          16 :         if (out->output_table_r &&
    1228          16 :                         out->output_table_g &&
    1229           8 :                         out->output_table_b) {
    1230           8 :                 precache = true;
    1231             :         }
    1232             : 
    1233             :         // This precache assumes RGB_SIGNATURE (fails on GRAY_SIGNATURE, for instance)
    1234           8 :         if (qcms_supports_iccv4 &&
    1235           0 :                         (in_type == QCMS_DATA_RGB_8 || in_type == QCMS_DATA_RGBA_8) &&
    1236           0 :                         (in->A2B0 || out->B2A0 || in->mAB || out->mAB))
    1237             :                 {
    1238             :                 // Precache the transformation to a CLUT 33x33x33 in size.
    1239             :                 // 33 is used by many profiles and works well in pratice. 
    1240             :                 // This evenly divides 256 into blocks of 8x8x8.
    1241             :                 // TODO For transforming small data sets of about 200x200 or less
    1242             :                 // precaching should be avoided.
    1243           0 :                 qcms_transform *result = qcms_transform_precacheLUT_float(transform, in, out, 33, in_type);
    1244           0 :                 if (!result) {
    1245           0 :                         assert(0 && "precacheLUT failed");
    1246             :                         qcms_transform_release(transform);
    1247             :                         return NULL;
    1248             :                 }
    1249           0 :                 return result;
    1250             :         }
    1251             : 
    1252           8 :         if (precache) {
    1253           8 :                 transform->output_table_r = precache_reference(out->output_table_r);
    1254           8 :                 transform->output_table_g = precache_reference(out->output_table_g);
    1255           8 :                 transform->output_table_b = precache_reference(out->output_table_b);
    1256             :         } else {
    1257           0 :                 if (!out->redTRC || !out->greenTRC || !out->blueTRC) {
    1258           0 :                         qcms_transform_release(transform);
    1259           0 :                         return NO_MEM_TRANSFORM;
    1260             :                 }
    1261           0 :                 build_output_lut(out->redTRC, &transform->output_gamma_lut_r, &transform->output_gamma_lut_r_length);
    1262           0 :                 build_output_lut(out->greenTRC, &transform->output_gamma_lut_g, &transform->output_gamma_lut_g_length);
    1263           0 :                 build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, &transform->output_gamma_lut_b_length);
    1264           0 :                 if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g || !transform->output_gamma_lut_b) {
    1265           0 :                         qcms_transform_release(transform);
    1266           0 :                         return NO_MEM_TRANSFORM;
    1267             :                 }
    1268             :         }
    1269             : 
    1270           8 :         if (in->color_space == RGB_SIGNATURE) {
    1271             :                 struct matrix in_matrix, out_matrix, result;
    1272             : 
    1273           8 :                 if (in_type != QCMS_DATA_RGB_8 &&
    1274             :                     in_type != QCMS_DATA_RGBA_8){
    1275           0 :                         assert(0 && "input type");
    1276             :                         qcms_transform_release(transform);
    1277           0 :                         return NULL;
    1278             :                 }
    1279           8 :                 if (precache) {
    1280             : #ifdef X86
    1281           8 :                     if (sse_version_available() >= 2) {
    1282           8 :                             if (in_type == QCMS_DATA_RGB_8)
    1283           0 :                                     transform->transform_fn = qcms_transform_data_rgb_out_lut_sse2;
    1284             :                             else
    1285           8 :                                     transform->transform_fn = qcms_transform_data_rgba_out_lut_sse2;
    1286             : 
    1287             : #if !(defined(_MSC_VER) && defined(_M_AMD64))
    1288             :                     /* Microsoft Compiler for x64 doesn't support MMX.
    1289             :                      * SSE code uses MMX so that we disable on x64 */
    1290             :                     } else
    1291           0 :                     if (sse_version_available() >= 1) {
    1292           0 :                             if (in_type == QCMS_DATA_RGB_8)
    1293           0 :                                     transform->transform_fn = qcms_transform_data_rgb_out_lut_sse1;
    1294             :                             else
    1295           0 :                                     transform->transform_fn = qcms_transform_data_rgba_out_lut_sse1;
    1296             : #endif
    1297             :                     } else
    1298             : #endif
    1299             : #if (defined(__POWERPC__) || defined(__powerpc__) && !defined(__NO_FPRS__))
    1300             :                     if (have_altivec()) {
    1301             :                             if (in_type == QCMS_DATA_RGB_8)
    1302             :                                     transform->transform_fn = qcms_transform_data_rgb_out_lut_altivec;
    1303             :                             else
    1304             :                                     transform->transform_fn = qcms_transform_data_rgba_out_lut_altivec;
    1305             :                     } else
    1306             : #endif
    1307             :                         {
    1308           0 :                                 if (in_type == QCMS_DATA_RGB_8)
    1309           0 :                                         transform->transform_fn = qcms_transform_data_rgb_out_lut_precache;
    1310             :                                 else
    1311           0 :                                         transform->transform_fn = qcms_transform_data_rgba_out_lut_precache;
    1312             :                         }
    1313             :                 } else {
    1314           0 :                         if (in_type == QCMS_DATA_RGB_8)
    1315           0 :                                 transform->transform_fn = qcms_transform_data_rgb_out_lut;
    1316             :                         else
    1317           0 :                                 transform->transform_fn = qcms_transform_data_rgba_out_lut;
    1318             :                 }
    1319             : 
    1320             :                 //XXX: avoid duplicating tables if we can
    1321           8 :                 transform->input_gamma_table_r = build_input_gamma_table(in->redTRC);
    1322           8 :                 transform->input_gamma_table_g = build_input_gamma_table(in->greenTRC);
    1323           8 :                 transform->input_gamma_table_b = build_input_gamma_table(in->blueTRC);
    1324           8 :                 if (!transform->input_gamma_table_r || !transform->input_gamma_table_g || !transform->input_gamma_table_b) {
    1325           0 :                         qcms_transform_release(transform);
    1326           0 :                         return NO_MEM_TRANSFORM;
    1327             :                 }
    1328             : 
    1329             : 
    1330             :                 /* build combined colorant matrix */
    1331           8 :                 in_matrix = build_colorant_matrix(in);
    1332           8 :                 out_matrix = build_colorant_matrix(out);
    1333           8 :                 out_matrix = matrix_invert(out_matrix);
    1334           8 :                 if (out_matrix.invalid) {
    1335           0 :                         qcms_transform_release(transform);
    1336           0 :                         return NULL;
    1337             :                 }
    1338           8 :                 result = matrix_multiply(out_matrix, in_matrix);
    1339             : 
    1340             :                 /* check for NaN values in the matrix and bail if we find any */
    1341          32 :                 for (unsigned i = 0 ; i < 3 ; ++i) {
    1342          96 :                         for (unsigned j = 0 ; j < 3 ; ++j) {
    1343          72 :                                 if (result.m[i][j] != result.m[i][j]) {
    1344           0 :                                         qcms_transform_release(transform);
    1345           0 :                                         return NULL;
    1346             :                                 }
    1347             :                         }
    1348             :                 }
    1349             : 
    1350             :                 /* store the results in column major mode
    1351             :                  * this makes doing the multiplication with sse easier */
    1352           8 :                 transform->matrix[0][0] = result.m[0][0];
    1353           8 :                 transform->matrix[1][0] = result.m[0][1];
    1354           8 :                 transform->matrix[2][0] = result.m[0][2];
    1355           8 :                 transform->matrix[0][1] = result.m[1][0];
    1356           8 :                 transform->matrix[1][1] = result.m[1][1];
    1357           8 :                 transform->matrix[2][1] = result.m[1][2];
    1358           8 :                 transform->matrix[0][2] = result.m[2][0];
    1359           8 :                 transform->matrix[1][2] = result.m[2][1];
    1360           8 :                 transform->matrix[2][2] = result.m[2][2];
    1361             : 
    1362           0 :         } else if (in->color_space == GRAY_SIGNATURE) {
    1363           0 :                 if (in_type != QCMS_DATA_GRAY_8 &&
    1364             :                                 in_type != QCMS_DATA_GRAYA_8){
    1365           0 :                         assert(0 && "input type");
    1366             :                         qcms_transform_release(transform);
    1367             :                         return NULL;
    1368             :                 }
    1369             : 
    1370           0 :                 transform->input_gamma_table_gray = build_input_gamma_table(in->grayTRC);
    1371           0 :                 if (!transform->input_gamma_table_gray) {
    1372           0 :                         qcms_transform_release(transform);
    1373           0 :                         return NO_MEM_TRANSFORM;
    1374             :                 }
    1375             : 
    1376           0 :                 if (precache) {
    1377           0 :                         if (in_type == QCMS_DATA_GRAY_8) {
    1378           0 :                                 transform->transform_fn = qcms_transform_data_gray_out_precache;
    1379             :                         } else {
    1380           0 :                                 transform->transform_fn = qcms_transform_data_graya_out_precache;
    1381             :                         }
    1382             :                 } else {
    1383           0 :                         if (in_type == QCMS_DATA_GRAY_8) {
    1384           0 :                                 transform->transform_fn = qcms_transform_data_gray_out_lut;
    1385             :                         } else {
    1386           0 :                                 transform->transform_fn = qcms_transform_data_graya_out_lut;
    1387             :                         }
    1388             :                 }
    1389             :         } else {
    1390           0 :                 assert(0 && "unexpected colorspace");
    1391             :                 qcms_transform_release(transform);
    1392             :                 return NULL;
    1393             :         }
    1394           8 :         return transform;
    1395             : }
    1396             : 
    1397             : #if defined(__GNUC__) && defined(__i386__)
    1398             : /* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */
    1399             : __attribute__((__force_align_arg_pointer__))
    1400             : #endif
    1401          42 : void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length)
    1402             : {
    1403          42 :         transform->transform_fn(transform, src, dest, length);
    1404          42 : }
    1405             : 
    1406             : qcms_bool qcms_supports_iccv4;
    1407           0 : void qcms_enable_iccv4()
    1408             : {
    1409           0 :         qcms_supports_iccv4 = true;
    1410           0 : }

Generated by: LCOV version 1.13