DIRECTORY Basics USING [BITAND, BITSHIFT, bitsPerWord, BoundsCheck, CARD, DoubleShiftRight, HighHalf, logBitsPerWord, LongDivMod, LongMult, LongNumber, NonNegative, RawWords, ShortNumber], CountedVM USING [Handle, SimpleAllocate], FunctionCache USING [Cache, CacheInfo, GetInfo, GlobalCache, SetLimits], ImagerSample, ImagerTransformation, PrincOps USING [BBptr, BBTableSpace, BitAddress, BitBltFlags, BitBltTable], PrincOpsUtils USING [AlignedBBTable, BITBLT, LongCopy, LongZero], Real, SF, Terminal USING [FrameBuffer], VM USING [CantAllocate, wordsPerPage]; ImagerSampleImpl: CEDAR MONITOR IMPORTS Basics, CountedVM, FunctionCache, ImagerTransformation, PrincOpsUtils, Real, SF, VM EXPORTS ImagerSample ~ BEGIN OPEN ImagerSample; MultipleReleaseOfScratch: PUBLIC ERROR ~ CODE; NewSamples: PUBLIC PROC [length: NAT, scratch: SampleBuffer _ NIL] RETURNS [SampleBuffer] ~ { new: SampleBuffer _ scratch; IF new=NIL OR new.maxLength= length THEN { scratchSampleBuffers[i] _ NIL; buf.length _ length; RETURN [buf]; }; ENDLOOP; RETURN [NEW[SampleBufferRep[length] _ [length: length, samples:]]] }; ReleaseScratchSamples: PUBLIC ENTRY PROC [samples: SampleBuffer] ~ { r: SampleBuffer _ samples; samples.length _ 0; FOR i: NAT IN [0..nScratchSampleBuffers) UNTIL r = NIL DO t: SampleBuffer _ scratchSampleBuffers[i]; scratchSampleBuffers[i] _ r; r _ t; IF r = samples THEN RETURN WITH ERROR MultipleReleaseOfScratch; ENDLOOP; }; DoWithScratchSamples: PUBLIC PROC [length: NAT, action: PROC [SampleBuffer]] ~ { scratch: SampleBuffer ~ ObtainScratchSamples[length]; action[scratch ! UNWIND => ReleaseScratchSamples[scratch]]; ReleaseScratchSamples[scratch]; }; InlinePointerToSamples: PROC [samples: SampleBuffer, start: NAT, count: NAT] RETURNS [LONG POINTER TO RawSamples] ~ TRUSTED INLINE { check: NAT ~ NAT[samples.maxLength-start]-count; RETURN[LOOPHOLE[samples, LONG POINTER]+SIZE[SampleBufferRep[start]]]; }; PointerToSamples: PUBLIC PROC [samples: SampleBuffer, start: NAT, count: NAT] RETURNS [LONG POINTER TO RawSamples] ~ { RETURN [InlinePointerToSamples[samples, start, count]] }; FlipSamples: PUBLIC PROC [samples: SampleBuffer] ~ { size: NAT ~ samples.length; IF size>0 THEN TRUSTED { pointer: LONG POINTER TO RawSamples ~ InlinePointerToSamples[samples, 0, size]; last: NAT ~ size-1; FOR j: NAT IN [0..size/2) DO k: NAT ~ last-j; t: Sample ~ pointer[j]; pointer[j] _ pointer[k]; pointer[k] _ t; ENDLOOP; }; }; FillSamples: PUBLIC PROC [samples: SampleBuffer, value: Sample, start, count: NAT] ~ { size: NAT ~ MIN[samples.length-start, count]; IF size>0 THEN TRUSTED { pointer: LONG POINTER TO RawSamples ~ InlinePointerToSamples[samples, start, size]; pointer[0] _ value; PrincOpsUtils.LongCopy[from: pointer, nwords: size-1, to: pointer+1]; }; }; bitsPerWord: NAT ~ Basics.bitsPerWord; logBitsPerWord: NAT ~ Basics.logBitsPerWord; CARD: TYPE ~ Basics.CARD; nullBitBltTable: PrincOps.BitBltTable ~ [ dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0, flags: [] ]; IsPowerOfTwo: PROC [c: CARDINAL] RETURNS [BOOL] ~ INLINE { RETURN[Basics.BITAND[c, c-1]=0]; }; WordsForBits: PROC [bitCount: CARD] RETURNS [CARD] ~ INLINE { RETURN[Basics.DoubleShiftRight[[lc[bitCount+(bitsPerWord-1)]], logBitsPerWord].lc] }; Lg: PROC [a: NAT] RETURNS [lg: NAT _ 0] ~ { b: CARDINAL _ 1; UNTIL b >= a DO lg _ lg + 1; b _ b*2; ENDLOOP; }; IndexBit: PROC [addr: PrincOps.BitAddress, offset: CARD] RETURNS [PrincOps.BitAddress] ~ TRUSTED INLINE { Addr: TYPE ~ MACHINE DEPENDENT RECORD [pointer: LONG POINTER, offset: CARDINAL]; pointer: LONG POINTER ~ LOOPHOLE[addr, Addr].pointer; index: Basics.LongNumber ~ [lc[LOOPHOLE[addr, Addr].offset+offset]]; RETURN[LOOPHOLE[Addr[ pointer: pointer+Basics.DoubleShiftRight[index, logBitsPerWord].lc, offset: index.lo MOD bitsPerWord ]]]; }; BitsForSamples: PUBLIC PROC [fSize: NAT, bitsPerSample: NAT] RETURNS [NAT] ~ { RETURN[Basics.BITAND[Basics.LongMult[fSize, bitsPerSample]+(bitsPerWord-1), WORD.LAST-(bitsPerWord-1)]]; }; WordsForLines: PUBLIC PROC [sSize: NAT, bitsPerLine: NAT] RETURNS [INT] ~ { IF (bitsPerLine MOD bitsPerWord)=0 THEN RETURN[Basics.LongMult[sSize, bitsPerLine/bitsPerWord]] ELSE RETURN[WordsForBits[Basics.LongMult[sSize, bitsPerLine]]]; }; WordsForMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample, bitsPerLine: NAT _ 0] RETURNS [INT] ~ { IF bitsPerLine=0 THEN bitsPerLine _ BitsForSamples[size.f, bitsPerSample]; RETURN[WordsForLines[size.s, bitsPerLine]]; }; UnsafeNewSampleMap: PUBLIC UNSAFE PROC [size: Vec, bitsPerSample: BitsPerSample, bitsPerLine: NAT, base: PrincOps.BitAddress, ref: REF, words: INT, scratchDescriptor: SampleMap _ NIL] RETURNS [SampleMap] ~ { dataBitsPerLine: INT ~ Basics.LongMult[NAT[size.f], bitsPerSample]; fillBits: INT ~ Basics.NonNegative[bitsPerLine-dataBitsPerLine]; -- check sufficient bitsPerLine totalBits: INT ~ INT[base.bit]+Basics.LongMult[NAT[size.s], bitsPerLine]-fillBits; totalWords: INT ~ WordsForBits[totalBits]; spareWords: INT ~ Basics.NonNegative[words-totalWords]; -- check sufficient words s: SampleMap _ scratchDescriptor; IF s=NIL THEN s _ NEW[SampleMapRep]; s^ _ [size: size, bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine, base: base, ref: ref]; RETURN[s]; }; WordSeqRep: TYPE ~ RECORD [SEQUENCE length: NAT OF WORD]; maxWordSeqWords: INT _ VM.wordsPerPage-VM.wordsPerPage/10-SIZE[WordSeqRep[0]]; NewSampleMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample, bitsPerLine: NAT _ 0, scratch: SampleMap _ NIL] RETURNS [SampleMap] ~ { actualBitsPerLine: NAT ~ SELECT bitsPerLine FROM 0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine; IF scratch#NIL AND scratch.size.Eq[size] AND scratch.bitsPerSample=bitsPerSample AND scratch.bitsPerLine=actualBitsPerLine THEN RETURN[scratch] ELSE { words: INT ~ WordsForLines[size.s, actualBitsPerLine]; IF words <= maxWordSeqWords THEN { wordSeq: REF WordSeqRep ~ NEW[WordSeqRep[words]]; TRUSTED { RETURN[UnsafeNewSampleMap[ size: size, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine, base: [word: @wordSeq[0], bit: 0], ref: wordSeq, words: wordSeq.length ]] }; } ELSE RETURN[MapFromVM[vm: Allocate[words], size: size, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine]]; }; }; Allocate: PROC [nWords: CARD] RETURNS [vm: CountedVM.Handle _ NIL] ~ { nWords _ MAX[nWords, 256]; vm _ CountedVM.SimpleAllocate[nWords ! VM.CantAllocate => CONTINUE]; IF vm = NIL THEN { cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[]; info: FunctionCache.CacheInfo ~ FunctionCache.GetInfo[cache]; FunctionCache.SetLimits[cache, 0, 0]; FunctionCache.SetLimits[x: cache, maxEntries: info.maxEntries, maxTotalSize: info.maxTotalSize]; ClearScratchMaps[]; vm _ CountedVM.SimpleAllocate[nWords]; }; }; MapFromVM: PUBLIC PROC [vm: CountedVM.Handle, size: Vec, bitsPerSample: BitsPerSample _ 1, bitsPerLine: NAT _ 0 ] RETURNS [SampleMap] ~ { actualBitsPerLine: NAT ~ SELECT bitsPerLine FROM 0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine; TRUSTED { RETURN[UnsafeNewSampleMap[ size: size, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine, base: [word: vm.pointer, bit: 0], ref: vm, words: vm.words ]] }; }; MapFromFrameBuffer: PUBLIC PROC [frameBuffer: Terminal.FrameBuffer] RETURNS [SampleMap] ~ { bitsPerLine: NAT ~ Basics.LongMult[frameBuffer.wordsPerLine, bitsPerWord]; vm: CountedVM.Handle ~ frameBuffer.vm; base: LONG POINTER ~ frameBuffer.base; baseOffset: INT ~ LOOPHOLE[base, INT]-LOOPHOLE[vm.pointer, INT]; words: INT ~ vm.words-Basics.NonNegative[baseOffset]; TRUSTED { RETURN[UnsafeNewSampleMap[ size: [s: frameBuffer.height, f: frameBuffer.width], bitsPerSample: frameBuffer.bitsPerPixel, bitsPerLine: bitsPerLine, base: [word: base, bit: 0], ref: vm, words: words ]] }; }; SubMap: PUBLIC PROC [self: SampleMap, box: Box] RETURNS [SampleMap] ~ TRUSTED { size: Vec ~ self.size.Min[box.max].Sub[box.min]; skipBits: CARD ~ Basics.LongMult[box.min.s, self.bitsPerLine] + Basics.LongMult[box.min.f, self.bitsPerSample]; new: SampleMap ~ NEW[SampleMapRep _ self^]; new.size _ size; new.base _ IndexBit[self.base, skipBits]; RETURN[new]; }; CopyMap: PUBLIC PROC [self: SampleMap, box: Box] RETURNS [SampleMap] ~ { size: Vec ~ self.size.Min[box.max].Sub[box.min]; new: SampleMap ~ NewSampleMap[size: size, bitsPerSample: self.bitsPerSample]; Transfer[dst: new, src: self, srcMin: box.min]; RETURN[new]; }; nScratchMaps: NAT ~ 5; scratchMaps: ARRAY [0..nScratchMaps) OF SampleMap _ ALL[NIL]; ObtainScratchMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample] RETURNS [SampleMap] ~ { TryObtainScratch: ENTRY PROC RETURNS [SampleMap] ~ TRUSTED { bitsPerLine: NAT ~ BitsForSamples[fSize: size.f, bitsPerSample: bitsPerSample]; nWords: INT ~ WordsForLines[sSize: size.s, bitsPerLine: bitsPerLine]; FOR i: NAT IN [0..nScratchMaps) DO s: SampleMap _ scratchMaps[i]; IF s # NIL THEN WITH s.ref SELECT FROM vm: CountedVM.Handle => { IF vm.words >= nWords THEN { scratchMaps[i] _ NIL; s.size _ size; s.bitsPerSample _ bitsPerSample; s.bitsPerLine _ bitsPerLine; s.base _ [word: vm.pointer, bit: 0]; RETURN [s] }; }; ENDCASE => NULL; ENDLOOP; RETURN [NIL] }; s: SampleMap _ TryObtainScratch[]; IF s = NIL THEN { vm: CountedVM.Handle ~ Allocate[WordsForMap[size, bitsPerSample]]; s _ MapFromVM[vm: vm, size: size, bitsPerSample: bitsPerSample]; }; RETURN [s] }; ReleaseScratchMap: PUBLIC ENTRY PROC [self: SampleMap] ~ { r: SampleMap _ self; FOR i: NAT IN [0..nScratchMaps) UNTIL r = NIL DO t: SampleMap _ scratchMaps[i]; scratchMaps[i] _ r; r _ t; IF r = self THEN RETURN WITH ERROR MultipleReleaseOfScratch; ENDLOOP; }; ClearScratchMaps: ENTRY PROC ~ { FOR i: NAT IN [0..nScratchMaps) DO scratchMaps[i] _ NIL; ENDLOOP; }; DoWithScratchMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample, action: PROC [SampleMap]] ~ { scratch: SampleMap ~ ObtainScratchMap[size, bitsPerSample]; action[scratch ! UNWIND => ReleaseScratchMap[scratch]]; ReleaseScratchMap[scratch]; }; Clear: PUBLIC PROC [self: SampleMap] ~ TRUSTED { bitsPerLine: NAT ~ self.bitsPerLine; pointer: LONG POINTER _ self.base.word; fillBitsPerLine: NAT ~ bitsPerLine - self.bitsPerSample*self.size.f; IF fillBitsPerLine < bitsPerWord THEN { nBits: CARD ~ Basics.LongMult[bitsPerLine, self.size.s]+self.base.bit-fillBitsPerLine; nWords: CARD _ WordsForBits[nBits]; WHILE nWords > 32768 DO PrincOpsUtils.LongZero[pointer, 32768]; pointer _ pointer + 32768; nWords _ nWords - 32768; ENDLOOP; PrincOpsUtils.LongZero[pointer, nWords]; } ELSE { dataBitsPlus: NAT ~ self.bitsPerSample*self.size.f + (bitsPerWord-1); bit: CARDINAL _ self.base.bit; FOR i: NAT IN [0..self.size.s) DO PrincOpsUtils.LongZero[where: pointer, nwords: (bit + dataBitsPlus)/bitsPerWord]; bit _ bit + bitsPerLine; pointer _ pointer + bit/bitsPerWord; bit _ bit MOD bitsPerWord; ENDLOOP; }; }; Get: PUBLIC PROC [self: SampleMap, index: Vec] RETURNS [Sample] ~ TRUSTED { bitsPerSample: NAT ~ self.bitsPerSample; bitIndex: CARD ~ Basics.LongMult[Basics.BoundsCheck[index.s, self.size.s], self.bitsPerLine] + Basics.LongMult[Basics.BoundsCheck[index.f, self.size.f], bitsPerSample]; bitAddress: PrincOps.BitAddress ~ IndexBit[self.base, bitIndex]; IF bitsPerSample=1 THEN TRUSTED { pointer: LONG POINTER TO Basics.ShortNumber ~ bitAddress.word; RETURN[LOOPHOLE[pointer.bits[bitAddress.bit]]]; } ELSE TRUSTED { pointer: LONG POINTER TO Basics.RawWords ~ bitAddress.word; shiftAmt: INTEGER ~ bitAddress.bit + bitsPerSample - bitsPerWord; mask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1; shiftedBits: WORD ~ IF shiftAmt <= 0 THEN Basics.BITSHIFT[pointer[0], shiftAmt] ELSE Basics.BITSHIFT[pointer[0], shiftAmt] + Basics.BITSHIFT[pointer[1], shiftAmt-16]; RETURN[Basics.BITAND[shiftedBits, mask]]; }; }; Put: PUBLIC PROC [self: SampleMap, index: Vec, value: Sample, function: Function _ nullFunction] ~ { bitsPerSample: NAT ~ self.bitsPerSample; bitIndex: CARD ~ Basics.LongMult[Basics.BoundsCheck[index.s, self.size.s], self.bitsPerLine] + Basics.LongMult[Basics.BoundsCheck[index.f, self.size.f], bitsPerSample]; bitAddress: PrincOps.BitAddress ~ IndexBit[self.base, bitIndex]; IF bitsPerSample=1 AND function=nullFunction THEN TRUSTED { pointer: LONG POINTER TO Basics.ShortNumber ~ bitAddress.word; pointer.bits[bitAddress.bit] _ LOOPHOLE[value]; } ELSE TRUSTED { bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; bb^ _ nullBitBltTable; bb.dst _ bitAddress; bb.src.word _ @value; bb.src.bit _ bitsPerWord-bitsPerSample; bb.height _ 1; bb.width _ bitsPerSample; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; PrincOpsUtils.BITBLT[bb]; }; }; GetSamples: PUBLIC PROC [self: SampleMap, min: Vec _ zeroVec, delta: Vec _ [s: 0, f: 1], samples: SampleBuffer, start: NAT _ 0, count: NAT _ maxCount] ~ { size: NAT ~ MIN[samples.length-start, count]; IF size#0 THEN TRUSTED { bitsPerLine: NAT ~ self.bitsPerLine; bitsPerSample: NAT ~ self.bitsPerSample; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; bbTableSpace: PrincOps.BBTableSpace; bufferPointer: LONG POINTER ~ InlinePointerToSamples[samples, start, size]; srcBitIndex: CARD _ Basics.LongMult[Basics.BoundsCheck[min.s, self.size.s], bitsPerLine] + Basics.LongMult[Basics.BoundsCheck[min.f, self.size.f], bitsPerSample]; srcBitDelta: NAT ~ Basics.LongMult[delta.s, bitsPerLine] + Basics.LongMult[delta.f, bitsPerSample]; srcBitSkipDelta: NAT _ srcBitDelta; lgSkip: NAT _ 0; skipMinusOne: NAT _ 0; IF delta.s#0 THEN []_Basics.BoundsCheck[min.s+Basics.LongMult[size-1, delta.s], self.size.s]; IF delta.f#0 THEN []_Basics.BoundsCheck[min.f+Basics.LongMult[size-1, delta.f], self.size.f]; WHILE srcBitSkipDelta<=NAT.LAST/2 AND CARDINAL[srcBitSkipDelta] MOD bitsPerWord#0 DO srcBitSkipDelta _ srcBitSkipDelta*2; lgSkip _ lgSkip+1; ENDLOOP; skipMinusOne _ Basics.BITSHIFT[1, lgSkip]-1; PrincOpsUtils.LongZero[where: bufferPointer, nwords: size]; bb^ _ nullBitBltTable; bb.dst.bit _ bitsPerWord-bitsPerSample; bb.dstBpl _ Basics.BITSHIFT[bitsPerWord, lgSkip]; bb.srcDesc _ [srcBpl[srcBitSkipDelta]]; bb.width _ bitsPerSample; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; FOR i: NAT IN [0..Basics.BITSHIFT[1, lgSkip]) DO bb.src _ IndexBit[self.base, srcBitIndex]; bb.height _ Basics.BITSHIFT[size-i+skipMinusOne, -lgSkip]; bb.dst.word _ bufferPointer+i; PrincOpsUtils.BITBLT[bb]; srcBitIndex _ srcBitIndex + srcBitDelta; ENDLOOP; }; }; PutSamples: PUBLIC PROC [self: SampleMap, min: Vec _ zeroVec, delta: Vec _ [s: 0, f: 1], samples: SampleBuffer, start: NAT _ 0, count: NAT _ maxCount, function: Function _ nullFunction] ~ { size: NAT ~ MIN[samples.length-start, count]; IF size # 0 THEN TRUSTED { bitsPerLine: NAT ~ self.bitsPerLine; bitsPerSample: NAT ~ self.bitsPerSample; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; bbTableSpace: PrincOps.BBTableSpace; bufferPointer: LONG POINTER ~ InlinePointerToSamples[samples, start, size]; dstBitIndex: CARD _ Basics.LongMult[Basics.BoundsCheck[min.s, self.size.s], bitsPerLine] + Basics.LongMult[Basics.BoundsCheck[min.f, self.size.f], bitsPerSample]; dstBitDelta: NAT _ Basics.LongMult[delta.s, bitsPerLine] + Basics.LongMult[delta.f, bitsPerSample]; dstBitSkipDelta: NAT _ dstBitDelta; lgSkip: NAT _ 0; skipMinusOne: NAT _ 0; IF delta.s#0 THEN []_Basics.BoundsCheck[min.s+Basics.LongMult[size-1, delta.s], self.size.s]; IF delta.f#0 THEN []_Basics.BoundsCheck[min.f+Basics.LongMult[size-1, delta.f], self.size.f]; WHILE dstBitSkipDelta<=NAT.LAST/2 AND CARDINAL[dstBitSkipDelta] MOD bitsPerWord#0 DO dstBitSkipDelta _ dstBitSkipDelta*2; lgSkip _ lgSkip+1; ENDLOOP; skipMinusOne _ Basics.BITSHIFT[1, lgSkip]-1; bb^ _ nullBitBltTable; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; bb.dstBpl _ dstBitSkipDelta; bb.src.bit _ bitsPerWord-bitsPerSample; bb.srcDesc _ [srcBpl[Basics.BITSHIFT[bitsPerWord, lgSkip]]]; bb.width _ bitsPerSample; FOR i: NAT IN [0..Basics.BITSHIFT[1, lgSkip]) DO bb.dst _ IndexBit[self.base, dstBitIndex]; bb.height _ Basics.BITSHIFT[size-i+skipMinusOne, -lgSkip]; bb.src.word _ bufferPointer+i; PrincOpsUtils.BITBLT[bb]; dstBitIndex _ dstBitIndex + dstBitDelta; ENDLOOP; }; }; DumbTransfer: PROC [dst: SampleMap, src: SampleMap, dstMin: Vec _ zeroVec, srcMin: Vec _ zeroVec, size: Vec _ maxVec, function: Function _ nullFunction] ~ { sSize: NAT ~ MIN[dst.size.s-dstMin.s, src.size.s-srcMin.s, size.s]; fSize: NAT ~ MIN[dst.size.f-dstMin.f, src.size.f-srcMin.f, size.f]; FOR s: NAT IN [0..sSize) DO FOR f: NAT IN [0..fSize) DO offset: Vec ~ [s: s, f: f]; value: Sample ~ Get[src, srcMin.Add[offset]]; Put[dst, dstMin.Add[offset], value, function]; ENDLOOP; ENDLOOP; }; BufferTransfer: PROC [dst: SampleMap, src: SampleMap, dstMin: Vec _ zeroVec, srcMin: Vec _ zeroVec, size: Vec _ maxVec, function: Function _ nullFunction] ~ { sSize: NAT ~ MIN[dst.size.s-dstMin.s, src.size.s-srcMin.s, size.s]; fSize: NAT ~ MIN[dst.size.f-dstMin.f, src.size.f-srcMin.f, size.f]; transferAction: PROC [samples: SampleBuffer] ~ { FOR s: NAT IN [0..sSize) DO GetSamples[self: src, min: [s: srcMin.s+s, f: srcMin.f], samples: samples]; PutSamples[self: dst, min: [s: dstMin.s+s, f: dstMin.f], samples: samples, function: function]; ENDLOOP; }; DoWithScratchSamples[length: fSize, action: transferAction]; }; Transfer: PUBLIC PROC [dst: SampleMap, src: SampleMap, dstMin: Vec _ zeroVec, srcMin: Vec _ zeroVec, size: Vec _ maxVec, function: Function _ nullFunction] ~ { bitsPerSample: NAT ~ dst.bitsPerSample; IF src.bitsPerSample=bitsPerSample THEN TRUSTED { sSize: NAT ~ MIN[dst.size.s-dstMin.s, src.size.s-srcMin.s, size.s]; fSize: NAT ~ MIN[dst.size.f-dstMin.f, src.size.f-srcMin.f, size.f]; bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; dstBitIndex: CARD ~ Basics.LongMult[dstMin.s, dst.bitsPerLine] + Basics.LongMult[dstMin.f, bitsPerSample]; srcBitIndex: CARD ~ Basics.LongMult[srcMin.s, src.bitsPerLine] + Basics.LongMult[srcMin.f, bitsPerSample]; bb^ _ nullBitBltTable; bb.dst _ IndexBit[dst.base, dstBitIndex]; bb.dstBpl _ dst.bitsPerLine; bb.src _ IndexBit[src.base, srcBitIndex]; bb.srcDesc _ [srcBpl[src.bitsPerLine]]; bb.width _ fSize*bitsPerSample; bb.height _ sSize; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; PrincOpsUtils.BITBLT[bb]; } ELSE BufferTransfer[dst, src, dstMin, srcMin, size, function]; }; TransferBoxes: PUBLIC PROC [dst: SampleMap, src: SampleMap, boxes: BoxGenerator, dstOffset: Vec _ zeroVec, srcOffset: Vec _ zeroVec, function: Function _ nullFunction] ~ { bitsPerSample: NAT ~ dst.bitsPerSample; IF src.bitsPerSample=bitsPerSample THEN TRUSTED { bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; fastTransfer: BoxAction ~ TRUSTED { dstMin: Vec ~ box.min.Add[dstOffset]; srcMin: Vec ~ box.min.Add[srcOffset]; sSize: NAT ~ MIN[dst.size.s-dstMin.s, src.size.s-srcMin.s, box.max.s-box.min.s]; fSize: NAT ~ MIN[dst.size.f-dstMin.f, src.size.f-srcMin.f, box.max.f-box.min.f]; dstBitIndex: CARD ~ Basics.LongMult[dstMin.s, dst.bitsPerLine] + Basics.LongMult[dstMin.f, bitsPerSample]; srcBitIndex: CARD ~ Basics.LongMult[srcMin.s, src.bitsPerLine] + Basics.LongMult[srcMin.f, bitsPerSample]; bb.dst _ IndexBit[dst.base, dstBitIndex]; bb.src _ IndexBit[src.base, srcBitIndex]; bb.width _ fSize*bitsPerSample; bb.height _ sSize; PrincOpsUtils.BITBLT[bb]; }; bb^ _ nullBitBltTable; bb.dstBpl _ dst.bitsPerLine; bb.srcDesc _ [srcBpl[src.bitsPerLine]]; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; boxes[fastTransfer]; } ELSE { slowTransfer: BoxAction ~ { dstMin: Vec ~ box.min.Add[dstOffset]; srcMin: Vec ~ box.min.Add[srcOffset]; BufferTransfer[dst, src, dstMin, srcMin, box.max.Sub[box.min], function]; }; boxes[slowTransfer]; }; }; Move: PUBLIC PROC [self: SampleMap, dstMin: Vec _ zeroVec, srcMin: Vec _ zeroVec, size: Vec _ maxVec, function: Function _ nullFunction] ~ TRUSTED { sSize: NAT ~ MIN[self.size.s-srcMin.s, self.size.s-dstMin.s, size.s]; fSize: NAT ~ MIN[self.size.f-srcMin.f, self.size.f-dstMin.f, size.f]; bitsPerSample: NAT ~ self.bitsPerSample; bitsPerLine: NAT ~ self.bitsPerLine; dstBitIndex: CARD _ Basics.LongMult[dstMin.s, bitsPerLine] + Basics.LongMult[dstMin.f, bitsPerSample]; srcBitIndex: CARD _ Basics.LongMult[srcMin.s, bitsPerLine] + Basics.LongMult[srcMin.f, bitsPerSample]; bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; bb^ _ nullBitBltTable; bb.dstBpl _ bitsPerLine; bb.srcDesc _ [srcBpl[bitsPerLine]]; bb.height _ sSize; bb.width _ bitsPerSample * fSize; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; IF sSize > 0 AND (srcMin.f+fSize) > dstMin.f AND (dstMin.f+fSize) > srcMin.f AND (srcMin.s+sSize) > dstMin.s AND (dstMin.s+sSize) > srcMin.s THEN { bb.flags.disjoint _ FALSE; -- the rectangles overlap IF srcMin.s=dstMin.s THEN bb.flags.disjointItems _ FALSE; -- so do the items IF dstMin.s>srcMin.s OR (dstMin.s=srcMin.s AND dstMin.f>srcMin.f) THEN { delta: CARD ~ Basics.LongMult[sSize-1, bitsPerLine]; bb.flags.direction _ backward; -- reverse direction bb.srcDesc.srcBpl _ bb.dstBpl _ -bitsPerLine; dstBitIndex _ dstBitIndex + delta; srcBitIndex _ srcBitIndex + delta; }; }; bb.dst _ IndexBit[self.base, dstBitIndex]; bb.src _ IndexBit[self.base, srcBitIndex]; PrincOpsUtils.BITBLT[bb]; }; MoveBoxes: PUBLIC PROC [self: SampleMap, boxes: BoxGenerator, dstOffset: Vec _ zeroVec, srcOffset: Vec _ zeroVec, function: Function _ nullFunction] ~ TRUSTED { bitsPerSample: NAT ~ self.bitsPerSample; bitsPerLine: NAT ~ self.bitsPerLine; bbTableSpace: PrincOps.BBTableSpace; bbPtr: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; initialFlags: PrincOps.BitBltFlags ~ [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: function.srcFunc, dstFunc: function.dstFunc]; fastMoveBox: BoxAction ~ TRUSTED { bb: PrincOps.BBptr ~ bbPtr; dstMin: Vec ~ dstOffset.Add[box.min]; srcMin: Vec ~ srcOffset.Add[box.min]; sSize: NAT ~ MIN[self.size.s-dstMin.s, self.size.s-srcMin.s, box.max.s-box.min.s]; fSize: NAT ~ MIN[self.size.f-dstMin.f, self.size.f-srcMin.f, box.max.f-box.min.f]; dstBitIndex: CARD _ Basics.LongMult[dstMin.s, bitsPerLine] + Basics.LongMult[dstMin.f, bitsPerSample]; srcBitIndex: CARD _ Basics.LongMult[srcMin.s, bitsPerLine] + Basics.LongMult[srcMin.f, bitsPerSample]; bpl: INTEGER _ bitsPerLine; flags: PrincOps.BitBltFlags _ initialFlags; IF sSize > 0 AND (srcMin.f+fSize) > dstMin.f AND (dstMin.f+fSize) > srcMin.f AND (srcMin.s+sSize) > dstMin.s AND (dstMin.s+sSize) > srcMin.s THEN { flags.disjoint _ FALSE; -- the rectangles overlap IF srcMin.s=dstMin.s THEN flags.disjointItems _ FALSE; -- so do the items IF dstMin.s>srcMin.s OR (dstMin.s=srcMin.s AND dstMin.f>srcMin.f) THEN { delta: CARD ~ Basics.LongMult[sSize-1, bitsPerLine]; flags.direction _ backward; -- reverse direction bpl _ -bpl; dstBitIndex _ dstBitIndex + delta; srcBitIndex _ srcBitIndex + delta; }; }; bb.dst _ IndexBit[self.base, dstBitIndex]; bb.dstBpl _ bpl; bb.src _ IndexBit[self.base, srcBitIndex]; bb.srcDesc _ [srcBpl[bpl]]; bb.height _ sSize; bb.width _ bitsPerSample * fSize; bb.flags _ flags; PrincOpsUtils.BITBLT[bb]; }; bbPtr^ _ nullBitBltTable; boxes[fastMoveBox]; }; DumbFill: PROC [self: SampleMap, box: Box _ maxBox, value: Sample, function: Function _ nullFunction] ~ { sSize: NAT ~ MIN[self.size.s, box.max.s]-box.min.s; fSize: NAT ~ MIN[self.size.f, box.max.f]-box.min.f; FOR s: NAT IN [0..sSize) DO FOR f: NAT IN [0..fSize) DO offset: Vec ~ [s: s, f: f]; Put[self, box.min.Add[offset], value, function]; ENDLOOP; ENDLOOP; }; replicator: ARRAY [0..4] OF Sample ~ [0FFFFH, 05555H, 01111H, 00101H, 00001H]; Fill: PUBLIC PROC [self: SampleMap, box: Box _ maxBox, value: Sample, function: Function _ nullFunction] ~ { bitsPerSample: NAT ~ self.bitsPerSample; bitsPerLine: NAT ~ self.bitsPerLine; IF (bitsPerWord MOD bitsPerSample)=0 THEN TRUSTED { sSize: NAT ~ MIN[self.size.s, box.max.s]-box.min.s; fSize: NAT ~ MIN[self.size.f, box.max.f]-box.min.f; dstBitIndex: CARD ~ Basics.LongMult[box.min.s, bitsPerLine] + Basics.LongMult[box.min.f, bitsPerSample]; bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; bitMask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1; replicatedPixel: WORD _ Basics.BITAND[value, bitMask] * replicator[Lg[bitsPerSample]]; IF function = [null, complement] THEN { replicatedPixel _ WORD.LAST-replicatedPixel; function _ [null, null]; }; bb^ _ nullBitBltTable; bb.dst _ IndexBit[self.base, dstBitIndex]; bb.dstBpl _ bitsPerLine; bb.src.word _ @replicatedPixel; bb.srcDesc.gray _ [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]; bb.width _ fSize*bitsPerSample; bb.height _ sSize; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: TRUE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; PrincOpsUtils.BITBLT[bb]; } ELSE DumbFill[self: self, box: box, value: value, function: function]; }; FillBoxes: PUBLIC PROC [self: SampleMap, boxes: BoxGenerator, value: Sample _ maxSample, function: Function _ nullFunction] ~ { bitsPerSample: NAT ~ self.bitsPerSample; IF (bitsPerWord MOD bitsPerSample)=0 THEN TRUSTED { lgBitsPerSample: NAT ~ Lg[bitsPerSample]; bitsPerLine: NAT ~ self.bitsPerLine; bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; fastFill: BoxAction ~ TRUSTED { sSize: NAT ~ MIN[self.size.s, box.max.s]-box.min.s; fSize: NAT ~ MIN[self.size.f, box.max.f]-box.min.f; dstBitIndex: CARD ~ Basics.LongMult[box.min.s, bitsPerLine] + Basics.BITSHIFT[box.min.f, lgBitsPerSample]; bb.dst _ IndexBit[self.base, dstBitIndex]; bb.width _ Basics.BITSHIFT[fSize, lgBitsPerSample]; bb.height _ sSize; PrincOpsUtils.BITBLT[bb]; }; bitMask: WORD ~ Basics.BITSHIFT[1, bitsPerSample]-1; replicatedPixel: WORD _ Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample]; IF function = [null, complement] THEN { replicatedPixel _ WORD.LAST-replicatedPixel; function _ [null, null]; }; bb^ _ nullBitBltTable; bb.dstBpl _ bitsPerLine; bb.src.word _ @replicatedPixel; bb.srcDesc.gray _ [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: TRUE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; boxes[fastFill]; } ELSE { slowFill: BoxAction ~ { DumbFill[self, box, value, function] }; boxes[slowFill]; }; }; Zeros: PROC [pointer: LONG POINTER, count: NAT] RETURNS [BOOL] ~ TRUSTED { chomp: NAT ~ 8; scratch: ARRAY [0..chomp) OF WORD _ ALL[0]; bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; bb^ _ nullBitBltTable; bb.dstBpl _ 0; bb.srcDesc _ [srcBpl[chomp*bitsPerWord]]; bb.height _ count/chomp; bb.width _ chomp*bitsPerWord; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, dstFunc: or]; bb.dst.word _ @scratch; bb.src.word _ pointer; IF bb.height # 0 THEN PrincOpsUtils.BITBLT[bb]; bb.src.word _ pointer + Basics.LongMult[bb.height, chomp]; bb.height _ 1; bb.width _ count MOD chomp; IF bb.width # 0 THEN PrincOpsUtils.BITBLT[bb]; FOR i: NAT IN [0..MIN[chomp, count]) DO IF scratch[i]#0 THEN RETURN [FALSE] ENDLOOP; RETURN [TRUE]; }; Equal: PUBLIC PROC [ map1: SampleMap, box1: Box _ maxBox, map2: SampleMap, box2: Box _ maxBox ] RETURNS [equal: BOOL _ TRUE] ~ { size: Vec ~ map1.size.Min[box1.max].Sub[box1.min]; size2: Vec ~ map2.size.Min[box2.max].Sub[box2.min]; bandAction: PROC [band: SampleMap] ~ { bandSize: Vec _ band.size; Clear[band]; FOR s: NAT _ 0, s+bandSize.s WHILE s equal _ FALSE; ENDLOOP; }; IF size.s=0 OR size.f=0 THEN NULL ELSE IF size.s#size2.s OR size.f#size2.f THEN equal _ FALSE ELSE { bandLines: NAT ~ MIN[MAX[4096/size.f, 1], size.s]; bandBitsPerSample: NAT ~ MAX[map1.bitsPerSample, map2.bitsPerSample]; DoWithScratchMap[[s: bandLines, f: size.f], bandBitsPerSample, bandAction]; }; }; IsAll: PUBLIC PROC [self: SampleMap, box: Box _ maxBox, value: Sample _ 0] RETURNS [equal: BOOL _ TRUE] ~ { size: Vec ~ self.size.Min[box.max].Sub[box.min]; bitsPerSample: NAT ~ self.bitsPerSample; bitMask: Sample ~ Basics.BITSHIFT[1, bitsPerSample]-1; bandAction: PROC [band: SampleMap] ~ { bandSize: Vec _ band.size; Clear[band]; FOR s: NAT _ 0, s+bandSize.s WHILE s equal _ FALSE; ENDLOOP; }; IF size.s=0 OR size.f=0 THEN NULL ELSE IF Basics.BITAND[value, bitMask]#0 THEN equal _ FALSE ELSE { bandLines: NAT ~ MIN[MAX[4096/size.f, 1], size.s]; DoWithScratchMap[[s: bandLines, f: size.f], bitsPerSample, bandAction]; }; }; Trim: PUBLIC PROC [self: SampleMap, box: Box, background: Sample _ 0] RETURNS [Box] ~ { size: Vec ~ self.size.Min[box.max].Sub[box.min]; min: Vec _ box.min; max: Vec _ min.Add[size]; bandAction: PROC [band: SampleMap] ~ { bandWords: NAT ~ WordsForLines[band.size.s, band.bitsPerLine]; Clear[band]; WHILE min.s < max.s -- OR EXIT below -- DO Transfer[dst: band, src: self, srcMin: [s: max.s-1, f: min.f]]; IF background#0 THEN Fill[self: band, value: background, function: [xor, null]]; IF Zeros[band.base.word, bandWords] THEN max.s _ max.s-1 ELSE EXIT; ENDLOOP; WHILE min.s < max.s -- OR EXIT below -- DO Transfer[dst: band, src: self, srcMin: [s: min.s, f: min.f]]; IF background#0 THEN Fill[self: band, value: background, function: [xor, null]]; IF Zeros[band.base.word, bandWords] THEN min.s _ min.s+1 ELSE EXIT; ENDLOOP; }; DoWithScratchMap[size: [s: 1, f: size.f], bitsPerSample: self.bitsPerSample, action: bandAction]; IF min.s=max.s THEN max.f _ min.f ELSE FOR delta: NAT _ 16, delta/4 UNTIL delta=0 DO WHILE min.f+delta <= max.f -- OR EXIT below -- DO margin: Box ~ [min: [s: min.s, f: max.f-delta], max: max]; IF IsAll[self, margin, background] THEN max.f _ max.f-delta ELSE EXIT; ENDLOOP; WHILE min.f+delta <= max.f -- OR EXIT below -- DO margin: Box ~ [min: min, max: [s: max.s, f: min.f+delta]]; IF IsAll[self, margin, background] THEN min.f _ min.f+delta ELSE EXIT; ENDLOOP; ENDLOOP; RETURN[[min: min, max: max]]; }; TileFromStipple: PUBLIC PROC [stipple: WORD, bitsPerSample: BitsPerSample _ 1, value0: Sample _ 0, value1: Sample _ Sample.LAST, scratch: SampleMap _ NIL] RETURNS [SampleMap] ~ { easy: BOOL ~ (16 MOD (bitsPerSample*4) = 0) OR (bitsPerSample=8 AND Basics.BITAND[stipple, 3333H] = Basics.BITAND[stipple/4, 3333H]); size: Vec ~ IF easy THEN [s: 4, f: 16/bitsPerSample] ELSE [s: 8, f: 16]; temp: SampleMap ~ ObtainScratchMap[[4, 4], bitsPerSample]; tile: SampleMap ~ NewSampleMap[size: size, bitsPerSample: bitsPerSample, scratch: scratch]; Clear[tile]; FOR s: NAT DECREASING IN [0..4) DO FOR f: NAT DECREASING IN [0..4) DO Put[temp, [s: s, f: f], (IF (stipple MOD 2)=0 THEN value0 ELSE value1)]; stipple _ stipple/2; ENDLOOP; ENDLOOP; Tile[self: tile, tile: temp]; ReleaseScratchMap[temp]; RETURN[tile]; }; QR: TYPE ~ RECORD [quotient: INTEGER, remainder: NAT]; DivMod: PROC [n: INT, d: NAT] RETURNS [qr: QR] ~ { IF d#1 THEN { nn: Basics.LongNumber _ [li[n]]; qq: Basics.LongNumber _ [lc[0]]; IF nn.li < 0 THEN {nn.highbits _ nn.highbits + d; -- qq.highbits _ CARDINAL.LAST --}; [quotient: qq.lowbits, remainder: qr.remainder] _ Basics.LongDivMod[nn.lc, d]; qr.quotient _ LOOPHOLE[qq.lowbits]; } ELSE RETURN [[quotient: n, remainder: 0]]; }; Mod: PROC [n: INT, d: NAT] RETURNS [remainder: NAT] ~ { IF d#1 THEN { nn: Basics.LongNumber _ [li[n]]; WHILE nn.li < 0 DO nn.highbits _ nn.highbits + d ENDLOOP; WHILE nn.highbits >= d DO nn.highbits _ nn.highbits - d ENDLOOP; RETURN [Basics.LongDivMod[nn.lc, d].remainder]; } ELSE RETURN [remainder: 0]; }; QMul: PROC [a: INTEGER, b: CARDINAL] RETURNS [INT] ~ INLINE { IF a >= 0 THEN RETURN [LOOPHOLE[Basics.LongMult[a, b]]] ELSE RETURN [-LOOPHOLE[Basics.LongMult[-a, b], INT]] }; DumbTile: PROC [self: SampleMap, box: Box _ maxBox, tile: SampleMap, s0, f0: INT _ 0, phase: NAT _ 0, function: Function _ nullFunction] ~ { size: Vec ~ self.size.Min[box.max].Sub[box.min]; FOR s: NAT IN [0..size.s) DO dstIndex: Vec _ [s: box.min.s+s, f: box.min.f]; sOffset: INT ~ dstIndex.s-s0; qr: QR ~ DivMod[sOffset, tile.size.s]; fOffset: INT ~ dstIndex.f-(f0+QMul[qr.quotient, phase]); srcIndex: Vec _ [s: qr.remainder, f: Mod[fOffset, tile.size.f]]; FOR f: NAT IN [0..size.f) DO Put[self, dstIndex, Get[tile, srcIndex], function]; dstIndex.f _ dstIndex.f+1; srcIndex.f _ srcIndex.f+1; IF srcIndex.f=tile.size.f THEN srcIndex.f _ 0; ENDLOOP; ENDLOOP; }; GeneralTile: PROC [self: SampleMap, box: Box _ maxBox, tile: SampleMap, s0, f0: INT _ 0, phase: NAT _ 0, function: Function _ nullFunction] ~ { boxes: BoxGenerator ~ { boxAction[box] }; GeneralTileBoxes[self, boxes, tile, s0, f0, phase, function]; }; GeneralTileBoxes: PROC [self: SampleMap, boxes: BoxGenerator, tile: SampleMap, s0, f0: INT _ 0, phase: NAT _ 0, function: Function _ nullFunction] ~ { generalTile: BoxAction ~ { size: Vec ~ self.size.Min[box.max].Sub[box.min]; dstMax: Vec ~ box.min.Add[size]; srcMax: Vec ~ tile.size; dstRow: Vec _ box.min; sOffset: INT ~ dstRow.s-s0; qr: QR ~ DivMod[sOffset, srcMax.s]; fOffset: INT ~ dstRow.f-(f0+QMul[qr.quotient, phase]); srcRow: Vec _ [s: qr.remainder, f: Mod[fOffset, srcMax.f]]; WHILE dstRow.s < dstMax.s DO sSize: NAT ~ MIN[srcMax.s-srcRow.s, dstMax.s-dstRow.s]; dstCol: Vec _ dstRow; srcCol: Vec _ srcRow; WHILE dstCol.f < dstMax.f DO fSize: NAT ~ MIN[srcMax.f-srcCol.f, dstMax.f-dstCol.f]; Transfer[dst: self, src: tile, dstMin: dstCol, srcMin: srcCol, size: [s: sSize, f: fSize], function: function]; dstCol.f _ dstCol.f+fSize; srcCol.f _ 0; ENDLOOP; dstRow.s _ dstRow.s+sSize; srcRow.s _ 0; srcRow.f _ IF srcRow.f=tile.size.f THEN phase _ phase MOD tile.size.f; boxes[generalTile]; }; Tile: PUBLIC PROC [self: SampleMap, box: Box _ maxBox, tile: SampleMap, s0, f0: INT _ 0, phase: NAT _ 0, function: Function _ nullFunction] ~ { boxes: BoxGenerator ~ { boxAction[box] }; TileBoxes[self, boxes, tile, s0, f0, phase, function]; }; smallWidth: NAT _ 128; TileBoxes: PUBLIC PROC [self: SampleMap, boxes: BoxGenerator, tile: SampleMap, s0, f0: INT _ 0, phase: NAT _ 0, function: Function _ nullFunction] ~ { bitsPerSample: NAT ~ self.bitsPerSample; IF phase=0 AND tile.bitsPerSample=bitsPerSample AND tile.bitsPerLine=bitsPerWord AND tile.base.bit=0 AND tile.size.f*bitsPerSample=bitsPerWord AND tile.size.s IN [1..16] AND function#[null, complement] THEN TRUSTED { bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; sOffset: CARDINAL ~ Mod[-s0, tile.size.s]; fOffset: CARDINAL ~ Mod[-f0, tile.size.f]; fastTile: BoxAction ~ TRUSTED { size: Vec ~ self.size.Min[box.max].Sub[box.min]; dstBitIndex: CARD ~ Basics.LongMult[box.min.s, self.bitsPerLine] + Basics.LongMult[box.min.f, bitsPerSample]; yOffset: CARDINAL ~ (sOffset+box.min.s) MOD tile.size.s; bb.dst _ IndexBit[self.base, dstBitIndex]; bb.src.word _ tile.base.word+yOffset; bb.src.bit _ (fOffset+box.min.f) MOD bitsPerWord; bb.srcDesc.gray.yOffset _ yOffset; bb.height _ size.s; bb.width _ size.f*bitsPerSample; PrincOpsUtils.BITBLT[bb]; }; bb^ _ nullBitBltTable; bb.dstBpl _ self.bitsPerLine; bb.srcDesc.gray _ [yOffset: 0, widthMinusOne: 0, heightMinusOne: tile.size.s-1]; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: TRUE]; bb.flags.srcFunc _ function.srcFunc; bb.flags.dstFunc _ function.dstFunc; boxes[fastTile]; } ELSE IF tile.bitsPerSample#bitsPerSample OR tile.size.f*bitsPerSample < MIN[smallWidth, self.size.f*bitsPerSample] THEN { ns: CARDINAL ~ 1; nf: CARDINAL ~ ((smallWidth+tile.size.f-1)/tile.size.f+bitsPerSample-1)/bitsPerSample; macroTileSize: Vec ~ [s: ns*tile.size.s, f: nf*tile.size.f]; macroTileAction: PROC [macroTile: SampleMap] ~ { GeneralTile[self: macroTile, tile: tile, phase: phase]; GeneralTileBoxes[self: self, boxes: boxes, tile: macroTile, s0: s0, f0: f0, phase: ns*phase, function: function]; }; DoWithScratchMap[macroTileSize, bitsPerSample, macroTileAction]; } ELSE GeneralTileBoxes[self, boxes, tile, s0, f0, phase, function]; }; NewPixels: PUBLIC PROC [samplesPerPixel: NAT, length: NAT, scratch: PixelBuffer _ NIL] RETURNS [PixelBuffer] ~ { new: PixelBuffer _ scratch; IF new=NIL OR new.samplesPerPixel#samplesPerPixel THEN new _ NEW[PixelBufferRep[samplesPerPixel]]; FOR i: NAT IN[0..samplesPerPixel) DO new[i] _ NewSamples[length: length, scratch: new[i]]; ENDLOOP; new.length _ length; RETURN[new]; }; nScratchPixelBuffers: NAT ~ 6; scratchPixelBuffers: ARRAY [0..nScratchPixelBuffers) OF PixelBuffer _ ALL[NIL]; ObtainScratchPixels: PUBLIC ENTRY PROC [samplesPerPixel: NAT, length: NAT] RETURNS [PixelBuffer] ~ { RETURN[NewPixels[samplesPerPixel: samplesPerPixel, length: length]] }; ReleaseScratchPixels: PUBLIC ENTRY PROC [pixels: PixelBuffer] ~ { }; DoWithScratchPixels: PUBLIC PROC [samplesPerPixel: NAT, length: NAT, action: PROC [PixelBuffer]] ~ { scratch: PixelBuffer ~ ObtainScratchPixels[samplesPerPixel: samplesPerPixel, length: length]; action[scratch ! UNWIND => ReleaseScratchPixels[scratch]]; ReleaseScratchPixels[scratch]; }; NewPixelMap: PUBLIC PROC [samplesPerPixel: NAT, size: Vec, maxSample: PixelProc, scratch: PixelMap _ NIL] RETURNS [PixelMap] ~ { new: PixelMap _ scratch; IF new=NIL OR new.samplesPerPixel#samplesPerPixel THEN new _ NEW[PixelMapRep[samplesPerPixel]]; FOR i: NAT IN[0..samplesPerPixel) DO bitsPerSample: BitsPerSample ~ SELECT maxSample[i] FROM <2 => 1, <4 => 2, <16 => 4, <256 => 8, ENDCASE => BitsPerSample.LAST; new[i] _ NewSampleMap[size: size, bitsPerSample: bitsPerSample, scratch: new[i]]; ENDLOOP; new.size _ size; RETURN[new]; }; GetPixels: PUBLIC PROC [self: PixelMap, min: Vec _ zeroVec, delta: Vec _ [s: 0, f: 1], pixels: PixelBuffer, start: NAT _ 0, count: NAT _ maxCount] ~ { FOR i: NAT IN [0..self.samplesPerPixel) DO GetSamples[self[i], min, delta, pixels[i], start, count]; ENDLOOP; }; PutPixels: PUBLIC PROC [self: PixelMap, min: Vec _ zeroVec, delta: Vec _ [s: 0, f: 1], pixels: PixelBuffer, start: NAT _ 0, count: NAT _ maxCount, function: Function _ nullFunction] ~ { FOR i: NAT IN [0..self.samplesPerPixel) DO PutSamples[self[i], min, delta, pixels[i], start, count, function]; ENDLOOP; }; realLastInt: REAL _ REAL[LAST[INT]]; Floor: PROC[a: REAL] RETURNS[c: REAL] ~ { IF ABS[a] >= realLastInt THEN RETURN [a]; c _ REAL[Real.Fix[a]]; IF c>a THEN RETURN[c-1] ELSE RETURN[c] }; ScaledFromRealMod: PROC [d: REAL, m: CARDINAL] RETURNS [INT] ~ { s: Basics.LongNumber _ [li[0]]; IF m # 0 THEN { modulus: REAL ~ m; residue: REAL _ IF d IN [0..modulus) THEN d ELSE (d-modulus*Floor[d/modulus]); s.li _ Real.Round[Real.FScale[residue, Basics.bitsPerWord]]; IF s.li < 0 THEN {s.hi _ s.hi + m}; IF s.hi >= m THEN {s.hi _ s.hi - m}; IF s.hi >= m THEN ERROR; }; RETURN [s.li]; }; Sample1: TYPE ~ Sample[0..1B]; Sample8: TYPE ~ Sample[0..377B]; RawSamples1: TYPE ~ RECORD [PACKED SEQUENCE COMPUTED CARDINAL OF Sample1]; RawSamples8: TYPE ~ RECORD [PACKED SEQUENCE COMPUTED CARDINAL OF Sample8]; Pointer1: TYPE ~ LONG POINTER TO RawSamples1; Pointer8: TYPE ~ LONG POINTER TO RawSamples8; Resample: PUBLIC PROC [self: PixelMap, m: Transformation, interpolate: BOOL, boxes: BoxGenerator, bounds: Box, action: ResampleAction] ~ { samplesPerPixel: NAT ~ self.samplesPerPixel; sSize: NAT ~ self.size.s; -- source size in slow (scan line) direction fSize: NAT ~ self.size.f; -- source size in fast (pixel) direction ssDelta: Basics.LongNumber; -- source delta s for destination f increment fsDelta: Basics.LongNumber; -- source delta f for destination f increment sfDelta: Basics.LongNumber; -- source delta s for destination s increment ffDelta: Basics.LongNumber; -- source delta f for destination s increment d0: Vec _ bounds.min; -- initial destination position s0: Basics.LongNumber; -- initial source s (corresponding to d0) f0: Basics.LongNumber; -- initial source f (corresponding to d0) pixels: PixelBuffer ~ ObtainScratchPixels[samplesPerPixel, SF.SizeF[bounds]]; rs, rf: REAL _ 0.0; boxActionPointSamples: BoxAction ~ { count: NAT ~ SF.SizeF[box]; smax: Basics.LongNumber ~ [pair[hi: sSize, lo: 0]]; fmax: Basics.LongNumber ~ [pair[hi: fSize, lo: 0]]; ssup: Basics.LongNumber ~ ssDelta; fsup: Basics.LongNumber ~ fsDelta; ssdn: Basics.LongNumber ~ [lc[smax.lc-ssup.lc]]; fsdn: Basics.LongNumber ~ [lc[fmax.lc-fsup.lc]]; sfup: Basics.LongNumber ~ sfDelta; ffup: Basics.LongNumber ~ ffDelta; sfdn: Basics.LongNumber ~ [lc[smax.lc-sfup.lc]]; ffdn: Basics.LongNumber ~ [lc[fmax.lc-ffup.lc]]; d1: Vec _ d0; -- destination position at start of scan line s1: Basics.LongNumber _ s0; -- source s corresponding to d1 f1: Basics.LongNumber _ f0; -- source f corresponding to d1 WHILE d1.s { rs _ rf _ 0; CONTINUE }]; sfDelta.li _ ScaledFromRealMod[rs, sSize]; ffDelta.li _ ScaledFromRealMod[rf, fSize]; [[rs, rf]] _ m.InverseTransformVec[[1, 0] ! Real.RealException => { rs _ rf _ 0; CONTINUE }]; ssDelta.li _ ScaledFromRealMod[rs, sSize]; fsDelta.li _ ScaledFromRealMod[rf, fSize]; [[rs, rf]] _ m.InverseTransform[[d0.s+0.5, d0.f+0.5] ! Real.RealException => { rs _ rf _ 0; CONTINUE }]; IF interpolate THEN { rs _ rs-0.5; rf _ rf-0.5 }; s0.li _ ScaledFromRealMod[rs, sSize]; f0.li _ ScaledFromRealMod[rf, fSize]; boxes[IF interpolate THEN boxActionInterpolatedSamples ELSE boxActionPointSamples]; ReleaseScratchPixels[pixels]; }; END. VImagerSampleImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Michael Plass, January 21, 1986 12:07:30 pm PST Doug Wyatt, March 6, 1986 4:09:53 pm PST Push least-recently-used towards end. This record is to designed to avoid the need for partial-word access to the bit offset field, since the BITBLT format has carefully made this field word-aligned, and PrincOps.BitAddress has just as carefully made it hard to take advantage of this fact. Returns required number of bits, rounded up to a whole number of words. Try flushing the global function cache to get some space back. *** test for excessive size here ? *** Push least-recently-used towards end. -- Contiguous, except perhaps for some padding. -- -- Discontiguous, so do pieces (could BITBLT here; which is cheaper?) -- -- Bug in Dorado BITBLT microcode (as of December 4, 1985) -- fastFillW: BoxAction ~ TRUSTED { sSize: NAT ~ MIN[self.size.s, box.max.s]-box.min.s; fSize: NAT ~ MIN[self.size.f, box.max.f]-box.min.f; dstBitIndex: NAT ~ self.base.bit + Basics.BITSHIFT[box.min.f, lgBitsPerSample]; bb.dst.word _ self.base.word + Basics.LongMult[box.min.s, wordsPerLine] + dstBitIndex/bitsPerWord; bb.dst.bit _ dstBitIndex MOD bitsPerWord; bb.width _ Basics.BITSHIFT[fSize, lgBitsPerSample]; bb.height _ sSize; PrincOpsUtils.BITBLT[bb]; }; -- Bug in Dorado BITBLT microcode (as of December 4, 1985) -- -- Checks for (pointer+i)^ = 0 for i IN [0..count) -- -- Number-theoretic: 0 <= remainder < d, n = quotient*d + remainder -- -- quotient _ qq.li; -- -- Number-theoretic: 0 <= remainder < d -- generalTile: BoxAction ~ { DumbTile[self, box, tile, s0, f0, phase, function]; }; FOR i: NAT IN [0..nScratchPixelBuffers) DO buf: PixelBuffer ~ scratchPixelBuffers[i]; IF buf # NIL AND buf.maxLength >= length THEN { scratchPixelBuffers[i] _ NIL; buf.length _ length; RETURN [buf]; }; ENDLOOP; r: PixelBuffer _ samples; samples.length _ 0; FOR i: NAT IN [0..nScratchPixelBuffers) UNTIL r = NIL DO -- Push least-recently-used towards end. -- t: PixelBuffer _ scratchPixelBuffers[i]; scratchPixelBuffers[i] _ r; r _ t; IF r = samples THEN RETURN WITH ERROR MultipleReleaseOfScratch; ENDLOOP; Now some paranoia about floating-point fuzz -- Calculate i* (34 cycles) -- -- Calculate w* (68 cycles) -- -- Calculate line* (80 cycles) -- -- Fetch v* (52 cycles) -- -- Calculate value (143 cycles) -- -- Store result (14 cycles) -- -- Bump s and f (66 cycles) -- -- Total (480 cycles): 30.7 microseconds/sample; 8 seconds for 512*512 samples -- -- Set increments -- A singular transformation means the image can't be shown anyway. -- Set initial position -- Κ8-˜codešœ™Kšœ Οmœ=™HK™/K™(—K˜šΟk ˜ Kš œžœžœžœžœt˜²Kšœ žœ˜)Kšœžœ5˜HKšœ ˜ Kšœ˜Kšœ žœ=˜KKšœžœžœ˜AKšœ˜Kšžœ˜Kšœ žœ˜Kšžœžœ˜&—K˜KšΠblœžœž˜KšžœNžœž˜[Kšžœ ˜Kšœžœžœ˜K˜šœžœžœžœ˜.K˜—K˜š Οn œžœžœ žœžœžœ˜^Kšœ˜Kš žœžœžœžœžœ˜KK˜Kšžœ˜ K˜K˜—Kšœžœ˜š œžœžœžœžœ˜RK˜—š  œžœžœžœ žœžœ˜Pšžœžœžœž˜+Kšœ,˜,šžœžœžœžœ˜/Kšœžœ˜Kšœ˜Kšžœ˜ Kšœ˜—Kšžœ˜—Kšžœžœ7˜BKšœ˜K˜—š œžœžœžœ˜DKšœ˜Kšœ˜š žœžœžœžœžœž˜9J™%Jšœ*˜*Jšœ˜Jšœ˜Kš žœ žœžœžœžœ˜?Kšžœ˜—Kšœ˜K˜—š  œžœžœ žœ žœ˜PKšœ5˜5Kšœžœ$˜;Kšœ˜Kšœ˜K˜—K˜š œžœ žœ žœžœžœžœžœžœžœ˜…Jšœžœžœ ˜0Kš žœžœ žœžœžœ˜EKšœ˜K˜—š œžœžœ žœ žœžœžœžœžœ˜wKšžœ0˜6Kšœ˜K˜—š  œžœžœ˜4Kšœžœ˜šžœžœžœ˜Kšœ žœžœžœ7˜OKšœžœ ˜šžœžœžœ ž˜Kšœžœ ˜Kšœ˜Kšœ˜Kšœ˜Kšžœ˜—K˜—Kšœ˜K˜—š  œžœžœ6žœ˜VKšœžœžœ˜-šžœžœžœ˜Kšœ žœžœžœ;˜SK˜K˜EK˜—Kšœ˜K˜—K˜Kšœ žœ˜&Kšœžœ˜,šžœžœ žœ˜K˜—šœ)˜)Kšœ žœ˜$Kšœ žœ ˜/Kšœ˜Kšœ˜K˜—š   œžœžœžœžœžœ˜:Kšžœžœ ˜ Kšœ˜K˜—š   œžœ žœžœžœžœ˜=KšžœL˜RKšœ˜K˜—š  œžœžœžœžœ ˜+Kšœžœ˜šžœž˜Kšœ ˜ Kšœ˜Kšžœ˜—Kšœ˜K˜—š  œžœ%žœžœžœžœ˜jšœžœžœž œžœ žœžœ žœ˜PKšœό™ό—Kšœ žœžœžœ˜5Kšœžœ˜Dšžœžœ˜KšœD˜DKšœžœ ˜ Kšœ˜—Kšœ˜K˜—K˜š œžœžœ žœžœžœžœ˜NK™GKšžœžœ8žœžœ˜hKšœ˜K˜—š  œžœžœ žœžœžœžœ˜Kšžœžœ˜#Kšžœžœ1˜Kšœ9˜9Kšœ=˜=Kšœ%˜%Kšœ`˜`K˜Kšœ&˜&Kšœ˜—Kšœ˜K˜—š   œžœžœSžœžœ˜Ššœžœžœ ž˜0Kšœ,žœ˜C—šžœžœ˜$KšœI˜IKšœ:˜:Kšœ˜—Kšœ˜K˜—š œžœžœ&žœ˜\Kšœ žœ:˜JK˜&Kšœžœžœ˜&Kš œ žœžœžœžœ žœ˜@Kšœžœ+˜5šžœžœ˜$Kšœ4˜4KšœB˜BKšœ1˜1Kšœ˜—K˜K˜—š  œžœžœžœžœ˜OKšœ0˜0šœ žœ˜Kšœ/˜/Kšœ/˜/—Kšœžœ˜+Kšœ˜Kšœ)˜)Kšžœ˜ Kšœ˜K˜—š œžœžœžœ˜HKšœ0˜0KšœM˜MKšœ/˜/Kšžœ˜ Kšœ˜K˜—K˜Kšœžœ˜š œ žœžœ žœžœ˜=K˜—š œžœžœ,žœ˜`š  œžœžœžœžœ˜Kšžœžœ ˜/K˜—šžœžœ˜Kšœ žœžœžœ#˜;Kšœ žœ0˜AKšœžœ žœ˜1šœ žœžœ˜$Kšžœžœ˜*Kšžœžœ žœ˜V—Kšžœžœ˜)K˜—Kšœ˜K˜—š œžœžœU˜eKšœžœ˜(šœ žœ˜KšœN˜NKšœI˜I—Kšœ@˜@šžœžœžœžœ˜;Kšœ žœžœžœ&˜>Kšœžœ˜/K˜—šžœžœ˜K˜$KšœA˜AK˜Kšœ˜Kšœ˜Kšœ'˜'K˜Kšœ˜Kšœžœžœžœ˜>Kšœ$˜$Kšœ$˜$Kšœžœ˜K˜—Kšœ˜K˜—K˜š   œžœžœ`žœ žœ˜šJšœžœžœ˜-šžœžœžœ˜Jšœ žœ˜$Jšœžœ˜(JšœA˜AJ˜$Kšœžœžœ0˜Kšœ žœ˜KšœG˜GKšœG˜G—šœ žœ˜Kšœ(˜(Kšœ(˜(—Kšœžœ˜#Kšœžœ˜Kšœžœ˜Kšžœ žœL˜]Kšžœ žœL˜]š žœžœžœžœžœžœž˜TKšœ7˜7Kšžœ˜—Jšœžœ˜,Jšœ;˜;Kšœ˜Kšœ'˜'Kšœžœ˜1Kšœ'˜'Kšœ˜Kšœžœžœžœ˜>š žœžœžœ žœ ž˜0Kšœ*˜*Kšœžœ˜:Kšœ˜Jšœžœ˜Kšœ(˜(Kšžœ˜—K˜—Kšœ˜K˜—š   œžœžœ`žœ žœ3˜½Jšœžœžœ˜-šžœ žœžœ˜Jšœ žœ˜$Jšœžœ˜(JšœA˜AJ˜$Kšœžœžœ0˜Kšœ žœ˜KšœG˜GKšœG˜G—šœ žœ˜Kšœ(˜(Kšœ(˜(—Kšœžœ˜#Kšœžœ˜Kšœžœ˜Kšžœ žœL˜]Kšžœ žœL˜]š žœžœžœžœžœžœž˜TKšœ7˜7Kšžœ˜—Jšœžœ˜,Kšœ˜Kšœžœžœžœ˜>Kšœ$˜$Kšœ$˜$Kšœ˜Kšœ'˜'Kšœžœ˜Kšœ$˜$Kšœ$˜$Kšœžœ˜Kšœ˜—Kšžœ:˜>Kšœ˜K˜—š  œžœžœ“˜­Kšœžœ˜'šžœ!žœžœ˜1K˜$KšœA˜Ašœžœ˜#Kšœ%˜%Kšœ%˜%Kšœžœžœ@˜PKšœžœžœ@˜Pšœ žœ˜Kšœ-˜-Kšœ)˜)—šœ žœ˜Kšœ-˜-Kšœ)˜)—Kšœ)˜)Kšœ)˜)Kšœ˜Kšœ˜Kšœžœ˜K˜—Kšœ˜Kšœ˜Kšœ'˜'Kšœžœžœžœ˜>Kšœ$˜$Kšœ$˜$Kšœ˜Kšœ˜—šžœ˜šœ˜Kšœ%˜%Kšœ%˜%KšœI˜IKšœ˜—Kšœ˜K˜—Kšœ˜K˜—K˜š œžœžœ|žœ˜–Kšœžœžœ5˜EKšœžœžœ5˜EKšœžœ˜(Kšœ žœ˜$šœ žœ˜Kšœ)˜)Kšœ)˜)—šœ žœ˜Kšœ)˜)Kšœ)˜)—K˜$KšœA˜AKšœ˜Kšœ˜Kšœ#˜#Kšœ˜Kšœžœ˜!Kšœžœžœžœ˜>Kšœ$˜$Kšœ$˜$š žœ žœžœžœžœžœ˜”Kšœžœ‘˜4Kšžœžœžœ‘˜Lšžœžœžœžœ˜HKšœžœ)˜4Kšœ‘˜3Kšœ-˜-Kšœ"˜"Kšœ"˜"K˜—Kšœ˜—Kšœ*˜*Kšœ*˜*Kšœžœ˜Kšœ˜K˜—š  œžœžœƒžœ˜’Kšœžœ˜(Kšœ žœ˜$K˜$KšœD˜DKšœDžœžœ žœ8˜£šœžœ˜"Kšœ˜Kšœ%˜%Kšœ%˜%KšœžœžœB˜RKšœžœžœB˜Ršœ žœ˜Kšœ)˜)Kšœ)˜)—šœ žœ˜Kšœ)˜)Kšœ)˜)—Kšœžœ˜Kšœ+˜+š žœ žœžœžœžœžœ˜”Kšœžœ‘˜1Kšžœžœžœ‘˜Išžœžœžœžœ˜HKšœžœ)˜4Kšœ‘˜0Kšœ ˜ Kšœ"˜"Kšœ"˜"K˜—Kšœ˜—Kšœ*˜*Kšœ˜Kšœ*˜*Kšœ˜Kšœ˜Kšœžœ˜!Kšœ˜Kšœžœ˜K˜—Kšœ˜K˜Kšœ˜K˜—K™š œžœ[˜iKšœžœžœ#˜3Kšœžœžœ#˜3šžœžœžœ ž˜šžœžœžœ ž˜K˜Kšœ0˜0Kšžœ˜—Kšžœ˜—Kšœ˜K˜—šœ žœžœ Οf&œ˜NK˜—š œžœžœ[˜lKšœžœ˜(Kšœ žœ˜$šžœžœžœžœ˜3Kšœžœžœ#˜3Kšœžœžœ#˜3šœ žœ˜Kšœ*˜*Kšœ*˜*—K˜$KšœA˜AKšœ žœ žœ˜4Kšœžœ žœ1˜Všžœžœ˜'Kšœ=™=Kšœžœžœ˜,Kšœ˜Kšœ˜—Kšœ˜Kšœ*˜*Kšœ˜Kšœ˜KšœD˜DKšœ˜Kšœ˜Kšœžœžœžœ˜=Kšœ$˜$Kšœ$˜$Kšœžœ˜Kšœ˜—KšžœB˜FKšœ˜K˜—š  œžœžœj˜€Kšœžœ˜(šžœžœžœžœ˜3Kšœžœ˜)Kšœ žœ˜$K˜$KšœA˜Ašœžœ˜Kšœžœžœ#˜3Kšœžœžœ#˜3šœ žœ˜Kšœ*˜*Kšœžœ˜,—Kšœ*˜*Kšœžœ˜3Kšœ˜Kšœžœ˜K˜—šœžœ™ Kšœžœžœ#™3Kšœžœžœ#™3šœ žœ™#Kšœžœ™,—šœ™Kšœ+™+Kšœ™—Kšœžœ ™)Kšœžœ™3Kšœ™Kšœžœ™K™—Kšœ žœ žœ˜4Kšœžœ žœ/˜Tšžœžœ˜'K™=Kšœžœžœ˜,Kšœ˜Kšœ˜—Kšœ˜Kšœ˜Kšœ˜KšœD˜DKšœžœžœžœ˜=Kšœ$˜$Kšœ$˜$Kšœ˜Kšœ˜—šžœ˜Kšœ?˜?Kšœ˜K˜—Kšœ˜K˜—K˜š œžœ žœžœ žœžœžœžœ˜JKšœ%žœ™5Kšœžœ˜Kš œ žœ žœžœžœ˜+K˜$KšœA˜AKšœ˜Kšœ˜Kšœ)˜)Kšœ˜Kšœ˜Kšœžœžœ˜>Kšœ˜Kšœ˜Kšžœžœžœ˜/Kšœ:˜:Kšœ˜Kšœžœ˜Kšžœžœžœ˜.Kšžœžœžœžœžœžœžœžœžœžœ˜TKšžœžœ˜Kšœ˜K˜—š  œžœžœNžœ žœžœ˜€Kšœ2˜2Kšœ3˜3šœ žœ˜&K˜Kšœ ˜ šžœžœžœ ž˜.Kšœžœ ˜Kšžœžœ˜(KšœY˜YK•StartOfExpansionu[dest: SampleMapOps.SampleMap, destStart: CVEC, source: SampleMapOps.SubMap, function: SampleMapOps.Function]šœq˜qKšžœžœEžœžœ ˜^Kšžœžœ˜!Kšžœ˜—K˜—Kšžœ žœ žœž˜!Kš žœžœžœžœ ž˜;šžœ˜Kšœ žœžœžœ˜2Kšœžœžœ)˜EKšœK˜KK˜—Kšœ˜K˜—š  œžœžœ:žœ žœžœ˜lKšœ0˜0Kšœžœ˜(Kšœžœ˜6šœ žœ˜&K˜Kšœ ˜ šžœžœžœ ž˜.Kšœžœ ˜Kšžœžœ˜(KšœW˜WKšžœ žœ[˜jKšžœžœDžœžœ ˜^Kšžœžœ˜!Kšžœ˜—K˜—Kšžœ žœ žœž˜!Kš žœžœžœžœ ž˜:šžœ˜Kšœ žœžœžœ˜2KšœG˜GK˜—Kšœ˜K˜—š œžœžœ5žœ ˜WK˜0K˜-šœ žœ˜&Kšœ žœ0˜>Kšœ ˜ šžœ‘œž˜*J–u[dest: SampleMapOps.SampleMap, destStart: CVEC, source: SampleMapOps.SubMap, function: SampleMapOps.Function]šœ?˜?J–S[dest: SampleMapOps.SubMap, value: CARDINAL, function: SampleMapOps.Function]šžœžœ<˜PKšžœ"žœžœžœ˜CKšžœ˜—šžœ‘œž˜*J–u[dest: SampleMapOps.SampleMap, destStart: CVEC, source: SampleMapOps.SubMap, function: SampleMapOps.Function]šœ=˜=J–S[dest: SampleMapOps.SubMap, value: CARDINAL, function: SampleMapOps.Function]šžœžœ<˜PKšžœ"žœžœžœ˜CKšžœ˜—K˜—Kšœa˜aKšžœ žœ˜!š žœžœžœžœ ž˜2šžœ‘œž˜1Kšœ:˜:Kšžœ!žœžœžœ˜FKšžœ˜—šžœ‘œž˜1Kšœ:˜:Kšžœ!žœžœžœ˜FKšžœ˜—Kšžœ˜—Kšžœ˜Kšœ˜K˜—K™š œžœžœ žœQžœžœžœ˜΄Kš œžœžœžœžœžœžœ˜…Kšœ žœžœžœ˜HKšœ:˜:Kšœ[˜[K˜ š žœžœž œžœž˜"š žœžœž œžœž˜"Kš œžœ žœžœžœ ˜HKšœ˜Kšžœ˜—Kšžœ˜—Kšœ˜Kšœ˜Kšžœ˜ Kšœ˜K˜—K˜š žœžœžœ žœ žœ˜6K˜—š  œžœžœžœžœžœ˜2KšœF™Fšžœžœ˜ Kšœ ˜ Kšœ ˜ Kšžœ žœ!‘!œ˜UKšœN˜NKšœ™Kšœžœ ˜#Kšœ˜—Kšžœžœ˜*Kšœ˜K˜—š  œžœžœžœžœ žœ˜7Kšœ*™*šžœžœ˜ Kšœ ˜ Kšžœ žœžœ˜9Kšžœžœžœ˜@Kšžœ)˜/Kšœ˜—Kšžœžœ˜Kšœ˜K˜—š œžœžœžœžœžœžœ˜=Kšžœžœžœžœ˜7Kšžœžœžœžœ˜4Kšœ˜K˜—K˜š œžœ@žœ žœ-˜ŽK˜0šžœžœžœ ž˜K˜/Kšœ žœ˜Kšœžœ ˜&Kšœ žœ,˜8Kšœ@˜@šžœžœžœ ž˜Kšœ3˜3K˜Kšœ˜Kšžœžœ˜.Kšžœ˜—Kšžœ˜—Kšœ˜K˜—š  œžœ@žœ žœ-˜‘Kšœ)˜)K˜=K˜K˜—š œžœBžœ žœ-˜˜™K™3K™—˜K˜0K˜ Kšœ˜K˜Kšœ žœ˜Kšœžœ˜#Kšœ žœ*˜6Kšœ;˜;šžœž˜Kšœžœžœ'˜7K˜K˜šžœž˜Kšœžœžœ'˜7Kšœp˜pK˜K˜ Kšžœ˜—K˜K˜ Kšœ žœžœžœ˜SKšžœ˜—K˜—Kšžœžœžœ ˜9K˜Kšœ˜K˜—š  œžœžœ@žœ žœ-˜‘K˜)K˜6K˜K˜—šœ žœ˜K™—š   œžœžœBžœ žœ-˜˜Kšœžœ˜(šžœ žœ"žœžœžœ'žœ žœ žœžœžœ˜ΨK˜$KšœA˜AKšœ žœ˜*Kšœ žœ˜*šœžœ˜K˜0šœ žœ˜Kšœ.˜.Kšœ*˜*—Kšœ žœžœ ˜8Kšœ*˜*Kšœ%˜%Kšœ!žœ ˜1Kšœ"˜"Kšœ˜Kšœ ˜ Kšœžœ˜Kšœ˜—Kšœ˜Kšœ˜KšœP˜PKšœžœžœžœ˜=Kšœ$˜$Kšœ$˜$Kšœ˜Kšœ˜—š žœžœ"žœžœ(žœ˜zKšœžœ˜KšœžœJ˜VKšœ<˜<šœžœ˜0Kšœ7˜7Kšœq˜qKšœ˜—Kšœ@˜@K˜—Kšžœ>˜BKšœ˜K˜—K˜š  œžœžœžœ žœžœžœ˜qKšœ˜šžœžœžœ%ž˜6Kšœžœ"˜+—šžœžœžœž˜$Kšœ5˜5Kšžœ˜—K˜Kšžœ˜ K˜K˜—Kšœžœ˜š œžœžœžœžœ˜OK˜—š œžœžœžœžœ žœžœ˜ešžœžœžœž™*Kšœ*™*šžœžœžœžœ™/Kšœžœ™Kšœ™Kšžœ™ Kšœ™—Kšžœ™—Kšžœ=˜CKšœ˜K˜—š œžœžœžœ˜AKšœ™Kšœ™š žœžœžœžœžœž™8J™+Jšœ(™(Jšœ™Jšœ™Kš žœ žœžœžœžœ™?Kšžœ™—Kšœ˜K˜—š  œžœžœžœ žœ žœ˜eKšœ]˜]Kšœžœ#˜:Kšœ˜Kšœ˜K˜—š   œžœžœžœ8žœžœ˜Kšœ˜šžœžœžœ%ž˜6Kšœžœ˜(—šžœžœžœž˜$šœžœž˜7Kšœ'žœžœ˜E—KšœQ˜QKšžœ˜—Kšœ˜Kšžœ˜ K˜K˜—š   œžœžœ]žœ žœ˜–šžœžœžœž˜*J˜9Kšžœ˜—K˜K˜—š   œžœžœ]žœ žœ3˜Ήšžœžœžœž˜*J˜CKšžœ˜—K˜K˜—K˜š œ žœžœžœžœ˜$K˜—š  œžœžœžœžœ˜)Kšžœžœžœžœ˜)Kšœžœ˜Kš žœžœžœžœžœ˜&K˜K˜—š  œžœžœžœžœžœ˜@Kšœ˜šžœžœ˜Kšœ žœ˜Kš œ žœžœžœžœžœ˜NKšœ<˜