gfx/qcms/chain.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/qcms/chain.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,989 @@
     1.4 +/* vim: set ts=8 sw=8 noexpandtab: */
     1.5 +//  qcms
     1.6 +//  Copyright (C) 2009 Mozilla Corporation
     1.7 +//  Copyright (C) 1998-2007 Marti Maria
     1.8 +//
     1.9 +// Permission is hereby granted, free of charge, to any person obtaining 
    1.10 +// a copy of this software and associated documentation files (the "Software"), 
    1.11 +// to deal in the Software without restriction, including without limitation 
    1.12 +// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
    1.13 +// and/or sell copies of the Software, and to permit persons to whom the Software 
    1.14 +// is furnished to do so, subject to the following conditions:
    1.15 +//
    1.16 +// The above copyright notice and this permission notice shall be included in 
    1.17 +// all copies or substantial portions of the Software.
    1.18 +//
    1.19 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
    1.20 +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
    1.21 +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
    1.22 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
    1.23 +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
    1.24 +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
    1.25 +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    1.26 +
    1.27 +#include <stdlib.h>
    1.28 +#include <math.h>
    1.29 +#include <assert.h>
    1.30 +#include <string.h> //memcpy
    1.31 +#include "qcmsint.h"
    1.32 +#include "transform_util.h"
    1.33 +#include "matrix.h"
    1.34 +
    1.35 +static struct matrix build_lut_matrix(struct lutType *lut)
    1.36 +{
    1.37 +	struct matrix result;
    1.38 +	if (lut) {
    1.39 +		result.m[0][0] = s15Fixed16Number_to_float(lut->e00);
    1.40 +		result.m[0][1] = s15Fixed16Number_to_float(lut->e01);
    1.41 +		result.m[0][2] = s15Fixed16Number_to_float(lut->e02);
    1.42 +		result.m[1][0] = s15Fixed16Number_to_float(lut->e10);
    1.43 +		result.m[1][1] = s15Fixed16Number_to_float(lut->e11);
    1.44 +		result.m[1][2] = s15Fixed16Number_to_float(lut->e12);
    1.45 +		result.m[2][0] = s15Fixed16Number_to_float(lut->e20);
    1.46 +		result.m[2][1] = s15Fixed16Number_to_float(lut->e21);
    1.47 +		result.m[2][2] = s15Fixed16Number_to_float(lut->e22);
    1.48 +		result.invalid = false;
    1.49 +	} else {
    1.50 +		memset(&result, 0, sizeof(struct matrix));
    1.51 +		result.invalid = true;
    1.52 +	}
    1.53 +	return result;
    1.54 +}
    1.55 +
    1.56 +static struct matrix build_mAB_matrix(struct lutmABType *lut)
    1.57 +{
    1.58 +	struct matrix result;
    1.59 +	if (lut) {
    1.60 +		result.m[0][0] = s15Fixed16Number_to_float(lut->e00);
    1.61 +		result.m[0][1] = s15Fixed16Number_to_float(lut->e01);
    1.62 +		result.m[0][2] = s15Fixed16Number_to_float(lut->e02);
    1.63 +		result.m[1][0] = s15Fixed16Number_to_float(lut->e10);
    1.64 +		result.m[1][1] = s15Fixed16Number_to_float(lut->e11);
    1.65 +		result.m[1][2] = s15Fixed16Number_to_float(lut->e12);
    1.66 +		result.m[2][0] = s15Fixed16Number_to_float(lut->e20);
    1.67 +		result.m[2][1] = s15Fixed16Number_to_float(lut->e21);
    1.68 +		result.m[2][2] = s15Fixed16Number_to_float(lut->e22);
    1.69 +		result.invalid = false;
    1.70 +	} else {
    1.71 +		memset(&result, 0, sizeof(struct matrix));
    1.72 +		result.invalid = true;
    1.73 +	}
    1.74 +	return result;
    1.75 +}
    1.76 +
    1.77 +//Based on lcms cmsLab2XYZ
    1.78 +#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)
    1.79 +#define f_1(t) (t <= (24.0f/116.0f)) ? ((108.0/841.0) * (t - (16.0/116.0))) : (t * t * t)
    1.80 +static void qcms_transform_module_LAB_to_XYZ(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    1.81 +{
    1.82 +	size_t i;
    1.83 +	// lcms: D50 XYZ values
    1.84 +	float WhitePointX = 0.9642f;
    1.85 +	float WhitePointY = 1.0f;
    1.86 +	float WhitePointZ = 0.8249f;
    1.87 +	for (i = 0; i < length; i++) {
    1.88 +		float device_L = *src++ * 100.0f;
    1.89 +		float device_a = *src++ * 255.0f - 128.0f;
    1.90 +		float device_b = *src++ * 255.0f - 128.0f;
    1.91 +		float y = (device_L + 16.0f) / 116.0f;
    1.92 +
    1.93 +		float X = f_1((y + 0.002f * device_a)) * WhitePointX;
    1.94 +		float Y = f_1(y) * WhitePointY;
    1.95 +		float Z = f_1((y - 0.005f * device_b)) * WhitePointZ;
    1.96 +		*dest++ = X / (1.0 + 32767.0/32768.0);
    1.97 +		*dest++ = Y / (1.0 + 32767.0/32768.0);
    1.98 +		*dest++ = Z / (1.0 + 32767.0/32768.0);
    1.99 +	}
   1.100 +}
   1.101 +
   1.102 +//Based on lcms cmsXYZ2Lab
   1.103 +static void qcms_transform_module_XYZ_to_LAB(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.104 +{
   1.105 +	size_t i;
   1.106 +        // lcms: D50 XYZ values
   1.107 +        float WhitePointX = 0.9642f;
   1.108 +        float WhitePointY = 1.0f;
   1.109 +        float WhitePointZ = 0.8249f;
   1.110 +        for (i = 0; i < length; i++) {
   1.111 +                float device_x = *src++ * (1.0 + 32767.0/32768.0) / WhitePointX;
   1.112 +                float device_y = *src++ * (1.0 + 32767.0/32768.0) / WhitePointY;
   1.113 +                float device_z = *src++ * (1.0 + 32767.0/32768.0) / WhitePointZ;
   1.114 +
   1.115 +		float fx = f(device_x);
   1.116 +		float fy = f(device_y);
   1.117 +		float fz = f(device_z);
   1.118 +
   1.119 +                float L = 116.0f*fy - 16.0f;
   1.120 +                float a = 500.0f*(fx - fy);
   1.121 +                float b = 200.0f*(fy - fz);
   1.122 +                *dest++ = L / 100.0f;
   1.123 +                *dest++ = (a+128.0f) / 255.0f;
   1.124 +                *dest++ = (b+128.0f) / 255.0f;
   1.125 +        }
   1.126 +
   1.127 +}
   1.128 +
   1.129 +static void qcms_transform_module_clut_only(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.130 +{
   1.131 +	size_t i;
   1.132 +	int xy_len = 1;
   1.133 +	int x_len = transform->grid_size;
   1.134 +	int len = x_len * x_len;
   1.135 +	float* r_table = transform->r_clut;
   1.136 +	float* g_table = transform->g_clut;
   1.137 +	float* b_table = transform->b_clut;
   1.138 +
   1.139 +	for (i = 0; i < length; i++) {
   1.140 +		float linear_r = *src++;
   1.141 +		float linear_g = *src++;
   1.142 +		float linear_b = *src++;
   1.143 +
   1.144 +		int x = floorf(linear_r * (transform->grid_size-1));
   1.145 +		int y = floorf(linear_g * (transform->grid_size-1));
   1.146 +		int z = floorf(linear_b * (transform->grid_size-1));
   1.147 +		int x_n = ceilf(linear_r * (transform->grid_size-1));
   1.148 +		int y_n = ceilf(linear_g * (transform->grid_size-1));
   1.149 +		int z_n = ceilf(linear_b * (transform->grid_size-1));
   1.150 +		float x_d = linear_r * (transform->grid_size-1) - x;
   1.151 +		float y_d = linear_g * (transform->grid_size-1) - y;
   1.152 +		float z_d = linear_b * (transform->grid_size-1) - z;
   1.153 +
   1.154 +		float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
   1.155 +		float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
   1.156 +		float r_y1 = lerp(r_x1, r_x2, y_d);
   1.157 +		float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
   1.158 +		float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
   1.159 +		float r_y2 = lerp(r_x3, r_x4, y_d);
   1.160 +		float clut_r = lerp(r_y1, r_y2, z_d);
   1.161 +
   1.162 +		float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
   1.163 +		float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
   1.164 +		float g_y1 = lerp(g_x1, g_x2, y_d);
   1.165 +		float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
   1.166 +		float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
   1.167 +		float g_y2 = lerp(g_x3, g_x4, y_d);
   1.168 +		float clut_g = lerp(g_y1, g_y2, z_d);
   1.169 +
   1.170 +		float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
   1.171 +		float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
   1.172 +		float b_y1 = lerp(b_x1, b_x2, y_d);
   1.173 +		float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
   1.174 +		float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
   1.175 +		float b_y2 = lerp(b_x3, b_x4, y_d);
   1.176 +		float clut_b = lerp(b_y1, b_y2, z_d);
   1.177 +
   1.178 +		*dest++ = clamp_float(clut_r);
   1.179 +		*dest++ = clamp_float(clut_g);
   1.180 +		*dest++ = clamp_float(clut_b);
   1.181 +	}
   1.182 +}
   1.183 +
   1.184 +static void qcms_transform_module_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.185 +{
   1.186 +	size_t i;
   1.187 +	int xy_len = 1;
   1.188 +	int x_len = transform->grid_size;
   1.189 +	int len = x_len * x_len;
   1.190 +	float* r_table = transform->r_clut;
   1.191 +	float* g_table = transform->g_clut;
   1.192 +	float* b_table = transform->b_clut;
   1.193 +	for (i = 0; i < length; i++) {
   1.194 +		float device_r = *src++;
   1.195 +		float device_g = *src++;
   1.196 +		float device_b = *src++;
   1.197 +		float linear_r = lut_interp_linear_float(device_r,
   1.198 +				transform->input_clut_table_r, transform->input_clut_table_length);
   1.199 +		float linear_g = lut_interp_linear_float(device_g,
   1.200 +				transform->input_clut_table_g, transform->input_clut_table_length);
   1.201 +		float linear_b = lut_interp_linear_float(device_b,
   1.202 +				transform->input_clut_table_b, transform->input_clut_table_length);
   1.203 +
   1.204 +		int x = floorf(linear_r * (transform->grid_size-1));
   1.205 +		int y = floorf(linear_g * (transform->grid_size-1));
   1.206 +		int z = floorf(linear_b * (transform->grid_size-1));
   1.207 +		int x_n = ceilf(linear_r * (transform->grid_size-1));
   1.208 +		int y_n = ceilf(linear_g * (transform->grid_size-1));
   1.209 +		int z_n = ceilf(linear_b * (transform->grid_size-1));
   1.210 +		float x_d = linear_r * (transform->grid_size-1) - x;
   1.211 +		float y_d = linear_g * (transform->grid_size-1) - y;
   1.212 +		float z_d = linear_b * (transform->grid_size-1) - z;
   1.213 +
   1.214 +		float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
   1.215 +		float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
   1.216 +		float r_y1 = lerp(r_x1, r_x2, y_d);
   1.217 +		float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
   1.218 +		float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
   1.219 +		float r_y2 = lerp(r_x3, r_x4, y_d);
   1.220 +		float clut_r = lerp(r_y1, r_y2, z_d);
   1.221 +
   1.222 +		float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
   1.223 +		float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
   1.224 +		float g_y1 = lerp(g_x1, g_x2, y_d);
   1.225 +		float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
   1.226 +		float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
   1.227 +		float g_y2 = lerp(g_x3, g_x4, y_d);
   1.228 +		float clut_g = lerp(g_y1, g_y2, z_d);
   1.229 +
   1.230 +		float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
   1.231 +		float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
   1.232 +		float b_y1 = lerp(b_x1, b_x2, y_d);
   1.233 +		float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
   1.234 +		float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
   1.235 +		float b_y2 = lerp(b_x3, b_x4, y_d);
   1.236 +		float clut_b = lerp(b_y1, b_y2, z_d);
   1.237 +
   1.238 +		float pcs_r = lut_interp_linear_float(clut_r,
   1.239 +				transform->output_clut_table_r, transform->output_clut_table_length);
   1.240 +		float pcs_g = lut_interp_linear_float(clut_g,
   1.241 +				transform->output_clut_table_g, transform->output_clut_table_length);
   1.242 +		float pcs_b = lut_interp_linear_float(clut_b,
   1.243 +				transform->output_clut_table_b, transform->output_clut_table_length);
   1.244 +
   1.245 +		*dest++ = clamp_float(pcs_r);
   1.246 +		*dest++ = clamp_float(pcs_g);
   1.247 +		*dest++ = clamp_float(pcs_b);
   1.248 +	}
   1.249 +}
   1.250 +
   1.251 +/* NOT USED
   1.252 +static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.253 +{
   1.254 +	size_t i;
   1.255 +	int xy_len = 1;
   1.256 +	int x_len = transform->grid_size;
   1.257 +	int len = x_len * x_len;
   1.258 +	float* r_table = transform->r_clut;
   1.259 +	float* g_table = transform->g_clut;
   1.260 +	float* b_table = transform->b_clut;
   1.261 +	float c0_r, c1_r, c2_r, c3_r;
   1.262 +	float c0_g, c1_g, c2_g, c3_g;
   1.263 +	float c0_b, c1_b, c2_b, c3_b;
   1.264 +	float clut_r, clut_g, clut_b;
   1.265 +	float pcs_r, pcs_g, pcs_b;
   1.266 +	for (i = 0; i < length; i++) {
   1.267 +		float device_r = *src++;
   1.268 +		float device_g = *src++;
   1.269 +		float device_b = *src++;
   1.270 +		float linear_r = lut_interp_linear_float(device_r,
   1.271 +				transform->input_clut_table_r, transform->input_clut_table_length);
   1.272 +		float linear_g = lut_interp_linear_float(device_g,
   1.273 +				transform->input_clut_table_g, transform->input_clut_table_length);
   1.274 +		float linear_b = lut_interp_linear_float(device_b,
   1.275 +				transform->input_clut_table_b, transform->input_clut_table_length);
   1.276 +
   1.277 +		int x = floorf(linear_r * (transform->grid_size-1));
   1.278 +		int y = floorf(linear_g * (transform->grid_size-1));
   1.279 +		int z = floorf(linear_b * (transform->grid_size-1));
   1.280 +		int x_n = ceilf(linear_r * (transform->grid_size-1));
   1.281 +		int y_n = ceilf(linear_g * (transform->grid_size-1));
   1.282 +		int z_n = ceilf(linear_b * (transform->grid_size-1));
   1.283 +		float rx = linear_r * (transform->grid_size-1) - x;
   1.284 +		float ry = linear_g * (transform->grid_size-1) - y;
   1.285 +		float rz = linear_b * (transform->grid_size-1) - z;
   1.286 +
   1.287 +		c0_r = CLU(r_table, x, y, z);
   1.288 +		c0_g = CLU(g_table, x, y, z);
   1.289 +		c0_b = CLU(b_table, x, y, z);
   1.290 +		if( rx >= ry ) {
   1.291 +			if (ry >= rz) { //rx >= ry && ry >= rz
   1.292 +				c1_r = CLU(r_table, x_n, y, z) - c0_r;
   1.293 +				c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
   1.294 +				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
   1.295 +				c1_g = CLU(g_table, x_n, y, z) - c0_g;
   1.296 +				c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
   1.297 +				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
   1.298 +				c1_b = CLU(b_table, x_n, y, z) - c0_b;
   1.299 +				c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
   1.300 +				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
   1.301 +			} else {
   1.302 +				if (rx >= rz) { //rx >= rz && rz >= ry
   1.303 +					c1_r = CLU(r_table, x_n, y, z) - c0_r;
   1.304 +					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
   1.305 +					c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
   1.306 +					c1_g = CLU(g_table, x_n, y, z) - c0_g;
   1.307 +					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
   1.308 +					c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
   1.309 +					c1_b = CLU(b_table, x_n, y, z) - c0_b;
   1.310 +					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
   1.311 +					c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
   1.312 +				} else { //rz > rx && rx >= ry
   1.313 +					c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
   1.314 +					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
   1.315 +					c3_r = CLU(r_table, x, y, z_n) - c0_r;
   1.316 +					c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
   1.317 +					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
   1.318 +					c3_g = CLU(g_table, x, y, z_n) - c0_g;
   1.319 +					c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
   1.320 +					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
   1.321 +					c3_b = CLU(b_table, x, y, z_n) - c0_b;
   1.322 +				}
   1.323 +			}
   1.324 +		} else {
   1.325 +			if (rx >= rz) { //ry > rx && rx >= rz
   1.326 +				c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
   1.327 +				c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
   1.328 +				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
   1.329 +				c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
   1.330 +				c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
   1.331 +				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
   1.332 +				c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
   1.333 +				c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
   1.334 +				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
   1.335 +			} else {
   1.336 +				if (ry >= rz) { //ry >= rz && rz > rx 
   1.337 +					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
   1.338 +					c2_r = CLU(r_table, x, y_n, z) - c0_r;
   1.339 +					c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
   1.340 +					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
   1.341 +					c2_g = CLU(g_table, x, y_n, z) - c0_g;
   1.342 +					c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
   1.343 +					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
   1.344 +					c2_b = CLU(b_table, x, y_n, z) - c0_b;
   1.345 +					c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
   1.346 +				} else { //rz > ry && ry > rx
   1.347 +					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
   1.348 +					c2_r = CLU(r_table, x, y_n, z) - c0_r;
   1.349 +					c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
   1.350 +					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
   1.351 +					c2_g = CLU(g_table, x, y_n, z) - c0_g;
   1.352 +					c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
   1.353 +					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
   1.354 +					c2_b = CLU(b_table, x, y_n, z) - c0_b;
   1.355 +					c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
   1.356 +				}
   1.357 +			}
   1.358 +		}
   1.359 +
   1.360 +		clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
   1.361 +		clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
   1.362 +		clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
   1.363 +
   1.364 +		pcs_r = lut_interp_linear_float(clut_r,
   1.365 +				transform->output_clut_table_r, transform->output_clut_table_length);
   1.366 +		pcs_g = lut_interp_linear_float(clut_g,
   1.367 +				transform->output_clut_table_g, transform->output_clut_table_length);
   1.368 +		pcs_b = lut_interp_linear_float(clut_b,
   1.369 +				transform->output_clut_table_b, transform->output_clut_table_length);
   1.370 +		*dest++ = clamp_float(pcs_r);
   1.371 +		*dest++ = clamp_float(pcs_g);
   1.372 +		*dest++ = clamp_float(pcs_b);
   1.373 +	}
   1.374 +}
   1.375 +*/
   1.376 +
   1.377 +static void qcms_transform_module_gamma_table(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.378 +{
   1.379 +	size_t i;
   1.380 +	float out_r, out_g, out_b;
   1.381 +	for (i = 0; i < length; i++) {
   1.382 +		float in_r = *src++;
   1.383 +		float in_g = *src++;
   1.384 +		float in_b = *src++;
   1.385 +
   1.386 +		out_r = lut_interp_linear_float(in_r, transform->input_clut_table_r, 256);
   1.387 +		out_g = lut_interp_linear_float(in_g, transform->input_clut_table_g, 256);
   1.388 +		out_b = lut_interp_linear_float(in_b, transform->input_clut_table_b, 256);
   1.389 +
   1.390 +		*dest++ = clamp_float(out_r);
   1.391 +		*dest++ = clamp_float(out_g);
   1.392 +		*dest++ = clamp_float(out_b);
   1.393 +	}
   1.394 +}
   1.395 +
   1.396 +static void qcms_transform_module_gamma_lut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.397 +{
   1.398 +	size_t i;
   1.399 +	float out_r, out_g, out_b;
   1.400 +	for (i = 0; i < length; i++) {
   1.401 +		float in_r = *src++;
   1.402 +		float in_g = *src++;
   1.403 +		float in_b = *src++;
   1.404 +
   1.405 +		out_r = lut_interp_linear(in_r,
   1.406 +				transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
   1.407 +		out_g = lut_interp_linear(in_g,
   1.408 +				transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
   1.409 +		out_b = lut_interp_linear(in_b,
   1.410 +				transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
   1.411 +
   1.412 +		*dest++ = clamp_float(out_r);
   1.413 +		*dest++ = clamp_float(out_g);
   1.414 +		*dest++ = clamp_float(out_b);
   1.415 +	}
   1.416 +}
   1.417 +
   1.418 +static void qcms_transform_module_matrix_translate(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.419 +{
   1.420 +	size_t i;
   1.421 +	struct matrix mat;
   1.422 +
   1.423 +	/* store the results in column major mode
   1.424 +	 * this makes doing the multiplication with sse easier */
   1.425 +	mat.m[0][0] = transform->matrix.m[0][0];
   1.426 +	mat.m[1][0] = transform->matrix.m[0][1];
   1.427 +	mat.m[2][0] = transform->matrix.m[0][2];
   1.428 +	mat.m[0][1] = transform->matrix.m[1][0];
   1.429 +	mat.m[1][1] = transform->matrix.m[1][1];
   1.430 +	mat.m[2][1] = transform->matrix.m[1][2];
   1.431 +	mat.m[0][2] = transform->matrix.m[2][0];
   1.432 +	mat.m[1][2] = transform->matrix.m[2][1];
   1.433 +	mat.m[2][2] = transform->matrix.m[2][2];
   1.434 +
   1.435 +	for (i = 0; i < length; i++) {
   1.436 +		float in_r = *src++;
   1.437 +		float in_g = *src++;
   1.438 +		float in_b = *src++;
   1.439 +
   1.440 +		float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b + transform->tx;
   1.441 +		float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b + transform->ty;
   1.442 +		float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b + transform->tz;
   1.443 +
   1.444 +		*dest++ = clamp_float(out_r);
   1.445 +		*dest++ = clamp_float(out_g);
   1.446 +		*dest++ = clamp_float(out_b);
   1.447 +	}
   1.448 +}
   1.449 +
   1.450 +static void qcms_transform_module_matrix(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
   1.451 +{
   1.452 +	size_t i;
   1.453 +	struct matrix mat;
   1.454 +
   1.455 +	/* store the results in column major mode
   1.456 +	 * this makes doing the multiplication with sse easier */
   1.457 +	mat.m[0][0] = transform->matrix.m[0][0];
   1.458 +	mat.m[1][0] = transform->matrix.m[0][1];
   1.459 +	mat.m[2][0] = transform->matrix.m[0][2];
   1.460 +	mat.m[0][1] = transform->matrix.m[1][0];
   1.461 +	mat.m[1][1] = transform->matrix.m[1][1];
   1.462 +	mat.m[2][1] = transform->matrix.m[1][2];
   1.463 +	mat.m[0][2] = transform->matrix.m[2][0];
   1.464 +	mat.m[1][2] = transform->matrix.m[2][1];
   1.465 +	mat.m[2][2] = transform->matrix.m[2][2];
   1.466 +
   1.467 +	for (i = 0; i < length; i++) {
   1.468 +		float in_r = *src++;
   1.469 +		float in_g = *src++;
   1.470 +		float in_b = *src++;
   1.471 +
   1.472 +		float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b;
   1.473 +		float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b;
   1.474 +		float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b;
   1.475 +
   1.476 +		*dest++ = clamp_float(out_r);
   1.477 +		*dest++ = clamp_float(out_g);
   1.478 +		*dest++ = clamp_float(out_b);
   1.479 +	}
   1.480 +}
   1.481 +
   1.482 +static struct qcms_modular_transform* qcms_modular_transform_alloc() {
   1.483 +	return calloc(1, sizeof(struct qcms_modular_transform));
   1.484 +}
   1.485 +
   1.486 +static void qcms_modular_transform_release(struct qcms_modular_transform *transform)
   1.487 +{
   1.488 +	struct qcms_modular_transform *next_transform;
   1.489 +	while (transform != NULL) {
   1.490 +		next_transform = transform->next_transform;
   1.491 +		// clut may use a single block of memory.
   1.492 +		// Perhaps we should remove this to simply the code.
   1.493 +		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) {
   1.494 +			if (transform->input_clut_table_r) free(transform->input_clut_table_r);
   1.495 +		} else {
   1.496 +			if (transform->input_clut_table_r) free(transform->input_clut_table_r);
   1.497 +			if (transform->input_clut_table_g) free(transform->input_clut_table_g);
   1.498 +			if (transform->input_clut_table_b) free(transform->input_clut_table_b);
   1.499 +		}
   1.500 +		if (transform->r_clut + 1 == transform->g_clut && transform->g_clut + 1 == transform->b_clut) {
   1.501 +			if (transform->r_clut) free(transform->r_clut);
   1.502 +		} else {
   1.503 +			if (transform->r_clut) free(transform->r_clut);
   1.504 +			if (transform->g_clut) free(transform->g_clut);
   1.505 +			if (transform->b_clut) free(transform->b_clut);
   1.506 +		}
   1.507 +		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) {
   1.508 +			if (transform->output_clut_table_r) free(transform->output_clut_table_r);
   1.509 +		} else {
   1.510 +			if (transform->output_clut_table_r) free(transform->output_clut_table_r);
   1.511 +			if (transform->output_clut_table_g) free(transform->output_clut_table_g);
   1.512 +			if (transform->output_clut_table_b) free(transform->output_clut_table_b);
   1.513 +		}
   1.514 +		if (transform->output_gamma_lut_r) free(transform->output_gamma_lut_r);
   1.515 +		if (transform->output_gamma_lut_g) free(transform->output_gamma_lut_g);
   1.516 +		if (transform->output_gamma_lut_b) free(transform->output_gamma_lut_b);
   1.517 +		free(transform);
   1.518 +		transform = next_transform;
   1.519 +	}
   1.520 +}
   1.521 +
   1.522 +/* Set transform to be the next element in the linked list. */
   1.523 +static void append_transform(struct qcms_modular_transform *transform, struct qcms_modular_transform ***next_transform)
   1.524 +{
   1.525 +	**next_transform = transform;
   1.526 +	while (transform) {
   1.527 +		*next_transform = &(transform->next_transform);
   1.528 +		transform = transform->next_transform;
   1.529 +	}
   1.530 +}
   1.531 +
   1.532 +/* reverse the transformation list (used by mBA) */
   1.533 +static struct qcms_modular_transform* reverse_transform(struct qcms_modular_transform *transform) 
   1.534 +{
   1.535 +	struct qcms_modular_transform *prev_transform = NULL;
   1.536 +	while (transform != NULL) {
   1.537 +		struct qcms_modular_transform *next_transform = transform->next_transform;
   1.538 +		transform->next_transform = prev_transform;
   1.539 +		prev_transform = transform;
   1.540 +		transform = next_transform;
   1.541 +	}
   1.542 +	
   1.543 +	return prev_transform;
   1.544 +}
   1.545 +
   1.546 +#define EMPTY_TRANSFORM_LIST NULL
   1.547 +static struct qcms_modular_transform* qcms_modular_transform_create_mAB(struct lutmABType *lut)
   1.548 +{
   1.549 +	struct qcms_modular_transform *first_transform = NULL;
   1.550 +	struct qcms_modular_transform **next_transform = &first_transform;
   1.551 +	struct qcms_modular_transform *transform = NULL;
   1.552 +
   1.553 +	if (lut->a_curves[0] != NULL) {
   1.554 +		size_t clut_length;
   1.555 +		float *clut;
   1.556 +
   1.557 +		// If the A curve is present this also implies the 
   1.558 +		// presence of a CLUT.
   1.559 +		if (!lut->clut_table) 
   1.560 +			goto fail;
   1.561 +
   1.562 +		// Prepare A curve.
   1.563 +		transform = qcms_modular_transform_alloc();
   1.564 +		if (!transform)
   1.565 +			goto fail;
   1.566 +		append_transform(transform, &next_transform);
   1.567 +		transform->input_clut_table_r = build_input_gamma_table(lut->a_curves[0]);
   1.568 +		transform->input_clut_table_g = build_input_gamma_table(lut->a_curves[1]);
   1.569 +		transform->input_clut_table_b = build_input_gamma_table(lut->a_curves[2]);
   1.570 +		transform->transform_module_fn = qcms_transform_module_gamma_table;
   1.571 +		if (lut->num_grid_points[0] != lut->num_grid_points[1] ||
   1.572 +			lut->num_grid_points[1] != lut->num_grid_points[2] ) {
   1.573 +			//XXX: We don't currently support clut that are not squared!
   1.574 +			goto fail;
   1.575 +		}
   1.576 +
   1.577 +		// Prepare CLUT
   1.578 +		transform = qcms_modular_transform_alloc();
   1.579 +		if (!transform) 
   1.580 +			goto fail;
   1.581 +		append_transform(transform, &next_transform);
   1.582 +		clut_length = sizeof(float)*pow(lut->num_grid_points[0], 3)*3;
   1.583 +		clut = malloc(clut_length);
   1.584 +		if (!clut)
   1.585 +			goto fail;
   1.586 +		memcpy(clut, lut->clut_table, clut_length);
   1.587 +		transform->r_clut = clut + 0;
   1.588 +		transform->g_clut = clut + 1;
   1.589 +		transform->b_clut = clut + 2;
   1.590 +		transform->grid_size = lut->num_grid_points[0];
   1.591 +		transform->transform_module_fn = qcms_transform_module_clut_only;
   1.592 +	}
   1.593 +	if (lut->m_curves[0] != NULL) {
   1.594 +		// M curve imples the presence of a Matrix
   1.595 +
   1.596 +		// Prepare M curve
   1.597 +		transform = qcms_modular_transform_alloc();
   1.598 +		if (!transform)
   1.599 +			goto fail;
   1.600 +		append_transform(transform, &next_transform);
   1.601 +		transform->input_clut_table_r = build_input_gamma_table(lut->m_curves[0]);
   1.602 +		transform->input_clut_table_g = build_input_gamma_table(lut->m_curves[1]);
   1.603 +		transform->input_clut_table_b = build_input_gamma_table(lut->m_curves[2]);
   1.604 +		transform->transform_module_fn = qcms_transform_module_gamma_table;
   1.605 +
   1.606 +		// Prepare Matrix
   1.607 +		transform = qcms_modular_transform_alloc();
   1.608 +		if (!transform) 
   1.609 +			goto fail;
   1.610 +		append_transform(transform, &next_transform);
   1.611 +		transform->matrix = build_mAB_matrix(lut);
   1.612 +		if (transform->matrix.invalid)
   1.613 +			goto fail;
   1.614 +		transform->tx = s15Fixed16Number_to_float(lut->e03);
   1.615 +		transform->ty = s15Fixed16Number_to_float(lut->e13);
   1.616 +		transform->tz = s15Fixed16Number_to_float(lut->e23);
   1.617 +		transform->transform_module_fn = qcms_transform_module_matrix_translate;
   1.618 +	}
   1.619 +	if (lut->b_curves[0] != NULL) {
   1.620 +		// Prepare B curve
   1.621 +		transform = qcms_modular_transform_alloc();
   1.622 +		if (!transform) 
   1.623 +			goto fail;
   1.624 +		append_transform(transform, &next_transform);
   1.625 +		transform->input_clut_table_r = build_input_gamma_table(lut->b_curves[0]);
   1.626 +		transform->input_clut_table_g = build_input_gamma_table(lut->b_curves[1]);
   1.627 +		transform->input_clut_table_b = build_input_gamma_table(lut->b_curves[2]);
   1.628 +		transform->transform_module_fn = qcms_transform_module_gamma_table;
   1.629 +	} else {
   1.630 +		// B curve is mandatory
   1.631 +		goto fail;
   1.632 +	}
   1.633 +
   1.634 +	if (lut->reversed) {
   1.635 +		// mBA are identical to mAB except that the transformation order
   1.636 +		// is reversed
   1.637 +		first_transform = reverse_transform(first_transform);
   1.638 +	}
   1.639 +
   1.640 +	return first_transform;
   1.641 +fail:
   1.642 +	qcms_modular_transform_release(first_transform);
   1.643 +	return NULL;
   1.644 +}
   1.645 +
   1.646 +static struct qcms_modular_transform* qcms_modular_transform_create_lut(struct lutType *lut)
   1.647 +{
   1.648 +	struct qcms_modular_transform *first_transform = NULL;
   1.649 +	struct qcms_modular_transform **next_transform = &first_transform;
   1.650 +	struct qcms_modular_transform *transform = NULL;
   1.651 +
   1.652 +	size_t in_curve_len, clut_length, out_curve_len;
   1.653 +	float *in_curves, *clut, *out_curves;
   1.654 +
   1.655 +	// Prepare Matrix
   1.656 +	transform = qcms_modular_transform_alloc();
   1.657 +	if (!transform) 
   1.658 +		goto fail;
   1.659 +	append_transform(transform, &next_transform);
   1.660 +	transform->matrix = build_lut_matrix(lut);
   1.661 +	if (transform->matrix.invalid)
   1.662 +		goto fail;
   1.663 +	transform->transform_module_fn = qcms_transform_module_matrix;
   1.664 +
   1.665 +	// Prepare input curves
   1.666 +	transform = qcms_modular_transform_alloc();
   1.667 +	if (!transform) 
   1.668 +		goto fail;
   1.669 +	append_transform(transform, &next_transform);
   1.670 +	in_curve_len = sizeof(float)*lut->num_input_table_entries * 3;
   1.671 +	in_curves = malloc(in_curve_len);
   1.672 +	if (!in_curves) 
   1.673 +		goto fail;
   1.674 +	memcpy(in_curves, lut->input_table, in_curve_len);
   1.675 +	transform->input_clut_table_r = in_curves + lut->num_input_table_entries * 0;
   1.676 +	transform->input_clut_table_g = in_curves + lut->num_input_table_entries * 1;
   1.677 +	transform->input_clut_table_b = in_curves + lut->num_input_table_entries * 2;
   1.678 +	transform->input_clut_table_length = lut->num_input_table_entries;
   1.679 +
   1.680 +	// Prepare table
   1.681 +	clut_length = sizeof(float)*pow(lut->num_clut_grid_points, 3)*3;
   1.682 +	clut = malloc(clut_length);
   1.683 +	if (!clut) 
   1.684 +		goto fail;
   1.685 +	memcpy(clut, lut->clut_table, clut_length);
   1.686 +	transform->r_clut = clut + 0;
   1.687 +	transform->g_clut = clut + 1;
   1.688 +	transform->b_clut = clut + 2;
   1.689 +	transform->grid_size = lut->num_clut_grid_points;
   1.690 +
   1.691 +	// Prepare output curves
   1.692 +	out_curve_len = sizeof(float) * lut->num_output_table_entries * 3;
   1.693 +	out_curves = malloc(out_curve_len);
   1.694 +	if (!out_curves) 
   1.695 +		goto fail;
   1.696 +	memcpy(out_curves, lut->output_table, out_curve_len);
   1.697 +	transform->output_clut_table_r = out_curves + lut->num_output_table_entries * 0;
   1.698 +	transform->output_clut_table_g = out_curves + lut->num_output_table_entries * 1;
   1.699 +	transform->output_clut_table_b = out_curves + lut->num_output_table_entries * 2;
   1.700 +	transform->output_clut_table_length = lut->num_output_table_entries;
   1.701 +	transform->transform_module_fn = qcms_transform_module_clut;
   1.702 +
   1.703 +	return first_transform;
   1.704 +fail:
   1.705 +	qcms_modular_transform_release(first_transform);
   1.706 +	return NULL;
   1.707 +}
   1.708 +
   1.709 +struct qcms_modular_transform* qcms_modular_transform_create_input(qcms_profile *in)
   1.710 +{
   1.711 +	struct qcms_modular_transform *first_transform = NULL;
   1.712 +	struct qcms_modular_transform **next_transform = &first_transform;
   1.713 +
   1.714 +	if (in->A2B0) {
   1.715 +		struct qcms_modular_transform *lut_transform;
   1.716 +		lut_transform = qcms_modular_transform_create_lut(in->A2B0);
   1.717 +		if (!lut_transform)
   1.718 +			goto fail;
   1.719 +		append_transform(lut_transform, &next_transform);
   1.720 +	} else if (in->mAB && in->mAB->num_in_channels == 3 && in->mAB->num_out_channels == 3) {
   1.721 +		struct qcms_modular_transform *mAB_transform;
   1.722 +		mAB_transform = qcms_modular_transform_create_mAB(in->mAB);
   1.723 +		if (!mAB_transform)
   1.724 +			goto fail;
   1.725 +		append_transform(mAB_transform, &next_transform);
   1.726 +
   1.727 +	} else {
   1.728 +		struct qcms_modular_transform *transform;
   1.729 +
   1.730 +		transform = qcms_modular_transform_alloc();
   1.731 +		if (!transform)
   1.732 +			goto fail;
   1.733 +		append_transform(transform, &next_transform);
   1.734 +		transform->input_clut_table_r = build_input_gamma_table(in->redTRC);
   1.735 +		transform->input_clut_table_g = build_input_gamma_table(in->greenTRC);
   1.736 +		transform->input_clut_table_b = build_input_gamma_table(in->blueTRC);
   1.737 +		transform->transform_module_fn = qcms_transform_module_gamma_table;
   1.738 +		if (!transform->input_clut_table_r || !transform->input_clut_table_g ||
   1.739 +				!transform->input_clut_table_b) {
   1.740 +			goto fail;
   1.741 +		}
   1.742 +
   1.743 +		transform = qcms_modular_transform_alloc();
   1.744 +		if (!transform) 
   1.745 +			goto fail;
   1.746 +		append_transform(transform, &next_transform);
   1.747 +		transform->matrix.m[0][0] = 1/1.999969482421875f;
   1.748 +		transform->matrix.m[0][1] = 0.f;
   1.749 +		transform->matrix.m[0][2] = 0.f;
   1.750 +		transform->matrix.m[1][0] = 0.f;
   1.751 +		transform->matrix.m[1][1] = 1/1.999969482421875f;
   1.752 +		transform->matrix.m[1][2] = 0.f;
   1.753 +		transform->matrix.m[2][0] = 0.f;
   1.754 +		transform->matrix.m[2][1] = 0.f;
   1.755 +		transform->matrix.m[2][2] = 1/1.999969482421875f;
   1.756 +		transform->matrix.invalid = false;
   1.757 +		transform->transform_module_fn = qcms_transform_module_matrix;
   1.758 +
   1.759 +		transform = qcms_modular_transform_alloc();
   1.760 +		if (!transform) 
   1.761 +			goto fail;
   1.762 +		append_transform(transform, &next_transform);
   1.763 +		transform->matrix = build_colorant_matrix(in);
   1.764 +		transform->transform_module_fn = qcms_transform_module_matrix;
   1.765 +	}
   1.766 +
   1.767 +	return first_transform;
   1.768 +fail:
   1.769 +	qcms_modular_transform_release(first_transform);
   1.770 +	return EMPTY_TRANSFORM_LIST;
   1.771 +}
   1.772 +static struct qcms_modular_transform* qcms_modular_transform_create_output(qcms_profile *out)
   1.773 +{
   1.774 +	struct qcms_modular_transform *first_transform = NULL;
   1.775 +	struct qcms_modular_transform **next_transform = &first_transform;
   1.776 +
   1.777 +	if (out->B2A0) {
   1.778 +		struct qcms_modular_transform *lut_transform;
   1.779 +		lut_transform = qcms_modular_transform_create_lut(out->B2A0);
   1.780 +		if (!lut_transform) 
   1.781 +			goto fail;
   1.782 +		append_transform(lut_transform, &next_transform);
   1.783 +	} else if (out->mBA && out->mBA->num_in_channels == 3 && out->mBA->num_out_channels == 3) {
   1.784 +		struct qcms_modular_transform *lut_transform;
   1.785 +		lut_transform = qcms_modular_transform_create_mAB(out->mBA);
   1.786 +		if (!lut_transform) 
   1.787 +			goto fail;
   1.788 +		append_transform(lut_transform, &next_transform);
   1.789 +	} else if (out->redTRC && out->greenTRC && out->blueTRC) {
   1.790 +		struct qcms_modular_transform *transform;
   1.791 +
   1.792 +		transform = qcms_modular_transform_alloc();
   1.793 +		if (!transform) 
   1.794 +			goto fail;
   1.795 +		append_transform(transform, &next_transform);
   1.796 +		transform->matrix = matrix_invert(build_colorant_matrix(out));
   1.797 +		transform->transform_module_fn = qcms_transform_module_matrix;
   1.798 +
   1.799 +		transform = qcms_modular_transform_alloc();
   1.800 +		if (!transform) 
   1.801 +			goto fail;
   1.802 +		append_transform(transform, &next_transform);
   1.803 +		transform->matrix.m[0][0] = 1.999969482421875f;
   1.804 +		transform->matrix.m[0][1] = 0.f;
   1.805 +		transform->matrix.m[0][2] = 0.f;
   1.806 +		transform->matrix.m[1][0] = 0.f;
   1.807 +		transform->matrix.m[1][1] = 1.999969482421875f;
   1.808 +		transform->matrix.m[1][2] = 0.f;
   1.809 +		transform->matrix.m[2][0] = 0.f;
   1.810 +		transform->matrix.m[2][1] = 0.f;
   1.811 +		transform->matrix.m[2][2] = 1.999969482421875f;
   1.812 +		transform->matrix.invalid = false;
   1.813 +		transform->transform_module_fn = qcms_transform_module_matrix;
   1.814 +
   1.815 +		transform = qcms_modular_transform_alloc();
   1.816 +		if (!transform) 
   1.817 +			goto fail;
   1.818 +		append_transform(transform, &next_transform);
   1.819 +		build_output_lut(out->redTRC, &transform->output_gamma_lut_r,
   1.820 +			&transform->output_gamma_lut_r_length);
   1.821 +		build_output_lut(out->greenTRC, &transform->output_gamma_lut_g,
   1.822 +			&transform->output_gamma_lut_g_length);
   1.823 +		build_output_lut(out->blueTRC, &transform->output_gamma_lut_b,
   1.824 +			&transform->output_gamma_lut_b_length);
   1.825 +		transform->transform_module_fn = qcms_transform_module_gamma_lut;
   1.826 +
   1.827 +		if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g ||
   1.828 +				!transform->output_gamma_lut_b) {
   1.829 +			goto fail;
   1.830 +		}
   1.831 +	} else {
   1.832 +		assert(0 && "Unsupported output profile workflow.");
   1.833 +		return NULL;
   1.834 +	}
   1.835 +
   1.836 +	return first_transform;
   1.837 +fail:
   1.838 +	qcms_modular_transform_release(first_transform);
   1.839 +	return EMPTY_TRANSFORM_LIST;
   1.840 +}
   1.841 +
   1.842 +/* Not Completed
   1.843 +// Simplify the transformation chain to an equivalent transformation chain
   1.844 +static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
   1.845 +{
   1.846 +	struct qcms_modular_transform *first_transform = NULL;
   1.847 +	struct qcms_modular_transform *curr_trans = transform;
   1.848 +	struct qcms_modular_transform *prev_trans = NULL;
   1.849 +	while (curr_trans) {
   1.850 +		struct qcms_modular_transform *next_trans = curr_trans->next_transform;
   1.851 +		if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
   1.852 +			if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
   1.853 +				curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
   1.854 +				goto remove_next;	
   1.855 +			}
   1.856 +		}
   1.857 +		if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
   1.858 +			bool isLinear = true;
   1.859 +			uint16_t i;
   1.860 +			for (i = 0; isLinear && i < 256; i++) {
   1.861 +				isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
   1.862 +				isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
   1.863 +				isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
   1.864 +			}
   1.865 +			goto remove_current;
   1.866 +		}
   1.867 +		
   1.868 +next_transform:
   1.869 +		if (!next_trans) break;
   1.870 +		prev_trans = curr_trans;
   1.871 +		curr_trans = next_trans;
   1.872 +		continue;
   1.873 +remove_current:
   1.874 +		if (curr_trans == transform) {
   1.875 +			//Update head
   1.876 +			transform = next_trans;
   1.877 +		} else {
   1.878 +			prev_trans->next_transform = next_trans;
   1.879 +		}
   1.880 +		curr_trans->next_transform = NULL;
   1.881 +		qcms_modular_transform_release(curr_trans);
   1.882 +		//return transform;
   1.883 +		return qcms_modular_transform_reduce(transform);
   1.884 +remove_next:
   1.885 +		curr_trans->next_transform = next_trans->next_transform;
   1.886 +		next_trans->next_transform = NULL;
   1.887 +		qcms_modular_transform_release(next_trans);
   1.888 +		continue;
   1.889 +	}
   1.890 +	return transform;
   1.891 +}
   1.892 +*/
   1.893 +
   1.894 +static struct qcms_modular_transform* qcms_modular_transform_create(qcms_profile *in, qcms_profile *out)
   1.895 +{
   1.896 +	struct qcms_modular_transform *first_transform = NULL;
   1.897 +	struct qcms_modular_transform **next_transform = &first_transform;
   1.898 +
   1.899 +	if (in->color_space == RGB_SIGNATURE) {
   1.900 +		struct qcms_modular_transform* rgb_to_pcs;
   1.901 +		rgb_to_pcs = qcms_modular_transform_create_input(in);
   1.902 +		if (!rgb_to_pcs) 
   1.903 +			goto fail;
   1.904 +		append_transform(rgb_to_pcs, &next_transform);
   1.905 +	} else {
   1.906 +		assert(0 && "input color space not supported");
   1.907 +		goto fail;
   1.908 +	}
   1.909 +
   1.910 +	if (in->pcs == LAB_SIGNATURE && out->pcs == XYZ_SIGNATURE) {
   1.911 +		struct qcms_modular_transform* lab_to_pcs;
   1.912 +		lab_to_pcs = qcms_modular_transform_alloc();
   1.913 +		if (!lab_to_pcs) 
   1.914 +			goto fail;
   1.915 +		append_transform(lab_to_pcs, &next_transform);
   1.916 +		lab_to_pcs->transform_module_fn = qcms_transform_module_LAB_to_XYZ;
   1.917 +	}
   1.918 +
   1.919 +	// This does not improve accuracy in practice, something is wrong here.
   1.920 +	//if (in->chromaticAdaption.invalid == false) {
   1.921 +	//	struct qcms_modular_transform* chromaticAdaption;
   1.922 +	//	chromaticAdaption = qcms_modular_transform_alloc();
   1.923 +	//	if (!chromaticAdaption) 
   1.924 +	//		goto fail;
   1.925 +	//	append_transform(chromaticAdaption, &next_transform);
   1.926 +	//	chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
   1.927 +	//	chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
   1.928 +	//}
   1.929 +
   1.930 +        if (in->pcs == XYZ_SIGNATURE && out->pcs == LAB_SIGNATURE) {
   1.931 +		struct qcms_modular_transform* pcs_to_lab;
   1.932 +		pcs_to_lab = qcms_modular_transform_alloc();
   1.933 +		if (!pcs_to_lab) 
   1.934 +			goto fail;
   1.935 +		append_transform(pcs_to_lab, &next_transform);
   1.936 +		pcs_to_lab->transform_module_fn = qcms_transform_module_XYZ_to_LAB;
   1.937 +	}
   1.938 +
   1.939 +	if (out->color_space == RGB_SIGNATURE) {
   1.940 +		struct qcms_modular_transform* pcs_to_rgb;
   1.941 +		pcs_to_rgb = qcms_modular_transform_create_output(out);
   1.942 +		if (!pcs_to_rgb) 
   1.943 +			goto fail;
   1.944 +		append_transform(pcs_to_rgb, &next_transform);
   1.945 +	} else {
   1.946 +		assert(0 && "output color space not supported");
   1.947 +		goto fail;
   1.948 +	}
   1.949 +	// Not Completed
   1.950 +	//return qcms_modular_transform_reduce(first_transform);
   1.951 +	return first_transform;
   1.952 +fail:
   1.953 +	qcms_modular_transform_release(first_transform);
   1.954 +	return EMPTY_TRANSFORM_LIST;
   1.955 +}
   1.956 +
   1.957 +static float* qcms_modular_transform_data(struct qcms_modular_transform *transform, float *src, float *dest, size_t len)
   1.958 +{
   1.959 +        while (transform != NULL) {
   1.960 +                // Keep swaping src/dest when performing a transform to use less memory.
   1.961 +                float *new_src = dest;
   1.962 +		const transform_module_fn_t transform_fn = transform->transform_module_fn;
   1.963 +		if (transform_fn != qcms_transform_module_gamma_table &&
   1.964 +		    transform_fn != qcms_transform_module_gamma_lut &&
   1.965 +		    transform_fn != qcms_transform_module_clut &&
   1.966 +		    transform_fn != qcms_transform_module_clut_only &&
   1.967 +		    transform_fn != qcms_transform_module_matrix &&
   1.968 +		    transform_fn != qcms_transform_module_matrix_translate &&
   1.969 +		    transform_fn != qcms_transform_module_LAB_to_XYZ &&
   1.970 +		    transform_fn != qcms_transform_module_XYZ_to_LAB) {
   1.971 +			assert(0 && "Unsupported transform module");
   1.972 +			return NULL;
   1.973 +		}
   1.974 +                transform->transform_module_fn(transform,src,dest,len);
   1.975 +                dest = src;
   1.976 +                src = new_src;
   1.977 +                transform = transform->next_transform;
   1.978 +        }
   1.979 +        // The results end up in the src buffer because of the switching
   1.980 +        return src;
   1.981 +}
   1.982 +
   1.983 +float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize)
   1.984 +{
   1.985 +	struct qcms_modular_transform *transform_list = qcms_modular_transform_create(in, out);
   1.986 +	if (transform_list != NULL) {
   1.987 +		float *lut = qcms_modular_transform_data(transform_list, src, dest, lutSize/3);
   1.988 +		qcms_modular_transform_release(transform_list);
   1.989 +		return lut;
   1.990 +	}
   1.991 +	return NULL;
   1.992 +}

mercurial