GLImpl.mesa - implements the graphics list interface
last changed by Mik Lamming, January 4, 1983 11:05 am
Last Edited by: Stone, March 18, 1983 2:15 pm
DIRECTORY
CGPath USING [Copy],
GL,
Graphics USING [ClipArea, ClipBox, Concat, Context, ContextRep, DrawArea, DrawBox, DrawImage, DrawRope, DrawStroke, DrawTo, GraphicsProcs, NewContext, nullMark, Save, Restore, SetColor, SetCP, SetFat, SetDefaultFont, SetPaintMode, Translate],
GraphicsBasic USING [Box, Color, FontRef, ImageRef, Mark, PaintMode, Path, StrokeEnds, Texture, YMode],
GraphicsOps USING [Disable, DrawTexturedBox, MoveDeviceRectangle, SetYMode],
Rope USING [Concat, FromChar, ROPE];
GLImpl: CEDAR MONITOR
IMPORTS CGPath, Graphics, GraphicsOps, Rope
EXPORTS GL = {
Types
This is not a CedarGraphics context! It is a special structure created by the graphics list package. It holds the graphics list while it is being built and a pointer to a disabled screen context. The latter is used to hold CedarGraphics variables such as CP, which may be needed by the client while it is computing the list. Note that pointers to both the head and tail of the list are maintained.
Context: TYPE = REF ContextRec;
ContextRec:
TYPE =
RECORD [
context: Graphics.Context ← NIL,
head, tail: List ← NIL
];
This defines the range of nodes that comprise a graphics list. Note that there is one for each CedarGraphics function that modifies the graphics context.
CommandType: TYPE = {LockNode, SetCP, DrawTo, DrawStroke, DrawArea, DrawBox, DrawImage, Translate, Concat, SetColor, SetPaintMode, SetFat, SetDefaultFont, DrawChars, ClipArea, ClipBox, Save, Restore, DrawBits, SetYMode, DrawTexturedBox, Disable, MoveDeviceRectangle };
Graphics lists comprise a singly linked sequence of the following types of node. Every list has a LockNode at its head which contains the condition variables for ensuring sequential access.
List: PUBLIC TYPE = REF ListRec;
ListRec:
PUBLIC TYPE =
RECORD [
next: List, -- pointer to the next mode in the graphics list
command:
SELECT commandType: CommandType
FROM
LockNode => [locked:BOOLEAN, notInUse:CONDITION],
SetCP => [x,y: REAL, rel: BOOLEAN],
DrawTo => [x,y: REAL, rel: BOOLEAN],
DrawStroke => [path: GraphicsBasic.Path, width: REAL, closed: BOOLEAN,
ends: GraphicsBasic.StrokeEnds],
DrawArea => [path: GraphicsBasic.Path, parityFill: BOOLEAN],
DrawBox => [box: GraphicsBasic.Box],
DrawImage => [image: GraphicsBasic.ImageRef, raw: BOOLEAN],
Translate => [tx,ty: REAL, round: BOOLEAN],
Concat => [m11,m12,m21,m22: REAL],
SetColor => [color: GraphicsBasic.Color],
SetPaintMode => [mode: GraphicsBasic.PaintMode],
SetFat => [fat: BOOLEAN],
SetDefaultFont => [font: GraphicsBasic.FontRef],
DrawChars => [font: GraphicsBasic.FontRef, rope: Rope.ROPE], --N.B., this doesn't match call args
ClipArea => [path: GraphicsBasic.Path, parityFill: BOOLEAN, exclude: BOOLEAN],
ClipBox => [box: GraphicsBasic.Box, exclude: BOOLEAN],
Save => [mark: CARDINAL],
Restore => [mark: CARDINAL],
DrawBits => [base: LONG POINTER, raster: CARDINAL, bitsPerPixel: [0..16),
x, y, w, h: CARDINAL, xorigin, yorigin: INTEGER],
SetYMode => [mode: GraphicsBasic.YMode],
DrawTexturedBox => [box: GraphicsBasic.Box, texture: GraphicsBasic.Texture],
Disable => [],
MoveDeviceRectangle => [width, height, fromX, fromY, toX, toY: NAT]
ENDCASE
];
Globals
This vector of procedure calls gets plugged into the regular CedarGraphics context structure which is returned to the client by the procedure GL.Start. These preocedures effectively ambush calls to CedarGraphics and construct list nodes.
glProcs:
REF Graphics.GraphicsProcs ←
NEW[Graphics.GraphicsProcs ← [
GetCP: AmbushGetCP,
SetCP: AmbushSetCP,
DrawTo: AmbushDrawTo,
DrawStroke: AmbushDrawStroke,
DrawArea: AmbushDrawArea,
DrawBox:AmbushDrawBox,
DrawImage: AmbushDrawImage,
Translate: AmbushTranslate,
Concat: AmbushConcat,
WorldToUser: AmbushWorldToUser,
UserToWorld: AmbushUserToWorld,
SetColor: AmbushSetColor,
GetColor: AmbushGetColor,
SetPaintMode: AmbushSetPaintMode,
SetFat: AmbushSetFat,
GetDefaultFont: AmbushGetDefaultFont,
SetDefaultFont: AmbushSetDefaultFont,
DrawChars: AmbushDrawChars,
ClipArea: AmbushClipArea,
ClipBox: AmbushClipBox,
IsPointVisible: AmbushIsPointVisible,
IsRectangular: AmbushIsRectangular,
GetBounds: AmbushGetBounds,
Visible: AmbushVisible,
Save: AmbushSave,
Restore: AmbushRestore,
DrawBits: AmbushDrawBits,
UserToDevice: AmbushUserToDevice,
DeviceToUser: AmbushDeviceToUser,
GetYMode: AmbushGetYMode,
SetYMode: AmbushSetYMode,
DrawTexturedBox: AmbushDrawTexturedBox,
Disable: AmbushDisable,
MoveDeviceRectangle: AmbushMoveDeviceRectangle
]];
Client Interface Procedures
Start:
PUBLIC
PROC[oldList: List ←
NIL]
RETURNS[dc:Graphics.Context] = {
Creates an ambushing context (dc). dc is a record which looks like a CedarGraphics context. The procs portion points to the GraphicsList ambushing procedures. The data portion points to a record containing the graphics list under construction and a pointer to a regular but disabled CedarGraphics context. The graphics list is monitored!
context: Context;
tail: List;
newDc: Graphics.Context ← Graphics.NewContext[]; -- get a new screen context
IF oldList = NIL THEN oldList ← NEW[LockNode ListRec]; -- client wants new list
Lock[oldList]; -- prevent other reads or writes
GraphicsOps.Disable[newDc]; -- only want its variables not its output
FOR tail ← oldList, tail.next
WHILE tail.next #
NIL
DO
--find the tail for appends
ENDLOOP;
Make a special structure to hold the list and the dummy context for interpretation by ambushing routines.
context ←
NEW[ContextRec ← [context: newDc, head: oldList, tail: tail]];
Now fake a new CG context, plug the special structure into the data field and pass it back to the client
dc ← NEW[Graphics.ContextRep ← [procs:glProcs, data:context]];
};
End:
PUBLIC
PROC[dc: Graphics.Context]
RETURNS [list: List] = {
Extracts the current list from the context and returns it to the user.
IF dc.procs # glProcs
THEN
RETURN[NIL];
list ← NARROW[dc.data, Context].head;
UnLock[list]; -- free it up for the next client
};
Draw:
PUBLIC PROC[context: Graphics.Context, list: List] =
TRUSTED {
Display a graphics list. But only if it is not being updated
head:List ← list;
Lock[head]; -- prevent other reads or writes
WHILE list #
NIL
DO
-- execute each node on the list
WITH node:list
SELECT
FROM
SetCP =>
Graphics.SetCP[context, node.x, node.y, node.rel];
DrawTo =>
Graphics.DrawTo[context, node.x, node.y, node.rel];
DrawStroke =>
Graphics.DrawStroke[context, node.path, node.width, node.closed, node.ends];
DrawArea =>
Graphics.DrawArea[context, node.path, node.parityFill];
DrawBox =>
Graphics.DrawBox[context, node.box];
DrawImage =>
Graphics.DrawImage[context, node.image, node.raw];
Translate =>
Graphics.Translate[context, node.tx, node.ty, node.round];
Concat =>
Graphics.Concat[context, node.m11, node.m12, node.m21, node.m22];
SetColor =>
Graphics.SetColor[context, node.color];
SetPaintMode =>
[] ← Graphics.SetPaintMode[context, node.mode];
SetFat =>
[] ← Graphics.SetFat[context, node.fat];
SetDefaultFont =>
Graphics.SetDefaultFont[context, node.font];
DrawChars =>
Graphics.DrawRope[self: context, rope: node.rope, font: node.font];
ClipArea =>
Graphics.ClipArea[context, node.path, node.parityFill, node.exclude];
ClipBox =>
Graphics.ClipBox[context, node.box, node.exclude];
Save =>
[] ← Graphics.Save[context];
Restore =>
Graphics.Restore[context];
DrawBits =>
Graphics.DrawBits[context, node.base, node.raster, node.bitsPerPixel, node.x, node.y, node.w, node.h, node.xorigin, node.yorigin];
SetYMode =>
GraphicsOps.SetYMode[context, node.mode];
DrawTexturedBox =>
GraphicsOps.DrawTexturedBox[context, node.box, node.texture];
Disable =>
GraphicsOps.Disable[context];
MoveDeviceRectangle =>
GraphicsOps.MoveDeviceRectangle[context, node.width, node.height, node.fromX, node.fromY, node.toX, node.toY];
ENDCASE;
list ← list.next;
ENDLOOP;
UnLock[head]; -- free it up for the next client
};
Internal Monitor Procedures
Lock:
ENTRY
PROC[list: List] =
TRUSTED {
Mark a graphics list as 'In Use' and set its monitor lock
WITH node:list
SELECT
FROM
LockNode => {
WHILE node.locked
DO
WAIT node.notInUse;
ENDLOOP;
node.locked ← TRUE;
};
ENDCASE => ERROR;
};
UnLock:
ENTRY
PROC[list: List] =
TRUSTED {
Mark a graphics list as 'not in use' and clear its monitor lock
WITH node:list
SELECT
FROM
LockNode => {
node.locked ← FALSE;
NOTIFY node.notInUse;
};
ENDCASE => ERROR;
};
Ambushing Procedures
AmbushGetCP:
PUBLIC
PROC[self: Graphics.Context, rounded:
BOOLEAN]
RETURNS[x,y:
REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.GetCP[glContext.context, rounded];
};
AmbushSetCP:
PUBLIC
PROCEDURE[self: Graphics.Context, x,y:
REAL, rel:
BOOLEAN ←
FALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.SetCP[glContext.context, x, y, rel];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetCP [x: x, y: y, rel: rel]]];
};
AmbushDrawTo:
PUBLIC
PROCEDURE[self: Graphics.Context, x,y:
REAL, rel:
BOOLEAN ←
FALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawTo[glContext.context, x, y, rel];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawTo [x: x, y: y, rel: rel]]];
};
AmbushDrawStroke:
PUBLIC
PROCEDURE[self: Graphics.Context, path: GraphicsBasic.Path, width:
REAL, closed:
BOOLEAN, ends: GraphicsBasic.StrokeEnds] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawStroke[glContext.context, path, width, closed, ends];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command:DrawStroke [path: CGPath.Copy[path], width: width, closed: closed, ends: ends]]];
};
AmbushDrawArea:
PUBLIC
PROCEDURE[self: Graphics.Context, path: GraphicsBasic.Path, parityFill:
BOOLEAN ←
FALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawArea[glContext.context, path, parityFill];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command:DrawArea [path: CGPath.Copy[path], parityFill: parityFill]]];
};
AmbushDrawBox:
PUBLIC
PROCEDURE[self: Graphics.Context, box: GraphicsBasic.Box] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawBox[glContext.context, box];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawBox [box: box]]];
};
AmbushDrawImage:
PUBLIC
PROCEDURE[self: Graphics.Context, image: GraphicsBasic.ImageRef, raw:
BOOLEAN] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawImage[glContext.context, image, raw];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawImage [image: image, raw: raw]]];
};
AmbushTranslate:
PUBLIC
PROC[self: Graphics.Context, tx,ty:
REAL, round:
BOOLEAN ←
FALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Translate[glContext.context, tx, ty, round];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Translate [tx: tx, ty: ty, round: round]]];
};
AmbushConcat:
PUBLIC
PROCEDURE[self: Graphics.Context, m11,m12,m21,m22:
REAL] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Concat[glContext.context, m11, m12, m21, m22];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Concat [m11: m11, m12: m12, m21: m21, m22: m22]]];
};
AmbushWorldToUser:
PUBLIC
PROC[self: Graphics.Context, wx,wy:
REAL]
RETURNS[x,y:
REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.WorldToUser[glContext.context, wx, wy];
};
AmbushUserToWorld:
PUBLIC
PROC[self: Graphics.Context, x,y:
REAL]
RETURNS[wx,wy:
REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.UserToWorld[glContext.context, x, y];
};
AmbushSetColor:
PUBLIC
PROCEDURE[self: Graphics.Context, color: GraphicsBasic.Color] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.SetColor[glContext.context, color];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetColor [color: color]]];
};
AmbushGetColor:
PUBLIC
PROC[self: Graphics.Context]
RETURNS[GraphicsBasic.Color] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.GetColor[glContext.context];
};
AmbushSetPaintMode:
PUBLIC
PROC[self: Graphics.Context, mode: GraphicsBasic.PaintMode]
RETURNS[GraphicsBasic.PaintMode] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetPaintMode [mode: mode]]];
RETURN glContext.context.procs.SetPaintMode[glContext.context, mode];
AmbushSetFat:
PUBLIC
PROCEDURE[self: Graphics.Context, fat:
BOOLEAN]
RETURNS[
BOOLEAN] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetFat [fat: fat]]];
RETURN glContext.context.procs.SetFat[glContext.context, fat];
};
AmbushGetDefaultFont:
PUBLIC
PROC[self: Graphics.Context]
RETURNS[GraphicsBasic.FontRef] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.GetDefaultFont[glContext.context];
};
AmbushSetDefaultFont:
PUBLIC
PROC[self: Graphics.Context, font: GraphicsBasic.FontRef] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetDefaultFont [font: font]]];
glContext.context.procs.SetDefaultFont[glContext.context, font];
};
AmbushDrawChars:
PUBLIC
PROC[self: Graphics.Context, font: GraphicsBasic.FontRef, map:
PROC[
SAFE
PROC[
CHAR]
RETURNS[
BOOL]]] = {
glContext: Context ← NARROW[self.data];
r: Rope.ROPE;
MakeRope:
SAFE
PROC[c:
CHAR]
RETURNS[
BOOL] =
CHECKED {
r ← Rope.Concat[r, Rope.FromChar[c]];
RETURN[FALSE]};
map[MakeRope];
glContext.context.procs.DrawChars[glContext.context, font, map];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawChars [font: font, rope: r]]];
};
AmbushClipArea:
PUBLIC
PROCEDURE[self: Graphics.Context, path: GraphicsBasic.Path, parityFill:
BOOLEAN, exclude:
BOOLEAN] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.ClipArea[glContext.context, path, parityFill, exclude];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: ClipArea [path: CGPath.Copy[path], parityFill: parityFill, exclude: exclude]]];
};
AmbushClipBox:
PUBLIC
PROCEDURE[self: Graphics.Context, box: GraphicsBasic.Box,
exclude: BOOLEAN ← FALSE] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.ClipBox[glContext.context, box, exclude];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: ClipBox [box: box, exclude: exclude]]];
};
AmbushIsPointVisible:
PUBLIC
PROC[self: Graphics.Context, x,y:
REAL]
RETURNS[
BOOLEAN] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.IsPointVisible[glContext.context, x, y]];
};
AmbushIsRectangular:
PUBLIC
PROC[self: Graphics.Context]
RETURNS[
BOOLEAN] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.IsRectangular[glContext.context]];
};
AmbushGetBounds:
PUBLIC
PROC[self: Graphics.Context]
RETURNS[GraphicsBasic.Box] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.GetBounds[glContext.context]];
};
AmbushVisible:
PUBLIC
PROC[self: Graphics.Context]
RETURNS[
BOOLEAN] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.Visible[glContext.context]];
};
AmbushSave:
PUBLIC
PROCEDURE[self: Graphics.Context]
RETURNS[GraphicsBasic.Mark] = {
glContext: Context ← NARROW[self.data];
mark: GraphicsBasic.Mark ← glContext.context.procs.Save[glContext.context];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Save [mark: 0]]];
RETURN[mark];
};
AmbushRestore: PUBLIC PROCEDURE[self: Graphics.Context, mark: GraphicsBasic.Mark ← Graphics.nullMark] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Restore[glContext.context, mark];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Restore [mark: 0]]];
};
AmbushDrawBits:
PUBLIC
UNSAFE
PROC[self: Graphics.Context,
base:
LONG
POINTER, raster:
CARDINAL, bitsPerPixel: [0..16),
x, y, w, h:
CARDINAL, xorigin, yorigin:
INTEGER] =
TRUSTED {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawBits[glContext.context, base, raster, bitsPerPixel, x, y, w, h, xorigin, yorigin];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawBits [base: base, raster: raster, bitsPerPixel: bitsPerPixel, x: x, y: y, w: w, h: h, xorigin: xorigin, yorigin: yorigin]]];
};
AmbushUserToDevice:
PUBLIC
PROC[self: Graphics.Context, x, y:
REAL, rel:
BOOLEAN]
RETURNS[tx, ty:
REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.UserToDevice[glContext.context, x, y, rel];
};
AmbushDeviceToUser:
PUBLIC
PROC[self: Graphics.Context, tx, ty:
REAL, rel:
BOOLEAN]
RETURNS[x, y:
REAL] = {
glContext: Context ← NARROW[self.data];
RETURN glContext.context.procs.DeviceToUser[glContext.context, tx, ty, rel];
};
AmbushGetYMode:
PUBLIC
PROC[self: Graphics.Context]
RETURNS[GraphicsBasic.YMode] = {
glContext: Context ← NARROW[self.data];
RETURN[glContext.context.procs.GetYMode[glContext.context]];
};
AmbushSetYMode:
PUBLIC
PROC[self: Graphics.Context, mode: GraphicsBasic.YMode] = {
glContext: Context ← NARROW[self.data];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: SetYMode [mode: mode]]];
glContext.context.procs.SetYMode[glContext.context, mode];
};
AmbushDrawTexturedBox:
PUBLIC
PROC[self: Graphics.Context, box: GraphicsBasic.Box, texture: GraphicsBasic.Texture] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.DrawTexturedBox[glContext.context, box, texture];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: DrawTexturedBox [box: box, texture: texture]]];
};
AmbushDisable:
PUBLIC
PROC[self: Graphics.Context] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.Disable[glContext.context];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: Disable []]];
};
AmbushMoveDeviceRectangle:
PUBLIC
PROC[self: Graphics.Context, width, height, fromX, fromY, toX, toY:
NAT] = {
glContext: Context ← NARROW[self.data];
glContext.context.procs.MoveDeviceRectangle[glContext.context, width, height, fromX, fromY, toX, toY];
glContext.tail ← glContext.tail.next ← NEW[ListRec ← [command: MoveDeviceRectangle[width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]]];
};
}.