-- AltoDeviceImpl.mesa
-- Last changed by Doug Wyatt, January 12, 1982 4:31 PM
DIRECTORY
Device,
OpaqueDevice USING [],
AltoDevice USING [Bitmap, ScreenBitmap],
Vector USING [Vec, Matrix, Add, Sub],
ImageObj USING [Handle, SetSamplePosition, GetNextSample],
Mapper USING [Handle, Translate, Concat, InverseMap, InverseMapDelta],
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, SetYCur, PutPixel,Rect, Ref, Free],
Memory USING [zone, mds],
Real USING [Fix, FixC, FixI],
InlineDefs USING [ HighHalf, LowHalf],
TimeDefs USING [PackedTime, CurrentDayTime];
AltoDeviceImpl: PROGRAM
IMPORTS AltoDevice,Memory,Vector,ImageObj,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.zone;
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,
mapper:Mapper.Handle,
image:ImageObj.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 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,mapper:Mapper.Handle_NIL,image:ImageObj.Handle_NIL]
RETURNS[Pipe.Handle] = {
d: DataRef=LOOPHOLE[self.data];
blt: Blt.Handle=Blt.New[d.bca,d.bmr];
BltStyle[blt,style];
RETURN[IF mapper=NIL THEN MakePipe[blt]
ELSE MakeImagePipe[blt,mapper,image]];
};
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, mapper:NIL, image:NIL, refs: 1
]];
RETURN[zone.NEW[Pipe.Object _ [procs: pProcs, data: LOOPHOLE[p]]]]
};
MakeImagePipe: PROC[blt: Blt.Handle, m:Mapper.Handle,i:ImageObj.Handle] RETURNS[Pipe.Handle] = --INLINE-- {
p: PDataRef = zone.NEW[PData _ [
list: NIL, curr: NIL, start: NIL, miny: 0, maxy: 0,
blt: blt, mapper:m, image:i,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] AND p.image = NIL THEN {
blt: Blt.Handle=p.blt;
IF pause THEN Pause;
Blt.SetX[blt,Ceiling[r.ll.x],Floor[r.ur.x]+1];
Blt.SetY[blt,Real.FixC[r.ll.y],Real.FixC[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;
IF p.image = NIL THEN DrawPolygon[p] ELSE DrawImagePolygon[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];
};
Floor: PROC[r: REAL] RETURNS[CARDINAL] = INLINE {
i:CARDINAL_Real.FixC[r];
RETURN[IF r = i THEN i-1 ELSE i]
};
Ceiling: PROC[r: REAL] RETURNS[CARDINAL] = INLINE {
i:CARDINAL_Real.FixC[r];
RETURN[IF r = i THEN i ELSE i+1]
};
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]]
};
Edge: TYPE = RECORD [
ytop: CARDINAL,
xt: REAL,
udx: REAL,
vert: BOOLEAN,
end: PointRef
];
DrawPolygon: PROC[p: PDataRef] = {
blt: Blt.Handle=p.blt;
ycurr: CARDINAL_Real.FixC[p.miny];
ystop: CARDINAL=Real.FixC[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;
CurLX: PROC RETURNS[CARDINAL] =
INLINE {RETURN[Ceiling[edge[l].xt]];};
CurRX: PROC RETURNS[CARDINAL] =
INLINE {RETURN[Floor[edge[r].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_Real.FixC[e.y]; IF ytop > ycurr THEN EXIT;
ENDLOOP;
delta_(e.x-s.x)/(e.y-s.y);
xt_s.x+(ycurr+1.0-s.y)*delta;
IF (ytop-ycurr)>1 THEN { udx_delta; vert_(udx=0) }
ELSE { udx_0; vert_FALSE };
};
-- Code for DrawPolygon starts here
WHILE ycurrycurr THEN {xt_xt+udx;} ELSE {NextEdge[side];} };
NextPt: PROC[p: PointRef, side: Side] RETURNS[PointRef] =
INLINE { RETURN[p.link[side]] };
NextEdge: PROC[side: Side] = {
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_Real.FixC[e.y]; IF ytop>ycurr THEN EXIT;
ENDLOOP;
delta_(e.x-s.x)/(e.y-s.y);
xt_s.x+(ycurr+1.0-s.y)*delta;
IF side=l THEN
{i_Mapper.InverseMap[mapper,Vector.Vec[xt,ycurr+1]];
id_Mapper.InverseMapDelta[mapper,Vector.Vec[delta,1]];
};
IF (ytop-ycurr)>1 THEN { udx_delta; vert_(udx=0) }
ELSE { udx_0; vert_FALSE };
};
-- Code for DrawPolygon starts here
sx_Mapper.InverseMapDelta[mapper,Vector.Vec[1,0]];
my_(ycurr MOD 3)*3;
WHILE ycurr< ystop DO
v:Vector.Vec;
maxx,x:CARDINAL;
ynext: CARDINAL_ycurr+1;
Bump[l]; Bump[r];
i_Vector.Add[i,id];
x_CurLX[];
mx_x MOD 3;
maxx_CurRX[];
v_CurSX[l];
ImageObj.SetSamplePosition[image,v.x,v.y,sx.x,sx.y];
Blt.SetYCur[blt,ycurr];
WHILE x <= maxx DO
IF ImageObj.GetNextSample[image] < tsh[my+mx]
THEN Blt.PutPixel[blt,x];
x_x+1;
mx_IF mx = 2 THEN 0 ELSE mx + 1;
ENDLOOP;
ycurr_ynext;
my_IF my = 6 THEN 0 ELSE my + 3;
ENDLOOP;
FreeList[p];
};
-- Text stuff (ecchhh!)
TData: TYPE = RECORD [
d: DataRef,
blt: Blt.Handle,
font: AltoFont.Handle
];
TDataRef: TYPE = LONG POINTER TO TData;
tProcs: LONG POINTER TO READONLY Text.Procs = zone.NEW[Text.Procs = [
CharInfo: TCharInfo,
StringInfo: TStringInfo,
FontInfo: TFontInfo,
DrawChar: TDrawChar,
DrawString: TDrawString,
Free: TFree
]];
fProcs: LONG POINTER TO READONLY Text.Procs = zone.NEW[Text.Procs = [
CharInfo: TCharInfo,
StringInfo: TStringInfo,
FontInfo: TFontInfo,
DrawChar: FDrawChar,
DrawString: FDrawString,
Free: TFree
]];
CNewText: PROC[self: Handle, id: Font.Id, size: REAL,
pm: POINTER TO READONLY Vector.Matrix,
style: POINTER TO READONLY Style.Data]
RETURNS[Text.Handle] = {
d: DataRef=LOOPHOLE[self.data];
t: TDataRef=zone.NEW[TData _ [d: d, blt: TextBlt[d,style], font: NIL]];
fake: BOOLEAN_FALSE;
IF UniformScale[pm^] THEN size_size*pm.a11 ELSE fake_TRUE;
IF size IN[FIRST[CARDINAL]..LAST[CARDINAL]] THEN
t.font_AltoFont.New[id,Real.FixC[size+0.49]];
IF t.font=NIL THEN { fake_TRUE; t.font_AltoFont.Default[] };
RETURN[zone.NEW[Text.Object _ [
procs: IF fake THEN fProcs ELSE tProcs, data: LOOPHOLE[t]
]]];
};
TextBlt: PROC[d: DataRef, style: POINTER TO READONLY Style.Data]
RETURNS[Blt.Handle] = INLINE {
blt: Blt.Handle=Blt.New[d.bca,d.bmr];
BltStyle[blt,LOOPHOLE[style]]; -- LOOPHOLE to defeat READONLY
blt.bbt.sourcetype_andgray;
RETURN[blt];
};
UniformScale: PROC[m: Vector.Matrix] RETURNS[BOOLEAN] = INLINE {
eps: REAL=1E-6;
Tiny: PROC[r: REAL] RETURNS[BOOLEAN] = INLINE { RETURN[ABS[r]0 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)