Bug 674230 - Merge from qcms github branch v4; r=jmuizelaar

This commit is contained in:
Benoit Girard 2011-07-27 10:11:19 -04:00
parent e5b1c12c0c
commit 3cde961989
11 changed files with 2825 additions and 657 deletions

View File

@ -13,7 +13,13 @@ DIST_INSTALL = 1
EXPORTS = qcms.h qcmstypes.h
CSRCS = iccread.c transform.c
CSRCS = \
chain.c \
iccread.c \
matrix.c \
transform.c \
transform_util.c \
$(NULL)
ifeq (86,$(findstring 86,$(OS_TEST)))
CSRCS += transform-sse2.c

989
gfx/qcms/chain.c Normal file
View File

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

30
gfx/qcms/chain.h Normal file
View File

@ -0,0 +1,30 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_CHAIN_H
#define _QCMS_CHAIN_H
// Generates and returns a 3D LUT with lutSize^3 samples using the provided src/dest.
float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize);
#endif

View File

@ -1,3 +1,4 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
@ -23,9 +24,13 @@
#include <math.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <string.h> //memset
#include "qcmsint.h"
/* It might be worth having a unified limit on content controlled
* allocation per profile. This would remove the need for many
* of the arbitrary limits that we used */
typedef uint32_t be32;
typedef uint16_t be16;
@ -120,12 +125,15 @@ static s15Fixed16Number read_s15Fixed16Number(struct mem_source *mem, size_t off
return read_u32(mem, offset);
}
#if 0
static uInt8Number read_uInt8Number(struct mem_source *mem, size_t offset)
{
return read_u8(mem, offset);
}
static uInt16Number read_uInt16Number(struct mem_source *mem, size_t offset)
{
return read_u16(mem, offset);
}
#endif
#define BAD_VALUE_PROFILE NULL
#define INVALID_PROFILE NULL
@ -144,16 +152,21 @@ static void check_CMM_type_signature(struct mem_source *src)
static void check_profile_version(struct mem_source *src)
{
/*
uint8_t major_revision = read_u8(src, 8 + 0);
uint8_t minor_revision = read_u8(src, 8 + 1);
*/
uint8_t reserved1 = read_u8(src, 8 + 2);
uint8_t reserved2 = read_u8(src, 8 + 3);
/* Checking the version doesn't buy us anything
if (major_revision != 0x4) {
if (major_revision > 0x2)
invalid_source(src, "Unsupported major revision");
if (minor_revision > 0x40)
invalid_source(src, "Unsupported minor revision");
}
*/
if (reserved1 != 0 || reserved2 != 0)
invalid_source(src, "Invalid reserved bytes");
}
@ -172,8 +185,9 @@ static void read_class_signature(qcms_profile *profile, struct mem_source *mem)
switch (profile->class) {
case DISPLAY_DEVICE_PROFILE:
case INPUT_DEVICE_PROFILE:
break;
case OUTPUT_DEVICE_PROFILE:
case COLOR_SPACE_PROFILE:
break;
default:
invalid_source(mem, "Invalid Profile/Device Class signature");
}
@ -191,6 +205,18 @@ static void read_color_space(qcms_profile *profile, struct mem_source *mem)
}
}
static void read_pcs(qcms_profile *profile, struct mem_source *mem)
{
profile->pcs = read_u32(mem, 20);
switch (profile->pcs) {
case XYZ_SIGNATURE:
case LAB_SIGNATURE:
break;
default:
invalid_source(mem, "Unsupported pcs");
}
}
struct tag
{
uint32_t signature;
@ -240,6 +266,9 @@ qcms_bool qcms_profile_is_bogus(qcms_profile *profile)
if (profile->color_space != RGB_SIGNATURE)
return false;
if (profile->A2B0 || profile->B2A0)
return false;
rX = s15Fixed16Number_to_float(profile->redColorant.X);
rY = s15Fixed16Number_to_float(profile->redColorant.Y);
rZ = s15Fixed16Number_to_float(profile->redColorant.Z);
@ -300,6 +329,8 @@ qcms_bool qcms_profile_is_bogus(qcms_profile *profile)
#define TAG_gTRC 0x67545243
#define TAG_kTRC 0x6b545243
#define TAG_A2B0 0x41324230
#define TAG_B2A0 0x42324130
#define TAG_CHAD 0x63686164
static struct tag *find_tag(struct tag_index index, uint32_t tag_id)
{
@ -313,10 +344,39 @@ static struct tag *find_tag(struct tag_index index, uint32_t tag_id)
return tag;
}
#define XYZ_TYPE 0x58595a20 // 'XYZ '
#define CURVE_TYPE 0x63757276 // 'curv'
#define LUT16_TYPE 0x6d667432 // 'mft2'
#define LUT8_TYPE 0x6d667431 // 'mft1'
#define XYZ_TYPE 0x58595a20 // 'XYZ '
#define CURVE_TYPE 0x63757276 // 'curv'
#define PARAMETRIC_CURVE_TYPE 0x70617261 // 'para'
#define LUT16_TYPE 0x6d667432 // 'mft2'
#define LUT8_TYPE 0x6d667431 // 'mft1'
#define LUT_MAB_TYPE 0x6d414220 // 'mAB '
#define LUT_MBA_TYPE 0x6d424120 // 'mBA '
#define CHROMATIC_TYPE 0x73663332 // 'sf32'
static struct matrix read_tag_s15Fixed16ArrayType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
{
struct tag *tag = find_tag(index, tag_id);
struct matrix matrix;
if (tag) {
uint8_t i;
uint32_t offset = tag->offset;
uint32_t type = read_u32(src, offset);
// Check mandatory type signature for s16Fixed16ArrayType
if (type != CHROMATIC_TYPE) {
invalid_source(src, "unexpected type, expected 'sf32'");
}
for (i = 0; i < 9; i++) {
matrix.m[i/3][i%3] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset+8+i*4));
}
matrix.invalid = false;
} else {
matrix.invalid = true;
invalid_source(src, "missing sf32tag");
}
return matrix;
}
static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
{
@ -337,20 +397,24 @@ static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_inde
return num;
}
static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
// Read the tag at a given offset rather then the tag_index.
// This method is used when reading mAB tags where nested curveType are
// present that are not part of the tag_index.
static struct curveType *read_curveType(struct mem_source *src, uint32_t offset, uint32_t *len)
{
struct tag *tag = find_tag(index, tag_id);
static const size_t COUNT_TO_LENGTH[5] = {1, 3, 4, 5, 7};
struct curveType *curve = NULL;
if (tag) {
uint32_t offset = tag->offset;
uint32_t type = read_u32(src, offset);
uint32_t count = read_u32(src, offset+8);
unsigned int i;
uint32_t type = read_u32(src, offset);
uint32_t count;
int i;
if (type != CURVE_TYPE) {
invalid_source(src, "unexpected type, expected CURV");
return NULL;
}
if (type != CURVE_TYPE && type != PARAMETRIC_CURVE_TYPE) {
invalid_source(src, "unexpected type, expected CURV or PARA");
return NULL;
}
if (type == CURVE_TYPE) {
count = read_u32(src, offset+8);
#define MAX_CURVE_ENTRIES 40000 //arbitrary
if (count > MAX_CURVE_ENTRIES) {
@ -362,9 +426,50 @@ static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_i
return NULL;
curve->count = count;
curve->type = type;
for (i=0; i<count; i++) {
curve->data[i] = read_u16(src, offset + 12 + i *2);
curve->data[i] = read_u16(src, offset + 12 + i*2);
}
*len = 12 + count * 2;
} else { //PARAMETRIC_CURVE_TYPE
count = read_u16(src, offset+8);
if (count > 4) {
invalid_source(src, "parametric function type not supported.");
return NULL;
}
curve = malloc(sizeof(struct curveType));
if (!curve)
return NULL;
curve->count = count;
curve->type = type;
for (i=0; i < COUNT_TO_LENGTH[count]; i++) {
curve->parameter[i] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset + 12 + i*4));
}
*len = 12 + COUNT_TO_LENGTH[count] * 4;
if ((count == 1 || count == 2)) {
/* we have a type 1 or type 2 function that has a division by 'a' */
float a = curve->parameter[1];
if (a == 0.f)
invalid_source(src, "parametricCurve definition causes division by zero.");
}
}
return curve;
}
static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
{
struct tag *tag = find_tag(index, tag_id);
struct curveType *curve = NULL;
if (tag) {
uint32_t len;
return read_curveType(src, tag->offset, &len);
} else {
invalid_source(src, "missing curvetag");
}
@ -372,7 +477,178 @@ static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_i
return curve;
}
/* This function's not done yet */
#define MAX_CLUT_SIZE 500000 // arbitrary
#define MAX_CHANNELS 10 // arbitrary
static void read_nested_curveType(struct mem_source *src, struct curveType *(*curveArray)[MAX_CHANNELS], uint8_t num_channels, uint32_t curve_offset)
{
uint32_t channel_offset = 0;
int i;
for (i = 0; i < num_channels; i++) {
uint32_t tag_len;
(*curveArray)[i] = read_curveType(src, curve_offset + channel_offset, &tag_len);
if (!(*curveArray)[i]) {
invalid_source(src, "invalid nested curveType curve");
}
channel_offset += tag_len;
// 4 byte aligned
if ((tag_len % 4) != 0)
channel_offset += 4 - (tag_len % 4);
}
}
static void mAB_release(struct lutmABType *lut)
{
uint8_t i;
for (i = 0; i < lut->num_in_channels; i++){
free(lut->a_curves[i]);
}
for (i = 0; i < lut->num_out_channels; i++){
free(lut->b_curves[i]);
free(lut->m_curves[i]);
}
free(lut);
}
/* See section 10.10 for specs */
static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
{
struct tag *tag = find_tag(index, tag_id);
uint32_t offset = tag->offset;
uint32_t a_curve_offset, b_curve_offset, m_curve_offset;
uint32_t matrix_offset;
uint32_t clut_offset;
uint32_t clut_size = 1;
uint8_t clut_precision;
uint32_t type = read_u32(src, offset);
uint8_t num_in_channels, num_out_channels;
struct lutmABType *lut;
int i;
if (type != LUT_MAB_TYPE && type != LUT_MBA_TYPE) {
return NULL;
}
num_in_channels = read_u8(src, offset + 8);
num_out_channels = read_u8(src, offset + 8);
if (num_in_channels > MAX_CHANNELS || num_out_channels > MAX_CHANNELS)
return NULL;
// We require 3in/out channels since we only support RGB->XYZ (or RGB->LAB)
// XXX: If we remove this restriction make sure that the number of channels
// is less or equal to the maximum number of mAB curves in qcmsint.h
// also check for clut_size overflow.
if (num_in_channels != 3 || num_out_channels != 3)
return NULL;
// some of this data is optional and is denoted by a zero offset
// we also use this to track their existance
a_curve_offset = read_u32(src, offset + 28);
clut_offset = read_u32(src, offset + 24);
m_curve_offset = read_u32(src, offset + 20);
matrix_offset = read_u32(src, offset + 16);
b_curve_offset = read_u32(src, offset + 12);
// Convert offsets relative to the tag to relative to the profile
// preserve zero for optional fields
if (a_curve_offset)
a_curve_offset += offset;
if (clut_offset)
clut_offset += offset;
if (m_curve_offset)
m_curve_offset += offset;
if (matrix_offset)
matrix_offset += offset;
if (b_curve_offset)
b_curve_offset += offset;
if (clut_offset) {
assert (num_in_channels == 3);
// clut_size can not overflow since lg(256^num_in_channels) = 24 bits.
for (i = 0; i < num_in_channels; i++) {
clut_size *= read_u8(src, clut_offset + i);
}
} else {
clut_size = 0;
}
// 24bits * 3 won't overflow either
clut_size = clut_size * num_out_channels;
if (clut_size > MAX_CLUT_SIZE)
return NULL;
lut = malloc(sizeof(struct lutmABType) + (clut_size) * sizeof(float));
if (!lut)
return NULL;
// we'll fill in the rest below
memset(lut, 0, sizeof(struct lutmABType));
lut->clut_table = &lut->clut_table_data[0];
for (i = 0; i < num_in_channels; i++) {
lut->num_grid_points[i] = read_u8(src, clut_offset + i);
}
// Reverse the processing of transformation elements for mBA type.
lut->reversed = (type == LUT_MBA_TYPE);
lut->num_in_channels = num_in_channels;
lut->num_out_channels = num_out_channels;
if (matrix_offset) {
// read the matrix if we have it
lut->e00 = read_s15Fixed16Number(src, matrix_offset+4*0);
lut->e01 = read_s15Fixed16Number(src, matrix_offset+4*1);
lut->e02 = read_s15Fixed16Number(src, matrix_offset+4*2);
lut->e10 = read_s15Fixed16Number(src, matrix_offset+4*3);
lut->e11 = read_s15Fixed16Number(src, matrix_offset+4*4);
lut->e12 = read_s15Fixed16Number(src, matrix_offset+4*5);
lut->e20 = read_s15Fixed16Number(src, matrix_offset+4*6);
lut->e21 = read_s15Fixed16Number(src, matrix_offset+4*7);
lut->e22 = read_s15Fixed16Number(src, matrix_offset+4*8);
lut->e03 = read_s15Fixed16Number(src, matrix_offset+4*9);
lut->e13 = read_s15Fixed16Number(src, matrix_offset+4*10);
lut->e23 = read_s15Fixed16Number(src, matrix_offset+4*11);
}
if (a_curve_offset) {
read_nested_curveType(src, &lut->a_curves, num_in_channels, a_curve_offset);
}
if (m_curve_offset) {
read_nested_curveType(src, &lut->m_curves, num_out_channels, m_curve_offset);
}
if (b_curve_offset) {
read_nested_curveType(src, &lut->b_curves, num_out_channels, b_curve_offset);
} else {
invalid_source(src, "B curves required");
}
if (clut_offset) {
clut_precision = read_u8(src, clut_offset + 16);
if (clut_precision == 1) {
for (i = 0; i < clut_size; i++) {
lut->clut_table[i] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + 20 + i*1));
}
} else if (clut_precision == 2) {
for (i = 0; i < clut_size; i++) {
lut->clut_table[i] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + 20 + i*2));
}
} else {
invalid_source(src, "Invalid clut precision");
}
}
if (!src->valid) {
mAB_release(lut);
return NULL;
}
return lut;
}
static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
{
struct tag *tag = find_tag(index, tag_id);
@ -381,32 +657,53 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
uint16_t num_input_table_entries;
uint16_t num_output_table_entries;
uint8_t in_chan, grid_points, out_chan;
uint32_t clut_offset, output_offset;
uint32_t clut_size;
size_t entry_size;
struct lutType *lut;
int i;
num_input_table_entries = read_u16(src, offset + 48);
num_output_table_entries = read_u16(src, offset + 50);
/* I'm not sure why the spec specifies a fixed number of entries for LUT8 tables even though
* they have room for the num_entries fields */
if (type == LUT8_TYPE) {
num_input_table_entries = 256;
num_output_table_entries = 256;
entry_size = 1;
} else if (type == LUT16_TYPE) {
num_input_table_entries = read_u16(src, offset + 48);
num_output_table_entries = read_u16(src, offset + 50);
entry_size = 2;
} else {
assert(0); // the caller checks that this doesn't happen
invalid_source(src, "Unexpected lut type");
return NULL;
}
in_chan = read_u8(src, offset + 8);
out_chan = read_u8(src, offset + 9);
grid_points = read_u8(src, offset + 10);
if (!src->valid)
return NULL;
clut_size = in_chan * grid_points * out_chan;
#define MAX_CLUT_SIZE 10000 // arbitrary
clut_size = pow(grid_points, in_chan);
if (clut_size > MAX_CLUT_SIZE) {
return NULL;
}
if (type != LUT16_TYPE && type != LUT8_TYPE)
if (in_chan != 3 || out_chan != 3) {
return NULL;
}
lut = malloc(sizeof(struct lutType) + (clut_size + num_input_table_entries + num_output_table_entries)*sizeof(uint8_t));
if (!lut)
lut = malloc(sizeof(struct lutType) + (num_input_table_entries * in_chan + clut_size*out_chan + num_output_table_entries * out_chan)*sizeof(float));
if (!lut) {
return NULL;
}
/* compute the offsets of tables */
lut->input_table = &lut->table_data[0];
lut->clut_table = &lut->table_data[in_chan*num_input_table_entries];
lut->output_table = &lut->table_data[in_chan*num_input_table_entries + clut_size*out_chan];
lut->num_input_table_entries = num_input_table_entries;
lut->num_output_table_entries = num_output_table_entries;
lut->num_input_channels = read_u8(src, offset + 8);
lut->num_output_channels = read_u8(src, offset + 9);
lut->num_clut_grid_points = read_u8(src, offset + 10);
@ -420,7 +717,36 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
lut->e21 = read_s15Fixed16Number(src, offset+40);
lut->e22 = read_s15Fixed16Number(src, offset+44);
//TODO: finish up
for (i = 0; i < lut->num_input_table_entries * in_chan; i++) {
if (type == LUT8_TYPE) {
lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + 52 + i * entry_size));
} else {
lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + 52 + i * entry_size));
}
}
clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size;
for (i = 0; i < clut_size * out_chan; i+=3) {
if (type == LUT8_TYPE) {
lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0));
lut->clut_table[i+1] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 1));
lut->clut_table[i+2] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 2));
} else {
lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 0));
lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 2));
lut->clut_table[i+2] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 4));
}
}
output_offset = clut_offset + clut_size * out_chan * entry_size;
for (i = 0; i < lut->num_output_table_entries * out_chan; i++) {
if (type == LUT8_TYPE) {
lut->output_table[i] = uInt8Number_to_float(read_uInt8Number(src, output_offset + i*entry_size));
} else {
lut->output_table[i] = uInt16Number_to_float(read_uInt16Number(src, output_offset + i*entry_size));
}
}
return lut;
}
@ -443,6 +769,8 @@ qcms_profile *qcms_profile_create(void)
return calloc(sizeof(qcms_profile), 1);
}
/* build sRGB gamma table */
/* based on cmsBuildParametricGamma() */
static uint16_t *build_sRGB_gamma_table(int num_entries)
@ -493,6 +821,7 @@ static struct curveType *curve_from_table(uint16_t *table, int num_entries)
curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries);
if (!curve)
return NULL;
curve->type = CURVE_TYPE;
curve->count = num_entries;
for (i = 0; i < num_entries; i++) {
curve->data[i] = table[i];
@ -502,12 +831,12 @@ static struct curveType *curve_from_table(uint16_t *table, int num_entries)
static uint16_t float_to_u8Fixed8Number(float a)
{
if (a > (255. + 255./256))
if (a > (255.f + 255.f/256))
return 0xffff;
else if (a < 0.)
else if (a < 0.f)
return 0;
else
return floor(a*256. + .5);
return floor(a*256.f + .5f);
}
static struct curveType *curve_from_gamma(float gamma)
@ -522,14 +851,6 @@ static struct curveType *curve_from_gamma(float gamma)
return curve;
}
static void qcms_profile_fini(qcms_profile *profile)
{
free(profile->redTRC);
free(profile->blueTRC);
free(profile->greenTRC);
free(profile->grayTRC);
free(profile);
}
//XXX: it would be nice if we had a way of ensuring
// everything in a profile was initialized regardless of how it was created
@ -547,7 +868,7 @@ qcms_profile* qcms_profile_create_rgb_with_gamma(
//XXX: should store the whitepoint
if (!set_rgb_colorants(profile, white_point, primaries)) {
qcms_profile_fini(profile);
qcms_profile_release(profile);
return INVALID_PROFILE;
}
@ -556,7 +877,7 @@ qcms_profile* qcms_profile_create_rgb_with_gamma(
profile->greenTRC = curve_from_gamma(gamma);
if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) {
qcms_profile_fini(profile);
qcms_profile_release(profile);
return NO_MEM_PROFILE;
}
profile->class = DISPLAY_DEVICE_PROFILE;
@ -576,7 +897,7 @@ qcms_profile* qcms_profile_create_rgb_with_table(
//XXX: should store the whitepoint
if (!set_rgb_colorants(profile, white_point, primaries)) {
qcms_profile_fini(profile);
qcms_profile_release(profile);
return INVALID_PROFILE;
}
@ -585,7 +906,7 @@ qcms_profile* qcms_profile_create_rgb_with_table(
profile->greenTRC = curve_from_table(table, num_entries);
if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) {
qcms_profile_fini(profile);
qcms_profile_release(profile);
return NO_MEM_PROFILE;
}
profile->class = DISPLAY_DEVICE_PROFILE;
@ -697,6 +1018,7 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
read_class_signature(profile, src);
read_rendering_intent(profile, src);
read_color_space(profile, src);
read_pcs(profile, src);
//TODO read rest of profile stuff
if (!src->valid)
@ -706,23 +1028,48 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
if (!src->valid || !index.tags)
goto invalid_tag_table;
if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_DEVICE_PROFILE) {
if (profile->color_space == RGB_SIGNATURE) {
if (find_tag(index, TAG_CHAD)) {
profile->chromaticAdaption = read_tag_s15Fixed16ArrayType(src, index, TAG_CHAD);
} else {
profile->chromaticAdaption.invalid = true; //Signal the data is not present
}
profile->redColorant = read_tag_XYZType(src, index, TAG_rXYZ);
profile->blueColorant = read_tag_XYZType(src, index, TAG_bXYZ);
profile->greenColorant = read_tag_XYZType(src, index, TAG_gXYZ);
if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_DEVICE_PROFILE ||
profile->class == OUTPUT_DEVICE_PROFILE || profile->class == COLOR_SPACE_PROFILE) {
if (profile->color_space == RGB_SIGNATURE) {
if (find_tag(index, TAG_A2B0)) {
if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT8_TYPE ||
read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT16_TYPE) {
profile->A2B0 = read_tag_lutType(src, index, TAG_A2B0);
} else if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT_MAB_TYPE) {
profile->mAB = read_tag_lutmABType(src, index, TAG_A2B0);
}
}
if (find_tag(index, TAG_B2A0)) {
if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT8_TYPE ||
read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT16_TYPE) {
profile->B2A0 = read_tag_lutType(src, index, TAG_B2A0);
} else if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT_MBA_TYPE) {
profile->mBA = read_tag_lutmABType(src, index, TAG_B2A0);
}
}
if (find_tag(index, TAG_rXYZ) || !qcms_supports_iccv4) {
profile->redColorant = read_tag_XYZType(src, index, TAG_rXYZ);
profile->greenColorant = read_tag_XYZType(src, index, TAG_gXYZ);
profile->blueColorant = read_tag_XYZType(src, index, TAG_bXYZ);
}
if (!src->valid)
goto invalid_tag_table;
profile->redTRC = read_tag_curveType(src, index, TAG_rTRC);
profile->blueTRC = read_tag_curveType(src, index, TAG_bTRC);
profile->greenTRC = read_tag_curveType(src, index, TAG_gTRC);
if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC)
goto invalid_tag_table;
if (find_tag(index, TAG_rTRC) || !qcms_supports_iccv4) {
profile->redTRC = read_tag_curveType(src, index, TAG_rTRC);
profile->greenTRC = read_tag_curveType(src, index, TAG_gTRC);
profile->blueTRC = read_tag_curveType(src, index, TAG_bTRC);
if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC)
goto invalid_tag_table;
}
} else if (profile->color_space == GRAY_SIGNATURE) {
profile->grayTRC = read_tag_curveType(src, index, TAG_kTRC);
@ -730,10 +1077,9 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
goto invalid_tag_table;
} else {
assert(0 && "read_color_space protects against entering here");
goto invalid_tag_table;
}
} else if (0 && profile->class == OUTPUT_DEVICE_PROFILE) {
profile->A2B0 = read_tag_lutType(src, index, TAG_A2B0);
} else {
goto invalid_tag_table;
}
@ -748,7 +1094,7 @@ qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
invalid_tag_table:
free(index.tags);
invalid_profile:
qcms_profile_fini(profile);
qcms_profile_release(profile);
return INVALID_PROFILE;
}
@ -763,6 +1109,11 @@ qcms_profile_get_color_space(qcms_profile *profile)
return profile->color_space;
}
static void lut_release(struct lutType *lut)
{
free(lut);
}
void qcms_profile_release(qcms_profile *profile)
{
if (profile->output_table_r)
@ -772,9 +1123,24 @@ void qcms_profile_release(qcms_profile *profile)
if (profile->output_table_b)
precache_release(profile->output_table_b);
qcms_profile_fini(profile);
if (profile->A2B0)
lut_release(profile->A2B0);
if (profile->B2A0)
lut_release(profile->B2A0);
if (profile->mAB)
mAB_release(profile->mAB);
if (profile->mBA)
mAB_release(profile->mBA);
free(profile->redTRC);
free(profile->blueTRC);
free(profile->greenTRC);
free(profile->grayTRC);
free(profile);
}
#include <stdio.h>
qcms_profile* qcms_profile_from_file(FILE *file)
{

136
gfx/qcms/matrix.c Normal file
View File

@ -0,0 +1,136 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <stdlib.h>
#include "qcmsint.h"
#include "matrix.h"
struct vector matrix_eval(struct matrix mat, struct vector v)
{
struct vector result;
result.v[0] = mat.m[0][0]*v.v[0] + mat.m[0][1]*v.v[1] + mat.m[0][2]*v.v[2];
result.v[1] = mat.m[1][0]*v.v[0] + mat.m[1][1]*v.v[1] + mat.m[1][2]*v.v[2];
result.v[2] = mat.m[2][0]*v.v[0] + mat.m[2][1]*v.v[1] + mat.m[2][2]*v.v[2];
return result;
}
//XXX: should probably pass by reference and we could
//probably reuse this computation in matrix_invert
float matrix_det(struct matrix mat)
{
float det;
det = mat.m[0][0]*mat.m[1][1]*mat.m[2][2] +
mat.m[0][1]*mat.m[1][2]*mat.m[2][0] +
mat.m[0][2]*mat.m[1][0]*mat.m[2][1] -
mat.m[0][0]*mat.m[1][2]*mat.m[2][1] -
mat.m[0][1]*mat.m[1][0]*mat.m[2][2] -
mat.m[0][2]*mat.m[1][1]*mat.m[2][0];
return det;
}
/* from pixman and cairo and Mathematics for Game Programmers */
/* lcms uses gauss-jordan elimination with partial pivoting which is
* less efficient and not as numerically stable. See Mathematics for
* Game Programmers. */
struct matrix matrix_invert(struct matrix mat)
{
struct matrix dest_mat;
int i,j;
static int a[3] = { 2, 2, 1 };
static int b[3] = { 1, 0, 0 };
/* inv (A) = 1/det (A) * adj (A) */
float det = matrix_det(mat);
if (det == 0) {
dest_mat.invalid = true;
} else {
dest_mat.invalid = false;
}
det = 1/det;
for (j = 0; j < 3; j++) {
for (i = 0; i < 3; i++) {
double p;
int ai = a[i];
int aj = a[j];
int bi = b[i];
int bj = b[j];
p = mat.m[ai][aj] * mat.m[bi][bj] -
mat.m[ai][bj] * mat.m[bi][aj];
if (((i + j) & 1) != 0)
p = -p;
dest_mat.m[j][i] = det * p;
}
}
return dest_mat;
}
struct matrix matrix_identity(void)
{
struct matrix i;
i.m[0][0] = 1;
i.m[0][1] = 0;
i.m[0][2] = 0;
i.m[1][0] = 0;
i.m[1][1] = 1;
i.m[1][2] = 0;
i.m[2][0] = 0;
i.m[2][1] = 0;
i.m[2][2] = 1;
i.invalid = false;
return i;
}
struct matrix matrix_invalid(void)
{
struct matrix inv = matrix_identity();
inv.invalid = true;
return inv;
}
/* from pixman */
/* MAT3per... */
struct matrix matrix_multiply(struct matrix a, struct matrix b)
{
struct matrix result;
int dx, dy;
int o;
for (dy = 0; dy < 3; dy++) {
for (dx = 0; dx < 3; dx++) {
double v = 0;
for (o = 0; o < 3; o++) {
v += a.m[dy][o] * b.m[o][dx];
}
result.m[dy][dx] = v;
}
}
result.invalid = a.invalid || b.invalid;
return result;
}

39
gfx/qcms/matrix.h Normal file
View File

@ -0,0 +1,39 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_MATRIX_H
#define _QCMS_MATRIX_H
struct vector {
float v[3];
};
struct vector matrix_eval(struct matrix mat, struct vector v);
float matrix_det(struct matrix mat);
struct matrix matrix_identity(void);
struct matrix matrix_multiply(struct matrix a, struct matrix b);
struct matrix matrix_invert(struct matrix mat);
struct matrix matrix_invalid(void);
#endif

View File

@ -147,7 +147,7 @@ void qcms_transform_release(qcms_transform *);
void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length);
void qcms_enable_iccv4();
#ifdef __cplusplus
}

