ImagerColorImpl.mesa
Copyright Ó 1987, 1988, 1991, 1992 by Xerox Corporation. All rights reserved.
Michael Plass, March 8, 1993 10:52 am PST
Doug Wyatt, January 19, 1987 7:34:21 pm PST
Maureen Stone, April 28, 1987 2:22:20 pm PDT
DIRECTORY
ImagerTestProbe,
Atom,
ImagerError USING [Error, Warning],
ImagerColor,
ImagerColorPrivate,
ImagerPixel USING [PixelBuffer, PixelProc],
ImagerPixelArray USING [MaxSampleValue, PixelArray],
ImagerSample,
ImagerTransformation USING [Transformation],
Real USING [Fix, Round],
RealFns USING [Power],
RealInline USING [MCRound],
RefTab,
Rope,
SymTab USING [Fetch];
ImagerColorImpl: CEDAR PROGRAM
IMPORTS Atom, ImagerError, ImagerColorPrivate, ImagerPixelArray, Real, RealFns, RealInline, RefTab, Rope, SymTab, ImagerTestProbe
EXPORTS ImagerColor, ImagerColorPrivate, ImagerTestProbe
~ BEGIN
ColorOperatorClass: TYPE ~ ImagerColorPrivate.ColorOperatorClass;
ColorOperatorClassRep: PUBLIC TYPE ~ ImagerColorPrivate.ColorOperatorClassRep;
StructureClass: TYPE ~ REF StructureClassRep;
StructureClassRep: PUBLIC TYPE ~ ImagerColorPrivate.StructureClassRep;
Color: TYPE ~ ImagerColor.Color;
ColorOperator: TYPE ~ ImagerColor.ColorOperator;
ColorRep: TYPE ~ ImagerColor.ColorRep;
ConstantColor: TYPE ~ ImagerColor.ConstantColor;
OpConstantColor: TYPE ~ ImagerColor.OpConstantColor;
RGB: TYPE ~ ImagerColor.RGB;
SampledBlack: TYPE ~ ImagerColor.SampledBlack;
SampledColor: TYPE ~ ImagerColor.SampledColor;
SampleTableProc: TYPE ~ ImagerColor.SampleTableProc;
SpecialColor: TYPE ~ ImagerColor.SpecialColor;
Structure: TYPE ~ ImagerColor.Structure;
StructureRep: TYPE ~ ImagerColor.StructureRep;
ColorOperatorCreateProc: TYPE ~ ImagerColorPrivate.ColorOperatorCreateProc;
DataEqualProc: TYPE ~ ImagerColorPrivate.DataEqualProc;
Element: TYPE ~ ImagerColorPrivate.Element;
ElementGenerator: TYPE ~ ImagerColorPrivate.ElementGenerator;
GetCreateDataProc: TYPE ~ ImagerColorPrivate.GetCreateDataProc;
GetPixelEncodingProc: TYPE ~ ImagerColorPrivate.GetPixelEncodingProc;
SampleEncoding: TYPE ~ ImagerColorPrivate.SampleEncoding;
StippleData: TYPE ~ ImagerColorPrivate.StippleData;
StippleDataRep: TYPE ~ ImagerColorPrivate.StippleDataRep;
TranslateProc: TYPE ~ ImagerColorPrivate.TranslateProc;
ApplyProc: TYPE ~ ImagerColorPrivate.ApplyProc;
TupleProc: TYPE ~ ImagerColorPrivate.TupleProc;
Function: TYPE ~ ImagerSample.Function;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
PixelBuffer: TYPE ~ ImagerPixel.PixelBuffer;
PixelProc: TYPE ~ ImagerPixel.PixelProc;
ROPE: TYPE ~ Rope.ROPE;
SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
Transformation: TYPE ~ ImagerTransformation.Transformation;
ColorTransformGenerator: TYPE ~ ImagerColorPrivate.ColorTransformGenerator;
ColorSpace: TYPE ~ ImagerColorPrivate.ColorSpace;
ColorPoint: TYPE ~ ImagerColorPrivate.ColorPoint;
PixelEncoding: TYPE ~ ImagerColorPrivate.PixelEncoding;
Named Color Registry
Find: PUBLIC PROC [name: ROPE] RETURNS [ConstantColor] ~ {
color: ConstantColor ~ NARROW[SymTab.Fetch[ImagerColorPrivate.namedColorTable, name].val];
RETURN [color]
};
Common Support
ClipRange: PROC [x, max: REAL] RETURNS [REAL] ~ INLINE {
RETURN [IF x <= 0.0 THEN 0.0 ELSE IF x > max THEN max ELSE x]
};
Clip01: PROC [x: REAL] RETURNS [REAL] ~ INLINE {
RETURN [ClipRange[x, 1.0]]
};
ClipAndRound: PROC [x, max: REAL] RETURNS [CARDINAL] ~ INLINE {
RETURN [RealInline.MCRound[ClipRange[x, max]]]
};
SampleFromReal: PROC [r: REAL, max: CARDINAL] RETURNS [CARDINAL] ~ {
RETURN [RealInline.MCRound[Clip01[r]*max]]
};
SampleEncodingFetch: PROC [sampleEncoding: ImagerColorPrivate.SampleEncoding, value: REAL] RETURNS [REAL] ~ {
RETURN [IF sampleEncoding = NIL THEN value ELSE sampleEncoding[MIN[MAX[RealInline.MCRound[value], 0], sampleEncoding.size-1]]];
};
MakePE: PROC [size: NAT ¬ 0, sampleTableProc: ImagerColor.SampleTableProc ¬ NIL] RETURNS [PixelEncoding] ~ {
se: SampleEncoding ~ ImagerColorPrivate.MakeSampleEncoding[size, sampleTableProc];
IF se = NIL THEN RETURN [NIL] ELSE {
pe: PixelEncoding ~ NEW[ImagerColor.PixelEncodingRep[1]];
pe[0] ¬ se;
RETURN [pe]
};
};
GetSE: PROC [pm: PixelEncoding] RETURNS [SampleEncoding] ~ {
IF pm = NIL THEN RETURN [NIL];
IF pm.samplesPerPixel # 1 THEN ERROR;
RETURN [pm[0]]
};
GrayLinear ColorOperator
DataGrayLinear: TYPE ~ REF DataGrayLinearRep;
DataGrayLinearRep: TYPE ~ RECORD [sWhite, sBlack: REAL, pe: PixelEncoding];
classGrayLinear: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/GrayLinear",
createColorOperator: CreateColorOperatorGrayLinear,
getCreateData: GetCreateDataGrayLinear,
getPixelEncoding: GetPixelEncodingGrayLinear,
apply: ApplyGrayLinear,
dataEqual: DataEqualGrayLinear,
supportedOutputs: LIST[$Y]
];
CreateColorOperatorGrayLinear: ColorOperatorCreateProc ~ {
size: INT ~ ImagerColorPrivate.SCheckSize[structure, 3];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
mapStructure: Structure ~ ImagerColorPrivate.SGetStructure[structure, 2];
sampleTable: PROC [i: NAT] RETURNS [REAL] ~ {RETURN [ImagerColorPrivate.SGetReal[mapStructure, i]]};
colorOperator: ColorOperator ~ NewColorOperatorGrayLinear[sWhite: sWhite, sBlack: sBlack, sampleTableSize: ImagerColorPrivate.SSize[mapStructure], sampleTableProc: sampleTable];
RETURN [colorOperator]
};
GetCreateDataGrayLinear: GetCreateDataProc ~ {
data: DataGrayLinear ~ NARROW[colorOperator.data];
mapStructure: Structure ~ ImagerColorPrivate.StructureFromSampleEncoding[GetSE[data.pe]];
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.sWhite]]];
elementAction[[real[data.sBlack]]];
elementAction[[structure[mapStructure]]];
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
GetPixelEncodingGrayLinear: GetPixelEncodingProc ~ {
data: DataGrayLinear ~ NARROW[colorOperator.data];
RETURN [data.pe]
};
ApplyGrayLinear: ApplyProc ~ {
data: DataGrayLinear ~ NARROW[colorOperator.data];
s: REAL ~ pixelIn[0];
f: REAL ~ (s-data.sWhite)/(data.sBlack-data.sWhite);
x: REAL ~ IF f<=0 THEN 1 ELSE IF f>=1 THEN 0 ELSE 1-f;
out[0] ¬ x;
};
DataEqualGrayLinear: DataEqualProc ~ {
a: DataGrayLinear ~ NARROW[selfData];
b: DataGrayLinear ~ NARROW[otherData];
RETURN [a.sWhite = b.sWhite AND a.sBlack = b.sBlack AND ImagerColorPrivate.PixelEncodingEqual[a.pe, b.pe]]
};
NewColorOperatorGrayLinear: PUBLIC PROC [sWhite, sBlack: REAL, sampleTableSize: NAT ¬ 0, sampleTableProc: SampleTableProc ¬ NIL] RETURNS [ColorOperator] ~ {
data: DataGrayLinear ~ NEW[DataGrayLinearRep ¬ [
sWhite: sWhite,
sBlack: sBlack,
pe: MakePE[sampleTableSize, sampleTableProc]
]];
RETURN[ImagerColorPrivate.NewColorOperator[chromatic: FALSE, samplesPerPixelIn: 1, class: classGrayLinear, data: data, appearanceHints: NIL]];
};
makeGrayUnit: NAT ~ 1000;
makeGrayLinear: ColorOperator ~ NewColorOperatorGrayLinear[0.0, REAL[makeGrayUnit]];
ColorFromGray: PUBLIC PROC [f: REAL] RETURNS [OpConstantColor] ~ {
color: OpConstantColor ~ NEW[ColorRep.constant.op[1]];
color.colorOperator ¬ makeGrayLinear;
color.pixel[0] ¬ SampleFromReal[f, makeGrayUnit];
RETURN [color];
};
GrayDensity ColorOperator
DataGrayDensity: TYPE ~ REF DataGrayDensityRep;
DataGrayDensityRep: TYPE ~ RECORD [sWhite, sBlack, dBlack: REAL, pe: PixelEncoding];
classGrayDensity: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/GrayDensity",
createColorOperator: CreateColorOperatorGrayDensity,
getCreateData: GetCreateDataGrayDensity,
getPixelEncoding: GetPixelEncodingGrayDensity,
apply: ApplyGrayDensity,
dataEqual: DataEqualGrayDensity,
supportedOutputs: LIST[$Y]
];
CreateColorOperatorGrayDensity: ColorOperatorCreateProc ~ {
size: INT ~ ImagerColorPrivate.SCheckSize[structure, 4];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
dBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 2];
mapStructure: Structure ~ ImagerColorPrivate.SGetStructure[structure, 3];
sampleTable: PROC [i: NAT] RETURNS [REAL] ~ {RETURN [ImagerColorPrivate.SGetReal[mapStructure, i]]};
colorOperator: ColorOperator ~ NewColorOperatorGrayDensity[sWhite: sWhite, sBlack: sBlack, dBlack: dBlack, sampleTableSize: ImagerColorPrivate.SSize[mapStructure], sampleTableProc: sampleTable];
RETURN [colorOperator]
};
GetCreateDataGrayDensity: GetCreateDataProc ~ {
data: DataGrayDensity ~ NARROW[colorOperator.data];
mapStructure: Structure ~ ImagerColorPrivate.StructureFromSampleEncoding[GetSE[data.pe]];
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.sWhite]]];
elementAction[[real[data.sBlack]]];
elementAction[[real[data.dBlack]]];
elementAction[[structure[mapStructure]]];
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
GetPixelEncodingGrayDensity: GetPixelEncodingProc ~ {
data: DataGrayDensity ~ NARROW[colorOperator.data];
RETURN [data.pe]
};
ApplyGrayDensity: ApplyProc ~ {
data: DataGrayDensity ~ NARROW[colorOperator.data];
s: REAL ~ pixelIn[0];
d: REAL ~ ((s-data.sWhite)/(data.sBlack-data.sWhite))*data.dBlack;
f: REAL ~ RealFns.Power[base: 10, exponent: -d];
x: REAL ~ Clip01[1-f];
out[0] ¬ x;
};
DataEqualGrayDensity: DataEqualProc ~ {
a: DataGrayDensity ~ NARROW[selfData];
b: DataGrayDensity ~ NARROW[otherData];
RETURN [a.sWhite = b.sWhite AND a.sBlack = b.sBlack AND a.dBlack = b.dBlack AND ImagerColorPrivate.PixelEncodingEqual[a.pe, b.pe]]
};
NewColorOperatorGrayDensity: PUBLIC PROC [sWhite, sBlack, dBlack: REAL, sampleTableSize: NAT ¬ 0, sampleTableProc: SampleTableProc ¬ NIL] RETURNS [ColorOperator] ~ {
data: DataGrayDensity ~ NEW[DataGrayDensityRep ¬ [
sWhite: sWhite, sBlack: sBlack, dBlack: dBlack,
pe: MakePE[sampleTableSize, sampleTableProc]
]];
RETURN[ImagerColorPrivate.NewColorOperator[chromatic: FALSE, samplesPerPixelIn: 1, class: classGrayDensity, data: data, appearanceHints: NIL]];
};
GrayVisual ColorOperator
DataGrayVisual: TYPE ~ REF DataGrayVisualRep;
DataGrayVisualRep: TYPE ~ RECORD [sWhite, sBlack: REAL, pe: PixelEncoding];
classGrayVisual: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/GrayVisual",
createColorOperator: CreateColorOperatorGrayVisual,
getCreateData: GetCreateDataGrayVisual,
getPixelEncoding: GetPixelEncodingGrayVisual,
apply: ApplyGrayVisual,
dataEqual: DataEqualGrayVisual,
supportedOutputs: LIST[$Y]
];
CreateColorOperatorGrayVisual: ColorOperatorCreateProc ~ {
size: INT ~ ImagerColorPrivate.SCheckSize[structure, 3];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
mapStructure: Structure ~ ImagerColorPrivate.SGetStructure[structure, 2];
sampleTable: PROC [i: NAT] RETURNS [REAL] ~ {RETURN [ImagerColorPrivate.SGetReal[mapStructure, i]]};
colorOperator: ColorOperator ~ NewColorOperatorGrayVisual[sWhite: sWhite, sBlack: sBlack, sampleTableSize: ImagerColorPrivate.SSize[mapStructure], sampleTableProc: sampleTable];
RETURN [colorOperator]
};
GetCreateDataGrayVisual: GetCreateDataProc ~ {
data: DataGrayVisual ~ NARROW[colorOperator.data];
mapStructure: Structure ~ ImagerColorPrivate.StructureFromSampleEncoding[GetSE[data.pe]];
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.sWhite]]];
elementAction[[real[data.sBlack]]];
elementAction[[structure[mapStructure]]];
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
GetPixelEncodingGrayVisual: GetPixelEncodingProc ~ {
data: DataGrayVisual ~ NARROW[colorOperator.data];
RETURN [data.pe]
};
ApplyGrayVisual: ApplyProc ~ {
data: DataGrayVisual ~ NARROW[colorOperator.data];
s: REAL ~ pixelIn[0];
L: REAL ~ (s-data.sBlack)/(data.sWhite-data.sBlack);
Y: REAL ~ IF L<=0.08 THEN L/9.033 ELSE ((L+0.16)/1.16)**3;
out[0] ¬ Clip01[Y];
};
DataEqualGrayVisual: DataEqualProc ~ {
a: DataGrayVisual ~ NARROW[selfData];
b: DataGrayVisual ~ NARROW[otherData];
RETURN [a.sWhite = b.sWhite AND a.sBlack = b.sBlack AND ImagerColorPrivate.PixelEncodingEqual[a.pe, b.pe]]
};
NewColorOperatorGrayVisual: PUBLIC PROC [sWhite, sBlack: REAL, sampleTableSize: NAT ¬ 0, sampleTableProc: SampleTableProc ¬ NIL] RETURNS [ColorOperator] ~ {
data: DataGrayVisual ~ NEW[DataGrayVisualRep ¬ [
sWhite: sWhite, sBlack: sBlack,
pe: MakePE[sampleTableSize, sampleTableProc]
]];
RETURN[ImagerColorPrivate.NewColorOperator[chromatic: FALSE, samplesPerPixelIn: 1, class: classGrayVisual, data: data, appearanceHints: NIL]];
};
Map ColorOperator
DataMap: TYPE ~ REF DataMapRep;
DataMapRep: TYPE ~ RECORD [v: SEQUENCE size: NAT OF ConstantColor];
classMapChromatic: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/Map",
createColorOperator: CreateColorOperatorMap,
getCreateData: GetCreateDataMap,
getPixelEncoding: GetIdentityPixelEncoding,
apply: ApplyMap,
dataEqual: DataEqualMap,
supportedOutputs: LIST[$Y, $RGB]
];
classMapMonochrome: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/Map",
createColorOperator: CreateColorOperatorMap,
getCreateData: GetCreateDataMap,
getPixelEncoding: GetIdentityPixelEncoding,
apply: ApplyMap,
dataEqual: DataEqualMap,
supportedOutputs: LIST[$Y]
];
CreateColorOperatorMap: ColorOperatorCreateProc ~ {
size: INT ~ ImagerColorPrivate.SSize[structure];
map: PROC [i: NAT] RETURNS [ConstantColor] ~ {
color: Color ~ ImagerColorPrivate.SGetColor[structure, i];
WITH color SELECT FROM
constantColor: ConstantColor => RETURN [constantColor];
ENDCASE => ERROR ImagerError.Error[error: [$wrongType, "Wrong type (expected ConstantColor)", LIST[[$Color, color]]]];
};
colorOperator: ColorOperator ~ NewColorOperatorMap[maxSampleValue: size-1, map: map];
RETURN [colorOperator]
};
GetCreateDataMap: GetCreateDataProc ~ {
data: DataMap ~ NARROW[colorOperator.data];
structure: Structure ~ StructureFromDataMap[data];
RETURN [structure]
};
GetIdentityPixelEncoding: GetPixelEncodingProc ~ { RETURN [NIL] };
ApplyMap: ApplyProc ~ {
data: DataMap ~ NARROW[colorOperator.data];
color: OpConstantColor ~ NarrowToOpConstantColor[data[MIN[MAX[Real.Round[pixelIn[0]], 0], data.size-1]]];
SELECT colorSpace FROM
$Y => out[0] ¬ ImagerColorPrivate.IntensityFromColor[color];
$RGB => [[out[0], out[1], out[2]]] ¬ RGBFromColor[color];
ENDCASE => ERROR;
};
DataEqualMap: DataEqualProc ~ {
a: DataMap ~ NARROW[selfData];
b: DataMap ~ NARROW[otherData];
IF a=b THEN RETURN [TRUE];
IF a.size # b.size THEN RETURN [FALSE];
FOR i: NAT IN [0..a.size) DO
IF NOT ImagerColorPrivate.ConstantColorsEqual[a[i], b[i]] THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE];
};
DataMapSize: PROC [structure: Structure] RETURNS [INT] ~ {
dataMap: DataMap ~ NARROW[structure.data];
RETURN [dataMap.size]
};
DataMapGet: PROC [structure: Structure, i: INT] RETURNS [Element] ~ {
dataMap: DataMap ~ NARROW[structure.data];
RETURN [[color[dataMap[i]]]]
};
dataMapStructureClass: StructureClass ~ NEW[StructureClassRep ¬ [size: DataMapSize, get: DataMapGet]];
StructureFromDataMap: PUBLIC PROC [dataMap: DataMap] RETURNS [Structure] ~ {
structure: Structure ~ NEW[ImagerColor.StructureRep ¬ [class: dataMapStructureClass, data: dataMap]];
RETURN [structure]
};
NewColorOperatorMap: PUBLIC PROC [maxSampleValue: NAT, map: PROC [NAT] RETURNS [ConstantColor]] RETURNS [ColorOperator] ~ {
data: DataMap ~ NEW[DataMapRep[maxSampleValue+1]];
chromatic: BOOL ¬ FALSE;
FOR s0: NAT IN [0..maxSampleValue] DO
color: ConstantColor ~ map[s0];
op: OpConstantColor ~ NarrowToOpConstantColor[color];
data.v[s0] ¬ color;
chromatic ¬ chromatic OR op.colorOperator.chromatic;
ENDLOOP;
RETURN[ImagerColorPrivate.NewColorOperator[chromatic: chromatic, samplesPerPixelIn: 1, class: IF chromatic THEN classMapChromatic ELSE classMapMonochrome, data: data, appearanceHints: NIL]];
};
BuildMap ColorOperator
DataBuildMap: TYPE ~ REF DataBuildMapRep;
DataBuildMapRep: TYPE ~ RECORD [
v: SampleEncoding,
colorOperator: ColorOperator
];
classBuildMapTable: RefTab.Ref ~ RefTab.Create[];
ClassBuildMap: PROC [supportedOutputs: LIST OF ColorSpace] RETURNS [classBuildMap: ColorOperatorClass] ~ {
Action: RefTab.UpdateAction ~ {
IF found
THEN { classBuildMap ¬ NARROW[val] }
ELSE {
classBuildMap ¬ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/BuildMap",
createColorOperator: CreateColorOperatorBuildMap,
getCreateData: GetCreateDataBuildMap,
getPixelEncoding: GetIdentityPixelEncoding,
apply: ApplyBuildMap,
dataEqual: DataEqualBuildMap,
supportedOutputs: supportedOutputs
];
RETURN [store, classBuildMap]
};
};
RefTab.Update[classBuildMapTable, supportedOutputs, Action];
};
CreateColorOperatorBuildMap: ColorOperatorCreateProc ~ {
size: INT ~ ImagerColorPrivate.SCheckSize[structure, 2];
v: Structure ~ ImagerColorPrivate.SGetStructure[structure, 0];
baseColorOperator: ColorOperator ~ ImagerColorPrivate.SGetColorOperator[structure, 1];
map: PROC [i: NAT] RETURNS [REAL] ~ { RETURN [ImagerColorPrivate.SGetReal[v, i]] };
colorOperator: ColorOperator ~ NewColorOperatorBuildMap[colorOperator: baseColorOperator, maxSampleValue: ImagerColorPrivate.SSize[v]-1, map: map];
RETURN [colorOperator]
};
GetCreateDataBuildMap: GetCreateDataProc ~ {
data: DataBuildMap ~ NARROW[colorOperator.data];
MapElements: ElementGenerator ~ { FOR i: NAT IN [0..data.v.size) DO elementAction[[real[data.v[i]]]] ENDLOOP };
mapStructure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[MapElements];
GenerateElements: ElementGenerator ~ {
elementAction[[structure[mapStructure]]];
elementAction[[colorOperator[data.colorOperator]]];
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
ApplyBuildMap: ApplyProc ~ {
data: DataBuildMap ~ NARROW[colorOperator.data];
class: ColorOperatorClass ~ data.colorOperator.class;
pe: PixelEncoding ~ ImagerColorPrivate.GetPixelEncoding[data.colorOperator];
PixelMapped: TupleProc ~ {
check: [0..1) ~ i;
s0: REAL ~ pixelIn[0];
s: REAL ~ SampleEncodingFetch[data.v, s0];
t: REAL ~ ImagerColorPrivate.ApplyPixelEncoding[pe, i, s];
RETURN [t];
};
class.apply[data.colorOperator, PixelMapped, colorSpace, out];
};
DataEqualBuildMap: DataEqualProc ~ {
a: DataBuildMap ~ NARROW[selfData];
b: DataBuildMap ~ NARROW[otherData];
IF a.colorOperator # b.colorOperator THEN RETURN [FALSE];
IF a.v.size # b.v.size THEN RETURN [FALSE];
FOR i: NAT IN [0..a.v.size) DO IF a.v[i] # b.v[i] THEN RETURN [FALSE] ENDLOOP;
RETURN [TRUE];
};
NewColorOperatorBuildMap: PUBLIC PROC [colorOperator: ColorOperator, maxSampleValue: NAT, map: PROC [NAT] RETURNS [REAL]] RETURNS [ColorOperator] ~ {
data: DataBuildMap ~ NEW[DataBuildMapRep];
data.v ¬ ImagerColorPrivate.MakeSampleEncoding[size: maxSampleValue+1, sampleTableProc: map];
data.colorOperator ¬ colorOperator;
RETURN ImagerColorPrivate.NewColorOperator[chromatic: colorOperator.chromatic, samplesPerPixelIn: 1, class: ClassBuildMap[colorOperator.class.supportedOutputs], data: data, appearanceHints: NIL];
};
RGB ColorOperator
DataRGB: TYPE ~ REF DataRGBRep;
DataRGBRep: TYPE ~ RECORD [
sWhite: REAL,
sBlack: REAL,
map: PixelEncoding
];
classRGB: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/RGBLinear",
createColorOperator: CreateColorOperatorRGB,
getCreateData: GetCreateDataRGB,
getPixelEncoding: GetPixelEncodingRGB,
apply: ApplyRGB,
dataEqual: DataEqualRGB,
supportedOutputs: LIST[$Y, $RGB]
];
CreateColorOperatorRGB: ColorOperatorCreateProc ~ {
consistencyTest: INT ~ ImagerColorPrivate.SCheckSize[structure, 4];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
pixelEncodingStructure: Structure ~ ImagerColorPrivate.SGetOptionalStructure[structure, 2, 3];
map: PixelEncoding ~ ImagerColorPrivate.PixelEncodingFromStructure[pixelEncodingStructure];
appearanceHints: Structure ~ ImagerColorPrivate.SGetStructure[structure, 3];
colorOperator: ColorOperator ~ NewColorOperatorRGB[sWhite: sWhite, sBlack: sBlack, map: map, appearanceHints: appearanceHints];
RETURN [colorOperator]
};
GetCreateDataRGB: GetCreateDataProc ~ {
data: DataRGB ~ NARROW[colorOperator.data];
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.sWhite]]];
elementAction[[real[data.sBlack]]];
IF (data.map = NIL)
THEN { elementAction[[int[0]]] }
ELSE {
elementAction[[structure[ImagerColorPrivate.StructureFromPixelEncoding[data.map]]]]
};
IF (colorOperator.appearanceHints = NIL) THEN elementAction[[int[0]]]
ELSE elementAction[[structure[colorOperator.appearanceHints]]]
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
GetPixelEncodingRGB: GetPixelEncodingProc ~ {
data: DataRGB ~ NARROW[colorOperator.data];
RETURN [data.map]
};
ApplyRGB: ApplyProc ~ {
data: DataRGB ~ NARROW[colorOperator.data];
sWhite: REAL ~ data.sWhite;
sBlack: REAL ~ data.sBlack;
d: REAL ~ 1.0 / (sWhite - sBlack);
pm: PixelEncoding ~ data.map;
x: ARRAY [0..3) OF REAL;
FOR i: [0..3) IN [0..3) DO
x[i] ¬ (pixelIn[i] - sBlack) * d;
ENDLOOP;
SELECT colorSpace FROM
$RGB => FOR i: NAT IN [0..3) DO out[i] ¬ x[i] ENDLOOP;
$Y => {
Note: Prior to Cedar10.0, we used coefficients of 0.30, 0.59, 0.11
out[0] ¬ Clip01[0.253*x[0]+0.684*x[1]+0.063*x[2]];
};
ENDCASE => ERROR;
};
DataEqualRGB: DataEqualProc ~ {
a: DataRGB ~ NARROW[selfData];
b: DataRGB ~ NARROW[otherData];
IF (a.sWhite # b.sWhite) OR (a.sBlack # b.sBlack) THEN RETURN [FALSE];
IF NOT ImagerColorPrivate.PixelEncodingEqual[a.map, b.map] THEN RETURN [FALSE];
RETURN [TRUE];
};
NewColorOperatorRGB: PUBLIC PROC [sWhite: REAL, sBlack: REAL ¬ 0, map: PixelEncoding ¬ NIL, appearanceHints: Structure ¬ NIL] RETURNS [ColorOperator] ~ {
data: DataRGB ~ NEW[DataRGBRep ¬ [sWhite: sWhite, sBlack: sBlack, map: map]];
RETURN [ImagerColorPrivate.NewColorOperator[chromatic: TRUE, samplesPerPixelIn: 3, class: classRGB, data: data, appearanceHints: appearanceHints]]
};
rgb1000: ColorOperator ~ NewColorOperatorRGB[makeGrayUnit];
ColorFromRGB: PUBLIC PROC [rgb: RGB] RETURNS [OpConstantColor] ~ {
color: OpConstantColor ~ NEW[ColorRep.constant.op[3]];
color.colorOperator ¬ rgb1000;
color.pixel[0] ¬ SampleFromReal[rgb.R, makeGrayUnit];
color.pixel[1] ¬ SampleFromReal[rgb.G, makeGrayUnit];
color.pixel[2] ¬ SampleFromReal[rgb.B, makeGrayUnit];
RETURN [color];
};
SolidColors
Note: This must get initialized AFTER the rgb1000 ColorOperator.
SolidColor: TYPE ~ { black, red, green, blue, cyan, magenta, yellow, white };
solidColors: REF ARRAY SolidColor OF ConstantColor ~ InitSolidColors[];
InitSolidColors: PROC RETURNS [REF ARRAY SolidColor OF ConstantColor] ~ {
a: REF ARRAY SolidColor OF ConstantColor ~ NEW[ARRAY SolidColor OF ConstantColor];
Def: PROC [name: ROPE, r, g, b: REAL] RETURNS [ConstantColor] ~ {
RETURN [Define[name, $Solid, ColorFromRGB[[r, g, b]]]]
};
a[black] ¬ Def["Xerox/Solid/Black", 0, 0, 0];
a[red] ¬ Def["Xerox/Solid/Red", 1, 0, 0];
a[green] ¬ Def["Xerox/Solid/Green", 0, 1, 0];
a[blue] ¬ Def["Xerox/Solid/Blue", 0, 0, 1];
a[cyan] ¬ Def["Xerox/Solid/Cyan", 0, 1, 1];
a[magenta] ¬ Def["Xerox/Solid/Magenta", 1, 0, 1];
a[yellow] ¬ Def["Xerox/Solid/Yellow", 1, 1, 0];
a[white] ¬ Def["Xerox/Solid/White", 1, 1, 1];
RETURN [a]
};
HSV
ColorFromHSV: PUBLIC PROC [H, S, V: REAL] RETURNS [OpConstantColor] ~ {
RETURN [ColorFromRGB[RGBFromHSV[H, S, V]]]
};
HSVFromColor: PUBLIC PROC [c: OpConstantColor] RETURNS [H, S, V: REAL] ~ {
RETURN HSVFromRGB[RGBFromColor[c]]
};
ToRange: PROC [v: REAL] RETURNS [REAL] = {
ensures that v is in [0..1]
RETURN[IF v<0 THEN 0 ELSE IF v>1 THEN 1 ELSE v];
};
RGBFromHSV: PROC [hue, saturation, value: REAL] RETURNS [RGB] ~ {
ihue: INT;
fhue, m, n, k: REAL;
IF value=0 OR saturation=0 THEN RETURN[[value, value, value]];
hue ¬ ToRange[hue];
saturation ¬ ToRange[saturation];
value ¬ ToRange[value];
hue ¬ hue*6;
ihue ¬ Real.Fix[hue]; --integer hue
fhue ¬ hue-ihue; --fractional hue
IF ihue=6 THEN ihue ¬ 0;
m ¬ value*(1-saturation);
n ¬ value*(1-(saturation*fhue));
k ¬ value*(1-(saturation*(1-fhue)));
SELECT ihue FROM
0 => RETURN[[value,k,m]];
1 => RETURN[[n,value,m]];
2 => RETURN[[m,value,k]];
3 => RETURN[[m,n,value]];
4 => RETURN[[k,m,value]];
5 => RETURN[[value,m,n]];
ENDCASE => RETURN[[0,0,0]];
};
HSVFromRGB: PROC [val: RGB] RETURNS [H, S, V: REAL] ~ {
r: REAL ~ ToRange[val.R];
g: REAL ~ ToRange[val.G];
b: REAL ~ ToRange[val.B];
min: REAL ~ MIN[r, g, b]; -- amount of white
max: REAL ~ MAX[r, g, b]; -- maximum "brightness"
value: REAL ~ max;
saturation: REAL ~ IF max#0 THEN (max-min)/max ELSE 0;
IF saturation = 0
THEN RETURN[0, 0, value] --gray
ELSE {
rc: REAL ~ (max - r)/(max - min);
gc: REAL ~ (max - g)/(max - min);
bc: REAL ~ (max - b)/(max - min);
hue: REAL ¬ (SELECT max FROM
r => bc-gc,
g => 2+rc-bc,
b => 4+gc-rc,
ENDCASE => ERROR)/6.0;
IF hue<0 THEN hue ¬ hue + 1;
RETURN[hue, saturation, value];
};
};
Xerox/Research/RGBLinear compatibility
researchRGBLinear: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/Research/RGBLinear",
createColorOperator: CreateColorOperatorOldRGB,
getCreateData: NIL,
getPixelEncoding: NIL,
apply: NIL,
dataEqual: NIL,
supportedOutputs: NIL
];
CreateColorOperatorOldRGB: ColorOperatorCreateProc ~ {
consistencyTest: INT ~ ImagerColorPrivate.SCheckSize[structure, 1];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
colorOperator: ColorOperator ~ NewColorOperatorRGB[sWhite: sWhite, sBlack: 0, map: NIL, appearanceHints: NIL];
RETURN [colorOperator]
};
RenderingHints
addRenderingHints: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/Research/AddRenderingHints",
createColorOperator: CreateColorOperatorWithRenderingHints,
getCreateData: NIL,
getPixelEncoding: NIL,
apply: NIL,
dataEqual: NIL,
supportedOutputs: NIL
];
CreateColorOperatorWithRenderingHints: ColorOperatorCreateProc ~ {
size: INT ~ ImagerColorPrivate.SCheckSize[structure, 2];
baseColorOperator: ColorOperator ~ ImagerColorPrivate.SGetColorOperator[structure, 0];
appearanceHints: Structure ~ ImagerColorPrivate.SGetStructure[structure, 1];
colorOperator: ColorOperator ~ NewRenderingHints[colorOperator: baseColorOperator, appearanceHints: appearanceHints];
SIGNAL ImagerError.Warning[[$obsoleteConstruct, "Obsolete color model operator", LIST[[$name, class.name]]]];
RETURN [colorOperator];
};
NewRenderingHints: PROC [colorOperator: ColorOperator, appearanceHints: Structure] RETURNS [ColorOperator] ~ {
newRenderingHints: Structure ~ ImagerColorPrivate.SMergeProp[appearanceHints, colorOperator.appearanceHints];
RETURN[ImagerColorPrivate.NewColorOperator[chromatic: colorOperator.chromatic, samplesPerPixelIn: colorOperator.samplesPerPixelIn, class: colorOperator.class, data: colorOperator.data, appearanceHints: newRenderingHints]];
};
Matrix3 Support
Matrix3: TYPE ~ REF Matrix3Rep;
Matrix3Rep: TYPE ~ ARRAY [0..3) OF ARRAY [0..3) OF REAL;
Det3: PROC [m: Matrix3] RETURNS [REAL] ~ {
a00: REAL ~ m[0][0];
a01: REAL ~ m[0][1];
a02: REAL ~ m[0][2];
a10: REAL ~ m[1][0];
a11: REAL ~ m[1][1];
a12: REAL ~ m[1][2];
a20: REAL ~ m[2][0];
a21: REAL ~ m[2][1];
a22: REAL ~ m[2][2];
RETURN [
+ a00*a11*a22 + a01*a12*a20 + a02*a10*a21
- a20*a11*a02 - a21*a12*a00 - a22*a10*a01
]
};
Cofactor3: PROC [m: Matrix3, i, j: [0..3)] RETURNS [REAL] ~ {
p: PROC [k: [0..2)] RETURNS [[0..3)] ~ INLINE {RETURN [IF k < i THEN k ELSE k+1]};
q: PROC [k: [0..2)] RETURNS [[0..3)] ~ INLINE {RETURN [IF k < j THEN k ELSE k+1]};
c: PROC [I, J: [0..2)] RETURNS [REAL] ~ INLINE {RETURN [m[p[I]][q[J]]]};
d: REAL ~ c[0, 0] * c[1, 1] - c[1, 0] * c[0, 1];
RETURN [IF (i+j) MOD 2 = 0 THEN d ELSE -d];
};
Invert3: PROC [m: Matrix3] RETURNS [Matrix3] ~ {
a: Matrix3 ¬ NEW[Matrix3Rep];
det: REAL ¬ Det3[m];
FOR i: [0..3) IN [0..3) DO
FOR j: [0..3) IN [0..3) DO
a[j][i] ¬ Cofactor3[m, i, j]/det;
ENDLOOP;
ENDLOOP;
RETURN [a];
};
Mul3: PROC [m, n: Matrix3] RETURNS [Matrix3] ~ {
a: Matrix3 ¬ NEW[Matrix3Rep];
FOR i: [0..3) IN [0..3) DO
FOR j: [0..3) IN [0..3) DO
s: REAL ¬ 0;
FOR k: [0..3) IN [0..3) DO
s ¬ s + m[i][k] * n[k][j];
ENDLOOP;
a[i][j] ¬ s;
ENDLOOP;
ENDLOOP;
RETURN [a];
};
Apply3: PROC [m: Matrix3, u: ARRAY [0..3) OF REAL] RETURNS [v: ARRAY [0..3) OF REAL] ~ {
FOR i: [0..3) IN [0..3) DO
v[i] ¬ m[i][0]*u[0] + m[i][1]*u[1] + m[i][2]*u[2];
ENDLOOP;
};
Apply3Out: PROC [m: Matrix3, u0, u1, u2: REAL, out: ColorPoint] ~ {
FOR i: [0..3) IN [0..3) DO
out[i] ¬ m[i][0]*u0 + m[i][1]*u1 + m[i][2]*u2;
ENDLOOP;
};
YESLinear
... Xerox/YESLinear (Color Encoding Standard)
YES: TYPE ~ ImagerColor.YES; -- RECORD [Y, E, S: REAL]
DataYES: TYPE ~ REF DataYESRep;
DataYESRep: TYPE ~ RECORD [
sWhite: REAL,
sBlack: REAL,
sneutral: REAL,
srange: REAL,
map: PixelEncoding
];
matYESFromRGB: Matrix3 ¬ NEW[Matrix3Rep ¬ [
[+0.253, +0.684, +0.063],
[+0.500, -0.500, +0.000],
[+0.250, +0.250, -0.500]
]];
matRGBFromYES: Matrix3 ¬ Invert3[matYESFromRGB];
classYES: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/YESLinear",
createColorOperator: CreateColorOperatorYES,
getCreateData: GetCreateDataYES,
getPixelEncoding: GetPixelEncodingYES,
apply: ApplyYES,
dataEqual: DataEqualYES,
supportedOutputs: LIST[$Y, $YES, $RGB]
];
yes1000neutral: REAL ~ 1000;
yes1000srange: REAL ~ 2000;
yes1000: ColorOperator ~ NewColorOperatorYES[sWhite: makeGrayUnit, sBlack: 0, sneutral: yes1000neutral, srange: yes1000srange];
ColorFromYES: PUBLIC PROC [yes: YES] RETURNS [OpConstantColor] ~ {
color: OpConstantColor ~ NEW[ColorRep.constant.op[3]];
maxES: REAL ~ 2*yes1000srange+yes1000neutral;
color.colorOperator ¬ yes1000;
color.pixel[0] ¬ SampleFromReal[yes.Y, makeGrayUnit];
color.pixel[1] ¬ ClipAndRound[yes.E*yes1000srange+yes1000neutral, maxES];
color.pixel[2] ¬ ClipAndRound[yes.S*yes1000srange+yes1000neutral, maxES];
RETURN [color];
};
YESFromColor: PUBLIC PROC [c: OpConstantColor] RETURNS [YES] ~ {
rgb: RGB ~ RGBFromColor[c];
yes: ARRAY [0..3) OF REAL ~ Apply3[matYESFromRGB, [rgb.R, rgb.G, rgb.B]];
RETURN [[yes[0], yes[1], yes[2]]]
};
CreateColorOperatorYES: ColorOperatorCreateProc ~ {
consistencyTest: INT ~ ImagerColorPrivate.SCheckSize[structure, 6];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
sneutral: REAL ~ ImagerColorPrivate.SGetReal[structure, 2];
srange: REAL ~ ImagerColorPrivate.SGetReal[structure, 3];
pixelEncodingStructure: Structure ~ ImagerColorPrivate.SGetOptionalStructure[structure, 4, 3];
map: PixelEncoding ~ ImagerColorPrivate.PixelEncodingFromStructure[pixelEncodingStructure];
appearanceHints: Structure ~ ImagerColorPrivate.SGetStructure[structure, 5];
colorOperator: ColorOperator ~ NewColorOperatorYES[sWhite: sWhite, sBlack: sBlack, sneutral: sneutral, srange: srange, map: map, appearanceHints: appearanceHints];
RETURN [colorOperator]
};
GetPixelEncodingYES: GetPixelEncodingProc ~ {
data: DataYES ~ NARROW[colorOperator.data];
RETURN [data.map]
};
ApplyYES: ApplyProc ~ {
data: DataYES ~ NARROW[colorOperator.data];
sWhite: REAL ~ data.sWhite;
sBlack: REAL ~ data.sBlack;
pm: PixelEncoding ~ data.map;
s: PROC [i: NAT] RETURNS [REAL] ~ INLINE { RETURN [pixelIn[i]] };
Y: REAL ~ (s[0] - sBlack) / (sWhite - sBlack);
IF colorSpace = $Y THEN { out[0] ¬ Y } ELSE {
sneutral: REAL ~ data.sneutral;
srange: REAL ~ data.srange;
E: REAL ~ (s[1] - sneutral) / (srange);
S: REAL ~ (s[2] - sneutral) / (srange);
SELECT colorSpace FROM
$YES => {
out[0] ¬ Y;
out[1] ¬ E;
out[2] ¬ S;
};
$RGB => {
Apply3Out[matRGBFromYES, Y, E, S, out];
};
ENDCASE => ERROR;
};
};
GetCreateDataYES: GetCreateDataProc ~ {
data: DataYES ~ NARROW[colorOperator.data];
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.sWhite]]];
elementAction[[real[data.sBlack]]];
elementAction[[real[data.sneutral]]];
elementAction[[real[data.srange]]];
IF (data.map = NIL)
THEN { elementAction[[int[0]]] }
ELSE {
elementAction[[structure[ImagerColorPrivate.StructureFromPixelEncoding[data.map]]]]
};
IF (colorOperator.appearanceHints = NIL) THEN elementAction[[int[0]]]
ELSE elementAction[[structure[colorOperator.appearanceHints]]]
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
DataEqualYES: DataEqualProc ~ {
a: DataYES ~ NARROW[selfData];
b: DataYES ~ NARROW[otherData];
IF (a.sWhite # b.sWhite) OR (a.sBlack # b.sBlack) OR (a.sneutral # b.sneutral) OR (a.srange # b.srange) THEN RETURN [FALSE];
IF NOT ImagerColorPrivate.PixelEncodingEqual[a.map, b.map] THEN RETURN [FALSE];
RETURN [TRUE];
};
NewColorOperatorYES: PUBLIC PROC [sWhite, sBlack, sneutral, srange: REAL, map: PixelEncoding ¬ NIL, appearanceHints: Structure ¬ NIL] RETURNS [ColorOperator] ~ {
data: DataYES ~ NEW[DataYESRep ¬ [sWhite: sWhite, sBlack: sBlack, sneutral: sneutral, srange: srange, map: map]];
RETURN [ImagerColorPrivate.NewColorOperator[chromatic: TRUE, samplesPerPixelIn: 3, class: classYES, data: data, appearanceHints: appearanceHints]]
};
CIELAB
... Xerox/CIELAB (Color Encoding Standard)
CIELAB: TYPE ~ ImagerColor.CIELAB; -- RECORD [lStar, aStar, bStar: REAL]
DataCIELAB: TYPE ~ REF DataCIELABRep;
DataCIELABRep: TYPE ~ DataYESRep; -- can share representation with YES
classCIELAB: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/CIELAB",
createColorOperator: CreateColorOperatorCIELAB,
getCreateData: GetCreateDataYES, -- same rep as YES
getPixelEncoding: GetPixelEncodingCIELAB,
apply: ApplyCIELAB,
dataEqual: DataEqualYES, -- same rep as YES
supportedOutputs: LIST[$Y, $CIELAB, $RGB]
];
matRGBFromXYZ: Matrix3 ¬ NEW[Matrix3Rep ¬ [
[+2.944, -1.461, -0.457],
[-1.095, +2.026, +0.036],
[+0.078, -0.272, +1.452]
]];
matXYZFromRGB: Matrix3 ¬ Invert3[matRGBFromXYZ];
cielab1000: ColorOperator ~ NewColorOperatorCIELAB[sWhite: 1000, sBlack: 0, sneutral: 500, srange: 10];
ColorFromCIELAB: PUBLIC PROC [cielab: CIELAB] RETURNS [OpConstantColor] ~ {
color: OpConstantColor ~ NEW[ColorRep.constant.op[3]];
color.colorOperator ¬ cielab1000;
color.pixel[0] ¬ Real.Round[cielab.lStar*10];
color.pixel[1] ¬ Real.Round[cielab.aStar*10+500];
color.pixel[2] ¬ Real.Round[cielab.bStar*10+500];
RETURN [color];
};
F: PROC [t: REAL] RETURNS [REAL] ~ {
The function f(t) defined in Color Encoding Standard section 3.1.3
RETURN [IF t > 0.008856 THEN t**(1/3.0) ELSE 7.787 * t + (16.0/116.0)];
};
G: PROC [ft: REAL] RETURNS [REAL] ~ {
The inverse of F
RETURN [IF ft > 0.2068929 THEN (ft*ft*ft) ELSE (ft-(16.0/116.0))/7.787];
};
Xn: REAL ~ 0.964;
Zn: REAL ~ 0.824;
chooseCIELABorRGBorY: LIST OF ColorSpace ~ LIST[$CIELAB, $RGB, $Y];
CIELABFromColor: PUBLIC PROC [c: OpConstantColor] RETURNS [r: CIELAB] ~ {
colorSpace: ColorSpace ~ ImagerColorPrivate.ChooseColorSpace[c.colorOperator, chooseCIELABorRGBorY];
p: ColorPoint ¬ ImagerColorPrivate.TransformConstantColor[c, colorSpace];
SELECT colorSpace FROM
$CIELAB => r ¬ [p[0], p[1], p[2]];
$RGB => {
xyz: ARRAY [0..3) OF REAL ~ Apply3[matXYZFromRGB, [p[0], p[1], p[2]]];
fY: REAL ~ F[xyz[1]];
r.lStar ¬ 116.0 * fY - 16;
r.aStar ¬ 500.0 * ( F[xyz[0]/Xn] - fY );
r.bStar ¬ 200.0 * ( fY - F[xyz[2]/Zn] );
};
$Y => {
fY: REAL ~ F[p[0]];
r.lStar ¬ 116.0 * fY - 16.0;
r.aStar ¬ 0;
r.bStar ¬ 0;
};
ENDCASE => ERROR;
p ¬ ImagerColorPrivate.DestroyColorPoint[p];
};
CreateColorOperatorCIELAB: ColorOperatorCreateProc ~ {
consistencyTest: INT ~ ImagerColorPrivate.SCheckSize[structure, 6];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
sneutral: REAL ~ ImagerColorPrivate.SGetReal[structure, 2];
srange: REAL ~ ImagerColorPrivate.SGetReal[structure, 3];
pixelEncodingStructure: Structure ~ ImagerColorPrivate.SGetOptionalStructure[structure, 4, 3];
map: PixelEncoding ~ ImagerColorPrivate.PixelEncodingFromStructure[pixelEncodingStructure];
appearanceHints: Structure ~ ImagerColorPrivate.SGetStructure[structure, 5];
colorOperator: ColorOperator ~ NewColorOperatorCIELAB[sWhite: sWhite, sBlack: sBlack, sneutral: sneutral, srange: srange, map: map, appearanceHints: appearanceHints];
RETURN [colorOperator]
};
GetPixelEncodingCIELAB: GetPixelEncodingProc ~ {
data: DataCIELAB ~ NARROW[colorOperator.data];
RETURN [data.map]
};
ApplyCIELAB: ApplyProc ~ {
data: DataCIELAB ~ NARROW[colorOperator.data];
sWhite: REAL ~ data.sWhite;
sBlack: REAL ~ data.sBlack;
pm: PixelEncoding ~ data.map;
s: PROC [i: NAT] RETURNS [REAL] ~ INLINE { RETURN [pixelIn[i]] };
Lstar01: REAL ~ Clip01[(s[0] - sBlack) / (sWhite - sBlack)];
IF colorSpace = $CIELAB
THEN {
sneutral: REAL ~ data.sneutral;
srange: REAL ~ data.srange;
out[0] ¬ Lstar01 * 100.0;
out[1] ¬ (s[1] - sneutral) / (srange);
out[2] ¬ (s[2] - sneutral) / (srange);
}
ELSE {
fY: REAL ~ (Lstar01 + 0.16) / 1.16;
Y: REAL ~ G[fY];
IF colorSpace = $Y
THEN { out[0] ¬ Y }
ELSE {
sneutral: REAL ~ data.sneutral;
srange: REAL ~ data.srange;
astar: REAL ~ (s[1] - sneutral) / (srange);
bstar: REAL ~ (s[2] - sneutral) / (srange);
X: REAL ~ G[(astar / 500.0) + fY] * Xn;
Z: REAL ~ G[fY - (bstar / 200.0)] * Zn;
SELECT colorSpace FROM
$RGB => {
Apply3Out[matRGBFromXYZ, X, Y, Z, out];
};
ENDCASE => ERROR;
};
};
};
NewColorOperatorCIELAB: PUBLIC PROC [sWhite, sBlack, sneutral, srange: REAL, map: PixelEncoding ¬ NIL, appearanceHints: Structure ¬ NIL] RETURNS [ColorOperator] ~ {
data: DataCIELAB ~ NEW[DataCIELABRep ¬ [sWhite: sWhite, sBlack: sBlack, sneutral: sneutral, srange: srange, map: map]];
RETURN [ImagerColorPrivate.NewColorOperator[chromatic: TRUE, samplesPerPixelIn: 3, class: classCIELAB, data: data, appearanceHints: appearanceHints]]
};
HighlightLinear
... Xerox/HighlightLinear (Color Encoding Standard)
Highlight: TYPE ~ ImagerColor.Highlight; -- RECORD [b, h: REAL]
DataHighlight: TYPE ~ REF DataHighlightRep;
DataHighlightRep: TYPE ~ RECORD [
sWhite: REAL,
sBlack: REAL,
map: PixelEncoding,
baseColor: ConstantColor,
base: RGB, -- computed from baseColor
baseY: REAL, -- computed from baseColor
highlightColor: ConstantColor,
highlight: RGB, -- computed from highlightColor
highlightY: REAL -- computed from highlightColor
];
classHighlightPassThru: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/HighlightLinear",
createColorOperator: CreateColorOperatorHighlight,
getCreateData: GetCreateDataHighlight,
getPixelEncoding: GetPixelEncodingHighlight,
apply: ApplyHighlight,
dataEqual: DataEqualHighlight,
supportedOutputs: LIST[$Highlight, $Y, $RGB]
];
classHighlight: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/HighlightLinear",
createColorOperator: CreateColorOperatorHighlight,
getCreateData: GetCreateDataHighlight,
getPixelEncoding: GetPixelEncodingHighlight,
apply: ApplyHighlight,
dataEqual: DataEqualHighlight,
supportedOutputs: LIST[$Y, $RGB]
];
ExtraColorFromHighlight: PROC [highlight: Highlight, baseColor: ConstantColor ¬ NIL, highlightColor: ConstantColor ¬ NIL, appearanceHints: Structure ¬ NIL] RETURNS [OpConstantColor] ~ {
color: OpConstantColor ~ NEW[ColorRep.constant.op[2]];
IF baseColor = NIL THEN baseColor ¬ Find["Xerox/Solid/White"];
IF highlightColor = NIL THEN highlightColor ¬ ExtraColorFromHighlight[[0, 1], Find["Xerox/Solid/White"], Find["Xerox/Solid/Red"]];
color.colorOperator ¬ ExtraNewColorOperatorHighlight[sWhite: makeGrayUnit, sBlack: 0, baseColor: baseColor, highlightColor: highlightColor, appearanceHints: appearanceHints];
color.pixel[0] ¬ SampleFromReal[highlight.b, makeGrayUnit];
color.pixel[1] ¬ SampleFromReal[highlight.h, makeGrayUnit];
RETURN [color]
};
ColorFromHighlight: PUBLIC PROC [highlight: Highlight] RETURNS [OpConstantColor] ~ {
RETURN [ExtraColorFromHighlight[highlight]];
};
HighlightFromColor: PUBLIC PROC [c: OpConstantColor] RETURNS [Highlight] ~ {
class: ColorOperatorClass ¬ c.colorOperator.class;
FOR tail: LIST OF ColorSpace ¬ class.supportedOutputs, tail.rest UNTIL tail=NIL DO
IF tail.first = $Highlight THEN {
p: ColorPoint ~ ImagerColorPrivate.TransformConstantColor[color: c, colorSpace: $Highlight];
RETURN [[p[0], p[1]]];
};
ENDLOOP;
ERROR ImagerError.Error[[$notImplemented, "ImagerColor.HighlightFromColor is not implemented for this color"]];
};
NarrowToConstantColor: PROC [color: Color] RETURNS [ConstantColor] ~ {
WITH color SELECT FROM
color: ConstantColor => RETURN [color];
ENDCASE => ERROR ImagerError.Error[[$illegalColor, "Color must be a ConstantColor", LIST[[$culprit, color]]]];
};
CreateColorOperatorHighlight: ColorOperatorCreateProc ~ {
consistencyTest: INT ~ ImagerColorPrivate.SCheckSize[structure, 6];
sWhite: REAL ~ ImagerColorPrivate.SGetReal[structure, 0];
sBlack: REAL ~ ImagerColorPrivate.SGetReal[structure, 1];
pixelEncodingStructure: Structure ~ ImagerColorPrivate.SGetOptionalStructure[structure, 2, 3];
baseColor: ConstantColor ~ NarrowToConstantColor[ImagerColorPrivate.SGetColor[structure, 3]];
highlightColor: ConstantColor ~ NarrowToConstantColor[ImagerColorPrivate.SGetColor[structure, 4]];
appearanceHints: Structure ~ ImagerColorPrivate.SGetStructure[structure, 5];
map: PixelEncoding ~ ImagerColorPrivate.PixelEncodingFromStructure[pixelEncodingStructure];
colorOperator: ColorOperator ~ ExtraNewColorOperatorHighlight[sWhite: sWhite, sBlack: sBlack, baseColor: baseColor, highlightColor: highlightColor, appearanceHints: appearanceHints];
RETURN [colorOperator]
};
GetPixelEncodingHighlight: GetPixelEncodingProc ~ {
data: DataHighlight ~ NARROW[colorOperator.data];
RETURN [data.map]
};
ApplyHighlight: ApplyProc ~ {
data: DataHighlight ~ NARROW[colorOperator.data];
sWhite: REAL ~ data.sWhite;
sBlack: REAL ~ data.sBlack;
pm: PixelEncoding ~ data.map;
s: PROC [i: NAT] RETURNS [REAL] ~ INLINE { RETURN [pixelIn[i]] };
b: REAL ~ (s[0] - sBlack) / (sWhite - sBlack);
h: REAL ~ (s[1] - sBlack) / (sWhite - sBlack);
SELECT colorSpace FROM
$Highlight => { -- This should only be allowed if the right basis is used.
out[0] ¬ b;
out[1] ¬ h;
};
$RGB => {
out[0] ¬ b * data.base.R + h * data.highlight.R;
out[1] ¬ b * data.base.G + h * data.highlight.G;
out[2] ¬ b * data.base.B + h * data.highlight.B;
};
$Y => {
out[0] ¬ b * data.baseY + h * data.highlightY;
};
ENDCASE => ERROR;
};
GetCreateDataHighlight: GetCreateDataProc ~ {
data: DataHighlight ~ NARROW[colorOperator.data];
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.sWhite]]];
elementAction[[real[data.sBlack]]];
IF (data.map = NIL)
THEN { elementAction[[int[0]]] }
ELSE {
elementAction[[structure[ImagerColorPrivate.StructureFromPixelEncoding[data.map]]]]
};
elementAction[[color[data.baseColor]]];
elementAction[[color[data.highlightColor]]];
IF (colorOperator.appearanceHints = NIL) THEN elementAction[[int[0]]]
ELSE elementAction[[structure[colorOperator.appearanceHints]]]
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
DataEqualHighlight: DataEqualProc ~ {
a: DataHighlight ~ NARROW[selfData];
b: DataHighlight ~ NARROW[otherData];
IF (a.sWhite # b.sWhite) OR (a.sBlack # b.sBlack) THEN RETURN [FALSE];
IF NOT ImagerColorPrivate.PixelEncodingEqual[a.map, b.map] THEN RETURN [FALSE];
IF NOT ImagerColorPrivate.ConstantColorsEqual[a.baseColor, b.baseColor] THEN RETURN [FALSE];
IF NOT ImagerColorPrivate.ConstantColorsEqual[a.highlightColor, b.highlightColor] THEN RETURN [FALSE];
RETURN [TRUE];
};
ExtraNewColorOperatorHighlight: PROC [sWhite, sBlack: REAL, map: PixelEncoding ¬ NIL, baseColor, highlightColor: ConstantColor, appearanceHints: Structure ¬ NIL] RETURNS [ColorOperator] ~ {
base: OpConstantColor ~ NarrowToOpConstantColor[baseColor];
highlight: OpConstantColor ~ NarrowToOpConstantColor[highlightColor];
baseRGB: RGB ~ RGBFromColor[base];
highlightY: REAL ~ ImagerColorPrivate.IntensityFromColor[highlight];
data: DataHighlight ~ NEW[DataHighlightRep ¬ [
sWhite: sWhite,
sBlack: sBlack,
map: map,
baseColor: baseColor,
base: baseRGB,
baseY: ImagerColorPrivate.IntensityFromColor[base],
highlightColor: highlightColor,
highlight: RGBFromColor[highlight],
highlightY: highlightY
]];
whiteBase: BOOL ~ MIN[baseRGB.R, baseRGB.G, baseRGB.B] >= 0.9;
normalHighlight: BOOL ~ highlightY IN [0.05 .. 0.95];
class: ImagerColorPrivate.ColorOperatorClass ~ IF whiteBase AND normalHighlight THEN classHighlightPassThru ELSE classHighlight;
The funny business about the class is that we want to be able to "pass through" highlight specifications ignoring the hue, if the basis vectors are close enough to reasonable. If they are sufficiently strange, we just don't allow the highlight space as an intermediate, which means it will go through the full color mapping.
The particular tests here are a pretty rough-and-ready cut at defining what is reasonable, and may need to be refined.
RETURN [ImagerColorPrivate.NewColorOperator[chromatic: TRUE, samplesPerPixelIn: 2, class: class, data: data, appearanceHints: appearanceHints]]
};
NewColorOperatorHighlight: PUBLIC PROC [sWhite, sBlack: REAL, map: PixelEncoding ¬ NIL, baseColor, highlightColor: OpConstantColor, appearanceHints: Structure ¬ NIL] RETURNS [ColorOperator] ~ {
RETURN [ExtraNewColorOperatorHighlight[sWhite: sWhite, sBlack: sBlack, map: map, baseColor: baseColor, highlightColor: highlightColor, appearanceHints: appearanceHints]]
};
Process, CMYK
... Xerox/Process (Color Encoding Standard)
SignalType: TYPE ~ ImagerColor.SignalType;
DataProcess: TYPE ~ REF DataProcessRep;
DataProcessRep: TYPE ~ RECORD [
su: REAL,
sz: REAL,
signalType: SignalType,
signalNames: LIST OF ConstantColor,
pe: PixelEncoding
];
typeForSignalName: ARRAY SignalType[dotPercent..density] OF ROPE ~ ["dotPercent", "reflectance", "transmittance", "density"];
SignalTypeFromSignalTypeName: PUBLIC PROC [name: ROPE] RETURNS [SignalType] ~ {
FOR signal: SignalType ¬ FIRST[SignalType], SUCC[signal] DO
rope: ROPE ~ SignalTypeNameFromSignalType[signal];
IF rope = NIL THEN ERROR ImagerError.Error[[$unknownSignalType, "Unknown Signal type for Xerox/Process color operator", LIST[[$signal, name]]]];
IF Rope.Equal[name, rope, FALSE] THEN RETURN [signal];
ENDLOOP;
};
SignalTypeNameFromSignalType: PUBLIC PROC [signal: SignalType] RETURNS [ROPE] ~ {
IF ORD[signal] < LENGTH[typeForSignalName]
THEN RETURN [typeForSignalName[signal]]
ELSE RETURN [NIL]
};
classProcess: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/Process",
createColorOperator: CreateColorOperatorProcess,
getCreateData: GetCreateDataProcess,
getPixelEncoding: GetPixelEncodingProcess,
apply: ApplyProcess,
dataEqual: DataEqualProcess,
supportedOutputs: LIST[$Y, $RGB]
];
cmykColorSpace: ColorSpace ¬ firstProcess; -- initialization is completed below.
classCMYK: ColorOperatorClass ~ ImagerColorPrivate.NewColorOperatorClass[
name: "Xerox/Process",
createColorOperator: CreateColorOperatorProcess,
getCreateData: GetCreateDataProcess,
getPixelEncoding: GetPixelEncodingProcess,
apply: ApplyProcess,
dataEqual: DataEqualProcess,
supportedOutputs: LIST[$Y, $RGB] -- initialization is completed below.
];
GetProcessArgIndices: PROC [structure: Structure] RETURNS [PACKED ARRAY ProcessArgs OF [0..5]] ~ {
There was a bug in early (prior to March 8, 1993) versions of Cedar10.1, where the structure elements were in the wrong order compared to the Color Encoding Standard. To accomodate Interpress masters that may have been created incorrectly, we'll allow this bogus order, but issue a warning.
WITH ImagerColorPrivate.SGet[structure, 2] SELECT FROM
e: Element.atom => {
SIGNAL ImagerError.Warning[[$illegalArguments, "Nonstandard arguments passed to Xerox/Process Color Model Operator"]];
RETURN [[su:0, sz:1, signalType:2, signalNames:3, pixEnc:4, deviceProps:5]];
};
ENDCASE => RETURN [[0, 1, 2, 3, 4, 5]];
};
ProcessArgs: TYPE ~ {su, sz, pixEnc, signalType, signalNames, deviceProps};
CreateColorOperatorProcess: ColorOperatorCreateProc ~ {
consistencyTest: INT ~ ImagerColorPrivate.SCheckSize[structure, 6];
ai: PACKED ARRAY ProcessArgs OF [0..5] ~ GetProcessArgIndices[structure];
su: REAL ~ ImagerColorPrivate.SGetReal[structure, ai[$su]];
sz: REAL ~ ImagerColorPrivate.SGetReal[structure, ai[$sz]];
signalTypeAtom: ATOM ~ ImagerColorPrivate.SGetAtom[structure, ai[$signalType]];
signalNamesStructure: Structure ~ ImagerColorPrivate.SGetStructure[structure, ai[$signalNames]];
signals: NAT ~ ImagerColorPrivate.SSize[signalNamesStructure];
SignalNames: PROC [signal: NAT] RETURNS [ConstantColor] ~ {
RETURN [NARROW[ImagerColorPrivate.SGetColor[signalNamesStructure, signal]]]
};
pixelEncodingStructure: Structure ~ ImagerColorPrivate.SGetOptionalStructure[structure, ai[$pixEnc], signals];
map: PixelEncoding ~ ImagerColorPrivate.PixelEncodingFromStructure[pixelEncodingStructure];
deviceProps: Structure ~ ImagerColorPrivate.SGetStructure[structure, ai[$deviceProps]];
colorOperator: ColorOperator ~ NewColorOperatorProcess[su: su, sz: sz, signals: signals, map: map, signalType: SignalTypeFromSignalTypeName[Atom.GetPName[signalTypeAtom]], signalNames: SignalNames, deviceProps: deviceProps];
RETURN [colorOperator]
};
GetCreateDataProcess: GetCreateDataProc ~ {
data: DataProcess ~ NARROW[colorOperator.data];
GenerateSignalNameElements: ElementGenerator ~ {
FOR tail: LIST OF ConstantColor ¬ data.signalNames, tail.rest UNTIL tail = NIL DO
elementAction[[color[tail.first]]];
ENDLOOP;
};
GenerateElements: ElementGenerator ~ {
elementAction[[real[data.su]]];
elementAction[[real[data.sz]]];
IF (data.pe = NIL)
THEN { elementAction[[int[0]]] }
ELSE {
elementAction[[structure[ImagerColorPrivate.StructureFromPixelEncoding[data.pe]]]]
};
elementAction[[atom[Atom.MakeAtom[
SignalTypeNameFromSignalType[data.signalType]]]]];
elementAction[[structure[ImagerColorPrivate.StructureFromElementGenerator[
GenerateSignalNameElements]]]];
IF (colorOperator.appearanceHints = NIL) THEN elementAction[[int[0]]]
ELSE elementAction[[structure[colorOperator.appearanceHints]]]
};
structure: Structure ~ ImagerColorPrivate.StructureFromElementGenerator[GenerateElements];
RETURN [structure]
};
GetPixelEncodingProcess: GetPixelEncodingProc ~ {
data: DataProcess ~ NARROW[colorOperator.data];
RETURN [data.pe]
};
ApplyProcess: ApplyProc ~ {
data: DataProcess ~ NARROW[colorOperator.data];
ImagerTestProbe.DoApplyProcess[su: data.su, sz: data.sz, signalType: data.signalType, signalNames: data.signalNames, pixelIn: pixelIn, colorSpace: colorSpace, out: out];
};
DoApplyProcess: PUBLIC <<ImagerTestProbe>> PROC [su: REAL, sz: REAL, signalType: SignalType, signalNames: LIST OF ConstantColor, pixelIn: TupleProc, colorSpace: ColorSpace, out: ColorPoint] ~ {
rgb: RGB ¬ [1, 1, 1]; -- start out white
i: NAT ¬ 0;
IF colorSpace = cmykColorSpace AND signalType = reflectance
THEN {
scaleFactor: REAL ~ 255.0 / ( sz - su );
FOR i: NAT IN [0..4) DO
out[i] ¬ (pixelIn[i] - su) * scaleFactor;
ENDLOOP;
}
ELSE {
FOR tail: LIST OF ConstantColor ¬ signalNames, tail.rest UNTIL tail = NIL DO
ink: RGB ¬ RGBFromColor[NarrowToOpConstantColor[tail.first]];
siv: REAL ~ pixelIn[i];
sivalue: REAL ~ ( siv - sz) / ( su - sz );
reflectance: REAL ¬ sivalue;
Comb: PROC [p, i: REAL] RETURNS [REAL] ~ INLINE {
RETURN [p * (1 - (1 - reflectance) * (1 - i))]
};
SELECT signalType FROM
reflectance => {};
ENDCASE => ERROR ImagerError.Error[[$unimplemented, "unimplemented signalType", LIST[[$signalType, SignalTypeNameFromSignalType[signalType]]]]];
rgb.R ¬ Comb[rgb.R, ink.R];
rgb.G ¬ Comb[rgb.G, ink.G];
rgb.B ¬ Comb[rgb.B, ink.B];
i ¬ i + 1;
ENDLOOP;
SELECT colorSpace FROM
$Y => {
out[0] ¬ Clip01[0.253*rgb.R+0.684*rgb.G+0.063*rgb.B];
};
$RGB => {
out[0] ¬ rgb.R;
out[1] ¬ rgb.G;
out[2] ¬ rgb.B;
};
ENDCASE => ERROR;
};
};
DataEqualProcess: DataEqualProc ~ {
a: DataProcess ~ NARROW[selfData];
b: DataProcess ~ NARROW[otherData];
IF (a.su # b.su) OR (a.sz # b.sz) OR (a.signalType # b.signalType) THEN RETURN [FALSE];
IF NOT EqualSignalNames[a.signalNames, b.signalNames] THEN RETURN [FALSE];
IF NOT ImagerColorPrivate.PixelEncodingEqual[a.pe, b.pe] THEN RETURN [FALSE];
RETURN [TRUE];
};
EqualSignalNames: PROC [a, b: LIST OF ConstantColor] RETURNS [BOOL] ~ {
atail: LIST OF ConstantColor ¬ a;
btail: LIST OF ConstantColor ¬ b;
UNTIL atail = NIL OR btail = NIL DO
IF atail.first # btail.first THEN RETURN [FALSE]; -- both colors should come from Find, so ref equality ought suffice.
atail ¬ atail.rest;
btail ¬ btail.rest;
ENDLOOP;
RETURN [atail = btail]
};
CMYKSignalNames: PROC [signal: NAT] RETURNS [ConstantColor] ~ {
solid: SolidColor ~ SELECT signal FROM 0 => cyan, 1 => magenta, 2 => yellow, 3 => black ENDCASE => black;
RETURN [solidColors[solid]]
};
EnlistSignalNames: PROC [signals: NAT, signalNames: PROC [signal: NAT] RETURNS [ConstantColor]] RETURNS [LIST OF ConstantColor] ~ {
signalNameList: LIST OF ConstantColor ¬ NIL;
FOR i: NAT DECREASING IN [0..signals) DO
signalNameList ¬ CONS[signalNames[i], signalNameList];
ENDLOOP;
RETURN [signalNameList]
};
cmykSignalNames: LIST OF ConstantColor ~ EnlistSignalNames[4, CMYKSignalNames];
NewColorOperatorProcess: PUBLIC PROC [su, sz: REAL, signals: NAT, map: PixelEncoding ¬ NIL, signalType: SignalType, signalNames: PROC [signal: NAT] RETURNS [ConstantColor], deviceProps: Structure ¬ NIL] RETURNS [ColorOperator] ~ {
signalNameList: LIST OF ConstantColor ~ EnlistSignalNames[signals, signalNames];
class: ColorOperatorClass ~ IF EqualSignalNames[signalNameList, cmykSignalNames] THEN classCMYK ELSE classProcess;
data: DataProcess ~ NEW[DataProcessRep ¬ [su: su, sz: sz, signalType: signalType, signalNames: signalNameList, pe: map]];
RETURN [ImagerColorPrivate.NewColorOperator[chromatic: TRUE, samplesPerPixelIn: signals, class: class, data: data, appearanceHints: deviceProps]];
};
NewColorOperatorCMYK: PUBLIC PROC [maxIn: NAT] RETURNS [ColorOperator] ~ {
RETURN [NewColorOperatorProcess[su: 0, sz: maxIn, signals: 4, signalType: $reflectance, signalNames: CMYKSignalNames]]
};
cmyk1000: ColorOperator ~ NewColorOperatorCMYK[1000];
CMYK: TYPE ~ ImagerColor.CMYK;
ColorFromCMYK: PUBLIC PROC [cmyk: CMYK] RETURNS [OpConstantColor] ~ {
color: OpConstantColor ~ NEW[ColorRep.constant.op[4]];
color.colorOperator ¬ cmyk1000;
color.pixel[0] ¬ Real.Round[cmyk.C*1000.0];
color.pixel[1] ¬ Real.Round[cmyk.M*1000.0];
color.pixel[2] ¬ Real.Round[cmyk.Y*1000.0];
color.pixel[3] ¬ Real.Round[cmyk.K*1000.0];
RETURN [color];
};
Making Colors
NarrowToOpConstantColor: PUBLIC PROC [color: ConstantColor] RETURNS [OpConstantColor] ~ {
c: ConstantColor ¬ color;
DO WITH c SELECT FROM
special: SpecialColor => c ¬ special.substitute;
op: OpConstantColor => RETURN [op];
ENDCASE => ERROR ImagerError.Error[[$noSubstituteColor, "No substitute for SpecialColor", LIST[[$Color, color]]]]
ENDLOOP;
};
ColorFromPixel: PUBLIC PROC [colorOperator: ColorOperator, pixel: PROC [sampleIndex: NAT] RETURNS [REAL]] RETURNS [ConstantColor] ~ {
size: NAT ~ colorOperator.samplesPerPixelIn;
color: OpConstantColor ~ NEW[ColorRep.constant.op[size]];
color.colorOperator ¬ colorOperator;
FOR i: NAT IN [0..size) DO color.pixel[i] ¬ pixel[i] ENDLOOP;
RETURN [color];
};
MakeSampledBlack: PUBLIC PROC [pa: PixelArray, um: Transformation, clear: BOOL ¬ FALSE] RETURNS [SampledBlack] ~ {
IF pa.samplesPerPixel#1 THEN ERROR;
IF ImagerPixelArray.MaxSampleValue[pa, 0]#1 THEN ERROR;
RETURN[NEW[ColorRep.sampledBlack ¬ [sampledBlack[pa: pa, um: um, clear: clear]]]];
};
MakeSampledColor: PUBLIC PROC [pa: PixelArray, um: Transformation, colorOperator: ColorOperator] RETURNS [SampledColor] ~ {
RETURN[NEW[ColorRep.sampled ¬ [sampled[pa: pa, um: um, colorOperator: colorOperator]]]];
};
eitherRGBorY: LIST OF ImagerColorPrivate.ColorSpace ~ LIST[$RGB, $Y];
RGBFromColor: PUBLIC PROC [c: OpConstantColor] RETURNS [rgb: RGB ¬ [0, 0, 0]] ~ {
p: ColorPoint ¬ ImagerColorPrivate.TransformConstantColor[c, ImagerColorPrivate.ChooseColorSpace[c.colorOperator, eitherRGBorY]];
SELECT p.dim FROM
1 => rgb ¬ [p[0], p[0], p[0]]; -- RGB from intensity
3 => rgb ¬ [p[0], p[1], p[2]]; -- full RGB
ENDCASE => ERROR;
p ¬ ImagerColorPrivate.DestroyColorPoint[p];
};
IntensityFromStipple: PUBLIC PROC [word: WORD] RETURNS [REAL] ~ {
nBits: NAT ~ 16;
bits: PACKED ARRAY [0..nBits) OF [0..1] ~ LOOPHOLE[word];
count: NAT ¬ 0; -- count the number of 1 bits
FOR i: NAT IN[0..nBits) DO count ¬ count+bits[i] ENDLOOP;
RETURN[REAL[nBits-count]/nBits];
};
ColorFromStipple: PUBLIC PROC [word: WORD, function: Function] RETURNS [SpecialColor] ~ {
data: ImagerColorPrivate.StippleData ~ NEW[ImagerColorPrivate.StippleDataRep ¬ [word: word, function: function]];
RETURN[NEW[ColorRep.constant.special ¬ [constant[special[type: $Stipple, name: NIL, data: data, substitute: ColorFromGray[1-IntensityFromStipple[word]]]]]]];
};
Define: PROC [name: ROPE, type: ATOM, substitute: ConstantColor] RETURNS [ConstantColor] ~ {
special: SpecialColor ~ NEW[ImagerColor.ColorRep.constant.special ¬ [constant[special[type: type, name: name, data: NIL, substitute: substitute]]]];
ImagerColorPrivate.RegisterNamedColor[special];
RETURN [special]
};
[] ¬ ImagerColorPrivate.DefineProcessSpace[
NewColorOperatorGrayLinear[sWhite: 1.0, sBlack: 0.0], $Y];
[] ¬ ImagerColorPrivate.DefineProcessSpace[
NewColorOperatorRGB[1.0], $RGB];
[] ¬ ImagerColorPrivate.DefineProcessSpace[
NewColorOperatorCIELAB[sWhite: 100.0, sBlack: 0.0, sneutral: 0.0, srange: 1.0], $CIELAB];
[] ¬ ImagerColorPrivate.DefineProcessSpace[
NewColorOperatorYES[sWhite: 1.0, sBlack: 0.0, sneutral: 0.0, srange: 1.0], $YES];
[] ¬ ImagerColorPrivate.DefineProcessSpace[
ExtraNewColorOperatorHighlight[
sWhite: 1.0,
sBlack: 0.0,
map: NIL,
baseColor: solidColors[white],
highlightColor: Define["Xerox/Highlight", $Highlight, ColorFromHighlight[[0, 1]]]
],
$Highlight];
The initialization of CMYK, in terms of a process space, is rather delicate...
cmykColorSpace ¬ ImagerColorPrivate.DefineProcessSpace[NewColorOperatorCMYK[255]];
classCMYK.supportedOutputs ¬ CONS[cmykColorSpace, classCMYK.supportedOutputs];
END.
IF debugStream # NIL THEN {
IO.PutFL[debugStream, "PIX: %10.3f %10.3f %10.3f\nLAB: %10.3f %10.3f %10.3f\nXYZ: %10.3f %10.3f %10.3f\nRGB: %10.3f %10.3f %10.3f\n\n", LIST [
[real[ s[0] ]],
[real[ s[1] ]],
[real[ s[2] ]],
[real[ Lstar01*100 ]],
[real[ astar ]],
[real[ bstar ]],
[real[ X ]],
[real[ Y ]],
[real[ Z ]],
[real[ out[0] ]],
[real[ out[1] ]],
[real[ out[2] ]]
]];
};