-- CGBitmapDeviceImpl.mesa -- Last changed by Doug Wyatt, November 2, 1982 10:26 am DIRECTORY CGArea USING [Empty, Ref, Remove], CGBitmapDevice USING [], -- exports only CGBitmapDeviceExtras USING [], -- exports only CGColor USING [GetStipple], CGDevice USING [Ref, Rep], CGMatrix USING [Inv, InvRel, IsTrivial, Make, Ref], CGSample USING [Flags, Sample0, Sample8, Table], CGScreen USING [Bits], CGSource USING [Mode, Ref, Type], CGStorage USING [pZone, qZone], GraphicsBasic USING [black, Box, Color, Trap, white], GraphicsColor USING [ColorToIntensity], BitBlt USING [AlignedBBTable, BBTableSpace, BITBLT, BitBltTable, BitBltTablePtr], Inline USING [DIVMOD, LongDivMod, LongMult], Real USING [Fix, FixC, RoundC]; CGBitmapDeviceImpl: CEDAR PROGRAM IMPORTS CGArea, CGColor, CGMatrix, CGSample, CGScreen, CGStorage, GraphicsColor, BitBlt, Inline, Real EXPORTS CGBitmapDevice, CGBitmapDeviceExtras = { OPEN CGDevice, GraphicsBasic; dataZone: ZONE = CGStorage.qZone; repZone: ZONE = CGStorage.qZone; brickZone: ZONE = CGStorage.pZone; Error: SIGNAL = CODE; Data: TYPE = REF DataRep; DataRep: TYPE = RECORD [ baseref: REF, -- ref for bitmap (or NIL if screen) base: LONG POINTER, -- base address of bitmap rast: CARDINAL, -- bitmap words per line lines: CARDINAL, -- bitmap lines matrix: CGMatrix.Ref -- base transformation matrix ]; New: PUBLIC PROC[base: REF, raster, height: CARDINAL] RETURNS[Ref] = { data: Data _ dataZone.NEW[DataRep _ [baseref: NIL, base: NIL, rast: 0, lines: 0, matrix: NIL]]; IF base=NIL THEN [base: data.base, raster: raster, height: height] _ CGScreen.Bits[] ELSE { data.baseref _ base; data.base _ LOOPHOLE[base] }; data.rast _ raster; data.lines _ height; data.matrix _ CGMatrix.Make[[1,0,0,-1,0,height]]; RETURN[repZone.NEW[Rep _ [ GetMatrix: GetMatrix, GetBounds: GetBounds, Show: Show, GetRaster: GetRaster, MoveBlock: MoveBlock, data: data]]]; }; GetMatrix: PROC[self: Ref] RETURNS[CGMatrix.Ref] = { data: Data _ NARROW[self.data]; RETURN[data.matrix]; }; GetBounds: PROC[self: Ref] RETURNS[Box] = { data: Data _ NARROW[self.data]; w: CARDINAL _ 16*data.rast; h: CARDINAL _ data.lines; e: REAL = 0.1; -- small fudge factor RETURN[[xmin: e, ymin: e, xmax: w-e, ymax: h-e]]; }; GetRaster: PROC[self: Ref] RETURNS[LONG POINTER,CARDINAL] = { data: Data _ NARROW[self.data]; RETURN[data.base,data.rast]; }; Show: PROC[self: Ref, area: CGArea.Ref, src: CGSource.Ref, map: CGMatrix.Ref] = { data: Data _ NARROW[self.data]; fat: BOOLEAN _ (src.fat AND src.type#array); UNTIL CGArea.Empty[area] DO trap: Trap _ CGArea.Remove[area]; IF trap.xbotL=trap.xtopL AND trap.xbotR=trap.xtopR THEN { rect: Box _ [xmin: trap.xbotL, ymin: trap.ybot, xmax: trap.xbotR, ymax: trap.ytop]; ShowRect[data,rect,src,map] } ELSE ShowTrap[data,trap,src,map]; ENDLOOP; }; Bot: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r]] }; Top: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r]+1] }; Fix: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r]] }; Rnd: PROC[r: REAL] RETURNS[CARDINAL] = INLINE { RETURN[Real.FixC[r+.5]] }; RndI: PROC[r: REAL] RETURNS[INTEGER] = INLINE { RETURN[Real.FixC[r+(LAST[INTEGER]+.5)]-LAST[INTEGER]] }; FixF: PROC[r: REAL] RETURNS[LONG INTEGER] = INLINE { RETURN[Real.Fix[r*200000B]] }; Brick: TYPE = REF BrickRep; BrickRep: TYPE = RECORD[w, h, d: CARDINAL, array: SEQUENCE size: CARDINAL OF CARDINAL]; brick3, brick4: Brick _ NIL; currentBrick, defaultBrick: Brick _ NIL; InitBricks: PROC = { index3: ARRAY[0..9) OF NAT = [8,3,6,5,1,9,2,7,4]; index4: ARRAY[0..16) OF NAT = [09,13,08,04,14,07,03,10,06,02,11,15,01,12,16,05]; brick3 _ brickZone.NEW[BrickRep[9] _ [w: 3, h: 3, d: 0, array: ]]; FOR i: NAT IN[0..9) DO brick3[i] _ (255*index3[i]+5)/10 ENDLOOP; brick4 _ brickZone.NEW[BrickRep[16] _ [w: 4, h: 4, d: 0, array: ]]; FOR i: NAT IN[0..16) DO brick4[i] _ (255*index4[i]+8)/17 ENDLOOP; currentBrick _ defaultBrick _ brick3; }; BadArgs: ERROR = CODE; SetBrick: PUBLIC PROC[w, h, d: CARDINAL, proc: PROC[i: CARDINAL] RETURNS[CARDINAL]] = { IF w>0 AND h>0 AND d TRUE, -- always easy array => (CGMatrix.IsTrivial[map] AND src.bps<2), -- bitmap is easy proc => FALSE, -- never easy ENDCASE => ERROR); fat: BOOLEAN _ (src.fat AND type#array); xmin, xmax, ymin, ymax: CARDINAL _ 0; dbase: LONG POINTER _ data.base; drast: CARDINAL _ data.rast; IF fat THEN { xmin _ Bot[rect.xmin]; xmax _ Top[rect.xmax]; ymin _ Bot[rect.ymin]; ymax _ Top[rect.ymax]; } ELSE { xmin _ Rnd[rect.xmin]; xmax _ Rnd[rect.xmax]; ymin _ Rnd[rect.ymin]; ymax _ Rnd[rect.ymax]; }; IF NOT(xmin or, invert => xor, ENDCASE => null); bb.dst.word _ dbase + Inline.LongMult[ymin, drast] + xmin/16; bb.dst.bit _ xmin MOD 16; bb.dstBpl _ drast*16; SELECT type FROM const => { yOffset: [0..4) _ ymin MOD 4; bb.srcDesc.gray _ [yOffset: 0, widthMinusOne: 0, heightMinusOne: 3]; bb.srcDesc.gray.yOffset _ yOffset; bb.src.word _ @gray + yOffset; bb.src.bit _ xmin MOD 16; gray _ MakeGray[color]; }; tile => { yOffset: [0..16) _ ymin MOD 16; bb.srcDesc.gray _ [yOffset: 0, widthMinusOne: 0, heightMinusOne: 15]; bb.srcDesc.gray.yOffset _ yOffset; bb.src.word _ src.xbase + yOffset; bb.src.bit _ xmin MOD 16; -- weird "transparent white" semantics for McGregor's grey text hack: IF mode=transparent AND color=0 THEN { bb.flags.srcFunc _ complement; bb.flags.dstFunc _ and }; }; array => { -- map transforms source coordinates to device coordinates -- Since this is an easy case, we know map is a simple translation; -- so subtracting map's translation elements will take us from device to source sxmin: CARDINAL _ Real.FixC[xmin+0.5-map.m.e]; symin: CARDINAL _ Real.FixC[ymin+0.5-map.m.f]; sbase: LONG POINTER _ src.xbase; srast: CARDINAL _ src.xrast; bb.flags.gray _ FALSE; IF (src.bps>0)=(color>0) THEN bb.flags.srcFunc _ complement; bb.src.word _ sbase + Inline.LongMult[symin,srast] + sxmin/16; bb.src.bit _ sxmin MOD 16; bb.srcDesc.srcBpl _ srast*16; }; ENDCASE => ERROR; bb.width _ xmax - xmin; bb.height _ ymax - ymin; BitBlt.BITBLT[bb]; } ELSE TRUSTED { table: CGSample.Table _ nullSampleTable; rsx, rsy, rsdx, rsdy: REAL; y: CARDINAL _ ymin; [[rsdx,rsdy]] _ CGMatrix.InvRel[map,[1,0]]; table.sdx _ FixF[rsdx]; table.sdy _ FixF[rsdy]; table.sBase _ src.xbase; table.sRast _ src.xrast; table.dLine _ dbase+Inline.LongMult[drast, ymin]; table.di _ xmin; table.count _ xmax-xmin; IF src.bps=8 THEN { brick: Brick _ currentBrick; bbase: LONG POINTER _ @brick[0]; bw, bh, bd, be, br, bx, by: CARDINAL; bw _ brick.w; bh _ brick.h; bd _ brick.d; be _ (IF bd=0 THEN 0 ELSE bw-bd); [quotient: br, remainder: by] _ Inline.DIVMOD[y, bh]; bx _ Inline.LongDivMod[Inline.LongMult[br, be], bw].remainder; table.bLine _ bbase + by*bw; table.bw _ bw; table.bi _ (xmin+bx) MOD bw; table.map _ LOOPHOLE[defaultMap]; DO [[rsx,rsy]] _ CGMatrix.Inv[map,[xmin+0.5,y+0.5]]; table.sx _ FixF[rsx]; table.sy _ FixF[rsy]; CGSample.Sample8[@table, mode]; IF (y _ y+1)=ymax THEN EXIT; table.dLine _ table.dLine+drast; IF (by _ by+1)=bh THEN { by _ 0; table.bLine _ bbase; IF bxymin THEN { IF ymin>ymax THEN SIGNAL Error; RETURN }; -- nothing to do dy _ trap.ytop-trap.ybot; -- delta y dxL _ (trap.xtopL-trap.xbotL)/dy; -- dx/dy, left dxR _ (trap.xtopR-trap.xbotR)/dy; -- dx/dy, right fy _ (ymin+0.5)-trap.ybot; -- distance to middle of first line xL _ trap.xbotL+fy*dxL; xR _ trap.xbotR+fy*dxR; -- left and right x at middle of current line y _ ymin; drast _ data.rast; dline _ data.base + Inline.LongMult[y, drast]; SELECT type FROM const, tile => { bbspace: BitBlt.BBTableSpace; bb: BitBlt.BitBltTablePtr _ BitBlt.AlignedBBTable[@bbspace]; gray: ARRAY [0..4) OF CARDINAL; -- gray pattern, if type=const pbase: LONG POINTER; -- pattern base address ph, py: CARDINAL; -- pattern height and current line bb^ _ nullBitBltTable; bb.dstBpl _ drast*16; bb.height _ 1; bb.flags.gray _ TRUE; bb.flags.dstFunc _ (SELECT mode FROM transparent => or, invert => xor, ENDCASE => null); IF type=const THEN { pbase _ @gray; ph _ 4; gray _ MakeGray[color] } ELSE { pbase _ src.xbase; ph _ 16; -- weird "transparent white" semantics for McGregor's grey text hack: IF mode=transparent AND color=0 THEN { bb.flags.srcFunc _ complement; bb.flags.dstFunc _ and }; }; py _ y MOD ph; bb.src.word _ pbase + py; bb.srcDesc.gray _ [yOffset: py, widthMinusOne: 0, heightMinusOne: ph-1]; DO xmin _ Rnd[xL]; xmax _ Rnd[xR]; IF xmin IF CGMatrix.IsTrivial[map] AND src.bps=0 THEN { -- easy array bbspace: BitBlt.BBTableSpace; bb: BitBlt.BitBltTablePtr _ BitBlt.AlignedBBTable[@bbspace]; tx, ty: INTEGER; sline: LONG POINTER; srast: CARDINAL; bb^ _ nullBitBltTable; bb.dstBpl _ drast*16; bb.height _ 1; bb.flags.gray _ FALSE; bb.flags.dstFunc _ (SELECT mode FROM transparent => or, invert => xor, ENDCASE => null); IF color=0 THEN { bb.flags.srcFunc _ complement; IF mode=transparent THEN bb.flags.dstFunc _ and }; -- The matrix 'map' transforms source coordinates to device coordinates. -- Here we know map is a simple translation, so subtracting its translation -- elements will take us from device to source coordinates. tx _ RndI[map.m.e]; ty _ RndI[map.m.f]; srast _ src.xrast; sline _ src.xbase + Inline.LongMult[(ymin-ty), srast]; bb.srcDesc.srcBpl _ srast*16; DO xmin _ Rnd[xL]; xmax _ Rnd[xR]; IF xmin ERROR; -- bad src.type }; ShowFatTrap: PROC[data: Data, trap: Trap, src: CGSource.Ref] = TRUSTED { type: CGSource.Type _ src.type; mode: CGSource.Mode _ src.mode; color: CARDINAL _ ColorToStipple[src.color]; ymin, ymax, ylast, xl, xr, y: CARDINAL; dxL, dxR, xbL, xbR, xtL, xtR, dy, fy: REAL; topL, topR: BOOLEAN; dline: LONG POINTER; -- destination line for current y drast: CARDINAL; -- destination raster width (in words) bbspace: BitBlt.BBTableSpace; bb: BitBlt.BitBltTablePtr _ BitBlt.AlignedBBTable[@bbspace]; gray: ARRAY [0..4) OF CARDINAL; -- gray pattern, if type=const pbase: LONG POINTER; -- pattern base address ph, py: CARDINAL; -- pattern height and current line -- compute ymin (bottom of first scanline) and ymax (top of last scanline) ymin _ Bot[trap.ybot]; ymax _ Top[trap.ytop]; IF NOT ymax>ymin THEN { SIGNAL Error; RETURN }; ylast _ ymax-1; -- ylast is last line y _ ymin; drast _ data.rast; dline _ data.base + Inline.LongMult[y, drast]; bb^ _ nullBitBltTable; bb.dstBpl _ drast*16; bb.height _ 1; bb.flags.gray _ TRUE; bb.flags.dstFunc _ (SELECT mode FROM transparent => or, invert => xor, ENDCASE => null); IF type=const THEN { pbase _ @gray; ph _ 4; gray _ MakeGray[color] } ELSE { pbase _ src.xbase; ph _ 16 }; py _ y MOD ph; bb.src.word _ pbase + py; bb.srcDesc.gray _ [yOffset: py, widthMinusOne: 0, heightMinusOne: ph-1]; xbL _ trap.xbotL; xbR _ trap.xbotR; -- bottom x IF ylast>ymin THEN { -- more than one line dy _ trap.ytop-trap.ybot; -- height of trapezoid dxL _ (trap.xtopL-trap.xbotL)/dy; -- left dx/dy dxR _ (trap.xtopR-trap.xbotR)/dy; -- right dx/dy fy _ (ymin+1)-trap.ybot; -- distance to top of first line xtL _ xbL+fy*dxL; xtR _ xbR+fy*dxR; -- top x topL _ (dxL<0); -- TRUE means leftmost x is at top of line topR _ (dxR>0); -- TRUE means rightmost x is at top of line } ELSE { -- exactly one line xtL _ trap.xtopL; xtR _ trap.xtopR; -- top x topL _ (xtLxbR); -- TRUE means rightmost x is at top of line }; DO -- for each scan line xl _ Bot[IF topL THEN xtL ELSE xbL]; xr _ Top[IF topR THEN xtR ELSE xbR]; IF xl0 AND height>0 AND fromXdx AND (dx+w)>sx AND (sy+h)>dy AND (dy+h)>sy THEN { bb.flags.disjoint _ FALSE; -- the rectangles overlap IF dy=sy THEN bb.flags.disjointItems _ FALSE; -- so do the items IF dy>sy OR (dy=sy AND dx>sx) THEN { -- reverse direction bb.flags.direction _ backward; bpl _ -bpl; sy _ sy + (h-1); dy _ dy + (h-1); }; }; bb.dst.word _ dbase + Inline.LongMult[dy, drast] + dx/16; bb.dst.bit _ dx MOD 16; bb.src.word _ dbase + Inline.LongMult[sy, drast] + sx/16; bb.src.bit _ sx MOD 16; bb.dstBpl _ bb.srcDesc.srcBpl _ bpl; bb.width _ w; bb.height _ h; BitBlt.BITBLT[bb]; }; }; ------ Initialization ------ InitBricks[]; }.