ImageGamutsImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Maureen Stone, July 25, 1989 2:16:24 pm PDT
Reads images, maps colors, Plots colors
DIRECTORY
ImageGamuts, CalibratedColor, AIS, SampleCache, IO, FS, ColorPlotGamuts, UserProfileOps, ImagerColor, ImagerColorFns, Rope, CreateCalibration, CalibratedColorFns, Process, PrintColorTransformation, ImagerPixel, Real;
ImageGamutsImpl: CEDAR PROGRAM
IMPORTS AIS, SampleCache, UserProfileOps, ColorPlotGamuts, CalibratedColor, CalibratedColorFns, IO, FS,Rope, CreateCalibration, Process, ImagerColorFns, PrintColorTransformation, ImagerPixel, Real
EXPORTS ImageGamuts
~ BEGIN OPEN ImageGamuts;
State: TYPE ~ ColorPlotGamuts.State;
Projection: TYPE ~ ColorPlotGamuts.Projection;
RGBCalibration: TYPE = CalibratedColor.RGBCalibration;
SampledCalibration: TYPE = CalibratedColor.SampledCalibration;
Sample: TYPE = SampleCache.Sample;
Cache: TYPE = SampleCache.Cache;
RefCache: TYPE = SampleCache.RefCache;
XYZ: TYPE = CalibratedColor.XYZ;
CIELAB: TYPE = CalibratedColor.CIELAB;
RGB: TYPE = ImagerColor.RGB;
ROPE: TYPE = Rope.ROPE;
AISColors: TYPE = {r,g,b, clipped};
AISFile: TYPE = RECORD[name: ROPENIL, fRef: AIS.FRef ← NIL, wRef: AIS.WRef ← NIL, raster: AIS.Raster ← NIL];
AISFiles: TYPE = ARRAY AISColors OF AISFile;
InputData: TYPE = REF InputDataRec;
InputDataRec: TYPE = RECORD[rgb: RGB];
Read Images
CreateInputData: PUBLIC PROC [fileStem: ROPE, rgbCal: RGBCalibration] RETURNS [inputData: RefCache] ~ {
Creates a ref cache of InputData values for an RGB image
aisFiles: AISFiles ← OpenFiles[fileStem];
nLines: NAT ← aisFiles[r].raster.scanCount;
nPixels: NAT ← aisFiles[r].raster.scanLength;
white: XYZ ← CalibratedColor.XYZFromRGB[[1,1,1], rgbCal];
ref: REFNIL;
key: Sample;
valid: BOOLEANFALSE;
inputData ← SampleCache.CreateRefCache[];
Process.SetPriority[Process.priorityBackground];
FOR line: NAT IN [0..nLines) DO
FOR pixel: NAT IN [0..nPixels) DO
key.s0 ← AIS.ReadSample[w: aisFiles[r].wRef,line: line, pixel: pixel];
key.s1 ← AIS.ReadSample[w: aisFiles[g].wRef,line: line, pixel: pixel];
key.s2 ← AIS.ReadSample[w: aisFiles[b].wRef,line: line, pixel: pixel];
[valid, ref] ← SampleCache.FetchRef[inputData, key];
IF NOT valid THEN {
data: InputData ← NEW[InputDataRec];
data.rgb ← [key.s0/255.0, key.s1/255.0, key.s2/255.0];
SampleCache.StoreRef[inputData, key, data]
};
ENDLOOP;
ENDLOOP;
CloseFiles[aisFiles];
};
OpenFiles: PUBLIC PROC [fileStem: ROPE] RETURNS [aisFiles: AISFiles] ~ {
Does all the usual AIS open stuff
aisFiles[r].name ← FindFile[fileStem, "AISseparationKeys.red", "-red"];
aisFiles[g].name ← FindFile[fileStem, "AISseparationKeys.green", "-grn"];
aisFiles[b].name ← FindFile[fileStem, "AISseparationKeys.blue", "-blu"];
FOR c: AISColors IN [r..b] DO
aisFiles[c].fRef ← AIS.OpenFile[aisFiles[c].name];
aisFiles[c].wRef ← AIS.OpenWindow[aisFiles[c].fRef];
aisFiles[c].raster ← AIS.ReadRaster[aisFiles[c].fRef]
ENDLOOP;
};
CloseFiles: PUBLIC PROC [aisFiles: AISFiles] ~ {
closes the files in the handle
FOR c: AISColors IN [r..b] DO AIS.CloseFile[aisFiles[c].fRef]; ENDLOOP;
IF aisFiles[clipped].name#NIL THEN AIS.CloseFile[aisFiles[clipped].fRef];
};
CreateFiles: PUBLIC PROC [fileStem: ROPE, raster: AIS.Raster] RETURNS [aisFiles: AISFiles] ~ {
Does all the usual AIS open stuff
aisFiles[r].name ← Rope.Cat[fileStem, "-red.ais"];
aisFiles[g].name ← Rope.Cat[fileStem, "-grn.ais"];
aisFiles[b].name ← Rope.Cat[fileStem, "-blu.ais"];
FOR c: AISColors IN [r..b] DO
aisFiles[c].fRef ← AIS.CreateFile[aisFiles[c].name, raster];
aisFiles[c].wRef ← AIS.OpenWindow[aisFiles[c].fRef];
aisFiles[c].raster ← raster;
ENDLOOP;
aisFiles[clipped].raster ← NEW[AIS.RasterPart ← [
scanCount: raster.scanCount,
scanLength: raster.scanLength,
scanMode: raster.scanMode,
bitsPerPixel: 0,
linesPerBlock: -1,
paddingPerBlock: 0
]];
aisFiles[clipped].name ← Rope.Cat[fileStem, "-clipped.ais"];
aisFiles[clipped].fRef ← AIS.CreateFile[aisFiles[clipped].name, aisFiles[clipped].raster];
aisFiles[clipped].wRef ← AIS.OpenWindow[aisFiles[clipped].fRef];
};
FindFile: PUBLIC PROC [stem, key, default: ROPE] RETURNS [ROPE] ~ {
fileList: LIST OF ROPE ← UserProfileOps.Expand[
"%g%k|%g|%g|.%k|AISExtensions|AIS|",
IO.rope[stem],
IO.rope[key], IO.rope[default]];
DO {
FileExists: PROCEDURE [fileName:Rope.ROPE] RETURNS [answer:BOOLEANTRUE] = {
s:IO.STREAMFS.StreamOpen[fileName:fileName
! FS.Error => TRUSTED {
answer←FALSE;
GOTO notThere;
}];
IO.Close[s];
EXITS notThere => NULL;
};
IF fileList = NIL THEN RETURN [NIL];
IF FileExists[fileList.first] THEN RETURN [fileList.first];
fileList ← fileList.rest
};
ENDLOOP;
};
Read and Write Log files
LogInputData: PROC [refCache: SampleCache.RefCache, logName, note: ROPE] ~ {
log: IO.STREAMFS.StreamOpen[logName, $create];
logEntry: PROC[key: Sample, data: REF, hits: INT] = {
in: InputData ← NARROW[data];
IO.PutF[log,"n: %g RGB: %g %g %g ", IO.int[hits+1], IO.int[key.s0], IO.int[key.s1], IO.int[key.s2]];
IO.PutF[log,"XYZ: %g %g %g ", IO.real[in.xyz.X], IO.real[in.xyz.Y], IO.real[in.xyz.Z]];
IO.PutF[log,"CIELAB: %g %g %g\n", IO.real[in.cielab.lStar], IO.real[in.cielab.aStar], IO.real[in.cielab.bStar]];
};
IO.PutF[log, "%g\n", IO.rope[note]];
IO.PutF[log,"probes: %g hits: %g entries: %g\n", IO.int[refCache.probes], IO.int[refCache.hits], IO.int[refCache.entries]];
SampleCache.EnumerateRefCache[refCache, logEntry];
IO.Close[log];
};
ReadInputData: PROC [logName: ROPE] RETURNS [inputData: SampleCache.RefCache] = {
data: InputData ← NEW[InputDataRec];
key: Sample;
log: STREAMFS.StreamOpen[logName];
checkKeyword: PROC[keyword: ROPE] = {
word: ROPE;
[word, ] ← IO.GetTokenRope[stream];
IF NOT Rope.Match[keyword, word, FALSE] THEN ERROR;
[] ← IO.GetChar[stream]; --strip off the :
};
inputData ← SampleCache.CreateRefCache[];
[] ← IO.GetToken
checkKeyword["n"];
hits ← IO.GetInt[stream];
checkKeyword["RGB"];
key.s0 ← IO.GetInt[stream]; --red
key.s1 ← IO.GetInt[stream]; --green
key.s2 ← IO.GetInt[stream]; --blue
checkKeyword["XYZ"];
data.xyz.X ← IO.GetReal[stream]; --X
data.xyz.Y ← IO.GetReal[stream]; --Y
data.xyz.Z ← IO.GetReal[stream]; --Z
checkKeyword["CIELAB"];
data.cielab.lStar ← IO.GetReal[stream]; --L*
data.cielab.aStar ← IO.GetReal[stream]; --a*
data.cielab.bStar ← IO.GetReal[stream]; --b*
SampleCache.StoreRef[inputData, key, data]
};
Plot Image Gamuts
PlotInputData: PUBLIC PROC [state: State, inputData: RefCache, mapData: REF, mapProc: MapProc, imageColorProc: ColorProc ← NIL] ~ {
Enumerates the input data and makes a plot
plot: PROC[key: Sample, data: REF, hits: INT] = {
inputData: InputData ← NARROW[data];
xyz: XYZ ← mapProc[key, inputData.rgb, mapData];
x,y: REAL;
rgb: RGB ← imageColorProc[key];
[x,y] ← ColorPlotGamuts.MapValues[state, xyz];
ColorPlotGamuts.MarkPoint[state, Real.Round[x], Real.Round[y], rgb];
};
Process.SetPriority[Process.priorityBackground];
SampleCache.EnumerateRefCache[inputData, plot];
};
GetCalibrations: PUBLIC PROC[rgbAtom, sampledAtom: ATOM] RETURNS[rgbCal: RGBCalibration, sampledCal: SampledCalibration] = {
rgbCal ← CreateCalibration.CreateRGB[CreateCalibration.GetRGBDataValues[rgbAtom]];
sampledCal ← CreateCalibration.CreateSampled[CreateCalibration.GetSampledFiles[sampledAtom], sampledAtom];
};
MakeChromaPlot: PUBLIC PROC [pi: PlotInfo, xAxis, yAxis: Projection, mapInfo: MapInfo] ~ {
rgbState: State ← ColorPlotGamuts.StateFromRGBCalibration[pi.rgbCal, xAxis, yAxis, pi.res, ColorPlotGamuts.RGBRainbow, pi.rgbCal];
sampledState: State ← ColorPlotGamuts.StateFromCalibration[pi.sampledCal, xAxis, yAxis, pi.res, ColorPlotGamuts.SampledRainbow, pi.sampledCal];
inputData: RefCache ← pi.inputData;
IF inputData = NIL THEN {
inputData ← CreateInputData[pi.aisRoot, pi.rgbCal];
pi.inputData ← inputData};
ColorPlotGamuts.RGBSampleAndPlot[rgbState, pi.rgbCal, 256, ColorPlotGamuts.HueCircle];
ColorPlotGamuts.SampleAndPlot[sampledState, pi.sampledCal, 256, ColorPlotGamuts.HueCircle];
IF mapInfo.rgbState THEN PlotInputData[rgbState, inputData, mapInfo.mapData, mapInfo.mapProc, Rainbow]
ELSE PlotInputData[sampledState, inputData, mapInfo.mapData, mapInfo.mapProc, Rainbow];
ColorPlotGamuts.StatesToInterpress[states: LIST[rgbState, sampledState], ipName: Rope.Cat[pi.ipRoot, ".ip"], label: TRUE, note: pi.note, labelColor: pi.labelColor, bleedBackground: pi.bleedBackground];
};
MakeLStarPlot: PUBLIC PROC [pi: PlotInfo, xAxis: Projection, mapInfo: MapInfo] ~ {
rgbState: State ← ColorPlotGamuts.StateFromRGBCalibration[pi.rgbCal, xAxis, lStar, pi.res, ColorPlotGamuts.White, pi.rgbCal];
sampledState: State ← ColorPlotGamuts.StateFromCalibration[pi.sampledCal, xAxis, lStar, pi.res, ColorPlotGamuts.Black, pi.sampledCal];
inputData: RefCache ← pi.inputData;
states: LIST OF State ← LIST[sampledState, rgbState];
IF inputData = NIL THEN {
inputData ← CreateInputData[pi.aisRoot, pi.rgbCal];
pi.inputData ← inputData};
ColorPlotGamuts.RGBSampleAndPlot[rgbState, pi.rgbCal, 8, ColorPlotGamuts.WholeGamut];
ColorPlotGamuts.SampleAndPlot[sampledState, pi.sampledCal, 16, ColorPlotGamuts.WholeGamut, FALSE];
IF mapInfo.rgbState THEN PlotInputData[rgbState, inputData, mapInfo.mapData, mapInfo.mapProc, SatRainbow]
ELSE {
PlotInputData[sampledState, inputData, mapInfo.mapData, mapInfo.mapProc, SatRainbow];
states ← LIST[rgbState, sampledState];
};
ColorPlotGamuts.StatesToInterpress[states: states, ipName: Rope.Cat[pi.ipRoot, ".ip"], label: TRUE, note: pi.note, labelColor: pi.labelColor, bleedBackground: pi.bleedBackground];
};
Make3LABViews: PUBLIC PROC [pi: PlotInfo, mapInfo: MapInfo] ~ {
ipRoot: ROPE ← pi.ipRoot;
pi.ipRoot ← Rope.Cat[ipRoot, "AB"];
MakeChromaPlot[pi: pi, xAxis: aStar, yAxis: bStar, mapInfo: mapInfo];
pi.ipRoot ← Rope.Cat[ipRoot, "AL"];
MakeLStarPlot[pi: pi, xAxis: aStar, mapInfo: mapInfo];
pi.ipRoot ← Rope.Cat[ipRoot, "BL"];
MakeLStarPlot[pi: pi, xAxis: bStar, mapInfo: mapInfo];
};
Rainbow: ColorProc = {
RETURN[[R: sample.s0/255.0, G: sample.s1/255.0, B: sample.s2/255.0]];
};
DoSat: PROC[in: RGB] RETURNS[out: RGB] = {
hsl: ImagerColorFns.HSL← ImagerColorFns.HSLFromRGB[boundRGB[in]];
boundRGB: PROC[val: RGB] RETURNS[RGB] = {
RETURN[[R: MAX[MIN[val.R, 1],0], G: MAX[MIN[val.G, 1],0], B: MAX[MIN[val.B, 1],0]]]};
out ← ImagerColorFns.RGBFromHSL[[H: hsl.H, S: hsl.S, L: 0.5]];
};
SatRainbow: ColorProc = {
rgb: RGB ← [R: sample.s0/255.0, G: sample.s1/255.0, B: sample.s2/255.0];
rgb ← DoSat[rgb];
RETURN[rgb];
};
SatRGB: ColorPlotGamuts.GetColorProc = { --assumes data is RGBCalibration
cal: RGBCalibration ← NARROW[data];
rgb: RGB ← CalibratedColor.RGBFromXYZ[xyz, cal];
rgb ← DoSat[rgb];
RETURN[rgb];
};
SatSampled: ColorPlotGamuts.GetColorProc = { --assumes data is RGBCalibration
cal: SampledCalibration ← NARROW[data];
triple: CalibratedColor.Triple ← CalibratedColor.TripleFromXYZ[xyz, cal].triple;
rgb: RGB ← [triple.v0, triple.v1, triple.v2];
rgb ← DoSat[rgb];
RETURN[rgb];
};
Mapped Plots
MatrixMapData: TYPE = REF MatrixMapDataRec;
MatrixMapDataRec: TYPE = RECORD [tables: PrintColorTransformation.MatrixTables, rgbCal: RGBCalibration]; -- used to see mapped values
PrinterMapData: TYPE = REF PrinterMapDataRec;
PrinterMapDataRec: TYPE = RECORD [tables: PrintColorTransformation.MatrixTables, sampledCal: SampledCalibration]; -- used to see printed values
GetPlotInfo: PUBLIC PROC [aisRoot, ipRoot, note: ROPE, res: NAT, rgbCal: RGBCalibration, sampledCal: SampledCalibration, labelColor: ImagerColor.Color, bleedBackground: BOOLEAN] RETURNS[PlotInfo] = {
RETURN[[aisRoot: aisRoot, ipRoot: ipRoot, note: note, res: res, rgbCal: rgbCal, sampledCal: sampledCal, labelColor: labelColor, bleedBackground: bleedBackground]];
};
GetMonitorMapInfo: PUBLIC PROC [rgbCal: RGBCalibration] RETURNS[MapInfo] = {
RETURN[[mapProc: RGBMap, mapData: rgbCal, rgbState: TRUE]];
};
GetMappedMapInfo: PUBLIC PROC [tables: PrintColorTransformation.MatrixTables, rgbCal: RGBCalibration] RETURNS[MapInfo] = {
data: MatrixMapData ← NEW[MatrixMapDataRec ← [
tables: tables,
rgbCal: rgbCal
]];
RETURN[[mapProc: MatrixMap, mapData: data, rgbState: TRUE]];
};
GetPrinterMapInfo: PUBLIC PROC [tables: PrintColorTransformation.MatrixTables, sampledCal: SampledCalibration] RETURNS[MapInfo] = {
data: PrinterMapData ← NEW[PrinterMapDataRec ← [
tables: tables,
sampledCal: sampledCal
]];
RETURN[[mapProc: PrinterMap, mapData: data, rgbState: FALSE]];
};
RGBMap: PUBLIC MapProc = {
rgbCal: RGBCalibration ← NARROW[data];
xyz: XYZ ← CalibratedColor.XYZFromRGB[rgb, rgbCal];
RETURN[xyz];
};
MatrixMap: PUBLIC MapProc = {
mmData: MatrixMapData ← NARROW[data];
mapped: Sample ← MatrixMapProc[rgbBytes, mmData.tables];
xyz: XYZ ← CalibratedColor.XYZFromRGB[[R: mapped.s0/255.0, G: mapped.s1/255.0, B: mapped.s2/255.0], mmData.rgbCal];
RETURN[xyz];
};
PrinterMap: PUBLIC MapProc = {
pmData: PrinterMapData ← NARROW[data];
mapped: Sample ← MatrixMapProc[rgbBytes, pmData.tables];
xyz: XYZ ← CalibratedColor.XYZFromTriple[
[v0: mapped.s0/255.0, v1: mapped.s1/255.0, v2: mapped.s2/255.0], pmData.sampledCal];
RETURN[xyz];
};
Map Image Gamuts
SampleProc: TYPE = PROC[in: Sample] RETURNS[out: Sample];
AdjustFiles: PUBLIC PROC [fileStem, adjusted: ROPE, proc: SampleProc] ~ {
Opens the files indicated by the fileStem and creates files adjusted by lightness
inFiles: AISFiles ← OpenFiles[fileStem];
outFiles: AISFiles ← CreateFiles[adjusted, inFiles[r].raster];
nLines: NAT ← inFiles[r].raster.scanCount;
nPixels: NAT ← inFiles[r].raster.scanLength;
key, sample: Sample;
valid: BOOLEANFALSE;
cache: Cache ← SampleCache.Create[];
Process.SetPriority[Process.priorityBackground];
FOR line: NAT IN [0..nLines) DO
FOR pixel: NAT IN [0..nPixels) DO
key.s0 ← AIS.ReadSample[w: inFiles[r].wRef,line: line, pixel: pixel];
key.s1 ← AIS.ReadSample[w: inFiles[g].wRef,line: line, pixel: pixel];
key.s2 ← AIS.ReadSample[w: inFiles[b].wRef,line: line, pixel: pixel];
[valid, sample] ← SampleCache.Fetch[cache, key];
IF NOT valid THEN {
sample ← proc[key];
SampleCache.Store[cache, key, sample]
};
AIS.WriteSample[w: outFiles[r].wRef,value: sample.s0, line: line, pixel: pixel];
AIS.WriteSample[w: outFiles[g].wRef,value: sample.s1, line: line, pixel: pixel];
AIS.WriteSample[w: outFiles[b].wRef,value: sample.s2, line: line, pixel: pixel];
AIS.WriteSample[w: outFiles[clipped].wRef,value: sample.s3, line: line, pixel: pixel];
ENDLOOP;
ENDLOOP;
CloseFiles[inFiles];
CloseFiles[outFiles];
};
SaturationMatrix: PUBLIC PROC [rgbToLStar: PrintColorTransformation.TRCTable, saturation: REAL, printerDMax: REAL] RETURNS [PrintColorTransformation.MatrixTables]~ {
matrix: PrintColorTransformation.MatrixN ← PrintColorTransformation.IdentityMatrix[];
RETURN[PrintColorTransformation.InitMatrixTables[
rgbToLStar: rgbToLStar,
densityToDotArea: PrintColorTransformation.DensityToLStar[printerDMax],
printerDMax: printerDMax,
saturation: saturation,
contrast: -0.0,
matrix: matrix]];
};
MatrixMapProc: PROC[in: Sample, matrixTables: PrintColorTransformation.MatrixTables] RETURNS [out: Sample] = {
rgbIn: ImagerPixel.PixelBuffer ← ImagerPixel.NewPixels[3,1];
cmyOut: ImagerPixel.PixelBuffer ← ImagerPixel.NewPixels[3,1];
maxSampleIn: ARRAY [0..3) OF CARDINAL ← [255, 255, 255];
rgbIn[0][0] ← in.s0;
rgbIn[1][0] ← in.s1;
rgbIn[2][0] ← in.s2;
PrintColorTransformation.MatrixTransform[maxSampleIn, rgbIn, cmyOut, matrixTables];
out.s0 ← 255-cmyOut[0][0];
out.s1 ← 255-cmyOut[1][0];
out.s2 ← 255-cmyOut[2][0];
out.s3 ← 0;
};
AdjustWithMatrix: PUBLIC PROC [fileStem, adjusted: ROPE, matrixTables: PrintColorTransformation.MatrixTables] ~ {
Maps R, G, B through LStar map independently. Can use either ConracToLStar or RGBToLStar for the array. This works like a gamma correction
rgbIn: ImagerPixel.PixelBuffer ← ImagerPixel.NewPixels[3,1];
cmyOut: ImagerPixel.PixelBuffer ← ImagerPixel.NewPixels[3,1];
maxSampleIn: ARRAY [0..3) OF CARDINAL ← [255, 255, 255];
map: SampleProc = {
rgbIn[0][0] ← in.s0;
rgbIn[1][0] ← in.s1;
rgbIn[2][0] ← in.s2;
PrintColorTransformation.MatrixTransform[maxSampleIn, rgbIn, cmyOut, matrixTables];
out.s0 ← 255-cmyOut[0][0];
out.s1 ← 255-cmyOut[1][0];
out.s2 ← 255-cmyOut[2][0];
out.s3 ← 0;
};
AdjustFiles[fileStem, adjusted, map];
};
AdjustRGBToLStar: PUBLIC PROC [fileStem, adjusted: ROPE, array: ARRAY[0..255] OF BYTE] ~ {
Maps R, G, B through LStar map independently. Can use either ConracToLStar or RGBToLStar for the array. This works like a gamma correction
map: SampleProc = {
out.s0 ← array[in.s0];
out.s1 ← array[in.s1];
out.s2 ← array[in.s2];
out.s3 ← 0;
};
AdjustFiles[fileStem, adjusted, map];
};
AdjustOutOfGamut: PUBLIC PROC [fileStem, adjusted: ROPE, useCIELAB: BOOLEANFALSE] ~ {
Makes an image that is black wherever the rgb to xyz (or cielab) to cmy conversion
is out-of-gamut, otherwise just copy the color over.
rgbCal: RGBCalibration;
sampledCal: SampledCalibration;
sampleWhite, rgbWhite: XYZ;
map: SampleProc = {
clipped: BOOLEAN;
xyz: XYZ ← CalibratedColor.XYZFromRGB[[in.s0/255.0, in.s1/255.0, in.s2/255.0],rgbCal];
IF useCIELAB THEN {
cielab: CIELAB ← CalibratedColorFns.CIELABFromXYZ[xyz,rgbWhite];
xyz ← [cielab.lStar, cielab.aStar, cielab.bStar];
};
[,,clipped] ← CalibratedColor.TripleFromXYZ[xyz,sampledCal, 0.1];
IF clipped THEN out ← [0,0,0,1]
ELSE {
out.s0 ← in.s0;
out.s1 ← in.s1;
out.s2 ← in.s2;
out.s3 ← 0;
};
};
[rgbCal, sampledCal] ← GetCalibrations[$Conrac, $Cromalin];
sampleWhite ← CalibratedColor.XYZFromTriple[[1,1,1], sampledCal];
rgbWhite ← CalibratedColor.XYZFromRGB[[1,1,1], rgbCal];
convert the sampled calibration to CIELAB.
IF useCIELAB THEN FOR v1: NAT IN [0..8) DO
FOR v2: NAT IN [0..8) DO
FOR v3: NAT IN [0..8) DO
xyz: XYZ;
cielab: CIELAB;
xyz.X ← sampledCal.cieX[v1][v2][v3];
xyz.Y ← sampledCal.cieY[v1][v2][v3];
xyz.Z ← sampledCal.cieX[v1][v2][v3];
cielab ← CalibratedColorFns.CIELABFromXYZ[xyz,sampleWhite];
sampledCal.cieX[v1][v2][v3] ← cielab.lStar;
sampledCal.cieY[v1][v2][v3] ← cielab.aStar;
sampledCal.cieZ[v1][v2][v3] ← cielab.bStar;
ENDLOOP;
ENDLOOP;
ENDLOOP;
AdjustFiles[fileStem, adjusted, map];
};
RGBToLStar: ARRAY[0..255] OF BYTE
[0, 9, 18, 26, 33, 39, 44, 48, 52, 56, 60, 63, 66, 69, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, 94, 96, 97, 99, 101, 103, 104, 106, 107, 109, 110, 112, 113, 115, 116, 117, 119, 120, 121, 123, 124, 125, 126, 128, 129, 130, 131, 132, 133, 134, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197, 198, 198, 199, 200, 200, 201, 201, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 215, 215, 216, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 236, 237, 237, 238, 238, 239, 239, 240, 240, 240, 241, 241, 242, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255];
AdjustRGBToLStarGC: PUBLIC PROC [fileStem, adjusted: ROPE] ~ {
Maps MIN[R,G,B] through the RGBToLStar trc and adds it back into the image
map: SampleProc = {
gc: BYTEMIN[in.s0, in.s1, in.s2];
newgc: BYTE ← RGBToLStar[gc];
r: NAT ← in.s0-gc+newgc;
g: NAT ← in.s1-gc+newgc;
b: NAT ← in.s2-gc+newgc;
IF r >255 OR g>255 OR b>255 THEN out.s3 ← 1 ELSE out.s3 ← 0;
out.s0 ← MIN[255, r];
out.s1 ← MIN[255, g];
out.s2 ← MIN[255, b];
};
AdjustFiles[fileStem, adjusted, map];
};
CMYToLStar: ARRAY[0..255] OF BYTE
[0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 54, 54, 55, 55, 56, 57, 57, 58, 58, 59, 59, 60, 61, 61, 62, 63, 63, 64, 64, 65, 66, 66, 67, 68, 68, 69, 70, 70, 71, 72, 72, 73, 74, 74, 75, 76, 76, 77, 78, 79, 79, 80, 81, 81, 82, 83, 84, 84, 85, 86, 87, 88, 88, 89, 90, 91, 92, 92, 93, 94, 95, 96, 96, 97, 98, 99, 100, 101, 102, 103, 104, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 134, 135, 136, 138, 139, 140, 142, 143, 145, 146, 148, 149, 151, 152, 154, 156, 158, 159, 161, 163, 165, 167, 169, 171, 174, 176, 178, 181, 183, 186, 189, 192, 195, 199, 203, 207, 211, 216, 222, 229, 237, 246, 255];
AdjustCMYToLStar: PUBLIC PROC [fileStem, adjusted: ROPE] ~ {
Maps MIN[cmy] through the CMYToLStar trc and adds it back into the image
CMYToLStar is derived from RGBToLStar, so this whole thing is highly dubious
map: SampleProc = {
c: NAT ← 255-in.s0;
m: NAT ← 255-in.s1;
y: NAT ← 255-in.s2;
gc: BYTEMIN[c,m,y];
newgc: BYTE ← CMYToLStar[gc];
r: NAT ← 255-(c-gc+newgc);
g: NAT ← 255-(m-gc+newgc);
b: NAT ← 255-(y-gc+newgc);
IF r >255 OR g>255 OR b>255 THEN out.s3 ← 1 ELSE out.s3 ← 0;
out.s0 ← MIN[255, r];
out.s1 ← MIN[255, g];
out.s2 ← MIN[255, b];
};
AdjustFiles[fileStem, adjusted, map];
};
ConracToLStarTRC: PROC RETURNS[trc: PrintColorTransformation.TRCTable] = {
trc ← NEW[PrintColorTransformation.TRCTableRec];
FOR i: NAT IN [0..256) DO
trc[i] ← ConracToLStar[i];
ENDLOOP;
};
ConracToLStar: ARRAY[0..255] OF BYTE
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 9, 11, 14, 16, 19, 22, 25, 29, 32, 35, 37, 39, 42, 44, 46, 48, 51, 53, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 83, 85, 87, 88, 90, 91, 93, 94, 95, 97, 98, 100, 101, 103, 104, 106, 107, 109, 110, 112, 113, 114, 116, 117, 118, 119, 121, 122, 123, 125, 126, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 143, 144, 145, 146, 147, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 170, 171, 172, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 181, 182, 183, 184, 185, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 201, 202, 203, 204, 205, 205, 206, 207, 208, 208, 209, 209, 210, 211, 212, 212, 213, 214, 215, 216, 216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 227, 227, 228, 228, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255];
END.