IIBitmapContextImpl.mesa
Copyright Ó 1986 by Xerox Corporation. All rights reserved.
Michael Plass, December 10, 1986 4:48:47 pm PST
DIRECTORY Basics, FunctionCache, II, IIBackdoor, IIBitmapContext, IIColor, IIColorPrivate, IIDevice, IIFont, IIMaskCache, IIPixel, IIPixelArray, IIPrivate, IIRaster, IIRasterShow, IISample, IIState, IITransformation, Real, Scaled, SF, PrincOps, PrincOpsUtils;
IIBitmapContextImpl: CEDAR PROGRAM
IMPORTS Basics, FunctionCache, II, IIBackdoor, IIColorPrivate, IIMaskCache, IIPixel, IIPixelArray, IIPrivate, IIRaster, IIRasterShow, IISample, IITransformation, Real, Scaled, SF, PrincOpsUtils
EXPORTS IIBitmapContext, II
SHARES IISample
~ BEGIN
Types
Context: TYPE ~ II.Context;
ScaledVec: TYPE ~ IIMaskCache.ScaledVec;
ShowData: TYPE ~ IIRasterShow.ShowData;
classCode: PUBLIC ATOM ← $Bitmap;
fontCacheMaxSize: NAT ← 4000;
Case: TYPE ~ {constant, tile, sampled, sampledBlack};
Data: TYPE ~ REF DataRep;
DataRep: TYPE ~ RECORD [
bitmap: IISample.SampleMap ← NIL,
case: Case ← constant,
constant: [0..1] ← 1,
function: IISample.Function ← [null, null],
maskBitmapFunction: IISample.Function ← [null, null],
tile: IIBitmapContext.Brick ← [0, NIL, 0], -- bitmap for case=tile
separation: ATOM,
brick: IIBitmapContext.Brick ← [0, NIL, 0], -- thresholds
scratchTileMap: IISample.SampleMap ← NIL, -- scratch storage for constant colors
scratchStippleMap: IISample.SampleMap ← NIL, -- scratch storage for stipples
paToDevice: IITransformation.Transformation,
source: IIPixel.PixelMap ← NIL,
sharedSource: BOOLFALSE
];
Public Procedures
Create: PUBLIC PROC [deviceSpaceSize: SF.Vec, scanMode: II.ScanMode, surfaceUnitsPerInch: II.VEC, pixelUnits: BOOL, fontCacheName: ATOM, fontTuner: IIDevice.FontTuner] RETURNS [Context] ~ {
data: Data ~ NEW[DataRep ← [paToDevice: IITransformation.Scale[1]]];
deviceParm: IIDevice.DeviceParm ← NEW[IIDevice.DeviceParmRep ← [
class: deviceClass,
sSize: deviceSpaceSize.s,
fSize: deviceSpaceSize.f,
scanMode: scanMode,
surfaceUnitsPerInch: surfaceUnitsPerInch,
surfaceUnitsPerPixel: 1,
fontTuner: fontTuner,
fontCache: IF fontCacheName = NIL THEN NIL ELSE IIMaskCache.GetNamedCache[fontCacheName, fontCacheMaxSize],
rastWeight: 2.0
]];
context: Context ~ IIRaster.Create[class: contextClass, deviceParm: deviceParm, data: data, pixelUnits: pixelUnits];
data.brick ← DefaultBrick[];
IIRaster.SetDeviceClipBox[context, [[0,0], [0,0]]];
RETURN [context];
};
SetBitmap: PUBLIC PROC [context: Context, bitmap: II.SampleMap] ~ {
data: Data ~ NARROW[context.data];
data.bitmap ← bitmap;
IIRaster.SetDeviceClipBox[context, IISample.GetBox[bitmap]];
};
GetBitmap: PUBLIC PROC [context: Context] RETURNS [II.SampleMap] ~ {
data: Data ~ NARROW[context.data];
RETURN [data.bitmap];
};
Color Setup
DefaultBrick: PROC RETURNS [IIBitmapContext.Brick] ~ {
b: II.SampleMap ~ IISample.NewSampleMap[box: [max: [4, 4]], bitsPerSample: 8];
b.Put[[0, 0], 00]; b.Put[[0, 1], 01]; b.Put[[0, 2], 13]; b.Put[[0, 3], 14];
b.Put[[1, 0], 08]; b.Put[[1, 1], 02]; b.Put[[1, 2], 03]; b.Put[[1, 3], 15];
b.Put[[2, 0], 09]; b.Put[[2, 1], 10]; b.Put[[2, 2], 04]; b.Put[[2, 3], 05];
b.Put[[3, 0], 07]; b.Put[[3, 1], 11]; b.Put[[3, 2], 12]; b.Put[[3, 3], 06];
RETURN [[maxSample: 15, sampleMap: b, phase: 0]]
};
SetHalftoneProperties: PUBLIC PROC [context: II.Context, separation: ATOM, brick: IIBitmapContext.Brick] ~ {
WITH context.data SELECT FROM
data: Data => {
data.brick ← brick;
data.separation ← separation;
II.SetColor[context, IIBackdoor.GetColor[context]]; -- to re-validate the color
};
ENDCASE => NULL;
};
MakeTile: PROC [f: REAL, brick: IIBitmapContext.Brick, data: Data] RETURNS [IIBitmapContext.Brick] ~ {
box: SF.Box ~ IISample.GetBox[brick.sampleMap];
sample: CARDINAL ~ Real.Round[f*(brick.maxSample+1)];
b: IISample.SampleBuffer ~ IISample.ObtainScratchSamples[SF.SizeF[box]];
sampleMap: IISample.SampleMap ~
IF data.scratchTileMap # NIL AND IISample.GetBox[data.scratchTileMap] = box
THEN data.scratchTileMap
ELSE (data.scratchTileMap ← IISample.NewSampleMap[box]);
FOR s: INTEGER IN [box.min.s..box.max.s) DO
IISample.GetSamples[map: brick.sampleMap, 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;
IISample.PutSamples[map: sampleMap, initIndex: [s, box.min.f], buffer: b];
ENDLOOP;
IISample.ReleaseScratchSamples[b];
RETURN [[maxSample: 1, sampleMap: sampleMap, phase: brick.phase]];
};
FractionFromConstantColor: PROC [data: Data, c: IIColor.ConstantColor] RETURNS [REAL] ~ {
f: REAL ~ IIColorPrivate.GrayFromColor[c];
Eventually this should do separation, black overprinting, etc.
RETURN [f]
};
SampledColorData: TYPE ~ REF SampledColorDataRep;
SampledColorDataRep: TYPE ~ RECORD [
pa: IIPixelArray.PixelArray,
colorOperator: IIColor.ColorOperator,
separation: ATOM,
maxSample: CARDINAL,
filterDiameter: NAT,
source: IIPixel.PixelMap
];
me: REF TEXT ~ "Bitmap"; -- a globally unique REF for use as a clientID in the global cache.
BitmapSetColor: PROC [context: Context, color: II.Color, viewToDevice: II.Transformation] RETURNS [IIDevice.AllowedMasks] ~ {
data: Data ~ NARROW [context.data];
allowBitmaps: BOOL;
IF NOT data.sharedSource THEN data.source ← NIL;
WITH color SELECT FROM
c: IIColor.ConstantColor => {
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];
data.case ← tile;
};
};
sampledColor: IIColor.SampledColor => {
BitmapSetSampledColor[context: context, sampledColor: sampledColor, viewToDevice: viewToDevice];
};
s: IIColor.SampledBlack => {
pa: IIPixelArray.PixelArray ~ s.pa;
IITransformation.ApplyCat[data.paToDevice, pa.m, s.um, viewToDevice];
data.function ← IF s.clear THEN [or, null] ELSE [null, null];
IF data.paToDevice.form = 3 AND data.paToDevice.integerTrans AND pa.sSize*pa.fSize <= 16384
THEN {
It would be nice to capture other easy transformations here, as long as the size of the resulting bitmap is reasonable.
min: SF.Vec ~ [s: data.paToDevice.tx, f: data.paToDevice.ty];
max: SF.Vec ~ [s: min.s + pa.sSize, f: min.f + pa.fSize];
data.case ← tile;
data.tile.maxSample ← 1;
data.tile.sampleMap ← IISample.NewSampleMap[box: [min: min, max: max]];
data.tile.phase ← 0;
IIPixelArray.Transfer[self: pa, dst: data.tile.sampleMap, dstMin: min];
}
ELSE {
One: IIPixel.PixelProc ~ {RETURN [1]};
data.case ← sampledBlack;
data.source ← IIPixel.NewPixelMap[samplesPerPixel: 1, maxSample: One, box: [max: [pa.sSize, pa.fSize]]];
IIPixelArray.Transfer[self: pa, dst: data.source[0]];
};
};
s: IIColor.SpecialColor => {
IF s.type = $Stipple
THEN {
stippleData: IIColorPrivate.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 ← IISample.TileFromStipple[stipple: stippleData.word, scratch: data.scratchStippleMap];
data.tile.phase ← 0;
};
}
ELSE IF s.substitute # NIL
THEN { [] ← BitmapSetColor[context, s.substitute, viewToDevice] }
ELSE { ERROR II.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]);
RETURN [[unorderedBoxes: data.function.dstFunc # xor, regionFill: data.case = constant, bitmap: allowBitmaps, rawBitmaps: allowBitmaps, runGroupChar: FALSE, rasterChar: FALSE]];
};
ComputeSource: PROC [sampledColor: IIColor.SampledColor, colorOperator: IIColor.ColorOperator, maxOut: PROC [i: NAT] RETURNS [CARDINAL], filterDiameter: NAT, separation: ATOM] RETURNS [IIPixel.PixelMap] ~ {
colorOutput: IIColorPrivate.ColorOutput ~ NEW[IIColorPrivate.ColorOutputRep ← [type: $Y, samplesPerPixelOut: 1, impl: NIL]];
pixelMap: IIPixel.PixelMap ~ IIColorPrivate.Translate[self: sampledColor.colorOperator, output: colorOutput, pa: sampledColor.pa, maxOut: maxOut];
Do any pre-filtering here. Need a box-filter implementation somewhere.
RETURN [pixelMap]
};
BitmapSetSampledColor: PROC [context: Context, sampledColor: IIColor.SampledColor, viewToDevice: II.Transformation] ~ {
data: Data ~ NARROW [context.data];
pa: IIPixelArray.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] };
IITransformation.ApplyCat[data.paToDevice, pa.m, sampledColor.um, viewToDevice];
filterDiameter ← MIN[MAX[Real.Round[1.0/MAX[IITransformation.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 {
colorOutput: IIColorPrivate.ColorOutput ~ NEW[IIColorPrivate.ColorOutputRep ← [type: $Y, samplesPerPixelOut: 1, impl: NIL]];
pixelMap: IIPixel.PixelMap ~ ComputeSource[sampledColor, sampledColor.colorOperator, 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: IISample.WordsForMap[size: IISample.GetSize[pixelMap[0]], bitsPerSample: IISample.GetBitsPerSample[pixelMap[0]]], clientID: me];
};
data.source ← scd.source;
data.sharedSource ← TRUE;
}
ELSE {
data.source ← ComputeSource[sampledColor, sampledColor.colorOperator, maxOut, filterDiameter, data.separation];
data.sharedSource ← FALSE;
};
};
Masking Procs
interpolate: BOOLTRUE;
BitmapMaskBoxes: PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
data: Data ~ NARROW[context.data];
SELECT data.case FROM
constant => {
IISample.FillBoxes[map: data.bitmap, boxes: boxes, value: data.constant, function: data.function];
};
tile => {
IISample.TileBoxes[map: data.bitmap, boxes: boxes, tile: data.tile.sampleMap, phase: data.tile.phase, function: data.function];
};
sampled => TRUSTED {
buffer: IISample.SampleBuffer ~ IISample.ObtainScratchSamples[SF.SizeF[bounds]];
Action: SAFE PROC [pixels: IIPixel.PixelBuffer, min: SF.Vec] ~ TRUSTED {
buffer.length ← pixels.length;
IISample.GetTileSamples[tile: data.brick.sampleMap, initIndex: min, buffer: buffer, phase: data.brick.phase];
IISample.Halftone[map: data.bitmap, min: min, sampleBuffer: pixels[0], thresholdBuffer: buffer, function: data.function];
};
IIPixel.Resample[self: data.source, m: data.paToDevice, interpolate: interpolate, boxes: boxes, bounds: bounds, action: Action];
IISample.ReleaseScratchSamples[buffer];
};
sampledBlack => {
Action: PROC [pixels: IIPixel.PixelBuffer, min: SF.Vec] ~ {
IISample.PutSamples[map: data.bitmap, initIndex: min, buffer: pixels[0], start: 0, count: pixels.length, function: data.function];
};
IIPixel.Resample[self: data.source, m: data.paToDevice, interpolate: FALSE, boxes: boxes, bounds: bounds, action: Action];
};
ENDCASE => ERROR;
};
BitmapMaskRegion: PROC [context: Context, bounds: SF.Box, edgeGenerator: PROC [IISample.EdgeAction]] ~ {
data: Data ~ NARROW[context.data];
IF data.case # constant THEN ERROR;
IISample.RegionFill[dst: data.bitmap, edgeGenerator: edgeGenerator, value: data.constant, function: data.function];
};
BitmapMaskBitmap: PROC [context: Context, bitmap: IISample.SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
data: Data ~ NARROW[context.data];
IISample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes, function: data.maskBitmapFunction];
};
rawArraySize: NAT ~ IISample.rawArraySize;
RawArray: TYPE ~ IISample.RawArray;
BitmapMaskRawBitmaps: PROC [context: Context, n: [0..rawArraySize], a: POINTER TO RawArray] ~ {
data: Data ~ NARROW[context.data];
IISample.MultipleTransfer[dst: data.bitmap, n: n, a: a, function: data.maskBitmapFunction];
};
BitmapDrawBitmap: PROC [context: Context, bitmap: IISample.SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ {
data: Data ~ NARROW[context.data];
IISample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes];
};
BitmapMoveBox: PROC [context: Context, dstMin, srcMin, size: SF.Vec] ~ {
data: Data ~ NARROW[context.data];
IISample.Move[map: data.bitmap, dstMin: dstMin, srcMin: srcMin, size: size];
};
BitmapDoBuffered: PROC [context: Context, bounds: SF.Box, copy: BOOL, action: PROC] ~ {
data: Data ~ NARROW[context.data];
buffer: IISample.SampleMap ~ IISample.ObtainScratchMap[box: bounds, bitsPerSample: 1];
bitmap: IISample.SampleMap ~ data.bitmap;
IF copy THEN IISample.Transfer[dst: buffer, src: bitmap];
data.bitmap ← buffer;
action[ ! UNWIND => data.bitmap ← bitmap ];
IISample.Transfer[dst: bitmap, src: buffer];
data.bitmap ← bitmap;
IISample.ReleaseScratchMap[buffer];
};
Fast Show
State: TYPE ~ IIState.State;
StateRep: PUBLIC TYPE ~ IIState.StateRep; -- export to II.StateRep
XChar: TYPE ~ IIFont.XChar;
RasterMask: TYPE ~ REF IIMaskCache.CharMaskRep.raster;
RawDescriptor: TYPE ~ IISample.RawDescriptor;
bitsPerWord: NAT ~ Basics.bitsPerWord;
worryNat: NAT ~ LAST[NAT]/2-1;
worryReal: REAL ← worryNat;
nullBitBltTable: PrincOps.BitBltTable ~ [
dst: [word: NIL, bit: 0], dstBpl: 0,
src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]],
width: 0, height: 0, flags: []
];
CommonChar: TYPE ~ IIRasterShow.CommonChar;
Ord: PROC [char: XChar] RETURNS [CARDINAL] ~ --INLINE-- {RETURN [LOOPHOLE[char]]};
BPLFromFSize: PROC [i: INTEGER] RETURNS [INTEGER] ~ --INLINE-- {
bplMask: CARDINAL ~ LAST[CARDINAL]-(bitsPerWord-1);
RETURN [LOOPHOLE[Basics.BITAND[LOOPHOLE[i+(bitsPerWord-1)], bplMask]]]
};
Trust: PROC [i: INTEGER] RETURNS [CARDINAL] ~ --INLINE-- { RETURN [LOOPHOLE[i]] };
BitmapFastShow: PUBLIC PROC [context: Context, string: IIFont.XStringProc, xrel: BOOL] ~ {
state: State ~ context.state;
data: Data ~ NARROW[context.data];
showData: ShowData ~ IIRasterShow.GetShowData[context];
easyMetrics: BOOL ~ (state.np.amplifySpace = 1.0 AND state.np.correctPass = 0);
box: SF.Box ← [];
TryScaled: PROC RETURNS [BOOL] ~ --INLINE-- {
IIRasterShow.TryFastState[state, showData];
RETURN [showData.valid];
};
IF NOT xrel AND showData.allow.rawBitmaps AND state.np.correctPass = 0 AND state.np.noImage = 0 AND showData.fontCache # NIL AND TryScaled[] AND data.bitmap.bitsPerLine MOD bitsPerWord = 0 THEN TRUSTED {
charArray: REF ARRAY CommonChar OF RasterMask ← IIRasterShow.GetCharArray[showData.fontAtom];
bbTableSpace: PrincOps.BBTableSpace;
bbPtr: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
dstWpl: CARDINAL ~ data.bitmap.bitsPerLine / bitsPerWord;
dstBase: LONG POINTER ~ data.bitmap.base.word;
dstBox: SF.Box ~ data.bitmap.box;
FastCharAction: SAFE PROC [char: XChar] ~ TRUSTED {
m: RasterMask ← NIL;
charBox: SF.Box;
QuickLook: SAFE PROC RETURNS [ok: BOOL] ~ CHECKED --INLINE-- {
IF Ord[char] IN CommonChar THEN {
m ← charArray[Ord[char]];
IF m = NIL THEN { m ← IIRasterShow.CommonCharLookup[showData, charArray, char]; IF m = NIL THEN RETURN [FALSE] };
charBox ← SF.Displace[m.box, [showData.cp.s.integerPart, showData.cp.f.integerPart]];
RETURN [TRUE];
};
RETURN [FALSE]
};
IF showData.valid AND QuickLook[] THEN {
SELECT TRUE FROM
SF.Inside[charBox, box] OR SF.Inside[charBox, box ← IIRaster.GetContainingBox[context, charBox.min]] => {
fSize: INTEGER ~ charBox.max.f-charBox.min.f;
IF fSize > 0 THEN TRUSTED {
bb: PrincOps.BBptr ~ bbPtr;
f0: CARDINAL ~ Trust[charBox.min.f-dstBox.min.f];
bb.dst.word ← dstBase + Basics.LongMult[Trust[charBox.min.s-dstBox.min.s], dstWpl] + f0/bitsPerWord;
bb.dst.bit ← f0 MOD bitsPerWord;
bb.src.word ← @(m[0]);
bb.srcDesc ← [srcBpl[BPLFromFSize[fSize]]];
bb.width ← Trust[fSize];
bb.height ← Trust[charBox.max.s-charBox.min.s];
PrincOpsUtils.BITBLT[bb];
};
IF easyMetrics THEN {
Can't overflow here because we were not clipped.
showData.cp.s ← showData.cp.s.PLUS[m.escapement.s];
showData.cp.f ← showData.cp.f.PLUS[m.escapement.f];
RETURN
};
};
SF.Empty[SF.Intersect[charBox, showData.clipBounds]] => { NULL };
ENDCASE => {
IIRasterShow.FlushState[state, showData];
IF NOT IIRasterShow.MaskCharMask[context, m] THEN ERROR; -- should never fail
showData.valid ← TRUE; -- Everything is really still valid
};
IIRasterShow.DoCharMetrics[state, showData, m];
RETURN;
};
IIRasterShow.FlushState[state, showData];
IIRasterShow.CachedShowChar[context, char];
IIRasterShow.TryFastState[state, showData];
};
IF NOT SF.Inside[box, data.bitmap.box] THEN ERROR;
bbPtr^ ← nullBitBltTable;
bbPtr.dstBpl ← data.bitmap.bitsPerLine;
bbPtr.flags ← [disjoint: TRUE, disjointItems: TRUE, gray: FALSE];
bbPtr.flags.srcFunc ← data.maskBitmapFunction.srcFunc;
bbPtr.flags.dstFunc ← data.maskBitmapFunction.dstFunc;
string[FastCharAction];
IIRasterShow.FlushState[state, showData];
}
ELSE IIRasterShow.FastShow[context, string, xrel];
};
Classes
deviceClass: IIDevice.DeviceClass ~ NEW[IIDevice.DeviceClassRep ← [
SetColor: BitmapSetColor,
SetPriority: NIL,
MaskBoxes: BitmapMaskBoxes,
MaskRegion: BitmapMaskRegion,
MaskBitmap: BitmapMaskBitmap,
MaskRawBitmaps: BitmapMaskRawBitmaps,
DrawBitmap: BitmapDrawBitmap,
MaskChar: NIL,
MoveBox: BitmapMoveBox,
DoBuffered: BitmapDoBuffered
]];
contextClass: IIPrivate.Class ~ CreateClass[];
CreateClass: PROC RETURNS [class: IIPrivate.Class] ~ --INLINE-- {
class ← IIRaster.CreateClass[type: classCode, deviceClass: deviceClass];
class.Show ← BitmapFastShow;
class.DrawObject ← IIPrivate.RasterDrawObject;
};
END.