ImagerColorImpl.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Stone, June 25, 1985 5:15:17 pm PDT
Michael Plass, August 1, 1985 5:13:38 pm PDT
Doug Wyatt, March 6, 1986 10:18:48 pm PST
DIRECTORY
Imager USING [Error],
ImagerColor,
ImagerColorPrivate,
ImagerPixelArray USING [GetPixels, MaxSampleValue, PixelArray],
ImagerSample,
Real USING [FixI, Round],
RealFns USING [Power],
RuntimeError USING [BoundsFault],
SF;
ImagerColorImpl: CEDAR PROGRAM
IMPORTS Imager, ImagerPixelArray, ImagerSample, Real, RealFns, RuntimeError
EXPORTS ImagerColor, ImagerColorPrivate
~ BEGIN OPEN ImagerColorPrivate, ImagerColor;
ColorOperatorClass: TYPE ~ ImagerColorPrivate.ColorOperatorClass;
ColorOperatorClassRep: PUBLIC TYPE ~ ImagerColorPrivate.ColorOperatorClassRep;
RGBCalibrationImpl: TYPE ~ ImagerColorPrivate.RGBCalibrationImpl;
RGBCalibrationImplRep: PUBLIC TYPE ~ ImagerColorPrivate.RGBCalibrationImplRep;
defaultCalibration: RGBCalibration ←
NIL;
GetDefaultCalibration:
PUBLIC
PROC
RETURNS [RGBCalibration] ~ {
RETURN[defaultCalibration];
};
CreateCalibration:
PUBLIC
PROC [type:
ATOM, red, green, blue: Chromaticity,
white: Chromaticity, YMax:
REAL ← 100]
RETURNS [RGBCalibration] ~ {
ERROR;
};
CIEFromRGB:
PUBLIC
PROC [rgb:
RGB, calibration: RGBCalibration ←
NIL]
RETURNS [
XYZ] ~ {
cal: RGBCalibration ~ IF calibration=NIL THEN defaultCalibration ELSE calibration;
impl: RGBCalibrationImpl ~ cal.impl;
RETURN[[
X: impl.cXR*rgb.R+impl.cXG*rgb.G+impl.cXB*rgb.B,
Y: impl.cYR*rgb.R+impl.cYG*rgb.G+impl.cYB*rgb.B,
Z: impl.cZR*rgb.R+impl.cZG*rgb.G+impl.cZB*rgb.B
]];
};
RGBFromCIE:
PUBLIC
PROC [cie:
XYZ, calibration: RGBCalibration ←
NIL]
RETURNS [
RGB] ~ {
cal: RGBCalibration ~ IF calibration=NIL THEN defaultCalibration ELSE calibration;
impl: RGBCalibrationImpl ~ cal.impl;
RETURN[[
R: impl.cRX*cie.X+impl.cRY*cie.Y+impl.cRZ*cie.Z,
G: impl.cGX*cie.X+impl.cGY*cie.Y+impl.cGZ*cie.Z,
B: impl.cBX*cie.X+impl.cBY*cie.Y+impl.cBZ*cie.Z
]];
};
RGBMaxY:
PUBLIC
PROC [c: Chromaticity, calibration: RGBCalibration ←
NIL]
RETURNS [Y:
REAL] ~ {
We want to find the maximum value of Y such that c.x, c.y, Y is inside of the current RGB gamut. (x,y,z)*S=(X,Y,Z). (x,y,z)*S*CIEToRGB=(r,g,b)*S. We want to find the maximum value for S such that R, G and B <=1. Find the MAX[r,g,b]. S=1/max. Y=y*S.
NOTE: there is no chromaticity value for black (r=g=b=0). Black is Y=0.
cie: XYZ ← [X: c.x, Y: c.y, Z: 1-(c.x+c.y)];
rgb: RGB ← RGBFromCIE[cie, calibration];
max: REAL ← MAX[MAX[rgb.R, rgb.G],rgb.B];
Y ← c.y/max; --it would be an unusual device that had max=0
};
ChromaticityFromCIE:
PUBLIC
PROC [c:
XYZ]
RETURNS [Chromaticity] ~ {
sum: REAL ~ c.X+c.Y+c.Z;
RETURN[[x: c.X/sum, y: c.Y/sum]];
};
CIEFromChromaticity:
PUBLIC
PROC [c: Chromaticity, Y:
REAL]
RETURNS [
XYZ] ~ {
scale: REAL ~ Y/c.y;
RETURN[[X: c.x*scale, Y: Y, Z: (1-c.x-c.y)*scale]];
};
ToRange:
PROC[v:
REAL]
RETURNS[
REAL] =
INLINE {
IF v IN[0..1] THEN RETURN[v] ELSE ERROR RuntimeError.BoundsFault;
};
ensures that v is in [0..1]; raises BoundsFault if not
These algorithms use the hexacone model described in
"Color Gamut Transform Pairs" by Alvy Ray Smith
Siggraph 1978, p. 12.
Algorithms from Foley and van Dam
HSLFromRGB:
PUBLIC
PROC [val:
RGB]
RETURNS [
HSL] ~ {
max,min,rc,gc,bc,del, h, s, l: REAL;
red: REAL ← ToRange[val.R];
green: REAL ← ToRange[val.G];
blue: REAL ← ToRange[val.B];
max ← MAX[red,MAX[green,blue]];
min ← MIN[red,MIN[green,blue]];
l ← (max+min)/2;
IF max=min THEN RETURN[[0,0,l]]; --gray
del ← max-min;
s ← IF l <= 0.5 THEN del/(max+min) ELSE del/(2-max-min);
rc ← (max-red)/del;
gc ← (max-green)/del;
bc ← (max-blue)/del;
IF max = red THEN h ← bc-gc --between yellow and magenta
ELSE IF max = green THEN h ← 2+rc-bc --between cyan and yellow
ELSE IF max = blue THEN h ← 4+gc-rc --between magenta and cyan
ELSE ERROR Imager.Error[[$invalidColor, "Invalid RGB color"]];
h ← h/6.0;
IF h < 0 THEN h ← h+1;
RETURN[[h, s, l]];
};
RGBFromHSL:
PUBLIC
PROC [val:
HSL]
RETURNS [
RGB] ~ {
m1,m2,hue,saturation, lightness, r, g, b: REAL;
Value:
PROC[n1,n2,h1:
REAL]
RETURNS[v:
REAL] = {
IF h1 > 360 THEN h1 ← h1-360;
IF h1 < 0 THEN h1 ← h1+360;
v ←
SELECT
TRUE
FROM
h1 IN [0..60) => n1+(n2-n1)*h1/60,
h1 IN [60..180) => n2,
h1 IN [180..240) => n1+(n2-n1)*(240-h1)/60,
ENDCASE => n1;
};
IF val.S=0 THEN RETURN[[val.L, val.L, val.L]];
saturation ← ToRange[val.S];
lightness ← ToRange[val.L];
hue ← 360*ToRange[val.H];
m2 ←
IF lightness <= 0.5
THEN lightness*(1+saturation)
ELSE lightness+saturation-lightness*saturation;
m1 ← 2*lightness-m2;
r ← Value[m1,m2,hue+120];
g ← Value[m1,m2,hue];
b ← Value[m1,m2,hue-120];
RETURN[[r, g, b]];
};
HSVFromRGB:
PUBLIC
PROC [val:
RGB]
RETURNS [
HSV] ~ {
r, g, b, h, s, v: REAL ← 0;
max,min,rc,gc,bc: REAL;
r ← ToRange[val.R]; g ← ToRange[val.G]; b ← ToRange[val.B];
min ← MIN[MIN[r,g],b]; --amount of white
v ← max ← MAX[MAX[r,g],b];--maximum "brightness"
IF max#0 THEN s ← (max-min)/max
ELSE s ← 0;
IF s=0 THEN RETURN[[0,0,v]]; --gray
rc ← (max - r)/(max - min);
gc ← (max - g)/(max - min);
bc ← (max - b)/(max - min);
IF r=max THEN h-gc
ELSE IF g=max THEN h𡤂+rc-bc
ELSE IF b=max THEN h𡤄+gc-rc;
h ← h / 6.0;
IF h<0 THEN h←h+1;
RETURN[[h, s, v]];
};
RGBFromHSV:
PUBLIC
PROC [val:
HSV]
RETURNS [
RGB] ~ {
hue, saturation, value: REAL;
ihue: INTEGER;
fhue,m,n,k: REAL;
IF val.V=0 OR val.S=0 THEN RETURN[[val.V, val.V, val.V]];
hue ← ToRange[val.H];
saturation ← ToRange[val.S];
value ← ToRange[val.V];
hue ← hue*6;
ihue ← Real.FixI[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]];
};
YIQfromRGB:
[ 0.30, 0.59, 0.11],
[ 0.60, -0.28, -0.32],
[ 0.21, -0.52, 0.31]
YIQFromRGB:
PUBLIC
PROC [val:
RGB]
RETURNS [
YIQ] ~ {
0.3, 0.59, 0.11
0.6, -0.28, -0.32
0.21, -0.52, 0.31
RETURN[[
Y: 0.30*val.R+0.59*val.G+0.11*val.B,
I: 0.60*val.R-0.28*val.G-0.32*val.B,
Q: 0.21*val.R-0.52*val.G+0.31*val.B
]];
};
RGBFromYIQ:
PUBLIC
PROC [val:
YIQ]
RETURNS [
RGB] ~ {
1.0, 0.9482623, 0.6240127
1.0, -0.2760664, -0.6398104
1.0, -1.10545, 1.729858
RETURN[[
R: 1.0*val.Y+0.9482623*val.I+0.6240127*val.Q,
G: 1.0*val.Y-0.2760664*val.I-0.6398104*val.Q,
B: 1.0*val.Y-1.10545*val.I+1.729858*val.Q
]];
};
IntensityFromGray:
PROC [f:
REAL]
RETURNS [
REAL] ~ {
IF f>=1 THEN RETURN[0];
IF f<=0 THEN RETURN[1];
RETURN[1-f];
};
IntensityFromRGB:
PROC [val:
RGB]
RETURNS [
REAL] ~ {
Y: REAL ~ 0.30*val.R+0.59*val.G+0.11*val.B;
IF Y<=0 THEN RETURN[0];
IF Y>=1 THEN RETURN[1];
RETURN[Y];
};
Apply:
PUBLIC
PROC [self: ColorOperator, pixel: PixelProc]
RETURNS [ConstantColor] ~ {
size: NAT ~ self.samplesPerPixelIn;
color: ConstantColor ~ NEW[ColorRep.constant[size] ← [constant[colorOperator: self, pixel: ]]];
FOR i: NAT IN[0..size) DO color.pixel[i] ← pixel[i] ENDLOOP;
RETURN[color];
};
TupleFromPixel:
PUBLIC
PROC [self: ColorOperator, output: ColorOutput,
pixelIn: PixelProc, tupleAction:
PROC [tupleOut: TupleProc]] ~ {
class: ColorOperatorClass ~ self.class;
class.TupleFromPixel[self, output, pixelIn, tupleAction];
};
PixelFromPixel:
PUBLIC
PROC [self: ColorOperator, output: ColorOutput,
pixelIn: PixelProc, maxOut: PixelProc, pixelAction:
PROC [pixelOut: PixelProc]] ~ {
class: ColorOperatorClass ~ self.class;
tupleAction:
PROC [tupleOut: TupleProc] ~ {
pixelOut: PixelProc ~ { RETURN[Real.Round[maxOut[i]*tupleOut[i]]] };
pixelAction[pixelOut];
};
class.TupleFromPixel[self, output, pixelIn, tupleAction];
};
TranslatePixels:
PUBLIC
PROC [self: ColorOperator, output: ColorOutput,
maxIn: PixelProc, maxOut: PixelProc, translateAction:
PROC [translate: TranslateProc]] ~ {
class: ColorOperatorClass ~ self.class;
IF class.TranslatePixels=
NIL
THEN {
slowTranslate: TranslateProc ~ {
samplesPerPixelOut: NAT ~ output.samplesPerPixelOut;
FOR j:
NAT
IN[0..pixelsIn.length)
DO
pixelIn: PixelProc ~ { RETURN[pixelsIn[i][j]] };
pixelOutAction:
PROC [pixelOut: PixelProc] ~ {
FOR i:
NAT
IN[0..samplesPerPixelOut)
DO
pixelsOut[i][j] ← pixelOut[i];
ENDLOOP;
};
PixelFromPixel[self, output, pixelIn, maxOut, pixelOutAction];
ENDLOOP;
};
translateAction[slowTranslate];
}
ELSE class.TranslatePixels[self, output, maxIn, maxOut, translateAction];
};
Translate:
PUBLIC
PROC [self: ColorOperator, output: ColorOutput,
pa: PixelArray, maxOut: PixelProc]
RETURNS [PixelMap] ~ {
size: SF.Vec ~ [s: NAT[pa.sSize], f: NAT[pa.fSize]];
samplesPerPixelIn: NAT ~ pa.samplesPerPixel;
samplesPerPixelOut: NAT ~ output.samplesPerPixelOut;
maxIn: PixelProc ~ { RETURN[pa.MaxSampleValue[i]] };
pm: PixelMap ~ ImagerSample.NewPixelMap[samplesPerPixelOut, size, maxOut];
translateAction:
PROC [translate: TranslateProc] ~ {
pixelsIn: PixelBuffer ~ ImagerSample.ObtainScratchPixels[samplesPerPixelIn, size.f];
pixelsOut: PixelBuffer ~ ImagerSample.ObtainScratchPixels[samplesPerPixelOut, size.f];
FOR s:
NAT
IN[0..size.s)
DO
pa.GetPixels[s: s, f: 0, pixels: pixelsIn];
translate[pixelsIn: pixelsIn, pixelsOut: pixelsOut];
pm.PutPixels[min: [s: s, f: 0], pixels: pixelsOut];
ENDLOOP;
ImagerSample.ReleaseScratchPixels[pixelsOut];
ImagerSample.ReleaseScratchPixels[pixelsIn];
};
TranslatePixels[self, output, maxIn, maxOut, translateAction];
RETURN[pm];
};
NewColorOperatorClass:
PUBLIC
PROC [
name:
ROPE,
TupleFromPixel: TupleFromPixelProc,
TranslatePixels: TranslatePixelsProc
]
RETURNS [ColorOperatorClass] ~ {
class: ColorOperatorClass ~
NEW[ColorOperatorClassRep ← [name: name,
TupleFromPixel: TupleFromPixel, TranslatePixels: TranslatePixels]];
RETURN[class];
};
TranslatePixelsTable:
PROC [self: ColorOperator, output: ColorOutput,
maxIn: PixelProc, maxOut: PixelProc, translateAction:
PROC [translate: TranslateProc]] ~ {
maxIn0: Sample ~ maxIn[0];
samplesPerPixelOut: NAT ~ output.samplesPerPixelOut;
table: PixelBuffer ~ ImagerSample.ObtainScratchPixels[samplesPerPixelOut, maxIn0+1];
tableTranslate: TranslateProc ~
TRUSTED {
count: NAT ~ pixelsIn.length;
FOR i:
NAT
IN[0..samplesPerPixelOut)
DO
samplesIn: SampleBuffer ~ pixelsIn[0];
samplesOut: SampleBuffer ~ pixelsOut[i];
samplesTable: SampleBuffer ~ table[i];
pointerIn: LONG POINTER TO ImagerSample.RawSamples ←
samplesIn.PointerToSamples[start: 0, count: count];
pointerOut: LONG POINTER TO ImagerSample.RawSamples ←
samplesOut.PointerToSamples[start: 0, count: count];
THROUGH [0..count/8)
DO
pointerOut[0] ← samplesTable[pointerIn[0]];
pointerOut[1] ← samplesTable[pointerIn[1]];
pointerOut[2] ← samplesTable[pointerIn[2]];
pointerOut[3] ← samplesTable[pointerIn[3]];
pointerOut[4] ← samplesTable[pointerIn[4]];
pointerOut[5] ← samplesTable[pointerIn[5]];
pointerOut[6] ← samplesTable[pointerIn[6]];
pointerOut[7] ← samplesTable[pointerIn[7]];
pointerIn ← pointerIn+8; pointerOut ← pointerOut+8;
ENDLOOP;
THROUGH [0..count
MOD 8)
DO
pointerOut[0] ← samplesTable[pointerIn[0]];
pointerIn ← pointerIn+1; pointerOut ← pointerOut+1;
ENDLOOP;
ENDLOOP;
};
FOR s0: Sample
IN[0..maxIn0]
DO
pixelIn: PixelProc ~ { check: [0..1) ~ i; RETURN[s0] };
pixelOutAction:
PROC [pixelOut: PixelProc] ~ {
FOR i: NAT IN[0..samplesPerPixelOut) DO table[i][s0] ← pixelOut[i] ENDLOOP;
};
PixelFromPixel[self, output, pixelIn, maxOut, pixelOutAction];
ENDLOOP;
translateAction[tableTranslate];
ImagerSample.ReleaseScratchPixels[table];
};
SampleTableProc:
TYPE ~
PROC [Sample]
RETURNS [
REAL];
NewSampleTable:
PROC [size:
NAT, proc: SampleTableProc]
RETURNS [SampleTable] ~ {
IF size=0 THEN RETURN[NIL]
ELSE {
map: SampleTable ~ NEW[SampleTableRep[size]];
FOR i: Sample IN[0..size) DO map[i] ← proc[i] ENDLOOP;
RETURN[map];
};
};
classGrayLinear: ColorOperatorClass ~ NewColorOperatorClass[
name: "Xerox/GrayLinear",
TupleFromPixel: TupleFromPixelGrayLinear,
TranslatePixels: TranslatePixelsTable
];
TupleFromPixelGrayLinear: TupleFromPixelProc ~ {
data: DataGrayLinear ~ NARROW[self.data];
s0: Sample ~ pixelIn[0];
s: REAL ~ IF data.map=NIL THEN REAL[s0] ELSE data.map[s0];
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;
tupleOut: TupleProc ~ { RETURN[x] };
tupleAction[tupleOut];
};
NewColorOperatorGrayLinear:
PUBLIC
PROC [sWhite, sBlack:
REAL,
sampleTableSize: Sample ← 0, sampleTableProc: SampleTableProc ←
NIL
]
RETURNS [ColorOperator] ~ {
data: DataGrayLinear ~
NEW[DataGrayLinearRep ← [
sWhite: sWhite, sBlack: sBlack,
map: NewSampleTable[sampleTableSize, sampleTableProc]
]];
RETURN[
NEW[ColorOperatorRep ← [
chromatic: FALSE, samplesPerPixelIn: 1,
class: classGrayLinear, data: data
]]];
};
classGrayDensity: ColorOperatorClass ~ NewColorOperatorClass[
name: "Xerox/GrayDensity",
TupleFromPixel: TupleFromPixelGrayDensity,
TranslatePixels: TranslatePixelsTable
];
TupleFromPixelGrayDensity: TupleFromPixelProc ~ {
data: DataGrayDensity ~ NARROW[self.data];
s0: Sample ~ pixelIn[0];
s: REAL ~ IF data.map=NIL THEN REAL[s0] ELSE data.map[s0];
d: REAL ~ ((s-data.sWhite)/(data.sBlack-data.sWhite))*data.dBlack;
f: REAL ~ RealFns.Power[base: 10, exponent: -d];
x: REAL ~ IF f<=0 THEN 1 ELSE IF f>=1 THEN 0 ELSE 1-f;
tupleOut: TupleProc ~ { RETURN[x] };
tupleAction[tupleOut];
};
NewColorOperatorGrayDensity:
PUBLIC
PROC [sWhite, sBlack, dBlack:
REAL,
sampleTableSize: Sample ← 0, sampleTableProc: SampleTableProc ←
NIL
]
RETURNS [ColorOperator] ~ {
data: DataGrayDensity ~
NEW[DataGrayDensityRep ← [
sWhite: sWhite, sBlack: sBlack, dBlack: dBlack,
map: NewSampleTable[sampleTableSize, sampleTableProc]
]];
RETURN[
NEW[ColorOperatorRep ← [
chromatic: FALSE, samplesPerPixelIn: 1,
class: classGrayDensity, data: data
]]];
};
classGrayVisual: ColorOperatorClass ~ NewColorOperatorClass[
name: "Xerox/GrayVisual",
TupleFromPixel: TupleFromPixelGrayVisual,
TranslatePixels: TranslatePixelsTable
];
TupleFromPixelGrayVisual: TupleFromPixelProc ~ {
data: DataGrayVisual ~ NARROW[self.data];
s0: Sample ~ pixelIn[0];
s: REAL ~ IF data.map=NIL THEN REAL[s0] ELSE data.map[s0];
L: REAL ~ (s-data.sBlack)/(data.sWhite-data.sBlack);
Y: REAL ~ IF L<=0.09 THEN L/0.09 ELSE RealFns.Power[base: (L+0.16)/0.25, exponent: 3];
f: REAL ~ 1-0.01*Y;
x: REAL ~ IF f<=0 THEN 1 ELSE IF f>=1 THEN 0 ELSE 1-f;
tupleOut: TupleProc ~ { RETURN[x] };
tupleAction[tupleOut];
};
NewColorOperatorGrayVisual:
PUBLIC
PROC [sWhite, sBlack:
REAL,
sampleTableSize: Sample ← 0, sampleTableProc: SampleTableProc ←
NIL
]
RETURNS [ColorOperator] ~ {
data: DataGrayVisual ~
NEW[DataGrayVisualRep ← [
sWhite: sWhite, sBlack: sBlack,
map: NewSampleTable[sampleTableSize, sampleTableProc]
]];
RETURN[
NEW[ColorOperatorRep ← [
chromatic: FALSE, samplesPerPixelIn: 1,
class: classGrayVisual, data: data
]]];
};
classMap: ColorOperatorClass ~ NewColorOperatorClass[
name: "Xerox/Map",
TupleFromPixel: TupleFromPixelMap,
TranslatePixels: TranslatePixelsTable
];
TupleFromPixelMap: TupleFromPixelProc ~ {
data: DataMap ~ NARROW[self.data];
color: ConstantColor ~ data[pixelIn[0]];
TupleFromColor[color, output, tupleAction];
};
NewColorOperatorMap:
PUBLIC
PROC [
maxSampleValue: Sample, map:
PROC [Sample]
RETURNS [ConstantColor]
]
RETURNS [ColorOperator] ~ {
data: DataMap ~ NEW[DataMapRep[maxSampleValue+1] ← [v: ]];
chromatic: BOOL ← FALSE;
FOR s0: Sample
IN[0..maxSampleValue]
DO
color: ConstantColor ~ map[s0];
data.v[s0] ← color;
chromatic ← chromatic OR color.colorOperator.chromatic;
ENDLOOP;
RETURN[
NEW[ColorOperatorRep ← [
chromatic: chromatic, samplesPerPixelIn: 1,
class: classMap, data: data
]]];
};
TupleFromPixelBuildMap: TupleFromPixelProc ~ {
data: DataBuildMap ~ NARROW[self.data];
pixelMapped: PixelProc ~ { check: [0..1) ~ i; s0: Sample ~ pixelIn[0]; RETURN[data[s0]] };
TupleFromPixel[data.colorOperator, output, pixelMapped, tupleAction];
};
classBuildMap: ColorOperatorClass ~
NEW[ColorOperatorClassRep ← [
name: "Xerox/BuildMap",
TupleFromPixel: TupleFromPixelBuildMap
]];
NewColorOperatorBuildMap:
PUBLIC
PROC [colorOperator: ColorOperator,
maxSampleValue: Sample, map:
PROC [Sample]
RETURNS [Sample]
]
RETURNS [ColorOperator] ~ {
data: DataBuildMap ~
NEW[DataBuildMapRep[maxSampleValue+1] ← [
colorOperator: colorOperator, v:
]];
FOR s0: Sample IN[0..maxSampleValue] DO data.v[s0] ← map[s0] ENDLOOP;
RETURN[
NEW[ColorOperatorRep ← [
chromatic: colorOperator.chromatic, samplesPerPixelIn: 1,
class: classBuildMap, data: data
]]];
};
TupleFromPixelRGB: TupleFromPixelProc ~ {
data: DataRGB ~ NARROW[self.data];
tupleRGB: TupleProc ~ {
check: [0..3) ~ i;
value: Sample ~ pixelIn[i];
max: Sample ~ data.maxIn;
RETURN[MIN[value, max]/REAL[max]];
};
tupleY: TupleProc ~ {
check: [0..1) ~ i;
val: RGB ~ [R: tupleRGB[0], G: tupleRGB[1], B: tupleRGB[2]];
RETURN[IntensityFromRGB[val]];
};
SELECT output.type
FROM
$RGB => tupleAction[tupleRGB];
$Y => tupleAction[tupleY];
ENDCASE => ERROR;
};
TranslatePixelsRGB:
PROC [self: ColorOperator, output: ColorOutput,
maxIn: PixelProc, maxOut: PixelProc, translateAction:
PROC [translate: TranslateProc]] ~ {
data: DataRGB ~ NARROW[self.data];
};
classRGB: ColorOperatorClass ~
NEW[ColorOperatorClassRep ← [
name: "Xerox/Research/RGB",
TupleFromPixel: TupleFromPixelRGB,
TranslatePixels: TranslatePixelsRGB
]];
NewColorOperatorRGB:
PUBLIC
PROC [maxIn: Sample]
RETURNS [ColorOperator] ~ {
data: DataRGB ~ NEW[DataRGBRep ← [maxIn: maxIn]];
RETURN[
NEW[ColorOperatorRep ← [
chromatic: TRUE, samplesPerPixelIn: 3,
class: classRGB, data: data
]]];
};
TupleFromPixelColorMap8: TupleFromPixelProc ~ {
data: DataColorMap8 ~ NARROW[self.data];
s0: Sample ~ pixelIn[0];
triple: ColorValueTriple ~ data[s0];
max: ColorValue ~ ColorValue.LAST;
tupleRGB: TupleProc ~ {
value: ColorValue ~ triple[i];
RETURN[REAL[value]/REAL[max]];
};
tupleY: TupleProc ~ {
check: [0..1) ~ i;
val: RGB ~ [R: tupleRGB[0], G: tupleRGB[1], B: tupleRGB[2]];
RETURN[IntensityFromRGB[val]];
};
SELECT output.type
FROM
$RGB => tupleAction[tupleRGB];
$Y => tupleAction[tupleY];
ENDCASE => ERROR;
};
classColorMap8: ColorOperatorClass ~ NewColorOperatorClass[
name: "Xerox/Research/ColorMap8",
TupleFromPixel: TupleFromPixelColorMap8,
TranslatePixels: TranslatePixelsTable
];
NewColorOperatorColorMap8:
PUBLIC
PROC [
map:
PROC [ChannelValue]
RETURNS [ColorValueTriple]
]
RETURNS [ColorOperator] ~ {
data: DataColorMap8 ~ NEW[DataColorMap8Rep];
FOR i: ChannelValue IN ChannelValue DO data[i] ← map[i] ENDLOOP;
RETURN[
NEW[ColorOperatorRep ← [
chromatic: TRUE, samplesPerPixelIn: 1,
class: classColorMap8, data: data
]]];
};
TupleFromColor:
PUBLIC
PROC [self: ConstantColor, output: ColorOutput,
tupleAction:
PROC [tupleOut: TupleProc]] ~ {
pixelIn: PixelProc ~ { RETURN[self.pixel[i]] };
TupleFromPixel[self.colorOperator, output, pixelIn, tupleAction];
};
PixelFromColor:
PUBLIC
PROC [self: ConstantColor, output: ColorOutput,
maxOut: PixelProc, pixelAction:
PROC [pixelOut: PixelProc]] ~ {
pixelIn: PixelProc ~ { RETURN[self.pixel[i]] };
PixelFromPixel[self.colorOperator, output, pixelIn, maxOut, pixelAction];
};
outputIntensity: ColorOutput ← NIL;
outputRGB: ColorOutput ← NIL;
IntensityFromColor:
PUBLIC
PROC [self: ConstantColor]
RETURNS [Y:
REAL ← 0] ~ {
pixelIn: PixelProc ~ { RETURN[self.pixel[i]] };
tupleAction: PROC [tupleOut: TupleProc] ~ { Y ← tupleOut[0] };
TupleFromPixel[self.colorOperator, outputIntensity, pixelIn, tupleAction];
};
RGBFromColor:
PUBLIC
PROC [self: ConstantColor]
RETURNS [rgb:
RGB ← [0, 0, 0]] ~ {
pixelIn: PixelProc ~ { RETURN[self.pixel[i]] };
tupleAction:
PROC [tupleOut: TupleProc] ~ {
rgb ← [R: tupleOut[0], G: tupleOut[1], B: tupleOut[2]];
};
TupleFromPixel[self.colorOperator, outputRGB, pixelIn, tupleAction];
};
IntensityFromStipple:
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: StippleData ~ NEW[StippleDataRep ← [word: word, function: function]];
RETURN[NEW[ColorRep.special ← [special[type: $Stipple, data: data, substitute: NIL]]]];
};
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]]]];
};
InitColorTable: PROC ~ {
PutColor[$White, NewGray[0.0]];
PutColor[$RGBWhite, NewRGB[[R: 1, G: 1, B: 1]]];
PutColor[$Black, NewGray[1.0]];
PutColor[$RGBBlack, NewRGB[[R: 0, G: 0, B: 0]]];
PutColor[$Invert, ColorFromStipple[word: WORD.LAST, function: invert]];
PutColor[$Clear, ColorFromStipple[word: 0, function: paint]];
PutColor[$Gray, NewGray[0.5]];
PutColor[$Red, NewRGB[[R: 1, G: 0, B: 0]]];
PutColor[$Green, NewRGB[[R: 0, G: 1, B: 0]]];
PutColor[$Blue, NewRGB[[R: 0, G: 0, B: 1]]];
PutColor[$Cyan, NewRGB[[R: 0, G: 1, B: 1]]];
PutColor[$Magenta, NewRGB[[R: 1, G: 0, B: 1]]];
PutColor[$Yellow, NewRGB[[R: 1, G: 1, B: 0]]];
PutColor[$Pink, NewHSL[[H: 0.0, S: 0.5, L: 0.7]]]; -- ???
PutColor[$Orange, NewHSL[[H: 0.04, S: 0.6, L: 0.4]]];
PutColor[$Brown, NewHSL[[H: 0.08, S: 0.6, L: 0.2]]];
PutColor[$Olive, NewHSL[[H: 0.25, S: 0.6, L: 0.2]]]; -- ???
PutColor[$YellowGreen, NewHSL[[H: 0.25, S: 0.6, L: 0.5]]]; -- ???
PutColor[$Purple, NewHSL[[H: 0.73, S: 0.6, L: 0.4]]];
};
END.
CubeRootFn: PROC [r: REAL] RETURNS [REAL] ~ {
IF r>0.008856 THEN RETURN[RealFns.Root[index: 3, arg: r]]
ELSE RETURN[(903.29*r+16.0)/116.0];
};
LStar: PUBLIC PROC [Y: REAL] RETURNS [REAL] ~ {
RETURN[116.0*CubeRootFn[Y/100]-16.0];
};
LABFromCIE: PUBLIC PROC [val, illum: XYZ] RETURNS [CIELAB] ~ {
fX: REAL ~ CubeRootFn[val.X/illum.X];
fY: REAL ~ CubeRootFn[val.Y/illum.Y];
fZ: REAL ~ CubeRootFn[val.Z/illum.Z];
RETURN[[L: 116*fY-16, a: 500*(fX-fY), b: 200*(fY-fZ)]];
};
CIEFromLAB: PUBLIC PROC [val: CIELAB, illum: XYZ] RETURNS [XYZ] ~ {
fY: REAL ~ (val.L+16)/116;
fX: REAL ~ fY+val.a/500;
fZ: REAL ~ fY-val.b/200;
RETURN[[X: illum.X*(fX*fX*fX), Y: illum.Y*(fY*fY*fY), Z: illum.Z*(fZ*fZ*fZ)]];
};
LUVFromCIE: PUBLIC PROC [val, illum: XYZ] RETURNS [CIELUV] ~ {
den: REAL ~ -2*val.x+12*val.y+3;
uPrime: REAL ~ 4*val.x/den;
vPrime: REAL ~ 9*val.y/den;
RETURN[[L: xxx, u: xxx, v: xxx]];
};