ImagerDitheredDeviceImpl.mesa
Copyright © 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
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: BOOLFALSE] 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: BOOLFALSE] 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;
Might want to do some pre-computation here someday.
};
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: BOOLTRUE] 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];
data.halftone ← halftone;
};
Check: PROC[x: CARDINAL, max: NAT] RETURNS[NAT] ~
TRUSTED MACHINE CODE { PrincOps.zINC; PrincOps.zBNDCK };
IF x IN[0..max] THEN RETURN[x] ELSE ERROR RuntimeError.BoundsFault
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: NATMIN[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] ~ {
For debugging.
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: BOOLTRUE;
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.
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: BOOLTRUE;
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 dsmin<ssmax AND dsmax>ssmin THEN {
bb.flags.disjoint ← FALSE;
IF dsmin=ssmin AND dfmin<sfmax AND dfmax>sfmin 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: WORD0E280H;
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.