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 "halffloat.h" |
| 31 #include "matrix.h" |
| 32 #include "transform_util.h" |
| 33 |
| 34 /* for MSVC, GCC, Intel, and Sun compilers */ |
| 35 #if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_AMD64
) || defined(__x86_64__) || defined(__x86_64) |
| 36 #define X86 |
| 37 #endif /* _M_IX86 || __i386__ || __i386 || _M_AMD64 || __x86_64__ || __x86_64 */ |
| 38 |
| 39 // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ |
| 40 // This is just an approximation, I am not handling all the non-linear |
| 41 // aspects of the RGB to XYZ process, and assumming that the gamma correction |
| 42 // has transitive property in the tranformation chain. |
| 43 // |
| 44 // the alghoritm: |
| 45 // |
| 46 // - First I build the absolute conversion matrix using |
| 47 // primaries in XYZ. This matrix is next inverted |
| 48 // - Then I eval the source white point across this matrix |
| 49 // obtaining the coeficients of the transformation |
| 50 // - Then, I apply these coeficients to the original matrix |
| 51 static struct matrix build_RGB_to_XYZ_transfer_matrix(qcms_CIE_xyY white, qcms_C
IE_xyYTRIPLE primrs) |
| 52 { |
| 53 struct matrix primaries; |
| 54 struct matrix primaries_invert; |
| 55 struct matrix result; |
| 56 struct vector white_point; |
| 57 struct vector coefs; |
| 58 |
| 59 double xn, yn; |
| 60 double xr, yr; |
| 61 double xg, yg; |
| 62 double xb, yb; |
| 63 |
| 64 xn = white.x; |
| 65 yn = white.y; |
| 66 |
| 67 if (yn == 0.0) |
| 68 return matrix_invalid(); |
| 69 |
| 70 xr = primrs.red.x; |
| 71 yr = primrs.red.y; |
| 72 xg = primrs.green.x; |
| 73 yg = primrs.green.y; |
| 74 xb = primrs.blue.x; |
| 75 yb = primrs.blue.y; |
| 76 |
| 77 primaries.m[0][0] = xr; |
| 78 primaries.m[0][1] = xg; |
| 79 primaries.m[0][2] = xb; |
| 80 |
| 81 primaries.m[1][0] = yr; |
| 82 primaries.m[1][1] = yg; |
| 83 primaries.m[1][2] = yb; |
| 84 |
| 85 primaries.m[2][0] = 1 - xr - yr; |
| 86 primaries.m[2][1] = 1 - xg - yg; |
| 87 primaries.m[2][2] = 1 - xb - yb; |
| 88 primaries.invalid = false; |
| 89 |
| 90 white_point.v[0] = xn/yn; |
| 91 white_point.v[1] = 1.; |
| 92 white_point.v[2] = (1.0-xn-yn)/yn; |
| 93 |
| 94 primaries_invert = matrix_invert(primaries); |
| 95 |
| 96 coefs = matrix_eval(primaries_invert, white_point); |
| 97 |
| 98 result.m[0][0] = coefs.v[0]*xr; |
| 99 result.m[0][1] = coefs.v[1]*xg; |
| 100 result.m[0][2] = coefs.v[2]*xb; |
| 101 |
| 102 result.m[1][0] = coefs.v[0]*yr; |
| 103 result.m[1][1] = coefs.v[1]*yg; |
| 104 result.m[1][2] = coefs.v[2]*yb; |
| 105 |
| 106 result.m[2][0] = coefs.v[0]*(1.-xr-yr); |
| 107 result.m[2][1] = coefs.v[1]*(1.-xg-yg); |
| 108 result.m[2][2] = coefs.v[2]*(1.-xb-yb); |
| 109 result.invalid = primaries_invert.invalid; |
| 110 |
| 111 return result; |
| 112 } |
| 113 |
| 114 struct CIE_XYZ { |
| 115 double X; |
| 116 double Y; |
| 117 double Z; |
| 118 }; |
| 119 |
| 120 /* CIE Illuminant D50 */ |
| 121 static const struct CIE_XYZ D50_XYZ = { |
| 122 0.9642, |
| 123 1.0000, |
| 124 0.8249 |
| 125 }; |
| 126 |
| 127 /* from lcms: xyY2XYZ() |
| 128 * corresponds to argyll: icmYxy2XYZ() */ |
| 129 static struct CIE_XYZ xyY2XYZ(qcms_CIE_xyY source) |
| 130 { |
| 131 struct CIE_XYZ dest; |
| 132 dest.X = (source.x / source.y) * source.Y; |
| 133 dest.Y = source.Y; |
| 134 dest.Z = ((1 - source.x - source.y) / source.y) * source.Y; |
| 135 return dest; |
| 136 } |
| 137 |
| 138 /* from lcms: ComputeChromaticAdaption */ |
| 139 // Compute chromatic adaption matrix using chad as cone matrix |
| 140 static struct matrix |
| 141 compute_chromatic_adaption(struct CIE_XYZ source_white_point, |
| 142 struct CIE_XYZ dest_white_point, |
| 143 struct matrix chad) |
| 144 { |
| 145 struct matrix chad_inv; |
| 146 struct vector cone_source_XYZ, cone_source_rgb; |
| 147 struct vector cone_dest_XYZ, cone_dest_rgb; |
| 148 struct matrix cone, tmp; |
| 149 |
| 150 tmp = chad; |
| 151 chad_inv = matrix_invert(tmp); |
| 152 |
| 153 cone_source_XYZ.v[0] = source_white_point.X; |
| 154 cone_source_XYZ.v[1] = source_white_point.Y; |
| 155 cone_source_XYZ.v[2] = source_white_point.Z; |
| 156 |
| 157 cone_dest_XYZ.v[0] = dest_white_point.X; |
| 158 cone_dest_XYZ.v[1] = dest_white_point.Y; |
| 159 cone_dest_XYZ.v[2] = dest_white_point.Z; |
| 160 |
| 161 cone_source_rgb = matrix_eval(chad, cone_source_XYZ); |
| 162 cone_dest_rgb = matrix_eval(chad, cone_dest_XYZ); |
| 163 |
| 164 cone.m[0][0] = cone_dest_rgb.v[0]/cone_source_rgb.v[0]; |
| 165 cone.m[0][1] = 0; |
| 166 cone.m[0][2] = 0; |
| 167 cone.m[1][0] = 0; |
| 168 cone.m[1][1] = cone_dest_rgb.v[1]/cone_source_rgb.v[1]; |
| 169 cone.m[1][2] = 0; |
| 170 cone.m[2][0] = 0; |
| 171 cone.m[2][1] = 0; |
| 172 cone.m[2][2] = cone_dest_rgb.v[2]/cone_source_rgb.v[2]; |
| 173 cone.invalid = false; |
| 174 |
| 175 // Normalize |
| 176 return matrix_multiply(chad_inv, matrix_multiply(cone, chad)); |
| 177 } |
| 178 |
| 179 /* from lcms: cmsAdaptionMatrix */ |
| 180 // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant T
oIll |
| 181 // Bradford is assumed |
| 182 static struct matrix |
| 183 adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumi
nation) |
| 184 { |
| 185 #if defined (_MSC_VER) |
| 186 #pragma warning(push) |
| 187 /* Disable double to float truncation warning 4305 */ |
| 188 #pragma warning(disable:4305) |
| 189 #endif |
| 190 struct matrix lam_rigg = {{ // Bradford matrix |
| 191 { 0.8951, 0.2664, -0.1614 }, |
| 192 { -0.7502, 1.7135, 0.0367 }, |
| 193 { 0.0389, -0.0685, 1.0296 } |
| 194 }}; |
| 195 #if defined (_MSC_VER) |
| 196 /* Restore warnings */ |
| 197 #pragma warning(pop) |
| 198 #endif |
| 199 return compute_chromatic_adaption(source_illumination, target_illuminati
on, lam_rigg); |
| 200 } |
| 201 |
| 202 /* from lcms: cmsAdaptMatrixToD50 */ |
| 203 static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_wh
ite_point) |
| 204 { |
| 205 struct CIE_XYZ DNN_XYZ; |
| 206 struct matrix Bradford; |
| 207 |
| 208 if (source_white_point.y == 0.0) |
| 209 return matrix_invalid(); |
| 210 |
| 211 DNN_XYZ = xyY2XYZ(source_white_point); |
| 212 |
| 213 Bradford = adaption_matrix(DNN_XYZ, D50_XYZ); |
| 214 |
| 215 return matrix_multiply(Bradford, r); |
| 216 } |
| 217 |
| 218 qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcm
s_CIE_xyYTRIPLE primaries) |
| 219 { |
| 220 struct CIE_XYZ source_white; |
| 221 struct matrix colorants; |
| 222 |
| 223 colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries); |
| 224 colorants = adapt_matrix_to_D50(colorants, white_point); |
| 225 |
| 226 if (colorants.invalid) |
| 227 return false; |
| 228 |
| 229 /* note: there's a transpose type of operation going on here */ |
| 230 profile->redColorant.X = double_to_s15Fixed16Number(colorants.m[0][0]); |
| 231 profile->redColorant.Y = double_to_s15Fixed16Number(colorants.m[1][0]); |
| 232 profile->redColorant.Z = double_to_s15Fixed16Number(colorants.m[2][0]); |
| 233 |
| 234 profile->greenColorant.X = double_to_s15Fixed16Number(colorants.m[0][1])
; |
| 235 profile->greenColorant.Y = double_to_s15Fixed16Number(colorants.m[1][1])
; |
| 236 profile->greenColorant.Z = double_to_s15Fixed16Number(colorants.m[2][1])
; |
| 237 |
| 238 profile->blueColorant.X = double_to_s15Fixed16Number(colorants.m[0][2]); |
| 239 profile->blueColorant.Y = double_to_s15Fixed16Number(colorants.m[1][2]); |
| 240 profile->blueColorant.Z = double_to_s15Fixed16Number(colorants.m[2][2]); |
| 241 |
| 242 /* Store the media white point */ |
| 243 source_white = xyY2XYZ(white_point); |
| 244 profile->mediaWhitePoint.X = double_to_s15Fixed16Number(source_white.X); |
| 245 profile->mediaWhitePoint.Y = double_to_s15Fixed16Number(source_white.Y); |
| 246 profile->mediaWhitePoint.Z = double_to_s15Fixed16Number(source_white.Z); |
| 247 |
| 248 return true; |
| 249 } |
| 250 |
| 251 #if 0 |
| 252 static void qcms_transform_data_rgb_out_pow(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 253 { |
| 254 const int r_out = output_format.r; |
| 255 const int b_out = output_format.b; |
| 256 |
| 257 int i; |
| 258 float (*mat)[4] = transform->matrix; |
| 259 for (i=0; i<length; i++) { |
| 260 unsigned char device_r = *src++; |
| 261 unsigned char device_g = *src++; |
| 262 unsigned char device_b = *src++; |
| 263 |
| 264 float linear_r = transform->input_gamma_table_r[device_r]; |
| 265 float linear_g = transform->input_gamma_table_g[device_g]; |
| 266 float linear_b = transform->input_gamma_table_b[device_b]; |
| 267 |
| 268 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 269 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 270 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 271 |
| 272 float out_device_r = pow(out_linear_r, transform->out_gamma_r); |
| 273 float out_device_g = pow(out_linear_g, transform->out_gamma_g); |
| 274 float out_device_b = pow(out_linear_b, transform->out_gamma_b); |
| 275 |
| 276 dest[r_out] = clamp_u8(out_device_r*255); |
| 277 dest[1] = clamp_u8(out_device_g*255); |
| 278 dest[b_out] = clamp_u8(out_device_b*255); |
| 279 dest += 3; |
| 280 } |
| 281 } |
| 282 #endif |
| 283 |
| 284 static void qcms_transform_data_gray_out_lut(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 285 { |
| 286 const int r_out = output_format.r; |
| 287 const int b_out = output_format.b; |
| 288 |
| 289 unsigned int i; |
| 290 for (i = 0; i < length; i++) { |
| 291 float out_device_r, out_device_g, out_device_b; |
| 292 unsigned char device = *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[r_out] = clamp_u8(out_device_r*255); |
| 301 dest[1] = clamp_u8(out_device_g*255); |
| 302 dest[b_out] = clamp_u8(out_device_b*255); |
| 303 dest += 3; |
| 304 } |
| 305 } |
| 306 |
| 307 /* Alpha is not corrected. |
| 308 A rationale for this is found in Alvy Ray's "Should Alpha Be Nonlinear If |
| 309 RGB Is?" Tech Memo 17 (December 14, 1998). |
| 310 See: ftp://ftp.alvyray.com/Acrobat/17_Nonln.pdf |
| 311 */ |
| 312 |
| 313 static void qcms_transform_data_graya_out_lut(qcms_transform *transform, unsigne
d char *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 314 { |
| 315 const int r_out = output_format.r; |
| 316 const int b_out = output_format.b; |
| 317 |
| 318 unsigned int i; |
| 319 for (i = 0; i < length; i++) { |
| 320 float out_device_r, out_device_g, out_device_b; |
| 321 unsigned char device = *src++; |
| 322 unsigned char alpha = *src++; |
| 323 |
| 324 float linear = transform->input_gamma_table_gray[device]; |
| 325 |
| 326 out_device_r = lut_interp_linear(linear, transform->output_gamma
_lut_r, transform->output_gamma_lut_r_length); |
| 327 out_device_g = lut_interp_linear(linear, transform->output_gamma
_lut_g, transform->output_gamma_lut_g_length); |
| 328 out_device_b = lut_interp_linear(linear, transform->output_gamma
_lut_b, transform->output_gamma_lut_b_length); |
| 329 |
| 330 dest[r_out] = clamp_u8(out_device_r*255); |
| 331 dest[1] = clamp_u8(out_device_g*255); |
| 332 dest[b_out] = clamp_u8(out_device_b*255); |
| 333 dest[3] = alpha; |
| 334 dest += 4; |
| 335 } |
| 336 } |
| 337 |
| 338 |
| 339 static void qcms_transform_data_gray_out_precache(qcms_transform *transform, uns
igned char *src, unsigned char *dest, size_t length, qcms_format_type output_for
mat) |
| 340 { |
| 341 const int r_out = output_format.r; |
| 342 const int b_out = output_format.b; |
| 343 |
| 344 unsigned int i; |
| 345 for (i = 0; i < length; i++) { |
| 346 unsigned char device = *src++; |
| 347 uint16_t gray; |
| 348 |
| 349 float linear = transform->input_gamma_table_gray[device]; |
| 350 |
| 351 /* we could round here... */ |
| 352 gray = linear * PRECACHE_OUTPUT_MAX; |
| 353 |
| 354 dest[r_out] = transform->output_table_r->data[gray]; |
| 355 dest[1] = transform->output_table_g->data[gray]; |
| 356 dest[b_out] = transform->output_table_b->data[gray]; |
| 357 dest += 3; |
| 358 } |
| 359 } |
| 360 |
| 361 |
| 362 static void qcms_transform_data_graya_out_precache(qcms_transform *transform, un
signed char *src, unsigned char *dest, size_t length, qcms_format_type output_fo
rmat) |
| 363 { |
| 364 const int r_out = output_format.r; |
| 365 const int b_out = output_format.b; |
| 366 |
| 367 unsigned int i; |
| 368 for (i = 0; i < length; i++) { |
| 369 unsigned char device = *src++; |
| 370 unsigned char alpha = *src++; |
| 371 uint16_t gray; |
| 372 |
| 373 float linear = transform->input_gamma_table_gray[device]; |
| 374 |
| 375 /* we could round here... */ |
| 376 gray = linear * PRECACHE_OUTPUT_MAX; |
| 377 |
| 378 dest[r_out] = transform->output_table_r->data[gray]; |
| 379 dest[1] = transform->output_table_g->data[gray]; |
| 380 dest[b_out] = transform->output_table_b->data[gray]; |
| 381 dest[3] = alpha; |
| 382 dest += 4; |
| 383 } |
| 384 } |
| 385 |
| 386 static void qcms_transform_data_rgb_out_lut_precache(qcms_transform *transform,
unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_
format) |
| 387 { |
| 388 const int r_out = output_format.r; |
| 389 const int b_out = output_format.b; |
| 390 |
| 391 unsigned int i; |
| 392 float (*mat)[4] = transform->matrix; |
| 393 for (i = 0; i < length; i++) { |
| 394 unsigned char device_r = *src++; |
| 395 unsigned char device_g = *src++; |
| 396 unsigned char device_b = *src++; |
| 397 uint16_t r, g, b; |
| 398 |
| 399 float linear_r = transform->input_gamma_table_r[device_r]; |
| 400 float linear_g = transform->input_gamma_table_g[device_g]; |
| 401 float linear_b = transform->input_gamma_table_b[device_b]; |
| 402 |
| 403 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 404 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 405 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 406 |
| 407 out_linear_r = clamp_float(out_linear_r); |
| 408 out_linear_g = clamp_float(out_linear_g); |
| 409 out_linear_b = clamp_float(out_linear_b); |
| 410 |
| 411 /* we could round here... */ |
| 412 r = out_linear_r * PRECACHE_OUTPUT_MAX; |
| 413 g = out_linear_g * PRECACHE_OUTPUT_MAX; |
| 414 b = out_linear_b * PRECACHE_OUTPUT_MAX; |
| 415 |
| 416 dest[r_out] = transform->output_table_r->data[r]; |
| 417 dest[1] = transform->output_table_g->data[g]; |
| 418 dest[b_out] = transform->output_table_b->data[b]; |
| 419 dest += 3; |
| 420 } |
| 421 } |
| 422 |
| 423 void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform, unsign
ed char *src, unsigned char *dest, size_t length, qcms_format_type output_format
) |
| 424 { |
| 425 const int r_out = output_format.r; |
| 426 const int b_out = output_format.b; |
| 427 |
| 428 unsigned int i; |
| 429 float (*mat)[4] = transform->matrix; |
| 430 for (i = 0; i < length; i++) { |
| 431 unsigned char device_r = *src++; |
| 432 unsigned char device_g = *src++; |
| 433 unsigned char device_b = *src++; |
| 434 unsigned char alpha = *src++; |
| 435 uint16_t r, g, b; |
| 436 |
| 437 float linear_r = transform->input_gamma_table_r[device_r]; |
| 438 float linear_g = transform->input_gamma_table_g[device_g]; |
| 439 float linear_b = transform->input_gamma_table_b[device_b]; |
| 440 |
| 441 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 442 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 443 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 444 |
| 445 out_linear_r = clamp_float(out_linear_r); |
| 446 out_linear_g = clamp_float(out_linear_g); |
| 447 out_linear_b = clamp_float(out_linear_b); |
| 448 |
| 449 /* we could round here... */ |
| 450 r = out_linear_r * PRECACHE_OUTPUT_MAX; |
| 451 g = out_linear_g * PRECACHE_OUTPUT_MAX; |
| 452 b = out_linear_b * PRECACHE_OUTPUT_MAX; |
| 453 |
| 454 dest[r_out] = transform->output_table_r->data[r]; |
| 455 dest[1] = transform->output_table_g->data[g]; |
| 456 dest[b_out] = transform->output_table_b->data[b]; |
| 457 dest[3] = alpha; |
| 458 dest += 4; |
| 459 } |
| 460 } |
| 461 |
| 462 // Not used |
| 463 /* |
| 464 static void qcms_transform_data_clut(qcms_transform *transform, unsigned char *s
rc, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 465 { |
| 466 const int r_out = output_format.r; |
| 467 const int b_out = output_format.b; |
| 468 |
| 469 unsigned int i; |
| 470 int xy_len = 1; |
| 471 int x_len = transform->grid_size; |
| 472 int len = x_len * x_len; |
| 473 float* r_table = transform->r_clut; |
| 474 float* g_table = transform->g_clut; |
| 475 float* b_table = transform->b_clut; |
| 476 |
| 477 for (i = 0; i < length; i++) { |
| 478 unsigned char in_r = *src++; |
| 479 unsigned char in_g = *src++; |
| 480 unsigned char in_b = *src++; |
| 481 float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = i
n_b/255.0f; |
| 482 |
| 483 int x = floor(linear_r * (transform->grid_size-1)); |
| 484 int y = floor(linear_g * (transform->grid_size-1)); |
| 485 int z = floor(linear_b * (transform->grid_size-1)); |
| 486 int x_n = ceil(linear_r * (transform->grid_size-1)); |
| 487 int y_n = ceil(linear_g * (transform->grid_size-1)); |
| 488 int z_n = ceil(linear_b * (transform->grid_size-1)); |
| 489 float x_d = linear_r * (transform->grid_size-1) - x; |
| 490 float y_d = linear_g * (transform->grid_size-1) - y; |
| 491 float z_d = linear_b * (transform->grid_size-1) - z; |
| 492 |
| 493 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d)
; |
| 494 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z),
x_d); |
| 495 float r_y1 = lerp(r_x1, r_x2, y_d); |
| 496 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n),
x_d); |
| 497 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_
n), x_d); |
| 498 float r_y2 = lerp(r_x3, r_x4, y_d); |
| 499 float clut_r = lerp(r_y1, r_y2, z_d); |
| 500 |
| 501 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d)
; |
| 502 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z),
x_d); |
| 503 float g_y1 = lerp(g_x1, g_x2, y_d); |
| 504 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n),
x_d); |
| 505 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_
n), x_d); |
| 506 float g_y2 = lerp(g_x3, g_x4, y_d); |
| 507 float clut_g = lerp(g_y1, g_y2, z_d); |
| 508 |
| 509 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d)
; |
| 510 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z),
x_d); |
| 511 float b_y1 = lerp(b_x1, b_x2, y_d); |
| 512 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n),
x_d); |
| 513 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_
n), x_d); |
| 514 float b_y2 = lerp(b_x3, b_x4, y_d); |
| 515 float clut_b = lerp(b_y1, b_y2, z_d); |
| 516 |
| 517 dest[r_out] = clamp_u8(clut_r*255.0f); |
| 518 dest[1] = clamp_u8(clut_g*255.0f); |
| 519 dest[b_out] = clamp_u8(clut_b*255.0f); |
| 520 dest += 3; |
| 521 } |
| 522 } |
| 523 */ |
| 524 |
| 525 // Using lcms' tetra interpolation algorithm. |
| 526 void qcms_transform_data_tetra_clut_rgba(qcms_transform *transform, unsigned cha
r *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 527 { |
| 528 const int r_out = output_format.r; |
| 529 const int b_out = output_format.b; |
| 530 |
| 531 unsigned int i; |
| 532 int xy_len = 1; |
| 533 int x_len = transform->grid_size; |
| 534 int len = x_len * x_len; |
| 535 float* r_table = transform->r_clut; |
| 536 float* g_table = transform->g_clut; |
| 537 float* b_table = transform->b_clut; |
| 538 float c0_r, c1_r, c2_r, c3_r; |
| 539 float c0_g, c1_g, c2_g, c3_g; |
| 540 float c0_b, c1_b, c2_b, c3_b; |
| 541 float clut_r, clut_g, clut_b; |
| 542 |
| 543 if (!(transform->transform_flags & TRANSFORM_FLAG_CLUT_CACHE)) |
| 544 qcms_transform_build_clut_cache(transform); |
| 545 |
| 546 for (i = 0; i < length; i++) { |
| 547 unsigned char in_r = *src++; |
| 548 unsigned char in_g = *src++; |
| 549 unsigned char in_b = *src++; |
| 550 unsigned char in_a = *src++; |
| 551 |
| 552 int x = transform->floor_cache[in_r]; |
| 553 int y = transform->floor_cache[in_g]; |
| 554 int z = transform->floor_cache[in_b]; |
| 555 |
| 556 int x_n = transform->ceil_cache[in_r]; |
| 557 int y_n = transform->ceil_cache[in_g]; |
| 558 int z_n = transform->ceil_cache[in_b]; |
| 559 |
| 560 float rx = transform->r_cache[in_r]; |
| 561 float ry = transform->r_cache[in_g]; |
| 562 float rz = transform->r_cache[in_b]; |
| 563 |
| 564 c0_r = CLU(r_table, x, y, z); |
| 565 c0_g = CLU(g_table, x, y, z); |
| 566 c0_b = CLU(b_table, x, y, z); |
| 567 |
| 568 if( rx >= ry ) { |
| 569 if (ry >= rz) { //rx >= ry && ry >= rz |
| 570 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 571 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x_n, y, z); |
| 572 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 573 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 574 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x_n, y, z); |
| 575 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 576 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 577 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x_n, y, z); |
| 578 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 579 } else { |
| 580 if (rx >= rz) { //rx >= rz && rz >= ry |
| 581 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 582 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 583 c3_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x_n, y, z); |
| 584 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 585 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 586 c3_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x_n, y, z); |
| 587 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 588 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 589 c3_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x_n, y, z); |
| 590 } else { //rz > rx && rx >= ry |
| 591 c1_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x, y, z_n); |
| 592 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 593 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 594 c1_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x, y, z_n); |
| 595 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 596 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 597 c1_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x, y, z_n); |
| 598 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 599 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 600 } |
| 601 } |
| 602 } else { |
| 603 if (rx >= rz) { //ry > rx && rx >= rz |
| 604 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x, y_n, z); |
| 605 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 606 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 607 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x, y_n, z); |
| 608 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 609 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 610 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x, y_n, z); |
| 611 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 612 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 613 } else { |
| 614 if (ry >= rz) { //ry >= rz && rz > rx |
| 615 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 616 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 617 c3_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y_n, z); |
| 618 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 619 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 620 c3_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y_n, z); |
| 621 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 622 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 623 c3_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y_n, z); |
| 624 } else { //rz > ry && ry > rx |
| 625 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 626 c2_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y, z_n); |
| 627 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 628 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 629 c2_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y, z_n); |
| 630 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 631 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 632 c2_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y, z_n); |
| 633 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 634 } |
| 635 } |
| 636 } |
| 637 |
| 638 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz; |
| 639 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz; |
| 640 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz; |
| 641 |
| 642 dest[r_out] = clamp_u8(clut_r*255.0f); |
| 643 dest[1] = clamp_u8(clut_g*255.0f); |
| 644 dest[b_out] = clamp_u8(clut_b*255.0f); |
| 645 dest[3] = in_a; |
| 646 dest += 4; |
| 647 } |
| 648 } |
| 649 |
| 650 // Using lcms' tetra interpolation code. |
| 651 static void qcms_transform_data_tetra_clut(qcms_transform *transform, unsigned c
har *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 652 { |
| 653 const int r_out = output_format.r; |
| 654 const int b_out = output_format.b; |
| 655 |
| 656 unsigned int i; |
| 657 int xy_len = 1; |
| 658 int x_len = transform->grid_size; |
| 659 int len = x_len * x_len; |
| 660 float* r_table = transform->r_clut; |
| 661 float* g_table = transform->g_clut; |
| 662 float* b_table = transform->b_clut; |
| 663 float c0_r, c1_r, c2_r, c3_r; |
| 664 float c0_g, c1_g, c2_g, c3_g; |
| 665 float c0_b, c1_b, c2_b, c3_b; |
| 666 float clut_r, clut_g, clut_b; |
| 667 |
| 668 if (!(transform->transform_flags & TRANSFORM_FLAG_CLUT_CACHE)) |
| 669 qcms_transform_build_clut_cache(transform); |
| 670 |
| 671 for (i = 0; i < length; i++) { |
| 672 unsigned char in_r = *src++; |
| 673 unsigned char in_g = *src++; |
| 674 unsigned char in_b = *src++; |
| 675 |
| 676 int x = transform->floor_cache[in_r]; |
| 677 int y = transform->floor_cache[in_g]; |
| 678 int z = transform->floor_cache[in_b]; |
| 679 |
| 680 int x_n = transform->ceil_cache[in_r]; |
| 681 int y_n = transform->ceil_cache[in_g]; |
| 682 int z_n = transform->ceil_cache[in_b]; |
| 683 |
| 684 float rx = transform->r_cache[in_r]; |
| 685 float ry = transform->r_cache[in_g]; |
| 686 float rz = transform->r_cache[in_b]; |
| 687 |
| 688 c0_r = CLU(r_table, x, y, z); |
| 689 c0_g = CLU(g_table, x, y, z); |
| 690 c0_b = CLU(b_table, x, y, z); |
| 691 |
| 692 if( rx >= ry ) { |
| 693 if (ry >= rz) { //rx >= ry && ry >= rz |
| 694 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 695 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x_n, y, z); |
| 696 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 697 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 698 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x_n, y, z); |
| 699 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 700 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 701 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x_n, y, z); |
| 702 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 703 } else { |
| 704 if (rx >= rz) { //rx >= rz && rz >= ry |
| 705 c1_r = CLU(r_table, x_n, y, z) - c0_r; |
| 706 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 707 c3_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x_n, y, z); |
| 708 c1_g = CLU(g_table, x_n, y, z) - c0_g; |
| 709 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 710 c3_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x_n, y, z); |
| 711 c1_b = CLU(b_table, x_n, y, z) - c0_b; |
| 712 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 713 c3_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x_n, y, z); |
| 714 } else { //rz > rx && rx >= ry |
| 715 c1_r = CLU(r_table, x_n, y, z_n) - CLU(r
_table, x, y, z_n); |
| 716 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x_n, y, z_n); |
| 717 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 718 c1_g = CLU(g_table, x_n, y, z_n) - CLU(g
_table, x, y, z_n); |
| 719 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x_n, y, z_n); |
| 720 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 721 c1_b = CLU(b_table, x_n, y, z_n) - CLU(b
_table, x, y, z_n); |
| 722 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x_n, y, z_n); |
| 723 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 724 } |
| 725 } |
| 726 } else { |
| 727 if (rx >= rz) { //ry > rx && rx >= rz |
| 728 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table,
x, y_n, z); |
| 729 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 730 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table
, x_n, y_n, z); |
| 731 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table,
x, y_n, z); |
| 732 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 733 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table
, x_n, y_n, z); |
| 734 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table,
x, y_n, z); |
| 735 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 736 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table
, x_n, y_n, z); |
| 737 } else { |
| 738 if (ry >= rz) { //ry >= rz && rz > rx |
| 739 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 740 c2_r = CLU(r_table, x, y_n, z) - c0_r; |
| 741 c3_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y_n, z); |
| 742 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 743 c2_g = CLU(g_table, x, y_n, z) - c0_g; |
| 744 c3_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y_n, z); |
| 745 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 746 c2_b = CLU(b_table, x, y_n, z) - c0_b; |
| 747 c3_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y_n, z); |
| 748 } else { //rz > ry && ry > rx |
| 749 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU
(r_table, x, y_n, z_n); |
| 750 c2_r = CLU(r_table, x, y_n, z_n) - CLU(r
_table, x, y, z_n); |
| 751 c3_r = CLU(r_table, x, y, z_n) - c0_r; |
| 752 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU
(g_table, x, y_n, z_n); |
| 753 c2_g = CLU(g_table, x, y_n, z_n) - CLU(g
_table, x, y, z_n); |
| 754 c3_g = CLU(g_table, x, y, z_n) - c0_g; |
| 755 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU
(b_table, x, y_n, z_n); |
| 756 c2_b = CLU(b_table, x, y_n, z_n) - CLU(b
_table, x, y, z_n); |
| 757 c3_b = CLU(b_table, x, y, z_n) - c0_b; |
| 758 } |
| 759 } |
| 760 } |
| 761 |
| 762 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz; |
| 763 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz; |
| 764 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz; |
| 765 |
| 766 dest[r_out] = clamp_u8(clut_r*255.0f); |
| 767 dest[1] = clamp_u8(clut_g*255.0f); |
| 768 dest[b_out] = clamp_u8(clut_b*255.0f); |
| 769 dest += 3; |
| 770 } |
| 771 } |
| 772 |
| 773 static void qcms_transform_data_rgb_out_lut(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 774 { |
| 775 const int r_out = output_format.r; |
| 776 const int b_out = output_format.b; |
| 777 |
| 778 unsigned int i; |
| 779 float (*mat)[4] = transform->matrix; |
| 780 for (i = 0; i < length; i++) { |
| 781 unsigned char device_r = *src++; |
| 782 unsigned char device_g = *src++; |
| 783 unsigned char device_b = *src++; |
| 784 float out_device_r, out_device_g, out_device_b; |
| 785 |
| 786 float linear_r = transform->input_gamma_table_r[device_r]; |
| 787 float linear_g = transform->input_gamma_table_g[device_g]; |
| 788 float linear_b = transform->input_gamma_table_b[device_b]; |
| 789 |
| 790 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 791 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 792 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 793 |
| 794 out_linear_r = clamp_float(out_linear_r); |
| 795 out_linear_g = clamp_float(out_linear_g); |
| 796 out_linear_b = clamp_float(out_linear_b); |
| 797 |
| 798 out_device_r = lut_interp_linear(out_linear_r, |
| 799 transform->output_gamma_lut_r, transform->output
_gamma_lut_r_length); |
| 800 out_device_g = lut_interp_linear(out_linear_g, |
| 801 transform->output_gamma_lut_g, transform->output
_gamma_lut_g_length); |
| 802 out_device_b = lut_interp_linear(out_linear_b, |
| 803 transform->output_gamma_lut_b, transform->output
_gamma_lut_b_length); |
| 804 |
| 805 dest[r_out] = clamp_u8(out_device_r*255); |
| 806 dest[1] = clamp_u8(out_device_g*255); |
| 807 dest[b_out] = clamp_u8(out_device_b*255); |
| 808 dest += 3; |
| 809 } |
| 810 } |
| 811 |
| 812 static void qcms_transform_data_rgba_out_lut(qcms_transform *transform, unsigned
char *src, unsigned char *dest, size_t length, qcms_format_type output_format) |
| 813 { |
| 814 const int r_out = output_format.r; |
| 815 const int b_out = output_format.b; |
| 816 |
| 817 unsigned int i; |
| 818 float (*mat)[4] = transform->matrix; |
| 819 for (i = 0; i < length; i++) { |
| 820 unsigned char device_r = *src++; |
| 821 unsigned char device_g = *src++; |
| 822 unsigned char device_b = *src++; |
| 823 unsigned char alpha = *src++; |
| 824 float out_device_r, out_device_g, out_device_b; |
| 825 |
| 826 float linear_r = transform->input_gamma_table_r[device_r]; |
| 827 float linear_g = transform->input_gamma_table_g[device_g]; |
| 828 float linear_b = transform->input_gamma_table_b[device_b]; |
| 829 |
| 830 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 831 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 832 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 833 |
| 834 out_linear_r = clamp_float(out_linear_r); |
| 835 out_linear_g = clamp_float(out_linear_g); |
| 836 out_linear_b = clamp_float(out_linear_b); |
| 837 |
| 838 out_device_r = lut_interp_linear(out_linear_r, |
| 839 transform->output_gamma_lut_r, transform->output
_gamma_lut_r_length); |
| 840 out_device_g = lut_interp_linear(out_linear_g, |
| 841 transform->output_gamma_lut_g, transform->output
_gamma_lut_g_length); |
| 842 out_device_b = lut_interp_linear(out_linear_b, |
| 843 transform->output_gamma_lut_b, transform->output
_gamma_lut_b_length); |
| 844 |
| 845 dest[r_out] = clamp_u8(out_device_r*255); |
| 846 dest[1] = clamp_u8(out_device_g*255); |
| 847 dest[b_out] = clamp_u8(out_device_b*255); |
| 848 dest[3] = alpha; |
| 849 dest += 4; |
| 850 } |
| 851 } |
| 852 |
| 853 #if 0 |
| 854 static void qcms_transform_data_rgb_out_linear(qcms_transform *transform, unsign
ed char *src, unsigned char *dest, size_t length, qcms_format_type output_format
) |
| 855 { |
| 856 const int r_out = output_format.r; |
| 857 const int b_out = output_format.b; |
| 858 |
| 859 int i; |
| 860 float (*mat)[4] = transform->matrix; |
| 861 for (i = 0; i < length; i++) { |
| 862 unsigned char device_r = *src++; |
| 863 unsigned char device_g = *src++; |
| 864 unsigned char device_b = *src++; |
| 865 |
| 866 float linear_r = transform->input_gamma_table_r[device_r]; |
| 867 float linear_g = transform->input_gamma_table_g[device_g]; |
| 868 float linear_b = transform->input_gamma_table_b[device_b]; |
| 869 |
| 870 float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + m
at[2][0]*linear_b; |
| 871 float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + m
at[2][1]*linear_b; |
| 872 float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + m
at[2][2]*linear_b; |
| 873 |
| 874 dest[r_out] = clamp_u8(out_linear_r*255); |
| 875 dest[1] = clamp_u8(out_linear_g*255); |
| 876 dest[b_out] = clamp_u8(out_linear_b*255); |
| 877 dest += 3; |
| 878 } |
| 879 } |
| 880 #endif |
| 881 |
| 882 /* |
| 883 * If users create and destroy objects on different threads, even if the same |
| 884 * objects aren't used on different threads at the same time, we can still run |
| 885 * in to trouble with refcounts if they aren't atomic. |
| 886 * |
| 887 * This can lead to us prematurely deleting the precache if threads get unlucky |
| 888 * and write the wrong value to the ref count. |
| 889 */ |
| 890 static struct precache_output *precache_reference(struct precache_output *p) |
| 891 { |
| 892 qcms_atomic_increment(p->ref_count); |
| 893 return p; |
| 894 } |
| 895 |
| 896 static struct precache_output *precache_create() |
| 897 { |
| 898 struct precache_output *p = malloc(sizeof(struct precache_output)); |
| 899 if (p) |
| 900 p->ref_count = 1; |
| 901 return p; |
| 902 } |
| 903 |
| 904 void precache_release(struct precache_output *p) |
| 905 { |
| 906 if (qcms_atomic_decrement(p->ref_count) == 0) { |
| 907 free(p); |
| 908 } |
| 909 } |
| 910 |
| 911 #ifdef HAVE_POSIX_MEMALIGN |
| 912 static qcms_transform *transform_alloc(void) |
| 913 { |
| 914 qcms_transform *t; |
| 915 if (!posix_memalign(&t, 16, sizeof(*t))) { |
| 916 return t; |
| 917 } else { |
| 918 return NULL; |
| 919 } |
| 920 } |
| 921 static void transform_free(qcms_transform *t) |
| 922 { |
| 923 free(t); |
| 924 } |
| 925 #else |
| 926 static qcms_transform *transform_alloc(void) |
| 927 { |
| 928 /* transform needs to be aligned on a 16byte boundrary */ |
| 929 char *original_block = calloc(sizeof(qcms_transform) + sizeof(void*) + 1
6, 1); |
| 930 /* make room for a pointer to the block returned by calloc */ |
| 931 void *transform_start = original_block + sizeof(void*); |
| 932 /* align transform_start */ |
| 933 qcms_transform *transform_aligned = (qcms_transform*)(((uintptr_t)transf
orm_start + 15) & ~0xf); |
| 934 |
| 935 /* store a pointer to the block returned by calloc so that we can free i
t later */ |
| 936 void **(original_block_ptr) = (void**)transform_aligned; |
| 937 if (!original_block) |
| 938 return NULL; |
| 939 original_block_ptr--; |
| 940 *original_block_ptr = original_block; |
| 941 |
| 942 return transform_aligned; |
| 943 } |
| 944 static void transform_free(qcms_transform *t) |
| 945 { |
| 946 /* get at the pointer to the unaligned block returned by calloc */ |
| 947 void **p = (void**)t; |
| 948 p--; |
| 949 free(*p); |
| 950 } |
| 951 #endif |
| 952 |
| 953 void qcms_transform_release(qcms_transform *t) |
| 954 { |
| 955 /* ensure we only free the gamma tables once even if there are |
| 956 * multiple references to the same data */ |
| 957 |
| 958 if (t->output_table_r) |
| 959 precache_release(t->output_table_r); |
| 960 if (t->output_table_g) |
| 961 precache_release(t->output_table_g); |
| 962 if (t->output_table_b) |
| 963 precache_release(t->output_table_b); |
| 964 |
| 965 free(t->input_gamma_table_r); |
| 966 if (t->input_gamma_table_g != t->input_gamma_table_r) |
| 967 free(t->input_gamma_table_g); |
| 968 if (t->input_gamma_table_g != t->input_gamma_table_r && |
| 969 t->input_gamma_table_g != t->input_gamma_table_b) |
| 970 free(t->input_gamma_table_b); |
| 971 |
| 972 free(t->input_gamma_table_gray); |
| 973 |
| 974 free(t->output_gamma_lut_r); |
| 975 free(t->output_gamma_lut_g); |
| 976 free(t->output_gamma_lut_b); |
| 977 |
| 978 transform_free(t); |
| 979 } |
| 980 |
| 981 #ifdef X86 |
| 982 // Determine if we can build with SSE2 (this was partly copied from jmorecfg.h i
n |
| 983 // mozilla/jpeg) |
| 984 // ------------------------------------------------------------------------- |
| 985 #if defined(_M_IX86) && defined(_MSC_VER) |
| 986 #define HAS_CPUID |
| 987 /* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC |
| 988 register - I'm not sure if that ever happens on windows, but cpuid isn't |
| 989 on the critical path so we just preserve the register to be safe and to be |
| 990 consistent with the non-windows version. */ |
| 991 static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t
*d) { |
| 992 uint32_t a_, b_, c_, d_; |
| 993 __asm { |
| 994 xchg ebx, esi |
| 995 mov eax, fxn |
| 996 cpuid |
| 997 mov a_, eax |
| 998 mov b_, ebx |
| 999 mov c_, ecx |
| 1000 mov d_, edx |
| 1001 xchg ebx, esi |
| 1002 } |
| 1003 *a = a_; |
| 1004 *b = b_; |
| 1005 *c = c_; |
| 1006 *d = d_; |
| 1007 } |
| 1008 #elif (defined(__GNUC__) || defined(__SUNPRO_C)) && (defined(__i386__) || define
d(__i386)) |
| 1009 #define HAS_CPUID |
| 1010 /* Get us a CPUID function. We can't use ebx because it's the PIC register on |
| 1011 some platforms, so we use ESI instead and save ebx to avoid clobbering it. */ |
| 1012 static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t
*d) { |
| 1013 |
| 1014 uint32_t a_, b_, c_, d_; |
| 1015 __asm__ __volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;" |
| 1016 : "=a" (a_), "=S" (b_), "=c" (c_), "=d" (d_) : "a"
(fxn)); |
| 1017 *a = a_; |
| 1018 *b = b_; |
| 1019 *c = c_; |
| 1020 *d = d_; |
| 1021 } |
| 1022 #endif |
| 1023 |
| 1024 // -------------------------Runtime SSEx Detection----------------------------- |
| 1025 |
| 1026 /* MMX is always supported per |
| 1027 * Gecko v1.9.1 minimum CPU requirements */ |
| 1028 #define SSE1_EDX_MASK (1UL << 25) |
| 1029 #define SSE2_EDX_MASK (1UL << 26) |
| 1030 #define SSE3_ECX_MASK (1UL << 0) |
| 1031 |
| 1032 static int sse_version_available(void) |
| 1033 { |
| 1034 #if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) |
| 1035 /* we know at build time that 64-bit CPUs always have SSE2 |
| 1036 * this tells the compiler that non-SSE2 branches will never be |
| 1037 * taken (i.e. OK to optimze away the SSE1 and non-SIMD code */ |
| 1038 return 2; |
| 1039 #elif defined(HAS_CPUID) |
| 1040 static int sse_version = -1; |
| 1041 uint32_t a, b, c, d; |
| 1042 uint32_t function = 0x00000001; |
| 1043 |
| 1044 if (sse_version == -1) { |
| 1045 sse_version = 0; |
| 1046 cpuid(function, &a, &b, &c, &d); |
| 1047 if (c & SSE3_ECX_MASK) |
| 1048 sse_version = 3; |
| 1049 else if (d & SSE2_EDX_MASK) |
| 1050 sse_version = 2; |
| 1051 else if (d & SSE1_EDX_MASK) |
| 1052 sse_version = 1; |
| 1053 } |
| 1054 |
| 1055 return sse_version; |
| 1056 #else |
| 1057 return 0; |
| 1058 #endif |
| 1059 } |
| 1060 #endif |
| 1061 |
| 1062 static const struct matrix bradford_matrix = {{ { 0.8951f, 0.2664f,-0.1614f}, |
| 1063 {-0.7502f, 1.7135f, 0.0367f}, |
| 1064 { 0.0389f,-0.0685f, 1.0296f}}, |
| 1065 false}; |
| 1066 |
| 1067 static const struct matrix bradford_matrix_inv = {{ { 0.9869929f,-0.1470543f, 0.
1599627f}, |
| 1068 { 0.4323053f, 0.5183603f, 0.
0492912f}, |
| 1069 {-0.0085287f, 0.0400428f, 0.
9684867f}}, |
| 1070 false}; |
| 1071 |
| 1072 // See ICCv4 E.3 |
| 1073 struct matrix compute_whitepoint_adaption(float X, float Y, float Z) { |
| 1074 float p = (0.96422f*bradford_matrix.m[0][0] + 1.000f*bradford_matrix.m[1
][0] + 0.82521f*bradford_matrix.m[2][0]) / |
| 1075 (X*bradford_matrix.m[0][0] + Y*bradford_matrix.m[1][0]
+ Z*bradford_matrix.m[2][0] ); |
| 1076 float y = (0.96422f*bradford_matrix.m[0][1] + 1.000f*bradford_matrix.m[1
][1] + 0.82521f*bradford_matrix.m[2][1]) / |
| 1077 (X*bradford_matrix.m[0][1] + Y*bradford_matrix.m[1][1]
+ Z*bradford_matrix.m[2][1] ); |
| 1078 float b = (0.96422f*bradford_matrix.m[0][2] + 1.000f*bradford_matrix.m[1
][2] + 0.82521f*bradford_matrix.m[2][2]) / |
| 1079 (X*bradford_matrix.m[0][2] + Y*bradford_matrix.m[1][2]
+ Z*bradford_matrix.m[2][2] ); |
| 1080 struct matrix white_adaption = {{ {p,0,0}, {0,y,0}, {0,0,b}}, false}; |
| 1081 return matrix_multiply( bradford_matrix_inv, matrix_multiply(white_adapt
ion, bradford_matrix) ); |
| 1082 } |
| 1083 |
| 1084 void qcms_profile_precache_output_transform(qcms_profile *profile) |
| 1085 { |
| 1086 /* we only support precaching on rgb profiles */ |
| 1087 if (profile->color_space != RGB_SIGNATURE) |
| 1088 return; |
| 1089 |
| 1090 if (qcms_supports_iccv4) { |
| 1091 /* don't precache since we will use the B2A LUT */ |
| 1092 if (profile->B2A0) |
| 1093 return; |
| 1094 |
| 1095 /* don't precache since we will use the mBA LUT */ |
| 1096 if (profile->mBA) |
| 1097 return; |
| 1098 } |
| 1099 |
| 1100 /* don't precache if we do not have the TRC curves */ |
| 1101 if (!profile->redTRC || !profile->greenTRC || !profile->blueTRC) |
| 1102 return; |
| 1103 |
| 1104 if (!profile->output_table_r) { |
| 1105 profile->output_table_r = precache_create(); |
| 1106 if (profile->output_table_r && |
| 1107 !compute_precache(profile->redTRC, profile->outp
ut_table_r->data)) { |
| 1108 precache_release(profile->output_table_r); |
| 1109 profile->output_table_r = NULL; |
| 1110 } |
| 1111 } |
| 1112 if (!profile->output_table_g) { |
| 1113 profile->output_table_g = precache_create(); |
| 1114 if (profile->output_table_g && |
| 1115 !compute_precache(profile->greenTRC, profile->ou
tput_table_g->data)) { |
| 1116 precache_release(profile->output_table_g); |
| 1117 profile->output_table_g = NULL; |
| 1118 } |
| 1119 } |
| 1120 if (!profile->output_table_b) { |
| 1121 profile->output_table_b = precache_create(); |
| 1122 if (profile->output_table_b && |
| 1123 !compute_precache(profile->blueTRC, profile->out
put_table_b->data)) { |
| 1124 precache_release(profile->output_table_b); |
| 1125 profile->output_table_b = NULL; |
| 1126 } |
| 1127 } |
| 1128 } |
| 1129 |
| 1130 /* Replace the current transformation with a LUT transformation using a given nu
mber of sample points */ |
| 1131 qcms_transform* qcms_transform_precacheLUT_float(qcms_transform *transform, qcms
_profile *in, qcms_profile *out, |
| 1132 int samples, qcms_data_type in_
type) |
| 1133 { |
| 1134 /* The range between which 2 consecutive sample points can be used to in
terpolate */ |
| 1135 uint16_t x,y,z; |
| 1136 uint32_t l; |
| 1137 uint32_t lutSize = 3 * samples * samples * samples; |
| 1138 float* src = NULL; |
| 1139 float* dest = NULL; |
| 1140 float* lut = NULL; |
| 1141 float inverse; |
| 1142 |
| 1143 src = malloc(lutSize*sizeof(float)); |
| 1144 dest = malloc(lutSize*sizeof(float)); |
| 1145 |
| 1146 if (src && dest) { |
| 1147 /* Prepare a list of points we want to sample: x, y, z order */ |
| 1148 l = 0; |
| 1149 inverse = 1 / (float)(samples-1); |
| 1150 for (x = 0; x < samples; x++) { |
| 1151 for (y = 0; y < samples; y++) { |
| 1152 for (z = 0; z < samples; z++) { |
| 1153 src[l++] = x * inverse; // r |
| 1154 src[l++] = y * inverse; // g |
| 1155 src[l++] = z * inverse; // b |
| 1156 } |
| 1157 } |
| 1158 } |
| 1159 |
| 1160 lut = qcms_chain_transform(in, out, src, dest, lutSize); |
| 1161 |
| 1162 if (lut) { |
| 1163 transform->r_clut = &lut[0]; // r |
| 1164 transform->g_clut = &lut[1]; // g |
| 1165 transform->b_clut = &lut[2]; // b |
| 1166 transform->grid_size = samples; |
| 1167 |
| 1168 if (in_type == QCMS_DATA_RGBA_8) { |
| 1169 #if defined(SSE2_ENABLE) |
| 1170 if (sse_version_available() >= 2) { |
| 1171 transform->transform_fn = qcms_transform
_data_tetra_clut_rgba_sse2; |
| 1172 } else { |
| 1173 transform->transform_fn = qcms_transform
_data_tetra_clut_rgba; |
| 1174 } |
| 1175 #else |
| 1176 transform->transform_fn = qcms_transform_data_te
tra_clut_rgba; |
| 1177 #endif |
| 1178 } else { |
| 1179 transform->transform_fn = qcms_transform_data_te
tra_clut; |
| 1180 } |
| 1181 } |
| 1182 } |
| 1183 |
| 1184 // XXX: qcms_modular_transform_data may return the lut in either the src
or the |
| 1185 // dest buffer. If so, it must not be free-ed. |
| 1186 if (src && lut != src) { |
| 1187 free(src); |
| 1188 } |
| 1189 if (dest && lut != dest) { |
| 1190 free(dest); |
| 1191 } |
| 1192 |
| 1193 if (lut == NULL) { |
| 1194 return NULL; |
| 1195 } |
| 1196 return transform; |
| 1197 } |
| 1198 |
| 1199 /* Create a transform LUT using the given number of sample points. The transform
LUT data is stored |
| 1200 in the output (cube) in bgra format in zyx sample order. */ |
| 1201 qcms_bool qcms_transform_create_LUT_zyx_bgra(qcms_profile *in, qcms_profile *out
, qcms_intent intent, |
| 1202 int samples, unsigned char* cube) |
| 1203 { |
| 1204 uint16_t z,y,x; |
| 1205 uint32_t l,index; |
| 1206 uint32_t lutSize = 3 * samples * samples * samples; |
| 1207 |
| 1208 float* src = NULL; |
| 1209 float* dest = NULL; |
| 1210 float* lut = NULL; |
| 1211 float inverse; |
| 1212 |
| 1213 src = malloc(lutSize*sizeof(float)); |
| 1214 dest = malloc(lutSize*sizeof(float)); |
| 1215 |
| 1216 if (src && dest) { |
| 1217 /* Prepare a list of points we want to sample: z, y, x order */ |
| 1218 l = 0; |
| 1219 inverse = 1 / (float)(samples-1); |
| 1220 for (z = 0; z < samples; z++) { |
| 1221 for (y = 0; y < samples; y++) { |
| 1222 for (x = 0; x < samples; x++) { |
| 1223 src[l++] = x * inverse; // r |
| 1224 src[l++] = y * inverse; // g |
| 1225 src[l++] = z * inverse; // b |
| 1226 } |
| 1227 } |
| 1228 } |
| 1229 |
| 1230 lut = qcms_chain_transform(in, out, src, dest, lutSize); |
| 1231 |
| 1232 if (lut) { |
| 1233 index = l = 0; |
| 1234 for (z = 0; z < samples; z++) { |
| 1235 for (y = 0; y < samples; y++) { |
| 1236 for (x = 0; x < samples; x++) { |
| 1237 cube[index++] = (int)floorf(lut[
l + 2] * 255.0f + 0.5f); // b |
| 1238 cube[index++] = (int)floorf(lut[
l + 1] * 255.0f + 0.5f); // g |
| 1239 cube[index++] = (int)floorf(lut[
l + 0] * 255.0f + 0.5f); // r |
| 1240 cube[index++] = 255;
// a |
| 1241 l += 3; |
| 1242 } |
| 1243 } |
| 1244 } |
| 1245 } |
| 1246 } |
| 1247 |
| 1248 // XXX: qcms_modular_transform_data may return the lut data in either th
e src or |
| 1249 // dest buffer so free src, dest, and lut with care. |
| 1250 |
| 1251 if (src && lut != src) |
| 1252 free(src); |
| 1253 if (dest && lut != dest) |
| 1254 free(dest); |
| 1255 |
| 1256 if (lut) { |
| 1257 free(lut); |
| 1258 return true; |
| 1259 } |
| 1260 |
| 1261 return false; |
| 1262 } |
| 1263 |
| 1264 void qcms_transform_build_clut_cache(qcms_transform* transform) { |
| 1265 const int grid_factor = transform->grid_size - 1; |
| 1266 const float grid_scaled = (1.0f / 255.0f) * grid_factor; |
| 1267 int i; |
| 1268 |
| 1269 #define div_255_ceiling(value) (((value) + 254) / 255) |
| 1270 |
| 1271 for (i = 0; i < 256; i++) { |
| 1272 transform->ceil_cache[i] = div_255_ceiling(i * grid_factor); |
| 1273 transform->floor_cache[i] = i * grid_factor / 255; |
| 1274 transform->r_cache[i] = (i * grid_scaled) - transform->floor_cac
he[i]; |
| 1275 } |
| 1276 |
| 1277 #undef div_255_ceil |
| 1278 |
| 1279 transform->transform_flags |= TRANSFORM_FLAG_CLUT_CACHE; |
| 1280 } |
| 1281 |
| 1282 #define NO_MEM_TRANSFORM NULL |
| 1283 |
| 1284 qcms_transform* qcms_transform_create( |
| 1285 qcms_profile *in, qcms_data_type in_type, |
| 1286 qcms_profile *out, qcms_data_type out_type, |
| 1287 qcms_intent intent) |
| 1288 { |
| 1289 qcms_transform *transform = NULL; |
| 1290 bool precache = false; |
| 1291 int i, j; |
| 1292 |
| 1293 transform = transform_alloc(); |
| 1294 if (!transform) { |
| 1295 return NULL; |
| 1296 } |
| 1297 |
| 1298 if (out_type != QCMS_DATA_RGB_8 && out_type != QCMS_DATA_RGBA_8) { |
| 1299 assert(0 && "output type"); |
| 1300 qcms_transform_release(transform); |
| 1301 return NULL; |
| 1302 } |
| 1303 |
| 1304 transform->transform_flags = 0; |
| 1305 |
| 1306 if (out->output_table_r && out->output_table_g && out->output_table_b) { |
| 1307 precache = true; |
| 1308 } |
| 1309 |
| 1310 if (qcms_supports_iccv4 && (in->A2B0 || out->B2A0 || in->mAB || out->mAB
)) { |
| 1311 // Precache the transformation to a CLUT 33x33x33 in size. |
| 1312 // 33 is used by many profiles and works well in practice. |
| 1313 // This evenly divides 256 into blocks of 8x8x8. |
| 1314 // TODO For transforming small data sets of about 200x200 or les
s |
| 1315 // precaching should be avoided. |
| 1316 qcms_transform *result = qcms_transform_precacheLUT_float(transf
orm, in, out, 33, in_type); |
| 1317 if (!result) { |
| 1318 assert(0 && "precacheLUT failed"); |
| 1319 qcms_transform_release(transform); |
| 1320 return NULL; |
| 1321 } |
| 1322 return result; |
| 1323 } |
| 1324 |
| 1325 /* A matrix-based transform will be selected: check that the PCS |
| 1326 of the input/output profiles are the same, crbug.com/5120682 */ |
| 1327 if (in->pcs != out->pcs) { |
| 1328 qcms_transform_release(transform); |
| 1329 return NULL; |
| 1330 } |
| 1331 |
| 1332 if (precache) { |
| 1333 transform->output_table_r = precache_reference(out->output_table
_r); |
| 1334 transform->output_table_g = precache_reference(out->output_table
_g); |
| 1335 transform->output_table_b = precache_reference(out->output_table
_b); |
| 1336 } else { |
| 1337 if (!out->redTRC || !out->greenTRC || !out->blueTRC) { |
| 1338 qcms_transform_release(transform); |
| 1339 return NO_MEM_TRANSFORM; |
| 1340 } |
| 1341 |
| 1342 build_output_lut(out->redTRC, &transform->output_gamma_lut_r, &t
ransform->output_gamma_lut_r_length); |
| 1343 build_output_lut(out->greenTRC, &transform->output_gamma_lut_g,
&transform->output_gamma_lut_g_length); |
| 1344 build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, &
transform->output_gamma_lut_b_length); |
| 1345 |
| 1346 if (!transform->output_gamma_lut_r || !transform->output_gamma_l
ut_g || !transform->output_gamma_lut_b) { |
| 1347 qcms_transform_release(transform); |
| 1348 return NO_MEM_TRANSFORM; |
| 1349 } |
| 1350 } |
| 1351 |
| 1352 if (in->color_space == RGB_SIGNATURE) { |
| 1353 struct matrix in_matrix, out_matrix, result; |
| 1354 |
| 1355 if (in_type != QCMS_DATA_RGB_8 && in_type != QCMS_DATA_RGBA_8) { |
| 1356 assert(0 && "input type"); |
| 1357 qcms_transform_release(transform); |
| 1358 return NULL; |
| 1359 } |
| 1360 |
| 1361 if (precache) { |
| 1362 #if defined(SSE2_ENABLE) |
| 1363 if (sse_version_available() >= 2) { |
| 1364 if (in_type == QCMS_DATA_RGB_8) |
| 1365 transform->transform_fn = qcms_transform
_data_rgb_out_lut_sse2; |
| 1366 else |
| 1367 transform->transform_fn = qcms_transform
_data_rgba_out_lut_sse2; |
| 1368 } else |
| 1369 #endif |
| 1370 { |
| 1371 if (in_type == QCMS_DATA_RGB_8) |
| 1372 transform->transform_fn = qcms_transform
_data_rgb_out_lut_precache; |
| 1373 else |
| 1374 transform->transform_fn = qcms_transform
_data_rgba_out_lut_precache; |
| 1375 } |
| 1376 } else { |
| 1377 if (in_type == QCMS_DATA_RGB_8) |
| 1378 transform->transform_fn = qcms_transform_data_rg
b_out_lut; |
| 1379 else |
| 1380 transform->transform_fn = qcms_transform_data_rg
ba_out_lut; |
| 1381 } |
| 1382 |
| 1383 //XXX: avoid duplicating tables if we can |
| 1384 transform->input_gamma_table_r = build_input_gamma_table(in->red
TRC); |
| 1385 transform->input_gamma_table_g = build_input_gamma_table(in->gre
enTRC); |
| 1386 transform->input_gamma_table_b = build_input_gamma_table(in->blu
eTRC); |
| 1387 |
| 1388 if (!transform->input_gamma_table_r || !transform->input_gamma_t
able_g || !transform->input_gamma_table_b) { |
| 1389 qcms_transform_release(transform); |
| 1390 return NO_MEM_TRANSFORM; |
| 1391 } |
| 1392 |
| 1393 /* build combined colorant matrix */ |
| 1394 in_matrix = build_colorant_matrix(in); |
| 1395 out_matrix = build_colorant_matrix(out); |
| 1396 out_matrix = matrix_invert(out_matrix); |
| 1397 if (out_matrix.invalid) { |
| 1398 qcms_transform_release(transform); |
| 1399 return NULL; |
| 1400 } |
| 1401 result = matrix_multiply(out_matrix, in_matrix); |
| 1402 |
| 1403 /* check for NaN values in the matrix and bail if we find any |
| 1404 see also https://bugzilla.mozilla.org/show_bug.cgi?id=1170316
*/ |
| 1405 for (i = 0 ; i < 3 ; ++i) { |
| 1406 for (j = 0 ; j < 3 ; ++j) { |
| 1407 if (result.m[i][j] != result.m[i][j]) { |
| 1408 qcms_transform_release(transform); |
| 1409 return NULL; |
| 1410 } |
| 1411 } |
| 1412 } |
| 1413 |
| 1414 /* store the results in column major mode |
| 1415 * this makes doing the multiplication with sse easier */ |
| 1416 transform->matrix[0][0] = result.m[0][0]; |
| 1417 transform->matrix[1][0] = result.m[0][1]; |
| 1418 transform->matrix[2][0] = result.m[0][2]; |
| 1419 transform->matrix[0][1] = result.m[1][0]; |
| 1420 transform->matrix[1][1] = result.m[1][1]; |
| 1421 transform->matrix[2][1] = result.m[1][2]; |
| 1422 transform->matrix[0][2] = result.m[2][0]; |
| 1423 transform->matrix[1][2] = result.m[2][1]; |
| 1424 transform->matrix[2][2] = result.m[2][2]; |
| 1425 |
| 1426 /* Flag transform as matrix. */ |
| 1427 transform->transform_flags |= TRANSFORM_FLAG_MATRIX; |
| 1428 |
| 1429 } else if (in->color_space == GRAY_SIGNATURE) { |
| 1430 if (in_type != QCMS_DATA_GRAY_8 && in_type != QCMS_DATA_GRAYA_8)
{ |
| 1431 assert(0 && "input type"); |
| 1432 qcms_transform_release(transform); |
| 1433 return NULL; |
| 1434 } |
| 1435 |
| 1436 transform->input_gamma_table_gray = build_input_gamma_table(in->
grayTRC); |
| 1437 |
| 1438 if (!transform->input_gamma_table_gray) { |
| 1439 qcms_transform_release(transform); |
| 1440 return NO_MEM_TRANSFORM; |
| 1441 } |
| 1442 |
| 1443 if (precache) { |
| 1444 if (in_type == QCMS_DATA_GRAY_8) { |
| 1445 transform->transform_fn = qcms_transform_data_gr
ay_out_precache; |
| 1446 } else { |
| 1447 transform->transform_fn = qcms_transform_data_gr
aya_out_precache; |
| 1448 } |
| 1449 } else { |
| 1450 if (in_type == QCMS_DATA_GRAY_8) { |
| 1451 transform->transform_fn = qcms_transform_data_gr
ay_out_lut; |
| 1452 } else { |
| 1453 transform->transform_fn = qcms_transform_data_gr
aya_out_lut; |
| 1454 } |
| 1455 } |
| 1456 } else { |
| 1457 assert(0 && "unexpected colorspace"); |
| 1458 qcms_transform_release(transform); |
| 1459 return NULL; |
| 1460 } |
| 1461 |
| 1462 return transform; |
| 1463 } |
| 1464 |
| 1465 /* __force_align_arg_pointer__ is an x86-only attribute, and gcc/clang warns on
unused |
| 1466 * attributes. Don't use this on ARM or AMD64. __has_attribute can detect the pr
esence |
| 1467 * of the attribute but is currently only supported by clang */ |
| 1468 #if defined(__has_attribute) |
| 1469 #define HAS_FORCE_ALIGN_ARG_POINTER __has_attribute(__force_align_arg_pointer__) |
| 1470 #elif defined(__GNUC__) && defined(__i386__) |
| 1471 #define HAS_FORCE_ALIGN_ARG_POINTER 1 |
| 1472 #else |
| 1473 #define HAS_FORCE_ALIGN_ARG_POINTER 0 |
| 1474 #endif |
| 1475 |
| 1476 #if HAS_FORCE_ALIGN_ARG_POINTER |
| 1477 /* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */ |
| 1478 __attribute__((__force_align_arg_pointer__)) |
| 1479 #endif |
| 1480 void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_
t length) |
| 1481 { |
| 1482 static const struct _qcms_format_type output_rgbx = { 0, 2 }; |
| 1483 |
| 1484 transform->transform_fn(transform, src, dest, length, output_rgbx); |
| 1485 } |
| 1486 |
| 1487 void qcms_transform_data_type(qcms_transform *transform, void *src, void *dest,
size_t length, qcms_output_type type) |
| 1488 { |
| 1489 static const struct _qcms_format_type output_rgbx = { 0, 2 }; |
| 1490 static const struct _qcms_format_type output_bgrx = { 2, 0 }; |
| 1491 |
| 1492 transform->transform_fn(transform, src, dest, length, type == QCMS_OUTPU
T_BGRX ? output_bgrx : output_rgbx); |
| 1493 } |
| 1494 |
| 1495 #define ENABLE_ICC_V4_PROFILE_SUPPORT false |
| 1496 |
| 1497 qcms_bool qcms_supports_iccv4 = ENABLE_ICC_V4_PROFILE_SUPPORT; |
| 1498 |
| 1499 void qcms_enable_iccv4() |
| 1500 { |
| 1501 qcms_supports_iccv4 = true; |
| 1502 } |
| 1503 |
| 1504 static inline qcms_bool transform_is_matrix(qcms_transform *t) |
| 1505 { |
| 1506 return (t->transform_flags & TRANSFORM_FLAG_MATRIX) ? true : false; |
| 1507 } |
| 1508 |
| 1509 qcms_bool qcms_transform_is_matrix(qcms_transform *t) |
| 1510 { |
| 1511 return transform_is_matrix(t); |
| 1512 } |
| 1513 |
| 1514 float qcms_transform_get_matrix(qcms_transform *t, unsigned i, unsigned j) |
| 1515 { |
| 1516 assert(transform_is_matrix(t) && i < 3 && j < 3); |
| 1517 |
| 1518 // Return transform matrix element in row major order (permute i and j) |
| 1519 |
| 1520 return t->matrix[j][i]; |
| 1521 } |
| 1522 |
| 1523 static inline qcms_bool supported_trc_type(qcms_trc_type type) |
| 1524 { |
| 1525 return (type == QCMS_TRC_HALF_FLOAT || type == QCMS_TRC_USHORT); |
| 1526 } |
| 1527 |
| 1528 const uint16_t half_float_one = 0x3c00; |
| 1529 |
| 1530 size_t qcms_transform_get_input_trc_rgba(qcms_transform *t, qcms_profile *in, qc
ms_trc_type type, unsigned short *data) |
| 1531 { |
| 1532 const size_t size = 256; // The input gamma tables always have 256 entri
es. |
| 1533 |
| 1534 size_t i; |
| 1535 |
| 1536 if (in->color_space != RGB_SIGNATURE || !supported_trc_type(type)) |
| 1537 return 0; |
| 1538 |
| 1539 // qcms_profile *in is assumed to be the profile on the input-side of th
e color transform t. |
| 1540 // When a transform is created, the input gamma curve data is stored in
the transform ... |
| 1541 |
| 1542 if (!t->input_gamma_table_r || !t->input_gamma_table_g || !t->input_gamm
a_table_b) |
| 1543 return 0; |
| 1544 |
| 1545 // Report the size if no output data is requested. This allows callers t
o first work out the |
| 1546 // the curve size, then provide allocated memory sufficient to store the
curve rgba data. |
| 1547 |
| 1548 if (!data) |
| 1549 return size; |
| 1550 |
| 1551 switch(type) { |
| 1552 case QCMS_TRC_HALF_FLOAT: |
| 1553 for (i = 0; i < size; ++i) { |
| 1554 *data++ = float_to_half_float(t->input_gamma_tab
le_r[i]); // r |
| 1555 *data++ = float_to_half_float(t->input_gamma_tab
le_g[i]); // g |
| 1556 *data++ = float_to_half_float(t->input_gamma_tab
le_b[i]); // b |
| 1557 *data++ = half_float_one;
// a |
| 1558 } |
| 1559 break; |
| 1560 case QCMS_TRC_USHORT: |
| 1561 for (i = 0; i < size; ++i) { |
| 1562 *data++ = roundf(t->input_gamma_table_r[i] * 655
35.0); // r |
| 1563 *data++ = roundf(t->input_gamma_table_g[i] * 655
35.0); // g |
| 1564 *data++ = roundf(t->input_gamma_table_b[i] * 655
35.0); // b |
| 1565 *data++ = 65535;
// a |
| 1566 } |
| 1567 break; |
| 1568 default: |
| 1569 /* should not be reached */ |
| 1570 assert(0); |
| 1571 } |
| 1572 |
| 1573 return size; |
| 1574 } |
| 1575 |
| 1576 const float inverse65535 = (float) (1.0 / 65535.0); |
| 1577 |
| 1578 size_t qcms_transform_get_output_trc_rgba(qcms_transform *t, qcms_profile *out,
qcms_trc_type type, unsigned short *data) |
| 1579 { |
| 1580 size_t size, i; |
| 1581 |
| 1582 if (out->color_space != RGB_SIGNATURE || !supported_trc_type(type)) |
| 1583 return 0; |
| 1584 |
| 1585 // qcms_profile *out is assumed to be the profile on the output-side of
the transform t. |
| 1586 // If the transform output gamma curves need building, do that. They're
usually built when |
| 1587 // the transform was created, but sometimes not due to the output gamma
precache ... |
| 1588 |
| 1589 if (!out->redTRC || !out->greenTRC || !out->blueTRC) |
| 1590 return 0; |
| 1591 if (!t->output_gamma_lut_r) |
| 1592 build_output_lut(out->redTRC, &t->output_gamma_lut_r, &t->output
_gamma_lut_r_length); |
| 1593 if (!t->output_gamma_lut_g) |
| 1594 build_output_lut(out->greenTRC, &t->output_gamma_lut_g, &t->outp
ut_gamma_lut_g_length); |
| 1595 if (!t->output_gamma_lut_b) |
| 1596 build_output_lut(out->blueTRC, &t->output_gamma_lut_b, &t->outpu
t_gamma_lut_b_length); |
| 1597 |
| 1598 if (!t->output_gamma_lut_r || !t->output_gamma_lut_g || !t->output_gamma
_lut_b) |
| 1599 return 0; |
| 1600 |
| 1601 // Output gamma tables should have the same size and should have 4096 en
tries at most (the |
| 1602 // minimum is 256). Larger tables are rare and ignored here: fail by ret
urning 0. |
| 1603 |
| 1604 size = t->output_gamma_lut_r_length; |
| 1605 if (size != t->output_gamma_lut_g_length) |
| 1606 return 0; |
| 1607 if (size != t->output_gamma_lut_b_length) |
| 1608 return 0; |
| 1609 if (size < 256 || size > 4096) |
| 1610 return 0; |
| 1611 |
| 1612 // Report the size if no output data is requested. This allows callers t
o first work out the |
| 1613 // the curve size, then provide allocated memory sufficient to store the
curve rgba data. |
| 1614 |
| 1615 if (!data) |
| 1616 return size; |
| 1617 |
| 1618 switch (type) { |
| 1619 case QCMS_TRC_HALF_FLOAT: |
| 1620 for (i = 0; i < size; ++i) { |
| 1621 *data++ = float_to_half_float(t->output_gamma_lu
t_r[i] * inverse65535); // r |
| 1622 *data++ = float_to_half_float(t->output_gamma_lu
t_g[i] * inverse65535); // g |
| 1623 *data++ = float_to_half_float(t->output_gamma_lu
t_b[i] * inverse65535); // b |
| 1624 *data++ = half_float_one;
// a |
| 1625 } |
| 1626 break; |
| 1627 case QCMS_TRC_USHORT: |
| 1628 for (i = 0; i < size; ++i) { |
| 1629 *data++ = t->output_gamma_lut_r[i]; // r |
| 1630 *data++ = t->output_gamma_lut_g[i]; // g |
| 1631 *data++ = t->output_gamma_lut_b[i]; // b |
| 1632 *data++ = 65535; // a |
| 1633 } |
| 1634 break; |
| 1635 default: |
| 1636 /* should not be reached */ |
| 1637 assert(0); |
| 1638 } |
| 1639 |
| 1640 return size; |
| 1641 } |
OLD | NEW |