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: BOOL ← FALSE;
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:
BOOL ←
TRUE] = {
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: INTEGER ← IF 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: BOOL ← FALSE;
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: BOOL ← FALSE;
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: BOOL ← FALSE];
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
};