~
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;
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];
};
};
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;
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"];
SignalTypeFromSignal
TypeName:
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;
};
Signal
TypeNameFromSignalType:
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.
class
CMYK: 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]];
class
CMYK.supportedOutputs ¬
CONS[cmykColorSpace, class
CMYK.supportedOutputs];