DIRECTORY AIS USING [Buffer, CloseFile, CreateFile, FRef, OpenWindow, RasterPart, UnsafeWriteLine, WRef, WriteComment], Basics USING [BITAND, BITOR, BITSHIFT, LongNumber], Commander USING [CommandProc, Handle, Register], FS USING [ComponentPositions, Error, ExpandName, StreamOpen], IO USING [Error, GetBlock, GetChar, GetIndex, GetLength, GetTokenRope, IDProc, PutRope, RIS, SetIndex, STREAM, UnsafeGetBlock], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [Cat, Concat, Fetch, Find, FromChar, FromRefText, Length, Replace, ROPE, Substr]; FrameBufferReaderImpl: CEDAR PROGRAM IMPORTS AIS, Basics, Commander, FS, IO, RefText, Rope = BEGIN ROPE: TYPE ~ Rope.ROPE; Pixel: TYPE = [0..256); BufferRep: TYPE ~ PACKED ARRAY [0..1024) OF Pixel; CreateAISFilesFromSimpleFormat: PROC [cmd: Commander.Handle, upsideDown: BOOLEAN _ FALSE] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] ~ { fileName: ROPE _ IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc].token; inputStream: IO.STREAM _ FS.StreamOpen[fileName ! FS.Error => {result _ $Failure; msg _ error.explanation; GO TO FileProblem}]; comment: ROPE _ GetComment[inputStream]; cmd.out.PutRope[comment]; IF comment.Length > 256 THEN comment _ Rope.Cat[comment.Substr[0, 251], "...\n"]; { fullFileName: ROPE; cp: FS.ComponentPositions; fileNameStem: ROPE; [fullFileName, cp] _ FS.ExpandName[fileName]; fileNameStem _ fullFileName.Substr[cp.base.start, cp.base.length]; cmd.out.PutRope["\nStarting red"]; DoAISSeparation[inputStream, fileNameStem.Concat["-red.AIS"], comment, upsideDown]; cmd.out.PutRope["\nStarting green"]; DoAISSeparation[inputStream, fileNameStem.Concat["-green.AIS"], comment, upsideDown]; cmd.out.PutRope["\nStarting blue"]; DoAISSeparation[inputStream, fileNameStem.Concat["-blue.AIS"], comment, upsideDown]; cmd.out.PutRope[". . . Done\n"] }; EXITS FileProblem => NULL; }; GetComment: PROC [stream: IO.STREAM] RETURNS [line: ROPE] ~ { streamLength: INT _ stream.GetLength[]; rgbFrameBufferSize: INT ~ LONG[512]*LONG[512]*LONG[3]; commentLength: INT _ IF streamLength > rgbFrameBufferSize THEN streamLength-rgbFrameBufferSize ELSE streamLength MOD 512; buffer: REF TEXT = RefText.ObtainScratch[commentLength]; { ENABLE UNWIND => RefText.ReleaseScratch[buffer]; [] _ IO.GetBlock[stream, buffer, 0, commentLength]; line _ Rope.FromRefText[buffer]; }; RefText.ReleaseScratch[buffer]; FOR i: INT _ line.Find["\l"], line.Find["\l"] UNTIL i < 0 DO line _ line.Replace[i, 1, "\n"]; ENDLOOP; RETURN [line]; }; DoAISSeparation: PROC [s: IO.STREAM, aisFileName: ROPE, comment: ROPE, upsideDown: BOOLEAN _ FALSE] ~ TRUSTED { ais: AIS.FRef _ AIS.CreateFile[ name: aisFileName, raster: NEW[AIS.RasterPart _ [ scanCount: 512, scanLength: 512, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]], attributeLength: 1]; window: AIS.WRef _ AIS.OpenWindow[ais]; BufferRep: TYPE ~ PACKED ARRAY [0..512) OF [0..256); buffer: REF BufferRep _ NEW[BufferRep]; bufferDesc: AIS.Buffer _ [length: 512, addr: BASE[buffer^]]; AIS.WriteComment[ais, comment]; FOR i: NAT IN [0..512) DO [] _ s.UnsafeGetBlock[block: [base: LOOPHOLE[bufferDesc.addr], startIndex: 0, count: 512]]; AIS.UnsafeWriteLine[window, bufferDesc, IF upsideDown THEN 512-i-1 ELSE i]; ENDLOOP; AIS.CloseFile[ais]; }; LucasfilmMagicNumber: INT ~ 0164200B; LucasfilmHeader: TYPE = REF LucasfilmHeaderRec; LucasfilmHeaderRec: TYPE = RECORD [ magicNumber: INT, version: INTEGER, label: ROPE, pictureHeight: INTEGER, pictureWidth: INTEGER, tileHeight: INTEGER, tileWidth: INTEGER, pictureFormat: {A, B, BA, G, GA, GB, GBA, R, RA, RB, RBA, RG, RGA, RGB, RGBA}, pictureStorage: {encoded8bit, encoded12bit, dumped8bit, dumped12bit, encoded11bit, dumped11bit}, blockingFactor: INTEGER, alphaMode: {mattedToBlack, unassociated}, xOffset: INTEGER, yOffset: INTEGER, colorMapPointer: INT, colorMapFileName: ROPE]; DoLucasfilmFormatSeparations: PROC [s: IO.STREAM, aisFileNameRoot: ROPE, h: LucasfilmHeader] ~ TRUSTED { aisRed: AIS.FRef _ AIS.CreateFile[ name: aisFileNameRoot.Concat["-red.AIS"], raster: NEW[AIS.RasterPart _ [ scanCount: h.pictureHeight, scanLength: h.pictureWidth, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]], attributeLength: 1]; aisGreen: AIS.FRef _ AIS.CreateFile[ name: aisFileNameRoot.Concat["-green.AIS"], raster: NEW[AIS.RasterPart _ [ scanCount: h.pictureHeight, scanLength: h.pictureWidth, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]], attributeLength: 1]; aisBlue: AIS.FRef _ AIS.CreateFile[ name: aisFileNameRoot.Concat["-blue.AIS"], raster: NEW[AIS.RasterPart _ [ scanCount: h.pictureHeight, scanLength: h.pictureWidth, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]], attributeLength: 1]; windowRed: AIS.WRef _ AIS.OpenWindow[aisRed]; windowGreen: AIS.WRef _ AIS.OpenWindow[aisGreen]; windowBlue: AIS.WRef _ AIS.OpenWindow[aisBlue]; bufferRed: REF BufferRep _ NEW[BufferRep]; bufferGreen: REF BufferRep _ NEW[BufferRep]; bufferBlue: REF BufferRep _ NEW[BufferRep]; bufferDescRed: AIS.Buffer _ [length: h.pictureWidth, addr: BASE[bufferRed^]]; bufferDescGreen: AIS.Buffer _ [length: h.pictureWidth, addr: BASE[bufferGreen^]]; bufferDescBlue: AIS.Buffer _ [length: h.pictureWidth, addr: BASE[bufferBlue^]]; AIS.WriteComment[aisRed, h.label]; AIS.WriteComment[aisGreen, h.label]; AIS.WriteComment[aisBlue, h.label]; FOR scanLine: NAT IN [0..h.pictureHeight) DO ExtractLucasfilmScanLine[s, h, bufferRed, bufferGreen, bufferBlue]; AIS.UnsafeWriteLine[windowRed, bufferDescRed, scanLine]; AIS.UnsafeWriteLine[windowGreen, bufferDescGreen, scanLine]; AIS.UnsafeWriteLine[windowBlue, bufferDescBlue, scanLine]; ENDLOOP; AIS.CloseFile[aisRed]; AIS.CloseFile[aisGreen]; AIS.CloseFile[aisBlue]; }; ExtractLucasfilmScanLine: PROC [s: IO.STREAM, h: LucasfilmHeader, bufferRed, bufferGreen, bufferBlue: REF BufferRep] ~ { SELECT h.pictureStorage FROM dumped8bit => { SELECT h.pictureFormat FROM RGB => { FOR pixel: NAT IN [0..h.pictureWidth) DO bufferRed[pixel] _ LOOPHOLE[s.GetChar[]]; bufferGreen[pixel] _ LOOPHOLE[s.GetChar[]]; bufferBlue[pixel] _ LOOPHOLE[s.GetChar[]]; ENDLOOP; }; ENDCASE => ERROR; }; encoded8bit => { SELECT h.pictureFormat FROM RGB => { i: NAT _ 0; WHILE i < h.pictureWidth DO inCaseOfErrorRememberIndex: INT _ s.GetIndex[]; byte1: CARDINAL _ LOOPHOLE[s.GetChar[]]; byte2: CARDINAL _ LOOPHOLE[s.GetChar[]]; flag: INT _ Basics.BITSHIFT[byte1, -4]; -- flag <0:4> pixelCount: INT _ Basics.BITOR[Basics.BITSHIFT[Basics.BITAND[byte1, 17B], 8], byte2]; -- count <8:11> count <0:7> SELECT flag FROM 0 => { -- end of disk block index: INT ~ s.GetIndex[]; blockSize: INT ~ h.blockingFactor; blockNumber: INT ~ index/blockSize; blockStart: INT ~ blockNumber*blockSize; IF index#blockStart THEN s.SetIndex[blockStart+blockSize]; LOOP; }; 1 => ERROR; -- full channel dump 2 => { THROUGH [0..pixelCount] DO length: CARDINAL _ LOOPHOLE[s.GetChar[]]; redPixel: Pixel _ LOOPHOLE[s.GetChar[]]; greenPixel: Pixel _ LOOPHOLE[s.GetChar[]]; bluePixel: Pixel _ LOOPHOLE[s.GetChar[]]; THROUGH [0..length] DO bufferRed[i] _ redPixel; bufferGreen[i] _ greenPixel; bufferBlue[i] _ bluePixel; i _ i + 1; ENDLOOP; ENDLOOP; }; ENDCASE => ERROR; ENDLOOP; IF i # h.pictureWidth THEN ERROR; }; ENDCASE => ERROR; }; ENDCASE => ERROR; }; GetLucasfilmHeader: PROC [s: IO.STREAM] RETURNS [h: LucasfilmHeader _ NEW[LucasfilmHeaderRec]] ~ { i: NAT; FourBytes: PROC [s: IO.STREAM] RETURNS [INT] ~ TRUSTED { n: Basics.LongNumber; n.ll _ LOOPHOLE[s.GetChar[]]; n.lh _ LOOPHOLE[s.GetChar[]]; n.hl _ LOOPHOLE[s.GetChar[]]; n.hh _ LOOPHOLE[s.GetChar[]]; RETURN [n.li] }; TwoBytes: PROC [s: IO.STREAM] RETURNS [INTEGER] ~ TRUSTED { n: Basics.LongNumber; n.ll _ LOOPHOLE[s.GetChar[]]; n.lh _ LOOPHOLE[s.GetChar[]]; RETURN [n.lowbits] }; h.magicNumber _ FourBytes[s]; IF h.magicNumber # LucasfilmMagicNumber THEN ERROR; h.version _ TwoBytes[s]; h.label _ NIL; THROUGH [0..246) DO h.label _ Rope.Concat[h.label, Rope.FromChar[s.GetChar[]]]; ENDLOOP; FOR i DECREASING IN [0..246) WHILE h.label.Fetch[i] = '\000 DO ENDLOOP; h.label _ Rope.Substr[h.label, 0, i]; s.SetIndex[416]; h.pictureHeight _ TwoBytes[s]; h.pictureWidth _ TwoBytes[s]; h.tileHeight _ TwoBytes[s]; h.tileWidth _ TwoBytes[s]; IF h.pictureHeight # h.tileHeight OR h.pictureWidth # h.tileWidth THEN ERROR; h.pictureFormat _ SELECT TwoBytes[s] FROM 1B => A, 2B => B, 3B => BA, 4B => G, 5B => GA, 6B => GB, 7B => GBA, 10B => R, 11B => RA, 12B => RB, 13B => RBA, 14B => RG, 15B => RGA, 16B => RGB, 17B => RGBA, ENDCASE => RGBA; IF h.pictureFormat # RGB THEN ERROR; h.pictureStorage _ SELECT TwoBytes[s] FROM 0 => encoded8bit, 1 => encoded12bit, 2 => dumped8bit, 3 => dumped12bit, 4 => encoded11bit, 5 => dumped11bit, ENDCASE => encoded8bit; IF h.pictureStorage # dumped8bit AND h.pictureStorage # encoded8bit THEN ERROR; h.blockingFactor _ TwoBytes[s]; h.alphaMode _ IF TwoBytes[s] = 0 THEN mattedToBlack ELSE unassociated; h.xOffset _ TwoBytes[s]; h.yOffset _ TwoBytes[s]; IF h.xOffset # 0 OR h.yOffset # 0 THEN ERROR; s.SetIndex[h.blockingFactor]; }; GSLHeader: TYPE ~ REF GSLHeaderRec; GSLHeaderRec: TYPE ~ RECORD[ label: ROPE, rows: INTEGER, columns: INTEGER, pictureHeight: INTEGER, pictureWidth: INTEGER]; DoGSLFormatConversion: PROC [s: IO.STREAM, aisFileNameRoot: ROPE, h: GSLHeader] ~ TRUSTED { ais: AIS.FRef _ AIS.CreateFile[ name: aisFileNameRoot.Concat[".ais"], raster: NEW[AIS.RasterPart _ [ scanCount: h.pictureHeight, scanLength: h.pictureWidth, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]], attributeLength: 1]; window: AIS.WRef _ AIS.OpenWindow[ais]; buffer: REF BufferRep _ NEW[BufferRep]; bufferDesc: AIS.Buffer _ [length: h.pictureWidth, addr: BASE[buffer^]]; AIS.WriteComment[ais, h.label]; FOR scanLine: NAT IN [0..h.pictureHeight) DO ExtractGSLScanLine[s, h, buffer]; AIS.UnsafeWriteLine[window, bufferDesc, scanLine]; ENDLOOP; AIS.CloseFile[ais]; }; GetGSLHeader: PROC [s: IO.STREAM] RETURNS [h: GSLHeader _ NEW[GSLHeaderRec]] ~ { FourBytes: PROC [s: IO.STREAM] RETURNS [INT] ~ TRUSTED { n: Basics.LongNumber; n.ll _ LOOPHOLE[s.GetChar[]]; n.lh _ LOOPHOLE[s.GetChar[]]; n.hl _ LOOPHOLE[s.GetChar[]]; n.hh _ LOOPHOLE[s.GetChar[]]; RETURN [n.li] }; TwoBytes: PROC [s: IO.STREAM] RETURNS [INTEGER] ~ TRUSTED { n: Basics.LongNumber; n.ll _ LOOPHOLE[s.GetChar[]]; n.lh _ LOOPHOLE[s.GetChar[]]; RETURN [n.lowbits] }; [] _ TwoBytes[s]; -- record separator h.label _ NIL; h.rows _ FourBytes[s]; h.columns _ FourBytes[s]; h.pictureHeight _ FourBytes[s]; h.pictureWidth _ FourBytes[s]; IF h.pictureWidth MOD 2 = 1 THEN h.pictureWidth _ h.pictureWidth - 1; }; ExtractGSLScanLine: PROC [s: IO.STREAM, h: GSLHeader, buffer: REF BufferRep] ~ { TwoBytes: PROC [s: IO.STREAM] RETURNS [INTEGER] ~ TRUSTED { n: Basics.LongNumber; n.ll _ LOOPHOLE[s.GetChar[]]; n.lh _ LOOPHOLE[s.GetChar[]]; RETURN [n.lowbits] }; ByteValue: PROC [s: IO.STREAM] RETURNS [INTEGER] ~ TRUSTED { n: Basics.LongNumber; n.ll _ LOOPHOLE[s.GetChar[]]; n.lh _ 0; n.lowbits _ IF n.lowbits >= 128 THEN n.lowbits - 128 ELSE n.lowbits + 128; RETURN [n.lowbits] }; [] _ TwoBytes[s]; FOR i:NAT IN [0..h.columns) DO buffer[i] _ ByteValue[s]; ENDLOOP; FOR i: NAT IN [h.columns .. h.pictureWidth) DO buffer[i] _ buffer[i MOD h.columns]; ENDLOOP; }; ReadFrameBufferCmdProc: Commander.CommandProc ~ { [result, msg] _ CreateAISFilesFromSimpleFormat[cmd: cmd, upsideDown: FALSE]; }; ReadFrameBufferBottomUpCmdProc: Commander.CommandProc ~ { [result, msg] _ CreateAISFilesFromSimpleFormat[cmd: cmd, upsideDown: TRUE]; }; ReadLucasfilmCmdProc: Commander.CommandProc ~ { fileName: ROPE _ IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc ! IO.Error => GOTO NoFileName].token; inputStream: IO.STREAM _ FS.StreamOpen[fileName]; header: LucasfilmHeader _ GetLucasfilmHeader[inputStream]; cmd.out.PutRope[header.label]; IF header.label.Length > 256 THEN header.label _ Rope.Cat[header.label.Substr[0, 251], "...\n"]; { fullFileName: ROPE; cp: FS.ComponentPositions; fileNameStem: ROPE; [fullFileName, cp] _ FS.ExpandName[fileName]; fileNameStem _ fullFileName.Substr[cp.base.start, cp.base.length]; DoLucasfilmFormatSeparations[inputStream, fileNameStem, header]; }; EXITS NoFileName => RETURN [$failure, "No filename given for frame buffer image"]; }; ReadGSLImageCmdProc: Commander.CommandProc ~ { fileName: ROPE _ IO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc ! IO.Error => GOTO NoFileName].token; inputStream: IO.STREAM _ FS.StreamOpen[fileName]; header: GSLHeader _ GetGSLHeader[inputStream]; cmd.out.PutRope[header.label]; IF header.label.Length > 256 THEN header.label _ Rope.Cat[header.label.Substr[0, 251], "...\n"]; { fullFileName: ROPE; cp: FS.ComponentPositions; fileNameStem: ROPE; [fullFileName, cp] _ FS.ExpandName[fileName]; fileNameStem _ fullFileName.Substr[cp.base.start, cp.base.length]; DoGSLFormatConversion[inputStream, fileNameStem, header]; }; EXITS NoFileName => RETURN [$failure, "No filename given for frame buffer image"]; }; Commander.Register[key: "ReadFrameBuffer", proc: ReadFrameBufferCmdProc, doc: "ReadFrameBuffer framebufferimagefilename.rgb reads a 512x512 image into 3 color AIS files"]; Commander.Register[key: "ReadFrameBufferBottomUp", proc: ReadFrameBufferBottomUpCmdProc, doc: "ReadFrameBufferBottomUp framebufferimagefilename.rgb reads a 512x512 image into 3 color AIS files"]; Commander.Register[key: "ReadLucasfilm", proc: ReadLucasfilmCmdProc, doc: "ReadLucasfilm framebufferimagefilename.rgb reads a frame buffer image into 3 (or 4 if alpha) color AIS files. See [Indigo]Entries>84CSLN-0016.Tioga for details on the frame buffer image format."]; Commander.Register[key: "ReadGSLImage", proc: ReadGSLImageCmdProc, doc: "ReadGSLImage imagefilename.wpo reads an image into an AIS file. See Mike Okeefe or Fernando Ponce for details on the file format."]; END. ΚFrameBufferReaderImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Created by Rick Beach, July 17, 1985 5:49:26 pm PDT Doug Wyatt, September 24, 1985 5:29:41 pm PDT Simple Image Format Conversion The simple image format is one scan line per record for a 512x512 image: Record 1. A variable length ASCII text record that describes the image. Might contain title (Mount MandrillBrot), artist and creative note (Mike and Dave...), order of color separations (red, green, blue), bits per pixel (8), length of a scan line (s=512), number of pixels per scan line (p=512). Records 2..513. The 512 scan lines in the first color separation, e.g. red scan lines. Records 514..1025. The 512 scan lines in the second color separation, e.g. green scan lines. Records 1026..1537. The 512 scan lines in the third color separation, e.g. blue scan lines. If the comment is longer than 256 characters (an AIS file format limitation) then truncate it with an ellipsis. Similar to IO.GetLineRope, but uses knowledge about the framebuffer image file to determine how long the comment is in the frame buffer image. Lucasfilm Image Format Conversion The Lucasfilm image format is documented in CSL notebook entry [Indigo]Entries>84CSLN-0016.tioga Not all picture formats are implemented. GSL Electron Microscope Image Format Conversion This format was defined by Mike O'Keefe and Fernando Ponce in GSL for images created for the simulation of the electron microscope. Commands If the comment is longer than 256 characters (an AIS file format limitation) then truncate it with an ellipsis. If the comment is longer than 256 characters (an AIS file format limitation) then truncate it with an ellipsis. Κe˜code™Kšœ Οmœ1™