PDDitherImpl.mesa
Michael Plass, April 23, 1984 9:59:51 am PST
DIRECTORY Convert, Real, Basics, IO, FS, CommandTool, Commander, PDFileWriter, BasicTime, AIS, Rope, DisplayMap, Process, FSExtras, MessageWindow, Scaled, PieViewers, ViewerOps;
PDDitherImpl: CEDAR MONITOR
IMPORTS Convert, Real, Basics, IO, FS, CommandTool, Rope, Commander, PDFileWriter, AIS, DisplayMap, Process, FSExtras, MessageWindow, Scaled, PieViewers, ViewerOps
= BEGIN
ROPE: TYPE ~ Rope.ROPE;
InconsistentSeparations: ERROR ~ CODE;
NotEightBitsPerPixel: ERROR ~ CODE;
BufferRef: TYPE ~ REF BufferRep;
BufferRep: TYPE ~ PACKED ARRAY [0..8*1024) OF [0..256);
UnpackedRGB: TYPE ~ RECORD [r: REF UnpackedRep, g: REF UnpackedRep, b: REF UnpackedRep];
UnpackedRep: TYPE ~ RECORD [SEQUENCE length: NAT OF INTEGER];
PalixTripleTable: TYPE ~ ARRAY [0..24*24*24) OF INTEGER;
PropagateError: PROC [cur, nxt: REF UnpackedRep, err: INTEGER, j: NAT] ~ INLINE {
threeEighths, quarter: INTEGER;
IF err >= 0 THEN {
e: CARDINAL ← err;
threeEighths ← (e+e+e)/8;
quarter ← e/4;
}
ELSE {
e: CARDINAL ← -err;
threeEighths ← -INTEGER[(e+e+e)/8];
quarter ← -INTEGER[e/4];
};
cur[j+1] ← cur[j+1] + threeEighths;
nxt[j+1] ← nxt[j+1] + quarter;
nxt[j] ← nxt[j] + threeEighths;
};
DitherLine: PROC [prevPixels: BufferRef, curRGB: UnpackedRGB, curPixels: BufferRef, nextRGB: UnpackedRGB, length: NAT] ~ {
redError, greenError, blueError: INTEGER ← 0;
prevPalix: CARDINAL ← 0;
FOR j: NAT IN [0..length) DO
redTarget: INTEGER ← curRGB.r[j];
greenTarget: INTEGER ← curRGB.g[j];
blueTarget: INTEGER ← curRGB.b[j];
palix: CARDINAL ← DisplayMap.GetIndex[redTarget, greenTarget, blueTarget, mapTable];
prevLinePalix: CARDINAL ← prevPixels[j];
palixTriple: [0..24*24*24) ← (palix*24+prevPalix)*24+prevLinePalix;
redErr: INTEGER ← redTarget - redCombined^[palixTriple];
greenErr: INTEGER ← greenTarget - greenCombined^[palixTriple];
blueErr: INTEGER ← blueTarget - blueCombined^[palixTriple];
curPixels[j] ← prevPalix ← palix;
PropagateError[curRGB.r, nextRGB.r, redErr, j];
PropagateError[curRGB.g, nextRGB.g, greenErr, j];
PropagateError[curRGB.b, nextRGB.b, blueErr, j];
ENDLOOP;
};
PreAllocateFiles: PROC [pdFilename: ROPE, estimatedBits: REAL] RETURNS [INT] ~ {
bytes: INT ← Real.RoundLI[estimatedBits/8];
pages: INT ← FS.PagesForBytes[bytes];
tmp: FS.OpenFile ← FS.Create[name: "PDTemp$", setPages: TRUE, pages: pages, setKeep: TRUE];
pd: FS.OpenFile ← FS.Create[name: pdFilename, setPages: TRUE, pages: pages, setKeep: TRUE];
FS.Close[pd];
FS.Close[tmp];
RETURN [bytes]
};
Dither: ENTRY PROC [redFilename, greenFilename, blueFilename, pdFilename: ROPE, mag: REAL] ~ {
ENABLE UNWIND => NULL;
redAIS: AIS.FRef ← AIS.OpenFile[redFilename];
raster: AIS.Raster ← AIS.ReadRaster[redAIS];
estimatedBytes: INT ← PreAllocateFiles[pdFilename, (raster.scanCount*raster.scanLength*mag+10)*mag*4];
greenAIS: AIS.FRef ← AIS.OpenFile[greenFilename];
blueAIS: AIS.FRef ← AIS.OpenFile[blueFilename];
redW: AIS.WRef ← AIS.OpenWindow[redAIS];
greenW: AIS.WRef ← AIS.OpenWindow[greenAIS];
blueW: AIS.WRef ← AIS.OpenWindow[blueAIS];
redBuf: BufferRef ← NEW[BufferRep];
greenBuf: BufferRef ← NEW[BufferRep];
blueBuf: BufferRef ← NEW[BufferRep];
bufWords: NATSIZE[BufferRep];
sSize: INT ← Real.RoundLI[mag*raster.scanCount];
fSize: NATMIN[Real.RoundLI[mag*raster.scanLength], 8000];
sInputDelta: Scaled.Value ← Scaled.FromReal[(raster.scanCount+0.0)/sSize];
fInputDelta: Scaled.Value ← Scaled.FromReal[(raster.scanLength+0.0)/fSize];
sInput: Scaled.Value ← Scaled.Halve[sInputDelta];
sOutput: INT ← 0;
SetToner: PROC [toner: PDFileWriter.Toner, fractionInk: REAL] ~ {
SELECT fractionInk FROM
< 0.5 => pd.SetColorOff[toner];
ENDCASE => pd.SetColorInk[toner];
};
Color: PROC [b, c, m, y: REAL ← 0] ~ {
SetToner[black, b];
SetToner[cyan, c];
SetToner[magenta, m];
SetToner[yellow, y];
};
ColorBox: PROC [x: NAT, b, c, m, y: REAL ← 0] ~ {
Color[b, c, m, y];
pd.MaskRectangle[sMin: sSize + 25, sSize: 50, fMin: x*50, fSize: 50];
};
pd: PDFileWriter.PDState ← PDFileWriter.Create[
fileName: pdFilename,
deviceCode: VAL[7],
sResolution: 72,
fResolution: 72,
imageSSize: sSize + 100,
imageFSize: fSize,
bandSSize: 64,
copies: 1,
leftOverMode: TRUE
];
curRGB: UnpackedRGB ← [NEW[UnpackedRep[fSize+1]], NEW[UnpackedRep[fSize+1]], NEW[UnpackedRep[fSize+1]]];
nextRGB: UnpackedRGB ← [NEW[UnpackedRep[fSize+1]], NEW[UnpackedRep[fSize+1]], NEW[UnpackedRep[fSize+1]]];
GetNextRGB: PROC [scanNumber: NAT] ~ TRUSTED {
fInput: Scaled.Value ← Scaled.Halve[fInputDelta];
fOutput: NAT ← 0;
j: NAT ← 0;
scanNumber ← MIN[scanNumber, raster.scanCount-1];
AIS.UnsafeReadLine[redW, [length: bufWords, addr: LOOPHOLE[redBuf]], scanNumber];
AIS.UnsafeReadLine[greenW, [length: bufWords, addr: LOOPHOLE[greenBuf]], scanNumber];
AIS.UnsafeReadLine[blueW, [length: bufWords, addr: LOOPHOLE[blueBuf]], scanNumber];
WHILE (j ← Scaled.Floor[fInput]) < raster.scanLength DO
nextRGB.r[fOutput] ← redBuf[j];
nextRGB.g[fOutput] ← greenBuf[j];
nextRGB.b[fOutput] ← blueBuf[j];
fInput ← fInput.PLUS[fInputDelta];
fOutput ← fOutput + 1;
ENDLOOP;
};
prevPixels: BufferRef ← NEW[BufferRep];
curPixels: BufferRef ← NEW[BufferRep];
OutputSeparation: PROC [bitNumber: NAT, toner: PDFileWriter.Toner] ~ {
Deliver: PROC [captureScanLineProc: PDFileWriter.CaptureScanLineProc] ~ TRUSTED {
bits: PACKED ARRAY [0..8000) OF [0..1];
FOR j: CARDINAL IN [0..fSize) DO
bits[j] ← Basics.BITAND[Basics.BITSHIFT[curPixels[j], -bitNumber], 1];
ENDLOOP;
captureScanLineProc[@bits];
};
pd.ColorSamples[toner: toner, sMin: sOutput, fMin: 0, sSize: 1, fSize: fSize, deliverProc: Deliver];
};
pieViewer: PieViewers.PieViewer ← NIL;
pieViewer ← PieViewers.Create[parent: NIL, diameter: 0, total: raster.scanCount];
pieViewer.name ← pdFilename;
pieViewer.icon ← private;
BEGIN ENABLE UNWIND => {ViewerOps.DestroyViewer[pieViewer]; pieViewer ← NIL};
IF raster.bitsPerPixel # 8 THEN ERROR NotEightBitsPerPixel;
IF AIS.ReadRaster[greenAIS]^ # raster^ THEN ERROR InconsistentSeparations;
IF AIS.ReadRaster[blueAIS]^ # raster^ THEN ERROR InconsistentSeparations;
pd.StartImage[[TRUE, TRUE, TRUE, TRUE]];
GetNextRGB[Scaled.Floor[sInput]];
FOR f: NAT IN [0..fSize) DO curPixels[f] ← 0 ENDLOOP;
WHILE NAT[Scaled.Floor[sInput]] < raster.scanCount DO
PieViewers.Set[pieViewer, raster.scanCount-Scaled.Floor[sInput]];
{t: BufferRef ← prevPixels; prevPixels ← curPixels; curPixels ← t};
{t: UnpackedRGB ← curRGB; curRGB ← nextRGB; nextRGB ← t};
sInput ← sInput.PLUS[sInputDelta];
GetNextRGB[Scaled.Floor[sInput]];
DitherLine[prevPixels, curRGB, curPixels, nextRGB, fSize];
OutputSeparation[3, black];
OutputSeparation[2, cyan];
OutputSeparation[1, magenta];
OutputSeparation[0, yellow];
sOutput ← sOutput + 1;
ENDLOOP;
ColorBox[0, 0, 0, 0, 1];
ColorBox[1, 0, 0, 1, 0];
ColorBox[2, 0, 1, 0, 0];
ColorBox[3, 1, 0, 0, 0];
ColorBox[4, 0, 1, 1, 0];
ColorBox[5, 0, 1, 0, 1];
ColorBox[6, 0, 0, 1, 1];
ColorBox[7, 0, 1, 1, 1];
ColorBox[8, 1, 0, 0, 1];
ColorBox[9, 1, 0, 1, 0];
ColorBox[10, 1, 1, 0, 0];
ColorBox[11, 1, 0, 0, 0];
ColorBox[12, 1, 1, 1, 0];
ColorBox[13, 1, 1, 0, 1];
ColorBox[14, 1, 0, 1, 1];
ColorBox[15, 1, 1, 1, 1];
pd.EndPage;
pd.Close;
AIS.CloseWindow[blueW];
AIS.CloseWindow[greenW];
AIS.CloseWindow[redW];
AIS.CloseFile[blueAIS];
AIS.CloseFile[greenAIS];
AIS.CloseFile[redAIS];
END;
ViewerOps.DestroyViewer[pieViewer]; pieViewer ← NIL;
};
MakeDitheredPD: PROC [filestem: ROPE, log: IO.STREAM, mag: REAL] RETURNS [ditherFilename: ROPE] ~ {
redFilename: ROPE ← Rope.Concat[filestem, "-red.ais"];
greenFilename: ROPE ← Rope.Concat[filestem, "-grn.ais"];
blueFilename: ROPE ← Rope.Concat[filestem, "-blu.ais"];
fullFName, attachedTo: ROPENIL;
created: BasicTime.GMT;
ditherCreated: BasicTime.GMT ← BasicTime.nullGMT;
[fullFName: fullFName, attachedTo: attachedTo, created: created] ← FS.FileInfo[greenFilename ! FS.Error => {greenFilename ← Rope.Concat[filestem, "-green.ais"]; CONTINUE}];
[fullFName: fullFName, attachedTo: attachedTo, created: created] ← FS.FileInfo[blueFilename ! FS.Error => {blueFilename ← Rope.Concat[filestem, "-blue.ais"]; CONTINUE}];
[fullFName: fullFName, attachedTo: attachedTo, created: created] ← FS.FileInfo[redFilename];
ditherFilename ← LocalName[Rope.Concat[filestem, ".pd"]];
IF log # NIL THEN log.PutRope[ditherFilename];
Dither[redFilename, greenFilename, blueFilename, ditherFilename, mag];
IF log # NIL THEN log.PutRope[" written.\n"];
};
LocalName: PROC [fileName: ROPE] RETURNS [localName: ROPE] ~ {
fullName: ROPE;
cp: FS.ComponentPositions;
[fullName, cp] ← FS.ExpandName[fileName];
localName ← fullName.Substr[cp.base.start, cp.ext.start+cp.ext.length];
};
PDDitherCommand: Commander.CommandProc ~ {
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
filestem: ROPE ← args[1];
mag: INTIF args.argc > 2 THEN Convert.IntFromRope[args[2]] ELSE 1;
pdFilename: ROPE ← LocalName[Rope.Concat[filestem, ".pd"]];
ditherFilename: ROPENIL;
ditherFilename ← MakeDitheredPD[filestem, cmd.out, mag ! FS.Error => {IF error.group = user THEN {cmd.out.PutRope[error.explanation]; CONTINUE} ELSE REJECT}];
};
mapTable: DisplayMap.ColorTable;
mapPal: DisplayMap.PalTable;
redComp: ARRAY DisplayMap.ColorMapSize OF INTEGER;
greenComp: ARRAY DisplayMap.ColorMapSize OF INTEGER;
blueComp: ARRAY DisplayMap.ColorMapSize OF INTEGER;
redCombined: REF PalixTripleTable ← NEW[PalixTripleTable];
greenCombined: REF PalixTripleTable ← NEW[PalixTripleTable];
blueCombined: REF PalixTripleTable ← NEW[PalixTripleTable];
overlap: REAL ← 0.02854;
CombinedPalix: PROC [p, q: CARDINAL] RETURNS [r: CARDINAL] ~ {
r ← Basics.BITOR[p, q];
IF r >= 16 THEN
At least one of the colors was a catch-all, so kill the black layer.
r ← Basics.BITAND[r, 16+7];
};
InitMap: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
[mapTable, mapPal] ← DisplayMap.Restore["Versatec.tab"];
FOR c: DisplayMap.ColorMapSize IN [0..mapPal.size) DO
t: DisplayMap.TripletRec ← mapPal[c];
redComp[c] ← Real.RoundLI[t.r*255];
greenComp[c] ← Real.RoundLI[t.g*255];
blueComp[c] ← Real.RoundLI[t.b*255];
ENDLOOP;
FOR p: NAT IN [0..24) DO
t: DisplayMap.TripletRec ← mapPal[p];
FOR pi: NAT IN [0..24) DO
ti: DisplayMap.TripletRec ← mapPal[CombinedPalix[p, pi]];
FOR pj: NAT IN [0..24) DO
palixTriple: [0..24*24*24) ← (p*24+pi)*24+pj;
tj: DisplayMap.TripletRec ← mapPal[CombinedPalix[p, pj]];
red: REAL ← t.r * (1-2*overlap) + (ti.r + tj.r) * overlap;
green: REAL ← t.g * (1-2*overlap) + (ti.g + tj.g) * overlap;
blue: REAL ← t.b * (1-2*overlap) + (ti.b + tj.b) * overlap;
redCombined^[palixTriple] ← Real.RoundLI[red*255];
greenCombined^[palixTriple] ← Real.RoundLI[green*255];
blueCombined^[palixTriple] ← Real.RoundLI[blue*255];
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
WatchForNewMap: PROC ~ {
createEvent: REF READONLY FSExtras.CreateEvent ← NIL;
DO
createEvent ← FSExtras.NextCreateEvent[createEvent];
IF Rope.Find[createEvent.fName, "versatec.tab", 0, FALSE] >= 0 THEN {
InitMap[];
MessageWindow.Append[" Versatec.tab reloaded", TRUE];
};
ENDLOOP;
};
InitMap[];
TRUSTED {Process.Detach[FORK WatchForNewMap[]]};
Commander.Register["PDDither", PDDitherCommand, "Take a set of color ais files and make a dithered PD file for the Color Veratec."];
END.