<> <> <> <> <> <> <<>> DIRECTORY Atom, Basics, Convert, G3dBasic, G3dEdgeBlt, G3dRender, G3dScanConvert, ImagerPixel, ImagerSample, PrincOps, PrincOpsUtils, Real, RealFns, Rope, SF, Terminal; G3dScanConvertImpl: CEDAR MONITOR IMPORTS Atom, Basics, Convert, G3dEdgeBlt, G3dRender, ImagerPixel, ImagerSample, PrincOpsUtils, Real, RealFns EXPORTS G3dScanConvert ~ BEGIN <> LORA: TYPE ~ LIST OF REF ANY; RefSeq: TYPE ~ G3dRender.RefSeq; ROPE: TYPE ~ Rope.ROPE; LongNumber: TYPE ~ Basics.LongNumber; BytePair: TYPE ~ Basics.BytePair; SampleMap: TYPE ~ ImagerSample.SampleMap; EdgeDesc: TYPE ~ G3dEdgeBlt.EdgeDesc; EdgeSequence: TYPE ~ G3dEdgeBlt.EdgeSequence; <> EdgeBltTable: TYPE ~ G3dEdgeBlt.EdgeBltTable; Context: TYPE ~ G3dRender.Context; RGB: TYPE ~ G3dRender.RGB; -- RECORD[ R, G, B: REAL] RGBSequence: TYPE ~ G3dRender.RGBSequence; NatRGB: TYPE ~ G3dRender.NatRGB; NatRGBSequence: TYPE ~ G3dRender.NatRGBSequence; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; Pixel: TYPE ~ G3dRender.Pixel; PixelPart: TYPE ~ G3dRender.PixelPart; Box: TYPE ~ SF.Box; Pair: TYPE ~ G3dRender.Pair; -- RECORD[x, y: REAL] IntegerPair: TYPE ~ G3dRender.IntegerPair; -- RECORD[x, y: INTEGER] IntegerPairSequence: TYPE ~ G3dRender.IntegerPairSequence; IntSequence: TYPE ~ G3dRender.IntSequence; IntSequenceRep: TYPE ~ G3dBasic.IntSequenceRep; RealSequence: TYPE ~ G3dRender.RealSequence; RealSequenceRep: TYPE ~ G3dBasic.RealSequenceRep; Patch: TYPE ~ G3dRender.Patch; Shape: TYPE ~ G3dRender.Shape; ShadingClass: TYPE ~ G3dRender.ShadingClass; Spot: TYPE ~ G3dRender.Spot; TextureFunction: TYPE ~ G3dRender.TextureFunction; TextureMap: TYPE ~ G3dRender.TextureMap; PixelBuffer: TYPE ~ ImagerPixel.PixelBuffer; PixelBufferProc: TYPE ~ G3dScanConvert.PixelBufferProc; LinkedPoly: TYPE ~ RECORD [ lftVtces, rgtVtces: LIST OF CARD16 ]; <> Round: PROC [REAL] RETURNS [INT] ~ Real.Round; Fix: PROC [REAL] RETURNS [INT] ~ Real.Fix; GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~ Atom.GetPropFromList; <> constantBitBltTable: PrincOps.BitBltTable _ [ -- set up constant values in BitBlt tables dst: [word: NULL, bit: 0], dstBpl: 0, src: [word: NULL, 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] ]; longZero: LongNumber ~ [lc[0]]; longOne: LongNumber ~ [lc[1]]; 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]]; white: Pixel ~ [255, 255, 255, 0, 0]; <> numEdgeSequences: NAT ~ 6; edgeSequenceCachePtr: NAT _ numEdgeSequences; edgeSequenceCache: ARRAY [0..numEdgeSequences) OF REF EdgeSequence _ ALL[NIL]; statistics: BOOLEAN _ FALSE; polyCount, avePixelsPerPoly, aveScanSegLength, avePolyHeight: INT _ 0; pixelsPerPolyHist: IntSequence; scanSegLengthHist: IntSequence; polyHeightHist: IntSequence; useTextureCoords: BOOLEAN _ FALSE; <> InitHistograms: PROCEDURE [maxScanSeg, maxHeight: NAT] ~ { pixelsPerPolyHist _ NEW[ IntSequenceRep[maxScanSeg * maxHeight] ]; pixelsPerPolyHist.length _ maxScanSeg * maxHeight; scanSegLengthHist _ NEW[ IntSequenceRep[maxScanSeg] ]; scanSegLengthHist.length _ maxScanSeg; polyHeightHist _ NEW[ IntSequenceRep[maxHeight] ]; polyHeightHist.length _ maxHeight; polyCount _ 0; }; ClearHistograms: PROCEDURE [] ~ { FOR i: NAT IN [0..pixelsPerPolyHist.length) DO pixelsPerPolyHist[i] _ 0; ENDLOOP; FOR i: NAT IN [0..scanSegLengthHist.length) DO scanSegLengthHist[i] _ 0; ENDLOOP; FOR i: NAT IN [0..polyHeightHist.length) DO polyHeightHist[i] _ 0; ENDLOOP; polyCount _ 0; }; ShowHistograms: PROCEDURE [context: Context] ~ { ShowData: PROC[startWd, startHt: REAL, numbers: IntSequence, label: ROPE] ~ { maxVal: INT _ 0; lastJ: INT _ 0; wSize: REAL _ context.viewPort.w; FOR i: INT IN [0 .. numbers.length) DO IF maxVal < numbers[i] THEN maxVal _ numbers[i]; ENDLOOP; FOR i: INT IN [0 .. numbers.length) DO numbers[i] _ Round[2048 * numbers[i] / maxVal]; -- scale to known max ENDLOOP; context.class.draw2DRope[ context, "0", [startWd -.025, startHt -.025], white, .025*wSize ]; context.class.draw2DRope[ context, Convert.RopeFromInt[maxVal], [startWd - .06, startHt + .2], white, .025*wSize ]; context.class.draw2DRope[ context, Convert.RopeFromInt[numbers.length], [startWd + 0.762, startHt - .025], white, .025*wSize ]; context.class.draw2DRope[context, label, [startWd + 0.1, startHt - .037], white, .025*wSize]; FOR i: INT IN [ 0 .. Round[wSize * 0.75] ) DO s: REAL _ i / (wSize * 0.75); -- pctge. across histogram j: INT _ Fix[ numbers.length * s ]; -- index into histogram value: REAL _ numbers[j]; numVals: REAL _ 0; IF j - lastJ > 1 THEN { FOR k: INT IN (lastJ .. j) DO value _ value + numbers[k]; IF numbers[k] > 0 THEN numVals _ numVals + 1; ENDLOOP; value _ value / MAX[numVals, 1]; }; s _ 0.75 * s; -- distance normalized to 3/4 screen context.class.draw2DLine[ context, [startWd+s, startHt], [startWd+s, startHt + ( value / (4*2048) )], [255,255,255,0,0] ]; lastJ _ j; ENDLOOP; }; maxScanSeg, maxPolyHeight, maxPixelsPerPoly, total, count, largest: INT _ 0; FOR i: NAT IN [0..pixelsPerPolyHist.length) DO IF pixelsPerPolyHist[i] > maxPixelsPerPoly THEN maxPixelsPerPoly _ pixelsPerPolyHist[i]; total _ total + i * pixelsPerPolyHist[i]; IF pixelsPerPolyHist[i] > 0 THEN largest _ i ENDLOOP; pixelsPerPolyHist.length _ largest+1; avePixelsPerPoly _ total / polyCount; ShowData[0.125, 0.1, pixelsPerPolyHist, "Polygon size"]; total _ largest _ count _ 0; FOR i: NAT IN [0..scanSegLengthHist.length) DO IF scanSegLengthHist[i] > maxScanSeg THEN maxScanSeg _ scanSegLengthHist[i]; total _ total + i * scanSegLengthHist[i]; count _ count + scanSegLengthHist[i]; IF scanSegLengthHist[i] > 0 THEN largest _ i ENDLOOP; scanSegLengthHist.length _ largest+1; aveScanSegLength _ total / count; ShowData[0.125, 0.4, scanSegLengthHist, "Scan segment length"]; total _ largest _ count _ 0; FOR i: NAT IN [0..polyHeightHist.length) DO IF polyHeightHist[i] > maxPolyHeight THEN maxPolyHeight _ polyHeightHist[i]; total _ total + i * polyHeightHist[i]; count _ count + polyHeightHist[i]; IF polyHeightHist[i] > 0 THEN largest _ i ENDLOOP; polyHeightHist.length _ largest+1; avePolyHeight _ total / count; ShowData[0.125, 0.7, polyHeightHist, "Polygon height"]; }; <> Swap: PROCEDURE [first, second: INTEGER] RETURNS [INTEGER, INTEGER] = { RETURN [second, first]; }; SGN: PROCEDURE [number: INTEGER] RETURNS [INTEGER] = INLINE { <> IF number > 0 THEN RETURN[1] ELSE IF number < 0 THEN RETURN[-1] ELSE RETURN[0]; }; InlineIncr: PROC[edge: EdgeDesc, array: REF EdgeSequence _ NIL] RETURNS[EdgeDesc] ~ INLINE { edge.val _ edge.val + edge.lngthIncr; edge.bias _ edge.bias - 2*edge.hiccups; IF edge.bias <= 0 THEN { edge.val _ edge.val + edge.hicIncr; edge.bias _ edge.bias + 2*edge.length; }; edge.stepsLeft _ edge.stepsLeft - 1; IF edge.stepsLeft <= 0 -- Get next linked edge if this one exhausted THEN IF edge.nextEdge # 0 THEN edge _ array[edge.nextEdge]; RETURN[edge]; }; 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 ]]]; }; GetEdgeSeq: ENTRY PROC[length: NAT] RETURNS[REF EdgeSequence] ~ { ENABLE UNWIND => NULL; edges: REF EdgeSequence _ NIL; IF edgeSequenceCachePtr > 0 THEN { edgeSequenceCachePtr _ edgeSequenceCachePtr - 1; edges _ edgeSequenceCache[edgeSequenceCachePtr]; edgeSequenceCache[edgeSequenceCachePtr] _ NIL; }; IF edges = NIL OR length > edges.maxLength THEN edges _ NEW[ EdgeSequence[length] ]; edges.length _ length; RETURN[ edges ]; }; ReleaseEdgeSeq: ENTRY PROC[edges: REF EdgeSequence] ~ { ENABLE UNWIND => NULL; IF edgeSequenceCachePtr < numEdgeSequences THEN { edgeSequenceCache[edgeSequenceCachePtr] _ edges; FOR i: NAT IN [0..edgeSequenceCachePtr) DO IF edges = edgeSequenceCache[i] THEN SIGNAL G3dRender.Error[$Fatal, "Multiple release of EdgeSeq"]; ENDLOOP; edgeSequenceCachePtr _ edgeSequenceCachePtr + 1; }; }; Sqr: PROCEDURE [number: INT16] RETURNS [INT32] ~ INLINE { RETURN[ number * INT32[number] ]; }; Log2: PROC [n: INT] RETURNS [lg: NAT _ 0] ~ { -- finds log base 2 of input (from M. Plass) nn: CARD32 ~ n; k: CARD32 _ 1; UNTIL k=0 OR k>= nn DO lg _ lg + 1; k _ k + k; ENDLOOP; }; Power: PUBLIC PROC[ value: CARD16, power: NAT] RETURNS[ result: CARD16 ] ~ { binaryCount: NAT _ 2*power; -- makes highlights same size as those by ShadePt temp: CARD32 _ 65536; val: CARD32 _ value; IF power = 0 THEN RETURN[65535]; WHILE binaryCount > 0 DO -- compute power by repeated squares IF Basics.BITAND[binaryCount, 1] = 1 THEN temp _ Basics.HighHalf[temp * val]; val _ Basics.HighHalf[val * val]; binaryCount _ binaryCount/2; ENDLOOP; result _ temp; }; SampleMapBase: PROC[samples: SampleMap] RETURNS[CARD32, WORD] ~ { bytesPerLine: WORD; baseByteAddr: CARD32; WITH samples SELECT FROM raster: ImagerSample.RasterSampleMap => { base: PrincOps.BitAddress _ ImagerSample.GetBase[raster]; baseByteAddr _ CARD32[ Basics.DoubleShift[LOOPHOLE[base.word], 1].li + Basics.BITSHIFT[base.bit, -3] ]; bytesPerLine _ Basics.BITSHIFT[ImagerSample.GetBitsPerLine[raster], -3]; }; ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "only RasterSampleMaps here"]; RETURN[ baseByteAddr, bytesPerLine]; }; DoForPixelColors: PROC[ context: Context, action: PROC[dstAddr: CARD32, xStep, yStep: WORD, element: PixelPart] ] ~ { base: CARD32; bpl: WORD; depth, alpha: REF NAT; SELECT context.class.displayType FROM $FullColor => { [base, bpl] _ SampleMapBase[context.pixels[0]]; action[base, 1, bpl, r]; [base, bpl] _ SampleMapBase[context.pixels[1]]; action[base, 1, bpl, g]; [base, bpl] _ SampleMapBase[context.pixels[2]]; action[base, 1, bpl, b]; }; $PseudoColor, $Gray => { [base, bpl] _ SampleMapBase[context.pixels[0]]; action[base, 1, bpl, r]; }; ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Unexpected display type"]; alpha _ NARROW[ GetProp[context.displayProps, $Alpha] ]; IF alpha # NIL THEN { [base, bpl] _ SampleMapBase[context.pixels[alpha^]]; action[base, 2, bpl, a]; }; depth _ NARROW[ GetProp[context.displayProps, $Depth] ]; IF depth # NIL THEN { [base, bpl] _ SampleMapBase[context.pixels[depth^]]; action[base, 2, bpl, z]; }; }; ShowCoords: PROC[poly: REF Patch] RETURNS[list: LIST OF REF IntegerPair] ~ { FOR i: CARD16 DECREASING IN [0..poly.nVtces) DO p: REF IntegerPair _ NEW[IntegerPair _ [Round[poly[i].coord.sx], Round[poly[i].coord.sy]]]; list _ CONS[p, list]; ENDLOOP; RETURN[list]; }; MappedRGB: PUBLIC PROC[renderMode: ATOM, clr: RGB] RETURNS[NAT] ~ { SELECT renderMode FROM $Dithered => { mapVal: NAT _ Fix[clr.R*4.999]*24 + Fix[clr.G*5.999]*4 + Fix[clr.B*3.999]; IF mapVal >= 60 THEN mapVal _ mapVal + 135; -- move to top of map RETURN[ mapVal ]; }; $PseudoColor => RETURN[ Fix[clr.R*5.999]*42 + Fix[clr.G*6.999]*6 + Fix[clr.B*5.999] + 2 ]; ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Bad Render Mode"]; 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 G3dRender.Error[$MisMatch, "Bad Render Mode"]; RETURN[[255, 255, 255]]; }; <> DoWithPixels: PUBLIC PROC [ context: Context, start: IntegerPair, length: NAT, proc: PixelBufferProc ] ~ { pixels: PixelBuffer _ ImagerPixel.ObtainScratchPixels[ context.pixels.samplesPerPixel, length ]; fMin: INTEGER _ context.pixels.box.min.f; sMin: INTEGER _ context.pixels.box.min.s; initIndex: SF.Vec _ [ f: MAX[fMin, start.x + fMin], s: start.y + sMin ]; delta: SF.Vec _ [ f: 1, s: 0 ]; ImagerPixel.GetPixels[ -- get pixels for segment self: context.pixels, pixels: pixels, initIndex: initIndex, delta: delta, count: length ]; proc[pixels]; -- call back with requested pixels ImagerPixel.PutPixels[ -- return modified pixels self: context.pixels, pixels: pixels, initIndex: initIndex, delta: delta, count: length ]; ImagerPixel.ReleaseScratchPixels[pixels]; }; Write: PROC [ dstVal, srcVal: INT32 ] ~ INLINE { dstAddr: LONG POINTER _ LOOPHOLE[Basics.DoubleShiftRight[LOOPHOLE[dstVal], 1]]; srcValue: BYTE _ LOOPHOLE[srcVal, LongNumber.bytes].ll; -- lo byte, lo half of long IF Basics.DoubleAnd[LOOPHOLE[dstVal], longOne] # longZero -- lo byte? (right pixel) THEN TRUSTED { LOOPHOLE[dstAddr^, BytePair].low _ srcValue; } -- right pixel ELSE TRUSTED { LOOPHOLE[dstAddr^, BytePair].high _ srcValue; }; -- left pixel }; Dither: PROC [ dst, r, g, b, scanline: INT32 ] ~ { <> dstAddr: LONG POINTER TO INT16 _ LOOPHOLE[Basics.DoubleShiftRight[LOOPHOLE[dst], 1]]; srcValue: BYTE; red: INT16 _ LOOPHOLE[r, LongNumber.bytes].ll; -- lowest byte grn: INT16 _ LOOPHOLE[g, LongNumber.bytes].ll; -- of longword blu: INT16 _ LOOPHOLE[b, LongNumber.bytes].ll; y: NAT _ Basics.BITAND[scanline, 3]; x: NAT _ Basics.BITAND[Basics.LowHalf[dst], 3]; threshold: NAT _ ditherTable[x][y]; -- calculate x and y table coordinates (4 x 4 table) 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, val2G, val2B: NAT; 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; srcValue _ 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 IF Basics.DoubleAnd[LOOPHOLE[dst], longOne] # longZero -- lo byte? (right pixel) THEN TRUSTED { LOOPHOLE[dstAddr^, BytePair].low _ srcValue; } -- right pixel ELSE TRUSTED { LOOPHOLE[dstAddr^, BytePair].high _ srcValue; }; -- left pixel }; Mat: PROC [ rDst, gDst, bDst, aDst, r, g, b, a: INT32 ] ~ { }; DepthLess: PROC [ zDst, zSrc: INT32 ] RETURNS [ BOOLEAN ] ~ TRUSTED { -- INLINE dstAddr: LONG POINTER TO CARD16 _ LOOPHOLE[ Basics.DoubleShiftRight[LOOPHOLE[zDst], 1]]; IF dstAddr^ < LOOPHOLE[zSrc, LongNumber.pair].lo THEN RETURN[FALSE] -- old value closer ELSE { dstAddr^ _ LOOPHOLE[zSrc, LongNumber.pair].lo; RETURN[TRUE]; }; }; <> PutLine: PUBLIC PROC [context: Context, p1, p2: IntegerPair, color1, color2: Pixel ] ~ { DoLine: PROC[dstAddr: CARD32, xStep, yStep: WORD, element: PixelPart] ~ { clrValue1: CARD16 _ SELECT element FROM r => color1[r], g => color1[g], b => color1[b], a => color1[a], z => color1[z], ENDCASE => ERROR; clrValue2: CARD16 _ SELECT element FROM r => color2[r], g => color2[g], b => color2[b], a => color2[a], z => color2[z], ENDCASE => ERROR; src, dst: G3dEdgeBlt.EdgeDesc; dstAddr _ dstAddr + p1.y * INT[yStep] + p1.x; -- from base to line endpt IF tall THEN dst _ [ val: dstAddr, length: ABS[height], hiccups: ABS[width], lngthIncr: yStep * SGN[height], hicIncr: xStep * SGN[width] ] ELSE dst _ [ val: dstAddr, length: ABS[width], hiccups: ABS[height], lngthIncr: xStep * SGN[width], hicIncr: yStep * SGN[height] ]; IF clrValue1 = clrValue2 OR dst.length = 0 THEN src _ [ val: clrValue1, length: 0, indirect: FALSE ] -- constant shade along line ELSE { clrDiff: INTEGER _ INTEGER[clrValue2] - INTEGER[clrValue1]; -- varying shade src.val _ clrValue1; src.length _ dst.length; [src.lngthIncr, src.hiccups] _ Basics.DivMod[ABS[clrDiff], dst.length]; IF clrDiff < 0 -- color moving negatively? THEN { src.lngthIncr _ -src.lngthIncr; src.hicIncr _ -1; } ELSE src.hicIncr _ 1; src.indirect _ FALSE; }; G3dEdgeBlt.Blt[[dst, src]]; -- draw line including endpoints }; box: Box _ context.pixels.box; checkLimits: NAT; tall: BOOLEAN _ FALSE; length, hiccups: NAT; width: INTEGER _ p2.x - p1.x; height: INTEGER _ p2.y - p1.y; IF ABS[width] > ABS[height] THEN { length _ ABS[width]; hiccups _ ABS[height]; tall _ FALSE; } ELSE { length _ ABS[height]; hiccups _ ABS[width]; tall _ TRUE; }; box _ [ [0, 0], [box.max.s - box.min.s - 1, box.max.f - box.min.f - 1] ]; -- normalize box checkLimits _ p1.x-box.min.f; checkLimits _ p2.x-box.min.f; -- raise bounds error if < 0 checkLimits _ p1.y-box.min.s; checkLimits _ p2.y-box.min.s; checkLimits _ box.max.f-p1.x; checkLimits _ box.max.f-p2.x; checkLimits _ box.max.s-p1.y; checkLimits _ box.max.s-p2.y; DoForPixelColors[context, DoLine]; }; <> MakeDstEdge: PROC[bot, top: CtlPoint, dstAddr: CARD32, xStep, yStep: WORD] RETURNS[edge: EdgeDesc] ~ { <> pLo: IntegerPair _ [ Round[bot.sx], Round[bot.sy] ]; pHi: IntegerPair _ [ Round[top.sx], Round[top.sy] ]; width: INTEGER _ pHi.x - pLo.x; length: INTEGER _ pHi.y - pLo.y; IF length <= 0 THEN { edge.length _ 0; RETURN[edge]; }; edge.val _ dstAddr + pLo.y * INT[yStep] + pLo.x * INT[xStep]; -- from base to edge bottom edge.length _ length; edge.hiccups _ ABS[width]; [edge.lngthIncr, edge.hiccups] _ Basics.DivMod[ABS[width], edge.length]; IF width < 0 -- x-position moving negatively? THEN { edge.lngthIncr _ -edge.lngthIncr; edge.hicIncr _ -xStep; } ELSE edge.hicIncr _ xStep; edge.lngthIncr _ edge.lngthIncr * xStep + yStep; -- add in scanline to scanline step size edge.bias _ edge.length; -- initial bias to center hiccups edge.stepsLeft _ edge.length; -- set stepsLeft for count down }; MakeSrcEdge: PROC[bot, top: CtlPtInfo, element: PixelPart] RETURNS[edge: EdgeDesc] ~ { <> yStart: CARD16 _ Round[bot.coord.sy]; yEnd: CARD16 _ Round[top.coord.sy]; length: INTEGER _ INTEGER[yEnd] - INTEGER[yStart]; srcStart, srcEnd: CARD16; width: INT16; IF length <= 0 THEN { edge.length _ 0; RETURN[edge]; }; SELECT element FROM r => { srcStart _ Fix[bot.shade.er * 255.0]; srcEnd _ Fix[top.shade.er * 255.0]; }; g => { srcStart _ Fix[bot.shade.eg * 255.0]; srcEnd _ Fix[top.shade.eg * 255.0]; }; b => { srcStart _ Fix[bot.shade.eb * 255.0]; srcEnd _ Fix[top.shade.eb * 255.0]; }; a => { srcStart _ Fix[bot.shade.et * 255.0]; srcEnd _ Fix[top.shade.et * 255.0]; }; z => { srcStart _ Round[bot.coord.sz]; srcEnd _ Round[top.coord.sz]; }; ENDCASE => ERROR; width _ srcEnd - INT32[srcStart]; edge.val _ srcStart; -- initial value edge.length _ length; edge.hiccups _ ABS[width]; [edge.lngthIncr, edge.hiccups] _ Basics.DivMod[ABS[width], edge.length]; IF width < 0 -- color moving negatively? THEN { edge.lngthIncr _ -edge.lngthIncr; edge.hicIncr _ -1; } ELSE edge.hicIncr _ 1; edge.bias _ edge.length; -- initial bias to center hiccups edge.stepsLeft _ edge.length; -- set stepsLeft for count down }; LinkEdges: PROC [ poly: REF Patch, box: Box] RETURNS [LinkedPoly] ~ { <> checkLimits: NAT; lft, rgt: LIST OF CARD16 _ NIL; least, most: REAL _ poly[0].coord.sy; topVtx, botVtx, vtx: CARD16 _ 0; box _ [ [0, 0], [box.max.s - box.min.s - 1, box.max.f - box.min.f - 1] ]; -- normalize box FOR i: CARD16 IN [1..poly.nVtces) DO -- find top and bottom vertex p: IntegerPair _ [ Round[poly[i].coord.sx], Round[poly[i].coord.sy] ]; checkLimits _ p.y-box.min.s; checkLimits _ p.x-box.min.f; -- check bounds checkLimits _ box.max.s-p.y; checkLimits _ box.max.f-p.x; IF poly[i].coord.sy < least THEN { least _ poly[i].coord.sy; botVtx _ i; } ELSE IF poly[i].coord.sy > most THEN { most _ poly[i].coord.sy; topVtx _ i; }; ENDLOOP; IF Fix[most] > Fix[least] THEN { vtx _ topVtx; lft _ CONS[topVtx, lft]; WHILE vtx # botVtx DO -- walk down left side vtx _ vtx + 1; IF vtx = poly.nVtces THEN vtx _ 0; lft _ CONS[ vtx, lft ]; ENDLOOP; vtx _ topVtx; rgt _ CONS[topVtx, rgt]; WHILE vtx # botVtx DO -- walk down right side vtx _ IF vtx = 0 THEN poly.nVtces - 1 ELSE vtx - 1; rgt _ CONS[vtx, rgt]; ENDLOOP; }; RETURN[ [lft, rgt] ]; }; LinkDst: PROC[ dst: REF EdgeSequence, links: LIST OF CARD16, plygn: REF Patch, ptr, size: NAT, dstAddr: CARD32, xStep, yStep: WORD] ~ { FOR list: LIST OF CARD16 _ links, list.rest UNTIL list.rest = NIL DO dst[ptr] _ MakeDstEdge[ plygn[list.first].coord, plygn[list.rest.first].coord, dstAddr, xStep, yStep ]; IF dst[ptr].length > 0 THEN { IF ptr - size >= 0 THEN dst[ptr-size].nextEdge _ ptr; -- link to last edge ptr _ ptr + size; }; ENDLOOP; }; LinkSrc: PROC[ src: REF EdgeSequence, links: LIST OF CARD16, plygn: REF Patch, ptr, size: NAT, element: PixelPart] ~ { FOR list: LIST OF CARD16 _ links, list.rest UNTIL list.rest = NIL DO src[ptr] _ MakeSrcEdge[ plygn[list.first], plygn[list.rest.first], element ]; IF src[ptr].length > 0 THEN { IF ptr - size >= 0 THEN src[ptr-size].nextEdge _ ptr; -- link to last edge ptr _ ptr + size; }; ENDLOOP; }; FastFlatTiler: PUBLIC PROC [context: Context, plygn: REF Patch, color: Pixel] ~ { DoPoly: PROC[dstAddr: CARD32, xStep, yStep: WORD, element: PixelPart] ~ { lft: REF EdgeSequence _ GetEdgeSeq[plygn.nVtces]; rgt: REF EdgeSequence _ GetEdgeSeq[plygn.nVtces]; clrValue: CARD16 _ SELECT element FROM r => color[r], g => color[g], b => color[b], z => color[z], ENDCASE => ERROR; IF element # z -- load high half of word to make brick for bitblt THEN LOOPHOLE[clrValue, Basics.ShortNumber].hi _ clrValue; LinkDst[lft, links.lftVtces, plygn, 0, 1, dstAddr, xStep, yStep]; -- chains for g3dEdgeBlt LinkDst[rgt, links.rgtVtces, plygn, 0, 1, dstAddr, xStep, yStep]; IF lft[0].length # 0 AND rgt[0].length # 0 THEN TRUSTED { -- not a zero-height poly bb^ _ constantBitBltTable; -- set up constant values in BitBlt table bb.dstBpl _ Basics.BITSHIFT[yStep, 3]; bb.src.word _ @clrValue; WHILE TRUE DO -- write scan segment with bitblt, then bump to next edge width: INT16 _ rgt[0].val - lft[0].val; IF width < 0 OR width > INT16[yStep] THEN width _ 0; -- twisted polygon bb.dst _ [word: LOOPHOLE[Basics.DoubleShiftRight[LOOPHOLE[lft[0].val], 1]], bit: Basics.BITSHIFT[ Basics.BITAND[ Basics.LowHalf[lft[0].val], 1], 3] ]; bb.width _ Basics.BITSHIFT[width + 1, 3]; -- convert from bytes to bits PrincOpsUtils.BITBLT[bb]; -- do bitblt lft[0] _ InlineIncr[lft[0], lft]; rgt[0] _ InlineIncr[rgt[0], rgt]; -- incr. edges IF lft[0].stepsLeft = 0 OR rgt[0].stepsLeft = 0 THEN EXIT ENDLOOP; }; ReleaseEdgeSeq[lft]; ReleaseEdgeSeq[rgt]; }; links: LinkedPoly; bbspace: PrincOps.BBTableSpace; -- get BitBlt table bb: PrincOps.BitBltTablePtr; TRUSTED { bb _ PrincOpsUtils.AlignedBBTable[@bbspace]; }; SELECT context.class.displayType FROM $PseudoColor => color[r] _ 42 * (color[r] * 6 / 256) + 6 * (color[g] * 7 / 256) + (color[b] * 6 / 256) +2; $Gray => color[r] _ (color[r] + color[g] + color[b]) / 3; ENDCASE; links _ LinkEdges[ plygn, context.pixels.box ]; -- link vertices into left and right side chains IF links.lftVtces # NIL THEN DoForPixelColors[context, DoPoly]; }; ScanPoly: PROC [context: Context, lftDst, lftSrc, rgtDst, rgtSrc: REF EdgeSequence, dstSize, srcSize, dstDpth, srcDpth, scanline: NAT, pixelProc: PROC[scanDst, scanSrc: REF EdgeSequence, scanline: NAT] _ NIL] ~ { <> polyHeight, pixelCount: NAT _ 0; dithering: BOOLEAN _ IF context.class.displayType = $PseudoColor THEN TRUE ELSE FALSE; clrsPerPixel: NAT _ IF context.class.displayType = $FullColor THEN 3 ELSE 1; scanSrc: REF EdgeSequence _ GetEdgeSeq[srcSize]; scanDst: REF EdgeSequence _ GetEdgeSeq[dstSize]; IF lftDst[0].length # 0 AND rgtDst[0].length # 0 THEN WHILE TRUE DO segLength: INT16 _ rgtDst[0].val - lftDst[0].val + 1; IF segLength <= 0 OR segLength > lftDst[0].lngthIncr THEN EXIT; -- twisted or backfacing FOR i: NAT IN [0..dstSize) DO -- build scan segment destinations scanDst[i] _ [ val: lftDst[i].val, length: segLength, lngthIncr: ABS[lftDst[i].hicIncr] ]; scanDst[i].bias _ scanDst[i].stepsLeft _ segLength; ENDLOOP; FOR i: NAT IN [0..srcSize) DO -- build scan segment sources width: INT16 _ rgtSrc[i].val - lftSrc[i].val; scanSrc[i] _ [val: lftSrc[i].val, length: segLength, indirect: FALSE ]; [scanSrc[i].lngthIncr, scanSrc[i].hiccups] _ Basics.DivMod[ABS[width], segLength]; IF width < 0 -- color/z moving negatively? THEN { scanSrc[i].lngthIncr _ -scanSrc[i].lngthIncr; scanSrc[i].hicIncr _ -1; } ELSE scanSrc[i].hicIncr _ 1; scanSrc[i].bias _ scanSrc[i].stepsLeft _ segLength; ENDLOOP; IF statistics THEN { -- gather statistics on polygon sizes polyHeight _ polyHeight + 1; pixelCount _ pixelCount + segLength; IF CARD16[segLength] >= scanSegLengthHist.length THEN segLength _ scanSegLengthHist.length - 1; -- catch overflowing values scanSegLengthHist[segLength] _ scanSegLengthHist[segLength] + 1; -- histogram }; WHILE TRUE DO -- Do scan segment, pixel by pixel IF NOT context.depthBuffering OR DepthLess[scanDst[dstDpth].val, scanSrc[srcDpth].val] THEN { -- test and put z IF pixelProc # NIL THEN { pixelProc[scanDst, scanSrc, scanline]; IF context.stopMe^ THEN RETURN; } ELSE IF dithering THEN Dither[ scanDst[0].val, scanSrc[0].val, scanSrc[1].val, scanSrc[2].val, scanline ] ELSE FOR i: NAT IN[0..clrsPerPixel) DO Write[scanDst[i].val, scanSrc[i].val]; ENDLOOP; }; FOR i: NAT IN [0..srcSize) DO scanSrc[i] _ InlineIncr[scanSrc[i]]; ENDLOOP; FOR i: NAT IN [0..dstSize) DO scanDst[i] _ InlineIncr[scanDst[i]]; ENDLOOP; IF scanDst[0].stepsLeft = 0 THEN EXIT ENDLOOP; FOR i: NAT IN [0..srcSize) DO lftSrc[i] _ InlineIncr[lftSrc[i], lftSrc]; rgtSrc[i] _ InlineIncr[rgtSrc[i], rgtSrc]; ENDLOOP; FOR i: NAT IN [0..dstSize) DO lftDst[i] _ InlineIncr[lftDst[i], lftDst]; rgtDst[i] _ InlineIncr[rgtDst[i], rgtDst]; ENDLOOP; IF lftDst[0].stepsLeft = 0 OR rgtDst[0].stepsLeft = 0 THEN EXIT; scanline _ scanline + 1; ENDLOOP; ReleaseEdgeSeq[scanSrc]; ReleaseEdgeSeq[scanDst]; IF statistics THEN { -- gather statistics on polygon sizes polyCount _ polyCount + 1; IF polyHeight >= polyHeightHist.length THEN polyHeight _ polyHeightHist.length - 1; polyHeightHist[polyHeight] _ polyHeightHist[polyHeight] + 1; IF pixelCount >= pixelsPerPolyHist.length THEN pixelCount _ pixelsPerPolyHist.length - 1; pixelsPerPolyHist[pixelCount] _ pixelsPerPolyHist[pixelCount] + 1; }; }; LerpTiler: PUBLIC PROC [context: Context, plygn: REF Patch] ~ { lftSrc, rgtSrc, lftDst, rgtDst: REF EdgeSequence _ NIL; scanline, srcSize, dstSize, dstDpth, srcDpth, srcPos: NAT _ 0; srcTmplate: Pixel _ IF context.class.displayType = $Gray THEN [1,0,0,0,0] ELSE [1,1,1,0,0]; dstTmplate: Pixel _ IF context.class.displayType = $FullColor THEN [1,1,1,0,0] ELSE [1,0,0,0,0]; dstBufPos: Pixel _ [0,1,2,0,0]; alpha: REF NAT _ NARROW[ GetProp[context.displayProps, $Alpha]]; depth: REF NAT _ NARROW[ GetProp[context.displayProps, $Depth]]; links: LinkedPoly; IF alpha # NIL THEN { dstBufPos[a] _ alpha^; srcTmplate[a] _ dstTmplate[a] _ 2; }; IF depth # NIL THEN { dstBufPos[z] _ depth^; srcTmplate[z] _ dstTmplate[z] _ 2; }; FOR i: PixelPart IN PixelPart DO IF srcTmplate[i] > 0 THEN srcSize _ srcSize + 1; IF dstTmplate[i] > 0 THEN dstSize _ dstSize + 1; ENDLOOP; IF depth # NIL THEN { dstDpth _ dstSize-1; srcDpth _ srcSize-1; }; links _ LinkEdges[ plygn, context.pixels.box ]; -- link vertices into left and right side chains IF links.lftVtces # NIL THEN { -- if non-zero height scanline _ Fix[plygn[links.lftVtces.first].coord.sy]; -- get first scanline lftSrc _ GetEdgeSeq[srcSize*plygn.nVtces]; rgtSrc _ GetEdgeSeq[srcSize*plygn.nVtces]; lftDst _ GetEdgeSeq[dstSize*plygn.nVtces]; rgtDst _ GetEdgeSeq[dstSize*plygn.nVtces]; FOR i: PixelPart IN PixelPart DO --Get Edge records for left and right sides IF dstTmplate[i] > 0 THEN { dstAddr, yStep: CARD32; [dstAddr, yStep] _ SampleMapBase[context.pixels[dstBufPos[i]]]; LinkDst[ lftDst, links.lftVtces, plygn, dstBufPos[i], dstSize, dstAddr, dstTmplate[i], yStep ]; LinkDst[ rgtDst, links.rgtVtces, plygn, dstBufPos[i], dstSize, dstAddr, dstTmplate[i], yStep ]; }; IF srcTmplate[i] > 0 THEN { LinkSrc[lftSrc, links.lftVtces, plygn, srcPos, srcSize, i ]; LinkSrc[rgtSrc, links.rgtVtces, plygn, srcPos, srcSize, i ]; srcPos _ srcPos + 1; }; ENDLOOP; ScanPoly[context, lftDst, lftSrc, rgtDst, rgtSrc, dstSize, srcSize, dstDpth, srcDpth, scanline]; ReleaseEdgeSeq[lftSrc]; ReleaseEdgeSeq[rgtSrc]; ReleaseEdgeSeq[lftDst]; ReleaseEdgeSeq[ rgtDst]; }; }; justNoticeable: NAT ~ Round[G3dScanConvert.justNoticeable * 65536]; spot: REF Spot _ NIL; HiliteTiler: PUBLIC PROC [context: Context, plygn: REF Patch, shininess: NAT] ~ { SimpleTexture: PROC[ red, grn, blu: CARD16, map: ImagerPixel.PixelMap, type: ATOM, ix, iy: INTEGER ] RETURNS [ newRed, newGrn, newBlu: CARD16 ] ~ { GetTxtrAddress: PROC[buf: ImagerPixel.PixelMap, ix, iy: INTEGER] RETURNS[ txtrX, txtrY: INTEGER ] ~ { x: REAL _ 1.0 * ix / (LAST[NAT]/32); y: REAL _ 1.0 * iy / (LAST[NAT]/32); bufWidth: NAT _ buf.box.max.f - buf.box.min.f; bufHeight: NAT _ buf.box.max.s - buf.box.min.s; txtrX _ Fix[x * bufWidth] MOD bufWidth; IF txtrX < 0 THEN txtrX _ txtrX + bufWidth; txtrY _ Fix[y * bufHeight] MOD bufHeight; IF txtrY < 0 THEN txtrY _ txtrY + bufHeight; }; pixel: ImagerPixel.PixelBuffer _ ImagerPixel.ObtainScratchPixels[map.samplesPerPixel, 1]; x, y: INTEGER; [x, y] _ GetTxtrAddress[map, ix, iy]; ImagerPixel.GetPixels[self: map, pixels: pixel, initIndex: [f: x, s: y+map.box.min.s], count: 1]; SELECT type FROM $Color => { red _ red * pixel[0][0] / 256; grn _ grn * pixel[1][0] / 256; blu _ blu * pixel[2][0] / 256; }; $Intensity => { red _ red * pixel[0][0] / 256; grn _ grn * pixel[0][0] / 256; blu _ blu * pixel[0][0] / 256; }; ENDCASE => SIGNAL G3dRender.Error[ $MisMatch, "Unknown texture type, or antialiasing needed" ]; ImagerPixel.ReleaseScratchPixels[pixel]; RETURN[red, grn, blu]; }; TextureFn: PROC[ red, grn, blu: CARD16, txtrFn: REF TextureFunction, x, y, z, dx, dy, dz: INT ] RETURNS [ newRed, newGrn, newBlu: CARD16 ] ~ { <> IF spot = NIL THEN { spot _ NEW[Spot]; spot.val _ NEW[RealSequenceRep[6]]; spot.val.length _ 6; spot.yIncr _ NEW[RealSequenceRep[6]]; spot.yIncr.length _ 6; spot.xIncr _ NEW[RealSequenceRep[6]]; spot.xIncr.length _ 6; spot.yIncr[3] _ spot.yIncr[4] _ spot.yIncr[5] _ 0.0; spot.xIncr[3] _ spot.xIncr[4] _ spot.xIncr[5] _ 0.0; }; spot.val[0] _ red/255.0; spot.val[1] _ grn/255.0; spot.val[2] _ blu/255.0; spot.val[3] _ 1.0 * x / (LAST[NAT]/32); spot.val[4] _ 1.0 * y / (LAST[NAT]/32); spot.val[5] _ 1.0 * z / (LAST[NAT]/32); <> <<{ dx _ ABS[spot.val[3] - lx]; dy _ ABS[spot.val[4] - ly]; dz _ ABS[spot.val[5] - lz]; }; >> <> spot.xIncr[3] _ (1.0 * dx / (LAST[NAT]/32)) / (LAST[NAT]/32); spot.xIncr[4] _ (1.0 * dy / (LAST[NAT]/32)) / (LAST[NAT]/32); spot.xIncr[5] _ (1.0 * dz / (LAST[NAT]/32)) / (LAST[NAT]/32); txtrFn.proc[context, plygn.renderData.shadingClass, spot, txtrFn.props]; RETURN[ Real.Round[spot.val[0]*255], Real.Round[spot.val[1]*255], Real.Round[spot.val[2]*255] ]; }; Hilight: PROC[ scanDst, scanSrc: REF EdgeSequence, scanline: NAT ] ~ { red, grn, blu: CARD16 _ 0; hltRed, hltGrn, hltBlu: CARD16 _ 0; reflStart: NAT _ srcSize - hltCnt * 2; hltStart: NAT _ 0; noticeableHilite: BOOLEAN _ FALSE; red _ scanSrc[0].val; IF NOT gray THEN { -- if gray, scanSrc[1].val, scanSrc[2].val are not grn & blu grn _ scanSrc[1].val; blu _ scanSrc[2].val; }; IF texture THEN { WITH plygn.renderData.shadingClass.texture.first SELECT FROM -- only one texture txtrMap: REF TextureMap => -- modify with mapped texture WITH txtrMap.pixels SELECT FROM buf: ImagerPixel.PixelMap => [red, grn, blu] _ SimpleTexture[ red, grn, blu, buf, txtrMap.type, scanSrc[reflStart-3].val, scanSrc[reflStart-2].val ]; ENDCASE => G3dRender.Error[$MisMatch, "Only simple mapped texture"]; txtrFn: REF TextureFunction => { -- solid or other function-based texture j: NAT _ reflStart-3; s: INT _ LAST[NAT]/32; [red, grn, blu] _ TextureFn[ red, grn, blu, txtrFn, scanSrc[j].val, scanSrc[j+1].val, scanSrc[j+2].val, s*scanSrc[j].lngthIncr + s*(scanSrc[j].hicIncr * scanSrc[j].hiccups)/scanSrc[j].length, s*scanSrc[j+1].lngthIncr + s*(scanSrc[j+1].hicIncr * scanSrc[j+1].hiccups)/scanSrc[j+1].length, s*scanSrc[j+2].lngthIncr + s*(scanSrc[j+2].hicIncr * scanSrc[j+2].hiccups)/scanSrc[j+2].length ]; }; ENDCASE => SIGNAL G3dRender.Error[$Unimplemented, "Unexpected texture type"]; }; FOR i: NAT IN [hltStart..hltCnt) DO j: NAT _ reflStart + i * 2; sqrX: INT32 _ Sqr[ scanSrc[j].val*2 ]; -- scale to full card range from half integer sqrY: INT32 _ Sqr[ scanSrc[j+1].val*2 ]; pctHilite: CARD32 _ Power[ 2 * Basics.HighHalf[LAST[INT32] - (sqrX+sqrY)], shininess ]; IF pctHilite > justNoticeable THEN { -- Scale light color by hilite strength hltRed _ MIN[ hltRed + CARD32[Basics.HighHalf[pctHilite * lightColor[i].r]], 65535 ]; hltGrn _ MIN[ hltGrn + CARD32[Basics.HighHalf[pctHilite * lightColor[i].g]], 65535 ]; hltBlu _ MIN[ hltBlu + CARD32[Basics.HighHalf[pctHilite * lightColor[i].b]], 65535 ]; noticeableHilite _ TRUE; }; ENDLOOP; IF noticeableHilite THEN { hltRed _ Basics.HighByte[hltRed]; hltGrn _ Basics.HighByte[hltGrn]; hltBlu _ Basics.HighByte[hltBlu]; IF gray THEN hltRed _ (hltRed + hltGrn + hltBlu) / 3; red _ MIN[ red + Basics.HighByte[(255 - red) * 2 * hltRed], 255 ]; IF NOT gray THEN { grn _ MIN[ grn + Basics.HighByte[(255 - grn) * 2 * hltGrn], 255 ]; blu _ MIN[ blu + Basics.HighByte[(255 - blu) * 2 * hltBlu], 255 ]; }; }; IF dithering THEN Dither[ scanDst[0].val, red, grn, blu, scanline ] ELSE IF gray THEN Write[scanDst[0].val, red] ELSE { Write[scanDst[0].val, red]; Write[scanDst[1].val, grn]; Write[scanDst[2].val, blu]; }; }; lftSrc, rgtSrc, lftDst, rgtDst: REF EdgeSequence _ NIL; scanline, srcSize, dstSize, dstDpth, srcDpth, srcPos: NAT _ 0; lx, ly, lz: REAL _ 0.0; -- for texture functions srcTmplate: Pixel _ IF context.class.displayType = $Gray THEN [1,0,0,0,0] ELSE [1,1,1,0,0]; dstTmplate: Pixel _ IF context.class.displayType = $FullColor THEN [1,1,1,0,0] ELSE [1,0,0,0,0]; dstBufPos: Pixel _ [0,1,2,0,0]; alpha: REF NAT _ NARROW[ GetProp[context.displayProps, $Alpha] ]; depth: REF NAT _ NARROW[ GetProp[context.displayProps, $Depth] ]; dithering: BOOLEAN _ IF context.class.displayType = $PseudoColor THEN TRUE ELSE FALSE; gray: BOOLEAN _ IF context.class.displayType = $Gray THEN TRUE ELSE FALSE; texture: BOOLEAN _ IF plygn.renderData.shadingClass.texture # NIL THEN TRUE ELSE FALSE; lightColor: NatRGBSequence _ NARROW[ GetProp[plygn.props, $LightColors] ]; hltCnt: NAT _ IF lightColor # NIL THEN lightColor.length ELSE 0; links: LinkedPoly; IF alpha # NIL THEN { dstBufPos[a] _ alpha^; srcTmplate[a] _ dstTmplate[a] _ 2; }; IF depth # NIL THEN { dstBufPos[z] _ depth^; srcTmplate[z] _ dstTmplate[z] _ 2; }; FOR i: PixelPart IN PixelPart DO IF srcTmplate[i] > 0 THEN srcSize _ srcSize + 1; IF dstTmplate[i] > 0 THEN dstSize _ dstSize + 1; ENDLOOP; IF depth # NIL THEN { dstDpth _ dstSize-1; srcDpth _ srcSize-1; }; IF texture THEN srcSize _ srcSize + 3; -- make room for texure coordinates srcSize _ srcSize + hltCnt * 2; -- reflection vector for each hilight-causing light source links _ LinkEdges[ plygn, context.pixels.box ]; -- link vertices into left and right side chains IF links.lftVtces # NIL THEN { -- if non-zero height scanline _ Fix[plygn[links.lftVtces.first].coord.sy]; -- get first scanline lftSrc _ GetEdgeSeq[srcSize*plygn.nVtces]; rgtSrc _ GetEdgeSeq[srcSize*plygn.nVtces]; lftDst _ GetEdgeSeq[dstSize*plygn.nVtces]; rgtDst _ GetEdgeSeq[dstSize*plygn.nVtces]; FOR i: PixelPart IN PixelPart DO --Get Edge records for left and right sides IF dstTmplate[i] > 0 THEN { dstAddr, yStep: CARD32; [dstAddr, yStep] _ SampleMapBase[context.pixels[dstBufPos[i]]]; LinkDst[ lftDst, links.lftVtces, plygn, dstBufPos[i], dstSize, dstAddr, dstTmplate[i], yStep ]; LinkDst[ rgtDst, links.rgtVtces, plygn, dstBufPos[i], dstSize, dstAddr, dstTmplate[i], yStep ]; }; IF srcTmplate[i] > 0 THEN { LinkSrc[lftSrc, links.lftVtces, plygn, srcPos, srcSize, i ]; LinkSrc[rgtSrc, links.rgtVtces, plygn, srcPos, srcSize, i ]; srcPos _ srcPos + 1; }; ENDLOOP; LinkAdditionalData[lftSrc, links.lftVtces, plygn, srcSize, hltCnt, texture ]; LinkAdditionalData[rgtSrc, links.rgtVtces, plygn, srcSize, hltCnt, texture ]; ScanPoly[ context, lftDst, lftSrc, rgtDst, rgtSrc, dstSize, srcSize, dstDpth, srcDpth, scanline, Hilight ]; ReleaseEdgeSeq[lftSrc]; ReleaseEdgeSeq[rgtSrc]; ReleaseEdgeSeq[lftDst]; ReleaseEdgeSeq[ rgtDst]; }; <> plygn.props _ Atom.RemPropFromList[plygn.props, $LightColors]; }; LinkAdditionalData: PROC[ src: REF EdgeSequence, links: LIST OF CARD16, plygn: REF Patch, srcSize, hltCnt: NAT, texture: BOOL] ~ { BuildEdgeDesc: PROC[length: INT16, start, end: INT32] RETURNS [edge: EdgeDesc] ~ { width: INT16 _ end - start; edge.length _ length; edge.val _ start; edge.hiccups _ ABS[width]; [edge.lngthIncr, edge.hiccups] _ Basics.DivMod[ABS[width], edge.length]; IF width < 0 -- color (etc.) moving negatively? THEN { edge.lngthIncr _ -edge.lngthIncr; edge.hicIncr _ -1; } ELSE edge.hicIncr _ 1; edge.bias _ edge.length; -- initial bias to center hiccups edge.stepsLeft _ edge.length; -- set stepsLeft for count down IF ptr-srcSize >= 0 THEN src[ptr-srcSize].nextEdge _ ptr; -- set ptr from last one }; ptr: NAT _ srcSize - hltCnt*2; FOR list: LIST OF CARD16 _ links, list.rest UNTIL list.rest = NIL DO bot: CtlPtInfo _ plygn[list.first]; top: CtlPtInfo _ plygn[list.rest.first]; <> srcStart: IntegerPairSequence _ IF hltCnt > 0 THEN NARROW[bot.data] ELSE NIL; srcEnd: IntegerPairSequence _ IF hltCnt > 0 THEN NARROW[top.data] ELSE NIL; yStart: CARD16 _ Round[bot.coord.sy]; yEnd: CARD16 _ Round[top.coord.sy]; length: INTEGER _ INTEGER[yEnd] - INTEGER[yStart]; IF length > 0 THEN { IF texture THEN { WITH plygn.renderData.shadingClass.texture.first SELECT FROM -- only one texture txtrMap: REF TextureMap => { -- modify with mapped texture bigC: INT16 _ LAST[NAT]/32; ptr _ ptr - 3; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.shade.txtrX], Round[bigC*top.shade.txtrX] ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.shade.txtrY], Round[bigC*top.shade.txtrY] ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, 0, 0 ]; -- filler to avoid bounds errors ptr _ ptr + 1; }; txtrFn: REF TextureFunction => { -- solid or other function-based texture bigC: INT16 _ LAST[NAT]/32; IF useTextureCoords THEN { ptr _ ptr - 3; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.shade.txtrX], Round[bigC*top.shade.txtrX] ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.shade.txtrY], Round[bigC*top.shade.txtrY] ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, 0, 0 ]; -- filler to avoid bounds errors ptr _ ptr + 1; } ELSE { ptr _ ptr - 3; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.coord.x], Round[bigC*top.coord.x] ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.coord.y], Round[bigC*top.coord.y] ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, Round[bigC*bot.coord.z], Round[bigC*top.coord.z] ]; ptr _ ptr + 1; } }; ENDCASE => SIGNAL G3dRender.Error[$Unimplemented, "Unexpected texture type"]; }; FOR i: NAT IN [0..hltCnt) DO -- add in reflection vector for each highlight src[ptr] _ BuildEdgeDesc[ length, srcStart[i].x, srcEnd[i].x ]; ptr _ ptr + 1; src[ptr] _ BuildEdgeDesc[ length, srcStart[i].y, srcEnd[i].y ]; ptr _ ptr + 1; ENDLOOP; }; IF length > 0 THEN ptr _ ptr + srcSize - hltCnt*2; -- skip over other data ENDLOOP; }; <> CheckLimits: PROC[] ~ { }; ImageTransform: PROC[dst, src: SampleMap, dstPos: IntegerPair, theta, scale: REAL] ~ { i: NAT _ 0; -- loop index dstEdge, srcEdge, dstStart, dstBias, srcStart: EdgeDesc; srcAddr, dstAddr: CARD16; srcBytesPerLine, dstBytesPerLine: WORD; srcBox: Box _ ImagerSample.GetBox[src]; ebt: EdgeBltTable; scaledWidth: NAT _ Round[scale * (srcBox.max.f - srcBox.min.f)]; scaledHeight: NAT _ Round[scale * (srcBox.max.s - srcBox.min.s)]; dstEdgeLength: CARD16 _ Round[RealFns.CosDeg[theta] * scaledWidth]; dstEdgeHiccups: CARD16 _ Round[RealFns.SinDeg[theta] * scaledWidth]; CheckLimits[]; -- ensure transformed image fits in target buffer, 0 1.0 THEN -srcBytesPerLine ELSE srcBytesPerLine, -- hiccup step bias: srcBox.max.s - srcBox.min.s -- supply bias since not using G3dEdgeBlt ]; dstEdge _ [ -- walks across destinatation image along slanted edge val: dstStart.val, length: dstEdgeLength, -- horizontal dimension of edge hiccups: dstEdgeHiccups, -- vertical dimension of edge lngthIncr: 1, -- horizontal step to next pixel hicIncr: -dstBytesPerLine, -- 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: srcBox.max.f - srcBox.min.f, -- width of source image hiccups: ABS[INTEGER[srcBox.max.f - srcBox.min.f] - scaledWidth], -- pixels to drop/add to scale down/up lngthIncr: 1, -- horizontal step to next pixel hicIncr: IF scale > 1.0 THEN -1 ELSE 1, -- hiccup step to next pixel bias: 0, -- will be supplied by G3dEdgeBlt indirect: TRUE -- incrementing addresses ]; ebt _ [dstEdge, srcEdge, [TRUE, FALSE] ]; WHILE i < dstStart.length DO G3dEdgeBlt.Blt[ebt]; IF dstBias.val <= - dstEdgeLength -- equals 0 after G3dEdgeBlt 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 _ InlineIncr[dstBias]; dstStart _ InlineIncr[dstStart]; ebt.dst.val _ dstStart.val; srcStart _ InlineIncr[srcStart]; ebt.src.val _ srcStart.val; ENDLOOP; }; END.