ImagerRavenBitmapContextImpl.mesa
Copyright Ó 1986, 1987, 1988, 1989, 1992 by Xerox Corporation. All rights reserved.
Michael Plass, September 18, 1989 11:01:05 am PDT
Doug Wyatt, January 19, 1987 9:01:22 pm PST
Maureen Stone, June 21, 1987 8:33:01 pm PDT
Tim Diebert: January 4, 1988 10:34:08 am PST
Last tweaked by Mike Spreitzer on April 17, 1990 11:34:15 am PDT
Willie-s, April 2, 1992 8:11 pm PST
DIRECTORY
ColorRegistry,
FunctionCache,
Imager,
ImagerBackdoor,
ImagerBox,
ImagerBrick,
ImagerRavenBitmapContext,
ImagerColor,
ImagerColorPrivate,
ImagerDevice,
ImagerFont,
ImagerMaskCache,
ImagerPath,
ImagerPixel,
ImagerPixelArray,
ImagerPrivate,
ImagerRaster,
ImagerSample,
ImagerState,
ImagerTransformation,
Real,
SF,
Vector2;
ImagerRavenBitmapContextImpl: CEDAR PROGRAM
IMPORTS FunctionCache, Imager, ImagerBackdoor, ImagerColor, ImagerColorPrivate, ImagerDevice, ImagerMaskCache, ImagerPixel, ImagerPixelArray, ImagerRaster, ImagerSample, ImagerState, ImagerTransformation, Real, SF, Vector2
EXPORTS ImagerRavenBitmapContext, Imager
SHARES ImagerSample
~ BEGIN
Types
Color: TYPE ~ ImagerColor.Color;
ColorOperator: TYPE ~ ImagerColor.ColorOperator;
Context: TYPE ~ Imager.Context;
Device: TYPE ~ ImagerDevice.Device;
Object: TYPE ~ Imager.Object;
PixelMap: TYPE ~ ImagerPixel.PixelMap;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
RasterSampleMap: TYPE ~ ImagerSample.RasterSampleMap;
Rectangle: TYPE ~ ImagerBox.Rectangle;
SampleMap: TYPE ~ ImagerSample.SampleMap;
ScanMode: TYPE ~ ImagerTransformation.ScanMode;
Transformation: TYPE ~ ImagerTransformation.Transformation;
VEC: TYPE ~ Vector2.VEC;
classCode: PUBLIC ATOM ¬ $Bitmap;
Case: TYPE ~ {constant, tile, sampled, sampledBlack};
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD [
bitmap: SampleMap ¬ NIL,
savedBuffer: ImagerSample.SampleMap ¬ NIL,
case: Case ¬ constant,
constant: [0..1] ¬ 1,
function: ImagerSample.Function ¬ [null, null],
maskBitmapFunction: ImagerSample.Function ¬ [null, null],
tile: ImagerBrick.Brick ¬ [0, NIL, 0], -- bitmap for case=tile
separation: ATOM,
brick: ImagerBrick.Brick ¬ [0, NIL, 0], -- thresholds
paToDevice: Transformation,
source: ImagerPixel.PixelMap ¬ NIL,
scratchSampleMapThatIsInUse: SampleMap ¬ NIL, -- SampleMap that should go back to the scratch pool at the next SetColor
scratchStippleMap: SampleMap ¬ NIL, -- scratch storage for stipple case
scratchPixelMap: PixelMap ¬ NIL, -- scratch descriptor for AccessBuffer
scratchBitmapContext: Imager.Context ¬ NIL -- for making fancy tiles
];
Public Procedures
Create: PUBLIC PROC [deviceSpaceSize: SF.Vec, scanMode: ScanMode, surfaceUnitsPerInch: VEC, pixelUnits: BOOL, fontCacheName: ATOM, deviceCode: ATOM ¬ $Raven300] RETURNS [Context] ~ {
data: Data ~ NEW[DataRep ¬ [paToDevice: ImagerTransformation.Scale[1]]];
deviceParm: ImagerDevice.DeviceParm ¬ ImagerDevice.MakeDeviceParm[
class: deviceClass,
sSize: deviceSpaceSize.s,
fSize: deviceSpaceSize.f,
scanMode: scanMode,
surfaceUnitsPerInch: surfaceUnitsPerInch,
surfaceUnitsPerPixel: 1,
fontCache: IF fontCacheName = NIL THEN NIL ELSE ImagerMaskCache.GetNamedCache[fontCacheName]
];
context: Context ~ ImagerRaster.Create[class: contextClass, deviceClass: deviceClass, deviceParm: deviceParm, data: data, pixelUnits: pixelUnits];
For the dashed line hack: Stash the MaskStroke and the MaskVector Procs.
black: ImagerColor.Color ¬ ImagerColor.Find["Xerox/Research/Distinct/Black"];
white: ImagerColor.Color ¬ ImagerColor.Find["Xerox/Research/Distinct/White"];
oldClass: REF ClassRep ¬ context.class;  --crack the opaque type
newClass: REF ClassRep ¬ NEW[ClassRep ¬ oldClass­];
ImagerRasterMaskVector ¬ oldClass.MaskVector;
ImagerRasterMaskStroke ¬ oldClass.MaskStroke;
newClass.MaskStroke ¬ MyMaskStroke;
newClass.MaskVector ¬ MyMaskVector;
Imager.PutProp[context, $DistinctBlack, black];
Imager.PutProp[context, $DistinctWhite, white];
Imager.PutProp[context, $DeviceCode, deviceCode];
Imager.PutProp[context, $DeviceIndex, NEW[NAT ¬ (SELECT deviceCode FROM $Raven300 => 7, $BWPlatemaker => 6, ENDCASE => 7)] ]; --from ColorRegistryImpl
context.class ¬ newClass; --replace the class with our local subclass
data.brick ¬ defaultBrick;
ImagerRaster.SetDeviceClipBox[context, [[0,0], [0,0]]];
RETURN [context];
};
SetBitmap: PUBLIC PROC [context: Context, bitmap: SampleMap] ~ {
data: Data ~ NARROW[ImagerRaster.GetDevice[context].data];
data.bitmap ¬ bitmap;
ImagerRaster.SetDeviceClipBox[context, IF bitmap = NIL THEN [] ELSE ImagerSample.GetBox[bitmap]];
};
GetBitmap: PUBLIC PROC [context: Context] RETURNS [SampleMap] ~ {
data: Data ~ NARROW[ImagerRaster.GetDevice[context].data];
RETURN [data.bitmap];
};
Dashed Line Hack
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep;
PathProc: TYPE = ImagerPath.PathProc;
The great, dashed line hack. In each of these procedures, the call to SetColor will be noticed by the call to Validate in ImagerRaster. The color has to be set at the conclusion of the call in case the next masks are areas.
ImagerRasterMaskVector: PROC [context: Context, p1, p2: VEC] ¬ NIL;
ImagerRasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ¬ NIL;
DoDash: PROC[context: Context] RETURNS[BOOLEAN] = {
WITH ImagerBackdoor.GetColor[context] SELECT FROM
color: ImagerColor.SpecialColor => IF color.type=$Distinct THEN {
nameData: ColorRegistry.Data ¬ NARROW[color.data];
IF nameData.id#$Black AND nameData.id#$White AND nameData.id#$Gray THEN {
widthInPixels: REAL ¬ Vector2.Length[ImagerBackdoor.TransformVec[context: context, v: [ImagerBackdoor.GetReal[context, strokeWidth], 0], from: client, to: device]];
IF widthInPixels < 10 THEN RETURN[TRUE]; --define 10 device pixels as ok for texture
}};
ENDCASE;
RETURN[FALSE];
};
MyMaskVector: PROC [context: Context, p1, p2: VEC] = {
IF DoDash[context] THEN {
black: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctBlack]];
white: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctWhite]];
path: ImagerPath.PathProc = {moveTo[p1]; lineTo[p2]};
color: ImagerColor.SpecialColor ¬ NARROW[ImagerState.StateGetColor[context]];
ImagerState.StateSetColor[context, white];
ImagerRasterMaskVector[context, p1, p2];
ImagerState.StateSetColor[context, black];
IRBMaskDashedStroke[context, color, path, FALSE];
ImagerState.StateSetColor[context, color];
}
ELSE ImagerRasterMaskVector[context, p1, p2];
};
MyMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] = {
IF DoDash[context] THEN {
black: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctBlack]];
white: ImagerColor.Color ¬ NARROW[Imager.GetProp[context, $DistinctWhite]];
color: ImagerColor.SpecialColor ¬ NARROW[ImagerState.StateGetColor[context]];
ImagerState.StateSetColor[context, white];
ImagerRasterMaskStroke[context, path, closed];
ImagerState.StateSetColor[context, black];
IRBMaskDashedStroke[context, color, path, closed];
ImagerState.StateSetColor[context, color];
}
ELSE ImagerRasterMaskStroke[context, path, closed];
};
yellowDash: ARRAY [0..3] OF REAL ¬ [0,2.5,0,2.5];
redDash: ARRAY [0..3] OF REAL ¬ [3,2,3,2];
greenDash: ARRAY [0..3] OF REAL ¬ [1,2,1,2];
blueDash: ARRAY [0..3] OF REAL ¬ [4,2,1,2];
IRBMaskDashedStroke: PROC [context: Context, color: ImagerColor.SpecialColor, path: PathProc, closed: BOOL] = {
nameData: ColorRegistry.Data ¬ NARROW[color.data];
Inner: PROC ~ {
unit: REAL ¬ 8;
array: ARRAY [0..3] OF REAL ¬ SELECT nameData.id FROM
$Red => redDash, $Green => greenDash,
$Blue => blueDash, $Yellow => yellowDash,
ENDCASE => ERROR;
Pattern: PROC[i: NAT] RETURNS[REAL] = {
v: VEC ¬ ImagerBackdoor.TransformVec[context: context, v: [array[i]*unit, 0], from: device, to: client];
RETURN[Vector2.Length[v]]};
Imager.SetStrokeEnd[context, round];
Imager.SetStrokeJoint[context, round];
Imager.MaskDashedStroke[context, path, 4, Pattern, 0, 0];
};
Imager.DoSave[context, Inner];
};
Color Setup
MakeSimpleBrick: PROC [t: ARRAY [0..4) OF PACKED ARRAY [0..4) OF [0..16)] RETURNS [ImagerBrick.Brick] ~ {
b: SampleMap ~ ImagerSample.NewSampleMap[box: [max: [4, 4]], bitsPerSample: 8];
FOR s: NAT IN [0..4) DO
FOR f: NAT IN [0..4) DO
ImagerSample.Put[b, [s, f], t[s][f]];
ENDLOOP;
ENDLOOP;
RETURN [[maxSample: 15, sampleMap: b, phase: 0]]
};
coarseBrick: ImagerBrick.Brick ¬ MakeSimpleBrick[[
[00, 01, 13, 14],
[08, 02, 03, 15],
[09, 10, 04, 05],
[07, 11, 12, 06]
]];
fineBrick: ImagerBrick.Brick ¬ MakeSimpleBrick[[
[00, 08, 02, 10],
[14, 04, 12, 06],
[03, 11, 01, 09],
[13, 07, 15, 05]
]];
defaultBrick: ImagerBrick.Brick ¬ fineBrick;
SetBrick: PUBLIC PROC [context: Context, brick: ImagerBrick.Brick] ~ {
WITH ImagerRaster.GetDevice[context].data SELECT FROM
data: Data => {
data.brick ¬ brick;
Imager.SetColor[context, ImagerBackdoor.GetColor[context]]; -- to re-validate the color
};
ENDCASE => NULL;
};
GetBrick: PUBLIC PROC [context: Context] RETURNS [ImagerBrick.Brick] ~ {
WITH ImagerRaster.GetDevice[context].data SELECT FROM
data: Data => {
RETURN [data.brick]
};
ENDCASE => NULL;
RETURN [[0, NIL, 0]]
};
SetSeparation: PUBLIC PROC [context: Context, separation: ATOM] ~ {
WITH ImagerRaster.GetDevice[context].data SELECT FROM
data: Data => {
data.separation ¬ separation;
Imager.SetColor[context, ImagerBackdoor.GetColor[context]]; -- to re-validate the color
};
ENDCASE => NULL;
};
GetSeparation: PUBLIC PROC [context: Context] RETURNS [ATOM] ~ {
WITH ImagerRaster.GetDevice[context].data SELECT FROM
data: Data => { RETURN [data.separation] };
ENDCASE => NULL;
RETURN [NIL]
};
GetExtendedBox: PROC [brick: ImagerBrick.Brick] RETURNS [SF.Box] ~ {
This computes a box for a bitmap tile that will hit a fast case of BITBLT, if this is possible.
box: SF.Box ¬ ImagerSample.GetBox[brick.sampleMap];
size: SF.Vec ~ ImagerSample.GetSize[brick.sampleMap];
fSizeBox: NAT ~ size.f;
IF brick.phase = 0 AND size.f IN [1..16) AND size.s IN [1..16) AND 16 MOD NAT[size.f] = 0 THEN {
box.max.f ¬ box.min.f+16;
};
RETURN [box]
};
MakeTile: PROC [f: REAL, brick: ImagerBrick.Brick] RETURNS [ImagerBrick.Brick] ~ {
box: SF.Box ~ GetExtendedBox[brick];
sample: CARDINAL ~ Real.Round[f*(brick.maxSample+1)];
b: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[box]];
sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box];
FOR s: INTEGER IN [box.min.s..box.max.s) DO
ImagerSample.GetTileSamples[tile: brick.sampleMap, phase: brick.phase, initIndex: [s, box.min.f], buffer: b];
FOR j: INTEGER IN [0..b.length) DO
b[j] ¬ IF sample > b[j] THEN 1 ELSE 0;
ENDLOOP;
ImagerSample.PutSamples[map: sampleMap, initIndex: [s, box.min.f], buffer: b];
ENDLOOP;
ImagerSample.ReleaseScratchSamples[b];
RETURN [[maxSample: 1, sampleMap: sampleMap, phase: brick.phase]];
};
FractionFromConstantColor: PROC [data: Data, c: ImagerColor.OpConstantColor] RETURNS [REAL] ~ {
f: REAL ~ ImagerColorPrivate.GrayFromColor[c];
Eventually this should do separation, black overprinting, etc.
RETURN [f]
};
SampledColorData: TYPE ~ REF SampledColorDataRep;
SampledColorDataRep: TYPE ~ RECORD [
pa: PixelArray,
colorOperator: ColorOperator,
separation: ATOM,
maxSample: CARDINAL,
filterDiameter: NAT,
source: ImagerPixel.PixelMap
];
me: REF TEXT ~ "Bitmap"; -- a globally unique REF for use as a clientID in the global cache.
BitmapSetColor: PROC [device: Device, color: Color, viewToDevice: Transformation] ~ {
data: Data ~ NARROW [device.data];
realRaster: BOOL ~ WITH data.bitmap SELECT FROM r: RasterSampleMap => TRUE ENDCASE => FALSE;
allowBitmaps, hardSampledCase: BOOL;
{
First clean up any scratch storage from earlier calls.
data.source ¬ NIL;
data.tile.sampleMap ¬ NIL;
IF data.scratchSampleMapThatIsInUse # NIL THEN {
ImagerSample.ReleaseScratchMap[data.scratchSampleMapThatIsInUse];
data.scratchSampleMapThatIsInUse ¬ NIL;
};
};
WITH color SELECT FROM
c: ImagerColor.OpConstantColor => {
f: REAL ~ FractionFromConstantColor[data, c];
data.case ¬ constant;
data.function ¬ [null, null];
SELECT f FROM
< 0.00001 => { data.case ¬ constant; data.constant ¬ 0 };
> 0.99999 => { data.case ¬ constant; data.constant ¬ 1 };
ENDCASE => {
data.tile ¬ MakeTile[f, data.brick];
data.scratchSampleMapThatIsInUse ¬ data.tile.sampleMap;
data.case ¬ tile;
};
};
sampledColor: ImagerColor.SampledColor => {
BitmapSetSampledColor[device: device, sampledColor: sampledColor, viewToDevice: viewToDevice];
};
s: ImagerColor.SampledBlack => {
pa: PixelArray ~ s.pa;
ImagerTransformation.ApplyCat[data.paToDevice, pa.m, s.um, viewToDevice];
data.function ¬ IF s.clear THEN [or, null] ELSE [null, null];
SELECT TRUE FROM
data.paToDevice.form = 3 AND data.paToDevice.integerTrans => {
This bitmap is already in the right orientation for the output raster; just use it!
min: SF.Vec ~ [s: data.paToDevice.tx, f: data.paToDevice.ty];
max: SF.Vec ~ [s: min.s + pa.sSize, f: min.f + pa.fSize];
sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box: [min: min, max: max]];
ImagerPixelArray.Transfer[pa: pa, dst: sampleMap, dstMin: min];
data.case ¬ tile;
data.tile.maxSample ¬ 1;
data.tile.sampleMap ¬ sampleMap;
data.scratchSampleMapThatIsInUse ¬ sampleMap;
data.tile.phase ¬ 0;
};
newStuff AND TryTransformingBitmapIntoTile[data, pa] => {
TryTransformingBitmapIntoTile actually did the setup.
};
ENDCASE => {
This bitmap is not easy to turn into a simple tile, so we resort to the slow way.
sampleMap: SampleMap ~ ImagerSample.ObtainScratchMap[box: [max: [pa.sSize, pa.fSize]]];
ImagerPixelArray.Transfer[pa: pa, dst: sampleMap];
data.case ¬ sampledBlack;
data.source ¬ ImagerPixel.MakePixelMap[sampleMap];
data.scratchSampleMapThatIsInUse ¬ sampleMap;
};
};
s: ImagerColor.SpecialColor => {
SELECT s.type FROM
$Stipple => {
stippleData: ImagerColorPrivate.StippleData ~ NARROW[s.data];
data.function ¬ stippleData.function;
SELECT stippleData.word FROM
00000H => { data.case ¬ constant; data.constant ¬ 0 };
0FFFFH => { data.case ¬ constant; data.constant ¬ 1 };
ENDCASE => {
data.case ¬ tile;
data.tile.sampleMap ¬ data.scratchStippleMap ¬ ImagerSample.TileFromStipple[stipple: stippleData.word, scratch: data.scratchStippleMap];
data.tile.phase ¬ 0;
};
};
$BitmapTile => {
This is for specifying fancier stipples. It would be cleaner if there were a data type defined in an interface somewhere for this, but for now we use a LIST.
list: LIST OF REF ~ NARROW[s.data];
brick: REF ImagerBrick.Brick ~ NARROW[list.first];
rf: REF ImagerSample.Function ~ NARROW[list.rest.first];
data.function ¬ rf­;
data.case ¬ tile;
data.tile.sampleMap ¬ brick.sampleMap;
data.tile.phase ¬ brick.phase;
};
<<$Distinct, $ChipNDale => { -- the distinct named colors
nameData: ColorRegistry.Data ¬ NARROW[s.data];
deviceIndex: NAT ¬ NARROW[Imager.GetProp[context, $DeviceIndex], REF NAT]­;
deviceCode: ATOM ¬ NARROW[Imager.GetProp[context, $DeviceCode]];
value: REF ColorRegistry.DisplayBrick;
ColorRegistryProcs.InitPrinterColors[deviceType: deviceCode, colorType: s.type]; --won't do any work if already initialized
value ¬ NARROW[nameData.deviceValues[deviceIndex]];
data.function ¬ value.function;
data.case ¬ tile;
data.tile.sampleMap ¬ value.tile.sampleMap;
data.tile.phase ¬ value.tile.phase;
};>>
ENDCASE => {
IF s.substitute # NIL
THEN { BitmapSetColor[device, s.substitute, viewToDevice] }
ELSE { ERROR Imager.Error[[code: $unknownSpecialColor, explanation: "Unknown special color has no substitute value"]] };
};
};
ENDCASE => ERROR;
data.maskBitmapFunction ¬ [null, null];
IF data.case = constant THEN SELECT data.function FROM
[null, null] => {
SELECT data.constant FROM
0 => data.maskBitmapFunction ¬ [and, complement];
1 => data.maskBitmapFunction ¬ [or, null];
ENDCASE => NULL;
};
[xor, null] => {
IF data.constant = 1 THEN data.maskBitmapFunction ¬ [xor, null];
};
ENDCASE => NULL;
allowBitmaps ¬ (data.maskBitmapFunction # [null, null]);
hardSampledCase ¬ data.case = sampledBlack OR data.case = sampled;
device.state.allow ¬ [
unorderedBoxes: NOT hardSampledCase,
multipleCoverage: (data.function.dstFunc # xor AND NOT hardSampledCase),
regionFill: data.case = constant AND realRaster,
bitmap: allowBitmaps,
rawBitmaps: allowBitmaps AND realRaster,
runGroupChar: FALSE,
rasterChar: FALSE,
maskThinVector: FALSE
];
};
newStuff: BOOL ¬ TRUE; -- for testing
TryTransformingBitmapIntoTile: PROC [data: Data, pa: PixelArray] RETURNS [BOOL] ~ {
m: Transformation ~ data.paToDevice;
IF m.form#0 THEN {
sRect: Rectangle ~ [0, 0, pa.sSize, pa.fSize];
dRect: Rectangle ~ ImagerTransformation.TransformRectangle[m, sRect];
IF MAX[ABS[dRect.x], ABS[dRect.y], ABS[dRect.w], ABS[dRect.h]] <= 16383 AND ABS[dRect.w*dRect.h] <= 1048576.0 THEN {
sSize: NAT ~ Real.Round[dRect.w];
fSize: NAT ~ Real.Round[dRect.h];
IF dRect.w-sSize+4.0 = 4.0 AND dRect.h-fSize+4.0 = 4.0 THEN {
This bitmap is not quite right for the output raster, but we can make it so.
action: PROC ~ {
pamInverse: Transformation ~ ImagerTransformation.Invert[pa.m];
Imager.TranslateT[data.scratchBitmapContext, [-dRect.x, -dRect.y]];
Imager.ConcatT[data.scratchBitmapContext, m];
Imager.ConcatT[data.scratchBitmapContext, pamInverse];
Imager.MaskPixel[data.scratchBitmapContext, pa];
ImagerTransformation.Destroy[pamInverse];
};
sampleMap: SampleMap ¬ ImagerSample.ObtainScratchMap[box: [max: [sSize, fSize]]];
tileMap: SampleMap ~ ImagerSample.Shift[sampleMap, [s: Real.Floor[dRect.x+0.5], f: Real.Floor[dRect.y+0.5]]];
ImagerSample.Clear[sampleMap];
IF data.scratchBitmapContext = NIL THEN data.scratchBitmapContext ¬ Create[deviceSpaceSize: SF.maxVec, scanMode: [slow: right, fast: up], surfaceUnitsPerInch: [1,1], pixelUnits: TRUE, fontCacheName: $Bitmap];
SetBitmap[data.scratchBitmapContext, sampleMap];
Imager.DoSave[data.scratchBitmapContext, action];
SetBitmap[data.scratchBitmapContext, NIL];
TRUSTED {ImagerSample.ReleaseDescriptor[sampleMap]; sampleMap ¬ NIL};
data.case ¬ tile;
data.tile.maxSample ¬ 1;
data.tile.sampleMap ¬ tileMap;
data.scratchSampleMapThatIsInUse ¬ tileMap;
data.tile.phase ¬ 0;
RETURN [TRUE];
};
};
};
RETURN [FALSE];
};
ComputeSource: PROC [sampledColor: ImagerColor.SampledColor, maxOut: PROC [i: NAT] RETURNS [CARDINAL], filterDiameter: NAT, separation: ATOM] RETURNS [ImagerPixel.PixelMap] ~ {
colorOperator: ColorOperator ~ sampledColor.colorOperator;
pixelMap: ImagerPixel.PixelMap ~ ImagerColorPrivate.Translate[colorOperator: sampledColor.colorOperator, output: [type: $Y, samplesPerPixelOut: 1], pa: sampledColor.pa, maxOut: maxOut];
Do any pre-filtering here. Need a box-filter implementation somewhere.
RETURN [pixelMap]
};
BitmapSetSampledColor: PROC [device: Device, sampledColor: ImagerColor.SampledColor, viewToDevice: Transformation] ~ {
data: Data ~ NARROW [device.data];
pa: PixelArray ~ sampledColor.pa;
largerPaSize: NAT ~ MIN[pa.sSize, pa.fSize] + 1;
cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[];
filterDiameter: NAT ¬ 1; -- for low-pass filtering
scd: SampledColorData ¬ NIL;
compare: FunctionCache.CompareProc ~ {
WITH argument SELECT FROM
scd: SampledColorData => RETURN [scd.pa = pa AND scd.colorOperator = sampledColor.colorOperator AND scd.separation = data.separation AND scd.maxSample = data.brick.maxSample+1 AND scd.filterDiameter = filterDiameter];
ENDCASE => RETURN [FALSE]
};
maxOut: PROC [i: NAT] RETURNS [CARDINAL] ~ { RETURN [data.brick.maxSample+1] };
ImagerTransformation.ApplyCat[data.paToDevice, pa.m, sampledColor.um, viewToDevice];
IF interpolate THEN filterDiameter ¬ MIN[MAX[Real.Round[1.0/MAX[ImagerTransformation.SingularValues[data.paToDevice].y, 1.0/largerPaSize]], 1], 255];
data.case ¬ sampled;
data.function ¬ [null, null];
IF pa.immutable
THEN {
scd ¬ NARROW[FunctionCache.Lookup[cache, compare, me].value];
IF scd = NIL THEN {
pixelMap: ImagerPixel.PixelMap ~ ComputeSource[sampledColor, maxOut, filterDiameter, data.separation];
scd ¬ NEW [SampledColorDataRep ¬ [pa: pa, colorOperator: sampledColor.colorOperator, separation: data.separation, maxSample: data.brick.maxSample+1, filterDiameter: filterDiameter, source: pixelMap]];
FunctionCache.Insert[x: cache, argument: scd, value: scd, size: ImagerSample.WordsForMap[size: ImagerSample.GetSize[pixelMap[0]], bitsPerSample: ImagerSample.GetBitsPerSample[pixelMap[0]]], clientID: me];
};
data.source ¬ scd.source;
}
ELSE {
data.source ¬ ComputeSource[sampledColor, maxOut, filterDiameter, data.separation];
If ComputeSource got a pixel map that had sample maps out of the scratch pool, we could do data.scratchSampleMapThatIsInUse ← data.source[0]; here.
};
};
Masking Procs
interpolate: BOOL ¬ FALSE;
BitmapMaskBoxes: PROC [device: Device, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
data: Data ~ NARROW[device.data];
SELECT data.case FROM
constant => {
ImagerSample.FillBoxes[map: data.bitmap, boxes: boxes, value: data.constant, function: data.function];
};
tile => {
ImagerSample.TileBoxes[map: data.bitmap, boxes: boxes, tile: data.tile.sampleMap, phase: data.tile.phase, function: data.function];
};
sampled => TRUSTED {
buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[bounds]];
Action: SAFE PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ TRUSTED {
buffer.length ¬ pixels.length;
ImagerSample.GetTileSamples[tile: data.brick.sampleMap, initIndex: min, buffer: buffer, phase: data.brick.phase];
ImagerSample.Halftone[map: data.bitmap, min: min, sampleBuffer: pixels[0], thresholdBuffer: buffer, function: data.function];
};
ImagerPixel.Resample[self: data.source, m: data.paToDevice, interpolate: interpolate, boxes: boxes, bounds: bounds, action: Action];
ImagerSample.ReleaseScratchSamples[buffer];
};
sampledBlack => {
Action: PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ {
ImagerSample.PutSamples[map: data.bitmap, initIndex: min, buffer: pixels[0], start: 0, count: pixels.length, function: data.function];
};
ImagerPixel.Resample[self: data.source, m: data.paToDevice, interpolate: FALSE, boxes: boxes, bounds: bounds, action: Action];
};
ENDCASE => ERROR;
};
BitmapMaskRegion: PROC [device: Device, bounds: SF.Box, edgeGenerator: PROC [ImagerSample.EdgeAction]] ~ {
data: Data ~ NARROW[device.data];
IF data.case # constant THEN ERROR;
ImagerSample.RegionFill[dst: NARROW[data.bitmap], edgeGenerator: edgeGenerator, value: data.constant, function: data.function];
};
BitmapMaskBitmap: PROC [device: Device, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
data: Data ~ NARROW[device.data];
ImagerSample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes, function: data.maskBitmapFunction];
};
BitmapMaskRawBitmaps: PROC [device: Device, list: LIST OF ImagerSample.RawDescriptor] ~ {
data: Data ~ NARROW[device.data];
ImagerSample.RawListTransfer[dst: NARROW[data.bitmap], src: list, function: data.maskBitmapFunction];
};
BitmapDrawBitmap: PROC [device: Device, bitmap: SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
data: Data ~ NARROW[device.data];
ImagerSample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes];
};
BitmapMoveBox: PROC [device: Device, dstMin, srcMin, size: SF.Vec] ~ {
data: Data ~ NARROW[device.data];
ImagerSample.Move[map: data.bitmap, dstMin: dstMin, srcMin: srcMin, size: size];
};
BitmapSwitchBuffer: PROC [device: Device, bounds: SF.Box, copy: BOOL, alt: BOOL] ~ {
data: Data ~ NARROW[device.data];
oldBuffer: SampleMap ~ data.bitmap;
newBuffer: SampleMap ~ IF alt THEN ImagerSample.ObtainScratchMap[box: bounds, bitsPerSample: 1] ELSE data.savedBuffer;
IF copy THEN ImagerSample.Transfer[dst: newBuffer, src: oldBuffer];
data.bitmap ¬ newBuffer;
IF alt
THEN { data.savedBuffer ¬ oldBuffer }
ELSE { data.savedBuffer ¬ NIL; ImagerSample.ReleaseScratchMap[oldBuffer] };
};
Objects
objectCache: FunctionCache.Cache ¬ FunctionCache.Create[maxEntries: 30, maxTotalSize: 250000];
ObjectCacheData: TYPE ~ REF ObjectCacheDataRep;
ObjectCacheDataRep: TYPE ~ RECORD [
object: Object,
clientToDevice: Transformation,
v: SELECT tag: * FROM
notCached => [],
bitmap => [bitmap: SampleMap],
boxes => [boxes: ManhattanPolygon],
ENDCASE
];
<<maxBitsForCachedObject: REAL ~ 2000000;
BitmapDrawObject: PUBLIC PROC [context: Context, object: Object, position: VEC, interactive: BOOL] ~ {
contextBox: ImagerBox.Box ~ ImagerBox.BoxFromRectangle[ImagerBackdoor.GetBounds[context]];
objectRect: Rectangle ~ [x: position.x+object.clip.x, y: position.y+object.clip.y, w: object.clip.w, h: object.clip.h];
intersection: ImagerBox.Box ~ ImagerBox.IntersectBox[contextBox, ImagerBox.BoxFromRectangle[objectRect]];
IF intersection.xmax=intersection.xmin THEN RETURN; -- not visible
IF interactive THEN {
clientToDevice: Transformation ~ ImagerBackdoor.GetTransformation[context: context, from: client, to: device];
Compare: PROC [argument: REF] RETURNS [BOOL] ~ {
key: ObjectCacheData ~ NARROW[argument];
IF object # key.object THEN RETURN [FALSE];
RETURN [ImagerTransformation.CloseToTranslation[key.clientToDevice, clientToDevice]];
};
bounds: Rectangle ~ ImagerTransformation.TransformRectangle[clientToDevice, objectRect];
IF ABS[bounds.w*bounds.h] <= maxBitsForCachedObject THEN {
objectCacheData: ObjectCacheData ¬ NARROW [FunctionCache.Lookup[x: objectCache, compare: Compare, clientID: NIL].value];
IF objectCacheData = NIL THEN {
bitmap: SampleMap ¬ NIL;
size: INT ¬ 0;
ok: BOOL ¬ TRUE;
Op: PROC [c: Context] ~ { Imager.ClipRectangle[c, object.clip]; object.draw[object, c] };
bitmap ¬ ImagerMaskCapture.CaptureBitmap[Op, clientToDevice, TRUE !
ImagerMaskCapture.Cant => {ok ¬ FALSE; CONTINUE}];
IF ok
THEN {
o: REF ObjectCacheDataRep.bitmap ¬ NEW[ObjectCacheDataRep.bitmap];
objectCacheData ¬ o;
o.bitmap ¬ bitmap;
size ¬ ImagerSample.WordsForMap[ImagerSample.GetSize[bitmap]] + 10;
}
ELSE {
objectCacheData ¬ NEW[ObjectCacheDataRep.notCached];
size ¬ SIZE[ObjectCacheDataRep.notCached];
};
objectCacheData.object ¬ object;
objectCacheData.clientToDevice ¬ ImagerTransformation.Copy[clientToDevice];
FunctionCache.Insert[x: objectCache, argument: objectCacheData, value: objectCacheData, size: size, clientID: NIL];
};
WITH objectCacheData SELECT FROM
ocd: REF ObjectCacheDataRep.bitmap => {
t1: VEC ~ ImagerTransformation.TransformVec[clientToDevice, position];
t2: VEC ~ [clientToDevice.c, clientToDevice.f];
t3: VEC ~ [ocd.clientToDevice.c, ocd.clientToDevice.f];
t: VEC ¬ Vector2.Add[t1, Vector2.Sub[t2, t3]];
save: PROC ~ {
ImagerBackdoor.SetT[context, identity];
Imager.MaskBitmap[context: context, bitmap: ocd.bitmap, scanMode: [slow: right, fast: up], position: t];
};
Imager.DoSave[context, save];
ImagerTransformation.Destroy[clientToDevice];
RETURN;
};
ENDCASE => NULL;
};
ImagerTransformation.Destroy[clientToDevice];
};
ImagerPrivate.DefaultDrawObject[context, object, position, interactive];
};
identity: Transformation ~ ImagerTransformation.Scale[1];
>>
BitmapGetBufferColorOperator: PROC [context: Context] RETURNS [ColorOperator] ~ {
RETURN [ImagerColor.NewColorOperatorGrayLinear[sWhite: 0, sBlack: 1]]
};
BitmapAccessBuffer: PROC [device: Device, box: SF.Box, action: PROC [pixelMap: PixelMap]] ~ {
data: Data ~ NARROW[device.data];
bitmap: SampleMap ~ ImagerSample.Clip[data.bitmap, box];
pixelMap: PixelMap ~ IF data.scratchPixelMap = NIL THEN NEW[ImagerPixel.PixelMapRep[1]] ELSE data.scratchPixelMap;
data.scratchPixelMap ¬ NIL;
pixelMap.box ¬ ImagerSample.GetBox[bitmap];
pixelMap[0] ¬ bitmap;
action[pixelMap];
pixelMap[0] ¬ NIL;
TRUSTED {ImagerSample.ReleaseDescriptor[bitmap]};
data.scratchPixelMap ¬ pixelMap;
};
Classes
deviceClass: ImagerDevice.DeviceClass ~ NEW[ImagerDevice.DeviceClassRep ¬ [
SetColor: BitmapSetColor,
SetPriority: NIL,
MaskBoxes: BitmapMaskBoxes,
MaskRegion: BitmapMaskRegion,
MaskBitmap: BitmapMaskBitmap,
MaskRawBitmaps: BitmapMaskRawBitmaps,
DrawBitmap: BitmapDrawBitmap,
MaskChar: NIL,
MoveBox: BitmapMoveBox,
SwitchBuffer: BitmapSwitchBuffer,
AccessBuffer: BitmapAccessBuffer
]];
contextClass: ImagerPrivate.Class ~ CreateClass[];
CreateClass: PROC RETURNS [class: ImagerPrivate.Class] ~ INLINE {
class ¬ ImagerRaster.CreateClass[type: classCode];
<<class.DrawObject ¬ BitmapDrawObject;>>
class.GetBufferColorOperator ¬ BitmapGetBufferColorOperator;
};
END.