View File

@ -1,3 +1,4 @@
/* vim: set ts=8 sw=8 noexpandtab: */
#include "qcms.h"
#include "qcmstypes.h"
@ -29,6 +30,19 @@ struct _qcms_transform {
float *input_gamma_table_g;
float *input_gamma_table_b;
float *input_clut_table_r;
float *input_clut_table_g;
float *input_clut_table_b;
uint16_t input_clut_table_length;
float *r_clut;
float *g_clut;
float *b_clut;
uint16_t grid_size;
float *output_clut_table_r;
float *output_clut_table_g;
float *output_clut_table_b;
uint16_t output_clut_table_length;
float *input_gamma_table_gray;
float out_gamma_r;
@ -56,8 +70,42 @@ struct _qcms_transform {
void (*transform_fn)(struct _qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length);
};
struct matrix {
float m[3][3];
bool invalid;
};
struct qcms_modular_transform {
struct matrix matrix;
float tx, ty, tz;
float *input_clut_table_r;
float *input_clut_table_g;
float *input_clut_table_b;
uint16_t input_clut_table_length;
float *r_clut;
float *g_clut;
float *b_clut;
uint16_t grid_size;
float *output_clut_table_r;
float *output_clut_table_g;
float *output_clut_table_b;
uint16_t output_clut_table_length;
uint16_t *output_gamma_lut_r;
uint16_t *output_gamma_lut_g;
uint16_t *output_gamma_lut_b;
size_t output_gamma_lut_r_length;
size_t output_gamma_lut_g_length;
size_t output_gamma_lut_b_length;
void (*transform_module_fn)(struct qcms_modular_transform *transform, float *src, float *dest, size_t length);
struct qcms_modular_transform *next_transform;
};
typedef int32_t s15Fixed16Number;
typedef uint16_t uInt16Number;
typedef uint8_t uInt8Number;
struct XYZNumber {
s15Fixed16Number X;
@ -66,11 +114,43 @@ struct XYZNumber {
};
struct curveType {
uint32_t type;
uint32_t count;
float parameter[7];
uInt16Number data[];
};
struct lutType {
struct lutmABType {
uint8_t num_in_channels;
uint8_t num_out_channels;
// 16 is the upperbound, actual is 0..num_in_channels.
uint8_t num_grid_points[16];
s15Fixed16Number e00;
s15Fixed16Number e01;
s15Fixed16Number e02;
s15Fixed16Number e03;
s15Fixed16Number e10;
s15Fixed16Number e11;
s15Fixed16Number e12;
s15Fixed16Number e13;
s15Fixed16Number e20;
s15Fixed16Number e21;
s15Fixed16Number e22;
s15Fixed16Number e23;
// reversed elements (for mBA)
bool reversed;
float *clut_table;
struct curveType *a_curves[10];
struct curveType *b_curves[10];
struct curveType *m_curves[10];
float clut_table_data[];
};
/* should lut8Type and lut16Type be different types? */
struct lutType { // used by lut8Type/lut16Type (mft2) only
uint8_t num_input_channels;
uint8_t num_output_channels;
uint8_t num_clut_grid_points;
@ -88,9 +168,11 @@ struct lutType {
uint16_t num_input_table_entries;
uint16_t num_output_table_entries;
uint16_t *input_table;
uint16_t *clut_table;
uint16_t *output_table;
float *input_table;
float *clut_table;
float *output_table;
float table_data[];
};
#if 0
/* this is from an intial idea of having the struct correspond to the data in
@ -113,10 +195,13 @@ struct tag_value {
#define RGB_SIGNATURE 0x52474220
#define GRAY_SIGNATURE 0x47524159
#define XYZ_SIGNATURE 0x58595A20
#define LAB_SIGNATURE 0x4C616220
struct _qcms_profile {
uint32_t class;
uint32_t color_space;
uint32_t pcs;
qcms_intent rendering_intent;
struct XYZNumber redColorant;
struct XYZNumber blueColorant;
@ -126,6 +211,10 @@ struct _qcms_profile {
struct curveType *greenTRC;
struct curveType *grayTRC;
struct lutType *A2B0;
struct lutType *B2A0;
struct lutmABType *mAB;
struct lutmABType *mBA;
struct matrix chromaticAdaption;
struct precache_output *output_table_r;
struct precache_output *output_table_g;
@ -136,6 +225,8 @@ struct _qcms_profile {
#define inline _inline
#endif
/* produces the nearest float to 'a' with a maximum error
* of 1/1024 which happens for large values like 0x40000040 */
static inline float s15Fixed16Number_to_float(s15Fixed16Number a)
{
return ((int32_t)a)/65536.f;
@ -146,6 +237,17 @@ static inline s15Fixed16Number double_to_s15Fixed16Number(double v)
return (int32_t)(v*65536);
}
static inline float uInt8Number_to_float(uInt8Number a)
{
return ((int32_t)a)/255.f;
}
static inline float uInt16Number_to_float(uInt16Number a)
{
return ((int32_t)a)/65535.f;
}
void precache_release(struct precache_output *p);
qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries);
@ -165,3 +267,5 @@ void qcms_transform_data_rgba_out_lut_sse1(qcms_transform *transform,
unsigned char *src,
unsigned char *dest,
size_t length);
extern qcms_bool qcms_supports_iccv4;

File diff suppressed because it is too large Load Diff

539
gfx/qcms/transform_util.c Normal file
View File

@ -0,0 +1,539 @@
#define _ISOC99_SOURCE /* for INFINITY */
#include <math.h>
#include <assert.h>
#include <string.h> //memcpy
#include "qcmsint.h"
#include "transform_util.h"
#include "matrix.h"
#if !defined(INFINITY)
#define INFINITY HUGE_VAL
#endif
#define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
/* value must be a value between 0 and 1 */
//XXX: is the above a good restriction to have?
float lut_interp_linear(double value, uint16_t *table, int length)
{
int upper, lower;
value = value * (length - 1); // scale to length of the array
upper = ceil(value);
lower = floor(value);
//XXX: can we be more performant here?
value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value);
/* scale the value */
return value * (1./65535.);
}
/* same as above but takes and returns a uint16_t value representing a range from 0..1 */
uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
{
/* Start scaling input_value to the length of the array: 65535*(length-1).
* We'll divide out the 65535 next */
uint32_t value = (input_value * (length - 1));
uint32_t upper = (value + 65534) / 65535; /* equivalent to ceil(value/65535) */
uint32_t lower = value / 65535; /* equivalent to floor(value/65535) */
/* interp is the distance from upper to value scaled to 0..65535 */
uint32_t interp = value % 65535;
value = (table[upper]*(interp) + table[lower]*(65535 - interp))/65535; // 0..65535*65535
return value;
}
/* same as above but takes an input_value from 0..PRECACHE_OUTPUT_MAX
* and returns a uint8_t value representing a range from 0..1 */
static
uint8_t lut_interp_linear_precache_output(uint32_t input_value, uint16_t *table, int length)
{
/* Start scaling input_value to the length of the array: PRECACHE_OUTPUT_MAX*(length-1).
* We'll divide out the PRECACHE_OUTPUT_MAX next */
uint32_t value = (input_value * (length - 1));
/* equivalent to ceil(value/PRECACHE_OUTPUT_MAX) */
uint32_t upper = (value + PRECACHE_OUTPUT_MAX-1) / PRECACHE_OUTPUT_MAX;
/* equivalent to floor(value/PRECACHE_OUTPUT_MAX) */
uint32_t lower = value / PRECACHE_OUTPUT_MAX;
/* interp is the distance from upper to value scaled to 0..PRECACHE_OUTPUT_MAX */
uint32_t interp = value % PRECACHE_OUTPUT_MAX;
/* the table values range from 0..65535 */
value = (table[upper]*(interp) + table[lower]*(PRECACHE_OUTPUT_MAX - interp)); // 0..(65535*PRECACHE_OUTPUT_MAX)
/* round and scale */
value += (PRECACHE_OUTPUT_MAX*65535/255)/2;
value /= (PRECACHE_OUTPUT_MAX*65535/255); // scale to 0..255
return value;
}
/* value must be a value between 0 and 1 */
//XXX: is the above a good restriction to have?
float lut_interp_linear_float(float value, float *table, int length)
{
int upper, lower;
value = value * (length - 1);
upper = ceil(value);
lower = floor(value);
//XXX: can we be more performant here?
value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value);
/* scale the value */
return value;
}
#if 0
/* if we use a different representation i.e. one that goes from 0 to 0x1000 we can be more efficient
* because we can avoid the divisions and use a shifting instead */
/* same as above but takes and returns a uint16_t value representing a range from 0..1 */
uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
{
uint32_t value = (input_value * (length - 1));
uint32_t upper = (value + 4095) / 4096; /* equivalent to ceil(value/4096) */
uint32_t lower = value / 4096; /* equivalent to floor(value/4096) */
uint32_t interp = value % 4096;
value = (table[upper]*(interp) + table[lower]*(4096 - interp))/4096; // 0..4096*4096
return value;
}
#endif
void compute_curve_gamma_table_type1(float gamma_table[256], double gamma)
{
unsigned int i;
for (i = 0; i < 256; i++) {
gamma_table[i] = pow(i/255., gamma);
}
}
void compute_curve_gamma_table_type2(float gamma_table[256], uint16_t *table, int length)
{
unsigned int i;
for (i = 0; i < 256; i++) {
gamma_table[i] = lut_interp_linear(i/255., table, length);
}
}
void compute_curve_gamma_table_type_parametric(float gamma_table[256], float parameter[7], int count)
{
size_t X;
float interval;
float a, b, c, e, f;
float y = parameter[0];
if (count == 0) {
a = 1;
b = 0;
c = 0;
e = 0;
f = 0;
interval = -INFINITY;
} else if(count == 1) {
a = parameter[1];
b = parameter[2];
c = 0;
e = 0;
f = 0;
interval = -1 * parameter[2] / parameter[1];
} else if(count == 2) {
a = parameter[1];
b = parameter[2];
c = 0;
e = parameter[3];
f = parameter[3];
interval = -1 * parameter[2] / parameter[1];
} else if(count == 3) {
a = parameter[1];
b = parameter[2];
c = parameter[3];
e = -c;
f = 0;
interval = parameter[4];
} else if(count == 4) {
a = parameter[1];
b = parameter[2];
c = parameter[3];
e = parameter[5] - c;
f = parameter[6];
interval = parameter[4];
} else {
assert(0 && "invalid parametric function type.");
a = 1;
b = 0;
c = 0;
e = 0;
f = 0;
interval = -INFINITY;
}
for (X = 0; X < 256; X++) {
if (X >= interval) {
// XXX The equations are not exactly as definied in the spec but are
// algebraic equivilent.
// TODO Should division by 255 be for the whole expression.
gamma_table[X] = pow(a * X / 255. + b, y) + c + e;
} else {
gamma_table[X] = c * X / 255. + f;
}
}
}
void compute_curve_gamma_table_type0(float gamma_table[256])
{
unsigned int i;
for (i = 0; i < 256; i++) {
gamma_table[i] = i/255.;
}
}
float clamp_float(float a)
{
if (a > 1.)
return 1.;
else if (a < 0)
return 0;
else
return a;
}
unsigned char clamp_u8(float v)
{
if (v > 255.)
return 255;
else if (v < 0)
return 0;
else
return floor(v+.5);
}
float u8Fixed8Number_to_float(uint16_t x)
{
// 0x0000 = 0.
// 0x0100 = 1.
// 0xffff = 255 + 255/256
return x/256.;
}
float *build_input_gamma_table(struct curveType *TRC)
{
float *gamma_table;
if (!TRC) return NULL;
gamma_table = malloc(sizeof(float)*256);
if (gamma_table) {
if (TRC->type == PARAMETRIC_CURVE_TYPE) {
compute_curve_gamma_table_type_parametric(gamma_table, TRC->parameter, TRC->count);
} else {
if (TRC->count == 0) {
compute_curve_gamma_table_type0(gamma_table);
} else if (TRC->count == 1) {
compute_curve_gamma_table_type1(gamma_table, u8Fixed8Number_to_float(TRC->data[0]));
} else {
compute_curve_gamma_table_type2(gamma_table, TRC->data, TRC->count);
}
}
}
return gamma_table;
}
struct matrix build_colorant_matrix(qcms_profile *p)
{
struct matrix result;
result.m[0][0] = s15Fixed16Number_to_float(p->redColorant.X);
result.m[0][1] = s15Fixed16Number_to_float(p->greenColorant.X);
result.m[0][2] = s15Fixed16Number_to_float(p->blueColorant.X);
result.m[1][0] = s15Fixed16Number_to_float(p->redColorant.Y);
result.m[1][1] = s15Fixed16Number_to_float(p->greenColorant.Y);
result.m[1][2] = s15Fixed16Number_to_float(p->blueColorant.Y);
result.m[2][0] = s15Fixed16Number_to_float(p->redColorant.Z);
result.m[2][1] = s15Fixed16Number_to_float(p->greenColorant.Z);
result.m[2][2] = s15Fixed16Number_to_float(p->blueColorant.Z);
result.invalid = false;
return result;
}
/* The following code is copied nearly directly from lcms.
* I think it could be much better. For example, Argyll seems to have better code in
* icmTable_lookup_bwd and icmTable_setup_bwd. However, for now this is a quick way
* to a working solution and allows for easy comparing with lcms. */
uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int length)
{
int l = 1;
int r = 0x10000;
int x = 0, res; // 'int' Give spacing for negative values
int NumZeroes, NumPoles;
int cell0, cell1;
double val2;
double y0, y1, x0, x1;
double a, b, f;
// July/27 2001 - Expanded to handle degenerated curves with an arbitrary
// number of elements containing 0 at the begining of the table (Zeroes)
// and another arbitrary number of poles (FFFFh) at the end.
// First the zero and pole extents are computed, then value is compared.
NumZeroes = 0;
while (LutTable[NumZeroes] == 0 && NumZeroes < length-1)
NumZeroes++;
// There are no zeros at the beginning and we are trying to find a zero, so
// return anything. It seems zero would be the less destructive choice
/* I'm not sure that this makes sense, but oh well... */
if (NumZeroes == 0 && Value == 0)
return 0;
NumPoles = 0;
while (LutTable[length-1- NumPoles] == 0xFFFF && NumPoles < length-1)
NumPoles++;
// Does the curve belong to this case?
if (NumZeroes > 1 || NumPoles > 1)
{
int a, b;
// Identify if value fall downto 0 or FFFF zone
if (Value == 0) return 0;
// if (Value == 0xFFFF) return 0xFFFF;
// else restrict to valid zone
a = ((NumZeroes-1) * 0xFFFF) / (length-1);
b = ((length-1 - NumPoles) * 0xFFFF) / (length-1);
l = a - 1;
r = b + 1;
}
// Seems not a degenerated case... apply binary search
while (r > l) {
x = (l + r) / 2;
res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
if (res == Value) {
// Found exact match.
return (uint16_fract_t) (x - 1);
}
if (res > Value) r = x - 1;
else l = x + 1;
}
// Not found, should we interpolate?
// Get surrounding nodes
val2 = (length-1) * ((double) (x - 1) / 65535.0);
cell0 = (int) floor(val2);
cell1 = (int) ceil(val2);
if (cell0 == cell1) return (uint16_fract_t) x;
y0 = LutTable[cell0] ;
x0 = (65535.0 * cell0) / (length-1);
y1 = LutTable[cell1] ;
x1 = (65535.0 * cell1) / (length-1);
a = (y1 - y0) / (x1 - x0);
b = y0 - a * x0;
if (fabs(a) < 0.01) return (uint16_fract_t) x;
f = ((Value - b) / a);
if (f < 0.0) return (uint16_fract_t) 0;
if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
return (uint16_fract_t) floor(f + 0.5);
}
/*
The number of entries needed to invert a lookup table should not
necessarily be the same as the original number of entries. This is
especially true of lookup tables that have a small number of entries.
For example:
Using a table like:
{0, 3104, 14263, 34802, 65535}
invert_lut will produce an inverse of:
{3, 34459, 47529, 56801, 65535}
which has an maximum error of about 9855 (pixel difference of ~38.346)
For now, we punt the decision of output size to the caller. */
static uint16_t *invert_lut(uint16_t *table, int length, int out_length)
{
int i;
/* for now we invert the lut by creating a lut of size out_length
* and attempting to lookup a value for each entry using lut_inverse_interp16 */
uint16_t *output = malloc(sizeof(uint16_t)*out_length);
if (!output)
return NULL;
for (i = 0; i < out_length; i++) {
double x = ((double) i * 65535.) / (double) (out_length - 1);
uint16_fract_t input = floor(x + .5);
output[i] = lut_inverse_interp16(input, table, length);
}
return output;
}
static void compute_precache_pow(uint8_t *output, float gamma)
{
uint32_t v = 0;
for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
//XXX: don't do integer/float conversion... and round?
output[v] = 255. * pow(v/(double)PRECACHE_OUTPUT_MAX, gamma);
}
}
void compute_precache_lut(uint8_t *output, uint16_t *table, int length)
{
uint32_t v = 0;
for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
output[v] = lut_interp_linear_precache_output(v, table, length);
}
}
void compute_precache_linear(uint8_t *output)
{
uint32_t v = 0;
for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
//XXX: round?
output[v] = v / (PRECACHE_OUTPUT_SIZE/256);
}
}
qcms_bool compute_precache(struct curveType *trc, uint8_t *output)
{
if (trc->type == PARAMETRIC_CURVE_TYPE) {
float gamma_table[256];
uint16_t gamma_table_uint[256];
uint16_t i;
uint16_t *inverted;
int inverted_size = 256;
compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
for(i = 0; i < 256; i++) {
gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
}
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, howeve r it is what lcms uses.
// the maximum number we would need is 65535 because that's the
// accuracy used for computing the pre cache table
if (inverted_size < 256)
inverted_size = 256;
inverted = invert_lut(gamma_table_uint, 256, inverted_size);
if (!inverted)
return false;
compute_precache_lut(output, inverted, inverted_size);
free(inverted);
} else {
if (trc->count == 0) {
compute_precache_linear(output);
} else if (trc->count == 1) {
compute_precache_pow(output, 1./u8Fixed8Number_to_float(trc->data[0]));
} else {
uint16_t *inverted;
int inverted_size = trc->count;
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, howeve r it is what lcms uses.
// the maximum number we would need is 65535 because that's the
// accuracy used for computing the pre cache table
if (inverted_size < 256)
inverted_size = 256;
inverted = invert_lut(trc->data, trc->count, inverted_size);
if (!inverted)
return false;
compute_precache_lut(output, inverted, inverted_size);
free(inverted);
}
}
return true;
}
static uint16_t *build_linear_table(int length)
{
int i;
uint16_t *output = malloc(sizeof(uint16_t)*length);
if (!output)
return NULL;
for (i = 0; i < length; i++) {
double x = ((double) i * 65535.) / (double) (length - 1);
uint16_fract_t input = floor(x + .5);
output[i] = input;
}
return output;
}
static uint16_t *build_pow_table(float gamma, int length)
{
int i;
uint16_t *output = malloc(sizeof(uint16_t)*length);
if (!output)
return NULL;
for (i = 0; i < length; i++) {
uint16_fract_t result;
double x = ((double) i) / (double) (length - 1);
x = pow(x, gamma); //XXX turn this conversion into a function
result = floor(x*65535. + .5);
output[i] = result;
}
return output;
}
void build_output_lut(struct curveType *trc,
uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
{
if (trc->type == PARAMETRIC_CURVE_TYPE) {
float gamma_table[256];
uint16_t i;
uint16_t *output = malloc(sizeof(uint16_t)*256);
if (!output) {
*output_gamma_lut = NULL;
return;
}
compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
*output_gamma_lut_length = 256;
for(i = 0; i < 256; i++) {
output[i] = (uint16_t)(gamma_table[i] * 65535);
}
*output_gamma_lut = output;
} else {
if (trc->count == 0) {
*output_gamma_lut = build_linear_table(4096);
*output_gamma_lut_length = 4096;
} else if (trc->count == 1) {
float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
*output_gamma_lut = build_pow_table(gamma, 4096);
*output_gamma_lut_length = 4096;
} else {
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, however it is what lcms uses.
*output_gamma_lut_length = trc->count;
if (*output_gamma_lut_length < 256)
*output_gamma_lut_length = 256;
*output_gamma_lut = invert_lut(trc->data, trc->count, *output_gamma_lut_length);
}
}
}

59
gfx/qcms/transform_util.h Normal file
View File

@ -0,0 +1,59 @@
/* vim: set ts=8 sw=8 noexpandtab: */
// qcms
// Copyright (C) 2009 Mozilla Foundation
// Copyright (C) 1998-2007 Marti Maria
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef _QCMS_TRANSFORM_UTIL_H
#define _QCMS_TRANSFORM_UTIL_H
#include <stdlib.h>
#define CLU(table,x,y,z) table[(x*len + y*x_len + z*xy_len)*3]
//XXX: could use a bettername
typedef uint16_t uint16_fract_t;
float lut_interp_linear(double value, uint16_t *table, int length);
float lut_interp_linear_float(float value, float *table, int length);
uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length);
static inline float lerp(float a, float b, float t)
{
return a*(1.f-t) + b*t;
}
unsigned char clamp_u8(float v);
float clamp_float(float a);
float u8Fixed8Number_to_float(uint16_t x);
float *build_input_gamma_table(struct curveType *TRC);
struct matrix build_colorant_matrix(qcms_profile *p);
void build_output_lut(struct curveType *trc,
uint16_t **output_gamma_lut, size_t *output_gamma_lut_length);
struct matrix matrix_invert(struct matrix mat);
qcms_bool compute_precache(struct curveType *trc, uint8_t *output);
#endif