BitmapEditImpl.mesa
Michael Plass, September 12, 1983 7:31 pm
DIRECTORY
BitmapEdit,
Environment,
Graphics,
GraphicsColor,
ImagerPixelMaps,
UserTerminal,
TIPTables,
TIPUser,
ViewerClasses,
ViewerOps,
Process
;
BitmapEditImpl:
CEDAR
MONITOR
IMPORTS Graphics, GraphicsColor, ImagerPixelMaps, UserTerminal, 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: ImagerPixelMaps.PixelMap,
shared: BOOLEAN,
refreshProcess: PROCESS
];
NewBitmap:
PROC [height, width:
NAT]
RETURNS [bitmap: Bitmap] ~ {
bitmap ← NEW[BitmapRep];
bitmap.xSize ← width;
bitmap.ySize ← height;
bitmap.pixelMap ← ImagerPixelMaps.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 {UserTerminal.WaitForScanLine[0]};
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: ImagerPixelMaps.PixelMap, sWidth, fWidth:
INTEGER, shared:
BOOLEAN ←
FALSE] ~ {
bitmap: Bitmap ← NARROW[viewer.data];
window: ImagerPixelMaps.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: ImagerPixelMaps.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: Graphics.Color ← Graphics.white;
black: Graphics.Color ← Graphics.black;
grey: Graphics.Color ← GraphicsColor.IntensityToColor[0.5];
unknownAtom: ATOM ← NIL;
BitmapPaintProc:
PROC [self: ViewerClasses.Viewer, context: Graphics.Context, whatChanged:
REF, clear:
BOOL] ~ {
bitmap: Bitmap ← NARROW[self.data];
pixelSpacing: NAT ← MIN[PixelSpacing[bitmap.xSize, self.cw-1], PixelSpacing[bitmap.ySize, self.ch-1]];
pixelSize: NAT ← MAX[pixelSpacing-1, 1];
context.Translate[(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.DrawBox[[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: INTEGER ← MAX[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.DrawBox[[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.DrawBox[[-bitmap.xMin*pixelSpacing, -8-bitmap.yMin*pixelSpacing, 1-bitmap.xMin*pixelSpacing, 8-bitmap.yMin*pixelSpacing]];
context.DrawBox[[-8-bitmap.xMin*pixelSpacing, -bitmap.yMin*pixelSpacing, 8-bitmap.xMin*pixelSpacing, 1-bitmap.yMin*pixelSpacing]];
context.DrawBox[[(bitmap.xWidth-bitmap.xMin)*pixelSpacing, -4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing, 1+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.DrawBox[[-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[Graphics.white];
context.DrawBox[[-9999, h, 9999, 9999]];
context.DrawBox[[-9999, 0, 0, h]];
context.DrawBox[[pixelSpacing*bitmap.xSize+1, 0, 9999, h]];
context.DrawBox[[-9999, -9999, 9999, 0]];
};
};
$SetOrigin => {
IF pixelSpacing > pixelSize
THEN {
context.SetColor[black];
context.DrawBox[[-bitmap.xMin*pixelSpacing, -8-bitmap.yMin*pixelSpacing, 1-bitmap.xMin*pixelSpacing, 8-bitmap.yMin*pixelSpacing]];
context.DrawBox[[-8-bitmap.xMin*pixelSpacing, -bitmap.yMin*pixelSpacing, 8-bitmap.xMin*pixelSpacing, 1-bitmap.yMin*pixelSpacing]];
context.DrawBox[[(bitmap.xWidth-bitmap.xMin)*pixelSpacing, -4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing, 1+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.DrawBox[[-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[Graphics.white];
context.DrawBox[[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.DrawBox[[x, 0, x+1, yDisplay]];
ENDLOOP;
FOR i:
NAT
IN [0..bitmap.ySize]
DO
y: REAL ← i*pixelSpacing;
context.DrawBox[[0, y, xDisplay, y+1]];
ENDLOOP;
context.SetColor[black];
context.DrawBox[[-bitmap.xMin*pixelSpacing, -8-bitmap.yMin*pixelSpacing, 1-bitmap.xMin*pixelSpacing, 8-bitmap.yMin*pixelSpacing]];
context.DrawBox[[-8-bitmap.xMin*pixelSpacing, -bitmap.yMin*pixelSpacing, 8-bitmap.xMin*pixelSpacing, 1-bitmap.yMin*pixelSpacing]];
context.DrawBox[[(bitmap.xWidth-bitmap.xMin)*pixelSpacing, -4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing, 1+(bitmap.xWidth-bitmap.xMin)*pixelSpacing, 4+(bitmap.yWidth-bitmap.yMin)*pixelSpacing]];
context.DrawBox[[-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.DrawBox[[0, 0, 1, yDisplay]];
context.DrawBox[[xDisplay, 0, xDisplay+1, yDisplay]];
context.DrawBox[[0, 0, xDisplay, 1]];
context.DrawBox[[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.DrawBox[[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: Environment.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: NAT ← MIN[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.