Coverage Report

Created: 2018-09-25 14:53

/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
}