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 |