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: NAT _ SIZE[BufferRep]; sSize: INT _ Real.RoundLI[mag*raster.scanCount]; fSize: NAT _ MIN[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: ROPE _ NIL; 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: INT _ IF args.argc > 2 THEN Convert.IntFromRope[args[2]] ELSE 1; pdFilename: ROPE _ LocalName[Rope.Concat[filestem, ".pd"]]; ditherFilename: ROPE _ NIL; 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 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. ŠPDDitherImpl.mesa Michael Plass, April 23, 1984 9:59:51 am PST At least one of the colors was a catch-all, so kill the black layer. Ê Ý˜J™J™,J˜JšÏk œQœT˜±J˜šœ ˜Jšœœœ.œM˜£Jšœ˜Jšœœœ˜Jšœœœ˜&šœœœ˜#J˜—Jšœ œœ ˜ Jš œ œœœ œ ˜7Jš œ œœœœœ˜XJš œ œœœ œœœ˜=š œœœœœ˜8J˜—š Ïnœœ œœœœ˜QJšœœ˜šœ œ˜Jšœœ˜Jšœ˜Jšœ˜Jšœ˜—šœ˜Jšœœ˜Jšœœ ˜#Jšœ œ˜Jšœ˜—Jšœ#˜#Jšœ˜Jšœ˜Jšœ˜J˜—šž œœbœ˜zJšœ!œ˜-Jšœ œ˜šœœœ ˜Jšœ œ˜!Jšœ œ˜#Jšœ œ˜"JšœœE˜TJšœœ˜(JšœC˜CJšœœ)˜8Jšœ œ-˜>Jšœ œ+˜;Jšœ!˜!Jšœ/˜/Jšœ1˜1Jšœ0˜0Jš˜—Jšœ˜J˜—š žœœœœœœ˜PJšœœ!˜+Jšœœ˜%Jš œœ œ#œœ˜[Jš œœ œ$œœ˜[Jšœ ˜ Jšœ ˜Jšœ˜Jšœ˜J˜—š žœœœ8œœ˜^Jšœœœ˜Jšœœ"˜-Jšœœ œ˜,JšœœS˜fJšœ œ$˜1Jšœ œ#˜/Jšœœ˜(Jšœœ!˜,Jšœœ ˜*Jšœœ ˜#Jšœœ ˜%Jšœœ ˜$Jšœ œœ ˜ Jšœœ&˜0Jšœœœ,˜œ˜©JšœCœ˜\Jšœ9˜9Jšœœœ˜.JšœF˜FJšœœœ˜-Jšœ˜J˜—š ž œœ œœ œ˜>Jšœ œ˜Jšœœ˜Jšœœ˜)JšœG˜GJšœ˜J˜—šžœ˜*Jšœ:˜:Jšœ œ ˜Jš œœœœœ˜EJšœ œ+˜;Jšœœœ˜Jš œ9œ œœ&œœœ˜žJšœ˜J˜—Jšžœ˜ Jšžœ˜Jšœ œœœ˜2Jšœ œœœ˜4Jšœ œœœ˜3Jšœ œœ˜:Jšœœœ˜Jšœ œ˜šœ ˜J™DJšœ œ ˜—Jšœ˜J˜—šžœœœ˜Jšœœœ˜Jšœ8˜8procšœœ˜5Jšœ%˜%Jšœ#˜#Jšœ%˜%Jšœ$˜$Jšœ˜—šœœœ ˜Jšœ%˜%šœœœ ˜Jšœ9˜9šœœœ ˜Jšœ-˜-Jšœ9˜9Jšœœ1˜:Jšœœ1˜