michael@0: /* vim: set ts=8 sw=8 noexpandtab: */ michael@0: // qcms michael@0: // Copyright (C) 2009 Mozilla Corporation michael@0: // Copyright (C) 1998-2007 Marti Maria michael@0: // michael@0: // Permission is hereby granted, free of charge, to any person obtaining michael@0: // a copy of this software and associated documentation files (the "Software"), michael@0: // to deal in the Software without restriction, including without limitation michael@0: // the rights to use, copy, modify, merge, publish, distribute, sublicense, michael@0: // and/or sell copies of the Software, and to permit persons to whom the Software michael@0: // is furnished to do so, subject to the following conditions: michael@0: // michael@0: // The above copyright notice and this permission notice shall be included in michael@0: // all copies or substantial portions of the Software. michael@0: // michael@0: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, michael@0: // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO michael@0: // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND michael@0: // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE michael@0: // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION michael@0: // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION michael@0: // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include //memcpy michael@0: #include "qcmsint.h" michael@0: #include "transform_util.h" michael@0: #include "matrix.h" michael@0: michael@0: static struct matrix build_lut_matrix(struct lutType *lut) michael@0: { michael@0: struct matrix result; michael@0: if (lut) { michael@0: result.m[0][0] = s15Fixed16Number_to_float(lut->e00); michael@0: result.m[0][1] = s15Fixed16Number_to_float(lut->e01); michael@0: result.m[0][2] = s15Fixed16Number_to_float(lut->e02); michael@0: result.m[1][0] = s15Fixed16Number_to_float(lut->e10); michael@0: result.m[1][1] = s15Fixed16Number_to_float(lut->e11); michael@0: result.m[1][2] = s15Fixed16Number_to_float(lut->e12); michael@0: result.m[2][0] = s15Fixed16Number_to_float(lut->e20); michael@0: result.m[2][1] = s15Fixed16Number_to_float(lut->e21); michael@0: result.m[2][2] = s15Fixed16Number_to_float(lut->e22); michael@0: result.invalid = false; michael@0: } else { michael@0: memset(&result, 0, sizeof(struct matrix)); michael@0: result.invalid = true; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static struct matrix build_mAB_matrix(struct lutmABType *lut) michael@0: { michael@0: struct matrix result; michael@0: if (lut) { michael@0: result.m[0][0] = s15Fixed16Number_to_float(lut->e00); michael@0: result.m[0][1] = s15Fixed16Number_to_float(lut->e01); michael@0: result.m[0][2] = s15Fixed16Number_to_float(lut->e02); michael@0: result.m[1][0] = s15Fixed16Number_to_float(lut->e10); michael@0: result.m[1][1] = s15Fixed16Number_to_float(lut->e11); michael@0: result.m[1][2] = s15Fixed16Number_to_float(lut->e12); michael@0: result.m[2][0] = s15Fixed16Number_to_float(lut->e20); michael@0: result.m[2][1] = s15Fixed16Number_to_float(lut->e21); michael@0: result.m[2][2] = s15Fixed16Number_to_float(lut->e22); michael@0: result.invalid = false; michael@0: } else { michael@0: memset(&result, 0, sizeof(struct matrix)); michael@0: result.invalid = true; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: //Based on lcms cmsLab2XYZ michael@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) michael@0: #define f_1(t) (t <= (24.0f/116.0f)) ? ((108.0/841.0) * (t - (16.0/116.0))) : (t * t * t) michael@0: static void qcms_transform_module_LAB_to_XYZ(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: // lcms: D50 XYZ values michael@0: float WhitePointX = 0.9642f; michael@0: float WhitePointY = 1.0f; michael@0: float WhitePointZ = 0.8249f; michael@0: for (i = 0; i < length; i++) { michael@0: float device_L = *src++ * 100.0f; michael@0: float device_a = *src++ * 255.0f - 128.0f; michael@0: float device_b = *src++ * 255.0f - 128.0f; michael@0: float y = (device_L + 16.0f) / 116.0f; michael@0: michael@0: float X = f_1((y + 0.002f * device_a)) * WhitePointX; michael@0: float Y = f_1(y) * WhitePointY; michael@0: float Z = f_1((y - 0.005f * device_b)) * WhitePointZ; michael@0: *dest++ = X / (1.0 + 32767.0/32768.0); michael@0: *dest++ = Y / (1.0 + 32767.0/32768.0); michael@0: *dest++ = Z / (1.0 + 32767.0/32768.0); michael@0: } michael@0: } michael@0: michael@0: //Based on lcms cmsXYZ2Lab michael@0: static void qcms_transform_module_XYZ_to_LAB(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: // lcms: D50 XYZ values michael@0: float WhitePointX = 0.9642f; michael@0: float WhitePointY = 1.0f; michael@0: float WhitePointZ = 0.8249f; michael@0: for (i = 0; i < length; i++) { michael@0: float device_x = *src++ * (1.0 + 32767.0/32768.0) / WhitePointX; michael@0: float device_y = *src++ * (1.0 + 32767.0/32768.0) / WhitePointY; michael@0: float device_z = *src++ * (1.0 + 32767.0/32768.0) / WhitePointZ; michael@0: michael@0: float fx = f(device_x); michael@0: float fy = f(device_y); michael@0: float fz = f(device_z); michael@0: michael@0: float L = 116.0f*fy - 16.0f; michael@0: float a = 500.0f*(fx - fy); michael@0: float b = 200.0f*(fy - fz); michael@0: *dest++ = L / 100.0f; michael@0: *dest++ = (a+128.0f) / 255.0f; michael@0: *dest++ = (b+128.0f) / 255.0f; michael@0: } michael@0: michael@0: } michael@0: michael@0: static void qcms_transform_module_clut_only(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: int xy_len = 1; michael@0: int x_len = transform->grid_size; michael@0: int len = x_len * x_len; michael@0: float* r_table = transform->r_clut; michael@0: float* g_table = transform->g_clut; michael@0: float* b_table = transform->b_clut; michael@0: michael@0: for (i = 0; i < length; i++) { michael@0: float linear_r = *src++; michael@0: float linear_g = *src++; michael@0: float linear_b = *src++; michael@0: michael@0: int x = floorf(linear_r * (transform->grid_size-1)); michael@0: int y = floorf(linear_g * (transform->grid_size-1)); michael@0: int z = floorf(linear_b * (transform->grid_size-1)); michael@0: int x_n = ceilf(linear_r * (transform->grid_size-1)); michael@0: int y_n = ceilf(linear_g * (transform->grid_size-1)); michael@0: int z_n = ceilf(linear_b * (transform->grid_size-1)); michael@0: float x_d = linear_r * (transform->grid_size-1) - x; michael@0: float y_d = linear_g * (transform->grid_size-1) - y; michael@0: float z_d = linear_b * (transform->grid_size-1) - z; michael@0: michael@0: float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d); michael@0: float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d); michael@0: float r_y1 = lerp(r_x1, r_x2, y_d); michael@0: float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d); michael@0: float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d); michael@0: float r_y2 = lerp(r_x3, r_x4, y_d); michael@0: float clut_r = lerp(r_y1, r_y2, z_d); michael@0: michael@0: float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d); michael@0: float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d); michael@0: float g_y1 = lerp(g_x1, g_x2, y_d); michael@0: float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d); michael@0: float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d); michael@0: float g_y2 = lerp(g_x3, g_x4, y_d); michael@0: float clut_g = lerp(g_y1, g_y2, z_d); michael@0: michael@0: float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d); michael@0: float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d); michael@0: float b_y1 = lerp(b_x1, b_x2, y_d); michael@0: float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d); michael@0: float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d); michael@0: float b_y2 = lerp(b_x3, b_x4, y_d); michael@0: float clut_b = lerp(b_y1, b_y2, z_d); michael@0: michael@0: *dest++ = clamp_float(clut_r); michael@0: *dest++ = clamp_float(clut_g); michael@0: *dest++ = clamp_float(clut_b); michael@0: } michael@0: } michael@0: michael@0: static void qcms_transform_module_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: int xy_len = 1; michael@0: int x_len = transform->grid_size; michael@0: int len = x_len * x_len; michael@0: float* r_table = transform->r_clut; michael@0: float* g_table = transform->g_clut; michael@0: float* b_table = transform->b_clut; michael@0: for (i = 0; i < length; i++) { michael@0: float device_r = *src++; michael@0: float device_g = *src++; michael@0: float device_b = *src++; michael@0: float linear_r = lut_interp_linear_float(device_r, michael@0: transform->input_clut_table_r, transform->input_clut_table_length); michael@0: float linear_g = lut_interp_linear_float(device_g, michael@0: transform->input_clut_table_g, transform->input_clut_table_length); michael@0: float linear_b = lut_interp_linear_float(device_b, michael@0: transform->input_clut_table_b, transform->input_clut_table_length); michael@0: michael@0: int x = floorf(linear_r * (transform->grid_size-1)); michael@0: int y = floorf(linear_g * (transform->grid_size-1)); michael@0: int z = floorf(linear_b * (transform->grid_size-1)); michael@0: int x_n = ceilf(linear_r * (transform->grid_size-1)); michael@0: int y_n = ceilf(linear_g * (transform->grid_size-1)); michael@0: int z_n = ceilf(linear_b * (transform->grid_size-1)); michael@0: float x_d = linear_r * (transform->grid_size-1) - x; michael@0: float y_d = linear_g * (transform->grid_size-1) - y; michael@0: float z_d = linear_b * (transform->grid_size-1) - z; michael@0: michael@0: float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d); michael@0: float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d); michael@0: float r_y1 = lerp(r_x1, r_x2, y_d); michael@0: float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d); michael@0: float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d); michael@0: float r_y2 = lerp(r_x3, r_x4, y_d); michael@0: float clut_r = lerp(r_y1, r_y2, z_d); michael@0: michael@0: float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d); michael@0: float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d); michael@0: float g_y1 = lerp(g_x1, g_x2, y_d); michael@0: float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d); michael@0: float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d); michael@0: float g_y2 = lerp(g_x3, g_x4, y_d); michael@0: float clut_g = lerp(g_y1, g_y2, z_d); michael@0: michael@0: float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d); michael@0: float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d); michael@0: float b_y1 = lerp(b_x1, b_x2, y_d); michael@0: float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d); michael@0: float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d); michael@0: float b_y2 = lerp(b_x3, b_x4, y_d); michael@0: float clut_b = lerp(b_y1, b_y2, z_d); michael@0: michael@0: float pcs_r = lut_interp_linear_float(clut_r, michael@0: transform->output_clut_table_r, transform->output_clut_table_length); michael@0: float pcs_g = lut_interp_linear_float(clut_g, michael@0: transform->output_clut_table_g, transform->output_clut_table_length); michael@0: float pcs_b = lut_interp_linear_float(clut_b, michael@0: transform->output_clut_table_b, transform->output_clut_table_length); michael@0: michael@0: *dest++ = clamp_float(pcs_r); michael@0: *dest++ = clamp_float(pcs_g); michael@0: *dest++ = clamp_float(pcs_b); michael@0: } michael@0: } michael@0: michael@0: /* NOT USED michael@0: static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: int xy_len = 1; michael@0: int x_len = transform->grid_size; michael@0: int len = x_len * x_len; michael@0: float* r_table = transform->r_clut; michael@0: float* g_table = transform->g_clut; michael@0: float* b_table = transform->b_clut; michael@0: float c0_r, c1_r, c2_r, c3_r; michael@0: float c0_g, c1_g, c2_g, c3_g; michael@0: float c0_b, c1_b, c2_b, c3_b; michael@0: float clut_r, clut_g, clut_b; michael@0: float pcs_r, pcs_g, pcs_b; michael@0: for (i = 0; i < length; i++) { michael@0: float device_r = *src++; michael@0: float device_g = *src++; michael@0: float device_b = *src++; michael@0: float linear_r = lut_interp_linear_float(device_r, michael@0: transform->input_clut_table_r, transform->input_clut_table_length); michael@0: float linear_g = lut_interp_linear_float(device_g, michael@0: transform->input_clut_table_g, transform->input_clut_table_length); michael@0: float linear_b = lut_interp_linear_float(device_b, michael@0: transform->input_clut_table_b, transform->input_clut_table_length); michael@0: michael@0: int x = floorf(linear_r * (transform->grid_size-1)); michael@0: int y = floorf(linear_g * (transform->grid_size-1)); michael@0: int z = floorf(linear_b * (transform->grid_size-1)); michael@0: int x_n = ceilf(linear_r * (transform->grid_size-1)); michael@0: int y_n = ceilf(linear_g * (transform->grid_size-1)); michael@0: int z_n = ceilf(linear_b * (transform->grid_size-1)); michael@0: float rx = linear_r * (transform->grid_size-1) - x; michael@0: float ry = linear_g * (transform->grid_size-1) - y; michael@0: float rz = linear_b * (transform->grid_size-1) - z; michael@0: michael@0: c0_r = CLU(r_table, x, y, z); michael@0: c0_g = CLU(g_table, x, y, z); michael@0: c0_b = CLU(b_table, x, y, z); michael@0: if( rx >= ry ) { michael@0: if (ry >= rz) { //rx >= ry && ry >= rz michael@0: c1_r = CLU(r_table, x_n, y, z) - c0_r; michael@0: c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z); michael@0: c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z); michael@0: c1_g = CLU(g_table, x_n, y, z) - c0_g; michael@0: c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z); michael@0: c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z); michael@0: c1_b = CLU(b_table, x_n, y, z) - c0_b; michael@0: c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z); michael@0: c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z); michael@0: } else { michael@0: if (rx >= rz) { //rx >= rz && rz >= ry michael@0: c1_r = CLU(r_table, x_n, y, z) - c0_r; michael@0: c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n); michael@0: c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z); michael@0: c1_g = CLU(g_table, x_n, y, z) - c0_g; michael@0: c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n); michael@0: c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z); michael@0: c1_b = CLU(b_table, x_n, y, z) - c0_b; michael@0: c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n); michael@0: c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z); michael@0: } else { //rz > rx && rx >= ry michael@0: c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n); michael@0: c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n); michael@0: c3_r = CLU(r_table, x, y, z_n) - c0_r; michael@0: c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n); michael@0: c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n); michael@0: c3_g = CLU(g_table, x, y, z_n) - c0_g; michael@0: c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n); michael@0: c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n); michael@0: c3_b = CLU(b_table, x, y, z_n) - c0_b; michael@0: } michael@0: } michael@0: } else { michael@0: if (rx >= rz) { //ry > rx && rx >= rz michael@0: c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z); michael@0: c2_r = CLU(r_table, x_n, y_n, z) - c0_r; michael@0: c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z); michael@0: c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z); michael@0: c2_g = CLU(g_table, x_n, y_n, z) - c0_g; michael@0: c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z); michael@0: c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z); michael@0: c2_b = CLU(b_table, x_n, y_n, z) - c0_b; michael@0: c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z); michael@0: } else { michael@0: if (ry >= rz) { //ry >= rz && rz > rx michael@0: c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n); michael@0: c2_r = CLU(r_table, x, y_n, z) - c0_r; michael@0: c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z); michael@0: c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n); michael@0: c2_g = CLU(g_table, x, y_n, z) - c0_g; michael@0: c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z); michael@0: c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n); michael@0: c2_b = CLU(b_table, x, y_n, z) - c0_b; michael@0: c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z); michael@0: } else { //rz > ry && ry > rx michael@0: c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n); michael@0: c2_r = CLU(r_table, x, y_n, z) - c0_r; michael@0: c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z); michael@0: c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n); michael@0: c2_g = CLU(g_table, x, y_n, z) - c0_g; michael@0: c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z); michael@0: c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n); michael@0: c2_b = CLU(b_table, x, y_n, z) - c0_b; michael@0: c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z); michael@0: } michael@0: } michael@0: } michael@0: michael@0: clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz; michael@0: clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz; michael@0: clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz; michael@0: michael@0: pcs_r = lut_interp_linear_float(clut_r, michael@0: transform->output_clut_table_r, transform->output_clut_table_length); michael@0: pcs_g = lut_interp_linear_float(clut_g, michael@0: transform->output_clut_table_g, transform->output_clut_table_length); michael@0: pcs_b = lut_interp_linear_float(clut_b, michael@0: transform->output_clut_table_b, transform->output_clut_table_length); michael@0: *dest++ = clamp_float(pcs_r); michael@0: *dest++ = clamp_float(pcs_g); michael@0: *dest++ = clamp_float(pcs_b); michael@0: } michael@0: } michael@0: */ michael@0: michael@0: static void qcms_transform_module_gamma_table(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: float out_r, out_g, out_b; michael@0: for (i = 0; i < length; i++) { michael@0: float in_r = *src++; michael@0: float in_g = *src++; michael@0: float in_b = *src++; michael@0: michael@0: out_r = lut_interp_linear_float(in_r, transform->input_clut_table_r, 256); michael@0: out_g = lut_interp_linear_float(in_g, transform->input_clut_table_g, 256); michael@0: out_b = lut_interp_linear_float(in_b, transform->input_clut_table_b, 256); michael@0: michael@0: *dest++ = clamp_float(out_r); michael@0: *dest++ = clamp_float(out_g); michael@0: *dest++ = clamp_float(out_b); michael@0: } michael@0: } michael@0: michael@0: static void qcms_transform_module_gamma_lut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: float out_r, out_g, out_b; michael@0: for (i = 0; i < length; i++) { michael@0: float in_r = *src++; michael@0: float in_g = *src++; michael@0: float in_b = *src++; michael@0: michael@0: out_r = lut_interp_linear(in_r, michael@0: transform->output_gamma_lut_r, transform->output_gamma_lut_r_length); michael@0: out_g = lut_interp_linear(in_g, michael@0: transform->output_gamma_lut_g, transform->output_gamma_lut_g_length); michael@0: out_b = lut_interp_linear(in_b, michael@0: transform->output_gamma_lut_b, transform->output_gamma_lut_b_length); michael@0: michael@0: *dest++ = clamp_float(out_r); michael@0: *dest++ = clamp_float(out_g); michael@0: *dest++ = clamp_float(out_b); michael@0: } michael@0: } michael@0: michael@0: static void qcms_transform_module_matrix_translate(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: struct matrix mat; michael@0: michael@0: /* store the results in column major mode michael@0: * this makes doing the multiplication with sse easier */ michael@0: mat.m[0][0] = transform->matrix.m[0][0]; michael@0: mat.m[1][0] = transform->matrix.m[0][1]; michael@0: mat.m[2][0] = transform->matrix.m[0][2]; michael@0: mat.m[0][1] = transform->matrix.m[1][0]; michael@0: mat.m[1][1] = transform->matrix.m[1][1]; michael@0: mat.m[2][1] = transform->matrix.m[1][2]; michael@0: mat.m[0][2] = transform->matrix.m[2][0]; michael@0: mat.m[1][2] = transform->matrix.m[2][1]; michael@0: mat.m[2][2] = transform->matrix.m[2][2]; michael@0: michael@0: for (i = 0; i < length; i++) { michael@0: float in_r = *src++; michael@0: float in_g = *src++; michael@0: float in_b = *src++; michael@0: michael@0: float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b + transform->tx; michael@0: float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b + transform->ty; michael@0: float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b + transform->tz; michael@0: michael@0: *dest++ = clamp_float(out_r); michael@0: *dest++ = clamp_float(out_g); michael@0: *dest++ = clamp_float(out_b); michael@0: } michael@0: } michael@0: michael@0: static void qcms_transform_module_matrix(struct qcms_modular_transform *transform, float *src, float *dest, size_t length) michael@0: { michael@0: size_t i; michael@0: struct matrix mat; michael@0: michael@0: /* store the results in column major mode michael@0: * this makes doing the multiplication with sse easier */ michael@0: mat.m[0][0] = transform->matrix.m[0][0]; michael@0: mat.m[1][0] = transform->matrix.m[0][1]; michael@0: mat.m[2][0] = transform->matrix.m[0][2]; michael@0: mat.m[0][1] = transform->matrix.m[1][0]; michael@0: mat.m[1][1] = transform->matrix.m[1][1]; michael@0: mat.m[2][1] = transform->matrix.m[1][2]; michael@0: mat.m[0][2] = transform->matrix.m[2][0]; michael@0: mat.m[1][2] = transform->matrix.m[2][1]; michael@0: mat.m[2][2] = transform->matrix.m[2][2]; michael@0: michael@0: for (i = 0; i < length; i++) { michael@0: float in_r = *src++; michael@0: float in_g = *src++; michael@0: float in_b = *src++; michael@0: michael@0: float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b; michael@0: float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b; michael@0: float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b; michael@0: michael@0: *dest++ = clamp_float(out_r); michael@0: *dest++ = clamp_float(out_g); michael@0: *dest++ = clamp_float(out_b); michael@0: } michael@0: } michael@0: michael@0: static struct qcms_modular_transform* qcms_modular_transform_alloc() { michael@0: return calloc(1, sizeof(struct qcms_modular_transform)); michael@0: } michael@0: michael@0: static void qcms_modular_transform_release(struct qcms_modular_transform *transform) michael@0: { michael@0: struct qcms_modular_transform *next_transform; michael@0: while (transform != NULL) { michael@0: next_transform = transform->next_transform; michael@0: // clut may use a single block of memory. michael@0: // Perhaps we should remove this to simply the code. michael@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) { michael@0: if (transform->input_clut_table_r) free(transform->input_clut_table_r); michael@0: } else { michael@0: if (transform->input_clut_table_r) free(transform->input_clut_table_r); michael@0: if (transform->input_clut_table_g) free(transform->input_clut_table_g); michael@0: if (transform->input_clut_table_b) free(transform->input_clut_table_b); michael@0: } michael@0: if (transform->r_clut + 1 == transform->g_clut && transform->g_clut + 1 == transform->b_clut) { michael@0: if (transform->r_clut) free(transform->r_clut); michael@0: } else { michael@0: if (transform->r_clut) free(transform->r_clut); michael@0: if (transform->g_clut) free(transform->g_clut); michael@0: if (transform->b_clut) free(transform->b_clut); michael@0: } michael@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) { michael@0: if (transform->output_clut_table_r) free(transform->output_clut_table_r); michael@0: } else { michael@0: if (transform->output_clut_table_r) free(transform->output_clut_table_r); michael@0: if (transform->output_clut_table_g) free(transform->output_clut_table_g); michael@0: if (transform->output_clut_table_b) free(transform->output_clut_table_b); michael@0: } michael@0: if (transform->output_gamma_lut_r) free(transform->output_gamma_lut_r); michael@0: if (transform->output_gamma_lut_g) free(transform->output_gamma_lut_g); michael@0: if (transform->output_gamma_lut_b) free(transform->output_gamma_lut_b); michael@0: free(transform); michael@0: transform = next_transform; michael@0: } michael@0: } michael@0: michael@0: /* Set transform to be the next element in the linked list. */ michael@0: static void append_transform(struct qcms_modular_transform *transform, struct qcms_modular_transform ***next_transform) michael@0: { michael@0: **next_transform = transform; michael@0: while (transform) { michael@0: *next_transform = &(transform->next_transform); michael@0: transform = transform->next_transform; michael@0: } michael@0: } michael@0: michael@0: /* reverse the transformation list (used by mBA) */ michael@0: static struct qcms_modular_transform* reverse_transform(struct qcms_modular_transform *transform) michael@0: { michael@0: struct qcms_modular_transform *prev_transform = NULL; michael@0: while (transform != NULL) { michael@0: struct qcms_modular_transform *next_transform = transform->next_transform; michael@0: transform->next_transform = prev_transform; michael@0: prev_transform = transform; michael@0: transform = next_transform; michael@0: } michael@0: michael@0: return prev_transform; michael@0: } michael@0: michael@0: #define EMPTY_TRANSFORM_LIST NULL michael@0: static struct qcms_modular_transform* qcms_modular_transform_create_mAB(struct lutmABType *lut) michael@0: { michael@0: struct qcms_modular_transform *first_transform = NULL; michael@0: struct qcms_modular_transform **next_transform = &first_transform; michael@0: struct qcms_modular_transform *transform = NULL; michael@0: michael@0: if (lut->a_curves[0] != NULL) { michael@0: size_t clut_length; michael@0: float *clut; michael@0: michael@0: // If the A curve is present this also implies the michael@0: // presence of a CLUT. michael@0: if (!lut->clut_table) michael@0: goto fail; michael@0: michael@0: // Prepare A curve. michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->input_clut_table_r = build_input_gamma_table(lut->a_curves[0]); michael@0: transform->input_clut_table_g = build_input_gamma_table(lut->a_curves[1]); michael@0: transform->input_clut_table_b = build_input_gamma_table(lut->a_curves[2]); michael@0: transform->transform_module_fn = qcms_transform_module_gamma_table; michael@0: if (lut->num_grid_points[0] != lut->num_grid_points[1] || michael@0: lut->num_grid_points[1] != lut->num_grid_points[2] ) { michael@0: //XXX: We don't currently support clut that are not squared! michael@0: goto fail; michael@0: } michael@0: michael@0: // Prepare CLUT michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: clut_length = sizeof(float)*pow(lut->num_grid_points[0], 3)*3; michael@0: clut = malloc(clut_length); michael@0: if (!clut) michael@0: goto fail; michael@0: memcpy(clut, lut->clut_table, clut_length); michael@0: transform->r_clut = clut + 0; michael@0: transform->g_clut = clut + 1; michael@0: transform->b_clut = clut + 2; michael@0: transform->grid_size = lut->num_grid_points[0]; michael@0: transform->transform_module_fn = qcms_transform_module_clut_only; michael@0: } michael@0: if (lut->m_curves[0] != NULL) { michael@0: // M curve imples the presence of a Matrix michael@0: michael@0: // Prepare M curve michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->input_clut_table_r = build_input_gamma_table(lut->m_curves[0]); michael@0: transform->input_clut_table_g = build_input_gamma_table(lut->m_curves[1]); michael@0: transform->input_clut_table_b = build_input_gamma_table(lut->m_curves[2]); michael@0: transform->transform_module_fn = qcms_transform_module_gamma_table; michael@0: michael@0: // Prepare Matrix michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->matrix = build_mAB_matrix(lut); michael@0: if (transform->matrix.invalid) michael@0: goto fail; michael@0: transform->tx = s15Fixed16Number_to_float(lut->e03); michael@0: transform->ty = s15Fixed16Number_to_float(lut->e13); michael@0: transform->tz = s15Fixed16Number_to_float(lut->e23); michael@0: transform->transform_module_fn = qcms_transform_module_matrix_translate; michael@0: } michael@0: if (lut->b_curves[0] != NULL) { michael@0: // Prepare B curve michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->input_clut_table_r = build_input_gamma_table(lut->b_curves[0]); michael@0: transform->input_clut_table_g = build_input_gamma_table(lut->b_curves[1]); michael@0: transform->input_clut_table_b = build_input_gamma_table(lut->b_curves[2]); michael@0: transform->transform_module_fn = qcms_transform_module_gamma_table; michael@0: } else { michael@0: // B curve is mandatory michael@0: goto fail; michael@0: } michael@0: michael@0: if (lut->reversed) { michael@0: // mBA are identical to mAB except that the transformation order michael@0: // is reversed michael@0: first_transform = reverse_transform(first_transform); michael@0: } michael@0: michael@0: return first_transform; michael@0: fail: michael@0: qcms_modular_transform_release(first_transform); michael@0: return NULL; michael@0: } michael@0: michael@0: static struct qcms_modular_transform* qcms_modular_transform_create_lut(struct lutType *lut) michael@0: { michael@0: struct qcms_modular_transform *first_transform = NULL; michael@0: struct qcms_modular_transform **next_transform = &first_transform; michael@0: struct qcms_modular_transform *transform = NULL; michael@0: michael@0: size_t in_curve_len, clut_length, out_curve_len; michael@0: float *in_curves, *clut, *out_curves; michael@0: michael@0: // Prepare Matrix michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->matrix = build_lut_matrix(lut); michael@0: if (transform->matrix.invalid) michael@0: goto fail; michael@0: transform->transform_module_fn = qcms_transform_module_matrix; michael@0: michael@0: // Prepare input curves michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: in_curve_len = sizeof(float)*lut->num_input_table_entries * 3; michael@0: in_curves = malloc(in_curve_len); michael@0: if (!in_curves) michael@0: goto fail; michael@0: memcpy(in_curves, lut->input_table, in_curve_len); michael@0: transform->input_clut_table_r = in_curves + lut->num_input_table_entries * 0; michael@0: transform->input_clut_table_g = in_curves + lut->num_input_table_entries * 1; michael@0: transform->input_clut_table_b = in_curves + lut->num_input_table_entries * 2; michael@0: transform->input_clut_table_length = lut->num_input_table_entries; michael@0: michael@0: // Prepare table michael@0: clut_length = sizeof(float)*pow(lut->num_clut_grid_points, 3)*3; michael@0: clut = malloc(clut_length); michael@0: if (!clut) michael@0: goto fail; michael@0: memcpy(clut, lut->clut_table, clut_length); michael@0: transform->r_clut = clut + 0; michael@0: transform->g_clut = clut + 1; michael@0: transform->b_clut = clut + 2; michael@0: transform->grid_size = lut->num_clut_grid_points; michael@0: michael@0: // Prepare output curves michael@0: out_curve_len = sizeof(float) * lut->num_output_table_entries * 3; michael@0: out_curves = malloc(out_curve_len); michael@0: if (!out_curves) michael@0: goto fail; michael@0: memcpy(out_curves, lut->output_table, out_curve_len); michael@0: transform->output_clut_table_r = out_curves + lut->num_output_table_entries * 0; michael@0: transform->output_clut_table_g = out_curves + lut->num_output_table_entries * 1; michael@0: transform->output_clut_table_b = out_curves + lut->num_output_table_entries * 2; michael@0: transform->output_clut_table_length = lut->num_output_table_entries; michael@0: transform->transform_module_fn = qcms_transform_module_clut; michael@0: michael@0: return first_transform; michael@0: fail: michael@0: qcms_modular_transform_release(first_transform); michael@0: return NULL; michael@0: } michael@0: michael@0: struct qcms_modular_transform* qcms_modular_transform_create_input(qcms_profile *in) michael@0: { michael@0: struct qcms_modular_transform *first_transform = NULL; michael@0: struct qcms_modular_transform **next_transform = &first_transform; michael@0: michael@0: if (in->A2B0) { michael@0: struct qcms_modular_transform *lut_transform; michael@0: lut_transform = qcms_modular_transform_create_lut(in->A2B0); michael@0: if (!lut_transform) michael@0: goto fail; michael@0: append_transform(lut_transform, &next_transform); michael@0: } else if (in->mAB && in->mAB->num_in_channels == 3 && in->mAB->num_out_channels == 3) { michael@0: struct qcms_modular_transform *mAB_transform; michael@0: mAB_transform = qcms_modular_transform_create_mAB(in->mAB); michael@0: if (!mAB_transform) michael@0: goto fail; michael@0: append_transform(mAB_transform, &next_transform); michael@0: michael@0: } else { michael@0: struct qcms_modular_transform *transform; michael@0: michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->input_clut_table_r = build_input_gamma_table(in->redTRC); michael@0: transform->input_clut_table_g = build_input_gamma_table(in->greenTRC); michael@0: transform->input_clut_table_b = build_input_gamma_table(in->blueTRC); michael@0: transform->transform_module_fn = qcms_transform_module_gamma_table; michael@0: if (!transform->input_clut_table_r || !transform->input_clut_table_g || michael@0: !transform->input_clut_table_b) { michael@0: goto fail; michael@0: } michael@0: michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->matrix.m[0][0] = 1/1.999969482421875f; michael@0: transform->matrix.m[0][1] = 0.f; michael@0: transform->matrix.m[0][2] = 0.f; michael@0: transform->matrix.m[1][0] = 0.f; michael@0: transform->matrix.m[1][1] = 1/1.999969482421875f; michael@0: transform->matrix.m[1][2] = 0.f; michael@0: transform->matrix.m[2][0] = 0.f; michael@0: transform->matrix.m[2][1] = 0.f; michael@0: transform->matrix.m[2][2] = 1/1.999969482421875f; michael@0: transform->matrix.invalid = false; michael@0: transform->transform_module_fn = qcms_transform_module_matrix; michael@0: michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->matrix = build_colorant_matrix(in); michael@0: transform->transform_module_fn = qcms_transform_module_matrix; michael@0: } michael@0: michael@0: return first_transform; michael@0: fail: michael@0: qcms_modular_transform_release(first_transform); michael@0: return EMPTY_TRANSFORM_LIST; michael@0: } michael@0: static struct qcms_modular_transform* qcms_modular_transform_create_output(qcms_profile *out) michael@0: { michael@0: struct qcms_modular_transform *first_transform = NULL; michael@0: struct qcms_modular_transform **next_transform = &first_transform; michael@0: michael@0: if (out->B2A0) { michael@0: struct qcms_modular_transform *lut_transform; michael@0: lut_transform = qcms_modular_transform_create_lut(out->B2A0); michael@0: if (!lut_transform) michael@0: goto fail; michael@0: append_transform(lut_transform, &next_transform); michael@0: } else if (out->mBA && out->mBA->num_in_channels == 3 && out->mBA->num_out_channels == 3) { michael@0: struct qcms_modular_transform *lut_transform; michael@0: lut_transform = qcms_modular_transform_create_mAB(out->mBA); michael@0: if (!lut_transform) michael@0: goto fail; michael@0: append_transform(lut_transform, &next_transform); michael@0: } else if (out->redTRC && out->greenTRC && out->blueTRC) { michael@0: struct qcms_modular_transform *transform; michael@0: michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->matrix = matrix_invert(build_colorant_matrix(out)); michael@0: transform->transform_module_fn = qcms_transform_module_matrix; michael@0: michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: transform->matrix.m[0][0] = 1.999969482421875f; michael@0: transform->matrix.m[0][1] = 0.f; michael@0: transform->matrix.m[0][2] = 0.f; michael@0: transform->matrix.m[1][0] = 0.f; michael@0: transform->matrix.m[1][1] = 1.999969482421875f; michael@0: transform->matrix.m[1][2] = 0.f; michael@0: transform->matrix.m[2][0] = 0.f; michael@0: transform->matrix.m[2][1] = 0.f; michael@0: transform->matrix.m[2][2] = 1.999969482421875f; michael@0: transform->matrix.invalid = false; michael@0: transform->transform_module_fn = qcms_transform_module_matrix; michael@0: michael@0: transform = qcms_modular_transform_alloc(); michael@0: if (!transform) michael@0: goto fail; michael@0: append_transform(transform, &next_transform); michael@0: build_output_lut(out->redTRC, &transform->output_gamma_lut_r, michael@0: &transform->output_gamma_lut_r_length); michael@0: build_output_lut(out->greenTRC, &transform->output_gamma_lut_g, michael@0: &transform->output_gamma_lut_g_length); michael@0: build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, michael@0: &transform->output_gamma_lut_b_length); michael@0: transform->transform_module_fn = qcms_transform_module_gamma_lut; michael@0: michael@0: if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g || michael@0: !transform->output_gamma_lut_b) { michael@0: goto fail; michael@0: } michael@0: } else { michael@0: assert(0 && "Unsupported output profile workflow."); michael@0: return NULL; michael@0: } michael@0: michael@0: return first_transform; michael@0: fail: michael@0: qcms_modular_transform_release(first_transform); michael@0: return EMPTY_TRANSFORM_LIST; michael@0: } michael@0: michael@0: /* Not Completed michael@0: // Simplify the transformation chain to an equivalent transformation chain michael@0: static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform) michael@0: { michael@0: struct qcms_modular_transform *first_transform = NULL; michael@0: struct qcms_modular_transform *curr_trans = transform; michael@0: struct qcms_modular_transform *prev_trans = NULL; michael@0: while (curr_trans) { michael@0: struct qcms_modular_transform *next_trans = curr_trans->next_transform; michael@0: if (curr_trans->transform_module_fn == qcms_transform_module_matrix) { michael@0: if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) { michael@0: curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix); michael@0: goto remove_next; michael@0: } michael@0: } michael@0: if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) { michael@0: bool isLinear = true; michael@0: uint16_t i; michael@0: for (i = 0; isLinear && i < 256; i++) { michael@0: isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i; michael@0: isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i; michael@0: isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i; michael@0: } michael@0: goto remove_current; michael@0: } michael@0: michael@0: next_transform: michael@0: if (!next_trans) break; michael@0: prev_trans = curr_trans; michael@0: curr_trans = next_trans; michael@0: continue; michael@0: remove_current: michael@0: if (curr_trans == transform) { michael@0: //Update head michael@0: transform = next_trans; michael@0: } else { michael@0: prev_trans->next_transform = next_trans; michael@0: } michael@0: curr_trans->next_transform = NULL; michael@0: qcms_modular_transform_release(curr_trans); michael@0: //return transform; michael@0: return qcms_modular_transform_reduce(transform); michael@0: remove_next: michael@0: curr_trans->next_transform = next_trans->next_transform; michael@0: next_trans->next_transform = NULL; michael@0: qcms_modular_transform_release(next_trans); michael@0: continue; michael@0: } michael@0: return transform; michael@0: } michael@0: */ michael@0: michael@0: static struct qcms_modular_transform* qcms_modular_transform_create(qcms_profile *in, qcms_profile *out) michael@0: { michael@0: struct qcms_modular_transform *first_transform = NULL; michael@0: struct qcms_modular_transform **next_transform = &first_transform; michael@0: michael@0: if (in->color_space == RGB_SIGNATURE) { michael@0: struct qcms_modular_transform* rgb_to_pcs; michael@0: rgb_to_pcs = qcms_modular_transform_create_input(in); michael@0: if (!rgb_to_pcs) michael@0: goto fail; michael@0: append_transform(rgb_to_pcs, &next_transform); michael@0: } else { michael@0: assert(0 && "input color space not supported"); michael@0: goto fail; michael@0: } michael@0: michael@0: if (in->pcs == LAB_SIGNATURE && out->pcs == XYZ_SIGNATURE) { michael@0: struct qcms_modular_transform* lab_to_pcs; michael@0: lab_to_pcs = qcms_modular_transform_alloc(); michael@0: if (!lab_to_pcs) michael@0: goto fail; michael@0: append_transform(lab_to_pcs, &next_transform); michael@0: lab_to_pcs->transform_module_fn = qcms_transform_module_LAB_to_XYZ; michael@0: } michael@0: michael@0: // This does not improve accuracy in practice, something is wrong here. michael@0: //if (in->chromaticAdaption.invalid == false) { michael@0: // struct qcms_modular_transform* chromaticAdaption; michael@0: // chromaticAdaption = qcms_modular_transform_alloc(); michael@0: // if (!chromaticAdaption) michael@0: // goto fail; michael@0: // append_transform(chromaticAdaption, &next_transform); michael@0: // chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption); michael@0: // chromaticAdaption->transform_module_fn = qcms_transform_module_matrix; michael@0: //} michael@0: michael@0: if (in->pcs == XYZ_SIGNATURE && out->pcs == LAB_SIGNATURE) { michael@0: struct qcms_modular_transform* pcs_to_lab; michael@0: pcs_to_lab = qcms_modular_transform_alloc(); michael@0: if (!pcs_to_lab) michael@0: goto fail; michael@0: append_transform(pcs_to_lab, &next_transform); michael@0: pcs_to_lab->transform_module_fn = qcms_transform_module_XYZ_to_LAB; michael@0: } michael@0: michael@0: if (out->color_space == RGB_SIGNATURE) { michael@0: struct qcms_modular_transform* pcs_to_rgb; michael@0: pcs_to_rgb = qcms_modular_transform_create_output(out); michael@0: if (!pcs_to_rgb) michael@0: goto fail; michael@0: append_transform(pcs_to_rgb, &next_transform); michael@0: } else { michael@0: assert(0 && "output color space not supported"); michael@0: goto fail; michael@0: } michael@0: // Not Completed michael@0: //return qcms_modular_transform_reduce(first_transform); michael@0: return first_transform; michael@0: fail: michael@0: qcms_modular_transform_release(first_transform); michael@0: return EMPTY_TRANSFORM_LIST; michael@0: } michael@0: michael@0: static float* qcms_modular_transform_data(struct qcms_modular_transform *transform, float *src, float *dest, size_t len) michael@0: { michael@0: while (transform != NULL) { michael@0: // Keep swaping src/dest when performing a transform to use less memory. michael@0: float *new_src = dest; michael@0: const transform_module_fn_t transform_fn = transform->transform_module_fn; michael@0: if (transform_fn != qcms_transform_module_gamma_table && michael@0: transform_fn != qcms_transform_module_gamma_lut && michael@0: transform_fn != qcms_transform_module_clut && michael@0: transform_fn != qcms_transform_module_clut_only && michael@0: transform_fn != qcms_transform_module_matrix && michael@0: transform_fn != qcms_transform_module_matrix_translate && michael@0: transform_fn != qcms_transform_module_LAB_to_XYZ && michael@0: transform_fn != qcms_transform_module_XYZ_to_LAB) { michael@0: assert(0 && "Unsupported transform module"); michael@0: return NULL; michael@0: } michael@0: transform->transform_module_fn(transform,src,dest,len); michael@0: dest = src; michael@0: src = new_src; michael@0: transform = transform->next_transform; michael@0: } michael@0: // The results end up in the src buffer because of the switching michael@0: return src; michael@0: } michael@0: michael@0: float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize) michael@0: { michael@0: struct qcms_modular_transform *transform_list = qcms_modular_transform_create(in, out); michael@0: if (transform_list != NULL) { michael@0: float *lut = qcms_modular_transform_data(transform_list, src, dest, lutSize/3); michael@0: qcms_modular_transform_release(transform_list); michael@0: return lut; michael@0: } michael@0: return NULL; michael@0: }