ImagerFunctionDeviceImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, October 7, 1985 11:07:12 am PDT
Last edited by: Mik Lamming - November 19, 1985 5:30:36 pm PST
DIRECTORY Basics, FunctionCache, Imager, ImagerCache, ImagerColorDefs, ImagerColorPrivate, ImagerDevice, ImagerFunctionDevice, ImagerMask, ImagerOps, ImagerPixelArrayDefs, ImagerPixelMap, ImagerRaster, ImagerSample, ImagerTransformation, PixelMapOps, PrincOps, Real, Vector2;
ImagerFunctionDeviceImpl: CEDAR PROGRAM
IMPORTS Basics, FunctionCache, Imager, ImagerCache, ImagerMask, ImagerOps, ImagerPixelMap, ImagerRaster, ImagerSample, ImagerTransformation, PixelMapOps, Real
EXPORTS ImagerFunctionDevice, ImagerColorDefs
~ BEGIN OPEN ImagerFunctionDevice;
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;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
Sample: TYPE ~ ImagerSample.Sample;
Sampler: TYPE ~ ImagerSample.Sampler;
SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
UnsafeSamples: TYPE ~ ImagerSample.UnsafeSamples;
DeviceBox: TYPE ~ ImagerDevice.DeviceBox;
Rectangle: TYPE ~ ImagerTransformation.Rectangle;
Context: TYPE ~ Imager.Context;
Device: TYPE ~ ImagerDevice.Device;
RunProc: TYPE ~ ImagerDevice.RunProc;
BoxProc: TYPE ~ ImagerDevice.BoxProc;
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: $FunctionDevice,
SetColor: FunctionDeviceSetColor,
SetPriority: FunctionDeviceSetPriority,
SetHalftone: FunctionDeviceSetHalftone,
MaskRuns: FunctionDeviceMaskRuns,
MaskBoxes: FunctionDeviceMaskBoxes,
MaskBits: FunctionDeviceMaskBits,
MoveBoxes: FunctionDeviceMoveBoxes
]];
Lg: PROC [n: NAT] RETURNS [NAT] ~ {
RETURN[SELECT n FROM 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4, ENDCASE => ERROR]
};
fontCacheID: ATOM ~ $FunctionDevice;
fontCacheSize: NAT ← 4000;
fontRastWeight: REAL ← 0.0;
Case: TYPE ~ {nil, constant, sampled};
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD[
clientFunc: ClientFunc ← NIL,  -- function called to apply color to pms
pms: LIST OF ImagerPixelMap.PixelMap, -- device image buffers
samplesPerPixel: NAT ← , -- device needs this many 16 bit samples to represent constantColor
maxValue: Sample,
sOrigin, fOrigin: INTEGER,
deviceToPixel: Transformation,
case: Case ← nil,  -- what type of color: sampled or constant
constantColor: SampleBuffer ← NIL, -- samplesPerPixel 8(room for 16) bit numbers
sampledColor: LIST OF ImagerPixelMap.PixelMap ← NIL, -- samplesPerPixel 8 bit pixelmaps
paToDevice: Transformation ← NIL, -- transformation from pa coords to pixelMap coords
sampBuffer: ImagerSample.SampleBuffer ←, -- scan line buffer for samples from sampledColor
lineBuffer: ImagerSample.SampleBuffer ←, -- for ops that cannot go directly to frame
sampler: ImagerSample.Sampler -- sampler information
];
SampledColorData: TYPE ~ REF SampledColorDataRep;
SampledColorDataRep: TYPE ~ RECORD [
colorPms: LIST OF PixelMap
];
me: REF TEXT ~ "FuncDev"; -- a globally unique REF for use as a clientID in the global cache.
LikeScreen: PUBLIC PROC [sSize: NAT] RETURNS [Transformation] ~ {
m: Transformation ~ ImagerTransformation.Translate[[sSize, 0]];
m.ApplyPreRotate[90];
RETURN [m];
};
Create: PUBLIC PROC [
samplesPerPixel:NAT,  -- device color has this many 16 bit samples / pixel
devicePms: LIST OF PixelMap, -- where this device stores the image
deviceToPixel: Transformation ← NIL, -- transformation from device space
initialScale: REAL ← 1.0,  -- 1 unit = 1 pixel
clientFunc: ClientFunc ← NIL-- devicePms ← clientFunc(devicePms, color)
]
RETURNS [context: Context] ~ {
device: ImagerDevice.Device ~ DeviceFromPixelMap[samplesPerPixel, devicePms, deviceToPixel, clientFunc];
fontCache: ImagerCache.Ref ~ ImagerCache.GetNamedCache[atom: fontCacheID, createSize: fontCacheSize];
context ← ImagerRaster.Create[device: device, pixelUnits: TRUE, fontCache: fontCache, rastWeight: fontRastWeight];
Imager.ScaleT[context, initialScale];
};
CreateFromDevice: PUBLIC PROC [
device: ImagerDevice.Device,
initialScale: REAL ← 1.0  -- 1 unit = 1 pixel
]
RETURNS [context: Context] ~ {
fontCache: ImagerCache.Ref ~ ImagerCache.GetNamedCache[atom: fontCacheID, createSize: fontCacheSize];
context ← ImagerRaster.Create[device: device, pixelUnits: TRUE, fontCache: fontCache, rastWeight: fontRastWeight];
Imager.ScaleT[context, initialScale];
};
DeviceBoxFromRectangle: PROC [r: Rectangle] RETURNS [DeviceBox] ~ {
smin: CARDINAL ← Real.RoundC[r.x];
fmin: CARDINAL ← Real.RoundC[r.y];
smax: CARDINAL ← Real.RoundC[r.x+r.w];
fmax: CARDINAL ← Real.RoundC[r.y+r.h];
IF smin > smax THEN {t: CARDINAL ← smin; smin ← smax; smax ← t};
IF fmin > fmax THEN {t: CARDINAL ← fmin; fmin ← fmax; fmax ← t};
RETURN [[smin: smin, fmin: fmin, smax: smax, fmax: fmax]]
};
DeviceFromPixelMap: PUBLIC PROC [
samplesPerPixel:NAT,
pms: LIST OF PixelMap,
deviceToPixel: Transformation,
clientFunc: ClientFunc
] RETURNS [ImagerDevice.Device] ~ {
w: ImagerPixelMap.DeviceRectangle ← [0, 0, 0, 0];
normPms: LIST OF PixelMap ← NIL; -- where normalised pixelmaps are stored
sampler: ImagerSample.Sampler ~ NEW[ImagerSample.SamplerRep ← []];
maxValue: CARDINAL ← 0;
sampBufferSize: NAT ← 0;
data: Data;
nPms: NAT ← 0;
IF pms#NIL THEN {
NormalisePms: PROC [lpms:LIST OF PixelMap, s,f: NAT] RETURNS [rpms: LIST OF PixelMap, howMany:NAT ← 0] ~ {
IF lpms=NIL THEN RETURN [NIL, 0]
ELSE {
pm: PixelMap ← lpms.first;
IF pm.sSize#s OR pm.fSize#f THEN ERROR;
pm ← pm.Clip[w].ShiftMap[-w.sMin, -w.fMin];
[rpms, howMany] ← NormalisePms[lpms.rest, s, f];
RETURN [CONS[pm, rpms], howMany+1];
};
};
firstPm: PixelMap ← pms.first;
w ← ImagerPixelMap.BoundedWindow[firstPm];
maxValue ← Basics.BITSHIFT[1, Basics.BITSHIFT[1, firstPm.refRep.lgBitsPerPixel]]-1;
[normPms, nPms] ← NormalisePms[pms, pms.first.sSize, pms.first.fSize];
sampBufferSize ← firstPm.fSize;
};
data ← NEW[DataRep ← [
clientFunc: clientFunc,
pms: normPms,
samplesPerPixel: samplesPerPixel,
maxValue: maxValue,
sOrigin: w.sMin,
fOrigin: w.fMin,
deviceToPixel: ImagerTransformation.Scale[1],
sampBuffer: ImagerSample.NewBuffer[samplesPerPixel, sampBufferSize+2],
lineBuffer: ImagerSample.NewBuffer[nPms, sampBufferSize+2],
sampler: sampler
]];
RETURN [NEW[ImagerDevice.DeviceRep ← [
class: class,
box: [smin: 0, fmin: 0, smax: w.sSize, fmax: w.fSize],
surfaceToDevice: deviceToPixel,
surfaceUnitsPerInch: [72, 72],
surfaceUnitsPerPixel: 1,
data: data]]]
};
FunctionDeviceSetPriority: PROC [device: Device, priorityImportant: BOOL] ~ {
};
FunctionDeviceSetHalftone: PROC [
device: Device,
halftone: ImagerDevice.HalftoneParameters] ~ {
};
PixelFromIntensity: PROC[i: REAL, maxValue: CARDINAL] RETURNS[CARDINAL] ~ {
IF i<=0.0 THEN RETURN[0];
IF i>=1.0 THEN RETURN[maxValue];
RETURN[Real.RoundC[i*maxValue]];
};
FunctionDeviceSetColor: PROC [device: Device, color: Color, viewToDevice: Transformation] ~ {
data: Data ~ NARROW[device.data];
data.sampledColor ← NIL; -- drop old pixelmaps on the floor
WITH color SELECT FROM
color: ConstantColor => {
data.case ← constant;
data.constantColor ← NARROW[color.data];
};
color: SampledColor => {
pa: PixelArray ~ color.pa;
cache: FunctionCache.Cache ← FunctionCache.GlobalCache[];
compare: FunctionCache.CompareProc ~ {RETURN [argument=pa]};
colorPms: LIST OF PixelMap ← NARROW[FunctionCache.Lookup[cache, compare, me].value];
IF color = NIL THEN ERROR;
IF pa.samplesPerPixel#data.samplesPerPixel THEN ERROR;
data.case ← sampled;
data.paToDevice ← ImagerTransformation.Cat[pa.m, color.um, viewToDevice];
IF colorPms=NIL THEN {
words: LONG CARDINAL ← 0;
FOR sampleIx: NAT DECREASING IN [0..pa.samplesPerPixel) DO
colorPms ← CONS[ImagerOps.PixelMapFromPixelArray[pa, sampleIx], colorPms];
words ← words + colorPms.first.refRep.words;
ENDLOOP;
data.sampledColor ← colorPms;
FunctionCache.Insert[cache, pa, colorPms, words, me];
}
ELSE
data.sampledColor ← colorPms;
};
ENDCASE => ERROR; -- unknown color variant
};
MaskRunsInternal: PROC[data: Data, bounds: DeviceBox, runs: PROC[RunProc]] ~ {
sbs: SampleBuffer ~ data.sampBuffer;
sampler: ImagerSample.Sampler ~ data.sampler;
lbs: SampleBuffer ← data.lineBuffer;
pms: LIST OF PixelMap ← data.pms;
Run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ {
localPms: LIST OF PixelMap;
IF data.case = sampled THEN {
colorPms: LIST OF PixelMap ← data.sampledColor;
FOR sampleIx: NAT IN [0..data.samplesPerPixel) DO
t: PixelMap ← colorPms.first;
sampler.base ← t.refRep.pointer;
sampler.wordsPerLine ← t.refRep.rast;
sampler.bitsPerSample ← Basics.BITSHIFT[1, t.refRep.lgBitsPerPixel];
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: sMin, f: fMin];
ImagerSample.GetPointSamples[sampler: sampler, s: sMin, f: fMin, buffer: sbs, bi: sampleIx, bj: fMin, count: fSize];
colorPms ← colorPms.rest;
ENDLOOP;
}
ELSE { -- constant
FOR sampleIx: NAT IN [0..data.samplesPerPixel) DO
PixelMapOps.FillSamples[buffer: sbs, i: sampleIx, j: fMin, count: fSize, sample: ImagerSample.GetSample[data.constantColor, sampleIx, 0]];
ENDLOOP;
};
localPms ← pms;
FOR sampleIx:NAT ← 0, sampleIx+1 WHILE localPms#NIL DO -- copy destination pixels into a lineBuffer
PixelMapOps.GetF[pixelMap: localPms.first, s: sMin, f: fMin, buffer: lbs, bi: sampleIx, bj: fMin, count: fSize];
localPms ← localPms.rest;
ENDLOOP;
IF data.clientFunc#NIL THEN -- apply the client function
data.clientFunc[fMin, fSize, sbs, lbs];
localPms pms;
FOR sampleIx:NAT ← 0, sampleIx+1 WHILE localPms#NIL DO
PixelMapOps.PutF[pixelMap: localPms.first, s: sMin, f: fMin, buffer: lbs, bi: sampleIx, bj: fMin, count: fSize];
localPms ← localPms.rest;
ENDLOOP;
};
runs[Run];
};
FunctionDeviceMaskRuns: PROC[device: Device, bounds: DeviceBox, runs: PROC[RunProc]] ~ {
data: Data ~ NARROW[device.data];
functionDeviceMaskRunsAction: PROC ~ { MaskRunsInternal[data, bounds, runs] };
MaskRunsInternal[data, bounds, runs];
};
FunctionDeviceMaskBoxes: PROC[device: Device, bounds: DeviceBox, boxes: PROC[BoxProc]] ~ {
data: Data ~ NARROW[device.data];
maskBoxesInternal: PROC ~ {
functionDeviceBox: PROC[box: DeviceBox] ~ {
runs: PROC[run: RunProc] ~ { ImagerMask.RunsFromBox[box: box, run: run] };
MaskRunsInternal[data: data, bounds: box, runs: runs];
};
boxes[functionDeviceBox];
};
maskBoxesInternal[];
};
FunctionDeviceMaskBits: PROC[device: Device, srcBase: LONG POINTER, srcWordsPerLine: NAT,
ts, tf: INTEGER, boxes: PROC[BoxProc]] ~ {
data: Data ~ NARROW[device.data];
functionDeviceMaskBitsBox: PROC[box: DeviceBox] ~ {
functionDeviceMaskBitsBoxAction: 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];
};
MaskRunsInternal[data: data, bounds: box, runs: runs];
};
functionDeviceMaskBitsBoxAction[];
};
boxes[functionDeviceMaskBitsBox];
};
FunctionDeviceMoveBoxes: PROC [device: Device, ts, tf: INTEGER, boxes: PROC[BoxProc]] ~ {
ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented"]];
};
MakeConstantColor: PUBLIC PROC [sample: SampleBuffer]
RETURNS [color: ImagerColorDefs.ConstantColor] ~ {
color ← NEW[ImagerColorDefs.ColorRep[constant]];
color.data ← sample;
};
MakePMS: PUBLIC PROC [n, sSize, fSize:NAT] RETURNS [LIST OF PixelMap] ~ {
IF n=0 THEN RETURN [NIL]
ELSE {
pm: PixelMap ← ImagerPixelMap.Create[3, [0,0, sSize, fSize]];
pm.Fill[[0,0,sSize, fSize], 255, [null, null]];
RETURN [CONS[pm, MakePMS[n-1, sSize, fSize]]]
}
};
END.