11998 lines
282 KiB
Plaintext
11998 lines
282 KiB
Plaintext
(* ImageEn Build 7.0.0.06.2637 @ 7-4-17 14:58:42.679 *)
|
|
//
|
|
// Little cms
|
|
// Copyright (C) 1998-2004 Marti Maria
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the Software
|
|
// is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
// Version 1.13
|
|
|
|
(*
|
|
File version 1001
|
|
*)
|
|
|
|
|
|
unit ielcms;
|
|
|
|
{$I ie.inc}
|
|
|
|
interface
|
|
|
|
{$IFDEF IEINCLUDECMS}
|
|
|
|
uses
|
|
Windows, Classes, SysUtils, imageenproc, hyiedefs;
|
|
|
|
type
|
|
|
|
LCMSHANDLE = pointer;
|
|
cmsHPROFILE = LCMSHANDLE;
|
|
cmsHTRANSFORM = LCMSHANDLE;
|
|
PcmsHTRANSFORM = ^cmsHTRANSFORM;
|
|
|
|
const
|
|
// Pixel types
|
|
|
|
PT_ANY = 0;
|
|
PT_GRAY = 3;
|
|
PT_RGB = 4;
|
|
PT_CMY = 5;
|
|
PT_CMYK = 6;
|
|
PT_YCbCr = 7;
|
|
PT_YUV = 8; // Lu'v'
|
|
PT_XYZ = 9;
|
|
PT_Lab = 10;
|
|
PT_YUVK = 11; // Lu'v'K
|
|
PT_HSV = 12;
|
|
PT_HLS = 13;
|
|
PT_Yxy = 14;
|
|
PT_HiFi = 15;
|
|
PT_HiFi7 = 16;
|
|
PT_HiFi8 = 17;
|
|
|
|
TYPE_GRAY_8 = ((PT_GRAY shl 16) or (1 shl 3) or (1));
|
|
TYPE_GRAY_8_REV = ((PT_GRAY shl 16) or (1 shl 3) or (1) or (1 shl 13));
|
|
TYPE_GRAY_16 = ((PT_GRAY shl 16) or (1 shl 3) or (2));
|
|
TYPE_GRAY_16_REV = ((PT_GRAY shl 16) or (1 shl 3) or (2) or (1 shl 13));
|
|
TYPE_GRAY_16_SE = ((PT_GRAY shl 16) or (1 shl 3) or (2) or (1 shl 11));
|
|
TYPE_GRAYA_8 = ((PT_GRAY shl 16) or (1 shl 7) or (1 shl 3) or (1));
|
|
TYPE_GRAYA_16 = ((PT_GRAY shl 16) or (1 shl 7) or (1 shl 3) or (2));
|
|
TYPE_GRAYA_16_SE = ((PT_GRAY shl 16) or (1 shl 7) or (1 shl 3) or (2) or (1 shl 11));
|
|
TYPE_GRAYA_8_PLANAR = ((PT_GRAY shl 16) or (1 shl 7) or (1 shl 3) or (1) or (1 shl 12));
|
|
TYPE_GRAYA_16_PLANAR = ((PT_GRAY shl 16) or (1 shl 7) or (1 shl 3) or (2) or (1 shl 12));
|
|
|
|
TYPE_RGB_8 = ((PT_RGB shl 16) or (3 shl 3) or (1));
|
|
TYPE_RGB_8_PLANAR = ((PT_RGB shl 16) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_BGR_8 = ((PT_RGB shl 16) or (3 shl 3) or (1) or (1 shl 10));
|
|
TYPE_BGR_8_PLANAR = ((PT_RGB shl 16) or (3 shl 3) or (1) or (1 shl 10) or (1 shl 12));
|
|
TYPE_RGB_16 = ((PT_RGB shl 16) or (3 shl 3) or (2));
|
|
TYPE_RGB_16_PLANAR = ((PT_RGB shl 16) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_RGB_16_SE = ((PT_RGB shl 16) or (3 shl 3) or (2) or (1 shl 11));
|
|
TYPE_BGR_16 = ((PT_RGB shl 16) or (3 shl 3) or (2) or (1 shl 10));
|
|
TYPE_BGR_16_PLANAR = ((PT_RGB shl 16) or (3 shl 3) or (2) or (1 shl 10) or (1 shl 12));
|
|
TYPE_BGR_16_SE = ((PT_RGB shl 16) or (3 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
|
|
TYPE_RGBA_8 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (1));
|
|
TYPE_RGBA_8_PLANAR = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_RGBA_16 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2));
|
|
TYPE_RGBA_16_PLANAR = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_RGBA_16_SE = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_ARGB_8 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (1) or (1 shl 14));
|
|
TYPE_ARGB_16 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 14));
|
|
|
|
TYPE_ABGR_8 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (1) or (1 shl 10));
|
|
TYPE_ABGR_16 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 10));
|
|
TYPE_ABGR_16_PLANAR = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 10) or (1 shl 12));
|
|
TYPE_ABGR_16_SE = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
|
|
TYPE_BGRA_8 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (1) or (1 shl 10) or (1 shl 14));
|
|
TYPE_BGRA_16 = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 10) or (1 shl 14));
|
|
TYPE_BGRA_16_SE = ((PT_RGB shl 16) or (1 shl 7) or (3 shl 3) or (2) or (1 shl 11) or (1 shl 14));
|
|
|
|
TYPE_CMY_8 = ((PT_CMY shl 16) or (3 shl 3) or (1));
|
|
TYPE_CMY_8_PLANAR = ((PT_CMY shl 16) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_CMY_16 = ((PT_CMY shl 16) or (3 shl 3) or (2));
|
|
TYPE_CMY_16_PLANAR = ((PT_CMY shl 16) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_CMY_16_SE = ((PT_CMY shl 16) or (3 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_CMYK_8 = ((PT_CMYK shl 16) or (4 shl 3) or (1));
|
|
TYPE_CMYK_8_REV = ((PT_CMYK shl 16) or (4 shl 3) or (1) or (1 shl 13));
|
|
TYPE_YUVK_8 = TYPE_CMYK_8_REV;
|
|
TYPE_CMYK_8_PLANAR = ((PT_CMYK shl 16) or (4 shl 3) or (1) or (1 shl 12));
|
|
TYPE_CMYK_16 = ((PT_CMYK shl 16) or (4 shl 3) or (2));
|
|
TYPE_CMYK_16_REV = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 13));
|
|
TYPE_YUVK_16 = TYPE_CMYK_16_REV;
|
|
TYPE_CMYK_16_PLANAR = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 12));
|
|
TYPE_CMYK_16_SE = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_KYMC_8 = ((PT_CMYK shl 16) or (4 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC_16 = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC_16_SE = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
|
|
TYPE_KCMY_8 = ((PT_CMYK shl 16) or (4 shl 3) or (1) or (1 shl 14));
|
|
TYPE_KCMY_8_REV = ((PT_CMYK shl 16) or (4 shl 3) or (1) or (1 shl 13) or (1 shl 14));
|
|
TYPE_KCMY_16 = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 14));
|
|
TYPE_KCMY_16_REV = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 13) or (1 shl 14));
|
|
TYPE_KCMY_16_SE = ((PT_CMYK shl 16) or (4 shl 3) or (2) or (1 shl 11) or (1 shl 14));
|
|
|
|
TYPE_CMYK5_8 = ((5 shl 3) or (1));
|
|
TYPE_CMYK5_16 = ((5 shl 3) or (2));
|
|
TYPE_CMYK5_16_SE = ((5 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC5_8 = ((5 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC5_16 = ((5 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC5_16_SE = ((5 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
|
|
TYPE_CMYKcm_8 = ((6 shl 3) or (1));
|
|
TYPE_CMYKcm_8_PLANAR = ((6 shl 3) or (1) or (1 shl 12));
|
|
TYPE_CMYKcm_16 = ((6 shl 3) or (2));
|
|
TYPE_CMYKcm_16_PLANAR = ((6 shl 3) or (2) or (1 shl 12));
|
|
TYPE_CMYKcm_16_SE = ((6 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_CMYK7_8 = ((7 shl 3) or (1));
|
|
TYPE_CMYK7_16 = ((7 shl 3) or (2));
|
|
TYPE_CMYK7_16_SE = ((7 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC7_8 = ((7 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC7_16 = ((7 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC7_16_SE = ((7 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
TYPE_CMYK8_8 = ((8 shl 3) or (1));
|
|
TYPE_CMYK8_16 = ((8 shl 3) or (2));
|
|
TYPE_CMYK8_16_SE = ((8 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC8_8 = ((8 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC8_16 = ((8 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC8_16_SE = ((8 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
TYPE_CMYK9_8 = ((9 shl 3) or (1));
|
|
TYPE_CMYK9_16 = ((9 shl 3) or (2));
|
|
TYPE_CMYK9_16_SE = ((9 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC9_8 = ((9 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC9_16 = ((9 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC9_16_SE = ((9 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
TYPE_CMYK10_8 = ((10 shl 3) or (1));
|
|
TYPE_CMYK10_16 = ((10 shl 3) or (2));
|
|
TYPE_CMYK10_16_SE = ((10 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC10_8 = ((10 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC10_16 = ((10 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC10_16_SE = ((10 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
TYPE_CMYK11_8 = ((11 shl 3) or (1));
|
|
TYPE_CMYK11_16 = ((11 shl 3) or (2));
|
|
TYPE_CMYK11_16_SE = ((11 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC11_8 = ((11 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC11_16 = ((11 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC11_16_SE = ((11 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
TYPE_CMYK12_8 = ((12 shl 3) or (1));
|
|
TYPE_CMYK12_16 = ((12 shl 3) or (2));
|
|
TYPE_CMYK12_16_SE = ((12 shl 3) or (2) or (1 shl 11));
|
|
TYPE_KYMC12_8 = ((12 shl 3) or (1) or (1 shl 10));
|
|
TYPE_KYMC12_16 = ((12 shl 3) or (2) or (1 shl 10));
|
|
TYPE_KYMC12_16_SE = ((12 shl 3) or (2) or (1 shl 10) or (1 shl 11));
|
|
|
|
TYPE_XYZ_16 = ((PT_XYZ shl 16) or (3 shl 3) or (2));
|
|
TYPE_Lab_8 = ((PT_Lab shl 16) or (3 shl 3) or (1));
|
|
TYPE_ALab_8 = ((PT_Lab shl 16) or (3 shl 3) or (1) or (1 shl 7) or (1 shl 10));
|
|
TYPE_Lab_16 = ((PT_Lab shl 16) or (3 shl 3) or (2));
|
|
TYPE_Yxy_16 = ((PT_Yxy shl 16) or (3 shl 3) or (2));
|
|
|
|
TYPE_YCbCr_8 = ((PT_YCbCr shl 16) or (3 shl 3) or (1));
|
|
TYPE_YCbCr_8_PLANAR = ((PT_YCbCr shl 16) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_YCbCr_16 = ((PT_YCbCr shl 16) or (3 shl 3) or (2));
|
|
TYPE_YCbCr_16_PLANAR = ((PT_YCbCr shl 16) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_YCbCr_16_SE = ((PT_YCbCr shl 16) or (3 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_YUV_8 = ((PT_YUV shl 16) or (3 shl 3) or (1));
|
|
TYPE_YUV_8_PLANAR = ((PT_YUV shl 16) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_YUV_16 = ((PT_YUV shl 16) or (3 shl 3) or (2));
|
|
TYPE_YUV_16_PLANAR = ((PT_YUV shl 16) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_YUV_16_SE = ((PT_YUV shl 16) or (3 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_HLS_8 = ((PT_HLS shl 16) or (3 shl 3) or (1));
|
|
TYPE_HLS_8_PLANAR = ((PT_HLS shl 16) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_HLS_16 = ((PT_HLS shl 16) or (3 shl 3) or (2));
|
|
TYPE_HLS_16_PLANAR = ((PT_HLS shl 16) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_HLS_16_SE = ((PT_HLS shl 16) or (3 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_HSV_8 = ((PT_HSV shl 16) or (3 shl 3) or (1));
|
|
TYPE_HSV_8_PLANAR = ((PT_HSV shl 16) or (3 shl 3) or (1) or (1 shl 12));
|
|
TYPE_HSV_16 = ((PT_HSV shl 16) or (3 shl 3) or (2));
|
|
TYPE_HSV_16_PLANAR = ((PT_HSV shl 16) or (3 shl 3) or (2) or (1 shl 12));
|
|
TYPE_HSV_16_SE = ((PT_HSV shl 16) or (3 shl 3) or (2) or (1 shl 11));
|
|
|
|
TYPE_NAMED_COLOR_INDEX = ((1 shl 3) or (2));
|
|
|
|
TYPE_XYZ_DBL = ((PT_XYZ shl 16) or (3 shl 3) or (0));
|
|
TYPE_Lab_DBL = ((PT_Lab shl 16) or (3 shl 3) or (0));
|
|
TYPE_GRAY_DBL = ((PT_GRAY shl 16) or (1 shl 3) or (0));
|
|
TYPE_RGB_DBL = ((PT_RGB shl 16) or (3 shl 3) or (0));
|
|
TYPE_CMYK_DBL = ((PT_CMYK shl 16) or (4 shl 3) or (0));
|
|
|
|
// Intents
|
|
|
|
INTENT_PERCEPTUAL = 0;
|
|
INTENT_RELATIVE_COLORIMETRIC = 1;
|
|
INTENT_SATURATION = 2;
|
|
INTENT_ABSOLUTE_COLORIMETRIC = 3;
|
|
|
|
cmsFLAGS_NOTPRECALC = $0100;
|
|
cmsFLAGS_NULLTRANSFORM = $0200; // Don't transform anyway
|
|
cmsFLAGS_HIGHRESPRECALC = $0400; // Use more memory to give better accurancy
|
|
cmsFLAGS_LOWRESPRECALC = $0800; // Use less memory to minimize resouces
|
|
|
|
function IEcmsOpenProfileFromFile(stream: TStream; save: boolean; save8bit: boolean): cmsHPROFILE;
|
|
function IEcmsCreateTransform(Input: cmsHPROFILE; InputFormat: DWORD; Output: cmsHPROFILE; OutputFormat: DWORD; Intent: integer; dwFlags: DWORD): cmsHTRANSFORM;
|
|
function IEcmsCloseProfile(hProfile: cmsHPROFILE): longbool;
|
|
procedure IEcmsDeleteTransform(hTransform: cmsHTRANSFORM);
|
|
procedure IEcmsDoTransform(Transform: cmsHTRANSFORM; InputBuffer: pointer; OutputBuffer: pointer; Size: dword);
|
|
function IEcmsCreate_sRGBProfile: cmsHPROFILE;
|
|
function IEcmsCreateLabProfile(WhitePoint_x, WhitePoint_y, WhitePoint_Y_: double): cmsHPROFILE;
|
|
function IEcmsCreateLabProfileD50: cmsHPROFILE;
|
|
function IEcmsCreateXYZProfile: cmsHPROFILE;
|
|
function IEcmsWhitePointFromTemp(TempK: integer; var WhitePoint_x, WhitePoint_y, WhitePoint_Y_: double): boolean;
|
|
|
|
implementation
|
|
|
|
uses
|
|
math, hyieutils;
|
|
|
|
{$WARNINGS OFF}
|
|
|
|
var
|
|
UsedSpace: integer;
|
|
|
|
const
|
|
MAX_TABLE_TAG = 50;
|
|
|
|
D50X = (0.9642);
|
|
D50Y = (1.0);
|
|
D50Z = (0.8249);
|
|
|
|
icMagicNumber = $61637370; // 'acsp' */
|
|
icVersionNumber = $02100000; // 2.1.0, BCD */
|
|
|
|
icSigChromaticAdaptationTag = $63686164; // 'chad'
|
|
|
|
VX = 0;
|
|
VY = 1;
|
|
VZ = 2;
|
|
|
|
MAXCHANNELS = 16;
|
|
|
|
LUT_V4_OUTPUT_EMULATE_V2 = $10000; // Is a V4 output LUT, emulating V2
|
|
LUT_V4_INPUT_EMULATE_V2 = $20000; // Is a V4 input LUT, emulating V2
|
|
LUT_V2_OUTPUT_EMULATE_V4 = $40000; // Is a V2 output LUT, emulating V4
|
|
LUT_V2_INPUT_EMULATE_V4 = $80000; // Is a V2 input LUT, emulating V4
|
|
|
|
// LUT handling
|
|
|
|
LUT_HASMATRIX = $0001;
|
|
LUT_HASTL1 = $0002;
|
|
LUT_HASTL2 = $0008;
|
|
LUT_HAS3DGRID = $0010;
|
|
|
|
LUT_HASMATRIX3 = $0020; // Matrix + offset for LutAToB
|
|
LUT_HASMATRIX4 = $0040; // Matrix + offset for LutBToA
|
|
|
|
LUT_HASTL3 = $0100; // '3' curves for LutAToB
|
|
LUT_HASTL4 = $0200; // '4' curves for LutBToA
|
|
|
|
AlarmR: WORD = $8FFF;
|
|
AlarmG: WORD = $8FFF;
|
|
AlarmB: WORD = $8FFF;
|
|
|
|
icSigLuvKData = $4C75764B; // 'LuvK'
|
|
icSigHexachromeData = $4D434836; // MCH6
|
|
icSigHeptachromeData = $4D434837; // MCH7
|
|
icSigOctachromeData = $4D434838; // MCH8
|
|
|
|
icSiglutAtoBType = $6D414220; // mAB
|
|
icSiglutBtoAType = $6D424120; // mBA
|
|
|
|
icSigParametricCurveType = $70617261; // parametric (ICC 4.0)
|
|
|
|
cmsFLAGS_NOTCACHE = $0040; // Inhibit 1-pixel cache
|
|
|
|
XYZRel = 0;
|
|
LabRel = 1;
|
|
|
|
cmsFLAGS_GAMUTCHECK = $1000; // Out of Gamut alarm
|
|
cmsFLAGS_SOFTPROOFING = $4000; // Do softproofing
|
|
|
|
icSigChromaticityTag = $6368726D; // As per Addendum 2 to Spec. ICC.1:1998-09
|
|
|
|
lcmsSignature = $6C636D73;
|
|
|
|
icSigChromaticityType = $6368726D;
|
|
|
|
SAMPLER_HASTL1 = LUT_HASTL1;
|
|
|
|
SAMPLER_HASTL2 = LUT_HASTL2;
|
|
|
|
SAMPLER_INSPECT = $01000000;
|
|
|
|
ERR_THERESHOLD = 5;
|
|
|
|
cmsFLAGS_MATRIXINPUT = $0001;
|
|
cmsFLAGS_MATRIXOUTPUT = $0002;
|
|
MATSHAPER_INPUT = $0004;
|
|
|
|
MATSHAPER_OUTPUT = $0008;
|
|
MATSHAPER_HASINPSHAPER = $0010;
|
|
|
|
MATSHAPER_ALLSMELTED = (MATSHAPER_INPUT or MATSHAPER_OUTPUT);
|
|
|
|
MATSHAPER_HASMATRIX = $0001;
|
|
|
|
MATSHAPER_HASSHAPER = $0002;
|
|
|
|
icReflective = $00000000; // Bit pos 0 */
|
|
icTransparency = $00000001; // Bit pos 0 */
|
|
icGlossy = $00000000; // Bit pos 1 */
|
|
icMatte = $00000002; // Bit pos 1 */
|
|
|
|
LCMS_BPFLAGS_D50_ADAPTED = $0001;
|
|
|
|
PERCEPTUAL_BLACK_X = (0.00336);
|
|
PERCEPTUAL_BLACK_Y = (0.0034731);
|
|
PERCEPTUAL_BLACK_Z = (0.00287);
|
|
|
|
LCMS_USED_AS_INPUT = 0;
|
|
LCMS_USED_AS_OUTPUT = 1;
|
|
LCMS_USED_AS_PROOF = 2;
|
|
|
|
cmsFLAGS_WHITEBLACKCOMPENSATION = $2000;
|
|
cmsFLAGS_BLACKPOINTCOMPENSATION = cmsFLAGS_WHITEBLACKCOMPENSATION;
|
|
|
|
cmsFLAGS_NOPRELINEARIZATION = $0010;
|
|
|
|
type
|
|
|
|
icUInt32Number = dword;
|
|
icInt32Number = integer;
|
|
icSignature = integer;
|
|
icUInt16Number = word;
|
|
icInt8Number = shortint;
|
|
icUInt8Number = byte;
|
|
|
|
icUInt64Number = array[0..1] of dword;
|
|
icS15Fixed16Number = icInt32Number;
|
|
|
|
Fixed32 = icInt32Number;
|
|
|
|
type
|
|
icProfileClassSignature = integer;
|
|
const
|
|
icSigInputClass = $73636E72; (* 'scnr' *)
|
|
icSigDisplayClass = $6D6E7472; (* 'mntr' *)
|
|
icSigOutputClass = $70727472; (* 'prtr' *)
|
|
icSigLinkClass = $6C696E6B; (* 'link' *)
|
|
icSigAbstractClass = $61627374; (* 'abst' *)
|
|
icSigColorSpaceClass = $73706163; (* 'spac' *)
|
|
icSigNamedColorClass = $6E6D636C; (* 'nmcl' *)
|
|
icMaxEnumClass = $FFFFFFFF;
|
|
|
|
type
|
|
icColorSpaceSignature = integer;
|
|
const
|
|
icSigXYZData = $58595A20; (* 'XYZ ' *)
|
|
icSigLabData = $4C616220; (* 'Lab ' *)
|
|
icSigLuvData = $4C757620; (* 'Luv ' *)
|
|
icSigYCbCrData = $59436272; (* 'YCbr' *)
|
|
icSigYxyData = $59787920; (* 'Yxy ' *)
|
|
icSigRgbData = $52474220; (* 'RGB ' *)
|
|
icSigGrayData = $47524159; (* 'GRAY' *)
|
|
icSigHsvData = $48535620; (* 'HSV ' *)
|
|
icSigHlsData = $484C5320; (* 'HLS ' *)
|
|
icSigCmykData = $434D594B; (* 'CMYK' *)
|
|
icSigCmyData = $434D5920; (* 'CMY ' *)
|
|
icSig2colorData = $32434C52; (* '2CLR' *)
|
|
icSig3colorData = $33434C52; (* '3CLR' *)
|
|
icSig4colorData = $34434C52; (* '4CLR' *)
|
|
icSig5colorData = $35434C52; (* '5CLR' *)
|
|
icSig6colorData = $36434C52; (* '6CLR' *)
|
|
icSig7colorData = $37434C52; (* '7CLR' *)
|
|
icSig8colorData = $38434C52; (* '8CLR' *)
|
|
icSig9colorData = $39434C52; (* '9CLR' *)
|
|
icSig10colorData = $41434C52; (* 'ACLR' *)
|
|
icSig11colorData = $42434C52; (* 'BCLR' *)
|
|
icSig12colorData = $43434C52; (* 'CCLR' *)
|
|
icSig13colorData = $44434C52; (* 'DCLR' *)
|
|
icSig14colorData = $45434C52; (* 'ECLR' *)
|
|
icSig15colorData = $46434C52; (* 'FCLR' *)
|
|
icMaxEnumData = $FFFFFFFF;
|
|
|
|
type
|
|
icTagSignature = integer;
|
|
const
|
|
icSigAToB0Tag = $41324230; (* 'A2B0' *)
|
|
icSigAToB1Tag = $41324231; (* 'A2B1' *)
|
|
icSigAToB2Tag = $41324232; (* 'A2B2' *)
|
|
icSigBlueColorantTag = $6258595A; (* 'bXYZ' *)
|
|
icSigBlueTRCTag = $62545243; (* 'bTRC' *)
|
|
icSigBToA0Tag = $42324130; (* 'B2A0' *)
|
|
icSigBToA1Tag = $42324131; (* 'B2A1' *)
|
|
icSigBToA2Tag = $42324132; (* 'B2A2' *)
|
|
icSigCalibrationDateTimeTag = $63616C74; (* 'calt' *)
|
|
icSigCharTargetTag = $74617267; (* 'targ' *)
|
|
icSigCopyrightTag = $63707274; (* 'cprt' *)
|
|
icSigCrdInfoTag = $63726469; (* 'crdi' *)
|
|
icSigDeviceMfgDescTag = $646D6E64; (* 'dmnd' *)
|
|
icSigDeviceModelDescTag = $646D6464; (* 'dmdd' *)
|
|
icSigGamutTag = $67616D74; (* 'gamt ' *)
|
|
icSigGrayTRCTag = $6B545243; (* 'kTRC' *)
|
|
icSigGreenColorantTag = $6758595A; (* 'gXYZ' *)
|
|
icSigGreenTRCTag = $67545243; (* 'gTRC' *)
|
|
icSigLuminanceTag = $6C756D69; (* 'lumi' *)
|
|
icSigMeasurementTag = $6D656173; (* 'meas' *)
|
|
icSigMediaBlackPointTag = $626B7074; (* 'bkpt' *)
|
|
icSigMediaWhitePointTag = $77747074; (* 'wtpt' *)
|
|
icSigNamedColorTag = $6E636F6C; (* 'ncol'
|
|
* OBSOLETE, use ncl2 *)
|
|
icSigNamedColor2Tag = $6E636C32; (* 'ncl2' *)
|
|
icSigPreview0Tag = $70726530; (* 'pre0' *)
|
|
icSigPreview1Tag = $70726531; (* 'pre1' *)
|
|
icSigPreview2Tag = $70726532; (* 'pre2' *)
|
|
icSigProfileDescriptionTag = $64657363; (* 'desc' *)
|
|
icSigProfileSequenceDescTag = $70736571; (* 'pseq' *)
|
|
icSigPs2CRD0Tag = $70736430; (* 'psd0' *)
|
|
icSigPs2CRD1Tag = $70736431; (* 'psd1' *)
|
|
icSigPs2CRD2Tag = $70736432; (* 'psd2' *)
|
|
icSigPs2CRD3Tag = $70736433; (* 'psd3' *)
|
|
icSigPs2CSATag = $70733273; (* 'ps2s' *)
|
|
icSigPs2RenderingIntentTag = $70733269; (* 'ps2i' *)
|
|
icSigRedColorantTag = $7258595A; (* 'rXYZ' *)
|
|
icSigRedTRCTag = $72545243; (* 'rTRC' *)
|
|
icSigScreeningDescTag = $73637264; (* 'scrd' *)
|
|
icSigScreeningTag = $7363726E; (* 'scrn' *)
|
|
icSigTechnologyTag = $74656368; (* 'tech' *)
|
|
icSigUcrBgTag = $62666420; (* 'bfd ' *)
|
|
icSigViewingCondDescTag = $76756564; (* 'vued' *)
|
|
icSigViewingConditionsTag = $76696577; (* 'view' *)
|
|
icMaxEnumTag = $FFFFFFFF;
|
|
|
|
type
|
|
PicTagSignature = ^icTagSignature;
|
|
|
|
icRenderingIntent = integer;
|
|
const
|
|
icPerceptual = 0;
|
|
icRelativeColorimetric = 1;
|
|
icSaturation = 2;
|
|
icAbsoluteColorimetric = 3;
|
|
icMaxEnumIntent = $FFFFFFFF;
|
|
|
|
type
|
|
icPlatformSignature = integer;
|
|
|
|
const
|
|
icSigMacintosh = $4150504C; // 'APPL' */
|
|
icSigMicrosoft = $4D534654; // 'MSFT' */
|
|
icSigSolaris = $53554E57; // 'SUNW' */
|
|
icSigSGI = $53474920; // 'SGI ' */
|
|
icSigTaligent = $54474E54; // 'TGNT' */
|
|
icMaxEnumPlatform = $FFFFFFFF;
|
|
|
|
type
|
|
icTagTypeSignature = integer;
|
|
const
|
|
icSigCurveType = $63757276; // 'curv' */
|
|
icSigDataType = $64617461; // 'data' */
|
|
icSigDateTimeType = $6474696D; // 'dtim' */
|
|
icSigLut16Type = $6D667432; // 'mft2' */
|
|
icSigLut8Type = $6D667431; // 'mft1' */
|
|
icSigMeasurementType = $6D656173; // 'meas' */
|
|
icSigNamedColorType = $6E636F6C; // 'ncol'
|
|
//* OBSOLETE; use ncl2 */
|
|
icSigProfileSequenceDescType = $70736571; // 'pseq' */
|
|
icSigS15Fixed16ArrayType = $73663332; // 'sf32' */
|
|
icSigScreeningType = $7363726E; // 'scrn' */
|
|
icSigSignatureType = $73696720; // 'sig ' */
|
|
icSigTextType = $74657874; // 'text' */
|
|
icSigTextDescriptionType = $64657363; // 'desc' */
|
|
icSigU16Fixed16ArrayType = $75663332; // 'uf32' */
|
|
icSigUcrBgType = $62666420; // 'bfd ' */
|
|
icSigUInt16ArrayType = $75693136; // 'ui16' */
|
|
icSigUInt32ArrayType = $75693332; // 'ui32' */
|
|
icSigUInt64ArrayType = $75693634; // 'ui64' */
|
|
icSigUInt8ArrayType = $75693038; // 'ui08' */
|
|
icSigViewingConditionsType = $76696577; // 'view' */
|
|
icSigXYZType = $58595A20; // 'XYZ ' */
|
|
icSigXYZArrayType = $58595A20; // 'XYZ ' */
|
|
icSigNamedColor2Type = $6E636C32; // 'ncl2' */
|
|
icSigCrdInfoType = $63726469; // 'crdi' */
|
|
icMaxEnumType = $FFFFFFFF;
|
|
|
|
// technology signature descriptions */
|
|
type
|
|
icTechnologySignature = integer;
|
|
const
|
|
icSigDigitalCamera = $6463616D; // 'dcam' */
|
|
icSigFilmScanner = $6673636E; // 'fscn' */
|
|
icSigReflectiveScanner = $7273636E; // 'rscn' */
|
|
icSigInkJetPrinter = $696A6574; // 'ijet' */
|
|
icSigThermalWaxPrinter = $74776178; // 'twax' */
|
|
icSigElectrophotographicPrinter = $6570686F; // 'epho' */
|
|
icSigElectrostaticPrinter = $65737461; // 'esta' */
|
|
icSigDyeSublimationPrinter = $64737562; // 'dsub' */
|
|
icSigPhotographicPaperPrinter = $7270686F; // 'rpho' */
|
|
icSigFilmWriter = $6670726E; // 'fprn' */
|
|
icSigVideoMonitor = $7669646D; // 'vidm' */
|
|
icSigVideoCamera = $76696463; // 'vidc' */
|
|
icSigProjectionTelevision = $706A7476; // 'pjtv' */
|
|
icSigCRTDisplay = $43525420; // 'CRT ' */
|
|
icSigPMDisplay = $504D4420; // 'PMD ' */
|
|
icSigAMDisplay = $414D4420; // 'AMD ' */
|
|
icSigPhotoCD = $4B504344; // 'KPCD' */
|
|
icSigPhotoImageSetter = $696D6773; // 'imgs' */
|
|
icSigGravure = $67726176; // 'grav' */
|
|
icSigOffsetLithography = $6F666673; // 'offs' */
|
|
icSigSilkscreen = $73696C6B; // 'silk' */
|
|
icSigFlexography = $666C6578; // 'flex' */
|
|
icMaxEnumTechnology = $FFFFFFFF;
|
|
|
|
type
|
|
cmsCIEXYZ = packed record
|
|
X: double;
|
|
Y: double;
|
|
Z: double;
|
|
end;
|
|
LPcmsCIEXYZ = ^cmsCIEXYZ;
|
|
|
|
VEC3 = packed record
|
|
n: array[0..2] of double;
|
|
end;
|
|
|
|
LPVEC3 = ^VEC3;
|
|
|
|
MAT3 = packed record
|
|
v: array[0..2] of VEC3;
|
|
end;
|
|
|
|
LPMAT3 = ^MAT3;
|
|
|
|
icTag = packed record
|
|
sig: icTagSignature;
|
|
offset: icUInt32Number;
|
|
size: icUInt32Number;
|
|
end;
|
|
|
|
icDateTimeNumber = packed record
|
|
year: icUInt16Number;
|
|
month: icUInt16Number;
|
|
day: icUInt16Number;
|
|
hours: icUInt16Number;
|
|
minutes: icUInt16Number;
|
|
seconds: icUInt16Number;
|
|
end;
|
|
PicDateTimeNumber = ^icDateTimeNumber;
|
|
|
|
icXYZNumber = packed record
|
|
X: icS15Fixed16Number;
|
|
Y: icS15Fixed16Number;
|
|
Z: icS15Fixed16Number;
|
|
end;
|
|
|
|
icHeader = packed record
|
|
size: icUInt32Number;
|
|
cmmId: icSignature;
|
|
version: icUInt32Number;
|
|
deviceClass: icProfileClassSignature;
|
|
colorSpace: icColorSpaceSignature;
|
|
pcs: icColorSpaceSignature;
|
|
date: icDateTimeNumber;
|
|
magic: icSignature;
|
|
xplatform: icPlatformSignature;
|
|
flags: icUInt32Number;
|
|
manufacturer: icSignature;
|
|
model: icUInt32Number;
|
|
attributes: icUInt64Number;
|
|
renderingIntent: icUInt32Number;
|
|
illuminant: icXYZNumber;
|
|
creator: icSignature;
|
|
reserved: array[0..43] of icInt8Number;
|
|
end;
|
|
|
|
TReadFunction = function(buffer: pointer; size: integer; count: integer; stream: TStream): integer;
|
|
TSeekFunction = function(stream: TStream; offset: int64): longbool;
|
|
TCloseFunction = function(stream: TStream): longbool;
|
|
TTellFunction = function(stream: TStream): int64;
|
|
TWriteFunction = function(stream: TStream; size: integer; Ptr: pointer): longbool;
|
|
|
|
LCMSICCPROFILE = packed record
|
|
stream: TStream;
|
|
DeviceClass: icProfileClassSignature;
|
|
ColorSpace: icColorSpaceSignature;
|
|
PCS: icColorSpaceSignature;
|
|
RenderingIntent: icRenderingIntent;
|
|
flags: icUInt32Number;
|
|
Illuminant: cmsCIEXYZ;
|
|
Version: icUInt32Number;
|
|
ChromaticAdaptation: MAT3;
|
|
MediaWhitePoint: cmsCIEXYZ;
|
|
MediaBlackPoint: cmsCIEXYZ;
|
|
ProfileID: array[0..15] of BYTE;
|
|
TagCount: icInt32Number;
|
|
TagNames: array[0..MAX_TABLE_TAG - 1] of icTagSignature;
|
|
TagSizes: array[0..MAX_TABLE_TAG - 1] of integer;
|
|
TagOffsets: array[0..MAX_TABLE_TAG - 1] of integer;
|
|
TagPtrs: array[0..MAX_TABLE_TAG - 1] of pointer;
|
|
PhysicalFile: TStream;
|
|
IsWrite: longbool;
|
|
SaveAs8Bits: longbool;
|
|
Read: TReadFunction;
|
|
Seek: TSeekFunction;
|
|
Close: TCloseFunction;
|
|
Tell: TTellFunction;
|
|
Write: TWriteFunction;
|
|
end;
|
|
LPLCMSICCPROFILE = ^LCMSICCPROFILE;
|
|
|
|
icTagBase = packed record
|
|
sig: icTagTypeSignature; // Signature */
|
|
reserved: array[0..3] of icInt8Number; // Reserved, set to 0 */
|
|
end;
|
|
|
|
WVEC3 = packed record
|
|
n: array[0..2] of Fixed32;
|
|
end;
|
|
LPWVEC3 = ^WVEC3;
|
|
|
|
WMAT3 = packed record
|
|
v: array[0..2] of WVEC3;
|
|
end;
|
|
LPWMAT3 = ^WMAT3;
|
|
|
|
_LPcmsTRANSFORM = ^_cmsTRANSFORM;
|
|
|
|
_cmsCOLORCALLBACKFN = procedure(Transform: _LPcmsTRANSFORM;
|
|
InputBuffer: pointer;
|
|
OutputBuffer: pointer; Size: dword);
|
|
|
|
_cmsFIXFN = function(info: _LPcmsTRANSFORM; ToUnroll: pwordarray; Buffer: pbyte): pbyte;
|
|
|
|
_cmsTRANSFN = procedure(Transform: _LPcmsTRANSFORM; xIn: pwordarray; xOut: pwordarray);
|
|
|
|
_cmsADJFN = procedure(xIn: pwordarray; xOut: pwordarray; m: LPWMAT3; b: LPWVEC3);
|
|
|
|
LPLUT = ^LUT;
|
|
|
|
LPL16PARAMS = ^L16PARAMS;
|
|
|
|
_cms3DLERP = procedure(Input: pwordarray; Output: pwordarray; LutTable: pwordarray; p: LPL16PARAMS);
|
|
|
|
//_lcms_l8opt_struc
|
|
L8PARAMS = packed record
|
|
X0, Y0, Z0: array[0..255] of dword;
|
|
rx, ry, rz: array[0..255] of word;
|
|
end;
|
|
|
|
LPL8PARAMS = ^L8PARAMS;
|
|
|
|
// _lcms_l16params_struc
|
|
L16PARAMS = packed record
|
|
|
|
nSamples: integer;
|
|
nInputs: integer;
|
|
nOutputs: integer;
|
|
|
|
Domain: WORD;
|
|
|
|
opta1, opta2: integer;
|
|
opta3, opta4: integer;
|
|
opta5, opta6: integer;
|
|
opta7, opta8: integer;
|
|
|
|
Interp3D: _cms3DLERP;
|
|
|
|
p8: LPL8PARAMS;
|
|
|
|
end;
|
|
|
|
//struct _lcms_LUT_struc
|
|
LUT = packed record
|
|
|
|
wFlags: DWORD;
|
|
Matrix: WMAT3;
|
|
|
|
InputChan: dword;
|
|
OutputChan: dword;
|
|
InputEntries: dword;
|
|
OutputEntries: dword;
|
|
cLutPoints: dword;
|
|
|
|
L1: array[0..MAXCHANNELS - 1] of PWORD;
|
|
L2: array[0..MAXCHANNELS - 1] of PWORD;
|
|
|
|
T: PWORD;
|
|
Tsize: integer;
|
|
|
|
In16params: L16PARAMS;
|
|
Out16params: L16PARAMS;
|
|
CLut16params: L16PARAMS;
|
|
|
|
Intent: integer;
|
|
|
|
Mat3: WMAT3;
|
|
Ofs3: WVEC3;
|
|
L3: array[0..MAXCHANNELS - 1] of PWORD;
|
|
L3params: L16PARAMS;
|
|
L3Entries: dword;
|
|
|
|
Mat4: WMAT3;
|
|
Ofs4: WVEC3;
|
|
L4: array[0..MAXCHANNELS - 1] of PWORD;
|
|
L4params: L16PARAMS;
|
|
L4Entries: dword;
|
|
end;
|
|
|
|
MATSHAPER = packed record
|
|
dwFlags: DWORD;
|
|
|
|
Matrix: WMAT3;
|
|
|
|
p16: L16PARAMS;
|
|
L: array[0..2] of PWORD;
|
|
|
|
p2_16: L16PARAMS;
|
|
L2: array[0..2] of PWORD;
|
|
end;
|
|
|
|
LPMATSHAPER = ^MATSHAPER;
|
|
|
|
cmsNAMEDCOLOR = packed record
|
|
Name: array[0..MAX_PATH - 1] of AnsiChar;
|
|
PCS: array[0..2] of WORD;
|
|
DeviceColorant: array[0..MAXCHANNELS - 1] of WORD;
|
|
end;
|
|
LPcmsNAMEDCOLOR = ^cmsNAMEDCOLOR;
|
|
|
|
cmsNAMEDCOLORLIST = packed record
|
|
nColors: integer;
|
|
Allocated: integer;
|
|
ColorantCount: integer;
|
|
Prefix: array[0..32] of AnsiChar;
|
|
Suffix: array[0..32] of AnsiChar;
|
|
List: array[0..0] of cmsNAMEDCOLOR;
|
|
end;
|
|
|
|
LPcmsNAMEDCOLORLIST = ^cmsNAMEDCOLORLIST;
|
|
|
|
// Transformation
|
|
_cmsTRANSFORM = packed record
|
|
|
|
InputFormat, OutputFormat: DWORD;
|
|
|
|
StrideIn, StrideOut: DWORD;
|
|
|
|
Intent, ProofIntent: integer;
|
|
DoGamutCheck: integer;
|
|
|
|
InputProfile: cmsHPROFILE;
|
|
OutputProfile: cmsHPROFILE;
|
|
PreviewProfile: cmsHPROFILE;
|
|
|
|
EntryColorSpace: icColorSpaceSignature;
|
|
ExitColorSpace: icColorSpaceSignature;
|
|
|
|
m1, m2: WMAT3;
|
|
of1, of2: WVEC3;
|
|
|
|
xform: _cmsCOLORCALLBACKFN;
|
|
|
|
FromInput: _cmsFIXFN;
|
|
FromDevice: _cmsTRANSFN;
|
|
Stage1: _cmsADJFN;
|
|
Stage2: _cmsADJFN;
|
|
ToDevice: _cmsTRANSFN;
|
|
ToOutput: _cmsFIXFN;
|
|
|
|
Device2PCS: LPLUT;
|
|
PCS2Device: LPLUT;
|
|
Gamut: LPLUT;
|
|
Preview: LPLUT;
|
|
|
|
DeviceLink: LPLUT;
|
|
|
|
InMatShaper: LPMATSHAPER;
|
|
OutMatShaper: LPMATSHAPER;
|
|
SmeltMatShaper: LPMATSHAPER;
|
|
|
|
Phase1, Phase2, Phase3: integer;
|
|
|
|
NamedColorList: LPcmsNAMEDCOLORLIST;
|
|
|
|
lInputV4Lab, lOutputV4Lab: longbool;
|
|
|
|
CacheIn: array[0..MAXCHANNELS - 1] of word;
|
|
CacheOut: array[0..MAXCHANNELS - 1] of word;
|
|
|
|
end;
|
|
|
|
_cmstransform_struct = _cmsTRANSFORM;
|
|
P_cmstransform_struct = ^_cmstransform_struct;
|
|
|
|
cmsCIELab = packed record
|
|
|
|
L: double;
|
|
a: double;
|
|
b: double;
|
|
|
|
end;
|
|
|
|
LPcmsCIELab = ^cmsCIELab;
|
|
|
|
// lut8, input & output tables are always 256 bytes in length */
|
|
icLut8 = packed record
|
|
inputChan: icUInt8Number; // Num of input channels */
|
|
outputChan: icUInt8Number; // Num of output channels */
|
|
clutPoints: icUInt8Number; // Num of grid points */
|
|
pad: icInt8Number;
|
|
e00: icS15Fixed16Number; // e00 in the 3 * 3 */
|
|
e01: icS15Fixed16Number; // e01 in the 3 * 3 */
|
|
e02: icS15Fixed16Number; // e02 in the 3 * 3 */
|
|
e10: icS15Fixed16Number; // e10 in the 3 * 3 */
|
|
e11: icS15Fixed16Number; // e11 in the 3 * 3 */
|
|
e12: icS15Fixed16Number; // e12 in the 3 * 3 */
|
|
e20: icS15Fixed16Number; // e20 in the 3 * 3 */
|
|
e21: icS15Fixed16Number; // e21 in the 3 * 3 */
|
|
e22: icS15Fixed16Number; // e22 in the 3 * 3 */
|
|
data: array[0..0] of icUInt8Number; // Data follows see spec */
|
|
(*
|
|
* Data that follows is of this form
|
|
*
|
|
* icUInt8Number inputTable[inputChan][256]; * The in-table
|
|
* icUInt8Number clutTable[icAny]; * The clut
|
|
* icUInt8Number outputTable[outputChan][256]; * The out-table
|
|
*)
|
|
end;
|
|
|
|
_cmsTestAlign8 = packed record
|
|
a: icS15Fixed16Number;
|
|
b: icUInt8Number;
|
|
end;
|
|
|
|
// lut16 */
|
|
icLut16 = packed record
|
|
inputChan: icUInt8Number; // Number of input channels */
|
|
outputChan: icUInt8Number; // Number of output channels */
|
|
clutPoints: icUInt8Number; // Number of grid points */
|
|
pad: icInt8Number; // Padding for byte alignment */
|
|
e00: icS15Fixed16Number; // e00 in the 3 * 3 */
|
|
e01: icS15Fixed16Number; // e01 in the 3 * 3 */
|
|
e02: icS15Fixed16Number; // e02 in the 3 * 3 */
|
|
e10: icS15Fixed16Number; // e10 in the 3 * 3 */
|
|
e11: icS15Fixed16Number; // e11 in the 3 * 3 */
|
|
e12: icS15Fixed16Number; // e12 in the 3 * 3 */
|
|
e20: icS15Fixed16Number; // e20 in the 3 * 3 */
|
|
e21: icS15Fixed16Number; // e21 in the 3 * 3 */
|
|
e22: icS15Fixed16Number; // e22 in the 3 * 3 */
|
|
inputEnt: icUInt16Number; // Num of in-table entries */
|
|
outputEnt: icUInt16Number; // Num of out-table entries */
|
|
data: array[0..0] of icUInt16Number; // Data follows see spec */
|
|
(*
|
|
* Data that follows is of this form
|
|
*
|
|
* icUInt16Number inputTable[inputChan][icAny]; * The in-table
|
|
* icUInt16Number clutTable[icAny]; * The clut
|
|
* icUInt16Number outputTable[outputChan][icAny]; * The out-table
|
|
*)
|
|
end;
|
|
|
|
_cmsTestAlign16 = packed record
|
|
a: icS15Fixed16Number;
|
|
b: icUInt16Number;
|
|
end;
|
|
|
|
// icLutAtoB
|
|
icLutAtoB = packed record
|
|
inputChan: icUInt8Number; // Number of input channels
|
|
outputChan: icUInt8Number; // Number of output channels
|
|
pad1: icUInt8Number;
|
|
pad2: icUInt8Number;
|
|
offsetB: icUInt32Number; // Offset to first "B" curve
|
|
offsetMat: icUInt32Number; // Offset to matrix
|
|
offsetM: icUInt32Number; // Offset to first "M" curve
|
|
offsetC: icUInt32Number; // Offset to CLUT
|
|
offsetA: icUInt32Number; // Offset to first "A" curve
|
|
//icUInt8Number data[icAny]; Data follows see spec for size */
|
|
end;
|
|
|
|
GAMMATABLE = packed record
|
|
|
|
nEntries: integer;
|
|
GammaTable: array[0..0] of WORD;
|
|
|
|
end;
|
|
|
|
LPGAMMATABLE = ^GAMMATABLE;
|
|
|
|
GAMMATABLEArray = array[0..$EFFFFFF] of LPGAMMATABLE;
|
|
LPGAMMATABLEArray = ^GAMMATABLEArray;
|
|
|
|
TPWORDARRAY = array[0..$EFFFFFF] of pword;
|
|
PPWORDARRAY = ^TPWORDARRAY;
|
|
|
|
icCLutStruct = packed record
|
|
gridPoints: array[0..15] of icUInt8Number; // Number of grid points in each dimension.
|
|
prec: icUInt8Number; // Precision of data elements in bytes.
|
|
pad1: icUInt8Number;
|
|
pad2: icUInt8Number;
|
|
pad3: icUInt8Number;
|
|
//icUInt8Number data[icAny]; Data follows see spec for size */
|
|
end;
|
|
|
|
// icLutBtoA
|
|
icLutBtoA = packed record
|
|
inputChan: icUInt8Number; // Number of input channels
|
|
outputChan: icUInt8Number; // Number of output channels
|
|
pad1: icUInt8Number;
|
|
pad2: icUInt8Number;
|
|
offsetB: icUInt32Number; // Offset to first "B" curve
|
|
offsetMat: icUInt32Number; // Offset to matrix
|
|
offsetM: icUInt32Number; // Offset to first "M" curve
|
|
offsetC: icUInt32Number; // Offset to CLUT
|
|
offsetA: icUInt32Number; // Offset to first "A" curve
|
|
//icUInt8Number data[icAny]; Data follows see spec for size */
|
|
end;
|
|
|
|
icNamedColor2 = packed record
|
|
vendorFlag: icUInt32Number; // Bottom 16 bits for IC use */
|
|
count: icUInt32Number; // Count of named colors */
|
|
nDeviceCoords: icUInt32Number; // Num of device coordinates */
|
|
prefix: array[0..31] of icInt8Number; // Prefix for each color name */
|
|
suffix: array[0..31] of icInt8Number; // Suffix for each color name */
|
|
data: array[0..0] of icInt8Number; // Named color data follows */
|
|
end;
|
|
PicNamedColor2 = ^icNamedColor2;
|
|
|
|
GAMUTCHAIN = packed record
|
|
|
|
hForward, hReverse: cmsHTRANSFORM;
|
|
end;
|
|
|
|
LPGAMUTCHAIN = ^GAMUTCHAIN;
|
|
|
|
cmsCIExyY = packed record
|
|
|
|
x: double;
|
|
y_mi: double;
|
|
Y_ma: double;
|
|
|
|
end;
|
|
|
|
LPcmsCIExyY = ^cmsCIExyY;
|
|
|
|
cmsCIExyYTRIPLE = packed record
|
|
|
|
Red: cmsCIExyY;
|
|
Green: cmsCIExyY;
|
|
Blue: cmsCIExyY;
|
|
|
|
end;
|
|
|
|
LPcmsCIExyYTRIPLE = ^cmsCIExyYTRIPLE;
|
|
|
|
cmsCIEXYZTRIPLE = packed record
|
|
|
|
Red: cmsCIEXYZ;
|
|
Green: cmsCIEXYZ;
|
|
Blue: cmsCIEXYZ;
|
|
|
|
end;
|
|
|
|
LPcmsCIEXYZTRIPLE = ^cmsCIEXYZTRIPLE;
|
|
|
|
cmsPSEQDESC = packed record
|
|
|
|
deviceMfg: icSignature;
|
|
deviceModel: icSignature;
|
|
attributes: array[0..1] of icUInt32Number;
|
|
technology: icTechnologySignature;
|
|
|
|
Manufacturer: array[0..511] of AnsiChar;
|
|
Model: array[0..511] of AnsiChar;
|
|
|
|
end;
|
|
|
|
LPcmsPSEQDESC = ^cmsPSEQDESC;
|
|
|
|
cmsSEQ = packed record
|
|
|
|
n: integer;
|
|
seq: array[0..0] of cmsPSEQDESC;
|
|
|
|
end;
|
|
LPcmsSEQ = ^cmsSEQ;
|
|
|
|
// Profile sequence structure */
|
|
icDescStruct = packed record
|
|
deviceMfg: icSignature; // Dev Manufacturer */
|
|
deviceModel: icSignature; // Dev Model */
|
|
attributes: icUInt64Number; // Dev attributes */
|
|
technology: icTechnologySignature; // Technology sig */
|
|
data: array[0..0] of icInt8Number; // Desc text follows */
|
|
end;
|
|
|
|
_cmsSAMPLER = function(xIn: pwordarray; xOut: pwordarray; Cargo: pointer): integer;
|
|
|
|
const
|
|
|
|
Device2PCS: array[0..3] of icTagSignature = (icSigAToB0Tag, // Perceptual
|
|
icSigAToB1Tag, // Relative colorimetric
|
|
icSigAToB2Tag, // Saturation
|
|
icSigAToB1Tag); // Absolute colorimetric
|
|
// (Relative/WhitePoint)
|
|
|
|
PCS2Device: array[0..3] of icTagSignature = (icSigBToA0Tag, // Perceptual
|
|
icSigBToA1Tag, // Relative colorimetric
|
|
icSigBToA2Tag, // Saturation
|
|
icSigBToA1Tag); // Absolute colorimetric
|
|
// (Relative/WhitePoint)
|
|
Preview: array[0..3] of icTagSignature = (icSigPreview0Tag,
|
|
icSigPreview1Tag,
|
|
icSigPreview2Tag,
|
|
icSigPreview1Tag);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function DOUBLE_TO_FIXED(x: double): Fixed32; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := round(x * 65536 );
|
|
end;
|
|
|
|
function FIXED_TO_DOUBLE(x: Fixed32): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := x / 65536;
|
|
end;
|
|
|
|
function FixedMul(a: Fixed32; b: Fixed32): Fixed32; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
//result := DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(a) * FIXED_TO_DOUBLE(b));
|
|
result := round(((a / 65536) * (b / 65536)) * 65536 );
|
|
end;
|
|
|
|
procedure DSWAP(var a, b: double); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
var
|
|
tmp: double;
|
|
begin
|
|
tmp := a;
|
|
a := b;
|
|
b := tmp;
|
|
end;
|
|
|
|
function IEFileRead(buffer: pointer; size: integer; count: integer; stream: TStream): integer;
|
|
begin
|
|
result := stream.Read(pbyte(buffer)^, size * count) div size;
|
|
end;
|
|
|
|
function IEFileSeek(stream: TStream; offset: int64): longbool;
|
|
begin
|
|
result := false; // fdv
|
|
stream.Position := offset;
|
|
end;
|
|
|
|
function IEFileClose(stream: TStream): longbool;
|
|
begin
|
|
// nothing to do because we work with streams
|
|
end;
|
|
|
|
function IEFileTell(stream: TStream): int64;
|
|
begin
|
|
result := stream.position;
|
|
end;
|
|
|
|
function IEFileWrite(stream: TStream; size: integer; Ptr: pointer): longbool;
|
|
begin
|
|
result := true; // fdv
|
|
stream.Write(pbyte(Ptr)^, size);
|
|
end;
|
|
|
|
procedure AdjustEndianess16(iepByte: pbytearray);
|
|
var
|
|
tmp: byte;
|
|
begin
|
|
tmp := iepByte[0];
|
|
iepByte[0] := iepByte[1];
|
|
iepByte[1] := tmp;
|
|
end;
|
|
|
|
procedure AdjustEndianess32(iepByte: pbyte);
|
|
var
|
|
temp1: byte;
|
|
temp2: byte;
|
|
tp: pbyte;
|
|
begin
|
|
temp1 := iepByte^;
|
|
inc(iepByte);
|
|
temp2 := iepByte^;
|
|
inc(iepByte);
|
|
tp := iepByte;
|
|
dec(tp);
|
|
tp^ := iepByte^;
|
|
iepByte^ := temp2;
|
|
inc(iepByte);
|
|
tp := iepByte;
|
|
dec(tp, 3);
|
|
tp^ := iepByte^;
|
|
iepByte^ := temp1;
|
|
end;
|
|
|
|
(*
|
|
procedure AdjustEndianess32(iepByte: pbyte);
|
|
begin
|
|
pinteger(iepByte)^ := IESwapDWord(pinteger(iepByte)^);
|
|
end;
|
|
*)
|
|
|
|
|
|
// Initiate a vector (double version)
|
|
|
|
procedure VEC3init(r: LPVEC3; x, y, z: double); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
r^.n[VX] := x;
|
|
r^.n[VY] := y;
|
|
r^.n[VZ] := z;
|
|
end;
|
|
|
|
// Identity
|
|
|
|
procedure MAT3identity(a: LPMAT3);
|
|
begin
|
|
VEC3init(@a^.v[0], 1.0, 0.0, 0.0);
|
|
VEC3init(@a^.v[1], 0.0, 1.0, 0.0);
|
|
VEC3init(@a^.v[2], 0.0, 0.0, 1.0);
|
|
end;
|
|
|
|
// Swap two double vectors
|
|
|
|
procedure VEC3swap(a: LPVEC3; b: LPVEC3);
|
|
begin
|
|
DSWAP(a^.n[VX], b^.n[VX]);
|
|
DSWAP(a^.n[VY], b^.n[VY]);
|
|
DSWAP(a^.n[VZ], b^.n[VZ]);
|
|
end;
|
|
|
|
// Divide a vector by a constant
|
|
|
|
procedure VEC3divK(r: LPVEC3; v: LPVEC3; d: double);
|
|
var
|
|
d_inv: double;
|
|
begin
|
|
d_inv := 1 / d;
|
|
r^.n[VX] := v^.n[VX] * d_inv;
|
|
r^.n[VY] := v^.n[VY] * d_inv;
|
|
r^.n[VZ] := v^.n[VZ] * d_inv;
|
|
end;
|
|
|
|
// Multiply by a constant
|
|
|
|
procedure VEC3perK(r: LPVEC3; v: LPVEC3; d: double);
|
|
begin
|
|
r^.n[VX] := v^.n[VX] * d;
|
|
r^.n[VY] := v^.n[VY] * d;
|
|
r^.n[VZ] := v^.n[VZ] * d;
|
|
end;
|
|
|
|
// Minus
|
|
|
|
procedure VEC3minus(r: LPVEC3; a: LPVEC3; b: LPVEC3);
|
|
begin
|
|
r^.n[VX] := a^.n[VX] - b^.n[VX];
|
|
r^.n[VY] := a^.n[VY] - b^.n[VY];
|
|
r^.n[VZ] := a^.n[VZ] - b^.n[VZ];
|
|
end;
|
|
|
|
// Inverse of a matrix b = a^(-1)
|
|
// Gauss-Jordan elimination with partial pivoting
|
|
|
|
function MAT3inverse(a: LPMAT3; b: LPMAT3): integer;
|
|
var
|
|
i, j, max: integer;
|
|
temp: VEC3;
|
|
begin
|
|
|
|
MAT3identity(b);
|
|
|
|
for j := 0 to 2 do
|
|
begin
|
|
max := j;
|
|
for i := j + 1 to 2 do
|
|
if (abs(a^.v[i].n[j]) > abs(a^.v[max].n[j])) then
|
|
max := i;
|
|
|
|
VEC3swap(@a^.v[max], @a^.v[j]);
|
|
VEC3swap(@b^.v[max], @b^.v[j]);
|
|
|
|
if (a^.v[j].n[j] = 0) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
VEC3divK(@b^.v[j], @b^.v[j], a^.v[j].n[j]);
|
|
VEC3divK(@a^.v[j], @a^.v[j], a^.v[j].n[j]);
|
|
|
|
for i := 0 to 2 do
|
|
if (i <> j) then
|
|
begin
|
|
|
|
VEC3perK(@temp, @b^.v[j], a^.v[i].n[j]);
|
|
VEC3minus(@b^.v[i], @b^.v[i], @temp);
|
|
|
|
VEC3perK(@temp, @a^.v[j], a^.v[i].n[j]);
|
|
VEC3minus(@a^.v[i], @a^.v[i], @temp);
|
|
end;
|
|
end;
|
|
result := 1;
|
|
end;
|
|
|
|
function Convert15Fixed16(fix32: icS15Fixed16Number): double;
|
|
var
|
|
floater, sign, mid, hack: double;
|
|
Whole, FracPart: integer;
|
|
begin
|
|
AdjustEndianess32(@fix32);
|
|
|
|
if fix32 < 0 then
|
|
sign := -1
|
|
else
|
|
sign := 1;
|
|
|
|
fix32 := abs(fix32);
|
|
|
|
Whole := LOWORD(fix32 shr 16);
|
|
FracPart := LOWORD(fix32 and $0000FFFF);
|
|
|
|
hack := 65536.0;
|
|
mid := FracPart / hack;
|
|
floater := Whole + mid;
|
|
|
|
result := sign * floater;
|
|
end;
|
|
|
|
// Allocate ICC struct. I/O routines are passed through
|
|
|
|
function ICCAllocStruct(Read: TReadFunction; Seek: TSeekFunction; Tell: TTellFunction; Close: TCloseFunction): LPLCMSICCPROFILE;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
getmem(Icc, sizeof(LCMSICCPROFILE));
|
|
if (Icc = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
ZeroMemory(Icc, sizeof(LCMSICCPROFILE));
|
|
|
|
Icc^.Read := Read;
|
|
Icc^.Seek := Seek;
|
|
Icc^.Tell := Tell;
|
|
Icc^.Close := Close;
|
|
Icc^.Write := nil;
|
|
|
|
Icc^.Illuminant.X := D50X;
|
|
Icc^.Illuminant.Y := D50Y;
|
|
Icc^.Illuminant.Z := D50Z;
|
|
|
|
Icc^.TagCount := 0;
|
|
|
|
result := Icc;
|
|
end;
|
|
|
|
function _cmsCreateProfilePlaceholder: cmsHPROFILE;
|
|
begin
|
|
result := cmsHPROFILE(ICCAllocStruct(nil, nil, nil, nil));
|
|
end;
|
|
|
|
const
|
|
D50XYZ: cmsCIEXYZ = (X: D50X; Y: D50Y; Z: D50Z);
|
|
|
|
function cmsD50_XYZ: LPcmsCIEXYZ; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := @D50XYZ;
|
|
end;
|
|
|
|
procedure NormalizeXYZ(Dest: LPcmsCIEXYZ);
|
|
begin
|
|
while (Dest^.X > 2) and
|
|
(Dest^.Y > 2) and
|
|
(Dest^.Z > 2) do
|
|
begin
|
|
|
|
Dest^.X := Dest^.X / 10;
|
|
Dest^.Y := Dest^.Y / 10;
|
|
Dest^.Z := Dest^.Z / 10;
|
|
end;
|
|
end;
|
|
|
|
function CreateICCProfileHandler(ICCfile: TStream; Read: TReadFunction; Seek: TSeekFunction; Tell: TTellFunction; Close: TCloseFunction): LPLCMSICCPROFILE;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
Tag: icTag;
|
|
Header: icHeader;
|
|
TagCount, i: icInt32Number;
|
|
begin
|
|
result := nil;
|
|
Icc := ICCAllocStruct(Read, Seek, Tell, Close);
|
|
if (Icc = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.stream := ICCfile;
|
|
Icc^.Read(@Header, sizeof(icHeader), 1, ICCfile);
|
|
|
|
AdjustEndianess32(@Header.size);
|
|
AdjustEndianess32(@Header.cmmId);
|
|
AdjustEndianess32(@Header.version);
|
|
AdjustEndianess32(@Header.deviceClass);
|
|
AdjustEndianess32(@Header.colorSpace);
|
|
AdjustEndianess32(@Header.pcs);
|
|
AdjustEndianess32(@Header.magic);
|
|
AdjustEndianess32(@Header.flags);
|
|
AdjustEndianess32(@Header.renderingIntent);
|
|
|
|
try
|
|
if (Header.magic <> icMagicNumber) then
|
|
exit;
|
|
if (Icc^.Read(@TagCount, sizeof(icInt32Number), 1, ICCfile) <> 1) then
|
|
exit;
|
|
|
|
AdjustEndianess32(@TagCount);
|
|
|
|
Icc^.DeviceClass := Header.deviceClass;
|
|
Icc^.ColorSpace := Header.colorSpace;
|
|
Icc^.PCS := Header.pcs;
|
|
Icc^.RenderingIntent := icRenderingIntent(Header.renderingIntent);
|
|
Icc^.flags := Header.flags;
|
|
Icc^.Illuminant.X := Convert15Fixed16(Header.illuminant.X);
|
|
Icc^.Illuminant.Y := Convert15Fixed16(Header.illuminant.Y);
|
|
Icc^.Illuminant.Z := Convert15Fixed16(Header.illuminant.Z);
|
|
Icc^.Version := Header.version;
|
|
|
|
Icc^.Illuminant := cmsD50_XYZ^;
|
|
|
|
CopyMemory(@Icc^.ProfileID[0], @Header.reserved[0], 16);
|
|
|
|
NormalizeXYZ(@Icc^.Illuminant);
|
|
|
|
Icc^.TagCount := TagCount;
|
|
for i := 0 to TagCount - 1 do
|
|
begin
|
|
Icc^.Read(@Tag, sizeof(icTag), 1, ICCfile);
|
|
|
|
AdjustEndianess32(@Tag.offset);
|
|
AdjustEndianess32(@Tag.size);
|
|
AdjustEndianess32(@Tag.sig);
|
|
|
|
Icc^.TagNames[i] := Tag.sig;
|
|
Icc^.TagOffsets[i] := Tag.offset;
|
|
Icc^.TagSizes[i] := Tag.size;
|
|
end;
|
|
|
|
result := Icc;
|
|
|
|
finally
|
|
if result = nil then
|
|
begin
|
|
Icc^.Close(ICCfile);
|
|
freemem(Icc);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Does search for a specific tag in tag dictionary
|
|
// Returns position or -1 if tag not found
|
|
|
|
function SearchTag(Profile: LPLCMSICCPROFILE; sig: icTagSignature): icInt32Number;
|
|
var
|
|
i: icInt32Number;
|
|
begin
|
|
if (integer(sig) = 0) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to Profile^.TagCount - 1 do
|
|
begin
|
|
if (sig = Profile^.TagNames[i]) then
|
|
begin
|
|
result := i;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := -1;
|
|
end;
|
|
|
|
function ReadICCXYZ(hProfile: cmsHPROFILE; sig: icTagSignature; Value: LPcmsCIEXYZ; lIsFatal: longbool): integer;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
Base: icTagBase;
|
|
offset: integer;
|
|
n: integer;
|
|
XYZ: icXYZNumber;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
n := SearchTag(Icc, sig);
|
|
if (n < 0) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
if (Icc^.stream = nil) then
|
|
begin
|
|
CopyMemory(Value, Icc^.TagPtrs[n], Icc^.TagSizes[n]);
|
|
result := Icc^.TagSizes[n];
|
|
exit;
|
|
end;
|
|
|
|
offset := Icc^.TagOffsets[n];
|
|
|
|
if (Icc^.Seek(Icc^.stream, offset)) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@Base, 1, sizeof(icTagBase), Icc^.stream);
|
|
AdjustEndianess32(@Base.sig);
|
|
|
|
case Base.sig of
|
|
icTagTypeSignature($7C3B10C),
|
|
icSigXYZType:
|
|
begin
|
|
Icc^.Read(@XYZ, sizeof(icXYZNumber), 1, Icc^.stream);
|
|
Value^.X := Convert15Fixed16(XYZ.X);
|
|
Value^.Y := Convert15Fixed16(XYZ.Y);
|
|
Value^.Z := Convert15Fixed16(XYZ.Z);
|
|
end;
|
|
else
|
|
if (lIsFatal) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
end;
|
|
result := 1;
|
|
end;
|
|
|
|
// Read a icSigS15Fixed16ArrayType (currently only a 3x3 matrix)
|
|
|
|
function ReadICCXYZArray(hProfile: cmsHPROFILE; sig: icTagSignature; v: LPMAT3): integer;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
Base: icTagBase;
|
|
offset, sz: integer;
|
|
i, n: integer;
|
|
XYZ: array[0..2] of icXYZNumber;
|
|
XYZdbl: array[0..2] of cmsCIEXYZ;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
n := SearchTag(Icc, sig);
|
|
if (n < 0) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
if (Icc^.stream = nil) then
|
|
begin
|
|
|
|
CopyMemory(v, Icc^.TagPtrs[n], Icc^.TagSizes[n]);
|
|
|
|
result := Icc^.TagSizes[n];
|
|
exit;
|
|
end;
|
|
|
|
offset := Icc^.TagOffsets[n];
|
|
|
|
if (Icc^.Seek(Icc^.stream, offset)) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@Base, 1, sizeof(icTagBase), Icc^.stream);
|
|
AdjustEndianess32(@Base.sig);
|
|
|
|
case (Base.sig) of
|
|
|
|
icSigS15Fixed16ArrayType:
|
|
begin
|
|
|
|
sz := Icc^.TagSizes[n] div sizeof(icXYZNumber);
|
|
|
|
if (sz <> 3) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Bad array size of %d entries.", sz);
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@XYZ[0], sizeof(icXYZNumber), 3, Icc^.stream);
|
|
|
|
for i := 0 to 2 do
|
|
begin
|
|
|
|
XYZdbl[i].X := Convert15Fixed16(XYZ[i].X);
|
|
XYZdbl[i].Y := Convert15Fixed16(XYZ[i].Y);
|
|
XYZdbl[i].Z := Convert15Fixed16(XYZ[i].Z);
|
|
end;
|
|
|
|
CopyMemory(v, @XYZdbl[0], 3 * sizeof(cmsCIEXYZ));
|
|
end;
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", Base.sig);
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
result := sizeof(MAT3);
|
|
end;
|
|
|
|
// linear transform
|
|
|
|
procedure MAT3eval(r: LPVEC3; a: LPMAT3; v: LPVEC3);
|
|
begin
|
|
r^.n[VX] := a^.v[0].n[VX] * v^.n[VX] + a^.v[0].n[VY] * v^.n[VY] + a^.v[0].n[VZ] * v^.n[VZ];
|
|
r^.n[VY] := a^.v[1].n[VX] * v^.n[VX] + a^.v[1].n[VY] * v^.n[VY] + a^.v[1].n[VZ] * v^.n[VZ];
|
|
r^.n[VZ] := a^.v[2].n[VX] * v^.n[VX] + a^.v[2].n[VY] * v^.n[VY] + a^.v[2].n[VZ] * v^.n[VZ];
|
|
end;
|
|
|
|
// Evaluates a XYZ tristimulous across chromatic adaptation matrix
|
|
|
|
procedure EvalCHRM(Dest: LPcmsCIEXYZ; Chrm: LPMAT3; Src: LPcmsCIEXYZ);
|
|
var
|
|
d, s: VEC3;
|
|
begin
|
|
s.n[VX] := Src^.X;
|
|
s.n[VY] := Src^.Y;
|
|
s.n[VZ] := Src^.Z;
|
|
|
|
MAT3eval(@d, Chrm, @s);
|
|
|
|
Dest^.X := d.n[VX];
|
|
Dest^.Y := d.n[VY];
|
|
Dest^.Z := d.n[VZ];
|
|
end;
|
|
|
|
function ROWCOL(a, b: LPMAT3; i, j: integer): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := a^.v[i].n[0] * b^.v[0].n[j] + a^.v[i].n[1] * b^.v[1].n[j] + a^.v[i].n[2] * b^.v[2].n[j]
|
|
end;
|
|
|
|
// Multiply two matrices
|
|
|
|
procedure MAT3per(r: LPMAT3; a: LPMAT3; b: LPMAT3);
|
|
begin
|
|
VEC3init(@r^.v[0], ROWCOL(a, b, 0, 0), ROWCOL(a, b, 0, 1), ROWCOL(a, b, 0, 2));
|
|
VEC3init(@r^.v[1], ROWCOL(a, b, 1, 0), ROWCOL(a, b, 1, 1), ROWCOL(a, b, 1, 2));
|
|
VEC3init(@r^.v[2], ROWCOL(a, b, 2, 0), ROWCOL(a, b, 2, 1), ROWCOL(a, b, 2, 2));
|
|
(*
|
|
with a^.v[0] do
|
|
VEC3init(@r^.v[0], n[0] * b^.v[0].n[0] + n[1] * b^.v[1].n[0] + n[2] * b^.v[2].n[0],
|
|
n[0] * b^.v[0].n[1] + n[1] * b^.v[1].n[1] + n[2] * b^.v[2].n[1],
|
|
n[0] * b^.v[0].n[2] + n[1] * b^.v[1].n[2] + n[2] * b^.v[2].n[2]);
|
|
with a^.v[1] do
|
|
VEC3init(@r^.v[1], n[0] * b^.v[0].n[0] + n[1] * b^.v[1].n[0] + n[2] * b^.v[2].n[0],
|
|
n[0] * b^.v[0].n[1] + n[1] * b^.v[1].n[1] + n[2] * b^.v[2].n[1],
|
|
n[0] * b^.v[0].n[2] + n[1] * b^.v[1].n[2] + n[2] * b^.v[2].n[2]);
|
|
with a^.v[2] do
|
|
VEC3init(@r^.v[2], n[0] * b^.v[0].n[0] + n[1] * b^.v[1].n[0] + n[2] * b^.v[2].n[0],
|
|
n[0] * b^.v[0].n[1] + n[1] * b^.v[1].n[1] + n[2] * b^.v[2].n[1],
|
|
n[0] * b^.v[0].n[2] + n[1] * b^.v[1].n[2] + n[2] * b^.v[2].n[2]);
|
|
*)
|
|
end;
|
|
|
|
// Compute chromatic adaptation matrix using Chad as cone matrix
|
|
|
|
procedure ComputeChromaticAdaptation(Conversion: LPMAT3; SourceWhitePoint: LPcmsCIEXYZ; DestWhitePoint: LPcmsCIEXYZ; Chad: LPMAT3);
|
|
var
|
|
Chad_Inv: MAT3;
|
|
ConeSourceXYZ, ConeSourceRGB: VEC3;
|
|
ConeDestXYZ, ConeDestRGB: VEC3;
|
|
Cone, Tmp: MAT3;
|
|
begin
|
|
Tmp := Chad^;
|
|
MAT3inverse(@Tmp, @Chad_Inv);
|
|
|
|
VEC3init(@ConeSourceXYZ, SourceWhitePoint^.X,
|
|
SourceWhitePoint^.Y,
|
|
SourceWhitePoint^.Z);
|
|
|
|
VEC3init(@ConeDestXYZ, DestWhitePoint^.X,
|
|
DestWhitePoint^.Y,
|
|
DestWhitePoint^.Z);
|
|
|
|
MAT3eval(@ConeSourceRGB, Chad, @ConeSourceXYZ);
|
|
MAT3eval(@ConeDestRGB, Chad, @ConeDestXYZ);
|
|
|
|
VEC3init(@Cone.v[0], ConeDestRGB.n[0] / ConeSourceRGB.n[0], 0.0, 0.0);
|
|
VEC3init(@Cone.v[1], 0.0, ConeDestRGB.n[1] / ConeSourceRGB.n[1], 0.0);
|
|
VEC3init(@Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2] / ConeSourceRGB.n[2]);
|
|
|
|
MAT3per(@Tmp, @Cone, Chad);
|
|
MAT3per(Conversion, @Chad_Inv, @Tmp);
|
|
end;
|
|
|
|
// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
|
|
// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed
|
|
|
|
function cmsAdaptationMatrix(r: LPMAT3; ConeMatrix: LPMAT3; FromIll: LPcmsCIEXYZ; ToIll: LPcmsCIEXYZ): longbool;
|
|
const
|
|
LamRigg: MAT3 = (
|
|
v: ((n: (0.8951, 0.2664, -0.1614)),
|
|
(n: (-0.7502, 1.7135, 0.0367)),
|
|
(n: (0.0389, -0.0685, 1.0296)))
|
|
);
|
|
begin
|
|
if (ConeMatrix = nil) then
|
|
ConeMatrix := @LamRigg;
|
|
ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
|
|
result := true;
|
|
end;
|
|
|
|
procedure ReadCriticalTags(Icc: LPLCMSICCPROFILE);
|
|
const
|
|
Brfd: MAT3 = (
|
|
v: ((n: (0.8951, 0.2664, -0.1614)),
|
|
(n: (-0.7502, 1.7135, 0.0367)),
|
|
(n: (0.0389, -0.0685, 1.0296)))
|
|
);
|
|
var
|
|
hProfile: cmsHPROFILE;
|
|
ChrmCanonical: MAT3;
|
|
begin
|
|
hProfile := cmsHPROFILE(Icc);
|
|
|
|
if (Icc^.Version >= $4000000) then
|
|
begin
|
|
if (ReadICCXYZ(hProfile, icSigMediaWhitePointTag, @Icc^.MediaWhitePoint, FALSE) < 0) then
|
|
begin
|
|
Icc^.MediaWhitePoint := cmsD50_XYZ()^;
|
|
end;
|
|
|
|
if (ReadICCXYZ(hProfile, icSigMediaBlackPointTag, @Icc^.MediaBlackPoint, FALSE) < 0) then
|
|
begin
|
|
Icc^.MediaBlackPoint.X := 0;
|
|
Icc^.MediaBlackPoint.Y := 0;
|
|
Icc^.MediaBlackPoint.X := 0;
|
|
end;
|
|
|
|
NormalizeXYZ(@Icc^.MediaWhitePoint);
|
|
NormalizeXYZ(@Icc^.MediaBlackPoint);
|
|
|
|
if (ReadICCXYZArray(hProfile, icTagSignature(icSigChromaticAdaptationTag), @ChrmCanonical) > 0) then
|
|
begin
|
|
MAT3inverse(@ChrmCanonical, @Icc^.ChromaticAdaptation);
|
|
end
|
|
else
|
|
begin
|
|
MAT3identity(@Icc^.ChromaticAdaptation);
|
|
end;
|
|
|
|
EvalCHRM(@Icc^.MediaWhitePoint, @Icc^.ChromaticAdaptation, @Icc^.MediaWhitePoint);
|
|
EvalCHRM(@Icc^.MediaBlackPoint, @Icc^.ChromaticAdaptation, @Icc^.MediaBlackPoint);
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (ReadICCXYZ(hProfile, icSigMediaWhitePointTag, @Icc^.MediaWhitePoint, FALSE) < 0) then
|
|
begin
|
|
Icc^.MediaWhitePoint := cmsD50_XYZ()^;
|
|
end;
|
|
|
|
if (ReadICCXYZ(hProfile, icSigMediaBlackPointTag, @Icc^.MediaBlackPoint, FALSE) < 0) then
|
|
begin
|
|
Icc^.MediaBlackPoint.X := 0;
|
|
Icc^.MediaBlackPoint.Y := 0;
|
|
Icc^.MediaBlackPoint.X := 0;
|
|
end;
|
|
|
|
NormalizeXYZ(@Icc^.MediaWhitePoint);
|
|
NormalizeXYZ(@Icc^.MediaBlackPoint);
|
|
|
|
cmsAdaptationMatrix(@Icc^.ChromaticAdaptation, @Brfd, @Icc^.Illuminant, @Icc^.MediaWhitePoint);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
// Create profile from disk file
|
|
|
|
function IEcmsOpenProfileFromFile(stream: TStream; save: boolean; save8bit: boolean): cmsHPROFILE;
|
|
var
|
|
NewIcc: LPLCMSICCPROFILE;
|
|
hEmpty: cmsHPROFILE;
|
|
begin
|
|
|
|
if save then
|
|
begin
|
|
|
|
hEmpty := _cmsCreateProfilePlaceholder();
|
|
NewIcc := LPLCMSICCPROFILE(hEmpty);
|
|
NewIcc^.IsWrite := TRUE;
|
|
NewIcc^.PhysicalFile := stream;
|
|
|
|
if save8bit then
|
|
NewIcc^.SaveAs8Bits := TRUE;
|
|
result := hEmpty;
|
|
exit;
|
|
end;
|
|
|
|
NewIcc := CreateICCProfileHandler(stream, IEFileRead, IEFileSeek, IEFileTell, IEFileClose);
|
|
if (NewIcc = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
ReadCriticalTags(NewIcc);
|
|
|
|
NewIcc^.PhysicalFile := stream;
|
|
NewIcc^.IsWrite := FALSE;
|
|
result := cmsHPROFILE(NewIcc);
|
|
end;
|
|
|
|
// Auxiliary: allocate transform struct and set to defaults
|
|
|
|
function AllocEmptyTransform: _LPcmsTRANSFORM;
|
|
var
|
|
p: _LPcmsTRANSFORM;
|
|
begin
|
|
|
|
getmem(p, sizeof(_cmsTRANSFORM));
|
|
if (p = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: malloc() failed");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
ZeroMemory(p, sizeof(_cmsTRANSFORM));
|
|
|
|
p^.xform := nil;
|
|
p^.Intent := INTENT_PERCEPTUAL;
|
|
p^.ProofIntent := INTENT_ABSOLUTE_COLORIMETRIC;
|
|
p^.DoGamutCheck := 0;
|
|
p^.InputProfile := nil;
|
|
p^.OutputProfile := nil;
|
|
p^.PreviewProfile := nil;
|
|
p^.Preview := nil;
|
|
p^.Gamut := nil;
|
|
p^.DeviceLink := nil;
|
|
p^.InMatShaper := nil;
|
|
p^.OutMatShaper := nil;
|
|
p^.SmeltMatShaper := nil;
|
|
p^.NamedColorList := nil;
|
|
p^.EntryColorSpace := icColorSpaceSignature(0);
|
|
p^.ExitColorSpace := icColorSpaceSignature(0);
|
|
|
|
result := p;
|
|
end;
|
|
|
|
function _cmsEndPointsBySpace(Space: icColorSpaceSignature; var White: pword; var Black: pword; nOutputs: pinteger): longbool;
|
|
const
|
|
RGBblack: array[0..3] of word = (0, 0, 0, 0);
|
|
RGBwhite: array[0..3] of word = ($FFFF, $FFFF, $FFFF, 0);
|
|
CMYKblack: array[0..3] of word = ($FFFF, $FFFF, $FFFF, $FFFF);
|
|
CMYKwhite: array[0..3] of word = (0, 0, 0, 0);
|
|
LABblack: array[0..3] of word = (0, $8000, $8000, 0);
|
|
LABwhite: array[0..3] of word = ($FF00, $8000, $8000, 0);
|
|
CMYblack: array[0..3] of word = ($FFFF, $FFFF, $FFFF, 0);
|
|
CMYwhite: array[0..3] of word = (0, 0, 0, 0);
|
|
begin
|
|
case (Space) of
|
|
|
|
icSigRgbData:
|
|
begin
|
|
White := @RGBwhite;
|
|
Black := @RGBblack;
|
|
nOutputs^ := 3;
|
|
result := true;
|
|
exit;
|
|
end;
|
|
|
|
icSigLabData:
|
|
begin
|
|
White := @LABwhite;
|
|
Black := @LABblack;
|
|
nOutputs^ := 3;
|
|
result := true;
|
|
exit;
|
|
end;
|
|
|
|
icSigCmykData:
|
|
begin
|
|
White := @CMYKwhite;
|
|
Black := @CMYKblack;
|
|
nOutputs^ := 4;
|
|
result := true;
|
|
exit;
|
|
end;
|
|
|
|
icSigCmyData:
|
|
begin
|
|
White := @CMYwhite;
|
|
Black := @CMYblack;
|
|
nOutputs^ := 3;
|
|
result := true;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := false;
|
|
end;
|
|
|
|
function _cmsWhiteBySpace(Space: icColorSpaceSignature): PWORD;
|
|
const
|
|
Default: array[0..MAXCHANNELS - 1] of word = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
var
|
|
White, Black: pword;
|
|
Dummy: integer;
|
|
begin
|
|
White := nil;
|
|
Black := nil;
|
|
if (_cmsEndPointsBySpace(Space, White, Black, @Dummy)) then
|
|
result := White
|
|
else
|
|
result := @Default;
|
|
end;
|
|
|
|
function cmsGetColorSpace(hProfile: cmsHPROFILE): icColorSpaceSignature;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
result := Icc^.ColorSpace;
|
|
end;
|
|
|
|
procedure COPY_3CHANS(xto, xfrom: pwordarray); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
xto[0] := xfrom[0];
|
|
xto[1] := xfrom[1];
|
|
xto[2] := xfrom[2];
|
|
end;
|
|
|
|
function FROM_V2_TO_V4(x: word): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((((x) shl 8) + (x)) + $80) shr 8);
|
|
end;
|
|
|
|
function FROM_V4_TO_V2(x: word): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := ((((x) shl 8) + $80) div 257);
|
|
end;
|
|
|
|
function ToFixedDomain(a: integer): Fixed32; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := a + ((a + $7FFF) div $FFFF);
|
|
end;
|
|
|
|
function FromFixedDomain(a: Fixed32): integer; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := a - ((a + $7FFF) shr 16);
|
|
end;
|
|
|
|
(*
|
|
function FixedMul(a: Fixed32; b: Fixed32): Fixed32;
|
|
begin
|
|
result := round(((a / 65536) * (b / 65536)) * 65536 );
|
|
end;
|
|
*)
|
|
|
|
procedure MAT3evalW(r: LPWVEC3; a: LPWMAT3; v: LPWVEC3);
|
|
var
|
|
o1, o2, o3: double;
|
|
begin
|
|
with v^ do
|
|
begin
|
|
o1 := n[0] / 65536;
|
|
o2 := n[1] / 65536;
|
|
o3 := n[2] / 65536;
|
|
end;
|
|
with a^.v[0] do
|
|
r^.n[VX] := round(((n[0] / 65536) * o1) * 65536 ) +
|
|
round(((n[1] / 65536) * o2) * 65536 ) +
|
|
round(((n[2] / 65536) * o3) * 65536 );
|
|
|
|
with a^.v[1] do
|
|
r^.n[VY] := round(((n[0] / 65536) * o1) * 65536 ) +
|
|
round(((n[1] / 65536) * o2) * 65536 ) +
|
|
round(((n[2] / 65536) * o3) * 65536 );
|
|
|
|
with a^.v[2] do
|
|
r^.n[VZ] := round(((n[0] / 65536) * o1) * 65536 ) +
|
|
round(((n[1] / 65536) * o2) * 65536 ) +
|
|
round(((n[2] / 65536) * o3) * 65536 );
|
|
|
|
(*
|
|
r^.n[VX] := FixedMul(a^.v[0].n[0], v^.n[0]) +
|
|
FixedMul(a^.v[0].n[1], v^.n[1]) +
|
|
FixedMul(a^.v[0].n[2], v^.n[2]);
|
|
|
|
r^.n[VY] := FixedMul(a^.v[1].n[0], v^.n[0]) +
|
|
FixedMul(a^.v[1].n[1], v^.n[1]) +
|
|
FixedMul(a^.v[1].n[2], v^.n[2]);
|
|
|
|
r^.n[VZ] := FixedMul(a^.v[2].n[0], v^.n[0]) +
|
|
FixedMul(a^.v[2].n[1], v^.n[1]) +
|
|
FixedMul(a^.v[2].n[2], v^.n[2]);
|
|
*)
|
|
end;
|
|
|
|
function Clamp_RGB(xin: integer): word;
|
|
begin
|
|
if (xin < 0) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
if (xin > $FFFF) then
|
|
result := $FFFF
|
|
else
|
|
result := xin;
|
|
end;
|
|
|
|
function cmsLinearInterpLUT16(Value: WORD; LutTable: pwordarray; p: LPL16PARAMS): word;
|
|
var
|
|
y1, y0: double;
|
|
y: double;
|
|
val2, rest: double;
|
|
cell0, cell1: integer;
|
|
begin
|
|
if (Value = $FFFF) then
|
|
begin
|
|
result := LutTable[p^.Domain];
|
|
exit;
|
|
end;
|
|
|
|
val2 := p^.Domain * (Value / 65535.0);
|
|
|
|
cell0 := Floor(val2);
|
|
cell1 := Ceil(val2);
|
|
|
|
rest := val2 - cell0;
|
|
|
|
y0 := LutTable[cell0];
|
|
y1 := LutTable[cell1];
|
|
|
|
y := y0 + (y1 - y0) * rest;
|
|
|
|
result := floor(y + 0.5);
|
|
end;
|
|
|
|
procedure cmsEvalLUT(Lut: LPLUT; xIn: pwordarray; xOut: pwordarray);
|
|
var
|
|
i: dword;
|
|
StageABC, StageLMN: array[0..MAXCHANNELS - 1] of WORD;
|
|
InVect, OutVect: WVEC3;
|
|
begin
|
|
(*
|
|
for i := 0 to Lut ^. InputChan-1 do
|
|
StageABC[i] := xIn[i];
|
|
|
|
if (Lut ^.wFlags and LUT_V4_OUTPUT_EMULATE_V2)<>0 then
|
|
begin
|
|
|
|
StageABC[0] := FROM_V2_TO_V4(StageABC[0]);
|
|
StageABC[1] := FROM_V2_TO_V4(StageABC[1]);
|
|
StageABC[2] := FROM_V2_TO_V4(StageABC[2]);
|
|
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_V2_OUTPUT_EMULATE_V4)<>0 then
|
|
begin
|
|
|
|
StageABC[0] := FROM_V4_TO_V2(StageABC[0]);
|
|
StageABC[1] := FROM_V4_TO_V2(StageABC[1]);
|
|
StageABC[2] := FROM_V4_TO_V2(StageABC[2]);
|
|
end;
|
|
|
|
if (Lut ^. wFlags and LUT_HASMATRIX)<>0 then
|
|
begin
|
|
|
|
InVect.n[VX] := ToFixedDomain(StageABC[0]);
|
|
InVect.n[VY] := ToFixedDomain(StageABC[1]);
|
|
InVect.n[VZ] := ToFixedDomain(StageABC[2]);
|
|
|
|
MAT3evalW(@OutVect, @Lut ^. Matrix, @InVect);
|
|
|
|
StageABC[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
StageABC[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
StageABC[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
end;
|
|
|
|
if (Lut ^. wFlags and LUT_HASTL1)<>0 then
|
|
begin
|
|
|
|
for i := 0 to Lut ^. InputChan-1 do
|
|
StageABC[i] := cmsLinearInterpLUT16(StageABC[i],
|
|
PWordArray(Lut ^. L1[i]),
|
|
@Lut ^. In16params);
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_HASMATRIX3)<>0 then
|
|
begin
|
|
|
|
InVect.n[VX] := ToFixedDomain(StageABC[0]);
|
|
InVect.n[VY] := ToFixedDomain(StageABC[1]);
|
|
InVect.n[VZ] := ToFixedDomain(StageABC[2]);
|
|
|
|
MAT3evalW(@OutVect, @Lut ^. Mat3, @InVect);
|
|
|
|
OutVect.n[VX] := OutVect.n[VX] + Lut ^.Ofs3.n[VX];
|
|
OutVect.n[VY] := OutVect.n[VY] + Lut ^.Ofs3.n[VY];
|
|
OutVect.n[VZ] := OutVect.n[VZ] + Lut ^.Ofs3.n[VZ];
|
|
|
|
StageABC[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
StageABC[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
StageABC[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_HASTL3)<>0 then
|
|
begin
|
|
|
|
for i := 0 to Lut ^. InputChan-1 do
|
|
StageABC[i] := cmsLinearInterpLUT16(StageABC[i],
|
|
pwordarray(Lut ^. L3[i]),
|
|
@Lut ^. L3params);
|
|
|
|
end;
|
|
|
|
if (Lut ^. wFlags and LUT_HAS3DGRID)<>0 then
|
|
begin
|
|
|
|
Lut ^.CLut16params.Interp3D(@StageABC, @StageLMN, pwordarray(Lut ^. T), @Lut ^. CLut16params);
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
for i := 0 to Lut ^. InputChan-1 do
|
|
StageLMN[i] := StageABC[i];
|
|
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_HASTL4)<>0 then
|
|
begin
|
|
|
|
for i := 0 to Lut ^. OutputChan-1 do
|
|
StageLMN[i] := cmsLinearInterpLUT16(StageLMN[i],
|
|
pwordarray(Lut ^. L4[i]),
|
|
@Lut ^. L4params);
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_HASMATRIX4)<>0 then
|
|
begin
|
|
|
|
InVect.n[VX] := ToFixedDomain(StageLMN[0]);
|
|
InVect.n[VY] := ToFixedDomain(StageLMN[1]);
|
|
InVect.n[VZ] := ToFixedDomain(StageLMN[2]);
|
|
|
|
MAT3evalW(@OutVect, @Lut ^. Mat4, @InVect);
|
|
|
|
OutVect.n[VX] := OutVect.n[VX]+ Lut ^.Ofs4.n[VX];
|
|
OutVect.n[VY] := OutVect.n[VY]+ Lut ^.Ofs4.n[VY];
|
|
OutVect.n[VZ] := OutVect.n[VZ]+ Lut ^.Ofs4.n[VZ];
|
|
|
|
StageLMN[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
StageLMN[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
StageLMN[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
|
|
end;
|
|
|
|
if (Lut ^. wFlags and LUT_HASTL2)<>0 then
|
|
begin
|
|
|
|
for i := 0 to Lut ^. OutputChan-1 do
|
|
xOut[i] := cmsLinearInterpLUT16(StageLMN[i],
|
|
pwordarray(Lut ^. L2[i]),
|
|
@Lut ^. Out16params);
|
|
end
|
|
else
|
|
begin
|
|
for i := 0 to Lut ^. OutputChan-1 do
|
|
xOut[i] := StageLMN[i];
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_V4_INPUT_EMULATE_V2)<>0 then
|
|
begin
|
|
|
|
xOut[0] := FROM_V4_TO_V2(xOut[0]);
|
|
xOut[1] := FROM_V4_TO_V2(xOut[1]);
|
|
xOut[2] := FROM_V4_TO_V2(xOut[2]);
|
|
|
|
end;
|
|
|
|
if (Lut ^.wFlags and LUT_V2_INPUT_EMULATE_V4)<>0 then
|
|
begin
|
|
|
|
xOut[0] := FROM_V2_TO_V4(xOut[0]);
|
|
xOut[1] := FROM_V2_TO_V4(xOut[1]);
|
|
xOut[2] := FROM_V2_TO_V4(xOut[2]);
|
|
end;
|
|
*)
|
|
with Lut^ do
|
|
begin
|
|
for i := 0 to InputChan - 1 do
|
|
StageABC[i] := xIn[i];
|
|
|
|
if (wFlags and LUT_V4_OUTPUT_EMULATE_V2) <> 0 then
|
|
begin
|
|
|
|
StageABC[0] := FROM_V2_TO_V4(StageABC[0]);
|
|
StageABC[1] := FROM_V2_TO_V4(StageABC[1]);
|
|
StageABC[2] := FROM_V2_TO_V4(StageABC[2]);
|
|
|
|
end;
|
|
|
|
if (wFlags and LUT_V2_OUTPUT_EMULATE_V4) <> 0 then
|
|
begin
|
|
|
|
StageABC[0] := FROM_V4_TO_V2(StageABC[0]);
|
|
StageABC[1] := FROM_V4_TO_V2(StageABC[1]);
|
|
StageABC[2] := FROM_V4_TO_V2(StageABC[2]);
|
|
end;
|
|
|
|
if (wFlags and LUT_HASMATRIX) <> 0 then
|
|
begin
|
|
|
|
InVect.n[VX] := ToFixedDomain(StageABC[0]);
|
|
InVect.n[VY] := ToFixedDomain(StageABC[1]);
|
|
InVect.n[VZ] := ToFixedDomain(StageABC[2]);
|
|
|
|
MAT3evalW(@OutVect, @Matrix, @InVect);
|
|
|
|
StageABC[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
StageABC[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
StageABC[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
end;
|
|
|
|
if (wFlags and LUT_HASTL1) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to InputChan - 1 do
|
|
StageABC[i] := cmsLinearInterpLUT16(StageABC[i],
|
|
PWordArray(L1[i]),
|
|
@In16params);
|
|
end;
|
|
|
|
if (wFlags and LUT_HASMATRIX3) <> 0 then
|
|
begin
|
|
|
|
InVect.n[VX] := ToFixedDomain(StageABC[0]);
|
|
InVect.n[VY] := ToFixedDomain(StageABC[1]);
|
|
InVect.n[VZ] := ToFixedDomain(StageABC[2]);
|
|
|
|
MAT3evalW(@OutVect, @Mat3, @InVect);
|
|
|
|
OutVect.n[VX] := OutVect.n[VX] + Ofs3.n[VX];
|
|
OutVect.n[VY] := OutVect.n[VY] + Ofs3.n[VY];
|
|
OutVect.n[VZ] := OutVect.n[VZ] + Ofs3.n[VZ];
|
|
|
|
StageABC[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
StageABC[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
StageABC[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
|
|
end;
|
|
|
|
if (wFlags and LUT_HASTL3) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to InputChan - 1 do
|
|
StageABC[i] := cmsLinearInterpLUT16(StageABC[i],
|
|
pwordarray(L3[i]),
|
|
@L3params);
|
|
|
|
end;
|
|
|
|
if (wFlags and LUT_HAS3DGRID) <> 0 then
|
|
begin
|
|
|
|
CLut16params.Interp3D(@StageABC, @StageLMN, pwordarray(T), @CLut16params);
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
for i := 0 to InputChan - 1 do
|
|
StageLMN[i] := StageABC[i];
|
|
|
|
end;
|
|
|
|
if (wFlags and LUT_HASTL4) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to OutputChan - 1 do
|
|
StageLMN[i] := cmsLinearInterpLUT16(StageLMN[i],
|
|
pwordarray(L4[i]),
|
|
@L4params);
|
|
end;
|
|
|
|
if (wFlags and LUT_HASMATRIX4) <> 0 then
|
|
begin
|
|
|
|
InVect.n[VX] := ToFixedDomain(StageLMN[0]);
|
|
InVect.n[VY] := ToFixedDomain(StageLMN[1]);
|
|
InVect.n[VZ] := ToFixedDomain(StageLMN[2]);
|
|
|
|
MAT3evalW(@OutVect, @Mat4, @InVect);
|
|
|
|
OutVect.n[VX] := OutVect.n[VX] + Ofs4.n[VX];
|
|
OutVect.n[VY] := OutVect.n[VY] + Ofs4.n[VY];
|
|
OutVect.n[VZ] := OutVect.n[VZ] + Ofs4.n[VZ];
|
|
|
|
StageLMN[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
StageLMN[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
StageLMN[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
|
|
end;
|
|
|
|
if (wFlags and LUT_HASTL2) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to OutputChan - 1 do
|
|
xOut[i] := cmsLinearInterpLUT16(StageLMN[i],
|
|
pwordarray(L2[i]),
|
|
@Out16params);
|
|
end
|
|
else
|
|
begin
|
|
for i := 0 to OutputChan - 1 do
|
|
xOut[i] := StageLMN[i];
|
|
end;
|
|
|
|
if (wFlags and LUT_V4_INPUT_EMULATE_V2) <> 0 then
|
|
begin
|
|
|
|
xOut[0] := FROM_V4_TO_V2(xOut[0]);
|
|
xOut[1] := FROM_V4_TO_V2(xOut[1]);
|
|
xOut[2] := FROM_V4_TO_V2(xOut[2]);
|
|
|
|
end;
|
|
|
|
if (wFlags and LUT_V2_INPUT_EMULATE_V4) <> 0 then
|
|
begin
|
|
|
|
xOut[0] := FROM_V2_TO_V4(xOut[0]);
|
|
xOut[1] := FROM_V2_TO_V4(xOut[1]);
|
|
xOut[2] := FROM_V2_TO_V4(xOut[2]);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// This is the "normal" proofing transform
|
|
|
|
procedure NormalXFORM(p: _LPcmsTRANSFORM; xin: pointer; xout: pointer; Size: dword);
|
|
var
|
|
accum: PBYTE;
|
|
output: PBYTE;
|
|
wIn, wOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
wStageABC, wPCS: array[0..2] of WORD;
|
|
wStageLMN: array[0..MAXCHANNELS - 1] of WORD;
|
|
wGamut: array[0..0] of WORD;
|
|
i, n: dword;
|
|
wPreview: array[0..2] of WORD;
|
|
begin
|
|
|
|
accum := xin;
|
|
output := xout;
|
|
n := Size;
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
|
|
accum := p^.FromInput(p, @wIn, accum);
|
|
|
|
p^.FromDevice(p, @wIn, @wStageABC);
|
|
|
|
if (@p^.Stage1 <> nil) then
|
|
begin
|
|
|
|
p^.Stage1(@wStageABC, @wPCS, @p^.m1, @p^.of1);
|
|
|
|
if (wPCS[0] = $FFFF) and (wPCS[1] = $FFFF) and (wPCS[2] = $FFFF) then
|
|
begin
|
|
|
|
output := p^.ToOutput(_LPcmsTRANSFORM(p),
|
|
pwordarray(_cmsWhiteBySpace(cmsGetColorSpace(p^.OutputProfile))),
|
|
output);
|
|
continue;
|
|
end;
|
|
end
|
|
else
|
|
COPY_3CHANS(@wPCS, @wStageABC);
|
|
|
|
if (p^.Gamut <> nil) then
|
|
begin
|
|
|
|
cmsEvalLUT(p^.Gamut, @wPCS, @wGamut);
|
|
|
|
if (wGamut[0] >= 1) then
|
|
begin
|
|
|
|
wOut[0] := AlarmR;
|
|
wOut[1] := AlarmG;
|
|
wOut[2] := AlarmB;
|
|
wOut[3] := 0;
|
|
|
|
output := p^.ToOutput(_LPcmsTRANSFORM(p), @wOut, output);
|
|
continue;
|
|
end;
|
|
end;
|
|
|
|
if (p^.Preview <> nil) then
|
|
begin
|
|
|
|
cmsEvalLUT(p^.Preview, @wPCS, @wPreview);
|
|
COPY_3CHANS(@wPCS, @wPreview);
|
|
end;
|
|
|
|
if (@p^.Stage2 <> nil) then
|
|
begin
|
|
p^.Stage2(@wPCS, @wStageLMN, @p^.m2, @p^.of2);
|
|
if (wPCS[0] = $FFFF) and (wPCS[1] = $FFFF) and (wPCS[2] = $FFFF) then
|
|
begin
|
|
|
|
output := p^.ToOutput(_LPcmsTRANSFORM(p),
|
|
pwordarray(_cmsWhiteBySpace(cmsGetColorSpace(p^.OutputProfile))),
|
|
output);
|
|
|
|
continue;
|
|
end;
|
|
|
|
end
|
|
else
|
|
COPY_3CHANS(@wStageLMN, @wPCS);
|
|
|
|
p^.ToDevice(p, @wStageLMN, @wOut);
|
|
|
|
output := p^.ToOutput(_LPcmsTRANSFORM(p), @wOut, output);
|
|
end;
|
|
end;
|
|
|
|
function cmsGetDeviceClass(hProfile: cmsHPROFILE): icProfileClassSignature;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
result := Icc^.DeviceClass;
|
|
end;
|
|
|
|
(*
|
|
function CHANNELS_SH(c: dword): dword;
|
|
begin
|
|
result := ((c) shl 3);
|
|
end;
|
|
*)
|
|
|
|
(*
|
|
function BYTES_SH(b: dword): dword;
|
|
begin
|
|
result := (b);
|
|
end;
|
|
*)
|
|
|
|
(*
|
|
function TYPE_NAMED_COLOR_INDEX: dword;
|
|
begin
|
|
result := (CHANNELS_SH(1) or BYTES_SH(2));
|
|
end;
|
|
*)
|
|
|
|
(*
|
|
function T_BYTES(b: dword): dword;
|
|
begin
|
|
result := ((b) and 7);
|
|
end;
|
|
*)
|
|
|
|
function T_COLORSPACE(s: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((s) shr 16) and 31);
|
|
end;
|
|
|
|
function L2Fix4(L: double): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := round(L * 655.35 );
|
|
end;
|
|
|
|
function ab2Fix4(ab: double): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := round((ab + 128.0) * 257.0 );
|
|
end;
|
|
|
|
procedure cmsFloat2LabEncoded4(wLab: pwordarray; fLab: LPcmsCIELab);
|
|
var
|
|
Lab: cmsCIELab;
|
|
begin
|
|
|
|
Lab.L := fLab^.L;
|
|
Lab.a := fLab^.a;
|
|
Lab.b := fLab^.b;
|
|
|
|
if (Lab.L < 0) then
|
|
Lab.L := 0;
|
|
if (Lab.L > 100) then
|
|
Lab.L := 100;
|
|
|
|
if (Lab.a < -128) then
|
|
Lab.a := -128;
|
|
if (Lab.a > 127) then
|
|
Lab.a := 127;
|
|
if (Lab.b < -128) then
|
|
Lab.b := -128;
|
|
if (Lab.b > 127) then
|
|
Lab.b := 127;
|
|
|
|
wLab[0] := L2Fix4(Lab.L);
|
|
wLab[1] := ab2Fix4(Lab.a);
|
|
wLab[2] := ab2Fix4(Lab.b);
|
|
end;
|
|
|
|
function L2Fix3(L: double): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := round(L * 652.800 );
|
|
end;
|
|
|
|
function ab2Fix3(ab: double): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := round((ab + 128.0) * 256.0 );
|
|
end;
|
|
|
|
procedure cmsFloat2LabEncoded(wLab: pwordarray; fLab: LPcmsCIELab);
|
|
var
|
|
Lab: cmsCIELab;
|
|
begin
|
|
|
|
Lab.L := fLab^.L;
|
|
Lab.a := fLab^.a;
|
|
Lab.b := fLab^.b;
|
|
|
|
if (Lab.L < 0) then
|
|
Lab.L := 0;
|
|
if (Lab.L > 100) then
|
|
Lab.L := 100;
|
|
|
|
if (Lab.a < -128) then
|
|
Lab.a := -128;
|
|
if (Lab.a > 127.9961) then
|
|
Lab.a := 127.9961;
|
|
if (Lab.b < -128) then
|
|
Lab.b := -128;
|
|
if (Lab.b > 127.9961) then
|
|
Lab.b := 127.9961;
|
|
|
|
wLab[0] := L2Fix3(Lab.L);
|
|
wLab[1] := ab2Fix3(Lab.a);
|
|
wLab[2] := ab2Fix3(Lab.b);
|
|
end;
|
|
|
|
// floating point
|
|
|
|
function UnrollLabDouble(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
if (info^.lInputV4Lab) then
|
|
cmsFloat2LabEncoded4(wIn, LPcmsCIELab(accum))
|
|
else
|
|
cmsFloat2LabEncoded(wIn, LPcmsCIELab(accum));
|
|
|
|
inc(accum, sizeof(cmsCIELab));
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
// In XYZ All 3 components are encoded using 1.15 fixed point
|
|
|
|
function XYZ2Fix(d: double): WORD; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := floor(d * 32768.0 + 0.5);
|
|
end;
|
|
|
|
procedure cmsFloat2XYZEncoded(MXYZ: pwordarray; fXYZ: LPcmsCIEXYZ);
|
|
var
|
|
xyz: cmsCIEXYZ;
|
|
begin
|
|
|
|
xyz.X := fXYZ^.X;
|
|
xyz.Y := fXYZ^.Y;
|
|
xyz.Z := fXYZ^.Z;
|
|
|
|
if (xyz.Y <= 0) then
|
|
begin
|
|
|
|
xyz.X := 0;
|
|
xyz.Y := 0;
|
|
xyz.Z := 0;
|
|
end;
|
|
|
|
if (xyz.X > 1.99996) then
|
|
xyz.X := 1.99996;
|
|
|
|
if (xyz.X < 0) then
|
|
xyz.X := 0;
|
|
|
|
if (xyz.Y > 1.99996) then
|
|
xyz.Y := 1.99996;
|
|
|
|
if (xyz.Y < 0) then
|
|
xyz.Y := 0;
|
|
|
|
if (xyz.Z > 1.99996) then
|
|
xyz.Z := 1.99996;
|
|
|
|
if (xyz.Z < 0) then
|
|
xyz.Z := 0;
|
|
|
|
MXYZ[0] := XYZ2Fix(xyz.X);
|
|
MXYZ[1] := XYZ2Fix(xyz.Y);
|
|
MXYZ[2] := XYZ2Fix(xyz.Z);
|
|
|
|
end;
|
|
|
|
function UnrollXYZDouble(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
cmsFloat2XYZEncoded(wIn, LPcmsCIEXYZ(accum));
|
|
inc(accum, sizeof(cmsCIEXYZ));
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function T_CHANNELS(c: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((c) shr 3) and 15);
|
|
end;
|
|
|
|
function T_EXTRA(e: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((e) shr 7) and 7);
|
|
end;
|
|
|
|
type
|
|
tdoublearray = array[0..$EFFFFFF] of double;
|
|
|
|
pdoublearray = ^tdoublearray;
|
|
|
|
// Remaining cases are between 0..1.0
|
|
|
|
function UnrollDouble(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
Inks: pdoublearray;
|
|
nChan: integer;
|
|
i: integer;
|
|
v: double;
|
|
begin
|
|
Inks := pdoublearray(accum);
|
|
nChan := T_CHANNELS(info^.InputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
v := floor(Inks[i] * 65535.0 + 0.5);
|
|
|
|
if (v > 65535.0) then
|
|
v := 65535.0;
|
|
if (v < 0) then
|
|
v := 0;
|
|
|
|
wIn[i] := trunc(v);
|
|
end;
|
|
|
|
inc(accum, (nChan + T_EXTRA(info^.InputFormat)) * sizeof(double));
|
|
result := accum;
|
|
end;
|
|
|
|
// Inks does come in percentage
|
|
|
|
function UnrollInkDouble(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
Inks: pdoublearray;
|
|
nChan, i: integer;
|
|
v: double;
|
|
begin
|
|
Inks := pdoublearray(accum);
|
|
nChan := T_CHANNELS(info^.InputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
v := floor(Inks[i] * 655.35 + 0.5);
|
|
|
|
if (v > 65535.0) then
|
|
v := 65535.0;
|
|
if (v < 0) then
|
|
v := 0;
|
|
|
|
wIn[i] := trunc(v);
|
|
end;
|
|
|
|
inc(accum, (nChan + T_EXTRA(info^.InputFormat)) * sizeof(double));
|
|
result := accum;
|
|
end;
|
|
|
|
function T_PLANAR(p: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((p) shr 12) and 1);
|
|
end;
|
|
|
|
function RGB_8_TO_16(rgb: word): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := ((((rgb)) shl 8) or (rgb));
|
|
end;
|
|
|
|
function UnrollPlanarBytes(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
Init: pbyte;
|
|
begin
|
|
nChan := T_CHANNELS(info^.InputFormat);
|
|
Init := accum;
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
wIn[i] := RGB_8_TO_16(accum^);
|
|
inc(accum, info^.StrideIn);
|
|
end;
|
|
inc(Init);
|
|
result := Init;
|
|
end;
|
|
|
|
function T_ENDIAN16(e: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((e) shr 11) and 1);
|
|
end;
|
|
|
|
function CHANGE_ENDIAN(w: word): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((w) shl 8) or ((w) shr 8));
|
|
end;
|
|
|
|
function UnrollPlanarWordsBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
Init: pbyte;
|
|
begin
|
|
nChan := T_CHANNELS(info^.InputFormat);
|
|
Init := accum;
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
wIn[i] := CHANGE_ENDIAN(pword(accum)^);
|
|
inc(accum, (info^.StrideIn * 2));
|
|
end;
|
|
inc(Init, 2);
|
|
result := Init;
|
|
end;
|
|
|
|
function UnrollPlanarWords(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
Init: pbyte;
|
|
begin
|
|
nChan := T_CHANNELS(info^.InputFormat);
|
|
Init := accum;
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
wIn[i] := pword(accum)^;
|
|
inc(accum, (info^.StrideIn * 2));
|
|
end;
|
|
|
|
inc(Init, 2);
|
|
result := Init;
|
|
end;
|
|
|
|
function T_FLAVOR(s: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((s) shr 13) and 1);
|
|
end;
|
|
|
|
function REVERSE_FLAVOR_16(x: word): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (($FFFF - (x)));
|
|
end;
|
|
|
|
function Unroll1ByteReversed(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := REVERSE_FLAVOR_16(RGB_8_TO_16(accum^));
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum);
|
|
result := accum;
|
|
end;
|
|
|
|
// Monochrome duplicates L into RGB for null-transforms
|
|
|
|
function Unroll1Byte(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum);
|
|
result := accum;
|
|
end;
|
|
|
|
function T_SWAPFIRST(s: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((s) shr 14) and 1);
|
|
end;
|
|
|
|
function Unroll2ByteSwapFirst(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[3] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum);
|
|
result := accum;
|
|
end;
|
|
|
|
// Monochrome + alpha. Alpha is lost
|
|
|
|
function Unroll2Byte(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum);
|
|
wIn[3] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
result := accum;
|
|
end;
|
|
|
|
function T_DOSWAP(e: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((e) shr 10) and 1);
|
|
end;
|
|
|
|
// BRG
|
|
|
|
function Unroll3BytesSwap(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
wIn[1] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
wIn[0] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll1ByteSkip2(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum);
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll3Bytes(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
|
|
wIn[0] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
wIn[1] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
inc(accum);
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4BytesSwapSwapFirst(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
inc(accum); // K
|
|
wIn[1] := RGB_8_TO_16(accum^);
|
|
inc(accum); // Y
|
|
wIn[0] := RGB_8_TO_16(accum^);
|
|
inc(accum); // M
|
|
wIn[3] := RGB_8_TO_16(accum^);
|
|
inc(accum); // C
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4BytesSwapFirst(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
|
|
wIn[3] := RGB_8_TO_16(accum^);
|
|
inc(accum); // K
|
|
wIn[0] := RGB_8_TO_16(accum^);
|
|
inc(accum); // C
|
|
wIn[1] := RGB_8_TO_16(accum^);
|
|
inc(accum); // M
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
inc(accum); // Y
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
// KYMC
|
|
|
|
function Unroll4BytesSwap(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[3] := RGB_8_TO_16(accum^);
|
|
inc(accum); // K
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
inc(accum); // Y
|
|
wIn[1] := RGB_8_TO_16(accum^);
|
|
inc(accum); // M
|
|
wIn[0] := RGB_8_TO_16(accum^);
|
|
inc(accum); // C
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function REVERSE_FLAVOR_8(x: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (($FF - (x)));
|
|
end;
|
|
|
|
function Unroll4BytesReverse(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
i: word;
|
|
begin
|
|
(*
|
|
result := accum;
|
|
wIn[0] := RGB_8_TO_16(($FF - result^));
|
|
inc(result); // C
|
|
wIn[1] := RGB_8_TO_16(($FF - result^));
|
|
inc(result); // M
|
|
wIn[2] := RGB_8_TO_16(($FF - result^));
|
|
inc(result); // Y
|
|
wIn[3] := RGB_8_TO_16(($FF - result^));
|
|
inc(result); // K
|
|
*)
|
|
|
|
result := accum;
|
|
i := $FF-result^;
|
|
wIn[0] := ((i shl 8) or i);
|
|
|
|
inc(result);
|
|
i := $FF-result^;
|
|
wIn[1] := ((i shl 8) or i);
|
|
|
|
inc(result);
|
|
i := $FF-result^;
|
|
wIn[2] := ((i shl 8) or i);
|
|
|
|
inc(result);
|
|
i := $FF-result^;
|
|
wIn[3] := ((i shl 8) or i);
|
|
inc(result);
|
|
|
|
end;
|
|
|
|
function Unroll4Bytes(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
|
|
wIn[0] := RGB_8_TO_16(accum^);
|
|
inc(accum); // C
|
|
wIn[1] := RGB_8_TO_16(accum^);
|
|
inc(accum); // M
|
|
wIn[2] := RGB_8_TO_16(accum^);
|
|
inc(accum); // Y
|
|
wIn[3] := RGB_8_TO_16(accum^);
|
|
inc(accum); // K
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
//result := ((( (rgb)) shl 8)or (rgb));
|
|
|
|
function UnrollAnyBytes(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
begin
|
|
|
|
nChan := (info^.InputFormat shr 3) and 15; //nChan := T_CHANNELS(info ^. InputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
wIn[i] := (accum^ shl 8) or accum^; // wIn[i] := RGB_8_TO_16(accum^)
|
|
inc(accum);
|
|
end;
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll1WordBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll1WordReversed(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := REVERSE_FLAVOR_16(PWORD(accum)^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll1Word(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := PWORD(accum)^;
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll2WordBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum, 2);
|
|
wIn[3] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll2WordSwapFirst(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[3] := PWORD(accum)^;
|
|
inc(accum, 2);
|
|
wIn[2] := PWORD(accum)^;
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum, 2);
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll2Word(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := PWORD(accum)^;
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
inc(accum, 2);
|
|
wIn[3] := PWORD(accum)^;
|
|
inc(accum, 2);
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll3WordsSwapBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
wIn[1] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
wIn[0] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll3WordsSwap(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := PWORD(accum)^;
|
|
inc(accum, 2);
|
|
wIn[1] := PWORD(accum)^;
|
|
inc(accum, 2);
|
|
wIn[0] := PWORD(accum)^;
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll3WordsBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[0] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
wIn[1] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
wIn[2] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll3Words(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[0] := PWORD(accum)^;
|
|
inc(accum, 2); // C R
|
|
wIn[1] := PWORD(accum)^;
|
|
inc(accum, 2); // M G
|
|
wIn[2] := PWORD(accum)^;
|
|
inc(accum, 2); // Y B
|
|
result := accum;
|
|
end;
|
|
|
|
// KYMC
|
|
|
|
function Unroll4WordsSwapBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[3] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //K
|
|
wIn[2] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //Y
|
|
wIn[1] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //M
|
|
wIn[0] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //C
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4WordsSwapSwapFirst(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := PWORD(accum)^;
|
|
inc(accum, 2); // K
|
|
wIn[1] := PWORD(accum)^;
|
|
inc(accum, 2); // Y
|
|
wIn[0] := PWORD(accum)^;
|
|
inc(accum, 2); // M
|
|
wIn[3] := PWORD(accum)^;
|
|
inc(accum, 2); // C
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
// KYMC
|
|
|
|
function Unroll4WordsSwap(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[3] := PWORD(accum)^;
|
|
inc(accum, 2); // K
|
|
wIn[2] := PWORD(accum)^;
|
|
inc(accum, 2); // Y
|
|
wIn[1] := PWORD(accum)^;
|
|
inc(accum, 2); // M
|
|
wIn[0] := PWORD(accum)^;
|
|
inc(accum, 2); // C
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll1WordSkip3(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[2] := accum^;
|
|
wIn[1] := wIn[2];
|
|
wIn[0] := wIn[1];
|
|
|
|
inc(accum, 8);
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4WordsBigEndianReverse(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[0] := REVERSE_FLAVOR_16(CHANGE_ENDIAN(PWORD(accum)^));
|
|
inc(accum, 2); //C
|
|
wIn[1] := REVERSE_FLAVOR_16(CHANGE_ENDIAN(PWORD(accum)^));
|
|
inc(accum, 2); //M
|
|
wIn[2] := REVERSE_FLAVOR_16(CHANGE_ENDIAN(PWORD(accum)^));
|
|
inc(accum, 2); //Y
|
|
wIn[3] := REVERSE_FLAVOR_16(CHANGE_ENDIAN(PWORD(accum)^));
|
|
inc(accum, 2); //K
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4WordsBigEndian(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[0] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //C
|
|
wIn[1] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //M
|
|
wIn[2] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //Y
|
|
wIn[3] := CHANGE_ENDIAN(PWORD(accum)^);
|
|
inc(accum, 2); //K
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4WordsSwapFirst(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[3] := PWORD(accum)^;
|
|
inc(accum, 2); // K
|
|
wIn[0] := PWORD(accum)^;
|
|
inc(accum, 2); // C
|
|
wIn[1] := PWORD(accum)^;
|
|
inc(accum, 2); // M
|
|
wIn[2] := PWORD(accum)^;
|
|
inc(accum, 2); // Y
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4WordsReverse(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
wIn[0] := REVERSE_FLAVOR_16(PWORD(accum)^);
|
|
inc(accum, 2); // C
|
|
wIn[1] := REVERSE_FLAVOR_16(PWORD(accum)^);
|
|
inc(accum, 2); // M
|
|
wIn[2] := REVERSE_FLAVOR_16(PWORD(accum)^);
|
|
inc(accum, 2); // Y
|
|
wIn[3] := REVERSE_FLAVOR_16(PWORD(accum)^);
|
|
inc(accum, 2); // K
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
function Unroll4Words(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
begin
|
|
result := accum;
|
|
wIn[0] := PWORD(result)^;
|
|
inc(result, 2); // C
|
|
wIn[1] := PWORD(result)^;
|
|
inc(result, 2); // M
|
|
wIn[2] := PWORD(result)^;
|
|
inc(result, 2); // Y
|
|
wIn[3] := PWORD(result)^;
|
|
inc(result, 2); // K
|
|
|
|
end;
|
|
|
|
function UnrollAnyWords(info: _LPcmsTRANSFORM; wIn: pwordarray; accum: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.InputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
wIn[i] := PWORD(accum)^;
|
|
inc(accum, 2);
|
|
end;
|
|
|
|
result := accum;
|
|
end;
|
|
|
|
// choose routine from Input identifier
|
|
|
|
function _cmsIdentifyInputFormat(xform: _LPcmsTRANSFORM; dwInput: DWORD): _cmsFIXFN;
|
|
var
|
|
FromInput: _cmsFIXFN;
|
|
begin
|
|
FromInput := nil;
|
|
|
|
if (xform <> nil) then
|
|
begin
|
|
|
|
if (xform^.InputProfile <> nil) then
|
|
begin
|
|
|
|
if (cmsGetDeviceClass(xform^.InputProfile) = icSigNamedColorClass) then
|
|
begin
|
|
|
|
if (dwInput <> TYPE_NAMED_COLOR_INDEX) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Named color needs TYPE_NAMED_COLOR_INDEX");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if ((dwInput and 7) = 0) then
|
|
begin
|
|
|
|
case (T_COLORSPACE(dwInput)) of
|
|
|
|
PT_Lab:
|
|
FromInput := UnrollLabDouble;
|
|
PT_XYZ:
|
|
FromInput := UnrollXYZDouble;
|
|
|
|
PT_GRAY,
|
|
PT_RGB,
|
|
PT_YCbCr,
|
|
PT_YUV,
|
|
PT_YUVK,
|
|
PT_HSV,
|
|
PT_HLS,
|
|
PT_Yxy:
|
|
FromInput := UnrollDouble;
|
|
|
|
else
|
|
FromInput := UnrollInkDouble;
|
|
|
|
end
|
|
|
|
end
|
|
else
|
|
if (T_PLANAR(dwInput) <> 0) then
|
|
begin
|
|
|
|
case ((dwInput and 7)) of
|
|
|
|
1:
|
|
FromInput := UnrollPlanarBytes;
|
|
|
|
2:
|
|
if (T_ENDIAN16(dwInput) <> 0) then
|
|
FromInput := UnrollPlanarWordsBigEndian
|
|
else
|
|
FromInput := UnrollPlanarWords;
|
|
|
|
end
|
|
end
|
|
else
|
|
begin
|
|
|
|
case ((dwInput and 7)) of
|
|
|
|
1:
|
|
|
|
case (T_CHANNELS(dwInput) + T_EXTRA(dwInput)) of
|
|
1: if (T_FLAVOR(dwInput) <> 0) then
|
|
FromInput := Unroll1ByteReversed
|
|
else
|
|
FromInput := Unroll1Byte;
|
|
|
|
2: if (T_SWAPFIRST(dwInput) <> 0) then
|
|
FromInput := Unroll2ByteSwapFirst
|
|
else
|
|
FromInput := Unroll2Byte;
|
|
|
|
3: if (T_DOSWAP(dwInput) <> 0) then
|
|
FromInput := Unroll3BytesSwap
|
|
else
|
|
begin
|
|
if (T_EXTRA(dwInput) = 2) then
|
|
FromInput := Unroll1ByteSkip2
|
|
else
|
|
FromInput := Unroll3Bytes;
|
|
end;
|
|
|
|
4: if (T_DOSWAP(dwInput) <> 0) then
|
|
begin
|
|
if (T_SWAPFIRST(dwInput) <> 0) then
|
|
FromInput := Unroll4BytesSwapSwapFirst
|
|
else
|
|
FromInput := Unroll4BytesSwap;
|
|
end
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwInput) <> 0) then
|
|
FromInput := Unroll4BytesSwapFirst
|
|
else
|
|
begin
|
|
if ((((dwInput) shr 13) and 1) <> 0) then
|
|
FromInput := Unroll4BytesReverse
|
|
else
|
|
FromInput := Unroll4Bytes;
|
|
end;
|
|
end;
|
|
|
|
5,
|
|
6,
|
|
7,
|
|
8:
|
|
if (T_DOSWAP(dwInput) = 0) and (T_SWAPFIRST(dwInput) = 0) then
|
|
FromInput := UnrollAnyBytes;
|
|
|
|
end;
|
|
|
|
2:
|
|
case (T_CHANNELS(dwInput) + T_EXTRA(dwInput)) of
|
|
1: if (T_ENDIAN16(dwInput) <> 0) then
|
|
FromInput := Unroll1WordBigEndian
|
|
else
|
|
if (T_FLAVOR(dwInput) <> 0) then
|
|
FromInput := Unroll1WordReversed
|
|
else
|
|
FromInput := Unroll1Word;
|
|
|
|
2: if (T_ENDIAN16(dwInput) <> 0) then
|
|
FromInput := Unroll2WordBigEndian
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwInput) <> 0) then
|
|
FromInput := Unroll2WordSwapFirst
|
|
else
|
|
FromInput := Unroll2Word;
|
|
end;
|
|
|
|
3: if (T_DOSWAP(dwInput) <> 0) then
|
|
begin
|
|
if (T_ENDIAN16(dwInput) <> 0) then
|
|
FromInput := Unroll3WordsSwapBigEndian
|
|
else
|
|
FromInput := Unroll3WordsSwap;
|
|
end
|
|
else
|
|
begin
|
|
if (T_ENDIAN16(dwInput) <> 0) then
|
|
FromInput := Unroll3WordsBigEndian
|
|
else
|
|
FromInput := Unroll3Words;
|
|
end;
|
|
|
|
4: if (T_DOSWAP(dwInput) <> 0) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwInput) <> 0) then
|
|
FromInput := Unroll4WordsSwapBigEndian
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwInput) <> 0) then
|
|
FromInput := Unroll4WordsSwapSwapFirst
|
|
else
|
|
FromInput := Unroll4WordsSwap;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (T_EXTRA(dwInput) = 3) then
|
|
FromInput := Unroll1WordSkip3
|
|
else
|
|
if (T_ENDIAN16(dwInput) <> 0) then
|
|
begin
|
|
|
|
if (T_FLAVOR(dwInput) <> 0) then
|
|
FromInput := Unroll4WordsBigEndianReverse
|
|
else
|
|
FromInput := Unroll4WordsBigEndian;
|
|
end
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwInput) <> 0) then
|
|
FromInput := Unroll4WordsSwapFirst
|
|
else
|
|
begin
|
|
if ((((dwInput) shr 13) and 1) <> 0) then
|
|
FromInput := Unroll4WordsReverse
|
|
else
|
|
FromInput := Unroll4Words;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
5,
|
|
6,
|
|
7,
|
|
8:
|
|
if (T_DOSWAP(dwInput) = 0) and (T_SWAPFIRST(dwInput) = 0) then
|
|
FromInput := UnrollAnyWords;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//if (FromInput=nil) then;
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unknown input format");
|
|
|
|
result := FromInput;
|
|
end;
|
|
|
|
function L2float4(v: WORD): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
var
|
|
fix32: Fixed32;
|
|
begin
|
|
fix32 := v;
|
|
result := fix32 / 655.35;
|
|
end;
|
|
|
|
function ab2float4(v: WORD): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
var
|
|
fix32: Fixed32;
|
|
begin
|
|
fix32 := v;
|
|
result := (fix32 / 257.0) - 128.0;
|
|
end;
|
|
|
|
function L2float3(v: WORD): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
var
|
|
fix32: Fixed32;
|
|
begin
|
|
fix32 := v;
|
|
result := fix32 / 652.800;
|
|
end;
|
|
|
|
procedure cmsLabEncoded2Float4(Lab: LPcmsCIELab; wLab: pwordarray);
|
|
begin
|
|
Lab^.L := L2float4(wLab[0]);
|
|
Lab^.a := ab2float4(wLab[1]);
|
|
Lab^.b := ab2float4(wLab[2]);
|
|
end;
|
|
|
|
function ab2float3(v: WORD): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
var
|
|
fix32: Fixed32;
|
|
begin
|
|
fix32 := v;
|
|
result := (fix32 / 256.0) - 128.0;
|
|
end;
|
|
|
|
procedure cmsLabEncoded2Float(Lab: LPcmsCIELab; wLab: pwordarray);
|
|
begin
|
|
Lab^.L := L2float3(wLab[0]);
|
|
Lab^.a := ab2float3(wLab[1]);
|
|
Lab^.b := ab2float3(wLab[2]);
|
|
end;
|
|
|
|
// Unencoded Float values -- don't try optimize speed
|
|
|
|
function PackLabDouble(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
if (Info^.lOutputV4Lab) then
|
|
cmsLabEncoded2Float4(LPcmsCIELab(output), wOut)
|
|
else
|
|
cmsLabEncoded2Float(LPcmsCIELab(output), wOut);
|
|
|
|
inc(output, sizeof(cmsCIELab));
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function XYZ2float(v: WORD): double;
|
|
var
|
|
fix32: Fixed32;
|
|
begin
|
|
fix32 := v shl 1;
|
|
result := FIXED_TO_DOUBLE(fix32);
|
|
end;
|
|
|
|
procedure cmsXYZEncoded2Float(fXYZ: LPcmsCIEXYZ; XYZ: pwordarray);
|
|
begin
|
|
|
|
fXYZ^.X := XYZ2float(XYZ[0]);
|
|
fXYZ^.Y := XYZ2float(XYZ[1]);
|
|
fXYZ^.Z := XYZ2float(XYZ[2]);
|
|
|
|
end;
|
|
|
|
function PackXYZDouble(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
cmsXYZEncoded2Float(LPcmsCIEXYZ(output), wOut);
|
|
inc(output, sizeof(cmsCIEXYZ));
|
|
result := output;
|
|
end;
|
|
|
|
function PackDouble(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
Inks: pdoublearray;
|
|
i, nChan: integer;
|
|
begin
|
|
Inks := pdoublearray(output);
|
|
nChan := T_CHANNELS(Info^.OutputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
Inks[i] := wOut[i] / 65535;
|
|
end;
|
|
|
|
inc(output, (nChan + T_EXTRA(Info^.OutputFormat)) * sizeof(double));
|
|
result := output;
|
|
end;
|
|
|
|
function PackInkDouble(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
Inks: pdoublearray;
|
|
i, nChan: integer;
|
|
begin
|
|
Inks := pdoublearray(output);
|
|
nChan := T_CHANNELS(Info^.OutputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
Inks[i] := wOut[i] / 655.35;
|
|
end;
|
|
|
|
inc(output, (nChan + T_EXTRA(Info^.OutputFormat)) * sizeof(double));
|
|
result := output;
|
|
|
|
end;
|
|
|
|
function RGB_16_TO_8(rgb: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := ((((rgb) * 65281 + 8388608) shr 24) and $FF);
|
|
end;
|
|
|
|
function PackPlanarBytes(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
Init: pbyte;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
Init := output;
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
output^ := RGB_16_TO_8(wOut[i]);
|
|
inc(output, info^.StrideOut);
|
|
|
|
end;
|
|
inc(Init);
|
|
result := Init;
|
|
end;
|
|
|
|
function PackPlanarWords(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan: integer;
|
|
i: integer;
|
|
Init: pbyte;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
Init := output;
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
|
|
pword(output)^ := wOut[i];
|
|
inc(output, (info^.StrideOut * sizeof(WORD)));
|
|
end;
|
|
inc(Init, 2);
|
|
result := Init;
|
|
|
|
end;
|
|
|
|
function Pack1Byte(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1ByteAndSkip1SwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1ByteAndSkip1(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
inc(output);
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3BytesSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
result := output;
|
|
result^ := ((((wOut[2]) * 65281 + 8388608) shr 24) and $FF);
|
|
inc(result);
|
|
result^ := ((((wOut[1]) * 65281 + 8388608) shr 24) and $FF);
|
|
inc(result);
|
|
result^ := ((((wOut[0]) * 65281 + 8388608) shr 24) and $FF);
|
|
inc(result);
|
|
end;
|
|
|
|
function Pack3Bytes(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3BytesAndSkip1SwapSwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3BytesAndSkip1SwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3BytesAndSkip1Swap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3BytesAndSkip1(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4BytesSwapSwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[3]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4BytesSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[3]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4BytesSwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[3]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4BytesReverse(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[0]));
|
|
inc(output);
|
|
output^ := REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[1]));
|
|
inc(output);
|
|
output^ := REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[2]));
|
|
inc(output);
|
|
output^ := REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[3]));
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4Bytes(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[3]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack6BytesSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[3]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[4]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[5]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack6Bytes(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[0]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[1]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[2]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[3]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[4]);
|
|
inc(output);
|
|
output^ := RGB_16_TO_8(wOut[5]);
|
|
inc(output);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function PackNBytesSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
i, nChan: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
|
|
i := nChan - 1;
|
|
while (i >= 0) do
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[i]);
|
|
inc(output);
|
|
dec(i);
|
|
end;
|
|
result := output;
|
|
|
|
end;
|
|
|
|
function PackNBytes(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan, i: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
output^ := RGB_16_TO_8(wOut[i]);
|
|
inc(output);
|
|
end;
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1WordBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1Word(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1WordAndSkip1BigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 4);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1WordAndSkip1SwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack1WordAndSkip1(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 4);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsSwapBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3Words(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsAndSkip1SwapBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsAndSkip1SwapSwapFirst(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsAndSkip1Swap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsAndSkip1BigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack3WordsAndSkip1(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4WordsSwapBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[3]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4WordsSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[3];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4WordsBigEndianReverse(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[0]));
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[1]));
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[2]));
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[3]));
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4WordsBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[3]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4WordsReverse(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := REVERSE_FLAVOR_16(wOut[0]);
|
|
inc(output, 2);
|
|
pword(output)^ := REVERSE_FLAVOR_16(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := REVERSE_FLAVOR_16(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := REVERSE_FLAVOR_16(wOut[3]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack4Words(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[3];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack6WordsSwapBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[3]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[4]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[5]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack6WordsSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[3];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[4];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[5];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack6WordsBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[0]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[1]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[2]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[3]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[4]);
|
|
inc(output, 2);
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[5]);
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function Pack6Words(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
begin
|
|
pword(output)^ := wOut[0];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[1];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[2];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[3];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[4];
|
|
inc(output, 2);
|
|
pword(output)^ := wOut[5];
|
|
inc(output, 2);
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function PackNWordsSwapBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan, i: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
|
|
i := nChan - 1;
|
|
while (i >= 0) do
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[i]);
|
|
inc(output, sizeof(WORD));
|
|
dec(i);
|
|
end;
|
|
result := output;
|
|
end;
|
|
|
|
function PackNWordsSwap(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan, i: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
|
|
i := nChan - 1;
|
|
while (i >= 0) do
|
|
begin
|
|
pword(output)^ := wOut[i];
|
|
inc(output, sizeof(WORD));
|
|
dec(i);
|
|
end;
|
|
|
|
result := output;
|
|
end;
|
|
|
|
function PackNWordsBigEndian(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan, i: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
pword(output)^ := CHANGE_ENDIAN(wOut[i]);
|
|
inc(output, sizeof(WORD));
|
|
end;
|
|
result := output;
|
|
end;
|
|
|
|
function PackNWords(Info: _LPcmsTRANSFORM; wOut: pwordarray; output: pbyte): pbyte;
|
|
var
|
|
nChan, i: integer;
|
|
begin
|
|
nChan := T_CHANNELS(info^.OutputFormat);
|
|
|
|
for i := 0 to nChan - 1 do
|
|
begin
|
|
pword(output)^ := wOut[i];
|
|
inc(output, sizeof(WORD));
|
|
end;
|
|
|
|
result := output;
|
|
end;
|
|
|
|
// choose routine from Input identifier
|
|
|
|
function _cmsIdentifyOutputFormat(xform: _LPcmsTRANSFORM; dwOutput: DWORD): _cmsFIXFN;
|
|
var
|
|
ToOutput: _cmsFIXFN;
|
|
begin
|
|
ToOutput := nil;
|
|
|
|
if ((dwOutput and 7) = 0) then
|
|
begin
|
|
|
|
case (T_COLORSPACE(dwOutput)) of
|
|
|
|
PT_Lab:
|
|
ToOutput := PackLabDouble;
|
|
PT_XYZ:
|
|
ToOutput := PackXYZDouble;
|
|
|
|
PT_GRAY,
|
|
PT_RGB,
|
|
PT_YCbCr,
|
|
PT_YUV,
|
|
PT_YUVK,
|
|
PT_HSV,
|
|
PT_HLS,
|
|
PT_Yxy:
|
|
ToOutput := PackDouble;
|
|
|
|
else
|
|
ToOutput := PackInkDouble;
|
|
end;
|
|
|
|
end
|
|
else
|
|
if (T_PLANAR(dwOutput) <> 0) then
|
|
begin
|
|
|
|
case ((dwOutput and 7)) of
|
|
|
|
1: ToOutput := PackPlanarBytes;
|
|
|
|
2: if (T_ENDIAN16(dwOutput) = 0) then
|
|
ToOutput := PackPlanarWords;
|
|
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
|
|
case ((dwOutput and 7)) of
|
|
|
|
1:
|
|
case (T_CHANNELS(dwOutput)) of
|
|
1:
|
|
begin
|
|
ToOutput := Pack1Byte;
|
|
if (T_EXTRA(dwOutput) = 1) then
|
|
begin
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack1ByteAndSkip1SwapFirst
|
|
else
|
|
ToOutput := Pack1ByteAndSkip1;
|
|
end;
|
|
end;
|
|
|
|
3:
|
|
case (T_EXTRA(dwOutput)) of
|
|
|
|
0: if (T_DOSWAP(dwOutput) <> 0) then
|
|
ToOutput := Pack3BytesSwap
|
|
else
|
|
ToOutput := Pack3Bytes;
|
|
|
|
1: if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack3BytesAndSkip1SwapSwapFirst
|
|
else
|
|
ToOutput := Pack3BytesAndSkip1Swap;
|
|
end
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack3BytesAndSkip1SwapFirst
|
|
else
|
|
ToOutput := Pack3BytesAndSkip1;
|
|
end;
|
|
end;
|
|
|
|
4: if (T_EXTRA(dwOutput) = 0) then
|
|
begin
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack4BytesSwapSwapFirst
|
|
else
|
|
ToOutput := Pack4BytesSwap;
|
|
end
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack4BytesSwapFirst
|
|
else
|
|
begin
|
|
|
|
if (T_FLAVOR(dwOutput) <> 0) then
|
|
ToOutput := Pack4BytesReverse
|
|
else
|
|
ToOutput := Pack4Bytes;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
6: if (T_EXTRA(dwOutput) = 0) then
|
|
begin
|
|
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
ToOutput := Pack6BytesSwap
|
|
else
|
|
ToOutput := Pack6Bytes;
|
|
end;
|
|
|
|
5,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
15:
|
|
|
|
if ((T_EXTRA(dwOutput) = 0) and (T_SWAPFIRST(dwOutput) = 0)) then
|
|
begin
|
|
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
ToOutput := PackNBytesSwap
|
|
else
|
|
ToOutput := PackNBytes;
|
|
end;
|
|
|
|
end;
|
|
|
|
2:
|
|
|
|
case (T_CHANNELS(dwOutput)) of
|
|
|
|
1:
|
|
begin
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
|
|
ToOutput := Pack1WordBigEndian
|
|
else
|
|
ToOutput := Pack1Word;
|
|
|
|
if (T_EXTRA(dwOutput) = 1) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
|
|
ToOutput := Pack1WordAndSkip1BigEndian
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack1WordAndSkip1SwapFirst
|
|
else
|
|
ToOutput := Pack1WordAndSkip1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
3:
|
|
|
|
case (T_EXTRA(dwOutput)) of
|
|
|
|
0:
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
|
|
ToOutput := Pack3WordsSwapBigEndian
|
|
else
|
|
ToOutput := Pack3WordsSwap;
|
|
end
|
|
else
|
|
begin
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
|
|
ToOutput := Pack3WordsBigEndian
|
|
else
|
|
ToOutput := Pack3Words;
|
|
end;
|
|
|
|
1: if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
|
|
ToOutput := Pack3WordsAndSkip1SwapBigEndian
|
|
else
|
|
begin
|
|
if (T_SWAPFIRST(dwOutput) <> 0) then
|
|
ToOutput := Pack3WordsAndSkip1SwapSwapFirst
|
|
else
|
|
ToOutput := Pack3WordsAndSkip1Swap;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
ToOutput := Pack3WordsAndSkip1BigEndian
|
|
else
|
|
ToOutput := Pack3WordsAndSkip1;
|
|
end;
|
|
end;
|
|
|
|
4:
|
|
if (T_EXTRA(dwOutput) = 0) then
|
|
begin
|
|
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
ToOutput := Pack4WordsSwapBigEndian
|
|
else
|
|
ToOutput := Pack4WordsSwap;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_FLAVOR(dwOutput) <> 0) then
|
|
ToOutput := Pack4WordsBigEndianReverse
|
|
else
|
|
ToOutput := Pack4WordsBigEndian;
|
|
end
|
|
else
|
|
begin
|
|
if (T_FLAVOR(dwOutput) <> 0) then
|
|
ToOutput := Pack4WordsReverse
|
|
else
|
|
ToOutput := Pack4Words;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
6:
|
|
if (T_EXTRA(dwOutput) = 0) then
|
|
begin
|
|
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
ToOutput := Pack6WordsSwapBigEndian
|
|
else
|
|
ToOutput := Pack6WordsSwap;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
ToOutput := Pack6WordsBigEndian
|
|
else
|
|
ToOutput := Pack6Words;
|
|
end;
|
|
end;
|
|
|
|
5,
|
|
7,
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
12,
|
|
13,
|
|
14,
|
|
15: if ((T_EXTRA(dwOutput) = 0) and (T_SWAPFIRST(dwOutput) = 0)) then
|
|
begin
|
|
|
|
if (T_DOSWAP(dwOutput) <> 0) then
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
ToOutput := PackNWordsSwapBigEndian
|
|
else
|
|
ToOutput := PackNWordsSwap;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (T_ENDIAN16(dwOutput) <> 0) then
|
|
ToOutput := PackNWordsBigEndian
|
|
else
|
|
ToOutput := PackNWords;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
//if (not ToOutput)
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unknown output format");
|
|
|
|
result := ToOutput;
|
|
end;
|
|
|
|
// Null transformation, only hold channels
|
|
|
|
procedure NullXFORM(p: _LPcmsTRANSFORM; xin: pointer; xout: pointer; Size: dword);
|
|
var
|
|
accum: pbyte;
|
|
output: pbyte;
|
|
wIn: array[0..MAXCHANNELS - 1] of WORD;
|
|
i, n: dword;
|
|
begin
|
|
accum := xin;
|
|
output := xout;
|
|
n := Size;
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
accum := p^.FromInput(p, @wIn, accum);
|
|
output := p^.ToOutput(p, @wIn, output);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure cmsFreeLUT(Lut: LPLUT);
|
|
var
|
|
i: integer; // 3.0.1
|
|
begin
|
|
|
|
if (Lut = nil) then
|
|
exit;
|
|
|
|
if (Lut^.T <> nil) then
|
|
freemem(Lut^.T);
|
|
|
|
for i := 0 to Lut^.OutputChan - 1 do
|
|
begin
|
|
if (Lut^.L2[i] <> nil) then
|
|
freemem(Lut^.L2[i]);
|
|
end;
|
|
|
|
for i := 0 to Lut^.InputChan - 1 do
|
|
begin
|
|
if (Lut^.L1[i] <> nil) then
|
|
freemem(Lut^.L1[i]);
|
|
end;
|
|
|
|
if (Lut^.wFlags and LUT_HASTL3) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to Lut^.InputChan - 1 do
|
|
begin
|
|
|
|
if (Lut^.L3[i] <> nil) then
|
|
freemem(Lut^.L3[i]);
|
|
end;
|
|
end;
|
|
|
|
if (Lut^.wFlags and LUT_HASTL4) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to Lut^.OutputChan - 1 do
|
|
begin
|
|
|
|
if (Lut^.L4[i] <> nil) then
|
|
freemem(Lut^.L4[i]);
|
|
end;
|
|
end;
|
|
|
|
if (Lut^.CLut16params.p8 <> nil) then
|
|
freemem(Lut^.CLut16params.p8);
|
|
|
|
freemem(Lut);
|
|
end;
|
|
|
|
procedure cmsFreeMatShaper(MatShaper: LPMATSHAPER);
|
|
var
|
|
i: integer;
|
|
begin
|
|
if (MatShaper = nil) then
|
|
exit;
|
|
|
|
for i := 0 to 2 do
|
|
begin
|
|
if (MatShaper^.L[i] <> nil) then
|
|
freemem(MatShaper^.L[i]);
|
|
if (MatShaper^.L2[i] <> nil) then
|
|
freemem(MatShaper^.L2[i]);
|
|
end;
|
|
|
|
freemem(MatShaper);
|
|
end;
|
|
|
|
procedure cmsFreeNamedColorList(v: LPcmsNAMEDCOLORLIST);
|
|
begin
|
|
if (v = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_RECOVERABLE, "Couldn't free a NULL named color list");
|
|
exit;
|
|
end;
|
|
freemem(v);
|
|
end;
|
|
|
|
procedure IEcmsDeleteTransform(hTransform: cmsHTRANSFORM);
|
|
var
|
|
p: _LPcmsTRANSFORM;
|
|
begin
|
|
p := _LPcmsTRANSFORM(hTransform);
|
|
if (p^.Device2PCS <> nil) then
|
|
cmsFreeLUT(p^.Device2PCS);
|
|
if (p^.PCS2Device <> nil) then
|
|
cmsFreeLUT(p^.PCS2Device);
|
|
if (p^.Gamut <> nil) then
|
|
cmsFreeLUT(p^.Gamut);
|
|
if (p^.Preview <> nil) then
|
|
cmsFreeLUT(p^.Preview);
|
|
if (p^.DeviceLink <> nil) then
|
|
cmsFreeLUT(p^.DeviceLink);
|
|
if (p^.InMatShaper <> nil) then
|
|
cmsFreeMatShaper(p^.InMatShaper);
|
|
if (p^.OutMatShaper <> nil) then
|
|
cmsFreeMatShaper(p^.OutMatShaper);
|
|
if (p^.SmeltMatShaper <> nil) then
|
|
cmsFreeMatShaper(p^.SmeltMatShaper);
|
|
if (p^.NamedColorList <> nil) then
|
|
cmsFreeNamedColorList(p^.NamedColorList);
|
|
|
|
freemem(p);
|
|
end;
|
|
|
|
// Translate from our colorspace to ICC representation
|
|
|
|
function _cmsICCcolorSpace(OurNotation: integer): icColorSpaceSignature;
|
|
begin
|
|
case (OurNotation) of
|
|
|
|
1,
|
|
PT_GRAY: result := icSigGrayData;
|
|
|
|
2,
|
|
PT_RGB: result := icSigRgbData;
|
|
|
|
PT_CMY: result := icSigCmyData;
|
|
PT_CMYK: result := icSigCmykData;
|
|
PT_YCbCr: result := icSigYCbCrData;
|
|
PT_YUV: result := icSigLuvData;
|
|
PT_XYZ: result := icSigXYZData;
|
|
PT_Lab: result := icSigLabData;
|
|
PT_YUVK: result := icColorSpaceSignature(icSigLuvKData);
|
|
PT_HSV: result := icSigHsvData;
|
|
PT_HLS: result := icSigHlsData;
|
|
PT_Yxy: result := icSigYxyData;
|
|
PT_HiFi: result := icColorSpaceSignature(icSigHexachromeData);
|
|
PT_HiFi7: result := icColorSpaceSignature(icSigHeptachromeData);
|
|
PT_HiFi8: result := icColorSpaceSignature(icSigOctachromeData);
|
|
|
|
else
|
|
result := icMaxEnumData;
|
|
end;
|
|
end;
|
|
|
|
function cmsGetPCS(hProfile: cmsHPROFILE): icColorSpaceSignature;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
result := Icc^.PCS;
|
|
end;
|
|
|
|
// Check colorspace
|
|
|
|
function IsProperColorSpace(hProfile: cmsHPROFILE; dwFormat: DWORD; lUsePCS: longbool): longbool;
|
|
var
|
|
Space: integer;
|
|
begin
|
|
Space := T_COLORSPACE(dwFormat);
|
|
|
|
if (Space = PT_ANY) then
|
|
begin
|
|
result := true;
|
|
exit;
|
|
end;
|
|
|
|
if (lUsePCS) then
|
|
result := (_cmsICCcolorSpace(Space) = cmsGetPCS(hProfile))
|
|
else
|
|
result := (_cmsICCcolorSpace(Space) = cmsGetColorSpace(hProfile));
|
|
|
|
end;
|
|
|
|
function cmsAllocLUT: LPLUT;
|
|
var
|
|
NewLUT: LPLUT;
|
|
begin
|
|
getmem(NewLUT, sizeof(LUT));
|
|
if (NewLUT <> nil) then
|
|
ZeroMemory(NewLUT, sizeof(LUT));
|
|
result := NewLUT;
|
|
end;
|
|
|
|
function DupBlockTab(Org: pointer; size: integer): pointer;
|
|
var
|
|
mem: pointer;
|
|
begin
|
|
getmem(mem, size);
|
|
CopyMemory(mem, Org, size);
|
|
result := mem;
|
|
end;
|
|
|
|
function cmsDupLUT(Orig: LPLUT): LPLUT;
|
|
var
|
|
NewLUT: LPLUT;
|
|
i: integer;
|
|
begin
|
|
NewLUT := cmsAllocLUT;
|
|
|
|
CopyMemory(NewLUT, Orig, sizeof(LUT));
|
|
|
|
for i := 0 to Orig^.InputChan - 1 do
|
|
NewLUT^.L1[i] := PWORD(DupBlockTab(Orig^.L1[i], sizeof(WORD) * Orig^.In16params.nSamples));
|
|
|
|
for i := 0 to Orig^.OutputChan - 1 do
|
|
NewLUT^.L2[i] := PWORD(DupBlockTab(Orig^.L2[i], sizeof(WORD) * Orig^.Out16params.nSamples));
|
|
|
|
NewLUT^.T := PWORD(DupBlockTab(Orig^.T, Orig^.Tsize));
|
|
|
|
result := NewLUT;
|
|
end;
|
|
|
|
function SIZEOF_UINT8_ALIGNED: integer; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (sizeof(_cmsTestAlign8) - sizeof(icS15Fixed16Number));
|
|
end;
|
|
|
|
// Convert to fixed point encoding is 1.0 = 0xFFFF
|
|
|
|
procedure VEC3toFix(r: LPWVEC3; v: LPVEC3);
|
|
begin
|
|
r^.n[VX] := round(v^.n[VX] * 65536 );
|
|
r^.n[VY] := round(v^.n[VY] * 65536 );
|
|
r^.n[VZ] := round(v^.n[VZ] * 65536 );
|
|
end;
|
|
|
|
procedure MAT3toFix(r: LPWMAT3; v: LPMAT3);
|
|
begin
|
|
VEC3toFix(@r^.v[0], @v^.v[0]);
|
|
VEC3toFix(@r^.v[1], @v^.v[1]);
|
|
VEC3toFix(@r^.v[2], @v^.v[2]);
|
|
end;
|
|
|
|
// Check id two vectors are the same, allowing tolerance
|
|
|
|
function RangeCheck(l, h, v: double): longbool; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (v >= l) and (v <= h);
|
|
end;
|
|
|
|
(*
|
|
function FIXED_TO_DOUBLE(x: Fixed32): double;
|
|
begin
|
|
result := x / 65536;
|
|
end;
|
|
*)
|
|
|
|
function VEC3equal(a: LPWVEC3; b: LPWVEC3; Tolerance: double): longbool;
|
|
var
|
|
i: integer;
|
|
c: double;
|
|
begin
|
|
|
|
for i := 0 to 2 do
|
|
begin
|
|
|
|
c := a^.n[i] / 65536;
|
|
if (not RangeCheck(c - Tolerance, c + Tolerance, (b^.n[i]/65536) )) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
result := TRUE;
|
|
end;
|
|
|
|
// Check if matrix is Identity. Allow a tolerance as %
|
|
|
|
function MAT3isIdentity(a: LPWMAT3; Tolerance: double): longbool;
|
|
var
|
|
i: integer;
|
|
Idd: MAT3;
|
|
Idf: WMAT3;
|
|
begin
|
|
|
|
MAT3identity(@Idd);
|
|
MAT3toFix(@Idf, @Idd);
|
|
|
|
for i := 0 to 2 do
|
|
if (not VEC3equal(@a^.v[i], @Idf.v[i], Tolerance)) then
|
|
begin
|
|
result := false;
|
|
exit;
|
|
end;
|
|
|
|
result := true;
|
|
end;
|
|
|
|
function TO16_TAB(x: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((x) shl 8) or (x));
|
|
end;
|
|
|
|
function _cmsQuantizeVal(i: double; MaxSamples: integer): WORD; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := floor(((i * 65535) / (MaxSamples - 1)) + 0.5);
|
|
end;
|
|
|
|
// Is a table linear?
|
|
|
|
function cmsIsLinear(Table: pwordarray; nEntries: integer): integer;
|
|
var
|
|
i, diff: integer;
|
|
begin
|
|
|
|
for i := 0 to nEntries - 1 do
|
|
begin
|
|
|
|
//diff := abs(Table[i] - _cmsQuantizeVal(i, nEntries));
|
|
diff := abs(Table[i] - (floor(((i * 65535) / (nEntries - 1)) + 0.5)));
|
|
|
|
if (diff > 3) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
function uipow(a, b: integer): dword;
|
|
var
|
|
rv: dword;
|
|
begin
|
|
rv := 1;
|
|
while b > 0 do
|
|
begin
|
|
rv := rv * a;
|
|
dec(b);
|
|
end;
|
|
result := rv;
|
|
end;
|
|
|
|
procedure cmsCalcL16Params(nSamples: integer; p: LPL16PARAMS);
|
|
begin
|
|
p^.nSamples := nSamples;
|
|
p^.Domain := (nSamples - 1);
|
|
p^.nOutputs := 1;
|
|
p^.nInputs := p^.nOutputs;
|
|
|
|
end;
|
|
|
|
function FIXED_TO_INT(x: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := ((x) shr 16);
|
|
end;
|
|
|
|
function FIXED_REST_TO_INT(x: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := ((x) and $FFFF);
|
|
end;
|
|
|
|
(*
|
|
function FixedLERP(a, l, h: Fixed32): Fixed32;
|
|
begin
|
|
result := round(((((h - l) * a) / 65536) + l) );
|
|
end;
|
|
*)
|
|
|
|
function FixedLERP(a, l, h: Fixed32): Fixed32;
|
|
var
|
|
dif: double;
|
|
begin
|
|
dif := h - l;
|
|
|
|
dif := dif * a;
|
|
dif := dif / 65536;
|
|
dif := dif + l;
|
|
|
|
result := round(dif);
|
|
end;
|
|
|
|
// Eval gray LUT having only one input channel
|
|
|
|
procedure Eval1Input(StageABC: pwordarray; StageLMN: pwordarray; LutTable: pwordarray; p16: LPL16PARAMS);
|
|
var
|
|
fk: Fixed32;
|
|
k0, k1, rk, xK0, xK1: Fixed32;
|
|
OutChan: integer;
|
|
begin
|
|
|
|
fk := ToFixedDomain(StageABC[0] * p16^.Domain);
|
|
k0 := FIXED_TO_INT(fk);
|
|
rk := FIXED_REST_TO_INT(fk);
|
|
|
|
if StageABC[0] <> $FFFF then
|
|
k1 := k0 + 1
|
|
else
|
|
k1 := k0;
|
|
|
|
xK0 := p16^.opta1 * k0;
|
|
xK1 := p16^.opta1 * k1;
|
|
|
|
for OutChan := 0 to p16^.nOutputs - 1 do
|
|
|
|
StageLMN[OutChan] := FixedLERP(rk, LutTable[xK0 + OutChan], LutTable[xK1 + OutChan]);
|
|
end;
|
|
|
|
// Tetrahedral interpolation, using Sakamoto algorithm.
|
|
|
|
procedure cmsTetrahedralInterp16(Input: pwordarray; Output: pwordarray; LutTable: pwordarray; p: LPL16PARAMS);
|
|
var
|
|
px, py, pz: double;
|
|
x0, y0, z0, x1, y1, z1: integer;
|
|
fx, fy, fz: double;
|
|
c1, c2, c3: double;
|
|
t1, t2: double;
|
|
i1: integer;
|
|
clutPoints, OutChan, TotalOut: integer;
|
|
(*
|
|
function DENS(X, Y, Z: integer): double;
|
|
begin
|
|
result := (LutTable[TotalOut*((Z)+clutPoints*((Y)+clutPoints*(X)))+OutChan])
|
|
end;
|
|
*)
|
|
begin
|
|
|
|
clutPoints := p^.Domain + 1;
|
|
TotalOut := p^.nOutputs;
|
|
|
|
px := (Input[0] * p^.Domain) / 65535;
|
|
py := (Input[1] * p^.Domain) / 65535;
|
|
pz := (Input[2] * p^.Domain) / 65535;
|
|
|
|
x0 := floor(px);
|
|
fx := (px - x0);
|
|
y0 := floor(py);
|
|
fy := (py - y0);
|
|
z0 := floor(pz);
|
|
fz := (pz - z0);
|
|
|
|
if Input[0] <> $FFFF then
|
|
x1 := x0 + 1
|
|
else
|
|
x1 := x0;
|
|
|
|
if Input[1] <> $FFFF then
|
|
y1 := y0 + 1
|
|
else
|
|
y1 := y0;
|
|
|
|
if Input[2] <> $FFFF then
|
|
z1 := z0 + 1
|
|
else
|
|
z1 := z0;
|
|
|
|
for OutChan := 0 to TotalOut - 1 do
|
|
begin
|
|
|
|
if (fx >= fy) and (fy >= fz) then
|
|
begin
|
|
//
|
|
i1 := clutPoints * (x1);
|
|
t1 := (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + i1)) + OutChan]);
|
|
t2 := (LutTable[TotalOut * ((z0) + clutPoints * ((y1) + i1)) + OutChan]);
|
|
c1 := t1 - (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + clutPoints * (x0))) + OutChan]);
|
|
c2 := t2 - t1;
|
|
c3 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + i1)) + OutChan]) - t2;
|
|
(*
|
|
c1 := DENS(x1, y0, z0) - DENS(x0, y0, z0);
|
|
c2 := DENS(x1, y1, z0) - DENS(x1, y0, z0);
|
|
c3 := DENS(x1, y1, z1) - DENS(x1, y1, z0);
|
|
*)
|
|
end
|
|
else
|
|
if (fx >= fz) and (fz >= fy) then
|
|
begin
|
|
i1 := clutPoints * (x1);
|
|
t1 := (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + i1)) + OutChan]);
|
|
t2 := (LutTable[TotalOut * ((z1) + clutPoints * ((y0) + i1)) + OutChan]);
|
|
c1 := t1 - (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + clutPoints * (x0))) + OutChan]);
|
|
c2 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + i1)) + OutChan]) - t2;
|
|
c3 := t2 - t1;
|
|
(*
|
|
c1 := DENS(x1, y0, z0) - DENS(x0, y0, z0);
|
|
c2 := DENS(x1, y1, z1) - DENS(x1, y0, z1);
|
|
c3 := DENS(x1, y0, z1) - DENS(x1, y0, z0);
|
|
*)
|
|
end
|
|
else
|
|
if (fz >= fx) and (fx >= fy) then
|
|
begin
|
|
t1 := (LutTable[TotalOut * ((Z1) + clutPoints * ((Y0) + clutPoints * (X1))) + OutChan]);
|
|
t2 := (LutTable[TotalOut * ((Z1) + clutPoints * ((Y0) + clutPoints * (X0))) + OutChan]);
|
|
c1 := t1 - t2;
|
|
c2 := (LutTable[TotalOut * ((Z1) + clutPoints * ((Y1) + clutPoints * (X1))) + OutChan]) - t1;
|
|
c3 := t2 - (LutTable[TotalOut * ((Z0) + clutPoints * ((Y0) + clutPoints * (X0))) + OutChan]);
|
|
(*
|
|
c1 := DENS(x1, y0, z1) - DENS(x0, y0, z1);
|
|
c2 := DENS(x1, y1, z1) - DENS(x1, y0, z1);
|
|
c3 := DENS(x0, y0, z1) - DENS(x0, y0, z0);
|
|
*)
|
|
end
|
|
else
|
|
if (fy >= fx) and (fx >= fz) then
|
|
begin
|
|
t1 := (LutTable[TotalOut * ((z0) + clutPoints * ((y1) + clutPoints * (x1))) + OutChan]);
|
|
t2 := (LutTable[TotalOut * ((z0) + clutPoints * ((y1) + clutPoints * (x0))) + OutChan]);
|
|
c1 := t1 - t2;
|
|
c2 := t2 - (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + clutPoints * (x0))) + OutChan]);
|
|
c3 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + clutPoints * (x1))) + OutChan]) - t1;
|
|
(*
|
|
c1 := DENS(x1, y1, z0) - DENS(x0, y1, z0);
|
|
c2 := DENS(x0, y1, z0) - DENS(x0, y0, z0);
|
|
c3 := DENS(x1, y1, z1) - DENS(x1, y1, z0);
|
|
*)
|
|
|
|
end
|
|
else
|
|
if (fy >= fz) and (fz >= fx) then
|
|
begin
|
|
t1 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + clutPoints * (x0))) + OutChan]);
|
|
t2 := (LutTable[TotalOut * ((z0) + clutPoints * ((y1) + clutPoints * (x0))) + OutChan]);
|
|
c1 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + clutPoints * (x1))) + OutChan]) - t1;
|
|
c2 := t2 - (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + clutPoints * (x0))) + OutChan]);
|
|
c3 := t1 - t2;
|
|
(*
|
|
c1 := DENS(x1, y1, z1) - DENS(x0, y1, z1);
|
|
c2 := DENS(x0, y1, z0) - DENS(x0, y0, z0);
|
|
c3 := DENS(x0, y1, z1) - DENS(x0, y1, z0);
|
|
*)
|
|
end
|
|
else
|
|
if (fz >= fy) and (fy >= fx) then
|
|
begin
|
|
t1 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + clutPoints * (x0))) + OutChan]);
|
|
t2 := (LutTable[TotalOut * ((z1) + clutPoints * ((y0) + clutPoints * (x0))) + OutChan]);
|
|
c1 := (LutTable[TotalOut * ((z1) + clutPoints * ((y1) + clutPoints * (x1))) + OutChan]) - t1;
|
|
c2 := t1 - t2;
|
|
c3 := t2 - (LutTable[TotalOut * ((z0) + clutPoints * ((y0) + clutPoints * (x0))) + OutChan]);
|
|
(*
|
|
c1 := DENS(x1, y1, z1) - DENS(x0, y1, z1);
|
|
c2 := DENS(x0, y1, z1) - DENS(x0, y0, z1);
|
|
c3 := DENS(x0, y0, z1) - DENS(x0, y0, z0);
|
|
*)
|
|
end
|
|
else
|
|
begin
|
|
c3 := 0;
|
|
c2 := 0;
|
|
c1 := 0;
|
|
end;
|
|
|
|
Output[OutChan] := floor((LutTable[TotalOut * ((Z0) + clutPoints * ((Y0) + clutPoints * (X0))) + OutChan]) + c1 * fx + c2 * fy + c3 * fz + 0.5);
|
|
end;
|
|
|
|
end;
|
|
|
|
// Trilinear interpolation (16 bits) - float version
|
|
|
|
(*
|
|
procedure cmsTrilinearInterp16(Input: pwordarray; Output: pwordarray; LutTable: pwordarray; p: LPL16PARAMS);
|
|
var
|
|
px, py, pz: double;
|
|
x0, y0, z0,
|
|
x1, y1, z1: integer;
|
|
clutPoints, TotalOut, OutChan: integer;
|
|
fx, fy, fz,
|
|
d000, d001, d010, d011,
|
|
d100, d101, d110, d111,
|
|
dx00, dx01, dx10, dx11,
|
|
dxy0, dxy1, dxyz: double;
|
|
|
|
function LERP(a, l, h: double): double;
|
|
begin
|
|
result := ((l)+(((h)-(l))*(a)));
|
|
end;
|
|
function DENS(X, Y, Z: integer): double;
|
|
begin
|
|
result := (LutTable[TotalOut*((Z)+clutPoints*((Y)+clutPoints*(X)))+OutChan]);
|
|
end;
|
|
|
|
begin
|
|
|
|
clutPoints := p ^. Domain + 1;
|
|
TotalOut := p ^. nOutputs;
|
|
|
|
px := ( Input[0] * (p^.Domain)) / 65535.0;
|
|
py := ( Input[1] * (p^.Domain)) / 65535.0;
|
|
pz := ( Input[2] * (p^.Domain)) / 65535.0;
|
|
|
|
x0 := floor(px); fx := px - x0;
|
|
y0 := floor(py); fy := py - y0;
|
|
z0 := floor(pz); fz := pz - z0;
|
|
|
|
if Input[0] <> $FFFF then
|
|
x1 := x0 + 1
|
|
else
|
|
x1 := x0 ;
|
|
|
|
if Input[1] <> $FFFF then
|
|
y1 := y0 + 1
|
|
else
|
|
y1 := y0;
|
|
|
|
if Input[2] <> $FFFF then
|
|
z1 := z0 + 1
|
|
else
|
|
z1 := z0;
|
|
|
|
for OutChan := 0 to TotalOut-1 do begin
|
|
|
|
d000 := DENS(x0, y0, z0);
|
|
d001 := DENS(x0, y0, z1);
|
|
d010 := DENS(x0, y1, z0);
|
|
d011 := DENS(x0, y1, z1);
|
|
|
|
d100 := DENS(x1, y0, z0);
|
|
d101 := DENS(x1, y0, z1);
|
|
d110 := DENS(x1, y1, z0);
|
|
d111 := DENS(x1, y1, z1);
|
|
|
|
dx00 := LERP(fx, d000, d100);
|
|
dx01 := LERP(fx, d001, d101);
|
|
dx10 := LERP(fx, d010, d110);
|
|
dx11 := LERP(fx, d011, d111);
|
|
|
|
dxy0 := LERP(fy, dx00, dx10);
|
|
dxy1 := LERP(fy, dx01, dx11);
|
|
|
|
dxyz := LERP(fz, dxy0, dxy1);
|
|
|
|
Output[OutChan] := floor(dxyz + 0.5);
|
|
end;
|
|
|
|
end;
|
|
*)
|
|
|
|
|
|
procedure cmsTrilinearInterp16(Input: pwordarray; Output: pwordarray;
|
|
LutTable: pwordarray; p: LPL16PARAMS);
|
|
|
|
var
|
|
OutChan, TotalOut: integer;
|
|
fx, fy, fz: Fixed32;
|
|
rx, ry, rz: WORD;
|
|
x0mi, y0mi, z0mi, x1mi, y1mi, z1mi: integer;
|
|
X0ma, X1ma, Y0ma, Y1ma, Z0ma, Z1ma: integer;
|
|
d000, d001, d010, d011,
|
|
d100, d101, d110, d111,
|
|
dx00, dx01, dx10, dx11,
|
|
dxy0, dxy1, dxyz: integer;
|
|
|
|
function DENS(i, j, k: integer): integer;
|
|
begin
|
|
result := (LutTable[(i) + (j) + (k) + OutChan]);
|
|
end;
|
|
function LERP(a, l, h: integer): word;
|
|
begin
|
|
result := l + ((((h - l) * a) + $8000) shr 16);
|
|
end;
|
|
|
|
begin
|
|
|
|
TotalOut := p^.nOutputs;
|
|
|
|
fx := ToFixedDomain(Input[0] * p^.Domain);
|
|
x0mi := FIXED_TO_INT(fx);
|
|
rx := FIXED_REST_TO_INT(fx);
|
|
|
|
fy := ToFixedDomain(Input[1] * p^.Domain);
|
|
y0mi := FIXED_TO_INT(fy);
|
|
ry := FIXED_REST_TO_INT(fy);
|
|
|
|
fz := ToFixedDomain(Input[2] * p^.Domain);
|
|
z0mi := FIXED_TO_INT(fz);
|
|
rz := FIXED_REST_TO_INT(fz);
|
|
|
|
if Input[0] <> $FFFF then
|
|
x1mi := x0mi + (1)
|
|
else
|
|
x1mi := x0mi + (0);
|
|
|
|
if Input[1] <> $FFFF then
|
|
y1mi := y0mi + (1)
|
|
else
|
|
y1mi := y0mi + (0);
|
|
|
|
if Input[2] <> $FFFF then
|
|
z1mi := z0mi + (1)
|
|
else
|
|
z1mi := z0mi + (0);
|
|
|
|
Z0ma := p^.opta1 * z0mi;
|
|
Z1ma := p^.opta1 * z1mi;
|
|
Y0ma := p^.opta2 * y0mi;
|
|
Y1ma := p^.opta2 * y1mi;
|
|
X0ma := p^.opta3 * x0mi;
|
|
X1ma := p^.opta3 * x1mi;
|
|
|
|
for OutChan := 0 to TotalOut - 1 do
|
|
begin
|
|
|
|
d000 := DENS(X0ma, Y0ma, Z0ma);
|
|
d001 := DENS(X0ma, Y0ma, Z1ma);
|
|
d010 := DENS(X0ma, Y1ma, Z0ma);
|
|
d011 := DENS(X0ma, Y1ma, Z1ma);
|
|
|
|
d100 := DENS(X1ma, Y0ma, Z0ma);
|
|
d101 := DENS(X1ma, Y0ma, Z1ma);
|
|
d110 := DENS(X1ma, Y1ma, Z0ma);
|
|
d111 := DENS(X1ma, Y1ma, Z1ma);
|
|
|
|
dx00 := LERP(rx, d000, d100);
|
|
dx01 := LERP(rx, d001, d101);
|
|
dx10 := LERP(rx, d010, d110);
|
|
dx11 := LERP(rx, d011, d111);
|
|
|
|
dxy0 := LERP(ry, dx00, dx10);
|
|
dxy1 := LERP(ry, dx01, dx11);
|
|
|
|
dxyz := LERP(rz, dxy0, dxy1);
|
|
|
|
Output[OutChan] := dxyz;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure Eval4Inputs(StageABC: pwordarray; StageLMN: pwordarray; LutTable: pwordarray; p16: LPL16PARAMS);
|
|
var
|
|
fk: Fixed32;
|
|
k0, rk: Fixed32;
|
|
xK0, xK1: integer;
|
|
T, T1: PWORD;
|
|
i: integer;
|
|
Tmp1, Tmp2: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
|
|
fk := ToFixedDomain(StageABC[0] * p16^.Domain);
|
|
k0 := ((fk) shr 16);
|
|
rk := ((fk) and $FFFF);
|
|
|
|
xK0 := p16^.opta4 * k0;
|
|
|
|
if StageABC[0] <> $FFFF then
|
|
xK1 := p16^.opta4 * (k0 + 1)
|
|
else
|
|
xK1 := p16^.opta4 * (k0);
|
|
|
|
p16^.nInputs := 3;
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK0);
|
|
|
|
T1 := PWORD(StageABC);
|
|
inc(T1);
|
|
cmsTetrahedralInterp16(pwordarray(T1), @Tmp1, pwordarray(T), p16);
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK1);
|
|
|
|
T1 := PWORD(StageABC);
|
|
inc(T1);
|
|
cmsTetrahedralInterp16(pwordarray(T1), @Tmp2, pwordarray(T), p16);
|
|
|
|
p16^.nInputs := 4;
|
|
for i := 0 to p16^.nOutputs - 1 do
|
|
//StageLMN[i] := FixedLERP(rk, Tmp1[i], Tmp2[i]);
|
|
StageLMN[i] := round(((((Tmp2[i] - Tmp1[i]) * rk) / 65536) + Tmp1[i]) );
|
|
|
|
end;
|
|
|
|
procedure Eval5Inputs(StageABC: pwordarray; StageLMN: pwordarray; LutTable: pwordarray; p16: LPL16PARAMS);
|
|
var
|
|
fk: Fixed32;
|
|
k0, rk: Fixed32;
|
|
xK0, xK1: integer;
|
|
T, T1: PWORD;
|
|
i: integer;
|
|
Tmp1, Tmp2: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
|
|
fk := ToFixedDomain(StageABC[0] * p16^.Domain);
|
|
k0 := FIXED_TO_INT(fk);
|
|
rk := FIXED_REST_TO_INT(fk);
|
|
|
|
xK0 := p16^.opta5 * k0;
|
|
|
|
if StageABC[0] <> $FFFF then
|
|
xK1 := p16^.opta5 * (k0 + (1))
|
|
else
|
|
xK1 := p16^.opta5 * (k0);
|
|
|
|
p16^.nInputs := 4;
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK0);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
Eval4Inputs(pwordarray(T1), @Tmp1, pwordarray(T), p16);
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK1);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
Eval4Inputs(pwordarray(T1), @Tmp2, pwordarray(T), p16);
|
|
|
|
p16^.nInputs := 5;
|
|
for i := 0 to p16^.nOutputs - 1 do
|
|
StageLMN[i] := FixedLERP(rk, Tmp1[i], Tmp2[i]);
|
|
|
|
end;
|
|
|
|
procedure Eval6Inputs(StageABC: pwordarray; StageLMN: pwordarray; LutTable: pwordarray; p16: LPL16PARAMS);
|
|
var
|
|
fk: Fixed32;
|
|
k0, rk: Fixed32;
|
|
xK0, xK1: integer;
|
|
T, T1: PWORD;
|
|
i: integer;
|
|
Tmp1, Tmp2: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
|
|
fk := ToFixedDomain(StageABC[0] * p16^.Domain);
|
|
k0 := FIXED_TO_INT(fk);
|
|
rk := FIXED_REST_TO_INT(fk);
|
|
|
|
xK0 := p16^.opta6 * k0;
|
|
|
|
if StageABC[0] <> $FFFF then
|
|
xK1 := p16^.opta6 * (k0 + (1))
|
|
else
|
|
xK1 := p16^.opta6 * (k0);
|
|
|
|
p16^.nInputs := 5;
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK0);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
|
|
Eval5Inputs(pwordarray(T1), @Tmp1, pwordarray(T), p16);
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK1);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
|
|
Eval5Inputs(pwordarray(T1), @Tmp2, pwordarray(T), p16);
|
|
|
|
p16^.nInputs := 6;
|
|
for i := 0 to p16^.nOutputs - 1 do
|
|
StageLMN[i] := FixedLERP(rk, Tmp1[i], Tmp2[i]);
|
|
|
|
end;
|
|
|
|
procedure Eval7Inputs(StageABC: pwordarray; StageLMN: pwordarray; LutTable: pwordarray; p16: LPL16PARAMS);
|
|
var
|
|
fk: Fixed32;
|
|
k0, rk: Fixed32;
|
|
xK0, xK1: integer;
|
|
T, T1: PWORD;
|
|
i: integer;
|
|
Tmp1, Tmp2: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
|
|
fk := ToFixedDomain(StageABC[0] * p16^.Domain);
|
|
k0 := FIXED_TO_INT(fk);
|
|
rk := FIXED_REST_TO_INT(fk);
|
|
|
|
xK0 := p16^.opta7 * k0;
|
|
|
|
if StageABC[0] <> $FFFF then
|
|
xK1 := p16^.opta7 * (k0 + (1))
|
|
else
|
|
xK1 := p16^.opta7 * (k0);
|
|
|
|
p16^.nInputs := 6;
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK0);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
|
|
Eval6Inputs(pwordarray(T1), @Tmp1, pwordarray(T), p16);
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK1);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
|
|
Eval6Inputs(pwordarray(T1), @Tmp2, pwordarray(T), p16);
|
|
|
|
p16^.nInputs := 7;
|
|
for i := 0 to p16^.nOutputs - 1 do
|
|
StageLMN[i] := FixedLERP(rk, Tmp1[i], Tmp2[i]);
|
|
|
|
end;
|
|
|
|
procedure Eval8Inputs(StageABC: pwordarray; StageLMN: pwordarray; LutTable: pwordarray; p16: LPL16PARAMS);
|
|
var
|
|
fk: Fixed32;
|
|
k0, rk: Fixed32;
|
|
xK0, xK1: integer;
|
|
T, T1: PWORD;
|
|
i: integer;
|
|
Tmp1, Tmp2: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
|
|
fk := ToFixedDomain(StageABC[0] * p16^.Domain);
|
|
k0 := FIXED_TO_INT(fk);
|
|
rk := FIXED_REST_TO_INT(fk);
|
|
|
|
xK0 := p16^.opta8 * k0;
|
|
|
|
if StageABC[0] <> $FFFF then
|
|
xK1 := p16^.opta8 * (k0 + (1))
|
|
else
|
|
xK1 := p16^.opta8 * (k0);
|
|
|
|
p16^.nInputs := 7;
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK0);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
|
|
Eval7Inputs(pwordarray(T1), @Tmp1, pwordarray(T), p16);
|
|
|
|
T := pword(LutTable);
|
|
inc(T, xK1);
|
|
|
|
T1 := pword(StageABC);
|
|
inc(T1);
|
|
|
|
Eval7Inputs(pwordarray(T1), @Tmp2, pwordarray(T), p16);
|
|
|
|
p16^.nInputs := 8;
|
|
for i := 0 to p16^.nOutputs - 1 do
|
|
StageLMN[i] := FixedLERP(rk, Tmp1[i], Tmp2[i]);
|
|
|
|
end;
|
|
|
|
// Fills optimization parameters
|
|
|
|
procedure cmsCalcCLUT16ParamsEx(nSamples: integer; InputChan: integer; OutputChan: integer; lUseTetrahedral: longbool; p: LPL16PARAMS);
|
|
var
|
|
clutPoints: integer;
|
|
begin
|
|
cmsCalcL16Params(nSamples, p);
|
|
|
|
p^.nInputs := InputChan;
|
|
p^.nOutputs := OutputChan;
|
|
|
|
clutPoints := p^.Domain + 1;
|
|
|
|
p^.opta1 := p^.nOutputs; // Z
|
|
p^.opta2 := p^.opta1 * clutPoints; // Y
|
|
p^.opta3 := p^.opta2 * clutPoints; // X
|
|
p^.opta4 := p^.opta3 * clutPoints; // Used only in 4 inputs LUT
|
|
p^.opta5 := p^.opta4 * clutPoints;
|
|
p^.opta6 := p^.opta5 * clutPoints; // Used only on 6 inputs LUT
|
|
p^.opta7 := p^.opta6 * clutPoints; // Used only on 7 inputs LUT
|
|
p^.opta8 := p^.opta7 * clutPoints; // Used only on 8 inputs LUT
|
|
|
|
case (InputChan) of
|
|
|
|
1: // Gray LUT
|
|
|
|
p^.Interp3D := Eval1Input;
|
|
|
|
3: // RGB et al
|
|
if (lUseTetrahedral) then
|
|
p^.Interp3D := cmsTetrahedralInterp16
|
|
else
|
|
p^.Interp3D := cmsTrilinearInterp16;
|
|
|
|
4: // CMYK LUT
|
|
p^.Interp3D := Eval4Inputs;
|
|
|
|
5: // 5 Inks
|
|
p^.Interp3D := Eval5Inputs;
|
|
|
|
6: // Hexachrome
|
|
p^.Interp3D := Eval6Inputs;
|
|
|
|
7: // 7 inks
|
|
p^.Interp3D := Eval7Inputs;
|
|
|
|
8: // 8 inks
|
|
p^.Interp3D := Eval8Inputs;
|
|
|
|
else
|
|
;
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported restoration (%d channels)", InputChan);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure cmsCalcCLUT16Params(nSamples: integer; InputChan: integer; OutputChan: integer; p: LPL16PARAMS);
|
|
begin
|
|
cmsCalcCLUT16ParamsEx(nSamples, InputChan, OutputChan, FALSE, p);
|
|
end;
|
|
|
|
procedure VEC3fromFix(r: LPVEC3; v: LPWVEC3);
|
|
begin
|
|
r^.n[VX] := FIXED_TO_DOUBLE(v^.n[VX]);
|
|
r^.n[VY] := FIXED_TO_DOUBLE(v^.n[VY]);
|
|
r^.n[VZ] := FIXED_TO_DOUBLE(v^.n[VZ]);
|
|
end;
|
|
|
|
procedure MAT3fromFix(r: LPMAT3; v: LPWMAT3);
|
|
begin
|
|
VEC3fromFix(@r^.v[0], @v^.v[0]);
|
|
VEC3fromFix(@r^.v[1], @v^.v[1]);
|
|
VEC3fromFix(@r^.v[2], @v^.v[2]);
|
|
end;
|
|
|
|
procedure FixLUT8(Lut: LPLUT; sig: icTagSignature; nTabSize: integer);
|
|
var
|
|
Fixup, Original, Result: MAT3;
|
|
PtrW: PWORD;
|
|
i: integer;
|
|
begin
|
|
|
|
case (sig) of
|
|
|
|
icSigBToA0Tag,
|
|
icSigBToA1Tag,
|
|
icSigBToA2Tag,
|
|
icSigGamutTag,
|
|
icSigPreview0Tag,
|
|
icSigPreview1Tag,
|
|
icSigPreview2Tag:
|
|
begin
|
|
|
|
VEC3init(@Fixup.v[0], $FFFF / $FF00, 0, 0);
|
|
VEC3init(@Fixup.v[1], 0, $FFFF / $FF00, 0);
|
|
VEC3init(@Fixup.v[2], 0, 0, $FFFF / $FF00);
|
|
|
|
MAT3fromFix(@Original, @Lut^.Matrix);
|
|
MAT3per(@Result, @Original, @Fixup);
|
|
MAT3toFix(@Lut^.Matrix, @Result);
|
|
|
|
Lut^.wFlags := Lut^.wFlags or LUT_HASMATRIX;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
|
|
PtrW := Lut^.T;
|
|
for i := 0 to nTabSize - 1 do
|
|
begin
|
|
|
|
PtrW^ := PtrW^ and $FF00;
|
|
inc(PtrW);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure ReadLUT8(Icc: LPLCMSICCPROFILE; NewLUT: LPLUT; sig: icTagSignature);
|
|
var
|
|
LUT8: icLut8;
|
|
Temp: pbytearray;
|
|
nTabSize: integer;
|
|
i, j: dword;
|
|
AllLinear: dword;
|
|
PtrW: pwordarray;
|
|
begin
|
|
|
|
Icc^.Read(@LUT8, sizeof(icLut8) - SIZEOF_UINT8_ALIGNED, 1, Icc^.stream);
|
|
|
|
NewLUT^.wFlags := LUT_HASTL1 or LUT_HASTL2 or LUT_HAS3DGRID;
|
|
NewLUT^.cLutPoints := LUT8.clutPoints;
|
|
NewLUT^.InputChan := LUT8.inputChan;
|
|
NewLUT^.OutputChan := LUT8.outputChan;
|
|
NewLUT^.InputEntries := 256;
|
|
NewLUT^.OutputEntries := 256;
|
|
|
|
AdjustEndianess32(PBYTE(@LUT8.e00));
|
|
AdjustEndianess32(PBYTE(@LUT8.e01));
|
|
AdjustEndianess32(PBYTE(@LUT8.e02));
|
|
AdjustEndianess32(PBYTE(@LUT8.e10));
|
|
AdjustEndianess32(PBYTE(@LUT8.e11));
|
|
AdjustEndianess32(PBYTE(@LUT8.e12));
|
|
AdjustEndianess32(PBYTE(@LUT8.e20));
|
|
AdjustEndianess32(PBYTE(@LUT8.e21));
|
|
AdjustEndianess32(PBYTE(@LUT8.e22));
|
|
|
|
NewLUT^.Matrix.v[0].n[0] := Fixed32(LUT8.e00);
|
|
NewLUT^.Matrix.v[0].n[1] := Fixed32(LUT8.e01);
|
|
NewLUT^.Matrix.v[0].n[2] := Fixed32(LUT8.e02);
|
|
NewLUT^.Matrix.v[1].n[0] := Fixed32(LUT8.e10);
|
|
NewLUT^.Matrix.v[1].n[1] := Fixed32(LUT8.e11);
|
|
NewLUT^.Matrix.v[1].n[2] := Fixed32(LUT8.e12);
|
|
NewLUT^.Matrix.v[2].n[0] := Fixed32(LUT8.e20);
|
|
NewLUT^.Matrix.v[2].n[1] := Fixed32(LUT8.e21);
|
|
NewLUT^.Matrix.v[2].n[2] := Fixed32(LUT8.e22);
|
|
|
|
if (not MAT3isIdentity(@NewLUT^.Matrix, 0.0001)) then
|
|
begin
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HASMATRIX;
|
|
end;
|
|
|
|
getmem(Temp, 256);
|
|
AllLinear := 0;
|
|
for i := 0 to NewLUT^.InputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * 256);
|
|
NewLUT^.L1[i] := pword(PtrW);
|
|
Icc^.Read(Temp, 1, 256, Icc^.stream);
|
|
for j := 0 to 255 do
|
|
PtrW[j] := TO16_TAB(Temp[j]);
|
|
AllLinear := AllLinear + cmsIsLinear(pwordarray(NewLUT^.L1[i]), NewLUT^.InputEntries);
|
|
end;
|
|
|
|
if (AllLinear = NewLUT^.InputChan) then
|
|
begin
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags and (not LUT_HASTL1);
|
|
end;
|
|
|
|
freemem(Temp);
|
|
|
|
nTabSize := (NewLUT^.OutputChan * uipow(NewLUT^.cLutPoints, NewLUT^.InputChan));
|
|
|
|
if (nTabSize > 0) then
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * nTabSize);
|
|
getmem(Temp, nTabSize);
|
|
Icc^.Read(Temp, 1, nTabSize, Icc^.stream);
|
|
|
|
NewLUT^.T := pword(PtrW);
|
|
NewLUT^.Tsize := (nTabSize * sizeof(WORD));
|
|
|
|
for i := 0 to nTabSize - 1 do
|
|
begin
|
|
|
|
pword(PtrW)^ := TO16_TAB(Temp[i]);
|
|
inc(pword(PtrW));
|
|
end;
|
|
freemem(Temp);
|
|
end
|
|
else
|
|
begin
|
|
NewLUT^.T := nil;
|
|
NewLUT^.Tsize := 0;
|
|
NewLUT^.wFlags := NewLUT^.wFlags and (not LUT_HAS3DGRID);
|
|
end;
|
|
|
|
getmem(Temp, 256);
|
|
AllLinear := 0;
|
|
for i := 0 to NewLUT^.OutputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * 256);
|
|
NewLUT^.L2[i] := pword(PtrW);
|
|
Icc^.Read(Temp, 1, 256, Icc^.stream);
|
|
for j := 0 to 255 do
|
|
PtrW[j] := TO16_TAB(Temp[j]);
|
|
AllLinear := AllLinear + cmsIsLinear(pwordarray(NewLUT^.L2[i]), 256);
|
|
end;
|
|
|
|
if (AllLinear = NewLUT^.OutputChan) then
|
|
begin
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags and (not LUT_HASTL2);
|
|
end;
|
|
|
|
freemem(Temp);
|
|
|
|
cmsCalcL16Params(NewLUT^.InputEntries, @NewLUT^.In16params);
|
|
cmsCalcL16Params(NewLUT^.OutputEntries, @NewLUT^.Out16params);
|
|
cmsCalcCLUT16Params(NewLUT^.cLutPoints, NewLUT^.InputChan,
|
|
NewLUT^.OutputChan,
|
|
@NewLUT^.CLut16params);
|
|
if (Icc^.PCS = icSigLabData) then
|
|
FixLUT8(NewLUT, sig, nTabSize);
|
|
|
|
end;
|
|
|
|
function SIZEOF_UINT16_ALIGNED: integer;
|
|
begin
|
|
result := (sizeof(_cmsTestAlign16) - sizeof(icS15Fixed16Number));
|
|
end;
|
|
|
|
procedure xswab(from: pointer; xto: pointer; len: integer);
|
|
var
|
|
temp: dword;
|
|
n: integer;
|
|
fp, tp: pbyte;
|
|
|
|
procedure STEP;
|
|
begin
|
|
temp := fp^;
|
|
inc(fp);
|
|
tp^ := fp^;
|
|
inc(tp);
|
|
inc(fp);
|
|
tp^ := temp;
|
|
inc(tp);
|
|
end;
|
|
begin
|
|
|
|
n := (len shr 1) + 1;
|
|
fp := from;
|
|
tp := xto;
|
|
|
|
dec(n);
|
|
while ((n) and 07) <> 0 do
|
|
begin
|
|
STEP;
|
|
dec(n);
|
|
end;
|
|
n := n shr 3;
|
|
dec(n);
|
|
while (n >= 0) do
|
|
begin
|
|
|
|
STEP;
|
|
STEP;
|
|
STEP;
|
|
STEP;
|
|
STEP;
|
|
STEP;
|
|
STEP;
|
|
STEP;
|
|
dec(n);
|
|
end;
|
|
end;
|
|
|
|
// swap bytes in a array of words
|
|
|
|
procedure AdjustEndianessArray16(p: PWORD; num_words: integer); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
xswab(p, p, num_words * sizeof(WORD));
|
|
end;
|
|
|
|
procedure ReadLUT16(Icc: LPLCMSICCPROFILE; NewLUT: LPLUT);
|
|
var
|
|
LUT16: icLut16;
|
|
nTabSize: integer;
|
|
i: dword;
|
|
AllLinear: dword;
|
|
PtrW: pword;
|
|
begin
|
|
|
|
Icc^.Read(@LUT16, sizeof(icLut16) - SIZEOF_UINT16_ALIGNED, 1, Icc^.stream);
|
|
|
|
NewLUT^.wFlags := LUT_HASTL1 or LUT_HASTL2 or LUT_HAS3DGRID;
|
|
NewLUT^.cLutPoints := LUT16.clutPoints;
|
|
NewLUT^.InputChan := LUT16.inputChan;
|
|
NewLUT^.OutputChan := LUT16.outputChan;
|
|
|
|
AdjustEndianess16(PBYTEARRAY(@LUT16.inputEnt));
|
|
AdjustEndianess16(PBYTEARRAY(@LUT16.outputEnt));
|
|
|
|
NewLUT^.InputEntries := LUT16.inputEnt;
|
|
NewLUT^.OutputEntries := LUT16.outputEnt;
|
|
|
|
AdjustEndianess32(PBYTE(@LUT16.e00));
|
|
AdjustEndianess32(PBYTE(@LUT16.e01));
|
|
AdjustEndianess32(PBYTE(@LUT16.e02));
|
|
AdjustEndianess32(PBYTE(@LUT16.e10));
|
|
AdjustEndianess32(PBYTE(@LUT16.e11));
|
|
AdjustEndianess32(PBYTE(@LUT16.e12));
|
|
AdjustEndianess32(PBYTE(@LUT16.e20));
|
|
AdjustEndianess32(PBYTE(@LUT16.e21));
|
|
AdjustEndianess32(PBYTE(@LUT16.e22));
|
|
|
|
NewLUT^.Matrix.v[0].n[0] := Fixed32(LUT16.e00);
|
|
NewLUT^.Matrix.v[0].n[1] := Fixed32(LUT16.e01);
|
|
NewLUT^.Matrix.v[0].n[2] := Fixed32(LUT16.e02);
|
|
NewLUT^.Matrix.v[1].n[0] := Fixed32(LUT16.e10);
|
|
NewLUT^.Matrix.v[1].n[1] := Fixed32(LUT16.e11);
|
|
NewLUT^.Matrix.v[1].n[2] := Fixed32(LUT16.e12);
|
|
NewLUT^.Matrix.v[2].n[0] := Fixed32(LUT16.e20);
|
|
NewLUT^.Matrix.v[2].n[1] := Fixed32(LUT16.e21);
|
|
NewLUT^.Matrix.v[2].n[2] := Fixed32(LUT16.e22);
|
|
|
|
if (not MAT3isIdentity(@NewLUT^.Matrix, 0.0001)) then
|
|
begin
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HASMATRIX;
|
|
end;
|
|
|
|
AllLinear := 0;
|
|
for i := 0 to NewLUT^.InputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewLUT^.InputEntries);
|
|
NewLUT^.L1[i] := PtrW;
|
|
Icc^.Read(PtrW, sizeof(WORD), NewLUT^.InputEntries, Icc^.stream);
|
|
AdjustEndianessArray16(PtrW, NewLUT^.InputEntries);
|
|
AllLinear := AllLinear + cmsIsLinear(pwordarray(NewLUT^.L1[i]), NewLUT^.InputEntries);
|
|
end;
|
|
|
|
if (AllLinear = NewLUT^.InputChan) then
|
|
begin
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags and (not LUT_HASTL1);
|
|
end;
|
|
|
|
nTabSize := (NewLUT^.OutputChan * uipow(NewLUT^.cLutPoints, NewLUT^.InputChan));
|
|
if (nTabSize > 0) then
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * nTabSize);
|
|
|
|
NewLUT^.T := PtrW;
|
|
NewLUT^.Tsize := (nTabSize * sizeof(WORD));
|
|
|
|
Icc^.Read(PtrW, sizeof(WORD), nTabSize, Icc^.stream);
|
|
AdjustEndianessArray16(NewLUT^.T, nTabSize);
|
|
end
|
|
else
|
|
begin
|
|
NewLUT^.T := nil;
|
|
NewLUT^.Tsize := 0;
|
|
NewLUT^.wFlags := NewLUT^.wFlags and (not LUT_HAS3DGRID);
|
|
end;
|
|
|
|
AllLinear := 0;
|
|
for i := 0 to NewLUT^.OutputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewLUT^.OutputEntries);
|
|
NewLUT^.L2[i] := PtrW;
|
|
Icc^.Read(PtrW, sizeof(WORD), NewLUT^.OutputEntries, Icc^.stream);
|
|
AdjustEndianessArray16(PtrW, NewLUT^.OutputEntries);
|
|
AllLinear := AllLinear + cmsIsLinear(pwordarray(NewLUT^.L2[i]), NewLUT^.OutputEntries);
|
|
end;
|
|
|
|
if (AllLinear = NewLUT^.OutputChan) then
|
|
begin
|
|
NewLUT^.wFlags := NewLUT^.wFlags and (not LUT_HASTL2);
|
|
end;
|
|
|
|
cmsCalcL16Params(NewLUT^.InputEntries, @NewLUT^.In16params);
|
|
cmsCalcL16Params(NewLUT^.OutputEntries, @NewLUT^.Out16params);
|
|
cmsCalcCLUT16Params(NewLUT^.cLutPoints, NewLUT^.InputChan,
|
|
NewLUT^.OutputChan,
|
|
@NewLUT^.CLut16params);
|
|
end;
|
|
|
|
function cmsAllocGamma(nEntries: integer): LPGAMMATABLE;
|
|
var
|
|
p: LPGAMMATABLE;
|
|
size: integer;
|
|
begin
|
|
|
|
size := sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries - 1));
|
|
|
|
getmem(p, size);
|
|
if (p = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.nEntries := nEntries;
|
|
ZeroMemory(@p^.GammaTable, nEntries * sizeof(WORD));
|
|
|
|
result := p;
|
|
end;
|
|
|
|
function FGamma(R: double; x: double): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := Power(R, x);
|
|
end;
|
|
|
|
// Build a gamma table based on gamma constant
|
|
|
|
function cmsBuildGamma(nEntries: integer; Gamma: double): LPGAMMATABLE;
|
|
var
|
|
p: LPGAMMATABLE;
|
|
Table: PWORDARRAY;
|
|
i: integer;
|
|
R, Val: double;
|
|
begin
|
|
|
|
if (nEntries > 65530) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Couldn't create gammatable of more than 65530 entries; 65530 assumed");
|
|
nEntries := 65530;
|
|
end;
|
|
|
|
p := cmsAllocGamma(nEntries);
|
|
if (p = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Table := @p^.GammaTable;
|
|
if (Gamma = 0.0) then
|
|
begin
|
|
ZeroMemory(Table, nEntries * sizeof(WORD));
|
|
result := p;
|
|
exit;
|
|
end;
|
|
|
|
if (Gamma = 1.0) then
|
|
begin
|
|
|
|
for i := 0 to nEntries - 1 do
|
|
begin
|
|
|
|
Table[i] := _cmsQuantizeVal(i, nEntries);
|
|
|
|
end;
|
|
result := p;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to nEntries - 1 do
|
|
begin
|
|
R := i / (nEntries - 1);
|
|
Val := FGamma(R, Gamma);
|
|
|
|
Table[i] := floor(Val * 65535 + 0.5);
|
|
end;
|
|
|
|
result := p;
|
|
end;
|
|
|
|
// Fixed point conversion
|
|
|
|
function Convert8Fixed8(fixed8: WORD): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
var
|
|
msb, lsb: BYTE;
|
|
begin
|
|
|
|
lsb := (fixed8 and $FF);
|
|
msb := ((fixed8 shr 8) and $FF);
|
|
|
|
result := (msb + (lsb / 256));
|
|
end;
|
|
|
|
procedure cmsFreeGamma(Gamma: LPGAMMATABLE);
|
|
begin
|
|
if (Gamma <> nil) then
|
|
freemem(Gamma);
|
|
end;
|
|
|
|
function cmsBuildParametricGamma(nEntries: integer; xType: integer; Params: pdoublearray): LPGAMMATABLE;
|
|
var
|
|
Table: LPGAMMATABLE;
|
|
R, Val, dval, e: double;
|
|
i: integer;
|
|
begin
|
|
|
|
Table := cmsAllocGamma(nEntries);
|
|
if (Table = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to nEntries - 1 do
|
|
begin
|
|
|
|
R := i / (nEntries - 1);
|
|
|
|
case (xType) of
|
|
|
|
1:
|
|
Val := Power(R, Params[0]);
|
|
|
|
-1:
|
|
Val := Power(R, 1 / Params[0]);
|
|
|
|
2:
|
|
if (R >= -Params[2] / Params[1]) then
|
|
begin
|
|
|
|
e := Params[1] * R + Params[2];
|
|
|
|
if (e > 0) then
|
|
Val := Power(e, Params[0])
|
|
else
|
|
Val := 0;
|
|
end
|
|
else
|
|
Val := 0;
|
|
|
|
-2:
|
|
begin
|
|
|
|
Val := (Power(R, 1.0 / Params[0]) - Params[2]) / Params[1];
|
|
if (Val < 0) then
|
|
Val := 0;
|
|
end;
|
|
|
|
3:
|
|
if (R >= -Params[2] / Params[1]) then
|
|
begin
|
|
|
|
e := Params[1] * R + Params[2];
|
|
Val := Power(e, Params[0]) + Params[3];
|
|
end
|
|
else
|
|
Val := Params[3];
|
|
|
|
-3:
|
|
if (R >= Params[3]) then
|
|
begin
|
|
e := R - Params[3];
|
|
Val := (Power(e, 1 / Params[0]) - Params[2]) / Params[1];
|
|
if (Val < 0) then
|
|
Val := 0;
|
|
end
|
|
else
|
|
begin
|
|
Val := -Params[2] / Params[1];
|
|
end;
|
|
|
|
4:
|
|
if (R >= Params[4]) then
|
|
begin
|
|
|
|
e := Params[1] * R + Params[2];
|
|
if (e > 0) then
|
|
Val := Power(e, Params[0])
|
|
else
|
|
Val := 0;
|
|
end
|
|
else
|
|
Val := R * Params[3];
|
|
|
|
-4:
|
|
if (R >= Power(Params[1] * Params[4] + Params[2], Params[0])) then
|
|
begin
|
|
|
|
Val := (Power(R, 1.0 / Params[0]) - Params[2]) / Params[1];
|
|
end
|
|
else
|
|
begin
|
|
Val := R / Params[3];
|
|
end;
|
|
|
|
5:
|
|
if (R >= Params[4]) then
|
|
begin
|
|
|
|
e := Params[1] * R + Params[2];
|
|
Val := Power(e, Params[0]) + Params[5];
|
|
end
|
|
else
|
|
Val := R * Params[3] + Params[6];
|
|
|
|
-5:
|
|
|
|
if (R >= Power(Params[1] * Params[4], Params[0]) + Params[5]) then
|
|
begin
|
|
|
|
Val := Power(R - Params[5], 1 / Params[0]) - Params[2] / Params[1];
|
|
end
|
|
else
|
|
begin
|
|
Val := (R - Params[6]) / Params[3];
|
|
end;
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(xType)-1);
|
|
cmsFreeGamma(Table);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
dval := Val * 65535.0 + 0.5;
|
|
if (dval > 65535) then
|
|
dval := 65535;
|
|
if (dval < 0) then
|
|
dval := 0;
|
|
|
|
Table^.GammaTable[i] := floor(dval);
|
|
end;
|
|
|
|
result := Table;
|
|
end;
|
|
|
|
function ReadCurve(Icc: LPLCMSICCPROFILE): LPGAMMATABLE;
|
|
const
|
|
ParamsByType: array[0..4] of integer = (1, 3, 4, 5, 7);
|
|
var
|
|
Count: icUInt32Number;
|
|
NewGamma: LPGAMMATABLE;
|
|
Base: icTagBase;
|
|
n: integer;
|
|
SingleGammaFixed: WORD;
|
|
|
|
Params: array[0..9] of double;
|
|
Num: icS15Fixed16Number;
|
|
Reserved: icUInt32Number;
|
|
xType: icUInt16Number;
|
|
i: integer;
|
|
begin
|
|
|
|
Icc^.Read(@Base, 1, sizeof(icTagBase), Icc^.stream);
|
|
AdjustEndianess32(@Base.sig);
|
|
|
|
case (Base.sig) of
|
|
|
|
icTagTypeSignature($9478EE00),
|
|
icSigCurveType:
|
|
begin
|
|
Icc^.Read(@Count, sizeof(icUInt32Number), 1, Icc^.stream);
|
|
|
|
AdjustEndianess32(@Count);
|
|
|
|
case (Count) of
|
|
|
|
0:
|
|
begin
|
|
NewGamma := cmsAllocGamma(2);
|
|
if (NewGamma = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
pwordarray(@NewGamma^.GammaTable)[0] := 0;
|
|
pwordarray(@NewGamma^.GammaTable)[1] := $FFFF;
|
|
result := NewGamma;
|
|
exit;
|
|
end;
|
|
|
|
1:
|
|
begin
|
|
Icc^.Read(@SingleGammaFixed, sizeof(WORD), 1, Icc^.stream);
|
|
AdjustEndianess16(@SingleGammaFixed);
|
|
result := cmsBuildGamma(4096, Convert8Fixed8(SingleGammaFixed));
|
|
exit;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
|
|
NewGamma := cmsAllocGamma(Count);
|
|
if (NewGamma = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@NewGamma^.GammaTable, sizeof(WORD), Count, Icc^.stream);
|
|
|
|
AdjustEndianessArray16(@NewGamma^.GammaTable, Count);
|
|
|
|
result := NewGamma;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
icTagTypeSignature(icSigParametricCurveType):
|
|
begin
|
|
|
|
Icc^.Read(@xType, sizeof(icUInt16Number), 1, Icc^.stream);
|
|
Icc^.Read(@Reserved, sizeof(icUInt16Number), 1, Icc^.stream);
|
|
|
|
AdjustEndianess16(@xType);
|
|
if (xType > 5) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", xType);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
ZeroMemory(@Params, 10 * sizeof(double));
|
|
n := ParamsByType[xType];
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
Num := 0;
|
|
Icc^.Read(@Num, sizeof(icS15Fixed16Number), 1, Icc^.stream);
|
|
Params[i] := Convert15Fixed16(Num);
|
|
end;
|
|
|
|
NewGamma := cmsBuildParametricGamma(4096, xType + 1, @Params);
|
|
result := NewGamma;
|
|
exit;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", Base.sig);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
function cmsAllocLinearTable(NewLUT: LPLUT; Tables: LPGAMMATABLEArray; nTable: integer): LPLUT;
|
|
var
|
|
i: dword;
|
|
PtrW: PWORD;
|
|
begin
|
|
|
|
case (nTable) of
|
|
|
|
1:
|
|
begin
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HASTL1;
|
|
cmsCalcL16Params(Tables[0]^.nEntries, @NewLUT^.In16params);
|
|
NewLUT^.InputEntries := Tables[0]^.nEntries;
|
|
|
|
for i := 0 to NewLUT^.InputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewLUT^.InputEntries);
|
|
NewLUT^.L1[i] := PtrW;
|
|
CopyMemory(PtrW, @Tables[i]^.GammaTable, sizeof(WORD) * NewLUT^.InputEntries);
|
|
end;
|
|
end;
|
|
|
|
2:
|
|
begin
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HASTL2;
|
|
cmsCalcL16Params(Tables[0]^.nEntries, @NewLUT^.Out16params);
|
|
NewLUT^.OutputEntries := Tables[0]^.nEntries;
|
|
for i := 0 to NewLUT^.OutputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewLUT^.OutputEntries);
|
|
NewLUT^.L2[i] := PtrW;
|
|
CopyMemory(PtrW, @Tables[i]^.GammaTable, sizeof(WORD) * NewLUT^.OutputEntries);
|
|
end;
|
|
end;
|
|
|
|
3:
|
|
begin
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HASTL3;
|
|
cmsCalcL16Params(Tables[0]^.nEntries, @NewLUT^.L3params);
|
|
NewLUT^.L3Entries := Tables[0]^.nEntries;
|
|
|
|
for i := 0 to NewLUT^.InputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewLUT^.L3Entries);
|
|
NewLUT^.L3[i] := PtrW;
|
|
CopyMemory(PtrW, @Tables[i]^.GammaTable, sizeof(WORD) * NewLUT^.L3Entries);
|
|
end;
|
|
end;
|
|
|
|
4:
|
|
begin
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HASTL4;
|
|
cmsCalcL16Params(Tables[0]^.nEntries, @NewLUT^.L4params);
|
|
NewLUT^.L4Entries := Tables[0]^.nEntries;
|
|
for i := 0 to NewLUT^.OutputChan - 1 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewLUT^.L4Entries);
|
|
NewLUT^.L4[i] := PtrW;
|
|
CopyMemory(PtrW, @Tables[i]^.GammaTable, sizeof(WORD) * NewLUT^.L4Entries);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
result := NewLUT;
|
|
end;
|
|
|
|
// Read a set of curves from specific offset
|
|
|
|
function ReadSetOfCurves(Icc: LPLCMSICCPROFILE; Offset: integer; NewLUT: LPLUT; nLocation: integer): longbool;
|
|
var
|
|
Curves: array[0..MAXCHANNELS - 1] of LPGAMMATABLE;
|
|
i, nCurves: dword;
|
|
begin
|
|
|
|
if (Icc^.Seek(Icc^.stream, Offset)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (nLocation = 1) or (nLocation = 3) then
|
|
|
|
nCurves := NewLUT^.InputChan
|
|
else
|
|
nCurves := NewLUT^.OutputChan;
|
|
|
|
try
|
|
|
|
// 3.0.1
|
|
for i := 0 to nCurves - 1 do
|
|
Curves[i] := nil;
|
|
|
|
for i := 0 to nCurves - 1 do
|
|
begin
|
|
Curves[i] := ReadCurve(LPLCMSICCPROFILE(Icc));
|
|
// 3.0.2
|
|
if Curves[i]=nil then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
cmsAllocLinearTable(NewLUT, @Curves, nLocation);
|
|
|
|
result := TRUE;
|
|
|
|
finally
|
|
|
|
for i := 0 to nCurves - 1 do
|
|
cmsFreeGamma(Curves[i]);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function cmsSetMatrixLUT4(Lut: LPLUT; M: LPMAT3; off: LPVEC3; dwFlags: DWORD): LPLUT;
|
|
const
|
|
Zero: VEC3 = (n: (0, 0, 0));
|
|
var
|
|
WMat: WMAT3;
|
|
Woff: WVEC3;
|
|
begin
|
|
|
|
MAT3toFix(@WMat, M);
|
|
|
|
if (off = nil) then
|
|
off := @Zero;
|
|
|
|
VEC3toFix(@Woff, off);
|
|
|
|
if MAT3isIdentity(@WMat, 0.0001) and (Woff.n[VX] = 0) and (Woff.n[VY] = 0) and (Woff.n[VZ] = 0) then
|
|
begin
|
|
result := Lut;
|
|
exit;
|
|
end;
|
|
|
|
case (dwFlags) of
|
|
|
|
LUT_HASMATRIX:
|
|
begin
|
|
Lut^.Matrix := WMat;
|
|
Lut^.wFlags := Lut^.wFlags or LUT_HASMATRIX;
|
|
end;
|
|
|
|
LUT_HASMATRIX3:
|
|
begin
|
|
Lut^.Mat3 := WMat;
|
|
Lut^.Ofs3 := Woff;
|
|
Lut^.wFlags := Lut^.wFlags or LUT_HASMATRIX3;
|
|
end;
|
|
|
|
LUT_HASMATRIX4:
|
|
begin
|
|
Lut^.Mat4 := WMat;
|
|
Lut^.Ofs4 := Woff;
|
|
Lut^.wFlags := Lut^.wFlags or LUT_HASMATRIX4;
|
|
end;
|
|
|
|
end;
|
|
|
|
result := Lut;
|
|
end;
|
|
|
|
function ReadMatrixOffset(Icc: LPLCMSICCPROFILE; Offset: integer; NewLUT: LPLUT; dwFlags: DWORD): longbool;
|
|
var
|
|
All: array[0..11] of icS15Fixed16Number;
|
|
i: integer;
|
|
m: MAT3;
|
|
o: VEC3;
|
|
begin
|
|
|
|
if (Icc^.Seek(Icc^.stream, Offset)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@All, sizeof(icS15Fixed16Number), 12, Icc^.stream);
|
|
|
|
for i := 0 to 11 do
|
|
AdjustEndianess32(@All[i]);
|
|
|
|
m.v[0].n[0] := FIXED_TO_DOUBLE(Fixed32(All[0]));
|
|
m.v[0].n[1] := FIXED_TO_DOUBLE(Fixed32(All[1]));
|
|
m.v[0].n[2] := FIXED_TO_DOUBLE(Fixed32(All[2]));
|
|
m.v[1].n[0] := FIXED_TO_DOUBLE(Fixed32(All[3]));
|
|
m.v[1].n[1] := FIXED_TO_DOUBLE(Fixed32(All[4]));
|
|
m.v[1].n[2] := FIXED_TO_DOUBLE(Fixed32(All[5]));
|
|
m.v[2].n[0] := FIXED_TO_DOUBLE(Fixed32(All[6]));
|
|
m.v[2].n[1] := FIXED_TO_DOUBLE(Fixed32(All[7]));
|
|
m.v[2].n[2] := FIXED_TO_DOUBLE(Fixed32(All[8]));
|
|
|
|
o.n[0] := FIXED_TO_DOUBLE(Fixed32(All[9]));
|
|
o.n[1] := FIXED_TO_DOUBLE(Fixed32(All[10]));
|
|
o.n[2] := FIXED_TO_DOUBLE(Fixed32(All[11]));
|
|
|
|
cmsSetMatrixLUT4(NewLUT, @m, @o, dwFlags);
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsAlloc3DGrid(NewLUT: LPLUT; clutPoints: integer; inputChan: integer; outputChan: integer): LPLUT;
|
|
var
|
|
nTabSize: DWORD;
|
|
begin
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_HAS3DGRID;
|
|
NewLUT^.cLutPoints := clutPoints;
|
|
NewLUT^.InputChan := inputChan;
|
|
NewLUT^.OutputChan := outputChan;
|
|
|
|
nTabSize := (NewLUT^.OutputChan * UIpow(NewLUT^.cLutPoints, NewLUT^.InputChan) * sizeof(WORD));
|
|
|
|
getmem(NewLUT^.T, nTabSize);
|
|
ZeroMemory(NewLUT^.T, nTabSize);
|
|
NewLUT^.Tsize := nTabSize;
|
|
|
|
cmsCalcCLUT16Params(NewLUT^.cLutPoints, NewLUT^.InputChan,
|
|
NewLUT^.OutputChan,
|
|
@NewLUT^.CLut16params);
|
|
|
|
result := NewLUT;
|
|
end;
|
|
|
|
function ReadCLUT(Icc: LPLCMSICCPROFILE; Offset: integer; NewLUT: LPLUT): longbool;
|
|
var
|
|
CLUT: icCLutStruct;
|
|
v: byte;
|
|
i: dword;
|
|
begin
|
|
|
|
if (Icc^.Seek(Icc^.stream, Offset)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
Icc^.Read(@CLUT, sizeof(icCLutStruct), 1, Icc^.stream);
|
|
|
|
cmsAlloc3DGrid(NewLUT, CLUT.gridPoints[0], NewLUT^.InputChan,
|
|
NewLUT^.OutputChan);
|
|
|
|
if (CLUT.prec = 1) then
|
|
begin
|
|
|
|
for i := 0 to NewLUT^.Tsize - 1 do
|
|
begin
|
|
Icc^.Read(@v, sizeof(BYTE), 1, Icc^.stream);
|
|
pwordarray(NewLUT^.T)^[i] := TO16_TAB(v);
|
|
end;
|
|
|
|
end
|
|
else
|
|
if (CLUT.prec = 2) then
|
|
begin
|
|
|
|
Icc^.Read(NewLUT^.T, sizeof(WORD), NewLUT^.Tsize div sizeof(WORD), Icc^.stream);
|
|
|
|
AdjustEndianessArray16(NewLUT^.T, NewLUT^.Tsize div sizeof(WORD));
|
|
end
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unknow precission of '%d'", CLUT.prec);
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function ReadLUT_A2B(Icc: LPLCMSICCPROFILE; NewLUT: LPLUT; BaseOffset: integer; sig: icTagSignature): longbool;
|
|
var
|
|
LUT16: icLutAtoB;
|
|
begin
|
|
|
|
result := true;
|
|
|
|
Icc^.Read(@LUT16, sizeof(icLutAtoB), 1, Icc^.stream);
|
|
|
|
NewLUT^.InputChan := LUT16.inputChan;
|
|
NewLUT^.OutputChan := LUT16.outputChan;
|
|
|
|
AdjustEndianess32(@LUT16.offsetB);
|
|
AdjustEndianess32(@LUT16.offsetMat);
|
|
AdjustEndianess32(@LUT16.offsetM);
|
|
AdjustEndianess32(@LUT16.offsetC);
|
|
AdjustEndianess32(@LUT16.offsetA);
|
|
|
|
if (LUT16.offsetB <> 0) then
|
|
result := result and ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 2); // 3.0.2
|
|
|
|
if (LUT16.offsetMat <> 0) then
|
|
ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX4);
|
|
|
|
if (LUT16.offsetM <> 0) then
|
|
result := result and ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 4); // 3.0.2
|
|
|
|
if (LUT16.offsetC <> 0) then
|
|
ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT);
|
|
|
|
if (LUT16.offsetA <> 0) then
|
|
result := result and ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 1); // 3.0.2
|
|
|
|
if (Icc^.PCS = icSigLabData) then
|
|
begin
|
|
|
|
case (sig) of
|
|
|
|
icSigAToB0Tag,
|
|
icSigAToB1Tag,
|
|
icSigAToB2Tag,
|
|
icSigGamutTag,
|
|
icSigPreview0Tag,
|
|
icSigPreview1Tag,
|
|
icSigPreview2Tag:
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_V4_INPUT_EMULATE_V2;
|
|
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
function ReadLUT_B2A(Icc: LPLCMSICCPROFILE; NewLUT: LPLUT; BaseOffset: integer; sig: icTagSignature): longbool;
|
|
var
|
|
LUT16: icLutBtoA;
|
|
begin
|
|
|
|
Icc^.Read(@LUT16, sizeof(icLutBtoA), 1, Icc^.stream);
|
|
|
|
NewLUT^.InputChan := LUT16.inputChan;
|
|
NewLUT^.OutputChan := LUT16.outputChan;
|
|
|
|
AdjustEndianess32(@LUT16.offsetB);
|
|
AdjustEndianess32(@LUT16.offsetMat);
|
|
AdjustEndianess32(@LUT16.offsetM);
|
|
AdjustEndianess32(@LUT16.offsetC);
|
|
AdjustEndianess32(@LUT16.offsetA);
|
|
|
|
if (LUT16.offsetB <> 0) then
|
|
ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 1);
|
|
|
|
if (LUT16.offsetMat <> 0) then
|
|
ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX3);
|
|
|
|
if (LUT16.offsetM <> 0) then
|
|
ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 3);
|
|
|
|
if (LUT16.offsetC <> 0) then
|
|
ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT);
|
|
|
|
if (LUT16.offsetA <> 0) then
|
|
ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 2);
|
|
|
|
if (Icc^.PCS = icSigLabData) then
|
|
begin
|
|
|
|
case (sig) of
|
|
|
|
icSigBToA0Tag,
|
|
icSigBToA1Tag,
|
|
icSigBToA2Tag,
|
|
icSigGamutTag,
|
|
icSigPreview0Tag,
|
|
icSigPreview1Tag,
|
|
icSigPreview2Tag:
|
|
|
|
NewLUT^.wFlags := NewLUT^.wFlags or LUT_V4_OUTPUT_EMULATE_V2;
|
|
end;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
// CLUT main reader
|
|
|
|
function cmsReadICCLut(hProfile: cmsHPROFILE; sig: icTagSignature): LPLUT;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
Base: icTagBase;
|
|
n: integer;
|
|
offset: integer;
|
|
NewLUT: LPLUT;
|
|
begin
|
|
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
n := SearchTag(Icc, sig);
|
|
if (n < 0) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Tag not found");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (Icc^.stream = nil) then
|
|
begin
|
|
result := cmsDupLUT(LPLUT(Icc^.TagPtrs[n]));
|
|
exit;
|
|
end;
|
|
|
|
offset := Icc^.TagOffsets[n];
|
|
|
|
if (Icc^.Seek(Icc^.stream, offset)) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@Base, sizeof(icTagBase), 1, Icc^.stream);
|
|
AdjustEndianess32(pbyte(@Base.sig));
|
|
|
|
NewLUT := cmsAllocLUT();
|
|
if (NewLUT = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "cmsAllocLUT() failed");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
case (Base.sig) of
|
|
|
|
icSigLut8Type: ReadLUT8(Icc, NewLUT, sig);
|
|
icSigLut16Type: ReadLUT16(Icc, NewLUT);
|
|
|
|
icTagTypeSignature(icSiglutAtoBType):
|
|
begin
|
|
// 3.0.2
|
|
if not ReadLUT_A2B(Icc, NewLUT, offset, sig) then
|
|
begin
|
|
cmsFreeLUT(NewLUT);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
end;
|
|
icTagTypeSignature(icSiglutBtoAType): ReadLUT_B2A(Icc, NewLUT, offset, sig);
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", Base.sig);
|
|
cmsFreeLUT(NewLUT);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := NewLUT;
|
|
end;
|
|
|
|
procedure PrecalculatedXFORM(p: _LPcmsTRANSFORM; xin: pointer; xout: pointer; Size: dword);
|
|
var
|
|
accum: pbyte;
|
|
output: pbyte;
|
|
wIn, wOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
i, n: dword;
|
|
begin
|
|
accum := xin;
|
|
output := xout;
|
|
n := Size;
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
|
|
accum := p^.FromInput(p, @wIn, accum);
|
|
cmsEvalLUT(p^.DeviceLink, @wIn, @wOut);
|
|
output := p^.ToOutput(p, @wOut, output);
|
|
end;
|
|
end;
|
|
|
|
procedure CachedXFORM(p: _LPcmsTRANSFORM; xin: pointer; xout: pointer; Size: dword);
|
|
var
|
|
accum: pbyte;
|
|
output: pbyte;
|
|
wIn, wOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
i, n: dword;
|
|
begin
|
|
|
|
accum := PBYTE(xin);
|
|
output := PBYTE(xout);
|
|
n := Size;
|
|
|
|
ZeroMemory(@wIn, sizeof(WORD) * MAXCHANNELS);
|
|
ZeroMemory(@wOut, sizeof(WORD) * MAXCHANNELS);
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
|
|
with p^ do
|
|
begin
|
|
accum := FromInput(p, @wIn, accum);
|
|
|
|
// it seems that disabling cache it speed up!
|
|
(*
|
|
if (comparemem(@wIn, @p ^.CacheIn, sizeof(WORD) * MAXCHANNELS) ) then
|
|
begin
|
|
|
|
CopyMemory(@wOut, @p ^.CacheOut, sizeof(WORD) * MAXCHANNELS);
|
|
end
|
|
else
|
|
begin
|
|
*)
|
|
|
|
//CopyMemory(@p ^.CacheIn, @wIn, sizeof(WORD) * MAXCHANNELS);
|
|
|
|
cmsEvalLUT(DeviceLink, @wIn, @wOut);
|
|
|
|
//CopyMemory(@p ^.CacheOut, @wOut, sizeof(WORD) * MAXCHANNELS);
|
|
|
|
//end;
|
|
|
|
output := ToOutput(p, @wOut, output);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure SetPrecalculatedTransform(p: _LPcmsTRANSFORM; dwFlags: DWORD);
|
|
begin
|
|
p^.xform := PrecalculatedXFORM;
|
|
|
|
if ((dwFlags and cmsFLAGS_NOTCACHE) = 0) then
|
|
begin
|
|
|
|
ZeroMemory(@p^.CacheIn, sizeof(WORD) * MAXCHANNELS);
|
|
cmsEvalLUT(p^.DeviceLink, @p^.CacheIn, @p^.CacheOut);
|
|
p^.xform := CachedXFORM;
|
|
end;
|
|
|
|
end;
|
|
|
|
// Transform is identified as device-link
|
|
|
|
function CreateDeviceLinkTransform(p: _LPcmsTRANSFORM; dwFlags: DWORD): cmsHPROFILE;
|
|
begin
|
|
|
|
if (not IsProperColorSpace(p^.InputProfile, p^.InputFormat, FALSE)) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on input");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (not IsProperColorSpace(p^.InputProfile, p^.OutputFormat, TRUE)) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on output");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.DeviceLink := cmsReadICCLut(p^.InputProfile, icSigAToB0Tag);
|
|
|
|
if (p^.DeviceLink = nil) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Noncompliant device-link profile");
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (p^.PreviewProfile <> nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Proofing not supported on device link transforms");
|
|
end;
|
|
|
|
if (p^.OutputProfile <> nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Output profile should be NULL, since this is a device-link transform");
|
|
end;
|
|
|
|
p^.Phase1 := -1;
|
|
p^.Phase2 := -1;
|
|
p^.Phase3 := -1;
|
|
|
|
SetPrecalculatedTransform(p, dwFlags);
|
|
|
|
p^.ExitColorSpace := cmsGetPCS(p^.InputProfile);
|
|
|
|
// Precalculated device-link profile is ready
|
|
result := cmsHTRANSFORM(p);
|
|
end;
|
|
|
|
function cmsAllocNamedColorList(n: integer): LPcmsNAMEDCOLORLIST;
|
|
var
|
|
size: integer;
|
|
v: LPcmsNAMEDCOLORLIST;
|
|
begin
|
|
size := sizeof(cmsNAMEDCOLORLIST) + (n - 1) * sizeof(cmsNAMEDCOLOR);
|
|
|
|
getmem(v, size);
|
|
|
|
if (v = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory creating named color list");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
ZeroMemory(v, size);
|
|
|
|
v^.nColors := n;
|
|
v^.Allocated := n;
|
|
v^.Prefix[0] := #0;
|
|
v^.Suffix[0] := #0;
|
|
|
|
result := v;
|
|
end;
|
|
|
|
function CheckHeader(v: LPcmsNAMEDCOLORLIST; nc2: PicNamedColor2): longbool;
|
|
begin
|
|
if (v^.Prefix[0] = #0) and (v^.Suffix[0] = #0) and (v^.ColorantCount = 0) then
|
|
begin
|
|
result := TRUE;
|
|
exit;
|
|
end;
|
|
|
|
if (strcomp(PAnsiChar(@v^.Prefix), PAnsiChar(@nc2^.prefix)) <> 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (strcomp(PAnsiChar(@v^.Suffix), PAnsiChar(@nc2^.suffix)) <> 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := (v^.ColorantCount = nc2^.nDeviceCoords);
|
|
end;
|
|
|
|
function GrowNamedColorList(v: LPcmsNAMEDCOLORLIST; ByElements: integer): LPcmsNAMEDCOLORLIST;
|
|
var
|
|
TheNewList: LPcmsNAMEDCOLORLIST;
|
|
NewElements: integer;
|
|
begin
|
|
if (ByElements > v^.Allocated) then
|
|
begin
|
|
|
|
if (v^.Allocated = 0) then
|
|
NewElements := 64
|
|
else
|
|
NewElements := v^.Allocated;
|
|
|
|
while (ByElements > NewElements) do
|
|
NewElements := NewElements * 2;
|
|
|
|
getmem(TheNewList, sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * NewElements));
|
|
|
|
if (TheNewList = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory reallocating named color list");
|
|
result := nil;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
CopyMemory(TheNewList, v, sizeof(cmsNAMEDCOLORLIST) + (v^.nColors - 1) * sizeof(cmsNAMEDCOLOR));
|
|
TheNewList^.Allocated := NewElements;
|
|
|
|
freemem(v);
|
|
result := TheNewList;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := v;
|
|
end;
|
|
|
|
function cmsAppendNamedColor(xform: cmsHTRANSFORM; Name: PAnsiChar; PCS: pwordarray; Colorant: pwordarray): longbool;
|
|
var
|
|
v: _LPcmsTRANSFORM;
|
|
List: LPcmsNAMEDCOLORLIST;
|
|
i: integer;
|
|
begin
|
|
v := _LPcmsTRANSFORM(xform);
|
|
|
|
if (v^.NamedColorList = nil) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
v^.NamedColorList := GrowNamedColorList(v^.NamedColorList, v^.NamedColorList^.nColors + 1);
|
|
|
|
List := v^.NamedColorList;
|
|
|
|
for i := 0 to MAXCHANNELS - 1 do
|
|
List^.List[List^.nColors].DeviceColorant[i] := Colorant[i];
|
|
|
|
for i := 0 to 2 do
|
|
List^.List[List^.nColors].PCS[i] := PCS[i];
|
|
|
|
strlcopy(List^.List[List^.nColors].Name, Name, MAX_PATH - 1);
|
|
|
|
inc(List^.nColors);
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsReadICCnamedColorList(xform: cmsHTRANSFORM; hProfile: cmsHPROFILE; sig: icTagSignature): integer;
|
|
var
|
|
v: _LPcmsTRANSFORM;
|
|
Icc: LPLCMSICCPROFILE;
|
|
n: integer;
|
|
Base: icTagBase;
|
|
offset: integer;
|
|
size: integer;
|
|
nc2: icNamedColor2;
|
|
i, j: integer;
|
|
PCS: array[0..2] of WORD;
|
|
Colorant: array[0..MAXCHANNELS - 1] of WORD;
|
|
Root: array[0..32] of AnsiChar;
|
|
begin
|
|
v := _LPcmsTRANSFORM(xform);
|
|
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
n := SearchTag(Icc, sig);
|
|
if (n < 0) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Named color tag not found");
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
if (Icc^.stream = nil) then
|
|
begin
|
|
|
|
size := Icc^.TagSizes[n];
|
|
|
|
if (v^.NamedColorList <> nil) then
|
|
cmsFreeNamedColorList(v^.NamedColorList);
|
|
getmem(v^.NamedColorList, size);
|
|
CopyMemory(v^.NamedColorList, Icc^.TagPtrs[n], size);
|
|
result := v^.NamedColorList^.nColors;
|
|
exit;
|
|
end;
|
|
|
|
offset := Icc^.TagOffsets[n];
|
|
|
|
if (Icc^.Seek(Icc^.stream, offset)) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@Base, 1, sizeof(icTagBase), Icc^.stream);
|
|
AdjustEndianess32(@Base.sig);
|
|
|
|
case (Base.sig) of
|
|
|
|
icSigNamedColorType:
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Ancient named color profiles are not supported.");
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
icSigNamedColor2Type:
|
|
begin
|
|
|
|
Icc^.Read(@nc2, sizeof(icNamedColor2) - SIZEOF_UINT8_ALIGNED, 1, Icc^.stream);
|
|
AdjustEndianess32(@nc2.vendorFlag);
|
|
AdjustEndianess32(@nc2.count);
|
|
AdjustEndianess32(@nc2.nDeviceCoords);
|
|
|
|
if (not CheckHeader(v^.NamedColorList, @nc2)) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "prefix/suffix/device for named color profiles mismatch.");
|
|
end;
|
|
|
|
strlcopy(v^.NamedColorList^.Prefix, @nc2.prefix, 32);
|
|
strlcopy(v^.NamedColorList^.Suffix, @nc2.suffix, 32);
|
|
v^.NamedColorList^.Suffix[32] := #0;
|
|
v^.NamedColorList^.Prefix[32] := v^.NamedColorList^.Suffix[32];
|
|
|
|
v^.NamedColorList^.ColorantCount := nc2.nDeviceCoords;
|
|
|
|
for i := 0 to nc2.count - 1 do
|
|
begin
|
|
|
|
ZeroMemory(@Colorant, sizeof(WORD) * MAXCHANNELS);
|
|
Icc^.Read(@Root, 1, 32, Icc^.stream);
|
|
Icc^.Read(@PCS, 3, sizeof(WORD), Icc^.stream);
|
|
|
|
for j := 0 to 2 do
|
|
AdjustEndianess16(@PCS[j]);
|
|
|
|
Icc^.Read(@Colorant, sizeof(WORD), nc2.nDeviceCoords, Icc^.stream);
|
|
|
|
for j := 0 to nc2.nDeviceCoords - 1 do
|
|
AdjustEndianess16(@Colorant[j]);
|
|
|
|
cmsAppendNamedColor(v, Root, @PCS, @Colorant);
|
|
end;
|
|
|
|
result := v^.NamedColorList^.nColors;
|
|
exit;
|
|
end;
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Bad tag signature '%lx' found.", Base.sig);
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure NC2deviceXform(p: _LPcmsTRANSFORM; xin: pointer; xout: pointer; Size: integer);
|
|
var
|
|
accum: pbyte;
|
|
output: pbyte;
|
|
wIn, wOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
i: dword;
|
|
begin
|
|
|
|
accum := xin;
|
|
output := xout;
|
|
|
|
for i := 0 to Size - 1 do
|
|
begin
|
|
|
|
accum := p^.FromInput(p, @wIn, accum);
|
|
CopyMemory(@wOut, @(p^.NamedColorList^.List[wIn[0]].DeviceColorant), sizeof(WORD) * MAXCHANNELS);
|
|
output := p^.ToOutput(p, @wOut, output);
|
|
end;
|
|
|
|
end;
|
|
|
|
function GetPhase(hProfile: cmsHPROFILE): integer;
|
|
begin
|
|
case (cmsGetPCS(hProfile)) of
|
|
|
|
icSigXYZData:
|
|
begin
|
|
result := XYZRel;
|
|
exit;
|
|
end;
|
|
|
|
icSigLabData:
|
|
begin
|
|
result := LabRel;
|
|
exit;
|
|
end;
|
|
|
|
else
|
|
; //cmsSignalError(LCMS_ERRC_ABORTED, "Invalid PCS");
|
|
end;
|
|
|
|
result := XYZRel;
|
|
end;
|
|
|
|
function cmsIsTag(hProfile: cmsHPROFILE; sig: icTagSignature): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
result := SearchTag(Icc, sig) >= 0;
|
|
|
|
end;
|
|
|
|
procedure cmsSetDeviceClass(hProfile: cmsHPROFILE; sig: icProfileClassSignature);
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
Icc^.DeviceClass := sig;
|
|
end;
|
|
|
|
procedure cmsSetColorSpace(hProfile: cmsHPROFILE; sig: icColorSpaceSignature);
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
Icc^.ColorSpace := sig;
|
|
end;
|
|
|
|
procedure cmsSetPCS(hProfile: cmsHPROFILE; pcs: icColorSpaceSignature);
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
Icc^.PCS := pcs;
|
|
end;
|
|
|
|
function InitTag(Icc: LPLCMSICCPROFILE; sig: icTagSignature; size: integer; Init: pointer): pointer;
|
|
var
|
|
Ptr: pointer;
|
|
i: icInt32Number;
|
|
begin
|
|
i := SearchTag(Icc, sig);
|
|
if (i >= 0) then
|
|
begin
|
|
|
|
if (Icc^.TagPtrs[i] <> nil) then
|
|
freemem(Icc^.TagPtrs[i]);
|
|
end
|
|
else
|
|
begin
|
|
i := Icc^.TagCount;
|
|
inc(Icc^.TagCount);
|
|
end;
|
|
|
|
getmem(Ptr, size);
|
|
CopyMemory(Ptr, Init, size);
|
|
|
|
Icc^.TagNames[i] := sig;
|
|
Icc^.TagSizes[i] := size;
|
|
Icc^.TagPtrs[i] := Ptr;
|
|
|
|
result := Ptr;
|
|
end;
|
|
|
|
function _cmsAddTextTag(hProfile: cmsHPROFILE; sig: icTagSignature; Text: PAnsiChar): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
InitTag(Icc, sig, IEStrLen(Text) + 1, Text);
|
|
result := TRUE;
|
|
end;
|
|
|
|
procedure cmsSetRenderingIntent(hProfile: cmsHPROFILE; RenderingIntent: integer);
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
Icc^.RenderingIntent := icRenderingIntent(RenderingIntent);
|
|
end;
|
|
|
|
function _cmsAddXYZTag(hProfile: cmsHPROFILE; sig: icTagSignature; XYZ: LPcmsCIEXYZ): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
InitTag(Icc, sig, sizeof(cmsCIEXYZ), XYZ);
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SizeOfGammaTab(xIn: LPGAMMATABLE): integer;
|
|
begin
|
|
result := sizeof(GAMMATABLE) + (xIn^.nEntries - 1) * sizeof(WORD);
|
|
end;
|
|
|
|
function _cmsAddGammaTag(hProfile: cmsHPROFILE; sig: icTagSignature; TransferFunction: LPGAMMATABLE): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
InitTag(Icc, sig, SizeOfGammaTab(TransferFunction), TransferFunction);
|
|
result := TRUE;
|
|
end;
|
|
|
|
function DupBlock(Icc: LPLCMSICCPROFILE; Block: pointer; size: integer): pointer;
|
|
begin
|
|
if (Block <> nil) and (size > 0) then
|
|
result := InitTag(Icc, icTagSignature(0), size, Block)
|
|
else
|
|
result := nil;
|
|
end;
|
|
|
|
function _cmsAddLUTTag(hProfile: cmsHPROFILE; sig: icTagSignature; xlut: pointer): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
Orig, Stored: LPLUT;
|
|
i: dword;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
Orig := LPLUT(xlut);
|
|
Stored := LPLUT(InitTag(Icc, icTagSignature(sig), sizeof(LUT), xlut));
|
|
|
|
for i := 0 to Orig^.InputChan - 1 do
|
|
Stored^.L1[i] := DupBlock(Icc, Orig^.L1[i], sizeof(WORD) * Orig^.In16params.nSamples);
|
|
|
|
for i := 0 to Orig^.OutputChan - 1 do
|
|
Stored^.L2[i] := DupBlock(Icc, Orig^.L2[i], sizeof(WORD) * Orig^.Out16params.nSamples);
|
|
|
|
Stored^.T := DupBlock(Icc, Orig^.T, Orig^.Tsize);
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function _cmsAddChromaticityTag(hProfile: cmsHPROFILE; sig: icTagSignature; Chrm: LPcmsCIExyYTRIPLE): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
InitTag(Icc, sig, sizeof(cmsCIExyYTRIPLE), Chrm);
|
|
result := TRUE;
|
|
end;
|
|
|
|
function _cmsAddSequenceDescriptionTag(hProfile: cmsHPROFILE; sig: icTagSignature; pseq: LPcmsSEQ): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
InitTag(Icc, sig, sizeof(integer) + pseq^.n * sizeof(cmsPSEQDESC), pseq);
|
|
result := TRUE;
|
|
|
|
end;
|
|
|
|
function _cmsAddNamedColorTag(hProfile: cmsHPROFILE; sig: icTagSignature; nc: LPcmsNAMEDCOLORLIST): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
InitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc^.nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
|
|
result := FALSE;
|
|
end;
|
|
|
|
function cmsAddTag(hProfile: cmsHPROFILE; sig: icTagSignature; Tag: pointer): longbool;
|
|
var
|
|
rc: longbool;
|
|
begin
|
|
|
|
case (sig) of
|
|
|
|
icSigCharTargetTag,
|
|
icSigCopyrightTag,
|
|
icSigProfileDescriptionTag,
|
|
icSigDeviceMfgDescTag,
|
|
icSigDeviceModelDescTag:
|
|
rc := _cmsAddTextTag(hProfile, sig, PAnsiChar(Tag));
|
|
|
|
icSigRedColorantTag,
|
|
icSigGreenColorantTag,
|
|
icSigBlueColorantTag,
|
|
icSigMediaWhitePointTag,
|
|
icSigMediaBlackPointTag:
|
|
rc := _cmsAddXYZTag(hProfile, sig, LPcmsCIEXYZ(Tag));
|
|
|
|
icSigRedTRCTag,
|
|
icSigGreenTRCTag,
|
|
icSigBlueTRCTag,
|
|
icSigGrayTRCTag:
|
|
rc := _cmsAddGammaTag(hProfile, sig, LPGAMMATABLE(Tag));
|
|
|
|
icSigAToB0Tag,
|
|
icSigAToB1Tag,
|
|
icSigAToB2Tag,
|
|
icSigBToA0Tag,
|
|
icSigBToA1Tag,
|
|
icSigBToA2Tag,
|
|
icSigGamutTag,
|
|
icSigPreview0Tag,
|
|
icSigPreview1Tag,
|
|
icSigPreview2Tag:
|
|
rc := _cmsAddLUTTag(hProfile, sig, Tag);
|
|
|
|
icTagSignature(icSigChromaticityTag):
|
|
rc := _cmsAddChromaticityTag(hProfile, sig, LPcmsCIExyYTRIPLE(Tag));
|
|
|
|
icSigProfileSequenceDescTag:
|
|
rc := _cmsAddSequenceDescriptionTag(hProfile, sig, LPcmsSEQ(Tag));
|
|
|
|
icSigNamedColor2Tag:
|
|
rc := _cmsAddNamedColorTag(hProfile, sig, LPcmsNAMEDCOLORLIST(Tag));
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "cmsAddTag: Tag '%x' is unsupported", sig);
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
case (sig) of
|
|
|
|
icSigMediaWhitePointTag,
|
|
icSigMediaBlackPointTag,
|
|
icTagSignature(icSigChromaticAdaptationTag):
|
|
|
|
ReadCriticalTags(LPLCMSICCPROFILE(hProfile));
|
|
|
|
end;
|
|
|
|
result := rc;
|
|
|
|
end;
|
|
|
|
procedure cmsxyY2XYZ(Dest: LPcmsCIEXYZ; Source: LPcmsCIExyY); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
|
|
Dest^.X := (Source^.x / Source^.y_mi) * Source^.Y_ma;
|
|
Dest^.Y := Source^.Y_ma;
|
|
Dest^.Z := ((1 - Source^.x - Source^.y_mi) / Source^.y_mi) * Source^.Y_ma;
|
|
end;
|
|
|
|
function cmsBuildRGB2XYZtransferMatrix(r: LPMAT3; WhitePt: LPcmsCIExyY; Primrs: LPcmsCIExyYTRIPLE): longbool;
|
|
var
|
|
WhitePoint, Coef: VEC3;
|
|
xResult, Primaries: MAT3;
|
|
xn, yn: double;
|
|
xr, yr: double;
|
|
xg, yg: double;
|
|
xb, yb: double;
|
|
begin
|
|
|
|
xn := WhitePt^.x;
|
|
yn := WhitePt^.y_mi;
|
|
xr := Primrs^.Red.x;
|
|
yr := Primrs^.Red.y_mi;
|
|
xg := Primrs^.Green.x;
|
|
yg := Primrs^.Green.y_mi;
|
|
xb := Primrs^.Blue.x;
|
|
yb := Primrs^.Blue.y_mi;
|
|
|
|
VEC3init(@Primaries.v[0], xr, xg, xb);
|
|
VEC3init(@Primaries.v[1], yr, yg, yb);
|
|
VEC3init(@Primaries.v[2], (1 - xr - yr), (1 - xg - yg), (1 - xb - yb));
|
|
|
|
if (MAT3inverse(@Primaries, @xResult) = 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
VEC3init(@WhitePoint, xn / yn, 1.0, (1.0 - xn - yn) / yn);
|
|
|
|
MAT3eval(@Coef, @xResult, @WhitePoint);
|
|
|
|
VEC3init(@r^.v[0], Coef.n[VX] * xr, Coef.n[VY] * xg, Coef.n[VZ] * xb);
|
|
VEC3init(@r^.v[1], Coef.n[VX] * yr, Coef.n[VY] * yg, Coef.n[VZ] * yb);
|
|
VEC3init(@r^.v[2], Coef.n[VX] * (1.0 - xr - yr), Coef.n[VY] * (1.0 - xg - yg), Coef.n[VZ] * (1.0 - xb - yb));
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function TransportValue32(Value: icInt32Number): icInt32Number;
|
|
var
|
|
Temp: icInt32Number;
|
|
begin
|
|
Temp := Value;
|
|
|
|
AdjustEndianess32(@Temp);
|
|
result := Temp;
|
|
end;
|
|
|
|
function TransportValue16(Value: WORD): WORD;
|
|
var
|
|
Temp: WORD;
|
|
begin
|
|
Temp := Value;
|
|
|
|
AdjustEndianess16(@Temp);
|
|
result := Temp;
|
|
end;
|
|
|
|
procedure EncodeDateTime(DateTime: PicDateTimeNumber);
|
|
var
|
|
tm: TDateTime;
|
|
year, month, day: word;
|
|
hour, min, sec, msec: word;
|
|
begin
|
|
|
|
tm := date + time;
|
|
DecodeDate(tm, year, month, day);
|
|
DecodeTime(tm, hour, min, sec, msec);
|
|
|
|
DateTime^.year := TransportValue16(year);
|
|
DateTime^.month := TransportValue16(month);
|
|
DateTime^.day := TransportValue16(day);
|
|
DateTime^.hours := TransportValue16(hour);
|
|
DateTime^.minutes := TransportValue16(min);
|
|
DateTime^.seconds := TransportValue16(sec);
|
|
|
|
end;
|
|
|
|
function SaveHeader(OutStream: TStream; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
Header: icHeader;
|
|
begin
|
|
|
|
Header.size := TransportValue32(icInt32Number(UsedSpace));
|
|
Header.cmmId := TransportValue32(lcmsSignature);
|
|
Header.version := TransportValue32(icInt32Number($02300000));
|
|
Header.deviceClass := icProfileClassSignature(TransportValue32(integer(Icc^.DeviceClass)));
|
|
Header.colorSpace := icColorSpaceSignature(TransportValue32(integer(Icc^.ColorSpace)));
|
|
Header.pcs := icColorSpaceSignature(TransportValue32(integer(Icc^.PCS)));
|
|
|
|
EncodeDateTime(@Header.date);
|
|
|
|
Header.magic := TransportValue32(icMagicNumber);
|
|
Header.xplatform := icPlatformSignature(TransportValue32(integer(icSigMicrosoft)));
|
|
|
|
Header.flags := TransportValue32(Icc^.flags);
|
|
Header.manufacturer := TransportValue32(lcmsSignature);
|
|
Header.model := TransportValue32(0);
|
|
Header.attributes[0] := TransportValue32(0);
|
|
Header.attributes[1] := TransportValue32(0);
|
|
|
|
Header.renderingIntent := TransportValue32(integer(Icc^.RenderingIntent));
|
|
|
|
Header.illuminant.X := TransportValue32(DOUBLE_TO_FIXED(Icc^.Illuminant.X));
|
|
Header.illuminant.Y := TransportValue32(DOUBLE_TO_FIXED(Icc^.Illuminant.Y));
|
|
Header.illuminant.Z := TransportValue32(DOUBLE_TO_FIXED(Icc^.Illuminant.Z));
|
|
|
|
Header.creator := TransportValue32(lcmsSignature);
|
|
|
|
ZeroMemory(@Header.reserved, sizeof(Header.reserved));
|
|
|
|
CopyMemory(@Header.reserved, @Icc^.ProfileID, 16);
|
|
|
|
UsedSpace := 0;
|
|
|
|
result := Icc^.Write(OutStream, sizeof(icHeader), @Header);
|
|
end;
|
|
|
|
function SaveTagDirectory(OutStream: TStream; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
i: icInt32Number;
|
|
Tag: icTag;
|
|
Count: icInt32Number;
|
|
begin
|
|
Count := 0;
|
|
|
|
for i := 0 to Icc^.TagCount - 1 do
|
|
begin
|
|
if (Icc^.TagNames[i] <> icTagSignature(0)) then
|
|
inc(Count);
|
|
end;
|
|
|
|
Count := TransportValue32(Count);
|
|
if (not Icc^.Write(OutStream, sizeof(icInt32Number), @Count)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to Icc^.TagCount - 1 do
|
|
begin
|
|
|
|
if (Icc^.TagNames[i] = icTagSignature(0)) then
|
|
continue;
|
|
|
|
Tag.sig := icTagSignature(TransportValue32(integer(Icc^.TagNames[i])));
|
|
Tag.offset := TransportValue32(icInt32Number(Icc^.TagOffsets[i]));
|
|
Tag.size := TransportValue32(icInt32Number(Icc^.TagSizes[i]));
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(icTag), @Tag)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function ALIGNLONG(x: dword): dword; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := (((x) + 3) and not (3)); // Aligns to DWORD boundary
|
|
end;
|
|
|
|
function SetupBase(OutStream: TStream; sig: icTagTypeSignature; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
Base: icTagBase;
|
|
begin
|
|
Base.sig := icTagTypeSignature(TransportValue32(integer(sig)));
|
|
ZeroMemory(@Base.reserved, sizeof(Base.reserved));
|
|
result := Icc^.Write(OutStream, sizeof(icTagBase), @Base);
|
|
end;
|
|
|
|
function SaveDescription(OutStream: TStream; Text: PAnsiChar; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
len, Count, TotalSize, AlignedSize: icUInt32Number;
|
|
Filler: array[0..255] of AnsiChar;
|
|
begin
|
|
|
|
len := icUInt32Number((IEStrLen(Text) + 1));
|
|
|
|
TotalSize := sizeof(icTagBase) + sizeof(icUInt32Number) + len +
|
|
sizeof(icUInt32Number) + sizeof(icUInt32Number) +
|
|
sizeof(icUInt16Number) + sizeof(icUInt8Number) + 67;
|
|
|
|
AlignedSize := TotalSize;
|
|
|
|
if (not SetupBase(OutStream, icSigTextDescriptionType, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
AlignedSize := AlignedSize - sizeof(icTagBase);
|
|
|
|
Count := TransportValue32(len);
|
|
if (not Icc^.Write(OutStream, sizeof(icUInt32Number), @Count)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
AlignedSize := AlignedSize - sizeof(icUInt32Number);
|
|
|
|
if (not Icc^.Write(OutStream, len, pointer(Text))) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
AlignedSize := AlignedSize - len;
|
|
|
|
ZeroMemory(@Filler, AlignedSize);
|
|
if (not Icc^.Write(OutStream, AlignedSize, @Filler)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveXYZNumber(OutStream: TStream; Value: LPcmsCIEXYZ; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
XYZ: icXYZNumber;
|
|
begin
|
|
|
|
if (not SetupBase(OutStream, icSigXYZType, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
XYZ.X := TransportValue32(DOUBLE_TO_FIXED(Value^.X));
|
|
XYZ.Y := TransportValue32(DOUBLE_TO_FIXED(Value^.Y));
|
|
XYZ.Z := TransportValue32(DOUBLE_TO_FIXED(Value^.Z));
|
|
|
|
result := Icc^.Write(OutStream, sizeof(icXYZNumber), @XYZ);
|
|
end;
|
|
|
|
function SaveGamma(OutStream: TStream; Gamma: LPGAMMATABLE; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
Count: icInt32Number;
|
|
i: integer;
|
|
Val: WORD;
|
|
begin
|
|
|
|
if (not SetupBase(OutStream, icSigCurveType, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
Count := TransportValue32(Gamma^.nEntries);
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(icInt32Number), @Count)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to Gamma^.nEntries - 1 do
|
|
begin
|
|
Val := TransportValue16(Gamma^.GammaTable[i]);
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(WORD), @Val)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveText(OutStream: TStream; Text: PAnsiChar; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
len: integer;
|
|
begin
|
|
len := IEStrLen(Text) + 1;
|
|
|
|
if (not SetupBase(OutStream, icSigTextType, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not Icc^.Write(OutStream, len, pointer(Text))) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveOneChromaticity(OutStream: TStream; x, y: double; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
xf, yf: Fixed32;
|
|
begin
|
|
|
|
xf := TransportValue32(DOUBLE_TO_FIXED(x));
|
|
yf := TransportValue32(DOUBLE_TO_FIXED(y));
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(Fixed32), @xf)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not Icc^.Write(OutStream, sizeof(Fixed32), @yf)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveChromaticities(OutStream: TStream; chrm: LPcmsCIExyYTRIPLE; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
nChans, Table: WORD;
|
|
begin
|
|
|
|
if (not SetupBase(OutStream, icTagTypeSignature(icSigChromaticityType), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
nChans := TransportValue16(3);
|
|
if (not Icc^.Write(OutStream, sizeof(WORD), @nChans)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
Table := TransportValue16(0);
|
|
if (not Icc^.Write(OutStream, sizeof(WORD), @Table)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (not SaveOneChromaticity(OutStream, chrm^.Red.x, chrm^.Red.y_mi, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not SaveOneChromaticity(OutStream, chrm^.Green.x, chrm^.Green.y_mi, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not SaveOneChromaticity(OutStream, chrm^.Blue.x, chrm^.Blue.y_mi, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveLUT8(OutStream: TStream; NewLUT: LPLUT; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
LUT8: icLut8;
|
|
i, j: integer;
|
|
nTabSize: integer;
|
|
val: BYTE;
|
|
begin
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL1) <> 0 then
|
|
begin
|
|
|
|
if (NewLUT^.InputEntries <> 256) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on prelinearization");
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL2) <> 0 then
|
|
begin
|
|
|
|
if (NewLUT^.OutputEntries <> 256) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on postlinearization");
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
if (not SetupBase(OutStream, icSigLut8Type, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
LUT8.clutPoints := icUInt8Number(NewLUT^.cLutPoints);
|
|
LUT8.inputChan := icUInt8Number(NewLUT^.InputChan);
|
|
LUT8.outputChan := icUInt8Number(NewLUT^.OutputChan);
|
|
|
|
if (NewLUT^.wFlags and LUT_HASMATRIX) <> 0 then
|
|
begin
|
|
|
|
LUT8.e00 := TransportValue32(NewLUT^.Matrix.v[0].n[0]);
|
|
LUT8.e01 := TransportValue32(NewLUT^.Matrix.v[0].n[1]);
|
|
LUT8.e02 := TransportValue32(NewLUT^.Matrix.v[0].n[2]);
|
|
LUT8.e10 := TransportValue32(NewLUT^.Matrix.v[1].n[0]);
|
|
LUT8.e11 := TransportValue32(NewLUT^.Matrix.v[1].n[1]);
|
|
LUT8.e12 := TransportValue32(NewLUT^.Matrix.v[1].n[2]);
|
|
LUT8.e20 := TransportValue32(NewLUT^.Matrix.v[2].n[0]);
|
|
LUT8.e21 := TransportValue32(NewLUT^.Matrix.v[2].n[1]);
|
|
LUT8.e22 := TransportValue32(NewLUT^.Matrix.v[2].n[2]);
|
|
end
|
|
else
|
|
begin
|
|
|
|
LUT8.e00 := TransportValue32(DOUBLE_TO_FIXED(1));
|
|
LUT8.e01 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT8.e02 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT8.e10 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT8.e11 := TransportValue32(DOUBLE_TO_FIXED(1));
|
|
LUT8.e12 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT8.e20 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT8.e21 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT8.e22 := TransportValue32(DOUBLE_TO_FIXED(1));
|
|
end;
|
|
|
|
Icc^.Write(OutStream, sizeof(icLut8) - SIZEOF_UINT8_ALIGNED, @LUT8);
|
|
|
|
for i := 0 to NewLUT^.InputChan - 1 do
|
|
begin
|
|
|
|
for j := 0 to 255 do
|
|
begin
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL1) <> 0 then
|
|
val := floor(pwordarray(NewLUT^.L1[i])[j] / 257.0 + 0.5)
|
|
else
|
|
val := j;
|
|
|
|
Icc^.Write(OutStream, 1, @val);
|
|
end;
|
|
|
|
end;
|
|
|
|
nTabSize := (NewLUT^.OutputChan * uipow(NewLUT^.cLutPoints,
|
|
NewLUT^.InputChan));
|
|
|
|
for j := 0 to nTabSize - 1 do
|
|
begin
|
|
|
|
val := floor(pwordarray(NewLUT^.T)[j] / 257.0 + 0.5);
|
|
Icc^.Write(OutStream, 1, @val);
|
|
end;
|
|
|
|
for i := 0 to NewLUT^.OutputChan - 1 do
|
|
begin
|
|
|
|
for j := 0 to 255 do
|
|
begin
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL2) <> 0 then
|
|
val := floor(pwordarray(NewLUT^.L2[i])[j] / 257.0 + 0.5)
|
|
else
|
|
val := j;
|
|
|
|
Icc^.Write(OutStream, 1, @val);
|
|
end;
|
|
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveWordsTable(OutStream: TStream; nEntries: integer; Tab: PWORD; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
nTabSize: integer;
|
|
PtrW: PWORD;
|
|
begin
|
|
nTabSize := sizeof(WORD) * nEntries;
|
|
getmem(PtrW, nTabSize);
|
|
|
|
if (PtrW = nil) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
CopyMemory(PtrW, Tab, nTabSize);
|
|
AdjustEndianessArray16(PtrW, nEntries);
|
|
Icc^.Write(OutStream, nTabSize, PtrW);
|
|
freemem(PtrW);
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveLUT(OutStream: TStream; NewLUT: LPLUT; Icc: LPLCMSICCPROFILE): longbool;
|
|
const
|
|
NullTbl: array[0..1] of WORD = (0, $FFFF);
|
|
var
|
|
LUT16: icLut16;
|
|
i: dword;
|
|
nTabSize: integer;
|
|
begin
|
|
|
|
if (not SetupBase(OutStream, icSigLut16Type, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
LUT16.clutPoints := icUInt8Number(NewLUT^.cLutPoints);
|
|
LUT16.inputChan := icUInt8Number(NewLUT^.InputChan);
|
|
LUT16.outputChan := icUInt8Number(NewLUT^.OutputChan);
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL1) <> 0 then
|
|
LUT16.inputEnt := TransportValue16((NewLUT^.InputEntries))
|
|
else
|
|
LUT16.inputEnt := TransportValue16((2));
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL2) <> 0 then
|
|
LUT16.outputEnt := TransportValue16((NewLUT^.OutputEntries))
|
|
else
|
|
LUT16.outputEnt := TransportValue16((2));
|
|
|
|
if (NewLUT^.wFlags and LUT_HASMATRIX) <> 0 then
|
|
begin
|
|
|
|
LUT16.e00 := TransportValue32(NewLUT^.Matrix.v[0].n[0]);
|
|
LUT16.e01 := TransportValue32(NewLUT^.Matrix.v[0].n[1]);
|
|
LUT16.e02 := TransportValue32(NewLUT^.Matrix.v[0].n[2]);
|
|
LUT16.e10 := TransportValue32(NewLUT^.Matrix.v[1].n[0]);
|
|
LUT16.e11 := TransportValue32(NewLUT^.Matrix.v[1].n[1]);
|
|
LUT16.e12 := TransportValue32(NewLUT^.Matrix.v[1].n[2]);
|
|
LUT16.e20 := TransportValue32(NewLUT^.Matrix.v[2].n[0]);
|
|
LUT16.e21 := TransportValue32(NewLUT^.Matrix.v[2].n[1]);
|
|
LUT16.e22 := TransportValue32(NewLUT^.Matrix.v[2].n[2]);
|
|
end
|
|
else
|
|
begin
|
|
|
|
LUT16.e00 := TransportValue32(DOUBLE_TO_FIXED(1));
|
|
LUT16.e01 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT16.e02 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT16.e10 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT16.e11 := TransportValue32(DOUBLE_TO_FIXED(1));
|
|
LUT16.e12 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT16.e20 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT16.e21 := TransportValue32(DOUBLE_TO_FIXED(0));
|
|
LUT16.e22 := TransportValue32(DOUBLE_TO_FIXED(1));
|
|
end;
|
|
|
|
Icc^.Write(OutStream, sizeof(icLut16) - SIZEOF_UINT16_ALIGNED, @LUT16);
|
|
|
|
for i := 0 to NewLUT^.InputChan - 1 do
|
|
begin
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL1) <> 0 then
|
|
begin
|
|
|
|
if (not SaveWordsTable(OutStream, NewLUT^.InputEntries, NewLUT^.L1[i], Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
end
|
|
else
|
|
Icc^.Write(OutStream, sizeof(WORD) * 2, @NullTbl);
|
|
end;
|
|
|
|
nTabSize := (NewLUT^.OutputChan * uipow(NewLUT^.cLutPoints, NewLUT^.InputChan));
|
|
|
|
if (not SaveWordsTable(OutStream, nTabSize, NewLUT^.T, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to NewLUT^.OutputChan - 1 do
|
|
begin
|
|
|
|
if (NewLUT^.wFlags and LUT_HASTL2) <> 0 then
|
|
begin
|
|
|
|
if (not SaveWordsTable(OutStream, NewLUT^.OutputEntries, NewLUT^.L2[i], Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
Icc^.Write(OutStream, sizeof(WORD) * 2, @NullTbl);
|
|
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveSequenceDescriptionTag(OutStream: TStream; seq: LPcmsSEQ; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
nSeqs: icUInt32Number;
|
|
DescStruct: icDescStruct;
|
|
i, n: integer;
|
|
pseq: LPcmsPSEQDESC;
|
|
sec: LPcmsPSEQDESC;
|
|
begin
|
|
n := seq^.n;
|
|
pseq := @seq^.seq;
|
|
|
|
if (not SetupBase(OutStream, icSigProfileSequenceDescType, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
nSeqs := TransportValue32(n);
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(icUInt32Number), @nSeqs)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
|
|
sec := pseq;
|
|
inc(sec, i);
|
|
|
|
DescStruct.deviceMfg := icSignature(icTagTypeSignature(TransportValue32(sec^.deviceMfg)));
|
|
DescStruct.deviceModel := icSignature(icTagTypeSignature(TransportValue32(sec^.deviceModel)));
|
|
DescStruct.technology := icTechnologySignature(TransportValue32(integer(sec^.technology)));
|
|
DescStruct.attributes[0] := TransportValue32(sec^.attributes[0]);
|
|
DescStruct.attributes[1] := TransportValue32(sec^.attributes[1]);
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, @DescStruct)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (not SaveDescription(OutStream, sec^.Manufacturer, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not SaveDescription(OutStream, sec^.Model, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveNamedColorList(OutStream: TStream; NamedColorList: LPcmsNAMEDCOLORLIST; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
vendorFlag: icUInt32Number; // Bottom 16 bits for IC use
|
|
count: icUInt32Number; // Count of named colors
|
|
nDeviceCoords: icUInt32Number; // Num of device coordinates
|
|
prefix: array[0..31] of icInt8Number; // Prefix for each color name
|
|
suffix: array[0..31] of icInt8Number; // Suffix for each color name
|
|
i: integer;
|
|
PCS: array[0..2] of icUInt16Number;
|
|
Colorant: array[0..MAXCHANNELS - 1] of icUInt16Number;
|
|
root: array[0..31] of icInt8Number;
|
|
Color: LPcmsNAMEDCOLOR;
|
|
j: integer;
|
|
begin
|
|
|
|
if (not SetupBase(OutStream, icSigNamedColor2Type, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
vendorFlag := TransportValue32(0);
|
|
count := TransportValue32(NamedColorList^.nColors);
|
|
nDeviceCoords := TransportValue32(NamedColorList^.ColorantCount);
|
|
|
|
strlcopy(@prefix, NamedColorList^.Prefix, 32);
|
|
strlcopy(@suffix, NamedColorList^.Suffix, 32);
|
|
|
|
if (not Icc^.Write(OutStream, sizeof(icUInt32Number), @vendorFlag)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not Icc^.Write(OutStream, sizeof(icUInt32Number), @count)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not Icc^.Write(OutStream, sizeof(icUInt32Number), @nDeviceCoords)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not Icc^.Write(OutStream, 32, @prefix)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not Icc^.Write(OutStream, 32, @suffix)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to NamedColorList^.nColors - 1 do
|
|
begin
|
|
|
|
Color := @NamedColorList^.List;
|
|
inc(Color, i);
|
|
|
|
strlcopy(@root, Color^.Name, 32);
|
|
if (not Icc^.Write(OutStream, 32, @root)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for j := 0 to 2 do
|
|
PCS[j] := TransportValue16(Color^.PCS[j]);
|
|
|
|
if (not Icc^.Write(OutStream, 3 * sizeof(icUInt16Number), @PCS)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
for j := 0 to NamedColorList^.ColorantCount - 1 do
|
|
Colorant[j] := TransportValue16(Color^.DeviceColorant[j]);
|
|
|
|
if (not Icc^.Write(OutStream, NamedColorList^.ColorantCount * sizeof(icUInt16Number), @Colorant)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function SaveTags(OutStream: TStream; Icc: LPLCMSICCPROFILE): longbool;
|
|
var
|
|
Data: PBYTE;
|
|
i: icInt32Number;
|
|
xBegin: integer;
|
|
AlignedSpace, FillerSize: integer;
|
|
Filler: array[0..19] of BYTE;
|
|
begin
|
|
|
|
for i := 0 to Icc^.TagCount - 1 do
|
|
begin
|
|
|
|
if (Icc^.TagNames[i] = icTagSignature(0)) then
|
|
continue;
|
|
|
|
AlignedSpace := ALIGNLONG(UsedSpace);
|
|
FillerSize := AlignedSpace - UsedSpace;
|
|
|
|
if (FillerSize > 0) then
|
|
begin
|
|
|
|
ZeroMemory(@Filler, 16);
|
|
if (not Icc^.Write(OutStream, FillerSize, @Filler)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
xBegin := UsedSpace;
|
|
Icc^.TagOffsets[i] := xBegin;
|
|
Data := Icc^.TagPtrs[i];
|
|
if (Data = nil) then
|
|
continue;
|
|
|
|
case (Icc^.TagNames[i]) of
|
|
|
|
icSigProfileDescriptionTag,
|
|
icSigDeviceMfgDescTag,
|
|
icSigDeviceModelDescTag:
|
|
if (not SaveDescription(OutStream, PAnsiChar(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
icSigRedColorantTag,
|
|
icSigGreenColorantTag,
|
|
icSigBlueColorantTag,
|
|
icSigMediaWhitePointTag,
|
|
icSigMediaBlackPointTag:
|
|
if (not SaveXYZNumber(OutStream, LPcmsCIEXYZ(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
icSigRedTRCTag,
|
|
icSigGreenTRCTag,
|
|
icSigBlueTRCTag,
|
|
icSigGrayTRCTag:
|
|
if (not SaveGamma(OutStream, LPGAMMATABLE(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
icSigCharTargetTag,
|
|
icSigCopyrightTag:
|
|
if (not SaveText(OutStream, PAnsiChar(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
icTagSignature(icSigChromaticityTag):
|
|
if (not SaveChromaticities(OutStream, LPcmsCIExyYTRIPLE(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
icSigAToB0Tag,
|
|
icSigAToB1Tag,
|
|
icSigAToB2Tag,
|
|
icSigBToA0Tag,
|
|
icSigBToA1Tag,
|
|
icSigBToA2Tag,
|
|
icSigGamutTag,
|
|
icSigPreview0Tag,
|
|
icSigPreview1Tag,
|
|
icSigPreview2Tag:
|
|
|
|
if (Icc^.SaveAs8Bits) then
|
|
begin
|
|
|
|
if (not SaveLUT8(OutStream, LPLUT(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (not SaveLUT(OutStream, LPLUT(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
icSigProfileSequenceDescTag:
|
|
if (not SaveSequenceDescriptionTag(OutStream, LPcmsSEQ(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
icTagSignature(icSigNamedColor2Type):
|
|
if (not SaveNamedColorList(OutStream, LPcmsNAMEDCOLORLIST(Data), Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
Icc^.TagSizes[i] := (UsedSpace - xBegin);
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function _cmsSaveProfile(hProfile: cmsHPROFILE; stream: TStream): longbool;
|
|
var
|
|
OutStream: TStream;
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
Icc^.Write := @FileWrite;
|
|
|
|
if (not SaveHeader(nil, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not SaveTagDirectory(nil, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not SaveTags(nil, Icc)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
OutStream := stream;
|
|
if (OutStream = nil) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (SaveHeader(OutStream, Icc)) and
|
|
(SaveTagDirectory(OutStream, Icc)) and
|
|
(SaveTags(OutStream, Icc)) then
|
|
begin
|
|
|
|
//OutStream.Free; // do not free, because we don't control the stream object
|
|
result := true;
|
|
end
|
|
else
|
|
begin
|
|
|
|
//OutStream.Free; // do not free, because we don't control the stream object
|
|
//unlink(FileName);
|
|
result := FALSE;
|
|
end;
|
|
end;
|
|
|
|
function IEcmsCloseProfile(hProfile: cmsHPROFILE): longbool;
|
|
var
|
|
icco: LPLCMSICCPROFILE;
|
|
xfile: tstream;
|
|
rc: longbool;
|
|
i: icInt32Number;
|
|
begin
|
|
icco := LPLCMSICCPROFILE(hProfile);
|
|
rc := TRUE;
|
|
|
|
if (icco = nil) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (icco^.IsWrite) then
|
|
begin
|
|
|
|
icco^.IsWrite := FALSE;
|
|
rc := _cmsSaveProfile(hProfile, icco^.PhysicalFile);
|
|
end;
|
|
|
|
xfile := icco^.stream;
|
|
|
|
if (xfile = nil) then
|
|
begin
|
|
|
|
for i := 0 to icco^.TagCount - 1 do
|
|
begin
|
|
|
|
if (icco^.TagPtrs[i] <> nil) then
|
|
freemem(icco^.TagPtrs[i]);
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
icco^.Close(xfile);
|
|
end;
|
|
|
|
freemem(icco);
|
|
|
|
result := rc;
|
|
end;
|
|
|
|
function cmsAdaptMatrixToD50(r: LPMAT3; SourceWhitePt: LPcmsCIExyY): longbool;
|
|
var
|
|
Dn: cmsCIEXYZ;
|
|
Bradford: MAT3;
|
|
Tmp: MAT3;
|
|
begin
|
|
|
|
cmsxyY2XYZ(@Dn, SourceWhitePt);
|
|
|
|
cmsAdaptationMatrix(@Bradford, nil, @Dn, cmsD50_XYZ);
|
|
|
|
Tmp := r^;
|
|
MAT3per(r, @Bradford, @Tmp);
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsCreateRGBProfile(WhitePoint: LPcmsCIExyY; Primaries: LPcmsCIExyYTRIPLE; TransferFunction: LPGAMMATABLEArray): cmsHPROFILE;
|
|
var
|
|
hICC: cmsHPROFILE;
|
|
tmp: cmsCIEXYZ;
|
|
MColorants: MAT3;
|
|
Colorants: cmsCIEXYZTRIPLE;
|
|
MaxWhite: cmsCIExyY;
|
|
begin
|
|
|
|
hICC := _cmsCreateProfilePlaceholder;
|
|
if (hICC = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsSetDeviceClass(hICC, icSigDisplayClass);
|
|
cmsSetColorSpace(hICC, icSigRgbData);
|
|
cmsSetPCS(hICC, icSigXYZData);
|
|
cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
|
|
|
|
cmsAddTag(hICC, icSigDeviceMfgDescTag, PAnsiChar('(lcms internal)'));
|
|
cmsAddTag(hICC, icSigProfileDescriptionTag, PAnsiChar('lcms RGB virtual profile'));
|
|
cmsAddTag(hICC, icSigDeviceModelDescTag, PAnsiChar('rgb built-in'));
|
|
|
|
if (WhitePoint <> nil) then
|
|
begin
|
|
|
|
cmsxyY2XYZ(@tmp, WhitePoint);
|
|
cmsAddTag(hICC, icSigMediaWhitePointTag, @tmp);
|
|
end;
|
|
|
|
if (WhitePoint <> nil) and (Primaries <> nil) then
|
|
begin
|
|
|
|
MaxWhite.x := WhitePoint^.x;
|
|
MaxWhite.y_mi := WhitePoint^.y_mi;
|
|
MaxWhite.Y_ma := 1.0;
|
|
|
|
if (cmsBuildRGB2XYZtransferMatrix(@MColorants, @MaxWhite, Primaries) = false) then
|
|
begin
|
|
IEcmsCloseProfile(hICC);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsAdaptMatrixToD50(@MColorants, @MaxWhite);
|
|
|
|
Colorants.Red.X := MColorants.v[0].n[0];
|
|
Colorants.Red.Y := MColorants.v[1].n[0];
|
|
Colorants.Red.Z := MColorants.v[2].n[0];
|
|
|
|
Colorants.Green.X := MColorants.v[0].n[1];
|
|
Colorants.Green.Y := MColorants.v[1].n[1];
|
|
Colorants.Green.Z := MColorants.v[2].n[1];
|
|
|
|
Colorants.Blue.X := MColorants.v[0].n[2];
|
|
Colorants.Blue.Y := MColorants.v[1].n[2];
|
|
Colorants.Blue.Z := MColorants.v[2].n[2];
|
|
|
|
cmsAddTag(hICC, icSigRedColorantTag, @Colorants.Red);
|
|
cmsAddTag(hICC, icSigBlueColorantTag, @Colorants.Blue);
|
|
cmsAddTag(hICC, icSigGreenColorantTag, @Colorants.Green);
|
|
end;
|
|
|
|
if (TransferFunction <> nil) then
|
|
begin
|
|
|
|
cmsAddTag(hICC, icSigRedTRCTag, TransferFunction[0]);
|
|
cmsAddTag(hICC, icSigGreenTRCTag, TransferFunction[1]);
|
|
cmsAddTag(hICC, icSigBlueTRCTag, TransferFunction[2]);
|
|
end;
|
|
|
|
if (Primaries <> nil) then
|
|
begin
|
|
cmsAddTag(hICC, icTagSignature(icSigChromaticityTag), Primaries);
|
|
end;
|
|
|
|
result := hICC;
|
|
end;
|
|
|
|
procedure cmsXYZ2xyY(Dest: LPcmsCIExyY; Source: LPcmsCIEXYZ);
|
|
var
|
|
ISum: double;
|
|
begin
|
|
ISum := 1 / (Source^.X + Source^.Y + Source^.Z);
|
|
|
|
Dest^.x := (Source^.X) * ISum;
|
|
Dest^.y_mi := (Source^.Y) * ISum;
|
|
Dest^.Y_ma := Source^.Y;
|
|
end;
|
|
|
|
const
|
|
D50xyY: cmsCIExyY = (x: 0; y_mi: 0; Y_ma: 0);
|
|
|
|
function cmsD50_xyY: LPcmsCIExyY;
|
|
begin
|
|
|
|
cmsXYZ2xyY(@D50xyY, cmsD50_XYZ);
|
|
|
|
result := @D50xyY;
|
|
end;
|
|
|
|
function Create3x3EmptyLUT: LPLUT;
|
|
var
|
|
AToB0: LPLUT;
|
|
begin
|
|
AToB0 := cmsAllocLUT;
|
|
AToB0^.OutputChan := 3;
|
|
AToB0^.InputChan := AToB0^.OutputChan;
|
|
|
|
result := AToB0;
|
|
end;
|
|
|
|
function cmsCreateLabProfile(WhitePoint: LPcmsCIExyY): cmsHPROFILE;
|
|
var
|
|
hProfile: cmsHPROFILE;
|
|
Lut: LPLUT;
|
|
begin
|
|
|
|
if WhitePoint = nil then
|
|
hProfile := cmsCreateRGBProfile(cmsD50_xyY, nil, nil)
|
|
else
|
|
hProfile := cmsCreateRGBProfile(WhitePoint, nil, nil);
|
|
|
|
cmsSetDeviceClass(hProfile, icSigAbstractClass);
|
|
cmsSetColorSpace(hProfile, icSigLabData);
|
|
cmsSetPCS(hProfile, icSigLabData);
|
|
|
|
cmsAddTag(hProfile, icSigDeviceMfgDescTag, PAnsiChar('(lcms internal)'));
|
|
cmsAddTag(hProfile, icSigProfileDescriptionTag, PAnsiChar('lcms Lab identity'));
|
|
cmsAddTag(hProfile, icSigDeviceModelDescTag, PAnsiChar('Lab built-in'));
|
|
|
|
Lut := Create3x3EmptyLUT;
|
|
if (Lut = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsAddTag(hProfile, icSigAToB0Tag, Lut);
|
|
cmsAddTag(hProfile, icSigBToA0Tag, Lut);
|
|
|
|
cmsFreeLUT(Lut);
|
|
|
|
result := hProfile;
|
|
end;
|
|
|
|
function IEcmsCreateLabProfile(WhitePoint_x, WhitePoint_y, WhitePoint_Y_: double): cmsHPROFILE;
|
|
var
|
|
xyY: cmsCIExyY;
|
|
begin
|
|
xyY.x := WhitePoint_x;
|
|
xyY.y_mi := WhitePoint_y;
|
|
xyY.Y_ma := WhitePoint_Y_;
|
|
result := cmsCreateLabProfile(@xyY);
|
|
end;
|
|
|
|
function IEcmsCreateLabProfileD50: cmsHPROFILE;
|
|
begin
|
|
result := cmsCreateLabProfile(nil);
|
|
end;
|
|
|
|
(*
|
|
function COLORSPACE_SH(s: dword): dword;
|
|
begin
|
|
result := ((s) shl 16);
|
|
end;
|
|
*)
|
|
|
|
(*
|
|
function TYPE_Lab_16: dword;
|
|
begin
|
|
result := (COLORSPACE_SH(PT_Lab) or CHANNELS_SH(3) or BYTES_SH(2))
|
|
end;
|
|
*)
|
|
|
|
function ipow(base: integer; exp: integer): integer;
|
|
var
|
|
res: integer;
|
|
begin
|
|
|
|
res := base;
|
|
dec(exp);
|
|
while (exp <> 0) do
|
|
begin
|
|
res := res * base;
|
|
dec(exp);
|
|
end;
|
|
|
|
result := res;
|
|
end;
|
|
|
|
function ComponentOf(n: integer; clut: integer; nColorant: integer): integer;
|
|
begin
|
|
if (nColorant <= 0) then
|
|
begin
|
|
result := (n mod clut);
|
|
exit;
|
|
end;
|
|
|
|
n := trunc(n / ipow(clut, nColorant) );
|
|
|
|
result := (n mod clut);
|
|
end;
|
|
|
|
function cmsReverseLinearInterpLUT16(Value: WORD; LutTable: pwordarray; p: LPL16PARAMS): word;
|
|
var
|
|
l: integer;
|
|
r: integer;
|
|
x, res: integer;
|
|
NumZeroes, NumPoles: integer;
|
|
cell0, cell1: integer;
|
|
val2: double;
|
|
y0, y1, x0, x1: double;
|
|
a, b: double;
|
|
ia, ib: integer;
|
|
begin
|
|
l := 1;
|
|
r := $10000;
|
|
x := 0;
|
|
|
|
NumZeroes := 0;
|
|
while (LutTable[NumZeroes] = 0) and (NumZeroes < p^.Domain) do
|
|
inc(NumZeroes);
|
|
|
|
NumPoles := 0;
|
|
while (LutTable[p^.Domain - NumPoles] = $FFFF) and (NumPoles < p^.Domain) do
|
|
inc(NumPoles);
|
|
|
|
if (NumZeroes > 1) or (NumPoles > 1) then
|
|
begin
|
|
|
|
if (Value = 0) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
if (Value = $FFFF) then
|
|
begin
|
|
result := $FFFF;
|
|
exit;
|
|
end;
|
|
|
|
ia := trunc(((NumZeroes - 1) * $FFFF) / p^.Domain );
|
|
ib := trunc(((p^.Domain - NumPoles) * $FFFF) / p^.Domain);
|
|
|
|
l := ia - 1;
|
|
r := ib + 1;
|
|
end;
|
|
|
|
while (r > l) do
|
|
begin
|
|
|
|
x := trunc((l + r) / 2 );
|
|
|
|
res := cmsLinearInterpLUT16((x - 1), LutTable, p);
|
|
|
|
if (res = Value) then
|
|
begin
|
|
|
|
result := (x - 1);
|
|
exit;
|
|
end;
|
|
|
|
if (res > Value) then
|
|
r := x - 1
|
|
else
|
|
l := x + 1;
|
|
end;
|
|
|
|
val2 := p^.Domain * ((x - 1) / 65535);
|
|
|
|
cell0 := Floor(val2);
|
|
cell1 := Ceil(val2);
|
|
|
|
if (cell0 = cell1) then
|
|
begin
|
|
result := x;
|
|
exit;
|
|
end;
|
|
|
|
y0 := LutTable[cell0];
|
|
x0 := (65535 * cell0) / p^.Domain;
|
|
|
|
y1 := LutTable[cell1];
|
|
x1 := (65535 * cell1) / p^.Domain;
|
|
|
|
a := (y1 - y0) / (x1 - x0);
|
|
b := y0 - a * x0;
|
|
|
|
if (a = 0) then
|
|
begin
|
|
result := x;
|
|
exit;
|
|
end;
|
|
|
|
result := floor(((Value - b) / a) + 0.5);
|
|
|
|
end;
|
|
|
|
function cmsSample3DGrid(Lut: LPLUT; Sampler: _cmsSAMPLER; Cargo: pointer; dwFlags: DWORD): longbool;
|
|
var
|
|
i, tt, nTotalPoints, Colorant, index: integer;
|
|
xIn, xOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
|
|
with Lut^ do
|
|
begin
|
|
nTotalPoints := ipow(cLutPoints, InputChan);
|
|
|
|
index := 0;
|
|
for i := 0 to nTotalPoints - 1 do
|
|
begin
|
|
|
|
for tt := 0 to InputChan - 1 do
|
|
begin
|
|
|
|
Colorant := ComponentOf(i, cLutPoints, (InputChan - tt - 1));
|
|
|
|
//xIn[tt] := _cmsQuantizeVal(Colorant, Lut^.cLutPoints);
|
|
xIn[tt] := floor(((Colorant * 65535) / (cLutPoints - 1)) + 0.5)
|
|
end;
|
|
|
|
if (dwFlags and SAMPLER_HASTL1) <> 0 then
|
|
begin
|
|
|
|
for tt := 0 to InputChan - 1 do
|
|
xIn[tt] := cmsReverseLinearInterpLUT16(xIn[tt], pwordarray(L1[tt]), @In16params);
|
|
end;
|
|
|
|
if (dwFlags and SAMPLER_INSPECT) <> 0 then
|
|
begin
|
|
|
|
for tt := 0 to OutputChan - 1 do
|
|
xOut[tt] := pwordarray(T)[index + tt];
|
|
end;
|
|
|
|
if (Sampler(@xIn, @xOut, Cargo)) = 0 then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (dwFlags and SAMPLER_INSPECT) = 0 then
|
|
begin
|
|
|
|
if (dwFlags and SAMPLER_HASTL2) <> 0 then
|
|
begin
|
|
|
|
for tt := 0 to OutputChan - 1 do
|
|
xOut[tt] := cmsReverseLinearInterpLUT16(xOut[tt],
|
|
pwordarray(L2[tt]),
|
|
@Out16params);
|
|
end;
|
|
|
|
for tt := 0 to OutputChan - 1 do
|
|
pwordarray(T)[index + tt] := xOut[tt];
|
|
|
|
end;
|
|
|
|
inc(index, OutputChan);
|
|
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
(*
|
|
nTotalPoints := ipow(Lut^.cLutPoints, Lut^.InputChan);
|
|
|
|
index := 0;
|
|
for i := 0 to nTotalPoints - 1 do
|
|
begin
|
|
|
|
for t := 0 to Lut^.InputChan - 1 do
|
|
begin
|
|
|
|
Colorant := ComponentOf(i, Lut^.cLutPoints, (Lut^.InputChan - t - 1));
|
|
|
|
//xIn[t] := _cmsQuantizeVal(Colorant, Lut^.cLutPoints);
|
|
xIn[t] := floor(((Colorant * 65535) / (Lut^.cLutPoints - 1)) + 0.5)
|
|
end;
|
|
|
|
if (dwFlags and SAMPLER_HASTL1) <> 0 then
|
|
begin
|
|
|
|
for t := 0 to Lut^.InputChan - 1 do
|
|
xIn[t] := cmsReverseLinearInterpLUT16(xIn[t], pwordarray(Lut^.L1[t]), @Lut^.In16params);
|
|
end;
|
|
|
|
if (dwFlags and SAMPLER_INSPECT) <> 0 then
|
|
begin
|
|
|
|
for t := 0 to Lut^.OutputChan - 1 do
|
|
xOut[t] := pwordarray(Lut^.T)[index + t];
|
|
end;
|
|
|
|
if (Sampler(@xIn, @xOut, Cargo)) = 0 then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (dwFlags and SAMPLER_INSPECT) = 0 then
|
|
begin
|
|
|
|
if (dwFlags and SAMPLER_HASTL2) <> 0 then
|
|
begin
|
|
|
|
for t := 0 to Lut^.OutputChan - 1 do
|
|
xOut[t] := cmsReverseLinearInterpLUT16(xOut[t],
|
|
pwordarray(Lut^.L2[t]),
|
|
@Lut^.Out16params);
|
|
end;
|
|
|
|
for t := 0 to Lut^.OutputChan - 1 do
|
|
pwordarray(Lut^.T)[index + t] := xOut[t];
|
|
|
|
end;
|
|
|
|
inc(index, Lut^.OutputChan);
|
|
|
|
end;
|
|
|
|
result := TRUE;
|
|
*)
|
|
end;
|
|
|
|
procedure IEcmsDoTransform(Transform: cmsHTRANSFORM; InputBuffer: pointer; OutputBuffer: pointer; Size: dword);
|
|
var
|
|
p: _LPcmsTRANSFORM;
|
|
begin
|
|
p := _LPcmsTRANSFORM(Transform);
|
|
|
|
p^.StrideOut := Size;
|
|
p^.StrideIn := p^.StrideOut;
|
|
|
|
p^.xform(p, InputBuffer, OutputBuffer, Size);
|
|
|
|
end;
|
|
|
|
function SoftProofSampler(xIn: pwordarray; xOut: pwordarray; Cargo: pointer): integer;
|
|
var
|
|
t: LPGAMUTCHAIN;
|
|
Colorant: array[0..MAXCHANNELS - 1] of WORD;
|
|
begin
|
|
t := LPGAMUTCHAIN(Cargo);
|
|
|
|
IEcmsDoTransform(t^.hForward, xIn, @Colorant, 1);
|
|
|
|
IEcmsDoTransform(t^.hReverse, @Colorant, xOut, 1);
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
function _cmsComputeSoftProofLUT(hProfile: cmsHPROFILE; nIntent: integer): LPLUT;
|
|
var
|
|
hLab: cmsHPROFILE;
|
|
SoftProof: LPLUT;
|
|
dwFormat: DWORD;
|
|
Chain: GAMUTCHAIN;
|
|
//nErrState: integer ;
|
|
begin
|
|
|
|
ZeroMemory(@Chain, sizeof(GAMUTCHAIN));
|
|
|
|
hLab := cmsCreateLabProfile(nil);
|
|
|
|
dwFormat := ((4 shl 3) or 2);
|
|
|
|
//nErrState := cmsErrorAction(LCMS_ERROR_IGNORE);
|
|
|
|
Chain.hForward := IEcmsCreateTransform(hLab, TYPE_Lab_16,
|
|
hProfile, dwFormat,
|
|
nIntent,
|
|
cmsFLAGS_NOTPRECALC);
|
|
|
|
Chain.hReverse := IEcmsCreateTransform(hProfile, dwFormat,
|
|
hLab, TYPE_Lab_16,
|
|
INTENT_RELATIVE_COLORIMETRIC,
|
|
cmsFLAGS_NOTPRECALC);
|
|
|
|
//cmsErrorAction(nErrState);
|
|
|
|
if (Chain.hForward <> nil) and (Chain.hReverse <> nil) then
|
|
begin
|
|
|
|
SoftProof := cmsAllocLUT;
|
|
SoftProof := cmsAlloc3DGrid(SoftProof, 33, 3, 3);
|
|
|
|
cmsSample3DGrid(SoftProof, SoftProofSampler, @Chain, 0);
|
|
end
|
|
else
|
|
SoftProof := nil;
|
|
|
|
if (Chain.hForward <> nil) then
|
|
IEcmsDeleteTransform(Chain.hForward);
|
|
if (Chain.hReverse <> nil) then
|
|
IEcmsDeleteTransform(Chain.hReverse);
|
|
|
|
IEcmsCloseProfile(hLab);
|
|
|
|
result := SoftProof;
|
|
end;
|
|
|
|
function cmsDeltaE(Lab1: LPcmsCIELab; Lab2: LPcmsCIELab): double;
|
|
var
|
|
dL, da, db: double;
|
|
begin
|
|
|
|
if (Lab1^.L < 0) or (Lab2^.L < 0) then
|
|
begin
|
|
result := 65536;
|
|
exit;
|
|
end;
|
|
|
|
if (Lab1^.a < -200) or (Lab1^.a > 200) then
|
|
begin
|
|
result := 65536;
|
|
exit;
|
|
end;
|
|
if (Lab1^.b < -200) or (Lab1^.b > 200) then
|
|
begin
|
|
result := 65536.;
|
|
exit;
|
|
end;
|
|
|
|
if (Lab2^.a < -200) or (Lab2^.a > 200) then
|
|
begin
|
|
result := 65536;
|
|
exit;
|
|
end;
|
|
if (Lab2^.b < -200) or (Lab2^.b > 200) then
|
|
begin
|
|
result := 65536;
|
|
exit;
|
|
end;
|
|
|
|
if (Lab1^.L = 0) and (Lab2^.L = 0) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
dL := abs(Lab1^.L - Lab2^.L);
|
|
da := abs(Lab1^.a - Lab2^.a);
|
|
db := abs(Lab1^.b - Lab2^.b);
|
|
|
|
result := Power(dL * dL + da * da + db * db, 0.5);
|
|
|
|
end;
|
|
|
|
function GamutSampler(xIn: pwordarray; xOut: pwordarray; Cargo: pointer): integer;
|
|
var
|
|
t: LPGAMUTCHAIN;
|
|
Proof, Check: array[0..MAXCHANNELS - 1] of WORD;
|
|
Proof2, Check2: array[0..MAXCHANNELS - 1] of WORD;
|
|
LabIn1, LabOut1: cmsCIELab;
|
|
LabIn2, LabOut2: cmsCIELab;
|
|
dE1, dE2, ErrorRatio: double;
|
|
begin
|
|
t := LPGAMUTCHAIN(Cargo);
|
|
|
|
//ErrorRatio := 1.0;
|
|
|
|
IEcmsDoTransform(t^.hForward, xIn, @Proof, 1);
|
|
|
|
IEcmsDoTransform(t^.hReverse, @Proof, @Check, 1);
|
|
|
|
IEcmsDoTransform(t^.hForward, @Check, @Proof2, 1);
|
|
IEcmsDoTransform(t^.hReverse, @Proof2, @Check2, 1);
|
|
|
|
if (Check[0] = $FFFF) and (Check[1] = $FFFF) and (Check[2] = $FFFF) then
|
|
|
|
xOut[0] := $F000
|
|
else
|
|
begin
|
|
|
|
cmsLabEncoded2Float(@LabIn1, xIn);
|
|
cmsLabEncoded2Float(@LabOut1, @Check);
|
|
|
|
dE1 := cmsDeltaE(@LabIn1, @LabOut1);
|
|
|
|
cmsLabEncoded2Float(@LabIn2, @Check);
|
|
cmsLabEncoded2Float(@LabOut2, @Check2);
|
|
|
|
dE2 := cmsDeltaE(@LabIn2, @LabOut2);
|
|
|
|
if (dE1 < ERR_THERESHOLD) and (dE2 < ERR_THERESHOLD) then
|
|
xOut[0] := 0
|
|
else
|
|
begin
|
|
if (dE1 < ERR_THERESHOLD) and (dE2 > ERR_THERESHOLD) then
|
|
xOut[0] := 0
|
|
else
|
|
begin
|
|
if (dE1 > ERR_THERESHOLD) and (dE2 < ERR_THERESHOLD) then
|
|
xOut[0] := floor((dE1 - ERR_THERESHOLD) + 0.5)
|
|
else
|
|
begin
|
|
|
|
if (dE2 = 0.0) then
|
|
ErrorRatio := dE1
|
|
else
|
|
ErrorRatio := dE1 / dE2;
|
|
|
|
if (ErrorRatio > ERR_THERESHOLD) then
|
|
xOut[0] := floor((ErrorRatio - ERR_THERESHOLD) + 0.5)
|
|
else
|
|
xOut[0] := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
function _cmsComputeGamutLUT(hProfile: cmsHPROFILE; Intent: integer): LPLUT;
|
|
var
|
|
hLab: cmsHPROFILE;
|
|
Gamut: LPLUT;
|
|
dwFormat: DWORD;
|
|
Chain: GAMUTCHAIN;
|
|
//nErrState: integer ;
|
|
begin
|
|
|
|
ZeroMemory(@Chain, sizeof(GAMUTCHAIN));
|
|
|
|
hLab := cmsCreateLabProfile(nil);
|
|
|
|
dwFormat := ((4 shl 3) or 2);
|
|
|
|
//nErrState := cmsErrorAction(LCMS_ERROR_IGNORE);
|
|
|
|
Chain.hForward := IEcmsCreateTransform(hLab, TYPE_Lab_16,
|
|
hProfile, dwFormat,
|
|
Intent,
|
|
cmsFLAGS_NOTPRECALC);
|
|
|
|
Chain.hReverse := IEcmsCreateTransform(hProfile, dwFormat,
|
|
hLab, TYPE_Lab_16,
|
|
Intent,
|
|
cmsFLAGS_NOTPRECALC);
|
|
|
|
//cmsErrorAction(nErrState);
|
|
|
|
if (Chain.hForward <> nil) and (Chain.hReverse <> nil) then
|
|
begin
|
|
|
|
Gamut := cmsAllocLUT;
|
|
Gamut := cmsAlloc3DGrid(Gamut, 42, 3, 1);
|
|
|
|
cmsSample3DGrid(Gamut, GamutSampler, @Chain, 0);
|
|
|
|
end
|
|
else
|
|
Gamut := nil;
|
|
|
|
if (Chain.hForward <> nil) then
|
|
IEcmsDeleteTransform(Chain.hForward);
|
|
if (Chain.hReverse <> nil) then
|
|
IEcmsDeleteTransform(Chain.hReverse);
|
|
|
|
IEcmsCloseProfile(hLab);
|
|
|
|
result := Gamut;
|
|
end;
|
|
|
|
procedure CreateProof(p: _LPcmsTRANSFORM; dwFlags: DWORD; ToTagPtr: PicTagSignature);
|
|
var
|
|
ProofTag: icTagSignature;
|
|
begin
|
|
|
|
if (dwFlags and cmsFLAGS_SOFTPROOFING) <> 0 then
|
|
begin
|
|
|
|
p^.Preview := _cmsComputeSoftProofLUT(p^.PreviewProfile, p^.Intent);
|
|
p^.Phase2 := LabRel;
|
|
|
|
ToTagPtr^ := PCS2Device[p^.ProofIntent];
|
|
|
|
if (p^.Preview = nil) then
|
|
begin
|
|
|
|
ProofTag := Preview[p^.Intent];
|
|
|
|
if (not cmsIsTag(p^.PreviewProfile, ProofTag)) then
|
|
begin
|
|
|
|
ProofTag := Preview[0];
|
|
if (not cmsIsTag(p^.PreviewProfile, ProofTag)) then
|
|
ProofTag := icTagSignature(0);
|
|
end;
|
|
|
|
if integer(ProofTag) <> 0 then
|
|
begin
|
|
|
|
p^.Preview := cmsReadICCLut(p^.PreviewProfile, ProofTag);
|
|
p^.Phase2 := GetPhase(p^.PreviewProfile);
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
p^.Preview := nil;
|
|
p^.PreviewProfile := nil;
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not previewing capabilities");
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
if (dwFlags and cmsFLAGS_GAMUTCHECK) <> 0 then
|
|
begin
|
|
|
|
p^.Gamut := _cmsComputeGamutLUT(p^.PreviewProfile, p^.Intent);
|
|
if (p^.Gamut = nil) then
|
|
begin
|
|
|
|
if (cmsIsTag(p^.PreviewProfile, icSigGamutTag)) then
|
|
begin
|
|
|
|
p^.Gamut := cmsReadICCLut(p^.PreviewProfile, icSigGamutTag);
|
|
end
|
|
else
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not gamut checking capabilities");
|
|
p^.Gamut := nil;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure NC2toPCS(p: _LPcmsTRANSFORM; xIn: pwordarray; xOut: pwordarray);
|
|
var
|
|
index: integer;
|
|
begin
|
|
index := xIn[0];
|
|
|
|
CopyMemory(xOut, @(p^.NamedColorList^.List[index].PCS), 3 * sizeof(WORD));
|
|
end;
|
|
|
|
function cmsLinearInterpFixed(Value1: WORD; LutTable: pwordarray; p: LPL16PARAMS): Fixed32;
|
|
var
|
|
y1, y0: Fixed32;
|
|
cell0: integer;
|
|
val3, Value: integer;
|
|
begin
|
|
|
|
Value := Value1;
|
|
if (Value = $FFFF) then
|
|
begin
|
|
result := LutTable[p^.Domain];
|
|
exit;
|
|
end;
|
|
|
|
val3 := p^.Domain * Value;
|
|
val3 := ToFixedDomain(val3);
|
|
|
|
cell0 := FIXED_TO_INT(val3);
|
|
|
|
y0 := LutTable[cell0];
|
|
y1 := LutTable[cell0 + 1];
|
|
|
|
result := y0 + FixedMul((y1 - y0), (val3 and $FFFF));
|
|
end;
|
|
|
|
function Clamp_XYZ(xin: integer): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
if (xin < 0) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
if (xin > $FFFF) then
|
|
begin
|
|
result := $FFFF;
|
|
exit;
|
|
end;
|
|
result := xin;
|
|
end;
|
|
|
|
procedure AllSmeltedBehaviour(MatShaper: LPMATSHAPER; xIn: pwordarray; xOut: pwordarray);
|
|
var
|
|
tmp: array[0..2] of WORD;
|
|
InVect, OutVect: WVEC3;
|
|
begin
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASINPSHAPER) <> 0 then
|
|
begin
|
|
InVect.n[VX] := cmsLinearInterpFixed(xIn[0], pwordarray(MatShaper^.L2[0]), @MatShaper^.p2_16);
|
|
InVect.n[VY] := cmsLinearInterpFixed(xIn[1], pwordarray(MatShaper^.L2[1]), @MatShaper^.p2_16);
|
|
InVect.n[VZ] := cmsLinearInterpFixed(xIn[2], pwordarray(MatShaper^.L2[2]), @MatShaper^.p2_16);
|
|
end
|
|
else
|
|
begin
|
|
InVect.n[VX] := ToFixedDomain(xIn[0]);
|
|
InVect.n[VY] := ToFixedDomain(xIn[1]);
|
|
InVect.n[VZ] := ToFixedDomain(xIn[2]);
|
|
end;
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASMATRIX) <> 0 then
|
|
begin
|
|
|
|
MAT3evalW(@OutVect, @MatShaper^.Matrix, @InVect);
|
|
end
|
|
else
|
|
begin
|
|
|
|
OutVect.n[VX] := InVect.n[VX];
|
|
OutVect.n[VY] := InVect.n[VY];
|
|
OutVect.n[VZ] := InVect.n[VZ];
|
|
end;
|
|
|
|
tmp[0] := Clamp_XYZ(FromFixedDomain(OutVect.n[VX]));
|
|
tmp[1] := Clamp_XYZ(FromFixedDomain(OutVect.n[VY]));
|
|
tmp[2] := Clamp_XYZ(FromFixedDomain(OutVect.n[VZ]));
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASSHAPER) <> 0 then
|
|
begin
|
|
xOut[0] := cmsLinearInterpLUT16(tmp[0], pwordarray(MatShaper^.L[0]), @MatShaper^.p16);
|
|
xOut[1] := cmsLinearInterpLUT16(tmp[1], pwordarray(MatShaper^.L[1]), @MatShaper^.p16);
|
|
xOut[2] := cmsLinearInterpLUT16(tmp[2], pwordarray(MatShaper^.L[2]), @MatShaper^.p16);
|
|
end
|
|
else
|
|
begin
|
|
xOut[0] := tmp[0];
|
|
xOut[1] := tmp[1];
|
|
xOut[2] := tmp[2];
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure InputBehaviour(MatShaper: LPMATSHAPER; xIn: pwordarray; xOut: pwordarray);
|
|
var
|
|
InVect, OutVect: WVEC3;
|
|
begin
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASSHAPER) <> 0 then
|
|
begin
|
|
InVect.n[VX] := cmsLinearInterpFixed(xIn[0], pwordarray(MatShaper^.L[0]), @MatShaper^.p16);
|
|
InVect.n[VY] := cmsLinearInterpFixed(xIn[1], pwordarray(MatShaper^.L[1]), @MatShaper^.p16);
|
|
InVect.n[VZ] := cmsLinearInterpFixed(xIn[2], pwordarray(MatShaper^.L[2]), @MatShaper^.p16);
|
|
end
|
|
else
|
|
begin
|
|
InVect.n[VX] := ToFixedDomain(xIn[0]);
|
|
InVect.n[VY] := ToFixedDomain(xIn[1]);
|
|
InVect.n[VZ] := ToFixedDomain(xIn[2]);
|
|
end;
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASMATRIX) <> 0 then
|
|
begin
|
|
MAT3evalW(@OutVect, @MatShaper^.Matrix, @InVect);
|
|
end
|
|
else
|
|
begin
|
|
OutVect := InVect;
|
|
end;
|
|
|
|
xOut[0] := Clamp_XYZ((OutVect.n[VX]) shr 1);
|
|
xOut[1] := Clamp_XYZ((OutVect.n[VY]) shr 1);
|
|
xOut[2] := Clamp_XYZ((OutVect.n[VZ]) shr 1);
|
|
|
|
end;
|
|
|
|
procedure OutputBehaviour(MatShaper: LPMATSHAPER; xIn: pwordarray; xOut: pwordarray);
|
|
var
|
|
InVect, OutVect: WVEC3;
|
|
i: integer;
|
|
begin
|
|
|
|
InVect.n[VX] := Fixed32(xIn[0] shl 1);
|
|
InVect.n[VY] := Fixed32(xIn[1] shl 1);
|
|
InVect.n[VZ] := Fixed32(xIn[2] shl 1);
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASMATRIX) <> 0 then
|
|
begin
|
|
|
|
MAT3evalW(@OutVect, @MatShaper^.Matrix, @InVect);
|
|
end
|
|
else
|
|
begin
|
|
OutVect := InVect;
|
|
end;
|
|
|
|
if (MatShaper^.dwFlags and MATSHAPER_HASSHAPER) <> 0 then
|
|
begin
|
|
|
|
for i := 0 to 2 do
|
|
begin
|
|
|
|
xOut[i] := cmsLinearInterpLUT16(
|
|
Clamp_RGB(FromFixedDomain(OutVect.n[i])),
|
|
pwordarray(MatShaper^.L[i]),
|
|
@MatShaper^.p16);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
|
|
xOut[0] := Clamp_RGB(FromFixedDomain(OutVect.n[VX]));
|
|
xOut[1] := Clamp_RGB(FromFixedDomain(OutVect.n[VY]));
|
|
xOut[2] := Clamp_RGB(FromFixedDomain(OutVect.n[VZ]));
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure cmsEvalMatShaper(MatShaper: LPMATSHAPER; xIn: pwordarray; xOut: pwordarray);
|
|
begin
|
|
|
|
if ((MatShaper^.dwFlags and MATSHAPER_ALLSMELTED) = MATSHAPER_ALLSMELTED) then
|
|
begin
|
|
AllSmeltedBehaviour(MatShaper, xIn, xOut);
|
|
exit;
|
|
end;
|
|
if (MatShaper^.dwFlags and MATSHAPER_INPUT) <> 0 then
|
|
begin
|
|
InputBehaviour(MatShaper, xIn, xOut);
|
|
exit;
|
|
end;
|
|
|
|
OutputBehaviour(MatShaper, xIn, xOut);
|
|
end;
|
|
|
|
procedure MatrixShaperXFORM(p: _LPcmsTRANSFORM; xin: pointer; xout: pointer; Size: dword);
|
|
var
|
|
accum: PBYTE;
|
|
output: PBYTE;
|
|
wIn, wOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
i, n: dword;
|
|
begin
|
|
accum := xin;
|
|
output := xout;
|
|
n := Size;
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
accum := p^.FromInput(p, @wIn, accum);
|
|
cmsEvalMatShaper(p^.SmeltMatShaper, @wIn, @wOut);
|
|
output := p^.ToOutput(p, @wOut, output);
|
|
end;
|
|
end;
|
|
|
|
function cmsTakeColorants(Dest: LPcmsCIEXYZTRIPLE; hProfile: cmsHPROFILE): longbool;
|
|
begin
|
|
if (ReadICCXYZ(hProfile, icSigRedColorantTag, @Dest^.Red, TRUE) < 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (ReadICCXYZ(hProfile, icSigGreenColorantTag, @Dest^.Green, TRUE) < 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (ReadICCXYZ(hProfile, icSigBlueColorantTag, @Dest^.Blue, TRUE) < 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsReadICCMatrixRGB2XYZ(r: LPMAT3; hProfile: cmsHPROFILE): longbool;
|
|
var
|
|
Primaries: cmsCIEXYZTRIPLE;
|
|
begin
|
|
|
|
if (not cmsTakeColorants(@Primaries, hProfile)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
VEC3init(@r^.v[0], Primaries.Red.X, Primaries.Green.X, Primaries.Blue.X);
|
|
VEC3init(@r^.v[1], Primaries.Red.Y, Primaries.Green.Y, Primaries.Blue.Y);
|
|
VEC3init(@r^.v[2], Primaries.Red.Z, Primaries.Green.Z, Primaries.Blue.Z);
|
|
|
|
result := TRUE;
|
|
|
|
end;
|
|
|
|
function cmsDupGamma(xIn: LPGAMMATABLE): LPGAMMATABLE;
|
|
var
|
|
Ptr: LPGAMMATABLE;
|
|
begin
|
|
|
|
Ptr := cmsAllocGamma(xIn^.nEntries);
|
|
if (Ptr = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
CopyMemory(@Ptr^.GammaTable,
|
|
@xIn^.GammaTable,
|
|
xIn^.nEntries * sizeof(WORD));
|
|
|
|
result := Ptr;
|
|
end;
|
|
|
|
function cmsReadICCGamma(hProfile: cmsHPROFILE; sig: icTagSignature): LPGAMMATABLE;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
offset: integer;
|
|
n: integer;
|
|
begin
|
|
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
n := SearchTag(Icc, sig);
|
|
if (n < 0) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Tag not found");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (Icc^.stream = nil) then
|
|
begin
|
|
|
|
result := cmsDupGamma(LPGAMMATABLE(Icc^.TagPtrs[n]));
|
|
exit;
|
|
end;
|
|
|
|
offset := Icc^.TagOffsets[n];
|
|
|
|
if (Icc^.Seek(Icc^.stream, offset)) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
result := ReadCurve(Icc);
|
|
|
|
end;
|
|
|
|
function cmsReverseGamma(nResultSamples: integer; InGamma: LPGAMMATABLE): LPGAMMATABLE;
|
|
var
|
|
i: integer;
|
|
L16In: L16PARAMS;
|
|
InPtr: PWORD;
|
|
p: LPGAMMATABLE;
|
|
wValIn, wValOut: WORD;
|
|
begin
|
|
|
|
p := cmsAllocGamma(nResultSamples);
|
|
if (p = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsCalcL16Params(InGamma^.nEntries, @L16In);
|
|
InPtr := @InGamma^.GammaTable;
|
|
|
|
for i := 0 to nResultSamples - 1 do
|
|
begin
|
|
|
|
//wValIn := _cmsQuantizeVal(i, nResultSamples);
|
|
wValIn := floor(((i * 65535) / (nResultSamples - 1)) + 0.5);
|
|
|
|
wValOut := cmsReverseLinearInterpLUT16(wValIn, pwordarray(InPtr), @L16In);
|
|
p^.GammaTable[i] := wValOut;
|
|
end;
|
|
|
|
result := p;
|
|
end;
|
|
|
|
function ReadCurveReversed(Icc: LPLCMSICCPROFILE): LPGAMMATABLE;
|
|
const
|
|
ParamsByType: array[0..4] of integer = (1, 3, 4, 5, 7);
|
|
var
|
|
Base: icTagBase;
|
|
NewGamma, ReturnGamma: LPGAMMATABLE;
|
|
Count: icUInt32Number;
|
|
n: integer;
|
|
SingleGammaFixed: WORD;
|
|
Params: array[0..9] of double;
|
|
Num: icS15Fixed16Number;
|
|
Reserved: icUInt32Number;
|
|
xType: icUInt16Number;
|
|
i: integer;
|
|
begin
|
|
|
|
Icc^.Read(@Base, 1, sizeof(icTagBase), Icc^.stream);
|
|
AdjustEndianess32(@Base.sig);
|
|
|
|
case (Base.sig) of
|
|
|
|
icTagTypeSignature($9478EE00),
|
|
icSigCurveType:
|
|
begin
|
|
|
|
Icc^.Read(@Count, sizeof(icUInt32Number), 1, Icc^.stream);
|
|
AdjustEndianess32(@Count);
|
|
|
|
case (Count) of
|
|
|
|
0:
|
|
begin
|
|
NewGamma := cmsAllocGamma(2);
|
|
if (NewGamma = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
NewGamma^.GammaTable[0] := 0;
|
|
pwordarray(@NewGamma^.GammaTable)[1] := $FFFF;
|
|
result := NewGamma;
|
|
end;
|
|
|
|
1:
|
|
begin
|
|
Icc^.Read(@SingleGammaFixed, sizeof(WORD), 1, Icc^.stream);
|
|
AdjustEndianess16(@SingleGammaFixed);
|
|
result := cmsBuildGamma(4096, 1 / Convert8Fixed8(SingleGammaFixed));
|
|
end;
|
|
|
|
else
|
|
begin
|
|
|
|
NewGamma := cmsAllocGamma(Count);
|
|
if (NewGamma = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Icc^.Read(@NewGamma^.GammaTable, sizeof(WORD), Count, Icc^.stream);
|
|
|
|
AdjustEndianessArray16(@NewGamma^.GammaTable, Count);
|
|
|
|
ReturnGamma := cmsReverseGamma(Count, NewGamma);
|
|
cmsFreeGamma(NewGamma);
|
|
|
|
result := ReturnGamma;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
icTagTypeSignature(icSigParametricCurveType):
|
|
begin
|
|
|
|
Icc^.Read(@xType, sizeof(icUInt16Number), 1, Icc^.stream);
|
|
Icc^.Read(@Reserved, sizeof(icUInt16Number), 1, Icc^.stream);
|
|
|
|
AdjustEndianess16(@xType);
|
|
if (xType > 5) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
ZeroMemory(@Params, 10 * sizeof(double));
|
|
n := ParamsByType[xType];
|
|
|
|
for i := 0 to n - 1 do
|
|
begin
|
|
Icc^.Read(@Num, sizeof(icS15Fixed16Number), 1, Icc^.stream);
|
|
Params[i] := Convert15Fixed16(Num);
|
|
end;
|
|
|
|
NewGamma := cmsBuildParametricGamma(4096, -(xType + 1), @Params);
|
|
result := NewGamma;
|
|
end;
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", Base.sig);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
function cmsReadICCGammaReversed(hProfile: cmsHPROFILE; sig: icTagSignature): LPGAMMATABLE;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
offset: integer;
|
|
n: integer;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
n := SearchTag(Icc, sig);
|
|
if (n < 0) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Tag not found");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (Icc^.stream = nil) then
|
|
begin
|
|
|
|
result := cmsReverseGamma(256, LPGAMMATABLE(Icc^.TagPtrs[n]));
|
|
exit;
|
|
end;
|
|
|
|
offset := Icc^.TagOffsets[n];
|
|
|
|
if (Icc^.Seek(Icc^.stream, offset)) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
result := ReadCurveReversed(Icc);
|
|
end;
|
|
|
|
function ComputeTables(Table: LPGAMMATABLEarray; xOut: PPWORDARRAY; p16: LPL16PARAMS): integer;
|
|
var
|
|
i, AllLinear: integer;
|
|
PtrW: PWORD;
|
|
begin
|
|
|
|
cmsCalcL16Params(Table[0]^.nEntries, p16);
|
|
|
|
AllLinear := 0;
|
|
for i := 0 to 2 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * p16^.nSamples);
|
|
|
|
if (PtrW = nil) then
|
|
begin
|
|
result := -1;
|
|
exit;
|
|
end;
|
|
|
|
CopyMemory(PtrW, @Table[i]^.GammaTable, sizeof(WORD) * Table[i]^.nEntries);
|
|
|
|
xOut[i] := PtrW;
|
|
|
|
inc(AllLinear, cmsIsLinear(pwordarray(PtrW), p16^.nSamples));
|
|
end;
|
|
|
|
if (AllLinear <> 3) then
|
|
result := 1
|
|
else
|
|
result := 0;
|
|
|
|
end;
|
|
|
|
function cmsAllocMatShaper2(Matrix: LPMAT3; xIn: LPGAMMATABLEArray; xOut: LPGAMMATABLEArray; Behaviour: DWORD): LPMATSHAPER;
|
|
var
|
|
NewMatShaper: LPMATSHAPER;
|
|
rc: integer;
|
|
begin
|
|
|
|
getmem(NewMatShaper, sizeof(MATSHAPER));
|
|
if (NewMatShaper <> nil) then
|
|
ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
|
|
|
|
NewMatShaper^.dwFlags := Behaviour and (MATSHAPER_ALLSMELTED);
|
|
|
|
MAT3toFix(@NewMatShaper^.Matrix, Matrix);
|
|
|
|
if (not MAT3isIdentity(@NewMatShaper^.Matrix, 0.00001)) then
|
|
NewMatShaper^.dwFlags := NewMatShaper^.dwFlags or MATSHAPER_HASMATRIX;
|
|
|
|
if (xOut <> nil) then
|
|
begin
|
|
|
|
rc := ComputeTables(xOut, @NewMatShaper^.L, @NewMatShaper^.p16);
|
|
if (rc < 0) then
|
|
begin
|
|
cmsFreeMatShaper(NewMatShaper);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
if (rc = 1) then
|
|
NewMatShaper^.dwFlags := NewMatShaper^.dwFlags or MATSHAPER_HASSHAPER;
|
|
end;
|
|
|
|
if (xIn <> nil) then
|
|
begin
|
|
|
|
rc := ComputeTables(xIn, @NewMatShaper^.L2, @NewMatShaper^.p2_16);
|
|
if (rc < 0) then
|
|
begin
|
|
cmsFreeMatShaper(NewMatShaper);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
if (rc = 1) then
|
|
NewMatShaper^.dwFlags := NewMatShaper^.dwFlags or MATSHAPER_HASINPSHAPER;
|
|
end;
|
|
|
|
result := NewMatShaper;
|
|
|
|
end;
|
|
|
|
procedure cmsFreeGammaTriple(Gamma: LPGAMMATABLEarray);
|
|
begin
|
|
cmsFreeGamma(Gamma[0]);
|
|
cmsFreeGamma(Gamma[1]);
|
|
cmsFreeGamma(Gamma[2]);
|
|
Gamma[2] := nil;
|
|
Gamma[1] := Gamma[2];
|
|
Gamma[0] := Gamma[1];
|
|
end;
|
|
|
|
function cmsBuildSmeltMatShaper(p: _LPcmsTRANSFORM): longbool;
|
|
var
|
|
From, xTo, ToInv, Transfer: MAT3;
|
|
xIn, InverseOut: array[0..2] of LPGAMMATABLE;
|
|
begin
|
|
|
|
if (not cmsReadICCMatrixRGB2XYZ(@From, p^.InputProfile)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (not cmsReadICCMatrixRGB2XYZ(@xTo, p^.OutputProfile)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
if (MAT3inverse(@xTo, @ToInv) < 0) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
MAT3per(@Transfer, @ToInv, @From);
|
|
|
|
xIn[0] := cmsReadICCGamma(p^.InputProfile, icSigRedTRCTag);
|
|
xIn[1] := cmsReadICCGamma(p^.InputProfile, icSigGreenTRCTag);
|
|
xIn[2] := cmsReadICCGamma(p^.InputProfile, icSigBlueTRCTag);
|
|
|
|
if (xIn[0] = nil) or (xIn[1] = nil) or (xIn[2] = nil) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
InverseOut[0] := cmsReadICCGammaReversed(p^.OutputProfile, icSigRedTRCTag);
|
|
InverseOut[1] := cmsReadICCGammaReversed(p^.OutputProfile, icSigGreenTRCTag);
|
|
InverseOut[2] := cmsReadICCGammaReversed(p^.OutputProfile, icSigBlueTRCTag);
|
|
|
|
p^.SmeltMatShaper := cmsAllocMatShaper2(@Transfer, @xIn, @InverseOut, MATSHAPER_ALLSMELTED);
|
|
|
|
cmsFreeGammaTriple(@xIn);
|
|
|
|
cmsFreeGammaTriple(@InverseOut);
|
|
|
|
result := (p^.SmeltMatShaper <> nil);
|
|
end;
|
|
|
|
procedure LUTtoPCS(p: _LPcmsTRANSFORM; xIn: pwordarray; xOut: pwordarray);
|
|
begin
|
|
cmsEvalLUT(p^.Device2PCS, xIn, xOut);
|
|
end;
|
|
|
|
procedure ShaperMatrixToPCS(p: _LPcmsTRANSFORM; xIn: pwordarray; xOut: pwordarray); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
cmsEvalMatShaper(p^.InMatShaper, xIn, xOut);
|
|
end;
|
|
|
|
function cmsTakeIluminant(Dest: LPcmsCIEXYZ; hProfile: cmsHPROFILE): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
Dest^ := Icc^.Illuminant;
|
|
|
|
result := true;
|
|
end;
|
|
|
|
function cmsAllocMatShaper(Matrix: LPMAT3; Tables: LPGAMMATABLEarray; Behaviour: DWORD): LPMATSHAPER;
|
|
var
|
|
NewMatShaper: LPMATSHAPER;
|
|
i, AllLinear: integer;
|
|
PtrW: PWORD;
|
|
|
|
begin
|
|
|
|
getmem(NewMatShaper, sizeof(MATSHAPER));
|
|
if (NewMatShaper <> nil) then
|
|
ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
|
|
|
|
NewMatShaper^.dwFlags := Behaviour and (MATSHAPER_ALLSMELTED);
|
|
|
|
MAT3toFix(@NewMatShaper^.Matrix, Matrix);
|
|
|
|
if (not MAT3isIdentity(@NewMatShaper^.Matrix, 0.00001)) then
|
|
NewMatShaper^.dwFlags := NewMatShaper^.dwFlags or MATSHAPER_HASMATRIX;
|
|
|
|
cmsCalcL16Params(Tables[0]^.nEntries, @NewMatShaper^.p16);
|
|
|
|
AllLinear := 0;
|
|
for i := 0 to 2 do
|
|
begin
|
|
|
|
getmem(PtrW, sizeof(WORD) * NewMatShaper^.p16.nSamples);
|
|
|
|
if (PtrW = nil) then
|
|
begin
|
|
cmsFreeMatShaper(NewMatShaper);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
CopyMemory(PtrW, @Tables[i]^.GammaTable,
|
|
sizeof(WORD) * Tables[i]^.nEntries);
|
|
|
|
NewMatShaper^.L[i] := PtrW;
|
|
|
|
inc(AllLinear, cmsIsLinear(pwordarray(PtrW), NewMatShaper^.p16.nSamples));
|
|
end;
|
|
|
|
if (AllLinear <> 3) then
|
|
NewMatShaper^.dwFlags := NewMatShaper^.dwFlags or MATSHAPER_HASSHAPER;
|
|
|
|
result := NewMatShaper;
|
|
end;
|
|
|
|
function cmsBuildGrayInputMatrixShaper(hProfile: cmsHPROFILE): LPMATSHAPER;
|
|
var
|
|
Illuminant: cmsCIEXYZ;
|
|
GrayTRC: LPGAMMATABLE;
|
|
Shapes: array[0..2] of LPGAMMATABLE;
|
|
MatShaper: LPMATSHAPER;
|
|
Scale: MAT3;
|
|
begin
|
|
|
|
GrayTRC := cmsReadICCGamma(hProfile, icSigGrayTRCTag);
|
|
cmsTakeIluminant(@Illuminant, hProfile);
|
|
|
|
Shapes[0] := cmsDupGamma(GrayTRC);
|
|
Shapes[1] := cmsDupGamma(GrayTRC);
|
|
Shapes[2] := cmsDupGamma(GrayTRC);
|
|
|
|
if (Shapes[0] = nil) or (Shapes[1] = nil) or (Shapes[2] = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsFreeGamma(GrayTRC);
|
|
|
|
VEC3init(@Scale.v[0], Illuminant.X / 3, Illuminant.X / 3, Illuminant.X / 3);
|
|
VEC3init(@Scale.v[1], Illuminant.Y / 3, Illuminant.Y / 3, Illuminant.Y / 3);
|
|
VEC3init(@Scale.v[2], Illuminant.Z / 3, Illuminant.Z / 3, Illuminant.Z / 3);
|
|
|
|
MatShaper := cmsAllocMatShaper(@Scale, @Shapes, MATSHAPER_INPUT);
|
|
cmsFreeGammaTriple(@Shapes);
|
|
result := MatShaper;
|
|
|
|
end;
|
|
|
|
function cmsBuildInputMatrixShaper(InputProfile: cmsHPROFILE; dwFlags: PDWORD): LPMATSHAPER;
|
|
var
|
|
DoubleMat: MAT3;
|
|
Shapes: array[0..2] of LPGAMMATABLE;
|
|
InMatSh: LPMATSHAPER;
|
|
begin
|
|
|
|
if (cmsGetColorSpace(InputProfile) = icSigGrayData) then
|
|
begin
|
|
if (dwFlags) <> nil then
|
|
dwFlags^ := dwFlags^ or cmsFLAGS_NOTPRECALC;
|
|
result := cmsBuildGrayInputMatrixShaper(InputProfile);
|
|
exit;
|
|
end;
|
|
|
|
if (not cmsReadICCMatrixRGB2XYZ(@DoubleMat, InputProfile)) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Shapes[0] := cmsReadICCGamma(InputProfile, icSigRedTRCTag);
|
|
Shapes[1] := cmsReadICCGamma(InputProfile, icSigGreenTRCTag);
|
|
Shapes[2] := cmsReadICCGamma(InputProfile, icSigBlueTRCTag);
|
|
|
|
if (Shapes[0] = nil) or (Shapes[1] = nil) or (Shapes[2] = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
InMatSh := cmsAllocMatShaper(@DoubleMat, @Shapes, MATSHAPER_INPUT);
|
|
|
|
cmsFreeGammaTriple(@Shapes);
|
|
|
|
result := InMatSh;
|
|
end;
|
|
|
|
procedure PCStoLUT(p: _LPcmsTRANSFORM; xIn: pwordarray; xOut: pwordarray);
|
|
begin
|
|
cmsEvalLUT(p^.PCS2Device, xIn, xOut);
|
|
end;
|
|
|
|
procedure PCStoShaperMatrix(p: _LPcmsTRANSFORM; xIn: pwordarray; xOut: pwordarray); {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
cmsEvalMatShaper(p^.OutMatShaper, xIn, xOut);
|
|
end;
|
|
|
|
function cmsBuildGrayOutputMatrixShaper(hProfile: cmsHPROFILE): LPMATSHAPER;
|
|
var
|
|
Illuminant: cmsCIEXYZ;
|
|
GrayTRC: LPGAMMATABLE;
|
|
Shapes: array[0..2] of LPGAMMATABLE;
|
|
MatShaper: LPMATSHAPER;
|
|
Scale: MAT3;
|
|
|
|
begin
|
|
|
|
GrayTRC := cmsReadICCGammaReversed(hProfile, icSigGrayTRCTag);
|
|
cmsTakeIluminant(@Illuminant, hProfile);
|
|
|
|
Shapes[0] := cmsDupGamma(GrayTRC);
|
|
Shapes[1] := cmsDupGamma(GrayTRC);
|
|
Shapes[2] := cmsDupGamma(GrayTRC);
|
|
|
|
if (Shapes[0] = nil) or (Shapes[1] = nil) or (Shapes[2] = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsFreeGamma(GrayTRC);
|
|
|
|
VEC3init(@Scale.v[0], 0, 1.0 / Illuminant.Y, 0);
|
|
VEC3init(@Scale.v[1], 0, 1.0 / Illuminant.Y, 0);
|
|
VEC3init(@Scale.v[2], 0, 1.0 / Illuminant.Y, 0);
|
|
|
|
MatShaper := cmsAllocMatShaper(@Scale, @Shapes, MATSHAPER_OUTPUT);
|
|
cmsFreeGammaTriple(@Shapes);
|
|
result := MatShaper;
|
|
|
|
end;
|
|
|
|
function cmsBuildOutputMatrixShaper(OutputProfile: cmsHPROFILE; dwFlags: PDWORD): LPMATSHAPER;
|
|
var
|
|
DoubleMat, DoubleInv: MAT3;
|
|
InverseShapes: array[0..2] of LPGAMMATABLE;
|
|
OutMatSh: LPMATSHAPER;
|
|
|
|
begin
|
|
|
|
if (cmsGetColorSpace(OutputProfile) = icSigGrayData) then
|
|
begin
|
|
if (dwFlags <> nil) then
|
|
dwFlags^ := dwFlags^ or cmsFLAGS_NOTPRECALC;
|
|
result := cmsBuildGrayOutputMatrixShaper(OutputProfile);
|
|
exit;
|
|
end;
|
|
|
|
if (not cmsReadICCMatrixRGB2XYZ(@DoubleMat, OutputProfile)) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (MAT3inverse(@DoubleMat, @DoubleInv) < 0) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
InverseShapes[0] := cmsReadICCGammaReversed(OutputProfile, icSigRedTRCTag);
|
|
InverseShapes[1] := cmsReadICCGammaReversed(OutputProfile, icSigGreenTRCTag);
|
|
InverseShapes[2] := cmsReadICCGammaReversed(OutputProfile, icSigBlueTRCTag);
|
|
|
|
OutMatSh := cmsAllocMatShaper(@DoubleInv, @InverseShapes, MATSHAPER_OUTPUT);
|
|
|
|
cmsFreeGammaTriple(@InverseShapes);
|
|
|
|
result := OutMatSh;
|
|
end;
|
|
|
|
function PickTransformRoutine(p: _LPcmsTRANSFORM; dwFlagsPtr: PDWORD; FromTagPtr: PicTagSignature; ToTagPtr: PicTagSignature): _LPcmsTRANSFORM;
|
|
begin
|
|
|
|
if (cmsGetDeviceClass(p^.InputProfile) = icSigNamedColorClass) then
|
|
begin
|
|
|
|
p^.FromDevice := NC2toPCS;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if (FromTagPtr^ = icTagSignature(0)) and (ToTagPtr^ = icTagSignature(0)) and (p^.PreviewProfile <> nil) and
|
|
(p^.Intent <> INTENT_ABSOLUTE_COLORIMETRIC) and (p^.EntryColorSpace = icSigRgbData) and
|
|
(p^.ExitColorSpace = icSigRgbData) then
|
|
begin
|
|
|
|
p^.xform := MatrixShaperXFORM;
|
|
dwFlagsPtr^ := dwFlagsPtr^ or cmsFLAGS_NOTPRECALC;
|
|
|
|
if (not cmsBuildSmeltMatShaper(p)) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "unable to smelt shaper-matrix, required tags missing");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
p^.Phase3 := XYZRel;
|
|
p^.Phase1 := p^.Phase3;
|
|
result := p;
|
|
exit;
|
|
|
|
end;
|
|
|
|
if (FromTagPtr^ <> icTagSignature(0)) then
|
|
begin
|
|
|
|
p^.FromDevice := LUTtoPCS;
|
|
p^.Device2PCS := cmsReadICCLut(p^.InputProfile, FromTagPtr^);
|
|
if (p^.Device2PCS = nil) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
p^.FromDevice := ShaperMatrixToPCS;
|
|
p^.InMatShaper := cmsBuildInputMatrixShaper(p^.InputProfile, dwFlagsPtr);
|
|
|
|
if (p^.InMatShaper = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.Phase1 := XYZRel;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if (ToTagPtr^ <> icTagSignature(0)) then
|
|
begin
|
|
|
|
p^.ToDevice := PCStoLUT;
|
|
p^.PCS2Device := cmsReadICCLut(p^.OutputProfile, ToTagPtr^);
|
|
if (p^.PCS2Device = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
p^.ToDevice := PCStoShaperMatrix;
|
|
p^.OutMatShaper := cmsBuildOutputMatrixShaper(p^.OutputProfile, dwFlagsPtr);
|
|
|
|
if (p^.OutMatShaper = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
p^.Phase3 := XYZRel;
|
|
|
|
end;
|
|
|
|
result := p;
|
|
end;
|
|
|
|
function cmsTakeMediaWhitePoint(Dest: LPcmsCIEXYZ; hProfile: cmsHPROFILE): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
Dest^ := Icc^.MediaWhitePoint;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsTakeMediaBlackPoint(Dest: LPcmsCIEXYZ; hProfile: cmsHPROFILE): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
Dest^ := Icc^.MediaBlackPoint;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsTakeHeaderFlags(hProfile: cmsHPROFILE): dword;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
result := Icc^.flags;
|
|
end;
|
|
|
|
function cmsReadChromaticAdaptationMatrix(r: LPMAT3; hProfile: cmsHPROFILE): longbool;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
|
|
if (ReadICCXYZArray(hProfile, icTagSignature(icSigChromaticAdaptationTag), r) < 0) then
|
|
begin
|
|
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
MAT3identity(r);
|
|
|
|
if ((cmsGetDeviceClass(hProfile) = icSigDisplayClass)) or
|
|
((cmsTakeHeaderFlags(hProfile) and icTransparency) <> 0) then
|
|
begin
|
|
|
|
cmsAdaptationMatrix(r, nil, @Icc^.MediaWhitePoint, @Icc^.Illuminant);
|
|
end;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsGetProfileICCversion(hProfile: cmsHPROFILE): dword;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
result := Icc^.Version;
|
|
end;
|
|
|
|
function cmsAdaptToIlluminant(xResult: LPcmsCIEXYZ;
|
|
SourceWhitePt: LPcmsCIEXYZ;
|
|
Illuminant: LPcmsCIEXYZ;
|
|
Value: LPcmsCIEXYZ): longbool;
|
|
var
|
|
Bradford: MAT3;
|
|
xIn, xOut: VEC3;
|
|
begin
|
|
|
|
cmsAdaptationMatrix(@Bradford, nil, SourceWhitePt, Illuminant);
|
|
|
|
VEC3init(@xIn, Value^.X, Value^.Y, Value^.Z);
|
|
MAT3eval(@xOut, @Bradford, @xIn);
|
|
|
|
xResult^.X := xOut.n[0];
|
|
xResult^.Y := xOut.n[1];
|
|
xResult^.Z := xOut.n[2];
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function GetV4PerceptualBlack(BlackPoint: LPcmsCIEXYZ; hProfile: cmsHPROFILE; dwFlags: DWORD): integer;
|
|
var
|
|
D50BlackPoint, MediaWhite: cmsCIEXYZ;
|
|
begin
|
|
if (dwFlags and LCMS_BPFLAGS_D50_ADAPTED) <> 0 then
|
|
begin
|
|
|
|
BlackPoint^.X := PERCEPTUAL_BLACK_X;
|
|
BlackPoint^.Y := PERCEPTUAL_BLACK_Y;
|
|
BlackPoint^.Z := PERCEPTUAL_BLACK_Z;
|
|
end
|
|
else
|
|
begin
|
|
|
|
cmsTakeMediaWhitePoint(@MediaWhite, hProfile);
|
|
D50BlackPoint.X := PERCEPTUAL_BLACK_X;
|
|
D50BlackPoint.Y := PERCEPTUAL_BLACK_Y;
|
|
D50BlackPoint.Z := PERCEPTUAL_BLACK_Z;
|
|
|
|
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ, @MediaWhite, @D50BlackPoint);
|
|
end;
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
|
|
function c_frexp(value: extended; eptr: pinteger): extended; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
frexp(value, result, eptr^);
|
|
end;
|
|
|
|
|
|
function CubeRoot(x: double): double;
|
|
var
|
|
fr, r: double;
|
|
ex, shx: integer;
|
|
begin
|
|
|
|
fr := c_frexp(x, @ex);
|
|
shx := ex mod 3;
|
|
|
|
if (shx > 0) then
|
|
shx := shx - 3;
|
|
|
|
ex := (ex - shx) div 3;
|
|
fr := ldexp(fr, shx);
|
|
|
|
fr := (((((45.2548339756803022511987494 * fr +
|
|
192.2798368355061050458134625) * fr +
|
|
119.1654824285581628956914143) * fr +
|
|
13.43250139086239872172837314) * fr +
|
|
0.1636161226585754240958355063)
|
|
/
|
|
((((14.80884093219134573786480845 * fr +
|
|
151.9714051044435648658557668) * fr +
|
|
168.5254414101568283957668343) * fr +
|
|
33.9905941350215598754191872) * fr +
|
|
1.0));
|
|
r := ldexp(fr, ex);
|
|
|
|
result := r;
|
|
end;
|
|
|
|
function f(t: double): double; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
|
|
if (t <= 0.008856) then
|
|
result := 7.787037037037037037037037037037 * t + (16 / 116)
|
|
else
|
|
result := CubeRoot(t);
|
|
|
|
end;
|
|
|
|
procedure cmsXYZ2Lab(WhitePoint: LPcmsCIEXYZ; Lab: LPcmsCIELab; xyz: LPcmsCIEXYZ);
|
|
var
|
|
fx, fy, fz: double;
|
|
begin
|
|
|
|
if (xyz^.X = 0) and (xyz^.Y = 0) and (xyz^.Z = 0) then
|
|
begin
|
|
Lab^.L := 0;
|
|
Lab^.a := 0;
|
|
Lab^.b := 0;
|
|
exit;
|
|
end;
|
|
|
|
if (WhitePoint = nil) then
|
|
WhitePoint := cmsD50_XYZ;
|
|
|
|
fx := f(xyz^.X / WhitePoint^.X);
|
|
fy := f(xyz^.Y / WhitePoint^.Y);
|
|
fz := f(xyz^.Z / WhitePoint^.Z);
|
|
|
|
Lab^.L := 116. * fy - 16.;
|
|
|
|
Lab^.a := 500. * (fx - fy);
|
|
Lab^.b := 200. * (fy - fz);
|
|
end;
|
|
|
|
function f_1(t: double): double;
|
|
var
|
|
tmp: double;
|
|
begin
|
|
|
|
if (t <= ((7.787 * 0.008856) + (16 / 116))) then
|
|
begin
|
|
|
|
tmp := ((t - (16 / 116)) / 7.787037037037037037037037037037);
|
|
if (tmp <= 0.0) then
|
|
begin
|
|
result := 0.0;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
result := tmp;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := t * t * t;
|
|
end;
|
|
|
|
procedure cmsLab2XYZ(WhitePoint: LPcmsCIEXYZ; xyz: LPcmsCIEXYZ; Lab: LPcmsCIELab);
|
|
var
|
|
x, y, z: double;
|
|
begin
|
|
|
|
if (Lab^.L <= 0) then
|
|
begin
|
|
xyz^.X := 0;
|
|
xyz^.Y := 0;
|
|
xyz^.Z := 0;
|
|
exit;
|
|
end;
|
|
|
|
if (WhitePoint = nil) then
|
|
WhitePoint := cmsD50_XYZ;
|
|
|
|
y := (Lab^.L + 16) / 116.0;
|
|
x := y + 0.002 * Lab^.a;
|
|
z := y - 0.005 * Lab^.b;
|
|
|
|
xyz^.X := f_1(x) * WhitePoint^.X;
|
|
xyz^.Y := f_1(y) * WhitePoint^.Y;
|
|
xyz^.Z := f_1(z) * WhitePoint^.Z;
|
|
|
|
end;
|
|
|
|
function cmsTakeRenderingIntent(hProfile: cmsHPROFILE): integer;
|
|
var
|
|
Icc: LPLCMSICCPROFILE;
|
|
begin
|
|
Icc := LPLCMSICCPROFILE(hProfile);
|
|
|
|
result := integer(Icc^.RenderingIntent);
|
|
end;
|
|
|
|
function cmsIsIntentSupported(hProfile: cmsHPROFILE;
|
|
Intent: integer; UsedDirection: integer): longbool;
|
|
var
|
|
TagTable: PicTagSignature;
|
|
begin
|
|
|
|
if (cmsGetDeviceClass(hProfile) <> icSigLinkClass) then
|
|
begin
|
|
|
|
case (UsedDirection) of
|
|
|
|
LCMS_USED_AS_INPUT: TagTable := @Device2PCS;
|
|
LCMS_USED_AS_OUTPUT: TagTable := @PCS2Device;
|
|
LCMS_USED_AS_PROOF: TagTable := @Preview;
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Unexpected direction (%d)", UsedDirection);
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
inc(TagTable, Intent);
|
|
result := cmsIsTag(hProfile, TagTable^);
|
|
exit;
|
|
end;
|
|
|
|
result := (cmsTakeRenderingIntent(hProfile) = Intent);
|
|
end;
|
|
|
|
(*
|
|
function TYPE_Lab_DBL: dword;
|
|
begin
|
|
result := (COLORSPACE_SH(PT_Lab) or CHANNELS_SH(3) or BYTES_SH(0));
|
|
end;
|
|
*)
|
|
|
|
(*
|
|
function TYPE_CMYK_16: dword;
|
|
begin
|
|
result := (COLORSPACE_SH(PT_CMYK) or CHANNELS_SH(4) or BYTES_SH(2));
|
|
end;
|
|
*)
|
|
|
|
function BlackPointUsingPerceptualBlack(BlackPoint: LPcmsCIEXYZ;
|
|
hProfile: cmsHPROFILE;
|
|
dwFlags: DWORD): integer;
|
|
var
|
|
hPercLab2CMYK, hRelColCMYK2Lab: cmsHTRANSFORM;
|
|
hLab: cmsHPROFILE;
|
|
LabIn, LabOut: cmsCIELab;
|
|
CMYK: array[0..MAXCHANNELS - 1] of WORD;
|
|
BlackXYZ, MediaWhite: cmsCIEXYZ;
|
|
|
|
begin
|
|
|
|
if (not cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) then
|
|
begin
|
|
|
|
BlackPoint^.Z := 0.0;
|
|
BlackPoint^.Y := BlackPoint^.Z;
|
|
BlackPoint^.X := BlackPoint^.Y;
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
hLab := cmsCreateLabProfile(nil);
|
|
|
|
hPercLab2CMYK := IEcmsCreateTransform(hLab, TYPE_Lab_DBL,
|
|
hProfile, TYPE_CMYK_16,
|
|
INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC);
|
|
|
|
hRelColCMYK2Lab := IEcmsCreateTransform(hProfile, TYPE_CMYK_16,
|
|
hLab, TYPE_Lab_DBL,
|
|
INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC);
|
|
|
|
LabIn.b := 0;
|
|
LabIn.a := LabIn.b;
|
|
LabIn.L := LabIn.a;
|
|
|
|
IEcmsDoTransform(hPercLab2CMYK, @LabIn, @CMYK, 1);
|
|
IEcmsDoTransform(hRelColCMYK2Lab, @CMYK, @LabOut, 1);
|
|
|
|
if (LabOut.L > 50) then
|
|
LabOut.L := 50;
|
|
LabOut.b := 0;
|
|
LabOut.a := LabOut.b;
|
|
|
|
IEcmsDeleteTransform(hPercLab2CMYK);
|
|
IEcmsDeleteTransform(hRelColCMYK2Lab);
|
|
IEcmsCloseProfile(hLab);
|
|
|
|
cmsLab2XYZ(nil, @BlackXYZ, @LabOut);
|
|
|
|
if ((dwFlags and LCMS_BPFLAGS_D50_ADAPTED) = 0) then
|
|
begin
|
|
cmsTakeMediaWhitePoint(@MediaWhite, hProfile);
|
|
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), @MediaWhite, @BlackXYZ);
|
|
end
|
|
else
|
|
BlackPoint^ := BlackXYZ;
|
|
|
|
result := 1;
|
|
|
|
end;
|
|
|
|
function BlackPointAsDarkerColorant(hInput: cmsHPROFILE;
|
|
Intent: integer;
|
|
BlackPoint: LPcmsCIEXYZ;
|
|
dwFlags: DWORD): integer;
|
|
var
|
|
White, Black: pWORD;
|
|
xform: cmsHTRANSFORM;
|
|
Space: icColorSpaceSignature;
|
|
nChannels: integer;
|
|
dwFormat: DWORD;
|
|
hLab: cmsHPROFILE;
|
|
Lab: cmsCIELab;
|
|
BlackXYZ, MediaWhite: cmsCIEXYZ;
|
|
begin
|
|
|
|
if (not cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) then
|
|
begin
|
|
|
|
BlackPoint^.Z := 0.0;
|
|
BlackPoint^.Y := BlackPoint^.Z;
|
|
BlackPoint^.X := BlackPoint^.Y;
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
Space := cmsGetColorSpace(hInput);
|
|
|
|
if (not _cmsEndPointsBySpace(Space, White, Black, @nChannels)) then
|
|
begin
|
|
|
|
BlackPoint^.Z := 0.0;
|
|
BlackPoint^.Y := BlackPoint^.Z;
|
|
BlackPoint^.X := BlackPoint^.Y;
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
|
|
dwFormat := (nChannels shl 3) or 2;
|
|
|
|
hLab := cmsCreateLabProfile(nil);
|
|
|
|
xform := IEcmsCreateTransform(hInput, dwFormat,
|
|
hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC);
|
|
|
|
IEcmsDoTransform(xform, Black, @Lab, 1);
|
|
|
|
Lab.b := 0;
|
|
Lab.a := Lab.b;
|
|
if (Lab.L > 50) then
|
|
Lab.L := 50;
|
|
|
|
IEcmsCloseProfile(hLab);
|
|
IEcmsDeleteTransform(xform);
|
|
|
|
cmsLab2XYZ(nil, @BlackXYZ, @Lab);
|
|
|
|
if (Intent = INTENT_ABSOLUTE_COLORIMETRIC) then
|
|
begin
|
|
|
|
BlackPoint^ := BlackXYZ;
|
|
end
|
|
else
|
|
begin
|
|
|
|
if ((dwFlags and LCMS_BPFLAGS_D50_ADAPTED) = 0) then
|
|
begin
|
|
|
|
cmsTakeMediaWhitePoint(@MediaWhite, hInput);
|
|
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), @MediaWhite, @BlackXYZ);
|
|
end
|
|
else
|
|
BlackPoint^ := BlackXYZ;
|
|
end;
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
function cmsDetectBlackPoint(BlackPoint: LPcmsCIEXYZ; hProfile: cmsHPROFILE; Intent: integer; dwFlags: DWORD): integer;
|
|
var
|
|
BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite: cmsCIEXYZ;
|
|
Lab: cmsCIELab;
|
|
begin
|
|
|
|
if ((cmsGetProfileICCversion(hProfile) >= $4000000) and
|
|
((Intent = INTENT_PERCEPTUAL) or (Intent = INTENT_SATURATION))) then
|
|
begin
|
|
|
|
result := GetV4PerceptualBlack(BlackPoint, hProfile, dwFlags);
|
|
exit;
|
|
end;
|
|
|
|
if (cmsIsTag(hProfile, icSigMediaBlackPointTag) and
|
|
(Intent = INTENT_RELATIVE_COLORIMETRIC)) then
|
|
begin
|
|
|
|
cmsTakeMediaBlackPoint(@BlackXYZ, hProfile);
|
|
cmsTakeMediaWhitePoint(@MediaWhite, hProfile);
|
|
|
|
cmsAdaptToIlluminant(@UntrustedBlackPoint, @MediaWhite, cmsD50_XYZ, @BlackXYZ);
|
|
|
|
cmsXYZ2Lab(nil, @Lab, @UntrustedBlackPoint);
|
|
Lab.b := 0;
|
|
Lab.a := Lab.b;
|
|
if (Lab.L > 50) then
|
|
Lab.L := 50;
|
|
|
|
cmsLab2XYZ(nil, @TrustedBlackPoint, @Lab);
|
|
|
|
if ((dwFlags and LCMS_BPFLAGS_D50_ADAPTED) = 0) then
|
|
cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ, @MediaWhite, @TrustedBlackPoint)
|
|
else
|
|
BlackPoint^ := TrustedBlackPoint;
|
|
end;
|
|
|
|
if (Intent = INTENT_RELATIVE_COLORIMETRIC) and
|
|
(cmsGetDeviceClass(hProfile) = icSigOutputClass) and
|
|
(cmsGetColorSpace(hProfile) = icSigCmykData) then
|
|
begin
|
|
result := BlackPointUsingPerceptualBlack(BlackPoint, hProfile, dwFlags);
|
|
exit;
|
|
end;
|
|
|
|
result := BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
|
|
|
|
end;
|
|
|
|
procedure Rel2RelStepAbsCoefs(BlackPointIn: LPcmsCIEXYZ;
|
|
WhitePointIn: LPcmsCIEXYZ;
|
|
IlluminantIn: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixIn: LPMAT3;
|
|
|
|
BlackPointOut: LPcmsCIEXYZ;
|
|
WhitePointOut: LPcmsCIEXYZ;
|
|
IlluminantOut: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixOut: LPMAT3;
|
|
|
|
m: LPMAT3; xof: LPVEC3);
|
|
|
|
var
|
|
WtPtIn, WtPtInAdapted: VEC3;
|
|
WtPtOut, WtPtOutAdapted: VEC3;
|
|
Scale, m1, m2, m3: MAT3;
|
|
begin
|
|
|
|
VEC3init(@WtPtIn, WhitePointIn^.X, WhitePointIn^.Y, WhitePointIn^.Z);
|
|
MAT3eval(@WtPtInAdapted, ChromaticAdaptationMatrixIn, @WtPtIn);
|
|
|
|
VEC3init(@WtPtOut, WhitePointOut^.X, WhitePointOut^.Y, WhitePointOut^.Z);
|
|
MAT3eval(@WtPtOutAdapted, ChromaticAdaptationMatrixOut, @WtPtOut);
|
|
|
|
VEC3init(@Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0);
|
|
VEC3init(@Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0);
|
|
VEC3init(@Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]);
|
|
|
|
m1 := ChromaticAdaptationMatrixIn^;
|
|
MAT3inverse(@m1, @m2);
|
|
|
|
MAT3per(@m3, @m2, @Scale);
|
|
MAT3per(m, @m3, ChromaticAdaptationMatrixOut);
|
|
VEC3init(xof, 0.0, 0.0, 0.0);
|
|
|
|
end;
|
|
|
|
procedure XYZ2XYZ(xIn: pwordarray; xOut: pwordarray; m: LPWMAT3; xof: LPWVEC3);
|
|
var
|
|
a, r: WVEC3;
|
|
|
|
begin
|
|
|
|
a.n[0] := xIn[0] shl 1;
|
|
a.n[1] := xIn[1] shl 1;
|
|
a.n[2] := xIn[2] shl 1;
|
|
|
|
MAT3evalW(@r, m, @a);
|
|
|
|
xOut[0] := Clamp_XYZ((r.n[VX] + xof^.n[VX]) shr 1);
|
|
xOut[1] := Clamp_XYZ((r.n[VY] + xof^.n[VY]) shr 1);
|
|
xOut[2] := Clamp_XYZ((r.n[VZ] + xof^.n[VZ]) shr 1);
|
|
end;
|
|
|
|
procedure ComputeBlackPointCompensationFactors(BlackPointIn: LPcmsCIEXYZ;
|
|
WhitePointIn: LPcmsCIEXYZ;
|
|
IlluminantIn: LPcmsCIEXYZ;
|
|
BlackPointOut: LPcmsCIEXYZ;
|
|
WhitePointOut: LPcmsCIEXYZ;
|
|
IlluminantOut: LPcmsCIEXYZ;
|
|
m: LPMAT3; xof: LPVEC3);
|
|
var
|
|
RelativeBlackPointIn, RelativeBlackPointOut: cmsCIEXYZ;
|
|
ax, ay, az, bx, by, bz, tx, ty, tz: double;
|
|
|
|
begin
|
|
|
|
cmsAdaptToIlluminant(@RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn);
|
|
cmsAdaptToIlluminant(@RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut);
|
|
|
|
tx := RelativeBlackPointIn.X - IlluminantIn^.X;
|
|
ty := RelativeBlackPointIn.Y - IlluminantIn^.Y;
|
|
tz := RelativeBlackPointIn.Z - IlluminantIn^.Z;
|
|
|
|
ax := (RelativeBlackPointOut.X - IlluminantOut^.X) / tx;
|
|
ay := (RelativeBlackPointOut.Y - IlluminantOut^.Y) / ty;
|
|
az := (RelativeBlackPointOut.Z - IlluminantOut^.Z) / tz;
|
|
|
|
bx := -IlluminantOut^.X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx;
|
|
by := -IlluminantOut^.Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty;
|
|
bz := -IlluminantOut^.Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz;
|
|
|
|
MAT3identity(m);
|
|
|
|
m^.v[VX].n[0] := ax;
|
|
m^.v[VY].n[1] := ay;
|
|
m^.v[VZ].n[2] := az;
|
|
|
|
VEC3init(xof, bx, by, bz);
|
|
|
|
end;
|
|
|
|
function Clamp_L(xin: Fixed32): word;
|
|
begin
|
|
if (xin = $FFFF) then
|
|
begin
|
|
result := $FFFF;
|
|
exit;
|
|
end;
|
|
|
|
if (xin > $FF00) then
|
|
begin
|
|
result := $FF00;
|
|
exit;
|
|
end;
|
|
result := xin;
|
|
end;
|
|
|
|
function ENCODE_AB(x: double): word; {$ifdef IESUPPORTINLINE} inline; {$endif}
|
|
begin
|
|
result := round(((x) + 128.0) * 256.0 );
|
|
end;
|
|
|
|
function Clamp_ab(xin: Fixed32): word;
|
|
begin
|
|
if (xin = $FFFF) then
|
|
begin
|
|
result := $FFFF;
|
|
exit;
|
|
end;
|
|
|
|
if (xin < 0) then
|
|
begin
|
|
result := ENCODE_AB(-128.0);
|
|
exit;
|
|
end;
|
|
if (xin > $FFFF) then
|
|
begin
|
|
result := ENCODE_AB(+127.9961);
|
|
exit;
|
|
end;
|
|
result := xin;
|
|
end;
|
|
|
|
procedure cmsXYZ2LabEncoded(XYZ: pwordarray; Lab: pwordarray);
|
|
var
|
|
X_ma, Y_ma, Z_ma: Fixed32;
|
|
x_mi, y_mi, z_mi, L, a, b: double;
|
|
fx, fy, fz: double;
|
|
wL, wa, wb: Fixed32;
|
|
begin
|
|
|
|
X_ma := XYZ[0] shl 1;
|
|
Y_ma := XYZ[1] shl 1;
|
|
Z_ma := XYZ[2] shl 1;
|
|
|
|
if (X_ma = 0) and (Y_ma = 0) and (Z_ma = 0) then
|
|
begin
|
|
|
|
Lab[0] := 0;
|
|
Lab[2] := $8000;
|
|
Lab[1] := Lab[2];
|
|
exit;
|
|
end;
|
|
|
|
x_mi := FIXED_TO_DOUBLE(X_ma) / D50X;
|
|
y_mi := FIXED_TO_DOUBLE(Y_ma) / D50Y;
|
|
z_mi := FIXED_TO_DOUBLE(Z_ma) / D50Z;
|
|
|
|
fx := f(x_mi);
|
|
fy := f(y_mi);
|
|
fz := f(z_mi);
|
|
|
|
L := 116 * fy - 16;
|
|
|
|
a := 500 * (fx - fy);
|
|
b := 200 * (fy - fz);
|
|
|
|
a := a + 128;
|
|
b := b + 128;
|
|
|
|
wL := round(L * 652.800 );
|
|
wa := round(a * 256.0 );
|
|
wb := round(b * 256.0 );
|
|
|
|
Lab[0] := Clamp_L(wL);
|
|
Lab[1] := Clamp_ab(wa);
|
|
Lab[2] := Clamp_ab(wb);
|
|
|
|
end;
|
|
|
|
procedure XYZ2Lab(xIn: pwordarray; xOut: pwordarray; m: LPWMAT3; xof: LPWVEC3);
|
|
var
|
|
XYZ: array[0..2] of WORD;
|
|
begin
|
|
|
|
XYZ2XYZ(xIn, @XYZ, m, xof);
|
|
cmsXYZ2LabEncoded(@XYZ, xOut);
|
|
end;
|
|
|
|
function FromXYZRelLUT(xAbsolute: integer;
|
|
BlackPointIn: LPcmsCIEXYZ;
|
|
WhitePointIn: LPcmsCIEXYZ;
|
|
IlluminantIn: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixIn: LPMAT3;
|
|
|
|
Phase2: integer; BlackPointOut: LPcmsCIEXYZ;
|
|
WhitePointOut: LPcmsCIEXYZ;
|
|
IlluminantOut: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixOut: LPMAT3;
|
|
|
|
DoBlackPointCompensation: integer;
|
|
var fn1: _cmsADJFN;
|
|
m: LPMAT3; xof: LPVEC3): integer;
|
|
|
|
begin
|
|
case (Phase2) of
|
|
|
|
XYZRel:
|
|
|
|
if (xAbsolute <> 0) then
|
|
begin
|
|
|
|
Rel2RelStepAbsCoefs(BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
ChromaticAdaptationMatrixIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
ChromaticAdaptationMatrixOut,
|
|
m, xof);
|
|
fn1 := XYZ2XYZ;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
fn1 := nil;
|
|
if (DoBlackPointCompensation <> 0) then
|
|
begin
|
|
|
|
fn1 := XYZ2XYZ;
|
|
ComputeBlackPointCompensationFactors(BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
m, xof);
|
|
|
|
end;
|
|
end;
|
|
|
|
LabRel:
|
|
|
|
if (xAbsolute <> 0) then
|
|
begin
|
|
|
|
Rel2RelStepAbsCoefs(BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
ChromaticAdaptationMatrixIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
ChromaticAdaptationMatrixOut,
|
|
m, xof);
|
|
|
|
fn1 := XYZ2Lab;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
MAT3identity(m);
|
|
VEC3init(xof, 0, 0, 0);
|
|
fn1 := XYZ2Lab;
|
|
|
|
if (DoBlackPointCompensation <> 0) then
|
|
begin
|
|
|
|
ComputeBlackPointCompensationFactors(BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
m, xof);
|
|
end;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
result := 1;
|
|
end;
|
|
|
|
procedure cmsLab2XYZEncoded(Lab: pwordarray; XYZ: pwordarray);
|
|
var
|
|
L, a, b: double;
|
|
X_ma, Y_ma, Z_ma, x_mi, y_mi, z_mi: double;
|
|
|
|
begin
|
|
|
|
L := (Lab[0] * 100) / 65280;
|
|
if (L = 0) then
|
|
begin
|
|
|
|
XYZ[0] := 0;
|
|
XYZ[1] := 0;
|
|
XYZ[2] := 0;
|
|
exit;
|
|
end;
|
|
|
|
a := (Lab[1] / 256.0) - 128;
|
|
b := (Lab[2] / 256.0) - 128;
|
|
|
|
y_mi := (L + 16) / 116;
|
|
x_mi := y_mi + 0.002 * a;
|
|
z_mi := y_mi - 0.005 * b;
|
|
|
|
X_ma := f_1(x_mi) * D50X;
|
|
Y_ma := f_1(y_mi) * D50Y;
|
|
Z_ma := f_1(z_mi) * D50Z;
|
|
|
|
XYZ[0] := Clamp_XYZ(floor(X_ma * 32768 + 0.5));
|
|
XYZ[1] := Clamp_XYZ(floor(Y_ma * 32768 + 0.5));
|
|
XYZ[2] := Clamp_XYZ(floor(Z_ma * 32768 + 0.5));
|
|
|
|
end;
|
|
|
|
procedure Lab2XYZ(xIn: pwordarray; xOut: pwordarray; m: LPWMAT3; xof: LPWVEC3);
|
|
var
|
|
XYZ: array[0..2] of word;
|
|
begin
|
|
cmsLab2XYZEncoded(xIn, @XYZ);
|
|
XYZ2XYZ(@XYZ, xOut, m, xof);
|
|
end;
|
|
|
|
procedure Lab2XYZ2Lab(xIn: pwordarray; xOut: pwordarray; m: LPWMAT3; xof: LPWVEC3);
|
|
var
|
|
XYZ, XYZ2: array[0..2] of WORD;
|
|
begin
|
|
cmsLab2XYZEncoded(xIn, @XYZ);
|
|
XYZ2XYZ(@XYZ, @XYZ2, m, xof);
|
|
cmsXYZ2LabEncoded(@XYZ2, xOut);
|
|
end;
|
|
|
|
function FromLabRelLUT(xAbsolute: integer;
|
|
BlackPointIn: LPcmsCIEXYZ;
|
|
WhitePointIn: LPcmsCIEXYZ;
|
|
IlluminantIn: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixIn: LPMAT3;
|
|
|
|
Phase2: integer; BlackPointOut: LPcmsCIEXYZ;
|
|
WhitePointOut: LPcmsCIEXYZ;
|
|
IlluminantOut: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixOut: LPMAT3;
|
|
|
|
DoBlackPointCompensation: integer;
|
|
var fn1: _cmsADJFN;
|
|
m: LPMAT3; xof: LPVEC3): integer;
|
|
begin
|
|
|
|
case (Phase2) of
|
|
|
|
XYZRel:
|
|
|
|
if (xAbsolute <> 0) then
|
|
begin
|
|
|
|
Rel2RelStepAbsCoefs(BlackPointIn,
|
|
WhitePointIn,
|
|
cmsD50_XYZ(),
|
|
ChromaticAdaptationMatrixIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
ChromaticAdaptationMatrixOut,
|
|
m, xof);
|
|
|
|
fn1 := Lab2XYZ;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
fn1 := Lab2XYZ;
|
|
if (DoBlackPointCompensation <> 0) then
|
|
begin
|
|
|
|
ComputeBlackPointCompensationFactors(BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
m, xof);
|
|
|
|
end;
|
|
end;
|
|
|
|
LabRel:
|
|
|
|
if (xAbsolute <> 0) then
|
|
begin
|
|
|
|
Rel2RelStepAbsCoefs(BlackPointIn,
|
|
WhitePointIn, IlluminantIn,
|
|
ChromaticAdaptationMatrixIn,
|
|
BlackPointOut,
|
|
WhitePointOut, cmsD50_XYZ(),
|
|
ChromaticAdaptationMatrixOut,
|
|
m, xof);
|
|
fn1 := Lab2XYZ2Lab;
|
|
end
|
|
else
|
|
begin
|
|
|
|
fn1 := nil;
|
|
if (DoBlackPointCompensation <> 0) then
|
|
begin
|
|
|
|
fn1 := Lab2XYZ2Lab;
|
|
ComputeBlackPointCompensationFactors(BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
m, xof);
|
|
|
|
end;
|
|
end;
|
|
|
|
else
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
procedure VEC3initF(r: LPWVEC3; x, y, z: double);
|
|
begin
|
|
r^.n[VX] := DOUBLE_TO_FIXED(x);
|
|
r^.n[VY] := DOUBLE_TO_FIXED(y);
|
|
r^.n[VZ] := DOUBLE_TO_FIXED(z);
|
|
end;
|
|
|
|
function IdentityParameters(m: LPWMAT3; xof: LPWVEC3): longbool;
|
|
var
|
|
wv0: WVEC3;
|
|
begin
|
|
|
|
VEC3initF(@wv0, 0, 0, 0);
|
|
|
|
if (not MAT3isIdentity(m, 0.00001)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
if (not VEC3equal(xof, @wv0, 0.00001)) then
|
|
begin
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function cmsChooseCnvrt(xAbsolute: integer;
|
|
Phase1: integer; BlackPointIn: LPcmsCIEXYZ;
|
|
WhitePointIn: LPcmsCIEXYZ;
|
|
IlluminantIn: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixIn: LPMAT3;
|
|
|
|
Phase2: integer; BlackPointOut: LPcmsCIEXYZ;
|
|
WhitePointOut: LPcmsCIEXYZ;
|
|
IlluminantOut: LPcmsCIEXYZ;
|
|
ChromaticAdaptationMatrixOut: LPMAT3;
|
|
|
|
DoBlackPointCompensation: integer;
|
|
var fn1: _cmsADJFN;
|
|
wm: LPWMAT3; wof: LPWVEC3): integer;
|
|
|
|
var
|
|
rc: integer;
|
|
m: MAT3;
|
|
xof: VEC3;
|
|
|
|
begin
|
|
|
|
MAT3identity(@m);
|
|
VEC3init(@xof, 0, 0, 0);
|
|
|
|
case (Phase1) of
|
|
|
|
XYZRel: rc := FromXYZRelLUT(xAbsolute,
|
|
BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
ChromaticAdaptationMatrixIn,
|
|
Phase2,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
ChromaticAdaptationMatrixOut,
|
|
DoBlackPointCompensation,
|
|
fn1, @m, @xof);
|
|
|
|
LabRel: rc := FromLabRelLUT(xAbsolute,
|
|
BlackPointIn,
|
|
WhitePointIn,
|
|
IlluminantIn,
|
|
ChromaticAdaptationMatrixIn,
|
|
Phase2,
|
|
BlackPointOut,
|
|
WhitePointOut,
|
|
IlluminantOut,
|
|
ChromaticAdaptationMatrixOut,
|
|
DoBlackPointCompensation,
|
|
fn1, @m, @xof);
|
|
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error");
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
MAT3toFix(wm, @m);
|
|
VEC3toFix(wof, @xof);
|
|
|
|
if (@fn1 = @XYZ2XYZ) or (@fn1 = @Lab2XYZ2Lab) then
|
|
begin
|
|
|
|
if (IdentityParameters(wm, wof)) then
|
|
fn1 := nil;
|
|
end;
|
|
|
|
result := rc;
|
|
end;
|
|
|
|
procedure TakeConversionRoutines(p: _LPcmsTRANSFORM; DoBPC: integer);
|
|
var
|
|
BlackPointIn, WhitePointIn, IlluminantIn: cmsCIEXYZ;
|
|
BlackPointOut, WhitePointOut, IlluminantOut: cmsCIEXYZ;
|
|
BlackPointProof, WhitePointProof, IlluminantProof: cmsCIEXYZ;
|
|
ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut: MAT3;
|
|
ChromaticAdaptationMatrixProof: MAT3;
|
|
|
|
begin
|
|
|
|
cmsTakeIluminant(@IlluminantIn, p^.InputProfile);
|
|
cmsTakeMediaWhitePoint(@WhitePointIn, p^.InputProfile);
|
|
cmsTakeMediaBlackPoint(@BlackPointIn, p^.InputProfile);
|
|
cmsReadChromaticAdaptationMatrix(@ChromaticAdaptationMatrixIn, p^.InputProfile);
|
|
|
|
cmsTakeIluminant(@IlluminantOut, p^.OutputProfile);
|
|
cmsTakeMediaWhitePoint(@WhitePointOut, p^.OutputProfile);
|
|
cmsTakeMediaBlackPoint(@BlackPointOut, p^.OutputProfile);
|
|
cmsReadChromaticAdaptationMatrix(@ChromaticAdaptationMatrixOut, p^.OutputProfile);
|
|
|
|
if (p^.Preview = nil) then
|
|
begin
|
|
|
|
if (p^.Intent = INTENT_PERCEPTUAL) or
|
|
(p^.Intent = INTENT_SATURATION) then
|
|
begin
|
|
|
|
if ((cmsGetProfileICCversion(p^.InputProfile) >= $4000000) or
|
|
(cmsGetProfileICCversion(p^.OutputProfile) >= $4000000)) then
|
|
begin
|
|
|
|
DoBPC := 1;
|
|
end;
|
|
end;
|
|
|
|
if (p^.Intent = INTENT_ABSOLUTE_COLORIMETRIC) then
|
|
DoBPC := 0;
|
|
|
|
if ((cmsGetDeviceClass(p^.InputProfile) = icSigAbstractClass) or
|
|
(cmsGetDeviceClass(p^.InputProfile) = icSigLinkClass)) then
|
|
DoBPC := 0;
|
|
|
|
if ((cmsGetDeviceClass(p^.OutputProfile) = icSigAbstractClass) or
|
|
(cmsGetDeviceClass(p^.OutputProfile) = icSigLinkClass)) then
|
|
DoBPC := 0;
|
|
|
|
if (DoBPC <> 0) then
|
|
begin
|
|
|
|
cmsDetectBlackPoint(@BlackPointIn, p^.InputProfile, p^.Intent, 0);
|
|
cmsDetectBlackPoint(@BlackPointOut, p^.OutputProfile, p^.Intent, 0);
|
|
|
|
if (BlackPointIn.X = BlackPointOut.X) and
|
|
(BlackPointIn.Y = BlackPointOut.Y) and
|
|
(BlackPointIn.Z = BlackPointOut.Z) then
|
|
DoBPC := 0;
|
|
|
|
end;
|
|
|
|
cmsChooseCnvrt(integer(p^.Intent = INTENT_ABSOLUTE_COLORIMETRIC),
|
|
|
|
p^.Phase1,
|
|
@BlackPointIn,
|
|
@WhitePointIn,
|
|
@IlluminantIn,
|
|
@ChromaticAdaptationMatrixIn,
|
|
|
|
p^.Phase3,
|
|
@BlackPointOut,
|
|
@WhitePointOut,
|
|
@IlluminantOut,
|
|
@ChromaticAdaptationMatrixOut,
|
|
|
|
DoBPC,
|
|
p^.Stage1,
|
|
@p^.m1, @p^.of1);
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
cmsTakeIluminant(@IlluminantProof, p^.PreviewProfile);
|
|
cmsTakeMediaWhitePoint(@WhitePointProof, p^.PreviewProfile);
|
|
cmsTakeMediaBlackPoint(@BlackPointProof, p^.PreviewProfile);
|
|
cmsReadChromaticAdaptationMatrix(@ChromaticAdaptationMatrixProof, p^.PreviewProfile);
|
|
|
|
if (DoBPC <> 0) then
|
|
begin
|
|
|
|
cmsDetectBlackPoint(@BlackPointProof, p^.PreviewProfile, p^.Intent, 0);
|
|
cmsDetectBlackPoint(@BlackPointIn, p^.InputProfile, p^.Intent, 0);
|
|
cmsDetectBlackPoint(@BlackPointOut, p^.OutputProfile, p^.Intent, 0);
|
|
|
|
if (BlackPointIn.X = BlackPointProof.X) and
|
|
(BlackPointIn.Y = BlackPointProof.Y) and
|
|
(BlackPointIn.Z = BlackPointProof.Z) then
|
|
DoBPC := 0;
|
|
|
|
end;
|
|
|
|
cmsChooseCnvrt(integer(p^.Intent = INTENT_ABSOLUTE_COLORIMETRIC),
|
|
|
|
p^.Phase1,
|
|
@BlackPointIn,
|
|
@WhitePointIn,
|
|
@IlluminantIn,
|
|
@ChromaticAdaptationMatrixIn,
|
|
|
|
p^.Phase2,
|
|
@BlackPointProof,
|
|
@WhitePointProof,
|
|
@IlluminantProof,
|
|
@ChromaticAdaptationMatrixProof,
|
|
DoBPC,
|
|
p^.Stage1,
|
|
@p^.m1, @p^.of1);
|
|
|
|
cmsChooseCnvrt(integer(p^.ProofIntent = INTENT_ABSOLUTE_COLORIMETRIC),
|
|
|
|
p^.Phase2,
|
|
@BlackPointProof,
|
|
@WhitePointProof,
|
|
@IlluminantProof,
|
|
@ChromaticAdaptationMatrixProof,
|
|
|
|
p^.Phase3,
|
|
@BlackPointOut,
|
|
@WhitePointOut,
|
|
@IlluminantOut,
|
|
@ChromaticAdaptationMatrixOut,
|
|
0,
|
|
p^.Stage2,
|
|
@p^.m2, @p^.of2);
|
|
end;
|
|
|
|
end;
|
|
|
|
function _cmsChannelsOf(ColorSpace: icColorSpaceSignature): integer;
|
|
begin
|
|
|
|
case (ColorSpace) of
|
|
|
|
icSigGrayData: result := 1;
|
|
|
|
icSig2colorData: result := 2;
|
|
|
|
icSigXYZData,
|
|
icSigLabData,
|
|
icSigLuvData,
|
|
icSigYCbCrData,
|
|
icSigYxyData,
|
|
icSigRgbData,
|
|
icSigHsvData,
|
|
icSigHlsData,
|
|
icSigCmyData,
|
|
icSig3colorData: result := 3;
|
|
|
|
icColorSpaceSignature(icSigLuvKData),
|
|
icSigCmykData,
|
|
icSig4colorData: result := 4;
|
|
|
|
icSig5colorData: result := 5;
|
|
|
|
icColorSpaceSignature(icSigHexachromeData),
|
|
icSig6colorData: result := 6;
|
|
|
|
icColorSpaceSignature(icSigHeptachromeData),
|
|
icSig7colorData: result := 7;
|
|
|
|
icColorSpaceSignature(icSigOctachromeData),
|
|
icSig8colorData: result := 8;
|
|
|
|
icSig9colorData: result := 9;
|
|
icSig10colorData: result := 10;
|
|
icSig11colorData: result := 11;
|
|
icSig12colorData: result := 12;
|
|
icSig13colorData: result := 13;
|
|
icSig14colorData: result := 14;
|
|
icSig15colorData: result := 15;
|
|
|
|
else
|
|
result := 3;
|
|
end;
|
|
|
|
end;
|
|
|
|
function _cmsReasonableGridpointsByColorspace(Colorspace: icColorSpaceSignature; dwFlags: DWORD): integer;
|
|
var
|
|
nChannels: integer;
|
|
begin
|
|
|
|
nChannels := _cmsChannelsOf(Colorspace);
|
|
|
|
if (dwFlags and cmsFLAGS_HIGHRESPRECALC) <> 0 then
|
|
begin
|
|
|
|
if (nChannels > 4) then
|
|
begin
|
|
result := 7;
|
|
exit;
|
|
end;
|
|
|
|
if (nChannels = 4) then
|
|
begin
|
|
result := 23;
|
|
exit;
|
|
end;
|
|
|
|
result := 48;
|
|
exit;
|
|
end;
|
|
|
|
if (dwFlags and cmsFLAGS_LOWRESPRECALC) <> 0 then
|
|
begin
|
|
|
|
if (nChannels > 4) then
|
|
begin
|
|
result := 6;
|
|
exit;
|
|
end;
|
|
|
|
if (nChannels = 1) then
|
|
begin
|
|
result := 33;
|
|
exit;
|
|
end;
|
|
|
|
result := 17;
|
|
exit;
|
|
end;
|
|
|
|
if (nChannels > 4) then
|
|
begin
|
|
result := 7;
|
|
exit;
|
|
end;
|
|
|
|
if (nChannels = 4) then
|
|
begin
|
|
result := 17;
|
|
exit;
|
|
end;
|
|
|
|
result := 33;
|
|
|
|
end;
|
|
|
|
function MostlyLinear(Table: pwordarray; nEntries: integer): integer;
|
|
var
|
|
i: integer;
|
|
diff: integer;
|
|
begin
|
|
|
|
for i := 5 to nEntries - 1 do
|
|
begin
|
|
|
|
diff := abs(Table[i] - _cmsQuantizeVal(i, nEntries));
|
|
if (diff > $0300) then
|
|
begin
|
|
result := 0;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
result := 1;
|
|
end;
|
|
|
|
function IsMonotonic(t: LPGAMMATABLE): longbool;
|
|
var
|
|
n: integer;
|
|
i, last: integer;
|
|
begin
|
|
n := t^.nEntries;
|
|
|
|
last := t^.GammaTable[n - 1];
|
|
|
|
for i := n - 2 downto 0 do
|
|
begin
|
|
|
|
if (t^.GammaTable[i] > last) then
|
|
begin
|
|
|
|
result := FALSE;
|
|
exit;
|
|
end
|
|
else
|
|
last := t^.GammaTable[i];
|
|
|
|
end;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
procedure SlopeLimiting(Table: pwordarray; nEntries: integer);
|
|
var
|
|
At: integer;
|
|
Val, Slope: double;
|
|
i: integer;
|
|
begin
|
|
At := floor(nEntries * 0.02 + 0.5);
|
|
|
|
Val := Table[At];
|
|
Slope := Val / At;
|
|
|
|
for i := 0 to At - 1 do
|
|
Table[i] := floor(i * Slope + 0.5);
|
|
|
|
end;
|
|
|
|
const
|
|
PRELINEARIZATION_POINTS = 4096;
|
|
|
|
procedure _cmsComputePrelinearizationTablesFromXFORM(h: PcmsHTRANSFORM; nTransforms: integer; Grid: LPLUT);
|
|
var
|
|
Trans: array[0..MAXCHANNELS - 1] of LPGAMMATABLE;
|
|
t, i, v: dword;
|
|
j: integer;
|
|
xIn, xOut: array[0..MAXCHANNELS - 1] of WORD;
|
|
lIsSuitable: longBOOL;
|
|
hh: PcmsHTRANSFORM;
|
|
|
|
begin
|
|
|
|
hh := h;
|
|
|
|
for t := 0 to Grid^.InputChan - 1 do
|
|
Trans[t] := cmsAllocGamma(PRELINEARIZATION_POINTS);
|
|
|
|
for i := 0 to PRELINEARIZATION_POINTS - 1 do
|
|
begin
|
|
|
|
h := hh;
|
|
|
|
v := _cmsQuantizeVal(i, PRELINEARIZATION_POINTS);
|
|
|
|
for t := 0 to Grid^.InputChan - 1 do
|
|
xIn[t] := v;
|
|
|
|
IEcmsDoTransform(h^, @xIn, @xOut, 1);
|
|
inc(h);
|
|
for j := 1 to nTransforms - 1 do
|
|
begin
|
|
IEcmsDoTransform(h^, @xOut, @xOut, 1);
|
|
inc(h);
|
|
end;
|
|
|
|
for t := 0 to Grid^.InputChan - 1 do
|
|
Trans[t]^.GammaTable[i] := xOut[t];
|
|
|
|
end;
|
|
|
|
lIsSuitable := TRUE;
|
|
t := 0;
|
|
while (lIsSuitable and (t < Grid^.InputChan)) do
|
|
begin
|
|
|
|
if (MostlyLinear(@Trans[t]^.GammaTable, PRELINEARIZATION_POINTS)) <> 0 then
|
|
lIsSuitable := FALSE;
|
|
|
|
if (not IsMonotonic(Trans[t])) then
|
|
lIsSuitable := FALSE;
|
|
|
|
inc(t);
|
|
|
|
end;
|
|
|
|
if (lIsSuitable) then
|
|
begin
|
|
|
|
for t := 0 to Grid^.InputChan - 1 do
|
|
SlopeLimiting(@Trans[t]^.GammaTable, Trans[t]^.nEntries);
|
|
end;
|
|
|
|
if (lIsSuitable) then
|
|
cmsAllocLinearTable(Grid, @Trans, 1);
|
|
|
|
for t := 0 to Grid^.InputChan - 1 do
|
|
cmsFreeGamma(Trans[t]);
|
|
|
|
end;
|
|
|
|
function XFormSampler(xIn: pwordarray; xOut: pwordarray; Cargo: pointer): integer;
|
|
begin
|
|
IEcmsDoTransform(cmsHTRANSFORM(Cargo), xIn, xOut, 1);
|
|
result := 1;
|
|
end;
|
|
|
|
function _cmsPrecalculateDeviceLink(h: cmsHTRANSFORM; dwFlags: DWORD): LPLUT;
|
|
var
|
|
p: _LPcmsTRANSFORM;
|
|
Grid: LPLUT;
|
|
nGridPoints: integer;
|
|
dwFormatIn, dwFormatOut: DWORD;
|
|
ChannelsIn, ChannelsOut: integer;
|
|
hOne: array[0..0] of cmsHTRANSFORM;
|
|
|
|
begin
|
|
p := _LPcmsTRANSFORM(h);
|
|
|
|
ChannelsIn := _cmsChannelsOf(p^.EntryColorSpace);
|
|
ChannelsOut := _cmsChannelsOf(p^.ExitColorSpace);
|
|
|
|
if (dwFlags and $00FF0000) <> 0 then
|
|
begin
|
|
|
|
nGridPoints := (dwFlags shr 16) and $FF;
|
|
|
|
end
|
|
else
|
|
begin
|
|
|
|
nGridPoints := _cmsReasonableGridpointsByColorspace(p^.EntryColorSpace, dwFlags);
|
|
end;
|
|
|
|
Grid := cmsAllocLUT();
|
|
if (Grid = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
Grid := cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut);
|
|
|
|
dwFormatIn := ((ChannelsIn shl 3) or 2);
|
|
dwFormatOut := ((ChannelsOut shl 3) or 2);
|
|
|
|
p^.FromInput := _cmsIdentifyInputFormat(p, dwFormatIn);
|
|
p^.ToOutput := _cmsIdentifyOutputFormat(p, dwFormatOut);
|
|
|
|
if (p^.EntryColorSpace = icSigRgbData) and
|
|
(p^.ExitColorSpace = icSigRgbData) and
|
|
((dwFlags and cmsFLAGS_NOPRELINEARIZATION) = 0) then
|
|
begin
|
|
|
|
hOne[0] := h;
|
|
|
|
_cmsComputePrelinearizationTablesFromXFORM(@hOne, 1, Grid);
|
|
end;
|
|
|
|
if (not cmsSample3DGrid(Grid, XFormSampler, p, Grid^.wFlags)) then
|
|
begin
|
|
|
|
cmsFreeLUT(Grid);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
result := Grid;
|
|
end;
|
|
|
|
procedure cmsTetrahedralInterp8(Input: pwordarray;
|
|
Output: pwordarray;
|
|
LutTable: pwordarray;
|
|
p: LPL16PARAMS);
|
|
var
|
|
r, g, b: integer;
|
|
rx, ry, rz: Fixed32;
|
|
c1, c2, c3, Rest: Fixed32;
|
|
OutChan: integer;
|
|
X0, X1, Y0, Y1, Z0, Z1: Fixed32;
|
|
TotalOut: integer;
|
|
p8: LPL8PARAMS;
|
|
|
|
function DENS(i, j, k: Fixed32): Fixed32;
|
|
begin
|
|
result := (LutTable[(i) + (j) + (k) + OutChan])
|
|
end;
|
|
|
|
begin
|
|
|
|
TotalOut := p^.nOutputs;
|
|
p8 := p^.p8;
|
|
|
|
r := Input[0] shr 8;
|
|
g := Input[1] shr 8;
|
|
b := Input[2] shr 8;
|
|
|
|
X1 := p8^.X0[r];
|
|
X0 := X1;
|
|
Y1 := p8^.Y0[g];
|
|
Y0 := Y1;
|
|
Z1 := p8^.Z0[b];
|
|
Z0 := Z1;
|
|
|
|
if (r <> 255) then
|
|
X1 := X1 + p^.opta3;
|
|
|
|
if (g <> 255) then
|
|
Y1 := Y1 + p^.opta2;
|
|
|
|
if (b <> 255) then
|
|
Z1 := Z1 + p^.opta1;
|
|
|
|
rx := p8^.rx[r];
|
|
ry := p8^.ry[g];
|
|
rz := p8^.rz[b];
|
|
|
|
for OutChan := 0 to TotalOut - 1 do
|
|
begin
|
|
|
|
if (rx >= ry) and (ry >= rz) then
|
|
begin
|
|
|
|
c1 := DENS(X1, Y0, Z0) - DENS(X0, Y0, Z0);
|
|
c2 := DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);
|
|
c3 := DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
|
|
|
|
end
|
|
else
|
|
if (rx >= rz) and (rz >= ry) then
|
|
begin
|
|
|
|
c1 := DENS(X1, Y0, Z0) - DENS(X0, Y0, Z0);
|
|
c2 := DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
|
|
c3 := DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);
|
|
|
|
end
|
|
else
|
|
if (rz >= rx) and (rx >= ry) then
|
|
begin
|
|
|
|
c1 := DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);
|
|
c2 := DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
|
|
c3 := DENS(X0, Y0, Z1) - DENS(X0, Y0, Z0);
|
|
|
|
end
|
|
else
|
|
if (ry >= rx) and (rx >= rz) then
|
|
begin
|
|
|
|
c1 := DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);
|
|
c2 := DENS(X0, Y1, Z0) - DENS(X0, Y0, Z0);
|
|
c3 := DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
|
|
|
|
end
|
|
else
|
|
if (ry >= rz) and (rz >= rx) then
|
|
begin
|
|
|
|
c1 := DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
|
|
c2 := DENS(X0, Y1, Z0) - DENS(X0, Y0, Z0);
|
|
c3 := DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);
|
|
|
|
end
|
|
else
|
|
if (rz >= ry) and (ry >= rx) then
|
|
begin
|
|
|
|
c1 := DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
|
|
c2 := DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);
|
|
c3 := DENS(X0, Y0, Z1) - DENS(X0, Y0, Z0);
|
|
|
|
end
|
|
else
|
|
begin
|
|
c3 := 0;
|
|
c2 := c3;
|
|
c1 := c2;
|
|
|
|
end;
|
|
|
|
Rest := c1 * rx + c2 * ry + c3 * rz;
|
|
|
|
Output[OutChan] := (DENS(X0, Y0, Z0) + ((ToFixedDomain(Rest)) + $8000) shr 16);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
function _cmsBlessLUT8(Lut: LPLUT): LPLUT;
|
|
var
|
|
i, j: integer;
|
|
StageABC: array[0..2] of WORD;
|
|
v1, v2, v3: Fixed32;
|
|
p8: LPL8PARAMS;
|
|
p: LPL16PARAMS;
|
|
begin
|
|
p := @Lut^.CLut16params;
|
|
|
|
getmem(p8, sizeof(L8PARAMS));
|
|
if (p8 = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
for i := 0 to 255 do
|
|
begin
|
|
|
|
StageABC[2] := RGB_8_TO_16(i);
|
|
StageABC[1] := StageABC[2];
|
|
StageABC[0] := StageABC[1];
|
|
|
|
if (Lut^.wFlags and LUT_HASTL1) <> 0 then
|
|
begin
|
|
|
|
for j := 0 to 2 do
|
|
StageABC[i] := cmsLinearInterpLUT16(StageABC[i],
|
|
pwordarray(Lut^.L1[i]),
|
|
@Lut^.In16params);
|
|
Lut^.wFlags := Lut^.wFlags and (not LUT_HASTL1);
|
|
end;
|
|
|
|
v1 := ToFixedDomain(StageABC[0] * p^.Domain);
|
|
v2 := ToFixedDomain(StageABC[1] * p^.Domain);
|
|
v3 := ToFixedDomain(StageABC[2] * p^.Domain);
|
|
|
|
p8^.X0[i] := p^.opta3 * FIXED_TO_INT(v1);
|
|
p8^.Y0[i] := p^.opta2 * FIXED_TO_INT(v2);
|
|
p8^.Z0[i] := p^.opta1 * FIXED_TO_INT(v3);
|
|
|
|
p8^.rx[i] := FIXED_REST_TO_INT(v1);
|
|
p8^.ry[i] := FIXED_REST_TO_INT(v2);
|
|
p8^.rz[i] := FIXED_REST_TO_INT(v3);
|
|
|
|
end;
|
|
|
|
Lut^.CLut16params.p8 := p8;
|
|
Lut^.CLut16params.Interp3D := cmsTetrahedralInterp8;
|
|
|
|
result := Lut;
|
|
|
|
end;
|
|
|
|
// Create a transform.
|
|
|
|
function cmsCreateProofingTransform(InputProfile: cmsHPROFILE; InputFormat: DWORD; OutputProfile: cmsHPROFILE; OutputFormat: DWORD; ProofingProfile: cmsHPROFILE; nIntent: integer; ProofingIntent: integer; dwFlags: DWORD): cmsHTRANSFORM;
|
|
var
|
|
p: _LPcmsTRANSFORM;
|
|
FromTag: icTagSignature;
|
|
ToTag: icTagSignature;
|
|
DeviceLink: LPLUT;
|
|
begin
|
|
|
|
if (nIntent < 0) or (nIntent > 3) or (ProofingIntent < 0) or (ProofingIntent > 3) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: intent mismatch");
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p := AllocEmptyTransform;
|
|
if (p = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.xform := NormalXFORM;
|
|
p^.Intent := nIntent;
|
|
p^.ProofIntent := ProofingIntent;
|
|
p^.DoGamutCheck := 0;
|
|
p^.InputProfile := InputProfile;
|
|
p^.OutputProfile := OutputProfile;
|
|
p^.PreviewProfile := ProofingProfile;
|
|
p^.InputFormat := InputFormat;
|
|
p^.OutputFormat := OutputFormat;
|
|
p^.lOutputV4Lab := false;
|
|
p^.lInputV4Lab := p^.lOutputV4Lab;
|
|
|
|
p^.FromInput := _cmsIdentifyInputFormat(p, InputFormat);
|
|
p^.ToOutput := _cmsIdentifyOutputFormat(p, OutputFormat);
|
|
|
|
if (((dwFlags and cmsFLAGS_NULLTRANSFORM) <> 0) or ((InputProfile = nil) and (OutputProfile = nil))) then
|
|
begin
|
|
p^.xform := NullXFORM;
|
|
result := cmsHTRANSFORM(p);
|
|
exit;
|
|
end;
|
|
|
|
if (InputProfile = nil) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Input profile cannot be NULL!");
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (cmsGetDeviceClass(InputProfile) = icSigLinkClass) then
|
|
begin
|
|
|
|
result := CreateDeviceLinkTransform(p, dwFlags);
|
|
exit;
|
|
end;
|
|
|
|
if (not IsProperColorSpace(InputProfile, InputFormat, FALSE)) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Input profile is operating on wrong colorspace");
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.EntryColorSpace := cmsGetColorSpace(InputProfile);
|
|
|
|
if (cmsGetDeviceClass(InputProfile) = icSigNamedColorClass) then
|
|
begin
|
|
|
|
if (p^.NamedColorList = nil) then
|
|
p^.NamedColorList := cmsAllocNamedColorList(0);
|
|
|
|
cmsReadICCnamedColorList(p, InputProfile, icSigNamedColor2Tag);
|
|
|
|
if (OutputProfile = nil) then
|
|
begin
|
|
|
|
p^.ExitColorSpace := p^.EntryColorSpace;
|
|
p^.xform := @NC2deviceXform;
|
|
result := cmsHTRANSFORM(p);
|
|
exit;
|
|
end;
|
|
|
|
dwFlags := dwFlags or cmsFLAGS_NOTPRECALC;
|
|
end;
|
|
|
|
if (OutputProfile = nil) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Output profile cannot be NULL!");
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
if (not IsProperColorSpace(OutputProfile, OutputFormat, FALSE)) then
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Output profile is operating on wrong colorspace");
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.ExitColorSpace := cmsGetColorSpace(OutputProfile);
|
|
|
|
if (cmsGetDeviceClass(OutputProfile) = icSigNamedColorClass) then
|
|
begin
|
|
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Named color profiles are not supported as output");
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
p^.Phase1 := GetPhase(InputProfile);
|
|
p^.Phase2 := -1;
|
|
p^.Phase3 := GetPhase(OutputProfile);
|
|
|
|
FromTag := Device2PCS[nIntent];
|
|
ToTag := PCS2Device[nIntent];
|
|
|
|
if (not cmsIsTag(InputProfile, FromTag)) then
|
|
begin
|
|
FromTag := Device2PCS[0];
|
|
if (not cmsIsTag(InputProfile, FromTag)) then
|
|
begin
|
|
FromTag := icTagSignature(0);
|
|
end;
|
|
end;
|
|
|
|
if (ProofingProfile <> nil) then
|
|
CreateProof(p, dwFlags, @ToTag);
|
|
|
|
if (not cmsIsTag(OutputProfile, ToTag)) then
|
|
begin
|
|
|
|
ToTag := PCS2Device[0];
|
|
|
|
if (cmsGetDeviceClass(OutputProfile) = icSigAbstractClass) then
|
|
begin
|
|
|
|
if (not cmsIsTag(OutputProfile, ToTag)) then
|
|
begin
|
|
ToTag := icTagSignature(icSigAToB0Tag);
|
|
end;
|
|
end;
|
|
if (not cmsIsTag(OutputProfile, ToTag)) then
|
|
ToTag := icTagSignature(0);
|
|
end;
|
|
|
|
if (dwFlags and cmsFLAGS_MATRIXINPUT) <> 0 then
|
|
FromTag := icTagSignature(0);
|
|
|
|
if (dwFlags and cmsFLAGS_MATRIXOUTPUT) <> 0 then
|
|
ToTag := icTagSignature(0);
|
|
|
|
if (dwFlags and cmsFLAGS_GAMUTCHECK) <> 0 then
|
|
begin
|
|
|
|
p^.DoGamutCheck := 1;
|
|
dwFlags := dwFlags or cmsFLAGS_NOTPRECALC;
|
|
end;
|
|
|
|
if (PickTransformRoutine(p, @dwFlags, @FromTag, @ToTag) = nil) then
|
|
begin
|
|
|
|
IEcmsDeleteTransform(cmsHTRANSFORM(p));
|
|
result := nil;
|
|
exit;
|
|
|
|
end;
|
|
|
|
TakeConversionRoutines(p, dwFlags and cmsFLAGS_BLACKPOINTCOMPENSATION);
|
|
|
|
if ((dwFlags and cmsFLAGS_NOTPRECALC) = 0) then
|
|
begin
|
|
|
|
DeviceLink := _cmsPrecalculateDeviceLink(cmsHTRANSFORM(p), dwFlags);
|
|
|
|
if (p^.EntryColorSpace = icSigRgbData) or (p^.EntryColorSpace = icSigCmyData) then
|
|
begin
|
|
|
|
DeviceLink^.CLut16params.Interp3D := cmsTetrahedralInterp16;
|
|
end;
|
|
|
|
if (((InputFormat and 7) = 1) and (T_CHANNELS(InputFormat) = 3)) then
|
|
begin
|
|
|
|
DeviceLink := _cmsBlessLUT8(DeviceLink);
|
|
if (DeviceLink = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
if (DeviceLink <> nil) then
|
|
begin
|
|
|
|
p^.DeviceLink := DeviceLink;
|
|
end
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "Cannot precalculate %d->%d channels transform!", T_CHANNELS(InputFormat), T_CHANNELS(OutputFormat));
|
|
IEcmsDeleteTransform(p);
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
SetPrecalculatedTransform(p, dwFlags);
|
|
|
|
end;
|
|
|
|
p^.FromInput := _cmsIdentifyInputFormat(p, InputFormat);
|
|
p^.ToOutput := _cmsIdentifyOutputFormat(p, OutputFormat);
|
|
|
|
result := p;
|
|
end;
|
|
|
|
// Wrapper por simpler non-proofing transforms.
|
|
|
|
function IEcmsCreateTransform(Input: cmsHPROFILE; InputFormat: DWORD; Output: cmsHPROFILE; OutputFormat: DWORD; Intent: integer; dwFlags: DWORD): cmsHTRANSFORM;
|
|
begin
|
|
result := cmsCreateProofingTransform(Input, InputFormat, Output, OutputFormat, nil, Intent, INTENT_ABSOLUTE_COLORIMETRIC, dwFlags);
|
|
end;
|
|
|
|
function cmsWhitePointFromTemp(TempK: integer; WhitePoint: LPcmsCIExyY): longbool;
|
|
var
|
|
x, y: double;
|
|
T, T2, T3: double;
|
|
|
|
begin
|
|
|
|
T := TempK;
|
|
T2 := T * T;
|
|
T3 := T2 * T;
|
|
|
|
if (T >= 4000) and (T <= 7000) then
|
|
begin
|
|
x := -4.6070 * (1E9 / T3) + 2.9678 * (1E6 / T2) + 0.09911 * (1E3 / T) + 0.244063;
|
|
end
|
|
else
|
|
if (T > 7000.0) and (T <= 25000.0) then
|
|
begin
|
|
x := -2.0064 * (1E9 / T3) + 1.9018 * (1E6 / T2) + 0.24748 * (1E3 / T) + 0.237040;
|
|
end
|
|
else
|
|
begin
|
|
//cmsSignalError(LCMS_ERRC_ABORTED, "cmsWhitePointFromTemp: invalid temp");
|
|
result := FALSE;
|
|
exit;
|
|
end;
|
|
|
|
y := -3.000 * (x * x) + 2.870 * x - 0.275;
|
|
|
|
WhitePoint^.x := x;
|
|
WhitePoint^.y_mi := y;
|
|
WhitePoint^.Y_ma := 1.0;
|
|
|
|
result := TRUE;
|
|
end;
|
|
|
|
function IEcmsWhitePointFromTemp(TempK: integer; var WhitePoint_x, WhitePoint_y, WhitePoint_Y_: double): boolean;
|
|
var
|
|
xyY: cmsCIExyY;
|
|
begin
|
|
result := cmsWhitePointFromTemp(tempK, @xyY);
|
|
WhitePoint_x := xyY.x;
|
|
WhitePoint_y := xyY.y_mi;
|
|
WhitePoint_Y_ := xyY.Y_ma;
|
|
end;
|
|
|
|
function Build_sRGBGamma: LPGAMMATABLE;
|
|
var
|
|
Parameters: array[0..4] of double;
|
|
begin
|
|
Parameters[0] := 2.4;
|
|
Parameters[1] := 1. / 1.055;
|
|
Parameters[2] := 0.055 / 1.055;
|
|
Parameters[3] := 1. / 12.92;
|
|
Parameters[4] := 0.04045;
|
|
|
|
result := cmsBuildParametricGamma(1024, 4, @Parameters);
|
|
end;
|
|
|
|
function IEcmsCreate_sRGBProfile: cmsHPROFILE;
|
|
const
|
|
Rec709Primaries: cmsCIExyYTRIPLE = (
|
|
Red: (x: 0.6400; y_mi: 0.3300; Y_ma: 1.0);
|
|
Green: (x: 0.3000; y_mi: 0.6000; Y_ma: 1.0);
|
|
Blue: (x: 0.1500; y_mi: 0.0600; Y_ma: 1.0)
|
|
);
|
|
|
|
var
|
|
D65: cmsCIExyY;
|
|
Gamma22: array[0..2] of LPGAMMATABLE;
|
|
hsRGB: cmsHPROFILE;
|
|
|
|
begin
|
|
|
|
cmsWhitePointFromTemp(6504, @D65);
|
|
Gamma22[2] := Build_sRGBGamma();
|
|
Gamma22[1] := Gamma22[2];
|
|
Gamma22[0] := Gamma22[1];
|
|
|
|
hsRGB := cmsCreateRGBProfile(@D65, @Rec709Primaries, @Gamma22);
|
|
cmsFreeGamma(Gamma22[0]);
|
|
|
|
cmsAddTag(hsRGB, icSigDeviceMfgDescTag, PAnsiChar('(lcms internal)'));
|
|
cmsAddTag(hsRGB, icSigDeviceModelDescTag, PAnsiChar('sRGB built-in'));
|
|
cmsAddTag(hsRGB, icSigProfileDescriptionTag, PAnsiChar('sRGB built-in'));
|
|
|
|
result := hsRGB;
|
|
end;
|
|
|
|
function IEcmsCreateXYZProfile: cmsHPROFILE;
|
|
var
|
|
hProfile: cmsHPROFILE;
|
|
Lut: LPLUT;
|
|
begin
|
|
|
|
hProfile := cmsCreateRGBProfile(cmsD50_xyY, nil, nil);
|
|
|
|
cmsSetDeviceClass(hProfile, icSigAbstractClass);
|
|
cmsSetColorSpace(hProfile, icSigXYZData);
|
|
cmsSetPCS(hProfile, icSigXYZData);
|
|
|
|
cmsAddTag(hProfile, icSigDeviceMfgDescTag, PAnsiChar('(lcms internal)'));
|
|
cmsAddTag(hProfile, icSigProfileDescriptionTag, PAnsiChar('lcms XYZ identity'));
|
|
cmsAddTag(hProfile, icSigDeviceModelDescTag, PAnsiChar('XYZ built-in'));
|
|
|
|
Lut := Create3x3EmptyLUT;
|
|
if (Lut = nil) then
|
|
begin
|
|
result := nil;
|
|
exit;
|
|
end;
|
|
|
|
cmsAddTag(hProfile, icSigAToB0Tag, Lut);
|
|
cmsAddTag(hProfile, icSigBToA0Tag, Lut);
|
|
cmsAddTag(hProfile, icSigPreview0Tag, Lut);
|
|
|
|
cmsFreeLUT(Lut);
|
|
|
|
result := hProfile;
|
|
end;
|
|
|
|
{$ELSE} // IEINCLUDECMS
|
|
|
|
implementation
|
|
|
|
{$ENDIF}
|
|
|
|
end.
|
|
|