FrameBufferReader.Mesa
Copyright c 1985 by Xerox Corporation. All rights reserved.
Created by Rick Beach, March 2, 1985 1:30:58 pm PST
DIRECTORY
AIS, Basics, Commander, FS, IO, RefText, Rope;
FrameBufferReader: 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;
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];
CreateAISFilesFromFrameBufferImage: Commander.CommandProc ~ {
CreateAISFiles[cmd: cmd, upsideDown: FALSE];
};
CreateAISFilesFromUofTFrameBufferImage: Commander.CommandProc ~ {
CreateAISFiles[cmd: cmd, upsideDown: TRUE];
};
CreateAISFiles: PROC [cmd: Commander.Handle, upsideDown: BOOLEANFALSE] ~ {
fileName: ROPEIO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc].token;
inputStream: IO.STREAMFS.StreamOpen[fileName];
comment: ROPE ← GetComment[inputStream];
cmd.out.PutRope[comment];
If the comment is longer than 256 characters (an AIS file format limitation) then truncate it with an ellipsis.
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"]
};
};
GetComment: PROC [stream: IO.STREAM] RETURNS [line: ROPE] ~ {
Similar to IO.GetLineRope, but uses knowledge about the framebuffer image file to determine how long the comment is in the frame buffer image.
streamLength: INT ← stream.GetLength[];
rgbFrameBufferSize: INT ~ LONG[512]*LONG[512]*LONG[3];
commentLength: INTIF 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: BOOLEANFALSE] ~ 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];
};
CreateAISFilesFromLucasfilmImage: Commander.CommandProc ~ {
fileName: ROPEIO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc ! IO.Error => GOTO NoFileName].token;
inputStream: IO.STREAMFS.StreamOpen[fileName];
header: LucasfilmHeader ← GetLucasfilmHeader[inputStream];
cmd.out.PutRope[header.label];
If the comment is longer than 256 characters (an AIS file format limitation) then truncate it with an ellipsis.
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"];
};
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: CARDINALLOOPHOLE[s.GetChar[]];
byte2: CARDINALLOOPHOLE[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 => LOOP; -- end of disk block
1 => ERROR; -- full channel dump
2 => {
THROUGH [0..pixelCount] DO
length: CARDINALLOOPHOLE[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];
CreateAISFilesFromGSLImage: Commander.CommandProc ~ {
fileName: ROPEIO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc ! IO.Error => GOTO NoFileName].token;
inputStream: IO.STREAMFS.StreamOpen[fileName];
header: GSLHeader ← GetGSLHeader[inputStream];
cmd.out.PutRope[header.label];
If the comment is longer than 256 characters (an AIS file format limitation) then truncate it with an ellipsis.
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"];
};
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;
};
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];
};
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;
};
Commander.Register[key: "ReadFrameBuffer", proc: CreateAISFilesFromFrameBufferImage, doc: "ReadFrameBuffer framebufferimagefilename.rgb reads a 512x512 image into 3 color AIS files"];
Commander.Register[key: "ReadUofTFrameBuffer", proc: CreateAISFilesFromUofTFrameBufferImage, doc: "ReadUofTFrameBuffer framebufferimagefilename.rgb reads a 512x512 image into 3 color AIS files"];
Commander.Register[key: "ReadLucasfilm", proc: CreateAISFilesFromLucasfilmImage, doc: "ReadLucasfilm framebufferimagefilename.rgb reads a frame buffer image into 3 (or 4 if alpha) color AIS files. See [Indigo]<CSL-Notebook>Entries>84CSLN-0016.Tioga for details on the frame buffer image format."];
Commander.Register[key: "ReadGSLImage", proc: CreateAISFilesFromGSLImage, doc: "ReadGSLImage imagefilename.wpo reads an image into an AIS file. See Mike Okeefe or Fernando Ponce for details on the file format."];
END.