-- AltoDeviceImpl.mesa
-- Last changed by Doug Wyatt, October 29, 1980 3:10 PM
DIRECTORY
Device,
OpaqueDevice USING [],
AltoDevice USING [Bitmap, ScreenBitmap],
Vector USING [Vec, Matrix, Add, Sub],
Mapper USING [Handle, Translate, Concat],
Area USING [Rec, Handle, Vertices, Rectangle, Rectangular, Free],
Poly USING [NewRec],
Pipe USING [Handle, Object, Procs, Put, Free],
Clipper USING [Handle, State, NewPipe, Push, Test, Pop],
Style USING [Data, PaintingFunction, Texture],
Font USING [Id],
Text USING [Info, Handle, Object, Procs],
AltoFont USING [Handle, BBox, Rast, New, Default,
Character, CharBox, StringBox, FontBox, Free],
Blt USING [Handle, New, Source, SetX, SetY, SetBox, Rect, Ref, Free],
Memory USING [NewZone, mds],
Real USING [Fix, FixC, FixI],
InlineDefs USING [LongNumber, HighHalf],
TimeDefs USING [PackedTime, CurrentDayTime];
AltoDeviceImpl: PROGRAM
IMPORTS AltoDevice,Memory,Vector,Mapper,Clipper,
Area,Poly,Pipe,AltoFont,Blt,
Real,InlineDefs,TimeDefs
EXPORTS AltoDevice,OpaqueDevice SHARES Device,Pipe,Text = {
OPEN Device;
pause: BOOLEAN_FALSE;
zone: UNCOUNTED ZONE = Memory.NewZone["AltoDeviceImpl"];
mds: MDSZone = Memory.mds;
Paint: TYPE = Style.PaintingFunction;
Texture: TYPE = Style.Texture;
DeviceObject: PUBLIC TYPE = Device.Object;
TextObject: PUBLIC TYPE = Text.Object;
-- Concrete form of the data
Data: TYPE = RECORD [
bca: LONG POINTER, -- bitmap address
bmr: CARDINAL, -- raster width in words
width,height: CARDINAL, -- width and height in bits
refs: CARDINAL
];
DataRef: TYPE = LONG POINTER TO Data;
procs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
NewPipe: CNewPipe,
NewText: CNewText,
ApplyBaseTransform: CApplyBaseTransform,
Boundary: CBoundary,
Free: CFree
]];
-- Procedures for creating a Device object
NewAltoDevice: PUBLIC PROC[b: AltoDevice.Bitmap] RETURNS[Handle] = {
d: DataRef = zone.NEW[Data _ [bca: b.base, bmr: b.raster,
width: 16*b.raster, height: b.height, refs: 1]];
RETURN[zone.NEW[Object _ [procs: procs, data: LOOPHOLE[d]]]];
};
screenHandle: Handle_NIL;
ScreenDevice: PUBLIC PROC RETURNS[Handle] = {
IF screenHandle=NIL THEN
screenHandle_NewAltoDevice[AltoDevice.ScreenBitmap[]];
RETURN[screenHandle];
};
-- Operations on an Alto Device
PData: TYPE = RECORD [
list,curr,start: PointRef,
miny,maxy: REAL,
blt: Blt.Handle,
refs: CARDINAL
];
PDataRef: TYPE = LONG POINTER TO PData;
pProcs: LONG POINTER TO READONLY Pipe.Procs = zone.NEW[Pipe.Procs = [
Put: PPut, Free: PFree]];
BltStyle: PROC[d: Blt.Handle, style: POINTER TO READONLY Style.Data] = {
Nibbles: TYPE = PACKED ARRAY[0..4) OF [0..17B];
t: Nibbles=LOOPHOLE[style.texture];
FOR i: CARDINAL IN[0..4) DO
LOOPHOLE[d.grays[i],Nibbles]_ALL[t[i]]
ENDLOOP;
SELECT style.paint FROM
replace => d.bbt.function_replace;
paint => d.bbt.function_paint;
invert => d.bbt.function_invert;
erase => d.bbt.function_erase;
ENDCASE;
};
CNewPipe: PROC[self: Handle, style: POINTER TO Style.Data]
RETURNS[Pipe.Handle] = {
d: DataRef=LOOPHOLE[self.data];
blt: Blt.Handle=Blt.New[d.bca,d.bmr];
BltStyle[blt,style];
RETURN[MakePipe[blt]];
};
MakePipe: PROC[blt: Blt.Handle] RETURNS[Pipe.Handle] = --INLINE-- {
p: PDataRef = zone.NEW[PData _ [
list: NIL, curr: NIL, start: NIL, miny: 0, maxy: 0,
blt: blt, refs: 1
]];
RETURN[zone.NEW[Pipe.Object _ [procs: pProcs, data: LOOPHOLE[p]]]]
};
Side: TYPE = {l,r};
Point: TYPE = RECORD [
link: ARRAY Side OF PointRef,
v: Vector.Vec
];
PointRef: TYPE = LONG POINTER TO Point;
-- This assumes that the polygon comes in anticlockwise!
Link: PROC[old,new: PointRef] = INLINE {
new.link[l]_old; old.link[r]_new
};
PPut: PROC[self: Pipe.Handle, area: Area.Handle] = {
p: PDataRef=LOOPHOLE[self.data];
r: Area.Rec=Area.Rectangle[area];
-- *** should test whether within display rectangle
IF Area.Rectangular[area] THEN {
blt: Blt.Handle=p.blt;
IF pause THEN Pause;
Blt.SetX[blt,RoundC[r.ll.x],RoundC[r.ur.x]];
Blt.SetY[blt,RoundC[r.ll.y],RoundC[r.ur.y]];
Blt.Rect[blt];
}
ELSE {
Put: PROC[v: Vector.Vec] = {
new: PointRef = zone.NEW[Point_[link: [NIL,NIL], v: v]];
IF p.list=NIL THEN {
p.list_p.curr_new;
p.miny_p.maxy_v.y; p.start_new;
}
ELSE {
Link[p.curr,new]; p.curr_new;
IF v.y
p.maxy THEN p.maxy_v.y;
};
};
Area.Vertices[area,Put];
IF p.list#NIL THEN {
Link[p.curr,p.list]; -- close the ring
IF pause THEN Pause;
DrawPolygon[p];
};
};
Area.Free[@area];
};
Pause: PROC = {
OPEN TimeDefs;
t: PackedTime=CurrentDayTime[];
WHILE CurrentDayTime[]=t DO ENDLOOP;
};
FreeList: PROC[d: PDataRef] = {
p: PointRef_d.list;
IF p=NIL THEN RETURN;
p.link[l].link[r]_NIL; -- break the circle
UNTIL p=NIL DO -- free all the Points
q: PointRef_p.link[r]; zone.FREE[@p]; p_q ENDLOOP;
d.list_d.curr_d.start_NIL;
};
PFree: PROC[self: Pipe.Handle] = {
p: PDataRef_LOOPHOLE[self.data];
Blt.Free[@p.blt];
zone.FREE[@p]; zone.FREE[@self];
};
Fraction: TYPE = LONG INTEGER;
sclup: InlineDefs.LongNumber _ [num[highbits: 1, lowbits: 0]];
scaleup: REAL=sclup.li;
RoundLI: PROC[r: REAL] RETURNS[LONG INTEGER] = INLINE {
RETURN[Real.Fix[r+.5]]
};
RoundC: PROC[r: REAL] RETURNS[CARDINAL] = INLINE {
RETURN[Real.FixC[r+.5]]
};
RoundI: PROC[r: REAL] RETURNS[INTEGER] = INLINE {
RETURN[Real.FixI[r+.5]]
};
-- fix a real into a signed integer.fraction
FixF: PROC[r: REAL] RETURNS[Fraction] = INLINE {
RETURN[RoundLI[r*scaleup]]
};
-- return the integer part of an integer.fraction
IntF: PROC[f: Fraction] RETURNS[INTEGER] = INLINE {
RETURN[InlineDefs.HighHalf[f]]
};
Edge: TYPE = RECORD [
ytop: CARDINAL,
xt: Fraction,
udx: Fraction,
vert: BOOLEAN,
end: PointRef
];
DrawPolygon: PROC[p: PDataRef] = {
blt: Blt.Handle=p.blt;
ycurr: CARDINAL_RoundC[p.miny];
ystop: CARDINAL=RoundC[p.maxy];
edge: ARRAY Side OF Edge_[
l: [ytop: ycurr, xt: 0, udx: 0, vert: FALSE, end: p.start],
r: [ytop: ycurr, xt: 0, udx: 0, vert: FALSE, end: p.start]
];
vertical: BOOLEAN_FALSE;
CurX: PROC[side: Side] RETURNS[CARDINAL] =
INLINE { RETURN[IntF[edge[side].xt]] };
Vert: PROC[side: Side] RETURNS[BOOLEAN] =
INLINE { RETURN[edge[side].vert] };
YTop: PROC[side: Side] RETURNS[CARDINAL] =
INLINE { RETURN[edge[side].ytop] };
Bump: PROC[side: Side] =
INLINE { OPEN edge[side];
IF ytop>ycurr THEN xt_xt+udx ELSE NextEdge[side] };
NextPt: PROC[p: PointRef, side: Side] RETURNS[PointRef] =
INLINE { RETURN[p.link[side]] };
NextEdge: PROC[side: Side] = INLINE {
OPEN edge[side];
s,e: Vector.Vec;
delta: REAL;
e_end.v;
DO -- advance to an edge that intersects ycurr
s_e; end_NextPt[end,side]; e_end.v;
ytop_RoundC[e.y]; IF ytop>ycurr THEN EXIT;
ENDLOOP;
delta_(e.x-s.x)/(e.y-s.y);
xt_FixF[(s.x+.5)+delta*((ycurr+.5)-s.y)];
IF (ytop-ycurr)>1 THEN { udx_FixF[delta]; vert_(udx=0) }
ELSE { udx_0; vert_FALSE };
};
-- Code for DrawPolygon starts here
WHILE ycurr0 THEN {
state: Clipper.State;
CharArea: PROC[orig: Vector.Vec] RETURNS[Area.Handle] = {
ll: Vector.Vec=Vector.Add[orig,[bbox.ox,bbox.oy]];
ur: Vector.Vec=Vector.Add[ll,[bbox.dx,bbox.dy]];
RETURN[Poly.NewRec[[ll,ur]]];
};
Clipper.Push[clipper,CharArea[origin]];
state_Clipper.Test[clipper];
IF state.in THEN {
rx: INTEGER=RoundI[origin.x];
ry: INTEGER=RoundI[origin.y];
blt: Blt.Handle=t.blt;
blt.sdx_rx-rast.x0; blt.sdy_ry-rast.y0;
Blt.Source[blt,rast.bca,rast.bmr];
IF state.out THEN {
pipe: Pipe.Handle_MakePipe[Blt.Ref[blt]];
pipe_Clipper.NewPipe[clipper,pipe];
Pipe.Put[pipe,CharArea[[rx,ry]]]; Pipe.Free[@pipe];
}
ELSE {
Blt.SetBox[blt,rx+bbox.ox,ry+bbox.oy,bbox.dx,bbox.dy];
Blt.Rect[blt];
};
};
Clipper.Pop[clipper];
};
RETURN[Vector.Add[origin,[bbox.wx,bbox.wy]]];
};
TDrawString: PROC[self: Text.Handle, s: LONG STRING,
origin: Vector.Vec, clipper: Clipper.Handle]
RETURNS[Vector.Vec] = {
t: TDataRef=LOOPHOLE[self.data];
bbox: AltoFont.BBox_AltoFont.StringBox[t.font,s];
state: Clipper.State;
StringArea: PROC[orig: Vector.Vec] RETURNS[Area.Handle] = {
ll: Vector.Vec=Vector.Add[orig,[bbox.ox,-(bbox.dy+bbox.oy)]];
ur: Vector.Vec=Vector.Add[ll,[bbox.dx,bbox.dy]];
RETURN[Poly.NewRec[[ll,ur]]];
};
Clipper.Push[clipper,StringArea[origin]];
state_Clipper.Test[clipper];
IF state.in THEN {
IF state.out THEN {
v: Vector.Vec_origin;
FOR i: CARDINAL IN[0..s.length) DO
v_TDrawChar[self,s[i],v,clipper]
ENDLOOP;
}
ELSE {
rx: INTEGER_RoundI[origin.x];
ry: INTEGER_RoundI[origin.y];
blt: Blt.Handle=t.blt;
FOR i: CARDINAL IN[0..s.length) DO
bbox: AltoFont.BBox;
rast: AltoFont.Rast;
AltoFont.Character[t.font,s[i],@bbox,@rast];
blt.sdx_rx-rast.x0; blt.sdy_ry-rast.y0;
Blt.Source[blt,rast.bca,rast.bmr];
Blt.SetBox[blt,rx+bbox.ox,ry+bbox.oy,bbox.dx,bbox.dy];
Blt.Rect[blt];
rx_rx+bbox.wx; ry_ry+bbox.wy;
ENDLOOP;
};
};
Clipper.Pop[clipper];
RETURN[Vector.Add[origin,[bbox.wx,-bbox.wy]]];
};
FDrawChar: PROC[self: Text.Handle, c: CHARACTER,
origin: Vector.Vec, clipper: Clipper.Handle]
RETURNS[Vector.Vec] = {
-- *** fix this
RETURN[origin];
};
FDrawString: PROC[self: Text.Handle, s: LONG STRING,
origin: Vector.Vec, clipper: Clipper.Handle]
RETURNS[Vector.Vec] = {
-- *** fix this
RETURN[origin];
};
TFree: PROC[selfPtr: LONG POINTER TO Text.Handle] = {
self: Text.Handle_selfPtr^;
t: TDataRef_LOOPHOLE[self.data];
selfPtr^_NIL;
Blt.Free[@t.blt];
AltoFont.Free[@t.font];
zone.FREE[@t];
zone.FREE[@self];
};
CApplyBaseTransform: PROC[self: Handle, mapper: Mapper.Handle] = {
d: DataRef=LOOPHOLE[self.data];
Mapper.Translate[mapper,[0,d.height]];
Mapper.Concat[mapper,[1,0,0,-1]];
};
CBoundary: PROC[self: Handle] RETURNS[Area.Handle] = {
d: DataRef=LOOPHOLE[self.data];
ll: Vector.Vec_[0,0];
ur: Vector.Vec_[d.width,d.height];
margin: Vector.Vec=[0.1,0.1];
ll_Vector.Add[ll,margin]; ur_Vector.Sub[ur,margin];
RETURN[Poly.NewRec[[ll,ur]]];
};
CFree: PROC[self: Handle] = {
d: DataRef_LOOPHOLE[self.data];
zone.FREE[@d]; zone.FREE[@self];
};
}.(670)