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], EdgeBlt USING [ EdgeBltError, EdgeBltTable, EdgeDesc ], ScanConvert USING [IntPair, IntPairSequence, RealSequence, Spot, SpotSequence, justNoticeable]; EdgeBltAppliedImpl: CEDAR MONITOR IMPORTS Atom, Basics, PrincOpsUtils, Terminal, Real, Pixels EXPORTS ScanConvert ~ BEGIN EdgeDesc: TYPE ~ EdgeBlt.EdgeDesc; EdgeBltTable: TYPE ~ EdgeBlt.EdgeBltTable; 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, xSgn, incrmnt, range, position: INTEGER, steep: BOOL ]; IncDescSeq: TYPE ~ RECORD [length: NAT _ 0, s: SEQUENCE maxLength: 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; ShiftL: PROC [val: INTEGER, log2Scale: NAT] RETURNS [INTEGER] -- shift left = INLINE { RETURN[INTEGER[Basics.BITSHIFT[ LOOPHOLE[val], log2Scale ]]] }; ShiftR: PROC [val: INTEGER, log2Scale: NAT] RETURNS [INTEGER] -- shift right = INLINE { RETURN[INTEGER[Basics.BITSHIFT[ LOOPHOLE[val], -INTEGER[log2Scale] ]]] }; SubPixel: PROC [position: INTEGER, log2Scale: NAT] RETURNS [INTEGER] ~ { RETURN[INTEGER[Basics.BITAND[ position, ShiftL[1, log2Scale] - 1 ]]]; }; 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, log2Scale: NAT] RETURNS[incr: IncrementalDesc] ~ { xDiff: INTEGER _ pEnd.x - pBeg.x; yDiff: INTEGER _ pEnd.y - pBeg.y; -- assumed positive IF yDiff <= 0 THEN yDiff _ LAST[NAT]; -- zero height edge, let increments => 0 IF yDiff > ABS[xDiff] THEN { -- steep edge (more vertical than horizontal) incr.incrmnt _ 2 * ABS[xDiff]; -- fractional increment incr.range _ 2 * yDiff; -- range to increment over incr.steep _ TRUE; } ELSE { -- shallow edge (more horizontal than vertical) incr.incrmnt _ 2 * yDiff; -- fractional increment incr.range _ 2 * ABS[xDiff]; -- range to increment over incr.steep _ FALSE; }; incr.xSgn _ SGN[xDiff]; -- get sign of horizontal increment IF log2Scale > 0 -- scaled for subpixel precision THEN { incr.val _ ShiftR[pBeg.x, log2Scale]; -- scale initial pixel value back to screen address IF NOT incr.steep THEN{ -- more horizontal line, fractional position inversely related to height partWay: INTEGER _ ShiftL[1, log2Scale-1] - SubPixel[pBeg.y, log2Scale]; incr.position _ - ShiftR[ partWay * incr.range, log2Scale ]; WHILE incr.position < 0 DO -- increment to end of scan segment incr.val _ incr.val + incr.xSgn; incr.position _ incr.position + incr.incrmnt; ENDLOOP; incr.position _ incr.position - incr.range; -- reset fractional part } ELSE { -- more vertical line, fractional position (difference from halfway) partWay: INTEGER _ SubPixel[pBeg.x, log2Scale] - ShiftL[1, log2Scale-1]; IF incr.xSgn < 0 THEN partWay _ - partWay; -- reverse for positive slope incr.position _ - ShiftR[ partWay * incr.range, log2Scale ] - incr.range/2; }; } ELSE { incr.val _ pBeg.x; -- initial value incr.position _ - incr.range/2; -- start at halfway position (midpixel) }; }; UpdateIncr: PROC[incr: IncrementalDesc] RETURNS [IncrementalDesc] ~ { WHILE TRUE DO IF NOT incr.steep THEN incr.val _ incr.val + incr.xSgn; -- always increment if shallow incr.position _ incr.position + incr.incrmnt; -- increment fractional position IF incr.position > 0 THEN { -- fractional position overflowed incr.position _ incr.position - incr.range; -- reset fractional part IF incr.steep THEN incr.val _ incr.val + incr.xSgn -- bump integer part ELSE EXIT; -- exit and render scan line if shallow }; IF incr.steep THEN EXIT; -- always exit if steep (one pixel per scanline) ENDLOOP; 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]; }; 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, log2Scale: NAT] 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, log2Scale ]; 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 log2Scale > 0 THEN { highest _ ShiftR[highest, log2Scale]; lowest _ ShiftR[lowest, log2Scale]; leftmost _ ShiftR[leftmost, log2Scale]; rightmost _ ShiftR[rightmost, log2Scale]; }; }; ConstantPoly: PUBLIC ENTRY PROC [buf: PixelBuffer, color: SampleSet, plygn: REF SpotSequence, log2Scale: NAT _ 0] ~ { 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 _ ShiftR[plygn[nxtLVtx].y, log2Scale]; left _ GetSlopeIncr[ [plygn[lVtx].x, plygn[lVtx].y], [plygn[nxtLVtx].x, plygn[nxtLVtx].y], log2Scale ]; ENDLOOP; WHILE yPosn >= nxtRHeight DO -- next right vertex reached? rVtx _ nxtRVtx; nxtRVtx _ (rVtx + 1) MOD plygn.length; nxtRHeight _ ShiftR[plygn[nxtRVtx].y, log2Scale]; right _ GetSlopeIncr[ [plygn[rVtx].x, plygn[rVtx].y], [plygn[nxtRVtx].x, plygn[nxtRVtx].y], log2Scale ]; 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, log2Scale]; 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, log2Scale: NAT _ 0] ~ { 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], 0 ]; grn _ GetSlopeIncr[ [lftG, left], [rgtG, right], 0 ]; blu _ GetSlopeIncr[ [lftB, left], [rgtB, right], 0 ]; IF depth # NIL THEN { z _ GetSlopeIncr[ [lftZ, left], [rgtZ, right], 0 ]; 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 lastLHeight: NAT _ nxtLHeight; lastRHeight: NAT _ nxtRHeight; WHILE yPosn >= nxtLHeight DO -- next left vertex reached? lVtx _ nxtLVtx; nxtLVtx _ (lVtx + plygn.length - 1) MOD plygn.length; nxtLHeight _ ShiftR[plygn[nxtLVtx].y, log2Scale]; left _ GetSlopeIncr[ [plygn[lVtx].x, plygn[lVtx].y], [plygn[nxtLVtx].x, plygn[nxtLVtx].y], log2Scale ]; clr _ GetColor[plygn[lVtx].val]; nxtClr _ GetColor[plygn[nxtLVtx].val]; lftR _ GetSlopeIncr[ [clr.r, lastLHeight], [nxtClr.r, nxtLHeight], 0 ]; lftG _ GetSlopeIncr[ [clr.g, lastLHeight], [nxtClr.g, nxtLHeight], 0 ]; lftB _ GetSlopeIncr[ [clr.b, lastLHeight], [nxtClr.b, nxtLHeight], 0 ]; IF depth # NIL THEN lftZ _ GetSlopeIncr[ [clr.z, lastLHeight], [nxtClr.z, nxtLHeight], 0]; ENDLOOP; WHILE yPosn >= nxtRHeight DO -- next right vertex reached? rVtx _ nxtRVtx; nxtRVtx _ (rVtx + 1) MOD plygn.length; nxtRHeight _ ShiftR[plygn[nxtRVtx].y, log2Scale]; right _ GetSlopeIncr[ [plygn[rVtx].x, plygn[rVtx].y], [plygn[nxtRVtx].x, plygn[nxtRVtx].y], log2Scale ]; clr _ GetColor[plygn[rVtx].val]; nxtClr _ GetColor[plygn[nxtRVtx].val]; rgtR _ GetSlopeIncr[ [clr.r, lastRHeight], [nxtClr.r, nxtRHeight], 0 ]; rgtG _ GetSlopeIncr[ [clr.g, lastRHeight], [nxtClr.g, nxtRHeight], 0 ]; rgtB _ GetSlopeIncr[ [clr.b, lastRHeight], [nxtClr.b, nxtRHeight], 0 ]; IF depth # NIL THEN rgtZ _ GetSlopeIncr[[clr.z, lastRHeight], [nxtClr.z, nxtRHeight], 0]; 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, log2Scale]; 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, log2Scale: NAT _ 0] ~ { 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; sqr1: INTEGER _ Sqr[ segSeq[j].val ]; sqr2: INTEGER _ Sqr[ segSeq[j+1].val ]; pctHilite: CARDINAL _ Power[ Basics.BITSHIFT[65535 - (sqr1 + sqr2), -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], 0 ]; 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.maxLength < 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]]; }; lftSeq.length _ rgtSeq.length _ segSeq.length _ plygn[lVtx].val.length; WHILE yPosn < highest DO -- work up through vertices this, next: REF RealSequence; lastLHeight: NAT _ nxtLHeight; lastRHeight: NAT _ nxtRHeight; WHILE yPosn >= nxtLHeight DO -- next left vertex reached? lVtx _ nxtLVtx; nxtLVtx _ (lVtx + plygn.length - 1) MOD plygn.length; nxtLHeight _ ShiftR[plygn[nxtLVtx].y, log2Scale]; left _ GetSlopeIncr[ [plygn[lVtx].x, plygn[lVtx].y], [plygn[nxtLVtx].x, plygn[nxtLVtx].y], log2Scale ]; 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]], lastLHeight], [Real.FixI[next[j]], nxtLHeight], 0 ]; ENDLOOP; ENDLOOP; WHILE yPosn >= nxtRHeight DO -- next right vertex reached? rVtx _ nxtRVtx; nxtRVtx _ (rVtx + 1) MOD plygn.length; nxtRHeight _ ShiftR[plygn[nxtRVtx].y, log2Scale]; right _ GetSlopeIncr[ [plygn[rVtx].x, plygn[rVtx].y], [plygn[nxtRVtx].x, plygn[nxtRVtx].y], log2Scale ]; 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]], lastRHeight], [Real.FixI[next[j]], nxtRHeight], 0 ]; 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, log2Scale]; 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; }; }; CheckLimits: PROC[] ~ { }; ImageTransform: PROC[dst, src: Pixels.SubMap, dstPos: IntPair, theta, scale: REAL] ~ { i: NAT _ 0; -- loop index dstEdge, srcEdge, dstStart, dstBias, srcStart: EdgeDesc; srcBytesPerLine, dstBytesPerLine: CARDINAL; ebt: EdgeBltTable; scaledWidth: NAT _ Real.RoundC[scale * src.subMap.size.f]; scaledHeight: NAT _ Real.RoundC[scale * src.subMap.size.s]; srcAddr: INT _ LOOPHOLE[src.subMap.sampleMap.base.word, INT]*2 + src.subMap.sampleMap.base.bit/8; dstAddr: INT _ LOOPHOLE[dst.subMap.sampleMap.base.word, INT]*2 + dst.subMap.sampleMap.base.bit/8; dstEdgeLength: CARDINAL _ Real.RoundC[RealFns.CosDeg[theta] * scaledWidth]; dstEdgeHiccups: CARDINAL _ Real.RoundC[RealFns.SinDeg[theta] * scaledWidth]; CheckLimits[]; -- ensure transformed image fits in target buffer, 0 1.0 THEN -srcBytesPerLine ELSE srcBytesPerLine, -- hiccup step bias: src.subMap.size.s -- supply bias since not using EdgeBlt ]; dstEdge _ [ -- walks across destinatation image along slanted edge val: dstStart.val, length: dstEdgeLength, -- horizontal dimension of edge hiccups: dstEdgeHiccups, -- vertical dimension of edge lngthIncr: dst.df, -- horizontal step to next pixel hicIncr: -dst.subMap.sampleMap.bitsPerLine / 8, -- vertical step to next pixel bias: dstBias.val, -- bias computed by dstBias indirect: TRUE -- incrementing addresses ]; srcEdge _ [ -- walks along scanline of source image, skipping or copying pixels as needed val: srcStart.val, length: src.subMap.size.f, -- width of source image hiccups: ABS[INTEGER[src.subMap.size.f] - scaledWidth], -- pixels to drop/add to scale down/up lngthIncr: src.df, -- horizontal step to next pixel hicIncr: IF scale > 1.0 THEN -src.df ELSE src.df, -- hiccup step to next pixel bias: 0, -- will be supplied by EdgeBlt indirect: TRUE -- incrementing addresses ]; ebt _ [dstEdge, srcEdge, [TRUE, FALSE] ]; WHILE i < dstStart.length DO EdgeBlt[ebt]; IF dstBias.val <= - dstEdgeLength -- equals 0 after EdgeBlt adds initial bias THEN { -- should be integrated better dstBias.val _ dstBias.val + 2 * dstEdgeLength; -- reset bias dstStart.val _ dstStart.val - dstStart.lngthIncr; -- move start position up one line } ELSE i _ i+1; ebt.dst.bias _ dstBias.val; -- set up bias for distance to first hiccup dstBias _ EdgeIncr[dstBias]; dstStart _ EdgeIncr[dstStart]; ebt.dst.val _ dstStart.val; srcStart _ EdgeIncr[srcStart]; ebt.src.val _ srcStart.val; ENDLOOP; }; END. *EdgeBltAppliedImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Frank Crow, March 16, 1987 12:59:41 pm PST Scan conversion operations on pixel buffers. Expects input to be properly clipped. Type Definitions Global Constants Utility Procedures Assumes 8-bit pixels IncrementalDesc: TYPE ~ RECORD [ val, xSgn, incrmnt, range, position: INTEGER, steep: BOOL ]; Get difference from half-pixel position Extended Bresenham incrementation Ordered dither for crude looks at full-color Ordered dither for crude looks at full-color using Imager's color map 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, xSgn, incrmnt, range, position: INTEGER, steep: BOOL ]; 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) Image Manipulation Κ&w˜Ihead2šœ™šœ Οmœ1™Mšžœ ˜—š  œžœžœžœžœ Ÿ,˜[Ošœžœžœ˜Ošœžœžœ˜šžœžœž˜Ošœ ˜ Ošœ ˜ Ošžœ˜—O˜O˜—š  œž œ žœ žœžœ žœ˜NLšœ žœ Ÿ2˜NLšœžœ˜Lšœžœ˜&šžœž˜Lšžœžœžœ˜˜>Lšœžœ˜9Lšžœžœžœ'˜Všžœ˜šžœ˜Lšžœ?Ÿ˜TLšžœŸ+˜B—LšžœΟ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šœ˜—š  œžœ!žœžœ˜\Mš œžœžœ(žœžœ™jJšœžœ˜%JšœžœŸ˜8Jš žœ žœ žœžœŸ(˜Nšžœ žœ˜šžœŸ-˜9JšœžœŸ˜>JšœŸ˜9Jšœ žœ˜Jšœ˜—šžœŸ/˜;JšœŸ˜9JšœžœŸ˜>Jšœ žœ˜J˜——Jšœ žœ Ÿ$˜?šžœŸ ˜3šžœ˜ Jšœ%Ÿ4˜Yšžœžœ ˜šžœŸH˜OJ™'Jšœ žœ8˜HJšœ<˜<šžœžœŸ#˜AJšœ$˜$Jšœ-˜-Jšžœ˜—Jšœ2Ÿ˜LJšœ˜—šžœŸD˜LJšœ žœ8˜HJšžœžœŸ˜IJšœK˜KJ˜——J˜—šžœ˜JšœŸ˜%Jšœ!Ÿ'˜HJ˜——Jšœ ˜—š  œžœžœ˜EJ™!šžœžœž˜ Jšžœžœ žœ$Ÿ˜XJšœ5Ÿ ˜Ušžœžœ Ÿ!˜HJšœ2Ÿ˜Lšžœ ˜Jšžœ&Ÿ˜>JšžœžœŸ'˜8—Jšœ˜—Jšžœ žœžœŸ0˜MJšžœ˜—Mšžœ ˜Mšœ˜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—Mšžœ˜Mšœ˜—š  œžœžœ žœ žœžœžœ˜Fšžœ žœ˜šœ˜Jšžœ žœŸ˜BJšœžœ ˜šžœ˜ Jšœ˜Jšœ žœ˜Jšœ žœ ˜Jšœ˜—Jšœ˜—šœ˜Jšœžœ˜šžœ˜ Jšœ˜Jšœ žœ˜Jšœ žœ ˜Jšœ˜—Jšœ˜—Jšžœžœ"˜4—Mšžœ˜Mšœ˜—š  œž œžœžœžœ˜IJšžœ˜Jšœ˜J˜—š œžœžœ žœžœ žœžœžœž œ˜„šžœ˜ Jšœžœ&˜GJšœ žœžœ'˜IJšœ˜—Jšœ˜J˜—š  œž œ žœžœžœžœ˜?Jš žœ žœžœžœžœ˜0Jšœ˜——™+š œžœžœ<˜Pš œžœ˜šžœžœžœžŸ)˜Pšžœ˜Lšžœ,˜0šžœžœ1Ÿ˜NLšŸ;™;Lšžœ@˜D——Lšžœ˜—L˜—Lšœ7˜7LšœQŸ ˜\LšœP˜Pšžœžœ˜ šžœŸ˜&Lšœ žœ"˜.Lš œ'žœžœ&žœžœ˜ŽLšœ˜—Lšžœ ˜—Mšœ˜——™ š   œžœžœžœžœ2žœ˜“J™Yšœ ˜ LšŸ œ™"—šžœžœžœž˜'JšœRŸ œ2˜Jšžœ˜—Jšœ"˜"Jšœ#˜#šžœžœžœž˜'Jšœžœ˜Jšœžœ˜šžœ˜Jšžœ'˜+Jšžœžœžœ˜/—šžœ˜Jšžœ˜Jšžœžœžœ˜3—Jšžœ˜ —šžœžœ˜Lšœ(˜(Lšœ'˜'Lšœ*˜*Lšœ*˜*Lšœ˜—Lšœ˜—š   œžœžœžœ;žœžœ ˜ƒLšœOžœž™YJšžœžœžœ˜Lšœ7˜7Jšœ0žœ ˜<š  œžœ˜Jšœžœ Ÿ˜'Jšœ6žœ˜@Jšœ Ÿ4˜TMš œžœžœ(žœžœ™iMšœ8Ÿ ˜XJšœ-˜-Jšœ)Ÿ˜Cšžœžœ Ÿ˜=šžœžœŸ˜@Jšœ7žœ˜HJšœ1˜1Jšœo˜oJšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ1˜1Jšœp˜pJšžœ˜—Jšœ%Ÿ˜@Jšœ˜Jšœ˜Jšœ Ÿ˜3Jšžœ˜—Jšžœžœ'Ÿ˜VJ˜—LšŸ*™*LšœT˜Tšžœžœ˜ šžœŸ˜!Lšœ žœ$˜0Lšœn˜nL˜—Lšžœ˜—Jšœ˜—š   œž œžœ$žœ žœ ˜vLšœ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šœ5˜5Jšœ5˜5Jšœ5˜5šžœ žœžœ˜Jšœ4˜4Jšœ'˜'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šœ˜M™#Mšœžœžœ1žœ™YJšœ9Ÿ ˜YJšœ-˜-Jšœ˜šžœžœŸ˜5Jšœ žœžœ˜@šžœžœŸ˜=Jšœ7žœ˜HJšœ1˜1šœ˜JšœR˜R—Jšœ$˜$Jšœ&˜&JšœG˜GJšœG˜GJšœG˜Gšžœ žœžœ˜JšœF˜F—Jšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ1˜1šœ˜JšœR˜R—Jšœ$˜$Jšœ&˜&JšœG˜GJšœG˜GJšœG˜Gšžœ žœžœ˜JšœE˜E—Jšžœ˜—Jšœ{˜{Jšœ˜JšœV˜VJšœ˜JšœT˜TJšžœ žœžœ<˜OJšœ Ÿ˜3Jšžœ˜Jšœ2™2—šžœ˜Jšžœ}Ÿ˜‚—J˜Jš’)™)—LšŸ*™*LšœT˜Tšžœžœ˜ šžœŸ˜!Lšœ žœ$˜0Lšœl˜lL˜—Lšžœ˜—Lšœ˜LšœžœžœŸ-˜[Lšœžœžœ˜,Lšœžœžœ˜-—š  œžœžœžœžœžœ žœ ˜†Lšœ7˜7Jšœžœžœžœ,˜CJšœžœžœžœ,˜CJšœ0žœ ˜<š  œžœ˜Jš œžœžœžœžœ˜%Jšœ žœžœžœ˜%Jš œžœžœžœžœ˜%š   œžœžœžœŸ˜LJš œžœžœ žœžœžœŸ˜Jšžœžœžœ"ž˜2JšœI˜IJšœk˜kJšžœ˜—Jšœ˜—š  œžœ žœžœ˜FJšŸ™Jšœžœ˜Jšœžœžœ˜"šžœžœžœž˜-Jšœžœ ˜Jšœžœ˜%Jšœžœ˜'Jšœ žœžœ*˜VšžœžœŸ(˜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šœM˜MJšžœ˜—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šœŸ!˜>šžœ+žœ˜3JšœžœŸ˜;Jšœ žœŸ˜5Lšœ žœŸ.˜PLšœ žœ˜!Lšœ˜—LšœH˜HšžœžœŸ˜5Jšœ žœ˜Jšœ žœžœ˜@šžœžœŸ˜=Jšœ7žœ˜HJšœ1˜1šœ˜JšœR˜R—Jšœ9˜9šžœžœžœž˜,Jšœm˜mJšžœ˜—Jšžœ˜—šžœžœŸ˜?Jšœ(žœ˜9Jšœ1˜1šœ˜JšœR˜R—Jšœ9˜9šžœžœžœž˜,Jšœk˜kJšžœ˜—Jšžœ˜—Jšœ6Ÿ˜MJšœ˜Jšœ˜šžœžœžœž˜#Jšœ$˜$Jšœ$˜$Jšžœ˜—Jšœ Ÿ˜3Jšžœ˜Jšœ2™2—Jšžœžœ4Ÿ˜KJšœ˜Jš’*™*—LšŸ*™*LšœT˜Tšžœžœ˜ šžœŸ˜!Lšœ žœ$˜0Lšœk˜kL˜—Lšžœ ˜—Lšœ˜——™N™(Nš œžœ2Ÿ˜\šœžœ(˜AJšœ-žœ˜5Jšœžœžœ˜Jšœžœžœ ˜.Jšœžœžœ˜-šœ<˜LšœŸ ˜=Lšœ1Ÿ˜OLšœŸ˜:Lšœ žœ Ÿ˜3Lšœ˜—šœ ŸM˜YLšœ˜Lšœ$Ÿ˜Lšœ4Ÿ&˜ZLšœ˜—Lšžœ ˜—Lšœ"Ÿ+˜MLšœ˜Lšœ=˜=Lšœ=˜=Lšžœ˜—L˜——Jšžœ˜—…—Z½ϋ