ImagerPDDeviceImpl.mesa
Copyright © 1984, 1985 Xerox Corporation. All rights reserved.
Michael Plass, May 12, 1985 3:44:25 pm PDT
Doug Wyatt, March 7, 1985 6:31:05 pm PST
DIRECTORY
ImagerColorDefs,
ImagerDevice,
ImagerColorPrivate,
ImagerPDDevice USING [],
ImagerPixelArray,
ImagerPixelMap USING [Create, DeviceRectangle, PixelMap],
ImagerPixelRow,
ImagerSampler USING [CreateSampler, DotScreen, HalftoneLine, ObtainInterpolatedSamples, Sampler],
ImagerTransformation,
PDFileWriter,
PrincOps USING [zBNDCK, zINC];
ImagerPDDeviceImpl: CEDAR PROGRAM
IMPORTS ImagerPixelMap, ImagerTransformation, PDFileWriter
EXPORTS ImagerPDDevice, ImagerColorDefs
~ BEGIN
Device: TYPE ~ ImagerDevice.Device;
RunProc: TYPE ~ ImagerDevice.RunProc;
HalftoneParameters: TYPE ~ ImagerDevice.HalftoneParameters;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Color: TYPE ~ ImagerColorDefs.Color;
ConstantColor: TYPE ~ ImagerColorDefs.ConstantColor;
SampledColor: TYPE ~ ImagerColorDefs.SampledColor;
ColorOperator: TYPE ~ ImagerColorDefs.ColorOperator;
ColorOperatorRep: TYPE ~ ImagerColorDefs.ColorOperatorRep;
ConstantColorImpl: TYPE ~ REF ConstantColorImplRep;
ConstantColorImplRep: PUBLIC TYPE ~ ImagerColorPrivate.ConstantColorImplRep; -- export to ImagerColorDefs.ConstantColorImplRep
PDState: TYPE ~ PDFileWriter.PDState;
Toner: TYPE ~ PDFileWriter.Toner;
LoadReference: TYPE ~ PDFileWriter.LoadReference;
dummyLoadReference: LoadReference ~ LAST[LoadReference];
GrayTileArray: TYPE ~ ARRAY Toner OF ARRAY [0..64) OF LoadReference;
StippleRep: TYPE ~ RECORD[loadReference: LoadReference, stipple: PixelMap];
Case: TYPE ~ {nil, constant, gray, sampled};
defaultHalftone: HalftoneParameters ← [dotsPerInch: 50, angle: 30, shape: 0.5];
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD [
pdState: PDState ← NIL,
sSize, fSize: CARDINAL,
case: Case ← nil, -- what type of color
grayTileRef: GrayTileArray ← ALL[ALL[dummyLoadReference]],
stipples: LIST OF StippleRep ← NIL,
dot: ImagerPixelMap.PixelMap, -- a halftone dot
halftone: HalftoneParameters ← defaultHalftone, -- halftone screen parameters
pa: ImagerPixelArray.PixelArray ← NIL, -- pixel array from sampled color
transparent: BOOLFALSE, -- transparent flag from sampled color
multiplier: CARDINAL ← 1, lgScale: INTEGER ← 0, -- scale pa samples to match dot samples
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
kjhg: NAT ← 0
];
class: ImagerDevice.Class ~ NEW[ImagerDevice.ClassRep ← [
type: $PD,
SetColor: SetColor,
SetPriority: SetPriority,
SetHalftone: SetHalftone,
MaskRuns: MaskRuns
]];
defaultDot: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[3, [0,0,16,16]];
ImagerSampler.DotScreen[r: 0.5, sSize: 16, fSize: 16, maxPixelValue: 255];
Create: PUBLIC PROC[pdState: PDState, sSize, fSize, sPixelsPerInch, fPixelsPerInch: CARDINAL]
RETURNS[Device] ~ {
data: Data ~ NEW[DataRep ← [pdState: pdState, sSize: sSize, fSize: fSize, dot: defaultDot]];
sInches: REAL ~ REAL[sSize]/REAL[sPixelsPerInch];
fInches: REAL ~ REAL[fSize]/REAL[fPixelsPerInch];
surfaceToDevice: Transformation ~ ImagerTransformation.Scale[1];
xPixelsPerInch: CARDINAL ← sPixelsPerInch;
yPixelsPerInch: CARDINAL ← fPixelsPerInch;
Call the larger dimension y; see the Interpress standard, section 4.3.1.
IF sInches>fInches THEN { -- s (y) is top-to-bottom, f (x) is left-to-right
surfaceToDevice.ApplyPreTranslate[[sSize, 0]];
surfaceToDevice.ApplyPreRotate[90];
xPixelsPerInch ← fPixelsPerInch; yPixelsPerInch ← sPixelsPerInch;
}
ELSE NULL; -- s (x) is left-to-right, f (y) is bottom-to-top
data.paRow ← ImagerPixelRow.CreatePixelRow[sMin: 0, fMin: 0, fSize: fSize];
data.dotRow ← ImagerPixelRow.CreatePixelRow[sMin: 0, fMin: 0, fSize: fSize];
RETURN[NEW[ImagerDevice.DeviceRep ← [class: class,
box: [smin: 0, fmin: 0, smax: sSize, fmax: fSize],
surfaceToDevice: surfaceToDevice,
surfaceUnitsPerInch: [xPixelsPerInch, yPixelsPerInch],
surfaceUnitsPerPixel: 1,
data: data]]];
};
Val: TYPE ~ CARDINAL;
Table: TYPE ~ REF TableRep;
TableRep: TYPE ~ RECORD[SEQUENCE size: NAT OF CARDINAL];
BuildTable: PROC[max: Val, scale: REAL, sWhite, sBlack: REAL,
map: ImagerColorDefs.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 => {
impl: ConstantColorImpl ~ color.impl;
SELECT impl.f FROM
> 0.99 => { -- black
SetInk: PROC[toner: Toner] ~ {data.pdState.SetColorInk[toner]};
data.pdState.DoForEachToner[SetInk];
};
< 0.001 => { -- white
SetClear: PROC[toner: Toner] ~ {data.pdState.SetColorClear[toner]};
data.pdState.DoForEachToner[SetClear];
};
ENDCASE => ERROR; -- not yet implemented
data.case ← constant;
};
color: SampledColor => {
pa: ImagerPixelArray.PixelArray ~ color.pa;
samples: ImagerPixelArrayOps.Row ~ NEW[ImagerPixelArrayOps.RowRep[pa.fSize]];
row: ImagerPixelRow.PixelRow ~ ImagerPixelRow.CreatePixelRow[
sMin: 0, fMin: 0, fSize: pa.fSize];
data.pa ← pa;
data.colorOperator ← color.colorOperator;
data.paToDevice ← ImagerTransformation.Cat[pa.paToClient, color.um, viewToDevice];
data.dotToDevice ← viewToDevice.Copy[];
data.dotToDevice.ApplyPreScale2[device.surfaceUnitsPerInch.Div[data.halftone.dotsPerInch*16]];
data.dotToDevice.ApplyPreRotate[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: ImagerPixelArrayOps.MaxSampleValue[pa, 0],
scale: 255, sWhite: op.sWhite, sBlack: op.sBlack, map: op.map];
FOR s: NAT IN[0..pa.sSize) DO
ImagerPixelArrayOps.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: ImagerPixelArrayOps.MaxSampleValue[pa, 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];
ImagerPixelArrayOps.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;
};
ENDCASE => ERROR; -- unknown color variant
};
SetPriority: PROC[device: Device, priorityImportant: BOOL] ~ {
data: Data ~ NARROW[device.data];
[] ← data.pdState.SetPriorityImportant[priorityImportant];
};
SetHalftone: PROC[device: Device, halftone: HalftoneParameters] ~ {
data: Data ~ NARROW[device.data];
data.halftone ← halftone;
};
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, bounds: ImagerDevice.DeviceBox, runs: PROC[RunProc]] ~ {
data: Data ~ NARROW[device.data];
SELECT data.case FROM
nil => ERROR; -- color not initialized
constant => {
deliverRuns: PROC[captureRun: PROC[sMin, fMin, fSize: CARDINAL]] ~ {
run: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ { captureRun[sMin, fMin, fSize] };
runs[run];
};
PDFileWriter.MaskRunGroup[data.pdState, deliverRuns];
};
sampled => {
lineBuffer: ImagerPixelMap.PixelMap ← ImagerPixelMap.Create[lgBitsPerPixel: 0,
bounds: [sMin: 0, fMin: 0, sSize: 1, fSize: data.fSize]];
linePointer: LONG POINTER ~ lineBuffer.refRep.pointer;
deliverLine: PROC[captureLine: PROC[LONG POINTER]] ~ { captureLine[linePointer] };
paRow: ImagerPixelRow.PixelRow ~ data.paRow;
dotRow: ImagerPixelRow.PixelRow ~ data.dotRow;
source: ImagerPixelMap.PixelMap ~ data.source;
run: PROC[sMin, fMin: INTEGER, fSize: NAT] ~ {
lineBuffer.sOrigin ← paRow.sOrigin ← dotRow.sOrigin ← Check[sMin, data.sSize];
lineBuffer.fOrigin ← paRow.fOrigin ← dotRow.fOrigin ← Check[fMin, data.fSize];
lineBuffer.fSize ← paRow.fSize ← dotRow.fSize ← Check[fSize, data.fSize-fMin];
Might want either to interpolate or to average here. How do we decide?
ImagerSampler.ObtainInterpolatedSamples[sampler: data.paSampler,
pixelRow: paRow, source: source, multiplier: data.multiplier, lgScale: data.lgScale];
ImagerSampler.ObtainInterpolatedSamples[sampler: data.dotSampler,
pixelRow: dotRow, source: data.dot];
ImagerSampler.HalftoneLine[dest: lineBuffer, pixels: paRow, thresholds: dotRow,
invertOutput: FALSE, transparent: data.transparent];
PDFileWriter.ColorSamples[pdState: data.pdState, toner: black,
sMin: sMin, fMin: fMin, sSize: 1, fSize: fSize, deliverProc: deliverLine,
tFlag: IF data.transparent THEN transparent ELSE opaque];
};
PDFileWriter.NewPriorityLevel[pdState: data.pdState, toner: black];
runs[run];
};
ENDCASE => ERROR; -- illegal case
};
END.