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