gfx/qcms/chain.c

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial