<> <> <> <> <> <<>> 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] ~ { <> <> 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; }; <> <<>> <> <<"Color Gamut Transform Pairs" by Alvy Ray Smith>> <> <> 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_bc-gc ELSE IF g=max THEN h_2+rc-bc ELSE IF b=max THEN h_4+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]]; }; <> <<[ 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]]]]; }; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<>> END. <> <0.008856 THEN RETURN[RealFns.Root[index: 3, arg: r]]>> <> <<};>> <<>> <> <> <<};>> <<>> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <<};>> <<>>