DIRECTORY Rope USING [Equal, Fetch, FromChar, Length, ROPE, ToRefText, FromRefText, Cat, Substr], CedarProcess USING [CheckAbort], OptronicsTape, FS USING [ComponentPositions, Error, ExpandName, FileInfo, StreamOpen], CommandTool USING [ArgumentVector, NumArgs, Parse], Commander USING [CommandProc, Register], Convert USING [CardFromRope, RopeFromTime], BasicTime USING [Now], AIS USING [CloseFile, CloseWindow, CreateFile, FRef, OpenWindow, Raster, RasterPart, WRef, WriteSample], UnixTapeOps USING[BackSpaceFile, CloseDrive, CloseOpenConnections, Density, ForwardSpaceFile, OpenDrive, ReadRecord, Rewind, TapeHandle, WriteFileMark, WriteRecord ], ImagerPixel USING [GetPixels, NewPixels, PixelBuffer, PixelMap, PixelMapRep], ImagerSample USING [GetBox], SF USING [SizeF, SizeS], IO USING [EndOfStream, GetLineRope, PutRope, SP, STREAM], UserCredentials USING [Get], ConvertRasterObject USING [SampleMapFromAIS], TapesCommon USING [FileHandleList, FileHandle, FileHandleRep]; OptronicsTapeImpl: CEDAR PROGRAM IMPORTS BasicTime, CedarProcess, Commander, CommandTool, Convert, ConvertRasterObject, FS, ImagerPixel, ImagerSample, SF, UnixTapeOps, Rope, AIS, IO, -- Real, RealFns, -- UserCredentials EXPORTS OptronicsTape ~ BEGIN OPEN OptronicsTape; TapeError: PUBLIC SIGNAL [reason: ErrorDesc] = CODE; ROPE: TYPE ~ Rope.ROPE; TapeHandle: TYPE ~ UnixTapeOps.TapeHandle; FileHandleList: TYPE ~ TapesCommon.FileHandleList; FileHandle: TYPE ~ TapesCommon.FileHandle; FileHandleRep: TYPE ~ TapesCommon.FileHandleRep; PixelBuffer: TYPE ~ ImagerPixel.PixelBuffer; PixelMap: TYPE ~ ImagerPixel.PixelMap; sizeHeader: NAT _ 512; red: NAT = 0; green: NAT = 1; blue: NAT = 2; State: TYPE ~ REF StateRep; StateRep: TYPE ~ RECORD [ interleaved: BOOLEAN, tapeHandle: TapeHandle _ NIL, buffer: REF TEXT _ NIL, linesLeftInFile: INT, rgbToDensity: TRCTable _ NIL ]; maxColorD: REAL _ 1.5; maxGrayD: REAL _ 2.0; headers: BOOLEAN _ FALSE; WriteTape: PUBLIC PROC [tapeHandle: TapeHandle, header: ROPE, files: FileSpecList] RETURNS [TapesCommon.FileHandleList] = { fileHandleList: FileHandleList _ NIL; makeFileHandle: PROC [interleaved: BOOLEAN, rgbToDensity: TRCTable, pixels,lines: INT] RETURNS[fileHandle: FileHandle] ~ { state: State _ NEW[StateRep _ [ interleaved: interleaved, tapeHandle: tapeHandle, buffer: NEW[TEXT[pixels]], linesLeftInFile: lines, rgbToDensity: rgbToDensity ]]; fileHandle _ NEW[FileHandleRep _ [newLine: WriteNewLine, data: state]]; }; reverse: PROC[files: FileHandleList] RETURNS[r: FileHandleList] = { r _ NIL; FOR f: FileHandleList _ files, f.rest UNTIL f=NIL DO r _ CONS[f.first, r]; ENDLOOP; }; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO fileSpec: FileSpec _ f.first; ppl: INT _ fileSpec.nPixels; lines: INT _ fileSpec.nLines; rgbToDensity: TRCTable _ MakeRGBToDensity[255,255, fileSpec.maxD]; fileHandleList _ CONS[makeFileHandle[fileSpec.rgbInterleaved,rgbToDensity,ppl,lines],fileHandleList]; ENDLOOP; IF headers THEN WriteTapeHeader[tapeHandle]; fileHandleList _ reverse[fileHandleList]; RETURN[fileHandleList]; }; GetTapeHandle: PROC [] RETURNS [TapeHandle] ~ { tapeHandle: TapeHandle _ UnixTapeOps.OpenDrive["Chroma"]; RETURN[tapeHandle]; }; WriteTapeHeader: PROC [tapeHandle: TapeHandle] ~ { headerAsText: REF TEXT; headerAsText _ Rope.ToRefText[ Rope.Cat[ "Written by ", UserCredentials.Get[].name, " at ", Convert.RopeFromTime[BasicTime.Now[]] ]]; IF ~ headers THEN RETURN; UnixTapeOps.Rewind[tapeHandle]; UnixTapeOps.WriteRecord[tapeHandle, headerAsText]; UnixTapeOps.WriteFileMark[tapeHandle]; }; OptronicsFromPPI: PROC [ppi: NAT] RETURNS [pixelsPerInch: PixelsPerInch] ~ { SELECT ppi FROM 63 => pixelsPerInch _ ppi63; 127 => pixelsPerInch _ ppi127; 254 => pixelsPerInch _ ppi254; 508 => pixelsPerInch _ ppi508; 1015 => pixelsPerInch _ ppi1015; 2030 => pixelsPerInch _ ppi2030; ENDCASE => SIGNAL TapeError[[$MisMatch, "illegitimate pixels per inch"]]; }; WriteNewLine: PROC[self: FileHandle, line: PixelBuffer] RETURNS[eof: BOOLEAN] = { state: State _ NARROW[self.data]; WriteScan: PROC[s: NAT] = { FOR i: INT IN [0..line.length) DO state.buffer[i] _ VAL[line[s][i]]; ENDLOOP; state.buffer.length _ line.length; UnixTapeOps.WriteRecord[state.tapeHandle, state.buffer]; }; RGBToDensity[line,state.rgbToDensity]; IF state.buffer=NIL OR state.buffer.maxLength < line.length THEN state.buffer _ NEW[TEXT[line.length]]; IF state.interleaved THEN { IF line.samplesPerPixel#3 THEN ERROR; FOR i: NAT IN [0..3) DO WriteScan[i]; ENDLOOP; } ELSE WriteScan[0]; state.linesLeftInFile _ state.linesLeftInFile-1; IF state.linesLeftInFile=0 THEN { UnixTapeOps.WriteFileMark[state.tapeHandle]; eof _ TRUE; }; }; SkipTapeFiles: PROC[self: TapeHandle, nFiles: NAT] ~ { FOR i: NAT IN [0..nFiles) DO UnixTapeOps.ForwardSpaceFile[self]; ENDLOOP; }; CloseTape: PROC[self: TapeHandle] ~ { UnixTapeOps.WriteFileMark[self]; UnixTapeOps.WriteFileMark[self]; UnixTapeOps.WriteFileMark[self]; UnixTapeOps.CloseDrive[self]; -- Close off tape }; AISToTape: PROC [tapeHandle: TapeHandle, aisRoot: ROPE, color: BOOLEAN, pixelsPerInch: NAT, report: IO.STREAM ] ~ { PasteInLabel: PROC[fileRoot: Rope.ROPE, label: Rope.ROPE] RETURNS[Rope.ROPE] ~ { cp: FS.ComponentPositions; fullFName, fName, ext: Rope.ROPE; [fullFName, cp, ] _ FS.ExpandName[fileRoot]; IF cp.ext.length = 0 THEN { fName _ Rope.Substr[ fullFName, 0, cp.ext.start]; -- name for filling in numbers ext _ "ais"; } ELSE { fName _ Rope.Substr[ fullFName, 0, cp.ext.start-1]; -- drop "." before ext ext _ Rope.Substr[ fullFName, cp.ext.start, cp.ext.length]; }; RETURN[ Rope.Cat[fName, label, ".", ext] ]; }; FindFile: PROC [fileRoot: Rope.ROPE, names: RECORD[ pref, alt, other: Rope.ROPE ] ] RETURNS [name: Rope.ROPE] ~ { ok: BOOLEAN _ TRUE; [] _ FS.FileInfo[name _ PasteInLabel[fileRoot, names.pref] ! FS.Error => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN[ name ] ELSE ok _ TRUE; [] _ FS.FileInfo[name _ PasteInLabel[fileRoot, names.alt] ! FS.Error => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN[ name ] ELSE ok _ TRUE; [] _ FS.FileInfo[name _ PasteInLabel[fileRoot, names.other] ! FS.Error => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN[ name ] ELSE ok _ TRUE; [] _ FS.FileInfo[name _ fileRoot ! FS.Error => {ok _ FALSE; CONTINUE}]; IF ok THEN RETURN[ name ] ELSE { SIGNAL TapeError[[ $MisMatch, Rope.Cat[" Can't find ", fileRoot, " or with extensions: ", Rope.Cat[names.pref, " ", names.alt, " ", names.other] ] ]]; RETURN[ NIL ]; }; }; numFiles, xSize, ySize: NAT; names: ARRAY[0..3) OF RECORD[ pref, alt, other: Rope.ROPE ]; pixels: PixelMap _ NEW[ImagerPixel.PixelMapRep[1]]; line: PixelBuffer; ppi: PixelsPerInch _ OptronicsFromPPI[pixelsPerInch]; rgbToDensity: TRCTable; IF color THEN { names[0] _ ["-red", "-r", NIL]; names[1] _ ["-grn", "-green", "-g"]; names[2] _ ["-blu", "-blue", "-b"]; numFiles _ 3; rgbToDensity _ MakeRGBToDensity[255,255, maxColorD]; } ELSE { names[0] _ ["-gray", "-grey", NIL]; numFiles _ 1; rgbToDensity _ MakeRGBToDensity[255,255, maxGrayD]; }; FOR i: NAT IN [0..numFiles) DO -- get a file for each primary fileName: Rope.ROPE _ FindFile[ aisRoot, names[i] ! TapeError => IF reason.code = $MisMatch THEN RESUME ]; IF fileName # NIL THEN { eof: BOOLEAN _ FALSE; fileHandle: FileHandle; pixels[0] _ ConvertRasterObject.SampleMapFromAIS[ aisfile: fileName, useVM: TRUE ]; pixels.box _ ImagerSample.GetBox[ pixels[0] ]; IF i = 0 THEN { xSize _ SF.SizeF[pixels.box]; ySize _ SF.SizeS[pixels.box]; line _ ImagerPixel.NewPixels[1, xSize]; } ELSE { IF (xSize # SF.SizeF[pixels.box]) OR (ySize # SF.SizeS[pixels.box]) THEN ERROR TapeError[[$Fatal, "Separations not the same size"]]; }; fileHandle _ NEW[FileHandleRep _ [newLine: WriteNewLine, data: NEW[StateRep _ [ interleaved: FALSE, tapeHandle: tapeHandle, buffer: NEW[TEXT[xSize]], linesLeftInFile: ySize, rgbToDensity: rgbToDensity ]]]]; IF headers THEN { UnixTapeOps.WriteRecord[ tapeHandle, Rope.ToRefText[ fileName ] ]; UnixTapeOps.WriteFileMark[tapeHandle]; }; IO.PutRope[report, Rope.Cat["Writing ", fileName, " "]]; FOR y: INTEGER IN [0..ySize) DO CedarProcess.CheckAbort[]; -- respond to Stop button ImagerPixel.GetPixels[self: pixels, initIndex: [s: y, f: 0], pixels: line, count: xSize]; eof _ WriteNewLine[self: fileHandle, line: line]; IF y = ((y / 100) * 100) THEN IO.PutRope[report, "."]; ENDLOOP; IO.PutRope[report, " finished\n"]; -- filemark written by WriteNewLine }; ENDLOOP; }; OneAISToTape: PUBLIC PROC [aisRoot: ROPE, color: BOOLEAN, ppi: NAT, report: IO.STREAM] ~{ tapeHandle: TapeHandle _ GetTapeHandle[]; IF headers THEN WriteTapeHeader[tapeHandle]; AISToTape[tapeHandle, aisRoot, color, ppi, report]; CloseTape[tapeHandle]; }; CmdFileToTape: PUBLIC PROC [fileName: ROPE, report: IO.STREAM] ~ { done: BOOLEAN _ FALSE; tapeHandle: TapeHandle _ GetTapeHandle[]; input: IO.STREAM _ FS.StreamOpen[fileName: fileName, accessOptions: $read]; IF headers THEN WriteTapeHeader[tapeHandle]; WHILE NOT done DO GetWord: PUBLIC PROC[] RETURNS[ROPE] ~ { -- Gets characters bound by white space output: ROPE _ NIL; char: CHAR _ IO.SP; length: NAT _ Rope.Length[line]; WHILE char = IO.SP DO char _ Rope.Fetch[line, index]; IF index >= length THEN RETURN[output]; index _ index + 1; ENDLOOP; WHILE char # IO.SP DO -- until trailing space output _ Rope.Cat[ output, Rope.FromChar[char] ]; IF index >= length THEN EXIT; -- exit if end char _ Rope.Fetch[line, index]; index _ index + 1; ENDLOOP; RETURN[output]; }; line: ROPE _ IO.GetLineRope[ input ! IO.EndOfStream => EXIT ]; index: NAT _ 0; clr: ROPE _ GetWord[]; color, doSkip: BOOLEAN _ FALSE; SELECT TRUE FROM Rope.Equal[ "OptronicsfromGrayAIS", clr, FALSE] => color _ FALSE; Rope.Equal[ "OptronicsfromGreyAIS", clr, FALSE] => color _ FALSE; Rope.Equal[ "OptronicsfromColorAIS", clr, FALSE] => color _ TRUE; Rope.Equal[ "OptronicsSkipFiles", clr, FALSE] => doSkip _ TRUE; ENDCASE => SIGNAL TapeError[[$MisMatch, "Bad color specification"]]; IF doSkip THEN { skip: NAT _ Convert.CardFromRope[GetWord[]]; SkipTapeFiles[tapeHandle, skip]; } ELSE { aisRoot: ROPE _ GetWord[]; pixelsPerInch: NAT _ Convert.CardFromRope[GetWord[]]; AISToTape[tapeHandle, aisRoot, color, pixelsPerInch, report]; }; ENDLOOP; CloseTape[tapeHandle]; }; ReadHeader: PUBLIC PROC [tapeHandle: TapeHandle] RETURNS [ROPE] = { headerAsText: REF TEXT _ NIL; fileMark: BOOLEAN; UnixTapeOps.Rewind[tapeHandle]; [fileMark, headerAsText] _ UnixTapeOps.ReadRecord[tapeHandle]; IF fileMark THEN ERROR; RETURN[Rope.FromRefText[headerAsText]]; }; aisColors: TYPE ~ {red, green, blue, gray}; AISInfo: TYPE ~ RECORD [fileName: ROPE, raster: AIS.Raster, fRef: AIS.FRef, wRef: AIS.WRef]; suffix: ARRAY aisColors OF ROPE = ["red", "grn", "blu",""]; FileToAIS: PUBLIC PROC [tapeHandle: TapeHandle, fileNumber: NAT, colorType: ColorType, aisRootName: ROPE, reduce: NAT _ 4] = { ais: ARRAY aisColors OF AISInfo; nLines, nPixels: NAT _ 0; fileMark: BOOLEAN _ FALSE; buffer: REF TEXT _ NIL; SetUp: PROC [c: aisColors] = { ais[c].raster _ NEW[AIS.RasterPart _ [scanCount: 1+nLines/reduce, scanLength: 1+nPixels/reduce, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]]; ais[c].fileName _ Rope.Cat[r1: aisRootName, r2: "-", r3: suffix[c], r4: ".ais"]; 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: aisColors] = { AIS.CloseWindow[w: ais[c].wRef]; AIS.CloseFile[f: ais[c].fRef]; }; GetScan: PROC[c: aisColors, line: NAT] = { scan: REF TEXT; [fileMark, scan] _ UnixTapeOps.ReadRecord[tapeHandle, NIL]; IF fileMark THEN ERROR; IF line MOD reduce #0 THEN RETURN; FOR pixel: NAT IN [0 .. nPixels) DO v: NAT _ ORD[scan[pixel]]; IF pixel MOD reduce #0 THEN LOOP; AIS.WriteSample[w: ais[c].wRef, value: v, line: line/reduce, pixel: pixel/reduce]; ENDLOOP; }; testFileMark: PROC = { [fileMark, ] _ UnixTapeOps.ReadRecord[tapeHandle, NIL]; IF NOT fileMark THEN ERROR; }; UnixTapeOps.Rewind[tapeHandle]; FOR i: NAT IN [0..fileNumber) DO --file 0 is the header UnixTapeOps.ForwardSpaceFile[tapeHandle]; --skip over the unwanted files ENDLOOP; DO --determine the file size by reading the file [fileMark, buffer] _ UnixTapeOps.ReadRecord[tapeHandle, buffer]; IF fileMark=TRUE THEN EXIT; nLines _ nLines+1; nPixels _ buffer.length; ENDLOOP; UnixTapeOps.BackSpaceFile[tapeHandle]; UnixTapeOps.BackSpaceFile[tapeHandle]; [fileMark, buffer] _ UnixTapeOps.ReadRecord[tapeHandle, buffer]; IF NOT fileMark THEN ERROR; IF colorType=rgb THEN nLines _ nLines/3; SELECT colorType FROM rgb => { FOR i: aisColors IN [red..blue] DO SetUp[c: i]; ENDLOOP; FOR lines: NAT IN [0 .. nLines) DO FOR i: aisColors IN [red..blue] DO GetScan[i, lines]; ENDLOOP; ENDLOOP; testFileMark[]; }; gray, singleColor => { SetUp[c: gray]; FOR lines: NAT IN [0 .. nLines) DO GetScan[gray, lines]; ENDLOOP; testFileMark[]; Done[gray]; }; ENDCASE => ERROR; IF colorType#gray THEN { FOR i: aisColors IN [red..blue] DO Done[c: i]; ENDLOOP; }; }; NewTRCTable: PROC [length: NAT] RETURNS [TRCTable] ~ { trc: TRCTable _ NEW[TRCTableRec[length]]; trc.length _ length; RETURN[trc]; }; MakeRGBToDensity: PUBLIC PROC[maxSampleOut, maxSampleIn: NAT _ 255, maxD: REAL _ 2.0] RETURNS[TRCTable] = { rgbToDensity: TRCTable _ NewTRCTable[maxSampleIn+1]; rgbToDensity[0] _ maxSampleOut; FOR i: NAT IN [1..maxSampleIn] DO rgbToDensity[i] _ maxSampleOut-i; ENDLOOP; RETURN[rgbToDensity]; }; MakeDensityToRGB: PUBLIC PROC[maxSampleOut, maxSampleIn: NAT _ 255, maxD: REAL _ 2.0] RETURNS[TRCTable] = { densityToRGB: TRCTable _ NewTRCTable[maxSampleIn+1]; densityToRGB[0] _ maxSampleOut; FOR i: NAT IN [1..maxSampleIn] DO densityToRGB[i] _ maxSampleOut-i; ENDLOOP; RETURN[densityToRGB]; }; RGBToDensity: PUBLIC PROC [raster: PixelBuffer, rgbToDensity: TRCTable] = { FOR j: NAT IN [0..raster.samplesPerPixel) DO FOR i: NAT IN [0..raster.length) DO raster[j][i] _ rgbToDensity[raster[j][i]]; ENDLOOP; ENDLOOP; }; DensityToRGB: PUBLIC PROC[raster: PixelBuffer, densityToRGB: TRCTable] = { FOR j: NAT IN [0..raster.samplesPerPixel) DO FOR i: NAT IN [0..raster.length) DO raster[j][i] _ densityToRGB[raster[j][i]]; ENDLOOP; ENDLOOP; }; sizeRecordGap: REAL _ 0.8; --the inter-record gap, in inches sizeFileGap: REAL _ 2; --the inter-file gap, in feet SizeOfFile: PUBLIC PROC [fileSpec: FileSpec, density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { d: INTEGER _ SELECT density FROM GCR6250 => 6250, PE1600 => 1600, ENDCASE => ERROR; scanLine: REAL _ sizeRecordGap+fileSpec.nPixels/d; sep: REAL _ fileSpec.nLines*scanLine/12.0; --size in feet IF fileSpec.rgbInterleaved THEN RETURN[3*sep+sizeFileGap] ELSE RETURN[sep+sizeFileGap]; }; SizeOfHeader: PUBLIC PROC [density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = {RETURN[sizeFileGap+sizeHeader/12.0]}; SizeOfList: PUBLIC PROC [files: FileSpecList, density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { sizeInFeet _ SizeOfHeader[density]; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO sizeInFeet _ sizeInFeet+SizeOfFile[f.first, density]; ENDLOOP; }; FileToTapeCmd: Commander.CommandProc ~ { argv: CommandTool.ArgumentVector ~ CommandTool.Parse[cmd]; pxlsPerIn: NAT _ 254; IF CommandTool.NumArgs[cmd] > 2 THEN pxlsPerIn _ Convert.CardFromRope[argv[2]]; WITH cmd.procData.clientData SELECT FROM atom: ATOM => SELECT atom FROM $Gray => OneAISToTape[aisRoot: argv[1], color: FALSE, ppi: pxlsPerIn, report: cmd.out ]; $Color => OneAISToTape[aisRoot: argv[1], color: TRUE, ppi: pxlsPerIn, report: cmd.out ]; $File => CmdFileToTape[fileName: argv[1], report: cmd.out ]; $CleanUp => UnixTapeOps.CloseOpenConnections[]; ENDCASE => ERROR; ENDCASE => ERROR; }; Commander.Register[ "OptronicsfromGrayAIS", FileToTapeCmd, "GrayAISToTape aisStem [pixelsPerInch]", $Gray ]; Commander.Register[ "OptronicsfromGreyAIS", FileToTapeCmd, "GreyAISToTape aisStem [pixelsPerInch]", $Gray ]; Commander.Register[ "OptronicsfromColorAIS", FileToTapeCmd, "ColorAISToTape aisStem [pixelsPerInch]", $Color ]; Commander.Register[ "OptronicsfromCmdFile", FileToTapeCmd, "CmdFileToTape cmdFile", $File ]; Commander.Register[ "OptronicsCleanUp", FileToTapeCmd, "CleanUp", $CleanUp ]; END. ROptronicsTapeImpl.mesa Copyright Σ 1987 by Xerox Corporation. All rights reserved. Maureen Stone, October 8, 1989 5:37:24 pm PDT Crow, April 4, 1989 7:01:48 pm PDT Writes Optronics tape format. The first record is a header block with a string in it Each RGB image is three files, one per color, with one record per scanline A gray image is a single file with one record per scanline Real USING [Round], RealFns USING [Log, Power], Write Tape Writes header and returns a list of FileHandle. Client rasterizes into each FileHandle in turn. newLine.eof is TRUE when tape has recorded nLines worth of rasters. Client can check newLine.eof to verify that the client and the tape are still in sync. In separated format, a color file will generate 3 FileHandles: red, green and blue. In interleaved format, a color file will generat 1 File handle that expects an RGB PixelBuffer interleaved will proceed 3 times faster than separated format. Writes header and returns a descriptor PPIFromOptronics: PROC [pixelsPerInch: PixelsPerInch] RETURNS [ppi: NAT] ~ { SELECT pixelsPerInch FROM ppi63 => ppi _ 63; ppi127 => ppi _ 127; ppi254 => ppi _ 254; ppi508 => ppi _ 508; ppi1015 => ppi _ 1015; ppi2030 => ppi _ 2030; ENDCASE => SIGNAL TapeError[[$MisMatch, "illegitimate pixels per inch"]]; }; Writes single AIS file (3 for color) to tape. Read Tape Converts a tape file to 1 or 3 AIS files. Note that these are RGB density files. If colorType=rgbSeparated the fileNumber should be the red file. To see an individual separation use colorType=gray. this is painful because reading all this data is noticeably slow. We should probably develop more detailed header conventions Utilities Use a linear Density Ramp until I understand this better (July 24, 1989, M. Stone) Treats incoming RGB as linear intensity, outputs RGB in linear density for optronics transparencies. This version makes a linear density map. Treats incoming RGB as linear intensity, outputs RGB in linear density for optronics transparencies. This version makes a linear density map. MakeRGBToDensity: PUBLIC PROC[maxSampleOut, maxSampleIn: NAT _ 255, maxD: REAL _ 2.0] RETURNS[rgbToDensity, densityToRGB: TRCTable] = { Treats incoming RGB as linear intensity, outputs RGB in linear density for optronics transparencies. rgbToDensity: TRCTable _ NEW[TRCTableRec[maxSampleIn]]; densityToRGB: TRCTable _ NEW[TRCTableRec[maxSampleOut]]; density: PROC [pixel: NAT] RETURNS[REAL] = { RETURN[RealFns.Log[base: 10, arg: REAL[maxSampleIn]/pixel]]; }; reflectance: PROC [pixel: NAT] RETURNS[REAL] = { RETURN[1.0/RealFns.Power[base: 10, exponent: pixel/scale]]; }; scale: REAL _ maxSampleOut/maxD; rgbToDensity _ NEW[TRCTableRec]; rgbToDensity[0] _ maxSampleOut; densityToRGB _ NEW[TRCTableRec]; densityToRGB[0] _ maxSampleIn; FOR i: NAT IN [1..maxSampleIn] DO rgbToDensity[i] _ MIN[255, Real.Round[scale*density[i]]]; densityToRGB[i] _ MIN[255, Real.Round[reflectance[i]*maxSampleIn]]; ENDLOOP; }; Sizes A big tape is 2400 feet, a small tape is 600 feet. The file (three color files or one intensity file) Convenience proc. SizeOfFile for each file in list + SizeOfHeader Commands Κͺ˜code•Mark outsideHeaderšœ™Kšœ<™—K˜KšΠblœœ˜ Kšœ³˜ΊKšœ˜šœ œ ˜K˜Iašœ œœœ˜5Kšœœœ˜Kšœ œ˜*Kšœœ˜2Kšœ œ˜*Kšœœ˜0Kšœ œ˜,Kšœ œ˜&K˜Kšœ œ˜Kšœœ˜ Kšœœ˜Kšœœ˜Kšœœœ ˜šœ œœ˜Kšœ œ˜Kšœ˜Kšœœ ˜Kšœ˜Kšœ˜Kšœ˜—K˜Kšœ œ˜Kšœ œ˜Kšœ œœ˜—headšœ ™ šΟn œ œ"œœ!˜{Kšœό™όKšœS™SKšœ^™^Kšœ?™?Kšœ!œ˜%š œœœ(œœ˜}šœœ ˜Kšœ˜Kšœ˜Kšœœœ ˜Kšœ˜Kšœ˜K˜—Kšœ œ7˜GK˜—šœ œœ˜CKšœœ˜šœ#œœ˜4Kšœœ ˜Kšœ˜—K˜—šœ!œœ˜2Kšœ˜Kšœœ˜Kšœœ˜KšœB˜Bšœ˜KšœP˜T—Kšœ˜—Kšœ œ˜,Kšœ)˜)Kšœ˜K˜KšŸ™—šŸ œœœ˜/Lšœ9˜9Lšœ ˜K˜—šŸœœ˜2Kšœ'™'Kšœœœ˜šœ)˜)Lšœ+˜+Lšœ-˜-Lšœ˜—Lšœ œœ˜Kšœ˜Kšœ2˜2Kšœ&˜&Kšœ˜—šŸœœ œœ™Lšœ™Kšœ™Kšœ™Kšœ™Kšœ™Kšœ™Kšœ™Kšœœ8™I—K™—šŸœœœœ#˜Lšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ ˜ Kšœœ8˜I—K˜—šΠbn Οbœ&œœ˜QKšœœ ˜!šŸ œœœ˜šœœœ˜!Kšœœ ˜"Kšœ˜—Kšœ"˜"K˜8K˜—Kšœ&˜&Kš œœœ&œœœ˜gšœœ˜šœœœ˜%Kš œœœœœ˜.K˜——Kšœ˜Kšœ0˜0šœœ˜!K˜,Kšœœ˜ K˜—K˜K˜—šŸ ‘œœ˜6šœœœ ˜Kšœ#˜#Kšœ˜—K˜—šŸ ‘œ˜%Kšœ ˜ Kšœ ˜ Kšœ ˜ KšœΟc˜0K˜—šŸ œœ#œ œœ œœ˜sKšœ-™-š Ÿ œœœœœœ˜PLšœœ2œ˜=Jšœœ˜,šœ˜šœ˜Lšœ3’˜QL˜ L˜—šœ˜Lšœ5’˜KL˜;L˜——Lšœ%˜+L˜L˜—šŸœœœ œœ œ œ˜vJšœœœ˜Jš œœ=œœœ˜hJš œœœ œœ˜,Jš œœ<œœœ˜gJš œœœ œœ˜,Jš œœ>œœœ˜iJš œœœ œœ˜,Jš œœœœœ˜Hšœœœ ˜šœ˜šœ ˜J˜ J˜yJ˜—Jšœœ˜J˜——K˜—Jšœœ˜Lš œœœœœ˜Lšœ'˜'L˜—šœ˜šœ œœ œ˜DLšœœ:˜D—L˜——šœ œ/œ ˜OKšœ œ˜Kšœ˜Kšœœœ ˜Kšœ˜Kšœ˜K˜L˜—šœ œ˜KšœB˜BKšœ&˜&K˜—Lšœ6˜8šœœœ ˜Lšœ’˜6LšœY˜YLšœ1˜1Lšœœœ˜6Lšœ˜—Lšœ"’#˜GLšœ˜—Lšœ˜—Kšœ˜K˜—šŸ œœœ œ œœ œœ˜YKšœ*˜*Kšœ œ˜,Kšœ3˜3Kšœ˜K˜—š Ÿ œœœ œ œœ˜BJšœœœ˜Kšœ*˜*Idefaultšœœœœ6˜KKšœ œ˜,šœœ˜š Ÿœœœœœ’)˜QJš œœœ œœœ˜*Jšœœ˜ šœœœœ˜Jšœ!˜!Jšœœœ ˜'Jšœ˜Jšœ˜—š œœœœ ’˜7Jšœ1˜1Jšœœœ’˜/Jšœ ˜ Jšœ˜Jšœ˜—Jšœ ˜Jšœ˜—Jš œœœœœ˜>Kšœœ˜Jšœœ ˜Jšœœ˜šœœ˜Jšœ)œ œ˜AJšœ)œ ˜AJšœ*œ ˜AJšœ'œœ˜?Kšœœ3˜D—šœœ˜Kšœœ#˜,Kšœ ˜ K˜—šœ˜Kšœ œ˜Kšœœ#˜5Kšœ=˜=K˜—Jšœ˜—Kšœ˜K˜—K˜—šœ ™ šŸ œ œœœ˜CKšœœ œ˜Kšœ œ˜Kšœ˜Kšœ>˜>Kšœ œœ˜Kšœ!˜'K˜K™—Kšœ œ˜+Kš œ œœ œ œœ œ˜\Kšœœ œœ˜;š Ÿ œ œ&œ%œ œ ˜~K™QKšœ@™@Kšœ3™3Kšœœ œ ˜ Kšœœ˜Kšœ œœ˜Kšœœœœ˜šŸœœ˜Kšœœœ˜§K•StartOfExpansionZ[r1: ROPE _ NIL, r2: ROPE _ NIL, r3: ROPE _ NIL, r4: ROPE _ NIL, r5: ROPE _ NIL]šœP˜PK–J[name: ROPE, raster: AIS.Raster, attributeLength: CARDINAL _ 0B (0)]šœœ:˜KK–‘[f: AIS.FRef, firstScan: CARDINAL _ 0B (0), lastScan: CARDINAL _ 177777B (65535), firstPixel: CARDINAL _ 0B (0), lastPixel: CARDINAL _ 177777B (65535)]šœœ˜-Kšœ˜—–E[tapeHandle: TapeOps.TapeHandle, waitForCompletion: BOOL _ FALSE]šŸœœ˜K–[w: AIS.WRef]šœ˜ K–[f: AIS.FRef]šœ˜Kšœ˜—šŸœœœ˜*Kšœœ˜Jšœ6œ˜;Jšœ œœ˜Kšœœ œœ˜"šœœœ˜#Kšœœœ˜Kšœœ œœ˜!KšœO˜RKšœ˜—K˜—š‘ œœ˜Jšœ2œ˜7Kšœœ œœ˜K˜—Kšœ˜š œœœœ’˜7Kšœ*’˜HKšœ˜—šœ’-˜0J™AJ™œœ˜sK™2Kšœœœ œœ œ œœ˜SKšœ œ$˜2Kšœœ"’˜9Kšœ œ œ˜WK˜K™—š Ÿ œ œ*œœœ˜†K™—šŸ œ œ?œœ˜tKšœŸ œŸ ™BKšœ#˜#šœ!œœ˜2Kšœ5˜5Kšœ˜—K˜——™š‘ œ˜(Kšœ:˜:Kšœ œ˜Kšœœ+˜Ošœœ˜(šœœœ˜Kšœ/œ$˜XKšœ0œ$˜XKšœ<˜