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

Generated by: LCOV version 1.13