CDVDraw.mesa
Copyright © 1983, 1985 by Xerox Corporation. All rights reserved.
Christian Jacobi, August 5, 1983 11:07 am
last edited by Christian Jacobi, September 4, 1985 8:07:42 pm PDT
DIRECTORY
Basics USING [BITSHIFT, BITAND, LongMult, logBitsPerWord],
CD,
CDInstances,
CDBasics,
CDColors,
CDOps,
CDOrient,
CDPrivate,
CDTexts,
CDValue,
CDVPrivate,
CDVScale,
Imager,
ImagerFont,
ImagerTransformation,
VFonts,
PrincOps USING [BBptr, BitAddress, BBTable, SrcDesc, BitBltFlags, GrayParm],
PrincOpsUtils USING [BITBLT],
Rope,
RuntimeError,
Terminal,
ViewerClasses,
ViewerSpecs;
CDVDraw: CEDAR MONITOR
IMPORTS Basics, CD, CDInstances, CDBasics, CDOps, CDOrient, CDValue, CDVPrivate, CDVScale, Imager, ImagerFont, ImagerTransformation, VFonts, PrincOpsUtils, RuntimeError, Terminal
EXPORTS CDVPrivate, CD --DrawRef's private fields-- =
BEGIN
ViewerPrivateRep: PUBLIC TYPE = CDVPrivate.MyGraphicRec;
ViewerSaveRep: PUBLIC TYPE = INTEGER; -- XXX placeholder
MyGraphicRef: TYPE = CDVPrivate.MyGraphicRef;
MyGraphicRec: TYPE = CDVPrivate.MyGraphicRec;
maxInfluence: CD.Number = 0;
virtual: Terminal.Virtual = Terminal.Current[];
defaultFont: Imager.Font = VFonts.DefaultFont[];
maskModBitsPerWord: CARDINAL = 15;
errrorReport: RECORD [
text: Rope.ROPE,
me: MyGraphicRef,
r: CD.Rect,
color: REF CDColors.Brick
];
bitBltFlagsForPaint: PrincOps.BitBltFlags = PrincOps.BitBltFlags[
direction: forward,
disjoint: TRUE,
disjointItems: TRUE,
gray: TRUE,
srcFunc: null,
dstFunc: or
];
bitBltFlagsForOutline: PrincOps.BitBltFlags = PrincOps.BitBltFlags[
direction: forward,
disjoint: TRUE,
disjointItems: TRUE,
gray: TRUE,
srcFunc: null,
dstFunc: or
];
bitBltFlagsForClearPattern: PrincOps.BitBltFlags = PrincOps.BitBltFlags[
direction: forward,
disjoint: TRUE,
disjointItems: TRUE,
gray: TRUE,
srcFunc: complement,
dstFunc: and
];
blackBrick: REF CDColors.Brick = NEW[CDColors.Brick←ALL[LAST[CARDINAL]]];
whiteBrick: REF CDColors.Brick = NEW[CDColors.Brick←ALL[0]];
BitBlitDraw: PROC[me: MyGraphicRef, r: CD.Rect, color: REF CDColors.Brick] =
-- r ALLREADY CLIPPED
TRUSTED --INLINE compiler overflows-- BEGIN
ENABLE {
UNWIND => NULL;
RuntimeError.UNCAUGHT => {
errrorReport.text ← "BitBlitDraw";
errrorReport.me ← me;
errrorReport.r←r;
errrorReport.color𡤌olor;
IF CDVPrivate.catchAny THEN GOTO SomeError ELSE REJECT;
};
};
xBit, x1, x2, y1, y2: CARDINAL;
vr: CD.Rect;
--DONE OUTSIDE r ← CDBasics.Intersection[r, me.deviceDrawRef.interestClip];
IF ~CDBasics.NonEmpty[r] THEN RETURN;
vr ← CDVScale.DesignToViewerRect[me.scale, r];
x1 ← MAX[vr.x1, 0];
y1 ← MAX[vr.y1, 0];
IF vr.x2<=0 OR vr.y2<=0 THEN RETURN;
x2 ← MIN[vr.x2, me.viewer.cw];  
y2 ← MIN[vr.y2, me.viewer.ch];
IF x1>=x2 OR y1>=y2 THEN RETURN;
xBit ← Basics.BITSHIFT[x1+me.vx, me.logbpp];
me.pBBptr.width ← Basics.BITSHIFT[x2-x1, me.logbpp];
me.pBBptr.height ← y2-y1;
y1 ← me.vy-y2;
me.pBBptr.dst ← [
me.screen
+ Basics.LongMult[y1, me.scWidthWords]
+ LONG[Basics.BITSHIFT[xBit, -Basics.logBitsPerWord]],,
Basics.BITAND[xBit, maskModBitsPerWord]
];
me.pBBptr.src ← [
LOOPHOLE[
LOOPHOLE[color, LONG CARDINAL] + Basics.BITAND[y1, 3B], LONG POINTER],,
Basics.BITAND[xBit, maskModBitsPerWord]
];
me.pBBptr.srcDesc.gray.yOffset ← Basics.BITAND[y1, 3B];
IF me.display=bw THEN PrincOpsUtils.BITBLT[me.pBBptr]
ELSE {
Blit: PROC [] =
TRUSTED BEGIN
PrincOpsUtils.BITBLT[me.pBBptr];
END;
Terminal.ModifyColorFrame[vt: virtual,
action: Blit,
xmin: x1+me.vx,
ymin: y1,
xmax: x2+me.vx,
ymax: y1+me.pBBptr.height
];
};
EXITS
SomeError => NULL;
END;
BitBlitOutLine: ENTRY PROC[r: CD.Rect, pr: CD.DrawRef] =
BEGIN
ENABLE {
UNWIND => NULL;
RuntimeError.UNCAUGHT => {
errrorReport.text ← "BitBlitOutLine";
errrorReport.me ← pr.viewerPrivate;
errrorReport.r ← r;
IF CDVPrivate.catchAny THEN GOTO SomeError ELSE REJECT;
};
};
me: MyGraphicRef = pr.viewerPrivate;
vr, clipr: CD.Rect;
DrawBlack: PROC[me: MyGraphicRef, x1, y1, x2, y2: INTEGER] =
--draw exclusive high border pixels
TRUSTED INLINE BEGIN
--no empty test; is guaranteed by caller
xBits: CARDINAL = Basics.BITSHIFT[x1+INTEGER[me.vx], me.logbpp];
me.xBBptr.width ← Basics.BITSHIFT[x2-x1, me.logbpp];
me.xBBptr.height ← y2-y1;
me.xBBptr.dst ← [
me.screen
+ Basics.LongMult[INTEGER[me.vy]-y2, me.scWidthWords]
+ LONG[Basics.BITSHIFT[xBits, -Basics.logBitsPerWord]],,
Basics.BITAND[xBits, maskModBitsPerWord]
];
PrincOpsUtils.BITBLT[me.xBBptr];
END;
DoIt: PROC [] =
BEGIN
IF vr.x1>=clipr.x1 THEN DrawBlack[me, clipr.x1, clipr.y1, clipr.x1+1, clipr.y2]; --left
IF vr.y2<=clipr.y2 THEN DrawBlack[me, clipr.x1, clipr.y2-1, clipr.x2, clipr.y2]; --top
IF vr.x2<=clipr.x2 THEN DrawBlack[me, clipr.x2-1, clipr.y1, clipr.x2, clipr.y2]; --right
IF vr.y1>=clipr.y1 THEN DrawBlack[me, clipr.x1, clipr.y1, clipr.x2, clipr.y1+1]; --bottom
END;
--clip to area where
--1) small enough: it does not crash on scaling later
--2) big enogh: if drawn, it does not draw artificial lines
IF r.x2>me.dClip.x2 THEN r.x2 ← me.dClip.x2+1;
IF r.y2>me.dClip.y2 THEN r.y2 ← me.dClip.y2+1;
IF r.x1<me.dClip.x1 THEN r.x1 ← me.dClip.x1-1;
IF r.y1<me.dClip.y1 THEN r.y1 ← me.dClip.y1-1;
vr ← CDVScale.DesignToViewerRect[me.scale, r];
clipr ← CDBasics.Intersection[
CDVScale.DesignToViewerRect[me.scale, me.deviceDrawRef.interestClip],
vr
];
clipr.x1 ← MAX[clipr.x1, 0];
clipr.y1 ← MAX[clipr.y1, 0];
clipr.x2 ← MIN[clipr.x2, LONG[me.viewer.cw]];
clipr.y2 ← MIN[clipr.y2, LONG[me.viewer.ch]];
IF clipr.x1>=clipr.x2 OR clipr.y1>=clipr.y2 THEN RETURN;
--harder empty test than CDBasics
--necessary because we add/subtract one
TRUSTED {
me.xBBptr.src ← [LOOPHOLE[blackBrick, LONG POINTER],,0];
me.xBBptr.flags ← bitBltFlagsForOutline;
me.xBBptr.srcDesc ← PrincOps.SrcDesc[gray[PrincOps.GrayParm[
yOffset: 0,
widthMinusOne: 0, --words
heightMinusOne: 0 --lines
]]];
};
IF me.display=bw THEN {
DoIt[];
IF vr.x1>=clipr.x1 THEN DrawBlack[me, clipr.x1, clipr.y1, clipr.x1+1, clipr.y2]; --left
IF vr.y2<=clipr.y2 THEN DrawBlack[me, clipr.x1, clipr.y2-1, clipr.x2, clipr.y2]; --top
IF vr.x2<=clipr.x2 THEN DrawBlack[me, clipr.x2-1, clipr.y1, clipr.x2, clipr.y2]; --right
IF vr.y1>=clipr.y1 THEN DrawBlack[me, clipr.x1, clipr.y1, clipr.x2, clipr.y1+1]; --bottom
}
ELSE {
Terminal.ModifyColorFrame[vt: virtual,
action: DoIt,
xmin: clipr.x1+me.vx,
ymin: me.vy+1-clipr.y2,
xmax: clipr.x2+me.vx-1,
ymax: me.vy-clipr.y1
];
};
EXITS
SomeError => NULL;
END;
BitBlitDrawRectForViewers: ENTRY PROC[r: CD.Rect, l: CD.Layer, pr: CD.DrawRef] =
BEGIN
ENABLE UNWIND => NULL;
me: MyGraphicRef = pr.viewerPrivate;
BitBlitDraw[me, CDBasics.Intersection[r, pr.interestClip], me.colorTable[l]];
END;
SetGround: --CD.SetGroundProc-- PROC [pr: CD.DrawRef, pushedOut: BOOL] =
BEGIN
WITH pr.devicePrivate SELECT FROM
me: MyGraphicRef =>
me.colorTable ← me.personalColors[me.display][IF pushedOut THEN pushedOut ELSE normal]
ENDCASE => NULL;
END;
DrawCommentForViewers: PUBLIC PROC[r: CD.Rect, comment: Rope.ROPE, pr: CD.DrawRef] =
BEGIN
topToFontLine: NAT = 9+2;
fontHeight: NAT = 10;
leftMargin: NAT = 2;
bothMargin: NAT = 2*leftMargin;
me: MyGraphicRef ← NARROW[pr.devicePrivate];
vr: CD.Rect ← CDVScale.DesignToViewerRect[me.scale, r];
IF vr.y2-vr.y1>fontHeight THEN {
xw: REAL = ImagerFont.RopeWidth[font: defaultFont, rope: comment].x;
IF vr.x2-vr.x1>xw+bothMargin THEN {
Imager.SetColor[me.viewContext, Imager.black];
Imager.SetFont[me.viewContext, defaultFont];
Imager.SetXY[me.viewContext, [vr.x1+leftMargin, vr.y2-topToFontLine]];
Imager.ShowRope[me.viewContext, comment];
}
};
END;
RepaintRectAreaInViewer: PUBLIC PROC[me: MyGraphicRef, rect: CD.Rect, eraseFirst: BOOL] =
BEGIN
ENABLE {
RuntimeError.UNCAUGHT => IF CDVPrivate.catchAny THEN GOTO SomeError ELSE REJECT
};
interestRect: CD.Rect;
UpdateDrawInformation[me];
me.deviceDrawRef.interestClip ← interestRect ← CDBasics.Intersection[rect, me.dClip];
IF CDBasics.NonEmpty[interestRect] THEN {
RepaintBackground[me, interestRect, eraseFirst];
CDOps.QuickDrawDesign[me.actualDesign, me.deviceDrawRef];
FOR pl: CDVPrivate.PainterList ← me.painterList, pl.rest WHILE pl#NIL DO
IF CDBasics.Intersect[interestRect, pl.first.rect] THEN
pl.first.proc[me, pl.first, interestRect !
RuntimeError.UNCAUGHT => IF CDVPrivate.catchAny THEN CONTINUE ELSE REJECT
]
ENDLOOP
}
EXITS SomeError=> NULL
END;
RepaintBackground: PUBLIC ENTRY PROC[me: MyGraphicRef, r: CD.Rect, eraseFirst: BOOL] =
BEGIN
ENABLE {
UNWIND => NULL;
RuntimeError.UNCAUGHT => {
errrorReport.text ← "background";
errrorReport.me ← me;
errrorReport.r ← r;
IF CDVPrivate.catchAny THEN GOTO SomeError ELSE REJECT
};
};
DrawOutside: PROC [r: CD.Rect] =
BEGIN
BitBlitDraw[me, CDBasics.Intersection[r, me.dClip], me.colorTable[CD.backGround]];
END;
--RepaintBackground
IF eraseFirst THEN {
TRUSTED {me.pBBptr.flags.dstFunc ← null};
BitBlitDraw[me, CDBasics.Intersection[r, me.dClip], whiteBrick];
TRUSTED {me.pBBptr.flags.dstFunc ← or};
};
IF me.actualDesign.actual.first.mightReplace#NIL AND me.environment AND
me.actualDesign.actual.first.mightReplace.ob#NIL THEN
CDBasics.DecomposeRect[r: r,
test: CDInstances.InstRectO[me.actualDesign.actual.first.mightReplace],
outside: DrawOutside
];
EXITS
SomeError => NULL;
END;
InitForBBLT: PROC [me: MyGraphicRef] =
BEGIN
IF me.viewer.column=color THEN TRUSTED {
m: Terminal.ColorMode = Terminal.GetColorMode[virtual];
IF m.full OR m.bitsPerPixelChannelA=0 THEN ERROR CDVPrivate.notSupportedColorMode;
me.bpp ← m.bitsPerPixelChannelA;
me.frame ← Terminal.GetColorFrameBufferA[virtual];
IF me.bpp=8 THEN {
me.display ← bit8;
me.logbpp ← 3;
}
ELSE IF me.bpp=4 THEN {
me.display ← bit4;
me.logbpp ← 2;
}
ELSE IF me.bpp=2 THEN {
me.display ← bit2;
me.logbpp ← 1;
}
ELSE IF me.bpp=1 THEN {
me.display ← bit1;
me.logbpp ← 0;
}
ELSE IF me.bpp=16 THEN {
--ERROR, but we try to avoid crashing...
me.display ← bit1;
me.logbpp ← 4;
}
ELSE ERROR;
}
ELSE { -- b+w
me.frame ← Terminal.GetBWFrameBuffer[virtual];
me.display ← bw;
me.logbpp ← 0;
};
me.bpp ← me.frame.bitsPerPixel;
me.screen ← me.frame.base;
me.colorTable ← me.personalColors[me.display][normal];
-- me.vx ← distance from left of screen to left most pixel
-- me.vy ← distance from top of screen to bottom most pixel
-- in device in pixels
me.vx ← me.viewer.cx+me.viewer.wx;
me.vy ← me.frame.height-(me.viewer.cy+me.viewer.wy);
me.scWidthWords ← me.frame.wordsPerLine;
----------
--fixed pBBptr initializations
--done in viewerprocess: me.pBBptr ← PrincOpsUtils.AlignedBBTable[@bBTableSpace];
TRUSTED BEGIN
me.pBBptr^ ← PrincOps.BBTable[
dst: TRASH,
dstBpl: me.frame.width*me.frame.bitsPerPixel,
src: TRASH,
srcDesc: PrincOps.SrcDesc[gray[PrincOps.GrayParm[
yOffset: 0, --is actually trash
widthMinusOne: 0, --words
heightMinusOne: 3 --lines
]]],
width: TRASH,
height: TRASH,
flags: bitBltFlagsForPaint
];
me.xBBptr^ ← me.pBBptr^;
END;
END;
SetupContext: PROC [pr: CD.DrawRef, me: MyGraphicRef] =
BEGIN
s: REAL;
pr.deviceContext ← me.viewContext;
s ← CDVScale.DesignToViewerFactor[me.scale];
pr.scaleHint ← s*me.suppressFactorForCells;
Imager.SetColor[me.viewContext, Imager.black];
Imager.SetFont[me.viewContext, defaultFont];
pr.b1 ← me.b1;
pr.b2 ← me.b2;
END;
ViewerDrawContext: PROC [pr: CD.DrawRef, proc: CD.DrawContextLayerProc, ob: CD.Object, pos: CD.Position, orient: CD.Orientation, layer: CD.Layer] =
--calls proc which may use context; mode and color are set to layer's need
--call is suppressed if layer does not need drawing; this is default.
--on recursive calls, the context may or may not include previous transformations
BEGIN
IF pr.deviceContext#NIL AND pr.contextFilter#NIL AND pr.contextFilter[layer].doit THEN {
CreateTransform: PROC [
cellSize: CD.Position,
cellInstOrient: CD.Orientation,
cellInstPos: CD.Position ← [0,0]]
RETURNS [Imager.Transformation] =
-- Given the size of a cell,
-- the position and orientation of an instance of that cell in "world"
-- co-ordinates, returns the transformation to be applied to
-- cell points to get world points.
INLINE BEGIN
RETURN [SELECT cellInstOrient FROM
--original--0 => ImagerTransformation.Create[1, 0, cellInstPos.x, 0, 1, cellInstPos.y],
1 =>   ImagerTransformation.Create[-1, 0, cellInstPos.x+cellSize.x, 0, 1, cellInstPos.y], -- reflection in x
--rotate90-- 2 => ImagerTransformation.Create[0, -1, cellInstPos.x+cellSize.y, 1, 0, cellInstPos.y], -- 90 degrees clockwise
3 =>   ImagerTransformation.Create[0, 1, cellInstPos.x, 1, 0, cellInstPos.y], -- 90 degrees clockwise followed by reflection in x
--rotate180-- 4 => ImagerTransformation.Create[-1, 0, cellInstPos.x+cellSize.x, 0, -1, cellInstPos.y+cellSize.y], -- 180 degrees clockwise
5 =>   ImagerTransformation.Create[1, 0, cellInstPos.x, 0, -1, cellInstPos.y+cellSize.y], -- 180 degrees clockwise followed by reflection in x
--rotate270-- 6 => ImagerTransformation.Create[0, 1, cellInstPos.x, -1, 0, cellInstPos.y+cellSize.x], -- 270 degrees clockwise
7 =>   ImagerTransformation.Create[0, -1, cellInstPos.x+cellSize.y, -1, 0, cellInstPos.y+cellSize.x], -- 270 degrees clockwise followed by reflection in x
ENDCASE => ERROR
];
END;
Action: PROC [] =
BEGIN
me: MyGraphicRef = NARROW[pr.viewerPrivate];
Imager.ScaleT[pr.deviceContext, CDVScale.DesignToViewerFactor[me.scale]];
Imager.TranslateT[pr.deviceContext, [-me.scale.off.x, -me.scale.off.y]];
IF ob#NIL THEN {
IF orient=CD.original THEN Imager.TranslateT[pr.deviceContext, [pos.x, pos.y]]
ELSE Imager.ConcatT[pr.deviceContext, CreateTransform[
cellSize: ob.size,
cellInstOrient: orient,
cellInstPos: pos
]];
--old, slower version
Imager.TranslateT[pr.deviceContext, [pos.x, pos.y]];
CDOrient.OrientateContext[pr.deviceContext, ob.size, orient];
};
Imager.SetColor[pr.deviceContext, pr.contextFilter[layer].color];
proc[pr.deviceContext, ob, layer];
END;
Imager.DoSave[pr.deviceContext, Action];
}
END;
InternalCreateDrawInformation: INTERNAL PROC [me: MyGraphicRef] =
BEGIN
pr: CD.DrawRef = CD.CreateDrawRef[me.actualDesign];
me.deviceDrawRef ← pr;
pr.contextFilter ← me.contextFilter ← GetContextFilter[me];
pr.drawRect ← BitBlitDrawRectForViewers;
pr.drawOutLine ← BitBlitOutLine;
pr.drawComment ← DrawCommentForViewers;
pr.drawContext ← ViewerDrawContext;
pr.setGround ← SetGround;
pr.devicePrivate ← me;
pr.stopFlag ← me.stoprequest;
pr.symbolics ← me.symbolics;
pr.environment ← me.environment;
pr.interestClip ← me.dClip ← CDVScale.GetClipRecord[me.scale, me.viewer.cw, me.viewer.ch];
pr.minimalSize ← --XXX--
MIN[(me.dClip.x2-me.dClip.x1)/8, (me.dClip.y2-me.dClip.y1)/8];
InitForBBLT[me];
pr.viewerPrivate ← me; -- this line last! it tells DummyNotify that the rest is initialized
END;
PROCEDURE NOT USED because Qickdraw gives us no chance
NamedDrawChild: PROC [inst: CD.Instance, pos: CD.Position, orient: CD.Orientation,
pr: CD.DrawRef] =
BEGIN
Comment: PROC [comment: Rope.ROPE] =
BEGIN
me: MyGraphicRef ← NARROW[pr.devicePrivate];
vpos: CD.Position = CDVScale.DesignToViewerPosition[me.scale, pos];
pr.deviceContext.SetColor[Imager.black];
pr.deviceContext.SetCP[vpos.x+1, vpos.y+1];
pr.deviceContext.DrawRope[comment];
END;
inst.ob.p.drawMe[inst, pos, orient, pr];
IF pr.b2 THEN {
WITH CDProperties.GetPropFromInstance[from: inst, prop: $SignalName] SELECT FROM
r: Rope.ROPE => Comment[r];
a: ATOM => Comment[Atom.GetPName[a]];
ENDCASE => NULL;
};
END;
UpdateDrawInformation: ENTRY PROC [me: MyGraphicRef] =
BEGIN
ENABLE UNWIND => NULL;
IF me.deviceDrawRef=NIL THEN
InternalCreateDrawInformation[me];
SetupContext[me.deviceDrawRef, me];
END;
CreateDrawInformation: PUBLIC ENTRY PROC [me: MyGraphicRef] =
BEGIN
ENABLE UNWIND => NULL;
InternalCreateDrawInformation[me];
SetupContext[me.deviceDrawRef, me];
END;
GetContextFilter: PROC [me: MyGraphicRef] RETURNS [REF CD.ContextFilter] =
BEGIN
WITH CDValue.Fetch[boundTo: me.actualDesign.technology, key: $ContextFilter, propagation: global] SELECT FROM
rcf: REF CD.ContextFilter => RETURN [rcf];
ENDCASE => RETURN [defaultContextFilter]
END;
defaultContextFilter: REF CD.ContextFilter = NEW[CD.ContextFilter];
FOR l: CD.Layer IN CD.Layer DO
defaultContextFilter[l].color ← Imager.black;
defaultContextFilter[l].doit ← TRUE;
ENDLOOP;
defaultContextFilter[CD.backGround].doit ← FALSE;
CDValue.EnregisterKey[$ContextFilter];
CDValue.Store[NIL, $ContextFilter, defaultContextFilter];
END.