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 ]; AISAnimationImpl: CEDAR PROGRAM IMPORTS Atom, BasicTime, CedarProcess, ColorDisplayRender, Convert, ConvertRasterObject, FS, ImagerBackdoor, ImagerPixel, ImagerSample, IO, Real, RenderWithPixels, Rope, SceneUtilities, SF, SurfaceRender, Terminal, ThreeDBasics EXPORTS AISAnimation ~ BEGIN 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]; 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]] ~ { 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; }; 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 ~ { 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]; }; 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 { 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 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 ]; }; 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 ~ { 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 ~ { 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]; }; END. AISAnimationImpl.mesa Copyright c 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 Internal Declarations Procedures for Animation Update log file, if animation, then store image Get pixels left in proplist by GetAIS PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY _ NIL ]; Support Procedures RenderWithPixels.CopyUnder should work without the following commented: PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY _ NIL ]; [ 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 ]; PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY _ NIL ]; PROC[ context: REF Context, imagerCtx: Imager.Context, data: REF ANY _ NIL ]; ΚO˜Ihead™šœ Οmœ1™žœžœžœ žœ ˜ΛJšœžœ˜(Nšœžœ˜Nšœ žœžœ žœ@˜fJ˜šžœ žœžœ˜Nšžœ5žœ˜DN˜—Jšœžœ &˜EJšœžœ ,˜KOšœ, ˜DN˜Ašžœ˜JšžœU˜Y—Nšžœ žœ˜-š žœžœžœžœ  ˜5šžœžœžœž˜*Nš žœžœžœžœ žœ˜GN˜Jšžœžœžœ $˜GNšœ ˜6N˜#Nšžœ˜—š žœ žœžœžœž œž˜FNš žœžœžœžœ žœ˜GN˜Jšžœžœžœ $˜GNšœ ˜6N˜#Nšžœ˜—Nšžœ˜—Jšœžœ +˜IN˜—š ‘œžœžœ žœžœžœ˜zNšœ žœ˜Jšœžœ˜*šžœ˜Nšžœ@žœ˜VNšžœ2žœ˜H—Nšœ/žœ˜BNš žœžœžœžœ žœ˜GJšžœžœžœ $˜GNšœ ˜6N˜#N˜—N˜—™š ‘œžœ žœžœ žœ˜8J˜Jšžœžœ˜,Jšœ‘˜—š ‘œžœžœžœžœžœ˜GNšœžœ-žœ˜8Jšœžœ˜,šžœ˜Nšžœ3 ˜UNšžœ6 ˜P—Nšžœ!˜'N˜—N˜š‘œžœžœžœ žœžœžœ˜WNšœ žœ#žœ˜8šžœž˜N˜N˜N˜Nšžœžœ<˜M—Nšžœ!˜'N˜N˜—š ‘ œžœžœžœžœžœ˜PNšœžœ2žœ˜=Jšœžœ˜,šžœ˜šžœ˜Nšœ3 ˜QN˜ N˜—šžœ˜Nšœ5 ˜KN˜;N˜——Nšžœ%˜+N˜N˜—š‘ œžœ žœžœ žœ žœžœ˜qJ˜Jšœžœ˜)Jšœžœ˜*Jš œžœžœžœžœ˜3Nš œžœžœ(žœžœ˜MJ˜Ašžœ˜Nšžœ˜Nšžœ" +˜Q—J˜šžœ $˜BJšžœ˜Jšžœ&˜*—Nšœ žœ˜N˜N˜—š ‘œžœžœ žœžœ˜ašžœžœ&˜CNšžœžœ>˜I—šžœžœ˜%šžœžœž˜šžœ˜J˜$N˜-˜Nšœžœ8˜DN˜—N˜—šžœž˜Jš œžœžœ(žœžœ˜Ošžœžœžœž˜%N˜TNšžœ˜—N˜———N˜—J˜š œžœžœžœžœ˜GJšœžœ˜J˜J˜J˜J˜J˜J˜J˜—š‘œžœžœ žœžœž œ žœ˜vJšœžœžœ˜Jš œžœ=žœžœžœ˜hJš žœžœžœ žœžœ˜,Jš œžœ<žœžœžœ˜gJš žœžœžœ žœžœ˜,Jš œžœ>žœžœžœ˜iJš žœžœžœ žœžœ˜,Jš œžœžœžœžœ˜Hšžœžœžœ ˜šžœ˜šžœ˜J˜ J˜yJ˜—Jšžœžœ˜J˜——L˜—Jš‘œž œ žœžœ˜Kš œžœžœžœžœžœ˜mJšœ ž ˜Nš œžœžœžœžœ˜N˜—šžœ žœ˜šžœ˜N˜ šœ4˜4Nšœž˜N˜—N˜*Nšœžœžœ ˜1Jšžœžœžœ $˜JNšœ ˜6šžœžœ ˜;šžœ˜Jšœ& ˜;šœ ˜;N˜ šžœ˜N˜Nšžœžœžœžœžœ žœžœ žœžœ˜sN˜—N˜—šžœžœ˜N˜"˜N˜5N˜\N˜—N˜DN˜p˜N˜LN˜—N˜—N˜—šžœ˜J™Gšžœžœ˜šžœ˜J˜“N˜HN˜—Nšžœ˜—˜N˜9N˜N˜—N˜——N˜——Nšžœ˜—˜)N˜DN˜—N˜NN˜N˜—Jš‘œž œ žœžœ˜Kš œžœžœžœžœžœ˜mš‘œžœžœ˜2Nšœžœžœžœ!˜3N˜,N˜?N˜?N˜?šžœžœžœžœ˜˜)N˜.N˜—Nšžœ˜—šžœžœžœ ž˜šžœžœžœ ž˜Nšœ žœžœ˜"Nšœžœžœ˜"Nšœžœžœ˜Nšžœ˜ —N˜XN˜XN˜XJšžœžœžœ $˜FNšœ ˜6Nšžœ˜—Nšžœ ˜ N˜—N˜ Jš œ žœžœ žœžœ˜/Jšœ /˜MN˜Jšžœžœžœ $˜JNšœ ˜6N˜ENšžœžœžœ2  ˜[šžœ(˜*NšžœžœB˜M—N˜N˜*Nšœžœžœ ˜2šžœžœ ˜;šžœ˜Jšœ& ˜;šžœžœžœžœ˜šœ ˜;N˜ šžœ˜N˜Nšžœžœžœžœžœ žœžœ žœžœ˜sN˜—N˜—Nšžœ˜—šžœžœ˜N˜"˜N˜5N˜\N˜—N˜DN˜p˜N˜LN˜—N˜—N˜—šžœ˜šžœžœ˜šžœ˜J˜“N˜HN˜—Nšžœ˜—šžœžœžœžœ˜˜N˜9N˜N˜—Nšžœ˜—N˜——N˜VN˜NN˜N˜—š‘ œ 6˜UJš žœ žœ+žœžœžœ™Mš‘œžœ˜%Jšœžœžœ˜Jš œžœžœ žœžœ˜'˜J˜Jšœžœ˜ Jšœ žœžœžœ˜5Jšœ žœžœžœ˜:Jšœžœ!žœžœ˜=J˜—J˜—J˜LN˜—š ‘œžœ/žœžœžœ˜sN˜VN˜"N˜N˜N˜Nšœ%  ˜0N˜#Nšœ  ˜(N˜—N™šžœ™ šžœ C™NNšœžœžœžœ™ENšœžœžœžœ™FN™—šžœ  7™EN™Nšœžœžœžœ™CN™——™N™Nšœžœ žœ  ™\Nšœžœ žœ™ONšœ žœžœžœ žœžœžœžœžœ žœžœ ™»N™—J˜—š ‘ œžœ%žœ žœžœ žœ)˜ŽNšœžœ˜šžœ˜ šžœ C˜NNšœžœžœžœ˜ANšœžœžœžœ˜BN˜—šžœ  7˜EN˜Nšœžœžœžœ˜?N˜——Nšœžœžœ  ˜ZNšœžœžœ˜MNšœ žœžœžœ žœžœ  œ žœžœžœ žœžœ˜΄N˜—š‘œ˜Jš žœ žœ+žœžœžœ™Mš‘œžœ˜%Jšœžœžœ˜Nš œžœžœ žœžœ˜&Nšœ žœžœ˜)N˜