NewerBottomImpl.Mesa
Last Edited by: Spreitzer, March 31, 1985 5:31:09 pm PST
DIRECTORY Carets, CaretsImpl, CGArea, CGContext, CGDevice, CGMatrix, CGPath, CGSource, Graphics, GraphicsBasic, GraphicsOps, NewerBottom, Process, Real, RealFns, VFonts, ViewerClasses, ViewerOps, ViewerPaintImpl;
NewerBottomImpl:
CEDAR
MONITOR
IMPORTS Carets, CaretsImpl, CGMatrix, CGPath, Graphics, GraphicsOps, Process, Real, RealFns, VFonts, ViewerOps, ViewerPaintImpl
EXPORTS NewerBottom
SHARES CaretsImpl, ViewerOps, ViewerPaintImpl =
BEGIN OPEN NewerBottom;
Area: TYPE = CGArea.Ref;
Color: TYPE = Graphics.Color;
ContextData: TYPE = CGContext.Ref;
Device: TYPE = CGDevice.Ref;
FontRef: TYPE = Graphics.FontRef;
ImageRef: TYPE = Graphics.ImageRef;
Mark: TYPE = Graphics.Mark;
Matrix: TYPE = CGMatrix.Ref;
PaintMode: TYPE = Graphics.PaintMode;
PaintVector: TYPE = ViewerPaintImpl.PaintVector;
Source: TYPE = CGSource.Ref;
StrokeEnds: TYPE = Graphics.StrokeEnds;
Viewer: TYPE = ViewerClasses.Viewer;
XYWH: TYPE = RECORD [x, y, w, h: INT];
Replace:
PUBLIC
PROC [conser: Conser, paint:
BOOL ←
TRUE] =
BEGIN
ENABLE
UNWIND =>
{ViewerOps.EnablePainting[]; Carets.ResumeCarets[]};
pv: PaintVector;
screen: Context;
Carets.SuspendCarets[];
ViewerOps.DisablePainting[];
[] ← (screen ← conser.Cons[conser.arg]).SetPaintMode[invert];
TRUSTED {
CaretsImpl.screen ← screen;
pv ← ViewerPaintImpl.vectorRoot};
FOR pv ← pv, pv.next
WHILE pv #
NIL
DO
pv.context ← conser.Cons[conser.arg];
pv.context.SetDefaultFont[VFonts.defaultGFont];
pv.viewer ← NIL;
ENDLOOP;
ViewerOps.EnablePainting[];
Carets.ResumeCarets[];
IF paint THEN ViewerOps.PaintEverything[];
END;
Vanillify:
PUBLIC
PROC [paint:
BOOL ←
TRUE] =
{Replace[vanillaConser, paint]};
vanillaConser: Conser ← NEW [ConserRep ← [ConsVanilla, NIL]];
ConsVanilla:
PROC [arg:
REF
ANY]
RETURNS [context: Context] =
{context ← Graphics.NewContext[]};
XFormer: TYPE = REF XFormerData;
XFormerData:
TYPE =
RECORD [
under: Context,
xform, altxform: Transform,
deviceClipBox: Box,
clipped: BOOL,
deviceB: Bitmap,
bufferB: Bitmap,
bufferC: Context];
worldToScreen, screenToWorld: Matrix;
xformerProcs:
REF Graphics.GraphicsProcs ←
NEW [Graphics.GraphicsProcs ← [
GetCP: XFormerGetCP,
SetCP: XFormerSetCP,
DrawTo: XFormerDrawTo,
DrawStroke: XFormerDrawStroke,
DrawArea: XFormerDrawArea,
DrawBox: XFormerDrawBox,
DrawImage: XFormerDrawImage,
Translate: XFormerTranslate,
Concat: XFormerConcat,
WorldToUser: XFormerWorldToUser,
UserToWorld: XFormerUserToWorld,
SetColor: XFormerSetColor,
GetColor: XFormerGetColor,
SetPaintMode: XFormerSetPaintMode,
SetFat: XFormerSetFat,
GetDefaultFont: XFormerGetDefaultFont,
SetDefaultFont: XFormerSetDefaultFont,
DrawChars: XFormerDrawChars,
ClipArea: XFormerClipArea,
ClipBox: XFormerClipBox,
IsPointVisible: XFormerIsPointVisible,
IsRectangular: XFormerIsRectangular,
GetBounds: XFormerGetBounds,
Visible: XFormerVisible,
Save: XFormerSave,
Restore: XFormerRestore,
DrawBits: XFormerDrawBits,
UserToDevice: XFormerUserToDevice,
DeviceToUser: XFormerDeviceToUser,
GetYMode: XFormerGetYMode,
SetYMode: XFormerSetYMode,
DrawTexturedBox: XFormerDrawTexturedBox,
Disable: XFormerDisable,
MoveDeviceRectangle: XFormerMoveDeviceRectangle
]];
Circle:
PUBLIC
PROC [x, y, r:
REAL]
RETURNS [p: Path] = {
a: REAL ← r * (RealFns.SqRt[2] - 1.0) * 4.0 / 3.0;
p ← Graphics.NewPath[];
Graphics.MoveTo[p, x+r, y];
Graphics.CurveTo[p, x+r, y+a, x+a, y+r, x, y+r];
Graphics.CurveTo[p, x-a, y+r, x-r, y+a, x-r, y];
Graphics.CurveTo[p, x-r, y-a, x-a, y-r, x, y-r];
Graphics.CurveTo[p, x+a, y-r, x+r, y-a, x+r, y];
};
DoXFormer:
PUBLIC
PROC [xfa: XFormArgRep, paint:
BOOL ←
TRUE] = {
xfa.screenB ← GraphicsOps.ScreenBitmap[];
xfa.bufferB ← GraphicsOps.NewBitmap[width: xfa.screenB.width, height: xfa.screenB.height];
Replace[
NEW [ConserRep ← [Cons: ConsXFormer, arg: NEW [XFormArgRep ← xfa]]],
paint]};
ConsXFormer:
PROC [arg:
REF
ANY]
RETURNS [context: Context] =
BEGIN
xfa: XFormArg ← NARROW[arg];
under: Context ← Graphics.NewContext[];
userClipBox: Box ← IF xfa.clipByBox THEN xfa.clipBox ELSE IF xfa.clipByPath THEN CGPath.Bounds[xfa.clipPath] ELSE [0, 0, 0, 0];
deviceClipBox: Box ← UserBoxToDevice[under, userClipBox];
context ←
NEW [Graphics.ContextRep ← [
procs: xformerProcs,
data:
NEW [XFormerData ← [
under: under,
xform: xfa.t,
altxform: Mult[screenToWorld.m, Mult[xfa.t, worldToScreen.m]],
deviceClipBox: deviceClipBox,
clipped: xfa.clipByBox OR xfa.clipByPath,
deviceB: xfa.screenB,
bufferB: xfa.bufferB,
bufferC: GraphicsOps.NewContextFromBitmap[xfa.bufferB]
]]
]];
IF xfa.clipByBox THEN Graphics.ClipBox[under, xfa.clipBox, TRUE] ELSE
IF xfa.clipByPath THEN Graphics.ClipArea[under, xfa.clipPath, FALSE, TRUE];
AppendTransform[under, xfa.t];
END;
UserBoxToDevice:
PROC [context: Context, user: Box]
RETURNS [device: Box] = {
x1, y1, x2, y2: REAL;
[x1, y1] ← GraphicsOps.UserToDevice[context, user.xmin, user.ymin];
[x2, y2] ← GraphicsOps.UserToDevice[context, user.xmax, user.ymax];
device ← [
xmin: MIN[x1, x2],
xmax: MAX[x1, x2],
ymin: MIN[y1, y2],
ymax: MAX[y1, y2]];
};
AppendTransform:
PROC [context: Context, t: Transform] = {
context.Translate[t.e, t.f];
context.Concat[t.a, t.b, t.c, t.d]};
XFormerGetCP:
PROC[self: Context, rounded:
BOOLEAN]
RETURNS[x,y:
REAL] =
BEGIN
xf: XFormer ← NARROW[self.data];
[x: x, y: y] ← xf.under.procs.GetCP[self: xf.under, rounded: rounded];
END;
XFormerSetCP:
PROC[self: Context, x,y:
REAL, rel:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.SetCP[self: xf.under, x: x, y: y, rel: rel];
END;
XFormerDrawTo:
PROC[self: Context, x,y:
REAL, rel:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawTo[self: xf.under, x: x, y: y, rel: rel];
END;
XFormerDrawStroke:
PROC[self: Context, path: Path, width:
REAL, closed:
BOOLEAN, ends: StrokeEnds] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawStroke[self: xf.under, path: path, width: width, closed: closed, ends: ends];
END;
XFormerDrawArea:
PROC[self: Context, path: Path, parityFill:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawArea[self: xf.under, path: path, parityFill: parityFill];
END;
XFormerDrawBox:
PROC[self: Context, box: Box] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawBox[self: xf.under, box: box];
END;
XFormerDrawImage:
PROC[self: Context, image: ImageRef, raw:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawImage[self: xf.under, image: image, raw: raw];
END;
XFormerTranslate:
PROC[self: Context, tx,ty:
REAL, round:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.Translate[self: xf.under, tx: tx, ty: ty, round: round];
END;
XFormerConcat:
PROC[self: Context, m11,m12,m21,m22:
REAL] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.Concat[self: xf.under, m11: m11, m12: m12, m21: m21, m22: m22];
END;
XFormerWorldToUser:
PROC[self: Context, wx,wy:
REAL]
RETURNS[x,y:
REAL] =
BEGIN
xf: XFormer ← NARROW[self.data];
[x: x, y: y] ← xf.under.procs.WorldToUser[self: xf.under, wx: wx, wy: wy];
END;
XFormerUserToWorld:
PROC[self: Context, x,y:
REAL]
RETURNS[wx,wy:
REAL] =
BEGIN
xf: XFormer ← NARROW[self.data];
[wx: wx, wy: wy] ← xf.under.procs.UserToWorld[self: xf.under, x: x, y: y];
END;
XFormerSetColor:
PROC[self: Context, color: Color] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.SetColor[self: xf.under, color: color];
END;
XFormerGetColor:
PROC[self: Context]
RETURNS[Color] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.GetColor[self: xf.under]];
END;
XFormerSetPaintMode:
PROC[self: Context, mode: PaintMode]
RETURNS[PaintMode] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.SetPaintMode[self: xf.under, mode: mode]];
END;
XFormerSetFat:
PROC[self: Context, fat:
BOOLEAN]
RETURNS[
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.SetFat[self: xf.under, fat: fat]];
END;
XFormerGetDefaultFont:
PROC[self: Context]
RETURNS[FontRef] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.GetDefaultFont[self: xf.under]];
END;
XFormerSetDefaultFont:
PROC[self: Context, font: FontRef] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.SetDefaultFont[self: xf.under, font: font];
END;
XFormerDrawChars:
PROC[self: Context, font: FontRef, map:
PROC[
PROC[
CHAR]
RETURNS[
BOOL]]] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawChars[self: xf.under, font: font, map: map];
END;
XFormerClipArea:
PROC[self: Context, path: Path, parityFill:
BOOLEAN, exclude:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.ClipArea[self: xf.under, path: path, parityFill: parityFill, exclude: exclude];
END;
XFormerClipBox:
PROC[self: Context, box: Box, exclude:
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.ClipBox[self: xf.under, box: box, exclude: exclude];
END;
XFormerIsPointVisible:
PROC[self: Context, x,y:
REAL]
RETURNS[
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.IsPointVisible[self: xf.under, x: x, y: y]];
END;
XFormerIsRectangular:
PROC[self: Context]
RETURNS[
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.IsRectangular[self: xf.under]];
END;
XFormerGetBounds:
PROC[self: Context]
RETURNS[Box] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.GetBounds[self: xf.under]];
END;
XFormerVisible:
PROC[self: Context]
RETURNS[
BOOLEAN] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.Visible[self: xf.under]];
END;
XFormerSave:
PROC[self: Context]
RETURNS[Mark] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.Save[self: xf.under]];
END;
XFormerRestore:
PROC[self: Context, mark: Mark] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.Restore[self: xf.under, mark: mark];
END;
XFormerDrawBits:
UNSAFE
PROC[self: Context,
base:
LONG
POINTER, raster:
CARDINAL, bitsPerPixel: [0..16),
x, y, w, h:
CARDINAL, xorigin, yorigin:
INTEGER] =
TRUSTED
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawBits[self: xf.under, base: base, raster: raster, bitsPerPixel: bitsPerPixel, x: x, y: y, w: w, h: h, xorigin: xorigin, yorigin: yorigin];
END;
XFormerUserToDevice:
PROC[self: Context, x, y:
REAL, rel:
BOOLEAN]
RETURNS[tx, ty:
REAL] =
BEGIN
xf: XFormer ← NARROW[self.data];
[tx, ty] ← xf.under.procs.UserToDevice[self: xf.under, x: x, y: y, rel: rel];
END;
XFormerDeviceToUser:
PROC[self: Context, tx, ty:
REAL, rel:
BOOLEAN]
RETURNS[x, y:
REAL] =
BEGIN
xf: XFormer ← NARROW[self.data];
[x, y] ← xf.under.procs.DeviceToUser[self: xf.under, tx: tx, ty: ty, rel: rel];
END;
XFormerGetYMode:
PROC[self: Context]
RETURNS[GraphicsBasic.YMode] =
BEGIN
xf: XFormer ← NARROW[self.data];
RETURN [xf.under.procs.GetYMode[self: xf.under]];
END;
XFormerSetYMode:
PROC[self: Context, mode: GraphicsBasic.YMode] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.SetYMode[self: xf.under, mode: mode];
END;
XFormerDrawTexturedBox:
PROC[self: Context, box: Box, texture: GraphicsBasic.Texture] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.DrawTexturedBox[self: xf.under, box: box, texture: texture];
END;
XFormerDisable:
PROC[self: Context] =
BEGIN
xf: XFormer ← NARROW[self.data];
xf.under.procs.Disable[self: xf.under];
END;
lastXF: XFormer ← NIL;
lastTo: XYWH ← [0, 0, 0, 0];
lastIntersected: BOOL ← FALSE;
forceDumb: BOOL ← FALSE;
XFormerMoveDeviceRectangle:
PROC [self: Context, width, height, fromX, fromY, toX, toY:
NAT] =
BEGIN
xf: XFormer ← NARROW[self.data];
from, to: XYWH;
from ← Xform[xf.altxform, [x: fromX, y: fromY, w: width, h: height]];
to ← Xform[xf.altxform, [x: toX, y: toY, w: width, h: height]];
[from.x, to.x, from.w, to.w] ← Clip[from.x, to.x, from.w, to.w];
[from.y, to.y, from.h, to.h] ← Clip[from.y, to.y, from.h, to.h];
lastXF ← xf;
lastTo ← to;
IF xf.clipped
AND (lastIntersected ← Intersect[xf.deviceClipBox, to])
AND
NOT forceDumb
THEN {
bcX, bcY, scpX, scpY, scX, scY: REAL;
[bcX, bcY] ← GraphicsOps.DeviceToUser[xf.bufferC, from.x, from.y];
Graphics.SetCP[xf.bufferC, bcX, bcY];
GraphicsOps.DrawBitmap[
self: xf.bufferC,
bitmap: xf.deviceB,
w: from.w, h: from.h,
x: from.x, y: from.y,
xorigin: from.x, yorigin: from.y];
[scpX, scpY] ← Graphics.GetCP[xf.under];
[scX, scY] ← GraphicsOps.DeviceToUser[xf.under, to.x, to.y];
Graphics.SetCP[xf.under, scX, scY];
GraphicsOps.DrawBitmap[
self: xf.under,
bitmap: xf.bufferB,
w: from.w, h: from.h,
x: from.x, y: from.y,
xorigin: from.x, yorigin: from.y];
Graphics.SetCP[xf.under, scpX, scpY];
}
ELSE xf.under.procs.MoveDeviceRectangle[self: xf.under, width: from.w, height: from.h, fromX: from.x, fromY: from.y, toX: to.x, toY: to.y];
lastTo ← to;
END;
Xform:
PROC [altXform: Transform, old:
XYWH]
RETURNS [new:
XYWH] =
BEGIN
Map:
PROC [x, y:
INT]
RETURNS [
X,
Y:
INT] = {
OPEN altXform;
X ← Real.RoundLI[a*x + c*y + e];
Y ← Real.RoundLI[b*x + d*y + f];
};
Cannonize:
PROC [z1, z2:
INT]
RETURNS [min, diff:
INT] = {
min ← MIN[z1, z2];
diff ← ABS[z1-z2];
};
x1, x2, y1, y2: INT;
[x1, y1] ← Map[old.x, old.y];
[x2, y2] ← Map[old.x + old.w, old.y + old.h];
[new.x, new.w] ← Cannonize[x1, x2];
[new.y, new.h] ← Cannonize[y1, y2];
END;
Clip:
PROC [x1, x2, w1, w2:
INT]
RETURNS [x1a, x2a, w1a, w2a:
INT] = {
diff: INT ← 0 - MIN[x1, x2];
IF diff <= 0 THEN RETURN [x1, x2, w1, w2];
IF diff >= w1 OR diff >= w2 THEN RETURN [0, 0, 0, 0];
RETURN [x1+diff, x2+diff, w1-diff, w2-diff];
};
Intersect:
PROC [box: Box, xywh:
XYWH]
RETURNS [b:
BOOL] =
{b ← (box.xmax > xywh.x) AND (xywh.x+xywh.w > box.xmin) AND (box.ymax > xywh.y) AND (xywh.y+xywh.h > box.ymin)};
Mult:
PROC [left, right: Transform]
RETURNS [ans: Transform] = {
ans ← [
a: left.a*right.a + left.b*right.c,
b: left.a*right.b + left.b*right.d,
c: left.c*right.a + left.d*right.c,
d: left.c*right.b + left.d*right.d,
e: left.e*right.a + left.f*right.c + right.e,
f: left.e*right.b + left.f*right.d + right.f]};
Forker: TYPE = REF ForkerRep;
ForkerRep:
TYPE =
RECORD [
subs: ContextList ← NIL,
next: CARDINAL ← 1,
marks: ForkerMark ← NIL];
ContextList: TYPE = LIST OF Context;
forkerProcs:
REF Graphics.GraphicsProcs ←
NEW [Graphics.GraphicsProcs ← [
GetCP: ForkerGetCP,
SetCP: ForkerSetCP,
DrawTo: ForkerDrawTo,
DrawStroke: ForkerDrawStroke,
DrawArea: ForkerDrawArea,
DrawBox: ForkerDrawBox,
DrawImage: ForkerDrawImage,
Translate: ForkerTranslate,
Concat: ForkerConcat,
WorldToUser: ForkerWorldToUser,
UserToWorld: ForkerUserToWorld,
SetColor: ForkerSetColor,
GetColor: ForkerGetColor,
SetPaintMode: ForkerSetPaintMode,
SetFat: ForkerSetFat,
GetDefaultFont: ForkerGetDefaultFont,
SetDefaultFont: ForkerSetDefaultFont,
DrawChars: ForkerDrawChars,
ClipArea: ForkerClipArea,
ClipBox: ForkerClipBox,
IsPointVisible: ForkerIsPointVisible,
IsRectangular: ForkerIsRectangular,
GetBounds: ForkerGetBounds,
Visible: ForkerVisible,
Save: ForkerSave,
Restore: ForkerRestore,
DrawBits: ForkerDrawBits,
UserToDevice: ForkerUserToDevice,
DeviceToUser: ForkerDeviceToUser,
GetYMode: ForkerGetYMode,
SetYMode: ForkerSetYMode,
DrawTexturedBox: ForkerDrawTexturedBox,
Disable: ForkerDisable,
MoveDeviceRectangle: ForkerMoveDeviceRectangle
]];
ConsForker:
PROC [arg:
REF
ANY]
RETURNS [context: Context] =
BEGIN
consers: LIST OF REF ANY ← NARROW[arg];
f: Forker ← NEW [ForkerRep ← []];
context ←
NEW [Graphics.ContextRep ← [
procs: forkerProcs,
data: f]];
FOR consers ← consers, consers.rest
WHILE consers #
NIL
DO
conser: Conser ← NARROW[consers.first];
f.subs ← CONS[conser.Cons[conser.arg], f.subs];
f.subs.first.SetCP[0, 0];
ENDLOOP;
END;
ForkerGetCP:
PROC[self: Context, rounded:
BOOLEAN]
RETURNS[x,y:
REAL] =
BEGIN
f: Forker ← NARROW[self.data];
[x, y] ← f.subs.first.procs.GetCP[self: f.subs.first, rounded: rounded];
END;
ForkerSetCP:
PROC[self: Context, x,y:
REAL, rel:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.SetCP[self: c.first, x: x,y: y, rel: rel];
ENDLOOP;
END;
ForkerDrawTo:
PROC[self: Context, x,y:
REAL, rel:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawTo[self: c.first, x: x,y: y, rel: rel];
ENDLOOP;
END;
ForkerDrawStroke:
PROC[self: Context, path: Path, width:
REAL, closed:
BOOLEAN, ends: StrokeEnds] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawStroke[self: c.first, path: path, width: width, closed: closed, ends: ends];
ENDLOOP;
END;
ForkerDrawArea:
PROC[self: Context, path: Path, parityFill:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawArea[self: c.first, path: path, parityFill: parityFill];
ENDLOOP;
END;
ForkerDrawBox:
PROC[self: Context, box: Box] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawBox[self: c.first, box: box];
ENDLOOP;
END;
ForkerDrawImage:
PROC[self: Context, image: ImageRef, raw:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawImage[self: c.first, image: image, raw: raw];
ENDLOOP;
END;
ForkerTranslate:
PROC[self: Context, tx,ty:
REAL, round:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.Translate[self: c.first, tx: tx,ty: ty, round: round];
ENDLOOP;
END;
ForkerConcat:
PROC[self: Context, m11, m12, m21, m22:
REAL] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.Concat[self: c.first, m11: m11, m12: m12, m21: m21, m22: m22];
ENDLOOP;
END;
ForkerWorldToUser:
PROC[self: Context, wx, wy:
REAL]
RETURNS[x, y:
REAL] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
[x, y] ← c.first.procs.WorldToUser[self: c.first, wx: wx, wy: wy];
ENDLOOP;
END;
ForkerUserToWorld:
PROC[self: Context, x, y:
REAL]
RETURNS[wx, wy:
REAL] =
BEGIN
f: Forker ← NARROW[self.data];
[wx, wy] ← f.subs.first.procs.UserToWorld[self: f.subs.first, x: x, y: y];
END;
ForkerSetColor:
PROC[self: Context, color: Color] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.SetColor[self: c.first, color: color];
ENDLOOP;
END;
ForkerGetColor:
PROC[self: Context]
RETURNS[Color] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.GetColor[self: f.subs.first]];
END;
ForkerSetPaintMode:
PROC[self: Context, mode: PaintMode]
RETURNS[was: PaintMode] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
was ← c.first.procs.SetPaintMode[self: c.first, mode: mode];
ENDLOOP;
END;
ForkerSetFat:
PROC[self: Context, fat:
BOOLEAN]
RETURNS[was:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
was ← c.first.procs.SetFat[self: c.first, fat: fat];
ENDLOOP;
END;
ForkerGetDefaultFont:
PROC[self: Context]
RETURNS[FontRef] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.GetDefaultFont[self: f.subs.first]];
END;
ForkerSetDefaultFont:
PROC[self: Context, font: FontRef] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.SetDefaultFont[self: c.first, font: font];
ENDLOOP;
END;
ForkerDrawChars:
PROC[self: Context, font: FontRef, map:
PROC[
PROC[
CHAR]
RETURNS[
BOOL]]] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawChars[self: c.first, font: font, map: map];
ENDLOOP;
END;
ForkerClipArea:
PROC[self: Context, path: Path, parityFill:
BOOLEAN, exclude:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.ClipArea[self: c.first, path: path, parityFill: parityFill, exclude: exclude];
ENDLOOP;
END;
ForkerClipBox:
PROC[self: Context, box: Box, exclude:
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.ClipBox[self: c.first, box: box, exclude: exclude];
ENDLOOP;
END;
ForkerIsPointVisible:
PROC[self: Context, x,y:
REAL]
RETURNS[
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.IsPointVisible[self: f.subs.first, x: x,y: y]];
END;
ForkerIsRectangular:
PROC[self: Context]
RETURNS[
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.IsRectangular[self: f.subs.first]];
END;
ForkerGetBounds:
PROC[self: Context]
RETURNS[Box] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.GetBounds[self: f.subs.first]];
END;
ForkerVisible:
PROC[self: Context]
RETURNS[
BOOLEAN] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.Visible[self: f.subs.first]];
END;
ForkerMark: TYPE = REF ForkerMarkRep;
ForkerMarkRep:
TYPE =
RECORD [
mark: Mark,
subs: SaveList,
prev: ForkerMark];
SaveList: TYPE = LIST OF SaveRep;
SaveRep:
TYPE =
RECORD [
context: Context,
mark: Mark];
ForkerSave:
PROC[self: Context]
RETURNS[mark: Mark] =
BEGIN
f: Forker ← NARROW[self.data];
f.marks ←
NEW [ForkerMarkRep ← [
mark: mark ← [f.next],
subs: NIL,
prev: f.marks]];
f.next ← f.next + 1;
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
f.marks.subs ← CONS[[c.first, c.first.procs.Save[self: c.first]], f.marks.subs];
ENDLOOP;
END;
ForkerRestore:
PROC[self: Context, mark: Mark] =
BEGIN
f: Forker ← NARROW[self.data];
Pop: PROC = {--f.next ← f.marks.mark; --f.marks ← f.marks.prev};
IF mark = GraphicsBasic.nullMark THEN NULL ELSE
WHILE (
IF f.marks =
NIL
THEN
FALSE
ELSE (f.marks.mark # mark))
DO
Pop[];
ENDLOOP;
IF ((mark = GraphicsBasic.baseMark) # (f.marks = NIL)) OR errorTest THEN ERROR;
IF f.marks =
NIL
THEN
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.Restore[self: c.first, mark: GraphicsBasic.baseMark];
ENDLOOP
ELSE
FOR s: SaveList ← f.marks.subs, s.rest
WHILE s #
NIL
DO
s.first.context.Restore[s.first.mark];
ENDLOOP;
END;
errorTest: BOOLEAN ← FALSE;
ForkerDrawBits:
UNSAFE
PROC[self: Context, base:
LONG
POINTER, raster:
CARDINAL, bitsPerPixel: [0..16), x, y, w, h:
CARDINAL, xorigin, yorigin:
INTEGER] =
TRUSTED
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawBits[self: c.first, base: base, raster: raster, bitsPerPixel: bitsPerPixel, x: x, y: y, w: w, h: h, xorigin: xorigin, yorigin: yorigin];
ENDLOOP;
END;
ForkerUserToDevice:
PROC[self: Context, x, y:
REAL, rel:
BOOLEAN]
RETURNS[tx, ty:
REAL] =
BEGIN
f: Forker ← NARROW[self.data];
[tx, ty] ← f.subs.first.procs.UserToDevice[self: f.subs.first, x: x, y: y, rel: rel];
END;
ForkerDeviceToUser:
PROC[self: Context, tx, ty:
REAL, rel:
BOOLEAN]
RETURNS[x, y:
REAL] =
BEGIN
f: Forker ← NARROW[self.data];
[x, y] ← f.subs.first.procs.DeviceToUser[self: f.subs.first, tx: tx, ty: ty, rel: rel];
END;
ForkerGetYMode:
PROC[self: Context]
RETURNS[GraphicsBasic.YMode] =
BEGIN
f: Forker ← NARROW[self.data];
RETURN [f.subs.first.procs.GetYMode[self: f.subs.first]];
END;
ForkerSetYMode:
PROC[self: Context, mode: GraphicsBasic.YMode] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.SetYMode[self: c.first, mode: mode];
ENDLOOP;
END;
ForkerDrawTexturedBox:
PROC[self: Context, box: Box, texture: GraphicsBasic.Texture] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.DrawTexturedBox[self: c.first, box: box, texture: texture];
ENDLOOP;
END;
ForkerDisable:
PROC[self: Context] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.Disable[self: c.first];
ENDLOOP;
END;
ForkerMoveDeviceRectangle:
PROC[self: Context, width, height, fromX, fromY, toX, toY:
NAT] =
BEGIN
f: Forker ← NARROW[self.data];
FOR c: ContextList ← f.subs, c.rest
WHILE c #
NIL
DO
c.first.procs.MoveDeviceRectangle[self: c.first, width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY];
ENDLOOP;
END;
Feedbacker: TYPE = REF FeedbackerRep;
FeedbackerRep:
TYPE =
RECORD [
xmin, ymin, dx, dy: INT ← -1,
screenToView: Transform ← [1, 0, 0, 1, 0, 0]];
screenBitmap: GraphicsOps.BitmapRef;
PaintFeedbacker: ViewerClasses.PaintProc
--PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL]-- =
BEGIN
fb: Feedbacker ← NARROW[self.data];
News:
ENTRY
PROC
RETURNS [news:
BOOLEAN] =
BEGIN
IF self.cw = fb.dx
AND self.ch = fb.dy
THEN
BEGIN
xmin, ymin: INTEGER;
[xmin, ymin] ← ViewerOps.UserToScreenCoords[self, 0, 0];
IF xmin = fb.xmin AND ymin = fb.ymin THEN RETURN [FALSE];
fb.xmin ← xmin;
fb.ymin ← ymin;
END
ELSE
BEGIN
[fb.xmin, fb.ymin] ← ViewerOps.UserToScreenCoords[self, 0, 0];
fb.dx ← self.cw;
fb.dy ← self.ch;
END;
news ← TRUE;
END;
IF self.iconic THEN RETURN
ELSE IF News[] THEN TRUSTED {Process.Detach[FORK Adjust[self, fb]]}
ELSE
IF clear
THEN
BEGIN
AppendTransform[context, [
fb.screenToView.a, 0,
0, fb.screenToView.d,
fb.screenToView.e - fb.xmin,
fb.screenToView.f - fb.ymin]];
context.SetCP[0, 0];
GraphicsOps.DrawBitmap[self: context,
bitmap: screenBitmap,
w: screenBitmap.width,
h: screenBitmap.height,
x: 0, y: 0,
xorigin: 0, yorigin: screenBitmap.height];
END;
END;
Adjust:
PROC [v: Viewer, fb: Feedbacker] =
BEGIN
scale: REAL ← MIN[REAL[fb.dx]/screenBitmap.width, REAL[fb.dy]/screenBitmap.height];
fb.screenToView.b ← fb.screenToView.c ← 0;
fb.screenToView.a ← fb.screenToView.d ← scale;
fb.screenToView.e ← (fb.xmin + fb.dx/2.0) - scale*(screenBitmap.width/2.0);
fb.screenToView.f ← (fb.ymin + fb.dy/2.0) - scale*(screenBitmap.height/2.0);
Replace[
NEW [ConserRep ← [
ConsForker,
LIST[
NEW [ConserRep ← [ConsXFormer,
NEW [Transform ← Mult[fb.screenToView, fb.screenToView]] ]],
NEW [ConserRep ← [ConsXFormer, NEW [Transform ← fb.screenToView]]],
vanillaConser
] ]] ];
END;
MakeFeedbacker:
PROC =
BEGIN
it ← ViewerOps.CreateViewer[
flavor: $Feedbacker,
info: [
data: NEW [FeedbackerRep ← []],
name: "Feedbacker",
iconic: TRUE],
paint: TRUE];
END;
it: Viewer ← NIL;
feedbacker: ViewerClasses.ViewerClass ←
NEW [ViewerClasses.ViewerClassRec ← [
flavor: $Feedbacker,
paint: PaintFeedbacker]];
Setup:
PROC =
BEGIN
screen: Context ← Graphics.NewContext[];
screenContextData: ContextData;
screenContextData ← NARROW[screen.data];
worldToScreen ← screenContextData.device.GetMatrix[screenContextData.device];
screenToWorld ← CGMatrix.Copy[worldToScreen];
CGMatrix.Invert[screenToWorld];
screenBitmap ← GraphicsOps.ScreenBitmap[];
ViewerOps.RegisterViewerClass[flavor: $Feedbacker, class: feedbacker];
END;
Setup[];
END.