TilerPackageImpl.mesa
Last Edited by: Crow, February 29, 1984 12:55:57 pm PST
DIRECTORY
Basics     USING [BITSHIFT],
Real     USING [Fix, FixI, FixC, RoundC, RoundI, Float],
RealFns    USING [Log, SqRt],
PolygonPackage  USING [Polygon, VertexInfo],
Imager    USING [Context],
ImagerDisplay  USING [DisplayData],
ImagerPixelMaps  USING [GetPixel, PixelMap, Create, Transfer],
ImagerPixelMapsExtras USING [SetPixel],
TilerPackage;
TilerPackageImpl: CEDAR PROGRAM
IMPORTS Basics, RealFns, Real, ImagerPixelMaps, ImagerPixelMapsExtras
EXPORTS TilerPackage
= BEGIN OPEN TilerPackage;
minSize: REAL ~ .004;
tblLngth: NAT ~ 256;
weightsCalculated: BOOLEANFALSE;
weight: ARRAY [0..tblLngth] OF REAL;
blendPixels: BOOLEANFALSE;
Display and Texture bits
cachedContext: Imager.Context;
displayData, textureData: ImagerDisplay.DisplayData ← NIL;
Mip Map table storage and values
mipTexture: BOOLEANFALSE;
mipTextureRange, sumTextureRange, interpThreshold: REAL;
mipTable: ARRAY [0..10) OF ImagerPixelMaps.PixelMap;
maxScale: REAL; -- largest meaningful ratio of texture to image
maxTable: NAT; -- NAT version of the above
Summed-Area Texture storage
IntSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF INT];
ScanSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF REF IntSequence];
txtrSum: REF ScanSequence ← NIL;
maxTxtrX, maxTxtrY: NAT;
Data Structure for trapezoid edges
EdgeBlock: TYPE = RECORD [
moreVertical: BOOLEAN, start, end: REAL,
x, y, xn, yn, zn, r, g, b, t, txtrX, txtrY: REAL,
xIncr, yIncr, xnIncr, ynIncr, znIncr, rIncr, gIncr, bIncr, tIncr, txtrXIncr, txtrYIncr: REAL
];
Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; };
Sgn: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE {
IF number < 0. THEN RETURN[-1.] ELSE RETURN[1.];
};
Ceiling: PROC[ in: REAL ] RETURNS[ out: REAL ] ~ {
out ← Real.Float[Real.RoundI[in]];
IF out < in THEN out ← out + 1.;
};
Floor: PROC[ in: REAL ] RETURNS[ out: REAL ] ~ {
out ← Real.Float[Real.RoundI[in]];
IF out > in THEN out ← out - 1.;
};
CalculateWeights: PROC[] ~ {
Calculates the integral over the left half of a pyramid function, equal to the left half of a parabolic window or B-spline basis function
FOR i: NAT IN [0..tblLngth/2] DO
t: REAL ← i * 1.0 / (tblLngth/2);
weight[i]     ← Sqr[t] / 2.;
weight[i + tblLngth/2] ← 1. - Sqr[1. - t] / 2.;
ENDLOOP;
weightsCalculated ← TRUE;
};
FancyTiler: PUBLIC PROC[context: Imager.Context, poly: REF PolygonPackage.Polygon] ~ {
least, top, bottom: REAL;
vtxCount, lVtx, rVtx, nxtlVtx, nxtrVtx, nVtcesMinusOne: NAT;
leftVtxNeeded, rightVtxNeeded: BOOLEAN;
rEdge, lEdge: REF EdgeBlock;
vtx: REF PolygonPackage.VertexInfo ← NEW[PolygonPackage.VertexInfo];
nxtVtx: REF PolygonPackage.VertexInfo ← NEW[PolygonPackage.VertexInfo];
IF context # cachedContext THEN {        -- cache context as global
displayData ← NARROW[context.data, ImagerDisplay.DisplayData];
cachedContext ← context;
};
IF NOT weightsCalculated THEN CalculateWeights[];
least ← poly.vtx[0].coord.y; nxtlVtx ← 0;
FOR i: CARDINAL IN [1..poly.nVtces) DO -- find bottom vertex
IF poly.vtx[i].coord.y < least
THEN { least ← poly.vtx[i].coord.y; nxtlVtx ← i; };
ENDLOOP;
nxtrVtx ← nxtlVtx; -- set pointers to bottom vertex
leftVtxNeeded ← rightVtxNeeded ← TRUE;
nVtcesMinusOne ← poly.nVtces - 1;
vtxCount ← 1;
WHILE vtxCount < poly.nVtces DO -- Do until all vertices reached
IF leftVtxNeeded THEN { -- work around left side
lVtx ← nxtlVtx; nxtlVtx ← (nxtlVtx + nVtcesMinusOne) MOD poly.nVtces;
vtx^ ← poly.vtx[lVtx];  nxtVtx^ ← poly.vtx[nxtlVtx];
lEdge ← MakeEdge[vtx, nxtVtx];
leftVtxNeeded ← FALSE;
};
IF rightVtxNeeded THEN { -- work around right side
rVtx ← nxtrVtx; nxtrVtx ← (nxtrVtx + 1) MOD poly.nVtces;
vtx^ ← poly.vtx[rVtx];  nxtVtx^ ← poly.vtx[nxtrVtx];
rEdge ← MakeEdge[vtx, nxtVtx];
rightVtxNeeded ← FALSE;
};
get trapezoid given by next higher vertex
IF poly.vtx[nxtlVtx].coord.y < poly.vtx[nxtrVtx].coord.y THEN {
top ← poly.vtx[nxtlVtx].coord.y; -- next left vertex reached
leftVtxNeeded ← TRUE; vtxCount ← vtxCount + 1;
}
ELSE {
top ← poly.vtx[nxtrVtx].coord.y; -- next right vertex reached
rightVtxNeeded ← TRUE; vtxCount ← vtxCount + 1;
};
bottom ← MAX[poly.vtx[lVtx].coord.y, poly.vtx[rVtx].coord.y];
ShowFancyTrap[ bottom, top, lEdge, rEdge];
ENDLOOP;
};
MakeEdge: PROC[vtx1, vtx2: REF PolygonPackage.VertexInfo] RETURNS [REF EdgeBlock] = {
length: REAL;
edge: REF EdgeBlock ← NEW [ EdgeBlock ];
IF ABS[vtx2.coord.y - vtx1.coord.y] >= ABS[vtx2.coord.x - vtx1.coord.x]
THEN {
length ← vtx2.coord.y - vtx1.coord.y;
edge.start ← MIN[vtx1.coord.y, vtx2.coord.y];
edge.end ← MAX[vtx1.coord.y, vtx2.coord.y];
edge.moreVertical ← TRUE;
}
ELSE {
length ← vtx2.coord.x - vtx1.coord.x;
edge.start ← MIN[vtx1.coord.x, vtx2.coord.x];
edge.end ← MAX[vtx1.coord.x, vtx2.coord.x];
edge.moreVertical ← FALSE;
};
IF ABS[length] < minSize THEN length ← 1.;        -- prevent divide errors
{ OPEN edge;   -- get scanline increment, set edge values on scanline
x  ← vtx1.coord.x;   xIncr  ← (vtx2.coord.x - vtx1.coord.x)   / length;
y  ← vtx1.coord.y;   yIncr  ← (vtx2.coord.y - vtx1.coord.y)   / length;
xn  ← vtx1.shade.xn;   xnIncr ← (vtx2.shade.xn - vtx1.shade.xn)  / length;
yn  ← vtx1.shade.yn;   ynIncr ← (vtx2.shade.yn - vtx1.shade.yn)  / length;
zn  ← vtx1.shade.zn;   znIncr ← (vtx2.shade.zn - vtx1.shade.zn)  / length;
r  ← vtx1.shade.r;   rIncr  ← (vtx2.shade.r - vtx1.shade.r)   / length;
g  ← vtx1.shade.g;   gIncr  ← (vtx2.shade.g - vtx1.shade.g)  / length;
b  ← vtx1.shade.b;   bIncr  ← (vtx2.shade.b - vtx1.shade.b)  / length;
t  ← vtx1.shade.t;   tIncr  ← (vtx2.shade.t - vtx1.shade.t)   / length;
txtrX ← vtx1.shade.txtrX;  txtrXIncr ← (vtx2.shade.txtrX - vtx1.shade.txtrX) / length;
txtrY ← vtx1.shade.txtrY;  txtrYIncr ← (vtx2.shade.txtrY - vtx1.shade.txtrY) / length;
};
RETURN[ edge ];
};
EvalEdgeAt: PROC[ edge: REF EdgeBlock, position: REAL, cvrgeNeeded: BOOLEANFALSE]
     RETURNS[ vtx: REF PolygonPackage.VertexInfo] ~ {
pos, dist: REAL;
vtx ← NEW[PolygonPackage.VertexInfo];
IF position > edge.end THEN pos ← edge.end    -- keep values between vertex values
ELSE IF position < edge.start THEN pos ← edge.start
ELSE pos ← position;
dist ← IF edge.moreVertical THEN pos - edge.y ELSE pos - edge.x;
vtx.coord.x ← edge.x + edge.xIncr * dist;
vtx.coord.y ← edge.y + edge.yIncr * dist;
vtx.shade.xn ← edge.xn + edge.xnIncr * dist;
vtx.shade.yn ← edge.yn + edge.ynIncr * dist;
vtx.shade.zn ← edge.zn + edge.znIncr * dist;
vtx.shade.r ← edge.r + edge.rIncr * dist;
vtx.shade.g ← edge.g + edge.gIncr * dist;
vtx.shade.b ← edge.b + edge.bIncr * dist;
vtx.shade.t ← edge.t + edge.tIncr * dist;
vtx.shade.txtrX ← edge.txtrX + edge.txtrXIncr * dist;
vtx.shade.txtrY ← edge.txtrY + edge.txtrYIncr * dist;
IF cvrgeNeeded THEN {
Get Pixel area coverage weighted by function stored in "weight"
rCoverage, rUnCoverage: REAL;
lCoverage: REAL ← position - edge.start;
IF lCoverage >= 1. THEN lCoverage ← 2.0   -- fully covered
ELSE IF lCoverage > -1.0 THEN lCoverage ← 1.0 + lCoverage  -- partially covered
ELSE lCoverage ← 0.;
lCoverage ← weight[Real.FixI[ tblLngth/2 * lCoverage ]];
rCoverage ← edge.end - position;
IF rCoverage >= 1. THEN rCoverage ← 2.0   -- fully covered
ELSE IF rCoverage > -1.0 THEN rCoverage ← 1.0 + rCoverage -- partially covered
ELSE rCoverage ← 0.;
rUnCoverage ← weight[Real.FixI[ tblLngth/2 * (2.0 - rCoverage) ]]; -- weight uncovered part
Modify transmittance to force blending with other scan segments
vtx.shade.t ← 1.0 - (1.0 - vtx.shade.t) * (lCoverage - rUnCoverage); -- l - r is total coverage
};
};
ShowFancyTrap: PROC[ bottom, top: REAL, lEdge, rEdge: REF EdgeBlock] ~ {
GetXcoordAt: PROC[edge: REF EdgeBlock, yPos: REAL] RETURNS [REAL] ~ {
dist: REAL ← yPos - edge.y;
RETURN [ edge.x + dist / edge.yIncr ];
};
sideways: BOOLEANTRUE;
tEdge, bEdge: REF EdgeBlock;
leftTopVtx, leftBotVtx, rightTopVtx, rightBotVtx: REF PolygonPackage.VertexInfo;
IF bottom + minSize >= top THEN RETURN[]; -- too thin to affect image
IF NOT (lEdge.moreVertical AND rEdge.moreVertical) THEN
IF lEdge.moreVertical THEN {
leftTopVtx ← EvalEdgeAt[lEdge, top];
leftBotVtx ← EvalEdgeAt[lEdge, bottom];
}
ELSE {
topX: REAL ← GetXcoordAt[lEdge, top];
botX: REAL ← GetXcoordAt[lEdge, bottom];
leftTopVtx ← EvalEdgeAt[lEdge, topX];
leftBotVtx ← EvalEdgeAt[lEdge, botX];
};
IF rEdge.moreVertical THEN {
rightTopVtx ← EvalEdgeAt[rEdge, top];
rightBotVtx ← EvalEdgeAt[rEdge, bottom];
}
ELSE {
topX: REAL ← GetXcoordAt[rEdge, top];
botX: REAL ← GetXcoordAt[rEdge, bottom];
rightTopVtx ← EvalEdgeAt[rEdge, topX];
rightBotVtx ← EvalEdgeAt[rEdge, botX];
};

if left side more horizontal, check for slope, make top or bottom edge,
do right triangle, do new left edge
IF NOT lEdge.moreVertical THEN
IF lEdge.yIncr < 0.
THEN {         -- left edge is more horizontal, top vertex is leftmost
tEdge: REF EdgeBlock ← MakeEdge[leftTopVtx, rightTopVtx]; bEdge ← lEdge;
IF leftBotVtx.coord.x < rightTopVtx.coord.x
THEN {        -- easy case: right triangle containing whole left edge
ShowSteepTrap[leftTopVtx.coord.x, leftBotVtx.coord.x, bEdge, tEdge, sideways];
lEdge ← MakeEdge[leftBotVtx, EvalEdgeAt[tEdge, leftBotVtx.coord.x]];
bEdge ← NIL; -- all drawn, won't be needed below
}
ELSE {        -- right top is left of left bottom
ShowSteepTrap[leftTopVtx.coord.x, rightTopVtx.coord.x, bEdge, tEdge, sideways];
lEdge ← MakeEdge[EvalEdgeAt[bEdge, rightTopVtx.coord.x], rightTopVtx];
};
}
ELSE {     -- left edge is more horizontal, bottom vertex is leftmost
bEdge: REF EdgeBlock ← MakeEdge[leftBotVtx, rightBotVtx]; tEdge ← lEdge;
IF leftTopVtx.coord.x < rightBotVtx.coord.x
THEN {      -- easy case: right triangle containing whole left edge
ShowSteepTrap[leftBotVtx.coord.x, leftTopVtx.coord.x, bEdge, tEdge, sideways];
lEdge ← MakeEdge[leftTopVtx, EvalEdgeAt[bEdge, leftTopVtx.coord.x]];
tEdge ← NIL; -- all drawn, won't be needed below
}
ELSE {      -- right bottom is left of left top
ShowSteepTrap[leftBotVtx.coord.x, rightBotVtx.coord.x, bEdge, tEdge, sideways];
lEdge ← MakeEdge[rightBotVtx, EvalEdgeAt[tEdge, rightBotVtx.coord.x]];
};
};
if right side more horizontal do likewise
IF NOT rEdge.moreVertical THEN
IF rEdge.yIncr < 0.
THEN {         -- right edge is more horizontal, top vertex is leftmost
bEdge: REF EdgeBlock ← MakeEdge[leftBotVtx, rightBotVtx]; tEdge ← rEdge;
IF leftBotVtx.coord.x < rightTopVtx.coord.x
THEN {       -- easy case: right triangle containing whole right edge
ShowSteepTrap[rightTopVtx.coord.x, rightBotVtx.coord.x, bEdge, tEdge, sideways];
rEdge ← MakeEdge[EvalEdgeAt[bEdge, rightTopVtx.coord.x], rightTopVtx];
tEdge ← NIL; -- all drawn, won't be needed below
}
ELSE {       -- left bottom is right of right top
ShowSteepTrap[leftBotVtx.coord.x, rightBotVtx.coord.x, bEdge, tEdge, sideways];
rEdge ← MakeEdge[EvalEdgeAt[bEdge, leftBotVtx.coord.x], leftBotVtx];
};
}
ELSE {         -- right edge is more horizontal, bottom vertex is leftmost
tEdge: REF EdgeBlock ← MakeEdge[leftTopVtx, rightTopVtx]; bEdge ← rEdge;
IF leftTopVtx.coord.x < rightBotVtx.coord.x
THEN {       -- easy case: right triangle containing whole right edge
ShowSteepTrap[rightBotVtx.coord.x, rightTopVtx.coord.x, bEdge, tEdge, sideways];
rEdge ← MakeEdge[rightBotVtx, EvalEdgeAt[tEdge, rightBotVtx.coord.x]];
bEdge ← NIL; -- all drawn, won't be needed below
}
ELSE {       -- left top is right of right bottom
ShowSteepTrap[leftTopVtx.coord.x, rightTopVtx.coord.x, bEdge, tEdge, sideways];
rEdge ← MakeEdge[EvalEdgeAt[bEdge, leftTopVtx.coord.x], leftTopVtx];
};
};
Do middle section
IF (lEdge.xIncr # rEdge.xIncr) OR ((lEdge.x + minSize) < rEdge.x)
THEN         -- non-parallepiped or thick enough
IF tEdge = NIL AND bEdge = NIL     -- middle rectangle or unmodified
THEN ShowSteepTrap[bottom, top, lEdge, rEdge]
ELSE IF NOT (tEdge = NIL OR bEdge = NIL)   -- both more horizontal
THEN ShowSteepTrap[lEdge.x, rEdge.x, bEdge, tEdge, sideways]
ELSE { -- hard case: make new polygon and recurse
a, b, c: REAL;        -- parameters for defining 45 degree lines
poly: REF PolygonPackage.Polygon ← NEW[PolygonPackage.Polygon[4]];
poly.nVtces ← 4;
IF   tEdge = NIL THEN IF bEdge.yIncr > 0.0 THEN tEdge ← lEdge ELSE tEdge ← rEdge
ELSE IF bEdge = NIL THEN IF tEdge.yIncr > 0.0 THEN bEdge ← rEdge ELSE bEdge ← lEdge;
IF tEdge.moreVertical
THEN           -- upper edge runs top to bottom
IF tEdge.xIncr < 0.0 THEN { -- originally right edge
poly.vtx[0] ← EvalEdgeAt[tEdge, bottom]^;
poly.vtx[1] ← EvalEdgeAt[tEdge, top]^;
poly.vtx[2] ← EvalEdgeAt[bEdge, lEdge.x]^;
poly.vtx[3] ← EvalEdgeAt[bEdge, GetXcoordAt[bEdge, bottom]]^;
a ← .707; b ← .707;   
}
ELSE {  -- originally left edge
poly.vtx[0] ← EvalEdgeAt[tEdge, top]^;
poly.vtx[1] ← EvalEdgeAt[tEdge, bottom]^;
poly.vtx[2] ← EvalEdgeAt[bEdge, GetXcoordAt[bEdge, bottom]]^;
poly.vtx[3] ← EvalEdgeAt[bEdge, rEdge.x]^;
a ← -.707; b ← .707;
}
ELSE      -- lower edge runs top to bottom
IF bEdge.xIncr > 0.0 THEN { -- originally right edge
poly.vtx[0] ← EvalEdgeAt[bEdge, bottom]^;
poly.vtx[1] ← EvalEdgeAt[bEdge, top]^;
poly.vtx[2] ← EvalEdgeAt[tEdge, GetXcoordAt[tEdge, top]]^;
poly.vtx[3] ← EvalEdgeAt[tEdge, lEdge.x]^;
a ← -.707; b ← .707;
}
ELSE {  -- originally left edge
poly.vtx[0] ← EvalEdgeAt[bEdge, top]^;
poly.vtx[1] ← EvalEdgeAt[bEdge, bottom]^;
poly.vtx[2] ← EvalEdgeAt[tEdge, rEdge.x]^;
poly.vtx[3] ← EvalEdgeAt[tEdge, GetXcoordAt[tEdge, top]]^;
a ← .707; b ← .707;
};
-- evaluate area based on distance of vertices from 45 degree line
c ← -(a * poly.vtx[0].coord.x + b * poly.vtx[0].coord.y);
IF (top - bottom) *
 ( MAX[a * poly.vtx[1].coord.x + b * poly.vtx[1].coord.y + c,
   a * poly.vtx[2].coord.x + b * poly.vtx[2].coord.y + c,
   a * poly.vtx[3].coord.x + b * poly.vtx[3].coord.y + c]
 - MIN[a * poly.vtx[1].coord.x + b * poly.vtx[1].coord.y + c,
   a * poly.vtx[2].coord.x + b * poly.vtx[2].coord.y + c,
   a * poly.vtx[3].coord.x + b * poly.vtx[3].coord.y + c] )
 < minSize
THEN RETURN[]         -- too small to matter
ELSE FancyTiler[cachedContext, poly]; -- go draw it (recursively)
};
};
ShowSteepTrap: PROC[ bottom, top: REAL, lEdge, rEdge: REF EdgeBlock,
        sideways: BOOLEANFALSE ] ~ {
getCvrge: BOOLEAN ~ TRUE;
lVtx, rVtx, vtx: REF PolygonPackage.VertexInfo;
scanSeg: REF EdgeBlock;
scanTxtrXIncr, scanTxtrYIncr: REAL;
lStartSave: REAL ← lEdge.start;  lEndSave: REAL ← lEdge.end; -- save limits to restore later
rStartSave: REAL ← rEdge.start; rEndSave: REAL ← rEdge.end;
botNat, topNat, lftNat, rgtNat: INTEGER;
IF bottom + minSize >= top THEN RETURN[]; -- too thin to affect image
lEdge.start ← rEdge.start ← bottom; -- set edge limits for coverage calcs.
lEdge.end ← rEdge.end ← top;
botNat ← Real.FixI[Floor[bottom]]; topNat ← Real.FixI[Ceiling[top]]; -- get integer range
IF sideways THEN {         -- correct for possible floating point error
botNat ← MAX[displayData[0].fMin,
     MIN[botNat, displayData[0].fMin + displayData[0].fSize - 1]];
topNat ← MAX[displayData[0].fMin,
     MIN[topNat, displayData[0].fMin + displayData[0].fSize - 1]];
}
ELSE {
botNat ← MAX[displayData[0].sMin,
     MIN[botNat, displayData[0].sMin + displayData[0].sSize - 1]];
topNat ← MAX[displayData[0].sMin,
     MIN[topNat, displayData[0].sMin + displayData[0].sSize - 1]];
};
FOR y: NAT IN [botNat..topNat] DO
lVtx ← EvalEdgeAt[lEdge, Real.Float[y], getCvrge];
rVtx ← EvalEdgeAt[rEdge, Real.Float[y], getCvrge];
IF sideways THEN {            -- on side, scan up and down
IF lVtx.coord.y + minSize < rVtx.coord.y THEN { -- do only if thick enough vertically
scanSeg ← MakeEdge[lVtx, rVtx];
IF txtrSum # NIL OR mipTexture = TRUE THEN {
divisor: REAL ← rVtx.coord.y - lVtx.coord.y;
IF ABS[divisor] < 1.0 THEN divisor ← 1.0 * Sgn[divisor];
scanTxtrXIncr ← (rEdge.txtrXIncr - lEdge.txtrXIncr) / divisor;
scanTxtrYIncr ← (rEdge.txtrYIncr - lEdge.txtrYIncr) / divisor;
};
lftNat ← Real.FixI[Floor[lVtx.coord.y]];        -- get integer range
lftNat ← MAX[displayData[0].sMin,   -- correct for possible floating point error
    MIN[lftNat, displayData[0].sMin + displayData[0].sSize - 1]];
rgtNat ← Real.FixI[Ceiling[rVtx.coord.y]];
rgtNat ← MAX[displayData[0].sMin,
    MIN[rgtNat,displayData[0].sMin + displayData[0].sSize - 1]];
FOR x: NAT IN [lftNat..rgtNat] DO
vtx ← EvalEdgeAt[scanSeg, Real.Float[x], getCvrge];
IF textureData = NIL         -- no texture
THEN IF blendPixels THEN BlendPixelAt[vtx^, y, x] ELSE AddPixelAt[vtx^, y, x]
ELSE IF txtrSum = NIL AND mipTexture = FALSE-- nearest texture pixel algorithm
THEN WriteTxtrAt[vtx^, y, x]
ELSE {             -- antialiased texture
txtrXIncr: REALMAX[ABS[(x - lVtx.coord.y) * scanTxtrXIncr + lEdge.txtrXIncr],
ABS[scanSeg.txtrXIncr] ] / 2.0;
txtrYIncr: REALMAX[ABS[(x - lVtx.coord.y) * scanTxtrYIncr + lEdge.txtrYIncr],
ABS[scanSeg.txtrYIncr] ] / 2.0;
IF txtrSum # NIL THEN WriteSumTxtrAt[vtx^, y, x, txtrXIncr, txtrYIncr]
      ELSE WriteMipTxtrAt[vtx^, y, x, txtrXIncr, txtrYIncr];
};
ENDLOOP;
};
}
ELSE                   -- upright, scan across
IF lVtx.coord.x + minSize < rVtx.coord.x THEN {   -- do only if wide enough
scanSeg ← MakeEdge[lVtx, rVtx];
IF txtrSum # NIL OR mipTexture = TRUE THEN {
divisor: REAL ← rVtx.coord.x - lVtx.coord.x;
IF ABS[divisor] < 1.0 THEN divisor ← 1.0 * Sgn[divisor];
scanTxtrXIncr ← (rEdge.txtrXIncr - lEdge.txtrXIncr) / divisor;
scanTxtrYIncr ← (rEdge.txtrYIncr - lEdge.txtrYIncr) / divisor;
};
lftNat ← Real.FixI[Floor[lVtx.coord.x]];        -- get integer range
lftNat ← MAX[displayData[0].fMin,   -- correct for possible floating point error
    MIN[lftNat, displayData[0].fMin + displayData[0].fSize - 1]];
rgtNat ← Real.FixI[Ceiling[rVtx.coord.x]];
rgtNat ← MAX[displayData[0].fMin,
    MIN[rgtNat,displayData[0].fMin + displayData[0].fSize - 1]];
FOR x: NAT IN [lftNat..rgtNat] DO
vtx ← EvalEdgeAt[scanSeg, Real.Float[x], getCvrge];
IF textureData = NIL         -- no texture
THEN IF blendPixels THEN BlendPixelAt[vtx^, x, y] ELSE AddPixelAt[vtx^, x, y]
ELSE IF txtrSum = NIL AND mipTexture = FALSE-- nearest texture pixel algorithm
THEN WriteTxtrAt[vtx^, x, y]
ELSE {             -- antialiased texture
txtrXIncr: REALMAX[ABS[(x - lVtx.coord.x) * scanTxtrXIncr + lEdge.txtrXIncr],
ABS[scanSeg.txtrXIncr] ] / 2.0;
txtrYIncr: REALMAX[ABS[(x - lVtx.coord.x) * scanTxtrYIncr + lEdge.txtrYIncr],
ABS[scanSeg.txtrYIncr] ] / 2.0;
IF txtrSum # NIL THEN WriteSumTxtrAt[vtx^, x, y, txtrXIncr, txtrYIncr]
      ELSE WriteMipTxtrAt[vtx^, x, y, txtrXIncr, txtrYIncr];
};
ENDLOOP;
};
ENDLOOP;
lEdge.start ← lStartSave; lEdge.end ← lEndSave; -- restore limits
rEdge.start ← rStartSave; rEdge.end ← rEndSave;
};
AddPixelAt: PROC[vtx: PolygonPackage.VertexInfo, x, y: NAT] ~ {
pxlValue: LONG CARDINAL ← Real.FixC[vtx.shade.g * 255];
IF vtx.shade.t > minSize    -- if t < minsize then opaque, just write pixel
THEN {
oldValue: LONG CARDINAL ← ImagerPixelMaps.GetPixel[displayData[0], y, x];
pxlValue ← Real.RoundC[pxlValue * (1.0 - vtx.shade.t)];
pxlValue ← MAX[ 1, MIN[pxlValue + oldValue, 255]];
};
ImagerPixelMapsExtras.SetPixel[displayData[0], y, x, pxlValue];
};
BlendPixelAt: PROC[vtx: PolygonPackage.VertexInfo, x, y: NAT] ~ {
pxlValue: LONG CARDINAL ← Real.FixC[vtx.shade.g * 255];
IF vtx.shade.t > minSize    -- if t < minsize then opaque, just write pixel
THEN {
oldValue: LONG CARDINAL ← ImagerPixelMaps.GetPixel[displayData[0], y, x];
pxlValue ← Real.RoundC[pxlValue + vtx.shade.t * (oldValue - pxlValue)];
};
ImagerPixelMapsExtras.SetPixel[displayData[0], y, x, pxlValue];
};
WritePixel for handling texture map
WriteTxtrAt: PROC[vtx: PolygonPackage.VertexInfo, x, y: NAT] ~ {
txtrValue, pxlValue: LONG CARDINAL;
txtrX, txtrY: INTEGER;
txtrX ← Real.Fix[vtx.shade.txtrX * textureData[0].fSize] MOD textureData[0].fSize;
IF txtrX < 0 THEN txtrX ← txtrX + textureData[0].fSize;
txtrY ← Real.Fix[vtx.shade.txtrY * textureData[0].sSize] MOD textureData[0].sSize;
IF txtrY < 0 THEN txtrY ← txtrY + textureData[0].sSize;
txtrY ← textureData[0].sSize - 1 - txtrY;  -- compensate for inverted pixel map
txtrValue ← ImagerPixelMaps.GetPixel[ textureData[0], txtrY, txtrX];
pxlValue ← Real.FixC[vtx.shade.g * txtrValue];
IF vtx.shade.t > minSize     -- if transmittance is significant, add to overwritten pixel
THEN {
oldValue: LONG CARDINAL ← ImagerPixelMaps.GetPixel[displayData[0], y, x];
pxlValue ← Real.RoundC[pxlValue * (1.0 - vtx.shade.t)];
pxlValue ← MAX[ 1, MIN[pxlValue + oldValue, 255]];
};
ImagerPixelMapsExtras.SetPixel[displayData[0], y, x, pxlValue];
};
WriteSumTxtrAt: PROC[vtx: PolygonPackage.VertexInfo, x, y: NAT, txtrXIncr, txtrYIncr: REAL] ~{
GetLerpedValue: PROC[ llVal, ulVal, urVal, lrVal: INT, xPos, yPos: REAL ]
RETURNS[ REAL ] ~ {
lowerValue: REAL ← llVal + xPos * (lrVal - llVal);
upperValue: REAL ← ulVal + xPos * (urVal - ulVal);
RETURN [ lowerValue + yPos * (upperValue - lowerValue) ];
};
CorrectSum: PROC[x, y: NAT] RETURNS[INT] ~ {
IF x < txtrSum[0].length AND y < txtrSum.length THEN {
RETURN[ txtrSum[y][x] ]
}
ELSE IF x >= txtrSum[0].length AND y >= txtrSum.length THEN {
x ← x - txtrSum[0].length;  y ← y - txtrSum.length;
RETURN[  CorrectSum[x, y]    + txtrSum[maxTxtrY][maxTxtrX]
   + CorrectSum[x, maxTxtrY]  + CorrectSum[maxTxtrX, y] ];
}
ELSE IF x >= txtrSum[0].length THEN {
x ← x - txtrSum[0].length;  
RETURN[ CorrectSum[x, y] + txtrSum[y][maxTxtrX] ];
}
ELSE {            -- IF y >= txtrSum.length
y ← y - txtrSum.length;
RETURN[ CorrectSum[x, y] + txtrSum[maxTxtrY][x] ];
};
};
GetValueAt: PROC[ x, y: REAL] RETURNS[ REAL ] ~ {
xPos, yPos: REAL; lX, lY, rX, uY: NAT; 
xPos ← x * txtrSum[0].length;
yPos ← y * txtrSum.length;
lX ← Real.Fix[xPos]; rX ← lX + 1;
lY ← Real.Fix[yPos]; uY ← lY + 1;
xPos ← xPos - Real.Fix[xPos];         -- get fractional part
yPos ← yPos - Real.Fix[yPos];
RETURN [ GetLerpedValue[
CorrectSum[lX, lY], CorrectSum[lX, uY], CorrectSum[rX, uY], CorrectSum[rX, lY],
xPos, yPos
] ];
};
pxlValue: INTEGER;
txtrValue: REAL;
area: REAL;
interp: BOOLEANTRUE;
txtrXIncr ← txtrXIncr * sumTextureRange; txtrYIncr ← txtrYIncr * sumTextureRange;
area ← 4 * txtrXIncr * txtrYIncr * txtrSum.length * txtrSum[0].length;
IF area > interpThreshold THEN interp ← FALSE;
WHILE vtx.shade.txtrX - txtrXIncr < 0.0 DO vtx.shade.txtrX ← vtx.shade.txtrX + 1.0; ENDLOOP;
WHILE vtx.shade.txtrY - txtrYIncr < 0.0 DO vtx.shade.txtrY ← vtx.shade.txtrY + 1.0; ENDLOOP;
IF interp THEN {
txtrValue ← GetValueAt[ vtx.shade.txtrX + txtrXIncr, vtx.shade.txtrY + txtrYIncr ]
  + GetValueAt[ vtx.shade.txtrX - txtrXIncr, vtx.shade.txtrY - txtrYIncr ]
  - GetValueAt[ vtx.shade.txtrX + txtrXIncr, vtx.shade.txtrY - txtrYIncr ]
  - GetValueAt[ vtx.shade.txtrX - txtrXIncr, vtx.shade.txtrY + txtrYIncr ];
txtrValue ← txtrValue / area;
}
ELSE {
lX, lY, rX, uY: NAT;
lX ← Real.RoundI[(vtx.shade.txtrX - txtrXIncr) * txtrSum[0].length];
rX ← Real.RoundI[(vtx.shade.txtrX + txtrXIncr) * txtrSum[0].length];
IF lX >= rX THEN rX ← lX + 1;
lY ← Real.RoundI[(vtx.shade.txtrY - txtrYIncr) * txtrSum.length];
uY ← Real.RoundI[(vtx.shade.txtrY + txtrYIncr) * txtrSum.length];
IF lY >= uY THEN uY ← lY + 1;
txtrValue ←
  ( CorrectSum[rX, uY] + CorrectSum[lX, lY] - CorrectSum[lX, uY] - CorrectSum[rX, lY] )
/ Real.FixI[ (rX - lX) * (uY - lY) ];
};
pxlValue ← Real.RoundI[vtx.shade.g * txtrValue];
IF pxlValue > 255 THEN pxlValue ← 255  ELSE IF pxlValue < 0 THEN pxlValue ← 0;
IF vtx.shade.t > minSize     -- if transmittance is significant, add to overwritten pixel
THEN {
oldValue: INTEGER ← ImagerPixelMaps.GetPixel[displayData[0], y, x];
pxlValue ← Real.RoundI[pxlValue * (1.0 - vtx.shade.t)];
pxlValue ← MAX[ 1, MIN[pxlValue + oldValue, 255]];
};
ImagerPixelMapsExtras.SetPixel[displayData[0], y, x, pxlValue];
};
WriteMipTxtrAt: PROC[vtx: PolygonPackage.VertexInfo, x, y: NAT, txtrXincr, txtrYincr: REAL] ~{
GetValueAt: PROC[mipTable: ImagerPixelMaps.PixelMap, txtrX, txtrY: REAL]
RETURNS[value: REAL] ~ {
GetVal: PROC[x, y: NAT] RETURNS[val: NAT] ~ {
x ← x MOD mipTable.fSize;
y ← y MOD mipTable.sSize;
val ← ImagerPixelMaps.GetPixel[mipTable, mipTable.sSize - 1 - y, x]; -- correct inversion
};
xAddr, yAddr: NAT;
xAlpha, yAlpha, leftVal, rightVal, upperVal, lowerVal: REAL;
txtrX ← txtrX * mipTable.fSize;
txtrY ← txtrY * mipTable.sSize; 
xAddr ← Real.FixC[txtrX]; yAddr ← Real.FixC[txtrY];   -- get fractional part
xAlpha ← txtrX - xAddr; yAlpha ← txtrY - yAddr;
leftVal ← GetVal[xAddr, yAddr+1];
rightVal ← GetVal[xAddr+1, yAddr+1];
upperVal ← leftVal + xAlpha * (rightVal - leftVal);
leftVal ← GetVal[xAddr, yAddr];
rightVal ← GetVal[xAddr+1, yAddr];
lowerVal ← leftVal + xAlpha * (rightVal - leftVal);
value ← lowerVal + yAlpha * (upperVal - lowerVal);
};
alpha, txtrValue, bigTxtrValue, smallTxtrValue: REAL;
pxlValue: INTEGER;
txtrScale: REAL ← mipTextureRange * RealFns.SqRt[Sqr[txtrXincr * mipTable[0].fSize] *
                Sqr[txtrYincr * mipTable[0].sSize] ];
logTxtrScale: REAL ← RealFns.Log[2.0, txtrScale];         -- Log base 2
SELECT TRUE FROM
logTxtrScale < 0.0 => {
bigTxtrValue ← GetValueAt[mipTable[0], vtx.shade.txtrX, vtx.shade.txtrY];
alpha ← 0.0;
};
logTxtrScale > maxScale => {
bigTxtrValue ← GetValueAt[mipTable[maxTable], vtx.shade.txtrX, vtx.shade.txtrY];
alpha ← 0.0;
};
ENDCASE => {
tblAddr: NAT ← Real.FixC[logTxtrScale];
bigTxtrValue ← GetValueAt[mipTable[tblAddr], vtx.shade.txtrX, vtx.shade.txtrY];
smallTxtrValue ← GetValueAt[mipTable[tblAddr + 1], vtx.shade.txtrX, vtx.shade.txtrY];
alpha ← ( txtrScale - Basics.BITSHIFT[1, tblAddr] ) / Basics.BITSHIFT[1, tblAddr];
};
txtrValue ← bigTxtrValue + alpha * (smallTxtrValue - bigTxtrValue);
pxlValue ← Real.RoundI[vtx.shade.g * txtrValue];
pxlValue ← MAX[0, MIN[255, pxlValue]];
IF vtx.shade.t > minSize -- if coverage significant, add to pixel
THEN {
oldValue: INTEGER ← ImagerPixelMaps.GetPixel[displayData[0], y, x];
pxlValue ← Real.RoundI[pxlValue * (1.0 - vtx.shade.t)];
pxlValue ← MAX[ 1, MIN[pxlValue + oldValue, 255]];
};
ImagerPixelMapsExtras.SetPixel[displayData[0], y, x, pxlValue];
};
Register pixel-writing style, blend with background or sum to background
BlendPixels: PUBLIC PROC[onNotOff: BOOLEAN] ~ {
blendPixels ← onNotOff;
};
Register a texture context with the tiler package
SetTexture: PUBLIC PROC[context: Imager.Context] ~ {
IF context = NIL THEN { txtrSum ← NIL; mipTexture ← FALSE; textureData ← NIL }
ELSE textureData ← NARROW[context.data, ImagerDisplay.DisplayData];
};
SumTexture: PUBLIC PROC[range, interp: REAL] ~ {
IF textureData = NIL THEN RETURN[];
{ sumTextureRange ← range;        -- store texture footprint width as global
interpThreshold ← interp;       -- store area above which no interpolation
maxTxtrX ← textureData[0].fSize - 1;     -- set global coordinate limits
maxTxtrY ← textureData[0].sSize - 1;
txtrSum ← NEW[ ScanSequence[maxTxtrY+1] ];
FOR y: NAT IN [0 .. maxTxtrY] DO
invY: NAT ← maxTxtrY - y;         -- inverted Y to invert texture
txtrSum[y] ← NEW[ IntSequence[maxTxtrX+1] ];
FOR x: NAT IN [0 .. maxTxtrX] DO
txtrSum[y][x] ← ImagerPixelMaps.GetPixel[textureData[0],
          invY + textureData[0].sMin + textureData[0].sOrigin,
           x + textureData[0].fMin + textureData[0].fOrigin];
IF x > 0 THEN {
txtrSum[y][x] ← txtrSum[y][x] + txtrSum[y][x - 1];    -- add area to left
IF y > 0 THEN   -- x > 0 and y > 0: add area below minus area below and to left
txtrSum[y][x] ← txtrSum[y][x] + (txtrSum[y - 1][x] - txtrSum[y - 1][x - 1]);
}
ELSE IF y > 0 THEN
txtrSum[y][x] ← txtrSum[y][x] + txtrSum[y - 1][x];    -- add area below
ENDLOOP;
ENDLOOP;
};
};
MipMapTexture: PUBLIC PROC[range: REAL, showMaps: BOOLEANFALSE] ~ {
ShowMip: PROC[mipTable: ImagerPixelMaps.PixelMap] ~ {
sOrigin, fOrigin: INTEGER;
sOrigin ← displayData[0].sOrigin; displayData[0].sOrigin ← sOrigin + mipTable.sSize;
fOrigin ← displayData[0].fOrigin; displayData[0].fOrigin ← fOrigin + mipTable.fSize;
ImagerPixelMaps.Transfer[displayData[0], mipTable, [NULL, NULL]];
displayData[0].sOrigin ← sOrigin;
displayData[0].fOrigin ← fOrigin;
};
IF textureData = NIL THEN RETURN[];
{ sDim: NAT ← textureData[0].sSize; fDim: NAT ← textureData[0].fSize;
sMin: NAT ← textureData[0].sMin + textureData[0].sOrigin;
fMin: NAT ← textureData[0].fMin + textureData[0].fOrigin;
minDimension: NATMIN[fDim, sDim];
mipTextureRange ← range;          -- save in global for later use
mipTable[0] ← ImagerPixelMaps.Create[3, [sMin, fMin, sDim, fDim]];
mipTable[0].sOrigin ← mipTable[0].fOrigin ← 0;
mipTable[0].refRep ← textureData[0].refRep;   --point at bits in original texture
IF showMaps THEN ShowMip[mipTable[0]];
minDimension ← minDimension / 2; sDim ← sDim / 2; fDim ← fDim / 2;
maxTable ← 0;
WHILE minDimension >= 1 DO
maxTable ← maxTable + 1;
mipTable[maxTable] ← ImagerPixelMaps.Create[3, [sMin, fMin, sDim, fDim]];
mipTable[maxTable].sOrigin ← mipTable[maxTable].fOrigin ← 0;
FOR j: NAT IN [sMin..sDim+sMin) DO
FOR i: NAT IN [fMin..fDim+fMin) DO
tbl: NAT ← maxTable - 1;
value: NAT ← ( ImagerPixelMaps.GetPixel[mipTable[tbl], i*2, j*2]
   + ImagerPixelMaps.GetPixel[mipTable[tbl], i*2 +1, j*2]
   + ImagerPixelMaps.GetPixel[mipTable[tbl], i*2, j*2 +1]
   + ImagerPixelMaps.GetPixel[mipTable[tbl], i*2 +1, j*2 +1] )
  / 4;
ImagerPixelMapsExtras.SetPixel[mipTable[maxTable], i, j, value];
ENDLOOP;
ENDLOOP;
minDimension ← minDimension / 2; sDim ← sDim / 2; fDim ← fDim / 2;
IF showMaps THEN ShowMip[mipTable[maxTable]];
ENDLOOP;
maxScale ← maxTable;
mipTexture ← TRUE;
};
};
END.