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 "chain.h" |
| 30 #include "matrix.h" |
| 31 #include "transform_util.h" |
| 32 |
| 33 /* for MSVC, GCC, Intel, and Sun compilers */ |
| 34 #if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_AMD64
) || defined(__x86_64__) || defined(__x86_64) |
| 35 #define X86 |
| 36 #endif /* _M_IX86 || __i386__ || __i386 || _M_AMD64 || __x86_64__ || __x86_64 */ |
| 37 |
| 38 // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ |
| 39 // This is just an approximation, I am not handling all the non-linear |
| 40 // aspects of the RGB to XYZ process, and assumming that the gamma correction |
| 41 // has transitive property in the tranformation chain. |
| 42 // |
| 43 // the alghoritm: |
| 44 // |
| 45 // - First I build the absolute conversion matrix using |
| 46 // primaries in XYZ. This matrix is next inverted |
| 47 // - Then I eval the source white point across this matrix |
| 48 // obtaining the coeficients of the transformation |
| 49 // - Then, I apply these coeficients to the original matrix |
| 50 static struct matrix build_RGB_to_XYZ_transfer_matrix(qcms_CIE_xyY white, qcms_C
IE_xyYTRIPLE primrs) |
| 51 { |
| 52 struct matrix primaries; |
| 53 struct matrix primaries_invert; |
| 54 struct matrix result; |
| 55 struct vector white_point; |
| 56 struct vector coefs; |
| 57 |
| 58 double xn, yn; |
| 59 double xr, yr; |
| 60 double xg, yg; |
| 61 double xb, yb; |
| 62 |
| 63 xn = white.x; |
| 64 yn = white.y; |
| 65 |
| 66 if (yn == 0.0) |
| 67 return matrix_invalid(); |
| 68 |
| 69 xr = primrs.red.x; |
| 70 yr = primrs.red.y; |
| 71 xg = primrs.green.x; |
| 72 yg = primrs.green.y; |
| 73 xb = primrs.blue.x; |
| 74 yb = primrs.blue.y; |
| 75 |
| 76 primaries.m[0][0] = xr; |
| 77 primaries.m[0][1] = xg; |
| 78 primaries.m[0][2] = xb; |
| 79 |
| 80 primaries.m[1][0] = yr; |
| 81 primaries.m[1][1] = yg; |
| 82 primaries.m[1][2] = yb; |
| 83 |
| 84 primaries.m[2][0] = 1 - xr - yr; |
| 85 primaries.m[2][1] = 1 - xg - yg; |
| 86 primaries.m[2][2] = 1 - xb - yb; |
| 87 primaries.invalid = false; |
| 88 |
| 89 white_point.v[0] = xn/yn; |
| 90 white_point.v[1] = 1.; |
| 91 white_point.v[2] = (1.0-xn-yn)/yn; |
| 92 |
| 93 primaries_invert = matrix_invert(primaries); |
| 94 |
| 95 coefs = matrix_eval(primaries_invert, white_point); |
| 96 |
| 97 result.m[0][0] = coefs.v[0]*xr; |
| 98 result.m[0][1] = coefs.v[1]*xg; |
| 99 result.m[0][2] = coefs.v[2]*xb; |
| 100 |
| 101 result.m[1][0] = coefs.v[0]*yr; |
| 102 result.m[1][1] = coefs.v[1]*yg; |
| 103 result.m[1][2] = coefs.v[2]*yb; |
| 104 |
| 105 result.m[2][0] = coefs.v[0]*(1.-xr-yr); |
| 106 result.m[2][1] = coefs.v[1]*(1.-xg-yg); |
| 107 result.m[2][2] = coefs.v[2]*(1.-xb-yb); |
| 108 result.invalid = primaries_invert.invalid; |
| 109 |
| 110 return result; |
| 111 } |
| 112 |
| 113 struct CIE_XYZ { |
| 114 double X; |
| 115 double Y; |
| 116 double Z; |
| 117 }; |
| 118 |
| 119 /* CIE Illuminant D50 */ |
| 120 static const struct CIE_XYZ D50_XYZ = { |
| 121 0.9642, |
| 122 1.0000, |
| 123 0.8249 |
| 124 }; |
| 125 |
| 126 /* from lcms: xyY2XYZ() |
| 127 * corresponds to argyll: icmYxy2XYZ() */ |
| 128 static struct CIE_XYZ xyY2XYZ(qcms_CIE_xyY source) |
| 129 { |
| 130 struct CIE_XYZ dest; |
| 131 dest.X = (source.x / source.y) * source.Y; |
| 132 dest.Y = source.Y; |
| 133 dest.Z = ((1 - source.x - source.y) / source.y) * source.Y; |
| 134 return dest; |
| 135 } |
| 136 |
| 137 /* from lcms: ComputeChromaticAdaption */ |
| 138 // Compute chromatic adaption matrix using chad as cone matrix |
| 139 static struct matrix |
| 140 compute_chromatic_adaption(struct CIE_XYZ source_white_point, |
| 141 struct CIE_XYZ dest_white_point, |
| 142 struct matrix chad) |
| 143 { |
| 144 struct matrix chad_inv; |
| 145 struct vector cone_source_XYZ, cone_source_rgb; |
| 146 struct vector cone_dest_XYZ, cone_dest_rgb; |
| 147 struct matrix cone, tmp; |
| 148 |
| 149 tmp = chad; |
| 150 chad_inv = matrix_invert(tmp); |
| 151 |
| 152 cone_source_XYZ.v[0] = source_white_point.X; |
| 153 cone_source_XYZ.v[1] = source_white_point.Y; |
| 154 cone_source_XYZ.v[2] = source_white_point.Z; |
| 155 |
| 156 cone_dest_XYZ.v[0] = dest_white_point.X; |
| 157 cone_dest_XYZ.v[1] = dest_white_point.Y; |
| 158 cone_dest_XYZ.v[2] = dest_white_point.Z; |
| 159 |
| 160 cone_source_rgb = matrix_eval(chad, cone_source_XYZ); |
| 161 cone_dest_rgb = matrix_eval(chad, cone_dest_XYZ); |
| 162 |
| 163 cone.m[0][0] = cone_dest_rgb.v[0]/cone_source_rgb.v[0]; |
| 164 cone.m[0][1] = 0; |
| 165 cone.m[0][2] = 0; |
| 166 cone.m[1][0] = 0; |
| 167 cone.m[1][1] = cone_dest_rgb.v[1]/cone_source_rgb.v[1]; |
| 168 cone.m[1][2] = 0; |
| 169 cone.m[2][0] = 0; |
| 170 cone.m[2][1] = 0; |
| 171 cone.m[2][2] = cone_dest_rgb.v[2]/cone_source_rgb.v[2]; |
| 172 cone.invalid = false; |
| 173 |
| 174 // Normalize |
| 175 return matrix_multiply(chad_inv, matrix_multiply(cone, chad)); |
| 176 } |
| 177 |
| 178 /* from lcms: cmsAdaptionMatrix */ |
| 179 // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant T
oIll |
| 180 // Bradford is assumed |
| 181 static struct matrix |
| 182 adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumi
nation) |
| 183 { |
| 184 struct matrix lam_rigg = {{ // Bradford matrix |
| 185 { 0.8951, 0.2664, -0.1614 }, |
| 186 { -0.7502, 1.7135, 0.0367 }, |
| 187 { 0.0389, -0.0685, 1.0296 } |
| 188 }}; |
| 189 return compute_chromatic_adaption(source_illumination, target_illuminati
on, lam_rigg); |
| 190 } |
| 191 |
| 192 /* from lcms: cmsAdaptMatrixToD50 */ |
| 193 static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_wh
ite_pt) |
| 194 { |
| 195 struct CIE_XYZ Dn; |
| 196 struct matrix Bradford; |
| 197 |
| 198 if (source_white_pt.y == 0.0) |
| 199 return matrix_invalid(); |
| 200 |
| 201 Dn = xyY2XYZ(source_white_pt); |
| 202 |
| 203 Bradford = adaption_matrix(Dn, D50_XYZ); |
| 204 return matrix_multiply(Bradford, r); |
| 205 } |
| 206 |
| 207 qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcm
s_CIE_xyYTRIPLE primaries) |
| 208 { |
| 209 struct matrix colorants; |
| 210 colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries); |
| 211 colorants = adapt_matrix_to_D50(colorants, white_point); |
| 212 |
| 213 if (colorants.invalid) |
| 214 return false; |
| 215 |
| 216 /* note: there's a transpose type of operation going on here */ |
| 217 profile->redColorant.X = double_to_s15Fixed16Number(colorants.m[0][0]); |
| 218 profile->redColorant.Y = double_to_s15Fixed16Number(colorants.m[1][0]); |
| 219 profile->redColorant.Z = double_to_s15Fixed16Number(colorants.m[2][0]); |
| 220 |
| 221 profile->greenColorant.X = double_to_s15Fixed16Number(colorants.m[0][1])
; |
| 222 profile->greenColorant.Y = double_to_s15Fixed16Number(colorants.m[1][1])
; |
| 223 profile->greenColorant.Z = double_to_s15Fixed16Number(colorants.m[2][1])
; |
| 224 |
| 225 profile->blueColorant.X = double_to_s15Fixed16Number(colorants.m[0][2]); |
| 226 profile->blueColorant.Y = double_to_s15Fixed16Number(colorants.m[1][2]); |
| 227 profile->blueColorant.Z = double_to_s15Fixed16Number(colorants.m[2][2]); |
| 228 |
| 229 return true; |
| 230 } |
| 231 |
| 232 #if 0 |
| 233 static void qcms_transform_data_rgb_out_pow(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length) |
| 234 { |
| 235 int i; |
| 236 float (*mat)[4] = transform->matrix; |
| 237 for (i=0; i<length; i++) { |
| 238 unsigned char device_r = *src++; |
| 239 unsigned char device_g = *src++; |
| 240 unsigned char device_b = *src++; |
| 241 |
| 242 float linear_r = transform->input_gamma_table_r[device_r]; |
| 243 float linear_g = transform->input_gamma_table_g[device_g]; |
| 244 float linear_b = transform->input_gamma_table_b[device_b]; |
| 245 |
| 246 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 247 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 248 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 249 |
| 250 float out_device_r = pow(out_linear_r, transform->out_gamma_r); |
| 251 float out_device_g = pow(out_linear_g, transform->out_gamma_g); |
| 252 float out_device_b = pow(out_linear_b, transform->out_gamma_b); |
| 253 |
| 254 *dest++ = clamp_u8(255*out_device_r); |
| 255 *dest++ = clamp_u8(255*out_device_g); |
| 256 *dest++ = clamp_u8(255*out_device_b); |
| 257 } |
| 258 } |
| 259 #endif |
| 260 |
| 261 static void qcms_transform_data_gray_out_lut(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length) |
| 262 { |
| 263 unsigned int i; |
| 264 for (i = 0; i < length; i++) { |
| 265 float out_device_r, out_device_g, out_device_b; |
| 266 unsigned char device = *src++; |
| 267 |
| 268 float linear = transform->input_gamma_table_gray[device]; |
| 269 |
| 270 out_device_r = lut_interp_linear(linear, transform->output_gamma
_lut_r, transform->output_gamma_lut_r_length); |
| 271 out_device_g = lut_interp_linear(linear, transform->output_gamma
_lut_g, transform->output_gamma_lut_g_length); |
| 272 out_device_b = lut_interp_linear(linear, transform->output_gamma
_lut_b, transform->output_gamma_lut_b_length); |
| 273 |
| 274 *dest++ = clamp_u8(out_device_r*255); |
| 275 *dest++ = clamp_u8(out_device_g*255); |
| 276 *dest++ = clamp_u8(out_device_b*255); |
| 277 } |
| 278 } |
| 279 |
| 280 /* Alpha is not corrected. |
| 281 A rationale for this is found in Alvy Ray's "Should Alpha Be Nonlinear If |
| 282 RGB Is?" Tech Memo 17 (December 14, 1998). |
| 283 See: ftp://ftp.alvyray.com/Acrobat/17_Nonln.pdf |
| 284 */ |
| 285 |
| 286 static void qcms_transform_data_graya_out_lut(qcms_transform *transform, unsigne
d char *src, unsigned char *dest, size_t length) |
| 287 { |
| 288 unsigned int i; |
| 289 for (i = 0; i < length; i++) { |
| 290 float out_device_r, out_device_g, out_device_b; |
| 291 unsigned char device = *src++; |
| 292 unsigned char alpha = *src++; |
| 293 |
| 294 float linear = transform->input_gamma_table_gray[device]; |
| 295 |
| 296 out_device_r = lut_interp_linear(linear, transform->output_gamma
_lut_r, transform->output_gamma_lut_r_length); |
| 297 out_device_g = lut_interp_linear(linear, transform->output_gamma
_lut_g, transform->output_gamma_lut_g_length); |
| 298 out_device_b = lut_interp_linear(linear, transform->output_gamma
_lut_b, transform->output_gamma_lut_b_length); |
| 299 |
| 300 *dest++ = clamp_u8(out_device_r*255); |
| 301 *dest++ = clamp_u8(out_device_g*255); |
| 302 *dest++ = clamp_u8(out_device_b*255); |
| 303 *dest++ = alpha; |
| 304 } |
| 305 } |
| 306 |
| 307 |
| 308 static void qcms_transform_data_gray_out_precache(qcms_transform *transform, uns
igned char *src, unsigned char *dest, size_t length) |
| 309 { |
| 310 unsigned int i; |
| 311 for (i = 0; i < length; i++) { |
| 312 unsigned char device = *src++; |
| 313 uint16_t gray; |
| 314 |
| 315 float linear = transform->input_gamma_table_gray[device]; |
| 316 |
| 317 /* we could round here... */ |
| 318 gray = linear * PRECACHE_OUTPUT_MAX; |
| 319 |
| 320 *dest++ = transform->output_table_r->data[gray]; |
| 321 *dest++ = transform->output_table_g->data[gray]; |
| 322 *dest++ = transform->output_table_b->data[gray]; |
| 323 } |
| 324 } |
| 325 |
| 326 static void qcms_transform_data_graya_out_precache(qcms_transform *transform, un
signed char *src, unsigned char *dest, size_t length) |
| 327 { |
| 328 unsigned int i; |
| 329 for (i = 0; i < length; i++) { |
| 330 unsigned char device = *src++; |
| 331 unsigned char alpha = *src++; |
| 332 uint16_t gray; |
| 333 |
| 334 float linear = transform->input_gamma_table_gray[device]; |
| 335 |
| 336 /* we could round here... */ |
| 337 gray = linear * PRECACHE_OUTPUT_MAX; |
| 338 |
| 339 *dest++ = transform->output_table_r->data[gray]; |
| 340 *dest++ = transform->output_table_g->data[gray]; |
| 341 *dest++ = transform->output_table_b->data[gray]; |
| 342 *dest++ = alpha; |
| 343 } |
| 344 } |
| 345 |
| 346 static void qcms_transform_data_rgb_out_lut_precache(qcms_transform *transform,
unsigned char *src, unsigned char *dest, size_t length) |
| 347 { |
| 348 unsigned int i; |
| 349 float (*mat)[4] = transform->matrix; |
| 350 for (i = 0; i < length; i++) { |
| 351 unsigned char device_r = *src++; |
| 352 unsigned char device_g = *src++; |
| 353 unsigned char device_b = *src++; |
| 354 uint16_t r, g, b; |
| 355 |
| 356 float linear_r = transform->input_gamma_table_r[device_r]; |
| 357 float linear_g = transform->input_gamma_table_g[device_g]; |
| 358 float linear_b = transform->input_gamma_table_b[device_b]; |
| 359 |
| 360 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 361 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 362 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 363 |
| 364 out_linear_r = clamp_float(out_linear_r); |
| 365 out_linear_g = clamp_float(out_linear_g); |
| 366 out_linear_b = clamp_float(out_linear_b); |
| 367 |
| 368 /* we could round here... */ |
| 369 r = out_linear_r * PRECACHE_OUTPUT_MAX; |
| 370 g = out_linear_g * PRECACHE_OUTPUT_MAX; |
| 371 b = out_linear_b * PRECACHE_OUTPUT_MAX; |
| 372 |
| 373 *dest++ = transform->output_table_r->data[r]; |
| 374 *dest++ = transform->output_table_g->data[g]; |
| 375 *dest++ = transform->output_table_b->data[b]; |
| 376 } |
| 377 } |
| 378 |
| 379 static void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform,
unsigned char *src, unsigned char *dest, size_t length) |
| 380 { |
| 381 unsigned int i; |
| 382 float (*mat)[4] = transform->matrix; |
| 383 for (i = 0; i < length; i++) { |
| 384 unsigned char device_r = *src++; |
| 385 unsigned char device_g = *src++; |
| 386 unsigned char device_b = *src++; |
| 387 unsigned char alpha = *src++; |
| 388 uint16_t r, g, b; |
| 389 |
| 390 float linear_r = transform->input_gamma_table_r[device_r]; |
| 391 float linear_g = transform->input_gamma_table_g[device_g]; |
| 392 float linear_b = transform->input_gamma_table_b[device_b]; |
| 393 |
| 394 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 395 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 396 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 397 |
| 398 out_linear_r = clamp_float(out_linear_r); |
| 399 out_linear_g = clamp_float(out_linear_g); |
| 400 out_linear_b = clamp_float(out_linear_b); |
| 401 |
| 402 /* we could round here... */ |
| 403 r = out_linear_r * PRECACHE_OUTPUT_MAX; |
| 404 g = out_linear_g * PRECACHE_OUTPUT_MAX; |
| 405 b = out_linear_b * PRECACHE_OUTPUT_MAX; |
| 406 |
| 407 *dest++ = transform->output_table_r->data[r]; |
| 408 *dest++ = transform->output_table_g->data[g]; |
| 409 *dest++ = transform->output_table_b->data[b]; |
| 410 *dest++ = alpha; |
| 411 } |
| 412 } |
| 413 |
| 414 // Not used |
| 415 /* |
| 416 static void qcms_transform_data_clut(qcms_transform *transform, unsigned char *s
rc, unsigned char *dest, size_t length) { |
| 417 unsigned int i; |
| 418 int xy_len = 1; |
| 419 int x_len = transform->grid_size; |
| 420 int len = x_len * x_len; |
| 421 float* r_table = transform->r_clut; |
| 422 float* g_table = transform->g_clut; |
| 423 float* b_table = transform->b_clut; |
| 424 |
| 425 for (i = 0; i < length; i++) { |
| 426 unsigned char in_r = *src++; |
| 427 unsigned char in_g = *src++; |
| 428 unsigned char in_b = *src++; |
| 429 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = i
n_b/255.0f; |
| 430 |
| 431 int x = floor(linear_r * (transform->grid_size-1)); |
| 432 int y = floor(linear_g * (transform->grid_size-1)); |
| 433 int z = floor(linear_b * (transform->grid_size-1)); |
| 434 int x_n = ceil(linear_r * (transform->grid_size-1)); |
| 435 int y_n = ceil(linear_g * (transform->grid_size-1)); |
| 436 int z_n = ceil(linear_b * (transform->grid_size-1)); |
| 437 float x_d = linear_r * (transform->grid_size-1) - x; |
| 438 float y_d = linear_g * (transform->grid_size-1) - y; |
| 439 float z_d = linear_b * (transform->grid_size-1) - z; |
| 440 |
| 441 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d)
; |
| 442 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z),
x_d); |
| 443 float r_y1 = lerp(r_x1, r_x2, y_d); |
| 444 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n),
x_d); |
| 445 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_
n), x_d); |
| 446 float r_y2 = lerp(r_x3, r_x4, y_d); |
| 447 float clut_r = lerp(r_y1, r_y2, z_d); |
| 448 |
| 449 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d)
; |
| 450 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z),
x_d); |
| 451 float g_y1 = lerp(g_x1, g_x2, y_d); |
| 452 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n),
x_d); |
| 453 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_
n), x_d); |
| 454 float g_y2 = lerp(g_x3, g_x4, y_d); |
| 455 float clut_g = lerp(g_y1, g_y2, z_d); |
| 456 |
| 457 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d)
; |
| 458 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z),
x_d); |
| 459 float b_y1 = lerp(b_x1, b_x2, y_d); |
| 460 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n),
x_d); |
| 461 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_
n), x_d); |
| 462 float b_y2 = lerp(b_x3, b_x4, y_d); |
| 463 float clut_b = lerp(b_y1, b_y2, z_d); |
| 464 |
| 465 *dest++ = clamp_u8(clut_r*255.0f); |
| 466 *dest++ = clamp_u8(clut_g*255.0f); |
| 467 *dest++ = clamp_u8(clut_b*255.0f); |
| 468 } |
| 469 } |
| 470 */ |
| 471 |
| 472 // Using lcms' tetra interpolation algorithm. |
| 473 static void qcms_transform_data_tetra_clut_rgba(qcms_transform *transform, unsig
ned char *src, unsigned char *dest, size_t length) { |
| 474 unsigned int i; |
| 475 int xy_len = 1; |
| 476 int x_len = transform->grid_size; |
| 477 int len = x_len * x_len; |
| 478 float* r_table = transform->r_clut; |
| 479 float* g_table = transform->g_clut; |
| 480 float* b_table = transform->b_clut; |
| 481 float c0_r, c1_r, c2_r, c3_r; |
| 482 float c0_g, c1_g, c2_g, c3_g; |
| 483 float c0_b, c1_b, c2_b, c3_b; |
| 484 float clut_r, clut_g, clut_b; |
| 485 for (i = 0; i < length; i++) { |
| 486 unsigned char in_r = *src++; |
| 487 unsigned char in_g = *src++; |
| 488 unsigned char in_b = *src++; |
| 489 unsigned char in_a = *src++; |
| 490 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = i
n_b/255.0f; |
| 491 |
| 492 int x = floor(linear_r * (transform->grid_size-1)); |
| 493 int y = floor(linear_g * (transform->grid_size-1)); |
| 494 int z = floor(linear_b * (transform->grid_size-1)); |
| 495 int x_n = ceil(linear_r * (transform->grid_size-1)); |
| 496 int y_n = ceil(linear_g * (transform->grid_size-1)); |
| 497 int z_n = ceil(linear_b * (transform->grid_size-1)); |
| 498 float rx = linear_r * (transform->grid_size-1) - x; |
| 499 float ry = linear_g * (transform->grid_size-1) - y; |
| 500 float rz = linear_b * (transform->grid_size-1) - z; |
| 501 |
| 502 c0_r = CLU(r_table, x, y, z); |
| 503 c0_g = CLU(g_table, x, y, z); |
| 504 c0_b = CLU(b_table, x, y, z); |
| 505 |
| 506 if( rx >= ry ) { |
| 507 if (ry >= rz) { //rx >= ry && ry >= rz |
| 508 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 509 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x_n, y, z); |
| 510 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 511 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 512 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x_n, y, z); |
| 513 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 514 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 515 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x_n, y, z); |
| 516 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 517 } else { |
| 518 if (rx >= rz) { //rx >= rz && rz >= ry |
| 519 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 520 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 521 c3_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x_n, y, z); |
| 522 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 523 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 524 c3_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x_n, y, z); |
| 525 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 526 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 527 c3_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x_n, y, z); |
| 528 } else { //rz > rx && rx >= ry |
| 529 c1_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x, y, z_n); |
| 530 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 531 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 532 c1_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x, y, z_n); |
| 533 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 534 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 535 c1_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x, y, z_n); |
| 536 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 537 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 538 } |
| 539 } |
| 540 } else { |
| 541 if (rx >= rz) { //ry > rx && rx >= rz |
| 542 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x, y_n, z); |
| 543 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 544 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 545 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x, y_n, z); |
| 546 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 547 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 548 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x, y_n, z); |
| 549 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 550 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 551 } else { |
| 552 if (ry >= rz) { //ry >= rz && rz > rx |
| 553 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 554 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 555 c3_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y_n, z); |
| 556 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 557 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 558 c3_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y_n, z); |
| 559 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 560 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 561 c3_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y_n, z); |
| 562 } else { //rz > ry && ry > rx |
| 563 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 564 c2_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y, z_n); |
| 565 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 566 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 567 c2_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y, z_n); |
| 568 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 569 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 570 c2_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y, z_n); |
| 571 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 572 } |
| 573 } |
| 574 } |
| 575 |
| 576 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz; |
| 577 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz; |
| 578 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz; |
| 579 |
| 580 *dest++ = clamp_u8(clut_r*255.0f); |
| 581 *dest++ = clamp_u8(clut_g*255.0f); |
| 582 *dest++ = clamp_u8(clut_b*255.0f); |
| 583 *dest++ = in_a; |
| 584 } |
| 585 } |
| 586 |
| 587 // Using lcms' tetra interpolation code. |
| 588 static void qcms_transform_data_tetra_clut(qcms_transform *transform, unsigned c
har *src, unsigned char *dest, size_t length) { |
| 589 unsigned int i; |
| 590 int xy_len = 1; |
| 591 int x_len = transform->grid_size; |
| 592 int len = x_len * x_len; |
| 593 float* r_table = transform->r_clut; |
| 594 float* g_table = transform->g_clut; |
| 595 float* b_table = transform->b_clut; |
| 596 float c0_r, c1_r, c2_r, c3_r; |
| 597 float c0_g, c1_g, c2_g, c3_g; |
| 598 float c0_b, c1_b, c2_b, c3_b; |
| 599 float clut_r, clut_g, clut_b; |
| 600 for (i = 0; i < length; i++) { |
| 601 unsigned char in_r = *src++; |
| 602 unsigned char in_g = *src++; |
| 603 unsigned char in_b = *src++; |
| 604 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = i
n_b/255.0f; |
| 605 |
| 606 int x = floor(linear_r * (transform->grid_size-1)); |
| 607 int y = floor(linear_g * (transform->grid_size-1)); |
| 608 int z = floor(linear_b * (transform->grid_size-1)); |
| 609 int x_n = ceil(linear_r * (transform->grid_size-1)); |
| 610 int y_n = ceil(linear_g * (transform->grid_size-1)); |
| 611 int z_n = ceil(linear_b * (transform->grid_size-1)); |
| 612 float rx = linear_r * (transform->grid_size-1) - x; |
| 613 float ry = linear_g * (transform->grid_size-1) - y; |
| 614 float rz = linear_b * (transform->grid_size-1) - z; |
| 615 |
| 616 c0_r = CLU(r_table, x, y, z); |
| 617 c0_g = CLU(g_table, x, y, z); |
| 618 c0_b = CLU(b_table, x, y, z); |
| 619 |
| 620 if( rx >= ry ) { |
| 621 if (ry >= rz) { //rx >= ry && ry >= rz |
| 622 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 623 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x_n, y, z); |
| 624 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 625 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 626 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x_n, y, z); |
| 627 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 628 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 629 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x_n, y, z); |
| 630 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 631 } else { |
| 632 if (rx >= rz) { //rx >= rz && rz >= ry |
| 633 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 634 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 635 c3_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x_n, y, z); |
| 636 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 637 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 638 c3_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x_n, y, z); |
| 639 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 640 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 641 c3_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x_n, y, z); |
| 642 } else { //rz > rx && rx >= ry |
| 643 c1_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x, y, z_n); |
| 644 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 645 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 646 c1_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x, y, z_n); |
| 647 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 648 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 649 c1_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x, y, z_n); |
| 650 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 651 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 652 } |
| 653 } |
| 654 } else { |
| 655 if (rx >= rz) { //ry > rx && rx >= rz |
| 656 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x, y_n, z); |
| 657 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 658 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 659 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x, y_n, z); |
| 660 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 661 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 662 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x, y_n, z); |
| 663 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 664 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 665 } else { |
| 666 if (ry >= rz) { //ry >= rz && rz > rx |
| 667 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 668 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 669 c3_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y_n, z); |
| 670 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 671 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 672 c3_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y_n, z); |
| 673 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 674 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 675 c3_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y_n, z); |
| 676 } else { //rz > ry && ry > rx |
| 677 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 678 c2_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y, z_n); |
| 679 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 680 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 681 c2_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y, z_n); |
| 682 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 683 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 684 c2_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y, z_n); |
| 685 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 686 } |
| 687 } |
| 688 } |
| 689 |
| 690 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz; |
| 691 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz; |
| 692 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz; |
| 693 |
| 694 *dest++ = clamp_u8(clut_r*255.0f); |
| 695 *dest++ = clamp_u8(clut_g*255.0f); |
| 696 *dest++ = clamp_u8(clut_b*255.0f); |
| 697 } |
| 698 } |
| 699 |
| 700 static void qcms_transform_data_rgb_out_lut(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length) |
| 701 { |
| 702 unsigned int i; |
| 703 float (*mat)[4] = transform->matrix; |
| 704 for (i = 0; i < length; i++) { |
| 705 unsigned char device_r = *src++; |
| 706 unsigned char device_g = *src++; |
| 707 unsigned char device_b = *src++; |
| 708 float out_device_r, out_device_g, out_device_b; |
| 709 |
| 710 float linear_r = transform->input_gamma_table_r[device_r]; |
| 711 float linear_g = transform->input_gamma_table_g[device_g]; |
| 712 float linear_b = transform->input_gamma_table_b[device_b]; |
| 713 |
| 714 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 715 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 716 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 717 |
| 718 out_linear_r = clamp_float(out_linear_r); |
| 719 out_linear_g = clamp_float(out_linear_g); |
| 720 out_linear_b = clamp_float(out_linear_b); |
| 721 |
| 722 out_device_r = lut_interp_linear(out_linear_r, |
| 723 transform->output_gamma_lut_r, transform->output
_gamma_lut_r_length); |
| 724 out_device_g = lut_interp_linear(out_linear_g, |
| 725 transform->output_gamma_lut_g, transform->output
_gamma_lut_g_length); |
| 726 out_device_b = lut_interp_linear(out_linear_b, |
| 727 transform->output_gamma_lut_b, transform->output
_gamma_lut_b_length); |
| 728 |
| 729 *dest++ = clamp_u8(out_device_r*255); |
| 730 *dest++ = clamp_u8(out_device_g*255); |
| 731 *dest++ = clamp_u8(out_device_b*255); |
| 732 } |
| 733 } |
| 734 |
| 735 static void qcms_transform_data_rgba_out_lut(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length) |
| 736 { |
| 737 unsigned int i; |
| 738 float (*mat)[4] = transform->matrix; |
| 739 for (i = 0; i < length; i++) { |
| 740 unsigned char device_r = *src++; |
| 741 unsigned char device_g = *src++; |
| 742 unsigned char device_b = *src++; |
| 743 unsigned char alpha = *src++; |
| 744 float out_device_r, out_device_g, out_device_b; |
| 745 |
| 746 float linear_r = transform->input_gamma_table_r[device_r]; |
| 747 float linear_g = transform->input_gamma_table_g[device_g]; |
| 748 float linear_b = transform->input_gamma_table_b[device_b]; |
| 749 |
| 750 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 751 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 752 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 753 |
| 754 out_linear_r = clamp_float(out_linear_r); |
| 755 out_linear_g = clamp_float(out_linear_g); |
| 756 out_linear_b = clamp_float(out_linear_b); |
| 757 |
| 758 out_device_r = lut_interp_linear(out_linear_r, |
| 759 transform->output_gamma_lut_r, transform->output
_gamma_lut_r_length); |
| 760 out_device_g = lut_interp_linear(out_linear_g, |
| 761 transform->output_gamma_lut_g, transform->output
_gamma_lut_g_length); |
| 762 out_device_b = lut_interp_linear(out_linear_b, |
| 763 transform->output_gamma_lut_b, transform->output
_gamma_lut_b_length); |
| 764 |
| 765 *dest++ = clamp_u8(out_device_r*255); |
| 766 *dest++ = clamp_u8(out_device_g*255); |
| 767 *dest++ = clamp_u8(out_device_b*255); |
| 768 *dest++ = alpha; |
| 769 } |
| 770 } |
| 771 |
| 772 #if 0 |
| 773 static void qcms_transform_data_rgb_out_linear(qcms_transform *transform, unsign
ed char *src, unsigned char *dest, size_t length) |
| 774 { |
| 775 int i; |
| 776 float (*mat)[4] = transform->matrix; |
| 777 for (i = 0; i < length; i++) { |
| 778 unsigned char device_r = *src++; |
| 779 unsigned char device_g = *src++; |
| 780 unsigned char device_b = *src++; |
| 781 |
| 782 float linear_r = transform->input_gamma_table_r[device_r]; |
| 783 float linear_g = transform->input_gamma_table_g[device_g]; |
| 784 float linear_b = transform->input_gamma_table_b[device_b]; |
| 785 |
| 786 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 787 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 788 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 789 |
| 790 *dest++ = clamp_u8(out_linear_r*255); |
| 791 *dest++ = clamp_u8(out_linear_g*255); |
| 792 *dest++ = clamp_u8(out_linear_b*255); |
| 793 } |
| 794 } |
| 795 #endif |
| 796 |
| 797 static struct precache_output *precache_reference(struct precache_output *p) |
| 798 { |
| 799 p->ref_count++; |
| 800 return p; |
| 801 } |
| 802 |
| 803 static struct precache_output *precache_create() |
| 804 { |
| 805 struct precache_output *p = malloc(sizeof(struct precache_output)); |
| 806 if (p) |
| 807 p->ref_count = 1; |
| 808 return p; |
| 809 } |
| 810 |
| 811 void precache_release(struct precache_output *p) |
| 812 { |
| 813 if (--p->ref_count == 0) { |
| 814 free(p); |
| 815 } |
| 816 } |
| 817 |
| 818 #ifdef HAS_POSIX_MEMALIGN |
| 819 static qcms_transform *transform_alloc(void) |
| 820 { |
| 821 qcms_transform *t; |
| 822 if (!posix_memalign(&t, 16, sizeof(*t))) { |
| 823 return t; |
| 824 } else { |
| 825 return NULL; |
| 826 } |
| 827 } |
| 828 static void transform_free(qcms_transform *t) |
| 829 { |
| 830 free(t); |
| 831 } |
| 832 #else |
| 833 static qcms_transform *transform_alloc(void) |
| 834 { |
| 835 /* transform needs to be aligned on a 16byte boundrary */ |
| 836 char *original_block = calloc(sizeof(qcms_transform) + sizeof(void*) + 1
6, 1); |
| 837 /* make room for a pointer to the block returned by calloc */ |
| 838 void *transform_start = original_block + sizeof(void*); |
| 839 /* align transform_start */ |
| 840 qcms_transform *transform_aligned = (qcms_transform*)(((uintptr_t)transf
orm_start + 15) & ~0xf); |
| 841 |
| 842 /* store a pointer to the block returned by calloc so that we can free i
t later */ |
| 843 void **(original_block_ptr) = (void**)transform_aligned; |
| 844 if (!original_block) |
| 845 return NULL; |
| 846 original_block_ptr--; |
| 847 *original_block_ptr = original_block; |
| 848 |
| 849 return transform_aligned; |
| 850 } |
| 851 static void transform_free(qcms_transform *t) |
| 852 { |
| 853 /* get at the pointer to the unaligned block returned by calloc */ |
| 854 void **p = (void**)t; |
| 855 p--; |
| 856 free(*p); |
| 857 } |
| 858 #endif |
| 859 |
| 860 void qcms_transform_release(qcms_transform *t) |
| 861 { |
| 862 /* ensure we only free the gamma tables once even if there are |
| 863 * multiple references to the same data */ |
| 864 |
| 865 if (t->output_table_r) |
| 866 precache_release(t->output_table_r); |
| 867 if (t->output_table_g) |
| 868 precache_release(t->output_table_g); |
| 869 if (t->output_table_b) |
| 870 precache_release(t->output_table_b); |
| 871 |
| 872 free(t->input_gamma_table_r); |
| 873 if (t->input_gamma_table_g != t->input_gamma_table_r) |
| 874 free(t->input_gamma_table_g); |
| 875 if (t->input_gamma_table_g != t->input_gamma_table_r && |
| 876 t->input_gamma_table_g != t->input_gamma_table_b) |
| 877 free(t->input_gamma_table_b); |
| 878 |
| 879 free(t->input_gamma_table_gray); |
| 880 |
| 881 free(t->output_gamma_lut_r); |
| 882 free(t->output_gamma_lut_g); |
| 883 free(t->output_gamma_lut_b); |
| 884 |
| 885 transform_free(t); |
| 886 } |
| 887 |
| 888 #ifdef X86 |
| 889 // Determine if we can build with SSE2 (this was partly copied from jmorecfg.h i
n |
| 890 // mozilla/jpeg) |
| 891 // ------------------------------------------------------------------------- |
| 892 #if defined(_M_IX86) && defined(_MSC_VER) |
| 893 #define HAS_CPUID |
| 894 /* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC |
| 895 register - I'm not sure if that ever happens on windows, but cpuid isn't |
| 896 on the critical path so we just preserve the register to be safe and to be |
| 897 consistent with the non-windows version. */ |
| 898 static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t
*d) { |
| 899 uint32_t a_, b_, c_, d_; |
| 900 __asm { |
| 901 xchg ebx, esi |
| 902 mov eax, fxn |
| 903 cpuid |
| 904 mov a_, eax |
| 905 mov b_, ebx |
| 906 mov c_, ecx |
| 907 mov d_, edx |
| 908 xchg ebx, esi |
| 909 } |
| 910 *a = a_; |
| 911 *b = b_; |
| 912 *c = c_; |
| 913 *d = d_; |
| 914 } |
| 915 #elif (defined(__GNUC__) || defined(__SUNPRO_C)) && (defined(__i386__) || define
d(__i386)) |
| 916 #define HAS_CPUID |
| 917 /* Get us a CPUID function. We can't use ebx because it's the PIC register on |
| 918 some platforms, so we use ESI instead and save ebx to avoid clobbering it. */ |
| 919 static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t
*d) { |
| 920 |
| 921 uint32_t a_, b_, c_, d_; |
| 922 __asm__ __volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;" |
| 923 : "=a" (a_), "=S" (b_), "=c" (c_), "=d" (d_) : "a"
(fxn)); |
| 924 *a = a_; |
| 925 *b = b_; |
| 926 *c = c_; |
| 927 *d = d_; |
| 928 } |
| 929 #endif |
| 930 |
| 931 // -------------------------Runtime SSEx Detection----------------------------- |
| 932 |
| 933 /* MMX is always supported per |
| 934 * Gecko v1.9.1 minimum CPU requirements */ |
| 935 #define SSE1_EDX_MASK (1UL << 25) |
| 936 #define SSE2_EDX_MASK (1UL << 26) |
| 937 #define SSE3_ECX_MASK (1UL << 0) |
| 938 |
| 939 static int sse_version_available(void) |
| 940 { |
| 941 #if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) |
| 942 /* we know at build time that 64-bit CPUs always have SSE2 |
| 943 * this tells the compiler that non-SSE2 branches will never be |
| 944 * taken (i.e. OK to optimze away the SSE1 and non-SIMD code */ |
| 945 return 2; |
| 946 #elif defined(HAS_CPUID) |
| 947 static int sse_version = -1; |
| 948 uint32_t a, b, c, d; |
| 949 uint32_t function = 0x00000001; |
| 950 |
| 951 if (sse_version == -1) { |
| 952 sse_version = 0; |
| 953 cpuid(function, &a, &b, &c, &d); |
| 954 if (c & SSE3_ECX_MASK) |
| 955 sse_version = 3; |
| 956 else if (d & SSE2_EDX_MASK) |
| 957 sse_version = 2; |
| 958 else if (d & SSE1_EDX_MASK) |
| 959 sse_version = 1; |
| 960 } |
| 961 |
| 962 return sse_version; |
| 963 #else |
| 964 return 0; |
| 965 #endif |
| 966 } |
| 967 #endif |
| 968 |
| 969 static const struct matrix bradford_matrix = {{ { 0.8951f, 0.2664f,-0.1614f}, |
| 970 {-0.7502f, 1.7135f, 0.0367f}, |
| 971 { 0.0389f,-0.0685f, 1.0296f}}, |
| 972 false}; |
| 973 |
| 974 static const struct matrix bradford_matrix_inv = {{ { 0.9869929f,-0.1470543f, 0.
1599627f}, |
| 975 { 0.4323053f, 0.5183603f, 0.
0492912f}, |
| 976 {-0.0085287f, 0.0400428f, 0.
9684867f}}, |
| 977 false}; |
| 978 |
| 979 // See ICCv4 E.3 |
| 980 struct matrix compute_whitepoint_adaption(float X, float Y, float Z) { |
| 981 float p = (0.96422f*bradford_matrix.m[0][0] + 1.000f*bradford_matrix.m[1
][0] + 0.82521f*bradford_matrix.m[2][0]) / |
| 982 (X*bradford_matrix.m[0][0] + Y*bradford_matrix.m[1][0]
+ Z*bradford_matrix.m[2][0] ); |
| 983 float y = (0.96422f*bradford_matrix.m[0][1] + 1.000f*bradford_matrix.m[1
][1] + 0.82521f*bradford_matrix.m[2][1]) / |
| 984 (X*bradford_matrix.m[0][1] + Y*bradford_matrix.m[1][1]
+ Z*bradford_matrix.m[2][1] ); |
| 985 float b = (0.96422f*bradford_matrix.m[0][2] + 1.000f*bradford_matrix.m[1
][2] + 0.82521f*bradford_matrix.m[2][2]) / |
| 986 (X*bradford_matrix.m[0][2] + Y*bradford_matrix.m[1][2]
+ Z*bradford_matrix.m[2][2] ); |
| 987 struct matrix white_adaption = {{ {p,0,0}, {0,y,0}, {0,0,b}}, false}; |
| 988 return matrix_multiply( bradford_matrix_inv, matrix_multiply(white_adapt
ion, bradford_matrix) ); |
| 989 } |
| 990 |
| 991 void qcms_profile_precache_output_transform(qcms_profile *profile) |
| 992 { |
| 993 /* we only support precaching on rgb profiles */ |
| 994 if (profile->color_space != RGB_SIGNATURE) |
| 995 return; |
| 996 |
| 997 /* don't precache since we will use the B2A LUT */ |
| 998 if (profile->B2A0) |
| 999 return; |
| 1000 |
| 1001 /* don't precache since we will use the mBA LUT */ |
| 1002 if (profile->mBA) |
| 1003 return; |
| 1004 |
| 1005 /* don't precache if we do not have the TRC curves */ |
| 1006 if (!profile->redTRC || !profile->greenTRC || !profile->blueTRC) |
| 1007 return; |
| 1008 |
| 1009 if (!profile->output_table_r) { |
| 1010 profile->output_table_r = precache_create(); |
| 1011 if (profile->output_table_r && |
| 1012 !compute_precache(profile->redTRC, profile->outp
ut_table_r->data)) { |
| 1013 precache_release(profile->output_table_r); |
| 1014 profile->output_table_r = NULL; |
| 1015 } |
| 1016 } |
| 1017 if (!profile->output_table_g) { |
| 1018 profile->output_table_g = precache_create(); |
| 1019 if (profile->output_table_g && |
| 1020 !compute_precache(profile->greenTRC, profile->ou
tput_table_g->data)) { |
| 1021 precache_release(profile->output_table_g); |
| 1022 profile->output_table_g = NULL; |
| 1023 } |
| 1024 } |
| 1025 if (!profile->output_table_b) { |
| 1026 profile->output_table_b = precache_create(); |
| 1027 if (profile->output_table_b && |
| 1028 !compute_precache(profile->blueTRC, profile->out
put_table_b->data)) { |
| 1029 precache_release(profile->output_table_b); |
| 1030 profile->output_table_b = NULL; |
| 1031 } |
| 1032 } |
| 1033 } |
| 1034 |
| 1035 /* Replace the current transformation with a LUT transformation using a given nu
mber of sample points */ |
| 1036 qcms_transform* qcms_transform_precacheLUT_float(qcms_transform *transform, qcms
_profile *in, qcms_profile *out, |
| 1037 int samples, qcms_data_type in_
type) |
| 1038 { |
| 1039 /* The range between which 2 consecutive sample points can be used to in
terpolate */ |
| 1040 uint16_t x,y,z; |
| 1041 uint32_t l; |
| 1042 uint32_t lutSize = 3 * samples * samples * samples; |
| 1043 float* src = NULL; |
| 1044 float* dest = NULL; |
| 1045 float* lut = NULL; |
| 1046 |
| 1047 src = malloc(lutSize*sizeof(float)); |
| 1048 dest = malloc(lutSize*sizeof(float)); |
| 1049 |
| 1050 if (src && dest) { |
| 1051 /* Prepare a list of points we want to sample */ |
| 1052 l = 0; |
| 1053 for (x = 0; x < samples; x++) { |
| 1054 for (y = 0; y < samples; y++) { |
| 1055 for (z = 0; z < samples; z++) { |
| 1056 src[l++] = x / (float)(samples-1); |
| 1057 src[l++] = y / (float)(samples-1); |
| 1058 src[l++] = z / (float)(samples-1); |
| 1059 } |
| 1060 } |
| 1061 } |
| 1062 |
| 1063 lut = qcms_chain_transform(in, out, src, dest, lutSize); |
| 1064 if (lut) { |
| 1065 transform->r_clut = &lut[0]; |
| 1066 transform->g_clut = &lut[1]; |
| 1067 transform->b_clut = &lut[2]; |
| 1068 transform->grid_size = samples; |
| 1069 if (in_type == QCMS_DATA_RGBA_8) { |
| 1070 transform->transform_fn = qcms_transform_data_te
tra_clut_rgba; |
| 1071 } else { |
| 1072 transform->transform_fn = qcms_transform_data_te
tra_clut; |
| 1073 } |
| 1074 } |
| 1075 } |
| 1076 |
| 1077 |
| 1078 //XXX: qcms_modular_transform_data may return either the src or dest buf
fer. If so it must not be free-ed |
| 1079 if (src && lut != src) { |
| 1080 free(src); |
| 1081 } else if (dest && lut != src) { |
| 1082 free(dest); |
| 1083 } |
| 1084 |
| 1085 if (lut == NULL) { |
| 1086 return NULL; |
| 1087 } |
| 1088 return transform; |
| 1089 } |
| 1090 |
| 1091 #define NO_MEM_TRANSFORM NULL |
| 1092 |
| 1093 qcms_transform* qcms_transform_create( |
| 1094 qcms_profile *in, qcms_data_type in_type, |
| 1095 qcms_profile *out, qcms_data_type out_type, |
| 1096 qcms_intent intent) |
| 1097 { |
| 1098 bool precache = false; |
| 1099 |
| 1100 qcms_transform *transform = transform_alloc(); |
| 1101 if (!transform) { |
| 1102 return NULL; |
| 1103 } |
| 1104 if (out_type != QCMS_DATA_RGB_8 && |
| 1105 out_type != QCMS_DATA_RGBA_8) { |
| 1106 assert(0 && "output type"); |
| 1107 transform_free(transform); |
| 1108 return NULL; |
| 1109 } |
| 1110 |
| 1111 if (out->output_table_r && |
| 1112 out->output_table_g && |
| 1113 out->output_table_b) { |
| 1114 precache = true; |
| 1115 } |
| 1116 |
| 1117 if (qcms_supports_iccv4 && (in->A2B0 || out->B2A0 || in->mAB || out->mAB
)) { |
| 1118 // Precache the transformation to a CLUT 33x33x33 in size. |
| 1119 // 33 is used by many profiles and works well in pratice. |
| 1120 // This evenly divides 256 into blocks of 8x8x8. |
| 1121 // TODO For transforming small data sets of about 200x200 or les
s |
| 1122 // precaching should be avoided. |
| 1123 qcms_transform *result = qcms_transform_precacheLUT_float(transf
orm, in, out, 33, in_type); |
| 1124 if (!result) { |
| 1125 assert(0 && "precacheLUT failed"); |
| 1126 transform_free(transform); |
| 1127 return NULL; |
| 1128 } |
| 1129 return result; |
| 1130 } |
| 1131 |
| 1132 if (precache) { |
| 1133 transform->output_table_r = precache_reference(out->output_table
_r); |
| 1134 transform->output_table_g = precache_reference(out->output_table
_g); |
| 1135 transform->output_table_b = precache_reference(out->output_table
_b); |
| 1136 } else { |
| 1137 if (!out->redTRC || !out->greenTRC || !out->blueTRC) { |
| 1138 qcms_transform_release(transform); |
| 1139 return NO_MEM_TRANSFORM; |
| 1140 } |
| 1141 build_output_lut(out->redTRC, &transform->output_gamma_lut_r, &t
ransform->output_gamma_lut_r_length); |
| 1142 build_output_lut(out->greenTRC, &transform->output_gamma_lut_g,
&transform->output_gamma_lut_g_length); |
| 1143 build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, &
transform->output_gamma_lut_b_length); |
| 1144 if (!transform->output_gamma_lut_r || !transform->output_gamma_l
ut_g || !transform->output_gamma_lut_b) { |
| 1145 qcms_transform_release(transform); |
| 1146 return NO_MEM_TRANSFORM; |
| 1147 } |
| 1148 } |
| 1149 |
| 1150 if (in->color_space == RGB_SIGNATURE) { |
| 1151 struct matrix in_matrix, out_matrix, result; |
| 1152 |
| 1153 if (in_type != QCMS_DATA_RGB_8 && |
| 1154 in_type != QCMS_DATA_RGBA_8){ |
| 1155 assert(0 && "input type"); |
| 1156 transform_free(transform); |
| 1157 return NULL; |
| 1158 } |
| 1159 if (precache) { |
| 1160 #ifdef X86 |
| 1161 if (sse_version_available() >= 2) { |
| 1162 if (in_type == QCMS_DATA_RGB_8) |
| 1163 transform->transform_fn = qcms_transform_dat
a_rgb_out_lut_sse2; |
| 1164 else |
| 1165 transform->transform_fn = qcms_transform_dat
a_rgba_out_lut_sse2; |
| 1166 |
| 1167 #if !(defined(_MSC_VER) && defined(_M_AMD64)) |
| 1168 /* Microsoft Compiler for x64 doesn't support MMX. |
| 1169 * SSE code uses MMX so that we disable on x64 */ |
| 1170 } else |
| 1171 if (sse_version_available() >= 1) { |
| 1172 if (in_type == QCMS_DATA_RGB_8) |
| 1173 transform->transform_fn = qcms_transform_dat
a_rgb_out_lut_sse1; |
| 1174 else |
| 1175 transform->transform_fn = qcms_transform_dat
a_rgba_out_lut_sse1; |
| 1176 #endif |
| 1177 } else |
| 1178 #endif |
| 1179 { |
| 1180 if (in_type == QCMS_DATA_RGB_8) |
| 1181 transform->transform_fn = qcms_transform
_data_rgb_out_lut_precache; |
| 1182 else |
| 1183 transform->transform_fn = qcms_transform
_data_rgba_out_lut_precache; |
| 1184 } |
| 1185 } else { |
| 1186 if (in_type == QCMS_DATA_RGB_8) |
| 1187 transform->transform_fn = qcms_transform_data_rg
b_out_lut; |
| 1188 else |
| 1189 transform->transform_fn = qcms_transform_data_rg
ba_out_lut; |
| 1190 } |
| 1191 |
| 1192 //XXX: avoid duplicating tables if we can |
| 1193 transform->input_gamma_table_r = build_input_gamma_table(in->red
TRC); |
| 1194 transform->input_gamma_table_g = build_input_gamma_table(in->gre
enTRC); |
| 1195 transform->input_gamma_table_b = build_input_gamma_table(in->blu
eTRC); |
| 1196 if (!transform->input_gamma_table_r || !transform->input_gamma_t
able_g || !transform->input_gamma_table_b) { |
| 1197 qcms_transform_release(transform); |
| 1198 return NO_MEM_TRANSFORM; |
| 1199 } |
| 1200 |
| 1201 |
| 1202 /* build combined colorant matrix */ |
| 1203 in_matrix = build_colorant_matrix(in); |
| 1204 out_matrix = build_colorant_matrix(out); |
| 1205 out_matrix = matrix_invert(out_matrix); |
| 1206 if (out_matrix.invalid) { |
| 1207 qcms_transform_release(transform); |
| 1208 return NULL; |
| 1209 } |
| 1210 result = matrix_multiply(out_matrix, in_matrix); |
| 1211 |
| 1212 /* store the results in column major mode |
| 1213 * this makes doing the multiplication with sse easier */ |
| 1214 transform->matrix[0][0] = result.m[0][0]; |
| 1215 transform->matrix[1][0] = result.m[0][1]; |
| 1216 transform->matrix[2][0] = result.m[0][2]; |
| 1217 transform->matrix[0][1] = result.m[1][0]; |
| 1218 transform->matrix[1][1] = result.m[1][1]; |
| 1219 transform->matrix[2][1] = result.m[1][2]; |
| 1220 transform->matrix[0][2] = result.m[2][0]; |
| 1221 transform->matrix[1][2] = result.m[2][1]; |
| 1222 transform->matrix[2][2] = result.m[2][2]; |
| 1223 |
| 1224 } else if (in->color_space == GRAY_SIGNATURE) { |
| 1225 if (in_type != QCMS_DATA_GRAY_8 && |
| 1226 in_type != QCMS_DATA_GRAYA_8){ |
| 1227 assert(0 && "input type"); |
| 1228 transform_free(transform); |
| 1229 return NULL; |
| 1230 } |
| 1231 |
| 1232 transform->input_gamma_table_gray = build_input_gamma_table(in->
grayTRC); |
| 1233 if (!transform->input_gamma_table_gray) { |
| 1234 qcms_transform_release(transform); |
| 1235 return NO_MEM_TRANSFORM; |
| 1236 } |
| 1237 |
| 1238 if (precache) { |
| 1239 if (in_type == QCMS_DATA_GRAY_8) { |
| 1240 transform->transform_fn = qcms_transform_data_gr
ay_out_precache; |
| 1241 } else { |
| 1242 transform->transform_fn = qcms_transform_data_gr
aya_out_precache; |
| 1243 } |
| 1244 } else { |
| 1245 if (in_type == QCMS_DATA_GRAY_8) { |
| 1246 transform->transform_fn = qcms_transform_data_gr
ay_out_lut; |
| 1247 } else { |
| 1248 transform->transform_fn = qcms_transform_data_gr
aya_out_lut; |
| 1249 } |
| 1250 } |
| 1251 } else { |
| 1252 assert(0 && "unexpected colorspace"); |
| 1253 transform_free(transform); |
| 1254 return NULL; |
| 1255 } |
| 1256 return transform; |
| 1257 } |
| 1258 |
| 1259 #if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) |
| 1260 /* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */ |
| 1261 __attribute__((__force_align_arg_pointer__)) |
| 1262 #endif |
| 1263 void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_
t length) |
| 1264 { |
| 1265 transform->transform_fn(transform, src, dest, length); |
| 1266 } |
| 1267 |
| 1268 qcms_bool qcms_supports_iccv4; |
| 1269 void qcms_enable_iccv4() |
| 1270 { |
| 1271 qcms_supports_iccv4 = true; |
| 1272 } |
OLD | NEW |