ImagerLFDeviceImpl.mesa
Copyright © 1984 Xerox Corporation. All rights reserved.
Michael Plass, June 19, 1984 1:19:53 pm PDT
Doug Wyatt, November 5, 1984 3:18:31 pm PST
DIRECTORY
Basics USING [bitsPerWord, LongMult],
ImagerColor,
ImagerDevice USING [Class, ClassRep, Device, DeviceRep, HalftoneParameters, RunProc],
ImagerPixelArray,
ImagerPixelMap USING [Create, DeviceRectangle, Function, PixelMap],
ImagerPixelRow USING [ClearPixelRow, CreatePixelRow, PixelRow, StorePixelRow],
ImagerSampler USING [CreateSampler, DotScreen, HalftoneLine, ObtainPointSamples, Sampler],
ImagerTransformation USING [Cat, Copy, PreRotate, PreScale, Transformation, Translate],
PrincOps USING [BBTableSpace, BitBltTable, BitBltTablePtr, zBNDCK, zINC],
PrincOpsUtils USING [AlignedBBTable, BITBLT],
Real USING [FixC],
Vector2 USING [VEC];
ImagerLFDeviceImpl: CEDAR PROGRAM
IMPORTS Basics, ImagerPixelArray, ImagerPixelMap, ImagerPixelRow, ImagerSampler, ImagerTransformation, PrincOpsUtils, Real
~ BEGIN
Device: TYPE ~ ImagerDevice.Device;
RunProc: TYPE ~ ImagerDevice.RunProc;
HalftoneParameters: TYPE ~ ImagerDevice.HalftoneParameters;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
VEC: TYPE ~ Vector2.VEC;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Color: TYPE ~ ImagerColor.Color;
ConstantColor: TYPE ~ ImagerColor.ConstantColor;
SampledColor: TYPE ~ ImagerColor.SampledColor;
SpecialColor: TYPE ~ ImagerColor.SpecialColor;
ColorOperator: TYPE ~ ImagerColor.ColorOperator;
ColorOperatorRep: TYPE ~ ImagerColor.ColorOperatorRep;
GrayArray: TYPE ~ ARRAY [0..16) OF CARDINAL;
grayHeight: NAT ~ 16; -- must not exceed 16, should be a power of 2
checkeredGray: GrayArray ~ [
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH, -- @ . @ . @ . @ . @ . @ . @ . @ .
05555H, -- . @ . @ . @ . @ . @ . @ . @ . @
0AAAAH -- @ . @ . @ . @ . @ . @ . @ . @ .
];
desktopGray: GrayArray ~ [
01111H, -- . . . @ . . . @ . . . @ . . . @
01111H, -- . . . @ . . . @ . . . @ . . . @
04444H, -- . @ . . . @ . . . @ . . . @ . .
04444H, -- . @ . . . @ . . . @ . . . @ . .
01111H, -- . . . @ . . . @ . . . @ . . . @
01111H, -- . . . @ . . . @ . . . @ . . . @
04444H, -- . @ . . . @ . . . @ . . . @ . .
04444H, -- . @ . . . @ . . . @ . . . @ . .
01111H, -- . . . @ . . . @ . . . @ . . . @
01111H, -- . . . @ . . . @ . . . @ . . . @
04444H, -- . @ . . . @ . . . @ . . . @ . .
04444H, -- . @ . . . @ . . . @ . . . @ . .
01111H, -- . . . @ . . . @ . . . @ . . . @
01111H, -- . . . @ . . . @ . . . @ . . . @
04444H, -- . @ . . . @ . . . @ . . . @ . .
04444H -- . @ . . . @ . . . @ . . . @ . .
];
Case: TYPE ~ {nil, constant, gray, sampled};
defaultHalftone: HalftoneParameters ← [dotsPerInch: 18, angle: 30, shape: 0.5];
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD[
frame: ImagerPixelMap.PixelMap, -- the bitmap
pixelsPerInch: REAL, -- declared resolution
dot: ImagerPixelMap.PixelMap, -- a halftone dot
case: Case ← nil, -- what type of color
function: ImagerPixelMap.Function ← [null, null], -- bitblt function
gray: GrayArray ← ALL[0], -- bitblt gray block
halftone: HalftoneParameters ← defaultHalftone, -- halftone screen parameters
pa: ImagerPixelArray.PixelArray ← NIL, -- pixel array from sampled color
multiplier: CARDINAL ← 1, lgScale: INTEGER ← 0, -- scale pa samples to match dot samples
transparent: BOOLFALSE,
paToDevice: Transformation ← NIL, -- transformation from pa coords to display
dotToDevice: Transformation ← NIL, -- transformation from dot coords to display
source: ImagerPixelMap.PixelMap ← [0, 0, 0, 0, 0, 0, NIL], -- source values from pixel array
paSampler: ImagerSampler.Sampler ← NIL, -- for sampling pixel array
dotSampler: ImagerSampler.Sampler ← NIL, -- for sampling halftone dot
paRow: ImagerPixelRow.PixelRow ← NIL, -- buffer for pixels sampled from pa
dotRow: ImagerPixelRow.PixelRow ← NIL -- buffer for pixels sampled from dot
];
class: ImagerDevice.Class ~ NEW[ImagerDevice.ClassRep ← [
type: $LFDisplay,
SetColor: SetColor,
SetPriority: SetPriority,
SetHalftone: SetHalftone,
MaskRuns: MaskRuns
]];
defaultDot: ImagerPixelMap.PixelMap ~ ImagerSampler.DotScreen[
r: 0.5, sSize: 16, fSize: 16, maxPixelValue: 255];
defaultPixelsPerInch: REAL ~ 72;
Create: PUBLIC PROC[frame: ImagerPixelMap.PixelMap,
pixelsPerInch: REAL ← defaultPixelsPerInch] RETURNS[Device] ~ {
data: Data ~ NEW[DataRep ← [frame: frame, pixelsPerInch: pixelsPerInch, dot: defaultDot]];
surfaceToDevice: Transformation ~ ImagerTransformation.Translate[[frame.sSize, 0]];
surfaceToDevice.PreRotate[90];
data.paRow ← ImagerPixelRow.CreatePixelRow[sMin: 0, fMin: 0, fSize: frame.fSize];
data.dotRow ← ImagerPixelRow.CreatePixelRow[sMin: 0, fMin: 0, fSize: frame.fSize];
RETURN[NEW[ImagerDevice.DeviceRep ← [class: class,
clipBox: [sMin: 0, fMin: 0, sSize: frame.sSize, fSize: frame.fSize],
surfaceToDevice: surfaceToDevice,
surfaceUnitsPerInch: [pixelsPerInch, pixelsPerInch],
surfaceUnitsPerPixel: 1,
data: data]]];
};
Val: TYPE ~ ImagerPixelArray.Val;
Table: TYPE ~ REF TableRep;
TableRep: TYPE ~ RECORD[SEQUENCE size: NAT OF CARDINAL];
BuildTable: PROC[max: Val, scale: REAL, sWhite, sBlack: REAL,
map: ImagerColor.SampleMap ← NIL] RETURNS[Table] ~ {
table: Table ~ NEW[TableRep[max+1]];
FOR s0: Val IN[0..max] DO
s: REAL ~ IF map=NIL THEN s0 ELSE map[s0];
v: REAL ~ MIN[MAX[(s-sBlack)/(sWhite-sBlack), 0], 1];
table[s0] ← Real.FixC[scale*v];
ENDLOOP;
RETURN[table];
};
SetColor: PROC[device: Device, color: Color, viewToDevice: Transformation] ~ {
data: Data ~ NARROW[device.data];
WITH color SELECT FROM
color: ConstantColor => {
data.function ← [null, null];
SELECT color.cie.Y FROM
0 => { data.gray ← ALL[177777B]; data.case ← constant };
1 => { data.gray ← ALL[0]; data.case ← constant };
ENDCASE => { data.gray ← checkeredGray; data.case ← gray };
};
color: SampledColor => {
pa: ImagerPixelArray.PixelArray ~ color.pa;
samples: ImagerPixelArray.Row ~ NEW[ImagerPixelArray.RowRep[pa.fSize]];
row: ImagerPixelRow.PixelRow ~ ImagerPixelRow.CreatePixelRow[
sMin: 0, fMin: 0, fSize: pa.fSize];
data.pa ← pa;
data.paToDevice ← ImagerTransformation.Cat[pa.m, color.um, viewToDevice];
data.dotToDevice ← viewToDevice.Copy[];
data.dotToDevice.PreScale[data.pixelsPerInch/(data.halftone.dotsPerInch*16)];
data.dotToDevice.PreRotate[data.halftone.angle];
data.source ← ImagerPixelMap.Create[lgBitsPerPixel: 3,
bounds: [sMin: 0, fMin: 0, sSize: pa.sSize, fSize: pa.fSize]];
WITH color.colorOperator SELECT FROM
op: REF ColorOperatorRep.grayLinear => {
table: Table ← NIL;
IF pa.samplesPerPixel#1 THEN ERROR; -- samplesPerPixel must be 1
table ← BuildTable[max: pa.MaxSampleValue[0], scale: 255,
sWhite: op.sWhite, sBlack: op.sBlack, map: op.map];
FOR s: NAT IN[0..pa.sSize) DO
ImagerPixelArray.GetRow[pa, samples, s, 0, 0];
FOR f: NAT IN[0..pa.fSize) DO row[f] ← table[samples[f]] ENDLOOP;
row.sOrigin ← s;
ImagerPixelRow.StorePixelRow[row, data.source];
ENDLOOP;
};
op: REF ColorOperatorRep.separations => {
tables: ARRAY[0..4) OF Table ← ALL[NIL];
IF pa.samplesPerPixel#op.samplesPerPixel THEN ERROR; -- samplesPerPixel must match
FOR i: NAT IN[0..pa.samplesPerPixel) DO
sep: ImagerColor.Separation ~ op[i];
tables[i] ← BuildTable[max: pa.MaxSampleValue[i], scale: 255*sep.cie.Y,
sWhite: sep.sMax, sBlack: sep.sMin, map: sep.map];
ENDLOOP;
FOR s: NAT IN[0..pa.sSize) DO
row.sOrigin ← s;
ImagerPixelRow.ClearPixelRow[row];
FOR i: NAT IN[0..pa.samplesPerPixel) DO
table: Table ~ tables[i];
ImagerPixelArray.GetRow[pa, samples, s, 0, i];
FOR f: NAT IN[0..pa.fSize) DO row[f] ← row[f]+table[samples[f]] ENDLOOP;
ENDLOOP;
ImagerPixelRow.StorePixelRow[row, data.source];
ENDLOOP;
};
ENDCASE => ERROR; -- unknown ColorOperator
data.paSampler ← ImagerSampler.CreateSampler[
m: data.paToDevice, x: 0, y: 0, sPixels: pa.sSize, fPixels: pa.fSize];
data.dotSampler ← ImagerSampler.CreateSampler[
m: data.dotToDevice, x: 0, y: 0, sPixels: data.dot.sSize, fPixels: data.dot.fSize];
data.case ← sampled;
};
color: SpecialColor => SELECT color.ref FROM
$XOR => {
data.function ← [xor, null];
data.gray ← ALL[177777B];
data.case ← constant;
};
ENDCASE => ERROR; -- unknown special color
ENDCASE => ERROR; -- unknown color variant
};
SetPriority: PROC[device: Device, priorityImportant: BOOL] ~ {
};
SetHalftone: PROC[device: Device, halftone: HalftoneParameters] ~ {
data: Data ~ NARROW[device.data];
data.halftone ← halftone;
};
nullBitBltTable: PrincOps.BitBltTable ~ [
dst: [word: NIL, bit: 0], dstBpl: 0,
src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]],
width: 0, height: 0, flags: []];
Check: PROC[x: INTEGER, max: NAT] RETURNS[NAT] ~
TRUSTED MACHINE CODE { PrincOps.zINC; PrincOps.zBNDCK };
IF x IN[0..max] THEN RETURN[x] ELSE ERROR RuntimeError.BoundsFault
MaskRuns: PROC[device: Device, runs: PROC[RunProc]] ~ {
data: Data ~ NARROW[device.data];
frame: ImagerPixelMap.PixelMap ~ data.frame;
SELECT data.case FROM
nil => ERROR; -- color not initialized
constant, gray => TRUSTED {
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbspace];
gray: LONG POINTER ~ @data.gray;
base: LONG POINTER ~ frame.refRep.pointer;
rast: CARDINAL ~ frame.refRep.rast;
runConstant: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED {
f: NAT ~ Check[fMin, frame.fSize];
s: NAT ~ Check[sMin, frame.sSize];
bb.dst.word ← base+Basics.LongMult[s, rast]+f/Basics.bitsPerWord;
bb.dst.bit ← f MOD Basics.bitsPerWord;
bb.width ← Check[fSize, frame.fSize-f];
bb.height ← Check[1, frame.sSize-s];
PrincOpsUtils.BITBLT[bb];
};
runGray: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED {
f: NAT ~ Check[fMin, frame.fSize];
s: NAT ~ Check[sMin, frame.sSize];
bb.dst.word ← base+Basics.LongMult[s, rast]+f/Basics.bitsPerWord;
bb.dst.bit ← bb.src.bit ← f MOD Basics.bitsPerWord;
bb.src.word ← gray+(bb.srcDesc.gray.yOffset ← s MOD grayHeight);
bb.width ← Check[fSize, frame.fSize-f];
bb.height ← Check[1, frame.sSize-s];
PrincOpsUtils.BITBLT[bb];
};
bb^ ← nullBitBltTable;
bb.dstBpl ← rast*Basics.bitsPerWord;
bb.src.word ← gray;
bb.src.bit ← 0;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: grayHeight-1];
bb.flags ← [direction: forward, disjoint: TRUE, gray: TRUE,
srcFunc: data.function.srcFunc, dstFunc: data.function.dstFunc];
runs[IF data.case=constant THEN runConstant ELSE runGray];
};
sampled => {
paRow: ImagerPixelRow.PixelRow ~ data.paRow;
dotRow: ImagerPixelRow.PixelRow ~ data.dotRow;
pa: ImagerPixelArray.PixelArray ~ data.pa; -- pixel array from sampled color
source: ImagerPixelMap.PixelMap ~ data.source;
run: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ {
paRow.sOrigin ← dotRow.sOrigin ← Check[sMin, frame.sSize];
paRow.fOrigin ← dotRow.fOrigin ← Check[fMin, frame.fSize];
paRow.fSize ← dotRow.fSize ← Check[fSize, frame.fSize-fMin];
Might want to interpolate or average here. How do we decide?
ImagerSampler.ObtainPointSamples[sampler: data.paSampler,
pixelRow: paRow, source: source, multiplier: data.multiplier, lgScale: data.lgScale];
ImagerSampler.ObtainPointSamples[sampler: data.dotSampler,
pixelRow: dotRow, source: data.dot];
ImagerSampler.HalftoneLine[dest: frame, pixels: paRow, thresholds: dotRow,
invertOutput: FALSE, transparent: data.transparent];
};
runs[run];
};
ENDCASE => ERROR; -- illegal case
};
MaskRectanglesGray: PROC[device: Device, rectangles: PROC[RectangleProc]] ~ TRUSTED {
data: Data ~ NARROW[device.data];
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbspace];
gray: LONG POINTER ~ @data.gray;
base: LONG POINTER ~ data.frame.refRep.pointer;
rast: CARDINAL ~ data.frame.refRep.rast;
fSizeDevice: NAT ~ data.frame.fSize;
sSizeDevice: NAT ~ data.frame.sSize;
rectangle: PROC[r: DeviceRectangle] ~ TRUSTED {
f: NAT ~ Check[r.fMin, fSizeDevice];
s: NAT ~ Check[r.sMin, sSizeDevice];
bb.dst.word ← base+Basics.LongMult[s, rast]+f/Basics.bitsPerWord;
bb.dst.bit ← bb.src.bit ← f MOD Basics.bitsPerWord;
bb.src.word ← gray+(bb.srcDesc.gray.yOffset ← s MOD grayHeight);
bb.width ← Check[r.fSize, fSizeDevice-f];
bb.height ← Check[r.sSize, sSizeDevice-s];
PrincOpsUtils.BITBLT[bb];
};
bb^ ← nullBitBltTable;
bb.dstBpl ← rast*Basics.bitsPerWord;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: grayHeight-1];
bb.flags ← [direction: forward, disjoint: TRUE, gray: TRUE,
srcFunc: data.function.srcFunc, dstFunc: data.function.dstFunc];
rectangles[rectangle];
};
MaskRectanglesConst: PROC[device: Device, rectangles: PROC[RectangleProc]] ~ TRUSTED {
data: Data ~ NARROW[device.data];
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbspace];
gray: LONG POINTER ~ @data.gray;
base: LONG POINTER ~ data.frame.refRep.pointer;
rast: CARDINAL ~ data.frame.refRep.rast;
fSizeDevice: NAT ~ data.frame.fSize;
sSizeDevice: NAT ~ data.frame.sSize;
rectangle: PROC[r: DeviceRectangle] ~ TRUSTED {
f: NAT ~ Check[r.fMin, fSizeDevice];
s: NAT ~ Check[r.sMin, sSizeDevice];
bb.dst.word ← base+Basics.LongMult[s, rast]+f/Basics.bitsPerWord;
bb.dst.bit ← f MOD Basics.bitsPerWord;
bb.width ← Check[r.fSize, fSizeDevice-f];
bb.height ← Check[r.sSize, sSizeDevice-s];
PrincOpsUtils.BITBLT[bb];
};
bb^ ← nullBitBltTable;
bb.dstBpl ← rast*Basics.bitsPerWord;
bb.src.word ← gray;
bb.src.bit ← 0;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: grayHeight-1];
bb.flags ← [direction: forward, disjoint: TRUE, gray: TRUE,
srcFunc: data.function.srcFunc, dstFunc: data.function.dstFunc];
rectangles[rectangle];
};
MaskRunsGray: PROC[device: Device, runs: PROC[RunProc]] ~ TRUSTED {
data: Data ~ NARROW[device.data];
bbspace: PrincOps.BBTableSpace;
bb: PrincOps.BitBltTablePtr ~ PrincOpsUtils.AlignedBBTable[@bbspace];
gray: LONG POINTER ~ @data.gray;
base: LONG POINTER ~ data.frame.refRep.pointer;
rast: CARDINAL ~ data.frame.refRep.rast;
fSizeDevice: NAT ~ data.frame.fSize;
sSizeDevice: NAT ~ data.frame.sSize;
run: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ TRUSTED {
f: NAT ~ Check[fMin, fSizeDevice];
s: NAT ~ Check[sMin, sSizeDevice];
bb.dst.word ← base+Basics.LongMult[s, rast]+f/Basics.bitsPerWord;
bb.dst.bit ← bb.src.bit ← f MOD Basics.bitsPerWord;
bb.src.word ← gray+(bb.srcDesc.gray.yOffset ← s MOD grayHeight);
bb.width ← Check[fSize, fSizeDevice-f];
bb.height ← Check[1, sSizeDevice-s];
PrincOpsUtils.BITBLT[bb];
};
bb^ ← nullBitBltTable;
bb.dstBpl ← rast*Basics.bitsPerWord;
bb.srcDesc.gray ← [yOffset: 0, widthMinusOne: 0, heightMinusOne: grayHeight-1];
bb.flags ← [direction: forward, disjoint: TRUE, gray: TRUE,
srcFunc: data.function.srcFunc, dstFunc: data.function.dstFunc];
runs[run];
};
END.