FrameBufferReaderImpl.mesa
Copyright © 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
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;
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.
CreateAISFilesFromSimpleFormat: PROC [cmd: Commander.Handle, upsideDown: BOOLEANFALSE]
RETURNS [result: REFNIL, msg: ROPENIL] ~ {
fileName: ROPEIO.GetTokenRope[IO.RIS[cmd.commandLine], IO.IDProc].token;
inputStream: IO.STREAMFS.StreamOpen[fileName !
FS.Error => {result ← $Failure; msg ← error.explanation; GO TO FileProblem}];
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"]
};
EXITS
FileProblem => NULL;
};
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];
};
Lucasfilm Image Format Conversion
The Lucasfilm image format is documented in CSL notebook entry [Indigo]<CSL-Notebook>Entries>84CSLN-0016.tioga
Not all picture formats are implemented.
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: 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 => { -- 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: 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];
};
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.
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;
};
Commands
ReadFrameBufferCmdProc: Commander.CommandProc ~ {
[result, msg] ← CreateAISFilesFromSimpleFormat[cmd: cmd, upsideDown: FALSE];
};
ReadFrameBufferBottomUpCmdProc: Commander.CommandProc ~ {
[result, msg] ← CreateAISFilesFromSimpleFormat[cmd: cmd, upsideDown: TRUE];
};
ReadLucasfilmCmdProc: 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"];
};
ReadGSLImageCmdProc: 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"];
};
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]<CSL-Notebook>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.