<<>> <> <> <> <> <> <> DIRECTORY Basics USING [BITAND, BITLSHIFT, BITNOT, BITOR, BITRSHIFT, BITXOR, CopyBits, FillBits, FillWords, NonNegative, RaiseBoundsFault, RawBytes, RawWords, UnsafeBlock], ImagerSample USING [BitAddress, BitsPerSample, EdgeAction, Function, maxBox, maxCount, maxSample, maxVec, nullFunction, ObjectGetProc, ObjectPutProc, ObjectSampleMap, RasterSampleMap, RawDescriptor, Sample, SampleBuffer, SampleBufferRep, SampleMap, SampleMapRep, zeroVec], ImagerSamplePrivate USING [SampleMapClassRep], ImagerSwitches USING [BoolValue, Define], RasterOp USING [forwardOp, Unpack, Pack, WordPtr, Offset, OrBlt, AndCBlt, Word, tileOp], RuntimeError USING [BoundsFault], SafeStorage USING [GetUntracedZone], ImagerScaled USING [Floor, PLUS, Value, zero], SF USING [Box, BoxAction, BoxGenerator, Intersect, Size, Vec], SFInline USING [Add, Displace, Empty, In, Intersect, Neg, Nonempty, Size, SizeF, SizeS, Sub]; ImagerSampleImpl: CEDAR MONITOR IMPORTS Basics, ImagerSwitches, RasterOp, RuntimeError, SafeStorage, ImagerScaled, SF, SFInline EXPORTS ImagerSample ~ BEGIN OPEN ImagerSample; <> useCharBlt: CHAR['a..'z] ~ ImagerSwitches.Define['c, $charblt, "Use CharBlt for characters", $true]; <> <> Box: TYPE ~ SF.Box; BoxAction: TYPE ~ SF.BoxAction; BoxGenerator: TYPE ~ SF.BoxGenerator; RawBytesPtr: TYPE ~ POINTER TO Basics.RawBytes; RawSamples: TYPE ~ Basics.RawWords; RawSamplesPtr: TYPE ~ LONG POINTER TO RawSamples; Vec: TYPE ~ SF.Vec; SampleMapClass: TYPE ~ REF SampleMapClassRep; SampleMapClassRep: PUBLIC TYPE = ImagerSamplePrivate.SampleMapClassRep; <> <<<>>> <> <> <<];>> <<>> MultipleReleaseOfScratch: PUBLIC ERROR ~ CODE; <> bpu: NAT = BITS[UNIT]; bpw: NAT = BITS[WORD]; upw: NAT = UNITS[WORD]; InlineSize: PROC [map: SampleMap] RETURNS [Vec] ~ INLINE { <<-- Assumes there is no crossover.>> RETURN [[s: map.box.max.s-map.box.min.s, f: map.box.max.f-map.box.min.f]] }; CleanBox: PROC [box: Box] RETURNS [Box] ~ INLINE { <<-- Eliminates crossover of the boundaries; we maintain this invariant to simplify the size calculations.>> IF box.min.s >= box.max.s OR box.min.f >= box.max.f THEN box.max ¬ box.min; RETURN [box]; }; IsPowerOfTwo: PROC [c: CARDINAL] RETURNS [BOOL] ~ INLINE { RETURN [Basics.BITAND[c, c-1]=0]; }; Lg: PROC [a: NAT] RETURNS [lg: NAT ¬ 0] ~ { WHILE a > 32 DO a ¬ (a + 31)/32; lg ¬ lg + 5; ENDLOOP; lg ¬ lg + LgTable[a]; }; LgTable: PACKED ARRAY [0..32] OF BYTE = [ 0, 0, 1, 2, 2, 3, 3, 3, 3, -- 0..8 4, 4, 4, 4, 4, 4, 4, 4, -- 9..16 5, 5, 5, 5, 5, 5, 5, 5, -- 17..24 5, 5, 5, 5, 5, 5, 5, 5]; -- 25..32 MaskFromBPS: PROC [bitsPerSample: NAT] RETURNS [WORD] ~ INLINE { RETURN [Basics.BITRSHIFT[LAST[WORD], Lo5[bpw-bitsPerSample]]] }; WordsForBits: PROC [bitCount: CARD] RETURNS [CARD] ~ INLINE { RETURN [(bitCount+(bpw-1))/bpw] }; WordFloorUnitsForBits: PROC [bitCount: CARD] RETURNS [CARD] ~ INLINE { RETURN [bitCount/bpw*upw] }; IndexBit: PROC [addr: BitAddress, offset: CARD] RETURNS [BitAddress] ~ INLINE { bitOffset: CARD = offset + addr.bit; mod: CARDINAL [0..bpw) = bitOffset MOD bpw; RETURN [[word: addr.word + (bitOffset - mod) / bpu, bit: mod]] }; IndexWord: PROC [word: POINTER, bitOffset: CARD] RETURNS [POINTER] ~ INLINE { mod: CARDINAL [0..bpw) = bitOffset MOD bpw; RETURN [word + (bitOffset - mod) / bpu] }; BoundsCheck: PROC [arg: INT, limit: NAT] RETURNS [NAT] ~ INLINE { IF LOOPHOLE[arg, CARD] >= CARD[limit] THEN ERROR RuntimeError.BoundsFault ELSE RETURN [arg] }; Check: PROC [value: INT, bound: NAT] RETURNS [NAT] ~ INLINE { IF LOOPHOLE[value, CARD] > CARD[bound] THEN ERROR RuntimeError.BoundsFault ELSE RETURN [value] }; <> 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; TRUSTED { buf ¬ SafeStorage.GetUntracedZone[].NEW[SampleBufferRep[length]] }; buf.length ¬ length; RETURN [buf] }; ReleaseScratchSamples: PUBLIC ENTRY PROC [buffer: SampleBuffer] ~ { slot: NAT ¬ 0; buffer.length ¬ 0; FOR i: NAT IN [0..nScratchSampleBuffers) DO t: SampleBuffer ¬ scratchSampleBuffers[i]; IF t = NIL THEN slot ¬ i ELSE IF buffer = t THEN RETURN WITH ERROR MultipleReleaseOfScratch; ENDLOOP; scratchSampleBuffers[slot] ¬ buffer; }; DoWithScratchSamples: PUBLIC PROC [length: NAT, action: PROC [SampleBuffer]] ~ { scratch: SampleBuffer ~ ObtainScratchSamples[length]; action[scratch ! UNWIND => ReleaseScratchSamples[scratch]]; ReleaseScratchSamples[scratch]; }; InlinePointerToSamples: PROC [buffer: SampleBuffer, start: NAT, count: NAT] RETURNS [RawSamplesPtr] ~ TRUSTED INLINE { check: NAT ~ NAT[buffer.maxLength-start]-count; RETURN[LOOPHOLE[buffer, LONG POINTER]+SIZE[SampleBufferRep[start]]]; }; PointerToSamples: PUBLIC PROC [buffer: SampleBuffer, start: NAT, count: NAT] RETURNS [RawSamplesPtr] ~ { RETURN [InlinePointerToSamples[buffer, start, count]] }; FlipSamples: PUBLIC PROC [buffer: SampleBuffer, start, count: NAT] ~ { size: NAT ~ MIN[buffer.length-start, count]; IF size > 0 THEN TRUSTED { pointer: RawSamplesPtr ~ InlinePointerToSamples[buffer, start, 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 [buffer: SampleBuffer, value: Sample, start, count: NAT] ~ { size: NAT ~ MIN[buffer.length-start, count]; IF size > 0 THEN TRUSTED { pointer: RawSamplesPtr ~ InlinePointerToSamples[buffer, start, size]; Basics.FillWords[dst: pointer, count: size, value: value]; }; }; CopySamples: PUBLIC PROC [dst, src: SampleBuffer, dstStart, srcStart, count: NAT] ~ { size: NAT = MIN[NAT[dst.length-dstStart], NAT[src.length-srcStart], count]; IF size > 0 THEN TRUSTED { dstPointer: RawSamplesPtr ¬ InlinePointerToSamples[dst, dstStart, size]; srcPointer: RawSamplesPtr ¬ InlinePointerToSamples[src, srcStart, size]; rem: INTEGER ¬ size; WHILE rem >= 4 DO dstPointer[0] ¬ srcPointer[0]; dstPointer[1] ¬ srcPointer[1]; dstPointer[2] ¬ srcPointer[2]; dstPointer[3] ¬ srcPointer[3]; dstPointer ¬ dstPointer + SIZE[WORD]*4; srcPointer ¬ srcPointer + SIZE[WORD]*4; rem ¬ rem - 4; ENDLOOP; IF rem > 0 THEN { dstPointer[0] ¬ srcPointer[0]; IF rem > 1 THEN { dstPointer[1] ¬ srcPointer[1]; IF rem > 2 THEN dstPointer[2] ¬ srcPointer[2]; }; }; }; }; ClearSamples: PUBLIC PROC [buffer: SampleBuffer, start, count: NAT] ~ { size: NAT ~ MIN[buffer.length-start, count]; IF size > 0 THEN TRUSTED { pointer: RawSamplesPtr ~ InlinePointerToSamples[buffer, start, size]; Basics.FillWords[dst: pointer, count: size, value: 0]; }; }; <> UnsafeNewSampleMap: PUBLIC UNSAFE PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: INT, base: BitAddress, ref: REF, words: INT, scratchDescriptor: SampleMap ¬ NIL] RETURNS [RasterSampleMap] ~ { size: Vec ~ SF.Size[box]; bpl: CARD ~ bitsPerLine; dataBitsPerLine: CARD ~ NAT[size.f]*LONG[bitsPerSample]; fillBits: CARD ~ INT[bitsPerLine]-INT[dataBitsPerLine]; -- check sufficient bitsPerLine totalBits: CARD ~ (NAT[size.s]*bpl)+base.bit-fillBits; totalWords: CARD ~ WordsForBits[totalBits]; spareWords: INT ~ Basics.NonNegative[words-INT[totalWords]]; -- check sufficient words s: RasterSampleMap ¬ NIL; WITH scratchDescriptor SELECT FROM r: RasterSampleMap => s ¬ r ENDCASE => s ¬ NEW[SampleMapRep.raster]; s­ ¬ [box: CleanBox[box], immutable: FALSE, bitsPerSample: bitsPerSample, v: raster[bitsPerLine: bpl, base: base, ref: ref]]; RETURN[s]; }; BitsForSamples: PUBLIC PROC [fSize: NAT, bitsPerSample: NAT] RETURNS [INT] ~ { <> num: CARD ~ ((CARD[fSize]*CARD[bitsPerSample] + (bpw-1)) / bpw) * bpw; RETURN[num]; }; WordsForLines: PUBLIC PROC [sSize: NAT, bitsPerLine: INT] RETURNS [INT] ~ { RETURN[WordsForBits[sSize*CARD[bitsPerLine]]]; }; WordsForMap: PUBLIC PROC [size: Vec, bitsPerSample: BitsPerSample, bitsPerLine: INT ¬ 0] RETURNS [INT] ~ { IF bitsPerLine=0 THEN bitsPerLine ¬ BitsForSamples[size.f, bitsPerSample]; RETURN[WordsForLines[size.s, bitsPerLine]]; }; WordSeqRep: TYPE ~ RECORD [SEQUENCE nWords: NAT OF WORD]; PointerToWords: UNSAFE PROC [wordSeq: REF WordSeqRep] RETURNS [LONG POINTER] ~ UNCHECKED INLINE { RETURN [LOOPHOLE[wordSeq, LONG POINTER]+SIZE[WordSeqRep[0]]] }; NewSampleMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: INT ¬ 0] RETURNS [RasterSampleMap] ~ TRUSTED { size: Vec ~ SF.Size[box]; actualBitsPerLine: INT ~ SELECT bitsPerLine FROM 0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine; words: INT ~ WordsForLines[size.s, actualBitsPerLine]; wordSeq: REF WordSeqRep ~ SafeStorage.GetUntracedZone[].NEW[WordSeqRep[words]]; map: RasterSampleMap ~ UnsafeNewSampleMap[ box: box, bitsPerSample: bitsPerSample, bitsPerLine: actualBitsPerLine, base: [word: PointerToWords[wordSeq], bit: 0], ref: wordSeq, words: wordSeq.nWords ]; RETURN[map] }; NewObjectSampleMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, getSamples: ObjectGetProc, putSamples: ObjectPutProc, data: REF] RETURNS [ObjectSampleMap] ~ { new: ObjectSampleMap ~ NEW[SampleMapRep.object ¬ [box: CleanBox[box], immutable: putSamples=NIL, bitsPerSample: bitsPerSample, v: object[class: NEW[SampleMapClassRep ¬ [getSamples: getSamples, putSamples: putSamples]], data: data]]]; RETURN [new] }; Copy: PUBLIC PROC [map: SampleMap, delta: Vec, box: Box] RETURNS [RasterSampleMap] ~ { temp: SampleMap ¬ ReIndex[map, delta, box]; new: RasterSampleMap ~ NewSampleMap[box: temp.box, bitsPerSample: map.bitsPerSample]; Transfer[dst: new, src: temp]; ReleaseDescriptor[temp]; RETURN[new]; }; nScratchDescs: NAT ~ 15; scratchDescsTop: [0..nScratchDescs] ¬ 0; scratchDescs: ARRAY [0..nScratchDescs) OF RasterSampleMap ¬ ALL[NIL]; NewDescriptor: ENTRY PROC [map: RasterSampleMap ¬ NIL] RETURNS [new: RasterSampleMap ¬ NIL] ~ { IF scratchDescsTop = 0 THEN new ¬ NEW[SampleMapRep.raster] ELSE { scratchDescsTop ¬ scratchDescsTop - 1; new ¬ scratchDescs[scratchDescsTop]; scratchDescs[scratchDescsTop] ¬ NIL; }; IF map # NIL THEN new­ ¬ map­; }; ReleaseDescriptor: PUBLIC ENTRY PROC [map: SampleMap] ~ { WITH map SELECT FROM map: RasterSampleMap => { FOR i: NAT IN [0..scratchDescsTop) DO IF map = scratchDescs[i] THEN RETURN WITH ERROR MultipleReleaseOfScratch; ENDLOOP; IF scratchDescsTop < nScratchDescs THEN { map.box ¬ [[0,0],[0,0]]; map.base.word ¬ NIL; map.ref ¬ NIL; scratchDescs[scratchDescsTop] ¬ map; scratchDescsTop ¬ scratchDescsTop + 1; }; }; ENDCASE => NULL; }; nScratchMaps: NAT ~ 10; scratchMaps: ARRAY [0..nScratchMaps) OF RasterSampleMap ¬ ALL[NIL]; ObtainScratchMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, bitsPerLine: INT ¬ 0] RETURNS [RasterSampleMap] ~ { size: Vec ~ SF.Size[box]; actualBitsPerLine: INT ~ SELECT bitsPerLine FROM 0 => BitsForSamples[size.f, bitsPerSample], ENDCASE => bitsPerLine; nWords: INT ~ WordsForLines[sSize: size.s, bitsPerLine: actualBitsPerLine]; TryObtainScratch: ENTRY PROC [nw: INT] RETURNS [RasterSampleMap] ~ TRUSTED { besti: INT ¬ -1; smallestWaste: INT ¬ INT.LAST; FOR i: NAT IN [0..nScratchMaps) DO <> s: RasterSampleMap = scratchMaps[i]; IF s # NIL THEN { WITH s.ref SELECT FROM wordSeq: REF WordSeqRep => { IF nw <= wordSeq.nWords THEN { waste: INT ~ wordSeq.nWords - nw; IF waste < smallestWaste THEN { besti ¬ i; smallestWaste ¬ waste; }; }; }; ENDCASE; }; ENDLOOP; IF besti >= 0 THEN { s: RasterSampleMap = scratchMaps[besti]; scratchMaps[besti] ¬ NIL; WITH s.ref SELECT FROM wordSeq: REF WordSeqRep => { ptr: POINTER ~ PointerToWords[wordSeq]; s.base ¬ [word: ptr, bit: 0]; }; ENDCASE => ERROR; RETURN [s]; }; RETURN [NIL] }; s: RasterSampleMap ¬ TryObtainScratch[nWords]; IF s = NIL THEN s ¬ NewSampleMap[box: box, bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine] ELSE { s.box ¬ CleanBox[box]; s.bitsPerSample ¬ bitsPerSample; s.bitsPerLine ¬ actualBitsPerLine; }; RETURN [s] }; ReleaseScratchMap: PUBLIC ENTRY PROC [map: SampleMap] ~ { IF map.immutable THEN RETURN; WITH map SELECT FROM map: RasterSampleMap => { slot: INT ¬ -1; WITH map.ref SELECT FROM wordSeq: REF WordSeqRep => {}; ENDCASE => RETURN; FOR i: NAT IN [0..nScratchMaps) DO t: RasterSampleMap ~ scratchMaps[i]; SELECT TRUE FROM t = NIL => slot ¬ i; map = t OR map.ref = t.ref => RETURN WITH ERROR MultipleReleaseOfScratch; ENDCASE; ENDLOOP; IF slot >= 0 THEN { scratchMaps[slot] ¬ map} ELSE { <> scratchMaps[0] ¬ map; FOR i: NAT IN (0..nScratchMaps) DO scratchMaps[i] ¬ NIL; ENDLOOP; }; }; ENDCASE; }; ClearScratchMaps: ENTRY PROC ~ { FOR i: NAT IN (0..nScratchMaps) DO scratchMaps[i] ¬ NIL; ENDLOOP; }; DoWithScratchMap: PUBLIC PROC [box: Box, bitsPerSample: BitsPerSample, action: PROC [RasterSampleMap]] ~ { scratch: RasterSampleMap ~ ObtainScratchMap[box: box, bitsPerSample: bitsPerSample]; action[scratch ! UNWIND => ReleaseScratchMap[scratch]]; ReleaseScratchMap[scratch]; }; <> InPlaceClip: UNSAFE PROC [map: RasterSampleMap, box: Box] ~ { newBox: Box ~ SFInline.Intersect[map.box, box]; skipBits: CARD ~ NAT[newBox.min.s-map.box.min.s]*CARD[map.bitsPerLine] + NAT[newBox.min.f-map.box.min.f]*CARD[map.bitsPerSample]; map.box ¬ CleanBox[newBox]; map.base ¬ IndexBit[map.base, skipBits]; }; BoundedAdd: PROC [a, b: INTEGER] RETURNS [INTEGER] ~ INLINE { IF a > 0 THEN RETURN [IF b < INTEGER.LAST-a THEN a+b ELSE INTEGER.LAST] ELSE RETURN [IF b > INTEGER.FIRST-a THEN a+b ELSE INTEGER.FIRST]; }; BoundedVecAdd: PROC [a, b: Vec] RETURNS [Vec] ~ INLINE { RETURN[[s: BoundedAdd[a.s, b.s], f: BoundedAdd[a.f, b.f]]] }; InPlaceShift: UNSAFE PROC [map: RasterSampleMap, delta: Vec] ~ UNCHECKED { newmin: SF.Vec ~ BoundedVecAdd[map.box.min, delta]; newmax: SF.Vec ~ BoundedVecAdd[map.box.max, delta]; chk: SF.Box ~ [min: SFInline.Sub[newmin, delta], max: SFInline.Sub[newmax, delta]]; IF chk # map.box THEN InPlaceClip[map, chk]; map.box ¬ CleanBox[[min: newmin, max: newmax]]; }; ObtainUnsafeDescriptor: PUBLIC UNSAFE PROC [size: Vec, bitsPerSample: BitsPerSample, bitsPerLine: INT, base: BitAddress, ref: REF, words: INT, rawMin, delta: Vec ¬ zeroVec] RETURNS [RasterSampleMap] ~ UNCHECKED { new: RasterSampleMap ~ UnsafeNewSampleMap[box: [[0, 0], SFInline.Add[rawMin, size]], bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine, base: base, ref: ref, words: words, scratchDescriptor: NewDescriptor[]]; InPlaceShift[new, SFInline.Neg[rawMin]]; InPlaceClip[new, [[0, 0], size]]; InPlaceShift[new, delta]; RETURN [new]; }; CheckBuffer: PROC [map: SampleMap, sInitIndex: INTEGER, fInitIndex: INTEGER, sDelta: INTEGER, fDelta: INTEGER, count: NAT] ~ { IF count # 0 THEN { countMinus1: INTEGER = count-1; s0: INTEGER ¬ sInitIndex; s1: INTEGER ¬ sInitIndex + (IF sDelta = 0 THEN 0 ELSE sDelta*countMinus1); f0: INTEGER ¬ fInitIndex; f1: INTEGER ¬ fInitIndex + (IF fDelta = 1 THEN countMinus1 ELSE fDelta*countMinus1); IF s0 > s1 THEN { t: INTEGER ¬ s0; s0 ¬ s1; s1 ¬ t }; IF f0 > f1 THEN { t: INTEGER ¬ f0; f0 ¬ f1; f1 ¬ t }; IF s0 < map.box.min.s OR f0 < map.box.min.f OR s1 >= map.box.max.s OR f1 >= map.box.max.f THEN Basics.RaiseBoundsFault[]; }; }; ReIndexDataRep: TYPE ~ RECORD [delta: SF.Vec, base: SampleMap]; ReIndexGetSamples: PROC [self: ObjectSampleMap, initIndex: SF.Vec, buffer: SampleBuffer, start, count: NAT] ~ { data: REF ReIndexDataRep ~ NARROW[self.data]; count ¬ MIN[buffer.length-start, count]; IF count > 0 THEN { GetSamples[map: data.base, initIndex: SFInline.Sub[initIndex, data.delta], buffer: buffer, start: start, count: count]; }; }; ReIndexPutSamples: PROC [self: ObjectSampleMap, initIndex: SF.Vec, buffer: SampleBuffer, start, count: NAT, function: Function] ~ { data: REF ReIndexDataRep ~ NARROW[self.data]; count ¬ MIN[buffer.length-start, count]; IF count > 0 THEN { PutSamples[map: data.base, initIndex: SFInline.Sub[initIndex, data.delta], buffer: buffer, start: start, count: count, function: function]; }; }; ReIndex: PUBLIC PROC [map: SampleMap, delta: Vec, box: Box] RETURNS [SampleMap] ~ TRUSTED { WITH map SELECT FROM map: RasterSampleMap => { new: RasterSampleMap ¬ NewDescriptor[map]; InPlaceShift[new, delta]; InPlaceClip[new, box]; RETURN [new] }; map: ObjectSampleMap => { data: REF ReIndexDataRep ~ NEW[ReIndexDataRep ¬ [delta: delta, base: map]]; newBox: SF.Box ~ SF.Intersect[box, SFInline.Displace[map.box, delta]]; new: ObjectSampleMap ~ NewObjectSampleMap[box: newBox, bitsPerSample: map.bitsPerSample, getSamples: ReIndexGetSamples, putSamples: IF map.immutable THEN NIL ELSE ReIndexPutSamples, data: data]; RETURN [new] }; ENDCASE => RETURN [NIL]; }; Clip: PUBLIC PROC [map: SampleMap, box: Box] RETURNS [SampleMap] ~ { RETURN[ReIndex[map: map, delta: zeroVec, box: box]]; }; Shift: PUBLIC PROC [map: SampleMap, delta: Vec] RETURNS [SampleMap] ~ { RETURN[ReIndex[map: map, delta: delta, box: maxBox]]; }; ZeroOrigin: PUBLIC PROC [map: SampleMap] RETURNS [SampleMap] ~ { RETURN[ReIndex[map: map, delta: SFInline.Neg[map.box.min], box: maxBox]]; }; <> GetUnsafeBlock: PUBLIC PROC [map: RasterSampleMap] RETURNS [Basics.UnsafeBlock] ~ { size: Vec ~ InlineSize[map]; bitsPerLine: CARD ~ map.bitsPerLine; dataBitsPerLine: CARD ~ CARD[map.bitsPerSample]*CARD[size.f]; fillBitsPerLine: [0..0] ~ bitsPerLine - dataBitsPerLine; padStart: [0..0] ~ map.base.bit MOD 8; nBytes: NAT ~ (CARD[size.s]*bitsPerLine + 7)/8; RETURN [[base: LOOPHOLE[map.base.word], startIndex: map.base.bit/8, count: nBytes]] }; <> ApplyFunction: PUBLIC PROC [function: Function, dstVal, srcVal: Sample] RETURNS [Sample] ~ { IF function.srcFunc = complement THEN srcVal ¬ Basics.BITNOT[srcVal]; SELECT function.dstFunc FROM null => RETURN [srcVal]; and => RETURN [Basics.BITAND[dstVal, srcVal]]; or => RETURN [Basics.BITOR[dstVal, srcVal]]; xor => RETURN [Basics.BITXOR[dstVal, srcVal]]; ENDCASE => ERROR; }; ApplyToSamples: PUBLIC PROC [function: Function, dst, src: SampleBuffer, start, count: NAT] ~ { clippedCount: CARD ¬ MIN[count, NAT[dst.length-start], NAT[src.length-start]]; d: RawSamplesPtr ¬ PointerToSamples[dst, start, clippedCount]; s: RawSamplesPtr ¬ PointerToSamples[src, start, clippedCount]; IF clippedCount # 0 THEN TRUSTED { bits: CARD ~ clippedCount * CARD[BITS[WORD]]; RasterOp.forwardOp[function.dstFunc][function.srcFunc][ dst: [word: LOOPHOLE[d], bit: 0], src: [word: LOOPHOLE[s], bit: 0], dstBpl: bits, srcBpl: bits, sSize: 1, fSize: bits ]; }; }; Lo5: PROC [i: INTEGER] RETURNS [[0..bpw)] = INLINE { RETURN [LOOPHOLE[i, CARDINAL] MOD bpw] }; Get: PUBLIC PROC [map: SampleMap, index: Vec] RETURNS [Sample] ~ TRUSTED { WITH map SELECT FROM map: RasterSampleMap => { size: Vec ~ InlineSize[map]; v: Vec ~ SFInline.Sub[index, map.box.min]; bitsPerSample: NAT ~ map.bitsPerSample; bitIndex: CARD ~ BoundsCheck[v.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v.f, size.f]*CARD[bitsPerSample]+map.base.bit; pointer: RawSamplesPtr ~ IndexWord[map.base.word, bitIndex]; shiftAmt: INTEGER ~ Lo5[bitIndex] + bitsPerSample - bpw; mask: WORD ~ MaskFromBPS[bitsPerSample]; shiftedBits: WORD ~ IF shiftAmt <= 0 THEN Basics.BITRSHIFT[pointer[0], Lo5[-shiftAmt]] ELSE Basics.BITLSHIFT[pointer[0], Lo5[shiftAmt]] + Basics.BITRSHIFT[pointer[1], Lo5[bpw-shiftAmt]]; RETURN[Basics.BITAND[shiftedBits, mask]]; }; map: ObjectSampleMap => { buffer: SampleBuffer ~ ObtainScratchSamples[1]; sample: Sample ¬ 0; true: BOOL[TRUE..TRUE] ~ SFInline.In[index, map.box]; class: SampleMapClass ~ map.class; class.getSamples[self: map, initIndex: index, buffer: buffer, start: 0, count: 1]; sample ¬ buffer[0]; ReleaseScratchSamples[buffer]; RETURN [sample] }; ENDCASE => ERROR; }; Put: PUBLIC PROC [map: SampleMap, index: Vec, value: Sample, function: Function ¬ nullFunction] ~ { false: BOOL[FALSE..FALSE] ~ map.immutable; WITH map SELECT FROM map: RasterSampleMap => TRUSTED { size: Vec ~ InlineSize[map]; v: Vec ~ SFInline.Sub[index, map.box.min]; bitsPerSample: NAT ~ map.bitsPerSample; bitIndex: CARD ~ BoundsCheck[v.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v.f, size.f]*CARD[bitsPerSample]+map.base.bit; Basics.CopyBits[ dstBase: LOOPHOLE[map.base.word], dstStart: bitIndex, srcBase: LOOPHOLE[@value], srcStart: Lo5[bpw - bitsPerSample], count: bitsPerSample ]; }; map: ObjectSampleMap => { buffer: SampleBuffer ~ ObtainScratchSamples[1]; true: BOOL[TRUE..TRUE] ~ SFInline.In[index, map.box]; class: SampleMapClass ~ map.class; buffer[0] ¬ value; class.putSamples[self: map, initIndex: index, buffer: buffer, start: 0, count: 1, function: function]; ReleaseScratchSamples[buffer]; }; ENDCASE => NULL; }; GetSamples: PUBLIC PROC [map: SampleMap, initIndex: Vec ¬ zeroVec, delta: Vec ¬ [s: 0, f: 1], buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount] ~ { size: Vec ~ InlineSize[map]; count ¬ MIN[buffer.length-start, count]; CheckBuffer[map: map, sInitIndex: initIndex.s, fInitIndex: initIndex.f, sDelta: delta.s, fDelta: delta.f, count: count]; IF delta.s = 0 AND delta.f = 1 AND count # 0 THEN { WITH map SELECT FROM map: RasterSampleMap => TRUSTED { v0: Vec ~ SFInline.Sub[initIndex, map.box.min]; srcBitIndex: CARD ~ BoundsCheck[v0.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v0.f, size.f]*CARD[map.bitsPerSample]; RasterOp.Unpack[dst: InlinePointerToSamples[buffer, start, count], src: IndexBit[map.base, srcBitIndex], bitsPerSample: map.bitsPerSample, nSamples: count]; }; map: ObjectSampleMap => { class: SampleMapClass ~ map.class; class.getSamples[self: map, initIndex: initIndex, buffer: buffer, start: start, count: count]; }; ENDCASE => ERROR; } ELSE { WITH map SELECT FROM map: RasterSampleMap => TRUSTED { v: Vec ~ SFInline.Sub[initIndex, map.box.min]; bitsPerSample: NAT ~ map.bitsPerSample; bitIndex: CARD ¬ BoundsCheck[v.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v.f, size.f]*CARD[bitsPerSample] + map.base.bit; bitIndexDelta: INT ~ INT[map.bitsPerLine]*delta.s + INT[map.bitsPerSample]*delta.f; SELECT TRUE FROM bitsPerSample = BITS[BYTE] AND (bitIndex MOD BITS[BYTE]) = 0 AND (LOOPHOLE[bitIndexDelta, CARD] MOD BITS[BYTE]) = 0 => { <> step: INT = bitIndexDelta / BITS[BYTE]; bx: CARDINAL ¬ bitIndex / BITS[BYTE]; src: RawBytesPtr = LOOPHOLE[map.base.word, RawBytesPtr]; dst: RawSamplesPtr ¬ LOOPHOLE[InlinePointerToSamples[buffer, start, count]]; lim: RawSamplesPtr = dst + count*SIZE[WORD]; lim4: RawSamplesPtr = lim - (count MOD 4)*SIZE[WORD]; WHILE dst # lim4 DO dst[0] ¬ src[bx]; bx ¬ bx + step; dst[1] ¬ src[bx]; bx ¬ bx + step; dst[2] ¬ src[bx]; bx ¬ bx + step; dst[3] ¬ src[bx]; bx ¬ bx + step; dst ¬ dst + SIZE[WORD]*4; ENDLOOP; WHILE dst # lim DO dst[0] ¬ src[bx]; dst ¬ dst + SIZE[WORD]; bx ¬ bx + step; ENDLOOP; }; ENDCASE => { mask: WORD ~ MaskFromBPS[bitsPerSample]; FOR i: NAT IN [start..start+count) DO <> pointer: RawSamplesPtr ~ IndexWord[map.base.word, bitIndex]; shiftAmt: INTEGER ~ Lo5[bitIndex] + bitsPerSample - bpw; shiftedBits: WORD ~ IF shiftAmt <= 0 THEN Basics.BITRSHIFT[pointer[0], Lo5[-shiftAmt]] ELSE Basics.BITLSHIFT[pointer[0], Lo5[shiftAmt]] + Basics.BITRSHIFT[pointer[1], Lo5[bpw-shiftAmt]]; buffer[i] ¬ Basics.BITAND[shiftedBits, mask]; bitIndex ¬ bitIndex + bitIndexDelta; ENDLOOP; }; }; map: ObjectSampleMap => { index: Vec ¬ initIndex; class: SampleMapClass ~ map.class; FOR i: NAT IN [start..start+count) DO class.getSamples[self: map, initIndex: index, buffer: buffer, start: i, count: 1]; index ¬ SFInline.Add[index, delta]; ENDLOOP; }; ENDCASE => ERROR; }; }; PutSamples: PUBLIC PROC [map: SampleMap, initIndex: Vec ¬ zeroVec, delta: Vec ¬ [s: 0, f: 1], buffer: SampleBuffer, start: NAT ¬ 0, count: NAT ¬ maxCount, function: Function ¬ nullFunction] ~ { false: BOOL[FALSE..FALSE] ~ map.immutable; size: Vec ~ InlineSize[map]; sDelta: INTEGER = delta.s; fDelta: INTEGER = delta.f; count ¬ MIN[buffer.length-start, count]; CheckBuffer[map: map, sInitIndex: initIndex.s, fInitIndex: initIndex.f, sDelta: sDelta, fDelta: fDelta, count: count]; IF count = 0 THEN RETURN; IF (sDelta = 0 AND fDelta = 1) THEN { srcBuffer: SampleBuffer ¬ buffer; IF function # nullFunction THEN { tmpBuffer: SampleBuffer ~ ObtainScratchSamples[length: start+count]; GetSamples[map: map, initIndex: initIndex, buffer: tmpBuffer, start: start, count: count]; ApplyToSamples[function: function, dst: tmpBuffer, src: buffer, start: start, count: count]; srcBuffer ¬ tmpBuffer; }; WITH map SELECT FROM map: RasterSampleMap => TRUSTED { v0: Vec ~ SFInline.Sub[initIndex, map.box.min]; dstBitIndex: CARD ~ BoundsCheck[v0.s, size.s]*CARD[map.bitsPerLine] + BoundsCheck[v0.f, size.f]*CARD[map.bitsPerSample]; RasterOp.Pack[dst: IndexBit[map.base, dstBitIndex], src: InlinePointerToSamples[srcBuffer, start, count], bitsPerSample: map.bitsPerSample, nSamples: count]; }; map: ObjectSampleMap => { class: SampleMapClass ~ map.class; class.putSamples[self: map, initIndex: initIndex, buffer: srcBuffer, start: start, count: count, function: function]; }; ENDCASE => ERROR; IF srcBuffer # buffer THEN ReleaseScratchSamples[buffer: srcBuffer]; RETURN; }; IF map.bitsPerSample = 8 AND function=nullFunction THEN WITH map SELECT FROM map: RasterSampleMap => TRUSTED { IF map.bitsPerLine MOD 8 = 0 AND map.base.bit MOD 8 = 0 THEN { dst: POINTER TO Basics.RawBytes ¬ LOOPHOLE[map.base.word]; byteIndex: INT ¬ (map.base.bit/8) + initIndex.s * (map.bitsPerLine/8) + initIndex.f; byteDelta: INT = sDelta * (map.bitsPerLine/8) + fDelta; src: POINTER TO Basics.RawWords = InlinePointerToSamples[buffer, start, count]; FOR i: CARDINAL IN [0..count) DO dst[byteIndex] ¬ src[i]; byteIndex ¬ byteIndex + byteDelta; ENDLOOP; RETURN; }; }; ENDCASE; CHECKED { <> index: Vec ¬ initIndex; FOR i: NAT IN [start..start+count) DO PutSamples[map: map, initIndex: index, buffer: buffer, start: i, count: 1, function: function]; index ¬ SFInline.Add[index, delta]; ENDLOOP; }; }; <> Clear: PUBLIC PROC [map: SampleMap] ~ { false: BOOL[FALSE..FALSE] ~ map.immutable; WITH map SELECT FROM map: RasterSampleMap => TRUSTED { sSize: CARDINAL ~ LOOPHOLE[map.box.max.s - map.box.min.s, CARDINAL]; fSize: CARDINAL ~ LOOPHOLE[map.box.max.f - map.box.min.f, CARDINAL]; bitsPerLine: CARD ~ map.bitsPerLine; dataBitsPerLine: CARD ~ CARD[map.bitsPerSample]*CARD[fSize]; pointer: LONG POINTER ¬ map.base.word; bit: CARD ¬ map.base.bit; IF sSize = 0 THEN RETURN; IF dataBitsPerLine+bpw > bitsPerLine THEN { <> Basics.FillWords[dst: pointer, count: WordsForBits[bit+bitsPerLine*CARD[sSize]-(bitsPerLine-dataBitsPerLine)], value: 0]; } ELSE { <> FOR i: CARDINAL IN [0..sSize) DO Basics.FillWords[dst: pointer, count: WordsForBits[bit+dataBitsPerLine], value: 0]; bit ¬ bit + bitsPerLine; pointer ¬ pointer + WordFloorUnitsForBits[bit]; bit ¬ bit MOD bpw; ENDLOOP; }; }; map: ObjectSampleMap => Fill[map, map.box, 0]; ENDCASE => NULL; }; <<>> BufferTransfer: PROC [dst: SampleMap, src: SampleMap, dstMin: Vec ¬ zeroVec, srcMin: Vec ¬ zeroVec, size: Vec ¬ maxVec, function: Function ¬ nullFunction] ~ { dstSize: Vec ~ InlineSize[dst]; srcSize: Vec ~ InlineSize[src]; sDst0: NAT ~ dstMin.s - dst.box.min.s; fDst0: NAT ~ dstMin.f - dst.box.min.f; sSrc0: NAT ~ srcMin.s - src.box.min.s; fSrc0: NAT ~ srcMin.f - src.box.min.f; sSize: NAT ~ Check[Check[size.s, dstSize.s-sDst0], srcSize.s-sSrc0]; fSize: NAT ~ Check[Check[size.f, dstSize.f-fDst0], srcSize.f-fSrc0]; transferAction: PROC [buffer: SampleBuffer] ~ { FOR s: NAT IN [0..sSize) DO GetSamples[map: src, initIndex: [s: srcMin.s+s, f: srcMin.f], buffer: buffer]; PutSamples[map: dst, initIndex: [s: dstMin.s+s, f: dstMin.f], buffer: buffer, function: function]; ENDLOOP; }; DoWithScratchSamples[length: fSize, action: transferAction]; }; BasicTransfer: PUBLIC PROC [dst: SampleMap, src: SampleMap, dstMin: Vec ¬ zeroVec, srcMin: Vec ¬ zeroVec, size: Vec, function: Function ¬ nullFunction] ~ { false: BOOL[FALSE..FALSE] ~ dst.immutable; bitsPerSample: NAT ~ dst.bitsPerSample; WITH dst SELECT FROM dst: RasterSampleMap => WITH src SELECT FROM src: RasterSampleMap => IF src.bitsPerSample=bitsPerSample THEN TRUSTED { dstSize: Vec ~ InlineSize[dst]; srcSize: Vec ~ InlineSize[src]; sDst0: NAT ~ dstMin.s - dst.box.min.s; fDst0: NAT ~ dstMin.f - dst.box.min.f; sSrc0: NAT ~ srcMin.s - src.box.min.s; fSrc0: NAT ~ srcMin.f - src.box.min.f; sSize: NAT ~ Check[Check[size.s, dstSize.s-sDst0], srcSize.s-sSrc0]; fSize: NAT ~ Check[Check[size.f, dstSize.f-fDst0], srcSize.f-fSrc0]; IF sSize#0 AND fSize#0 THEN RasterOp.forwardOp[function.dstFunc][function.srcFunc][ dst: IndexBit[dst.base, CARD[sDst0]*dst.bitsPerLine + CARD[fDst0]*bitsPerSample], src: IndexBit[src.base, CARD[sSrc0]*src.bitsPerLine + CARD[fSrc0]*bitsPerSample], dstBpl: dst.bitsPerLine, srcBpl: src.bitsPerLine, sSize: sSize, fSize: CARD[fSize]*bitsPerSample ]; RETURN; } ENDCASE ENDCASE; BufferTransfer[dst, src, dstMin, srcMin, size, function]; }; RawListTransfer: PUBLIC PROC [dst: RasterSampleMap, src: LIST OF RawDescriptor, function: Function ¬ nullFunction] ~ TRUSTED { false: BOOL[FALSE..FALSE] ~ dst.immutable; bitsPerSample: [1..1] ~ dst.bitsPerSample; dstPtr: RasterOp.WordPtr = LOOPHOLE[dst.base.word]; dstOff: NAT = dst.base.bit; IF ImagerSwitches.BoolValue[useCharBlt] AND (function = [or, null] OR function = [and, complement]) AND dst.bitsPerLine MOD BITS[RasterOp.Word] = 0 THEN { <> bitsPerUnit: NAT = BITS[UNIT]; dstUnitsPerLine: NAT = dst.bitsPerLine / bitsPerUnit; op: UNSAFE PROC [src: RasterOp.WordPtr, dst: RasterOp.WordPtr, offset: RasterOp.Offset, bpc: NAT, upl: INT, lines: INT] ~ (IF function = [or, null] THEN RasterOp.OrBlt ELSE RasterOp.AndCBlt); WHILE src # NIL DO sCheck: NAT ~ dst.box.max.s-src.first.box.max.s; fCheck: NAT ~ dst.box.max.f-src.first.box.max.f; sSize: INTEGER = src.first.box.max.s-src.first.box.min.s; fSize: INTEGER = src.first.box.max.f-src.first.box.min.f; IF sSize > 0 AND fSize > 0 THEN { dstBitIndex: CARD ~ dstOff + CARD[src.first.box.min.s-dst.box.min.s]*dst.bitsPerLine + CARD[src.first.box.min.f-dst.box.min.f]; -- (know bitsPerSample = 1) mod: RasterOp.Offset = dstBitIndex MOD BITS[RasterOp.Word]; op[ src: LOOPHOLE[src.first.basePointer, RasterOp.WordPtr], dst: dstPtr + (dstBitIndex-mod) / bitsPerUnit, offset: mod, bpc: fSize, upl: dstUnitsPerLine, lines: sSize]; <> }; src ¬ src.rest; ENDLOOP; } ELSE { <> op: UNSAFE PROC [dst: BitAddress, src: BitAddress, dstBpl, srcBpl, sSize, fSize: CARDINAL] ~ RasterOp.forwardOp[function.dstFunc][function.srcFunc]; WHILE src # NIL DO box: SF.Box ~ [min: src.first.box.min, max: src.first.box.max]; sCheck: NAT ~ dst.box.max.s-box.max.s; fCheck: NAT ~ dst.box.max.f-box.max.f; dstBitIndex: CARD ~ CARD[NAT[box.min.s-dst.box.min.s]]*dst.bitsPerLine + NAT[box.min.f-dst.box.min.f]; -- (know bitsPerSample = 1) sSize: NAT = SFInline.SizeS[box]; fSize: NAT = SFInline.SizeF[box]; -- (know bitsPerSample = 1) dstBA: BitAddress = IndexBit[dst.base, dstBitIndex]; IF sSize > 0 AND fSize > 0 THEN op[ dst: dstBA, src: [word: src.first.basePointer, bit: 0], dstBpl: dst.bitsPerLine, srcBpl: src.first.bitsPerLine, sSize: sSize, fSize: fSize ]; src ¬ src.rest; ENDLOOP; }; }; Transfer: PUBLIC PROC [dst: SampleMap, src: SampleMap, delta: Vec ¬ zeroVec, function: Function ¬ nullFunction] ~ { box: Box ~ SFInline.Intersect[dst.box, SFInline.Displace[src.box, delta]]; IF SFInline.Nonempty[box] THEN BasicTransfer[dst: dst, src: src, dstMin: box.min, srcMin: SFInline.Sub[box.min, delta], size: SFInline.Size[box], function: function]; }; TransferBoxes: PUBLIC PROC [dst: SampleMap, src: SampleMap, delta: Vec ¬ zeroVec, boxes: BoxGenerator, function: Function ¬ nullFunction] ~ { bitsPerSample: NAT ~ dst.bitsPerSample; false: BOOL[FALSE..FALSE] ~ dst.immutable; WITH dst SELECT FROM dst: RasterSampleMap => WITH src SELECT FROM src: RasterSampleMap => IF src.bitsPerSample=bitsPerSample THEN TRUSTED { dstBox: Box ~ dst.box; srcBox: Box ~ SFInline.Displace[src.box, delta]; bounds: Box ~ SFInline.Intersect[dstBox, srcBox]; dstBPL: NAT ~ dst.bitsPerLine; srcBPL: NAT ~ src.bitsPerLine; op: UNSAFE PROC [dst: BitAddress, src: BitAddress, dstBpl, srcBpl, sSize, fSize: CARDINAL] ~ RasterOp.forwardOp[function.dstFunc][function.srcFunc]; FastTransfer: BoxAction ~ TRUSTED { sSize: INTEGER = box.max.s-box.min.s; fSize: INTEGER = box.max.f-box.min.f; IF sSize > 0 AND fSize > 0 THEN { sSpaceLo: NAT ~ box.min.s-bounds.min.s; -- for bounds check fSpaceLo: NAT ~ box.min.f-bounds.min.f; sSpaceHi: NAT ~ bounds.max.s-box.max.s; fSpaceHi: NAT ~ bounds.max.f-box.max.f; dstBitIndex: CARD ~ CARD[NAT[box.min.s-dstBox.min.s]]*dstBPL + CARD[NAT[box.min.f-dstBox.min.f]]*bitsPerSample; srcBitIndex: CARD ~ CARD[NAT[box.min.s-srcBox.min.s]]*srcBPL + CARD[NAT[box.min.f-srcBox.min.f]]*bitsPerSample; dstBA: BitAddress = IndexBit[dst.base, dstBitIndex]; srcBA: BitAddress = IndexBit[src.base, srcBitIndex]; op[ dst: dstBA, src: srcBA, dstBpl: dstBPL, srcBpl: srcBPL, sSize: sSize, fSize: CARD[NAT[fSize]]*bitsPerSample ]; }; }; boxes[FastTransfer]; RETURN; } ENDCASE ENDCASE; -- slow case -- { SlowTransfer: BoxAction ~ { dstMin: Vec ~ box.min; srcMin: Vec ~ SFInline.Sub[box.min, delta]; BufferTransfer[dst, src, dstMin, srcMin, SFInline.Sub[box.max, box.min], function]; }; boxes[SlowTransfer]; }; }; Move: PUBLIC PROC [map: SampleMap, dstMin, srcMin, size: Vec, function: Function] ~ TRUSTED { false: BOOL[FALSE..FALSE] ~ map.immutable; selfSize: Vec ~ InlineSize[map]; sDst0: NAT ~ dstMin.s - map.box.min.s; fDst0: NAT ~ dstMin.f - map.box.min.f; sSrc0: NAT ~ srcMin.s - map.box.min.s; fSrc0: NAT ~ srcMin.f - map.box.min.f; sSize: NAT ~ Check[Check[size.s, selfSize.s-sDst0], selfSize.s-sSrc0]; fSize: NAT ~ Check[Check[size.f, selfSize.f-fDst0], selfSize.f-fSrc0]; IF sSize=0 OR fSize = 0 THEN RETURN; WITH map SELECT FROM map: RasterSampleMap => { bitsPerSample: NAT ~ map.bitsPerSample; bitsPerLine: NAT ~ map.bitsPerLine; dstBitIndex: CARD ¬ CARD[sDst0]*bitsPerLine + CARD[fDst0]*bitsPerSample; srcBitIndex: CARD ¬ CARD[sSrc0]*bitsPerLine + CARD[fSrc0]*bitsPerSample; IF (fSrc0+fSize) > fDst0 AND (fDst0+fSize) > fSrc0 AND (sSrc0+sSize) > sDst0 AND (sDst0+sSize) > sSrc0 AND (sDst0>sSrc0 OR (sDst0=sSrc0 AND fDst0>fSrc0)) THEN { <<-- the rectangles overlap in a bad way for forward moves>> <> <> <> <> <> <> <> <> <> <> <<];>> <<-- Until RasterOp.backwardOp exists, we will make do with another way.>> IF srcMin.s < dstMin.s THEN { <> bandSize: NAT ~ dstMin.s - srcMin.s; FOR sEnd: INTEGER ¬ size.s, sEnd-bandSize UNTIL sEnd <= 0 DO sBegin: INTEGER ¬ MAX[sEnd-bandSize, 0]; BasicTransfer[ dst: map, src: map, dstMin: [dstMin.s+sBegin, dstMin.f], srcMin: [srcMin.s+sBegin, srcMin.f], size: [sEnd-sBegin, size.f], function: function ]; ENDLOOP; } ELSE { <> temp: SampleMap ~ ObtainScratchMap[box: [max: size], bitsPerSample: map.bitsPerSample]; BasicTransfer[dst: temp, src: map, srcMin: srcMin, size: size, function: [null, null]]; BasicTransfer[dst: map, src: temp, dstMin: dstMin, size: size, function: function]; ReleaseScratchMap[temp]; }; } ELSE { RasterOp.forwardOp[function.dstFunc][function.srcFunc][ dst: IndexBit[map.base, dstBitIndex], src: IndexBit[map.base, srcBitIndex], dstBpl: bitsPerLine, srcBpl: bitsPerLine, sSize: sSize, fSize: CARD[fSize]*bitsPerSample ]; }; }; map: ObjectSampleMap => { buffer: SampleBuffer ~ ObtainScratchSamples[fSize]; class: SampleMapClass ~ map.class; IF sDst0 <= sSrc0 THEN { FOR s: NAT IN [0..sSize) DO class.getSamples[self: map, initIndex: [s+srcMin.s, srcMin.f], buffer: buffer, start: 0, count: fSize]; class.putSamples[self: map, initIndex: [s+dstMin.s, dstMin.f], buffer: buffer, start: 0, count: fSize, function: nullFunction]; ENDLOOP; } ELSE { FOR s: NAT DECREASING IN [0..sSize) DO class.getSamples[self: map, initIndex: [s+srcMin.s, srcMin.f], buffer: buffer, start: 0, count: fSize]; class.putSamples[self: map, initIndex: [s+dstMin.s, dstMin.f], buffer: buffer, start: 0, count: fSize, function: nullFunction]; ENDLOOP; }; ReleaseScratchSamples[buffer]; }; ENDCASE => NULL; }; BufferFill: PROC [map: SampleMap, box: Box ¬ maxBox, value: Sample, function: Function ¬ nullFunction] ~ { box ¬ SF.Intersect[map.box, box]; IF SFInline.Nonempty[box] THEN { buffer: SampleBuffer ~ ObtainScratchSamples[box.max.f-box.min.f]; FOR i: NAT IN [0..buffer.length) DO buffer[i] ¬ value ENDLOOP; FOR s: NAT IN [box.min.s..box.max.s) DO PutSamples[map: map, initIndex: [s, box.min.f], buffer: buffer, function: function]; ENDLOOP; ReleaseScratchSamples[buffer]; }; }; replicator: ARRAY [0..5] OF Sample ~ [WORD.LAST, WORD.LAST/3, WORD.LAST/15, WORD.LAST/255, WORD.LAST/65535, 1]; Fill: PUBLIC PROC [map: SampleMap, box: Box, value: Sample, function: Function ¬ nullFunction] ~ { false: BOOL[FALSE..FALSE] ~ map.immutable; bitsPerSample: NAT ~ map.bitsPerSample; WITH map SELECT FROM map: RasterSampleMap => IF IsPowerOfTwo[bitsPerSample] THEN TRUSTED { bitsPerLine: NAT ~ map.bitsPerLine; actualBox: Box ~ SFInline.Intersect[map.box, box]; sSize: CARDINAL ~ SFInline.SizeS[actualBox]; fSize: CARDINAL ~ SFInline.SizeF[actualBox]; dstBitIndex: CARD ~ CARD[NAT[actualBox.min.s-map.box.min.s]]*bitsPerLine + CARD[NAT[actualBox.min.f-map.box.min.f]]*bitsPerSample; bitMask: WORD ~ MaskFromBPS[bitsPerSample]; replicatedPixel: WORD ¬ Basics.BITAND[value, bitMask] * replicator[Lg[bitsPerSample]]; RasterOp.tileOp[function.dstFunc][function.srcFunc][ dst: IndexBit[map.base, dstBitIndex], src: LOOPHOLE[@replicatedPixel], dstBpl: bitsPerLine, src0: 0, sSizeTile: 1, sSize: sSize, fSize: CARD[fSize]*bitsPerSample ]; RETURN; }; ENDCASE; BufferFill[map: map, box: box, value: value, function: function]; }; FillBoxes: PUBLIC PROC [map: SampleMap, boxes: BoxGenerator, value: Sample ¬ maxSample, function: Function ¬ nullFunction] ~ { false: BOOL[FALSE..FALSE] ~ map.immutable; bitsPerSample: NAT ~ map.bitsPerSample; WITH map SELECT FROM map: RasterSampleMap => IF IsPowerOfTwo[bitsPerSample] THEN TRUSTED { lgBitsPerSample: [0..5] ~ Lg[bitsPerSample]; bitMask: WORD ~ Basics.BITLSHIFT[1, bitsPerSample]-1; replicatedPixel: WORD ¬ Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample]; op: UNSAFE PROC [dst: BitAddress, src: RawSamplesPtr, dstBpl, src0, sSizeTile, sSize, fSize: CARDINAL] ~ RasterOp.tileOp[function.dstFunc][function.srcFunc]; FastFill: BoxAction ~ TRUSTED { sMin: INTEGER ~ box.min.s; fMin: INTEGER ~ box.min.f; sMax: INTEGER ~ box.max.s; fMax: INTEGER ~ box.max.f; IF sMax > sMin AND fMax > fMin THEN { m: RasterSampleMap ~ map; bitsPerLine: CARDINAL ~ m.bitsPerLine; sDst: CARDINAL ~ LOOPHOLE[sMin-m.box.min.s]; fDst: CARDINAL ~ LOOPHOLE[fMin-m.box.min.f]; lgBPS: [0..5] ~ lgBitsPerSample; check: NAT ~ Basics.BITOR[Basics.BITOR[sDst, fDst], Basics.BITOR[LOOPHOLE[m.box.max.s-sMax, CARDINAL], LOOPHOLE[m.box.max.f-fMax, CARDINAL]]]; -- for bounds check bitDst: CARD ~ sDst*bitsPerLine + Basics.BITLSHIFT[fDst, lgBPS] + m.base.bit; fBitCount: CARD ~ Basics.BITLSHIFT[LOOPHOLE[fMax-fMin, CARDINAL], lgBPS]; mod: CARDINAL [0..bpw) = bitDst MOD bpw; wordAddress: POINTER ~ m.base.word + (bitDst - mod) / bpu; sSize: CARDINAL ~ LOOPHOLE[sMax-sMin, CARDINAL]; IF function = nullFunction THEN SELECT TRUE FROM (fBitCount=bitsPerLine AND mod=0 AND (fBitCount MOD bpw)=0) => { Basics.FillWords[dst: wordAddress, count: (fBitCount/bpw)*sSize, value: replicatedPixel]; RETURN; }; (sSize=1) => { Basics.FillBits[dstBase: wordAddress, dstStart: mod, count: fBitCount, value: replicatedPixel]; RETURN; }; ENDCASE; op[ dst: [word: wordAddress, bit: mod], src: LOOPHOLE[@replicatedPixel], dstBpl: bitsPerLine, src0: 0, sSizeTile: 1, sSize: sSize, fSize: fBitCount ]; }; }; boxes[FastFill]; RETURN; }; ENDCASE; -- slow case -- { SlowFill: BoxAction ~ { BufferFill[map, box, value, function] }; boxes[SlowFill]; }; }; RegionFill: PUBLIC PROC [dst: RasterSampleMap, edgeGenerator: PROC [EdgeAction], value: Sample, function: Function] ~ TRUSTED { false: BOOL[FALSE..FALSE] ~ dst.immutable; Edge: TYPE ~ RECORD [sMin: INTEGER, sMax: INTEGER, f0: ImagerScaled.Value, df: ImagerScaled.Value]; edges: ARRAY [0..1] OF Edge ¬ ALL[[0, 0, ImagerScaled.zero, ImagerScaled.zero]]; bitsPerSample: NAT ~ dst.bitsPerSample; zero: [0..0] ~ bpw MOD bitsPerSample; -- not implemented for strange bps . . . lgBitsPerSample: [0..5] ~ Lg[bitsPerSample]; -- so we can shift rather than multiply op: UNSAFE PROC [dst: BitAddress, src: RawSamplesPtr, dstBpl, src0, sSizeTile, sSize, fSize: CARDINAL] ~ RasterOp.tileOp[function.dstFunc][function.srcFunc]; edgeAction: SAFE PROC [which: [0..1], sMin: INTEGER, sCount: NAT, f0, df: ImagerScaled.Value] ~ TRUSTED { lgbps: [0..5] ~ lgBitsPerSample; Ix: SAFE PROC [f: INTEGER] RETURNS [CARDINAL] ~ CHECKED INLINE { RETURN [Basics.BITLSHIFT[LOOPHOLE[f, CARDINAL], lgbps]] }; e: ARRAY [0..1] OF Edge ¬ [ IF which = 0 THEN [sMin: sMin, sMax: sMin+sCount, f0: f0, df: df] ELSE edges[0], IF which = 1 THEN [sMin: sMin, sMax: sMin+sCount, f0: f0, df: df] ELSE edges[1] ]; IF (e[0].sMin < e[0].sMax) AND (e[1].sMin < e[1].sMax) AND (e[0].sMin < dst.box.max.s) THEN { s: INTEGER ¬ e[0].sMin; zero: [0..0] ~ s - e[1].sMin; box: Box ~ dst.box; sMax: INTEGER ~ MIN[e[0].sMax, e[1].sMax, box.max.s]; currentBox: SF.Box ¬ []; bitsPerLine: NAT ~ dst.bitsPerLine; line: BitAddress ¬ IndexBit[dst.base, CARD[NAT[s-box.min.s]]*bitsPerLine]; IF e[0].df = ImagerScaled.zero AND e[1].df = ImagerScaled.zero AND s >= box.min.s THEN { <> f0: INTEGER ¬ ImagerScaled.Floor[e[0].f0]; f1: INTEGER ¬ ImagerScaled.Floor[e[1].f0]; IF f0 > f1 THEN {t: INTEGER ¬ f0; f0 ¬ f1; f1 ¬ t}; IF f0 < box.min.f THEN f0 ¬ box.min.f; IF f1 > box.max.f THEN f1 ¬ box.max.f; IF f0 < f1 THEN { offset: CARDINAL ~ line.bit+Ix[f0-box.min.f]; op[ dst: [word: LOOPHOLE[line.word+SIZE[RawSamples[offset/bpw]]], bit: offset MOD bpw], src: LOOPHOLE[@replicatedPixel], dstBpl: bitsPerLine, src0: 0, sSizeTile: 1, sSize: sMax-s, fSize: Ix[f1-f0] ]; }; s ¬ sMax; } ELSE { WHILE s < sMax DO f0: INTEGER ¬ ImagerScaled.Floor[e[0].f0]; f1: INTEGER ¬ ImagerScaled.Floor[e[1].f0]; IF f0 > f1 THEN {t: INTEGER ¬ f0; f0 ¬ f1; f1 ¬ t}; IF f0 < box.min.f THEN f0 ¬ box.min.f; IF f1 > box.max.f THEN f1 ¬ box.max.f; IF s >= box.min.s AND f0 < f1 THEN { offset: CARDINAL ~ line.bit+Ix[f0-box.min.f]; op[ dst: [word: line.word+SIZE[RawSamples[offset/bpw]], bit: offset MOD bpw], src: LOOPHOLE[@replicatedPixel], dstBpl: bitsPerLine, src0: 0, sSizeTile: 1, sSize: 1, fSize: Ix[f1-f0] ]; }; e[0].f0 ¬ ImagerScaled.PLUS[e[0].f0, e[0].df]; e[1].f0 ¬ ImagerScaled.PLUS[e[1].f0, e[1].df]; {offset: CARDINAL ~ line.bit+bitsPerLine; line.word ¬ line.word + SIZE[RawSamples[offset/bpw]]; line.bit ¬ offset MOD bpw; }; s ¬ s + 1; ENDLOOP; }; e[0].sMin ¬ e[1].sMin ¬ s; }; edges ¬ e; }; bitMask: WORD ~ MaskFromBPS[bitsPerSample]; replicatedPixel: WORD ¬ Basics.BITAND[value, bitMask] * replicator[lgBitsPerSample]; edgeGenerator[edgeAction]; }; <> tileFromStippleRepl: NAT ¬ 32; <> TileFromStipple: PUBLIC PROC [stipple: WORD, bitsPerSample: BitsPerSample, value0: Sample, value1: Sample, scratch: SampleMap] RETURNS [SampleMap] ~ { <> easy: BOOL ~ (bpw MOD (bitsPerSample*4) = 0); size: Vec ~ IF easy THEN [s: 4, f: bpw/bitsPerSample] ELSE [s: 8, f: tileFromStippleRepl*4]; temp: SampleMap ~ ObtainScratchMap[[[0, 0], [4, 4]], bitsPerSample]; tile: SampleMap ¬ scratch; IF tile = NIL OR NOT (tile.bitsPerSample = bitsPerSample AND tile.box = [zeroVec, size]) THEN { tile ¬ NewSampleMap[box: [zeroVec, size], bitsPerSample: bitsPerSample] }; 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; GeneralTile[map: tile, tile: temp]; ReleaseScratchMap[temp]; RETURN[tile]; }; QR: TYPE ~ RECORD [quotient: INTEGER, remainder: NAT]; DivMod: PROC [n: INT, d: NAT] RETURNS [qr: QR] ~ { <> qr.remainder ¬ Mod[n, d]; qr.quotient ¬ (n-qr.remainder) / d; }; Mod: PROC [n: INT, d: NAT] RETURNS [NAT] ~ { <> IF d#1 THEN { IF n >= 0 THEN RETURN [CARD[n] MOD d] ELSE { remainder: INT ¬ n MOD d; WHILE remainder < 0 DO remainder ¬ remainder + d ENDLOOP; RETURN [remainder] }; } ELSE RETURN [0]; }; QMul: PROC [a: INTEGER, b: CARDINAL] RETURNS [INT] ~ INLINE { RETURN [INT[a]*b] }; GeneralTile: PROC [map: SampleMap, box: Box ¬ maxBox, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ { boxes: BoxGenerator ~ { boxAction[SF.Intersect[map.box, box]] }; GeneralTileBoxes[map, boxes, tile, phase, function]; }; GetTileSamples: PUBLIC PROC [tile: SampleMap, phase: NAT, initIndex: SF.Vec, buffer: SampleBuffer] ~ { s0: INT ~ tile.box.min.s; f0: INT ~ tile.box.min.f; sOffset: INT ~ initIndex.s-s0; tileSize: Vec ~ SFInline.Size[tile.box]; qr: QR ~ DivMod[sOffset, tileSize.s]; fOffset: INT ~ initIndex.f-(f0+QMul[qr.quotient, phase]); srcIndexRel: Vec ~ [s: qr.remainder, f: Mod[fOffset, tileSize.f]]; srcIndex: Vec ~ SFInline.Add[srcIndexRel, tile.box.min]; count1: NAT ~ MIN[buffer.length, tileSize.f-srcIndexRel.f]; count2: NAT ~ MIN[buffer.length-count1, srcIndexRel.f]; count3: NAT ~ buffer.length-count1-count2; GetSamples[map: tile, initIndex: srcIndex, delta: [0, 1], buffer: buffer, start: 0, count: count1]; IF count2 # 0 THEN GetSamples[map: tile, initIndex: [srcIndex.s, tile.box.min.f], delta: [0, 1], buffer: buffer, start: count1, count: count2]; IF count3 # 0 THEN CopySamples[dst: buffer, src: buffer, dstStart: count1+count2, srcStart: 0, count: count3]; }; GeneralTileBoxes: PROC [map: SampleMap, boxes: BoxGenerator, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ { GeneralTile: BoxAction ~ { s0: INT ~ tile.box.min.s; f0: INT ~ tile.box.min.f; actualBox: Box ~ SFInline.Intersect[map.box, box]; sOffset: INT ~ actualBox.min.s-s0; tileSize: Vec ~ SFInline.Size[tile.box]; qr: QR ~ DivMod[sOffset, tileSize.s]; fOffset: INT ~ actualBox.min.f-(f0+QMul[qr.quotient, phase]); dstRow: Vec ¬ actualBox.min; srcRow: Vec ¬ [s: qr.remainder, f: Mod[fOffset, tileSize.f]]; WHILE dstRow.s < actualBox.max.s DO sSize: NAT ~ MIN[tileSize.s-srcRow.s, actualBox.max.s-dstRow.s]; dstCol: Vec ¬ dstRow; srcCol: Vec ¬ srcRow; WHILE dstCol.f < actualBox.max.f DO fSize: NAT ~ MIN[tileSize.f-srcCol.f, actualBox.max.f-dstCol.f]; BasicTransfer[dst: map, src: tile, dstMin: dstCol, srcMin: SFInline.Add[tile.box.min, 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=tileSizeF THEN phase ¬ phase MOD tileSizeF; boxes[GeneralTile]; }; Tile: PUBLIC PROC [map: SampleMap, box: Box ¬ maxBox, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ { boxes: BoxGenerator ~ { boxAction[SF.Intersect[box, map.box]] }; TileBoxes[map, boxes, tile, phase, function]; }; smallWidth: NAT ¬ 256; <> TileBoxes: PUBLIC PROC [map: SampleMap, boxes: BoxGenerator, tile: SampleMap, phase: NAT ¬ 0, function: Function ¬ nullFunction] ~ { false: BOOL[FALSE..FALSE] ~ map.immutable; bitsPerSample: NAT ~ map.bitsPerSample; sSizeTile: NAT ~ tile.box.max.s-tile.box.min.s; fSizeTile: NAT ~ tile.box.max.f-tile.box.min.f; fSizeSelf: NAT ~ map.box.max.f-map.box.min.f; WITH map SELECT FROM map: RasterSampleMap => WITH tile SELECT FROM tile: RasterSampleMap => { <> <> <> < box.min.s AND box.max.f > box.min.f THEN {>> <> <> <> <> <> <> <<[[word: arg.dstWord, bit: arg.dstBit]] ¬ IndexBit[map.base, sDst*map.bitsPerLine+fDst];>> <> <> <> <> <> <<};>> <<};>> <> <> <> <> <> <> <> <> <> <> <> <<};>> IF phase=0 AND tile.bitsPerSample=bitsPerSample AND tile.bitsPerLine=bpw AND tile.base.bit=0 AND map.base.bit = 0 AND fSizeTile*bitsPerSample=bpw AND IsPowerOfTwo[sSizeTile] AND IsPowerOfTwo[bitsPerSample] THEN TRUSTED { sTileMask: CARDINAL ~ sSizeTile-1; -- sSizeTile is a power of 2 sOffset: CARDINAL ~ Basics.BITAND[LOOPHOLE[-tile.box.min.s, CARDINAL], sTileMask]; lgBitsPerSample: [0..5] ~ Lg[bitsPerSample]; op: UNSAFE PROC [dst: BitAddress, src: RawSamplesPtr, dstBpl, src0, sSizeTile, sSize, fSize: CARDINAL] ~ RasterOp.tileOp[function.dstFunc][function.srcFunc]; FastTile: BoxAction ~ TRUSTED { sMin: INTEGER ~ box.min.s; fMin: INTEGER ~ box.min.f; sMax: INTEGER ~ box.max.s; fMax: INTEGER ~ box.max.f; IF sMax > sMin AND fMax > fMin THEN { m: RasterSampleMap ~ map; bitsPerLine: CARDINAL ~ m.bitsPerLine; sDst: CARDINAL ~ LOOPHOLE[sMin-m.box.min.s]; fDst: CARDINAL ~ LOOPHOLE[fMin-m.box.min.f]; lgBPS: [0..5] ~ lgBitsPerSample; check: NAT ~ Basics.BITOR[Basics.BITOR[sDst, fDst], Basics.BITOR[LOOPHOLE[m.box.max.s-sMax], LOOPHOLE[m.box.max.f-fMax]]]; -- for bounds check bitDst: CARD ~ sDst*bitsPerLine + Basics.BITLSHIFT[fDst, lgBPS] + m.base.bit; fBitCount: CARD ~ Basics.BITLSHIFT[LOOPHOLE[fMax-fMin], lgBPS]; mod: CARDINAL [0..bpw) = bitDst MOD bpw; yOffset: CARDINAL ~ Basics.BITAND[sOffset+LOOPHOLE[sMin, CARDINAL], sTileMask]; op[ dst: [word: m.base.word + (bitDst - mod) / bpu, bit: mod], src: LOOPHOLE[tile.base.word], dstBpl: bitsPerLine, src0: yOffset, sSizeTile: sSizeTile, sSize: LOOPHOLE[sMax-sMin], fSize: LOOPHOLE[fBitCount] ]; }; }; boxes[FastTile]; RETURN; }; IF tile.bitsPerSample = map.bitsPerSample AND ( (fSizeTile*bitsPerSample >= MIN[fSizeSelf*bitsPerSample, smallWidth]) -- not worthwhile to build macro tile ) THEN { GeneralTileBoxes[map: map, boxes: boxes, tile: tile, phase: phase, function: function]; RETURN; }; } ENDCASE ENDCASE; -- General case -- { <> ns: CARDINAL ~ 1; nf: CARDINAL ~ ((smallWidth+fSizeTile-1)/fSizeTile+bitsPerSample-1)/bitsPerSample; macroTileSize: Vec ~ [s: ns*sSizeTile, f: nf*fSizeTile]; macroTile: RasterSampleMap ~ ObtainScratchMap[[tile.box.min, SFInline.Add[tile.box.min, macroTileSize]], bitsPerSample]; GeneralTile[map: macroTile, tile: tile, phase: phase]; GeneralTileBoxes[map: map, boxes: boxes, tile: macroTile, phase: ns*phase, function: function]; ReleaseScratchMap[macroTile]; }; }; BoxesFromBitmap: PUBLIC PROC [map: SampleMap, boxAction: BoxAction] ~ { WITH map SELECT FROM map: RasterSampleMap => { IF map.bitsPerSample = 1 THEN TRUSTED { sMin: INTEGER ~ map.box.min.s; fMin: INTEGER ~ map.box.min.f; fMax: INTEGER ~ map.box.max.f; lineStart: BitAddress ¬ map.base; IF map.box.min.f>=map.box.max.f THEN RETURN; FOR sRel: NAT IN [0..map.box.max.s-map.box.min.s) DO f: INTEGER ¬ map.box.min.f; wordPtr: LONG POINTER TO CARDINAL ¬ lineStart.word; wordsWorth: CARDINAL ¬ Basics.BITLSHIFT[wordPtr­, lineStart.bit]; remainingBitsInWord: INTEGER ¬ bpw-lineStart.bit; ScanFor: UNSAFE PROC [bit: [0..1]] ~ UNCHECKED INLINE { <=fMax or (high bit of wordsWorth) = bit.>> skip: CARDINAL ~ IF bit = 0 THEN LAST[CARDINAL] ELSE 0; hiBitMask: CARDINAL ~ LAST[CARDINAL]/2+1; bitAtHigh: CARDINAL ~ IF bit = 0 THEN 0 ELSE hiBitMask; WHILE f < fMax -- or EXIT below -- DO IF remainingBitsInWord = bpw AND wordsWorth = skip THEN { f ¬ f + remainingBitsInWord } ELSE { f ¬ f + remainingBitsInWord; WHILE remainingBitsInWord > 0 AND Basics.BITAND[wordsWorth, hiBitMask] # bitAtHigh DO remainingBitsInWord ¬ remainingBitsInWord-1; wordsWorth ¬ wordsWorth*2; ENDLOOP; f ¬ f - remainingBitsInWord; IF remainingBitsInWord > 0 THEN EXIT; remainingBitsInWord ¬ bpw; }; wordPtr ¬ wordPtr + SIZE[WORD]; IF f < fMax THEN wordsWorth ¬ wordPtr­; ENDLOOP; }; WHILE f < fMax DO fStart: INTEGER; ScanFor[1]; fStart ¬ f; ScanFor[0]; f ¬ MIN[f, fMax]; IF f > fStart THEN boxAction[[ min: [s: sMin+sRel, f: fStart], max: [s: sMin+sRel+1, f: f] ]]; ENDLOOP; lineStart ¬ IndexBit[lineStart, map.bitsPerLine]; ENDLOOP; RETURN; }; }; ENDCASE => NULL; -- hard case -- { FOR s: NAT IN [map.box.min.s..map.box.max.s) DO buffer: RasterSampleMap ~ ObtainScratchMap[box: [min: [s, map.box.min.f], max: [s+1, map.box.max.f]], bitsPerSample: 1]; Transfer[dst: buffer, src: map]; BoxesFromBitmap[buffer, boxAction]; ReleaseScratchMap[buffer]; ENDLOOP; }; ERROR; -- Only need 1 bpp case for the Imager }; Halftone: PUBLIC PROC [map: SampleMap, min: Vec, sampleBuffer, thresholdBuffer: SampleBuffer, function: Function] ~ TRUSTED { <> false: BOOL[FALSE..FALSE] ~ map.immutable; unitsPerBuffer: NAT ~ 8*16; bitsPerBuffer: NAT ~ unitsPerBuffer*16; BitBuffer: TYPE ~ PACKED ARRAY [0..unitsPerBuffer) OF [0..65535]; bitBuffer: BitBuffer; bitBufferPtr: LONG POINTER ~ @bitBuffer; bitBufferMap: SampleMap ~ ObtainUnsafeDescriptor[size: [s: 1, f: bitsPerBuffer], bitsPerSample: 1, bitsPerLine: bitsPerBuffer, base: [word: LOOPHOLE[bitBufferPtr], bit: 0], ref: NIL, words: WORDS[BitBuffer]]; count: NAT ~ MIN[sampleBuffer.length, thresholdBuffer.length]; index: SF.Vec ¬ min; residual: NAT ¬ count; s: LONG POINTER TO ARRAY [0..16) OF WORD ¬ LOOPHOLE[InlinePointerToSamples[buffer: sampleBuffer, start: 0, count: count]]; b: LONG POINTER TO ARRAY [0..16) OF WORD ¬ LOOPHOLE[InlinePointerToSamples[buffer: thresholdBuffer, start: 0, count: count]]; Bit: UNSAFE PROC [j: NAT] RETURNS [WORD] ~ UNCHECKED INLINE {RETURN [Basics.BITRSHIFT[s[j]-b[j]-1, bpw-1]]}; WHILE residual # 0 DO chunkSize: NAT ~ MIN[bitsPerBuffer, residual]; bitBufferIndex: NAT ¬ 0; chunkResidual: NAT ¬ chunkSize; WHILE chunkResidual >= 16 DO bitBuffer[bitBufferIndex] ¬ ((((((((((((((Bit[0]*2+Bit[1])*2+Bit[2])*2+Bit[3])*2+Bit[4])*2+Bit[5])*2+Bit[6])*2+Bit[7])*2+Bit[8])*2+Bit[9])*2+Bit[10])*2+Bit[11])*2+Bit[12])*2+Bit[13])*2+Bit[14])*2+Bit[15]; bitBufferIndex ¬ bitBufferIndex + 1; chunkResidual ¬ chunkResidual - 16; s ¬ s + SIZE[ARRAY [0..16) OF CARDINAL]; b ¬ b + SIZE[ARRAY [0..16) OF CARDINAL]; ENDLOOP; IF chunkResidual # 0 THEN { w: CARDINAL ¬ 0; IF chunkSize # residual THEN ERROR; -- must be last time through FOR i: NAT IN [0..chunkResidual) DO w ¬ w*2 + Bit[i] ENDLOOP; bitBuffer[bitBufferIndex] ¬ Basics.BITLSHIFT[w, Lo5[16-chunkResidual]]; }; BasicTransfer[dst: map, src: bitBufferMap, dstMin: index, size: [s: 1, f: chunkSize], function: function]; index.f ¬ index.f + chunkSize; residual ¬ residual - chunkSize; ENDLOOP; ReleaseDescriptor[bitBufferMap]; }; Zeros: PROC [pointer: LONG POINTER, count: NAT] RETURNS [BOOL] ~ TRUSTED { <<-- Checks for (pointer+i)^ = 0 for i IN [0..count) -->> p: LONG POINTER TO ARRAY [0..8) OF WORD ¬ LOOPHOLE[pointer]; WHILE count >= 8 DO IF Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[Basics.BITOR[p[0], p[1]], p[2]], p[3]], p[4]], p[5]], p[6]], p[7]] # 0 THEN RETURN [FALSE]; p ¬ p + SIZE[ARRAY [0..8) OF WORD]; count ¬ count - 8; ENDLOOP; WHILE count > 0 DO IF p[0] # 0 THEN RETURN [FALSE]; p ¬ p + SIZE[WORD]; count ¬ count - 1; ENDLOOP; RETURN [TRUE]; }; Equal: PUBLIC PROC [s1, s2: SampleMap] RETURNS [equal: BOOL ¬ TRUE] ~ { BandAction: PROC [band: RasterSampleMap] ~ { boxSize: NAT ~ SFInline.SizeS[s1.box]; bandSize: NAT ~ SFInline.SizeS[band.box]; bandWords: NAT ~ WordsForLines[bandSize, band.bitsPerLine]; Clear[band]; FOR s: INTEGER ¬ 0, s+bandSize WHILE s < boxSize DO Transfer[dst: band, src: s1, delta: [s: -s, f: 0]]; Transfer[dst: band, src: s2, delta: [s: -s, f: 0], function: [xor, null]]; IF NOT Zeros[LOOPHOLE[band.base.word], bandWords] THEN { equal ¬ FALSE; EXIT }; ENDLOOP; }; IF SFInline.Empty[s1.box] AND SFInline.Empty[s2.box] THEN NULL ELSE IF s1.box # s2.box THEN equal ¬ FALSE ELSE { bandBitsPerSample: NAT ~ MAX[s1.bitsPerSample, s2.bitsPerSample]; bandLines: NAT ~ MIN[MAX[4096/SFInline.SizeF[s1.box], 1], SFInline.SizeS[s1.box]]; bandBox: Box ¬ s1.box; bandBox.max.s ¬ bandBox.min.s + bandLines; DoWithScratchMap[bandBox, bandBitsPerSample, BandAction]; }; }; IsAll: PUBLIC PROC [map: SampleMap, box: Box ¬ maxBox, value: Sample ¬ 0] RETURNS [equal: BOOL ¬ TRUE] ~ { clipped: SampleMap ~ Clip[map, box]; IF SFInline.Nonempty[clipped.box] THEN { BandAction: PROC [band: RasterSampleMap] ~ { boxSize: NAT ~ SFInline.SizeS[clipped.box]; bandSize: NAT ~ SFInline.SizeS[band.box]; bandWords: NAT ~ WordsForLines[bandSize, band.bitsPerLine]; Clear[band]; FOR s: INTEGER ¬ 0, s+bandSize WHILE s < boxSize DO Transfer[dst: band, src: clipped, delta: [s: -s, f: 0]]; IF value # 0 THEN Fill[map: band, box: maxBox, value: value, function: [xor, null]]; IF NOT Zeros[LOOPHOLE[band.base.word], bandWords] THEN { equal ¬ FALSE; EXIT }; ENDLOOP; }; bandLines: NAT ~ MIN[MAX[4096/SFInline.SizeF[clipped.box], 1], SFInline.SizeS[clipped.box]]; bandBox: Box ¬ clipped.box; bandBox.max.s ¬ bandBox.min.s + bandLines; DoWithScratchMap[bandBox, clipped.bitsPerSample, BandAction]; }; ReleaseDescriptor[clipped]; }; Trim: PUBLIC PROC [map: SampleMap, box: Box, background: Sample] RETURNS [Box] ~ { actualBox: Box ~ CleanBox[SF.Intersect[map.box, box]]; min: Vec ¬ actualBox.min; max: Vec ¬ actualBox.max; BandAction: PROC [band: RasterSampleMap] ~ { bandWords: NAT ~ WordsForLines[band.box.max.s, band.bitsPerLine]; Clear[band]; WHILE min.s < max.s -- OR EXIT below -- DO BasicTransfer[dst: band, src: map, srcMin: [s: max.s-1, f: min.f], size: band.box.max]; IF background#0 THEN Fill[map: band, box: band.box, value: background, function: [xor, null]]; IF Zeros[LOOPHOLE[band.base.word], bandWords] THEN max.s ¬ max.s-1 ELSE EXIT; ENDLOOP; WHILE min.s < max.s -- OR EXIT below -- DO BasicTransfer[dst: band, src: map, srcMin: [s: min.s, f: min.f], size: band.box.max]; IF background#0 THEN Fill[map: band, box: band.box, value: background, function: [xor, null]]; IF Zeros[LOOPHOLE[band.base.word], bandWords] THEN min.s ¬ min.s+1 ELSE EXIT; ENDLOOP; }; DoWithScratchMap[box: [zeroVec, [s: 1, f: SFInline.SizeF[actualBox]]], bitsPerSample: map.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[map, 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[map, margin, background] THEN min.f ¬ min.f+delta ELSE EXIT; ENDLOOP; ENDLOOP; }; RETURN [CleanBox[[min: min, max: max]]]; }; <> <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<>> <> <<>> <> <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <<};>> <<>> <> <> <> <<};>> END.