LCOV - code coverage report
Current view: directory - gfx/qcms - chain.c (source / functions) Found Hit Coverage
Test: app.info Lines: 538 0 0.0 %
Date: 2012-06-02 Functions: 21 0 0.0 %

       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 "transform_util.h"
      30                 : #include "matrix.h"
      31                 : 
      32               0 : static struct matrix build_lut_matrix(struct lutType *lut)
      33                 : {
      34                 :         struct matrix result;
      35               0 :         if (lut) {
      36               0 :                 result.m[0][0] = s15Fixed16Number_to_float(lut->e00);
      37               0 :                 result.m[0][1] = s15Fixed16Number_to_float(lut->e01);
      38               0 :                 result.m[0][2] = s15Fixed16Number_to_float(lut->e02);
      39               0 :                 result.m[1][0] = s15Fixed16Number_to_float(lut->e10);
      40               0 :                 result.m[1][1] = s15Fixed16Number_to_float(lut->e11);
      41               0 :                 result.m[1][2] = s15Fixed16Number_to_float(lut->e12);
      42               0 :                 result.m[2][0] = s15Fixed16Number_to_float(lut->e20);
      43               0 :                 result.m[2][1] = s15Fixed16Number_to_float(lut->e21);
      44               0 :                 result.m[2][2] = s15Fixed16Number_to_float(lut->e22);
      45               0 :                 result.invalid = false;
      46                 :         } else {
      47               0 :                 memset(&result, 0, sizeof(struct matrix));
      48               0 :                 result.invalid = true;
      49                 :         }
      50               0 :         return result;
      51                 : }
      52                 : 
      53               0 : static struct matrix build_mAB_matrix(struct lutmABType *lut)
      54                 : {
      55                 :         struct matrix result;
      56               0 :         if (lut) {
      57               0 :                 result.m[0][0] = s15Fixed16Number_to_float(lut->e00);
      58               0 :                 result.m[0][1] = s15Fixed16Number_to_float(lut->e01);
      59               0 :                 result.m[0][2] = s15Fixed16Number_to_float(lut->e02);
      60               0 :                 result.m[1][0] = s15Fixed16Number_to_float(lut->e10);
      61               0 :                 result.m[1][1] = s15Fixed16Number_to_float(lut->e11);
      62               0 :                 result.m[1][2] = s15Fixed16Number_to_float(lut->e12);
      63               0 :                 result.m[2][0] = s15Fixed16Number_to_float(lut->e20);
      64               0 :                 result.m[2][1] = s15Fixed16Number_to_float(lut->e21);
      65               0 :                 result.m[2][2] = s15Fixed16Number_to_float(lut->e22);
      66               0 :                 result.invalid = false;
      67                 :         } else {
      68               0 :                 memset(&result, 0, sizeof(struct matrix));
      69               0 :                 result.invalid = true;
      70                 :         }
      71               0 :         return result;
      72                 : }
      73                 : 
      74                 : //Based on lcms cmsLab2XYZ
      75                 : #define f(t) (t <= (24.0f/116.0f)*(24.0f/116.0f)*(24.0f/116.0f)) ? ((841.0/108.0) * t + (16.0/116.0)) : pow(t,1.0/3.0)
      76                 : #define f_1(t) (t <= (24.0f/116.0f)) ? ((108.0/841.0) * (t - (16.0/116.0))) : (t * t * t)
      77               0 : static void qcms_transform_module_LAB_to_XYZ(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
      78                 : {
      79                 :         size_t i;
      80                 :         // lcms: D50 XYZ values
      81               0 :         float WhitePointX = 0.9642f;
      82               0 :         float WhitePointY = 1.0f;
      83               0 :         float WhitePointZ = 0.8249f;
      84               0 :         for (i = 0; i < length; i++) {
      85               0 :                 float device_L = *src++ * 100.0f;
      86               0 :                 float device_a = *src++ * 255.0f - 128.0f;
      87               0 :                 float device_b = *src++ * 255.0f - 128.0f;
      88               0 :                 float y = (device_L + 16.0f) / 116.0f;
      89                 : 
      90               0 :                 float X = f_1((y + 0.002f * device_a)) * WhitePointX;
      91               0 :                 float Y = f_1(y) * WhitePointY;
      92               0 :                 float Z = f_1((y - 0.005f * device_b)) * WhitePointZ;
      93               0 :                 *dest++ = X / (1.0 + 32767.0/32768.0);
      94               0 :                 *dest++ = Y / (1.0 + 32767.0/32768.0);
      95               0 :                 *dest++ = Z / (1.0 + 32767.0/32768.0);
      96                 :         }
      97               0 : }
      98                 : 
      99                 : //Based on lcms cmsXYZ2Lab
     100               0 : static void qcms_transform_module_XYZ_to_LAB(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     101                 : {
     102                 :         size_t i;
     103                 :         // lcms: D50 XYZ values
     104               0 :         float WhitePointX = 0.9642f;
     105               0 :         float WhitePointY = 1.0f;
     106               0 :         float WhitePointZ = 0.8249f;
     107               0 :         for (i = 0; i < length; i++) {
     108               0 :                 float device_x = *src++ * (1.0 + 32767.0/32768.0) / WhitePointX;
     109               0 :                 float device_y = *src++ * (1.0 + 32767.0/32768.0) / WhitePointY;
     110               0 :                 float device_z = *src++ * (1.0 + 32767.0/32768.0) / WhitePointZ;
     111                 : 
     112               0 :                 float fx = f(device_x);
     113               0 :                 float fy = f(device_y);
     114               0 :                 float fz = f(device_z);
     115                 : 
     116               0 :                 float L = 116.0f*fy - 16.0f;
     117               0 :                 float a = 500.0f*(fx - fy);
     118               0 :                 float b = 200.0f*(fy - fz);
     119               0 :                 *dest++ = L / 100.0f;
     120               0 :                 *dest++ = (a+128.0f) / 255.0f;
     121               0 :                 *dest++ = (b+128.0f) / 255.0f;
     122                 :         }
     123                 : 
     124               0 : }
     125                 : 
     126               0 : static void qcms_transform_module_clut_only(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     127                 : {
     128                 :         size_t i;
     129               0 :         int xy_len = 1;
     130               0 :         int x_len = transform->grid_size;
     131               0 :         int len = x_len * x_len;
     132               0 :         float* r_table = transform->r_clut;
     133               0 :         float* g_table = transform->g_clut;
     134               0 :         float* b_table = transform->b_clut;
     135                 : 
     136               0 :         for (i = 0; i < length; i++) {
     137               0 :                 float linear_r = *src++;
     138               0 :                 float linear_g = *src++;
     139               0 :                 float linear_b = *src++;
     140                 : 
     141               0 :                 int x = floor(linear_r * (transform->grid_size-1));
     142               0 :                 int y = floor(linear_g * (transform->grid_size-1));
     143               0 :                 int z = floor(linear_b * (transform->grid_size-1));
     144               0 :                 int x_n = ceil(linear_r * (transform->grid_size-1));
     145               0 :                 int y_n = ceil(linear_g * (transform->grid_size-1));
     146               0 :                 int z_n = ceil(linear_b * (transform->grid_size-1));
     147               0 :                 float x_d = linear_r * (transform->grid_size-1) - x;
     148               0 :                 float y_d = linear_g * (transform->grid_size-1) - y;
     149               0 :                 float z_d = linear_b * (transform->grid_size-1) - z;
     150                 : 
     151               0 :                 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
     152               0 :                 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
     153               0 :                 float r_y1 = lerp(r_x1, r_x2, y_d);
     154               0 :                 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
     155               0 :                 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
     156               0 :                 float r_y2 = lerp(r_x3, r_x4, y_d);
     157               0 :                 float clut_r = lerp(r_y1, r_y2, z_d);
     158                 : 
     159               0 :                 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
     160               0 :                 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
     161               0 :                 float g_y1 = lerp(g_x1, g_x2, y_d);
     162               0 :                 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
     163               0 :                 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
     164               0 :                 float g_y2 = lerp(g_x3, g_x4, y_d);
     165               0 :                 float clut_g = lerp(g_y1, g_y2, z_d);
     166                 : 
     167               0 :                 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
     168               0 :                 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
     169               0 :                 float b_y1 = lerp(b_x1, b_x2, y_d);
     170               0 :                 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
     171               0 :                 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
     172               0 :                 float b_y2 = lerp(b_x3, b_x4, y_d);
     173               0 :                 float clut_b = lerp(b_y1, b_y2, z_d);
     174                 : 
     175               0 :                 *dest++ = clamp_float(clut_r);
     176               0 :                 *dest++ = clamp_float(clut_g);
     177               0 :                 *dest++ = clamp_float(clut_b);
     178                 :         }
     179               0 : }
     180                 : 
     181               0 : static void qcms_transform_module_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     182                 : {
     183                 :         size_t i;
     184               0 :         int xy_len = 1;
     185               0 :         int x_len = transform->grid_size;
     186               0 :         int len = x_len * x_len;
     187               0 :         float* r_table = transform->r_clut;
     188               0 :         float* g_table = transform->g_clut;
     189               0 :         float* b_table = transform->b_clut;
     190               0 :         for (i = 0; i < length; i++) {
     191               0 :                 float device_r = *src++;
     192               0 :                 float device_g = *src++;
     193               0 :                 float device_b = *src++;
     194               0 :                 float linear_r = lut_interp_linear_float(device_r,
     195               0 :                                 transform->input_clut_table_r, transform->input_clut_table_length);
     196               0 :                 float linear_g = lut_interp_linear_float(device_g,
     197               0 :                                 transform->input_clut_table_g, transform->input_clut_table_length);
     198               0 :                 float linear_b = lut_interp_linear_float(device_b,
     199               0 :                                 transform->input_clut_table_b, transform->input_clut_table_length);
     200                 : 
     201               0 :                 int x = floor(linear_r * (transform->grid_size-1));
     202               0 :                 int y = floor(linear_g * (transform->grid_size-1));
     203               0 :                 int z = floor(linear_b * (transform->grid_size-1));
     204               0 :                 int x_n = ceil(linear_r * (transform->grid_size-1));
     205               0 :                 int y_n = ceil(linear_g * (transform->grid_size-1));
     206               0 :                 int z_n = ceil(linear_b * (transform->grid_size-1));
     207               0 :                 float x_d = linear_r * (transform->grid_size-1) - x;
     208               0 :                 float y_d = linear_g * (transform->grid_size-1) - y;
     209               0 :                 float z_d = linear_b * (transform->grid_size-1) - z;
     210                 : 
     211               0 :                 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
     212               0 :                 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
     213               0 :                 float r_y1 = lerp(r_x1, r_x2, y_d);
     214               0 :                 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
     215               0 :                 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
     216               0 :                 float r_y2 = lerp(r_x3, r_x4, y_d);
     217               0 :                 float clut_r = lerp(r_y1, r_y2, z_d);
     218                 : 
     219               0 :                 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
     220               0 :                 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
     221               0 :                 float g_y1 = lerp(g_x1, g_x2, y_d);
     222               0 :                 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
     223               0 :                 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
     224               0 :                 float g_y2 = lerp(g_x3, g_x4, y_d);
     225               0 :                 float clut_g = lerp(g_y1, g_y2, z_d);
     226                 : 
     227               0 :                 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
     228               0 :                 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
     229               0 :                 float b_y1 = lerp(b_x1, b_x2, y_d);
     230               0 :                 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
     231               0 :                 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
     232               0 :                 float b_y2 = lerp(b_x3, b_x4, y_d);
     233               0 :                 float clut_b = lerp(b_y1, b_y2, z_d);
     234                 : 
     235               0 :                 float pcs_r = lut_interp_linear_float(clut_r,
     236               0 :                                 transform->output_clut_table_r, transform->output_clut_table_length);
     237               0 :                 float pcs_g = lut_interp_linear_float(clut_g,
     238               0 :                                 transform->output_clut_table_g, transform->output_clut_table_length);
     239               0 :                 float pcs_b = lut_interp_linear_float(clut_b,
     240               0 :                                 transform->output_clut_table_b, transform->output_clut_table_length);
     241                 : 
     242               0 :                 *dest++ = clamp_float(pcs_r);
     243               0 :                 *dest++ = clamp_float(pcs_g);
     244               0 :                 *dest++ = clamp_float(pcs_b);
     245                 :         }
     246               0 : }
     247                 : 
     248                 : /* NOT USED
     249                 : static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     250                 : {
     251                 :         size_t i;
     252                 :         int xy_len = 1;
     253                 :         int x_len = transform->grid_size;
     254                 :         int len = x_len * x_len;
     255                 :         float* r_table = transform->r_clut;
     256                 :         float* g_table = transform->g_clut;
     257                 :         float* b_table = transform->b_clut;
     258                 :         float c0_r, c1_r, c2_r, c3_r;
     259                 :         float c0_g, c1_g, c2_g, c3_g;
     260                 :         float c0_b, c1_b, c2_b, c3_b;
     261                 :         float clut_r, clut_g, clut_b;
     262                 :         float pcs_r, pcs_g, pcs_b;
     263                 :         for (i = 0; i < length; i++) {
     264                 :                 float device_r = *src++;
     265                 :                 float device_g = *src++;
     266                 :                 float device_b = *src++;
     267                 :                 float linear_r = lut_interp_linear_float(device_r,
     268                 :                                 transform->input_clut_table_r, transform->input_clut_table_length);
     269                 :                 float linear_g = lut_interp_linear_float(device_g,
     270                 :                                 transform->input_clut_table_g, transform->input_clut_table_length);
     271                 :                 float linear_b = lut_interp_linear_float(device_b,
     272                 :                                 transform->input_clut_table_b, transform->input_clut_table_length);
     273                 : 
     274                 :                 int x = floor(linear_r * (transform->grid_size-1));
     275                 :                 int y = floor(linear_g * (transform->grid_size-1));
     276                 :                 int z = floor(linear_b * (transform->grid_size-1));
     277                 :                 int x_n = ceil(linear_r * (transform->grid_size-1));
     278                 :                 int y_n = ceil(linear_g * (transform->grid_size-1));
     279                 :                 int z_n = ceil(linear_b * (transform->grid_size-1));
     280                 :                 float rx = linear_r * (transform->grid_size-1) - x;
     281                 :                 float ry = linear_g * (transform->grid_size-1) - y;
     282                 :                 float rz = linear_b * (transform->grid_size-1) - z;
     283                 : 
     284                 :                 c0_r = CLU(r_table, x, y, z);
     285                 :                 c0_g = CLU(g_table, x, y, z);
     286                 :                 c0_b = CLU(b_table, x, y, z);
     287                 :                 if( rx >= ry ) {
     288                 :                         if (ry >= rz) { //rx >= ry && ry >= rz
     289                 :                                 c1_r = CLU(r_table, x_n, y, z) - c0_r;
     290                 :                                 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
     291                 :                                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     292                 :                                 c1_g = CLU(g_table, x_n, y, z) - c0_g;
     293                 :                                 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
     294                 :                                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     295                 :                                 c1_b = CLU(b_table, x_n, y, z) - c0_b;
     296                 :                                 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
     297                 :                                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     298                 :                         } else {
     299                 :                                 if (rx >= rz) { //rx >= rz && rz >= ry
     300                 :                                         c1_r = CLU(r_table, x_n, y, z) - c0_r;
     301                 :                                         c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
     302                 :                                         c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
     303                 :                                         c1_g = CLU(g_table, x_n, y, z) - c0_g;
     304                 :                                         c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
     305                 :                                         c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
     306                 :                                         c1_b = CLU(b_table, x_n, y, z) - c0_b;
     307                 :                                         c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
     308                 :                                         c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
     309                 :                                 } else { //rz > rx && rx >= ry
     310                 :                                         c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
     311                 :                                         c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
     312                 :                                         c3_r = CLU(r_table, x, y, z_n) - c0_r;
     313                 :                                         c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
     314                 :                                         c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
     315                 :                                         c3_g = CLU(g_table, x, y, z_n) - c0_g;
     316                 :                                         c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
     317                 :                                         c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
     318                 :                                         c3_b = CLU(b_table, x, y, z_n) - c0_b;
     319                 :                                 }
     320                 :                         }
     321                 :                 } else {
     322                 :                         if (rx >= rz) { //ry > rx && rx >= rz
     323                 :                                 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
     324                 :                                 c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
     325                 :                                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     326                 :                                 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
     327                 :                                 c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
     328                 :                                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     329                 :                                 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
     330                 :                                 c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
     331                 :                                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     332                 :                         } else {
     333                 :                                 if (ry >= rz) { //ry >= rz && rz > rx 
     334                 :                                         c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
     335                 :                                         c2_r = CLU(r_table, x, y_n, z) - c0_r;
     336                 :                                         c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
     337                 :                                         c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
     338                 :                                         c2_g = CLU(g_table, x, y_n, z) - c0_g;
     339                 :                                         c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
     340                 :                                         c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
     341                 :                                         c2_b = CLU(b_table, x, y_n, z) - c0_b;
     342                 :                                         c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
     343                 :                                 } else { //rz > ry && ry > rx
     344                 :                                         c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
     345                 :                                         c2_r = CLU(r_table, x, y_n, z) - c0_r;
     346                 :                                         c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
     347                 :                                         c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
     348                 :                                         c2_g = CLU(g_table, x, y_n, z) - c0_g;
     349                 :                                         c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
     350                 :                                         c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
     351                 :                                         c2_b = CLU(b_table, x, y_n, z) - c0_b;
     352                 :                                         c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
     353                 :                                 }
     354                 :                         }
     355                 :                 }
     356                 : 
     357                 :                 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
     358                 :                 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
     359                 :                 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
     360                 : 
     361                 :                 pcs_r = lut_interp_linear_float(clut_r,
     362                 :                                 transform->output_clut_table_r, transform->output_clut_table_length);
     363                 :                 pcs_g = lut_interp_linear_float(clut_g,
     364                 :                                 transform->output_clut_table_g, transform->output_clut_table_length);
     365                 :                 pcs_b = lut_interp_linear_float(clut_b,
     366                 :                                 transform->output_clut_table_b, transform->output_clut_table_length);
     367                 :                 *dest++ = clamp_float(pcs_r);
     368                 :                 *dest++ = clamp_float(pcs_g);
     369                 :                 *dest++ = clamp_float(pcs_b);
     370                 :         }
     371                 : }
     372                 : */
     373                 : 
     374               0 : static void qcms_transform_module_gamma_table(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     375                 : {
     376                 :         size_t i;
     377                 :         float out_r, out_g, out_b;
     378               0 :         for (i = 0; i < length; i++) {
     379               0 :                 float in_r = *src++;
     380               0 :                 float in_g = *src++;
     381               0 :                 float in_b = *src++;
     382                 : 
     383               0 :                 out_r = lut_interp_linear_float(in_r, transform->input_clut_table_r, 256);
     384               0 :                 out_g = lut_interp_linear_float(in_g, transform->input_clut_table_g, 256);
     385               0 :                 out_b = lut_interp_linear_float(in_b, transform->input_clut_table_b, 256);
     386                 : 
     387               0 :                 *dest++ = clamp_float(out_r);
     388               0 :                 *dest++ = clamp_float(out_g);
     389               0 :                 *dest++ = clamp_float(out_b);
     390                 :         }
     391               0 : }
     392                 : 
     393               0 : static void qcms_transform_module_gamma_lut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     394                 : {
     395                 :         size_t i;
     396                 :         float out_r, out_g, out_b;
     397               0 :         for (i = 0; i < length; i++) {
     398               0 :                 float in_r = *src++;
     399               0 :                 float in_g = *src++;
     400               0 :                 float in_b = *src++;
     401                 : 
     402               0 :                 out_r = lut_interp_linear(in_r,
     403               0 :                                 transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
     404               0 :                 out_g = lut_interp_linear(in_g,
     405               0 :                                 transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
     406               0 :                 out_b = lut_interp_linear(in_b,
     407               0 :                                 transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
     408                 : 
     409               0 :                 *dest++ = clamp_float(out_r);
     410               0 :                 *dest++ = clamp_float(out_g);
     411               0 :                 *dest++ = clamp_float(out_b);
     412                 :         }
     413               0 : }
     414                 : 
     415               0 : static void qcms_transform_module_matrix_translate(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     416                 : {
     417                 :         size_t i;
     418                 :         struct matrix mat;
     419                 : 
     420                 :         /* store the results in column major mode
     421                 :          * this makes doing the multiplication with sse easier */
     422               0 :         mat.m[0][0] = transform->matrix.m[0][0];
     423               0 :         mat.m[1][0] = transform->matrix.m[0][1];
     424               0 :         mat.m[2][0] = transform->matrix.m[0][2];
     425               0 :         mat.m[0][1] = transform->matrix.m[1][0];
     426               0 :         mat.m[1][1] = transform->matrix.m[1][1];
     427               0 :         mat.m[2][1] = transform->matrix.m[1][2];
     428               0 :         mat.m[0][2] = transform->matrix.m[2][0];
     429               0 :         mat.m[1][2] = transform->matrix.m[2][1];
     430               0 :         mat.m[2][2] = transform->matrix.m[2][2];
     431                 : 
     432               0 :         for (i = 0; i < length; i++) {
     433               0 :                 float in_r = *src++;
     434               0 :                 float in_g = *src++;
     435               0 :                 float in_b = *src++;
     436                 : 
     437               0 :                 float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b + transform->tx;
     438               0 :                 float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b + transform->ty;
     439               0 :                 float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b + transform->tz;
     440                 : 
     441               0 :                 *dest++ = clamp_float(out_r);
     442               0 :                 *dest++ = clamp_float(out_g);
     443               0 :                 *dest++ = clamp_float(out_b);
     444                 :         }
     445               0 : }
     446                 : 
     447               0 : static void qcms_transform_module_matrix(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     448                 : {
     449                 :         size_t i;
     450                 :         struct matrix mat;
     451                 : 
     452                 :         /* store the results in column major mode
     453                 :          * this makes doing the multiplication with sse easier */
     454               0 :         mat.m[0][0] = transform->matrix.m[0][0];
     455               0 :         mat.m[1][0] = transform->matrix.m[0][1];
     456               0 :         mat.m[2][0] = transform->matrix.m[0][2];
     457               0 :         mat.m[0][1] = transform->matrix.m[1][0];
     458               0 :         mat.m[1][1] = transform->matrix.m[1][1];
     459               0 :         mat.m[2][1] = transform->matrix.m[1][2];
     460               0 :         mat.m[0][2] = transform->matrix.m[2][0];
     461               0 :         mat.m[1][2] = transform->matrix.m[2][1];
     462               0 :         mat.m[2][2] = transform->matrix.m[2][2];
     463                 : 
     464               0 :         for (i = 0; i < length; i++) {
     465               0 :                 float in_r = *src++;
     466               0 :                 float in_g = *src++;
     467               0 :                 float in_b = *src++;
     468                 : 
     469               0 :                 float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b;
     470               0 :                 float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b;
     471               0 :                 float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b;
     472                 : 
     473               0 :                 *dest++ = clamp_float(out_r);
     474               0 :                 *dest++ = clamp_float(out_g);
     475               0 :                 *dest++ = clamp_float(out_b);
     476                 :         }
     477               0 : }
     478                 : 
     479               0 : static struct qcms_modular_transform* qcms_modular_transform_alloc() {
     480               0 :         return calloc(1, sizeof(struct qcms_modular_transform));
     481                 : }
     482                 : 
     483               0 : static void qcms_modular_transform_release(struct qcms_modular_transform *transform)
     484                 : {
     485                 :         struct qcms_modular_transform *next_transform;
     486               0 :         while (transform != NULL) {
     487               0 :                 next_transform = transform->next_transform;
     488                 :                 // clut may use a single block of memory.
     489                 :                 // Perhaps we should remove this to simply the code.
     490               0 :                 if (transform->input_clut_table_r + transform->input_clut_table_length == transform->input_clut_table_g && transform->input_clut_table_g + transform->input_clut_table_length == transform->input_clut_table_b) {
     491               0 :                         if (transform->input_clut_table_r) free(transform->input_clut_table_r);
     492                 :                 } else {
     493               0 :                         if (transform->input_clut_table_r) free(transform->input_clut_table_r);
     494               0 :                         if (transform->input_clut_table_g) free(transform->input_clut_table_g);
     495               0 :                         if (transform->input_clut_table_b) free(transform->input_clut_table_b);
     496                 :                 }
     497               0 :                 if (transform->r_clut + 1 == transform->g_clut && transform->g_clut + 1 == transform->b_clut) {
     498               0 :                         if (transform->r_clut) free(transform->r_clut);
     499                 :                 } else {
     500               0 :                         if (transform->r_clut) free(transform->r_clut);
     501               0 :                         if (transform->g_clut) free(transform->g_clut);
     502               0 :                         if (transform->b_clut) free(transform->b_clut);
     503                 :                 }
     504               0 :                 if (transform->output_clut_table_r + transform->output_clut_table_length == transform->output_clut_table_g && transform->output_clut_table_g+ transform->output_clut_table_length == transform->output_clut_table_b) {
     505               0 :                         if (transform->output_clut_table_r) free(transform->output_clut_table_r);
     506                 :                 } else {
     507               0 :                         if (transform->output_clut_table_r) free(transform->output_clut_table_r);
     508               0 :                         if (transform->output_clut_table_g) free(transform->output_clut_table_g);
     509               0 :                         if (transform->output_clut_table_b) free(transform->output_clut_table_b);
     510                 :                 }
     511               0 :                 if (transform->output_gamma_lut_r) free(transform->output_gamma_lut_r);
     512               0 :                 if (transform->output_gamma_lut_g) free(transform->output_gamma_lut_g);
     513               0 :                 if (transform->output_gamma_lut_b) free(transform->output_gamma_lut_b);
     514               0 :                 free(transform);
     515               0 :                 transform = next_transform;
     516                 :         }
     517               0 : }
     518                 : 
     519                 : /* Set transform to be the next element in the linked list. */
     520               0 : static void append_transform(struct qcms_modular_transform *transform, struct qcms_modular_transform ***next_transform)
     521                 : {
     522               0 :         **next_transform = transform;
     523               0 :         while (transform) {
     524               0 :                 *next_transform = &(transform->next_transform);
     525               0 :                 transform = transform->next_transform;
     526                 :         }
     527               0 : }
     528                 : 
     529                 : /* reverse the transformation list (used by mBA) */
     530               0 : static struct qcms_modular_transform* reverse_transform(struct qcms_modular_transform *transform) 
     531                 : {
     532               0 :         struct qcms_modular_transform *prev_transform = NULL;
     533               0 :         while (transform != NULL) {
     534               0 :                 struct qcms_modular_transform *next_transform = transform->next_transform;
     535               0 :                 transform->next_transform = prev_transform;
     536               0 :                 prev_transform = transform;
     537               0 :                 transform = next_transform;
     538                 :         }
     539                 :         
     540               0 :         return prev_transform;
     541                 : }
     542                 : 
     543                 : #define EMPTY_TRANSFORM_LIST NULL
     544               0 : static struct qcms_modular_transform* qcms_modular_transform_create_mAB(struct lutmABType *lut)
     545                 : {
     546               0 :         struct qcms_modular_transform *first_transform = NULL;
     547               0 :         struct qcms_modular_transform **next_transform = &first_transform;
     548               0 :         struct qcms_modular_transform *transform = NULL;
     549                 : 
     550               0 :         if (lut->a_curves[0] != NULL) {
     551                 :                 size_t clut_length;
     552                 :                 float *clut;
     553                 : 
     554                 :                 // If the A curve is present this also implies the 
     555                 :                 // presence of a CLUT.
     556               0 :                 if (!lut->clut_table) 
     557               0 :                         goto fail;
     558                 : 
     559                 :                 // Prepare A curve.
     560               0 :                 transform = qcms_modular_transform_alloc();
     561               0 :                 if (!transform)
     562               0 :                         goto fail;
     563               0 :                 append_transform(transform, &next_transform);
     564               0 :                 transform->input_clut_table_r = build_input_gamma_table(lut->a_curves[0]);
     565               0 :                 transform->input_clut_table_g = build_input_gamma_table(lut->a_curves[1]);
     566               0 :                 transform->input_clut_table_b = build_input_gamma_table(lut->a_curves[2]);
     567               0 :                 transform->transform_module_fn = qcms_transform_module_gamma_table;
     568               0 :                 if (lut->num_grid_points[0] != lut->num_grid_points[1] ||
     569               0 :                         lut->num_grid_points[1] != lut->num_grid_points[2] ) {
     570                 :                         //XXX: We don't currently support clut that are not squared!
     571                 :                         goto fail;
     572                 :                 }
     573                 : 
     574                 :                 // Prepare CLUT
     575               0 :                 transform = qcms_modular_transform_alloc();
     576               0 :                 if (!transform) 
     577               0 :                         goto fail;
     578               0 :                 append_transform(transform, &next_transform);
     579               0 :                 clut_length = sizeof(float)*pow(lut->num_grid_points[0], 3)*3;
     580               0 :                 clut = malloc(clut_length);
     581               0 :                 if (!clut)
     582               0 :                         goto fail;
     583               0 :                 memcpy(clut, lut->clut_table, clut_length);
     584               0 :                 transform->r_clut = clut + 0;
     585               0 :                 transform->g_clut = clut + 1;
     586               0 :                 transform->b_clut = clut + 2;
     587               0 :                 transform->grid_size = lut->num_grid_points[0];
     588               0 :                 transform->transform_module_fn = qcms_transform_module_clut_only;
     589                 :         }
     590               0 :         if (lut->m_curves[0] != NULL) {
     591                 :                 // M curve imples the presence of a Matrix
     592                 : 
     593                 :                 // Prepare M curve
     594               0 :                 transform = qcms_modular_transform_alloc();
     595               0 :                 if (!transform)
     596               0 :                         goto fail;
     597               0 :                 append_transform(transform, &next_transform);
     598               0 :                 transform->input_clut_table_r = build_input_gamma_table(lut->m_curves[0]);
     599               0 :                 transform->input_clut_table_g = build_input_gamma_table(lut->m_curves[1]);
     600               0 :                 transform->input_clut_table_b = build_input_gamma_table(lut->m_curves[2]);
     601               0 :                 transform->transform_module_fn = qcms_transform_module_gamma_table;
     602                 : 
     603                 :                 // Prepare Matrix
     604               0 :                 transform = qcms_modular_transform_alloc();
     605               0 :                 if (!transform) 
     606               0 :                         goto fail;
     607               0 :                 append_transform(transform, &next_transform);
     608               0 :                 transform->matrix = build_mAB_matrix(lut);
     609               0 :                 if (transform->matrix.invalid)
     610               0 :                         goto fail;
     611               0 :                 transform->tx = s15Fixed16Number_to_float(lut->e03);
     612               0 :                 transform->ty = s15Fixed16Number_to_float(lut->e13);
     613               0 :                 transform->tz = s15Fixed16Number_to_float(lut->e23);
     614               0 :                 transform->transform_module_fn = qcms_transform_module_matrix_translate;
     615                 :         }
     616               0 :         if (lut->b_curves[0] != NULL) {
     617                 :                 // Prepare B curve
     618               0 :                 transform = qcms_modular_transform_alloc();
     619               0 :                 if (!transform) 
     620               0 :                         goto fail;
     621               0 :                 append_transform(transform, &next_transform);
     622               0 :                 transform->input_clut_table_r = build_input_gamma_table(lut->b_curves[0]);
     623               0 :                 transform->input_clut_table_g = build_input_gamma_table(lut->b_curves[1]);
     624               0 :                 transform->input_clut_table_b = build_input_gamma_table(lut->b_curves[2]);
     625               0 :                 transform->transform_module_fn = qcms_transform_module_gamma_table;
     626                 :         } else {
     627                 :                 // B curve is mandatory
     628               0 :                 goto fail;
     629                 :         }
     630                 : 
     631               0 :         if (lut->reversed) {
     632                 :                 // mBA are identical to mAB except that the transformation order
     633                 :                 // is reversed
     634               0 :                 first_transform = reverse_transform(first_transform);
     635                 :         }
     636                 : 
     637               0 :         return first_transform;
     638                 : fail:
     639               0 :         qcms_modular_transform_release(first_transform);
     640               0 :         return NULL;
     641                 : }
     642                 : 
     643               0 : static struct qcms_modular_transform* qcms_modular_transform_create_lut(struct lutType *lut)
     644                 : {
     645               0 :         struct qcms_modular_transform *first_transform = NULL;
     646               0 :         struct qcms_modular_transform **next_transform = &first_transform;
     647               0 :         struct qcms_modular_transform *transform = NULL;
     648                 : 
     649                 :         size_t in_curve_len, clut_length, out_curve_len;
     650                 :         float *in_curves, *clut, *out_curves;
     651                 : 
     652                 :         // Prepare Matrix
     653               0 :         transform = qcms_modular_transform_alloc();
     654               0 :         if (!transform) 
     655               0 :                 goto fail;
     656               0 :         append_transform(transform, &next_transform);
     657               0 :         transform->matrix = build_lut_matrix(lut);
     658               0 :         if (transform->matrix.invalid)
     659               0 :                 goto fail;
     660               0 :         transform->transform_module_fn = qcms_transform_module_matrix;
     661                 : 
     662                 :         // Prepare input curves
     663               0 :         transform = qcms_modular_transform_alloc();
     664               0 :         if (!transform) 
     665               0 :                 goto fail;
     666               0 :         append_transform(transform, &next_transform);
     667               0 :         in_curve_len = sizeof(float)*lut->num_input_table_entries * 3;
     668               0 :         in_curves = malloc(in_curve_len);
     669               0 :         if (!in_curves) 
     670               0 :                 goto fail;
     671               0 :         memcpy(in_curves, lut->input_table, in_curve_len);
     672               0 :         transform->input_clut_table_r = in_curves + lut->num_input_table_entries * 0;
     673               0 :         transform->input_clut_table_g = in_curves + lut->num_input_table_entries * 1;
     674               0 :         transform->input_clut_table_b = in_curves + lut->num_input_table_entries * 2;
     675               0 :         transform->input_clut_table_length = lut->num_input_table_entries;
     676                 : 
     677                 :         // Prepare table
     678               0 :         clut_length = sizeof(float)*pow(lut->num_clut_grid_points, 3)*3;
     679               0 :         clut = malloc(clut_length);
     680               0 :         if (!clut) 
     681               0 :                 goto fail;
     682               0 :         memcpy(clut, lut->clut_table, clut_length);
     683               0 :         transform->r_clut = clut + 0;
     684               0 :         transform->g_clut = clut + 1;
     685               0 :         transform->b_clut = clut + 2;
     686               0 :         transform->grid_size = lut->num_clut_grid_points;
     687                 : 
     688                 :         // Prepare output curves
     689               0 :         out_curve_len = sizeof(float) * lut->num_output_table_entries * 3;
     690               0 :         out_curves = malloc(out_curve_len);
     691               0 :         if (!out_curves) 
     692               0 :                 goto fail;
     693               0 :         memcpy(out_curves, lut->output_table, out_curve_len);
     694               0 :         transform->output_clut_table_r = out_curves + lut->num_output_table_entries * 0;
     695               0 :         transform->output_clut_table_g = out_curves + lut->num_output_table_entries * 1;
     696               0 :         transform->output_clut_table_b = out_curves + lut->num_output_table_entries * 2;
     697               0 :         transform->output_clut_table_length = lut->num_output_table_entries;
     698               0 :         transform->transform_module_fn = qcms_transform_module_clut;
     699                 : 
     700               0 :         return first_transform;
     701                 : fail:
     702               0 :         qcms_modular_transform_release(first_transform);
     703               0 :         return NULL;
     704                 : }
     705                 : 
     706               0 : struct qcms_modular_transform* qcms_modular_transform_create_input(qcms_profile *in)
     707                 : {
     708               0 :         struct qcms_modular_transform *first_transform = NULL;
     709               0 :         struct qcms_modular_transform **next_transform = &first_transform;
     710                 : 
     711               0 :         if (in->A2B0) {
     712                 :                 struct qcms_modular_transform *lut_transform;
     713               0 :                 lut_transform = qcms_modular_transform_create_lut(in->A2B0);
     714               0 :                 if (!lut_transform)
     715               0 :                         goto fail;
     716               0 :                 append_transform(lut_transform, &next_transform);
     717               0 :         } else if (in->mAB && in->mAB->num_in_channels == 3 && in->mAB->num_out_channels == 3) {
     718                 :                 struct qcms_modular_transform *mAB_transform;
     719               0 :                 mAB_transform = qcms_modular_transform_create_mAB(in->mAB);
     720               0 :                 if (!mAB_transform)
     721               0 :                         goto fail;
     722               0 :                 append_transform(mAB_transform, &next_transform);
     723                 : 
     724                 :         } else {
     725                 :                 struct qcms_modular_transform *transform;
     726                 : 
     727               0 :                 transform = qcms_modular_transform_alloc();
     728               0 :                 if (!transform)
     729               0 :                         goto fail;
     730               0 :                 append_transform(transform, &next_transform);
     731               0 :                 transform->input_clut_table_r = build_input_gamma_table(in->redTRC);
     732               0 :                 transform->input_clut_table_g = build_input_gamma_table(in->greenTRC);
     733               0 :                 transform->input_clut_table_b = build_input_gamma_table(in->blueTRC);
     734               0 :                 transform->transform_module_fn = qcms_transform_module_gamma_table;
     735               0 :                 if (!transform->input_clut_table_r || !transform->input_clut_table_g ||
     736               0 :                                 !transform->input_clut_table_b) {
     737                 :                         goto fail;
     738                 :                 }
     739                 : 
     740               0 :                 transform = qcms_modular_transform_alloc();
     741               0 :                 if (!transform) 
     742               0 :                         goto fail;
     743               0 :                 append_transform(transform, &next_transform);
     744               0 :                 transform->matrix.m[0][0] = 1/1.999969482421875f;
     745               0 :                 transform->matrix.m[0][1] = 0.f;
     746               0 :                 transform->matrix.m[0][2] = 0.f;
     747               0 :                 transform->matrix.m[1][0] = 0.f;
     748               0 :                 transform->matrix.m[1][1] = 1/1.999969482421875f;
     749               0 :                 transform->matrix.m[1][2] = 0.f;
     750               0 :                 transform->matrix.m[2][0] = 0.f;
     751               0 :                 transform->matrix.m[2][1] = 0.f;
     752               0 :                 transform->matrix.m[2][2] = 1/1.999969482421875f;
     753               0 :                 transform->matrix.invalid = false;
     754               0 :                 transform->transform_module_fn = qcms_transform_module_matrix;
     755                 : 
     756               0 :                 transform = qcms_modular_transform_alloc();
     757               0 :                 if (!transform) 
     758               0 :                         goto fail;
     759               0 :                 append_transform(transform, &next_transform);
     760               0 :                 transform->matrix = build_colorant_matrix(in);
     761               0 :                 transform->transform_module_fn = qcms_transform_module_matrix;
     762                 :         }
     763                 : 
     764               0 :         return first_transform;
     765                 : fail:
     766               0 :         qcms_modular_transform_release(first_transform);
     767               0 :         return EMPTY_TRANSFORM_LIST;
     768                 : }
     769               0 : static struct qcms_modular_transform* qcms_modular_transform_create_output(qcms_profile *out)
     770                 : {
     771               0 :         struct qcms_modular_transform *first_transform = NULL;
     772               0 :         struct qcms_modular_transform **next_transform = &first_transform;
     773                 : 
     774               0 :         if (out->B2A0) {
     775                 :                 struct qcms_modular_transform *lut_transform;
     776               0 :                 lut_transform = qcms_modular_transform_create_lut(out->B2A0);
     777               0 :                 if (!lut_transform) 
     778               0 :                         goto fail;
     779               0 :                 append_transform(lut_transform, &next_transform);
     780               0 :         } else if (out->mBA && out->mBA->num_in_channels == 3 && out->mBA->num_out_channels == 3) {
     781                 :                 struct qcms_modular_transform *lut_transform;
     782               0 :                 lut_transform = qcms_modular_transform_create_mAB(out->mBA);
     783               0 :                 if (!lut_transform) 
     784               0 :                         goto fail;
     785               0 :                 append_transform(lut_transform, &next_transform);
     786               0 :         } else if (out->redTRC && out->greenTRC && out->blueTRC) {
     787                 :                 struct qcms_modular_transform *transform;
     788                 : 
     789               0 :                 transform = qcms_modular_transform_alloc();
     790               0 :                 if (!transform) 
     791               0 :                         goto fail;
     792               0 :                 append_transform(transform, &next_transform);
     793               0 :                 transform->matrix = matrix_invert(build_colorant_matrix(out));
     794               0 :                 transform->transform_module_fn = qcms_transform_module_matrix;
     795                 : 
     796               0 :                 transform = qcms_modular_transform_alloc();
     797               0 :                 if (!transform) 
     798               0 :                         goto fail;
     799               0 :                 append_transform(transform, &next_transform);
     800               0 :                 transform->matrix.m[0][0] = 1.999969482421875f;
     801               0 :                 transform->matrix.m[0][1] = 0.f;
     802               0 :                 transform->matrix.m[0][2] = 0.f;
     803               0 :                 transform->matrix.m[1][0] = 0.f;
     804               0 :                 transform->matrix.m[1][1] = 1.999969482421875f;
     805               0 :                 transform->matrix.m[1][2] = 0.f;
     806               0 :                 transform->matrix.m[2][0] = 0.f;
     807               0 :                 transform->matrix.m[2][1] = 0.f;
     808               0 :                 transform->matrix.m[2][2] = 1.999969482421875f;
     809               0 :                 transform->matrix.invalid = false;
     810               0 :                 transform->transform_module_fn = qcms_transform_module_matrix;
     811                 : 
     812               0 :                 transform = qcms_modular_transform_alloc();
     813               0 :                 if (!transform) 
     814               0 :                         goto fail;
     815               0 :                 append_transform(transform, &next_transform);
     816               0 :                 build_output_lut(out->redTRC, &transform->output_gamma_lut_r,
     817                 :                         &transform->output_gamma_lut_r_length);
     818               0 :                 build_output_lut(out->greenTRC, &transform->output_gamma_lut_g,
     819                 :                         &transform->output_gamma_lut_g_length);
     820               0 :                 build_output_lut(out->blueTRC, &transform->output_gamma_lut_b,
     821                 :                         &transform->output_gamma_lut_b_length);
     822               0 :                 transform->transform_module_fn = qcms_transform_module_gamma_lut;
     823                 : 
     824               0 :                 if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g ||
     825               0 :                                 !transform->output_gamma_lut_b) {
     826                 :                         goto fail;
     827                 :                 }
     828                 :         } else {
     829               0 :                 assert(0 && "Unsupported output profile workflow.");
     830                 :                 return NULL;
     831                 :         }
     832                 : 
     833               0 :         return first_transform;
     834                 : fail:
     835               0 :         qcms_modular_transform_release(first_transform);
     836               0 :         return EMPTY_TRANSFORM_LIST;
     837                 : }
     838                 : 
     839                 : /* Not Completed
     840                 : // Simplify the transformation chain to an equivalent transformation chain
     841                 : static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
     842                 : {
     843                 :         struct qcms_modular_transform *first_transform = NULL;
     844                 :         struct qcms_modular_transform *curr_trans = transform;
     845                 :         struct qcms_modular_transform *prev_trans = NULL;
     846                 :         while (curr_trans) {
     847                 :                 struct qcms_modular_transform *next_trans = curr_trans->next_transform;
     848                 :                 if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
     849                 :                         if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
     850                 :                                 curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
     851                 :                                 goto remove_next;       
     852                 :                         }
     853                 :                 }
     854                 :                 if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
     855                 :                         bool isLinear = true;
     856                 :                         uint16_t i;
     857                 :                         for (i = 0; isLinear && i < 256; i++) {
     858                 :                                 isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
     859                 :                                 isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
     860                 :                                 isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
     861                 :                         }
     862                 :                         goto remove_current;
     863                 :                 }
     864                 :                 
     865                 : next_transform:
     866                 :                 if (!next_trans) break;
     867                 :                 prev_trans = curr_trans;
     868                 :                 curr_trans = next_trans;
     869                 :                 continue;
     870                 : remove_current:
     871                 :                 if (curr_trans == transform) {
     872                 :                         //Update head
     873                 :                         transform = next_trans;
     874                 :                 } else {
     875                 :                         prev_trans->next_transform = next_trans;
     876                 :                 }
     877                 :                 curr_trans->next_transform = NULL;
     878                 :                 qcms_modular_transform_release(curr_trans);
     879                 :                 //return transform;
     880                 :                 return qcms_modular_transform_reduce(transform);
     881                 : remove_next:
     882                 :                 curr_trans->next_transform = next_trans->next_transform;
     883                 :                 next_trans->next_transform = NULL;
     884                 :                 qcms_modular_transform_release(next_trans);
     885                 :                 continue;
     886                 :         }
     887                 :         return transform;
     888                 : }
     889                 : */
     890                 : 
     891               0 : static struct qcms_modular_transform* qcms_modular_transform_create(qcms_profile *in, qcms_profile *out)
     892                 : {
     893               0 :         struct qcms_modular_transform *first_transform = NULL;
     894               0 :         struct qcms_modular_transform **next_transform = &first_transform;
     895                 : 
     896               0 :         if (in->color_space == RGB_SIGNATURE) {
     897                 :                 struct qcms_modular_transform* rgb_to_pcs;
     898               0 :                 rgb_to_pcs = qcms_modular_transform_create_input(in);
     899               0 :                 if (!rgb_to_pcs) 
     900               0 :                         goto fail;
     901               0 :                 append_transform(rgb_to_pcs, &next_transform);
     902                 :         } else {
     903               0 :                 assert(0 && "input color space not supported");
     904                 :                 goto fail;
     905                 :         }
     906                 : 
     907               0 :         if (in->pcs == LAB_SIGNATURE && out->pcs == XYZ_SIGNATURE) {
     908                 :                 struct qcms_modular_transform* lab_to_pcs;
     909               0 :                 lab_to_pcs = qcms_modular_transform_alloc();
     910               0 :                 if (!lab_to_pcs) 
     911               0 :                         goto fail;
     912               0 :                 append_transform(lab_to_pcs, &next_transform);
     913               0 :                 lab_to_pcs->transform_module_fn = qcms_transform_module_LAB_to_XYZ;
     914                 :         }
     915                 : 
     916                 :         // This does not improve accuracy in practice, something is wrong here.
     917                 :         //if (in->chromaticAdaption.invalid == false) {
     918                 :         //      struct qcms_modular_transform* chromaticAdaption;
     919                 :         //      chromaticAdaption = qcms_modular_transform_alloc();
     920                 :         //      if (!chromaticAdaption) 
     921                 :         //              goto fail;
     922                 :         //      append_transform(chromaticAdaption, &next_transform);
     923                 :         //      chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
     924                 :         //      chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
     925                 :         //}
     926                 : 
     927               0 :         if (in->pcs == XYZ_SIGNATURE && out->pcs == LAB_SIGNATURE) {
     928                 :                 struct qcms_modular_transform* pcs_to_lab;
     929               0 :                 pcs_to_lab = qcms_modular_transform_alloc();
     930               0 :                 if (!pcs_to_lab) 
     931               0 :                         goto fail;
     932               0 :                 append_transform(pcs_to_lab, &next_transform);
     933               0 :                 pcs_to_lab->transform_module_fn = qcms_transform_module_XYZ_to_LAB;
     934                 :         }
     935                 : 
     936               0 :         if (out->color_space == RGB_SIGNATURE) {
     937                 :                 struct qcms_modular_transform* pcs_to_rgb;
     938               0 :                 pcs_to_rgb = qcms_modular_transform_create_output(out);
     939               0 :                 if (!pcs_to_rgb) 
     940               0 :                         goto fail;
     941               0 :                 append_transform(pcs_to_rgb, &next_transform);
     942                 :         } else {
     943               0 :                 assert(0 && "output color space not supported");
     944                 :                 goto fail;
     945                 :         }
     946                 :         // Not Completed
     947                 :         //return qcms_modular_transform_reduce(first_transform);
     948               0 :         return first_transform;
     949                 : fail:
     950               0 :         qcms_modular_transform_release(first_transform);
     951               0 :         return EMPTY_TRANSFORM_LIST;
     952                 : }
     953                 : 
     954               0 : static float* qcms_modular_transform_data(struct qcms_modular_transform *transform, float *src, float *dest, size_t len)
     955                 : {
     956               0 :         while (transform != NULL) {
     957                 :                 // Keep swaping src/dest when performing a transform to use less memory.
     958               0 :                 float *new_src = dest;
     959               0 :                 const transform_module_fn_t transform_fn = transform->transform_module_fn;
     960               0 :                 if (transform_fn != qcms_transform_module_gamma_table &&
     961               0 :                     transform_fn != qcms_transform_module_gamma_lut &&
     962               0 :                     transform_fn != qcms_transform_module_clut &&
     963               0 :                     transform_fn != qcms_transform_module_clut_only &&
     964               0 :                     transform_fn != qcms_transform_module_matrix &&
     965               0 :                     transform_fn != qcms_transform_module_matrix_translate &&
     966               0 :                     transform_fn != qcms_transform_module_LAB_to_XYZ &&
     967                 :                     transform_fn != qcms_transform_module_XYZ_to_LAB) {
     968               0 :                         assert(0 && "Unsupported transform module");
     969                 :                         return NULL;
     970                 :                 }
     971               0 :                 transform->transform_module_fn(transform,src,dest,len);
     972               0 :                 dest = src;
     973               0 :                 src = new_src;
     974               0 :                 transform = transform->next_transform;
     975                 :         }
     976                 :         // The results end up in the src buffer because of the switching
     977               0 :         return src;
     978                 : }
     979                 : 
     980               0 : float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize)
     981                 : {
     982               0 :         struct qcms_modular_transform *transform_list = qcms_modular_transform_create(in, out);
     983               0 :         if (transform_list != NULL) {
     984               0 :                 float *lut = qcms_modular_transform_data(transform_list, src, dest, lutSize/3);
     985               0 :                 qcms_modular_transform_release(transform_list);
     986               0 :                 return lut;
     987                 :         }
     988               0 :         return NULL;
     989                 : }

Generated by: LCOV version 1.7