Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, March 31, 1989 11:23:33 am PST
Bloomenthal, September 26, 1988 12:04:43 pm PDT
DIRECTORY
Atom USING [ GetPropFromList, PutPropOnList, RemPropFromList ],
CedarProcess USING [ CheckAbort, DoWithPriority ],
Rope USING [ Cat, Equal, ROPE, Substr, Length ],
BasicTime USING [ Now ],
Convert USING [ RopeFromInt, RopeFromTime ],
Real USING [ Round ],
FS USING [ ExpandName, ComponentPositions, FileInfo, Error,
StreamOpen ],
IO USING [ Close, GetChar, GetRopeLiteral, GetInt, EndOf, EndOfStream,
PutChar, PutRope, STREAM ],
Terminal USING [ Virtual, Current, WaitForBWVerticalRetrace ],
ImagerBackdoor USING [ AccessBufferRectangle ],
ImagerPixel USING [ PixelMap, MakePixelMap ],
ImagerSample USING [ BasicTransfer, Clip, GetBox, GetSamples, NewSampleMap,
NewSamples, PutSamples, SampleBuffer, SampleMap,
Transfer ],
SF USING [ SizeF, SizeS ],
ConvertRasterObject USING [ AISFromSampleMap, SampleMapFromAIS ],
ThreeDBasics USING [ Box, Context, Error, ImagerProc, ImagerProcRec, Pair, IntegerPair
],
SceneUtilities USING [ PrependWorkingDirectory ],
SurfaceRender USING [ ValidateContext ],
RenderWithPixels USING [ AllocatePixelMemory ],
ColorDisplayRender USING [ StuffBuf ],
AISAnimation USING [ FrameSequence ];
Internal Declarations
Context: TYPE ~ ThreeDBasics.Context;
Box: TYPE ~ ThreeDBasics.Box;
Pair: TYPE ~ ThreeDBasics.Pair;
IntegerPair: TYPE ~ ThreeDBasics.IntegerPair;
ImagerProc: TYPE ~ ThreeDBasics.ImagerProc;
ImagerProcRec: TYPE ~ ThreeDBasics.ImagerProcRec;
RopeSequence: TYPE ~ RECORD[ SEQUENCE length: NAT OF Rope.ROPE ];
FrameSequence: TYPE ~ AISAnimation.FrameSequence;
PixelMap: TYPE ~ ImagerPixel.PixelMap;
SampleMap: TYPE ~ ImagerSample.SampleMap;
ROPE: TYPE ~ Rope.ROPE;
LORA: TYPE ~ LIST OF REF ANY;
bitsPer75thSecond: INT ~ 314573; -- roughly what bitBlt can move in one LF field time
labelsEnabled: BOOLEAN ← FALSE;
labelPosn: Pair ← [0.02, 0.02];
Procedures for Animation
PlayBackNumberedAISFiles:
PUBLIC
PROC[context:
REF Context,
fileRoot : Rope.
ROPE,
startNum, numFiles, framesPerSec, secondsPlayingTime :
NAT] ~ {
frames: REF FrameSequence ← CacheNumberedAISFiles[context, fileRoot, numFiles, startNum];
PlayBackAISCache[context, frames, framesPerSec, secondsPlayingTime];
};
StoreFiles:
PUBLIC
PROC[context:
REF Context, fileRoot: Rope.
ROPE, number:
NAT ←
LAST[
NAT]] ~ {
Update log file, if animation, then store image
cp: FS.ComponentPositions; fullFName, ext: Rope.ROPE;
IF number #
LAST[
NAT]
THEN {
log: IO.STREAM ;
log ←
FS.StreamOpen[
fileName: GetLogFileName[ SceneUtilities.PrependWorkingDirectory[context, fileRoot] ],
accessOptions: $append,
wDir: NARROW[ Atom.GetPropFromList[context.props, $WDir] ]
];
log.PutRope[ Rope.Cat[
" Frame no. ", Convert.RopeFromInt[number], " written at ",
Convert.RopeFromTime[from: BasicTime.Now[], start: months, end: seconds], "\n"
] ];
IO.Close[log];
};
[fullFName, cp, ] ← FS.ExpandName[fileRoot]; -- is this an RGB file name?
ext ← Rope.Substr[ fullFName, cp.ext.start, cp.ext.length];
IF number #
LAST[
NAT]
THEN fullFName ← PasteInSequenceNo[
SceneUtilities.PrependWorkingDirectory[context, fileRoot],
number
]
ELSE fullFName ← SceneUtilities.PrependWorkingDirectory[context, fileRoot];
IF Rope.Equal[ext, "rgb",
FALSE]
THEN PutRGB[ context, fullFName ]
ELSE PutAIS[ context, fullFName ];
};
CacheAISFiles:
PUBLIC
PROC[context:
REF Context, fileOfNames: Rope.
ROPE,
frames:
REF FrameSequence ←
NIL]
RETURNS[
REF FrameSequence] ~ {
pictureNames: REF RopeSequence;
GetName:
PROC[number:
NAT]
RETURNS[Rope.
ROPE] ~ {
RETURN[ pictureNames[number] ];
};
Extend:
PROC[names:
REF RopeSequence] ~ {
newNames: REF RopeSequence ← NEW[ RopeSequence[names.length*2] ];
FOR i: NAT IN [0..names.length) DO newNames[i] ← names[i]; ENDLOOP;
names ← newNames;
};
input: IO.STREAM ← FS.StreamOpen[ fileName: fileOfNames ];
numFiles: NAT ← IO.GetInt[input]; -- first thing in file is number of pictures
pictureNames ← NEW[ RopeSequence[numFiles] ];
numFiles ← 0;
WHILE
NOT
IO.EndOf[input]
DO
-- read robustly in case number is wrong
IF numFiles >= pictureNames.length THEN Extend[pictureNames];
pictureNames[numFiles] ← IO.GetRopeLiteral[input ! IO.EndOfStream => LOOP];
IF pictureNames[numFiles] # NIL THEN numFiles ← numFiles + 1;
ENDLOOP;
RETURN[ CacheFiles[context, numFiles, 0, GetName, frames] ];
};
CacheNumberedAISFiles:
PUBLIC
PROC[context:
REF Context,
fileRoot: Rope.
ROPE, numFiles:
NAT, start:
NAT ← 0]
RETURNS[
REF FrameSequence] ~ {
GetName:
PROC[number:
NAT]
RETURNS[Rope.
ROPE] ~ {
RETURN[ PasteInSequenceNo[
SceneUtilities.PrependWorkingDirectory[context, fileRoot],
number
] ];
};
RETURN[ CacheFiles[context, numFiles, start, GetName, NIL] ];
};
CacheFiles: PROC[context: REF Context, numFiles, start: NAT ← 0,
getname:
PROC[number:
NAT]
RETURNS[Rope.
ROPE],
frames:
REF FrameSequence ←
NIL ]
RETURNS[
REF FrameSequence] ~ {
Action:
PROC ~ {
frameFailed, firstFrameFound: BOOLEAN ← FALSE;
firstFrame, i: NAT ← start;
tmpSamples: ARRAY[0..5) OF SampleMap ← ALL[NIL];
WHILE
TRUE
DO
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
RenderWithPixels.AllocatePixelMemory[ context ]; -- get new set of pixels each frame
[] ← GetAIS[
context, getname[i]
! ThreeDBasics.Error =>
IF reason.code = $NotFound
THEN { frameFailed ← TRUE; CONTINUE; }
];
IF
NOT frameFailed
THEN firstFrameFound ← TRUE -- found frame, legit sequence started
ELSE
IF
NOT firstFrameFound
-- frame failed, has a sequence been started?
THEN
IF i < start + 16
-- try for 16 consecutive frames, just in case
THEN { firstFrame ← i ← i + 1; frameFailed ← FALSE; LOOP; }
ELSE EXIT -- frame not found after 16 tries, assume bad name
ELSE EXIT; -- must be end of sequence, earlier frames found but not this one
IF frames = NIL THEN frames ← NEW[ FrameSequence[numFiles] ];
IF frames.maxLength <= i - firstFrame
THEN {
-- extend FrameSequence if necessary
newFrames: REF FrameSequence ← NEW[ FrameSequence[frames.maxLength+16] ];
FOR j: NAT IN [0..frames.maxLength) DO newFrames[j] ← frames[j]; ENDLOOP;
frames ← newFrames;
};
Get pixels left in proplist by GetAIS
frames[i - firstFrame] ← NARROW[ Atom.GetPropFromList[context.props, $TempPixels] ];
IF labelsEnabled
AND context.viewer #
NIL
-- pull pixels out of viewer to get label
THEN
FOR k:
NAT
IN [0..frames[i - firstFrame].samplesPerPixel)
DO
context.class.drawInViewer[
context,
NEW[ImagerProcRec ← [
GetPixelsFromViewer, LIST[ NEW[NAT ← k], frames[i - firstFrame][k] ]
]]
];
ENDLOOP;
context.props ← Atom.RemPropFromList[context.props, $TempPixels]; -- save VM
frames.length ← i - firstFrame + 1;
i ← i + 1;
IF numFiles > 0 AND i - start >= numFiles THEN EXIT; -- found requested no. of files
ENDLOOP;
IF
NOT firstFrameFound
THEN {
frames ← NIL; -- no frames found, prevent further mistakes
SIGNAL ThreeDBasics.Error[[$NotFound, "File not found by that name"]];
};
};
CedarProcess.DoWithPriority[background, Action];
IF context.stopMe^ THEN RETURN[NIL] ELSE RETURN[frames];
};
GetPixelsFromViewer: ImagerProc ~ {
PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY ← NIL ];
DoGetPixels:
PROC[pixelMap: PixelMap] ~ {
list: LORA ← NARROW[data];
i: NAT ← NARROW[list.first, REF NAT]^;
TransferSamples[
dstMap: NARROW[list.rest.first],
srcMap: pixelMap[i],
xOffset: 0, yOffset: 0
];
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoGetPixels, context.viewPort^];
};
PlayBackAISCache:
PUBLIC
PROC[context:
REF Context,
frames:
REF FrameSequence,
framesPerSec, secondsPlayingTime:
NAT,
bothWays:
BOOLEAN ←
FALSE, startNum:
NAT ← 0] ~ {
vt: Terminal.Virtual ← context.terminal;
waitCount, transferTime: NAT;
repetitions: INT ← MAX[1,
INT[secondsPlayingTime] * framesPerSec / (frames.length-startNum)];
IF frames =
NIL
THEN {
SIGNAL ThreeDBasics.Error[[$MisMatch, "NoCachedFrames"]]; RETURN;
};
context.stopMe^ ← FALSE; -- get stop flag unstuck, if necessary
context.imageReady ← FALSE; -- discourage use of bits by other processes
context.class.validateDisplay[context]; -- update viewport, etc.
[waitCount, transferTime] ← GetWaitCount[context, framesPerSec];
IF waitCount = 0
THEN repetitions ← (secondsPlayingTime * 75 / transferTime) / (frames.length - startNum);
IF bothWays THEN repetitions ← repetitions/2;
FOR i:
INT
IN [0..repetitions)
DO
-- play back
FOR j:
NAT
IN [startNum..frames.length)
DO
FOR k: NAT IN [0..waitCount) DO vt.WaitForBWVerticalRetrace[]; ENDLOOP;
frames.currentFrame ← j;
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
ShowAISCacheFrame[context, frames];
ENDLOOP;
IF bothWays
THEN
FOR j:
NAT
DECREASING IN [startNum..frames.length)
DO
FOR k: NAT IN [0..waitCount) DO vt.WaitForBWVerticalRetrace[]; ENDLOOP;
frames.currentFrame ← j;
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
ShowAISCacheFrame[context, frames];
ENDLOOP;
ENDLOOP;
context.imageReady ← TRUE; -- encourage use of bits by other processes
};
ShowNextAISCacheFrame:
PUBLIC
PROC[context:
REF Context,
frames:
REF FrameSequence, framesPerSec:
INTEGER] ~ {
waitCount: NAT;
vt: Terminal.Virtual ← Terminal.Current[];
IF framesPerSec < 0
THEN frames.currentFrame ← (frames.currentFrame + frames.length-1) MOD frames.length
ELSE frames.currentFrame ← (frames.currentFrame + 1) MOD frames.length;
[waitCount: waitCount] ← GetWaitCount[context, ABS[framesPerSec]];
FOR k: NAT IN [0..waitCount) DO vt.WaitForBWVerticalRetrace[]; ENDLOOP;
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
ShowAISCacheFrame[context, frames];
};
Support Procedures
Ceiling:
PROC[number:
REAL]
RETURNS[result:
INTEGER] ~ {
result ← Real.Round[number];
IF result < number THEN result ← result + 1;
};
GetLogFileName:
PUBLIC
PROC[fileRoot: Rope.
ROPE]
RETURNS[Rope.
ROPE] ~ {
cp: FS.ComponentPositions; fullFName, fName: 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
ELSE fName ← Rope.Substr[ fullFName, 0, cp.ext.start-1]; -- drop "." before ext
RETURN[ Rope.Cat[fName, ".", "log"] ];
};
PasteInSequenceNo:
PUBLIC
PROC[fileRoot: Rope.
ROPE, number:
NAT]
RETURNS[Rope.
ROPE] ~ {
num: Rope.ROPE ← Convert.RopeFromInt[number, 10, FALSE];
SELECT Rope.Length[num]
FROM
1 => num ← Rope.Cat["00", num];
2 => num ← Rope.Cat["0", num];
3 => {};
ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Sequence # out of range"]];
RETURN[ PasteInLabel[fileRoot, num ] ];
};
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] ];
};
GetWaitCount:
PROC[context:
REF Context, framesPerSec:
NAT]
RETURNS[waitCount:
NAT, transferTime:
NAT] ~ {
width: INT ← Ceiling[context.viewPort.w];
height: INT ← Ceiling[context.viewPort.h];
addOn: NAT ← IF context.antiAliasing THEN 1 ELSE 0;
bitsPerPixel: NAT ← IF context.class.displayType = $FullColor THEN 24 ELSE 8;
transferTime ← width * height * bitsPerPixel / bitsPer75thSecond;
IF framesPerSec = 0
THEN waitCount ← 150
ELSE waitCount ← 75 / framesPerSec; -- LF display VerticalRetrace comes 75/sec.
IF transferTime > waitCount
-- take bitBlt overhead into account
THEN waitCount ← 0
ELSE waitCount ← waitCount - transferTime;
waitCount ← MAX[waitCount, 1];
};
ShowAISCacheFrame:
PUBLIC
PROC[ context:
REF Context,
frames:
REF FrameSequence ] ~ {
IF frames.currentFrame < 0
OR frames.currentFrame >= frames.length
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Frame number out of range"]];
IF frames[frames.currentFrame] #
NIL
THEN
IF context.viewer #
NIL
THEN {
context.class.updateViewer[context];
context.pixels ← frames[frames.currentFrame];
context.class.drawInViewer[
context, NEW[ImagerProcRec ← [ColorDisplayRender.StuffBuf, context]]
];
}
ELSE
{
samplesPerColor: NAT ← IF context.class.displayType = $FullColor THEN 3 ELSE 1;
FOR i:
NAT
IN [0..samplesPerColor)
DO
ImagerSample.Transfer[dst: context.pixels[i], src: frames[frames.currentFrame][i]];
ENDLOOP;
};
};
standardNames:
ARRAY[0..6)
OF
RECORD[ pref, alt, other: Rope.
ROPE ] ← [
["-gray", "-grey", NIL],
["-red", "-r", "-rd"],
["-grn", "-green", "-g"],
["-blu", "-blue", "-b"],
["-alpha", "-a", "-alp"],
["-depth", "-d", "-z"]
];
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 ThreeDBasics.Error[[
$MisMatch,
Rope.Cat[" Can't find ", fileRoot, " or with extensions: ",
Rope.Cat[names.pref, " ", names.alt, " ", names.other] ]
]];
RETURN[ NIL ];
};
};
GetAIS: PUBLIC PROC[context: REF Context, fileRoot: Rope.ROPE ← "Temp.ais",
xOffset, yOffset:
INTEGER ← 0, center:
BOOLEAN ←
TRUE ]
RETURNS[ xSize, ySize:
INTEGER] ~ {
numFiles: NAT;
names: ARRAY[0..5) OF RECORD[ pref, alt, other: Rope.ROPE ];
inSamples: ARRAY[0..5) OF SampleMap ← ALL[NIL];
pixelsFromAIS: PixelMap; -- place to put sample maps if needed elsewhere
IF context.stopMe^ THEN RETURN[0, 0]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
fileRoot ← SceneUtilities.PrependWorkingDirectory[context, fileRoot];
IF context.pixels = NIL THEN RenderWithPixels.AllocatePixelMemory[ context ]; -- image bfr
SELECT context.class.displayType
FROM
$Dithered, $PseudoColor => {
names[0] ← [NIL, NIL, NIL];
numFiles ← 1;
};
$Gray => {
names[0] ← standardNames[0];
numFiles ← 1;
};
$FullColor, $ImagerFullClr => {
names[0] ← standardNames[1];
names[1] ← standardNames[2];
names[2] ← standardNames[3];
numFiles ← 3;
};
ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Bad RenderMode"]];
IF context.antiAliasing
THEN { names[numFiles] ← standardNames[4]; numFiles ← numFiles + 1; };
IF context.depthBuffering
THEN { names[numFiles] ← standardNames[5]; numFiles ← numFiles + 1; };
FOR i:
NAT
IN [0..numFiles)
DO
-- get a file for each pixel entry
fileName: Rope.
ROPE ← FindFile[
fileRoot, names[i]
! ThreeDBasics.Error => IF reason.code = $MisMatch THEN RESUME
];
IF fileName #
NIL
THEN {
box: Box;
inSamples[i] ← ConvertRasterObject.SampleMapFromAIS[
aisfile: fileName, useVM: TRUE
];
box ← ImagerSample.GetBox[ inSamples[i] ];
xSize ← SF.SizeF[box]; ySize ← SF.SizeS[box];
IF context.stopMe^ THEN RETURN[0, 0]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
IF context.viewer #
NIL
-- on viewer, put pixels there
THEN {
context.class.updateViewer[context]; -- get viewPort, etc.
context.class.drawInViewer[
-- get pixel map from viewer
context,
NEW[ImagerProcRec ← [
WriteToViewer,
LIST[ NEW[NAT ← i], inSamples[i],
NEW[INTEGER ← xOffset], NEW[INTEGER ← yOffset],
NEW[BOOLEAN ← center]
]
]]
];
IF labelsEnabled
THEN {
intPos: IntegerPair; pos: Pair;
viewerBox: Box ← [
min: [ f: context.viewer.cx, s: context.viewer.cy ],
max: [ f: context.viewer.cw+context.viewer.cx,
s: context.viewer.ch+context.viewer.cy ]
];
[intPos, , ] ← ComputeBox[viewerBox, box, xOffset, xOffset, center];
pos ← [ 0.02 + 1.0 * intPos.x / context.pixels.box.max.f,
0.02 + 1.0 * intPos.y / context.pixels.box.max.s ];
context.class.draw2DRope[
context: context, rope: fileName, position: pos, size: 20 * xSize / 640.0
];
};
}
ELSE {
RenderWithPixels.CopyUnder should work without the following commented:
IF context.viewPort #
NIL
THEN {
box ← [ [Real.Round[context.viewPort.y], Real.Round[context.viewPort.x]],
[Real.Round[context.viewPort.h], Real.Round[context.viewPort.w]] ];
box.max.s ← box.max.s + box.min.s; box.max.f ← box.max.f + box.min.f;
}
ELSE box ← context.pixels.box;
TransferSamples[
ImagerSample.Clip[context.pixels[i], box], inSamples[i],
xOffset, yOffset, center
];
};
};
ENDLOOP;
pixelsFromAIS ← ImagerPixel.MakePixelMap[
inSamples[0], inSamples[1], inSamples[2], inSamples[3], inSamples[4]
];
context.props ← Atom.PutPropOnList[context.props, $TempPixels, pixelsFromAIS];
};
GetRGB: PUBLIC PROC[context: REF Context, fileRoot: Rope.ROPE ← "Temp.rgb",
xOffset, yOffset:
INTEGER ← 0, center:
BOOLEAN ←
TRUE ]
RETURNS[ xSize, ySize:
INTEGER] ~ {
SampleMapsFromRGB:
PROC[ fileName: Rope.
ROPE ] ~ {
in: IO.STREAM ← FS.StreamOpen[fileName: fileName];
box: Box ← [[f: 0, s: 0], [f: 720, s: 486]];
rBuf: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[720];
gBuf: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[720];
bBuf: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[720];
FOR i:
NAT
IN [0..3)
DO
inSamples[i] ← ImagerSample.NewSampleMap[
box: box, bitsPerSample: 8, bitsPerLine: 720*8
];
ENDLOOP;
FOR y:
NAT
IN [0..486)
DO
FOR x:
NAT
IN [0..720)
DO
rBuf[x] ← ORD[IO.GetChar[in]];
gBuf[x] ← ORD[IO.GetChar[in]];
bBuf[x] ← ORD[IO.GetChar[in]];
ENDLOOP;
ImagerSample.PutSamples[map: inSamples[0], initIndex: [y, 0], buffer: rBuf, count: 720];
ImagerSample.PutSamples[map: inSamples[1], initIndex: [y, 0], buffer: gBuf, count: 720];
ImagerSample.PutSamples[map: inSamples[2], initIndex: [y, 0], buffer: bBuf, count: 720];
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
ENDLOOP;
IO.Close[in];
};
box: Box;
inSamples: ARRAY[0..3) OF SampleMap ← ALL[NIL];
pixelsFromAIS: PixelMap; -- place to put sample maps if needed elsewhere
IF context.stopMe^ THEN RETURN[0, 0]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
fileRoot ← SceneUtilities.PrependWorkingDirectory[context, fileRoot];
IF context.pixels = NIL THEN RenderWithPixels.AllocatePixelMemory[ context ]; -- image bfr
IF context.class.displayType # $FullColor
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Must be a full-color display"]];
SampleMapsFromRGB[ fileRoot ];
box ← ImagerSample.GetBox[ inSamples[0] ];
xSize ← SF.SizeF[box]; ySize ← SF.SizeS[box];
IF context.viewer #
NIL
-- on viewer, put pixels there
THEN {
context.class.updateViewer[context]; -- get viewPort, etc.
FOR i:
NAT
IN [0..3)
DO
context.class.drawInViewer[
-- get pixel map from viewer
context,
NEW[ImagerProcRec ← [
WriteToViewer,
LIST[ NEW[NAT ← i], inSamples[i],
NEW[INTEGER ← xOffset], NEW[INTEGER ← yOffset],
NEW[BOOLEAN ← center]
]
]]
];
ENDLOOP;
IF labelsEnabled
THEN {
intPos: IntegerPair; pos: Pair;
viewerBox: Box ← [
min: [ f: context.viewer.cx, s: context.viewer.cy ],
max: [ f: context.viewer.cw+context.viewer.cx,
s: context.viewer.ch+context.viewer.cy ]
];
[intPos, , ] ← ComputeBox[viewerBox, box, xOffset, xOffset, center];
pos ← [ 0.02 + 1.0 * intPos.x / context.pixels.box.max.f,
0.02 + 1.0 * intPos.y / context.pixels.box.max.s ];
context.class.draw2DRope[
context: context, rope: fileRoot, position: pos, size: 20 * xSize / 640.0
];
};
}
ELSE {
IF context.viewPort #
NIL
THEN {
box ← [ [Real.Round[context.viewPort.y], Real.Round[context.viewPort.x]],
[Real.Round[context.viewPort.h], Real.Round[context.viewPort.w]] ];
box.max.s ← box.max.s + box.min.s; box.max.f ← box.max.f + box.min.f;
}
ELSE box ← context.pixels.box;
FOR i:
NAT
IN [0..3)
DO
TransferSamples[
ImagerSample.Clip[context.pixels[i], box], inSamples[i],
xOffset, yOffset, center
];
ENDLOOP;
};
pixelsFromAIS ← ImagerPixel.MakePixelMap[ inSamples[0], inSamples[1], inSamples[2] ];
context.props ← Atom.PutPropOnList[context.props, $TempPixels, pixelsFromAIS];
};
WriteToViewer: ImagerProc ~ {
-- get pixelmap from viewer and get sample map into it
PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY ← NIL ];
DoWrite:
PROC[pixelMap: PixelMap] ~ {
list: LORA ← NARROW[data];
i: NAT ← NARROW[list.first, REF NAT]^;
TransferSamples[
dstMap: pixelMap[i],
srcMap: NARROW[list.rest.first],
xOffset: NARROW[list.rest.rest.first, REF INTEGER]^,
yOffset: NARROW[list.rest.rest.rest.first, REF INTEGER]^,
center: NARROW[list.rest.rest.rest.rest.first, REF BOOLEAN]^
];
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoWrite, context.viewPort^];
};
TransferSamples:
PROC[ dstMap, srcMap: SampleMap, xOffset, yOffset:
INTEGER,
center:
BOOLEAN ←
TRUE ] ~ {
srcBox: Box ← ImagerSample.GetBox[srcMap]; dstBox: Box ← ImagerSample.GetBox[dstMap];
dstMin, srcMin, size: IntegerPair;
[dstMin, srcMin, size] ← ComputeBox[dstBox, srcBox, xOffset, yOffset, center];
ImagerSample.BasicTransfer[
dst: dstMap, src: srcMap,
dstMin: [f: dstMin.x, s: dstMin.y], -- position
srcMin: [f: srcMin.x, s: srcMin.y],
size: [f: size.x, s: size.y] -- clip
];
[
IF center
THEN { -- Shift larger sample map to fit in center of smaller, then offset
xDelta ← xOffset + (INTEGER[SF.SizeF[srcBox]] - SF.SizeF[dstBox]) /2;
yDelta ← -yOffset + (INTEGER[SF.SizeS[srcBox]] - SF.SizeS[dstBox]) /2;
}
ELSE { -- shift tio align with bottom of viewport, then offset
xDelta ← xOffset;
yDelta ← -yOffset + (INTEGER[SF.SizeS[srcBox]] - SF.SizeS[dstBox]);
};
ImagerSample.BasicTransfer[
dst: dstMap, src: srcMap,
dstMin: [f: dstBox.min.f + MAX[0, -xDelta], s: dstBox.min.s + MAX[0, -yDelta]], -- position
srcMin: [f: srcBox.min.f + MAX[0, xDelta], s: srcBox.min.s + MAX[0, yDelta]],
size: [ f: MIN[ SF.SizeF[srcBox] - MAX[0, xDelta], SF.SizeF[dstBox] - MAX[0, -xDelta] ],
s: MIN[ SF.SizeS[srcBox] - MAX[0, yDelta], SF.SizeS[dstBox] - MAX[0, -yDelta] ]
] -- clip
];
};
ComputeBox:
PROC[ box1, box2: Box, xOffset, yOffset:
INTEGER, center:
BOOLEAN ←
TRUE ]
RETURNS[box1Pos, box2Pos, size: IntegerPair] ~ {
xDelta, yDelta: INTEGER ← 0;
IF center
THEN {
-- Shift larger sample map to fit in center of smaller, then offset
xDelta ← xOffset + (INTEGER[SF.SizeF[box2]] - SF.SizeF[box1]) /2;
yDelta ← -yOffset + (INTEGER[SF.SizeS[box2]] - SF.SizeS[box1]) /2;
}
ELSE {
-- shift tio align with bottom of viewport, then offset
xDelta ← xOffset;
yDelta ← -yOffset + (INTEGER[SF.SizeS[box2]] - SF.SizeS[box1]);
};
box1Pos ← [x: box1.min.f + MAX[0, -xDelta], y: box1.min.s + MAX[0, -yDelta]]; -- position
box2Pos ← [x: box2.min.f + MAX[0, xDelta], y: box2.min.s + MAX[0, yDelta]];
size ← [ x: MIN[ SF.SizeF[box2] - MAX[0, xDelta], SF.SizeF[box1] - MAX[0, -xDelta] ], -- clip
y: MIN[ SF.SizeS[box2] - MAX[0, yDelta], SF.SizeS[box1] - MAX[0, -yDelta] ] ];
};
WriteFromViewer: ImagerProc ~ {
PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY ← NIL ];
DoWrite:
PROC[pixelMap: PixelMap] ~ {
list: LORA ← NARROW[data];
i: NAT ← NARROW[list.first, REF NAT]^;
fileName: ROPE ← NARROW[list.rest.first];
ConvertRasterObject.AISFromSampleMap[fileName, pixelMap[i]];
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoWrite, context.viewPort^];
};
PutAIS:
PUBLIC
PROC[context:
REF Context, fileRoot: Rope.
ROPE ← "Temp.ais",
doEverything:
BOOLEAN ←
FALSE ] ~ {
addOn: NAT ← IF context.antiAliasing THEN 1 ELSE 0;
numFiles: NAT;
names: ARRAY[0..4) OF Rope.ROPE ← ALL[NIL];
IF context.depthBuffering THEN addOn ← addOn + 1;
fileRoot ← SceneUtilities.PrependWorkingDirectory[context, fileRoot];
SELECT context.class.displayType
FROM
$Dithered, $PseudoColor => { names[0] ← NIL; numFiles ← 1; };
$Gray => { names[0] ← "-gray"; numFiles ← 1; };
$FullColor, $ImagerFullClr => {
names[0] ← "-red"; names[1] ← "-grn"; names[2] ← "-blu"; numFiles ← 3;
};
ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Bad RenderMode"]];
IF context.antiAliasing
AND doEverything
THEN { names[numFiles] ← "-alpha"; numFiles ← numFiles + 1; };
IF context.depthBuffering
AND doEverything
THEN { names[numFiles] ← "-depth"; numFiles ← numFiles + 1; };
SurfaceRender.ValidateContext[context];
FOR i:
NAT
IN [0..numFiles)
DO
-- write a file for each pixel entry
IF context.viewer #
NIL
THEN {
context.class.updateViewer[context]; -- get viewPort, etc.
context.class.drawInViewer[
context,
NEW[ImagerProcRec ← [
WriteFromViewer,
LIST[ NEW[NAT ← i], PasteInLabel[fileRoot, names[i]] ]
]]
]
}
ELSE {
box: Box;
IF context.viewPort #
NIL
THEN {
box ← [ [Real.Round[context.viewPort.y], Real.Round[context.viewPort.x]],
[Real.Round[context.viewPort.h], Real.Round[context.viewPort.w]] ];
box.max.s ← box.max.s + box.min.s; box.max.f ← box.max.f + box.min.f;
}
ELSE box ← context.pixels.box;
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
ConvertRasterObject.AISFromSampleMap[
PasteInLabel[fileRoot, names[i]],
ImagerSample.Clip[context.pixels[i], box]
];
};
ENDLOOP;
};
WriteRGBFromViewer: ImagerProc ~ {
PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY ← NIL ];
DoWrite:
PROC[pixelMap: PixelMap] ~ {
list: LORA ← NARROW[data];
fileName: ROPE ← NARROW[list.first];
RGBFromSampleMaps[context, fileName, pixelMap[0], pixelMap[1], pixelMap[2] ];
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoWrite, context.viewPort^];
};
PutRGB:
PUBLIC
PROC[context:
REF Context, fileRoot: Rope.
ROPE ← "Temp.rgb" ] ~ {
fileRoot ← SceneUtilities.PrependWorkingDirectory[context, fileRoot];
IF context.class.displayType # $FullColor
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Must be a full-color image"]];
SurfaceRender.ValidateContext[context];
IF context.pixels #
NIL
THEN RGBFromSampleMaps[ context, fileRoot,
context.pixels[0], context.pixels[1], context.pixels[2] ]
ELSE
IF context.viewer #
NIL
THEN {
context.class.updateViewer[context]; -- get viewPort, etc.
context.class.drawInViewer[
context,
NEW[ImagerProcRec ← [ WriteRGBFromViewer, LIST[ fileRoot ] ]]
]
}
};
RGBFromSampleMaps:
PROC[ context:
REF Context, fileName: Rope.
ROPE,
rMap, gMap, bMap: SampleMap ] ~ {
out: IO.STREAM ← FS.StreamOpen[fileName: fileName, accessOptions: create];
box: Box ← ImagerSample.GetBox[rMap];
xSize: NAT ← box.max.f - box.min.f;
ySize: NAT ← box.max.s - box.min.s;
yMin: INTEGER ← (486 - ySize) / 2;
xMin: INTEGER ← (720 - xSize) / 2;
rBuf: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[720];
gBuf: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[720];
bBuf: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[720];
FOR y:
NAT
IN [0..485]
DO
IF y >= yMin
AND y < yMin + ySize
THEN {
-- get a line from sample map
mpx: NAT ← box.min.f;
mpy: NAT ← y - yMin + box.min.s;
ImagerSample.GetSamples[map: rMap, initIndex: [mpy, mpx], buffer: rBuf, count: xSize];
ImagerSample.GetSamples[map: gMap, initIndex: [mpy, mpx], buffer: gBuf, count: xSize];
ImagerSample.GetSamples[map: bMap, initIndex: [mpy, mpx], buffer: bBuf, count: xSize];
};
FOR x:
NAT
IN [0..719]
DO
r,g,b: BYTE;
IF y < yMin
OR y > yMin + ySize
OR x < xMin
OR x > xMin + xSize
THEN r ← g ← b ← 0
ELSE { r ← rBuf[x - xMin]; g ← gBuf[x - xMin]; b ← bBuf[x - xMin]; };
IO.PutChar[out, VAL[r]]; IO.PutChar[out, VAL[g]]; IO.PutChar[out, VAL[b]];
ENDLOOP;
IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received
CedarProcess.CheckAbort[]; -- respond to Stop button
ENDLOOP;
IO.Close[out];
};