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; Find: PUBLIC PROC [name: ROPE] RETURNS [ConstantColor] ~ { color: ConstantColor ~ NARROW[SymTab.Fetch[ImagerColorPrivate.namedColorTable, name].val]; RETURN [color] }; 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]] }; 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]; }; 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]]; }; 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]]; }; 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]]; }; 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]; }; 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 => { 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]; }; 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] }; 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] = { 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]; }; }; 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] }; 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: 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; }; 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: 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] ~ { RETURN [IF t > 0.008856 THEN t**(1/3.0) ELSE 7.787 * t + (16.0/116.0)]; }; G: PROC [ft: REAL] RETURNS [REAL] ~ { 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]] }; 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; 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]] }; 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]] ~ { 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 <> 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]; }; 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]; cmykColorSpace ¬ ImagerColorPrivate.DefineProcessSpace[NewColorOperatorCMYK[255]]; classCMYK.supportedOutputs ¬ CONS[cmykColorSpace, classCMYK.supportedOutputs]; END. ˆ 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 Named Color Registry Common Support GrayLinear ColorOperator GrayDensity ColorOperator GrayVisual ColorOperator Map ColorOperator BuildMap ColorOperator RGB ColorOperator Note: Prior to Cedar10.0, we used coefficients of 0.30, 0.59, 0.11 SolidColors Note: This must get initialized AFTER the rgb1000 ColorOperator. HSV ensures that v is in [0..1] Xerox/Research/RGBLinear compatibility RenderingHints Matrix3 Support YESLinear ... Xerox/YESLinear (Color Encoding Standard) CIELAB ... Xerox/CIELAB (Color Encoding Standard) The function f(t) defined in Color Encoding Standard section 3.1.3 The inverse of F HighlightLinear ... Xerox/HighlightLinear (Color Encoding Standard) 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. Process, CMYK ... Xerox/Process (Color Encoding Standard) 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. Making Colors The initialization of CMYK, in terms of a process space, is rather delicate... 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] ]] ]]; }; Ê<š•NewlineDelimiter –(cedarcode) style™™Icodešœ ÏeœC™NK™)Kšœ+™+K™,J™—šÏk ˜ K• CharProps+Postfix (xerox/highlight) textNamedColor˜K˜Kšœ žœ˜#Kšœ ˜ Kšœ˜Kšœ žœ˜+Kšœžœ˜4Kšœ ˜ Kšœžœ˜,Kšœžœ˜Kšœžœ ˜Kšœ žœ ˜K˜K˜Kšœžœ ˜—K˜KšÐlnœžœž˜K–-rPostfix (xerox/highlight) textNamedColoršžœz˜K–-)Postfix (xerox/highlight) textNamedColoršžœ1˜8šœž˜K˜Kšœžœ)˜AKšœžœžœ,˜NK˜Kšœžœžœ˜-šœž œ(˜FK˜—Kšœžœ˜ Kšœžœ˜0Kšœ žœ˜&Kšœžœ˜0Kšœžœ˜4Kšžœžœžœ˜Kšœžœ˜.Kšœžœ˜.Kšœžœ˜4Kšœžœ˜.Kšœ žœ˜(Kšœžœ˜.K˜Kšœžœ.˜KKšœžœ$˜7Kšœ žœ˜+Kšœžœ'˜=Kšœžœ(˜?Kšœžœ+˜EKšœžœ%˜9Kšœ žœ"˜3Kšœžœ%˜9Kšœžœ$˜7Kšœ žœ ˜/šœ žœ ˜/K˜—Kšœ žœ˜'Kšœ žœ˜/Kšœ žœ˜,Kšœ žœ˜(Kšžœžœžœ˜Kšœžœ˜/Kšœžœ'˜;K˜Kšœžœ.˜KKšœ žœ!˜1Kšœ žœ!˜1Kšœžœ$˜7K˜—head™š Ïnœžœžœžœžœ˜:Kšœžœ=˜ZKšžœ˜Kšœ˜K˜——šœ™š   œžœ žœžœžœžœ˜8Kšžœžœ žœžœžœ žœžœ˜=Kšœ˜K˜—š  œžœžœžœžœžœ˜0Kšžœ˜Kšœ˜K˜—š   œžœ žœžœžœžœ˜?Kšžœ(˜.Kšœ˜K˜—š  œžœžœžœžœžœ˜DKšžœ$˜*Kšœ˜K˜—š  œžœ<žœžœžœ˜mKšžœžœžœžœžœžœžœ9˜Kšœ˜K˜—š  œžœžœ5žœžœ˜lKšœR˜Rš žœžœžœžœžœžœ˜$Kšœžœ"˜9Kšœ ˜ Kšžœ˜ Kšœ˜—Kšœ˜K˜—š œžœžœ˜˜>KšœV˜VKš œžœžœžœžœžœ'˜SKšœ“˜“Kšžœ˜Kšœ˜—K˜š œ˜,Kšœžœ˜0Kš   œžœžœžœžœ"žœ˜oKšœX˜Xš œ˜&Kšœ)˜)Kšœ3˜3Kšœ˜—KšœZ˜ZKšžœ ˜K˜K˜—š  œ˜Kšœžœ˜0Kšœ5˜5KšœL˜Lš  œ˜Kšœ˜Kšœžœ˜Kšœžœ#˜*Kšœžœ3˜:Kšžœ˜ Kšœ˜—Kšœ>˜>K˜K˜—š œ˜$Kšœžœ ˜#Kšœžœ ˜$Kšžœ#žœžœžœ˜9Kšžœžœžœžœ˜+Kšžœžœžœžœžœžœžœžœžœ˜NKšžœžœ˜K˜K˜—š œžœžœ0žœžœžœžœžœžœ˜•Kšœžœ˜*Kšœ]˜]Kšœ#˜#Kšžœ¸žœ˜ÃK˜——šœ™Kšœ žœžœ ˜šœ žœžœ˜Kšœžœ˜ Kšœžœ˜ Kšœ˜Kšœ˜K˜—šœH˜HKšœ˜Kšœ,˜,Kšœ ˜ Kšœ&˜&Kšœ˜Kšœ˜Kšœžœ ˜ Kšœ˜—K˜š œ˜3Kšœžœ/˜CKšœžœ-˜9Kšœžœ-˜9Kšœ^˜^Kšœ[˜[KšœL˜LKšœ˜Kšžœ˜Kšœ˜K˜—š œ˜'Kšœžœ˜+š œ˜&Kšœ#˜#Kšœ#˜#šžœ žœ˜Kšžœ˜ šžœ˜KšœS˜SKšœ˜——šžœ"žœžœ˜EKšžœ:˜>—Kšœ˜—KšœZ˜ZKšžœ ˜K˜K˜—š œ˜-Kšœžœ˜+Kšžœ ˜Kšœ˜K˜—š œ˜Kšœžœ˜+Kšœžœ˜Kšœžœ˜Kšœžœ˜"Kšœ˜Kšœžœžœžœ˜šžœ žœž˜Kšœ!˜!Kšžœ˜—šžœ ž˜Kš œžœžœžœžœžœ˜6šœ˜KšœB™BKšœ2˜2Kšœ˜—Kšžœžœ˜—K˜K˜—š  œ˜Kšœ žœ ˜Kšœ žœ ˜Kš žœžœžœžœžœ˜FKš žœžœ5žœžœžœ˜OKšžœžœ˜K˜—K˜š œžœžœ žœ žœžœžœžœ˜™Kšœžœ:˜MKšžœ1žœW˜’K˜K˜—šœ;˜;K˜—š   œžœžœžœžœ˜BKšœžœ˜6Kšœ˜Kšœ5˜5Kšœ5˜5Kšœ5˜5Kšžœ ˜K˜K˜——™ K™@Kšœ žœ=˜MKšœ žœžœ žœ#˜Gš  œžœžœžœžœ žœ˜IKš œžœžœ žœžœžœ žœ˜Rš  œžœžœ žœžœ˜AKšžœ0˜6K˜—K˜-K˜)K˜-K˜+K˜+K˜1K˜/K˜-Kšžœ˜ K˜——™š   œžœžœ žœžœ˜GKšžœ$˜*Kšœ˜K˜—š   œžœžœžœ žœ˜JKšžœ˜"Kšœ˜K˜—procš  œžœžœžœžœ˜*MšÏc™Mšžœžœžœžœžœžœžœ˜0Mšœ˜M˜—š   œžœžœžœžœ˜AMšœžœ˜ Mšœžœ˜Mšžœ žœžœžœ˜>Mšœ˜Mšœ!˜!Mšœ˜Mšœ ˜ Mšœ¡ ˜#Mšœ¡˜!Mšžœžœ ˜Mšœ˜Mšœ ˜ Mšœ$˜$šžœž˜Mšœžœ˜Mšœžœ˜Mšœžœ˜Mšœžœ˜Mšœžœ˜Mšœžœ˜Mšžœžœ ˜—Kšœ˜K˜—š   œžœžœžœ žœ˜7Mšœžœ˜Mšœžœ˜Mšœžœ˜Mšœžœžœ ¡˜,Mšœžœžœ ¡˜1Mšœžœ˜Mš œ žœžœžœžœ˜6šžœ˜Mšžœžœ¡˜šžœ˜Mšœžœ˜!Mšœžœ˜!Mšœžœ˜!šœžœžœž˜Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšžœžœ˜—Mšžœžœ˜Mšžœ˜Kšœ˜——K˜K˜—K˜—™&šœQ˜QKšœ!˜!Kšœ/˜/Kšœžœ˜Kšœžœ˜Kšœžœ˜ Kšœ žœ˜Kšœž˜Kšœ˜K˜—š œ˜6Kšœžœ/˜CKšœžœ-˜9KšœSžœžœ˜nKšžœ˜Kšœ˜——™šœQ˜QKšœ)˜)Kšœ;˜;Kšœžœ˜Kšœžœ˜Kšœžœ˜ Kšœ žœ˜Kšœž˜Kšœ˜K˜—š %œ˜BKšœžœ/˜8KšœV˜VKšœL˜LKšœu˜uKšžœKžœ˜mKšžœ˜Kšœ˜K˜—š œžœ<žœ˜nKšœm˜mKšžœ=žœš˜ÞK˜K˜——šœ™Kšœ žœžœ ˜Kš œ žœžœžœžœžœžœ˜8š œžœžœžœ˜*Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜šžœ˜KšÏf)˜)Kš¢)˜)Kšœ˜—Kšœ˜K˜—š  œžœžœžœ˜=Kšœžœ žœ žœžœžœžœžœ˜RKšœžœ žœ žœžœžœžœžœ˜RKš œžœžœžœžœžœ˜HKšœžœ)˜0Kš žœžœžœžœžœ˜+Kšœ˜K˜—š œžœžœ˜0Kšœ žœ ˜Kšœžœ ˜šžœ žœž˜šžœ žœž˜Kšœ!˜!Kšžœ˜—Kšžœ˜—Kšžœ˜ Kšœ˜K˜—š œžœžœ˜0Kšœ žœ ˜šžœ žœž˜šžœ žœž˜Kšœžœ˜ šžœ žœž˜Kšœ˜Kšžœ˜—Kšœ ˜ Kšžœ˜—Kšžœ˜—Kšžœ˜ Kšœ˜K˜—š œžœžœžœžœžœžœžœžœ˜Xšžœ žœž˜K˜2Kšžœ˜—Kšœ˜K˜—š  œžœžœ˜Cšžœ žœž˜K˜.Kšžœ˜—Kšœ˜K˜——™ Kšœ.™.šžœžœžœ¡˜6K˜—Kšœ žœžœ ˜šœ žœžœ˜Kšœžœ˜ Kšœžœ˜ Kšœ žœ˜Kšœžœ˜ Kšœ˜Kšœ˜K˜—šœžœ˜+Kš¢˜Kš¢˜Kš¢˜K˜K˜—šœ0˜0K˜—šœH˜HKšœ˜Kšœ,˜,Kšœ ˜ Kšœ&˜&Kšœ˜Kšœ˜Kšœžœ˜&Kšœ˜K˜—Kšœžœ˜Kšœžœ˜K˜š   œžœžœžœžœ˜BKšœžœ˜6Kšœžœ"˜-K˜Kšœ5˜5K˜IK˜IKšžœ ˜Kšœ˜K˜—š   œžœžœžœžœ˜@Kšœžœ˜Kšœžœžœžœ0˜IKšžœ˜!Kšœ˜—K˜š œ˜3Kšœžœ/˜CKšœžœ-˜9Kšœžœ-˜9Kšœ žœ-˜;Kšœžœ-˜9Kšœ^˜^Kšœ[˜[KšœL˜LKšœ£˜£Kšžœ˜Kšœ˜K˜—š œ˜-Kšœžœ˜+Kšžœ ˜Kšœ˜K˜—š œ˜Kšœžœ˜+Kšœžœ˜Kšœžœ˜Kšœ˜Kš œžœžœžœžœžœžœ˜AKš œžœ'˜.šžœžœžœ˜-Kšœ žœ˜Kšœžœ˜Kš œžœ ˜'Kš œžœ ˜'šžœ ž˜šœ ˜ Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜—šœ ˜ Kšœ'˜'Kšœ˜—Kšžœžœ˜—Kšœ˜—K˜K˜—š œ˜'Kšœžœ˜+š œ˜&Kšœ#˜#Kšœ#˜#Kšœ%˜%Kšœ#˜#šžœ žœ˜Kšžœ˜ šžœ˜KšœS˜SKšœ˜——šžœ"žœžœ˜EKšžœ:˜>—Kšœ˜—KšœZ˜ZKšžœ ˜K˜K˜—š  œ˜Kšœ žœ ˜Kšœ žœ ˜Kšžœžœžœžœžœžœžœ˜|Kš žœžœ5žœžœžœ˜OKšžœžœ˜K˜K˜—š œžœžœ$žœžœžœžœ˜¡Kšœžœ^˜qKšžœ1žœW˜’Kšœ˜K˜——™Kšœ+™+šžœžœžœ¡%˜HK˜—Kšœ žœžœ˜%šœžœ¡$˜FK˜—šœK˜KKšœ˜Kšœ/˜/Kšœ!¡˜3Kšœ)˜)Kšœ˜Kšœ¡˜+Kšœžœ˜)Kšœ˜K˜—šœžœ˜+Kš¢˜Kš¢˜Kš¢˜K˜K˜—šœ¢œ ¢˜0K˜—K˜gš  œžœžœ žœžœ˜KKšœžœ˜6Kšœ!˜!Kšœ-˜-K˜1K˜1Kšžœ ˜Kšœ˜K˜—š  œžœžœžœžœ˜$K™BKšžœžœžœ žœ˜GKšœ˜K˜—š  œžœžœžœžœ˜%K™Kšžœžœžœ žœ˜HKšœ˜K˜—KšœÏdœžœ ˜šœ£œžœ ˜K˜—Kšœžœžœžœ˜Cš  œžœžœžœžœ˜IKšœd˜dKšœI˜Išžœ ž˜K˜"šœ ˜ Kšœžœžœžœ-˜FKšœžœ ˜K˜Kšœ£œ ˜(Kšœ#£œ˜(Kšœ˜—šœ˜Kšœžœ ˜K˜K˜ K˜ Kšœ˜—Kšžœžœ˜—Kšœ,˜,Kšœ˜K˜—š œ˜6Kšœžœ/˜CKšœžœ-˜9Kšœžœ-˜9Kšœ žœ-˜;Kšœžœ-˜9Kšœ^˜^Kšœ[˜[KšœL˜LKšœ¦˜¦Kšžœ˜Kšœ˜K˜—š œ˜0Kšœžœ˜.Kšžœ ˜Kšœ˜K˜—š  œ˜Kšœžœ˜.Kšœžœ˜Kšœžœ˜Kšœ˜Kš œžœžœžœžœžœžœ˜AK˜KšœÏuœžœ/˜<šžœ˜šžœ˜Kšœ žœ˜Kšœžœ˜Kšœ ¤œ ˜Kšœ&˜&Kšœ&˜&Kšœ˜—šžœ˜Kšœžœ¤œ˜#Kšœžœ ˜šžœ˜Kšžœ˜šžœ˜Kšœ žœ˜Kšœžœ˜Kšœ¤œžœ ˜+Kšœ¤œžœ ˜+Kšœžœ¤œ£œ˜'Kšœžœ ¤œ£œ˜'šžœ ž˜šœ ˜ Kšœ'˜'Kšœ˜—Kšžœžœ˜—Kšœ˜——Kšœ˜——K˜K˜—š œžœžœ$žœžœžœžœ˜¤Kšœžœa˜wKšžœ1žœZ˜•Kšœ˜K˜——–+Postfix (Xerox/Highlight) textNamedColor™K™4Kšœ žœ¡˜?K˜Kšœžœžœ˜+šœžœžœ˜!Kšœžœ˜ Kšœžœ˜ K˜˜Kšœžœ¡˜%Kšœžœ¡˜'—˜Kšœ žœ¡˜/Kšœ žœ¡˜0—K˜K˜—˜VK˜K˜2K˜&K˜,K˜K˜Kšœžœ˜,K˜K˜—˜NK˜K˜2K˜&K˜,K˜K˜Kšœžœ ˜ K˜K˜—š  œžœ3žœ"žœžœžœ˜¹Kšœžœ˜6Kšžœ žœžœ'˜>Kšžœžœžœf˜‚K˜®K˜;K˜;Kšžœ˜Kšœ˜K˜—š œžœžœžœ˜TKšžœ&˜,K˜K˜—š œžœžœžœ˜LK˜2š žœžœžœ0žœžœž˜Ršžœžœ˜!K˜\Kšžœ˜K˜—Kšžœ˜—Kšžœj˜oK˜—K˜š œžœžœ˜Fšžœžœž˜Kšœžœ ˜'KšžœžœDžœ˜n—Kšœ˜K˜—š œ˜9Kšœžœ/˜CKšœžœ-˜9Kšœžœ-˜9K˜^K˜]K˜bK˜LK˜[K˜¶Kšžœ˜K˜K˜—š œ˜3Kšœžœ˜1Kšžœ ˜K˜K˜—š œ˜Kšœžœ˜1Kšœžœ˜Kšœžœ˜K˜Kš œžœžœžœžœžœžœ˜AKšœžœ'˜.Kšœžœ'˜.šžœ ž˜šœ¡:˜JK˜ K˜ K˜—˜ K˜0K˜0K˜0K˜—˜K˜.K˜—Kšžœžœ˜—K˜K˜—š œ˜-Kšœžœ˜1š œ˜&K˜#K˜#šžœ žœ˜Kšžœ˜ šžœ˜K˜SK˜——K˜'K˜,šžœ"žœžœ˜EKšžœ:˜>—K˜—K˜ZKšžœ ˜K˜K˜—š œ˜%Kšœžœ ˜$Kšœžœ ˜%Kš žœžœžœžœžœ˜FKš žœžœ5žœžœžœ˜OKš žœžœBžœžœžœ˜\Kš žœžœLžœžœžœ˜fKšžœžœ˜K˜K˜—š  œžœžœžœIžœžœ˜½K˜;K˜EKšœ žœ˜"Kšœ žœ4˜Dšœžœ˜.K˜K˜K˜ K˜K˜K˜3K˜K˜#K˜K˜—Kšœ žœžœ)˜>Kšœžœžœ˜5š œ/žœ žœžœžœ˜€K™ÅK™v—Kšžœ1žœT˜K˜K˜—š œžœžœžœžœKžœžœ˜ÁKšžœ£˜©K˜K˜——™ Kšœ+™+Kšœ žœ˜*Kšœ žœžœ˜'šœžœžœ˜Kšœžœ˜ Kšœžœ˜ K˜Kšœ žœžœ˜#K˜Kšœ˜—K˜šœžœ!žœžœ=˜}K˜—š  Ðnt œžœžœžœžœ˜Ošžœžœžœ ž˜;Kšœžœ Ïtœ˜2Kš žœžœžœžœ`žœ˜Kšžœžœžœžœ ˜6Kšžœ˜—Kšœ˜K˜—š  ¥ œžœžœžœžœ˜Qšžœžœ žœ˜*Kšžœžœ˜'Kšžœžœžœ˜—K˜K˜—˜LK˜K˜0K˜$K˜*K˜K˜Kšœžœ ˜ Kšœ˜K˜—šœ+¡%˜PK˜—–Postfix!(xerox/solid/cyan) textNamedColorPostfix$(xerox/solid/magenta) textNamedColorPostfix#(xerox/solid/yellow) textNamedColorAšœÏvœA˜IK˜K˜0K˜$K˜*K˜K˜Kšœžœ ¡%˜FKšœ˜K˜—š  œžœžœžœžœ žœ ˜bK™£šžœ'žœž˜6˜Kšžœoœ˜vKšžœF˜LK˜—Kšžœžœ˜'—Kšœ˜—Kšœ žœ:˜Kš œ˜7Kšœžœ/˜CKšœž œ žœ*˜IKšœžœ3˜;Kšœžœ3˜;Kšœžœ;˜OK˜`Kšœ žœ2˜>š  œžœ žœžœ˜;Kšžœžœ=˜KKšœ˜—K˜nKšœ[˜[K˜WKšœƒ¦œY˜àKšžœ˜Kšœ˜K˜—š œ˜+Kšœžœ˜/š œ˜0š žœžœžœ-žœžœž˜QK˜#Kšžœ˜—Kšœ˜—š œ˜&K˜K˜šžœ žœ˜Kšžœ˜ šžœ˜K˜RKšœ˜——˜"Kšœ¦œ(˜2—˜JK˜—šžœ"žœžœ˜EKšžœ:˜>—Kšœ˜—KšœZ˜ZKšžœ ˜K˜K˜—š œ˜1Kšœžœ˜/Kšžœ ˜Kšœ˜K˜—š  œ˜Kšœžœ˜/K–.Postfix (xerox/highlight) textNamedColor˜©K˜K˜—–0Postfix (xerox/highlight) textNamedColorš œžœžœžœžœ'žœžœP˜ÁKšœžœ¡˜(Kšœžœ˜ šžœžœ˜;šžœ˜Kšœ žœ˜(šžœžœžœž˜K˜)Kšžœ˜—K˜—šžœ˜š žœžœžœ(žœžœž˜LKšœžœ5˜=Kšœžœ˜Kšœ žœ˜*Kšœ žœ ˜š  œžœžœžœžœžœ˜1Kšžœ(˜.Kšœ˜—šžœ ž˜K˜Kšžœžœ@žœ<˜—K˜K˜K˜K˜ Kšžœ˜—šžœ ž˜˜K˜5K˜—˜ K˜K˜K˜K˜—Kšžœžœ˜—Kšœ˜——Kšœ˜K˜—š œ˜#Kšœžœ ˜"Kšœžœ ˜#Kš žœžœžœžœžœžœ˜WKš žœžœ0žœžœžœ˜JKš žœžœ3žœžœžœ˜MKšžœžœ˜K˜K˜—š  œžœžœžœžœžœ˜GKšœžœžœ˜!Kšœžœžœ˜!š žœ žœžœ žœž˜#Kš žœžœžœžœ¡D˜vK˜K˜Kšžœ˜—Kšžœ˜K˜K˜—š œžœ žœžœ˜?Kšœžœžœ2žœ ˜iKšžœ˜K˜K˜—š œžœ žœžœ žœžœžœžœžœ˜ƒKšœžœžœžœ˜,š žœžœž œžœž˜(Kšœžœ!˜6Kšžœ˜—Kšžœ˜K˜K˜—šœžœžœ7˜OK˜—š œžœžœ žœ žœžœ'žœ žœžœ+žœžœ˜æKšœžœžœ9˜PK–[Postfix!(xerox/solid/cyan) textNamedColorPostfix$(xerox/solid/magenta) textNamedColorPostfix#(xerox/solid/yellow) textNamedColorš œžœ3žœ§œžœ˜rKšœžœb˜yKšžœ1žœW˜’Kšœ˜K˜—š  œžœžœ žœžœ˜JKšžœp˜vKšœ˜K˜—K˜5Kšžœžœžœ˜š   œžœžœžœžœ˜EKšœžœ˜6K˜K˜+K˜+K˜+K˜+Kšžœ ˜Kšœ˜——™ š œžœžœžœ˜YKšœ˜šžœžœžœž˜Kšœ0˜0Kšœžœ˜#KšžœžœJžœ˜qKšžœ˜—Kšœ˜K˜—š œžœžœ'žœžœžœžœžœ˜…Kšœžœ#˜,Kšœžœ˜9Kšœ$˜$Kš žœžœžœ žœžœ˜=Kšžœ ˜K˜K™—š  œžœžœ-žœžœžœ˜rKšžœžœžœ˜#Kšžœ*žœžœ˜7KšžœžœH˜RK˜K˜—š œžœžœDžœ˜{KšžœžœN˜XK˜K˜—šœžœžœ!žœ ˜EK˜—š   œžœžœžœžœ˜QKšœ˜šžœž˜Kšœ¡˜4Kšœ¡ ˜*Kšžœžœ˜—Kšœ,˜,K˜K˜—š  œžœžœžœžœžœ˜AKšœžœ˜Kš œžœžœ žœ žœ˜9Kšœžœ¡˜-Kš žœžœžœ žœžœ˜9Kšžœžœ˜ K˜K˜—š  œžœžœžœžœ˜YKšœ'žœG˜qKšžœžœEžœK˜K˜K˜—š  œžœžœžœžœ˜\KšœžœYžœ˜”K˜/Kšžœ ˜K˜K˜—˜+Kšœ:˜:K˜—šœ+˜+Kšœ ˜ K˜—šœ+˜+K˜YK˜—šœ+˜+K˜QK˜—˜+˜K˜ K˜ Kšœžœ˜ K˜K˜QK˜—K˜ K˜—K™NK˜R–˜Postfix!(xerox/solid/cyan) textNamedColorPostfix$(xerox/solid/magenta) textNamedColorPostfix#(xerox/solid/yellow) textNamedColor/Postfix!(xerox/solid/cyan) textNamedColorPostfix$(xerox/solid/magenta) textNamedColorPostfix#(xerox/solid/yellow) textNamedColoršœ§œžœ§œ˜NK˜——Kšžœ˜Kš ™šžœžœžœ™šžœ†žœ™ŽKšœ™Kšœ¦œ™Kšœ¦œ™Kšœ¤œ ™Kšœ¤œ™Kšœ¤œ™Kšœ ™ Kšœ ™ Kšœ ™ Kšœ™Kšœ™Kšœ™K™—Kšœ™—K™K˜—…—Ùè