<> <> <> <> <<>> 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 ]; }; << Proc Body Starts Here!!!!>> <> [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 ]; }; << Proc Body Starts Here!!!!>> <> [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.