DIRECTORY CalibratedColor, CalibratedColorFns, Imager, ImagerColor, ColorPlotGamuts, ImagerPixel, ImagerPixelArray, ImagerInterpress, ImagerSample, ImagerFont, ImagerTransformation, Real, Rope, SF, IO, CedarProcess; ColorPlotGamutsImpl: CEDAR PROGRAM IMPORTS Imager, CalibratedColorFns, ImagerPixel, ImagerPixelArray, ImagerInterpress, ImagerSample, ImagerFont, Real, ImagerTransformation, CalibratedColor, ImagerColor, CedarProcess EXPORTS ColorPlotGamuts ~ BEGIN OPEN ColorPlotGamuts; SampleMap: TYPE = ImagerSample.SampleMap; ROPE: TYPE = Rope.ROPE; VEC: TYPE = Imager.VEC; XYZ: TYPE = CalibratedColor.XYZ; RGB: TYPE = ImagerColor.RGB; CreateMapState: PUBLIC PROC [xAxis, yAxis: Projection, res: NAT, white: XYZ, getColor: GetColorProc, getColorData: REF, backgroundColor: RGB _ [0.5, 0.5, 0.5]] RETURNS [State] = { state: State _ NEW[StateRec _ [ redMap: NIL, greenMap: NIL, blueMap: NIL, xAxis: xAxis, yAxis: yAxis, res: res, white: white, getColor: getColor, getColorData: getColorData, backgroundColor: backgroundColor, transformation: NIL --based on projections ]]; valsFromProjection: PROC[proj: Projection] RETURNS[min, max: REAL] = { SELECT proj FROM x => {min _ 0; max _ 0.8}; y => {min _ 0; max _ 0.9}; aStar => {min _ -140; max _ 135}; bStar => {min _ -125; max _ 125}; uStar => {min _ -180; max _ 195}; vStar => {min _ -175; max _ 175}; lStar, reflectance => {min _ 0; max _ 100}; hueAngle => {min _ 0; max _ 360}; ENDCASE => ERROR; }; xMin,xMax, yMin, yMax, dx, dy: REAL; [xMin,xMax] _ valsFromProjection[xAxis]; [yMin, yMax] _ valsFromProjection[yAxis]; dx _ xMax-xMin; dy _ yMax-yMin; IF dx > dy THEN [state.redMap,state.greenMap, state.blueMap] _ CreateSampleMaps[res, Real.Round[1+res*dy/dx], backgroundColor] ELSE [state.redMap,state.greenMap, state.blueMap] _ CreateSampleMaps[Real.Round[1+res*dx/dy], res, backgroundColor]; state.transformation _ ImagerTransformation.Translate[[-xMin, -yMin]]; state.transformation _ ImagerTransformation.Concat[state.transformation, ImagerTransformation.Scale[res/MAX[dx,dy]]]; RETURN[state]; }; CreateSampleMaps: PROC[xVal,yVal: NAT, backgroundColor: RGB] RETURNS[r,g,b: SampleMap] = { box: SF.Box _ [[f:0,s:0], [f: xVal+2, s: yVal+2]]; backgroundR, backgroundG, backgroundB: BYTE; [backgroundR, backgroundG, backgroundB] _ BytesFromRGB[backgroundColor]; r _ ImagerSample.NewSampleMap[box: box, bitsPerSample: 8]; g _ ImagerSample.NewSampleMap[box: box, bitsPerSample: 8]; b _ ImagerSample.NewSampleMap[box: box, bitsPerSample: 8]; ImagerSample.Fill[r,box, backgroundR]; ImagerSample.Fill[g,box, backgroundG]; ImagerSample.Fill[b,box, backgroundB]; }; BytesFromRGB: PROC [rgb: RGB] RETURNS [rByte, gByte, bByte: BYTE] ~ { rByte _ Real.Round[rgb.R*255]; gByte _ Real.Round[rgb.G*255]; bByte _ Real.Round[rgb.B*255]; }; MapColor: PUBLIC PROC [state: State, color: XYZ] RETURNS [x,y: REAL, rgb: RGB] = { [x,y] _ MapValues[state,color]; rgb _ state.getColor[color, state.getColorData]; }; MapValues: PUBLIC PROC [state: State, color: XYZ] RETURNS [x,y: REAL] = { valFromProjection: PROC[proj: Projection] RETURNS[val: REAL] = { SELECT proj FROM x => val _ CalibratedColorFns.ChromaticityFromXYZ[color].x; y => val _ CalibratedColorFns.ChromaticityFromXYZ[color].y; aStar => val _ CalibratedColorFns.CIELABFromXYZ[color, state.white].aStar; bStar => val _ CalibratedColorFns.CIELABFromXYZ[color, state.white].bStar; uStar => val _ CalibratedColorFns.CIELUVFromXYZ[color, state.white].uStar; vStar => val _ CalibratedColorFns.CIELUVFromXYZ[color, state.white].vStar; lStar => val _ CalibratedColorFns.LStarFromLuminance[color.Y, state.white.Y]; hueAngle => val _ CalibratedColorFns.HueAngleAB[ CalibratedColorFns.CIELABFromXYZ[color, state.white]]; reflectance => val _ color.Y/state.white.Y; ENDCASE => ERROR; }; x _ valFromProjection[state.xAxis]; y _ valFromProjection[state.yAxis]; [[x,y]] _ ImagerTransformation.Transform[state.transformation, [x,y]]; }; MarkColor: PUBLIC PROC[state: State, color: XYZ] = { x,y: REAL; rgb: RGB; [x,y, rgb] _ MapColor[state, color ! Real.RealException => GOTO Skip]; MarkPoint[state, Real.Round[x], Real.Round[y], rgb]; EXITS Skip => NULL; }; MarkColorVals: PUBLIC PROC[state: State, x, y: REAL, rgb: RGB] = { [[x,y]] _ ImagerTransformation.Transform[state.transformation, [x,y]]; MarkPoint[state, Real.Round[x], Real.Round[y], rgb]; }; MarkPoint: PUBLIC PROC[state: State, ix, iy: INTEGER, rgb: RGB] = { putInMap: PROC [sm: SampleMap, v: WORD] = { ImagerSample.Put[map: sm, index: [f: ix, s: iy], value: v]; ImagerSample.Put[map: sm, index: [f: ix+1, s: iy], value: v]; ImagerSample.Put[map: sm, index: [f: ix+1, s: iy+1], value: v]; ImagerSample.Put[map: sm, index: [f: ix, s: iy+1], value: v]; }; putInMap[state.redMap, Real.Round[255*rgb.R]]; putInMap[state.greenMap, Real.Round[255*rgb.G]]; putInMap[state.blueMap, Real.Round[255*rgb.B]]; }; Black: PUBLIC GetColorProc = {RETURN[[0,0,0]]}; --constant black Gray: PUBLIC GetColorProc = {RETURN[[0.5,0.5,0.5]]}; --constant gray White: PUBLIC GetColorProc = {RETURN[[1,1,1]]}; --constant white SampledRainbow: PUBLIC GetColorProc = { --assumes data is SampledCalibration cal: SampledCalibration _ NARROW[data]; rgb: RGB; [[rgb.R, rgb.G, rgb.B]] _ CalibratedColor.TripleFromXYZ[xyz, cal]; RETURN[rgb]; }; SampledGrayscale: PUBLIC GetColorProc = { --assumes data is SampledCalibration cal: SampledCalibration _ NARROW[data]; white: XYZ _ CalibratedColor.XYZFromTriple[CalibratedColor.TripleFromRGB[[1,1,1]], cal]; v: REAL _ xyz.Y/white.Y; RETURN[[v,v,v]]; }; RGBRainbow: PUBLIC GetColorProc = { --assumes data is RGBCalibration cal: RGBCalibration _ NARROW[data]; rgb: RGB _ CalibratedColor.RGBFromXYZ[xyz, cal]; RETURN[rgb]; }; RGBGrayscale: PUBLIC GetColorProc = { --assumes data is RGBCalibration cal: RGBCalibration _ NARROW[data]; white: XYZ _ CalibratedColor.XYZFromRGB[[1,1,1], cal]; v: REAL _ xyz.Y/white.Y; RETURN[[v,v,v]]; }; rgbOperator: ImagerColor.ColorOperator _ ImagerColor.NewColorOperatorRGB[255]; RopeFromProjection: PUBLIC PROC[proj: Projection] RETURNS[r: ROPE] = { SELECT proj FROM x => r _ "x"; y => r _ "y"; aStar => r _ "a*"; bStar => r _ "b*"; uStar => r _ "u*"; vStar => r _ "v*"; lStar => r _ "L*"; reflectance => r _ "R"; hueAngle => r _ "Hue"; ENDCASE => ERROR; }; ImageSampleMap: PUBLIC PROC[dc: Imager.Context, state: State] = { box: SF.Box _ ImagerSample.GetBox[state.redMap]; pa: ImagerPixelArray.PixelArray; pa _ ImagerPixelArray.FromPixelMap[pixelMap: ImagerPixel.MakePixelMap[state.redMap, state.greenMap, state.blueMap], box: box, scanMode: [slow: up, fast: right]]; Imager.SetSampledColor[dc, pa, NIL, rgbOperator]; Imager.MaskRectangleI[dc, box.min.f, box.min.s, (box.max.f-box.min.f), (box.max.s-box.min.s)]; }; fontSize: REAL _ 8; ImageAndLabel: PUBLIC PROC [dc: Imager.Context, state: State, note: ROPE _ NIL, labelColor: ImagerColor.Color _ NIL] = { centeredX: PROC[state: State] RETURNS[BOOLEAN] = { SELECT state.yAxis FROM x,y,lStar,reflectance => RETURN[FALSE]; ENDCASE => RETURN[TRUE]; }; centeredY: PROC[state: State] RETURNS[BOOLEAN] = { SELECT state.xAxis FROM x,y,lStar,reflectance => RETURN[FALSE]; ENDCASE => RETURN[TRUE]; }; font: ImagerFont.Font _ ImagerFont.Scale[ImagerFont.Find["Xerox/PressFonts/Helvetica-mrr"],fontSize]; xLabel: ROPE _ RopeFromProjection[state.xAxis]; yLabel: ROPE _ RopeFromProjection[state.yAxis]; do: PROC = { smBox: SF.Box _ ImagerSample.GetBox[state.redMap]; width: PROC[extent: ImagerFont.Extents] RETURNS[REAL] = { RETURN[extent.leftExtent+extent.rightExtent]}; height: PROC[extent: ImagerFont.Extents] RETURNS[REAL] = { RETURN[extent.ascent+extent.descent]}; yLabelBox: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font, yLabel]; xLabelBox: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font, xLabel]; noteBox: ImagerFont.Extents _ IF note#NIL THEN ImagerFont.RopeBoundingBox[font, note] ELSE [0,0,0,0]; gap: REAL _ 2.0; --standard small space noteOffset: REAL _ IF note#NIL THEN height[noteBox]+gap ELSE 0; xOffset: REAL _ IF centeredY[state] THEN 0 ELSE width[yLabelBox]/2.0; yOffset: REAL _ IF centeredX[state] THEN noteOffset ELSE noteOffset+height[xLabelBox]/2.0; doMap: PROC = { Imager.TranslateT[dc,[xOffset,yOffset]]; ImageSampleMap[dc, state]; }; doYAxis: PROC = { --we've been translated to the lower left corner of the map already Imager.TranslateT[dc,[(IF centeredY[state] THEN smBox.max.f/2.0 ELSE 0), smBox.max.s]]; Imager.MaskVector[dc, [0,0], [0,-smBox.max.s]]; Imager.SetXY[dc,[-width[yLabelBox]/2.0, yLabelBox.descent+gap]]; Imager.ShowRope[dc, yLabel]; }; doXAxis: PROC = { --we've been translated to the lower left corner of the map already Imager.TranslateT[dc,[smBox.max.f, (IF centeredX[state] THEN smBox.max.s/2.0 ELSE 0)]]; Imager.MaskVector[dc, [0,0], [-smBox.max.f,0]]; Imager.SetXY[dc, [gap, -height[xLabelBox]/2.0]]; Imager.ShowRope[dc, xLabel]; }; Imager.DoSave[dc, doMap]; IF labelColor#NIL THEN Imager.SetColor[dc, labelColor]; Imager.SetFont[dc, font]; Imager.SetStrokeWidth[dc,1]; IF note # NIL THEN { fullChartW: REAL _ smBox.max.f + xOffset + width[xLabelBox] +gap; Imager.SetXY[dc, [x: (fullChartW-width[noteBox])/2.0, y: noteBox.descent]]; Imager.ShowRope[dc,note]; }; Imager.TranslateT[dc, [xOffset,yOffset]]; Imager.DoSave[dc, doYAxis]; Imager.DoSave[dc, doXAxis]; }; Imager.DoSave[dc, do]; }; StatesToInterpress: PUBLIC PROC[states: LIST OF State, ipName: ROPE, label: BOOLEAN _ TRUE, note: ROPE _ NIL, labelColor: ImagerColor.Color _ NIL, bleedBackground: BOOLEAN _ FALSE] = { ref: ImagerInterpress.Ref _ ImagerInterpress.Create[ipName]; first: State _ states.first; box: SF.Box _ ImagerSample.GetBox[first.redMap]; combineMaps: PROC RETURNS[redMap, greenMap, blueMap: ImagerSample.SampleMap] = { backgroundR, backgroundG, backgroundB: BYTE; [backgroundR, backgroundG, backgroundB] _ BytesFromRGB[first.backgroundColor]; redMap _ ImagerSample.Copy[first.redMap, [0,0], box]; greenMap _ ImagerSample.Copy[first.greenMap, [0,0], box]; blueMap _ ImagerSample.Copy[first.blueMap, [0,0], box]; FOR list: LIST OF State _ states.rest, list.rest UNTIL list=NIL DO state: State _ list.first; FOR s: NAT IN [box.min.s..box.max.s) DO FOR f: NAT IN [box.min.f..box.max.f) DO r: BYTE _ ImagerSample.Get[state.redMap, [s: s, f: f]]; g: BYTE _ ImagerSample.Get[state.greenMap, [s: s, f: f]]; b: BYTE _ ImagerSample.Get[state.blueMap, [s: s, f: f]]; IF r # backgroundR OR g#backgroundG OR b#backgroundB THEN { ImagerSample.Put[redMap, [s: s, f: f], r]; ImagerSample.Put[greenMap, [s: s, f: f], g]; ImagerSample.Put[blueMap, [s: s, f: f], b]; }; ENDLOOP; ENDLOOP; ENDLOOP; }; action: PROC[dc: Imager.Context] = { IF bleedBackground THEN { size: REAL _ MAX[box.max.f, box.max.s]; Imager.SetColor[dc, ImagerColor.ColorFromRGB[first.backgroundColor]]; Imager.MaskRectangle[dc, [-size/2.0, -size/2.0, 2*size, 2*size]]; }; IF states.rest=NIL THEN ImageAndLabel[dc, states.first, note, labelColor] ELSE { --do the combining hack r,g,b: ImagerSample.SampleMap; temp: State; [r,g,b] _ combineMaps[]; temp _ NEW[StateRec _ [ redMap: r, greenMap: g, blueMap: b, xAxis: first.xAxis, yAxis: first.yAxis, res: first.res, white: first.white, --all of these last 5 fields are bogus, but don't matter getColor: first.getColor, getColorData: first.getColorData, backgroundColor: first.backgroundColor, transformation: first.transformation ]]; ImageAndLabel[dc, temp, note, labelColor]; }; }; ImagerInterpress.DoPage[ref, action, Imager.metersPerPoint]; ImagerInterpress.Close[ref]; }; StateFromCalibration: PUBLIC PROC [cal: SampledCalibration, xAxis, yAxis: Projection, res: NAT, getColor: GetColorProc, getColorData: REF _ NIL] RETURNS [State] = { white: XYZ _ CalibratedColor.XYZFromTriple[CalibratedColor.TripleFromRGB[[1,1,1]], cal]; RETURN[CreateMapState[xAxis, yAxis, res, white, getColor, getColorData]]; }; StateFromRGBCalibration: PUBLIC PROC [cal: RGBCalibration, xAxis, yAxis: Projection, res: NAT, getColor: GetColorProc, getColorData: REF _ NIL] RETURNS [State] = { white: XYZ _ CalibratedColor.XYZFromRGB[[1,1,1], cal]; RETURN[CreateMapState[xAxis, yAxis, res, white, getColor, getColorData]]; }; SampleAndPlot: PUBLIC PROC [state: State, cal: SampledCalibration, sample: NAT _ 8, sampleProc: SampleProc, substituteRainbow: BOOLEAN _ TRUE] = { newRGB: RGBProc = { x,y: REAL _ 0; mappedRGB: RGB; xyz: XYZ _ CalibratedColor.XYZFromTriple[CalibratedColor.TripleFromRGB[rgb], cal]; [x,y, mappedRGB] _ MapColor[state, xyz ! Real.RealException => CONTINUE]; IF substitute THEN mappedRGB _ rgb; MarkPoint[state, Real.Round[x], Real.Round[y], mappedRGB]; }; oldColor: GetColorProc; substitute: BOOLEAN _ FALSE; IF substituteRainbow THEN { oldColor _ state.getColor; state.getColor _ Black; substitute _ TRUE; }; CedarProcess.SetPriority[background]; sampleProc[sample, newRGB]; IF substitute THEN state.getColor _ oldColor; }; RGBSampleAndPlot: PUBLIC PROC [state: State, cal: RGBCalibration, sample: NAT _ 8, sampleProc: SampleProc] = { newRGB: RGBProc = { x,y: REAL _ 0; xyz: XYZ _ CalibratedColor.XYZFromRGB[rgb, cal]; mappedRGB: RGB; [x,y, mappedRGB] _ MapColor[state, xyz ! Real.RealException => CONTINUE]; MarkPoint[state, Real.Round[x], Real.Round[y], mappedRGB]; }; CedarProcess.SetPriority[background]; sampleProc[sample, newRGB]; }; GrayAxis: PUBLIC SampleProc = { step: REAL _ 1.0/(sample-1); FOR i: NAT IN [0..sample) DO v: REAL _ i*step; returnRGB[[v,v,v]]; ENDLOOP; }; InterpRGB: PROC[from, to: RGB, sample: NAT, returnRGB: RGBProc] = { step: REAL _ 1.0/(sample-1); interp: PROC[alpha, f, t: REAL] RETURNS[REAL] = {RETURN[alpha*t+(1-alpha)*f]}; FOR i: NAT IN [0..sample) DO v: REAL _ i*step; returnRGB[[R: interp[v,from.R,to.R], G: interp[v,from.G,to.G], B: interp[v,from.B,to.B]]]; ENDLOOP; }; HueCircle: PUBLIC SampleProc = { InterpRGB[[1,0,0], [1,1,0], sample, returnRGB]; InterpRGB[[1,1,0], [0,1,0], sample, returnRGB]; InterpRGB[[0,1,0], [0,1,1], sample, returnRGB]; InterpRGB[[0,1,1], [0,0,1], sample, returnRGB]; InterpRGB[[0,0,1], [1,0,1], sample, returnRGB]; InterpRGB[[1,0,1], [1,0,0], sample, returnRGB]; }; ColorsToWhite: PUBLIC SampleProc = { InterpRGB[[1,0,0], [1,1,1], sample, returnRGB]; InterpRGB[[1,0,1], [1,1,1], sample, returnRGB]; InterpRGB[[0,1,0], [1,1,1], sample, returnRGB]; InterpRGB[[0,1,1], [1,1,1], sample, returnRGB]; InterpRGB[[0,0,1], [1,1,1], sample, returnRGB]; InterpRGB[[1,1,0], [1,1,1], sample, returnRGB]; }; ColorsToBlack: PUBLIC SampleProc = { black: RGB _ [0.00001, 0.00001, 0.00001]; --avoid RealException for monitor black InterpRGB[[1,0,0], black, sample, returnRGB]; InterpRGB[[1,0,1], black, sample, returnRGB]; InterpRGB[[0,1,0], black, sample, returnRGB]; InterpRGB[[0,1,1], black, sample, returnRGB]; InterpRGB[[0,0,1], black, sample, returnRGB]; InterpRGB[[1,1,0], black, sample, returnRGB]; }; GamutSurface: PUBLIC SampleProc = { ColorsToBlack[sample, returnRGB]; ColorsToWhite[sample, returnRGB]; HueCircle[sample, returnRGB]; }; WholeGamut: PUBLIC SampleProc = { step: REAL _ 1.0/(sample-1); FOR r: NAT IN [0..sample) DO rv: REAL _ step*r; FOR g: NAT IN [0..sample) DO gv: REAL _ step*g; FOR b: NAT IN [0..sample) DO bv: REAL _ step*b; returnRGB[[rv,gv,bv]]; ENDLOOP; ENDLOOP; ENDLOOP; }; END. ศColorPlotGamutsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Maureen Stone July 23, 1986 12:07:04 sm PDT Maureen Stone, June 29, 1988 2:27:15 am PDT State: TYPE = REF StateRec; StateRec: TYPE = RECORD [ redMap,greenMap,blueMap: SampleMap, xAxis, yAxis: Projection, res: NAT, -- number of samples along longest dimension. white: XYZ, getColor: GetColorProc, getColorData: REF _ NIL, transformation: ImagerTransformation.Transformation --based on projections ]; Think in SampleMap pixels. GetColor Procs GetColorProc: TYPE = PROC[xyz: XYZ, data: REF _ NIL] RETURNS[RGB]; Imaging x axis is centered if value on y axis requires it Makes an InterpressMaster on ipName by calling ImageSampleMap or ImageAndLabel The states had better all be the same type. last state defines the label Device Gamuts Sampled XYZ to RGB and back is expensive. Since we are sampling the gamut as a function of RGB, we can do the rainbow colorproc without the conversion. substituteRainbow=TRUE means do the conversion IF substituteRainbow AND state.getColor=SampledRainbow THEN { RGBProc: TYPE = PROC[rgb: RGB]; SampleProc: TYPE = PROC[sample: NAT _ 8, returnRGB: RGBProc]; interpolate from black to white interpolates around the surface of the gamut (R,Y,G,C,B,M) in sample steps between vertices. ie, 8 steps between red and yellow, yellow and green, etc. Interpolates from the vertices to the white point. Interpolates from the vertices to the black point. interpolates uniformly along RGB in sample steps. Calls toXYZ to get the tristimulus value of the sample. Calls returns to get the value plotted (or whatever). The procedure toXYZ would be less necessary if CalibratedColor had a single calibration type. ส3˜codešœ™Kšœ ฯmœ1™