DIRECTORY Basics USING [ BITAND, BITSHIFT ], Atom USING [ GetPropFromList, PutPropOnList, RemPropFromList ], Process USING [ Pause, SecondsToTicks ], CedarProcess USING [ Abort, CheckAbort, DoWithPriority, Fork, ForkableProc, Join, Process ], ViewerClasses USING [ Viewer ], Terminal USING [ ColorMode, Current, GetColorFrameBufferA, GetColorFrameBufferB, GetColorMode, SetColor, SetBlueMap, SetGreenMap, SetRedMap, Virtual ], FS USING [ ComponentPositions, ExpandName ], IO USING [ STREAM ], Convert USING [ RopeFromReal ], Rope USING [ Cat, Equal, ROPE, Substr ] , Real USING [ Float, Fix, Round ], RealFns USING [ Power ], ColorDisplayManager USING [ Start ], Imager USING [ Context, Rectangle ], ImagerBackdoor USING [ AccessBufferRectangle ], ImagerSample USING [ Box, GetBase, GetBitsPerLine, GetBox, GetRef, GetSamples, MapFromFrameBuffer, NewSamples, ObtainScratchSamples, PutSamples, RasterSampleMap, ReleaseScratchSamples, SampleBuffer, Transfer, UnsafeNewSampleMap, WordsForLines ], ImagerPixel USING [ GetPixels, MakePixelMap, NewPixels, PixelBuffer, PixelMap, PutPixels ], G3dRender USING [ Box, ClipState, Context, ContextClass, ContextProc, Create, Error, GetDisplayType, ImagerProc, ImagerProcRec, NatSequence, Pair, PairSequence, Patch, PatchProc, Pixel, Quad, Rectangle, RegisterDisplayType, RGB, SetView, ShadingValue, ShapeInstance, ShapeSequence, Triple, TripleSequence, Vertex ], SceneUtilities USING [ GetTmpContext, SetViewPort, SetWindow, StartLog ], G3dSurfaceRender USING [ ValidateContext ], G3dRenderWithPixels USING [ AntiAliasing, BufferRendering, DepthBuffering, DoRope, DrawRope, AllocatePixelMemory, Draw2DPoly, Draw2DLine, FillInBackGround, MakeFrame, PolygonTiler, RopeDesc ], Animation3D USING [ MoveCtrOfInterestInOrbit, MoveEyePointInOrbit, MoveOnLine, MoveOnClosedCurve, MoveOnOpenCurve, ViewProc ], AISAnimation USING [ StoreFiles ], G3dColorDisplayRender USING [ ]; G3dColorDisplayRenderImpl: CEDAR MONITOR IMPORTS AISAnimation, Animation3D, Atom, Basics, CedarProcess, ColorDisplayManager, Convert, FS, ImagerBackdoor, ImagerPixel, ImagerSample, Process, Real, RealFns, G3dRenderWithPixels, Rope, SceneUtilities, G3dSurfaceRender, Terminal, G3dRender EXPORTS G3dColorDisplayRender ~ BEGIN ROPE: TYPE ~ Rope.ROPE; RopeDesc: TYPE ~ G3dRenderWithPixels.RopeDesc; Context: TYPE ~ G3dRender.Context; ContextProc: TYPE ~ G3dRender.ContextProc; ImagerProc: TYPE ~ G3dRender.ImagerProc; ContextClass: TYPE ~ G3dRender.ContextClass; Box: TYPE ~ G3dRender.Box; Rectangle: TYPE ~ G3dRender.Rectangle; Triple: TYPE ~ G3dRender.Triple; TripleSequence: TYPE ~ G3dRender.TripleSequence; RGB: TYPE ~ G3dRender.RGB; Pixel: TYPE ~ G3dRender.Pixel; Pair: TYPE ~ G3dRender.Pair; PairSequence: TYPE ~ G3dRender.PairSequence; IntRGB: TYPE ~ RECORD [r, g, b: CARDINAL]; IntRGBSequence: TYPE ~ RECORD [SEQUENCE length: NAT OF IntRGB]; NatSequence: TYPE ~ G3dRender.NatSequence; Patch: TYPE ~ G3dRender.Patch; PatchProc: TYPE ~ G3dRender.PatchProc; ClipState: TYPE ~ G3dRender.ClipState; PixelBuffer: TYPE ~ ImagerPixel.PixelBuffer; PixelMap: TYPE ~ ImagerPixel.PixelMap; ImagerProcRec: TYPE ~ G3dRender.ImagerProcRec; timeResolution: CARD16 _ 3; Ceiling: PROC[number: REAL] RETURNS[result: INTEGER] ~ { result _ Real.Round[number]; IF result < number THEN result _ result + 1; }; Floor: PROC[ in: REAL ] RETURNS[ out: INTEGER ] ~ { out _ Real.Round[in]; IF Real.Float[out] > in THEN out _ out - 1; }; UpdateFullColorDisplay: PROC [context: REF Context] ~ { screenPixels: PixelMap _ NARROW[Atom.GetPropFromList[context.displayProps, $ScreenPixels]]; xMin: NAT _ context.pixels.box.min.f; width: NAT _ context.pixels.box.max.f-xMin; buf1: ImagerSample.SampleBuffer _ ImagerSample.ObtainScratchSamples[width]; buf2: ImagerSample.SampleBuffer _ ImagerSample.ObtainScratchSamples[width]; DoUpdateFullColorDisplay[context, screenPixels, buf1, buf2, xMin, width]; ImagerSample.ReleaseScratchSamples[buf1]; ImagerSample.ReleaseScratchSamples[buf2]; }; PeriodicUpdateFullColorDisplay: CedarProcess.ForkableProc ~ { context: REF Context _ NARROW[data]; screenPixels: PixelMap _ NARROW[Atom.GetPropFromList[context.displayProps, $ScreenPixels]]; xMin: NAT _ context.pixels.box.min.f; width: NAT _ context.pixels.box.max.f-xMin; waitTime: CARD16 _ timeResolution*3; buf1: ImagerSample.SampleBuffer _ ImagerSample.NewSamples[width]; buf2: ImagerSample.SampleBuffer _ ImagerSample.NewSamples[width]; DO -- stuff buffered pixels onto screen every waitTime seconds Process.Pause[Process.SecondsToTicks[waitTime]]; DoUpdateFullColorDisplay[context, screenPixels, buf1, buf2, xMin, width]; CedarProcess.CheckAbort[ ]; ENDLOOP; }; DoUpdateFullColorDisplay: PROC [ context: REF Context, screenPixels: PixelMap, buf1, buf2: ImagerSample.SampleBuffer, xMin, width: NAT] ~ { IF screenPixels # NIL THEN FOR i: NAT IN [context.pixels.box.min.s..context.pixels.box.max.s) DO ImagerSample.GetSamples[ map: context.pixels[0], initIndex: [i, xMin], buffer: buf1, count: width]; ImagerSample.GetSamples[ map: context.pixels[1], initIndex: [i, xMin], buffer: buf2, count: width]; ImagerSample.PutSamples[ map: screenPixels[0], initIndex: [i, 2*xMin], buffer: buf1, delta: [0, 2], count: width]; ImagerSample.PutSamples[ map: screenPixels[0], initIndex: [i, 1+2*xMin], buffer: buf2, delta: [0, 2], count: width]; ENDLOOP; }; UpdateViewer: CedarProcess.ForkableProc ~ { -- updates viewer from buffered pixel map context: REF Context _ NARROW[data]; waitTime: CARD16 _ IF context.class.displayType = $FullColor THEN timeResolution * 3 ELSE timeResolution; WHILE TRUE DO -- stuff buffered pixels onto screen every waitTime seconds Process.Pause[ Process.SecondsToTicks[waitTime] ]; context.class.drawInViewer[ context, NEW[ImagerProcRec _ [StuffBuf, NIL]] ]; CedarProcess.CheckAbort[ ]; ENDLOOP; }; GetDisplay: ContextProc ~ { IF context.viewer # NIL THEN { context.class.updateViewer[ context ]; G3dRenderWithPixels.AllocatePixelMemory[context]; } ELSE GrabColorDisplay[context]; -- clear off color display and take it over SELECT context.class.displayType FROM $FullColor, $Gray => { LoadColorRamp[ context.terminal, [0.,0.,0.], [1.,1.,1.], [.43, .43, .43] ]; }; $PseudoColor => { LoadStd8BitClrMap[context.terminal]; }; ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected displayType"]]; context.pixelAspectRatio _ 1.0; -- standard square-pixel display assumed context.displayInValid _ TRUE; }; GrabColorDisplay: PROC[ context: REF Context ] ~ { vt: Terminal.Virtual; box: ImagerSample.Box; s0, s2: ImagerSample.RasterSampleMap _ NIL; clrType: ATOM; IF context.terminal = NIL THEN context.terminal _ vt _ Terminal.Current[] ELSE vt _ context.terminal; SELECT context.class.displayType FROM $PseudoColor => clrType _ $Dither8; $Gray => clrType _ $Gray8; $FullColor => clrType _ $FullColor; ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected Display type"]]; ColorDisplayManager.Start[ type: clrType, -- ATOM, Colordisplay types: $Gray8, $Dither8, $Dither1, $FullColor side: left, -- Interminal.Side {left, right} level: mouse, -- ColorDisplayManager.Level ~ {off, allocated, visible, mouse, viewers} resolution: none -- ColorDisplayDefs.ColorDisplayType {none, standard, highResolution} ]; SELECT context.class.displayType FROM $FullColor => { s0 _ ImagerSample.MapFromFrameBuffer[vt.GetColorFrameBufferA[]]; -- 16 bits, R&G s2 _ ImagerSample.MapFromFrameBuffer[vt.GetColorFrameBufferB[]]; -- 8 bits, B }; $PseudoColor, $Gray => s0 _ ImagerSample.MapFromFrameBuffer[vt.GetColorFrameBufferA[]]; ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected Display type"]]; box _ ImagerSample.GetBox[s0]; -- get frame buffer size G3dRenderWithPixels.AllocatePixelMemory[context]; IF context.class.displayType = $FullColor THEN { screenPxls: PixelMap; TRUSTED { screenPxls _ ImagerPixel.MakePixelMap[ ImagerSample.UnsafeNewSampleMap[ box: [box.min, [box.max.s, 2 * box.max.f]], bitsPerSample: 8, bitsPerLine: ImagerSample.GetBitsPerLine[s0], base: ImagerSample.GetBase[s0], ref: ImagerSample.GetRef[s0], words: ImagerSample.WordsForLines[ box.max.s, ImagerSample.GetBitsPerLine[s0] ] ] ]; }; context.displayProps _ Atom.PutPropOnList[context.displayProps, $ScreenPixels, screenPxls]; }; IF s0 # NIL THEN context.pixels[0] _ s0; -- on-screen mem (pseudoclr, grey, 16-bit R&G) IF s2 # NIL THEN context.pixels[2] _ s2; -- on-screen mem (Blue record for full-color) context.viewPort _ NIL; context.window _ NIL; }; ValidateDisplay: ContextProc ~{ -- update viewPort, etc. IF context.viewer # NIL THEN { IF Atom.GetPropFromList[ context.displayProps, $ViewerAdjusted ] # NIL THEN context.class.drawInViewer[context, NIL]; -- get specs for new viewer IF context.pixels = NIL OR context.pixels.samplesPerPixel = 1 THEN context.class.updateViewer[ context ]; -- drawing directly on screen context.stopMe^ _ FALSE; -- make sure stop button is released }; IF context.pixels # NIL -- check for mismatched viewPort and pixel storage THEN IF context.pixels.box.max.f < context.viewPort.w OR context.pixels.box.max.s < context.viewPort.h THEN G3dRenderWithPixels.AllocatePixelMemory[context]; }; Init: PROC[] ~ { -- register Imager-based drawing types standardClass: ContextClass _ [ displayType: $PseudoColor, setUpDisplayType: GetDisplay, validateDisplay: ValidateDisplay, render: MakeFrame, loadBackground: FillInBackGround, draw2DLine: Draw2DLine, draw2DPolygon: Draw2DPoly, draw2DRope: Draw2DRope, displayPolygon: PolygonTiler ]; G3dRender.RegisterDisplayType[ standardClass, $PseudoColor ]; standardClass.displayType _ $FullColor; G3dRender.RegisterDisplayType[ standardClass, $FullColor ]; standardClass.displayType _ $Gray; G3dRender.RegisterDisplayType[ standardClass, $Gray ]; }; MappedRGB: PUBLIC PROC[context: REF Context, clr: Pixel] RETURNS[Pixel] ~ { SELECT context.class.displayType FROM $ImagerDithered => { mapVal: NAT _ 24 * (clr[r]*5 / 256) + 4 * (clr[g]*6 / 256) + (clr[b]*4 / 256); IF mapVal >= 60 THEN mapVal _ mapVal + 135; -- move to top of map clr[r] _ mapVal; }; $PseudoColor => clr[r] _ 42 * (clr[r]*6 / 256) + 6 * (clr[g]*7 / 256) + (clr[b]*6 / 256) +2; $Gray => clr[r] _ ( clr[r] + clr[g] + clr[b] ) / 3; ENDCASE; -- ignore other modes RETURN[ clr ]; }; LoadStd8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual] ~ { Linearize: PROC [value: REAL] RETURNS[NAT] ~ { RETURN[ Real.Round[RealFns.Power[value / 255.0, .43] * 255.0] ]; }; IF vt = NIL THEN vt _ Terminal.Current[]; vt.SetColor[0, 0, 0, 0, 0]; vt.SetColor[1, 0, 0, 0, 0]; FOR i: NAT IN [2..254) DO -- 6 x 7 x 6 color cube j: NAT _ i - 2; red: NAT _ Linearize[51.0 * (j/42)]; grn: NAT _ Linearize[42.5 * ((j/6) MOD 7)]; blu: NAT _ Linearize[51.0 * (j MOD 6)]; vt.SetColor[i, 0, red, grn, blu]; ENDLOOP; vt.SetColor[254, 0, 255, 255, 255]; vt.SetColor[255, 0, 255, 255, 255]; }; LoadColorRamp: PUBLIC PROC [vt: Terminal.Virtual, clr1: RGB _ [0,0,0], clr2: RGB _ [255,255,255], exponent: RGB _ [.43,.43,.43] ] ~ { state: Terminal.ColorMode; maxVal: REAL; IF vt = NIL THEN vt _ Terminal.Current[]; state _ vt.GetColorMode[]; maxVal _ IF state.full THEN 255.0 ELSE Real.Float[Basics.BITSHIFT[1, state.bitsPerPixelChannelA] - 1]; clr1.R _ MAX[0.0, MIN[1.0, clr1.R]]; clr2.R _ MAX[0.0, MIN[1.0, clr2.R]]; clr1.G _ MAX[0.0, MIN[1.0, clr1.G]]; clr2.G _ MAX[0.0, MIN[1.0, clr2.G]]; clr1.B _ MAX[0.0, MIN[1.0, clr1.B]]; clr2.B _ MAX[0.0, MIN[1.0, clr2.B]]; FOR i: NAT IN [ 0 .. INTEGER[Real.Fix[maxVal]] ] DO -- linear ramp exponentiated jr: [0..256) _ Real.Fix[ RealFns.Power[clr1.R + i/maxVal * (clr2.R - clr1.R), exponent.R] * maxVal]; jg: [0..256) _ Real.Fix[ RealFns.Power[clr1.G + i/maxVal * (clr2.G - clr1.G), exponent.G] * maxVal]; jb: [0..256) _ Real.Fix[ RealFns.Power[clr1.B + i/maxVal * (clr2.B - clr1.B), exponent.B] * maxVal]; IF Terminal.GetColorMode[vt].full THEN { vt.SetRedMap[i, jr]; vt.SetGreenMap[i, jg]; vt.SetBlueMap[i, jb]; } ELSE vt.SetColor[i, 0, jr, jg, jb]; ENDLOOP; }; FillInBackGround: ContextProc ~ { IF context.viewer # NIL -- do through viewer THEN context.class.drawInViewer[context, NEW[ImagerProcRec _ [ViewerBackFill, NIL]]] ELSE G3dRenderWithPixels.FillInBackGround[context]; -- clear directly }; ViewerBackFill: ImagerProc ~ { -- get pixelmap into context and then do it DoFillInBackGround: PROC[pixelMap: PixelMap] ~ { tempPixels: PixelMap _ context.pixels; tempViewPort: Rectangle _ context.viewPort^; context.pixels _ pixelMap; context.viewPort.x _ context.viewPort.x + pixelMap.box.min.f; -- adjust to device coords context.viewPort.y _ context.viewPort.y + pixelMap.box.min.s; G3dRenderWithPixels.FillInBackGround[context]; context.pixels _ tempPixels; context.viewPort^ _ tempViewPort; }; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoFillInBackGround, context.viewPort^]; }; Draw2DLine: PROC[context: REF Context, p1, p2: Pair, color: Pixel] ~ { mapClr: Pixel _ MappedRGB[context, color]; IF context.viewer # NIL -- do through viewer THEN { data: REF LineDesc _ NEW[LineDesc _ [p1, p2, mapClr] ]; context.class.drawInViewer[context, NEW[ImagerProcRec _ [ViewerLine, data]]]; } ELSE G3dRenderWithPixels.Draw2DLine[context, p1, p2, mapClr]; -- do directly }; LineDesc: TYPE ~ RECORD[p1, p2: Pair, color: Pixel]; ViewerLine: ImagerProc ~ { -- get pixelmap into context and then do it DoDrawLine: PROC[pixelMap: PixelMap] ~ { desc: REF LineDesc _ NARROW[data]; tempPixels: PixelMap _ context.pixels; context.pixels _ pixelMap; G3dRenderWithPixels.Draw2DLine[context, desc.p1, desc.p2, desc.color]; context.pixels _ tempPixels; }; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoDrawLine, context.viewPort^]; }; Draw2DPoly: PROC[context: REF Context, poly: REF PairSequence, color: Pixel] ~ { mapClr: Pixel _ MappedRGB[context, color]; IF context.viewer # NIL -- do through viewer THEN { data: REF PolyDesc _ NEW[PolyDesc _ [poly, mapClr] ]; context.class.drawInViewer[context, NEW[ImagerProcRec _ [Viewer2DPoly, data]]]; } ELSE G3dRenderWithPixels.Draw2DPoly[context, poly, mapClr]; -- do directly }; PolyDesc: TYPE ~ RECORD[poly: REF PairSequence, color: Pixel]; Viewer2DPoly: ImagerProc ~ { -- get pixelmap into context and then do it DoDraw2DPoly: PROC[pixelMap: PixelMap] ~ { desc: REF PolyDesc _ NARROW[data]; tempPixels: PixelMap _ context.pixels; context.pixels _ pixelMap; G3dRenderWithPixels.Draw2DPoly[context, desc.poly, desc.color]; context.pixels _ tempPixels; }; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoDraw2DPoly, context.viewPort^]; }; PolygonTiler: PatchProc ~ { IF context.viewer # NIL -- do through viewer THEN { plyData: REF PolygonDesc _ NEW[PolygonDesc _ [patch, data] ]; context.class.drawInViewer[context, NEW[ImagerProcRec _ [ViewerTiler, plyData]]]; } ELSE { [] _ G3dRenderWithPixels.PolygonTiler[context, patch, data]; -- do directly }; RETURN[NIL]; }; PolygonDesc: TYPE ~ RECORD[patch: REF Patch, data: REF ANY]; ViewerTiler: ImagerProc ~ { -- get pixelmap into context and then do it DoPolygonTiler: PROC[pixelMap: PixelMap] ~ { desc: REF PolygonDesc _ NARROW[data]; tempPixels: PixelMap _ context.pixels; context.pixels _ pixelMap; [] _ G3dRenderWithPixels.PolygonTiler[context, desc.patch, desc.data]; context.pixels _ tempPixels; }; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoPolygonTiler, context.viewPort^]; }; Draw2DRope: PUBLIC RopeProc ~ { IF context.viewer = NIL THEN G3dRenderWithPixels.DrawRope[context, rope, position, color, size, font] ELSE { ropeData: REF RopeDesc _ NEW[ RopeDesc _ [rope, position, color, size, font] ]; context.class.drawInViewer[ context, NEW[ImagerProcRec _ [G3dRenderWithPixels.DoRope, ropeData]] ]; }; }; MakeFrame: PUBLIC ContextProc ~ { refreshProc: CedarProcess.Process _ NIL; tmpViewer: ViewerClasses.Viewer; tmpContext: REF Context; G3dSurfaceRender.ValidateContext[context]; -- ensures viewer, viewPort and pixel store updated IF context.viewer # NIL THEN tmpContext _ SceneUtilities.GetTmpContext[context] -- get modifiable context ELSE tmpContext _ context; -- drawing into memory or directly on display { ENABLE UNWIND => CedarProcess.Abort[refreshProc]; -- in case of aborts, etc. IF NOT tmpContext.doVisibly -- buffering, copy over when done THEN {tmpViewer _ tmpContext.viewer; tmpContext.viewer _ NIL} ELSE IF tmpContext.viewer # NIL AND tmpContext.pixels # NIL AND tmpContext.pixels.samplesPerPixel > 1 THEN { refreshProc _ CedarProcess.Fork[UpdateViewer, context]; -- start refresh tmpViewer _ tmpContext.viewer; tmpContext.viewer _ NIL; }; IF Atom.GetPropFromList[ context.displayProps, $ScreenPixels ] # NIL -- full color, no vwr THEN refreshProc _ CedarProcess.Fork[PeriodicUpdateFullColorDisplay, context]; G3dRenderWithPixels.MakeFrame[tmpContext]; }; IF refreshProc # NIL THEN CedarProcess.Abort[refreshProc]; -- kill periodic update IF NOT tmpContext.doVisibly OR tmpViewer # NIL THEN { tmpContext.viewer _ tmpViewer; context.class.drawInViewer[ tmpContext, NEW[ImagerProcRec _ [StuffBuf, NIL]] ]; }; IF refreshProc # NIL THEN { [] _ CedarProcess.Join[refreshProc]; -- wait for refreshProc done UpdateFullColorDisplay[context]; }; }; StuffBuf: PUBLIC ImagerProc ~ { DoTransfer: PROC[pixelMap: PixelMap] ~ { FOR i: NAT IN [0..samplesPerColor) DO ImagerSample.Transfer[dst: pixelMap[i], src: context.pixels[i], delta: pixelMap.box.min]; ENDLOOP; }; samplesPerColor: NAT _ IF context.class.displayType = $FullColor THEN 3 ELSE 1; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoTransfer, context.viewPort^]; }; MakeHiResFrame: PUBLIC PROC[ context: REF Context, width, height: NAT, name: ROPE, keepLog: BOOLEAN _ TRUE ] ~ { hiResCtxt: REF Context _ SceneUtilities.GetTmpContext[context]; hiResCtxt.viewer _ NIL; hiResCtxt.viewPort _ NEW[ -- set viewport directly to define pixelMap size Imager.Rectangle _ [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ] ]; G3dRenderWithPixels.AllocatePixelMemory[hiResCtxt]; -- get display memory SceneUtilities.SetViewPort[ hiResCtxt, [0.0, 0.0, Real.Float[width], Real.Float[height]] ]; hiResCtxt.class.render _ G3dRenderWithPixels.MakeFrame; -- load direct memory procs hiResCtxt.class.displayPolygon _ G3dRenderWithPixels.PolygonTiler; IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[hiResCtxt]; -- get state straight IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[hiResCtxt]; IF keepLog THEN [] _ SceneUtilities.StartLog[hiResCtxt]; FOR i: NAT IN [0..hiResCtxt.shapes.length) DO hiResCtxt.shapes[i].vtcesInValid _ TRUE; ENDLOOP; hiResCtxt.class.render[hiResCtxt]; AISAnimation.StoreFiles[hiResCtxt, name ]; -- store resulting image }; DitherImage: PUBLIC PROC[dstContext, rgbContext: REF Context] ~ { Action: PROC ~ { width: NAT _ Real.Fix[MIN[dstContext.viewPort.w, rgbContext.viewPort.w] ]; height: NAT _ Real.Fix[MIN[dstContext.viewPort.h, rgbContext.viewPort.h] ]; scanSegIn: PixelBuffer _ ImagerPixel.NewPixels[rgbContext.pixels.samplesPerPixel, width]; scanSegOut: PixelBuffer _ ImagerPixel.NewPixels[dstContext.pixels.samplesPerPixel, width]; IF rgbContext.pixels.samplesPerPixel < 3 THEN SIGNAL G3dRender.Error[[$MisMatch, "24-bit input needed for dithering"]]; FOR y: NAT IN [0..height) DO ImagerPixel.GetPixels[ -- get rgb pixels self: rgbContext.pixels, pixels: scanSegIn, initIndex: [f: 0, s: y], count: width ]; ImagerPixel.GetPixels[ -- get rgb pixels self: dstContext.pixels, pixels: scanSegOut, initIndex: [f: 0, s: y], count: width ]; FOR x: NAT IN [0..width) DO scanSegOut[0][x] _ DitheredRGB[$PseudoColor, x, y, scanSegIn[0][x], scanSegIn[1][x], scanSegIn[2][x] ]; ENDLOOP; ImagerPixel.PutPixels[ -- store result in foreground self: dstContext.pixels, pixels: scanSegOut, initIndex: [f: 0, s: y], count: width ]; ENDLOOP; }; CedarProcess.DoWithPriority[background, Action]; -- be nice to other processess }; ditherTable: ARRAY [0..4) OF ARRAY [0..4) OF NAT _ [[0,12,3,15], [8,4,11,7], [2,14,1,13], [10,6,9,5]]; DitheredRGB: PROC[renderMode: ATOM, x, y, red, grn, blu: INTEGER] RETURNS[INTEGER] ~ { val2R, val2G, val2B, pixelValue: NAT; SELECT renderMode FROM $PseudoColor => { threshold: NAT _ ditherTable[ Basics.BITAND[x,3] ][ Basics.BITAND[y,3] ]; valR: NAT _ Basics.BITSHIFT[ Basics.BITSHIFT[red,2] + red, -4 ]; -- (red * 5) / 16 valG: NAT _ Basics.BITSHIFT[ Basics.BITSHIFT[grn,2] + Basics.BITSHIFT[grn,1], -4 ]; valB: NAT _ Basics.BITSHIFT[ Basics.BITSHIFT[blu,2] + blu, -4 ]; -- (blu * 5) / 16 val2R _ Basics.BITSHIFT[valR,-4]; -- valR / 16 IF Basics.BITAND[valR,15] > threshold THEN val2R _ val2R + 1; -- valr MOD 16 val2G _ Basics.BITSHIFT[valG,-4]; IF Basics.BITAND[valG,15] > threshold THEN val2G _ val2G + 1; val2B _ Basics.BITSHIFT[valB,-4]; IF Basics.BITAND[valB,15] > threshold THEN val2B _ val2B + 1; RETURN[ MIN[ 255, Basics.BITSHIFT[val2R,5] + Basics.BITSHIFT[val2R,3] + Basics.BITSHIFT[val2R,1] + Basics.BITSHIFT[val2G,2] + Basics.BITSHIFT[val2G,1] + val2B + 2 ] ]; --val2R*42 + val2G*6 + val2B + 2 }; $ImagerDithered => { threshold: NAT _ ditherTable[x MOD 4][y MOD 4]; valR: NAT _ 4* red / 16; valG: NAT _ 5* grn / 16; valB: NAT _ 3* blu / 16; val2R _ valR/16; IF valR MOD 16 > threshold THEN val2R _ val2R + 1; val2G _ valG/16; IF valG MOD 16 > threshold THEN val2G _ val2G + 1; val2B _ valB/16; IF valB MOD 16 > threshold THEN val2B _ val2B + 1; pixelValue _ val2R*24 + val2G*4 + val2B; IF pixelValue >= 60 THEN pixelValue _ pixelValue + 135; -- move to top of map RETURN[ MIN[255, pixelValue] ]; }; ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected display type"]]; RETURN[ 255 ]; }; NotRGB: PROC[fileRoot: Rope.ROPE] RETURNS[BOOLEAN] ~ { -- not an RGB file name? cp: FS.ComponentPositions; fullFName, ext: Rope.ROPE; [fullFName, cp, ] _ FS.ExpandName[fileRoot]; ext _ Rope.Substr[ fullFName, cp.ext.start, cp.ext.length]; IF Rope.Equal[ext, "rgb", FALSE] THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; NewFrame: ENTRY Animation3D.ViewProc ~ { ENABLE UNWIND => NULL; filename: ROPE _ NARROW[Atom.GetPropFromList[context.props, $OutputFile]]; G3dRender.SetView[ context, lookingFrom, lookingAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ]; context.class.render[context]; -- make frame IF context.stopMe^ THEN RETURN[]; CedarProcess.CheckAbort[]; -- check for external abort request before proceeding IF filename # NIL THEN -- if storing frames IF context.class.displayType = $FullColor AND NotRGB[filename] THEN { -- dither to 8 bits per pixel for storage savings ditherContext: REF Context _ NARROW[ Atom.GetPropFromList[context.props, $DitherContext] ]; DitherImage[ditherContext, context]; AISAnimation.StoreFiles[ditherContext, filename, frameNo]; } ELSE AISAnimation.StoreFiles[context, filename, frameNo]; }; SetUpAnimation: PROC[ context: REF Context, filename: ROPE _ NIL ] ~ { context.stopMe^ _ FALSE; -- clear stop flag IF context.preferredRenderMode = $Imager OR filename # NIL -- using imager / writing files THEN G3dRenderWithPixels.BufferRendering[context, FALSE]; -- then draw on screen IF filename # NIL THEN { context.props _ Atom.PutPropOnList[context.props, $OutputFile, filename]; IF context.class.displayType = $FullColor THEN IF NotRGB[filename] THEN { ditherContext: REF Context _ G3dRender.Create[]; ditherContext.class _ NEW[ ContextClass _ G3dRender.GetDisplayType[$PseudoColor] ]; G3dSurfaceRender.ValidateContext[context]; -- get everything straight ditherContext.viewPort _ context.viewPort; G3dRenderWithPixels.AllocatePixelMemory[ditherContext]; context.props _ Atom.PutPropOnList[context.props, $DitherContext, ditherContext]; } ELSE { -- file being prepared for Abekas context.viewPort _ NEW[Imager.Rectangle _ [ 0.0, 0.0, 720.0, 486.0 ]]; G3dRenderWithPixels.AllocatePixelMemory[context]; -- get buffer memory of right size SceneUtilities.SetViewPort[ context, [0.0, 0.0, 720.0, 486.0] ]; SceneUtilities.SetWindow[context, [x: -1.0, y: -0.75, w: 2.0, h: 1.5] ]; context.pixelAspectRatio _ (4.0/3.0) / (720.0/486.0); -- pixel width/height }; }; }; FinishAnimation: PROC[ context: REF Context ] ~ { context.props _ Atom.RemPropFromList[context.props, $OutputFile]; context.props _ Atom.RemPropFromList[context.props, $DitherContext]; }; Orbit: PUBLIC PROC[ context: REF Context, lookingFrom, lookingAt, axis, base: Triple, moveEPNotCI: BOOLEAN _ TRUE, framesPerRev: NAT _ 16, startAt: NAT _ 0, endAt: NAT _ 32767, filename: ROPE _ NIL ] ~ { ENABLE UNWIND => NULL; SetUpAnimation[context, filename]; IF moveEPNotCI THEN Animation3D.MoveEyePointInOrbit[ context, lookingFrom, lookingAt, axis, base, NewFrame, framesPerRev, startAt, endAt ] ELSE Animation3D.MoveCtrOfInterestInOrbit[ context, lookingFrom, lookingAt, axis, base, NewFrame, framesPerRev, startAt, endAt ]; FinishAnimation[context]; }; MakeFramesFromTo: PUBLIC PROC[context: REF Context, lookingFrom, lookingAt, toLookingFrom, toLookingAt: Triple, framesOnLine: NAT, startAt, endAt: NAT _ 0, filename: ROPE _ NIL] ~ { ENABLE UNWIND => NULL; SetUpAnimation[context, filename]; IF endAt = 0 THEN endAt _ framesOnLine; -- default is full number of frames Animation3D.MoveOnLine[ context, lookingFrom, lookingAt, toLookingFrom, toLookingAt, NewFrame, framesOnLine, startAt, endAt ]; FinishAnimation[context]; }; TripletoRope: PROC[ r: Triple] RETURNS[ROPE] ~ { rope: ROPE; rope _ Rope.Cat[ " ", Convert.RopeFromReal[r.x], " ", Convert.RopeFromReal[r.y] ]; rope _ Rope.Cat[ rope, " ", Convert.RopeFromReal[r.z], " " ]; RETURN[ rope ]; }; MakeFramesOnPath: PUBLIC PROC[ context: REF Context, lookingFrom, lookingAt: LIST OF Triple, framesOnPath: NAT, startAt, endAt: NAT _ 0, filename: ROPE _ NIL, closed: BOOLEAN _ TRUE ] ~ { ENABLE UNWIND => NULL; ExpandSequence: PROC[seq: REF TripleSequence] ~ { newSeq: REF TripleSequence _ NEW[TripleSequence[seq.length + 64]]; FOR i: NAT IN [0..seq.length) DO newSeq[i] _ seq[i]; ENDLOOP; seq _ newSeq; }; lookingFroms: REF TripleSequence _ NEW[TripleSequence[16]]; lookingAts: REF TripleSequence _ NEW[TripleSequence[16]]; i: NAT _ 0; SetUpAnimation[context, filename]; FOR list: LIST OF Triple _ lookingFrom, list.rest UNTIL list = NIL DO lookingFroms[i] _ list.first; i _ i + 1; IF i >= lookingFroms.maxLength THEN ExpandSequence[lookingFroms]; ENDLOOP; lookingFroms.length _ i; i _ 0; FOR list: LIST OF Triple _ lookingAt, list.rest UNTIL list = NIL DO lookingAts[i] _ list.first; i _ i + 1; IF i >= lookingAts.maxLength THEN ExpandSequence[lookingAts]; ENDLOOP; lookingAts.length _ i; IF endAt = 0 THEN endAt _ framesOnPath; IF closed THEN Animation3D.MoveOnClosedCurve[ context, lookingFroms, lookingAts, NewFrame, framesOnPath, startAt, endAt ] ELSE Animation3D.MoveOnOpenCurve[ context, lookingFroms, lookingAts, NewFrame, framesOnPath, startAt, endAt ]; FinishAnimation[context]; }; Init[]; END. ‚G3dColorDisplayRenderImpl.mesa Last Edited by: Crow, April 4, 1989 9:56:19 am PDT Glassner, March 14, 1989 2:18:27 pm PST Bloomenthal, December 29, 1988 9:06:41 pm PST Types Globals Utility Procedures Updates from buffered pixel map height: NAT _ screenPixels.box.max.s; width: NAT _ screenPixels.box.max.f/2; Read 8 bit samples into red buffer: Read 8 bit samples into green buffer: Now, interleave the red and green 8 bit samples into the 16 bit red/green color display: Write the red, starting at 0, with delta.f = 2, thus writing the even bytes: Write the green, starting at 1, with delta.f = 2, thus writing the odd bytes: PROC [data: REF] RETURNS [results: REF _ NIL] Initialize Standard Displays Establishes PixelMap corresponding to bits on color display. If viewers are being buffered, sets up pixel maps in VM to be blitted across as appropriate. This is called by G3dRender.LoadDisplayType For using color display without viewers Reform screen memory to address by 8-bit chunks reconfigure R&G color display sample maps as a big, double-width 8-bit sample map: First sample map is on-screen, for full-color, first sample map is interleaved R&G, second is off-screen green, third is on-screen blue. Green and R&G and blitted together as necessary Computed by G3dSurfaceRender.ValidateView or ThreeDViewer.GetViewportFromViewer Colors Low-level drawing Put a string of characters on the screen Frame Generation and Animation The following blows up if the viewPort is NIL (As it often can be) !!! Use forked process to copy bits over periodically, too complex to do onscreen If PeriodicUpdateFullColorDisplay, in its final pass, missed some pixels being rendered: Ordered dither for crude looks at full-color Ordered dither for crude looks at full-color using Imager's color map PROC[context: Context, lookingFrom, lookingAt: Triple, frameNo: NAT] Animate along curved path of typed in points ΚΧ˜™J™2Icode™'K™-K™defaultšΟk ˜ Jšœ œœœ˜&Jšœ œ5˜CJšœ œ˜+JšœœR˜fJšœœ ˜ Jšœ œ£˜΄Jšœœ$˜1Jšœœœ˜Jšœ œ˜"Jšœ œœ œ ˜.Jšœ œ˜%Jšœ œ ˜Jšœœ ˜%Jšœ œ˜'Jšœœ˜0Jšœœ‡˜šJšœœS˜fJšœ œψœm˜ψJšœœ5˜JJšœœ˜,JšœœΏ˜ΨJšœœw˜ŠJšœœ˜#Jšœœ˜ ——headšΟlΠklžŸ˜(JšœWœ•˜υJšœ˜J˜Jšœ˜—šž™Jšœœœ˜Lšœ œ ˜0Lšœœ˜$Lšœ œ˜+Lšœ œ˜)Lšœ œ˜-Lšœœ˜Lšœ œ˜'Lšœœ˜"Lšœœ˜0Jšœœ œ˜Jšœœ˜!Jšœœ˜ Jšœœ˜-Jšœœœ œ˜-Jš œœœœ œœ ˜?Jšœ œ˜+Jšœœ˜!Jšœ œ˜(Jšœ œ˜(Jšœ œ˜.Jšœ œ˜(Lšœœ˜/—šž™Lšœœ˜—šž™š Οnœœ œœ œ˜8J˜Jšœœ˜,J˜—š  œœœœœ˜3J˜Jšœœ˜,J˜—š œœ œ ˜7JšœΟsœ‘œ<˜[Jšœœ˜%Jšœœ"˜,J˜KJ˜KJ˜IJ˜)J˜)Jšœ ˜—š œ˜=J™ Lšœ œ œ˜$Jšœ‘œ‘œ<˜[Jšœœ%œ™PJšœœ˜%Jšœœ"˜,Jšœ œ˜$J˜AJ˜AšœΟc;˜AJšœœ(˜0J˜IJ˜Jšœ˜—Jšœ ˜—š œœ˜ Jšœ œ ˜J˜J˜&Jšœ œ˜J˜Jšœœ˜š œœœœ6˜JJ™#˜J˜J—J™%˜J˜J—J™YJ™L˜J˜Y—J™M˜J˜[—Jšœ˜—J˜J˜—š  œ ’*˜VLš œœœ œœ™-Lšœ œ œ˜$Jš œ œœ(œ*œ˜šœœœ’;˜KJšœœ*˜2Jšœ%œœ˜LJ˜Jšœ˜—J˜——šž™š  œ˜Lš’^™^L™=L™,ašœœ˜šœ˜J˜&N˜1N˜—Lšœ’+œ˜O—šœ˜%˜N˜KL˜—˜L˜$L˜—Lšœœ8˜I—Nšœ"’(˜JLšœœ˜L˜—L˜š œœ œ˜3Lš’'™'J˜J˜Jšœ'œ˜+Jšœ œ˜šœœ˜Jšœ+˜/Jšœ˜—šœ˜%J˜#J˜J˜#Jšœœ9˜J—˜Jšœ’’<˜SJšœ’ ˜.Jšœ’H˜WJšœ’E˜VJ˜—šœ˜%˜LšœA’˜PLšœA’ ˜ML˜—˜L˜@—Lšœœ9˜J—Jšœ#’˜;J˜1šœ(œ˜0J™/J˜šœ˜ J™R˜&˜ J˜,J˜J˜-J˜J˜J˜OJ˜—J˜—J˜—L˜[J˜——˜L™ΉLšœœœ’.˜XLšœœœ’-˜WL˜šœœ˜J™O—Lšœœ˜L˜—š œ’˜:šœœœ˜šœA˜FJšœ%œ’˜K—šœœœ$˜>Jšœ*’˜K—Jšœœ’$˜@J˜—šœœ’2˜MLšœœ/˜6šœœ/˜5Lšœ1˜6——L˜—š œœ ’&˜=˜J˜J˜L˜!L˜L˜!L˜L˜L˜J˜J˜—J˜>J˜'J˜—š  œ’+˜Jš  œœ˜*Jšœœ œ˜"J˜DJ˜?J˜J˜—J˜QJ˜—š  œ˜šœœ ’˜7šœ˜Nšœ œœ˜=Nšœ$œ*˜QN˜—šœ˜Lšœ?’˜ML˜——Jšœœ˜ J˜Jš œ œœœœœ˜<—š  œ’+˜Jš œœ˜,Jšœœœ˜%J˜*J˜J˜FJ˜J˜—J˜SJ˜—š  œœ ˜J™(šœ˜JšœI˜Mšœ˜Jšœ œ œ3˜O˜J˜ Jšœ9˜šœ ’1˜ANšœœ œ˜$N˜3N˜J˜$J˜:N˜—Jšœ5˜9——N˜—š  œœ œœœ˜FNšœœ ’˜6šœ'œ ’ ˜ZNšœ.œ’˜Q—šœ œ˜J˜Išœ'˜)šœœ˜šœ˜Nšœœ˜0šœœ˜J˜5J˜—Nšœ-’˜GN˜*N˜7N˜QN˜—šœ’!˜,Nšœœ2˜HNšœ1’#˜TN˜@J˜HNšœ7’˜MN˜———N˜—N˜—š œœ œ˜1J˜AJ˜DN˜—š œœœ œMœœœœ œœœ˜θJšœœœ˜N˜#šœ ˜šœ"˜&N˜-N˜'N˜—šœ'˜+N˜.N˜'N˜——N˜J˜—š œ œ œgœœœœ˜ΡJšœœœ˜N˜#Nšœ œ’#˜MN˜N˜J˜J˜—š  œœ œœ˜0Jšœ˜ J˜SJ˜=Jšœ ˜J˜—J˜š œœœ œ0œœ&œœœœ œœ˜ζN™,Jšœœœ˜š œœœ˜1Jšœœœ"˜BJš œœœœœ˜>J˜ J˜—Nšœœœ˜;Nšœ œœ˜9Nšœœ˜ N˜#š œœœ!œœ˜EN˜+Nšœœ˜ANšœ˜—N˜N˜š œœœœœ˜CN˜)Nšœœ˜=Nšœ˜—N˜Nšœ œ˜'šœ˜ Nšœx˜|Nšœx˜|—N˜J˜J˜—J˜—J˜Jš˜—…—kR‰«