<> <> <> DIRECTORY Basics USING [BITAND, BITOR, BITXOR, LongMult, LongNumber], Buttons USING [ButtonProc, Create], Cursors USING [SetCursor], InterminalBackdoor USING [terminal], ImagerManhattan USING [CreateFromBox, Destroy, Difference, Polygon], ImagerPixelMap USING [BoundedWindow, Clear, Clip, Create, DeviceRectangle, Fill, Intersect, PixelMap, PixelMapRep, Reshape, ShiftMap, Transfer, Window], InputFocus USING [CaptureButtons, ReleaseButtons, SetInputFocus], Terminal USING [ColorCursorBitmapState, FrameBuffer, GetBWCursorPosition, GetBWFrameBuffer, GetColorBitmapState, GetColorCursorPosition, GetColorFrameBufferA, GetColorFrameBufferB, GetColorMode, GetKeys, Position, SetColorCursorState, Virtual, WaitForBWVerticalRetrace], TIPUser USING [InstantiateNewTIPTable, TIPTable], ViewerClasses USING [NotifyProc], ViewerLocks USING [CallUnderViewerTreeLock]; Magnifier: CEDAR PROGRAM IMPORTS Basics, Buttons, Cursors, ImagerManhattan, ImagerPixelMap, InputFocus, Terminal, TIPUser, ViewerLocks, InterminalBackdoor ~ BEGIN PixelMap: TYPE ~ ImagerPixelMap.PixelMap; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Notify: ViewerClasses.NotifyProc = TRUSTED {}; -- NOP myTIPTable: TIPUser.TIPTable _ TIPUser.InstantiateNewTIPTable["Magnifier.tip"]; -- Known NOP hShift: INTEGER _ 0; locking: BOOLEAN _ TRUE; LFDisplay: UNSAFE PROC RETURNS [ImagerPixelMap.PixelMap] ~ UNCHECKED { frameBuffer: Terminal.FrameBuffer ~ Terminal.GetBWFrameBuffer[InterminalBackdoor.terminal]; refRep: REF ImagerPixelMap.PixelMapRep ~ NEW[ImagerPixelMap.PixelMapRep _ [ ref: frameBuffer, pointer: frameBuffer.base, words: LONG[frameBuffer.wordsPerLine]*frameBuffer.height, lgBitsPerPixel: 0, rast: frameBuffer.wordsPerLine, lines: frameBuffer.height ]]; frame: ImagerPixelMap.PixelMap ~ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: frameBuffer.height, fSize: frameBuffer.width, refRep: refRep ]; RETURN [frame]; }; DoIt: PROCEDURE [width: NAT _ 420, height: NAT _ 260] ~ BEGIN vt: Terminal.Virtual _ InterminalBackdoor.terminal; It: PROC ~ { WHILE Terminal.GetKeys[vt][Red] = down DO ENDLOOP; UNTIL Terminal.GetKeys[vt][Red] = down DO mouse: Terminal.Position _ Terminal.GetBWCursorPosition[vt]; IF mouse = [-100, -100] THEN DoColor[width, height] ELSE DoBW[width, height]; ENDLOOP; }; Cursors.SetCursor[textPointer]; IF locking THEN { InputFocus.SetInputFocus[]; InputFocus.CaptureButtons[Notify, myTIPTable]; ViewerLocks.CallUnderViewerTreeLock[It]; InputFocus.ReleaseButtons[]; } ELSE It[]; END; TrimEdge: PROC [r: ImagerManhattan.Polygon] RETURNS [ImagerManhattan.Polygon] ~ { IF r=NIL OR r.rest#NIL OR r.first.sSize <= 2 OR r.first.fSize <= 2 THEN RETURN [r]; RETURN [LIST[[r.first.sMin+1, r.first.fMin+1, r.first.sSize-2, r.first.fSize-2]]] }; MouseOutOfBounds: SIGNAL ~ CODE; DoColor: PROCEDURE [width: NAT, height: NAT] ~ { vt: Terminal.Virtual _ InterminalBackdoor.terminal; BitDouble8: PROC [dest, source: PixelMap] ~ TRUSTED { srcr: DeviceRectangle _ source.BoundedWindow; dstr: DeviceRectangle _ ImagerPixelMap.Intersect[dest.BoundedWindow, [srcr.sMin*2, srcr.fMin*2, srcr.sSize*2, srcr.fSize*2]]; FOR s: INTEGER IN [dstr.sMin..dstr.sMin+dstr.sSize) DO destRow: LONG POINTER TO PACKED ARRAY [0..0) OF [0..256) _ dest.refRep.pointer + Basics.LongMult[(s - dest.sOrigin), dest.refRep.rast]; sourceRow: LONG POINTER TO PACKED ARRAY [0..0) OF [0..256) _ source.refRep.pointer + Basics.LongMult[NAT[s - 2*source.sOrigin]/2, source.refRep.rast]; FOR f: INTEGER IN [dstr.fMin..dstr.fMin+dstr.fSize) DO destRow[f-dest.fOrigin] _ sourceRow[NAT[f-2*source.fOrigin]/2]; ENDLOOP; ENDLOOP; }; BitDouble16: PROC [dest, source: PixelMap] ~ TRUSTED { srcr: DeviceRectangle _ source.BoundedWindow; dstr: DeviceRectangle _ ImagerPixelMap.Intersect[dest.BoundedWindow, [srcr.sMin*2, srcr.fMin*2, srcr.sSize*2, srcr.fSize*2]]; FOR s: INTEGER IN [dstr.sMin..dstr.sMin+dstr.sSize) DO destRow: LONG POINTER TO PACKED ARRAY [0..0) OF CARDINAL _ dest.refRep.pointer + Basics.LongMult[(s - dest.sOrigin), dest.refRep.rast]; sourceRow: LONG POINTER TO PACKED ARRAY [0..0) OF CARDINAL _ source.refRep.pointer + Basics.LongMult[NAT[s - 2*source.sOrigin]/2, source.refRep.rast]; FOR f: INTEGER IN [dstr.fMin..dstr.fMin+dstr.fSize) DO destRow[f-dest.fOrigin] _ sourceRow[NAT[f-2*source.fOrigin]/2]; ENDLOOP; ENDLOOP; }; Shuffle: PROC [ big, small: REF PixelMap, displayBounds: DeviceRectangle, BitDouble: PROC [dest, source: PixelMap], s, f: INTEGER, width, height: NAT ] ~ { r: DeviceRectangle _ small^.Window; IF r # big^.Window THEN ERROR; IF s = r.sMin AND f = r.fMin THEN Terminal.WaitForBWVerticalRetrace[vt] ELSE { newbox: DeviceRectangle _ [s, f, r.sSize, r.fSize]; sDelta: INTEGER _ s - r.sMin; fDelta: INTEGER _ f - r.fMin; old: ImagerManhattan.Polygon _ ImagerManhattan.CreateFromBox[r]; oldBig: PixelMap _ big^; new: ImagerManhattan.Polygon _ ImagerManhattan.CreateFromBox[newbox]; goodBigBits: ImagerManhattan.Polygon _ GoodBigBits[]; GoodBigBits: PROC RETURNS [ImagerManhattan.Polygon] ~ { b: DeviceRectangle _ ImagerPixelMap.Intersect[ [r.sMin+1, r.fMin+1, r.sSize-2, r.fSize-2], displayBounds ]; newb: DeviceRectangle _ b; newb.sMin _ newb.sMin + sDelta + sDelta; newb.fMin _ newb.fMin + fDelta + fDelta; newb _ ImagerPixelMap.Intersect[newb, b]; newb.sMin _ newb.sMin - sDelta; newb.fMin _ newb.fMin - fDelta; RETURN [ImagerManhattan.CreateFromBox[newb]] }; toRestoreFromSmall: ImagerManhattan.Polygon _ old.Difference[new]; toSaveIntoSmall: ImagerManhattan.Polygon _ new.Difference[old]; toRecompute: ImagerManhattan.Polygon _ TrimEdge[new].Difference[goodBigBits]; <> FOR p: LIST OF DeviceRectangle _ toRestoreFromSmall, p.rest UNTIL p=NIL DO big^.Transfer[small^.Clip[p.first]]; ENDLOOP; <> small^.Transfer[small^.ShiftMap[-sDelta, -fDelta]]; small.sOrigin _ small.sOrigin + sDelta; small.fOrigin _ small.fOrigin + fDelta; big.sMin _ big.sMin + sDelta; big.fMin _ big.fMin + fDelta; <> FOR p: LIST OF DeviceRectangle _ toSaveIntoSmall, p.rest UNTIL p=NIL DO small^.Transfer[big^.Clip[p.first]]; ENDLOOP; <> big^.Fill[[newbox.sMin, newbox.fMin, 1, newbox.fSize], 0]; big^.Fill[[newbox.sMin + newbox.sSize - 1, newbox.fMin, 1, newbox.fSize], 0]; big^.Fill[[newbox.sMin, newbox.fMin, newbox.sSize, 1], 0]; big^.Fill[[newbox.sMin, newbox.fMin + newbox.fSize - 1, newbox.sSize, 1], 0]; <> IF goodBigBits # NIL THEN big^.Clip[goodBigBits.first].Transfer[oldBig.ShiftMap[-sDelta, -fDelta]]; <> FOR p: LIST OF DeviceRectangle _ toRecompute, p.rest UNTIL p=NIL DO box: DeviceRectangle _ p.first; centeredBig: PixelMap _ big^.ShiftMap[-newbox.sMin - height/2, -newbox.fMin-width/2]; centeredSmall: PixelMap _ small^.ShiftMap[-newbox.sMin - height/2, -newbox.fMin-width/2]; box.sMin _ box.sMin - newbox.sMin - height/2; box.fMin _ box.fMin - newbox.fMin - width/2; BitDouble[centeredBig.Clip[box], centeredSmall]; ENDLOOP; old.Destroy; new.Destroy; goodBigBits.Destroy; toRestoreFromSmall.Destroy; toRecompute.Destroy; }; }; Do8BitColor: PROCEDURE [] ~ BEGIN oldColorCursorState: Terminal.ColorCursorBitmapState; small: REF PixelMap _ NEW[PixelMap _ ImagerPixelMap.Create[3, [-height, width, height, width]]]; big: REF PixelMap _ NEW[PixelMap]; displayBounds: DeviceRectangle; frameBuffer: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferA[vt]; big^ _ [sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: vt.colorHeight, fSize: vt.colorWidth, refRep: NEW [ImagerPixelMap.PixelMapRep _ [ pointer: frameBuffer.base, ref: frameBuffer.vm, words: LONG[frameBuffer.wordsPerLine]*frameBuffer.height, lgBitsPerPixel: 3, rast: frameBuffer.wordsPerLine, lines: frameBuffer.height ]] ]; displayBounds _ big^.Window; big.sOrigin _ 0; big.sMin _ -height; big.sSize _ height; big.fOrigin _ 0; big.fMin _ width; big.fSize _ width; oldColorCursorState _ vt.SetColorCursorState[invisible]; UNTIL Terminal.GetKeys[vt][Red] = down DO mouse: Terminal.Position _ Terminal.GetColorCursorPosition[vt]; s: INTEGER ~ INTEGER[mouse.y]-height/2; f: INTEGER ~ INTEGER[mouse.x]-width/2 + hShift; Shuffle[big, small, displayBounds, BitDouble8, s, f, width, height]; IF mouse = [-100, -100] THEN EXIT; ENDLOOP; big^.Transfer[small^]; [] _ vt.SetColorCursorState[oldColorCursorState]; END; Do24BitColor: PROCEDURE [] ~ BEGIN height2: NAT _ height/2; width2: NAT _ width/2; oldColorCursorState: Terminal.ColorCursorBitmapState; smallA: REF PixelMap _ NEW[PixelMap _ ImagerPixelMap.Create[4, [-height2, width2, height2, width2]]]; smallB: REF PixelMap _ NEW[PixelMap _ ImagerPixelMap.Create[3, [-height2, width2, height2, width2]]]; bigA: REF PixelMap _ NEW[PixelMap]; bigB: REF PixelMap _ NEW[PixelMap]; displayBounds: DeviceRectangle; A: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferA[vt]; B: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferB[vt]; bigA^ _ [sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: vt.colorHeight, fSize: vt.colorWidth, refRep: NEW [ImagerPixelMap.PixelMapRep _ [ pointer: A.base, ref: A.vm, words: LONG[A.wordsPerLine]*A.height, lgBitsPerPixel: 4, rast: A.wordsPerLine, lines: A.height ]] ]; bigB^ _ [sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: vt.colorHeight, fSize: vt.colorWidth, refRep: NEW [ImagerPixelMap.PixelMapRep _ [ pointer: B.base, ref: B.vm, words: LONG[B.wordsPerLine]*B.height, lgBitsPerPixel: 4, rast: B.wordsPerLine, lines: B.height ]] ]; displayBounds _ bigA^.Window; bigA.sOrigin _ bigB.sOrigin _ 0; bigA.sMin _ bigB.sMin _ -height2; bigA.sSize _ bigB.sSize _ height2; bigA.fOrigin _ bigB.fOrigin _ 0; bigA.fMin _ bigB.fMin _ width2; bigA.fSize _ bigB.fSize _ width2; oldColorCursorState _ vt.SetColorCursorState[invisible]; UNTIL Terminal.GetKeys[vt][Red] = down DO mouse: Terminal.Position _ Terminal.GetColorCursorPosition[vt]; s: INTEGER ~ INTEGER[mouse.y]-height2/2; f: INTEGER ~ INTEGER[mouse.x]-width2/2 + hShift; Shuffle[bigA, smallA, displayBounds, BitDouble16, s, f, width2, height2]; Shuffle[bigB, smallB, displayBounds, BitDouble8, s, f, width2, height2]; IF mouse = [-100, -100] THEN EXIT; ENDLOOP; bigA^.Transfer[smallA^]; bigB^.Transfer[smallB^]; [] _ vt.SetColorCursorState[oldColorCursorState]; END; <
> SELECT TRUE FROM ~vt.hasColorDisplay OR vt.GetColorBitmapState=none => NULL; vt.GetColorMode.full => Do24BitColor[]; ENDCASE => { SELECT vt.GetColorMode.bitsPerPixelChannelA FROM 8 => Do8BitColor[]; ENDCASE; }; }; DoBW: PROCEDURE [width: NAT, height: NAT] ~ BEGIN sTemp: PixelMap _ ImagerPixelMap.Create[0, [0, 0, 100, 16]]; dTemp: PixelMap _ ImagerPixelMap.Create[0, [0, 0, 200, 32]]; BitDouble: PROC [dest, source: PixelMap] ~ { dstr: DeviceRectangle _ dest.BoundedWindow; src: PixelMap _ source.Clip[[(dstr.sMin-1)/2, (dstr.fMin-1)/2, (dstr.sSize+3)/2, (dstr.fSize+3)/2]]; srcr: DeviceRectangle _ src.BoundedWindow; sTemp _ ImagerPixelMap.Reshape[sTemp.refRep, 0, [srcr.sMin, srcr.fMin, srcr.sSize, 16]]; dTemp _ ImagerPixelMap.Reshape[dTemp.refRep, 0, [srcr.sMin*2, srcr.fMin*2, srcr.sSize*2, 32]]; sTemp.fSize _ src.fSize; WHILE sTemp.fSize >= 16 DO sTemp.Transfer[src]; TRUSTED {BitDoubleColumn[dest: dTemp.refRep.pointer, source: sTemp.refRep.pointer, sourceWords: sTemp.sSize]}; dest.Transfer[dTemp]; sTemp.fSize _ sTemp.fSize - 16; sTemp.fOrigin _ sTemp.fOrigin + 16; dTemp.fOrigin _ dTemp.fOrigin + 32; ENDLOOP; IF sTemp.fSize > 0 THEN { sTemp.Clear; sTemp.Transfer[src]; TRUSTED {BitDoubleColumn[dest: dTemp.refRep.pointer, source: sTemp.refRep.pointer, sourceWords: sTemp.sSize]}; dest.Transfer[dTemp]; }; }; vt: Terminal.Virtual _ InterminalBackdoor.terminal; small: PixelMap _ ImagerPixelMap.Create[0, [-height, width, height, width]]; big: PixelMap; displayBounds: DeviceRectangle; TRUSTED {big _ LFDisplay[]}; displayBounds _ big.Window; big.sOrigin _ 0; big.sMin _ -height; big.sSize _ height; big.fOrigin _ 0; big.fMin _ width; big.fSize _ width; UNTIL Terminal.GetKeys[vt][Red] = down DO mouse: Terminal.Position _ Terminal.GetBWCursorPosition[vt]; s: INTEGER ~ INTEGER[mouse.y]-height/2; f: INTEGER ~ INTEGER[mouse.x]-width/2 + hShift; r: DeviceRectangle _ small.Window; IF r # big.Window THEN ERROR; IF s = r.sMin AND f = r.fMin THEN Terminal.WaitForBWVerticalRetrace[vt] ELSE { newbox: DeviceRectangle _ [s, f, r.sSize, r.fSize]; sDelta: INTEGER _ s - r.sMin; fDelta: INTEGER _ f - r.fMin; old: ImagerManhattan.Polygon _ ImagerManhattan.CreateFromBox[r]; oldBig: PixelMap _ big; new: ImagerManhattan.Polygon _ ImagerManhattan.CreateFromBox[newbox]; goodBigBits: ImagerManhattan.Polygon _ GoodBigBits[]; GoodBigBits: PROC RETURNS [ImagerManhattan.Polygon] ~ { b: DeviceRectangle _ ImagerPixelMap.Intersect[ [r.sMin+1, r.fMin+1, r.sSize-2, r.fSize-2], displayBounds ]; newb: DeviceRectangle _ b; newb.sMin _ newb.sMin + sDelta + sDelta; newb.fMin _ newb.fMin + fDelta + fDelta; newb _ ImagerPixelMap.Intersect[newb, b]; newb.sMin _ newb.sMin - sDelta; newb.fMin _ newb.fMin - fDelta; RETURN [ImagerManhattan.CreateFromBox[newb]] }; toRestoreFromSmall: ImagerManhattan.Polygon _ old.Difference[new]; toSaveIntoSmall: ImagerManhattan.Polygon _ new.Difference[old]; toRecompute: ImagerManhattan.Polygon _ TrimEdge[new].Difference[goodBigBits]; <> FOR p: LIST OF DeviceRectangle _ toRestoreFromSmall, p.rest UNTIL p=NIL DO big.Transfer[small.Clip[p.first]]; ENDLOOP; <> small.Transfer[small.ShiftMap[-sDelta, -fDelta]]; small.sOrigin _ small.sOrigin + sDelta; small.fOrigin _ small.fOrigin + fDelta; big.sMin _ big.sMin + sDelta; big.fMin _ big.fMin + fDelta; <> FOR p: LIST OF DeviceRectangle _ toSaveIntoSmall, p.rest UNTIL p=NIL DO small.Transfer[big.Clip[p.first]]; ENDLOOP; <> big.Fill[[newbox.sMin, newbox.fMin, 1, newbox.fSize], 1]; big.Fill[[newbox.sMin + newbox.sSize - 1, newbox.fMin, 1, newbox.fSize], 1]; big.Fill[[newbox.sMin, newbox.fMin, newbox.sSize, 1], 1]; big.Fill[[newbox.sMin, newbox.fMin + newbox.fSize - 1, newbox.sSize, 1], 1]; <> IF goodBigBits # NIL THEN big.Clip[goodBigBits.first].Transfer[oldBig.ShiftMap[-sDelta, -fDelta]]; <> FOR p: LIST OF DeviceRectangle _ toRecompute, p.rest UNTIL p=NIL DO box: DeviceRectangle _ p.first; centeredBig: PixelMap _ big.ShiftMap[-newbox.sMin - height/2, -newbox.fMin-width/2]; centeredSmall: PixelMap _ small.ShiftMap[-newbox.sMin - height/2, -newbox.fMin-width/2]; box.sMin _ box.sMin - newbox.sMin - height/2; box.fMin _ box.fMin - newbox.fMin - width/2; BitDouble[centeredBig.Clip[box], centeredSmall]; ENDLOOP; old.Destroy; new.Destroy; goodBigBits.Destroy; toRestoreFromSmall.Destroy; toRecompute.Destroy; }; IF mouse = [-100, -100] THEN EXIT; ENDLOOP; big.Transfer[small]; END; Sqr: PROC [a: CARDINAL] RETURNS [LONG CARDINAL] ~ INLINE { RETURN [Basics.LongMult[a, a]] }; FourWords: TYPE ~ PACKED ARRAY [0..4) OF CARDINAL; DotStyle: TYPE ~ {normal, light, b1, b2}; dotStyle: DotStyle _ normal; BitDoubleColumn: UNSAFE PROC [dest: LONG POINTER TO FourWords, source: LONG POINTER TO CARDINAL, sourceWords: NAT] ~ UNCHECKED { FOR i: NAT IN [0..sourceWords) DO w,ww: Basics.LongNumber _ [lc[0]]; c: CARDINAL _ source^; WHILE c # 0 DO b: CARDINAL _ c; c _ Basics.BITAND[c, c-1]; w.lc _ w.lc + Sqr[b - c]; ENDLOOP; SELECT dotStyle FROM normal => {w.lc _ ww.lc _ w.lc + w.lc + w.lc}; light => NULL; b1 => { ww.lc _ w.lc + w.lc; ww.highbits _ Basics.BITOR[ww.highbits, 05555H]; ww.lowbits _ Basics.BITOR[ww.lowbits, 05555H] }; b2 => { w.lc _ ww.lc _ w.lc + w.lc + w.lc; ww.highbits _ Basics.BITXOR[ww.highbits, 05555H]; ww.lowbits _ Basics.BITXOR[ww.lowbits, 05555H] }; ENDCASE => ERROR; dest^ _ [w.highbits, w.lowbits, ww.highbits, ww.lowbits]; dest _ dest + SIZE[FourWords]; source _ source + SIZE[CARDINAL]; ENDLOOP; }; Start: Buttons.ButtonProc ~ BEGIN SELECT mouseButton FROM red => DoIt[240, 130]; yellow => DoIt[420, 260]; blue => DoIt[640, 480]; ENDCASE => ERROR; END; [] _ Buttons.Create[[name: "Mag"], Start]; END.