<> <> <> <> <<>> DIRECTORY Atom, Basics, CedarProcess, ColorDisplayManager, ConvertRasterObject, FS, G3dColorDisplaySupport, G3dRender, G3dRenderWithPixels, G3dSortandDisplay, Imager, ImagerBackdoor, ImagerPixel, ImagerSample, IO, Process, Real, RealFns, Rope, SF, Terminal, ViewerClasses; G3dColorDisplaySupportImpl: CEDAR MONITOR IMPORTS Atom, Basics, CedarProcess, ColorDisplayManager, ConvertRasterObject, FS, G3dRender, G3dRenderWithPixels, G3dSortandDisplay, ImagerBackdoor, ImagerPixel, ImagerSample, IO, Process, Real, RealFns, Rope, SF, Terminal EXPORTS G3dColorDisplaySupport ~ BEGIN <> PropList: TYPE ~ Atom.PropList; ROPE: TYPE ~ Rope.ROPE; RopeDesc: TYPE ~ G3dRenderWithPixels.RopeDesc; RopeProc: TYPE ~ G3dRenderWithPixels.RopeProc; Context: TYPE ~ G3dRender.Context; ContextProc: TYPE ~ G3dRender.ContextProc; ImagerProc: TYPE ~ G3dRender.ImagerProc; ContextClass: TYPE ~ G3dRender.ContextClass; DisplayMode: TYPE ~ G3dRender.DisplayMode; Box: TYPE ~ ImagerSample.Box; Rectangle: TYPE ~ G3dRender.Rectangle; Triple: TYPE ~ G3dRender.Triple; TripleSequence: TYPE ~ G3dRender.TripleSequence; RGB: TYPE ~ G3dRender.RGB; Pixel: TYPE ~ G3dRender.Pixel; IntegerPair: TYPE ~ G3dRender.IntegerPair; 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; SampleMap: TYPE ~ ImagerSample.SampleMap; ImagerProcRec: TYPE ~ G3dRender.ImagerProcRec; LORA: TYPE ~ LIST OF REF ANY; <> timeResolution: CARD16 _ 3; <> PasteInLabel: PROC[fileRoot: Rope.ROPE, label: Rope.ROPE] RETURNS[Rope.ROPE] ~ G3dRender.PasteInLabel; GetProp: PROC [propList: PropList, prop: REF ANY] RETURNS [REF ANY] ~ Atom.GetPropFromList; PutProp: PROC [propList: PropList, prop: REF ANY, val: REF ANY] RETURNS [PropList] ~ Atom.PutPropOnList; <> 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: Context] ~ { screenPixels: PixelMap _ NARROW[GetProp[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: Context _ NARROW[data]; screenPixels: PixelMap _ NARROW[GetProp[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: 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: 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 ]; context.viewPort _ NEW[ Rectangle _ [context.viewer.cx, context.viewer.cy, context.viewer.cw, context.viewer.ch] ]; 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: Context ] ~ { <> vt: Terminal.Virtual; box: 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 context.viewPort _ NEW[ Rectangle _ [box.min.f, box.min.s, box.max.f - box.min.f, box.max.s - box.min.s] ]; 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 _ PutProp[context.displayProps, $ScreenPixels, screenPxls]; }; <> IF s2 = NIL THEN context.pixels[0] _ s0; -- on-screen mem (pseudoclr, grey) 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 GetProp[ 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]; } ELSE IF context.viewer = NIL 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.RegisterDisplayClass[ standardClass, $PseudoColor ]; standardClass.displayType _ $FullColor; G3dRender.RegisterDisplayClass[ standardClass, $FullColor ]; standardClass.displayType _ $Gray; G3dRender.RegisterDisplayClass[ standardClass, $Gray ]; }; <> MappedRGB: PUBLIC PROC[context: 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: 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: Context, poly: 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: 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]] ]; }; }; <> 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 G3dRender.Error[ $NotFound, Rope.Cat[" Can't find ", fileRoot, " or with extensions: ", Rope.Cat[names.pref, " ", names.alt, " ", names.other] ] ]; RETURN[ NIL ]; }; }; GetAIS: PUBLIC PROC[context: Context, fileRoot: Rope.ROPE, xOffset, yOffset: INTEGER _ 0, center: BOOLEAN _ TRUE, labeled: BOOLEAN _ FALSE ] 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 _ G3dRender.PrependWorkingDirectory[context, fileRoot]; IF context.pixels = NIL THEN G3dRenderWithPixels.AllocatePixelMemory[ context ]; -- image buffer 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 G3dRender.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] ]; 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 labeled THEN { -- put file name at bottom of display 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 / viewerBox.max.f, 0.02 + 1.0 * intPos.y / viewerBox.max.s ]; context.class.draw2DRope[ context: context, rope: fileName, position: pos, size: 20 * viewerBox.max.f / 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 _ PutProp[context.props, $TempPixels, pixelsFromAIS]; }; GetRGB: PUBLIC PROC[context: Context, fileRoot: Rope.ROPE, xOffset, yOffset: INTEGER _ 0, center: BOOLEAN _ TRUE, labeled: BOOLEAN _ FALSE ] 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 _ G3dRender.PrependWorkingDirectory[context, fileRoot]; <> IF context.pixels = NIL THEN G3dRenderWithPixels.AllocatePixelMemory[ context ]; IF context.class.displayType # $FullColor THEN SIGNAL G3dRender.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 labeled THEN { -- put file name at bottom of display 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 _ PutProp[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^]; }; GetPixelsFromViewer: PUBLIC PROC[ context: Context, dstMap: PixelMap ] ~ { <> context.class.drawInViewer[ context, NEW[ImagerProcRec _ [SnatchPixels, LIST[dstMap]]] ]; }; SnatchPixels: ImagerProc ~ { DoGetPixels: PROC[pixelMap: PixelMap] ~ { dstMap: PixelMap _ NARROW[NARROW[data, LORA].first]; FOR i: NAT IN [0..pixelMap.samplesPerPixel) DO TransferSamples[ dstMap: dstMap[i], srcMap: pixelMap[i], xOffset: 0, yOffset: 0 ]; ENDLOOP; }; ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoGetPixels, 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: 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 _ G3dRender.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 G3dRender.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; }; G3dSortandDisplay.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: Context, fileRoot: Rope.ROPE _ "Temp.rgb" ] ~ { fileRoot _ G3dRender.PrependWorkingDirectory[context, fileRoot]; IF context.class.displayType # $FullColor THEN SIGNAL G3dRender.Error[$MisMatch, "Must be a full-color image"]; G3dSortandDisplay.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: 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]; }; <> MakeFrame: PUBLIC ContextProc ~ { refreshProc: CedarProcess.Process _ NIL; tmpViewer: ViewerClasses.Viewer; tmpContext: Context; <> G3dSortandDisplay.ValidateContext[context]; -- ensures viewer, viewPort and pixel store updated IF context.viewer # NIL THEN tmpContext _ G3dRender.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; tmpContext.pixels _ context.pixels; IF tmpContext.pixels = NIL THEN { G3dRenderWithPixels.AllocatePixelMemory[context]; --gets pixelstore tmpContext.pixels _ context.pixels; }; } 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 GetProp[ 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: Context, width, height: NAT, name: ROPE] ~ { mode: DisplayMode; hiResCtxt: Context; SELECT context.class.displayType FROM $Gray => mode _ gray; $PseudoColor => mode _ dither; ENDCASE => mode _ fullColor; hiResCtxt _ G3dRender.CreateUndisplayedContext[ oldContext: context, width: width, height: height, displayMode: mode, keepLog: TRUE ]; hiResCtxt.viewer _ NIL; G3dRenderWithPixels.AntiAliasing[hiResCtxt]; IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[hiResCtxt]; hiResCtxt.changed _ TRUE; -- ensure screen coordinates get updated hiResCtxt.class.render[hiResCtxt]; G3dRender.StoreImage[hiResCtxt, name ]; -- store resulting image }; DitherImage: PUBLIC PROC[dstContext, rgbContext: 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 ]; }; Init[]; END.