DIRECTORY Basics USING [BITAND, BITSHIFT, bitsPerWord, BoundsCheck, BYTE, LongDivMod, LongMult, LongNumber, RawBytes, RawWords], FunctionCache USING [Cache, CompareProc, GlobalCache, Insert, Lookup], Imager USING [ClassRep, Context], ImagerCache USING [GetNamedCache, Ref], ImagerColor USING [RGB], ImagerColorDefs USING [Color, ColorOperator, ColorRep, ConstantColor, ConstantColorClassRep, ConstantColorImplRep, SampledColor], ImagerColorMap USING [MapEntry, StandardColorMapEntries], ImagerColorOperator USING [GetColorOperatorClass, Mapper, MapPixels, NewMapper], ImagerColorPrivate USING [ConstantColorClass, ConstantColorClassRep, ConstantColorImpl, ConstantColorImplRep], ImagerDevice USING [BoxProc, Class, ClassRep, Device, DeviceBox, DeviceRep, HalftoneParameters, RunProc], ImagerDither USING [CreateTable, Dither, DitherConstant, InitTable, Pack, PackedColorDesc, PackSequence, Table, WordDither, WordDitherConstant], ImagerDitheredDevice USING [MapEntries, SpecialPixel], ImagerDitheredDevicePrivate USING [Data, DataRep, SampledColorData, SampledColorDataRep, StippleArray], ImagerMask USING [RunsFromBits, RunsFromBox], ImagerPixelArray USING [GetPixels, MaxSampleValue], ImagerPixelArrayDefs USING [PixelArray], ImagerPixelMap USING [BoundedWindow, Clip, Create, DeviceRectangle, PixelMap, PixelMapRep, ShiftMap], ImagerRaster USING [Create], ImagerSample USING [GetPointer, GetPointSamples, NewBuffer, Sample, SampleBuffer, Sampler, SamplerRep, SetSamplerIncrements, SetSamplerPosition, UnsafePutF, UnsafeSamples], ImagerTransformation USING [ApplyPreRotate, Cat, Transformation, Translate], PrincOps USING [BBTableSpace, BitBltTable, BitBltTablePtr, DstFunc, op, zBNDCK, zINC], PrincOpsUtils USING [AlignedBBTable, BITBLT, LongCopy], Real USING [RoundC], Terminal USING [FrameBuffer, GetColorFrameBufferA, GetColorFrameBufferB, ModifyColorFrame, Virtual], Vector2 USING [VEC]; ImagerDitheredDeviceImpl: CEDAR PROGRAM IMPORTS Basics, FunctionCache, ImagerCache, ImagerColorMap, ImagerColorOperator, ImagerDither, ImagerMask, ImagerPixelArray, ImagerPixelMap, ImagerRaster, ImagerSample, ImagerTransformation, PrincOpsUtils, Real, Terminal EXPORTS ImagerColorDefs, ImagerRaster, ImagerDitheredDevice ~ BEGIN OPEN ImagerDevice, ImagerDitheredDevicePrivate; BYTE: TYPE ~ Basics.BYTE; VEC: TYPE ~ Vector2.VEC; Transformation: TYPE ~ ImagerTransformation.Transformation; Color: TYPE ~ ImagerColorDefs.Color; ConstantColor: TYPE ~ ImagerColorDefs.ConstantColor; SampledColor: TYPE ~ ImagerColorDefs.SampledColor; ColorOperator: TYPE ~ ImagerColorDefs.ColorOperator; PixelArray: TYPE ~ ImagerPixelArrayDefs.PixelArray; PixelMap: TYPE ~ ImagerPixelMap.PixelMap; Sample: TYPE ~ ImagerSample.Sample; SampleBuffer: TYPE ~ ImagerSample.SampleBuffer; UnsafeSamples: TYPE ~ ImagerSample.UnsafeSamples; RawBytes: TYPE ~ Basics.RawBytes; RawWords: TYPE ~ Basics.RawWords; ConstantColorImpl: TYPE ~ ImagerColorPrivate.ConstantColorImpl; ConstantColorImplRep: PUBLIC TYPE ~ ImagerColorPrivate.ConstantColorImplRep; ConstantColorClass: TYPE ~ ImagerColorPrivate.ConstantColorClass; ConstantColorClassRep: PUBLIC TYPE ~ ImagerColorPrivate.ConstantColorClassRep; bitsPerWord: NAT ~ Basics.bitsPerWord; class: ImagerDevice.Class ~ NEW[ImagerDevice.ClassRep _ [ type: $DitheredColorDisplay, SetColor: DitheredSetColor, SetPriority: DitheredSetPriority, SetHalftone: DitheredSetHalftone, MaskRuns: DitheredMaskRuns, MaskBoxes: DitheredMaskBoxes, MaskBits: DitheredMaskBits, MoveBoxes: DitheredMoveBoxes ]]; Lg: PROC [n: NAT] RETURNS [NAT] ~ { RETURN[SELECT n FROM 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4, ENDCASE => ERROR] }; PixelMapFromFrameBuffer: PROC [frameBuffer: Terminal.FrameBuffer] RETURNS [ImagerPixelMap.PixelMap] ~ { refRep: REF ImagerPixelMap.PixelMapRep ~ NEW[ImagerPixelMap.PixelMapRep _ [ ref: frameBuffer, pointer: frameBuffer.base, words: INT[frameBuffer.wordsPerLine]*INT[frameBuffer.height], lgBitsPerPixel: Lg[frameBuffer.bitsPerPixel], rast: frameBuffer.wordsPerLine, lines: frameBuffer.height ]]; frame: ImagerPixelMap.PixelMap ~ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: frameBuffer.height, fSize: frameBuffer.width, refRep: refRep ]; RETURN[frame]; }; fontCacheID: ATOM ~ $ColorDisplay; fontCacheSize: NAT _ 4000; fontRastWeight: REAL _ 0.0; ContextFromPixelMap: PUBLIC PROC [frame: ImagerPixelMap.PixelMap, displayHeight: NAT, pixelUnits: BOOL _ FALSE] RETURNS [Imager.Context] ~ { fontCache: ImagerCache.Ref ~ ImagerCache.GetNamedCache[ atom: fontCacheID, createSize: fontCacheSize]; RETURN [ImagerRaster.Create[device: DeviceFromPixelMap[frame, displayHeight], pixelUnits: pixelUnits, fontCache: fontCache, rastWeight: fontRastWeight]]; }; ContextFromColorTerminal: PUBLIC PROC [vt: Terminal.Virtual, pixelUnits: BOOL _ FALSE] RETURNS [Imager.Context] ~ { fontCache: ImagerCache.Ref ~ ImagerCache.GetNamedCache[ atom: fontCacheID, createSize: fontCacheSize]; RETURN [ImagerRaster.Create[device: DeviceFromColorTerminal[vt], pixelUnits: pixelUnits, fontCache: fontCache, rastWeight: fontRastWeight]]; }; SetMapEntries: PROC [data: Data, mapEntries: ImagerDitheredDevice.MapEntries] ~ { data.mapEntries _ mapEntries; }; DeviceFromPixelMap: PUBLIC PROC [frame: ImagerPixelMap.PixelMap, displayHeight: NAT, pixelsPerInch: REAL _ 72] RETURNS [ImagerDevice.Device] ~ { bitsPerPixel: NAT ~ Basics.BITSHIFT[1, frame.refRep.lgBitsPerPixel]; mapEntries: ImagerDitheredDevice.MapEntries ~ ImagerColorMap.StandardColorMapEntries[bitsPerPixel]; w: ImagerPixelMap.DeviceRectangle ~ ImagerPixelMap.BoundedWindow[frame]; pm: ImagerPixelMap.PixelMap ~ frame.Clip[w].ShiftMap[-w.sMin, -w.fMin]; sampBuffer: SampleBuffer ~ ImagerSample.NewBuffer[1, pm.fMin+pm.fSize]; lineBuffer: SampleBuffer ~ ImagerSample.NewBuffer[1, pm.fMin+pm.fSize]; sampler: ImagerSample.Sampler ~ NEW[ImagerSample.SamplerRep _ []]; data: Data ~ NEW[DataRep _ [frame: pm, sampBuffer: sampBuffer, lineBuffer: lineBuffer, sampler: sampler]]; surfaceToDevice: Transformation ~ ImagerTransformation.Translate[[displayHeight+w.sMin, w.fMin]]; surfaceToDevice.ApplyPreRotate[90]; SetMapEntries[data, mapEntries]; RETURN[NEW[ImagerDevice.DeviceRep _ [class: class, box: [smin: data.frame.sMin, fmin: data.frame.fMin, smax: w.sSize, fmax: w.fSize], surfaceToDevice: surfaceToDevice, surfaceUnitsPerInch: [pixelsPerInch, pixelsPerInch], surfaceUnitsPerPixel: 1, data: data]]]; }; DeviceFromColorTerminal: PUBLIC PROC [vt: Terminal.Virtual, aChannel: BOOL _ TRUE] RETURNS [ImagerDevice.Device] ~ { frameBuffer: Terminal.FrameBuffer ~ IF aChannel THEN Terminal.GetColorFrameBufferA[vt] ELSE Terminal.GetColorFrameBufferB[vt]; pm: PixelMap ~ PixelMapFromFrameBuffer[frameBuffer]; device: ImagerDevice.Device ~ DeviceFromPixelMap[pm, frameBuffer.height, vt.colorPixelsPerInch]; data: Data ~ NARROW[device.data]; data.terminal _ vt; RETURN [device]; }; NewColorMapDevice: PUBLIC PROC [terminal: Terminal.Virtual, bpp: NAT _ 8] RETURNS[Device] ~ { RETURN [DeviceFromColorTerminal[terminal]]; }; nullBitBltTable: PrincOps.BitBltTable ~ [ dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0, flags: [] ]; StippleArrayFromWord: PROC [t: WORD] RETURNS[array: StippleArray _ ALL[0]] ~ { bits: PACKED ARRAY [0..16) OF BOOL ~ LOOPHOLE[t]; FOR i: [0..16) IN [0..16) DO IF bits[i] THEN array[i] _ WORD.LAST ENDLOOP; }; PixelFromIntensity: PROC[i: REAL] RETURNS[BYTE] ~ { IF i<=0 THEN RETURN[0]; IF i>=1 THEN RETURN[BYTE.LAST]; RETURN[Real.RoundC[i*BYTE.LAST]]; }; replicator: ARRAY [0..4] OF WORD ~ [0FFFFH, 5555H, 1111H, 0101H, 1]; ReplicatePixel: PROC [value: WORD, lgBitsPerPixel: [0..4]] RETURNS [WORD] ~ { max: WORD ~ Basics.BITSHIFT[1, Basics.BITSHIFT[1, lgBitsPerPixel]]-1; value _ Basics.BITAND[value, max]; RETURN [value*replicator[lgBitsPerPixel]]; }; DitheredSetColor: PROC [device: Device, color: Color, viewToDevice: Transformation] ~ { data: Data ~ NARROW[device.data]; data.case _ nil; data.sampledColor _ NIL; data.sampledColorData _ NIL; WITH color SELECT FROM color: ConstantColor => { impl: ConstantColorImpl ~ color.impl; data.flags _ [disjoint: TRUE, gray: TRUE, srcFunc: null, dstFunc: null]; WITH color.data SELECT FROM special: REF SpecialPixel => { data.grayWord _ ReplicatePixel[special.value, data.frame.refRep.lgBitsPerPixel]; data.case _ constant; data.flags.dstFunc _ special.dstFunc; }; ENDCASE => { WITH impl: impl SELECT FROM stipple => { word: WORD ~ impl.word; SELECT impl.function FROM replace => { data.flags.srcFunc _ complement }; paint => { data.flags.srcFunc _ complement; data.flags.dstFunc _ and }; invert => { data.flags.dstFunc _ xor }; erase => { data.flags.dstFunc _ or }; ENDCASE => ERROR; IF word=WORD.LAST THEN {data.grayWord _ word; data.case _ constant} ELSE { data.stipple _ StippleArrayFromWord[word]; data.case _ stipple }; }; rgb => { pixelR: BYTE ~ PixelFromIntensity[impl.val.R]; pixelG: BYTE ~ PixelFromIntensity[impl.val.G]; pixelB: BYTE ~ PixelFromIntensity[impl.val.B]; data.packedRGB _ ImagerDither.Pack[pixelR, pixelG, pixelB, packing]; data.case _ rgb; }; ENDCASE => { value: BYTE ~ PixelFromIntensity[impl.Y]; data.packedRGB _ ImagerDither.Pack[value, value, value, packing]; data.case _ rgb; }; }; }; color: SampledColor => { pa: PixelArray ~ color.pa; um: Transformation ~ color.um; colorOperator: ColorOperator ~ color.colorOperator; data.sampledColor _ color; data.case _ sampled; data.zerosAreClear _ ImagerColorOperator.GetColorOperatorClass[colorOperator] = $SampledBlackClear; data.paToDevice _ ImagerTransformation.Cat[pa.m, color.um, viewToDevice]; SetUpSampledColorData[data]; }; ENDCASE => ERROR; -- unknown color variant IF data.case = rgb OR data.case = sampled THEN { SetUpDitherTable[data]; }; IF data.case # constant THEN { IF viewToDevice.integerTrans THEN { data.sTileOrg _ sTileSize-Mod[viewToDevice.tx, sTileSize]; data.fTileOrg _ fTileSize-Mod[viewToDevice.ty, fTileSize]; } ELSE {data.sTileOrg _ data.fTileOrg _ 0}; }; }; Mod: PROC [n: INTEGER, d: NAT] RETURNS [NAT] ~ { nn: Basics.LongNumber _ [li[n]]; IF nn.li < 0 THEN nn.highbits _ nn.highbits + d; RETURN [Basics.LongDivMod[nn.lc, d].remainder]; }; DitheredSetPriority: PROC [device: Device, priorityImportant: BOOL] ~ { }; DitheredSetHalftone: PROC [device: Device, halftone: HalftoneParameters] ~ { data: Data ~ NARROW[device.data]; }; Check: PROC[x: CARDINAL, max: NAT] RETURNS[NAT] ~ TRUSTED MACHINE CODE { PrincOps.zINC; PrincOps.zBNDCK }; DitheredMaskRunsInternal: PROC[data: Data, bounds: DeviceBox, runs: PROC[RunProc]] ~ TRUSTED { frame: PixelMap ~ data.frame; base: LONG POINTER ~ frame.refRep.pointer; wordsPerLine: NAT ~ frame.refRep.rast; bbspace: PrincOps.BBTableSpace; bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbspace]; lgBitsPerPixel: NAT ~ frame.refRep.lgBitsPerPixel; bitsPerPixel: NAT ~ Basics.BITSHIFT[1, lgBitsPerPixel]; heightLimit: NAT ~ frame.sMin+frame.sSize; widthLimit: NAT ~ frame.fMin+frame.fSize; SELECT data.case FROM nil => ERROR; -- color not initialized constant => { ditheredConstantRun: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { fmin: NAT ~ Check[fMin, widthLimit]; fmax: NAT ~ Check[fmin+fSize, widthLimit]; smin: NAT ~ Basics.BoundsCheck[sMin, heightLimit]; bit: CARDINAL ~ Basics.BITSHIFT[fmin, lgBitsPerPixel]; bb.dst.word _ base+Basics.LongMult[smin, wordsPerLine]+bit/bitsPerWord; bb.dst.bit _ bit MOD bitsPerWord; bb.width _ Basics.BITSHIFT[(fmax-fmin), lgBitsPerPixel]; PrincOpsUtils.BITBLT[bb]; }; bb^ _ nullBitBltTable; bb.srcDesc.gray _ [yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]; bb.src.word _ LOOPHOLE[@data.grayWord]; bb.dstBpl _ wordsPerLine*bitsPerWord; bb.height _ 1; bb.flags _ data.flags; runs[ditheredConstantRun]; }; stipple => { lineBuffer: ImagerSample.SampleBuffer ~ data.lineBuffer; stippleRun: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { fmin: NAT ~ Check[fMin, widthLimit]; fmax: NAT ~ Check[fmin+fSize, widthLimit]; smin: NAT ~ Basics.BoundsCheck[sMin, heightLimit]; srcLine: ImagerSample.UnsafeSamples ~ lineBuffer.GetPointer[0, 0, fSize]; sBase: NAT ~ (smin MOD 4) * 4; fStartMax: NAT _ MIN[fmax, fmin+4]; FOR f: NAT IN [fmin..fStartMax) DO lineBuffer[f-fmin] _ data.stipple[sBase + f MOD 4]; ENDLOOP; IF fmax > fStartMax THEN { PrincOpsUtils.LongCopy[from: srcLine, nwords: fmax-fStartMax, to: srcLine+4]; }; ImagerSample.UnsafePutF[samples: srcLine, count: fSize, s: smin, f: fmin, base: base, wordsPerLine: wordsPerLine, bitsPerSample: bitsPerPixel, srcFunc: data.flags.srcFunc, dstFunc: data.flags.dstFunc]; }; runs[stippleRun]; }; rgb => { IF bitsPerPixel = 8 THEN { rgbSampledRun8: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { s: NAT ~ Basics.BoundsCheck[sMin, heightLimit]; f: NAT ~ Check[fMin, widthLimit]; count: NAT ~ Check[fSize, widthLimit-f]; ImagerDither.DitherConstant[destLine: base+Basics.LongMult[s, wordsPerLine], start: f, count: fSize, packed: data.packedRGB, sTile: s+data.sTileOrg, fTile: f+data.fTileOrg, table: data.table]; }; runs[rgbSampledRun8]; } ELSE { lineBuf: LONG POINTER TO RawWords ~ data.lineBuffer.GetPointer[0, 0, widthLimit]; rgbSampledRun: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { s: NAT ~ Basics.BoundsCheck[sMin, heightLimit]; f: NAT ~ Check[fMin, widthLimit]; count: NAT ~ Check[fSize, widthLimit-f]; ImagerDither.WordDitherConstant[destLine: lineBuf, start: 0, count: fSize, packed: data.packedRGB, sTile: s+data.sTileOrg, fTile: f+data.fTileOrg, table: data.table]; ImagerSample.UnsafePutF[samples: lineBuf, count: fSize, s: s, f: f, base: base, wordsPerLine: wordsPerLine, bitsPerSample: bitsPerPixel]; }; runs[rgbSampledRun]; }; }; sampled => { table: ImagerDither.Table ~ data.table; scd: SampledColorData ~ data.sampledColorData; dstFunc: PrincOps.DstFunc ~ IF data.zerosAreClear THEN and ELSE null; IF data.paToDevice.form = 3 AND data.paToDevice.integerTrans AND bitsPerPixel = 8 AND dstFunc = null THEN { refRep: REF ImagerPixelMap.PixelMapRep ~ scd.source.refRep; sSizeSource: NAT ~ scd.source.sSize; fSizeSource: NAT ~ scd.source.fSize; ditheredSampledRunFast: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { smin: NAT ~ Check[sMin, heightLimit]; fmin: NAT _ Check[fMin, widthLimit]; count: NAT _ Check[fSize, widthLimit-fmin]; sMinSource: INTEGER ~ Mod[INT[smin]-data.paToDevice.tx, sSizeSource]; fMinSource: INTEGER _ Mod[INT[fmin]-data.paToDevice.ty, fSizeSource]; dstLine: LONG POINTER ~ base+Basics.LongMult[smin, wordsPerLine]; srcLine: LONG POINTER ~ refRep.pointer+Basics.LongMult[NAT[sMinSource], refRep.rast]; UNTIL count = 0 DO delta: NAT ~ MIN[count, fSizeSource-fMinSource]; ImagerDither.Dither[destLine: dstLine, start: fmin, count: delta, packed: srcLine+fMinSource, sTile: smin+data.sTileOrg, fTile: fmin+data.fTileOrg, table: data.table]; fMinSource _ fMinSource + delta; IF fMinSource >= fSizeSource THEN fMinSource _ fMinSource-fSizeSource; fmin _ fmin + delta; count _ count - delta; ENDLOOP; }; runs[ditheredSampledRunFast] } ELSE { t: ImagerPixelMap.PixelMap ~ scd.source; sampBuffer: SampleBuffer ~ data.sampBuffer; sampler: ImagerSample.Sampler ~ data.sampler; sampler.base _ t.refRep.pointer; sampler.wordsPerLine _ t.refRep.rast; sampler.bitsPerSample _ 16; sampler.sMin _ t.sMin; sampler.fMin _ t.fMin; sampler.sSize _ t.sSize; sampler.fSize _ t.fSize; ImagerSample.SetSamplerIncrements[sampler, data.paToDevice]; ImagerSample.SetSamplerPosition[sampler: sampler, m: data.paToDevice, s: bounds.smin, f: bounds.fmin]; IF bitsPerPixel = 8 AND dstFunc = null THEN { ditheredSampledRun8: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { s: NAT ~ Basics.BoundsCheck[sMin, heightLimit]; f: NAT ~ Check[fMin, widthLimit]; count: NAT ~ Check[fSize, widthLimit-f]; sampPointer: ImagerSample.UnsafeSamples ~ sampBuffer.GetPointer[0, f, count]; ImagerSample.GetPointSamples[sampler: sampler, s: s, f: f, buffer: sampBuffer, bi: 0, bj: f, count: count]; ImagerDither.Dither[destLine: base+Basics.LongMult[s, wordsPerLine], start: f, count: fSize, packed: sampPointer, sTile: s+data.sTileOrg, fTile: f+data.fTileOrg, table: data.table]; }; runs[ditheredSampledRun8] } ELSE { lineBuf: LONG POINTER TO RawWords ~ data.lineBuffer.GetPointer[0, 0, widthLimit]; ditheredSampledRun: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED { s: NAT ~ Basics.BoundsCheck[sMin, heightLimit]; f: NAT ~ Check[fMin, widthLimit]; count: NAT ~ Check[fSize, widthLimit-f]; sampPointer: ImagerSample.UnsafeSamples ~ sampBuffer.GetPointer[0, 0, count]; ImagerSample.GetPointSamples[sampler: sampler, s: s, f: f, buffer: sampBuffer, bi: 0, bj: 0, count: count]; ImagerDither.WordDither[destLine: lineBuf, start: 0, count: fSize, packed: sampPointer, sTile: s+data.sTileOrg, fTile: f+data.fTileOrg, table: data.table]; ImagerSample.UnsafePutF[samples: lineBuf, count: fSize, s: s, f: f, base: base, wordsPerLine: wordsPerLine, bitsPerSample: bitsPerPixel, dstFunc: dstFunc]; }; runs[ditheredSampledRun]; }; }; }; ENDCASE => ERROR; -- illegal case }; me: REF TEXT ~ "Dith"; -- a globally unique REF for use as a clientID in the global cache. packing: ImagerDither.PackedColorDesc _ [4, 4, 4, 4]; sTileSize: NAT _ 3; fTileSize: NAT _ 3; ExamineDitherTable: PROC [r, g, b: NAT, bitsPerPixel: NAT _ 8] RETURNS [a: ARRAY[0..16) OF NAT] ~ { data: Data ~ NEW[DataRep _ [sampBuffer:NIL, lineBuffer:NIL, sampler:NIL]]; data.mapEntries _ ImagerColorMap.StandardColorMapEntries[bitsPerPixel]; SetUpDitherTable[data]; IF data.table#NIL THEN TRUSTED { packed: WORD ~ ImagerDither.Pack[r, g, b, packing]; array: LONG POINTER TO RawBytes ~ data.table.space.pointer; FOR i: NAT IN [0..16) DO a[i] _ array[packed+i]; ENDLOOP; } ELSE ERROR; }; specialTwoBits: BOOL _ TRUE; SetUpDitherTable: PROC [data: Data] ~ { table: ImagerDither.Table _ data.table; IF table = NIL OR table.packing # packing OR table.sTileSize # sTileSize OR table.fTileSize # fTileSize THEN { cache: FunctionCache.Cache _ FunctionCache.GlobalCache[]; compare: FunctionCache.CompareProc ~ {RETURN [argument = data.mapEntries]}; table _ NARROW[FunctionCache.Lookup[cache, compare, me].value]; IF table = NIL OR table.packing # packing OR table.sTileSize # sTileSize OR table.fTileSize # fTileSize THEN { colors: LIST OF ImagerColorMap.MapEntry _ data.mapEntries; mapScale: NAT _ 1; IF specialTwoBits AND data.frame.refRep.lgBitsPerPixel=1 THEN { mapScale _ 2; FOR m: LIST OF ImagerColorMap.MapEntry _ colors, m.rest UNTIL m = NIL DO r: NAT _ 255; g: NAT _ 255; b: NAT _ 255; IF m.first.red # 0 THEN { g _ g-1; b _ b-1; }; IF m.first.green # 0 THEN { r _ r-1; b _ b-1; }; IF m.first.blue # 0 THEN { r _ r-1; g _ g-1; }; colors _ CONS[[m.first.mapIndex+4, r, g, b], colors]; ENDLOOP; }; IF colors = NIL THEN ERROR; table _ ImagerDither.CreateTable[packing: packing, sTileSize: sTileSize, fTileSize: fTileSize]; ImagerDither.InitTable[table, colors, mapScale]; FunctionCache.Insert[cache, data.mapEntries, table, INT[256]*256, me]; }; data.table _ table; }; }; SetUpSampledColorData: PROC [data: Data] ~ { cache: FunctionCache.Cache _ FunctionCache.GlobalCache[]; color: SampledColor ~ data.sampledColor; pa: PixelArray ~ color.pa; compare: FunctionCache.CompareProc ~ {RETURN [argument=pa]}; scd: SampledColorData _ NARROW[FunctionCache.Lookup[cache, compare, me].value]; IF color = NIL THEN ERROR; IF scd = NIL OR scd.packing # packing THEN TRUSTED { pa: PixelArray ~ color.pa; colorOperator: ColorOperator ~ color.colorOperator; samplesPerPixel: NAT ~ pa.samplesPerPixel; sSize: NAT ~ pa.sSize; fSize: NAT ~ pa.fSize; maxIn: Sample ~ ImagerPixelArray.MaxSampleValue[pa, 0]; pixels: SampleBuffer ~ ImagerSample.NewBuffer[samplesPerPixel, fSize]; buffer: SampleBuffer ~ ImagerSample.NewBuffer[3, fSize]; bufferPointerR: UnsafeSamples ~ buffer.GetPointer[0, 0, fSize]; bufferPointerG: UnsafeSamples ~ buffer.GetPointer[1, 0, fSize]; bufferPointerB: UnsafeSamples ~ buffer.GetPointer[2, 0, fSize]; mapperR: ImagerColorOperator.Mapper ~ ImagerColorOperator.NewMapper[ op: colorOperator, component: $Red, maxIn: maxIn, maxOut: 255]; mapperG: ImagerColorOperator.Mapper ~ ImagerColorOperator.NewMapper[ op: colorOperator, component: $Green, maxIn: maxIn, maxOut: 255]; mapperB: ImagerColorOperator.Mapper ~ ImagerColorOperator.NewMapper[ op: colorOperator, component: $Blue, maxIn: maxIn, maxOut: 255]; t: ImagerPixelMap.PixelMap ~ ImagerPixelMap.Create[4, [0, 0, sSize, fSize]]; line: LONG POINTER TO Basics.RawWords _ t.refRep.pointer; rast: NAT ~ t.refRep.rast; sampledColorDataSize: INT _ t.refRep.words+SIZE[ImagerPixelMap.PixelMap]+SIZE[ImagerPixelMap.PixelMapRep]; scd _ NEW[SampledColorDataRep _ [packing, t]]; FOR s: NAT IN[0..sSize) DO ImagerPixelArray.GetPixels[pa: pa, s: s, f: 0, buffer: pixels, count: fSize]; ImagerColorOperator.MapPixels[mapper: mapperR, pixels: pixels, buffer: buffer, bi: 0, count: fSize]; ImagerColorOperator.MapPixels[mapper: mapperG, pixels: pixels, buffer: buffer, bi: 1, count: fSize]; ImagerColorOperator.MapPixels[mapper: mapperB, pixels: pixels, buffer: buffer, bi: 2, count: fSize]; ImagerDither.PackSequence[dest: line, red: bufferPointerR, green: bufferPointerG, blue: bufferPointerB, count: fSize, packing: packing]; line _ line + rast; ENDLOOP; FunctionCache.Insert[cache, pa, scd, sampledColorDataSize, me]; }; data.sampledColorData _ scd; }; DitheredMaskBoxesInternal: PROC[data: Data, bounds: DeviceBox, boxes: PROC[BoxProc]] ~ { ditheredBox: PROC[box: DeviceBox] ~ { runs: PROC[run: RunProc] ~ { ImagerMask.RunsFromBox[box: box, run: run] }; DitheredMaskRunsInternal[data: data, bounds: box, runs: runs]; }; boxes[ditheredBox]; }; lockingCursor: BOOL _ TRUE; ModifyColorFrame: PROC [vt: Terminal.Virtual, action: PROC, xmin: NAT, ymin: NAT, xmax: NAT, ymax: NAT] ~ INLINE { IF vt = NIL OR NOT lockingCursor THEN action[] ELSE Terminal.ModifyColorFrame[vt: vt, action: action, xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax]; }; DitheredMaskRuns: PROC[device: Device, bounds: DeviceBox, runs: PROC[RunProc]] ~ { data: Data ~ NARROW[device.data]; ditheredMaskRunsAction: PROC ~ { DitheredMaskRunsInternal[data, bounds, runs] }; ModifyColorFrame[vt: data.terminal, action: ditheredMaskRunsAction, xmin: bounds.fmin, ymin: bounds.smin, xmax: bounds.fmax, ymax: bounds.smax]; }; DitheredMaskBoxes: PROC[device: Device, bounds: DeviceBox, boxes: PROC[BoxProc]] ~ { data: Data ~ NARROW[device.data]; ditheredMaskBoxesAction: PROC ~ { DitheredMaskBoxesInternal[data, bounds, boxes] }; ModifyColorFrame[vt: data.terminal, action: ditheredMaskBoxesAction, xmin: bounds.fmin, ymin: bounds.smin, xmax: bounds.fmax, ymax: bounds.smax]; }; DitheredMaskBits: PROC[device: Device, srcBase: LONG POINTER, srcWordsPerLine: NAT, ts, tf: INTEGER, boxes: PROC[BoxProc]] ~ { data: Data ~ NARROW[device.data]; ditheredMaskBitsBox: PROC[box: DeviceBox] ~ { ditheredMaskBitsBoxAction: PROC ~ { runs: PROC[run: RunProc] ~ { ImagerMask.RunsFromBits[ base: srcBase, wordsPerLine: srcWordsPerLine, sBits: box.smin-ts, fBits: box.fmin-tf, sRuns: box.smin, fRuns: box.fmin, sSize: box.smax-box.smin, fSize: box.fmax-box.fmin, run: run]; }; DitheredMaskRunsInternal[data: data, bounds: box, runs: runs]; }; ModifyColorFrame[vt: data.terminal, action: ditheredMaskBitsBoxAction, xmin: box.fmin, ymin: box.smin, xmax: box.fmax, ymax: box.smax]; }; boxes[ditheredMaskBitsBox]; }; DitheredMoveBoxes: PROC [device: Device, ts, tf: INTEGER, boxes: PROC[BoxProc]] ~ TRUSTED { data: Data ~ NARROW[device.data]; frame: PixelMap ~ data.frame; base: LONG POINTER ~ frame.refRep.pointer; wordsPerLine: NAT ~ frame.refRep.rast; lgBitsPerPixel: NAT ~ frame.refRep.lgBitsPerPixel; bitsPerPixel: NAT ~ Basics.BITSHIFT[1, lgBitsPerPixel]; heightLimit: NAT ~ frame.sMin+frame.sSize; widthLimit: NAT ~ frame.fMin+frame.fSize; bbspace: PrincOps.BBTableSpace; bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbspace]; action: SAFE PROC ~ TRUSTED { PrincOpsUtils.BITBLT[bb] }; moveBox: PROC[box: DeviceBox] ~ TRUSTED { dfmin: NAT ~ Check[box.fmin, widthLimit]; dsmin: NAT ~ Check[box.smin, heightLimit]; dfmax: NAT ~ Check[box.fmax, widthLimit]; dsmax: NAT ~ Check[box.smax, heightLimit]; sfmin: NAT ~ dfmin-tf; ssmin: NAT ~ dsmin-ts; sfmax: NAT ~ dfmax-tf; ssmax: NAT ~ dsmax-ts; dstBit: CARDINAL ~ Basics.BITSHIFT[dfmin, lgBitsPerPixel]; srcBit: CARDINAL ~ Basics.BITSHIFT[sfmin, lgBitsPerPixel]; bpl: INTEGER _ wordsPerLine*bitsPerWord; ss: NAT _ ssmin; ds: NAT _ dsmin; bb.flags _ [disjoint: TRUE, disjointItems: TRUE, direction: forward, gray: FALSE]; IF dsminssmin THEN { bb.flags.disjoint _ FALSE; IF dsmin=ssmin AND dfminsfmin THEN bb.flags.disjointItems _ FALSE; IF dsmin>ssmin OR (dsmin=ssmin AND dfmin>sfmin) THEN { bb.flags.direction _ backward; bpl _ -bpl; ss _ ssmax-1; ds _ dsmax-1; }; }; bb.dst.word _ base+Basics.LongMult[ds, wordsPerLine]+dstBit/bitsPerWord; bb.dst.bit _ dstBit MOD bitsPerWord; bb.dstBpl _ bpl; bb.src.word _ base+Basics.LongMult[ss, wordsPerLine]+srcBit/bitsPerWord; bb.src.bit _ srcBit MOD bitsPerWord; bb.srcDesc.srcBpl _ bpl; bb.width _ (dfmax-dfmin)*bitsPerPixel; bb.height _ dsmax-dsmin; ModifyColorFrame[vt: data.terminal, action: action, xmin: MIN[dfmin, sfmin], ymin: MIN[dsmin, ssmin], xmax: MAX[dfmax, sfmax], ymax: MAX[dsmax, ssmax]]; }; bb^ _ nullBitBltTable; boxes[moveBox]; }; SpecialPixel: TYPE ~ ImagerDitheredDevice.SpecialPixel; specialStipple: WORD _ 0E280H; ColorFromSpecialPixel: PUBLIC PROC [specialPixel: SpecialPixel] RETURNS [ConstantColor] ~ { impl: ConstantColorImpl ~ NEW[ConstantColorImplRep.stipple _ [ Y: 0.7, variant: stipple[word: 0E280H, function: replace]]]; RETURN[NEW[ImagerColorDefs.ColorRep.constant _ [variant: constant[impl: impl, data: NEW[SpecialPixel_specialPixel]]]]]; }; IntensityFromRGB: PROC [val: ImagerColor.RGB] RETURNS [REAL] ~ { Y: REAL ~ 0.30*val.R+0.59*val.G+0.11*val.B; IF Y<=0 THEN RETURN[0]; IF Y>=1 THEN RETURN[1]; RETURN[Y]; }; ColorFromSpecialRGB: PUBLIC PROC [specialPixel: SpecialPixel, rgb: ImagerColor.RGB] RETURNS [ConstantColor] ~ { impl: ConstantColorImpl ~ NEW[ConstantColorImplRep.rgb _ [ Y: IntensityFromRGB[rgb], variant: rgb[val: rgb]]]; RETURN[NEW[ImagerColorDefs.ColorRep.constant _ [variant: constant[impl: impl, data: NEW[SpecialPixel_specialPixel]]]]]; }; END. bImagerDitheredDeviceImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Michael Plass, August 1, 1985 3:32:25 pm PDT Doug Wyatt, May 30, 1985 11:25:23 pm PDT Might want to do some pre-computation here someday. data.halftone _ halftone; IF x IN[0..max] THEN RETURN[x] ELSE ERROR RuntimeError.BoundsFault For debugging. This causes the assumption that the two-bit-per-pixel case has the additive primaries and black; the gamut of the input gets effectively chopped in half in this case, so white is obtained by dithering red, green, and blue in approximately equal proportions. Κ ˜codešœ™Kšœ Οmœ7™BK™,K™(—K˜šΟk ˜ Jš œžœžœžœžœ8˜vJšœžœ3˜FJšœžœ˜!Jšœ žœ˜'Jšœ žœžœ˜Jšœžœl˜Jšœžœ%˜9Jšœžœ7˜PJšœžœV˜nJšœ žœW˜iJšœ žœ~˜Jšœžœ˜6JšœžœF˜gJšœ žœ˜-Jšœžœ˜3Jšœžœ˜(JšœžœQ˜eJšœ žœ ˜Jšœ žœš˜¬Jšœžœ2˜LJšœ žœH˜VJšœžœžœ ˜7Jšœžœ ˜Jšœ žœV˜dJšœžœžœ˜—K˜KšΠblœžœž˜'KšžœΥ˜άKšžœ4˜;Kšœžœžœ+˜7K˜Kšžœžœ žœ˜Kšžœžœ žœ˜Kšœžœ'˜;Kšœžœ˜$Kšœžœ!˜4Kšœžœ ˜2Kšœžœ!˜4Kšœ žœ#˜3Kšœ žœ˜)Kšœžœ˜#Kšœžœ˜/šœžœ˜1K˜—Kšœ žœ˜!Kšœ žœ˜!K˜Kšœžœ(˜?Kšœžœžœ+˜LK˜Kšœžœ)˜AKšœžœžœ,˜NK˜Kšœ žœ˜&K˜šœžœ˜9Kšœ˜Kšœ˜Kšœ!˜!Kšœ!˜!Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—š Οnœžœžœžœžœ˜#Kš žœžœžœ*žœžœ˜OK˜K˜—š œžœ%žœ˜hšœžœžœ˜KKšœ˜Kšœ˜Kšœžœžœ˜=Kšœ-˜-Kšœ˜Kšœ˜Kšœ˜—šœ"˜"Kšœ)˜)KšœC˜CKšœ˜—Kšžœ˜K˜K˜—Kšœ žœ˜"Kšœžœ˜šœžœ˜K˜—š œžœžœ1žœžœžœžœ˜ŒKšœf˜fKšžœ“˜™Kšœ˜K˜—š  œžœžœ$žœžœžœ˜sKšœf˜fKšžœ†˜ŒKšœ˜K˜—š  œžœ>˜Qšœ˜K™3—Kšœ˜K˜—š  œžœžœ1žœžœžœ˜Kšœžœ žœ!˜DKšœc˜cKšœH˜HKšœG˜GKšœG˜GKšœG˜GKšœ žœ˜BKšœ žœZ˜jKšœa˜aKšœ#˜#Kšœ ˜ šžœžœ(˜2KšœR˜RKšœ!˜!Kšœ4˜4Kšœ˜Kšœ˜—Kšœ˜K˜—š  œžœžœ"žœžœžœ˜tKšœ$žœ žœ#žœ#˜~Kšœ4˜4Kšœ`˜`Kšœ žœ˜!Kšœ˜Kšžœ ˜Kšœ˜K˜—š  œžœžœ#žœžœ ˜]Kšžœ%˜+K˜K˜—šœ)˜)Kšœ žœ˜$Kšœ žœ ˜/Kšœ˜Kšœ˜K˜—š  œžœžœžœžœ˜NKš œžœžœ žœžœžœ˜1Kšžœ žœ žœžœ žœ žœžœžœ˜JK˜K˜—š  œžœžœžœžœ˜3Kšžœžœžœ˜Kš žœžœžœžœžœ˜Kšžœžœžœ˜!K˜K˜—šœ žœžœžœΟfœ‘œ‘œ‘œ‘œ˜DK˜—š  œžœ žœžœžœ˜MKšœžœ žœ žœ˜EKšœžœ ˜"Kšžœ$˜*Kšœ˜K˜—š œžœA˜WKšœ žœ˜!Kšœ˜Kšœžœ˜Kšœžœ˜šžœžœž˜˜K˜%Kšœžœžœ ˜Hšžœ žœž˜šœ žœ˜KšœP˜PKšœ˜Kšœ%˜%K˜—šžœ˜ šžœ žœž˜šœ ˜ Kšœžœ ˜šžœž˜Kšœ/˜/KšœG˜GKšœ'˜'Kšœ%˜%Kšžœžœ˜—Kšžœžœžœžœ-˜CKšžœD˜HKšœ˜—šœ˜Kšœžœ"˜.Kšœžœ"˜.Kšœžœ"˜.KšœD˜DKšœ˜Kšœ˜—šžœ˜ Kšœžœ˜)KšœA˜AKšœ˜K˜——Kšœ˜——K˜—šœ˜Kšœ˜Kšœ˜Kšœ3˜3Kšœ˜K˜Kšœc˜cKšœI˜IKšœ˜K˜—KšžœžœΟc˜*—šžœžœžœ˜0Kšœ˜Kšœ˜—šžœžœ˜šžœžœ˜#Kšœ:˜:Kšœ:˜:Kšœ˜—Kšžœ%˜)Kšœ˜—Kšœ˜K˜—š  œžœžœžœžœžœ˜0Kšœ ˜ Kšžœ žœ˜0Kšžœ)˜/Kšœ˜K˜—š œžœ%žœ˜GKšœ˜K˜—š œžœ3˜LKšœ žœ˜!K™K˜K˜—š  œžœžœžœžœžœ˜1Kšžœžœžœ$˜8Kš žœžœ žœžœžœžœ™BK˜—š œžœ&žœ žœ˜^Kšœ˜Kšœžœžœ˜*Kšœžœ˜&K˜KšœE˜EKšœžœ˜2Kšœžœ žœ˜7Kšœ žœ˜*Kšœ žœ˜)šžœ ž˜Kšœžœ’˜&šœ ˜ š œžœ žœ žœžœ˜FKšœžœ˜$Kšœžœ!˜*Kšœžœ)˜2Kšœžœ žœ˜6KšœG˜GKšœžœ ˜!Kšœžœ˜8Kšœžœ˜K˜—Kšœ˜KšœD˜DKšœžœ˜'Kšœ%˜%Kšœ˜Kšœ˜Kšœ˜K˜—šœ ˜ Kšœ8˜8š œ žœ žœ žœžœ˜=Kšœžœ˜$Kšœžœ!˜*Kšœžœ)˜2KšœI˜IKšœžœ žœ˜Kšœ žœžœ˜#šžœžœžœž˜"Kšœ,žœ˜3Kšžœ˜—šžœžœ˜KšœM˜MKšœ˜—K•StartOfExpansionρ[samples: ImagerSample.UnsafeSamples, count: NAT, s: NAT _ 0, f: NAT _ 0, base: LONG POINTER, wordsPerLine: NAT, bitsPerSample: ImagerSample.BitsPerSample, srcFunc: PrincOps.SrcFunc _ null, dstFunc: PrincOps.DstFunc _ null]šœΙ˜ΙK˜—Kšœ˜K˜—˜šžœžœ˜š œžœ žœ žœžœ˜AKšœžœ)˜/Kšœžœ˜!Kšœžœ˜(Kšœΐ˜ΐKšœ˜—Kšœ˜K˜—šžœ˜Kšœ žœžœžœ9˜Qš œžœ žœ žœžœ˜@Kšœžœ)˜/Kšœžœ˜!Kšœžœ˜(Kšœ¦˜¦Kšœ‰˜‰Kšœ˜—Kšœ˜Kšœ˜—K˜—˜ Kšœ'˜'Kšœ.˜.Kšœžœžœžœ˜Eš žœžœžœžœžœ˜kKšœžœ0˜;Kšœ žœ˜$Kšœ žœ˜$š œžœ žœ žœžœ˜IKšœžœ˜%Kšœžœ˜$Kšœžœ!˜+Kšœ žœžœ(˜EKšœ žœžœ(˜EKšœ žœžœ,˜AKšœ žœžœ"žœ˜Ušžœ ž˜Kšœžœžœ ˜0Kšœ§˜§Kšœ ˜ Kšžœžœ%˜FKšœ˜Kšœ˜Kšžœ˜—Kšœ˜—Kšœ˜Kšœ˜—šžœ˜Kšœ(˜(Kšœ+˜+Kšœ-˜-Kšœ ˜ Kšœ%˜%Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ<˜K˜—Kšœ˜K˜K˜—–t[vt: Terminal.Virtual, action: PROC, xmin: NAT _ 0, ymin: NAT _ 0, xmax: NAT _ 32767, ymax: NAT _ 32767]šœžœžœ˜K˜—š œžœ žœžœžœžœžœžœ˜rKš žœžœžœžœžœ ˜.Kšžœc˜gKšœ˜K˜—š œžœ*žœ˜RKšœ žœ˜!Kšœžœ4˜PKšœ˜K˜K˜—š œžœ+žœ˜TKšœ žœ˜!Kšœžœ6˜SKšœ‘˜‘K˜K˜—š œžœžœžœžœ žœ žœ˜Kšœ žœ˜!šœžœ˜-šœžœ˜#šœžœδ˜ξKšœ˜—K˜>Kšœ˜—Kšœ‡˜‡K˜—Kšœ˜K˜K˜—š  œžœžœ žœ žœ˜[Kšœ žœ˜!Kšœ˜Kšœžœžœ˜*Kšœžœ˜&Kšœžœ˜2Kšœžœ žœ˜7Kšœ žœ˜*Kšœ žœ˜)K˜KšœE˜EKš œžœžœžœžœ˜9šœ žœžœ˜)Kšœžœ˜)Kšœžœ ˜*Kšœžœ˜)Kšœžœ ˜*Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ žœ˜:Kšœžœ žœ˜:Kšœžœ˜(Kšœžœ ˜Kšœžœ ˜Kšœžœžœžœ˜Ršžœ žœ žœ˜%Jšœžœ˜Kš žœ žœ žœ žœžœ˜Sšžœ žœžœžœ˜6JšœF˜FK˜—K˜—KšœH˜HKšœžœ ˜$Kšœ˜KšœH˜HKšœžœ ˜$Kšœ˜Kšœ&˜&Kšœ˜Kš œ:žœžœžœžœ˜™K˜—Kšœ˜K˜K˜K˜—šœžœ%˜7K˜—Kšœžœ‘œ˜š œžœžœžœ˜[KšœžœA‘œ˜{KšžœžœJžœ ˜wKšœ˜K˜—š  œžœžœžœžœ˜@Kšœžœ$˜+Kšžœžœžœ˜Kšžœžœžœ˜Kšžœ˜ K˜K˜—š  œžœžœ/žœžœ˜oKšœžœQ˜nKšžœžœJžœ ˜wKšœ˜—K™Kšžœ˜J˜—…—f‚‰