BufferedRefreshImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved.
Last edited by Bier on February 3, 1987
Contents: Builds up an abstraction of transparent layers onto which shapes can be drawn. Each layer may have a backing pixel map, in which case it need not be redrawn when other layers change. This scheme is used by Gargoyle and Solidviews.
Pier, March 25, 1987 4:16:00 pm PST
Bier, March 25, 1987 6:52:48 pm PST
DIRECTORY
BufferedRefresh, BufferedRefreshTypes, Imager, ImagerBackdoor, ImagerBitmapContext, ImagerSample, ImagerTransformation;
BufferedRefreshImpl:
CEDAR
PROGRAM
IMPORTS Imager, ImagerBackdoor, ImagerBitmapContext, ImagerSample, ImagerTransformation
EXPORTS BufferedRefresh =
BEGIN
RefreshProc: TYPE = BufferedRefreshTypes.RefreshProc;
Layer: TYPE = BufferedRefreshTypes.Layer;
Sandwich: TYPE = REF SandwichObj;
SandwichObj:
TYPE = BufferedRefreshTypes.SandwichObj;
LayerData: TYPE = REF LayerDataObj;
LayerDataObj: TYPE = BufferedRefreshTypes.LayerDataObj;
[Artwork node; type 'ArtworkInterpress on' to command tool]
StartLayerDataList:
PUBLIC
PROC []
RETURNS [entityList, ptr:
LIST
OF LayerData] = {
ptr ← entityList ← NIL;
};
AddLayerData:
PUBLIC
PROC [entity: LayerData, entityList, ptr:
LIST
OF LayerData]
RETURNS [newList, newPtr:
LIST
OF LayerData] = {
IF ptr =
NIL
THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
};
CreateSandwich:
PUBLIC
PROC [layers:
LIST
OF Layer]
RETURNS [sandwich: Sandwich] = {
Layers should be specified in back to front order.
layerData: LayerData;
ptr: LIST OF LayerData;
sandwich ← NEW[SandwichObj];
[sandwich.layers, ptr] ← StartLayerDataList[];
FOR list:
LIST
OF Layer ← layers, list.rest
UNTIL list =
NIL
DO
layerData ← NEW[LayerDataObj ← [name: list.first.name, refreshProc: list.first.refreshProc, hasMap: list.first.backingMap]];
[sandwich.layers, ptr] ← AddLayerData[layerData, sandwich.layers, ptr];
ENDLOOP;
};
FitSandwichToScreen:
PUBLIC
PROC [sandwich: Sandwich, cw, ch:
INTEGER] = {
IF sandwich.chunkingBitmap=NIL THEN DoFitSandwichToScreen[sandwich, cw, ch]
ELSE {
curSize: ImagerSample.Vec ← ImagerSample.GetSize[sandwich.chunkingBitmap];
IF curSize.s#ch OR curSize.f#cw THEN DoFitSandwichToScreen[sandwich, cw, ch];
};
};
DoFitSandwichToScreen:
PROC [sandwich: Sandwich, cw, ch:
INTEGER] = {
layer: LayerData;
rect: Imager.Rectangle;
Construct the chunking map.
sandwich.chunkingBitmap ← ImagerSample.NewSampleMap[[max: [ch, cw]]];
sandwich.chunkingContext ← ImagerBitmapContext.Create[[ch, cw], [down, right], [72.0, 72.0], TRUE, $Bitmap];
ImagerBitmapContext.SetBitmap[sandwich.chunkingContext, sandwich.chunkingBitmap];
sandwich.ch ← ch;
Construct the layer backing maps.
sandwich.viewerToClient ← ImagerTransformation.Scale[1.0];
sandwich.clientToViewer ← ImagerTransformation.Scale[1.0];
FOR list:
LIST
OF LayerData ← sandwich.layers, list.rest
UNTIL list =
NIL
DO
layer ← list.first;
IF layer.hasMap
THEN {
layer.backingMap ← ImagerSample.NewSampleMap[[max: [ch, cw]]];
layer.backingContext ← ImagerBitmapContext.Create[[ch, cw], [down, right], [72.0, 72.0], TRUE, $Bitmap];
ImagerBitmapContext.SetBitmap[layer.backingContext, layer.backingMap];
Initialize each bitmap to white.
rect ← ImagerBackdoor.GetBounds[layer.backingContext];
Imager.SetColor[layer.backingContext, Imager.white];
Imager.MaskRectangle[layer.backingContext, rect];
layer.mapOK ← FALSE;
}
ELSE {
layer.backingMap ← NIL;
layer.backingContext ← NIL;
};
ENDLOOP;
};
DrawSandwich:
PUBLIC
PROC [sandwich: Sandwich, screen: Imager.Context, clientToViewer, viewerToClient: Imager.Transformation, clientData:
REF
ANY, ignoreBackingMap:
BOOL ←
FALSE] = {
Draw the picture, derivable from the sandwich, on the screen. For backed layers that are OK, this will just dump the backing map on the screen. For backed layers that are not OK, this will remake the backing map by calling the refreshProc and dump the backing map on the screen. For unbacked layers, this will call the RefreshProc for that layer. Layers will be drawn in back-to-front order. If ignoreBackingMap is TRUE, all layers will be drawn by calling their refresh proc. Since, we currently use 1 bit per pixel backing maps, this must be done to show colors. All layers are initially NOT OK.
chunking: Imager.Context ← sandwich.chunkingContext;
boundRect: Imager.Rectangle;
rect: Imager.Rectangle;
layer: LayerData;
Update the Transformations (should be done atomically)
Imager.ConcatT[screen, viewerToClient]; -- get rid of the Biscrollers transformation
FOR list:
LIST
OF LayerData ← sandwich.layers, list.rest
UNTIL list =
NIL
DO
layer ← list.first;
IF layer.hasMap
THEN {
Imager.ConcatT[layer.backingContext, sandwich.viewerToClient]; -- undo the last
Imager.ConcatT[layer.backingContext, clientToViewer]; -- do the new
};
ENDLOOP;
sandwich.viewerToClient ← viewerToClient;
Draw
boundRect ← ImagerBackdoor.GetBounds[screen];
IF ignoreBackingMap
THEN {
Imager.SetColor[screen, Imager.white];
Imager.MaskRectangle[screen, boundRect];
}
ELSE {
Imager.SetColor[chunking, Imager.white];
Imager.MaskRectangle[chunking, boundRect];
};
FOR list:
LIST
OF LayerData ← sandwich.layers, list.rest
UNTIL list =
NIL
DO
layer ← list.first;
IF ignoreBackingMap
THEN {
DirectToScreen:
PROC = {
Imager.ConcatT[screen, clientToViewer];
rect ← ImagerBackdoor.GetBounds[screen];
layer.refreshProc[screen, rect, clientData];
};
Imager.DoSaveAll[screen, DirectToScreen];
}
ELSE
{
IF
NOT layer.hasMap
THEN {
DirectToChunking:
PROC = {
Imager.ConcatT[chunking, clientToViewer];
rect ← ImagerBackdoor.GetBounds[chunking];
layer.refreshProc[chunking, rect, clientData];
};
Imager.DoSaveAll[chunking, DirectToChunking];
}
ELSE {
IF
NOT layer.mapOK
THEN {
rect ← ImagerBackdoor.GetBounds[layer.backingContext];
Imager.SetColor[layer.backingContext, Imager.white];
Imager.MaskRectangle[layer.backingContext, rect];
layer.refreshProc[layer.backingContext, rect, clientData];
layer.mapOK ← TRUE;
};
LayerToChunking[sandwich, layer, chunking];
};
};
ENDLOOP;
IF NOT ignoreBackingMap THEN ChunkingToScreen[sandwich, screen];
};
ChunkingToScreen:
PROC [sandwich: Sandwich, screen: Imager.Context] = {
ChunkingToScreenAux:
PROC = {
Imager.DrawBitmap[screen, chunkingBitmap, [sandwich.ch, 0]];
};
chunkingBitmap: Imager.SampleMap ← sandwich.chunkingBitmap;
Imager.DoSaveAll[screen, ChunkingToScreenAux];
};
LayerToChunking:
PROC [sandwich: Sandwich, layer: LayerData, chunking: Imager.Context] = {
UpdateBackingMap:
PROC = {
Imager.SetColor[chunking, Imager.black];
Imager.MaskBitmap[chunking, backingMap, [sandwich.ch, 0]];
};
backingMap: Imager.SampleMap ← layer.backingMap;
Imager.DoSaveAll[chunking, UpdateBackingMap];
};
SetLayerOK:
PUBLIC
PROC [sandwich: Sandwich, layerName:
ATOM, ok:
BOOL] = {
layer: LayerData ← FindLayer[sandwich, layerName];
layer.mapOK ← ok;
};
GetLayerOK:
PUBLIC
PROC [sandwich: Sandwich, layerName:
ATOM]
RETURNS [ok:
BOOL] = {
layer: LayerData ← FindLayer[sandwich, layerName];
ok ← layer.mapOK;
};
GetLayerContext:
PUBLIC PROC [sandwich: Sandwich, layerName:
ATOM]
RETURNS [backingContext: Imager.Context] = {
layer: LayerData ← FindLayer[sandwich, layerName];
backingContext ← layer.backingContext;
};
FindLayer:
PROC [sandwich: Sandwich, layerName:
ATOM]
RETURNS [layer: LayerData] = {
FOR list:
LIST
OF LayerData ← sandwich.layers, list.rest
UNTIL list =
NIL
DO
IF list.first.name = layerName THEN RETURN[list.first];
ENDLOOP;
ERROR;
};
END.