-- GraphicsImpl.mesa
-- Last edited by Doug Wyatt, September 14, 1982 4:38 pm

DIRECTORY
CGArea USING [New, Ref],
CGBitmapDevice USING [New],
CGClipper USING [Assign, Bounds, Copy, New, Ref, Rep, SetBox, TestPoint],
CGContext USING [Ref, Rep, TouchClipper, TouchMatrix],
CGDevice USING [Ref, Rep],
CGFont USING [New, Ref, Rep],
CGMatrix USING [Assign, Copy, Inv, Map, Ref, Rep],
CGPath USING [CurveTo, LastPoint, LineTo, MoveTo, New, Rectangle, Reset],
CGPrivate, -- USING almost everything
CGReducer USING [New, Ref],
CGSource USING [Ref, Rep],
CGStorage USING [qZone],
Graphics USING [black, Context, ContextRep, ErrorType, GraphicsProcs, WarningType],
GraphicsBasic USING [Box, Color, FontRef, ImageRef, Mark, PaintMode, Path, Vec, YMode],
GraphicsOps USING [BitmapRef],
RealFns USING [CosDeg, SinDeg];

GraphicsImpl: CEDAR PROGRAM
IMPORTS CGArea, CGBitmapDevice, CGClipper, CGContext, CGFont,
CGMatrix, CGPath, CGPrivate, CGReducer, CGStorage, RealFns
EXPORTS Graphics, GraphicsBasic, GraphicsOps = { OPEN GraphicsBasic;

DeviceObject: PUBLIC TYPE = CGDevice.Rep; -- export to GraphicsBasic
FontObject: PUBLIC TYPE = CGFont.Rep; -- export to GraphicsBasic

Context: TYPE = Graphics.Context;
ContextData: TYPE = CGContext.Ref;

repZone: ZONE = CGStorage.qZone;
srcZone: ZONE = CGStorage.qZone;

procs: REF Graphics.GraphicsProcs ← repZone.NEW[Graphics.GraphicsProcs ← [
GetCP: CGPrivate.GetCP,
SetCP: CGPrivate.SetCP,
DrawTo: CGPrivate.DrawTo,
DrawStroke: CGPrivate.DrawStroke,
DrawArea: CGPrivate.DrawArea,
DrawBox: CGPrivate.DrawBox,
DrawImage: CGPrivate.DrawImage,
Translate: CGPrivate.Translate,
Concat: CGPrivate.Concat,
WorldToUser: CGPrivate.WorldToUser,
UserToWorld: CGPrivate.UserToWorld,
SetColor: CGPrivate.SetColor,
GetColor: CGPrivate.GetColor,
SetPaintMode: CGPrivate.SetPaintMode,
SetFat: CGPrivate.SetFat,
GetDefaultFont: CGPrivate.GetDefaultFont,
SetDefaultFont: CGPrivate.SetDefaultFont,
DrawChars: CGPrivate.DrawChars,
ClipArea: CGPrivate.ClipArea,
ClipBox: CGPrivate.ClipBox,
IsPointVisible: CGPrivate.IsPointVisible,
IsRectangular: CGPrivate.IsRectangular,
GetBounds: CGPrivate.GetBounds,
Visible: CGPrivate.Visible,
Save: CGPrivate.Save,
Restore: CGPrivate.Restore,
DrawBits: CGPrivate.DrawBits,
UserToDevice: CGPrivate.UserToDevice,
DeviceToUser: CGPrivate.DeviceToUser,
GetYMode: CGPrivate.GetYMode,
SetYMode: CGPrivate.SetYMode,
DrawTexturedBox: CGPrivate.DrawTexturedBox,
Disable: CGPrivate.Disable,
MoveDeviceRectangle: CGPrivate.MoveDeviceRectangle
]];

-- Signals --

Warning: PUBLIC SIGNAL[type: Graphics.WarningType] = CODE;
Error: PUBLIC ERROR[type: Graphics.ErrorType] = CODE;

-- Creating new contexts --

NewContext: PUBLIC PROC[device: CGDevice.Ref] RETURNS[Context] = {
d: ContextData ← repZone.NEW[CGContext.Rep ← [
device: NIL, -- output device
cp: [0,0], -- current position
matrix: NIL, -- current matrix
clipper: NIL, -- current clipper
reducer: NIL, -- reducer
area: NIL,
src: NIL, -- current source
haveRaster: FALSE,
boxing: FALSE, newbox: FALSE, -- boxing state
bbox: NIL
]];
m: CGMatrix.Ref ← NIL;
dbase: LONG POINTERNIL;
drast: CARDINAL ← 0;
IF device=NIL THEN device ← CGBitmapDevice.New[NIL,0,0]; -- the screen
m ← device.GetMatrix[device]; -- world to device transformation
d.device ← device;
d.cp ← CGMatrix.Map[m,[0,0]];
d.matrix ← CGMatrix.Copy[m];
d.clipper ← CGClipper.New[1];
d.reducer ← CGReducer.New[8];
d.area ← CGArea.New[4];
CGClipper.SetBox[d.clipper,device.GetBounds[device]];
d.src ← srcZone.NEW[CGSource.Rep ← [type: const, fat: FALSE, mode: opaque,
bps: 0, color: Graphics.black, xbase: NIL, xrast: 0, Get: NIL]];
IF device.GetRaster#NIL THEN { [dbase,drast] ← device.GetRaster[device] };
IF dbase#NIL THEN { d.dbase ← dbase; d.drast ← drast; d.haveRaster ← TRUE };
RETURN[repZone.NEW[Graphics.ContextRep ← [procs, d]]];
};

CopyContext: PUBLIC PROC[self: Context] RETURNS[Context] = {
d: ContextData ← NARROW[self.data];
device: CGDevice.Ref ← d.device;
copy: ContextData ← repZone.NEW[CGContext.Rep ← [
device: device, -- output device
cp: d.cp, -- current position
matrix: NIL, -- current matrix
clipper: NIL, -- current clipper
reducer: NIL, -- reducer
area: NIL,
src: NIL, -- current source
boxing: FALSE, newbox: FALSE, -- boxing state
bbox: NIL,
haveRaster: d.haveRaster,
dbase: d.dbase,
drast: d.drast
]];
copy.matrix ← CGMatrix.Copy[d.matrix];
copy.clipper ← CGClipper.Copy[d.clipper];
copy.reducer ← CGReducer.New[8];
copy.area ← CGArea.New[4];
copy.src ← srcZone.NEW[CGSource.Rep ← d.src^];
RETURN[repZone.NEW[Graphics.ContextRep ← [self.procs, copy]]];
};

-- Procedures independent of data representation --

minPathSize: NAT = 4;

NewPath: PUBLIC PROC[initialSize: INT ← 0] RETURNS[Path] = {
size: NATMIN[MAX[initialSize, minPathSize], LAST[NAT]];
path: Path ← CGPath.New[size];
RETURN[path]
};

LastPoint: PUBLIC PROC[self: Path] RETURNS[x, y: REAL] = {
v: Vec ← CGPath.LastPoint[self]; RETURN[v.x, v.y];
};

FlushPath: PUBLIC PROC[self: Path] = {
CGPath.Reset[self];
};

MoveTo: PUBLIC PROC[self: Path, x, y: REAL, flush: BOOLEANTRUE] = {
IF flush THEN CGPath.Reset[self];
CGPath.MoveTo[self, [x, y]];
};

LineTo: PUBLIC PROC[self: Path, x, y: REAL] = {
CGPath.LineTo[self, [x, y]];
};

CurveTo: PUBLIC PROC[self: Path, x1, y1, x2, y2, x3, y3: REAL] = {
CGPath.CurveTo[self, [x1, y1], [x2, y2], [x3, y3]];
};

Rectangle: PUBLIC PROC[self: Path, x0, y0, x1, y1: REAL] = {
CGPath.Rectangle[self, [x0, y0], [x1, y1]];
};


Rotate: PUBLIC PROC[self: Context, angle: REAL] = {
cos, sin: REAL;
eps: REAL = 1E-6;
TRUSTED { cos ← RealFns.CosDeg[angle]; sin ← RealFns.SinDeg[angle] };
IF ABS[sin]<eps THEN { sin ← 0; cos ← (IF cos>0 THEN 1 ELSE -1) };
IF ABS[cos]<eps THEN { cos ← 0; sin ← (IF sin>0 THEN 1 ELSE -1) };
self.procs.Concat[self, cos,sin,-sin,cos];
};

Map: PUBLIC PROC[sc,dc: Context, sx,sy: REAL] RETURNS[dx,dy: REAL] = {
wx, wy: REAL;
[wx, wy] ← sc.procs.UserToWorld[sc, sx, sy];
[dx, dy] ← dc.procs.WorldToUser[dc, wx, wy];
};

BitmapRef: TYPE = GraphicsOps.BitmapRef;

DrawBitmap: PUBLIC PROC[self: Context, bitmap: BitmapRef,
w, h: CARDINAL, x, y: CARDINAL, xorigin, yorigin: INTEGER] = {
w ← MIN[w, bitmap.width-(x ← MIN[x, bitmap.width])];
h ← MIN[h, bitmap.height-(y ← MIN[y, bitmap.height])];
TRUSTED { self.procs.DrawBits[self,
LOOPHOLE[bitmap.base], bitmap.raster, 0, x, y, w, h, xorigin, yorigin] };
};

NewContextFromBitmap: PUBLIC PROC[bitmap: BitmapRef] RETURNS[Context] = TRUSTED {
dev: CGDevice.Ref ← CGBitmapDevice.New[
base: LOOPHOLE[bitmap.base], raster: bitmap.raster, height: bitmap.height];
RETURN[NewContext[dev]];
};


MapperObject: PUBLIC TYPE = CGMatrix.Rep; -- export to GraphicsBasic
ClipperObject: PUBLIC TYPE = CGClipper.Rep; -- export to GraphicsBasic

GetMapper: PUBLIC PROC[self: Context] RETURNS[CGMatrix.Ref] = {
ctx: ContextData ← NARROW[self.data];
m: CGMatrix.Ref ← ctx.matrix;
RETURN[CGMatrix.Copy[m]];
};
SetMapper: PUBLIC PROC[self: Context, mapper: CGMatrix.Ref] = {
ctx: ContextData ← NARROW[self.data];
m: CGMatrix.Ref ← ctx.matrix;
CGContext.TouchMatrix[ctx];
CGMatrix.Assign[m,mapper];
};

GetClipper: PUBLIC PROC[self: Context] RETURNS[CGClipper.Ref] = {
ctx: ContextData ← NARROW[self.data];
clipper: CGClipper.Ref ← ctx.clipper;
RETURN[CGClipper.Copy[clipper]];
};
SetClipper: PUBLIC PROC[self: Context, clipper: CGClipper.Ref] = {
ctx: ContextData ← NARROW[self.data];
CGContext.TouchClipper[ctx];
CGClipper.Assign[ctx.clipper,clipper];
};

TestVisible: PUBLIC PROC[mapper: CGMatrix.Ref, clipper: CGClipper.Ref, x,y: REAL]
RETURNS[BOOLEAN] = {
p: Vec ← CGMatrix.Map[mapper,[x,y]];
RETURN[CGClipper.TestPoint[clipper,p]];
};

TestBounds: PUBLIC PROC[mapper: CGMatrix.Ref, clipper: CGClipper.Ref]
RETURNS[Box] = {
box: Box ← CGClipper.Bounds[clipper];
m: CGMatrix.Ref ← mapper;
result: Box;
a,b,c,d: Vec;
IF m.rectangular THEN {
a ← CGMatrix.Inv[m,[box.xmin,box.ymin]];
c ← CGMatrix.Inv[m,[box.xmax,box.ymax]];
result.xmin ← MIN[a.x,c.x];
result.xmax ← MAX[a.x,c.x];
result.ymin ← MIN[a.y,c.y];
result.ymax ← MAX[a.y,c.y];
}
ELSE {
a ← CGMatrix.Inv[m,[box.xmin,box.ymin]];
b ← CGMatrix.Inv[m,[box.xmax,box.ymin]];
c ← CGMatrix.Inv[m,[box.xmax,box.ymax]];
d ← CGMatrix.Inv[m,[box.xmin,box.ymax]];
result.xmin ← MIN[a.x,b.x,c.x,d.x];
result.xmax ← MAX[a.x,b.x,c.x,d.x];
result.ymin ← MIN[a.y,b.y,c.y,d.y];
result.ymax ← MAX[a.y,b.y,c.y,d.y];
};
RETURN[result];
};

BeginBox: PUBLIC PROC[self: Context] = {
ctx: ContextData ← NARROW[self.data];
SIGNAL Warning[notImplemented];
ctx.boxing ← ctx.newbox ← TRUE;
};

EndBox: PUBLIC PROC[self: Context] RETURNS[Box] = {
ctx: ContextData ← NARROW[self.data];
box: Box ← [0,0,0,0];
SIGNAL Warning[notImplemented];
ctx.boxing ← FALSE;
RETURN[box];
};

UnsafeNewFont: PUBLIC UNSAFE PROC[lp: LONG POINTER]
RETURNS[CGFont.Ref] = UNCHECKED { RETURN[CGFont.New[lp]] };

}.