-- 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.yp.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)