DIRECTORY Atom USING [ GetPropFromList ], Rope USING [ ROPE ], Basics USING [ BITAND, BITSHIFT, BytePair, DivMod, DoubleAnd, DoubleShift, DoubleShiftRight, HighByte, HighHalf, LongNumber, LowHalf, ShortNumber], PrincOps USING [ BBTableSpace, BitAddress, BitBltTable, BitBltTablePtr ], PrincOpsUtils USING [ AlignedBBTable, BITBLT ], Real USING [ Round, Fix ], RealFns USING [ CosDeg, SinDeg ], Convert USING [ RopeFromInt ], Terminal USING [ Virtual ], SF USING [ Vec ], ImagerPixel USING [ GetPixels, ObtainScratchPixels, PixelBuffer, PixelMap, PutPixels, ReleaseScratchPixels ], ImagerSample USING [ GetBase, GetBitsPerLine, GetBox, RasterSampleMap, SampleMap ], EdgeBlt USING [ Blt, EdgeBltTable, EdgeDesc, EdgeSequence ], ThreeDBasics USING [ Box, Context, Error, IntegerPair, IntegerPairSequence, IntSequence, NatRGB, NatRGBSequence, Pair, Patch, Pixel, PixelPart, RealSequence, RGB, RGBSequence, ShapeInstance, TextureMap, Vertex, VertexInfo ], ScanConvert USING [ PixelBufferProc, justNoticeable ]; ScanConvertImpl: CEDAR MONITOR IMPORTS Atom, Basics, Convert, EdgeBlt, ImagerPixel, ImagerSample, PrincOpsUtils, Real, RealFns, ThreeDBasics EXPORTS ScanConvert ~ BEGIN LORA: TYPE ~ LIST OF REF ANY; ROPE: TYPE ~ Rope.ROPE; LongNumber: TYPE ~ Basics.LongNumber; BytePair: TYPE ~ Basics.BytePair; SampleMap: TYPE ~ ImagerSample.SampleMap; EdgeDesc: TYPE ~ EdgeBlt.EdgeDesc; EdgeSequence: TYPE ~ EdgeBlt.EdgeSequence; EdgeBltTable: TYPE ~ EdgeBlt.EdgeBltTable; Context: TYPE ~ ThreeDBasics.Context; RGB: TYPE ~ ThreeDBasics.RGB; -- RECORD[ R, G, B: REAL] RGBSequence: TYPE ~ ThreeDBasics.RGBSequence; NatRGB: TYPE ~ ThreeDBasics.NatRGB; NatRGBSequence: TYPE ~ ThreeDBasics.NatRGBSequence; Vertex: TYPE ~ ThreeDBasics.Vertex; VertexInfo: TYPE ~ ThreeDBasics.VertexInfo; Pixel: TYPE ~ ThreeDBasics.Pixel; PixelPart: TYPE ~ ThreeDBasics.PixelPart; Box: TYPE ~ ThreeDBasics.Box; -- RECORD[x, y: REAL] Pair: TYPE ~ ThreeDBasics.Pair; -- RECORD[x, y: REAL] IntegerPair: TYPE ~ ThreeDBasics.IntegerPair; -- RECORD[x, y: INTEGER] IntegerPairSequence: TYPE ~ ThreeDBasics.IntegerPairSequence; IntSequence: TYPE ~ ThreeDBasics.IntSequence; RealSequence: TYPE ~ ThreeDBasics.RealSequence; Patch: TYPE ~ ThreeDBasics.Patch; ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance; TextureMap: TYPE ~ ThreeDBasics.TextureMap; PixelBuffer: TYPE ~ ImagerPixel.PixelBuffer; PixelBufferProc: TYPE ~ ScanConvert.PixelBufferProc; LinkedPoly: TYPE ~ RECORD [ lftVtces, rgtVtces: LIST OF CARD16 ]; 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: REF IntSequence; scanSegLengthHist: REF IntSequence; polyHeightHist: REF IntSequence; InitHistograms: PROCEDURE [maxScanSeg, maxHeight: NAT] ~ { pixelsPerPolyHist _ NEW[ IntSequence[maxScanSeg * maxHeight] ]; pixelsPerPolyHist.length _ maxScanSeg * maxHeight; scanSegLengthHist _ NEW[ IntSequence[maxScanSeg] ]; scanSegLengthHist.length _ maxScanSeg; polyHeightHist _ NEW[ IntSequence[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: REF Context] ~ { ShowData: PROC[startWd, startHt: REAL, numbers: REF 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] _ Real.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 .. Real.Round[wSize * 0.75] ) DO s: REAL _ i / (wSize * 0.75); -- pctge. across histogram j: INT _ Real.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 ThreeDBasics.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 ThreeDBasics.Error[[$MisMatch, "only RasterSampleMaps here"]]; RETURN[ baseByteAddr, bytesPerLine]; }; DoForPixelColors: PROC[ context: REF 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 ThreeDBasics.Error[[$MisMatch, "Unexpected display type"]]; alpha _ NARROW[ Atom.GetPropFromList[context.displayProps, $Alpha] ]; IF alpha # NIL THEN { [base, bpl] _ SampleMapBase[context.pixels[alpha^]]; action[base, 2, bpl, a]; }; depth _ NARROW[ Atom.GetPropFromList[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 _ [ Real.Round[poly[i].coord.sx], Real.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 _ Real.Fix[clr.R*4.999]*24 + Real.Fix[clr.G*5.999]*4 + Real.Fix[clr.B*3.999]; IF mapVal >= 60 THEN mapVal _ mapVal + 135; -- move to top of map RETURN[ mapVal ]; }; $PseudoColor => RETURN[ Real.Fix[clr.R*5.999]*42 + Real.Fix[clr.G*6.999]*6 + Real.Fix[clr.B*5.999] + 2 ]; ENDCASE => SIGNAL ThreeDBasics.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 ThreeDBasics.Error[[$MisMatch, "Bad Render Mode"]]; RETURN[[255, 255, 255]]; }; DoWithPixels: PUBLIC PROC [ context: REF 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: REF 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: EdgeBlt.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; }; EdgeBlt.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: Vertex, dstAddr: CARD32, xStep, yStep: WORD] RETURNS[edge: EdgeDesc] ~ { pLo: IntegerPair _ [ Real.Round[bot.sx], Real.Round[bot.sy] ]; pHi: IntegerPair _ [ Real.Round[top.sx], Real.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: VertexInfo, element: PixelPart] RETURNS[edge: EdgeDesc] ~ { yStart: CARD16 _ Real.Round[bot.coord.sy]; yEnd: CARD16 _ Real.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 _ Real.Fix[bot.shade.er * 255.0]; srcEnd _ Real.Fix[top.shade.er * 255.0]; }; g => { srcStart _ Real.Fix[bot.shade.eg * 255.0]; srcEnd _ Real.Fix[top.shade.eg * 255.0]; }; b => { srcStart _ Real.Fix[bot.shade.eb * 255.0]; srcEnd _ Real.Fix[top.shade.eb * 255.0]; }; a => { srcStart _ Real.Fix[bot.shade.et * 255.0]; srcEnd _ Real.Fix[top.shade.et * 255.0]; }; z => { srcStart _ Real.Round[bot.coord.sz]; srcEnd _ Real.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 _ [ Real.Round[poly[i].coord.sx], Real.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 Real.Fix[most] > Real.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: REF 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 edgeblt 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: REF Context, lftDst, lftSrc, rgtDst, rgtSrc: REF EdgeSequence, dstSize, srcSize, 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[dstSize-1].val, scanSrc[srcSize-1].val] THEN { -- test and put z IF pixelProc # NIL THEN pixelProc[ scanDst, scanSrc, scanline ] 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: REF Context, plygn: REF Patch] ~ { lftSrc, rgtSrc, lftDst, rgtDst: REF EdgeSequence _ NIL; scanline, srcSize, dstSize, 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[ Atom.GetPropFromList[context.displayProps, $Alpha]]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[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; links _ LinkEdges[ plygn, context.pixels.box ]; -- link vertices into left and right side chains IF links.lftVtces # NIL THEN { -- if non-zero height scanline _ Real.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, scanline]; ReleaseEdgeSeq[lftSrc]; ReleaseEdgeSeq[rgtSrc]; ReleaseEdgeSeq[lftDst]; ReleaseEdgeSeq[ rgtDst]; }; }; justNoticeable: NAT ~ Real.Round[ScanConvert.justNoticeable * 65536]; HiliteTiler: PUBLIC PROC [context: REF 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 _ Real.Fix[x * bufWidth] MOD bufWidth; IF txtrX < 0 THEN txtrX _ txtrX + bufWidth; txtrY _ Real.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 ThreeDBasics.Error[[$MisMatch, "Unknown texture type"]]; ImagerPixel.ReleaseScratchPixels[pixel]; RETURN[red, grn, blu]; }; 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 { FOR txtrList: LORA _ shape.shadingClass.texture, txtrList.rest UNTIL txtrList = NIL DO WITH txtrList.first SELECT FROM -- textures evaluated top to bottom texture: REF TextureMap => -- modify with mapped texture WITH texture.pixels SELECT FROM buf: ImagerPixel.PixelMap => [red, grn, blu] _ SimpleTexture[ red, grn, blu, buf, texture.type, scanSrc[reflStart].val, scanSrc[reflStart+1].val ]; ENDCASE => ThreeDBasics.Error[[$MisMatch, "Only simple mapped texture"]]; ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unexpected texture type"]]; ENDLOOP; hltStart _ 1; }; 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 _ hltRed + Basics.HighHalf[pctHilite * lightColor[i].r]; hltGrn _ hltGrn + Basics.HighHalf[pctHilite * lightColor[i].g]; hltBlu _ hltBlu + Basics.HighHalf[pctHilite * lightColor[i].b]; 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, 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[ Atom.GetPropFromList[context.displayProps, $Alpha] ]; depth: REF NAT _ NARROW[ Atom.GetPropFromList[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: REF NatRGBSequence _ NARROW[ Atom.GetPropFromList[plygn.props, $LightColors] ]; hltCnt: NAT _ IF lightColor # NIL THEN lightColor.length ELSE 0; links: LinkedPoly; IF texture THEN hltCnt _ MAX[hltCnt, 1]; 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; 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 _ Real.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; LinkAux[lftSrc, links.lftVtces, plygn, srcSize, hltCnt ]; LinkAux[rgtSrc, links.rgtVtces, plygn, srcSize, hltCnt ]; ScanPoly[context, lftDst, lftSrc, rgtDst, rgtSrc, dstSize, srcSize, scanline, Hilight]; ReleaseEdgeSeq[lftSrc]; ReleaseEdgeSeq[rgtSrc]; ReleaseEdgeSeq[lftDst]; ReleaseEdgeSeq[ rgtDst]; }; }; LinkAux: PROC[ src: REF EdgeSequence, links: LIST OF CARD16, plygn: REF Patch, srcSize, hltCnt: NAT] ~ { ptr: NAT _ srcSize - hltCnt*2; FOR list: LIST OF CARD16 _ links, list.rest UNTIL list.rest = NIL DO bot: VertexInfo _ plygn[list.first]; top: VertexInfo _ plygn[list.rest.first]; srcStart: REF IntegerPairSequence _ NARROW[bot.aux]; srcEnd: REF IntegerPairSequence _ NARROW[top.aux]; yStart: CARD16 _ Real.Round[bot.coord.sy]; yEnd: CARD16 _ Real.Round[top.coord.sy]; length: INTEGER _ INTEGER[yEnd] - INTEGER[yStart]; IF length > 0 THEN FOR i: NAT IN [0..hltCnt) DO FOR j: NAT IN [0..2) DO width: INT16; IF j = 0 THEN { width _ srcEnd[i].x - srcStart[i].x; src[ptr].val _ srcStart[i].x; } ELSE { width _ srcEnd[i].y - srcStart[i].y; src[ptr].val _ srcStart[i].y; }; src[ptr].length _ length; src[ptr].hiccups _ ABS[width]; [src[ptr].lngthIncr, src[ptr].hiccups] _ Basics.DivMod[ABS[width], src[ptr].length]; IF width < 0 -- color moving negatively? THEN { src[ptr].lngthIncr _ -src[ptr].lngthIncr; src[ptr].hicIncr _ -1; } ELSE src[ptr].hicIncr _ 1; src[ptr].bias _ src[ptr].length; -- initial bias to center hiccups src[ptr].stepsLeft _ src[ptr].length; -- set stepsLeft for count down IF ptr-srcSize >= 0 THEN src[ptr-srcSize].nextEdge _ ptr; -- set pointer from last one ptr _ ptr + 1; ENDLOOP; 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 _ Real.Round[scale * (srcBox.max.f - srcBox.min.f)]; scaledHeight: NAT _ Real.Round[scale * (srcBox.max.s - srcBox.min.s)]; dstEdgeLength: CARD16 _ Real.Round[RealFns.CosDeg[theta] * scaledWidth]; dstEdgeHiccups: CARD16 _ Real.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 EdgeBlt ]; dstEdge _ [ -- walks across destinatation image along slanted edge val: dstStart.val, length: dstEdgeLength, -- horizontal dimension of edge hiccups: dstEdgeHiccups, -- vertical dimension of edge lngthIncr: 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 EdgeBlt indirect: TRUE -- incrementing addresses ]; ebt _ [dstEdge, srcEdge, [TRUE, FALSE] ]; WHILE i < dstStart.length DO EdgeBlt.Blt[ebt]; IF dstBias.val <= - dstEdgeLength -- equals 0 after EdgeBlt adds initial bias THEN { -- should be integrated better dstBias.val _ dstBias.val + 2 * dstEdgeLength; -- reset bias dstStart.val _ dstStart.val - dstStart.lngthIncr; -- move start position up one line } ELSE i _ i+1; ebt.dst.bias _ dstBias.val; -- set up bias for distance to first hiccup dstBias _ InlineIncr[dstBias]; dstStart _ InlineIncr[dstStart]; ebt.dst.val _ dstStart.val; srcStart _ InlineIncr[srcStart]; ebt.src.val _ srcStart.val; ENDLOOP; }; END. NScanConvertImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Frank Crow, April 20, 1989 10:12:48 am PDT Scan conversion operations on pixel buffers. Expects input to be properly clipped. Bloomenthal, February 21, 1989 11:32:14 pm PST Type Definitions RECORD [length: NAT _ 0, s: SEQUENCE maxLength: NAT OF EdgeBlt.EdgeDesc ]; Constants Globals Statistical Procedures Utility Procedures Note! This returns zero if input is zero!! Pixel Operations Ordered dither for crude looks at full-color, $PseudoColor lookup table assumed Scan Conversion for Lines Scan Conversion for Convex Areas Build destination address edge description Build source value edge description Link vertices into left side and right side chains If not a zero-height poly then increment down edges and across scan segments Image Manipulation Κ%η˜Ihead2™šœ Οmœ1™Jšœ žœž˜-Jšœžœž˜#Jšœžœžœ˜4Jšœžœ˜#Jšœ žœ˜+Jšœžœ˜!Jšœ žœ˜)NšœžœŸœ˜Mšœ žœ!Ÿ˜JJšœžœ$˜=Mšœ žœ˜-Mšœžœ˜/Mšœžœ˜!Mšœžœ˜1Mšœ žœ˜,Mšœ žœ˜,Mšœžœ ˜5š œ žœžœžœžœžœ˜AJ˜——™ šœ0Ÿ*˜ZJšœ žœ˜&Jšœ žœ ˜J˜CJ˜Jšœ&žœžœžœ˜jJ˜—Mšœžœ˜Mšœžœ ˜Jš œ žœžœžœžœžœD˜tJ˜%—™Lšœžœ˜Lšœžœ˜-Lš œžœžœžœžœžœ˜OLšœ žœžœ˜Lšœ>žœ˜FLšœžœ ˜#Lšœžœ ˜#Lšœžœ ˜ L˜—™šΟnœž œžœ˜:Lšœžœ(˜?L˜2Lšœžœ˜3L˜&Lšœžœ˜/L˜"M˜M˜—š œž œ˜!Mš žœžœžœžœžœ˜SMš žœžœžœžœžœ˜SMš žœžœžœžœžœ˜MM˜M˜—š œž œ žœ ˜4š  œžœžœ žœžœ˜QMšœžœ˜Mšœžœ˜Mšœžœ˜!šžœžœžœž˜&Mšžœžœ˜0Mšžœ˜—šžœžœžœž˜&Mšœ6Ÿ˜KMšžœ˜—M˜]M˜}M˜‰M˜^šžœžœžœ#žœ˜3MšœžœŸ˜>Mšœžœ%Ÿ˜EMšœžœ˜Mšœ žœ˜šžœžœ˜šžœžœžœžœ˜Mšœžœžœ˜LMšžœ˜—Mšœžœ ˜ M˜—MšœŸ'˜>M˜‰M˜ Mšžœ˜—M˜—MšœDžœ˜Lšžœžœžœžœ˜0Mšžœ)žœ*˜YM˜)Mšžœžœ ˜,Mšžœ˜—M˜%M˜%M˜9M˜šžœžœžœžœ˜0Mšžœ#žœ$˜MM˜)M˜%Mšžœžœ ˜,Mšžœ˜—M˜%M˜!M˜@M˜šžœžœžœžœ˜-Mšžœ#žœ$˜MM˜&M˜"Mšžœžœ ˜)Mšžœ˜—M˜"M˜M˜7M˜——™š  œž œžœžœžœ˜IJšžœ˜J˜J˜—š  œž œ žœžœžœžœ˜?J™+Jšžœ žœžœžœžœ žœžœžœžœ˜QJšœ ˜—š   œžœžœžœžœ žœ˜aM˜%M˜'šžœžœ˜M˜#M˜&M˜—Mšœ#ž˜$šžœŸ-˜KMšžœžœžœ˜;—Mšžœ˜ M˜—š  œžœžœ žœžœžœŸ ˜LLš œžœžœžœžœžœ˜K—š  œžœžœ žœžœžœŸ˜OLš œžœžœžœžœžœžœ˜TL˜—š  œžœ žœ žœžœžœ˜HJšžœžœžœ)˜EJ˜—š   œžœžœ žœžœžœ˜AJšžœžœžœ˜Mšœžœžœ˜šžœžœž˜"M˜0M˜0Mšœ*žœ˜.M˜—Mš žœ žœžœžœ žœ˜UM˜Mšžœ ˜M˜—š œžœžœžœ˜7Jšžœžœžœ˜šžœ)žœ˜1M˜0šžœžœžœž˜*šžœžœ˜%Mšžœ=˜C—Mšžœ˜—M˜0M˜—M˜—š  œž œ žœžœžœžœ˜:Nšžœ žœ ˜%—š  œžœžœžœžœ Ÿ,˜[Lšœžœ˜Lšœžœ˜šžœžœž˜L˜ L˜ Lšžœ˜—L˜L˜—š  œž œ žœ žœžœ žœ˜LMšœ žœŸ2˜PMšœžœ ˜Mšœžœ ˜Mšžœ žœžœ˜ šžœžœŸ$˜@Mšžœžœžœ$˜MM˜!M˜Mšžœ˜—J˜J˜—š   œžœžœžœžœ˜AMšœžœž˜+šžœ žœž˜˜+M˜9Mšœžœžœ'žœ˜rMšœžœ*˜HM˜—Mšžœžœ?˜P—Mšžœ˜$Mšœ ˜—š œžœ žœžœ žœžœž œ˜ŠMš œžœ žœžœžœ˜4šžœž˜%˜M˜KM˜KM˜KM˜—˜M˜KM˜—Mšžœžœ<˜MJšœžœ7˜Ešžœ žœžœ˜J˜W—Jšœžœ7˜Ešžœ žœžœ˜J˜W——M˜—š  œžœžœžœžœžœžœ˜Lš žœžœž œžœž˜/JšœžœžœY˜qJšœžœ ˜Jšžœ˜ —Jšžœ˜ J˜—šΠbn œžœžœ žœžœžœžœ˜Cšžœ ž˜˜Jšœžœ\˜gJšžœžœŸ˜CJšžœ ˜J˜—šœžœ˜J˜OJ˜—Jšžœžœ4˜F—Nšžœ˜N˜—š  œžœžœ žœ žœžœžœ˜Fšžœ žœ˜˜Jšžœ žœŸ˜BJšœžœ ˜šžœ˜ J˜Jšœ žœ˜Jšœ žœ ˜J˜—J˜—˜Jšœžœ˜šžœ˜ J˜Jšœ žœ˜Jšœ žœ ˜J˜—J˜—Jšžœžœ4˜F—Nšžœ˜N˜——šΟb™š   œžœžœ žœ&žœ*˜{˜6N˜&N˜—Nšœžœ˜)Nšœžœ˜)Nšœ žœ žœ.˜JNšœžœ˜!šœ#Ÿ˜šœ#Ÿ˜MšœžœžœŸ˜=Mšœžœžœ˜.Jšœžœ žœ˜$Jšœžœ žœ˜/Jšœ žœŸ4˜YJšœžœ žœ žœŸ˜SJš œžœ žœ žœžœ˜SJšœžœ žœ žœŸ˜SJšœžœ˜JšœžœŸ ˜;JšžœžœžœŸ˜MJšœžœ˜%Jšžœžœžœ˜=Jšœžœ˜%Jšžœžœžœ˜=šœžœ˜Jšœžœžœžœžœžœ ŸœŸΟi ˜»—šžœžœŸ˜PMšž œžœžœŸ ˜OMšžœžœžœ*Ÿ ˜O—M˜—š œžœ'žœ˜;M˜—š   œžœžœžœžœžœž˜Oš œ žœžœžœžœžœ˜+Mšœžœ ˜,—šžœ žœ˜1MšžœžœžœŸ˜)Mšžœžœžœžœ˜J—M˜——™š œžœžœ žœ:˜\š œžœ žœžœ˜Išœ žœžœ ž˜'M˜3Mšœ!žœžœ˜3—šœ žœžœ ž˜'M˜3Mšœ!žœžœ˜3—M˜MšœžœŸ˜Hšžœ˜Mš žœžœžœ$žœžœ ˜ŠMš žœ žœžœ%žœžœ ˜Œ—šžœžœ˜*Jšžœ.žœŸ˜Všžœ˜Jšœ žœžœžœ Ÿ˜LJ˜J˜Jšœ-žœ˜GšžœŸ˜1Jšžœ<˜@Jšžœ˜—Jšœžœ˜J˜——MšœŸ ˜>M˜—M˜Mšœ žœ˜Mšœžœžœ˜Mšœžœ˜Mšœžœ˜Mšœžœ˜šžœžœ žœ ˜Mšžœ žœžœžœ˜HMšžœ žœžœžœ˜H—MšœJŸ˜ZMšœ?Ÿ˜[M˜>M˜>M˜>M˜"M˜——™ š   œžœžœžœžœ˜jJšŸ+™+M˜>M˜>Mšœžœ˜ Mšœžœ˜!Jšžœ žœžœ ˜:Mšœžœžœ Ÿ˜YMšœžœ˜Mšœžœ˜Jšœ/žœ˜HšžœŸ ˜4JšžœC˜GJšžœ˜—Jšœ1Ÿ(˜YMšœŸ!˜;Mšœ!Ÿ˜@J˜—š  œžœ1žœ˜]JšŸ$™$Mšœžœ&žœ˜VMšœžœžœ žœ ˜3Mšœžœ˜Mšœžœ˜ Jšžœ žœžœ ˜:šžœ ž˜M˜`M˜`M˜`M˜_M˜WMšžœžœ˜—Mšœžœ ˜!MšœŸ˜-Mšœžœ˜Mšœžœ˜Jšœ/žœ˜HšžœŸ˜/Jšžœ?˜CJšžœ˜—MšœŸ!˜;Mšœ!Ÿ˜@J˜—š  œžœ žœžœ˜FM™2Nšœ žœ˜Nš œ žœžœžœžœ˜Jšœ žœ-žœ˜JMšœJŸ˜Zš žœžœžœžœŸ˜PJ˜PJšœ>Ÿ˜MJ˜Ÿ˜SM˜VM˜Všžœžœ žœŸ+˜Ošžœžœ˜Mšœžœ˜M˜?M˜dM˜dM˜—šžœžœ˜M˜Ÿ˜SM˜VM˜Všžœžœ žœŸ+˜Ošžœžœ˜Mšœžœ˜M˜?M˜dM˜dM˜—šžœžœ˜M˜MšœŸ ˜:Mšœ#Ÿ˜AMšœŸ˜:Mšœ žœ Ÿ˜3M˜—šœ ŸM˜YM˜Mšœ,Ÿ˜DMšœ žœžœ.Ÿ&˜hMšœŸ ˜9Mšœ žœ žœžœŸ˜GMšœŸ˜4Mšœ žœ Ÿ˜2M˜—Mšœžœžœ˜)M˜šžœžœž˜M˜šžœ"Ÿ+˜OšžœŸ˜&Mšœ1Ÿ ˜>Mšœ4Ÿ&˜ZM˜—Mšžœ ˜—Mšœ"Ÿ+˜MM˜ M˜?M˜?Mšžœ˜—M˜——Jšžœ˜—…—’œΛΡ