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: BOOLEAN _ FALSE; weight: ARRAY [0..tblLngth] OF REAL; blendPixels: BOOLEAN _ FALSE; cachedContext: Imager.Context; displayData, textureData: ImagerDisplay.DisplayData _ NIL; mipTexture: BOOLEAN _ FALSE; 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 IntSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF INT]; ScanSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF REF IntSequence]; txtrSum: REF ScanSequence _ NIL; maxTxtrX, maxTxtrY: NAT; 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[] ~ { 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; }; 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: BOOLEAN _ FALSE] 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 { 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 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: BOOLEAN _ TRUE; 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 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 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]; }; }; 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: BOOLEAN _ FALSE ] ~ { 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: REAL _ MAX[ABS[(x - lVtx.coord.y) * scanTxtrXIncr + lEdge.txtrXIncr], ABS[scanSeg.txtrXIncr] ] / 2.0; txtrYIncr: REAL _ MAX[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: REAL _ MAX[ABS[(x - lVtx.coord.x) * scanTxtrXIncr + lEdge.txtrXIncr], ABS[scanSeg.txtrXIncr] ] / 2.0; txtrYIncr: REAL _ MAX[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]; }; 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: BOOLEAN _ TRUE; 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]; }; BlendPixels: PUBLIC PROC[onNotOff: BOOLEAN] ~ { blendPixels _ onNotOff; }; 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: BOOLEAN _ FALSE] ~ { 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: NAT _ MIN[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. PTilerPackageImpl.mesa Last Edited by: Crow, February 29, 1984 12:55:57 pm PST Display and Texture bits Mip Map table storage and values Summed-Area Texture storage Data Structure for trapezoid edges 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 get trapezoid given by next higher vertex Get Pixel area coverage weighted by function stored in "weight" Modify transmittance to force blending with other scan segments if left side more horizontal, check for slope, make top or bottom edge, do right triangle, do new left edge if right side more horizontal do likewise Do middle section WritePixel for handling texture map Register pixel-writing style, blend with background or sum to background Register a texture context with the tiler package Κξ˜headšœ™Jšœ7™7J˜šΟk ˜ Jšœ œœ˜Jšœ œ*˜8Jšœ œ ˜Jšœœ˜,Jšœ œ ˜Jšœœ˜#Jšœœ(˜>Jšœœ ˜'Jšœ ˜ —J˜—head2šœœ˜Iašœœ,˜EMšœ˜J˜Jšœ œ ˜J˜Jšœ œ˜Jšœ œ˜Jšœœœ˜#Jšœœœœ˜$šœ œœ˜Jšœ™—Jšœ˜šœ6œ˜:Jšœ!™!—Jšœ œœ˜Jšœ3œ˜8Jšœ œ œ˜4Jšœ œ Οc/˜Gšœ œ ž˜5J™—Jš œ œœœ œœœ˜9Jš œœœœ œœœ˜EJšœ œœ˜ šœœ˜J™"—šœ œœ˜Jšœœœ˜)Jšœ,œ˜2JšœX˜\Jšœ˜—J˜—šΟnœ œ œœœœœ˜SM˜—unitš Ÿœ œ œœœœ˜8Mšœ  œ œ˜3Mšœ˜M˜—š Ÿœœœœœ˜2J˜"Jšœ œ˜!J˜J˜—š Ÿœœœœœ˜0J˜"Jšœ œ˜!˜J˜——šŸœœ˜J™‰šœœœœ˜"Jšœœ˜!Jšœ˜Jšœ/˜/Jšœ˜—Jšœœ˜Jšœ˜J˜—šŸ œ œ œ˜WJšœœ˜Jšœ8œ˜=Jšœœ˜'Jšœœ ˜Jšœ œœ˜HJšœœœ˜GJ˜šœœž"˜CJšœœ*˜>Jšœ˜J˜—Jšœœœ˜1N˜-š œœœœž˜JJšœ˜Jšœ4˜8Jšœ˜ —Jšœ)ž!˜JJšœ!œ˜'J˜!J˜ šœœ ž!˜Jšœœž˜FJšœ6œ ˜FJšœ4˜4Jšœ˜Jšœœ˜J˜—šœœž˜GJšœ+œ ˜;Jšœ4˜4Jšœ˜Jšœœ˜J˜Jšœ)™)—šœ7œ˜AJšœ+ž˜FJšœœ˜1J˜—šœ˜Jšœ)ž˜FJšœœ˜2J˜—Jšœ œ1˜=J˜+Jšœ˜—˜J˜——š Ÿœœ œœœ˜VJšœœ˜ Jšœœ œ˜(J˜Jšœœ!œ˜Gšœ˜J˜%Jšœ œ ˜0Jšœ œ˜+Jšœœ˜J˜—šœ˜J˜%Jšœ œ ˜0Jšœ œ˜+Jšœœ˜J˜—Jšœœœž˜Jšœœ ž7˜KJ˜GJ˜HJ˜KJ˜JJ˜JJ˜GJ˜FJ˜FJ˜GJ˜VJ˜VJ˜—Jšœ ˜J˜J˜—Jš Ÿ œœœœœœ˜Ušœœœ ˜6Jšœ œ˜Jšœœ˜%Jšœœž$˜RJšœœœ˜3Jšœ˜Jšœœœœ˜AJ˜)J˜)J˜,J˜,J˜,J˜)J˜)J˜)J˜)J˜5J˜5J˜šœ œ˜Jšœ?™?Jšœ˜Nšœ œ˜)Jšœœž˜MJšœœœž˜OJšœ)˜-Jšœ8˜8Nšœ ˜ Jšœœž˜MJšœœœž˜NJšœ(˜,JšœCž˜[J™Jšœ?™?JšœDž˜`J˜—J˜J˜—šŸ œœœœ˜Hš Ÿ œœœœœœ˜FJšœœ˜Jšœ ˜&J˜—Jšœ œœ˜Jšœœ ˜Jšœ2œ˜PNšœœœž˜Išœœœ˜7šœœ˜J˜$J˜'J˜Jšœ˜Jšœœ˜%Jšœœ˜)J˜%J˜%J˜—šœœ˜J˜%J˜(J˜Jšœ˜Jšœœ˜%Jšœœ˜)J˜&J˜&J˜——JšœH™HJšœ#™#šœœ˜Jšœ˜šœ ž8˜GJšœœB˜LJšœ*˜,šœž7˜JJ˜NJ˜DJšœœž#˜JJ˜—šœž$˜6J˜OJ˜FJ˜—J˜—šœž;˜TJšœœB˜LJšœ*˜,šœž=˜IJ˜NJ˜DJšœœž#˜JJ˜—šœ ž#˜3J˜OJ˜FJ˜—J˜J˜——Jšœ*™*šœœ˜Jšœ˜šœ ž9˜IJšœœB˜LJšœ*˜,šœž?˜KJ˜PJ˜GJšœœž#˜JJ˜—šœž+˜6J˜OJ˜DJ˜—J˜—šœ ž<˜LJšœœB˜LJšœ*˜,šœž>˜JJ˜QJ˜GJšœœž#˜JJ˜—šœž*˜5J˜OJ˜DJ˜—J˜——J™Jšœ™Jšœœ ˜Ašœ$ž$˜Lš œ œœ œ ž&˜NJšœ)˜-—š œœœ œœ œž˜FJšœ8˜<—šœž*˜6Jšœ œ ž*˜@Jšœœœ˜BJ˜Jš œ œœœœœ˜RJšœœ œœœœœ˜TJšœ˜šœž ˜9šœœž˜CJ˜*J˜&J˜*J˜=J˜J˜—šœ'ž˜BJ˜&J˜*J˜>J˜+J˜J˜——šœ&ž ˜Jšœœž˜CJ˜*J˜&J˜;J˜*J˜J˜—šœ'ž˜BJ˜&J˜*J˜+J˜:J˜J˜——J˜BJšœ9˜9Jšœ˜Jšœœ8˜>Jšœ:˜:Jšœ9˜9Jšœœ8˜>Jšœ:˜:Jšœ<˜˜>Jšœ@˜@šœœ˜Jšœ6ž˜Išœœž@˜QJšœM˜M—Jšœ˜—šœœœ˜Jšœ6ž˜G—Jšœ˜—Jšœ˜—J˜—J˜J˜—š Ÿ œœœœ œœ˜FšŸœœ(˜5Jšœœ˜JšœX˜XJšœW˜WJšœ4œœ˜AJšœ%˜%Jšœ!˜!J˜—Jšœœœœ˜#šœ œ"œ˜IJšœœ4˜=Jšœœ0˜9Jšœœœ ˜%Jšœ"ž˜AJšœB˜BJšœ.˜.Jšœ-ž$˜QJšœ œ˜&JšœH˜HJ˜ šœ˜Jšœ˜JšœI˜IJšœ<˜<šœœœ˜"šœœœ˜"Jšœœ˜Jšœœ6˜@Jšœ9˜9Jšœ9˜9Jšœ>˜>Jšœ˜Jšœ@˜@Jšœ˜—Jšœ˜ —JšœH˜HJšœ œ˜-Jšœ˜Jšœ˜—Jšœ œ˜J˜—Jšœ˜J˜Jšœ˜——…—r`ž