DIRECTORY Atom USING [GetPropFromList], Basics USING [LongMult, BITSHIFT, BITAND, logBitsPerWord, bitsPerWord, BITOR, BITNOT, BytePair], PrincOps USING [DstFunc, SrcFunc, BitAddress, BBTableSpace, BBptr], PrincOpsUtils USING [AlignedBBTable, BITBLT], Real USING [RoundC, FixI, FixC, Float], Terminal USING [ModifyColorFrame, Virtual], ImagerColor USING [RGB], SampleMapOps USING [SampleMap, Function], Vector2 USING [VEC], Pixels USING [Extent, GetPixel, GetSampleSet, GetTerminalYOffset, PixelBuffer, PixelOp, PutPixel, SampleSet, SampleSetSequence, SubMap, TerminalFromBuffer, XfmMapPlace], ScanConvert USING [IntPair, IntPairSequence, RealSequence, Spot, SpotSequence, justNoticeable]; ScanConvertImpl: CEDAR MONITOR IMPORTS Atom, Basics, PrincOpsUtils, Terminal, Real, Pixels EXPORTS ScanConvert ~ BEGIN PixelBuffer: TYPE ~ Pixels.PixelBuffer; RGB: TYPE ~ ImagerColor.RGB; -- RECORD[ R, G, B: REAL] IntRGB: TYPE ~ RECORD[ r, g, b: CARDINAL]; IntRGBZ: TYPE ~ RECORD[ r, g, b, z: CARDINAL]; Pair: TYPE ~ Vector2.VEC; -- RECORD[x, y: REAL] IntPair: TYPE ~ ScanConvert.IntPair; -- RECORD[x, y: INTEGER] IntPairSequence: TYPE ~ ScanConvert.IntPairSequence; RealSequence: TYPE ~ ScanConvert.RealSequence; SampleSet: TYPE ~ Pixels.SampleSet; SampleSetSequence: TYPE ~ Pixels.SampleSetSequence; Spot: TYPE ~ ScanConvert.Spot; SpotSequence: TYPE ~ ScanConvert.SpotSequence; BytePair: TYPE ~ Basics.BytePair; Function: TYPE ~ SampleMapOps.Function; IncrementalDesc: TYPE ~ RECORD [val, intInc, intSgn, fracInc, fracRng, fracPos: INTEGER]; IncDescSeq: TYPE ~ RECORD [SEQUENCE length: NAT OF IncrementalDesc]; ScanConvertError: PUBLIC SIGNAL [reason: ATOM] = CODE; lgBitsPerWord: NAT ~ Basics.logBitsPerWord; bitsPerWord: NAT ~ Basics.bitsPerWord; bltValue: ARRAY [0..4] OF CARDINAL; bbspace: ARRAY[0..4) OF PrincOps.BBTableSpace; bb: ARRAY[0..4) OF PrincOps.BBptr; bbWdsPerLine: ARRAY[0..4) OF NAT; Extend: PUBLIC PROC[seq: REF RealSequence, newLength: NAT] RETURNS[REF RealSequence]~{ newSeq: REF RealSequence _ NEW[ RealSequence[newLength] ]; FOR i: NAT IN [0..seq.length) DO newSeq[i] _ seq[i]; ENDLOOP; newSeq.length _ seq.length; seq _ newSeq; RETURN[seq]; }; Sqr: PROCEDURE [number: INTEGER] RETURNS [INTEGER] ~ INLINE { RETURN[number * number]; }; Log2: PROC [n: INT] RETURNS [lg: NAT _ 0] ~ { -- finds log base 2 of input (from M. Plass) nn: LONG CARDINAL ~ n; k: LONG CARDINAL _ 1; UNTIL k=0 OR k>= nn DO lg _ lg + 1; k _ k + k; ENDLOOP; }; Power: PUBLIC PROC[ value: INTEGER, power: NAT] RETURNS[ result: INTEGER ] ~ { binaryCount: NAT _ power; -- makes highlights same size as those by ShadePt temp: REAL _ 1.0; val: REAL _ Real.Float[value] / 256.0; WHILE binaryCount > 0 DO IF Basics.BITAND[binaryCount, 1] = 1 THEN temp _ temp * val; val _ val * val; binaryCount _ binaryCount/2; ENDLOOP; result _ Real.RoundC[temp*256.0]; }; SetUpConstBlt: PROC[buf: PixelBuffer, y: INTEGER, color: SampleSet] ~ TRUSTED { j: NAT _ 0; FOR i: NAT IN [0..buf.pixels.length) DO dest: SampleMapOps.SampleMap _ buf.pixels[i].subMap.sampleMap; bbWdsPerLine[i] _ Basics.BITSHIFT[dest.bitsPerLine, -4]; IF Basics.BITAND[dest.bitsPerLine, 15] # 0 THEN bbWdsPerLine[i] _ bbWdsPerLine[i] + 1; IF buf.pixels[i].df = 2 THEN IF dest.base.bit = 0 THEN { bltValue[i] _ 256*color[j] + color[j+1]; j _ j + 2; } -- interleaved RG ELSE bltValue[i] _ 0 -- ignore interleaved map off word boundary ELSE { bltValue[i] _ color[j] * 00101H; j _ j + 1; }; -- replicate value bb[i] _ PrincOpsUtils.AlignedBBTable[@bbspace[i]]; bb[i]^ _ [ -- set up to point to beginning of scan line at polygon bottom dst: [word: dest.base.word + Basics.LongMult[y, bbWdsPerLine[i]], bit: 0], dstBpl: dest.bitsPerLine, src: [word: @bltValue[i], bit: 0], srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]], height: 1, width: 1, flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE, srcFunc: null, dstFunc: null] ]; ENDLOOP; }; DoConstBlt: PROC[buf: PixelBuffer, left, right: INTEGER] ~ TRUSTED { IF left > right THEN [left, right] _ Swap[left, right]; -- allow screwed up scan segments FOR i: NAT IN [0..buf.samplesPerPixel) DO scanLinePtr: LONG POINTER _ bb[i].dst.word; IF buf.pixels[i].df = 2 -- interleaved maps, write the one which is word-aligned THEN IF buf.pixels[i].subMap.sampleMap.base.bit = 0 THEN { bb[i].dst _ [word: scanLinePtr + left, bit: 0]; bb[i].width _ (right - left + 1) * 16; } ELSE LOOP -- not word-aligned, ignore ELSE { bb[i].dst _ [ word: scanLinePtr + Basics.BITSHIFT[left,-1], bit: Basics.BITSHIFT[Basics.BITAND[left,1], 3] ]; bb[i].width _ (right - left + 1) * 8; }; PrincOpsUtils.BITBLT[bb[i]]; bb[i].dst.word _ scanLinePtr + bbWdsPerLine[i]; -- increment scan pointer for next call ENDLOOP; }; GetSlopeIncr: PROC[pBeg, pEnd: IntPair] RETURNS[incr: IncrementalDesc] ~ { xDiff: INTEGER _ pEnd.x - pBeg.x; yDiff: INTEGER _ pEnd.y - pBeg.y; IF yDiff <= 0 THEN yDiff _ LAST[NAT]; -- zero height edge, let increments => 0 incr.intInc _ xDiff / yDiff; -- get integer increment incr.intSgn _ SGN[xDiff]; -- get sign of integer increment xDiff _ xDiff - incr.intInc * yDiff; -- subtract out effect of integer increment incr.fracInc _ 2 * ABS[xDiff]; -- fractional increment incr.fracRng _ 2 * yDiff; -- range to increment over incr.fracPos _ incr.fracInc - incr.fracRng/2; -- start at halfway position (midpixel) incr.val _ pBeg.x; -- initial value }; UpdateIncr: PROC[incr: IncrementalDesc] RETURNS [IncrementalDesc] ~ { IF incr.fracPos > 0 THEN { -- fractional position overflowed incr.fracPos _ incr.fracPos - incr.fracRng; -- reset fractional part incr.val _ incr.val + incr.intSgn; -- bump integer part }; incr.val _ incr.val + incr.intInc; -- increment integer part incr.fracPos _ incr.fracPos + incr.fracInc; -- increment fractional position RETURN[ incr ]; }; ditherTable: ARRAY [0..4) OF ARRAY [0..4) OF NAT _ [[0,12,3,15], [8,4,11,7], [2,14,1,13], [10,6,9,5]]; DitheredRGB: PUBLIC PROC[renderMode: ATOM, x, y, red, grn, blu: INTEGER] RETURNS[INTEGER] ~ { val2R, val2G, val2B, pixelValue: NAT; SELECT renderMode FROM $PseudoColor => { threshold: NAT _ ditherTable[ Basics.BITAND[x,3] ][ Basics.BITAND[y,3] ]; valR: NAT _ Basics.BITSHIFT[ Basics.BITSHIFT[red,2] + red, -4 ]; -- (red * 5) / 16 valG: NAT _ Basics.BITSHIFT[ Basics.BITSHIFT[grn,2] + Basics.BITSHIFT[grn,1], -4 ]; valB: NAT _ Basics.BITSHIFT[ Basics.BITSHIFT[blu,2] + blu, -4 ]; -- (blu * 5) / 16 val2R _ Basics.BITSHIFT[valR,-4]; -- valR / 16 IF Basics.BITAND[valR,15] > threshold THEN val2R _ val2R + 1; -- valr MOD 16 val2G _ Basics.BITSHIFT[valG,-4]; IF Basics.BITAND[valG,15] > threshold THEN val2G _ val2G + 1; val2B _ Basics.BITSHIFT[valB,-4]; IF Basics.BITAND[valB,15] > threshold THEN val2B _ val2B + 1; RETURN[ MIN[ 255, Basics.BITSHIFT[val2R,5] + Basics.BITSHIFT[val2R,3] + Basics.BITSHIFT[val2R,1] + Basics.BITSHIFT[val2G,2] + Basics.BITSHIFT[val2G,1] + val2B + 2 ] ]; --val2R*42 + val2G*6 + val2B + 2 }; $Dithered => { threshold: NAT _ ditherTable[x MOD 4][y MOD 4]; valR: NAT _ 4* red / 16; valG: NAT _ 5* grn / 16; valB: NAT _ 3* blu / 16; val2R _ valR/16; IF valR MOD 16 > threshold THEN val2R _ val2R + 1; val2G _ valG/16; IF valG MOD 16 > threshold THEN val2G _ val2G + 1; val2B _ valB/16; IF valB MOD 16 > threshold THEN val2B _ val2B + 1; pixelValue _ val2R*24 + val2G*4 + val2B; IF pixelValue >= 60 THEN pixelValue _ pixelValue + 135; -- move to top of map RETURN[ MIN[255, pixelValue] ]; }; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; RETURN[ 255 ]; }; MappedRGB: PUBLIC PROC[renderMode: ATOM, clr: RGB] RETURNS[NAT] ~ { SELECT renderMode FROM $Dithered => { mapVal: NAT _ Real.FixC[clr.R*4.999]*24 + Real.FixC[clr.G*5.999]*4 + Real.FixC[clr.B*3.999]; IF mapVal >= 60 THEN mapVal _ mapVal + 135; -- move to top of map RETURN[ mapVal ]; }; $PseudoColor => RETURN[ Real.FixC[clr.R*5.999]*42 + Real.FixC[clr.G*6.999]*6 + Real.FixC[clr.B*5.999] + 2 ]; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; RETURN[ 255 ]; }; RGBFromMap: PUBLIC PROC[renderMode: ATOM, value: NAT] RETURNS[RGB] ~ { SELECT renderMode FROM $Dithered => { IF value >= 60 THEN value _ value - 135; -- move from top of map value _ MIN[119, value]; RETURN[ [ R: (value / 24) / 5.0, G: (value MOD 24) / 4 / 6.0, B: (value MOD 4) / 4.0 ] ]; }; $PseudoColor => { value _ MIN[253, value] - 2; RETURN[ [ R: (value / 42) / 6.0, G: (value MOD 42) / 6 / 7.0, B: (value MOD 6) / 6.0 ] ]; }; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; RETURN[[255, 255, 255]]; }; Swap: PROCEDURE [first, second: INTEGER] RETURNS [INTEGER, INTEGER] = { RETURN [second, first]; }; BitAddr: UNSAFE PROC [lineStart: LONG POINTER, pixel: CARDINAL, lgBitsPerPixel: INTEGER] RETURNS [PrincOps.BitAddress] ~ UNCHECKED { RETURN [[ word: lineStart + Basics.BITSHIFT[pixel, lgBitsPerPixel-lgBitsPerWord], bit: Basics.BITAND[Basics.BITSHIFT[pixel, lgBitsPerPixel], bitsPerWord-1] ]] }; SGN: PROCEDURE [number: INTEGER] RETURNS [INTEGER] = INLINE { IF number >= 0 THEN RETURN[1] ELSE RETURN[-1]; }; justNoticeable: REAL ~ ScanConvert.justNoticeable; oldPixel: SampleSet _ Pixels.GetSampleSet[4]; -- own memory for pixel storage newPixel: SampleSet _ Pixels.GetSampleSet[4]; PutSpot: PUBLIC ENTRY PROC [ buf: PixelBuffer, spot: Spot, op, renderMode: ATOM ] ~ { ENABLE UNWIND => NULL; PutPixel: PROC[] ~ { DoIt: PROC[] ~ { Pixels.PutPixel[ buf, spot.x, spot.y, newPixel ]; }; IF vt # NIL THEN { yOffset: NAT _ Pixels.GetTerminalYOffset[buf]; x, y: INTEGER; [x, y] _ Pixels.XfmMapPlace[buf.pixels[buf.pixels.length-1], spot.x, spot.y ]; -- df = 1! Terminal.ModifyColorFrame[vt, DoIt, MAX[0, x-1], MAX[0, y+yOffset-1], x+1, y+1+yOffset]; } ELSE DoIt[]; }; GetPixel: PROC[] RETURNS[pixel: SampleSet] ~ { DoIt: PROC[] ~ { pixel _ Pixels.GetPixel[ buf, spot.x, spot.y, oldPixel ]; }; IF vt # NIL THEN { yOffset: NAT _ Pixels.GetTerminalYOffset[buf]; x, y: INTEGER; [x, y] _ Pixels.XfmMapPlace[buf.pixels[buf.pixels.length-1], spot.x, spot.y ]; -- df = 1! Terminal.ModifyColorFrame[vt, DoIt, MAX[0, x-1], MAX[0, y+yOffset-1], x+1, y+1+yOffset]; } ELSE DoIt[]; }; vt : Terminal.Virtual _ Pixels.TerminalFromBuffer[buf]; alpha: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Alpha] ]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Depth] ]; oldClr, newClr: RGB; newTrns, cvrge, oldCvrge: REAL; addOn: NAT _ 0; IF alpha # NIL THEN addOn _ addOn + 1; IF depth # NIL THEN addOn _ addOn + 1; IF alpha # NIL THEN { -- Using Alpha buffer IF spot.coverage < justNoticeable THEN RETURN; -- any changes would be insignificant IF op = $WriteUnder OR op = $WriteLineUnder OR spot.coverage < 1.0 - justNoticeable THEN oldPixel _ GetPixel[]; -- need to blend pixels IF (op = $WriteUnder OR op = $WriteLineUnder) AND oldPixel[alpha^] = 255 THEN RETURN; -- pixel already covered SELECT renderMode FROM -- get rgb color from old pixel $Dithered, $PseudoColor => oldClr _ RGBFromMap[ renderMode, oldPixel[0] ]; $FullColor, $Dorado24 => oldClr _ [ oldPixel[0]/255.0, oldPixel[1]/255.0, oldPixel[2]/255.0 ]; $Grey => oldClr _ [ oldPixel[0]/255.0, oldPixel[0]/255.0, oldPixel[0]/255.0 ]; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; [newClr, newTrns] _ spot.proc[spot]; -- extract color from spot cvrge _ spot.coverage; oldCvrge _ oldPixel[alpha^]/255.0; IF newTrns > justNoticeable THEN cvrge _ spot.coverage * (1.0 - newTrns); IF op = $WriteUnder OR op = $WriteLineUnder THEN IF oldPixel[alpha^] # 0 THEN { -- previous coverage blend under using VonNeumann rounding IF op = $WriteLineUnder THEN cvrge _ MAX[ 0.0, cvrge - oldCvrge ] -- only where new is greater ELSE cvrge _ MIN[ cvrge, 1.0 - oldCvrge ]; -- up to uncovered part of pixel newClr.R _ oldClr.R + cvrge * newClr.R; newClr.G _ oldClr.G + cvrge * newClr.G; newClr.B _ oldClr.B + cvrge * newClr.B; cvrge _ cvrge + oldCvrge; -- store new total coverage } ELSE { -- first surface written newClr.R _ cvrge * newClr.R; newClr.G _ cvrge * newClr.G; newClr.B _ cvrge * newClr.B; } ELSE { -- Writing over cvrge _ cvrge + oldCvrge - cvrge * oldCvrge; -- sum less overlap estimate newClr.R _ oldClr.R + cvrge * (newClr.R - oldClr.R); -- blend newClr.G _ oldClr.G + cvrge * (newClr.G - oldClr.G); newClr.B _ oldClr.B + cvrge * (newClr.B - oldClr.B); }; } ELSE [newClr, newTrns] _ spot.proc[spot]; -- No alpha buffer, just extract color from spot SELECT renderMode FROM $FullColor, $Dorado24 => { IF newPixel.maxLength < 3+addOn THEN newPixel _ Pixels.GetSampleSet[3+addOn]; newPixel[0] _ Real.RoundC[newClr.R * 255.0]; newPixel[1] _ Real.RoundC[newClr.G * 255.0]; newPixel[2] _ Real.RoundC[newClr.B * 255.0]; newPixel.length _ 3+addOn; }; $Grey => { IF newPixel.maxLength < 1+addOn THEN newPixel _ Pixels.GetSampleSet[1+addOn]; newPixel[0] _ Real.RoundC[255.0 * (newClr.R + newClr.G + newClr.B) / 3]; newPixel.length _ 1+addOn; }; $Dithered, $PseudoColor => { IF newPixel.maxLength < 1+addOn THEN newPixel _ Pixels.GetSampleSet[1+addOn]; newPixel[0] _ DitheredRGB[ renderMode, spot.x, spot.y, Real.RoundC[newClr.R * 255.0], Real.RoundC[newClr.G * 255.0], Real.RoundC[newClr.B * 255.0] ]; newPixel.length _ 1+addOn; }; ENDCASE => ScanConvertError[$BadRenderMode]; IF depth # NIL THEN newPixel[depth^] _ oldPixel[depth^]; -- no way to get it, currently IF alpha # NIL -- store coverage using VonNeumann rounding THEN { newPixel[alpha^] _ Basics.BITOR[ Real.RoundC[cvrge * 255.0], 1 ]; PutPixel[]; } ELSE Pixels.PixelOp[ buf, [spot.x, spot.y, 1, 1], newPixel, op ]; }; thePixel: SampleSet _ Pixels.GetSampleSet[4]; -- own memory for pixel retrieval GetSpot: PUBLIC ENTRY PROC [ buf: PixelBuffer, spot: Spot, renderMode: ATOM] RETURNS[ Spot ] ~ { ENABLE UNWIND => NULL; size: NAT _ 3; alpha: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Alpha] ]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Depth] ]; thePixel _ Pixels.GetPixel[buf, spot.x, spot.y, thePixel]; IF alpha # NIL THEN spot.coverage _ thePixel[alpha^] / 256.0; IF depth # NIL THEN { size _ 4; spot.val[3] _ thePixel[depth^]; }; IF spot.val = NIL OR spot.val.maxLength < size THEN spot.val _ NEW[RealSequence[size]]; SELECT renderMode FROM $FullColor, $Dorado24 => { spot.val[0] _ thePixel[0] / 256.0; spot.val[1] _ thePixel[1] / 256.0; spot.val[2] _ thePixel[2] / 256.0; }; $Grey => { spot.val[2] _ spot.val[1] _ spot.val[0] _ thePixel[0] / 256.0; }; $Dithered, $PseudoColor => { clr: RGB _ RGBFromMap[renderMode, thePixel[0] ]; spot.val[0] _ clr.R; spot.val[1] _ clr.G; spot.val[2] _ clr.B; }; ENDCASE => ScanConvertError[$BadRenderMode]; spot.val.length _ size; RETURN[spot]; }; PutLine: PUBLIC PROC [ buf: PixelBuffer, p1, p2: IntPair, color: SampleSet ] ~ { DoLine: PROC[] ~ { FOR i: NAT IN [0..buf.pixels.length) DO -- do one spot for each 8 bits or less IF buf.pixels[i].df = 1 THEN DrawLine[ buf.pixels[i], p1, p2, color[i] ] ELSE IF buf.pixels[i].subMap.sampleMap.base.bit = 0 -- do only for red map THEN DrawLine[ buf.pixels[i], p1, p2, color[i] * 256 + color[i+1] ]; ENDLOOP; }; vt : Terminal.Virtual _ Pixels.TerminalFromBuffer[buf]; [p1.x, p1.y] _ Pixels.XfmMapPlace[buf.pixels[buf.pixels.length-1], p1.x, p1.y ]; -- df = 1!! [p2.x, p2.y] _ Pixels.XfmMapPlace[buf.pixels[buf.pixels.length-1], p2.x, p2.y ]; IF vt # NIL THEN { -- Lock out cursor yOffset: NAT _ Pixels.GetTerminalYOffset[buf]; Terminal.ModifyColorFrame[ vt, DoLine, MIN[p1.x, p2.x], MIN[p1.y, p2.y] + yOffset, MAX[p1.x, p2.x], MAX[p1.y, p2.y] + yOffset ]; } ELSE DoLine[]; }; SetUpPoly: PROC [buf: PixelBuffer, plygn: REF SpotSequence _ NIL ] RETURNS [firstVtx, highest, lowest, leftmost, rightmost: NAT] ~ { firstVtx _ 0; FOR i: CARDINAL IN [0..plygn.length) DO [ plygn[i].x, plygn[i].y ] _ Pixels.XfmMapPlace[ buf.pixels[buf.pixels.length-1], -- df = 1! plygn[i].x, plygn[i].y ]; ENDLOOP; highest _ lowest _ plygn[0].y; leftmost _ rightmost _ plygn[0].x; FOR i: CARDINAL IN [1..plygn.length) DO yCoord: NAT _ plygn[i].y; xCoord: NAT _ plygn[i].x; IF yCoord < lowest THEN { lowest _ yCoord; firstVtx _ i; } ELSE IF yCoord > highest THEN highest _ yCoord; IF xCoord < leftmost THEN leftmost _ xCoord ELSE IF xCoord > rightmost THEN rightmost _ xCoord; ENDLOOP; }; ConstantPoly: PUBLIC ENTRY PROC [buf: PixelBuffer, color: SampleSet, plygn: REF SpotSequence] ~ { ENABLE UNWIND => NULL; vt : Terminal.Virtual _ Pixels.TerminalFromBuffer[buf]; highest, lowest, leftmost, rightmost, firstVtx: NAT _ 0; DoItConstant: PROC[] ~ { yPosn: NAT; -- current scan line nxtLVtx, nxtRVtx, rVtx, lVtx, nxtRHeight, nxtLHeight: NAT _ 0; left, right: IncrementalDesc; -- incremental descriptions for left and right edges nxtLVtx _ nxtRVtx _ rVtx _ lVtx _ firstVtx; -- set pointers to bottom vertex yPosn _ nxtRHeight _ nxtLHeight _ lowest; SetUpConstBlt[buf, yPosn, color]; -- set up scan segment blt WHILE yPosn < highest DO -- work up through vertices WHILE yPosn >= nxtLHeight DO -- next left vertex reached? lVtx _ nxtLVtx; nxtLVtx _ (lVtx + plygn.length - 1) MOD plygn.length; nxtLHeight _ plygn[nxtLVtx].y; left _ GetSlopeIncr[ [plygn[lVtx].x, plygn[lVtx].y], [plygn[nxtLVtx].x, plygn[nxtLVtx].y] ]; ENDLOOP; WHILE yPosn >= nxtRHeight DO -- next right vertex reached? rVtx _ nxtRVtx; nxtRVtx _ (rVtx + 1) MOD plygn.length; nxtRHeight _ plygn[nxtRVtx].y; right _ GetSlopeIncr[ [plygn[rVtx].x, plygn[rVtx].y], [plygn[nxtRVtx].x, plygn[nxtRVtx].y] ]; ENDLOOP; DoConstBlt[buf, left.val, right.val]; -- write segment left _ UpdateIncr[left]; right _ UpdateIncr[right]; yPosn _ yPosn + 1; -- update scan line ENDLOOP; IF yPosn > lowest THEN DoConstBlt[buf, left.val, right.val]; -- write top scan segment }; [firstVtx, highest, lowest, leftmost, rightmost] _ SetUpPoly[buf, plygn]; IF vt # NIL THEN { -- Lock out cursor yOffset: NAT _ Pixels.GetTerminalYOffset[ buf ]; Terminal.ModifyColorFrame[ vt, DoItConstant, leftmost, lowest+yOffset, rightmost, highest+yOffset ]; } ELSE DoItConstant[]; }; SmoothPoly: PUBLIC PROC [buf: PixelBuffer, plygn: REF SpotSequence, renderMode: ATOM] ~ { vt : Terminal.Virtual _ Pixels.TerminalFromBuffer[buf]; alpha: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Alpha] ]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Depth] ]; highest, lowest, leftmost, rightmost, firstVtx: NAT _ 0; DoItSmooth: PROC[] ~ { segPtr: ARRAY [0..5) OF LONG POINTER; wdsPerLine: ARRAY [0..5) OF CARDINAL; wrdPtr: ARRAY [0..5) OF LONG POINTER; SetUpScanSeg: PROC[yPosn: NAT] ~ TRUSTED { addOn: NAT _ IF alpha # NIL THEN 1 ELSE 0; -- make room for alpha buffer FOR i: NAT IN [0 .. buf.pixels.length - addOn) DO wdsPerLine[i] _ buf.pixels[i].subMap.sampleMap.bitsPerLine / bitsPerWord; segPtr[i] _ buf.pixels[i].subMap.sampleMap.base.word + Basics.LongMult[yPosn, wdsPerLine[i]]; ENDLOOP; }; PutScanSeg: PROC[ left, lftR, lftG, lftB, lftZ, right, rgtR, rgtG, rgtB, rgtZ: INTEGER ] ~ TRUSTED { doIt: BOOLEAN _ TRUE; red, grn, blu, z: IncrementalDesc; EvalDepth: PROC[] ~ TRUSTED { IF LOOPHOLE[wrdPtr[depth^]^, CARDINAL] > CARDINAL[z.val] THEN { LOOPHOLE[wrdPtr[depth^]^, CARDINAL] _ z.val; doIt _ TRUE; } ELSE doIt _ FALSE; z _ UpdateIncr[z]; wrdPtr[depth^] _ wrdPtr[depth^] + 1; }; IF left > right THEN [left, right] _ Swap[left, right]; red _ GetSlopeIncr[ [lftR, left], [rgtR, right] ]; grn _ GetSlopeIncr[ [lftG, left], [rgtG, right] ]; blu _ GetSlopeIncr[ [lftB, left], [rgtB, right] ]; IF depth # NIL THEN { z _ GetSlopeIncr[ [lftZ, left], [rgtZ, right] ]; wrdPtr[depth^] _ segPtr[depth^] + left; }; SELECT renderMode FROM $Dithered, $PseudoColor => { wrdPtr[0] _ segPtr[0] + Basics.BITSHIFT[left,-1]; FOR x: INTEGER IN [left..right] DO value: INTEGER _ DitheredRGB[renderMode, x, yPosn, red.val, grn.val, blu.val]; IF depth # NIL THEN EvalDepth[]; -- sets/clears doIt based on depth IF Basics.BITAND[x,1] = 0 THEN { IF doIt THEN LOOPHOLE[wrdPtr[0]^, BytePair].high _ value; } ELSE { IF doIt THEN LOOPHOLE[wrdPtr[0]^, BytePair].low _ value; wrdPtr[0] _ wrdPtr[0] + 1; }; -- odd, low byte, incr. to next red _ UpdateIncr[red]; grn _ UpdateIncr[grn]; blu _ UpdateIncr[blu]; ENDLOOP; }; $Grey => { wrdPtr[0] _ segPtr[0] + Basics.BITSHIFT[left,-1]; FOR x: INTEGER IN [left..right] DO IF depth # NIL THEN EvalDepth[]; -- sets/clears doIt based on depth IF Basics.BITAND[x,1] = 0 THEN { IF doIt THEN LOOPHOLE[wrdPtr[0]^, BytePair].high _ red.val; } ELSE { IF doIt THEN LOOPHOLE[wrdPtr[0]^, BytePair].low _ red.val; wrdPtr[0] _ wrdPtr[0] + 1; }; -- odd, low byte, incr. to next red _ UpdateIncr[red]; ENDLOOP; }; $FullColor, $Dorado24 => { wrdPtr[0] _ segPtr[0] + left; wrdPtr[2] _ segPtr[2] + Basics.BITSHIFT[left,-1]; FOR x: INTEGER IN [left..right] DO IF depth # NIL THEN EvalDepth[]; -- sets/clears doIt based on depth IF doIt THEN wrdPtr[0]^ _ Basics.BITSHIFT[red.val, 8] + grn.val; wrdPtr[0] _ wrdPtr[0] + 1; IF Basics.BITAND[x,1] = 0 THEN { IF doIt THEN LOOPHOLE[wrdPtr[2]^, BytePair].high _ blu.val; } ELSE { IF doIt THEN LOOPHOLE[wrdPtr[2]^, BytePair].low _ blu.val; wrdPtr[2] _ wrdPtr[2] + 1; }; -- odd, low byte, incr. to next red _ UpdateIncr[red]; grn _ UpdateIncr[grn]; blu _ UpdateIncr[blu]; ENDLOOP; segPtr[2] _ segPtr[2] + wdsPerLine[2]; }; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; IF depth # NIL THEN segPtr[depth^] _ segPtr[depth^] + wdsPerLine[depth^]; segPtr[0] _ segPtr[0] + wdsPerLine[0]; }; GetColor: PROC[color: REF RealSequence] RETURNS[outClr: IntRGBZ] ~ { SELECT color.length FROM 1 => outClr _ [Real.RoundC[color[0]*255.0], 0, 0, 0 ]; 2 => outClr _ [Real.RoundC[color[0]*255.0], 0, 0, Real.RoundC[color[1]*255.0] ]; 3 => outClr _ [Real.RoundC[color[0]*255.0], Real.RoundC[color[1]*255.0], Real.RoundC[color[2]*255.0], 0]; 4 => outClr _ [Real.RoundC[color[0]*255.0], Real.RoundC[color[1]*255.0], Real.RoundC[color[2]*255.0], Real.RoundC[color[3]] ]; ENDCASE => SIGNAL ScanConvertError[$BadLength]; }; yPosn: NAT; -- current scan line left, lftR, lftG, lftB, lftZ, right, rgtR, rgtG, rgtB, rgtZ: IncrementalDesc; -- edge description nxtLVtx, nxtRVtx, rVtx, lVtx, nxtRHeight, nxtLHeight: NAT _ 0; clr, nxtClr: IntRGBZ; nxtLVtx _ nxtRVtx _ rVtx _ lVtx _ firstVtx; -- set pointers to bottom vertex yPosn _ nxtRHeight _ nxtLHeight _ lowest; SetUpScanSeg[yPosn]; WHILE yPosn < highest DO -- work up through vertices WHILE yPosn >= nxtLHeight DO -- next left vertex reached? lVtx _ nxtLVtx; nxtLVtx _ (lVtx + plygn.length - 1) MOD plygn.length; nxtLHeight _ plygn[nxtLVtx].y; left _ GetSlopeIncr[ [plygn[lVtx].x, plygn[lVtx].y], [plygn[nxtLVtx].x, plygn[nxtLVtx].y] ]; clr _ GetColor[plygn[lVtx].val]; nxtClr _ GetColor[plygn[nxtLVtx].val]; lftR _ GetSlopeIncr[ [clr.r, plygn[lVtx].y], [nxtClr.r, plygn[nxtLVtx].y] ]; lftG _ GetSlopeIncr[ [clr.g, plygn[lVtx].y], [nxtClr.g, plygn[nxtLVtx].y] ]; lftB _ GetSlopeIncr[ [clr.b, plygn[lVtx].y], [nxtClr.b, plygn[nxtLVtx].y] ]; IF depth # NIL THEN lftZ _ GetSlopeIncr[ [clr.z, plygn[lVtx].y], [nxtClr.z, plygn[nxtLVtx].y] ]; ENDLOOP; WHILE yPosn >= nxtRHeight DO -- next right vertex reached? rVtx _ nxtRVtx; nxtRVtx _ (rVtx + 1) MOD plygn.length; nxtRHeight _ plygn[nxtRVtx].y; right _ GetSlopeIncr[ [plygn[rVtx].x, plygn[rVtx].y], [plygn[nxtRVtx].x, plygn[nxtRVtx].y] ]; clr _ GetColor[plygn[rVtx].val]; nxtClr _ GetColor[plygn[nxtRVtx].val]; rgtR _ GetSlopeIncr[ [clr.r, plygn[rVtx].y], [nxtClr.r, plygn[nxtRVtx].y] ]; rgtG _ GetSlopeIncr[ [clr.g, plygn[rVtx].y], [nxtClr.g, plygn[nxtRVtx].y] ]; rgtB _ GetSlopeIncr[ [clr.b, plygn[rVtx].y], [nxtClr.b, plygn[nxtRVtx].y] ]; IF depth # NIL THEN rgtZ _ GetSlopeIncr[ [clr.z, plygn[rVtx].y], [nxtClr.z, plygn[nxtRVtx].y] ]; ENDLOOP; PutScanSeg[ left.val, lftR.val, lftG.val, lftB.val, lftZ.val, right.val, rgtR.val, rgtG.val, rgtB.val, rgtZ.val ]; left _ UpdateIncr[left]; lftR _ UpdateIncr[lftR]; lftG _ UpdateIncr[lftG]; lftB _ UpdateIncr[lftB]; right _ UpdateIncr[right]; rgtR _ UpdateIncr[rgtR]; rgtG _ UpdateIncr[rgtG]; rgtB _ UpdateIncr[rgtB]; IF depth # NIL THEN { lftZ _ UpdateIncr[lftZ]; rgtZ _ UpdateIncr[rgtZ]; }; yPosn _ yPosn + 1; -- update scan line ENDLOOP; IF yPosn > lowest THEN PutScanSeg[ left.val, lftR.val, lftG.val, lftB.val, lftZ.val, right.val, rgtR.val, rgtG.val, rgtB.val, rgtZ.val ]; }; [firstVtx, highest, lowest, leftmost, rightmost] _ SetUpPoly[buf, plygn]; IF vt # NIL THEN { -- Lock out cursor yOffset: NAT _ Pixels.GetTerminalYOffset[ buf ]; Terminal.ModifyColorFrame[ vt, DoItSmooth, leftmost, lowest+yOffset, rightmost, highest+yOffset ]; } ELSE DoItSmooth[]; }; lftSeq: REF IncDescSeq _ NEW[IncDescSeq[9]]; -- 9 = rgb(3) + nml x-y(2) + light rgb(3) + z rgtSeq: REF IncDescSeq _ NEW[IncDescSeq[9]]; segSeq: REF IncDescSeq _ NEW[IncDescSeq[9]]; ShinyPoly: PUBLIC PROC [buf: PixelBuffer, plygn: REF SpotSequence, shininess: NAT, renderMode: ATOM] ~ { vt : Terminal.Virtual _ Pixels.TerminalFromBuffer[buf]; alpha: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Alpha] ]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Depth] ]; highest, lowest, leftmost, rightmost, firstVtx: NAT _ 0; DoItShiny: PROC[] ~ { segPtr: ARRAY [0..5) OF LONG POINTER; wdsPerLine: ARRAY [0..5) OF CARDINAL; wrdPtr: ARRAY [0..5) OF LONG POINTER; SetUpShinySeg: PROC[yPosn: NAT] ~ TRUSTED { -- set up scan segment pointers addOn: NAT _ IF alpha # NIL THEN 1 ELSE 0; -- make room for alpha buffer FOR i: NAT IN [0 .. buf.pixels.length - addOn) DO wdsPerLine[i] _ buf.pixels[i].subMap.sampleMap.bitsPerLine / bitsPerWord; segPtr[i] _ buf.pixels[i].subMap.sampleMap.base.word + Basics.LongMult[yPosn, wdsPerLine[i]]; ENDLOOP; }; GetShinyColor: PROC[ segSeq: REF IncDescSeq ] RETURNS[clr: IntRGB] ~ { red, grn, blu: CARDINAL _ 0; noticeableHilite: BOOLEAN _ FALSE; FOR i: NAT IN [ 0 .. (segSeq.length-3)/5 ) DO j: NAT _ i*5 + 3; pctHilite: CARDINAL _ Power[ MAX[ 0, INTEGER[Basics.BITSHIFT[65535 - Sqr[segSeq[j].val] - Sqr[segSeq[j+1].val], -8] ] ], shininess ]; IF pctHilite > justNoticeable THEN { -- Scale light color by hilite strength red _ red + Basics.BITSHIFT[pctHilite * segSeq[j+2].val, -8]; grn _ grn + Basics.BITSHIFT[pctHilite * segSeq[j+3].val, -8]; blu _ blu + Basics.BITSHIFT[pctHilite * segSeq[j+4].val, -8]; noticeableHilite _ TRUE; }; ENDLOOP; IF noticeableHilite THEN { clr.r _ MIN[255, segSeq[0].val + Basics.BITSHIFT[(255 - segSeq[0].val) * red, -8] ]; clr.g _ MIN[255, segSeq[1].val + Basics.BITSHIFT[(255 - segSeq[1].val) * grn, -8] ]; clr.b _ MIN[255, segSeq[2].val + Basics.BITSHIFT[(255 - segSeq[2].val) * blu, -8] ]; } ELSE { clr.r _ segSeq[0].val; clr.g _ segSeq[1].val; clr.b _ segSeq[2].val; }; FOR j: NAT IN [0..segSeq.length) DO -- increment for next pixel segSeq[j] _ UpdateIncr[ segSeq[j] ]; ENDLOOP; }; PutShinySeg: PROC[ left: INTEGER, lftSeq: REF IncDescSeq, right: INTEGER, rgtSeq: REF IncDescSeq ] ~ TRUSTED { doIt: BOOLEAN _ TRUE; EvalDepth: PROC[] ~ TRUSTED { IF LOOPHOLE[wrdPtr[depth^]^, CARDINAL] > CARDINAL[segSeq[segSeq.length-1].val] THEN { LOOPHOLE[wrdPtr[depth^]^, CARDINAL] _ segSeq[segSeq.length-1].val; doIt _ TRUE; } ELSE doIt _ FALSE; wrdPtr[depth^] _ wrdPtr[depth^] + 1; }; IF left > right THEN [left, right] _ Swap[left, right]; FOR j: NAT IN [0..lftSeq.length) DO segSeq[j] _ GetSlopeIncr[ [lftSeq[j].val, left], [rgtSeq[j].val, right] ]; ENDLOOP; IF depth # NIL THEN wrdPtr[depth^] _ segPtr[depth^] + left; SELECT renderMode FROM $Dithered, $PseudoColor, $Grey => { wrdPtr[0] _ segPtr[0] + Basics.BITSHIFT[left,-1]; FOR x: INTEGER IN [left..right] DO clr: IntRGB; value: INTEGER; IF depth # NIL THEN EvalDepth[]; -- sets/clears doIt based on depth clr _ GetShinyColor[segSeq]; value _ IF renderMode = $Grey THEN (clr.r+clr.g+clr.b)/3 ELSE DitheredRGB[renderMode, x, yPosn, clr.r, clr.g, clr.b]; IF Basics.BITAND[x,1] = 0 THEN { IF doIt THEN LOOPHOLE[wrdPtr[0]^, BytePair].high _ value; } ELSE { IF doIt THEN LOOPHOLE[wrdPtr[0]^, BytePair].low _ value; wrdPtr[0] _ wrdPtr[0] + 1; }; -- odd, low byte, incr. to next ENDLOOP; }; $FullColor, $Dorado24 => { wrdPtr[0] _ segPtr[0] + left; wrdPtr[2] _ segPtr[2] + Basics.BITSHIFT[left,-1]; FOR x: INTEGER IN [left..right] DO clr: IntRGB; IF depth # NIL THEN EvalDepth[]; -- sets/clears doIt based on depth clr _ GetShinyColor[segSeq]; IF doIt THEN wrdPtr[0]^ _ Basics.BITSHIFT[clr.r, 8] + clr.g; wrdPtr[0] _ wrdPtr[0] + 1; IF Basics.BITAND[x,1] = 0 THEN { IF doIt THEN LOOPHOLE[wrdPtr[2]^, BytePair].high _ clr.b; } ELSE { IF doIt THEN LOOPHOLE[wrdPtr[2]^, BytePair].low _ clr.b; wrdPtr[2] _ wrdPtr[2] + 1; }; -- odd, low byte, incr. to next ENDLOOP; segPtr[2] _ segPtr[2] + wdsPerLine[2]; }; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; IF depth # NIL THEN segPtr[depth^] _ segPtr[depth^] + wdsPerLine[depth^]; segPtr[0] _ segPtr[0] + wdsPerLine[0]; }; yPosn: NAT; -- current scan line left, right: IncrementalDesc; -- edge descriptions nxtLVtx, nxtRVtx, rVtx, lVtx, nxtRHeight, nxtLHeight: NAT _ 0; nxtLVtx _ nxtRVtx _ rVtx _ lVtx _ firstVtx; -- set pointers to bottom vertex yPosn _ nxtRHeight _ nxtLHeight _ lowest; SetUpShinySeg[yPosn]; -- prepare scan segment structure IF lftSeq.length < plygn[lVtx].val.length THEN { length: NAT_ plygn[lVtx].val.length; -- get more space lftSeq _ NEW[IncDescSeq[length]]; -- length = rgb(3) rgtSeq _ NEW[IncDescSeq[length]]; -- + (nml x-y(2) + light rgb(3)) * # of lights segSeq _ NEW[IncDescSeq[length]]; }; WHILE yPosn < highest DO -- work up through vertices this, next: REF RealSequence; WHILE yPosn >= nxtLHeight DO -- next left vertex reached? lVtx _ nxtLVtx; nxtLVtx _ (lVtx + plygn.length - 1) MOD plygn.length; nxtLHeight _ plygn[nxtLVtx].y; left _ GetSlopeIncr[ [plygn[lVtx].x, plygn[lVtx].y], [plygn[nxtLVtx].x, plygn[nxtLVtx].y] ]; this _ plygn[lVtx].val; next _ plygn[nxtLVtx].val; FOR j: NAT IN [0..plygn[lVtx].val.length) DO lftSeq[j] _ GetSlopeIncr[ [Real.FixI[this[j]], plygn[lVtx].y], [Real.FixI[next[j]], plygn[nxtLVtx].y] ]; ENDLOOP; ENDLOOP; WHILE yPosn >= nxtRHeight DO -- next right vertex reached? rVtx _ nxtRVtx; nxtRVtx _ (rVtx + 1) MOD plygn.length; nxtRHeight _ plygn[nxtRVtx].y; right _ GetSlopeIncr[ [plygn[rVtx].x, plygn[rVtx].y], [plygn[nxtRVtx].x, plygn[nxtRVtx].y] ]; this _ plygn[rVtx].val; next _ plygn[nxtRVtx].val; FOR j: NAT IN [0..plygn[lVtx].val.length) DO rgtSeq[j] _ GetSlopeIncr[ [Real.FixI[this[j]], plygn[rVtx].y], [Real.FixI[next[j]], plygn[nxtRVtx].y] ]; ENDLOOP; ENDLOOP; PutShinySeg[ left.val, lftSeq, right.val, rgtSeq ]; -- put out scan segment left _ UpdateIncr[left]; right _ UpdateIncr[right]; FOR j: NAT IN [0..lftSeq.length) DO lftSeq[j] _ UpdateIncr[ lftSeq[j] ]; rgtSeq[j] _ UpdateIncr[ rgtSeq[j] ]; ENDLOOP; yPosn _ yPosn + 1; -- update scan line ENDLOOP; IF yPosn > lowest THEN PutShinySeg[ left.val, lftSeq, right.val, rgtSeq ]; }; [firstVtx, highest, lowest, leftmost, rightmost] _ SetUpPoly[buf, plygn]; IF vt # NIL THEN { -- Lock out cursor yOffset: NAT _ Pixels.GetTerminalYOffset[ buf ]; Terminal.ModifyColorFrame[ vt, DoItShiny, leftmost, lowest+yOffset, rightmost, highest+yOffset ]; } ELSE DoItShiny[]; }; DrawLine: PROC [destination: Pixels.SubMap, p1, p2: IntPair, -- fast line, constant color pxlValue: CARDINAL, function: Function _ [null, null]] ~ { increment, bias, error, sBump, t, shiftDist: INTEGER; wrdPtr: LONG POINTER TO WORD; p1f: INTEGER _ p1.x; p1s: INTEGER _ p1.y; p2f: INTEGER _ p2.x; p2s: INTEGER _ p2.y; dest: SampleMapOps.SampleMap _ destination.subMap.sampleMap; bitsPerPixel: NAT ~ dest.bitsPerSample * destination.df; lgBitsPerPixel: NAT ~ Log2[bitsPerPixel]; logPxlsPerWd: NAT ~ Basics.logBitsPerWord - lgBitsPerPixel; wordsPerLine: NAT _ dest.bitsPerLine / bitsPerWord; maxShift: NAT ~ Basics.bitsPerWord - bitsPerPixel; maxValue: CARDINAL ~ Basics.BITSHIFT[1, bitsPerPixel] - 1; value: CARDINAL ~ MIN[pxlValue, maxValue]; IF p1f > p2f THEN { t _ p1f; p1f _ p2f; p2f _ t; t _ p1s; p1s _ p2s; p2s _ t; }; TRUSTED { wrdPtr _ LOOPHOLE[dest.base.word + Basics.LongMult[wordsPerLine, p1s] + Basics.BITSHIFT[p1f, -logPxlsPerWd] ]; }; shiftDist _ maxShift - Basics.BITSHIFT[ Basics.BITAND[ p1f, Basics.BITSHIFT[1, logPxlsPerWd] - 1], -- p1f MOD pixelsPerWord lgBitsPerPixel ]; IF (p2f - p1f) > ABS[p2s - p1s] THEN { increment _ LOOPHOLE[Basics.BITSHIFT[LOOPHOLE[p2s - p1s], 1], INTEGER]; bias _ LOOPHOLE[Basics.BITSHIFT[LOOPHOLE[p2f - p1f], 1], INTEGER]; sBump _ SGN[increment] * wordsPerLine; increment _ ABS[increment]; error _ increment - Basics.BITSHIFT[bias, -1]; IF lgBitsPerPixel = 3 -- speedup for 8 bits per pixel THEN FOR i: NAT IN [0..(p2f-p1f)] DO TRUSTED { IF shiftDist = 0 THEN { LOOPHOLE[wrdPtr^, BytePair].low _ value; wrdPtr _ wrdPtr + 1; shiftDist _ 8; } ELSE { LOOPHOLE[wrdPtr^, BytePair].high _ value; shiftDist _ 0; }; IF error > 0 THEN TRUSTED { wrdPtr _ wrdPtr + sBump; error _ error - bias; }; error _ error + increment; }; ENDLOOP ELSE FOR i: NAT IN [0..(p2f-p1f)] DO TRUSTED { wrdPtr^ _ Basics.BITOR[ -- deposit pixel bits in word Basics.BITAND[wrdPtr^, Basics.BITNOT[Basics.BITSHIFT[maxValue, shiftDist]]], Basics.BITSHIFT[value, shiftDist] ]; }; IF shiftDist = 0 THEN TRUSTED { wrdPtr _ wrdPtr + 1; shiftDist _ maxShift; } ELSE shiftDist _ shiftDist - bitsPerPixel; IF error > 0 THEN TRUSTED { wrdPtr _ wrdPtr + sBump; error _ error - bias; }; error _ error + increment; ENDLOOP; } ELSE { j: NAT _ Basics.BITSHIFT[shiftDist, -lgBitsPerPixel]; pixelsPerWd: NAT ~ Basics.BITSHIFT[1, logPxlsPerWd]; mask, values: ARRAY [0..16) OF CARDINAL; FOR i: NAT IN [0..pixelsPerWd) DO mask[i] _ Basics.BITNOT[Basics.BITSHIFT[maxValue, Basics.BITSHIFT[i, lgBitsPerPixel]]]; values[i] _ Basics.BITSHIFT[value, Basics.BITSHIFT[i, lgBitsPerPixel]]; ENDLOOP; increment _ LOOPHOLE[Basics.BITSHIFT[LOOPHOLE[p2f - p1f], 1], INTEGER]; bias _ LOOPHOLE[Basics.BITSHIFT[LOOPHOLE[p2s - p1s], 1], INTEGER]; sBump _ SGN[bias] * wordsPerLine; bias _ ABS[bias]; error _ increment - Basics.BITSHIFT[bias, -1]; IF lgBitsPerPixel = 3 -- speedup for 8 bits per pixel THEN FOR i: NAT IN [0..ABS[p2s - p1s]] DO TRUSTED { IF shiftDist = 0 THEN { LOOPHOLE[wrdPtr^, BytePair].low _ value; IF error > 0 THEN { wrdPtr _ wrdPtr + 1; shiftDist _ 8; error _ error - bias; }; } ELSE { LOOPHOLE[wrdPtr^, BytePair].high _ value; IF error > 0 THEN { shiftDist _ 0; error _ error - bias; }; }; wrdPtr _ wrdPtr + sBump; error _ error + increment; }; ENDLOOP ELSE FOR i: NAT IN [0..ABS[p2s - p1s]] DO TRUSTED { wrdPtr^ _ Basics.BITOR[ Basics.BITAND[wrdPtr^, mask[j]] , values[j]]; }; TRUSTED { wrdPtr _ wrdPtr + sBump; }; IF error > 0 THEN { error _ error - bias; IF j = 0 THEN TRUSTED { wrdPtr _ wrdPtr + 1; j _ pixelsPerWd - 1; } ELSE j _ j - 1; }; error _ error + increment; ENDLOOP; }; }; END. ˆOldScanConvertImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Frank Crow, March 31, 1987 2:01:55 pm PST Scan conversion operations on pixel buffers. Expects input to be properly clipped. Type Definitions Global Constants Utility Procedures Assumes 8-bit pixels Extended Bresenham incrementation Ordered dither for crude looks at full-color Ordered dither for crude looks at full-color using Imager's color map Point/Spot Operations Scan Conversion for Lines and Scan Segments rg interleaved, do both at once by packing up Red and Green Scan Conversion for Convex Areas Find bounding box and first vertex in scan order for polygon; prepare for scan conversion Convert to pixel map coordinates Scan convert constant shaded polygon, uses some global bitBlt state so must be ENTRY PROC IncrementalDesc: TYPE ~ RECORD[val, intInc, intSgn, fracInc, fracRng, fracPos: INTEGER]; Transform to framebuffer space, get limits bytePtr: LONG POINTER TO rawBytes; IncrementalDesc: TYPE ~ RECORD[val, intInc, intSgn, fracInc, fracRng, fracPos: INTEGER]; Write top segment (polygon includes all its edges) Proc Body Starts Here!!!! Transform to framebuffer space, get limits Calculate color with hilite Write top segment (polygon includes all its edges) Proc Body Starts Here!!!! Transform to framebuffer space, get limits Scan Conversion Teach this one how to use dithering etc. Get necessary constants based on bits per pixel Make sure of positive-going fast coordinate Get pointer to initial word and bit offset More horizontal line (moves faster along fast axis) More vertical line (moves faster along slow axis) Κ%ή˜Ihead2šœ™šœ Οmœ1™Lšžœ ˜—š  œžœžœžœžœ Ÿ,˜[Ošœžœžœ˜Ošœžœžœ˜šžœžœž˜Ošœ ˜ Ošœ ˜ Ošžœ˜—O˜O˜—š  œž œ žœ žœžœ žœ˜NMšœ žœ Ÿ2˜NMšœžœ˜Mšœžœ˜&šžœž˜Mšžœžœžœ˜˜>Mšœžœ˜9Mšžœžœžœ'˜Všžœ˜šžœ˜Mšžœ?Ÿ˜TMšžœŸ+˜B—MšžœΟfœŸ˜M—Jšœ2˜2šœ Ÿ>˜KJšœJ˜JJšœ˜Jšœ"˜"JšœC˜CJšœ ˜ Jšœ ˜ Jšœ&žœžœžœ˜jJšœ˜—Jšžœ˜—J˜—š  œžœ žœžœ˜DJšžœžœ&Ÿ!˜[šžœžœžœž˜)Jšœ žœžœ˜+šžœŸ8˜Ršžœžœ-˜4šžœ˜Jšœ4˜4Jšœ'˜'Jšœ˜—JšžœžœŸ˜*—šžœ˜ šœ˜Jšœžœ ˜.Jšœ žœžœ ˜/Jšœ˜—Jšœ&˜&Jšœ˜——Jšœžœ˜Jšœ0Ÿ'˜WJšžœ˜—Jšœ˜—š  œžœžœ˜LJšœžœžœ˜FJš žœ žœ žœžœŸ(˜NJšœ Ÿ˜8Jšœžœ Ÿ!˜>Jšœ(Ÿ+˜SJšœžœŸ˜>Jšœ!Ÿ˜;Jšœ/Ÿ'˜VJšœŸ˜+Jšœ ˜—š  œžœžœ˜EJ™!šžœžœŸ!˜HJšœ4Ÿ˜LJšœ,Ÿ˜@Jšœ˜—Jšœ,Ÿ˜EJšœ3Ÿ ˜SLšžœ ˜Lšœ˜Jš œ žœžœžœžœžœD˜t—š   œž œ žœžœžœžœ˜]Jšœ!žœ˜%šžœ ž˜˜J™,Jšœ žœžœžœ˜IJšœžœ žœ žœŸ˜SJš œžœ žœ žœžœ˜SJšœžœ žœ žœŸ˜SJšœžœŸ ˜;JšžœžœžœŸ˜MJšœžœ˜%Jšžœžœžœ˜=Jšœžœ˜%Jšžœžœžœ˜=šžœžœ˜Jšœžœžœžœžœžœ ŸœŸΟi ˜½—Jšœ˜—˜J™EJšœ žœžœžœ˜/Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœžœžœ˜FJšœžœžœžœ˜FJšœžœžœžœ˜FJšœ(˜(Jšžœžœ Ÿ˜NJšžœžœ˜Jšœ˜—Jšžœžœ"˜3—Jšžœ˜Jšœ˜—šΠbn œžœžœ žœžœžœžœ˜Cšžœ ž˜šœ˜Jšœžœ_˜jJšžœžœŸ˜CJšžœ ˜Jšœ˜—šœžœ˜JšœR˜RJšœ˜—Jšžœžœ"˜4—Lšžœ˜Lšœ˜—š  œžœžœ žœ žœžœžœ˜Fšžœ žœ˜šœ˜Jšžœ žœŸ˜BJšœžœ ˜šžœ˜ Jšœ˜Jšœ žœ˜Jšœ žœ ˜Jšœ˜—Jšœ˜—šœ˜Jšœžœ˜šžœ˜ Jšœ˜Jšœ žœ˜Jšœ žœ ˜Jšœ˜—Jšœ˜—Jšžœžœ"˜4—Lšžœ˜Lšœ˜—š  œž œžœžœžœ˜IJšžœ˜Jšœ˜J˜—š œžœžœ žœžœ žœžœžœž œ˜„šžœ˜ Jšœžœ&˜GJšœ žœžœ'˜IJšœ˜—Jšœ˜J˜—š  œž œ žœžœžœžœ˜?Jš žœ žœžœžœžœ˜0Jšœ˜——šΟb™šœžœ˜2Mšœ0Ÿ˜OMšœ-˜-M˜—š œžœ1žœ˜UJšžœžœžœ˜š œžœ˜Jš œžœ=˜Gšžœžœ˜ šžœ˜Mšœ žœ"˜.Mšœžœ˜MšœPŸ ˜ZMšœ-žœ žœ$˜aM˜—Mšžœ˜ —Jšœ˜—š œžœžœ˜.Jš œžœF˜Pšžœžœ˜ šžœ˜Mšœ žœ"˜.Mšœžœ˜MšœPŸ ˜ZMšœ-žœ žœ$˜aM˜—Mšžœ˜ —Jšœ˜—Mšœ7˜7Jšœžœžœžœ,˜CJšœžœžœžœ,˜CJšœžœ˜Mšœžœ˜Mšœžœ˜Mšžœ žœžœ˜&Mšžœ žœžœ˜&M˜šžœ žœžœŸ˜.Mšžœ žœžœŸ&˜Tšžœžœžœ&˜TMšžœ"Ÿ˜=—Mš žœžœžœžœžœŸ˜nšžœ žœ Ÿ˜?MšœK˜KMšœ˜MšœL˜LJšœR˜RJšžœžœ"˜4—Mšœ-Ÿ˜GMšœ˜Mšœ"˜"Mšžœžœ)˜Išžœžœ˜,šžœžœ˜šžœŸ:˜Dšžœ˜Mšžœ žœŸ˜HMšžœ žœŸ!˜L—Mšœ'˜'Mšœ'˜'Mšœ(˜(Mšœ Ÿ˜;M˜—šžœŸ˜,Mšœ˜Mšœ˜Mšœ˜M˜——šžœŸ˜#Mšœ,Ÿ˜KMšœ9Ÿ˜AMšœ4˜4Mšœ4˜4M˜——M˜—Jšžœ&Ÿ1˜[šžœ ž˜šœ˜Jšžœžœ)˜MJšœ,˜,Jšœ,˜,Jšœ,˜,Jšœ˜Jšœ˜—šœ ˜ Jšžœžœ)˜MJšœH˜HJšœ˜J˜—šœ˜Jšžœžœ)˜MJšœœ˜œJšœ˜Jšœ˜—Jšžœ&˜-—Mšžœ žœžœ&Ÿ˜Wšžœ žœ Ÿ+˜CMšžœžœ3˜YJšžœ=˜A—Mšœ˜Mšœ0Ÿ!˜QM˜—š œžœ-žœ žœ ˜hJšžœžœžœ˜Mšœžœ˜Jšœžœžœžœ,˜CJšœžœžœžœ,˜CMšœ:˜:Mšžœ žœžœ*˜=Mšžœ žœžœ4˜GMš žœ žœžœžœ žœ˜Xšžœ ž˜šœ˜Jšœ"˜"Jšœ"˜"Jšœ"˜"Jšœ˜—šœ ˜ Jšœ>˜>J˜—šœ˜Jšœžœ(˜0Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšžœ&˜-—Mšœ˜Mšžœ˜ Mšœ˜——™+š œžœžœ<˜Pš œžœ˜šžœžœžœžŸ)˜Pšžœ˜Mšžœ,˜0šžœžœ1Ÿ˜NMšŸ;™;Mšžœ@˜D——Mšžœ˜—M˜—Mšœ7˜7MšœQŸ ˜\MšœP˜Pšžœžœ˜ šžœŸ˜&Mšœ žœ"˜.Mš œ'žœžœ&žœžœ˜ŽMšœ˜—Mšžœ ˜—Lšœ˜——™ š   œžœžœžœ2žœ˜ŠJ™Yšœ ˜ MšŸ œ™"—šžœžœžœž˜'JšœRŸ œ'˜„Jšžœ˜—Jšœ"˜"Jšœ#˜#šžœžœžœž˜'Jšœžœ˜Jšœžœ˜šžœ˜Jšžœ'˜+Jšžœžœžœ˜/—šžœ˜Jšžœ˜Jšžœžœžœ˜3—Jšžœ˜ —M˜—š  œžœ;žœ˜oMšœOžœž™YJšžœžœžœ˜Mšœ7˜7Jšœ0žœ ˜<š  œžœ˜Jšœžœ Ÿ˜'Jšœ6žœ˜@Jšœ Ÿ4˜TLšœžœžœ1žœ™XJ˜Jšœ8Ÿ ˜XJšœ-˜-Jšœ)Ÿ˜Cšžœžœ Ÿ˜=šžœžœŸ˜@Jšœ7žœ˜HJšœ˜Jšœd˜dJšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ˜Jšœe˜eJšžœ˜—Jšœ%Ÿ˜@Jšœ˜Jšœ˜Jšœ Ÿ˜3Jšžœ˜—Jšžœžœ'Ÿ˜VJ˜—MšŸ*™*MšœI˜Išžœžœ˜ šžœŸ˜!Mšœ žœ$˜0Mšœn˜nM˜—Mšžœ˜—Jšœ˜—š  œž œžœžœ˜YMšœ7˜7Jšœžœžœžœ,˜CJšœžœžœžœ,˜CJšœ0žœ ˜<š  œžœ˜Jš œžœžœžœžœ˜%Jšœ žœžœžœ˜%Jš œžœžœžœžœ˜%š  œžœžœžœ˜*Jš œžœžœ žœžœžœŸ˜Jšžœžœžœ"ž˜2JšœI˜IJšœk˜kJšžœ˜—Jšœ˜—š  œžœ?žœžœ˜dJšœžœžœ˜Jšœ"˜"š  œžœžœ˜šžœžœžœžœ˜9šžœžœžœ ˜4Jšœ žœ˜—Jšžœžœ˜—Jšœ˜Jšœ$˜$J˜—Jšžœžœ#˜7Jšœ2˜2Jšœ2˜2Jšœ2˜2šžœ žœžœ˜Jšœ1˜1Jšœ'˜'Jšœ˜—šžœ ž˜˜Jšœžœ ˜1šžœžœžœž˜"Jšœžœ@˜NJšžœ žœžœŸ"˜Fšžœžœ ˜Jšžœžœžœžœ'˜DJš žœžœžœžœIŸ˜†—JšœH˜HJšžœ˜—J˜—šœ ˜ Jšœžœ ˜1šžœžœžœž˜"Jšžœ žœžœŸ"˜Fšžœžœ ˜Jšžœžœžœžœ)˜GJš žœžœžœžœKŸ˜ˆ—Jšœ˜Jšžœ˜—J˜—šœ˜Jšœ@žœ ˜Ršžœžœžœž˜"Jšžœ žœžœŸ"˜FJšžœžœžœ˜CJšœ˜šžœžœ ˜Jšžœžœžœžœ)˜GJš žœžœžœžœKŸ˜ˆ—JšœH˜HJšžœ˜—Jšœ&˜&J˜—Jšžœžœ"˜3—Jšžœžœžœ7˜JJšœ&˜&Jšœ˜—š œžœžœžœ˜Dšžœ ž˜Jšœ7˜7JšœQ˜QJšœo˜oJšœ„˜„Jšžœžœ˜0—J˜—JšœžœŸ˜/JšœMŸ˜aJšœ6žœ˜>Jšœ˜L™#Lšœžœžœ1žœ™YJšœ9Ÿ ˜YJšœ-˜-Jšœ˜šžœžœŸ˜5šžœžœŸ˜=Jšœ7žœ˜HJšœ˜Jšœ`˜`Jšœ$˜$Jšœ&˜&JšœL˜LJšœL˜LJšœL˜Lšžœ žœžœ˜JšœL˜L—Jšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ˜Jšœb˜bJšœ$˜$Jšœ&˜&JšœL˜LJšœL˜LJšœL˜Lšžœ žœžœ˜JšœL˜L—Jšžœ˜—Jšœ{˜{Jšœ˜JšœV˜VJšœ˜JšœT˜TJšžœ žœžœ<˜OJšœ Ÿ˜3Jšžœ˜Jšœ2™2—šžœ˜Jšžœ}Ÿ˜‚—J˜Jš’)™)—MšŸ*™*MšœI˜Išžœžœ˜ šžœŸ˜!Mšœ žœ$˜0Mšœl˜lM˜—Mšžœ˜—Mšœ˜MšœžœžœŸ-˜[Mšœžœžœ˜,Mšœžœžœ˜-—š   œžœžœžœžœžœ˜qMšœ7˜7Jšœžœžœžœ,˜CJšœžœžœžœ,˜CJšœ0žœ ˜<š  œžœ˜Jš œžœžœžœžœ˜%Jšœ žœžœžœ˜%Jš œžœžœžœžœ˜%š   œžœžœžœŸ˜LJš œžœžœ žœžœžœŸ˜Jšžœžœžœ"ž˜2JšœI˜IJšœk˜kJšžœ˜—Jšœ˜—š  œžœ žœžœ˜FJšŸ™Jšœžœ˜Jšœžœžœ˜"šžœžœžœž˜-Jšœžœ ˜šœ žœ ˜Jšžœžœžœ=˜\Jšœ ˜ Jšœ˜—šžœžœŸ(˜LJšœžœ"˜=Jšœžœ"˜=Jšœžœ"˜=Jšœžœ˜J˜—Jšžœ˜—šžœžœ˜Jšœžœžœ$˜TJšœžœžœ$˜TJšœžœžœ$˜TJ˜—šžœ˜Jšœ˜Jšœ˜Jšœ˜J˜—š žœžœžœžœŸ˜AJšœ$˜$Jšžœ˜—Jšœ˜—š  œžœžœ žœžœ žœžœ˜vJšœžœžœ˜š  œžœžœ˜šžœžœžœžœ˜Ošžœžœžœ ˜JJšœ žœ˜—Jšžœžœ˜—Jšœ$˜$J˜—Jšžœžœ#˜7šžœžœžœž˜#JšœJ˜JJšžœ˜—Jšžœ žœžœ(˜;šžœ ž˜šœ#˜#Jšœžœ ˜1šžœžœžœž˜"Jšœ žœž˜Jšžœ žœžœŸ"˜FJšœ˜šœžœ˜Jšžœ˜Jšžœ8˜<—šžœžœ ˜Jšžœžœžœžœ'˜EJš žœžœžœžœIŸ˜†—Jšžœ˜—J˜—šœ˜Jšœ@žœ ˜Ršžœžœžœž˜"Jšœ ž˜ Jšžœ žœžœŸ"˜FJšœ˜Jšžœžœžœ˜?Jšœ˜šžœžœ ˜Jšžœžœžœžœ'˜EJš žœžœžœžœIŸ˜†—Jšžœ˜—Jšœ&˜&J˜—Jšžœžœ"˜3—Jšžœ žœžœ6˜IJšœ&˜&Jšœ˜—JšœžœŸ˜/JšœŸœ˜4Jšœ6žœ˜?Jšœ;Ÿ ˜[Jšœ-˜-JšœŸ!˜>šžœ(žœ˜0JšœžœŸ˜;Jšœ žœŸ˜5Mšœ žœŸ.˜PMšœ žœ˜!Mšœ˜—šžœžœŸ˜5Jšœ žœ˜šžœžœŸ˜=Jšœ7žœ˜HJšœ˜Jšœ`˜`Jšœ9˜9šžœžœžœž˜,Jšœr˜rJšžœ˜—Jšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ˜Jšœb˜bJšœ9˜9šžœžœžœž˜,Jšœp˜pJšžœ˜—Jšžœ˜—Jšœ6Ÿ˜MJšœ˜Jšœ˜šžœžœžœž˜#Jšœ$˜$Jšœ$˜$Jšžœ˜—Jšœ Ÿ˜3Jšžœ˜Jšœ2™2—Jšžœžœ4Ÿ˜KJšœ˜Jš’*™*—MšŸ*™*MšœI˜Išžœžœ˜ šžœŸ˜!Mšœ žœ$˜0Mšœk˜kM˜—Mšžœ ˜—Mšœ˜——™N™(Nš œžœ2Ÿ˜\šœžœ(˜AJšœ-žœ˜5Jšœžœžœ˜Jšœžœžœ ˜.Jšœžœžœ˜-šœ<˜