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