15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* vim: set ts=8 sw=8 noexpandtab: */
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  qcms
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  Copyright (C) 2009 Mozilla Corporation
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  Copyright (C) 1998-2007 Marti Maria
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Permission is hereby granted, free of charge, to any person obtaining
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a copy of this software and associated documentation files (the "Software"),
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// to deal in the Software without restriction, including without limitation
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the rights to use, copy, modify, merge, publish, distribute, sublicense,
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and/or sell copies of the Software, and to permit persons to whom the Software
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is furnished to do so, subject to the following conditions:
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The above copyright notice and this permission notice shall be included in
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// all copies or substantial portions of the Software.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <math.h>
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <assert.h>
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h> //memcpy
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "qcmsint.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chain.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "matrix.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "transform_util.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* for MSVC, GCC, Intel, and Sun compilers */
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_AMD64) || defined(__x86_64__) || defined(__x86_64)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define X86
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif /* _M_IX86 || __i386__ || __i386 || _M_AMD64 || __x86_64__ || __x86_64 */
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This is just an approximation, I am not handling all the non-linear
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// aspects of the RGB to XYZ process, and assumming that the gamma correction
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// has transitive property in the tranformation chain.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the alghoritm:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//            - First I build the absolute conversion matrix using
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//              primaries in XYZ. This matrix is next inverted
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//            - Then I eval the source white point across this matrix
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//              obtaining the coeficients of the transformation
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//            - Then, I apply these coeficients to the original matrix
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct matrix build_RGB_to_XYZ_transfer_matrix(qcms_CIE_xyY white, qcms_CIE_xyYTRIPLE primrs)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix primaries;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix primaries_invert;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix result;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct vector white_point;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct vector coefs;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double xn, yn;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double xr, yr;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double xg, yg;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double xb, yb;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	xn = white.x;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	yn = white.y;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (yn == 0.0)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return matrix_invalid();
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	xr = primrs.red.x;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	yr = primrs.red.y;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	xg = primrs.green.x;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	yg = primrs.green.y;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	xb = primrs.blue.x;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	yb = primrs.blue.y;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[0][0] = xr;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[0][1] = xg;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[0][2] = xb;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[1][0] = yr;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[1][1] = yg;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[1][2] = yb;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[2][0] = 1 - xr - yr;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[2][1] = 1 - xg - yg;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.m[2][2] = 1 - xb - yb;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries.invalid = false;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	white_point.v[0] = xn/yn;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	white_point.v[1] = 1.;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	white_point.v[2] = (1.0-xn-yn)/yn;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	primaries_invert = matrix_invert(primaries);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	coefs = matrix_eval(primaries_invert, white_point);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[0][0] = coefs.v[0]*xr;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[0][1] = coefs.v[1]*xg;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[0][2] = coefs.v[2]*xb;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[1][0] = coefs.v[0]*yr;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[1][1] = coefs.v[1]*yg;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[1][2] = coefs.v[2]*yb;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[2][0] = coefs.v[0]*(1.-xr-yr);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[2][1] = coefs.v[1]*(1.-xg-yg);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.m[2][2] = coefs.v[2]*(1.-xb-yb);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	result.invalid = primaries_invert.invalid;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return result;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct CIE_XYZ {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double X;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double Y;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	double Z;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* CIE Illuminant D50 */
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const struct CIE_XYZ D50_XYZ = {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	0.9642,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	1.0000,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	0.8249
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* from lcms: xyY2XYZ()
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * corresponds to argyll: icmYxy2XYZ() */
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct CIE_XYZ xyY2XYZ(qcms_CIE_xyY source)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct CIE_XYZ dest;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	dest.X = (source.x / source.y) * source.Y;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	dest.Y = source.Y;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	dest.Z = ((1 - source.x - source.y) / source.y) * source.Y;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return dest;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* from lcms: ComputeChromaticAdaption */
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Compute chromatic adaption matrix using chad as cone matrix
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct matrix
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)compute_chromatic_adaption(struct CIE_XYZ source_white_point,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           struct CIE_XYZ dest_white_point,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           struct matrix chad)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix chad_inv;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct vector cone_source_XYZ, cone_source_rgb;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct vector cone_dest_XYZ, cone_dest_rgb;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix cone, tmp;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	tmp = chad;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	chad_inv = matrix_invert(tmp);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_source_XYZ.v[0] = source_white_point.X;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_source_XYZ.v[1] = source_white_point.Y;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_source_XYZ.v[2] = source_white_point.Z;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_dest_XYZ.v[0] = dest_white_point.X;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_dest_XYZ.v[1] = dest_white_point.Y;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_dest_XYZ.v[2] = dest_white_point.Z;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_source_rgb = matrix_eval(chad, cone_source_XYZ);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone_dest_rgb   = matrix_eval(chad, cone_dest_XYZ);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[0][0] = cone_dest_rgb.v[0]/cone_source_rgb.v[0];
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[0][1] = 0;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[0][2] = 0;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[1][0] = 0;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[1][1] = cone_dest_rgb.v[1]/cone_source_rgb.v[1];
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[1][2] = 0;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[2][0] = 0;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[2][1] = 0;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.m[2][2] = cone_dest_rgb.v[2]/cone_source_rgb.v[2];
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cone.invalid = false;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	// Normalize
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return matrix_multiply(chad_inv, matrix_multiply(cone, chad));
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* from lcms: cmsAdaptionMatrix */
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Bradford is assumed
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct matrix
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumination)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined (_MSC_VER)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(push)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Disable double to float truncation warning 4305 */
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(disable:4305)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix lam_rigg = {{ // Bradford matrix
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	                         {  0.8951,  0.2664, -0.1614 },
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	                         { -0.7502,  1.7135,  0.0367 },
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	                         {  0.0389, -0.0685,  1.0296 }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	                         }};
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined (_MSC_VER)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Restore warnings */
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma warning(pop)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return compute_chromatic_adaption(source_illumination, target_illumination, lam_rigg);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* from lcms: cmsAdaptMatrixToD50 */
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_white_pt)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct CIE_XYZ Dn;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix Bradford;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (source_white_pt.y == 0.0)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return matrix_invalid();
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	Dn = xyY2XYZ(source_white_pt);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	Bradford = adaption_matrix(Dn, D50_XYZ);
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return matrix_multiply(Bradford, r);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix colorants;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	colorants = adapt_matrix_to_D50(colorants, white_point);
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (colorants.invalid)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return false;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* note: there's a transpose type of operation going on here */
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->redColorant.X = double_to_s15Fixed16Number(colorants.m[0][0]);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->redColorant.Y = double_to_s15Fixed16Number(colorants.m[1][0]);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->redColorant.Z = double_to_s15Fixed16Number(colorants.m[2][0]);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->greenColorant.X = double_to_s15Fixed16Number(colorants.m[0][1]);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->greenColorant.Y = double_to_s15Fixed16Number(colorants.m[1][1]);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->greenColorant.Z = double_to_s15Fixed16Number(colorants.m[2][1]);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->blueColorant.X = double_to_s15Fixed16Number(colorants.m[0][2]);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->blueColorant.Y = double_to_s15Fixed16Number(colorants.m[1][2]);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	profile->blueColorant.Z = double_to_s15Fixed16Number(colorants.m[2][2]);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return true;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if 0
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_rgb_out_pow(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int i;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float (*mat)[4] = transform->matrix;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i=0; i<length; i++) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_r = *src++;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_g = *src++;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_b = *src++;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = transform->input_gamma_table_r[device_r];
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_g = transform->input_gamma_table_g[device_g];
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_b = transform->input_gamma_table_b[device_b];
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_r = pow(out_linear_r, transform->out_gamma_r);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_g = pow(out_linear_g, transform->out_gamma_g);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_b = pow(out_linear_b, transform->out_gamma_b);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(out_device_r*255);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(out_device_g*255);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(out_device_b*255);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_gray_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_r, out_device_g, out_device_b;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device = *src++;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear = transform->input_gamma_table_gray[device];
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(out_device_r*255);
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(out_device_g*255);
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(out_device_b*255);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Alpha is not corrected.
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   A rationale for this is found in Alvy Ray's "Should Alpha Be Nonlinear If
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   RGB Is?" Tech Memo 17 (December 14, 1998).
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	See: ftp://ftp.alvyray.com/Acrobat/17_Nonln.pdf
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_graya_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_r, out_device_g, out_device_b;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device = *src++;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char alpha = *src++;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear = transform->input_gamma_table_gray[device];
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(out_device_r*255);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(out_device_g*255);
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(out_device_b*255);
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[3]     = alpha;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 4;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_gray_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device = *src++;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		uint16_t gray;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear = transform->input_gamma_table_gray[device];
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* we could round here... */
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		gray = linear * PRECACHE_OUTPUT_MAX;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = transform->output_table_r->data[gray];
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = transform->output_table_g->data[gray];
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = transform->output_table_b->data[gray];
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_graya_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device = *src++;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char alpha = *src++;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		uint16_t gray;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear = transform->input_gamma_table_gray[device];
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* we could round here... */
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		gray = linear * PRECACHE_OUTPUT_MAX;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = transform->output_table_r->data[gray];
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = transform->output_table_g->data[gray];
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = transform->output_table_b->data[gray];
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[3]     = alpha;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 4;
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_rgb_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float (*mat)[4] = transform->matrix;
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_r = *src++;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_g = *src++;
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_b = *src++;
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		uint16_t r, g, b;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = transform->input_gamma_table_r[device_r];
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_g = transform->input_gamma_table_g[device_g];
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_b = transform->input_gamma_table_b[device_b];
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_r = clamp_float(out_linear_r);
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_g = clamp_float(out_linear_g);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_b = clamp_float(out_linear_b);
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* we could round here... */
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		r = out_linear_r * PRECACHE_OUTPUT_MAX;
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		g = out_linear_g * PRECACHE_OUTPUT_MAX;
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		b = out_linear_b * PRECACHE_OUTPUT_MAX;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = transform->output_table_r->data[r];
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = transform->output_table_g->data[g];
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = transform->output_table_b->data[b];
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float (*mat)[4] = transform->matrix;
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_r = *src++;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_g = *src++;
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_b = *src++;
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char alpha = *src++;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		uint16_t r, g, b;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = transform->input_gamma_table_r[device_r];
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_g = transform->input_gamma_table_g[device_g];
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_b = transform->input_gamma_table_b[device_b];
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_r = clamp_float(out_linear_r);
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_g = clamp_float(out_linear_g);
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_b = clamp_float(out_linear_b);
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* we could round here... */
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		r = out_linear_r * PRECACHE_OUTPUT_MAX;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		g = out_linear_g * PRECACHE_OUTPUT_MAX;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		b = out_linear_b * PRECACHE_OUTPUT_MAX;
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = transform->output_table_r->data[r];
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = transform->output_table_g->data[g];
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = transform->output_table_b->data[b];
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[3]     = alpha;
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 4;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Not used
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_clut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int xy_len = 1;
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int x_len = transform->grid_size;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int len = x_len * x_len;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* r_table = transform->r_clut;
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* g_table = transform->g_clut;
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* b_table = transform->b_clut;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_r = *src++;
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_g = *src++;
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_b = *src++;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int x = floor(linear_r * (transform->grid_size-1));
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int y = floor(linear_g * (transform->grid_size-1));
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int z = floor(linear_b * (transform->grid_size-1));
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int x_n = ceil(linear_r * (transform->grid_size-1));
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int y_n = ceil(linear_g * (transform->grid_size-1));
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int z_n = ceil(linear_b * (transform->grid_size-1));
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float x_d = linear_r * (transform->grid_size-1) - x;
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float y_d = linear_g * (transform->grid_size-1) - y;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float z_d = linear_b * (transform->grid_size-1) - z;
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float r_y1 = lerp(r_x1, r_x2, y_d);
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float r_y2 = lerp(r_x3, r_x4, y_d);
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float clut_r = lerp(r_y1, r_y2, z_d);
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float g_y1 = lerp(g_x1, g_x2, y_d);
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float g_y2 = lerp(g_x3, g_x4, y_d);
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float clut_g = lerp(g_y1, g_y2, z_d);
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float b_y1 = lerp(b_x1, b_x2, y_d);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float b_y2 = lerp(b_x3, b_x4, y_d);
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float clut_b = lerp(b_y1, b_y2, z_d);
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(clut_r*255.0f);
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(clut_g*255.0f);
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(clut_b*255.0f);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*/
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Using lcms' tetra interpolation algorithm.
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_tetra_clut_rgba(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int xy_len = 1;
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int x_len = transform->grid_size;
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int len = x_len * x_len;
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* r_table = transform->r_clut;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* g_table = transform->g_clut;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* b_table = transform->b_clut;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float c0_r, c1_r, c2_r, c3_r;
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float c0_g, c1_g, c2_g, c3_g;
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float c0_b, c1_b, c2_b, c3_b;
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float clut_r, clut_g, clut_b;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_r = *src++;
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_g = *src++;
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_b = *src++;
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_a = *src++;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int x = floor(linear_r * (transform->grid_size-1));
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int y = floor(linear_g * (transform->grid_size-1));
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int z = floor(linear_b * (transform->grid_size-1));
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int x_n = ceil(linear_r * (transform->grid_size-1));
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int y_n = ceil(linear_g * (transform->grid_size-1));
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int z_n = ceil(linear_b * (transform->grid_size-1));
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float rx = linear_r * (transform->grid_size-1) - x;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float ry = linear_g * (transform->grid_size-1) - y;
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float rz = linear_b * (transform->grid_size-1) - z;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		c0_r = CLU(r_table, x, y, z);
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		c0_g = CLU(g_table, x, y, z);
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		c0_b = CLU(b_table, x, y, z);
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if( rx >= ry ) {
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (ry >= rz) { //rx >= ry && ry >= rz
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_r = CLU(r_table, x_n, y, z) - c0_r;
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_g = CLU(g_table, x_n, y, z) - c0_g;
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_b = CLU(b_table, x_n, y, z) - c0_b;
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (rx >= rz) { //rx >= rz && rz >= ry
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y, z) - c0_r;
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y, z) - c0_g;
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y, z) - c0_b;
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				} else { //rz > rx && rx >= ry
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x, y, z_n) - c0_r;
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x, y, z_n) - c0_g;
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x, y, z_n) - c0_b;
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				}
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} else {
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (rx >= rz) { //ry > rx && rx >= rz
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_r = CLU(r_table, x, y_n, z) - c0_r;
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_g = CLU(g_table, x, y_n, z) - c0_g;
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_b = CLU(b_table, x, y_n, z) - c0_b;
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (ry >= rz) { //ry >= rz && rz > rx
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x, y_n, z) - c0_r;
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x, y_n, z) - c0_g;
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x, y_n, z) - c0_b;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				} else { //rz > ry && ry > rx
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x, y, z_n) - c0_r;
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x, y, z_n) - c0_g;
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x, y, z_n) - c0_b;
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				}
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(clut_r*255.0f);
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(clut_g*255.0f);
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(clut_b*255.0f);
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[3]     = in_a;
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 4;
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Using lcms' tetra interpolation code.
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_tetra_clut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int xy_len = 1;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int x_len = transform->grid_size;
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int len = x_len * x_len;
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* r_table = transform->r_clut;
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* g_table = transform->g_clut;
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* b_table = transform->b_clut;
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float c0_r, c1_r, c2_r, c3_r;
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float c0_g, c1_g, c2_g, c3_g;
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float c0_b, c1_b, c2_b, c3_b;
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float clut_r, clut_g, clut_b;
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_r = *src++;
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_g = *src++;
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char in_b = *src++;
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int x = floor(linear_r * (transform->grid_size-1));
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int y = floor(linear_g * (transform->grid_size-1));
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int z = floor(linear_b * (transform->grid_size-1));
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int x_n = ceil(linear_r * (transform->grid_size-1));
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int y_n = ceil(linear_g * (transform->grid_size-1));
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		int z_n = ceil(linear_b * (transform->grid_size-1));
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float rx = linear_r * (transform->grid_size-1) - x;
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float ry = linear_g * (transform->grid_size-1) - y;
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float rz = linear_b * (transform->grid_size-1) - z;
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		c0_r = CLU(r_table, x, y, z);
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		c0_g = CLU(g_table, x, y, z);
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		c0_b = CLU(b_table, x, y, z);
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if( rx >= ry ) {
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (ry >= rz) { //rx >= ry && ry >= rz
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_r = CLU(r_table, x_n, y, z) - c0_r;
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_g = CLU(g_table, x_n, y, z) - c0_g;
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_b = CLU(b_table, x_n, y, z) - c0_b;
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (rx >= rz) { //rx >= rz && rz >= ry
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y, z) - c0_r;
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y, z) - c0_g;
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y, z) - c0_b;
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				} else { //rz > rx && rx >= ry
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x, y, z_n) - c0_r;
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x, y, z_n) - c0_g;
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x, y, z_n) - c0_b;
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				}
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} else {
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (rx >= rz) { //ry > rx && rx >= rz
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_r = CLU(r_table, x, y_n, z) - c0_r;
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_g = CLU(g_table, x, y_n, z) - c0_g;
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c2_b = CLU(b_table, x, y_n, z) - c0_b;
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (ry >= rz) { //ry >= rz && rz > rx
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x, y_n, z) - c0_r;
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x, y_n, z) - c0_g;
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x, y_n, z) - c0_b;
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				} else { //rz > ry && ry > rx
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_r = CLU(r_table, x, y, z_n) - c0_r;
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_g = CLU(g_table, x, y, z_n) - c0_g;
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					c3_b = CLU(b_table, x, y, z_n) - c0_b;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				}
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(clut_r*255.0f);
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(clut_g*255.0f);
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(clut_b*255.0f);
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_rgb_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float (*mat)[4] = transform->matrix;
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_r = *src++;
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_g = *src++;
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_b = *src++;
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_r, out_device_g, out_device_b;
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = transform->input_gamma_table_r[device_r];
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_g = transform->input_gamma_table_g[device_g];
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_b = transform->input_gamma_table_b[device_b];
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_r = clamp_float(out_linear_r);
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_g = clamp_float(out_linear_g);
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_b = clamp_float(out_linear_b);
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_r = lut_interp_linear(out_linear_r,
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_g = lut_interp_linear(out_linear_g,
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_b = lut_interp_linear(out_linear_b,
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(out_device_r*255);
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(out_device_g*255);
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(out_device_b*255);
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_rgba_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int i;
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float (*mat)[4] = transform->matrix;
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_r = *src++;
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_g = *src++;
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_b = *src++;
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char alpha = *src++;
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_device_r, out_device_g, out_device_b;
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = transform->input_gamma_table_r[device_r];
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_g = transform->input_gamma_table_g[device_g];
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_b = transform->input_gamma_table_b[device_b];
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_r = clamp_float(out_linear_r);
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_g = clamp_float(out_linear_g);
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_linear_b = clamp_float(out_linear_b);
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_r = lut_interp_linear(out_linear_r,
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_g = lut_interp_linear(out_linear_g,
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_device_b = lut_interp_linear(out_linear_b,
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(out_device_r*255);
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(out_device_g*255);
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(out_device_b*255);
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[3]     = alpha;
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 4;
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if 0
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void qcms_transform_data_rgb_out_linear(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length, qcms_format_type output_format)
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int r_out = output_format.r;
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	const int b_out = output_format.b;
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int i;
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float (*mat)[4] = transform->matrix;
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i = 0; i < length; i++) {
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_r = *src++;
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_g = *src++;
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		unsigned char device_b = *src++;
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_r = transform->input_gamma_table_r[device_r];
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_g = transform->input_gamma_table_g[device_g];
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float linear_b = transform->input_gamma_table_b[device_b];
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[r_out] = clamp_u8(out_linear_r*255);
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[1]     = clamp_u8(out_linear_g*255);
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest[b_out] = clamp_u8(out_linear_b*255);
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		dest += 3;
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
862a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)/*
863a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * If users create and destroy objects on different threads, even if the same
864a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * objects aren't used on different threads at the same time, we can still run
865a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * in to trouble with refcounts if they aren't atomic.
866a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *
867a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * This can lead to us prematurely deleting the precache if threads get unlucky
868a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * and write the wrong value to the ref count.
869a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) */
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct precache_output *precache_reference(struct precache_output *p)
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
872a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)	qcms_atomic_increment(p->ref_count);
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return p;
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static struct precache_output *precache_create()
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct precache_output *p = malloc(sizeof(struct precache_output));
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (p)
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		p->ref_count = 1;
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return p;
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void precache_release(struct precache_output *p)
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
886a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)	if (qcms_atomic_decrement(p->ref_count) == 0) {
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		free(p);
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef HAVE_POSIX_MEMALIGN
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static qcms_transform *transform_alloc(void)
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	qcms_transform *t;
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!posix_memalign(&t, 16, sizeof(*t))) {
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return t;
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else {
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return NULL;
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void transform_free(qcms_transform *t)
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(t);
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static qcms_transform *transform_alloc(void)
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* transform needs to be aligned on a 16byte boundrary */
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	char *original_block = calloc(sizeof(qcms_transform) + sizeof(void*) + 16, 1);
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* make room for a pointer to the block returned by calloc */
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	void *transform_start = original_block + sizeof(void*);
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* align transform_start */
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	qcms_transform *transform_aligned = (qcms_transform*)(((uintptr_t)transform_start + 15) & ~0xf);
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* store a pointer to the block returned by calloc so that we can free it later */
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	void **(original_block_ptr) = (void**)transform_aligned;
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!original_block)
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return NULL;
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	original_block_ptr--;
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	*original_block_ptr = original_block;
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return transform_aligned;
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void transform_free(qcms_transform *t)
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* get at the pointer to the unaligned block returned by calloc */
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	void **p = (void**)t;
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	p--;
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(*p);
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qcms_transform_release(qcms_transform *t)
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* ensure we only free the gamma tables once even if there are
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	 * multiple references to the same data */
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (t->output_table_r)
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		precache_release(t->output_table_r);
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (t->output_table_g)
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		precache_release(t->output_table_g);
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (t->output_table_b)
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		precache_release(t->output_table_b);
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(t->input_gamma_table_r);
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (t->input_gamma_table_g != t->input_gamma_table_r)
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		free(t->input_gamma_table_g);
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (t->input_gamma_table_g != t->input_gamma_table_r &&
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	    t->input_gamma_table_g != t->input_gamma_table_b)
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		free(t->input_gamma_table_b);
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(t->input_gamma_table_gray);
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(t->output_gamma_lut_r);
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(t->output_gamma_lut_g);
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	free(t->output_gamma_lut_b);
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	transform_free(t);
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef X86
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Determine if we can build with SSE2 (this was partly copied from jmorecfg.h in
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// mozilla/jpeg)
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // -------------------------------------------------------------------------
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(_M_IX86) && defined(_MSC_VER)
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define HAS_CPUID
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   register - I'm not sure if that ever happens on windows, but cpuid isn't
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   on the critical path so we just preserve the register to be safe and to be
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   consistent with the non-windows version. */
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       uint32_t a_, b_, c_, d_;
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       __asm {
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              xchg   ebx, esi
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              mov    eax, fxn
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              cpuid
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              mov    a_, eax
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              mov    b_, ebx
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              mov    c_, ecx
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              mov    d_, edx
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              xchg   ebx, esi
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       }
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       *a = a_;
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       *b = b_;
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       *c = c_;
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       *d = d_;
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif (defined(__GNUC__) || defined(__SUNPRO_C)) && (defined(__i386__) || defined(__i386))
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define HAS_CPUID
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Get us a CPUID function. We can't use ebx because it's the PIC register on
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   some platforms, so we use ESI instead and save ebx to avoid clobbering it. */
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	uint32_t a_, b_, c_, d_;
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       __asm__ __volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;"
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             : "=a" (a_), "=S" (b_), "=c" (c_), "=d" (d_) : "a" (fxn));
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	   *a = a_;
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	   *b = b_;
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	   *c = c_;
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	   *d = d_;
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// -------------------------Runtime SSEx Detection-----------------------------
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* MMX is always supported per
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  Gecko v1.9.1 minimum CPU requirements */
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define SSE1_EDX_MASK (1UL << 25)
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define SSE2_EDX_MASK (1UL << 26)
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define SSE3_ECX_MASK (1UL <<  0)
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static int sse_version_available(void)
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* we know at build time that 64-bit CPUs always have SSE2
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	 * this tells the compiler that non-SSE2 branches will never be
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	 * taken (i.e. OK to optimze away the SSE1 and non-SIMD code */
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return 2;
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(HAS_CPUID)
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	static int sse_version = -1;
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	uint32_t a, b, c, d;
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	uint32_t function = 0x00000001;
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (sse_version == -1) {
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		sse_version = 0;
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		cpuid(function, &a, &b, &c, &d);
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (c & SSE3_ECX_MASK)
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			sse_version = 3;
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		else if (d & SSE2_EDX_MASK)
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			sse_version = 2;
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		else if (d & SSE1_EDX_MASK)
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			sse_version = 1;
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return sse_version;
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return 0;
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const struct matrix bradford_matrix = {{	{ 0.8951f, 0.2664f,-0.1614f},
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						{-0.7502f, 1.7135f, 0.0367f},
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						{ 0.0389f,-0.0685f, 1.0296f}},
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						false};
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const struct matrix bradford_matrix_inv = {{ { 0.9869929f,-0.1470543f, 0.1599627f},
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						    { 0.4323053f, 0.5183603f, 0.0492912f},
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						    {-0.0085287f, 0.0400428f, 0.9684867f}},
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)						    false};
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// See ICCv4 E.3
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct matrix compute_whitepoint_adaption(float X, float Y, float Z) {
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float p = (0.96422f*bradford_matrix.m[0][0] + 1.000f*bradford_matrix.m[1][0] + 0.82521f*bradford_matrix.m[2][0]) /
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		  (X*bradford_matrix.m[0][0]      + Y*bradford_matrix.m[1][0]      + Z*bradford_matrix.m[2][0]     );
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float y = (0.96422f*bradford_matrix.m[0][1] + 1.000f*bradford_matrix.m[1][1] + 0.82521f*bradford_matrix.m[2][1]) /
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		  (X*bradford_matrix.m[0][1]      + Y*bradford_matrix.m[1][1]      + Z*bradford_matrix.m[2][1]     );
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float b = (0.96422f*bradford_matrix.m[0][2] + 1.000f*bradford_matrix.m[1][2] + 0.82521f*bradford_matrix.m[2][2]) /
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		  (X*bradford_matrix.m[0][2]      + Y*bradford_matrix.m[1][2]      + Z*bradford_matrix.m[2][2]     );
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct matrix white_adaption = {{ {p,0,0}, {0,y,0}, {0,0,b}}, false};
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return matrix_multiply( bradford_matrix_inv, matrix_multiply(white_adaption, bradford_matrix) );
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qcms_profile_precache_output_transform(qcms_profile *profile)
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* we only support precaching on rgb profiles */
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (profile->color_space != RGB_SIGNATURE)
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (qcms_supports_iccv4) {
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* don't precache since we will use the B2A LUT */
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (profile->B2A0)
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return;
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* don't precache since we will use the mBA LUT */
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (profile->mBA)
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return;
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* don't precache if we do not have the TRC curves */
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!profile->redTRC || !profile->greenTRC || !profile->blueTRC)
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!profile->output_table_r) {
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		profile->output_table_r = precache_create();
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (profile->output_table_r &&
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				!compute_precache(profile->redTRC, profile->output_table_r->data)) {
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			precache_release(profile->output_table_r);
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			profile->output_table_r = NULL;
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!profile->output_table_g) {
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		profile->output_table_g = precache_create();
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (profile->output_table_g &&
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				!compute_precache(profile->greenTRC, profile->output_table_g->data)) {
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			precache_release(profile->output_table_g);
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			profile->output_table_g = NULL;
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!profile->output_table_b) {
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		profile->output_table_b = precache_create();
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (profile->output_table_b &&
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				!compute_precache(profile->blueTRC, profile->output_table_b->data)) {
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			precache_release(profile->output_table_b);
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			profile->output_table_b = NULL;
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Replace the current transformation with a LUT transformation using a given number of sample points */
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)qcms_transform* qcms_transform_precacheLUT_float(qcms_transform *transform, qcms_profile *in, qcms_profile *out,
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                 int samples, qcms_data_type in_type)
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* The range between which 2 consecutive sample points can be used to interpolate */
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	uint16_t x,y,z;
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	uint32_t l;
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	uint32_t lutSize = 3 * samples * samples * samples;
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* src = NULL;
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* dest = NULL;
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	float* lut = NULL;
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	src = malloc(lutSize*sizeof(float));
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	dest = malloc(lutSize*sizeof(float));
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (src && dest) {
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* Prepare a list of points we want to sample */
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		l = 0;
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		for (x = 0; x < samples; x++) {
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			for (y = 0; y < samples; y++) {
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				for (z = 0; z < samples; z++) {
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					src[l++] = x / (float)(samples-1);
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					src[l++] = y / (float)(samples-1);
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					src[l++] = z / (float)(samples-1);
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				}
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		lut = qcms_chain_transform(in, out, src, dest, lutSize);
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (lut) {
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform->r_clut = &lut[0];
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform->g_clut = &lut[1];
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform->b_clut = &lut[2];
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform->grid_size = samples;
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (in_type == QCMS_DATA_RGBA_8) {
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_tetra_clut_rgba;
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_tetra_clut;
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	//XXX: qcms_modular_transform_data may return either the src or dest buffer. If so it must not be free-ed
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (src && lut != src) {
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		free(src);
1156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch	}
1157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch	if (dest && lut != dest) {
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		free(dest);
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (lut == NULL) {
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return NULL;
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return transform;
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define NO_MEM_TRANSFORM NULL
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)qcms_transform* qcms_transform_create(
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		qcms_profile *in, qcms_data_type in_type,
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		qcms_profile *out, qcms_data_type out_type,
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		qcms_intent intent)
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	bool precache = false;
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        qcms_transform *transform = transform_alloc();
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!transform) {
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return NULL;
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (out_type != QCMS_DATA_RGB_8 &&
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                out_type != QCMS_DATA_RGBA_8) {
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            assert(0 && "output type");
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	    transform_free(transform);
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return NULL;
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (out->output_table_r &&
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			out->output_table_g &&
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			out->output_table_b) {
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		precache = true;
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (qcms_supports_iccv4 && (in->A2B0 || out->B2A0 || in->mAB || out->mAB)) {
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// Precache the transformation to a CLUT 33x33x33 in size.
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// 33 is used by many profiles and works well in pratice.
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// This evenly divides 256 into blocks of 8x8x8.
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// TODO For transforming small data sets of about 200x200 or less
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// precaching should be avoided.
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		qcms_transform *result = qcms_transform_precacheLUT_float(transform, in, out, 33, in_type);
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!result) {
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            		assert(0 && "precacheLUT failed");
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform_free(transform);
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NULL;
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return result;
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (precache) {
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->output_table_r = precache_reference(out->output_table_r);
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->output_table_g = precache_reference(out->output_table_g);
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->output_table_b = precache_reference(out->output_table_b);
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else {
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!out->redTRC || !out->greenTRC || !out->blueTRC) {
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			qcms_transform_release(transform);
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NO_MEM_TRANSFORM;
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		build_output_lut(out->redTRC, &transform->output_gamma_lut_r, &transform->output_gamma_lut_r_length);
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		build_output_lut(out->greenTRC, &transform->output_gamma_lut_g, &transform->output_gamma_lut_g_length);
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, &transform->output_gamma_lut_b_length);
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g || !transform->output_gamma_lut_b) {
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			qcms_transform_release(transform);
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NO_MEM_TRANSFORM;
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (in->color_space == RGB_SIGNATURE) {
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		struct matrix in_matrix, out_matrix, result;
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (in_type != QCMS_DATA_RGB_8 &&
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    in_type != QCMS_DATA_RGBA_8){
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                	assert(0 && "input type");
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform_free(transform);
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                	return NULL;
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            	}
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (precache) {
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(SSE2_ENABLE) && defined(X86)
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		    if (sse_version_available() >= 2) {
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			    if (in_type == QCMS_DATA_RGB_8)
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				    transform->transform_fn = qcms_transform_data_rgb_out_lut_sse2;
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			    else
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				    transform->transform_fn = qcms_transform_data_rgba_out_lut_sse2;
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(SSE2_ENABLE) && !(defined(_MSC_VER) && defined(_M_AMD64))
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    /* Microsoft Compiler for x64 doesn't support MMX.
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     * SSE code uses MMX so that we disable on x64 */
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		    } else
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		    if (sse_version_available() >= 1) {
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			    if (in_type == QCMS_DATA_RGB_8)
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				    transform->transform_fn = qcms_transform_data_rgb_out_lut_sse1;
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			    else
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				    transform->transform_fn = qcms_transform_data_rgba_out_lut_sse1;
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		    } else
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			{
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				if (in_type == QCMS_DATA_RGB_8)
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					transform->transform_fn = qcms_transform_data_rgb_out_lut_precache;
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				else
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)					transform->transform_fn = qcms_transform_data_rgba_out_lut_precache;
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} else {
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (in_type == QCMS_DATA_RGB_8)
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_rgb_out_lut;
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			else
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_rgba_out_lut;
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		//XXX: avoid duplicating tables if we can
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->input_gamma_table_r = build_input_gamma_table(in->redTRC);
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->input_gamma_table_g = build_input_gamma_table(in->greenTRC);
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->input_gamma_table_b = build_input_gamma_table(in->blueTRC);
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!transform->input_gamma_table_r || !transform->input_gamma_table_g || !transform->input_gamma_table_b) {
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			qcms_transform_release(transform);
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NO_MEM_TRANSFORM;
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* build combined colorant matrix */
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		in_matrix = build_colorant_matrix(in);
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_matrix = build_colorant_matrix(out);
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		out_matrix = matrix_invert(out_matrix);
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (out_matrix.invalid) {
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			qcms_transform_release(transform);
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NULL;
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		result = matrix_multiply(out_matrix, in_matrix);
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		/* store the results in column major mode
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		 * this makes doing the multiplication with sse easier */
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[0][0] = result.m[0][0];
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[1][0] = result.m[0][1];
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[2][0] = result.m[0][2];
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[0][1] = result.m[1][0];
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[1][1] = result.m[1][1];
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[2][1] = result.m[1][2];
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[0][2] = result.m[2][0];
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[1][2] = result.m[2][1];
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->matrix[2][2] = result.m[2][2];
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else if (in->color_space == GRAY_SIGNATURE) {
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (in_type != QCMS_DATA_GRAY_8 &&
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				in_type != QCMS_DATA_GRAYA_8){
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			assert(0 && "input type");
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			transform_free(transform);
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NULL;
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform->input_gamma_table_gray = build_input_gamma_table(in->grayTRC);
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!transform->input_gamma_table_gray) {
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			qcms_transform_release(transform);
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			return NO_MEM_TRANSFORM;
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (precache) {
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (in_type == QCMS_DATA_GRAY_8) {
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_gray_out_precache;
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_graya_out_precache;
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		} else {
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (in_type == QCMS_DATA_GRAY_8) {
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_gray_out_lut;
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			} else {
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				transform->transform_fn = qcms_transform_data_graya_out_lut;
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			}
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else {
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		assert(0 && "unexpected colorspace");
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		transform_free(transform);
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return NULL;
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return transform;
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/* __force_align_arg_pointer__ is an x86-only attribute, and gcc/clang warns on unused
13362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * attributes. Don't use this on ARM or AMD64. __has_attribute can detect the presence
13372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * of the attribute but is currently only supported by clang */
13382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(__has_attribute)
13392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define HAS_FORCE_ALIGN_ARG_POINTER __has_attribute(__force_align_arg_pointer__)
1340eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#elif defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) && !defined(__arm__) && !defined(__mips__)
13412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define HAS_FORCE_ALIGN_ARG_POINTER 1
13422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#else
13432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define HAS_FORCE_ALIGN_ARG_POINTER 0
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
13452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
13462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if HAS_FORCE_ALIGN_ARG_POINTER
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__attribute__((__force_align_arg_pointer__))
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length)
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	static const struct _qcms_format_type output_rgbx = { 0, 2 };
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	transform->transform_fn(transform, src, dest, length, output_rgbx);
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qcms_transform_data_type(qcms_transform *transform, void *src, void *dest, size_t length, qcms_output_type type)
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	static const struct _qcms_format_type output_rgbx = { 0, 2 };
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	static const struct _qcms_format_type output_bgrx = { 2, 0 };
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	transform->transform_fn(transform, src, dest, length, type == QCMS_OUTPUT_BGRX ? output_bgrx : output_rgbx);
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)qcms_bool qcms_supports_iccv4;
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void qcms_enable_iccv4()
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	qcms_supports_iccv4 = true;
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1370