BitmapEditImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, May 20, 1985 11:30:56 am PDT
DIRECTORY
Basics USING [LongNumber],
BitmapEdit USING [],
Imager USING [black, Box, Color, Context, MakeGray, MaskBox, SetColor, TranslateT, white],
ImagerPixelMap USING [Create, DeviceRectangle, Fill, GetBit, PixelMap, ShiftMap, Window],
Process USING [MsecToTicks, Pause, priorityBackground, SetPriority],
TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords],
ViewerClasses USING [NotifyProc, Viewer, ViewerClass, ViewerClassRec, ViewerRec],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetNewVersion];
BitmapEditImpl: CEDAR MONITOR
IMPORTS Imager, ImagerPixelMap, TIPUser, ViewerOps, Process
EXPORTS BitmapEdit
~ BEGIN
Bitmap: TYPE ~ REF BitmapRep;
BitmapRep: TYPE ~ RECORD [
xPrev, yPrev: INTEGER,
xSize, ySize: NAT,
xMin, yMin: INTEGER,
xWidth, yWidth: INTEGER,
pixelMap: ImagerPixelMap.PixelMap,
shared: BOOLEAN,
refreshProcess: PROCESS
];
NewBitmap: PROC [height, width: NAT] RETURNS [bitmap: Bitmap] ~ {
bitmap ← NEW[BitmapRep];
bitmap.xSize ← width;
bitmap.ySize ← height;
bitmap.pixelMap ← ImagerPixelMap.Create[0, [0, 0, height, width]];
};
GetBit: PROC [bitmap: Bitmap, x, y: INTEGER] RETURNS [value: NAT] ~ {
value ← bitmap.pixelMap.GetBit[bitmap.ySize-y-1, x];
};
StoreBit: PROC [bitmap: Bitmap, x, y: INTEGER, value: NAT] ~ {
bitmap.pixelMap.Fill[[bitmap.ySize-y-1, x, 1, 1], value];
};
CreateBitmapViewer: PUBLIC PROC [height, width: NAT, info: ViewerClasses.ViewerRec] RETURNS [ViewerClasses.Viewer] ~ {
info.data ← NewBitmap[height, width];
RETURN [ViewerOps.CreateViewer[$BitmapEdit, info]];
};
BitCoords: TYPE ~ REF BitCoordsRep;
BitCoordsRep: TYPE ~ RECORD [x, y: INTEGER];
Box: TYPE ~ REF BoxRep;
BoxRep: TYPE ~ RECORD [xMin, yMin, xMax, yMax: INTEGER];
boxHeight: NAT ← 5;
RefreshProcess: PROC [viewer: ViewerClasses.Viewer] ~ {
bitmap: Bitmap ← NARROW[viewer.data];
whatChanged: Box ← NEW[BoxRep];
x: INTEGER ← 0;
y: INTEGER ← 0;
TRUSTED {Process.SetPriority[Process.priorityBackground]};
UNTIL viewer.destroyed OR NOT bitmap.shared DO
TRUSTED {Process.Pause[Process.MsecToTicks[77]]};
IF y > bitmap.ySize THEN {y ← 0; x ← x + 1};
IF x > bitmap.xSize THEN x ← 0;
whatChanged^ ← [x, y, x+1, y+boxHeight];
ViewerOps.PaintViewer[viewer, client, FALSE, whatChanged];
y ← y + boxHeight;
ENDLOOP;
};
SetBitmap: PUBLIC PROC [viewer: ViewerClasses.Viewer, pixelMap: ImagerPixelMap.PixelMap, sWidth, fWidth: INTEGER, shared: BOOLEANFALSE] ~ {
bitmap: Bitmap ← NARROW[viewer.data];
window: ImagerPixelMap.DeviceRectangle ← pixelMap.Window;
IF bitmap.refreshProcess # NIL THEN {
bitmap.shared ← FALSE;
TRUSTED {[] ← JOIN bitmap.refreshProcess};
bitmap.refreshProcess ← NIL;
};
bitmap.xSize ← window.fSize;
bitmap.ySize ← window.sSize;
bitmap.xMin ← window.fMin;
bitmap.yMin ← -window.sMin-window.sSize;
bitmap.xPrev ← 0;
bitmap.yPrev ← 0;
bitmap.pixelMap ← pixelMap.ShiftMap[-window.sMin, -window.fMin];
bitmap.yWidth ← -sWidth;
bitmap.xWidth ← fWidth;
bitmap.shared ← shared;
IF shared THEN {
bitmap.refreshProcess ← FORK RefreshProcess[viewer];
};
ViewerOps.PaintViewer[viewer, client];
};
GetBitmap: PUBLIC PROC [viewer: ViewerClasses.Viewer] RETURNS [pixelMap: ImagerPixelMap.PixelMap, sWidth, fWidth: INTEGER] ~ {
bitmap: Bitmap ← NARROW[viewer.data];
pixelMap ← bitmap.pixelMap.ShiftMap[-bitmap.ySize-bitmap.yMin, bitmap.xMin];
sWidth ← -bitmap.yWidth;
fWidth ← bitmap.xWidth;
};
PixelSpacing: PROC [subjectSize, viewerSize: INT] RETURNS [pixelSpacing: INT] ~ {
pixelSpacing ← MAX[(viewerSize/MAX[subjectSize, 1])/2*2, 1];
};
white: Imager.Color ← Imager.white;
black: Imager.Color ← Imager.black;
grey: Imager.Color ← Imager.MakeGray[0.5];
unknownAtom: ATOMNIL;
BitmapPaintProc: PROC [self: ViewerClasses.Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL] RETURNS [BOOLFALSE] ~ {
bitmap: Bitmap ← NARROW[self.data];
pixelSpacing: NATMIN[PixelSpacing[bitmap.xSize, self.cw-1], PixelSpacing[bitmap.ySize, self.ch-1]];
pixelSize: NATMAX[pixelSpacing-1, 1];
context.TranslateT[[(self.cw-(pixelSpacing*bitmap.xSize+1))/2, (self.ch-(pixelSpacing*bitmap.ySize+1))/2]];
WITH whatChanged SELECT FROM
bitCoords: BitCoords => {
x: INTEGER ← bitCoords.x;
y: INTEGER ← bitCoords.y;
IF x IN [0..bitmap.xSize) AND y IN [0..bitmap.ySize) THEN {
xDisplay: REAL ← x*pixelSpacing + 1;
yDisplay: REAL ← y*pixelSpacing + 1;
context.SetColor[IF GetBit[bitmap, x, y] = 0 THEN white ELSE black];
context.MaskBox[[xDisplay, yDisplay, xDisplay+pixelSize, yDisplay+pixelSize]];
};
};
box: Box => {
cur: NAT ← 0;
context.SetColor[white];
FOR x: INTEGER IN [MAX[box.xMin, 0]..MIN[box.xMax, bitmap.xSize]) DO
xDisplay: REAL ← x*pixelSpacing + 1;
yStart: INTEGERMAX[box.yMin, 0];
yDisplay: REAL ← yStart*pixelSpacing + 1;
FOR y: INTEGER IN [yStart..MIN[box.yMax, bitmap.ySize]) DO
IF GetBit[bitmap, x, y] # cur THEN {
cur ← 1-cur;
context.SetColor[IF cur = 0 THEN white ELSE black];
};
context.MaskBox[[xDisplay, yDisplay, xDisplay+pixelSize, yDisplay+pixelSize]];
yDisplay ← yDisplay + pixelSpacing;
ENDLOOP;
ENDLOOP;
};
atom: ATOM => {
SELECT whatChanged FROM
$EraseOrigin => {
IF pixelSpacing > pixelSize THEN {
h: REAL ← pixelSpacing*bitmap.ySize+1;
context.SetColor[grey];
context.MaskBox[[-bitmap.xMin*pixelSpacing, -8-bitmap.yMin*pixelSpacing, 1-bitmap.xMin*pixelSpacing, 8-bitmap.yMin*pixelSpacing]];
context.MaskBox[[-8-bitmap.xMin*pixelSpacing, -bitmap.yMin*pixelSpacing, 8-bitmap.xMin*pixelSpacing, 1-bitmap.yMin*pixelSpacing]];
context.MaskBox[[(bitmap.xWidth-bitmap.xMin)*pixelSpacing, -4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing, 1+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.MaskBox[[-4+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, (bitmap.yWidth-bitmap.yMin)*pixelSpacing, 4+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 1+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.SetColor[white];
context.MaskBox[[-9999, h, 9999, 9999]];
context.MaskBox[[-9999, 0, 0, h]];
context.MaskBox[[pixelSpacing*bitmap.xSize+1, 0, 9999, h]];
context.MaskBox[[-9999, -9999, 9999, 0]];
};
};
$SetOrigin => {
IF pixelSpacing > pixelSize THEN {
context.SetColor[black];
context.MaskBox[[-bitmap.xMin*pixelSpacing, -8-bitmap.yMin*pixelSpacing, 1-bitmap.xMin*pixelSpacing, 8-bitmap.yMin*pixelSpacing]];
context.MaskBox[[-8-bitmap.xMin*pixelSpacing, -bitmap.yMin*pixelSpacing, 8-bitmap.xMin*pixelSpacing, 1-bitmap.yMin*pixelSpacing]];
context.MaskBox[[(bitmap.xWidth-bitmap.xMin)*pixelSpacing, -4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing, 1+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.MaskBox[[-4+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, (bitmap.yWidth-bitmap.yMin)*pixelSpacing, 4+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 1+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
};
};
ENDCASE => unknownAtom ← atom;
};
ENDCASE => {
IF NOT clear THEN {
context.SetColor[white];
context.MaskBox[[0, 0, self.cw, self.ch]];
};
context.SetColor[grey];
IF pixelSpacing > pixelSize THEN {
xDisplay: REAL ← bitmap.xSize*pixelSpacing+1;
yDisplay: REAL ← bitmap.ySize*pixelSpacing+1;
FOR i: NAT IN [0..bitmap.xSize] DO
x: REAL ← i*pixelSpacing;
context.MaskBox[[x, 0, x+1, yDisplay]];
ENDLOOP;
FOR i: NAT IN [0..bitmap.ySize] DO
y: REAL ← i*pixelSpacing;
context.MaskBox[[0, y, xDisplay, y+1]];
ENDLOOP;
context.SetColor[black];
context.MaskBox[[-bitmap.xMin*pixelSpacing, -8-bitmap.yMin*pixelSpacing, 1-bitmap.xMin*pixelSpacing, 8-bitmap.yMin*pixelSpacing]];
context.MaskBox[[-8-bitmap.xMin*pixelSpacing, -bitmap.yMin*pixelSpacing, 8-bitmap.xMin*pixelSpacing, 1-bitmap.yMin*pixelSpacing]];
context.MaskBox[[(bitmap.xWidth-bitmap.xMin)*pixelSpacing, -4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing, 1+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.MaskBox[[-4+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, (bitmap.yWidth-bitmap.yMin)*pixelSpacing, 4+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 1+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
}
ELSE {
xDisplay: REAL ← bitmap.xSize*pixelSpacing+1;
yDisplay: REAL ← bitmap.ySize*pixelSpacing+1;
context.MaskBox[[0, 0, 1, yDisplay]];
context.MaskBox[[xDisplay, 0, xDisplay+1, yDisplay]];
context.MaskBox[[0, 0, xDisplay, 1]];
context.MaskBox[[0, yDisplay, xDisplay, yDisplay+1]];
};
context.SetColor[black]; {
y: REAL ← 1;
s: REAL ← pixelSpacing;
FOR iy: NAT IN [0..bitmap.ySize) DO
x: REAL ← 1;
FOR ix: NAT IN [0..bitmap.xSize) DO
IF GetBit[bitmap, ix, iy] = 1 THEN context.MaskBox[[x, y, x+pixelSize, y+pixelSize]];
x ← x + s;
ENDLOOP;
y ← y + s;
ENDLOOP;
};
};
};
bitCoords: BitCoords ← NEW[BitCoordsRep];
StorePixel: PROC [self: ViewerClasses.Viewer, x, y: INTEGER, value: NAT] ~ {
bitmap: Bitmap ← NARROW[self.data];
bitmap.xPrev ← x;
bitmap.yPrev ← y;
IF x IN [0..bitmap.xSize) AND y IN [0..bitmap.ySize) THEN {
StoreBit[bitmap, x, y, value];
bitCoords.x ← x;
bitCoords.y ← y;
ViewerOps.PaintViewer[self, client, FALSE, bitCoords];
};
ViewerOps.SetNewVersion[self];
};
StorePixels: PROC [self: ViewerClasses.Viewer, x, y: INTEGER, value: NAT] ~ {
bitmap: Bitmap ← NARROW[self.data];
RecLine: PROC [x0, y0, x1, y1: INTEGER] ~ {
IF ABS[x0 - x1] > 1 OR ABS[y0 - y1] > 1 THEN {
xm: INTEGER ← (x0+x1)/2;
ym: INTEGER ← (y0+y1)/2;
RecLine[x0, y0, xm, ym];
RecLine[xm, ym, x1, y1];
};
IF x0 IN [0..bitmap.xSize) AND y0 IN [0..bitmap.ySize) THEN {
StoreBit[bitmap, x0, y0, value];
bitCoords.x ← x0;
bitCoords.y ← y0;
ViewerOps.PaintViewer[self, client, FALSE, bitCoords];
};
};
RecLine[bitmap.xPrev, bitmap.yPrev, x, y];
bitmap.xPrev ← x;
bitmap.yPrev ← y;
ViewerOps.SetNewVersion[self];
};
SetOrigin: PROC [self: ViewerClasses.Viewer, x, y: INTEGER] ~ {
bitmap: Bitmap ← NARROW[self.data];
IF bitmap.xMin = -x AND bitmap.yMin = - y THEN RETURN;
ViewerOps.PaintViewer[self, client, FALSE, $EraseOrigin];
bitmap.xMin ← - x;
bitmap.yMin ← - y;
ViewerOps.PaintViewer[self, client, FALSE, $SetOrigin];
ViewerOps.SetNewVersion[self];
};
SetWidth: PROC [self: ViewerClasses.Viewer, x, y: INTEGER] ~ {
bitmap: Bitmap ← NARROW[self.data];
IF bitmap.xWidth-bitmap.xMin = x AND bitmap.yWidth-bitmap.yMin = y THEN RETURN;
ViewerOps.PaintViewer[self, client, FALSE, $EraseOrigin];
bitmap.xWidth ← bitmap.xMin + x;
bitmap.yWidth ← bitmap.yMin + y;
ViewerOps.PaintViewer[self, client, FALSE, $SetOrigin];
ViewerOps.SetNewVersion[self];
};
Div: PROC[num: INT, denom: NAT] RETURNS [quotient: INT] ~ {
long: Basics.LongNumber;
long.li ← num;
quotient ← 0;
WHILE long.li < 0 DO
long.highbits ← long.highbits + denom;
quotient ← quotient - LAST[CARDINAL]-1;
ENDLOOP;
quotient ← quotient + long.lc/denom;
};
BitmapNotifyProc: ViewerClasses.NotifyProc = {
IF ISTYPE[input.first, TIPUser.TIPScreenCoords] THEN {
bitmap: Bitmap ← NARROW[self.data];
mousePlace: TIPUser.TIPScreenCoords ← NARROW[input.first];
pixelSpacing: NATMIN[PixelSpacing[bitmap.xSize, self.cw-1], PixelSpacing[bitmap.ySize, self.ch-1]];
x: INTEGER ← Div[mousePlace.mouseX*2 - (self.cw-(pixelSpacing*bitmap.xSize+1)), pixelSpacing*2];
y: INTEGER ← Div[mousePlace.mouseY*2 - (self.ch-(pixelSpacing*bitmap.ySize+1)), pixelSpacing*2];
SELECT input.rest.first FROM
$DrawPixel => StorePixel[self, x, y, 1];
$ErasePixel => StorePixel[self, x, y, 0];
$DrawPixels => StorePixels[self, x, y, 1];
$ErasePixels => StorePixels[self, x, y, 0];
$SetOrigin, $SetWidth => {
x ← Div[mousePlace.mouseX*2-(self.cw-(pixelSpacing*bitmap.xSize+1)-pixelSpacing), pixelSpacing*2];
y ← Div[mousePlace.mouseY*2-(self.ch-(pixelSpacing*bitmap.ySize+1)-pixelSpacing), pixelSpacing*2];
IF input.rest.first = $SetOrigin THEN SetOrigin[self, x, y] ELSE SetWidth[self, x, y];
};
ENDCASE => NULL;
};
};
bitmapClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
paint: BitmapPaintProc,
notify: BitmapNotifyProc,
tipTable: TIPUser.InstantiateNewTIPTable["BitmapEdit.TIP"]
]];
ViewerOps.RegisterViewerClass[$BitmapEdit, bitmapClass];
END.