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