~
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]]]
}
};