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 +}