DIRECTORY BitBlt, Environment, ImagerPixelMaps, Inline, Real, RealFns, ImagerScanConverter; ImagerScanConverterImpl: CEDAR PROGRAM IMPORTS BitBlt, Inline, ImagerPixelMaps, Real, RealFns EXPORTS ImagerScanConverter SHARES ImagerPixelMaps ~ BEGIN Bezier: TYPE ~ RECORD [s0, f0, s1, f1, s2, f2, s3, f3: REAL]; Pair: TYPE ~ RECORD [s, f: REAL]; lgFScale: NAT ~ 6; fScale: NAT ~ 64; -- must be a power of two. flatness: REAL _ 4; -- 1/max allowed error in pixels. BITAND: PROC [a, b: CARDINAL] RETURNS[CARDINAL] ~ INLINE {RETURN[Inline.BITAND[a, b]]}; BITANDI: PROC [a, b: INTEGER] RETURNS[INTEGER] ~ INLINE {RETURN[Inline.BITAND[a, b]]}; BITOR: PROC [a, b: CARDINAL] RETURNS[CARDINAL] ~ INLINE {RETURN[Inline.BITOR[a, b]]}; BITXOR: PROC [a, b: CARDINAL] RETURNS[CARDINAL] ~ INLINE {RETURN[Inline.BITXOR[a, b]]}; Shift: PROC [a: CARDINAL, b: INTEGER] RETURNS [CARDINAL] ~ INLINE {RETURN[Inline.BITSHIFT[a, b]]}; ShortScaledFloor: PROC [a: CARDINAL] RETURNS [INTEGER] ~ INLINE {RETURN[Inline.BITSHIFT[a, -lgFScale]]}; Mid: PROC [a, b: CARDINAL] RETURNS[CARDINAL] ~ INLINE {RETURN[Inline.BITSHIFT[(a+b),-1]]}; FloorI: PROC [r: REAL] RETURNS [t: INTEGER] ~ INLINE {t _ Real.RoundI[r]; IF t > r THEN t _ t-1}; CeilingI: PROC [r: REAL] RETURNS [t: INTEGER] ~ INLINE {t _ Real.RoundI[r]; IF t < r THEN t _ t+1}; Direction: TYPE ~ {increasing, decreasing}; deltaWrap: ARRAY Direction OF INTEGER ~ [1, -1]; PieceRep: TYPE ~ RECORD [ sOrg, fOrg: INTEGER, -- in device units sScale: CARDINAL, sOffset: CARDINAL, sFirstScan: INTEGER, -- sFirstScan + 1/2 defines the first scan line that crosses this piece. scanCount: CARDINAL, -- number of scan lines that cross this piece. direction: Direction, iterationsUntilFlat: NAT, f0: CARDINAL, s1, f1: CARDINAL, s2, f2: CARDINAL, s3, f3: CARDINAL ]; SegmentRep: TYPE ~ RECORD [ sFirstScan: INTEGER, -- sFirstScan + 1/2 defines the first scan line that crosses this segment. direction: Direction, scanCount: NAT, -- number of scan lines that cross this segment. s0, f0, s1, f1: REAL ]; DevicePath: PUBLIC TYPE ~ REF DevicePathRep; DevicePathRep: PUBLIC TYPE ~ RECORD [ scanLineCrossings: INT, sMin, sMax, fMin, fMax: INTEGER, pointTable: PointTable, bucketTable: BucketTable, segmentList: LIST OF SegmentRep, pieceList: LIST OF PieceRep ]; FValueFor: PROC [piece: LIST OF PieceRep, sMinusHalf: INTEGER] RETURNS [ans: INTEGER] ~ { ShortPair: TYPE ~ RECORD [a, b: CARDINAL]; p: PieceRep _ piece.first; sRel: CARDINAL _ NAT[(sMinusHalf - p.sOrg) * p.sScale + Shift[p.sScale, -1] - p.sOffset]; IF sRel > p.s3 THEN RETURN [ShortScaledFloor[p.f3]+p.fOrg]; THROUGH [0..p.iterationsUntilFlat) DO sAB, fAB, sBC, fBC: CARDINAL; IF p.s3 > LAST[CARDINAL]/8 THEN { p.s3 _ Shift[p.s3, -1]; p.s2 _ Shift[p.s2, -1]; p.s1 _ Shift[p.s1, -1]; sRel _ Shift[sRel, -1]; }; fAB _ p.f0 + 2*p.f1 + p.f2; sAB _ 2*p.s1 + p.s2; fBC _ p.f1 + 2*p.f2 + p.f3; sBC _ p.s1 + 2*p.s2 + p.s3; IF Shift[sRel, 3] < sAB+sBC THEN { [p.s3, p.f3] _ ShortPair[Shift[sAB+sBC+3, -2], Shift[fAB+fBC+7, -3]]; [p.s2, p.f2] _ ShortPair[Shift[sAB, -1], Shift[fAB+3, -2]]; p.f1 _ Shift[p.f0+p.f1, -1]; sRel _ 2*sRel; } ELSE { sShift: CARDINAL _ Shift[sAB+sBC+3, -2]; p.f0 _ Shift[fAB+fBC+7, -3]; [p.s1, p.f1] _ ShortPair[Shift[sBC, -1] - sShift, Shift[fBC+3, -2]]; [p.s2, p.f2] _ ShortPair[p.s2+p.s3 - sShift, Shift[p.f2+p.f3, -1]]; p.s3 _ 2*p.s3 - sShift; sRel _ 2*sRel - sShift; }; ENDLOOP; IF sRel > p.s3 THEN ERROR; IF p.s3=0 OR BITAND[BITXOR[p.f0, p.f3], LAST[CARDINAL]-(fScale-1)] = 0 THEN ans _ ShortScaledFloor[p.f0] + p.fOrg ELSE IF p.f3>p.f0 THEN ans _ p.fOrg + ShortScaledFloor[p.f0 + Inline.LongDiv[Inline.LongMult[sRel, p.f3-p.f0], p.s3]] ELSE ans _ p.fOrg + ShortScaledFloor[p.f3 + Inline.LongDiv[Inline.LongMult[p.s3-sRel, p.f0-p.f3], p.s3]]; }; IterationsUntilFlat: PROC [b: Bezier] RETURNS [iterationsUntilFlat: NAT] ~ { deltaX: REAL ~ b.s3 - b.s0; deltaY: REAL ~ b.f3 - b.f0; theta: REAL ~ RealFns.ArcTan[deltaY, deltaX]; cos: REAL ~ RealFns.Cos[theta]; sin: REAL ~ RealFns.Sin[theta]; y1: REAL ~ (b.f1-b.f0)*cos - (b.s1-b.s0)*sin; y2: REAL ~ (b.f2-b.f0)*cos - (b.s2-b.s0)*sin; r: REAL ~ IF ABS[y2] > 0 THEN y1/y2 ELSE 10.0; e: REAL _ MAX[ABS[y1], ABS[y2]]*flatness; iterationsUntilFlat _ 0; WHILE e>1 DO iterationsUntilFlat _ iterationsUntilFlat+1; e _ Real.FScale[e, -1]; ENDLOOP; IF r IN [0.5..2.0] THEN iterationsUntilFlat _ (iterationsUntilFlat * 695 + 1023)/1024; }; marginSize: REAL _ 8.0; Interpolate: PROC [t: REAL, a, b: Pair] RETURNS[r: Pair] ~ {RETURN [[a.s*(1-t)+b.s*t, a.f*(1-t)+b.f*t]]}; SubDivide: PROC [b: Bezier, t: REAL] RETURNS[low, high: Bezier] ~ BEGIN b0: Pair ~ [b.s0, b.f0]; b1: Pair ~ [b.s1, b.f1]; b2: Pair ~ [b.s2, b.f2]; b3: Pair ~ [b.s3, b.f3]; q1, q2, q3, qp1, qp2, q: Pair; q1 _ Interpolate[t, b0, b1]; q2 _ Interpolate[t, b1, b2]; q3 _ Interpolate[t, b2, b3]; qp1 _ Interpolate[t, q1, q2]; qp2 _ Interpolate[t, q2, q3]; q _ Interpolate[t, qp1, qp2]; low _ [b0.s, b0.f, q1.s, q1.f, qp1.s, qp1.f, q.s, q.f]; high _ [q.s, q.f, qp2.s, qp2.f, q3.s, q3.f, b3.s, b3.f]; END; CreatePath: PUBLIC PROC [ pathProc: ImagerScanConverter.PathProc, clipBox: ImagerScanConverter.DeviceRectangle, scratch: DevicePath _ NIL -- for re-use of storage ] RETURNS [DevicePath] ~ { devicePath: DevicePathRep; AppendSegment: PROC [s0, f0, s1, f1: REAL] ~ { segment: SegmentRep; IF s1 < s0 THEN { segment.direction _ decreasing; segment.s0 _ s1; segment.f0 _ f1; segment.s1 _ s0; segment.f1 _ f0; } ELSE { segment.direction _ increasing; segment.s0 _ s0; segment.f0 _ f0; segment.s1 _ s1; segment.f1 _ f1; }; segment.sFirstScan _ CeilingI[segment.s0-0.5]; segment.scanCount _ CeilingI[segment.s1-0.5] - segment.sFirstScan; IF segment.scanCount # 0 THEN { devicePath.scanLineCrossings _ devicePath.scanLineCrossings + segment.scanCount; IF segment.sFirstScan < devicePath.sMin THEN devicePath.sMin _ segment.sFirstScan; IF segment.sFirstScan + INTEGER[segment.scanCount] > devicePath.sMax THEN devicePath.sMax _ segment.sFirstScan + segment.scanCount; devicePath.fMin _ MIN[devicePath.fMin, FloorI[segment.f0], FloorI[segment.f1]]; devicePath.fMax _ MAX[devicePath.fMax, CeilingI[segment.f0], CeilingI[segment.f1]]; IF scratch = NIL OR scratch.segmentList = NIL THEN devicePath.segmentList _ CONS[segment, devicePath.segmentList] ELSE { t: LIST OF SegmentRep _ scratch.segmentList; scratch.segmentList _ t.rest; t.rest _ devicePath.segmentList; t.first _ segment; devicePath.segmentList _ t; }; }; }; AppendMonotoneShortPiece: PROC [b: Bezier, fMin: REAL] ~ { iterationsUntilFlat: NAT _ IterationsUntilFlat[b]; IF iterationsUntilFlat = 0 THEN AppendSegment[b.s0, b.f0, b.s3, b.f3] ELSE { piece: PieceRep; fOrg: REAL ~ (piece.fOrg _ FloorI[fMin-0.5]); sDelta: REAL _ ABS[b.s0-b.s3]; sMin, sOrg: REAL; lgSScale: CARDINAL _ 0; piece.sScale _ 1; WHILE piece.sScale devicePath.sMax THEN devicePath.sMax _ piece.sFirstScan + piece.scanCount; IF piece.fOrg < devicePath.fMin THEN devicePath.fMin _ piece.fOrg; devicePath.fMax _ MAX[devicePath.fMax, piece.fOrg + ShortScaledFloor[MAX[piece.f0, piece.f1, piece.f2, piece.f3]]+1]; IF scratch = NIL OR scratch.pieceList = NIL THEN devicePath.pieceList _ CONS[piece, devicePath.pieceList] ELSE { t: LIST OF PieceRep _ scratch.pieceList; scratch.pieceList _ t.rest; t.rest _ devicePath.pieceList; t.first _ piece; devicePath.pieceList _ t; }; }; }; }; sInnerMin: REAL _ MAX[-REAL[LAST[NAT]-2], REAL[clipBox.sMin]]; sInnerMax: REAL _ MIN[REAL[LAST[NAT]-2], REAL[clipBox.sMin] + clipBox.sSize]; fInnerMin: REAL _ MAX[-REAL[LAST[NAT]-2], REAL[clipBox.fMin]]; fInnerMax: REAL _ MIN[REAL[LAST[NAT]-2], REAL[clipBox.fMin] + clipBox.fSize]; sOuterMin: REAL _ MAX[-REAL[LAST[NAT]-1], sInnerMin - marginSize]; sOuterMax: REAL _ MIN[REAL[LAST[NAT]-1], sInnerMax + marginSize]; fOuterMin: REAL _ MAX[-REAL[LAST[NAT]-1], fInnerMin - marginSize]; fOuterMax: REAL _ MIN[REAL[LAST[NAT]-1], fInnerMax + marginSize]; ClipS: PROC [s: REAL] RETURNS [REAL] ~ {RETURN[MAX[MIN[s, sOuterMax], sOuterMin]]}; ClipF: PROC [f: REAL] RETURNS [REAL] ~ {RETURN[MAX[MIN[f, fOuterMax], fOuterMin]]}; AppendMonotoneBezier: PROC [bezier: Bezier] ~ { SubDivide: PROC ~ { Mid: PROC [a, b: REAL] RETURNS[REAL] ~ INLINE {RETURN[Real.FScale[a+b, -1]]}; a: Pair _ [Mid[bezier.s0, bezier.s1], Mid[bezier.f0, bezier.f1]]; b: Pair _ [Mid[bezier.s1, bezier.s2], Mid[bezier.f1, bezier.f2]]; c: Pair _ [Mid[bezier.s2, bezier.s3], Mid[bezier.f2, bezier.f3]]; ab: Pair _ [Mid[a.s, b.s], Mid[a.f, b.f]]; bc: Pair _ [Mid[b.s, c.s], Mid[b.f, c.f]]; abc: Pair _ [Mid[ab.s, bc.s], Mid[ab.f, bc.f]]; AppendMonotoneBezier[[bezier.s0, bezier.f0, a.s, a.f, ab.s, ab.f, abc.s, abc.f]]; AppendMonotoneBezier[[abc.s, abc.f, bc.s, bc.f, c.s, c.f, bezier.s3, bezier.f3]]; }; fMin: REAL ~ MIN[bezier.f0, bezier.f1, bezier.f2, bezier.f3]; fMax: REAL ~ MAX[bezier.f0, bezier.f1, bezier.f2, bezier.f3]; sEndMin: REAL ~ MIN[bezier.s0, bezier.s3]; sMin: REAL ~ MIN[sEndMin, bezier.s1, bezier.s2]; sMax: REAL ~ MAX[bezier.s0, bezier.s1, bezier.s2, bezier.s3]; sMinRegion: INTEGER _ SELECT sMin FROM < sOuterMin => -2, < sInnerMin => -1, <= sInnerMax => 0, <= sOuterMax => 1, ENDCASE => 2; fMinRegion: INTEGER _ SELECT fMin FROM < fOuterMin => -2, < fInnerMin => -1, <= fInnerMax => 0, <= fOuterMax => 1, ENDCASE => 2; sMaxRegion: INTEGER _ SELECT sMax FROM < sOuterMin => -2, < sInnerMin => -1, <= sInnerMax => 0, <= sOuterMax => 1, ENDCASE => 2; fMaxRegion: INTEGER _ SELECT fMax FROM < fOuterMin => -2, < fInnerMin => -1, <= fInnerMax => 0, <= fOuterMax => 1, ENDCASE => 2; IF MAX[ABS[sMinRegion], ABS[fMinRegion], ABS[sMinRegion], ABS[fMinRegion]] <= 1 THEN { IF sMax-sMin <= LAST[CARDINAL]/8 AND bezier.s1 >= sEndMin AND bezier.s2 >= sEndMin AND fMax-fMin <= LAST[CARDINAL]/(8*fScale) THEN AppendMonotoneShortPiece[bezier, fMin] ELSE SubDivide[]; } ELSE IF sMinRegion > 0 OR sMaxRegion < 0 OR fMinRegion > 0 OR fMaxRegion < 0 THEN AppendMonotoneBezier[[ ClipS[bezier.s0], ClipF[bezier.f0], ClipS[bezier.s1], ClipF[bezier.f1], ClipS[bezier.s2], ClipF[bezier.f2], ClipS[bezier.s3], ClipF[bezier.f3] ]] ELSE SubDivide[]; }; AppendBezier: PROC [b: Bezier] ~ { c0: REAL ~ b.s0; c1: REAL ~ 3*(b.s1 - c0); t: REAL ~ 3*(b.s2 - b.s1); c2: REAL ~ t - c1; c3: REAL ~ b.s3 - c0 - t; d0: REAL ~ c1; d1: REAL ~ 2*c2; d2: REAL ~ 3*c3; t0, t1: REAL _ -1; tt0, tt1: REAL _ -1; IF ABS[d2] > 0 THEN { b: REAL ~ d1/d2; c: REAL ~ d0/d2; d: REAL ~ b*b-4*c; IF d>=0 THEN { sqrt: REAL ~ Real.SqRt[d]; t0 _ (-b-(IF b>0 THEN sqrt ELSE -sqrt))/2.0; t1 _ IF ABS[t0] > 0 THEN c/t0 ELSE 0.0; }; } ELSE IF ABS[d1] > 0 THEN t0 _ -d0/d1; t0 _ MIN[1.0, MAX[0.0, t0]]; t1 _ MIN[1.0, MAX[0.0, t1]]; IF t0 > t1 THEN {t: REAL _ t0; t0 _ t1; t1 _ t}; IF 0.0 < t0 AND t0 < t1 AND t1 < 1.0 THEN { low, middle, high: Bezier; [middle, high] _ SubDivide[b, t1]; [low, middle] _ SubDivide[middle, t0/t1]; low.s2 _ low.s3; middle.s1 _ middle.s0; middle.s2 _ middle.s3; high.s1 _ high.s0; AppendMonotoneBezier[low]; AppendMonotoneBezier[middle]; AppendMonotoneBezier[high]; } ELSE IF t0 IN (0.0..1.0) OR t1 IN (0.0..1.0) THEN { low, high: Bezier; [low, high] _ SubDivide[b, IF t0 IN (0.0..1.0) THEN t0 ELSE t1]; low.s2 _ low.s3; high.s1 _ high.s0; AppendMonotoneBezier[low]; AppendMonotoneBezier[high]; } ELSE AppendMonotoneBezier[b]; }; firstPoint, lastPoint: Pair; started: BOOLEAN _ FALSE; Move: PROC [s, f: REAL] ~ { IF started THEN Close[] ELSE started _ TRUE; firstPoint _ lastPoint _ [s, f]; }; Line: PROC [s, f: REAL] ~ { b1, b2: Pair; IF NOT started THEN Move[s, f]; IF lastPoint.s IN [sOuterMin..sOuterMax] AND lastPoint.f IN [fOuterMin..fOuterMax] AND s IN [sOuterMin..sOuterMax] AND f IN [fOuterMin..fOuterMax] THEN AppendSegment[lastPoint.s, lastPoint.f, s, f] ELSE { b1 _ Interpolate[0.333333, lastPoint, [s, f]]; b2 _ Interpolate[0.666667, lastPoint, [s, f]]; AppendBezier[[lastPoint.s, lastPoint.f, b1.s, b1.f, b2.s, b2.f, s, f]]; }; lastPoint _ [s, f]; }; Curve: PROC [s1, f1, s2, f2, s3, f3: REAL] ~ { IF NOT started THEN Move[s1, f1]; AppendBezier[[lastPoint.s, lastPoint.f, s1, f1, s2, f2, s3, f3]]; lastPoint _ [s3, f3]; }; Close: PROC ~ { IF lastPoint # firstPoint THEN Line[firstPoint.s, firstPoint.f]; }; devicePath.scanLineCrossings _ 0; devicePath.sMin _ LAST[INTEGER]; devicePath.sMax _ FIRST[INTEGER]; devicePath.fMin _ LAST[INTEGER]; devicePath.fMax _ FIRST[INTEGER]; pathProc[Move, Line, Curve]; IF started THEN Close[]; IF devicePath.sMin > devicePath.sMax THEN devicePath.sMin _ devicePath.sMax _ devicePath.fMin _ devicePath.fMax _ 0; IF scratch # NIL THEN {devicePath.pointTable _ scratch.pointTable; devicePath.bucketTable _ scratch.bucketTable; scratch^ _ devicePath; RETURN [scratch]}; RETURN [NEW[DevicePathRep _ devicePath]]; }; Point: TYPE ~ MACHINE DEPENDENT RECORD [ fRel: NAT, -- relative to devicePath.fMin direction: Direction ]; PointTable: TYPE ~ REF PointTableRec; PointTableRec: TYPE ~ RECORD [ SEQUENCE maxLength: NAT OF Point ]; shellD: ARRAY [1..11] OF CARDINAL ~ [1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 65535]; Key: PROC [point: Point] RETURNS [CARDINAL] ~ TRUSTED INLINE {RETURN[LOOPHOLE[point]]}; ShellSort: PROC [table: PointTable, start, length: NAT] ~ TRUSTED { IF length>0 THEN { passesPlusOne: [1..11] _ 1; startPtr: LONG POINTER TO Point _ @(table[start]); endPtr: LONG POINTER TO Point _ @(table[start+length-1]) + SIZE[Point]; UNTIL shellD[passesPlusOne _ passesPlusOne+1] >= length DO NULL ENDLOOP; FOR pass: NAT DECREASING IN [1..passesPlusOne) DO h: NAT _ shellD[pass]; hTimesSize: CARDINAL _ h*SIZE[Point]; FOR iPtr: LONG POINTER TO Point _ startPtr, iPtr + SIZE[Point] UNTIL iPtr = endPtr DO a: Point _ iPtr^; jPtr: LONG POINTER TO Point _ iPtr; b: LONG POINTER TO Point; WHILE LOOPHOLE[(b _ jPtr-hTimesSize), LONG CARDINAL] >= LOOPHOLE[startPtr, LONG CARDINAL] AND Key[a] < Key[b^] DO jPtr^ _ b^; jPtr _ b; ENDLOOP; jPtr^ _ a; ENDLOOP; ENDLOOP; }; }; BucketTable: TYPE ~ REF BucketTableRec; BucketTableRec: TYPE ~ RECORD [length: NAT, seq: SEQUENCE maxLength: NAT OF INTEGER]; InitialBucketTable: PROC [devicePath: DevicePath, smin: INTEGER, smax: INTEGER, scratch: BucketTable] RETURNS [bucketTable: BucketTable] ~ { bucketTableSize: NAT _ smax - smin + 2; bucketTable _ IF scratch # NIL AND scratch.maxLength >= bucketTableSize THEN scratch ELSE NEW [BucketTableRec[bucketTableSize]]; bucketTable.length _ bucketTableSize; FOR i: NAT IN [0..bucketTableSize) DO bucketTable.seq[i] _ 0; ENDLOOP; FOR s: LIST OF SegmentRep _ devicePath.segmentList, s.rest UNTIL s = NIL DO start: INTEGER ~ MAX[s.first.sFirstScan, smin] - smin; end: INTEGER ~ MIN[s.first.sFirstScan+s.first.scanCount, smax] - smin; IF end > start THEN { bucketTable.seq[start] _ bucketTable.seq[start] + 1; bucketTable.seq[end] _ bucketTable.seq[end] - 1; }; ENDLOOP; FOR p: LIST OF PieceRep _ devicePath.pieceList, p.rest UNTIL p = NIL DO start: INTEGER ~ MAX[p.first.sFirstScan, smin] - smin; end: INTEGER ~ MIN[p.first.sFirstScan+p.first.scanCount, smax] - smin; IF end > start THEN { bucketTable.seq[start] _ bucketTable.seq[start] + 1; bucketTable.seq[end] _ bucketTable.seq[end] - 1; }; ENDLOOP; {sum, sumSum: NAT _ 0; FOR i: NAT IN [0..bucketTable.length) DO sum _ sum + bucketTable.seq[i]; bucketTable.seq[i] _ sumSum; sumSum _ sumSum + sum; ENDLOOP; }; }; BoundingBox: PUBLIC PROC [devicePath: DevicePath] RETURNS [ImagerScanConverter.DeviceRectangle] ~ { RETURN [[devicePath.sMin, devicePath.fMin, devicePath.sMax-devicePath.sMin, devicePath.fMax-devicePath.fMin]] }; NumberOfRuns: PUBLIC PROC [devicePath: DevicePath] RETURNS [numberOfRuns: INT] ~ { numberOfRuns _ devicePath.scanLineCrossings/2; }; maxBucketTableSize: NAT _ 808; maxPointTableSize: NAT _ 808*4; Convert: PROC [ devicePath: DevicePath, resultProc: PROC [devicePath: DevicePath, sStart, sEnd: INTEGER], clipBox: ImagerScanConverter.DeviceRectangle ] ~ { sStart: INTEGER _ MAX[clipBox.sMin, devicePath.sMin]; sFinal: INTEGER ~ MIN[INT[clipBox.sMin+clipBox.sSize], devicePath.sMax]; sEnd: INTEGER _ sFinal; fOrigin: INTEGER _ devicePath.fMin; bucketTable: BucketTable; WHILE sStart < sFinal DO IF sEnd - sStart > maxBucketTableSize THEN sEnd _ sStart + maxBucketTableSize; bucketTable _ devicePath.bucketTable _ InitialBucketTable[devicePath, sStart, sEnd, devicePath.bucketTable]; IF bucketTable.seq[bucketTable.length-1] > maxPointTableSize THEN { sEnd _ sStart; WHILE bucketTable.seq[sEnd-sStart] < maxPointTableSize DO sEnd _ sEnd+1; ENDLOOP; IF sEnd > sStart+1 THEN sEnd _ sEnd - 1; bucketTable _ devicePath.bucketTable _ InitialBucketTable[devicePath, sStart, sEnd, devicePath.bucketTable]; }; CHECKED { scanLineCrossings: NAT ~ bucketTable.seq[bucketTable.length-1]; pointTable: PointTable ~ devicePath.pointTable _ IF devicePath.pointTable # NIL AND devicePath.pointTable.maxLength >= scanLineCrossings THEN devicePath.pointTable ELSE NEW[PointTableRec[scanLineCrossings]]; points: NAT _ 0; FOR p: LIST OF SegmentRep _ devicePath.segmentList, p.rest UNTIL p = NIL DO firstScan: INTEGER ~ MAX[p.first.sFirstScan, sStart]; endScan: INTEGER ~ MIN[p.first.sFirstScan+p.first.scanCount, sEnd]; IF endScan > firstScan THEN TRUSTED { direction: Direction ~ p.first.direction; w: REAL ~ MAX[ABS[p.first.f1-p.first.f0], ABS[p.first.s1-p.first.s0]]; sDelta: INTEGER _ Real.RoundI[16536*(p.first.s1-p.first.s0)/w]; fDelta: INTEGER ~ Real.RoundI[16536*(p.first.f1-p.first.f0)/w]; fAbsDelta: INTEGER ~ ABS[fDelta]; fSignDelta: INTEGER ~ IF fAbsDelta = fDelta THEN 1 ELSE -1; sReal: REAL ~ firstScan + 0.5; fReal: REAL ~ p.first.f0 + (sReal-p.first.s0)*(p.first.f1-p.first.f0)/(p.first.s1-p.first.s0); f: INTEGER _ FloorI[fReal+0.5]; e: INTEGER _ IF fAbsDelta = fDelta THEN Real.RoundI[(f+0.5-fReal)*sDelta] ELSE Real.RoundI[(fReal-f+0.5)*sDelta]; bPtr: LONG POINTER TO INTEGER _ @(bucketTable.seq[firstScan-sStart]); IF sDelta <= 0 THEN { IF sDelta < 0 OR endScan # firstScan + 1 THEN ERROR; sDelta _ fAbsDelta; }; FOR s: INTEGER IN [firstScan..endScan) DO pointTable[bPtr^] _ [fRel: f-fOrigin, direction: direction]; bPtr^ _ bPtr^ + 1; points _ points + 1; bPtr _ bPtr + SIZE[INTEGER]; e _ e - fAbsDelta; WHILE e<0 DO f _ f + fSignDelta; e _ e + sDelta; ENDLOOP; ENDLOOP; }; ENDLOOP; FOR p: LIST OF PieceRep _ devicePath.pieceList, p.rest UNTIL p = NIL DO firstScan: INTEGER ~ MAX[p.first.sFirstScan, sStart]; endScan: INTEGER ~ MIN[p.first.sFirstScan+p.first.scanCount, sEnd]; direction: Direction _ p.first.direction; FOR s: INTEGER IN [firstScan..endScan) DO f: INTEGER _ FValueFor[p, s]; TRUSTED { bPtr: LONG POINTER TO INTEGER _ @(bucketTable.seq[s-sStart]); pointTable[bPtr^] _ [fRel: f-fOrigin, direction: direction]; bPtr^ _ bPtr^ + 1; points _ points + 1; }; ENDLOOP; ENDLOOP; IF points # scanLineCrossings THEN ERROR; resultProc[devicePath, sStart, sEnd]; }; sStart _ sEnd; sEnd _ sFinal; ENDLOOP; }; ConvertToRuns: PUBLIC PROC [ devicePath: DevicePath, runProc: PROC [sMin, fMin: INTEGER, fSize: NAT], clipBox: ImagerScanConverter.DeviceRectangle, parityFill: BOOLEAN _ FALSE ] ~ { Result: PROC [devicePath: DevicePath, sStart, sEnd: INTEGER] ~ { bucketTable: BucketTable ~ devicePath.bucketTable; pointTable: PointTable ~ devicePath.pointTable; parityMask: CARDINAL ~ IF parityFill THEN 1 ELSE LAST[CARDINAL]; scanLineCrossings: NAT ~ bucketTable.seq[bucketTable.length-1]; fOrigin: INTEGER ~ devicePath.fMin; wrap: INTEGER _ 0; pointNumber: NAT _ 0; FOR line: NAT IN [0..bucketTable.length) DO s: INTEGER ~ sStart + line; firstPointOnNextLine: NAT ~ bucketTable.seq[line]; numberOfPointsOnLine: NAT ~ firstPointOnNextLine - pointNumber; fStart: INTEGER; IF numberOfPointsOnLine = 2 THEN { p1, p2: Point; IF Key[p1 _ pointTable[pointNumber]] > Key[p2 _ pointTable[pointNumber+1]] THEN {pointTable[pointNumber] _ p2; pointTable[pointNumber+1] _ p1} } ELSE ShellSort[pointTable, pointNumber, numberOfPointsOnLine]; WHILE pointNumber < firstPointOnNextLine DO point: Point ~ pointTable[pointNumber]; pointNumber _ pointNumber + 1; IF BITANDI[wrap, parityMask] = 0 THEN fStart _ point.fRel + fOrigin; wrap _ wrap + deltaWrap[point.direction]; IF BITANDI[wrap, parityMask] = 0 THEN { fMin: INTEGER _ MAX[fStart, clipBox.fMin]; fMax: INTEGER _ MIN[point.fRel + fOrigin, clipBox.fMin+clipBox.fSize]; IF fMax > fMin THEN runProc[s, fMin, fMax-fMin]; }; ENDLOOP; IF wrap # 0 THEN ERROR; ENDLOOP; }; Convert[devicePath, Result, clipBox]; }; ConvertToPixels: PUBLIC PROC [ devicePath: DevicePath, pixelMap: ImagerPixelMaps.PixelMap, value: CARDINAL _ 1, parityFill: BOOLEAN _ FALSE, function: ImagerPixelMaps.Function ] ~ { clipBox: ImagerPixelMaps.DeviceRectangle _ ImagerPixelMaps.BoundedWindow[pixelMap]; lgBitsPerPixel: INTEGER _ pixelMap.refRep.lgBitsPerPixel; rast: CARDINAL _ pixelMap.refRep.rast; bbTableSpace: BitBlt.BBTableSpace; bbPtr: BitBlt.BBptr _ InitBBTable[]; InitBBTable: PROC RETURNS [bb: BitBlt.BBptr] ~ TRUSTED INLINE { bb _ BitBlt.AlignedBBTable[@bbTableSpace]; bb^ _ [ dst: [word: NIL, bit: 0], dstBpl: rast*Environment.bitsPerWord, src: [word: @replicatedPixel, bit: 0], srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]], height: 1, width: 0, flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: TRUE, srcFunc: function.srcFunc, dstFunc: function.dstFunc] ]; }; replicatedPixel: CARDINAL _ BITAND[value, Shift[1, Shift[1, lgBitsPerPixel]]-1] * ( SELECT lgBitsPerPixel FROM 0 => 0FFFFH, 1 => 05555H, 2 => 01111H, 3 => 00101H, 4 => 00001H, ENDCASE => ERROR ); Result: PROC [devicePath: DevicePath, sStart, sEnd: INTEGER] ~ TRUSTED { bb: BitBlt.BBptr ~ bbPtr; pointer: LONG POINTER ~ pixelMap.refRep.pointer; words: LONG CARDINAL ~ pixelMap.refRep.words; fBase: INTEGER ~ pixelMap.fOrigin; destLine: LONG POINTER _ pointer + Inline.LongMult[sStart-pixelMap.sOrigin, rast]; Run: PROC [fMin, fSize: INTEGER] ~ TRUSTED INLINE { bitIndex: CARDINAL _ Shift[fMin-fBase, lgBitsPerPixel]; bb.width _ Shift[fSize, lgBitsPerPixel]; bb.dst.word _ destLine + bitIndex / Environment.bitsPerWord; bb.dst.bit _ bitIndex MOD Environment.bitsPerWord; IF bb.dst.word-pointer < 0 OR bb.dst.word-pointer+NAT[bb.width+15]/16 > words THEN ERROR; BitBlt.BITBLT[bb]; }; bucketTable: BucketTable ~ devicePath.bucketTable; pointTable: PointTable ~ devicePath.pointTable; parityMask: CARDINAL ~ IF parityFill THEN 1 ELSE LAST[CARDINAL]; scanLineCrossings: NAT ~ bucketTable.seq[bucketTable.length-1]; fOrigin: INTEGER ~ devicePath.fMin; wrap: INTEGER _ 0; pointNumber: NAT _ 0; FOR line: NAT IN [0..bucketTable.length) DO s: INTEGER ~ sStart + line; firstPointOnNextLine: NAT ~ bucketTable.seq[line]; numberOfPointsOnLine: NAT ~ firstPointOnNextLine - pointNumber; fStart: INTEGER; IF numberOfPointsOnLine = 2 THEN { p1, p2: Point; IF Key[p1 _ pointTable[pointNumber]] > Key[p2 _ pointTable[pointNumber+1]] THEN {pointTable[pointNumber] _ p2; pointTable[pointNumber+1] _ p1} } ELSE ShellSort[pointTable, pointNumber, numberOfPointsOnLine]; WHILE pointNumber < firstPointOnNextLine DO point: Point ~ pointTable[pointNumber]; pointNumber _ pointNumber + 1; IF BITANDI[wrap, parityMask] = 0 THEN fStart _ point.fRel + fOrigin; wrap _ wrap + deltaWrap[point.direction]; IF BITANDI[wrap, parityMask] = 0 THEN { fMin: INTEGER _ MAX[fStart, clipBox.fMin]; fMax: INTEGER _ MIN[point.fRel + fOrigin, clipBox.fMin+clipBox.fSize]; IF fMax > fMin THEN Run[fMin, fMax-fMin]; }; ENDLOOP; IF wrap # 0 THEN ERROR; destLine _ destLine + rast; ENDLOOP; }; Convert[devicePath, Result, clipBox]; }; END. ,ImagerScanConverterImpl.mesa Michael Plass, October 3, 1983 9:30 am -- These parameters control the precision with which the inner loop b subdivision is done. -- Some inlines for basic bit-hacking operations. In device coordinates, the coordinates of point i is (fi/fScale + 1/2 + fOrg, (si+sOffset)/sScale + sOrg) this could be speeded up, if necessary First find the coefficients of the s projection. Now find the coefficients of the derivative. Find where the derivative vanishes. Now break it up as needed force monotonicity, in case of roundoff error force monotonicity, in case of roundoff error 1 word long, ordered so it can be sorted like a cardinal w cannot be exactly zero, since otherwise endScan = firstScan. Only one scan line, so sDelta just needs to be positive to avoid an infinite loop. ΚΕ˜Jšœ™J™&unitšΟk ˜ J˜J˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœ ˜&Jšœ/˜6Jšœ˜Jšœ˜—Jšœ˜Kšœœœ"œ˜=Kšœœœœ˜!šœ[™[Jšœ œ˜JšœœΟc˜,Jšœ œž!˜5—™1š Πknœœœœœ˜/Jšœœœœ ˜'—š Ÿœœœœœ˜.Jšœœœœ ˜'—š Ÿœœœœœ˜.Jšœœœœ ˜&—š Ÿœœœœœ˜/Jšœœœœ ˜'—š Οnœœœœœœ˜8Jšœœœœ ˜)—š  œœœœœ˜6Jšœœœœ˜1—š  œœœœœ˜,Jšœœœœ ˜-—š  œœœœœ˜+Jšœœœœ ˜5—š  œœœœœ˜-Jšœœœœ ˜5——Jšœ œ˜+Jšœ œ œœ ˜0šœ œœ˜Jšœ œž˜'Jšœœ˜Jšœ œ˜Jšœ œžH˜]Jšœ œž.˜CJšœ˜Jšœœ˜Jšœœ˜ Jšœœ˜Jšœœ˜šœ˜šœ4™4Jšœ4™4——J˜—šœ œœ˜Jšœ œžJ˜_Jšœ˜Jšœ œž0˜@Jšœ˜J˜—Jšœ  œœ˜,šœ œœ˜%Jšœœ˜Jšœœ˜ Jšœ˜Jšœ˜Jšœ œœ ˜ Jšœ œœ ˜J˜—š  œœ œœœœœ˜YJšœ œœœ˜*Jšœ˜JšœœœE˜YJšœ œœ!˜;šœ˜%Jšœœ˜šœœœœ˜!Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœ˜Jšœ˜šœœ˜"JšœE˜EJšœ;˜;Jšœ˜Jšœ˜Jšœ˜—šœ˜Jšœœ˜(Jšœ˜JšœD˜DJšœC˜CJšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ œœ˜š œœœœ œœ˜FJšœ&˜*—šœœ ˜Jšœ_˜cJšœe˜i—Jšœ˜—š œœ œœ˜LJšœœ˜Jšœœ˜J™&Jšœœ"˜-Jšœœ˜Jšœœ˜Jšœœ%˜-Jšœœ%˜-Jš œœœœ œœ˜.Jš œœœœœ˜)Jšœ˜šœ˜ Jšœ,˜,Jšœ˜Jšœ˜—Jšœœ œ?˜VJšœ˜—Kšœ œ˜š  œœœœ ˜:Jšœœ'˜.—š  œœœœ˜AJš˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœ7˜7Jšœ8˜8Jšœ˜—š  œœœ˜Jšœ'˜'Jšœ-˜-Jšœœž˜2Jšœœ˜Jšœ˜š  œœœ˜.Jšœ˜šœ œ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ.˜.Jšœœ:˜Bšœœ˜JšœP˜PJšœ&œ&˜RJšœœ&œ:˜ƒJšœœ:˜OJšœœ>˜Sš œ œœœ˜2Jšœœ!˜>—šœ˜Jšœœœ"˜,Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜—š œœœ˜:Jšœœ˜2Jšœœ&˜Ešœ˜Jšœ˜Jšœœ#˜-Jšœœœ ˜Jšœ œ˜Jšœ œ˜Jšœ˜š œœœœ œœ˜EJšœ˜Jšœ˜Jšœ ˜ Jšœ˜—šœ œ˜Jšœ=˜=Jšœ˜Jšœ˜—Jšœ˜"Jšœ ˜ Jšœ!˜!Jšœ9˜9Jšœ9˜9Jšœ9˜9Jšœ9˜9Jšœ<˜Jš œ œœœœœœ ˜MJš œ œœœœœœ˜>Jš œ œœœœœœ ˜MJš œ œœœœœ˜BJš œ œœœœœ˜AJš œ œœœœœ˜BJš œ œœœœœ˜AJš œœœœœœœœ˜SJš œœœœœœœœ˜Sš œœ˜/š  œœ˜Jš œœœœœœœ˜MJšœA˜AJšœA˜AJšœA˜AJšœ*˜*Jšœ*˜*Jšœ/˜/JšœQ˜QJšœQ˜QJ˜—Jšœœœ-˜=Jšœœœ-˜=Jšœ œœ˜*Jšœœœ ˜0Jšœœœ-˜=šœ œœ˜&J˜J˜J˜J˜Jšœ˜ —šœ œœ˜&J˜J˜J˜J˜Jšœ˜ —šœ œœ˜&J˜J˜J˜J˜Jšœ˜ —šœ œœ˜&J˜J˜J˜J˜Jšœ˜ —Jš œœœœœœ˜Ošœ˜Jšœœœœœœœœ œ'˜©Jšœ ˜Jšœ˜—š œœœœœ˜Qšœ˜Jšœ#˜#Jšœ#˜#Jšœ#˜#Jšœ"˜"Jšœ˜——Jšœ ˜J˜—š  œœ˜"™0Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ ˜Jšœœ˜—™,Jšœœ˜Jšœœ˜Jšœœ˜—™#Jšœœ˜Jšœ œ˜šœœ œ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜šœœ˜Jšœœ˜Jšœ œœœ ˜,Jš œœœ œœ˜'Jšœ˜—Jšœ˜—Jšœœœ œ ˜%—™Jšœœœ ˜Jšœœœ ˜Jšœ œœ˜0šœ œ œ œ˜+Jšœ˜Jšœ"˜"Jšœ)˜)™-Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœ˜Jšœ˜—š œœœ œœ œ˜3Jšœ˜Jš œœœ œœ˜@™-Jšœ˜Jšœ˜—Jšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜—Jšœ˜Jšœ œœ˜š œœœ˜Jšœ œ œ œ˜,Jšœ ˜ Jšœ˜—š œœœ˜J˜ Jšœœ œ ˜Jšœ œœ œ˜RJšœœœœ˜?Jšœ.˜2šœ˜Jšœ.˜.Jšœ.˜.JšœG˜GJ˜—Jšœ˜Jšœ˜—š œœœ˜.Jšœœ œ˜!JšœA˜AJšœ˜Jšœ˜—š œœ˜Jšœœ"˜@Jšœ˜—Jšœ!˜!Jšœœœ˜ Jšœœœ˜!Jšœœœ˜ Jšœœœ˜!Jšœ˜Jšœ œ ˜šœ#˜)JšœJ˜J—Jšœ œœsœ ˜šJšœœ˜)Jšœ˜—š œœœ œœ˜(J™8Jšœœž˜)Jšœ˜J˜—Jšœ œœ˜%šœœœ˜Jšœ œœ˜ J˜—Jšœœ œœ<˜]Jš œœœœœœœœ ˜Wš  œœ$œœ˜Cšœ œ˜Jšœ˜Jšœ œœœ˜2Jš œœœœ$œ˜GJšœ3œœœ˜Hš œœ œœ˜1Jšœœ˜Jšœ œœ˜%š œœ œœœ˜UJšœ˜Jšœœœœ˜#Jšœœœœ˜šœœœœœ œœœ˜qJšœ ˜ Jšœ ˜ Jšœ˜—Jšœ ˜ Jšœ˜—Jšœ˜—Jšœ˜—Jšœ˜—Jšœ œœ˜'Jšœœœ œœ œœœ˜Uš  œœ œœœ˜ŒJšœœ˜'š œœ œœ&œ˜TJšœœ#˜+—Jšœ%˜%šœœœ˜%Jšœ˜Jšœ˜—š œœœ-œœ˜KJšœœœ"˜6Jšœœœ4˜Fšœ œ˜Jšœ4˜4Jšœ0˜0Jšœ˜—Jšœ˜—š œœœ)œœ˜GJšœœœ"˜6Jšœœœ4˜Fšœ œ˜Jšœ4˜4Jšœ0˜0Jšœ˜—Jšœ˜—šœœ˜šœœœ˜(Jšœ˜Jšœ˜Jšœ˜Jšœ˜—J˜—Jšœ˜—š  œœœœ*˜cJšœg˜mJšœ˜—š   œœœœœ˜RJšœ.˜.Jšœ˜—Jšœœ˜Jšœœ ˜š œœ˜Jšœ˜Jšœ œœœ˜AJšœ,˜,Jšœ˜Jšœœœ ˜5Jšœœœœ/˜HJšœœ ˜Jšœ œ˜#Jšœ˜šœ˜Jšœ$œ$˜NJšœl˜lšœ;œ˜CJšœ˜šœ2˜9Jšœ˜Jšœ˜—Jšœœ˜(Jšœl˜lJšœ˜—šœ˜ Jšœœ)˜?šœ.˜0Jšœ˜Jšœ5˜8Jšœ˜Jšœœ#˜+—Jšœœ˜š œœœ-œœ˜KJšœ œœ˜5Jšœ œœ-˜Cšœœœ˜%Jšœ)˜)š œœœœœ˜FJšœ>™>—Jšœœ0˜?Jšœœ0˜?Jšœ œœ ˜!Jš œ œœœœ˜;Jšœœ˜JšœœS˜^Jšœœ˜šœœ˜ Jšœ˜Jšœ"˜&Jšœ#˜'—Jš œœœœœ(˜Ešœ œ˜Jšœ œœœ˜4šœ˜JšœR™R—Jšœ˜—šœœœ˜)Jšœ<˜˜>—J˜—Jšœ:˜>šœ$˜+Jšœ'˜'Jšœ˜Jšœœœ˜DJšœ)˜)šœœœ˜'Jšœœœ˜*Jšœœœ3˜FJšœ œ˜0J˜—Jšœ˜—Jšœ œœ˜Jšœ˜—Jšœ˜—Jšœ%˜%Jšœ˜—š œœœ˜Jšœ˜Jšœ#˜#Jšœœ˜Jšœ œœ˜Jšœ"˜"Jšœ˜JšœS˜SJšœœ"˜9Jšœœ˜&J˜"Jšœ$˜$š   œœœœœ˜?Jšœ*˜*˜Jšœ œ ˜Jšœ%˜%Jšœ&˜&JšœC˜CJ˜ Jšœ ˜ Jšœ&œœœ7˜‚J˜—Jšœ˜—šœœ˜šœ1˜7šœ˜JšœΟfœ˜ Jšœ‘œ˜ Jšœ‘œ˜ Jšœ‘œ˜ Jšœ‘œ˜ Jšœ˜—Jšœ˜——š œœ(œœ˜HJ˜Jšœ œœ˜0Jšœœœ˜-Jšœœ˜"Jšœ œœ<˜Rš  œœœœœ˜3Jšœ œ%˜7Jšœ(˜(Jšœ<˜˜>—J˜—Jšœ:˜>šœ$˜+Jšœ'˜'Jšœ˜Jšœœœ˜DJšœ)˜)šœœœ˜'Jšœœœ˜*Jšœœœ3˜FJšœ œ˜)J˜—Jšœ˜—Jšœ œœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ%˜%Jšœ˜—Jšœ˜—…—_