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 [PixelBuffer, SampleSet, GetSampleSet, SampleSetSequence, TerminalFromBuffer, GetPixel, PutPixel, PixelOp, SumLessProd, ByteAvrgWgtd, XfmMapPlace, SubMap], ScanConvert USING [IntVec2, IntVec2Sequence, RealSequence, Spot, SpotSequence, IntRGB, IntRGBT, justNoticeable]; ScanConvertImpl: CEDAR PROGRAM 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 ~ ScanConvert.IntRGB; -- RECORD[ r, g, b: CARDINAL] IntRGBT: TYPE ~ ScanConvert.IntRGBT; -- RECORD[ r, g, b, t: CARDINAL] Vec2: TYPE ~ Vector2.VEC; -- RECORD[x, y: REAL] IntVec2: TYPE ~ ScanConvert.IntVec2; -- RECORD[x, y: INTEGER] IntVec2Sequence: TYPE ~ ScanConvert.IntVec2Sequence; 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; 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; lgBitsPerWord: NAT ~ Basics.logBitsPerWord; bitsPerWord: NAT ~ Basics.bitsPerWord; 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: IntVec2] 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 $PseudoClr => { 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: IntRGB] RETURNS[NAT] ~ { SELECT renderMode FROM $Dithered => { mapVal: NAT _ Real.FixC[clr.r*5.0/255.0]*24 + Real.FixC[clr.g*6.0/255.0]*4 + Real.FixC[clr.b*4.0/255.0]; IF mapVal >= 60 THEN mapVal _ mapVal + 135; -- move to top of map RETURN[ mapVal ]; }; $PseudoClr => { fctr: REAL _ 5.99 / 255.0; fctr2: REAL _ 6.99 / 255.0; RETURN[ Real.FixC[clr.r*fctr]*42 + Real.FixC[clr.g*fctr2]*6 + Real.FixC[clr.b*fctr] +2 ]; }; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; RETURN[ 255 ]; }; RGBFromMap: PUBLIC PROC[renderMode: ATOM, value: NAT] RETURNS[IntRGB] ~ { SELECT renderMode FROM $Dithered => { IF value >= 60 THEN value _ value - 135; -- move from top of map value _ MIN[119, value]; RETURN[ [ r: (value / 24) * 256 / 5, g: (value MOD 24) / 4 * 256 / 6, b: (value MOD 4) * 256 / 6 ] ]; }; $PseudoClr => { value _ MIN[253, value] - 2; RETURN[ [ r: (value / 42) * 256 / 6, g: (value MOD 42) / 6 * 256 / 7, b: (value MOD 6) * 256 / 6 ] ]; }; 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]; }; oldPixel: SampleSet _ Pixels.GetSampleSet[4]; -- own memory for pixel storage newPixel: SampleSet _ Pixels.GetSampleSet[4]; justNoticeable: REAL ~ ScanConvert.justNoticeable; PutSpot: PUBLIC PROC [ buf: PixelBuffer, spot: Spot, op, renderMode: ATOM ] ~ { PutPixel: PROC[] ~ { DoIt: PROC[] ~ { Pixels.PutPixel[ buf, spot.x, spot.y, newPixel ]; }; IF vt # NIL THEN { x, y: NAT; [x, y] _ Pixels.XfmMapPlace[buf.pixels[buf.samplesPerPixel-1], spot.x, spot.y ]; Terminal.ModifyColorFrame[vt, DoIt, x, y, x+1, y+1]; } ELSE DoIt[]; }; GetPixel: PROC[] RETURNS[pixel: SampleSet] ~ { DoIt: PROC[] ~ { pixel _ Pixels.GetPixel[ buf, spot.x, spot.y, oldPixel ]; }; IF vt # NIL THEN { x, y: NAT; [x, y] _ Pixels.XfmMapPlace[buf.pixels[buf.samplesPerPixel-1], spot.x, spot.y ]; Terminal.ModifyColorFrame[vt, DoIt, x, y, x+1, y+1]; } 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: ScanConvert.IntRGB; newClr: ScanConvert.IntRGBT; addOn: NAT _ 0; cvrge: NAT; 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, $PseudoClr => oldClr _ RGBFromMap[ renderMode, oldPixel[0] ]; $FullClr, $Dorado24 => oldClr _ [ oldPixel[0], oldPixel[1], oldPixel[2] ]; $Grey => oldClr _ [ oldPixel[0], oldPixel[0], oldPixel[0] ]; ENDCASE => SIGNAL ScanConvertError[$BadRenderMode]; newClr _ spot.proc[spot]; -- extract color from spot cvrge _ Real.RoundC[spot.coverage * 255.0]; IF newClr.t > 0 THEN cvrge _ cvrge * (255 - newClr.t) / 256; 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, INTEGER[cvrge] - oldPixel[alpha^] ] ELSE cvrge _ MIN[ cvrge, 255 - oldPixel[alpha^] ]; -- only what's uncovered newClr.r _ Basics.BITOR[ oldClr.r + cvrge * newClr.r / 256, 1 ]; newClr.g _ Basics.BITOR[ oldClr.g + cvrge * newClr.g / 256, 1 ]; newClr.b _ Basics.BITOR[ oldClr.b + cvrge * newClr.b / 256, 1 ]; cvrge _ Basics.BITOR[ cvrge + oldPixel[alpha^], 1 ]; -- store total coverage } ELSE { -- first surface written newClr.r _ cvrge * newClr.r / 256; newClr.g _ cvrge * newClr.g / 256; newClr.b _ cvrge * newClr.b / 256; } ELSE { -- Writing over cvrge _ Pixels.SumLessProd[ oldPixel[alpha^], cvrge ]; -- sum less overlap estimate newClr.r _ Pixels.ByteAvrgWgtd[ b1: oldPixel[1], b2: newClr.r, wgt: cvrge ]; -- blend newClr.g _ Pixels.ByteAvrgWgtd[ b1: oldPixel[2], b2: newClr.g, wgt: cvrge ]; newClr.b _ Pixels.ByteAvrgWgtd[ b1: oldPixel[3], b2: newClr.b, wgt: cvrge ]; }; } ELSE newClr _ spot.proc[spot]; -- No alpha buffer, just extract color from spot SELECT renderMode FROM $FullClr, $Dorado24 => { IF newPixel.maxLength < 3+addOn THEN newPixel _ Pixels.GetSampleSet[3+addOn]; newPixel[0] _ newClr.r; newPixel[1] _ newClr.g; newPixel[2] _ newClr.b; newPixel.length _ 3+addOn; }; $Grey => { IF newPixel.maxLength < 1+addOn THEN newPixel _ Pixels.GetSampleSet[1+addOn]; newPixel[0] _ (newClr.r + newClr.g + newClr.b) / 3; newPixel.length _ 1+addOn; }; $Dithered, $PseudoClr => { IF newPixel.maxLength < 1+addOn THEN newPixel _ Pixels.GetSampleSet[1+addOn]; newPixel[0] _ DitheredRGB[ renderMode, spot.x, spot.y, newClr.r, newClr.g, newClr.b ]; newPixel.length _ 1+addOn; }; ENDCASE => ScanConvertError[$BadRenderMode]; IF depth # NIL THEN newPixel[depth^] _ oldPixel[depth^]; -- no way to get it, currently IF alpha # NIL THEN { newPixel[alpha^] _ cvrge; PutPixel[]; } ELSE Pixels.PixelOp[ buf, [spot.x, spot.y, 1, 1], newPixel, op ]; }; GetSpot: PUBLIC PROC [ buf: PixelBuffer, spot: Spot, renderMode: ATOM] RETURNS[ Spot ] ~ { size: NAT _ 3; alpha: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Alpha] ]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[buf.props, $Depth] ]; oldPixel _ Pixels.GetPixel[buf, spot.x, spot.y, oldPixel]; IF alpha # NIL THEN spot.coverage _ oldPixel[alpha^] / 256.0; IF depth # NIL THEN { size _ 4; spot.val[3] _ oldPixel[depth^]; }; IF spot.val = NIL OR spot.val.maxLength < size THEN spot.val _ NEW[RealSequence[size]]; SELECT renderMode FROM $FullClr, $Dorado24 => { spot.val[0] _ oldPixel[0] / 256.0; spot.val[1] _ oldPixel[1] / 256.0; spot.val[2] _ oldPixel[2] / 256.0; }; $Grey => { spot.val[2] _ spot.val[1] _ spot.val[0] _ oldPixel[0] / 256.0; }; $Dithered, $PseudoClr => { clr: IntRGB _ RGBFromMap[renderMode, oldPixel[0] ]; spot.val[0] _ clr.r / 256.0; spot.val[1] _ clr.g / 256.0; spot.val[2] _ clr.b / 256.0; }; ENDCASE => ScanConvertError[$BadRenderMode]; spot.val.length _ size; RETURN[spot]; }; PutLine: PUBLIC PROC [ buf: PixelBuffer, p1, p2: IntVec2, 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 -- 16-bit pixel map THEN DrawLine[ buf.pixels[i], p1, p2, color[i] * 256 + color[i+1] ]; ENDLOOP; }; vt : Terminal.Virtual _ Pixels.TerminalFromBuffer[buf]; IF vt # NIL THEN { x1, y1, x2, y2: NAT; [x1, y1] _ Pixels.XfmMapPlace[buf.pixels[buf.samplesPerPixel-1], p1.x, p1.y ]; [x2, y2] _ Pixels.XfmMapPlace[buf.pixels[buf.samplesPerPixel-1], p2.x, p2.y ]; Terminal.ModifyColorFrame[vt, DoLine, MIN[x1, x2], MIN[y1, y2], MAX[x1, x2], MAX[y1, y2] ]; } ELSE DoLine[]; }; ConstantPoly: PUBLIC PROC [buf: PixelBuffer, color: SampleSet, plygn: REF IntVec2Sequence] ~ { 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], plygn[nxtLVtx] ]; 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], plygn[nxtRVtx] ]; 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 }; FOR i: CARDINAL IN [0..plygn.length) DO -- convert to pixel map coordinates [ plygn[i].x, plygn[i].y ] _ Pixels.XfmMapPlace[ buf.pixels[buf.pixels.length-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; IF vt # NIL THEN Terminal.ModifyColorFrame[vt, DoItConstant, leftmost, lowest, rightmost, highest ] 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, $PseudoClr => { 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; }; $FullClr, $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: IntRGBT] ~ { SELECT color.length FROM 1 => outClr _ [Real.RoundC[color[0]], 0, 0, 0 ]; 2 => outClr _ [Real.RoundC[color[0]], 0, 0, Real.RoundC[color[1]] ]; 3 => outClr _ [Real.RoundC[color[0]], Real.RoundC[color[1]], Real.RoundC[color[2]], 0]; 4 => outClr _ [Real.RoundC[color[0]], Real.RoundC[color[1]], Real.RoundC[color[2]], 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: IntRGBT; 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.t, plygn[lVtx].y], [nxtClr.t, 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.t, plygn[rVtx].y], [nxtClr.t, 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 ]; }; FOR i: CARDINAL IN [0..plygn.length) DO -- convert to pixel map coordinates [ plygn[i].x, plygn[i].y ] _ Pixels.XfmMapPlace[ buf.pixels[buf.pixels.length-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; IF vt # NIL THEN Terminal.ModifyColorFrame[vt, DoItSmooth, leftmost, lowest, rightmost, highest ] 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 { 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, $PseudoClr, $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; }; $FullClr, $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 ]; }; FOR i: CARDINAL IN [0..plygn.length) DO -- convert to pixel map coordinates [ plygn[i].x, plygn[i].y ] _ Pixels.XfmMapPlace[ buf.pixels[buf.pixels.length-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; IF vt # NIL THEN Terminal.ModifyColorFrame[vt, DoItShiny, leftmost, lowest, rightmost, highest ] ELSE DoItShiny[]; }; DrawLine: PROC [destination: Pixels.SubMap, p1, p2: IntVec2, -- fast line, constant color pxlValue: CARDINAL, function: Function _ [null, null]] ~ { increment, bias, error, sBump, t, shiftDist: INTEGER; wrdPtr: LONG POINTER TO WORD; p1s, p1f, p2s, p2f: INTEGER; 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]; [p1f, p1s] _ Pixels.XfmMapPlace[destination, p1.x, p1.y]; [p2f, p2s] _ Pixels.XfmMapPlace[destination, p2.x, p2.y]; IF destination.df = 2 THEN { p1f _ p1f / 2; p2f _ p2f / 2; }; 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. όScanConvertImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Frank Crow, March 28, 1986 11:09:09 am PST Scan conversion operations on pixel buffers. Type Definitions 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 Lock out cursor Scan Conversion for Convex Areas IncrementalDesc: TYPE ~ RECORD[val, intInc, intSgn, fracInc, fracRng, fracPos: INTEGER]; find least and most values Lock out cursor bytePtr: LONG POINTER TO rawBytes; IncrementalDesc: TYPE ~ RECORD[val, intInc, intSgn, fracInc, fracRng, fracPos: INTEGER]; Write top segment (polygon includes all its edges) find least and most y-values Lock out cursor Calculate color with hilite Write top segment (polygon includes all its edges) find least and most y-values Lock out cursor 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‘œžœžœ žœžœžœ˜Fšžœ ž˜šœ˜Jšœžœk˜vJšžœžœŸ˜CJšžœ ˜Jšœ˜—šœ˜Jšœžœžœ˜9JšžœS˜YJšœ˜—Jšžœžœ"˜4—Lšžœ˜Lšœ˜—š ‘ œžœžœ žœ žœžœ ˜Išžœ žœ˜šœ˜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™Mšœ0Ÿ˜OMšœ-˜-šœžœ˜2M˜—š‘œž œ1žœ˜Oš‘œžœ˜Jš‘œžœ=˜Gšžœžœ˜ šžœ˜Mšœžœ˜ MšœP˜PMšœ4˜4M˜—Mšžœ˜ —Jšœ˜—š‘œžœžœ˜.Jš‘œžœF˜Pšžœžœ˜ šžœ˜Mšœžœ˜ MšœP˜PMšœ4˜4M˜—Mšžœ˜ —Jšœ˜—Mšœ7˜7Jšœžœžœžœ,˜CJšœžœžœžœ,˜CJšœ˜Jšœ˜Mšœžœ˜Mšœžœ˜ Mšžœ žœžœ˜&Mšžœ žœžœ˜&M˜šžœ žœžœŸ˜.Mšžœ žœžœŸ&˜Tšžœžœžœ&˜TMšžœ"Ÿ˜=—Mš žœžœžœžœžœŸ˜nšžœ žœ Ÿ˜?MšœI˜IMšœL˜LJšœ@˜@Jšžœžœ"˜4—Mšœ#Ÿ˜=Mšœ+˜+Mšžœžœ(˜<šžœžœ˜,šžœžœ˜šžœŸ:˜Dšžœ˜Mšžœ žœžœ˜:Mšžœ žœ"Ÿ˜L—Mšœžœ*˜AMšœžœ*˜AMšœžœ*˜AMšœžœ#Ÿ˜NM˜—šžœŸ˜,Mšœ"˜"Mšœ"˜"Mšœ"˜"M˜——šžœŸ˜#Mšœ6Ÿ˜TMšœMŸ˜UMšœL˜LMšœL˜LM˜——M˜—JšžœŸ1˜Sšžœ ž˜šœ˜Jšžœžœ)˜MJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœ ˜ Jšžœžœ)˜MJšœ3˜3Jšœ˜J˜—šœ˜Jšžœžœ)˜MJšœc˜cJšœ˜Jšœ˜—Jšžœ&˜-—Mšžœ žœžœ&Ÿ˜Wšžœ žœ˜Mšžœ/˜3Jšžœ=˜A—Mšœ˜—š‘œž œ-žœžœ ˜[Mšœžœ˜Jšœžœžœžœ,˜CJšœžœžœžœ,˜CMšœ:˜:Mšžœ žœžœ*˜=Mšžœ žœžœ4˜GMš žœ žœžœžœ žœ˜Xšžœ ž˜šœ˜Jšœ"˜"Jšœ"˜"Jšœ"˜"Jšœ˜—šœ ˜ Jšœ>˜>J˜—šœ˜Jšœ3˜3Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšžœ&˜-—Mšœ˜Mšžœ˜ Mšœ˜——™+š‘œžœžœ<˜Pš‘œžœ˜šžœžœžœžŸ)˜Pšžœ˜Mšžœ,˜0šžœžœ1Ÿ˜KMšžœ@˜D——Mšžœ˜—M˜MšŸ™—Mšœ7˜7šžœžœ˜ šžœ˜Mšœžœ˜MšœN˜NMšœN˜NMš œ&žœ žœžœ žœ ˜iMšœ˜—Mšžœ ˜—Lšœ˜——™ š‘ œž œ8žœ˜iMšœ7˜7Jšœ0žœ ˜<š‘ œžœ˜Jšœžœ Ÿ˜'Jšœ6žœ˜@Jšœ Ÿ4˜TLšœžœžœ1žœ™XJ˜Jšœ8Ÿ ˜XJšœ-˜-Jšœ)Ÿ˜Cšžœžœ Ÿ˜=šžœžœŸ˜@Jšœ7žœ˜HJšœ˜Jšœ3˜3Jšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ˜Jšœ4˜4Jšžœ˜—Jšœ%Ÿ˜@Jšœ˜Jšœ˜Jšœ Ÿ˜3Jšžœ˜—Jšžœžœ'Ÿ˜VJ˜—š žœžœžœžœŸ#˜MJšœt˜tJšžœ˜Jš£™—Jšœ"˜"Jšœ#˜#šžœžœžœž˜'Jšœžœ˜Jšœžœ˜šžœ˜Jšžœ'˜+Jšžœžœžœ˜0—šžœ˜Jšžœ˜Jšžœžœžœ˜4—Jšžœ˜ MšŸ™—šžœžœ˜ MšžœS˜WMšžœ˜—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šœ1˜1JšœE˜EJšœ]˜]Jšœr˜rJšžœžœ˜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˜—š žœžœžœžœŸ#˜MJšœu˜uJšžœ˜Jš£™—Jšœ"˜"Jšœ#˜#šžœžœžœž˜'Jšœžœ˜Jšœžœ˜šžœ˜Jšžœ'˜+Jšžœžœžœ˜0—šžœ˜Jšžœ˜Jšžœžœžœ˜4—Jšžœ˜ MšŸ™—šžœžœ˜ MšžœQ˜UMšžœ˜—Mšœ˜MšœžœžœŸ-˜[Mšœžœžœ˜,Mšœžœžœ˜-—š ‘ œžœžœžœžœžœ˜qMšœ7˜7Jšœžœžœžœ,˜CJšœžœžœžœ,˜CJšœ0žœ ˜<š‘ œžœ˜Jš œžœžœžœžœ˜%Jšœ žœžœžœ˜%Jš œžœžœžœžœ˜%š‘ œžœžœžœ˜+Jš œžœžœ žœžœžœŸ˜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˜—š žœžœžœžœŸ#˜MJšœu˜uJšžœ˜Jš£™—Jšœ"˜"Jšœ#˜#šžœžœžœž˜'Jšœžœ˜Jšœžœ˜šžœ˜Jšžœ'˜+Jšžœžœžœ˜0—šžœ˜Jšžœ˜Jšžœžœžœ˜4—Jšžœ˜ —˜MšŸ™—šžœžœ˜ MšžœP˜TMšžœ ˜—Mšœ˜——™N™(Nš‘œžœ2Ÿ˜\šœžœ(˜AJšœ-žœ˜5Jšœžœžœ˜Jšœžœ˜šœ<˜