DIRECTORY Basics, FunctionCache,
II, IIBackdoor, IIBitmapContext, IIColor, IIColorPrivate, IIDevice, IIFont, IIMaskCache, IIPixel, IIPixelArray, IIPrivate, IIRaster, IIRasterShow, IISample, IIState, IITransformation, Real, Scaled,
SF, PrincOps, PrincOpsUtils;
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: BOOL ← FALSE
];
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:
BOOL ←
TRUE;
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;
};