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