ViewerBLTImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Doug Wyatt, April 24, 1985 10:48:26 pm PST
Russ Atkinson (RRA) June 10, 1985 8:13:36 pm PDT
DIRECTORY
Carets USING [ResumeCarets, SuspendCarets],
Imager USING [black, Color, Context, MaskRectangleI, SetColor, white],
ImagerBackdoor USING [MoveViewRectangle],
Menus USING [ChangeNumberOfLines],
ViewerBLT USING [],
ViewerClasses USING [Column, HBltRule, PaintProc, PaintRectangle, PaintRectangleRec, VBltRule, Viewer],
ViewerForkers USING [ForkCall, ForkPaint],
ViewerLocks USING [CallUnderWriteLock],
ViewerOps USING [EstablishViewerPosition, FetchViewerClass, PaintViewer, UserToScreenCoords],
ViewerPrivate USING [GreyScreen, PaintScreen, rootViewerTree, Screen, ViewerScreen],
ViewerSpecs USING [captionHeight, colorScreenHeight, colorScreenWidth, openBottomY, openLeftLeftX, openLeftWidth, openRightLeftX, openRightWidth, openTopY, windowBorderSize];
ViewerBLTImpl: CEDAR PROGRAM
IMPORTS Carets, Imager, ImagerBackdoor, Menus, ViewerForkers, ViewerLocks, ViewerOps, ViewerPrivate, ViewerSpecs
EXPORTS ViewerBLT
SHARES Menus, ViewerOps, ViewerClasses
= BEGIN OPEN ViewerClasses, ViewerSpecs;
disable: BOOLFALSE;
ChangeNumberOfLines: PUBLIC PROC[viewer: Viewer, newLines: [0..5)] = {
LockedChangeLines: PROC = {
oldBox, newBox: Box;
IF viewer.menu = NIL THEN RETURN;
oldBox ← [viewer.wx+viewer.cx, viewer.wy+viewer.cy, viewer.cw, viewer.ch];
Menus.ChangeNumberOfLines[viewer.menu, newLines];
ViewerOps.EstablishViewerPosition[viewer, viewer.wx, viewer.wy, viewer.ww, viewer.wh];
newBox ← [viewer.wx+viewer.cx, viewer.wy+viewer.cy, viewer.cw, viewer.ch];
BLTViewer[viewer, oldBox, newBox];
};
ViewerLocks.CallUnderWriteLock[LockedChangeLines, viewer];
};
InternalPaintColumn: PUBLIC PROC [column: Column, paintIcons: BOOLTRUE] = {
oldIndex: CARDINAL;
oldSnapshot: ColumnSnapshot;
newSnapshot: ColumnSnapshot;
invalidTop, invalidBottom: INTEGER;
IF column = static THEN {
FOR v: Viewer ← ViewerPrivate.rootViewerTree[column], v.sibling UNTIL v=NIL DO
IF ~v.offDeskTop THEN ForkPaintViewerAll[v];
ENDLOOP;
RETURN
};
Carets.SuspendCarets[];
{
ENABLE UNWIND => Carets.ResumeCarets[];
IF ViewerPrivate.rootViewerTree[column] = NIL THEN {
height, leftX, width, bottomY: INTEGER;
[height, leftX, width, bottomY, ] ← ColumnInfo[column];
ViewerPrivate.GreyScreen[IF column=color THEN color ELSE bw, leftX, bottomY, width, height];
ClearSnapshot[snapshots[column].before];
IF paintIcons AND column # color THEN -- paint newly visible icons
FOR v: Viewer ← ViewerPrivate.rootViewerTree[static], v.sibling UNTIL v=NIL DO
IF ~v.iconic OR v.offDeskTop THEN LOOP;
IF v.wy + v.wh < bottomY THEN LOOP;
IF v.wx + v.ww < leftX THEN LOOP;
IF v.wx > leftX + width THEN LOOP;
ForkPaintViewerAll[v];
ENDLOOP;
GO TO done;
};
oldSnapshot ← snapshots[column].before;
newSnapshot ← SnapshotColumn[column];
snapshots[column].before ← newSnapshot;
snapshots[column].after ← oldSnapshot;
IF oldSnapshot = NIL OR disable THEN {
FOR v: Viewer ← ViewerPrivate.rootViewerTree[column], v.sibling UNTIL v=NIL DO
ForkPaintViewerAll[v];
ENDLOOP;
GO TO done;
};
try to save cycles by BLTing bit maps around.
this algorithm assumes that the column is almost in the same order as it was before, except for creating, deleting, or moving at most one viewer.
the algorithm should never blt an invalid bitmap.
invalidTop ← -1; -- top of section that has been blitted over
invalidBottom ← 1000; -- bottom of section that has been blitted over
iterate from bottom to top of column, BLTing bit maps if the space is free
FOR i: CARDINAL IN [0..newSnapshot.size) DO
IF newSnapshot[i].viewer = NIL THEN EXIT;
oldIndex ← GetIndex[oldSnapshot, newSnapshot[i].viewer];
IF oldIndex = oldSnapshot.size THEN LOOP; -- a new viewer
check to see if the bitmap is invalid
IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom
AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP;
check to see if BLTing would wipe out a bitmap above
IF newSnapshot[i].box.y + newSnapshot[i].box.h >
oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h THEN LOOP;
IF newSnapshot[i].box # oldSnapshot[oldIndex].box THEN {
BLTViewer[newSnapshot[i].viewer, oldSnapshot[oldIndex].box, newSnapshot[i].box];
invalidTop ← MAX[invalidTop, newSnapshot[i].box.y + newSnapshot[i].box.h];
invalidBottom ← MIN[invalidBottom, newSnapshot[i].box.y];
};
newSnapshot[i].painted ← TRUE;
ENDLOOP;
now do it from top to bottom, catching the ones we couldn't do before
FOR i: CARDINAL DECREASING IN [0..newSnapshot.size) DO
IF newSnapshot[i].viewer = NIL THEN LOOP;
IF newSnapshot[i].painted THEN LOOP;
oldIndex ← GetIndex[oldSnapshot, newSnapshot[i].viewer];
IF oldIndex = oldSnapshot.size THEN LOOP; -- a new viewer
check to see if the bitmap is invalid
IF oldSnapshot[oldIndex].box.y + oldSnapshot[oldIndex].box.h > invalidBottom
AND oldSnapshot[oldIndex].box.y < invalidTop THEN LOOP;
check to see if BLTing would wipe out a bitmap below
IF newSnapshot[1].viewer # NIL AND -- more than one viewer in column
newSnapshot[i].box.y < oldSnapshot[oldIndex].box.y THEN LOOP;
IF newSnapshot[i].box # oldSnapshot[oldIndex].box THEN {
BLTViewer[newSnapshot[i].viewer, oldSnapshot[oldIndex].box, newSnapshot[i].box];
invalidTop ← MAX[invalidTop, newSnapshot[i].box.y + newSnapshot[i].box.h];
invalidBottom ← MIN[invalidBottom, newSnapshot[i].box.y];
};
newSnapshot[i].painted ← TRUE;
ENDLOOP;
FOR i: CARDINAL IN [0..newSnapshot.size) DO
IF newSnapshot[i].viewer = NIL THEN EXIT;
IF ~newSnapshot[i].painted THEN ForkPaintViewerAll[newSnapshot[i].viewer];
ENDLOOP;
EXITS done => {};
};
Carets.ResumeCarets[];
};
BLTViewer: PROC [viewer: Viewer, oldBox, newBox: Box] = {
rect: PaintRectangle;
hBLT: HBltRule;
vBLT: VBltRule;
w, h, header: INTEGER;
fromX, fromY, toX, toY: INTEGER;
wbs: INTEGERIF viewer.border THEN windowBorderSize ELSE 0;
header ← viewer.wh - (viewer.cy + viewer.ch);
assume that the menu is the same for before and after
w ← MIN[oldBox.w, newBox.w];
h ← MIN[oldBox.h, newBox.h];
hBLT ← IF oldBox.w = newBox.w THEN left ELSE viewer.class.bltH;
SELECT hBLT FROM
left, center, right => {
later we will distinguish between left, right and center
IF oldBox.w # newBox.w THEN w ← w - wbs; -- don't include right border
toX ← newBox.x;
fromX ← oldBox.x;
};
ENDCASE => {ForkPaintViewerAll[viewer]; RETURN};
vBLT ← IF oldBox.h = newBox.h THEN top ELSE viewer.class.bltV;
SELECT vBLT FROM
top => {
IF oldBox.h # newBox.h THEN h ← h - wbs; -- don't include bottom border
fromY ← oldBox.y + oldBox.h - h;
toY ← newBox.y + newBox.h - h;
};
bottom => {
h ← h - header; -- don't include top header
fromY ← oldBox.y;
toY ← newBox.y;
};
center => {
fromY ← oldBox.y + wbs + oldBox.h/2 - h/2;
toY ← newBox.y + wbs + newBox.h/2 - h/2;
h ← h - header - wbs; -- don't include top or bottom
};
ENDCASE => {ForkPaintViewerAll[viewer]; RETURN};
{ OPEN viewer;
action: PROC [context: Imager.Context] ~ {
rect ← NEW[PaintRectangleRec ← [x: toX, y: toY, w: w, h: h]];
IF fromX # toX OR fromY # toY AND w >=0 AND h >= 0
THEN ImagerBackdoor.MoveViewRectangle[context: context,
width: w, height: h, fromX: fromX, fromY: fromY, toX: toX, toY: toY];
-- erase remainder
Imager.SetColor[context, Imager.white];
IF wy < rect.y THEN -- some on the bottom
Imager.MaskRectangleI[context, wx, wy, ww, rect.y - wy];
IF rect.h >=0 AND wy+wh > rect.y+rect.h THEN -- some on the top
Imager.MaskRectangleI[context, wx, rect.y+rect.h, ww, (wy+wh) - (rect.y+rect.h)];
IF wx < rect.x THEN -- some on the left
Imager.MaskRectangleI[context, wx, wy, rect.x - wx, wh];
IF rect.w >=0 AND wx+ww > rect.x+rect.w THEN -- some on the right
Imager.MaskRectangleI[context, rect.x+rect.w, wy, (wx+ww) - (rect.x+rect.w), wh];
Imager.SetColor[context, Imager.black];
IF viewer.border AND (oldBox.w # newBox.w OR oldBox.h # newBox.h) THEN {
Imager.MaskRectangleI[context, wx, wy, ww, wbs];
Imager.MaskRectangleI[context, wx, wy, wbs, wh];
Imager.MaskRectangleI[context, wx+ww-wbs, wy, wbs, wh];
Imager.MaskRectangleI[context, wx, wy+wh-wbs, ww, wbs];
};
};
ViewerPrivate.PaintScreen[ViewerPrivate.ViewerScreen[viewer], action];
};
IF viewer.wx < rect.x
OR
(viewer.wx + viewer.ww > rect.x + rect.w)
OR
(viewer.wy + viewer.wh - header) < rect.y
OR
(viewer.wy + viewer.wh > rect.y + rect.h) THEN {
repaint header
ViewerOps.PaintViewer[viewer, caption, FALSE];
ViewerOps.PaintViewer[viewer, menu, FALSE];
};
IF viewer.menu # NIL THEN {
reset cached position
viewer.menu.x ← wbs;
viewer.menu.y ← viewer.wh-captionHeight;
};
call RepaintViewer even if the shape didn't change
ViewerForkers.ForkCall[viewer, RepaintViewer, NEW[RepaintActionData ← [viewer: viewer, xChanged: oldBox.w # newBox.w, yChanged: oldBox.h # newBox.h, rect: rect]]];
};
RepaintActionData: TYPE = RECORD [
Private contract between RepaintViewer & BLTViewer.
viewer: Viewer, xChanged: BOOL, yChanged: BOOL, rect: PaintRectangle];
RepaintViewer: PROC [data: REF] = {
WITH data SELECT FROM
mine: REF RepaintActionData => {
viewer: Viewer ← mine.viewer;
ViewerOps.PaintViewer[viewer, client, FALSE, mine.rect];
if this is a container, repaint the sub-viewers that were x and y bound.
we cannot use the class name because the viewer may be a subclass of $Container.
IF ContainerPaint = NIL THEN
ContainerPaint ← ViewerOps.FetchViewerClass[$Container].paint;
IF viewer.class.paint = ContainerPaint THEN
RepaintBoundViewers[viewer, mine.rect, mine.xChanged, mine.yChanged];
};
ENDCASE;
};
RepaintBoundViewers: PROC[viewer: Viewer, rect: PaintRectangle, xChanged, yChanged: BOOL] = {
xBound: LIST OF Viewer ← NARROW[viewer.class.get[viewer, $XBound]];
yBound: LIST OF Viewer ← NARROW[viewer.class.get[viewer, $YBound]];
IF xChanged THEN {
repaint x bound
FOR list: LIST OF Viewer ← xBound, list.rest WHILE list # NIL DO
v: Viewer ← list.first;
IF v.class.flavor = $Text OR v.class.flavor = $Typescript THEN {ViewerOps.PaintViewer[v, all]; LOOP};
IF WithinRect[v, rect] THEN {
ViewerOps.PaintViewer[v, all, FALSE, rect];
IF v.class.paint = ContainerPaint THEN {
doubleBounded: BOOLFALSE;
IF yChanged THEN FOR l: LIST OF Viewer ← yBound, l.rest WHILE l # NIL DO
IF l.first = v THEN {doubleBounded ← TRUE; EXIT};
ENDLOOP;
RepaintBoundViewers[v, rect, TRUE, doubleBounded]};
ENDLOOP;
};
IF yChanged THEN {
repaint y bound
FOR list: LIST OF Viewer ← yBound, list.rest WHILE list # NIL DO
v: Viewer ← list.first;
painted: BOOLFALSE;
FOR list: LIST OF Viewer ← xBound, list.rest WHILE list # NIL DO
IF list.first = v THEN {painted ← TRUE; EXIT};
ENDLOOP;
IF painted THEN LOOP;
IF WithinRect[v, rect] THEN {
ViewerOps.PaintViewer[v, all, FALSE, rect];
IF v.class.paint = ContainerPaint THEN RepaintBoundViewers[v, rect, FALSE, TRUE];
ENDLOOP;
};
};
WithinRect: PROC[v: Viewer, rect: PaintRectangle] RETURNS[BOOL] = {
vx, vy: INTEGER;
IF v.parent.class.topDownCoordSys
THEN [vx, vy] ← ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy+v.wh]
ELSE [vx, vy] ← ViewerOps.UserToScreenCoords[v.parent, v.wx, v.wy];
RETURN[vx >= rect.x AND (vx + v.ww <= rect.x + rect.w) AND
vy >= rect.y AND (vy + v.wh <= rect.y + rect.h)];
};
ContainerPaint: ViewerClasses.PaintProc ← NIL;
ColumnInfo: PROC[column: Column] RETURNS[totalSpace, leftX, width, bottomY, nextY: INTEGER] = INLINE {
SELECT column FROM
left => {
totalSpace ← openTopY-openBottomY;
bottomY ← nextY ← openBottomY;
leftX ← openLeftLeftX;
width ← openLeftWidth;
};
right => {
totalSpace ← openTopY-openBottomY;
bottomY ← nextY ← openBottomY;
leftX ← openRightLeftX;
width ← openRightWidth;
};
color => {
totalSpace ← colorScreenHeight;
bottomY ← nextY ← 0;
leftX ← 0;
width ← colorScreenWidth;
};
ENDCASE => ERROR;
};
ColumnSnapshot: TYPE ~ REF ColumnSequence;
ColumnSequence: TYPE ~ RECORD[SEQUENCE size: NAT OF ColumnRec];
ColumnRec: TYPE ~ RECORD[viewer: Viewer, box: Box, painted: BOOLFALSE];
Box: TYPE = RECORD[x, y, w, h: INTEGER];
snapshots: ARRAY Column OF RECORD[before, after: ColumnSnapshot];
SnapshotColumn: PROC[column: Column] RETURNS[snapshot: ColumnSnapshot] = {
length: CARDINAL ← 0;
snapshot ← snapshots[column].after;
IF snapshot = NIL THEN snapshot ← NEW[ColumnSequence[10]];
ClearSnapshot[snapshot];
FOR v: Viewer ← ViewerPrivate.rootViewerTree[column], v.sibling UNTIL v = NIL DO
IF length = snapshot.size THEN { -- make a bigger snapshot
temp: ColumnSnapshot ← NEW[ColumnSequence[snapshot.size + 10]];
FOR i: CARDINAL IN [0..snapshot.size) DO
temp[i] ← snapshot[i];
ENDLOOP;
snapshot ← temp;
};
snapshot[length] ← [v, [v.wx, v.wy, v.ww, v.wh]];
length ← length + 1;
ENDLOOP;
};
ClearSnapshot: PROC[snapshot: ColumnSnapshot] = {
IF snapshot = NIL THEN RETURN;
FOR i: CARDINAL IN [0..snapshot.size) DO snapshot[i].viewer ← NIL; ENDLOOP;
};
GetIndex: PROC[snapshot: ColumnSnapshot, viewer: Viewer] RETURNS[CARDINAL] = {
FOR i: CARDINAL IN [0..snapshot.size) DO
IF snapshot[i].viewer = NIL THEN EXIT;
IF snapshot[i].viewer = viewer THEN RETURN[i];
ENDLOOP;
RETURN[snapshot.size];
};
Invalidate: PUBLIC PROC = {
ClearSnapshot[snapshots[left].before];
ClearSnapshot[snapshots[right].before];
ClearSnapshot[snapshots[color].before];
};
ForkPaintViewerAll: PROC [viewer: Viewer] = TRUSTED {
ViewerForkers.ForkPaint[viewer, all];
};
END.
Glitch: PUBLIC PROC [viewer: Viewer, nLines: INTEGER] = {
dY, sY, vY, vX: INTEGER;
w: INTEGER ← viewer.cw;
h: INTEGER ← viewer.ch-ABS[nLines];
dc: Graphics.Context ← ViewerOps.AcquireContext[NIL];
[vX, vY] ← ViewerOps.UserToScreenCoords[viewer, 0,
IF viewer.class.coordSys=top THEN 0 ELSE viewer.ch];
dY ← IF nLines>0 THEN vY ELSE vY-nLines;
sY ← IF nLines>0 THEN vY-nLines ELSE vY;
Graphics.SetCP[dc, vX, dY];
GraphicsOps.DrawBitmap[
self: dc,
bitmap: GraphicsOps.ScreenBitmap[],
w: w,
h: h,
x: vX,
y: ViewerSpecs.screenH-sY,
xorigin: vX,
yorigin: ViewerSpecs.screenH-sY
];
ViewerOps.ReleaseContext[dc];
};
VBLT: PUBLIC PROC [src: VPlace, dest: VPlace, srcw, srch: INTEGER] = {
ERROR; -- not yet implemented
};