DIRECTORY
ConvertToRasters, CrosfieldTape, OptronicsTape, Interpress, Imager, Rope, InterpressToTape, ImagerPixel, ImagerSample, ImagerTransformation, ImagerMaskCapture, PrintColor, Real, AIS, FS, IO, MessageWindow, PrintColorTransformations, TapesCommon, UnixTapeOps, SF, ImagerBox;
Converters
Writes the files specified on the tape in specified format format. List cannot be longer than 80, a generous limit given the typical file size.
WriteTape:
PUBLIC PROC [files:
LIST
OF IPSpec, format: Format, tapeSpec: TapeSpec] = {
SELECT format
FROM
crosfield => WriteCrosfieldTape[files, tapeSpec];
optronics => WriteOptronicsTape[files, tapeSpec];
ENDCASE;
};
As generating tapes and getting them processed takes a long time, it is important to be sure the correct bits are being written. Preview is used for checking position and windowing only. WriteAIS allows the more experience user to examine the CMYK or RGB density bytes produced by the different formats. For both routines, the the AIS files names will be generated by concatinating the wd with the root of the file name in the IPSpec. Typical use is to put all the AIS files in the same working directory.
It is inconvenient to examine AIS files at the high resolutions used by the tape formats.
reduceResolution reduces the resolution in the IPSpec by the indicated fraction.
Preview:
PUBLIC PROC [files:
LIST
OF IPSpec, wd:
ROPE ←
NIL, reduceResolution:
REAL ← 0.25] = {
This routine previews the IPSpec to be sure it is accurately windowed by making three RGB intensity files. The colorCorrection field of the IPSpec is ignored.
WriteRGBAIS[files: files, wd: wd, reduceResolution: reduceResolution, preview: TRUE];
};
WriteAIS:
PUBLIC PROC [files:
LIST
OF IPSpec, format: Format, wd:
ROPE ←
NIL, reduceResolution:
REAL ← 0.25] = {
Uses exactly the same code for rasterization as WriteTape but puts the bytes in 3 or 4 AIS files. This is CMYK negative files for Crosfield format, and RGB density files (will look negative) for Optronics. Use Preview to produce files easily viewed for proofing.
SELECT format
FROM
crosfield => WriteCrosfieldAIS[files: files, wd: wd, reduceResolution: reduceResolution];
optronics => WriteRGBAIS[files: files, wd: wd, reduceResolution: reduceResolution, preview: FALSE];
ENDCASE;
};
Internal Procedures
InitCrosfieldTape: PUBLIC PROC [files: LIST OF IPSpec, tapeSpec: TapeSpec, rewindOnClose: BOOLEAN] = {
Must include correct FileSpecs for every file that may appear on the tape.
tape: UnixTapeOps.TapeHandle ← UnixTapeOps.OpenDrive[tapeSpec.serverName, tapeSpec.drive, tapeSpec.density];
fileSpecList: CrosfieldTape.FileSpecList ← CrosfieldFromIPSpec[files];
size: REAL ← 0;
Check the size and write the master directory.
size ← CrosfieldTape.SizeOfList[fileSpecList, tapeSpec.density];
IF size > tapeSpec.tapeSize THEN SIGNAL WillNotFit;
CrosfieldTape.InitializeTape[tapeHandle: tape, files: fileSpecList, tapeNumber: tapeSpec.tapeNumber, name: tapeSpec.note];
IF rewindOnClose THEN UnixTapeOps.WriteEOT[tape];
UnixTapeOps.CloseDrive[tape, rewindOnClose];
};
AddToCrosfieldTape: PUBLIC PROC[tapeSpec: TapeSpec, fileNumber: NAT, file: IPSpec, inPosition, rewindOnClose: BOOLEAN] = {
File numbers start at 1. Files must be added in the order specified in the master directory.
tapeSpec should match the one used in the call to InitCrosfieldTape.
fileSpecList: CrosfieldTape.FileSpecList ← CrosfieldFromIPSpec[LIST[file]];
fileSpec: CrosfieldTape.FileSpec ← fileSpecList.first; --special case of a general procedure
tape: UnixTapeOps.TapeHandle;
tapeNotOpen: BOOLEAN ← TRUE;
rasterize: FileAction ~ {
crosfield: TapesCommon.FileHandle;
rasterProc: ConvertToRasters.RasterProc = {
--Callback from ConvertToRasters
IF tapeNotOpen
THEN {
--wait to open the tape until rasters start coming
tape ← UnixTapeOps.OpenDrive[serverName: tapeSpec.serverName, driveNumber: tapeSpec.drive,density: tapeSpec.density, rewind: FALSE];
crosfield ← CrosfieldTape.AddFileToTape[tape, fileSpec, fileNumber, tapeSpec.tapeNumber, inPosition];
tapeNotOpen ← FALSE;
};
[] ← crosfield.newLine[crosfield, raster]; --send one raster to tape
};
convert[rasterProc]; --images the InterpressMaster. May take a long time
};
DoFileList[files: LIST[file], fileAction: rasterize, reduceResolution: 1, format: crosfield];
IF rewindOnClose THEN UnixTapeOps.WriteEOT[tape];
UnixTapeOps.CloseDrive[tape, rewindOnClose];
};
CrosfieldFromIPSpec:
PROC[files:
LIST
OF IPSpec]
RETURNS [fileSpecList: CrosfieldTape.FileSpecList] = {
buildFileSpecs: FileAction ~ {
fileSpec: CrosfieldTape.FileSpec ←
NEW[CrosfieldTape.FileSpecRep ← [
pixelsPerInch: CrosfieldValToPPI[pixelsPerInch], nLines: nLines, nPixels: nPixels,
note: name, mapPixel: CrosfieldTape.ByteToCrosfield
]];
fileSpecList ← CONS[fileSpec, fileSpecList];
};
reverseList:
PROC[list: CrosfieldTape.FileSpecList]
RETURNS [new: CrosfieldTape.FileSpecList] = {
FOR l: CrosfieldTape.FileSpecList ← list, l.rest
UNTIL l=
NIL
DO
new ← CONS[l.first, new];
ENDLOOP;
};
DoFileList[files: files, fileAction: buildFileSpecs, reduceResolution: 1, format: crosfield];
fileSpecList ← reverseList[fileSpecList]; --aesthetics
};
WriteCrosfieldTape:
PROC [files:
LIST
OF IPSpec, tapeSpec: TapeSpec] = {
tape: UnixTapeOps.TapeHandle ← UnixTapeOps.OpenDrive[tapeSpec.serverName, tapeSpec.drive, tapeSpec.density];
fileSpecList: CrosfieldTape.FileSpecList ← CrosfieldFromIPSpec[files];
handleList, currentFile: TapesCommon.FileHandleList;
size: REAL ← 0;
rasterize: FileAction ~ {
crosfield: TapesCommon.FileHandle ← currentFile.first;
rasterProc: ConvertToRasters.RasterProc = {
--Callback from ConvertToRasters
[] ← crosfield.newLine[crosfield, raster]; --send one raster to tape
};
currentFile ← currentFile.rest;
convert[rasterProc]; --images the InterpressMaster
};
Check the size and write the master directory.
size ← CrosfieldTape.SizeOfList[fileSpecList, tapeSpec.density];
IF size > tapeSpec.tapeSize THEN SIGNAL WillNotFit;
handleList ← CrosfieldTape.WriteTape[tapeHandle: tape, files: fileSpecList, tapeNumber: tapeSpec.tapeNumber, name: tapeSpec.note];
currentFile ← handleList; --will be enumerated by rasterize
DoFileList[files: files, fileAction: rasterize, reduceResolution: 1, format: crosfield];
UnixTapeOps.WriteEOT[tape];
UnixTapeOps.CloseDrive[tape];
};
WriteOptronicsTape:
PROC [files:
LIST
OF IPSpec, tapeSpec: TapeSpec] = {
tape: UnixTapeOps.TapeHandle ← UnixTapeOps.OpenDrive[tapeSpec.serverName, tapeSpec.drive, tapeSpec.density];
fileSpecList: OptronicsTape.FileSpecList;
handleList, currentFile: TapesCommon.FileHandleList;
size: REAL ← 0;
buildFileSpecs: FileAction ~ {
fileSpec: OptronicsTape.FileSpec ←
NEW[OptronicsTape.FileSpecRep ← [
pixelsPerInch: OptronicsValToPPI[pixelsPerInch], nLines: nLines, nPixels: nPixels,
rgbInterleaved: NOT achromatic, maxD: DefaultMaxD[achromatic]
]];
fileSpecList ← CONS[fileSpec, fileSpecList];
};
reverseList:
PROC[list: OptronicsTape.FileSpecList]
RETURNS [new: OptronicsTape.FileSpecList] = {
FOR l: OptronicsTape.FileSpecList ← list, l.rest
UNTIL l=
NIL
DO
new ← CONS[l.first, new];
ENDLOOP;
};
rasterize: FileAction ~ {
fileHandle: TapesCommon.FileHandle ← currentFile.first;
rasterProc: ConvertToRasters.RasterProc = {
--Callback from ConvertToRasters
[] ← fileHandle.newLine[fileHandle, raster]; --send one raster to tape
};
currentFile ← currentFile.rest;
convert[rasterProc]; --images the InterpressMaster
};
Build the fileSpecs
DoFileList[files: files, fileAction: buildFileSpecs, reduceResolution: 1, format: optronics];
fileSpecList ← reverseList[fileSpecList]; --aesthetics
Check the size.
size ← OptronicsTape.SizeOfList[fileSpecList, tapeSpec.density];
IF size > tapeSpec.tapeSize THEN SIGNAL WillNotFit;
handleList ← OptronicsTape.WriteTape[tapeHandle: tape, header: tapeSpec.note, files: fileSpecList];
currentFile ← handleList; --will be enumerated by rasterize
DoFileList[files: files, fileAction: rasterize, reduceResolution: 1, format: optronics];
UnixTapeOps.WriteEOT[tape];
UnixTapeOps.CloseDrive[tape];
};
AISInfo: TYPE ~ RECORD [fileName: ROPE, raster: AIS.Raster, fRef: AIS.FRef, wRef: AIS.WRef];
WriteRGBAIS:
PROC [files:
LIST
OF IPSpec, wd:
ROPE ←
NIL, reduceResolution:
REAL ← 0.25, preview:
BOOLEAN] ~ {
Color: TYPE ~ MACHINE DEPENDENT {red(0), green(1), blue(2), gray(3)};
colorSuffix: ARRAY Color OF ROPE = ["red", "grn", "blu", NIL];
fileAction: FileAction ~ {
currentLine: NAT ← 0;
ais: ARRAY Color OF AISInfo;
rgbToDensity: OptronicsTape.TRCTable ← NIL;
SetUp:
PROC [c: Color] = {
ais[c].raster ← NEW[AIS.RasterPart ← [scanCount: nLines,
scanLength: nPixels, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]];
IF colorSuffix[c] =
NIL
THEN ais[c].fileName ←
IO.PutFR["%g%g.ais",
[rope[wd]], [rope[name]]]
ELSE ais[c].fileName ←
IO.PutFR["%g%g-%g.ais",
[rope[wd]], [rope[name]], [rope[colorSuffix[c]]]];
ais[c].fRef ← AIS.CreateFile[name: ais[c].fileName, raster: ais[c].raster];
ais[c].wRef ← AIS.OpenWindow[f: ais[c].fRef];
};
Done:
PROC [c: Color] = {
AIS.CloseWindow[w: ais[c].wRef];
AIS.CloseFile[f: ais[c].fRef];
};
rasterProc: ConvertToRasters.RasterProc = {
-- Callback from convert
length: NAT ~ raster.length;
writeLine:
PROC[c: Color] = {
wRef: AIS.WRef ~ ais[c].wRef;
buf: ImagerSample.SampleBuffer ~ raster[ORD[c]]; -- the order better be right!
*** for more speed, consider using AIS.UnsafeWriteLine here ***
FOR pixel:
NAT
IN [0..length)
DO
AIS.WriteSample[w: wRef, value: buf[pixel], line: currentLine, pixel: pixel];
ENDLOOP;
};
IF NOT preview THEN OptronicsTape.RGBToDensity[raster, rgbToDensity];
IF achromatic THEN writeLine[gray]
ELSE FOR c: Color IN [red..blue] DO writeLine[c]; ENDLOOP;
currentLine ← currentLine+1;
};
IF achromatic THEN SetUp[gray] ELSE {SetUp[red]; SetUp[green]; SetUp[blue]};
IF NOT preview THEN rgbToDensity ← OptronicsTape.MakeDensityToRGB[255,255, DefaultMaxD[achromatic]];
convert[rasterProc]; -- call to image the Interpress Master
IF achromatic THEN Done[gray] ELSE {Done[red]; Done[green]; Done[blue]};
};
DoFileList[files: files, fileAction: fileAction, reduceResolution: reduceResolution, format: optronics]
};
WriteCrosfieldAIS:
PROC [files:
LIST
OF IPSpec, wd:
ROPE ←
NIL, reduceResolution:
REAL ← 0.25] ~ {
Color: TYPE ~ MACHINE DEPENDENT {cyan(0), magenta(1), yellow(2), black(3)};
colorSuffix: ARRAY Color OF ROPE = ["cyan", "magenta", "yellow", "black"];
fileAction: FileAction ~ {
currentLine: NAT ← 0;
ais: ARRAY Color OF AISInfo;
SetUp:
PROC [c: Color] = {
ais[c].raster ← NEW[AIS.RasterPart ← [scanCount: nLines,
scanLength: nPixels, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]];
ais[c].fileName ←
IO.PutFR["%g%g-%g.ais",
[rope[wd]], [rope[name]], [rope[colorSuffix[c]]]];
ais[c].fRef ← AIS.CreateFile[name: ais[c].fileName, raster: ais[c].raster];
ais[c].wRef ← AIS.OpenWindow[f: ais[c].fRef];
};
Done:
PROC [c: Color] = {
AIS.CloseWindow[w: ais[c].wRef];
AIS.CloseFile[f: ais[c].fRef];
};
rasterProc: ConvertToRasters.RasterProc = {
-- Callback from convert
length: NAT ~ raster.length;
FOR c: Color
IN Color
DO
wRef: AIS.WRef ~ ais[c].wRef;
buf: ImagerSample.SampleBuffer ~ raster[ORD[c]]; -- the order better be right!
*** for more speed, consider using AIS.UnsafeWriteLine here ***
FOR pixel:
NAT
IN [0..length)
DO
AIS.WriteSample[w: wRef, value: buf[pixel], line: currentLine, pixel: pixel];
ENDLOOP;
ENDLOOP;
currentLine ← currentLine+1;
};
SetUp[cyan]; SetUp[magenta]; SetUp[yellow]; SetUp[black];
convert[rasterProc]; -- call to image the Interpress Master
Done[cyan]; Done[magenta]; Done[yellow]; Done[black];
};
DoFileList[files: files, fileAction: fileAction, reduceResolution: reduceResolution, format: crosfield];
};
FileAction:
TYPE ~
PROC [
name: ROPE,
nLines, nPixels: NAT,
pixelsPerInch: NAT,
convert: PROC [ConvertToRasters.RasterProc],
achromatic: BOOLEAN
] RETURNS [stop: BOOL ← FALSE];
metersPerInch: REAL = 0.0254;
defaultCC: ColorCorrection ← PrintColorTransformations.SWOPWithGCLinearLStar[];
DoFileList:
PROC [files:
LIST
OF IPSpec, fileAction: FileAction, reduceResolution:
REAL ← 1, format: Format] ~ {
log: Interpress.LogProc ~ {};
FOR list:
LIST
OF IPSpec ← files, list.rest
UNTIL list=
NIL
DO
ip: IPSpec ← list.first;
IF ip=
NIL
THEN
EXIT
ELSE {
name: ROPE ~ FileNameBase[ip.name];
comp: REAL ← Compensate[ip.pixelsPerInch, format]; --crosfield pixels aren't square
nLines: NAT ~ Real.Round[0.5+ip.sDim*ip.pixelsPerInch*reduceResolution*comp];
nPixels: NAT ~ Real.Round[0.5+ip.fDim*ip.pixelsPerInch*reduceResolution];
rasterType:
PROC
RETURNS[ConvertToRasters.RasterType] = {
IF format=crosfield THEN RETURN[cmyk]
ELSE IF ip.achromatic THEN RETURN[gray]
ELSE RETURN[rgb];
};
cc:
PROC
RETURNS[ColorCorrection] = {
IF format=optronics THEN RETURN[NIL];
IF ip.colorCorrection=NIL THEN RETURN[defaultCC]
ELSE RETURN[ip.colorCorrection];
};
rasterSpec: ConvertToRasters.RasterSpec ← [
type: rasterType[], fSize: nPixels, sSize: nLines,
surfaceUnitsPerPixel: ip.surfaceUnitsPerPixel,
colorCorrection: cc[]
];
convert:
PROC [rasterProc: ConvertToRasters.RasterProc] ~ {
call to image the Interpress Master
[] ← ConvertToRasters.FromIP[ip: ip.name, ppiF: ip.pixelsPerInch*reduceResolution, ppiS: ip.pixelsPerInch*reduceResolution*comp, page: ip.page, rasterSpec: rasterSpec, proc: rasterProc, x: ip.fOrg, y: ip.sOrg, w: ip.fDim, h: ip.sDim];
};
MessageWindow.Append[IO.PutFR["Starting on %g, %g lines by %g pixels at %g", IO.rope[name], IO.int[nLines], IO.int[nPixels],IO.time[]], TRUE];
IF fileAction[name: name, nLines: nLines, nPixels: nPixels, pixelsPerInch: ip.pixelsPerInch, convert: convert, achromatic: ip.achromatic].stop THEN EXIT;
}
ENDLOOP;
};
FileNameBase:
PROC [name:
ROPE]
RETURNS [
ROPE] ~ {
fullFName: ROPE; cp: FS.ComponentPositions;
[fullFName: fullFName, cp: cp] ← FS.ExpandName[name];
RETURN[Rope.Substr[fullFName, cp.base.start, cp.base.length]];
};
Compensate:
PROC [ppi:
REAL, format: Format]
RETURNS [yScale:
REAL] ~ {
Compensates for the non-square Crosfield pixels
IF format=crosfield
THEN yScale ←
SELECT ppi
FROM
300 => 304.8/300, 450 => 406.4/450, 600 => 609.6/600, ENDCASE => ERROR
ELSE yScale ← 1;
};
OptronicsValToPPI:
PROC [val:
NAT]
RETURNS [ppi: OptronicsTape.PixelsPerInch] ~ {
ppi ←
SELECT val
FROM
63 => ppi63, 127 => ppi127, 254 => ppi254, 508 => ppi508, 1015 => ppi1015, 2030 => ppi2030, ENDCASE => ERROR;
};
CrosfieldValToPPI:
PROC [val:
NAT]
RETURNS [ppi: CrosfieldTape.PixelsPerInch] ~ {
ppi ←
SELECT val
FROM
300 => ppi300, 450 => ppi450, 600 => ppi600, ENDCASE => ERROR;
};
DefaultMaxD:
PROC [achromatic:
BOOLEAN]
RETURNS [
REAL] ~ {
RETURN[IF achromatic THEN 2.0 ELSE 1.4];
};
Debugging Procs
MakeIPSpec: PROC [name: ROPE, page: NAT, pixelsPerInch: NAT, fDim: REAL, sDim: REAL, surfaceUnitsPerPixel: NAT ← 5, colorCorrection: ColorCorrection ← NIL, gray: BOOLEAN ← FALSE] RETURNS [IPSpec] ~ {
RETURN[NEW[InterpressToTape.IPSpecRep ← [name: name, page: page, pixelsPerInch: pixelsPerInch, fDim: fDim, sDim: sDim, surfaceUnitsPerPixel: surfaceUnitsPerPixel, colorCorrection: colorCorrection, gray: gray]]];
};
Sizes: TYPE = RECORD[name: ROPE, size: REAL];
ToTape: PROC [name: ROPE, page: NAT, pixelsPerInch: NAT, fDim, sDim: REAL, surfaceUnitsPerPixel: NAT ← 1, drive: NAT ← 1, nBands: NAT ← 1] ~ {
ipSpec: IPSpec ←NEW[InterpressToTape.IPSpecRep ← [name: Rope.Cat[name, ".ip"], page: page, pixelsPerInch: pixelsPerInch, fDim: fDim, sDim: sDim, surfaceUnitsPerPixel: surfaceUnitsPerPixel, nBands: nBands]];
InterpressToCrosfield[LIST[ipSpec], drive, name, 102];
};
ToAIS: PROC [name: ROPE, page: NAT, pixelsPerInch: NAT, fDim, sDim: REAL, surfaceUnitsPerPixel: NAT ← 1, nBands: NAT ← 1, pixelProc: CrosfieldTape.PixelProc, suffix: ROPE ← NIL] ~ {
ipSpec: IPSpec ←NEW[InterpressToTape.IPSpecRep ← [name: Rope.Cat[name, ".ip"], page: page, pixelsPerInch: pixelsPerInch, fDim: fDim, sDim: sDim, surfaceUnitsPerPixel: surfaceUnitsPerPixel, nBands: nBands]];
InterpressToAIS[LIST[ipSpec], suffix, pixelProc];
};