EdgeBltAppliedImpl.mesa
Copyright © 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.
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
Type Definitions
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;
Global Constants
lgBitsPerWord: NAT ~ Basics.logBitsPerWord;
bitsPerWord: NAT ~ Basics.bitsPerWord;
Utility Procedures
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 {
Assumes 8-bit pixels
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] ~ {
IncrementalDesc: TYPE ~ RECORD [ val, xSgn, incrmnt, range, position: INTEGER,
           steep: BOOL ];
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
Get difference from half-pixel position
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] ~ {
Extended Bresenham incrementation
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 => {
Ordered dither for crude looks at full-color
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 => {
Ordered dither for crude looks at full-color using Imager's color map
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];
};
Scan Conversion for Lines and Scan Segments
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
rg interleaved, do both at once by packing up Red and Green
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[];
};
Scan Conversion for Convex Areas
SetUpPoly: PROC [buf: PixelBuffer, plygn: REF SpotSequence, log2Scale: NAT]
   RETURNS [firstVtx, highest, lowest, leftmost, rightmost: NAT] ~ {
Find bounding box and first vertex in scan order for polygon; prepare for scan conversion
firstVtx ← 0;
Convert to pixel map coordinates
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] ~ {
Scan convert constant shaded polygon, uses some global bitBlt state so must be ENTRY PROC
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
IncrementalDesc: TYPE ~ RECORD [ val, xSgn, incrmnt, range, position: INTEGER,
           steep: BOOL ];
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
};
Transform to framebuffer space, get limits
[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 NATNARROW[ Atom.GetPropFromList[buf.props, $Alpha] ];
depth: REF NATNARROW[ 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: NATIF 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: BOOLEANTRUE;
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;
bytePtr: LONG POINTER TO rawBytes;
IncrementalDesc: TYPE ~ RECORD[val, intInc, intSgn, fracInc, fracRng, fracPos: INTEGER];
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;
Write top segment (polygon includes all its edges)
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!!!!
Transform to framebuffer space, get limits
[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 NATNARROW[ Atom.GetPropFromList[buf.props, $Alpha] ];
depth: REF NATNARROW[ 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: NATIF 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] ~ {
Calculate color with hilite
red, grn, blu: CARDINAL ← 0;
noticeableHilite: BOOLEANFALSE;
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: BOOLEANTRUE;
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;
Write top segment (polygon includes all its edges)
IF yPosn > lowest THEN PutShinySeg[ left.val, lftSeq, right.val, rgtSeq ];
};
                 Proc Body Starts Here!!!!
Transform to framebuffer space, get limits
[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[];
};
Scan Conversion
Teach this one how to use dithering etc.
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;
Get necessary constants based on bits per pixel
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];
Make sure of positive-going fast coordinate
IF p1f > p2f THEN { t ← p1f; p1f ← p2f; p2f ← t; t ← p1s; p1s ← p2s; p2s ← t; }; 
Get pointer to initial word and bit offset
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]
More horizontal line (moves faster along fast axis)
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;  
}
More vertical line (moves faster along slow axis)
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;
};
};
Image Manipulation
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: INTLOOPHOLE[src.subMap.sampleMap.base.word, INT]*2
      + src.subMap.sampleMap.base.bit/8;
dstAddr: INTLOOPHOLE[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<theta<pi/4, .5<scale<1.5
dstBytesPerLine ← dst.subMap.sampleMap.bitsPerLine / 8;
dstAddr ← dstAddr + dst.subMap.start.s * dstBytesPerLine + dst.subMap.start.f * dst.df;
dstAddr ← dstAddr + dstPos.y * dstBytesPerLine + dstPos.x * dst.df;
dstStart ← [   -- generates addresses walking down left edge of destination image
val: dstAddr,            -- start position for destination image
length: Real.RoundC[RealFns.CosDeg[theta] * scaledHeight], -- left edge height, dst image
hiccups: Real.RoundC[RealFns.SinDeg[theta] * scaledHeight], -- hiccups in left edge
lngthIncr: dst.subMap.sampleMap.bitsPerLine / 8, -- vertical step to next scanline
hicIncr: dst.df,           -- horizontal step to next pixel
bias: Real.RoundC[RealFns.CosDeg[theta] * scaledHeight] -- supply bias in lieu of EdgeBlt
];
dstBias ← [ -- generates bias to correctly place first hiccup in image scanline-to-edge blt
val: 0,             -- initial bias is zero
length: dstStart.length,         -- height of left edge of dst image
hiccups: dstStart.hiccups,        -- hiccups in left edge
lngthIncr: 0,            -- no change until hiccup
hicIncr: -2 * dstEdgeHiccups,       -- increment to bias
bias: dstStart.bias - 2 * dstStart.hiccups    -- same as dstStart, off by one
];
srcBytesPerLine ← src.subMap.sampleMap.bitsPerLine / 8;
srcStart ← [ -- walks down source image scanlines, skipping or replicating lines as needed
val: srcAddr + src.subMap.start.s * INT[srcBytesPerLine] + src.subMap.start.f * src.df,
length: src.subMap.size.s,     -- height of source image
hiccups: ABS[src.subMap.size.s - INTEGER[scaledHeight]], -- pixels to drop/add to scale down/up
lngthIncr: srcBytesPerLine,       -- vertical step to next scanline
hicIncr: IF scale > 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.