ImagerOpsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, August 1, 1985 3:21:06 pm PDT
DIRECTORY Imager, ImagerOps, ImagerPixelMap, ImagerPixelArray, ImagerTransformation, Terminal, ImagerBitmapDevicePrivate, ImagerDitheredDevicePrivate, ImagerColor24DevicePrivate, ImagerRasterPrivate, ImagerDevice, ImagerState, Vector2, ImagerManhattan, ImagerManhattanExtras, ImagerBackdoor, ImagerPixelArrayDefs, ImagerSample, UnsafeStorage, ImagerPixelArrayPrivate, PrincOps, Basics, PrincOpsUtils;
ImagerOpsImpl: CEDAR MONITOR
IMPORTS Imager, ImagerPixelMap, ImagerTransformation, ImagerRasterPrivate, ImagerManhattan, ImagerManhattanExtras, ImagerBackdoor, ImagerSample, UnsafeStorage, Basics, PrincOpsUtils, ImagerPixelArray, Terminal
EXPORTS ImagerOps, Imager, ImagerPixelArrayDefs
~ BEGIN
Context: TYPE ~ Imager.Context;
VEC: TYPE ~ Vector2.VEC;
Color: TYPE ~ Imager.Color;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Rectangle: TYPE ~ ImagerTransformation.Rectangle;
FrameBuffer: TYPE ~ Terminal.FrameBuffer;
Virtual: TYPE ~ Terminal.Virtual;
Kind: TYPE ~ ATOM;
State: TYPE ~ ImagerState.State;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep
PixelArrayFromPixelMaps: PUBLIC PROC [pms: LIST OF PixelMap, um: Transformation] RETURNS [PixelArray] ~ {
w: DeviceRectangle ~ ImagerPixelMap.BoundedWindow[pms.first];
samplesPerPixel: INT ← 0;
FOR p: LIST OF PixelMap ← pms.rest, p.rest UNTIL p=NIL DO
IF w # ImagerPixelMap.BoundedWindow[p.first] THEN ERROR Imager.Error[[$specification, "Pixel map windows do not match."]];
ENDLOOP;
FOR p: LIST OF PixelMap ← pms, p.rest UNTIL p=NIL DO
samplesPerPixel ← samplesPerPixel + 1;
p.first ← ImagerPixelMap.ShiftMap[p.first, -w.sMin, -w.fMin];
ENDLOOP;
RETURN [NEW[ImagerPixelArrayDefs.PixelArrayRep ← [
samplesPerPixel: samplesPerPixel,
sSize: w.sSize,
fSize: w.fSize,
m: ImagerTransformation.PostTranslate[um, [w.sMin, w.fMin]],
impl: NIL,
class: pixelArrayClass,
data: pms
]]];
};
PixelArrayClass: TYPE ~ ImagerPixelArrayPrivate.PixelArrayClass;
PixelArrayClassRep: PUBLIC TYPE ~ ImagerPixelArrayPrivate.PixelArrayClassRep;
pixelArrayClass: PixelArrayClass ~ NEW[PixelArrayClassRep ← [
type: $PixelMaps,
MaxSampleValue: PmsMaxSampleValue,
UnsafeGetSamples: PmsUnsafeGetSamples,
UnsafeGetBits: PmsUnsafeGetBits
]];
GetPM: PROC [pa: PixelArray, i: NAT] RETURNS [PixelMap] ~ {
pms: LIST OF PixelMap ← NARROW[pa.data];
THROUGH [0..Basics.BoundsCheck[i, pa.samplesPerPixel]) DO pms ← pms.rest ENDLOOP;
RETURN [pms.first];
};
PmsMaxSampleValue: PROC [pa: PixelArray, i: NAT] RETURNS [ImagerPixelArray.Sample] ~ {
pm: PixelMap ~ GetPM[pa, i];
RETURN [Basics.BITSHIFT[1, Basics.BITSHIFT[1, pm.refRep.lgBitsPerPixel]]-1];
};
PmsUnsafeGetSamples: UNSAFE PROC [pa: PixelArray, i: NAT, s, f: INT,
samples: ImagerSample.UnsafeSamples, count: NAT] ~ UNCHECKED {
pm: PixelMap ~ GetPM[pa, i];
ImagerSample.UnsafeGetF[samples: samples, count: count, base: pm.refRep.pointer, wordsPerLine: pm.refRep.rast, bitsPerSample: Basics.BITSHIFT[1, pm.refRep.lgBitsPerPixel], s: s-pm.sOrigin, f: f-pm.fOrigin];
};
bitsPerWord: NAT ~ Basics.bitsPerWord;
PmsUnsafeGetBits: UNSAFE PROC [pa: PixelArray, i: NAT ← 0, s, f: INT,
dst: PrincOps.BitAddress, dstBpl: INTEGER, width, height: CARDINAL,
srcFunc: PrincOps.SrcFunc ← null, dstFunc: PrincOps.DstFunc ← null] ~ UNCHECKED {
source: PixelMap ~ GetPM[pa, i];
sStartSource: NAT ~ s-source.sOrigin;
fMinSource: NAT ~ f-source.fOrigin;
bbTableSpace: PrincOps.BBTableSpace;
bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace];
[] ← Basics.BoundsCheck[width, source.fSize+1];
[] ← Basics.BoundsCheck[height, source.sSize+1];
[] ← Basics.BoundsCheck[source.refRep.lgBitsPerPixel, 1];
bb^ ← [
dstBpl: dstBpl,
srcDesc: [srcBpl[source.refRep.rast*bitsPerWord]],
height: height,
width: width,
flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: srcFunc, dstFunc: dstFunc],
dst: dst,
src: [word: (source.refRep.pointer + Basics.LongMult[sStartSource, source.refRep.rast] + (fMinSource/bitsPerWord)), bit: fMinSource MOD bitsPerWord]
];
PrincOpsUtils.BITBLT[bb];
};
PixelMapFromPixelArray: PUBLIC PROC [pa: PixelArray, i: NAT] RETURNS [PixelMap] ~ {
IF pa.class = pixelArrayClass THEN RETURN [GetPM[pa, i]]
ELSE {
max: INT ~ ImagerPixelArray.MaxSampleValue[pa, i];
bitsPerSample: NAT ~ Lg[max+1];
pm: PixelMap ~ ImagerPixelMap.Create[Lg[bitsPerSample], [0, 0, pa.sSize, pa.fSize]];
ImagerPixelMap.Clear[pm];
IF bitsPerSample = 1 THEN TRUSTED {
ImagerPixelArray.UnsafeGetBits[pa: pa, i: i, s: 0, f: 0, dst: [word: pm.refRep.pointer, bit: 0], dstBpl: pm.refRep.rast*bitsPerWord, width: pm.fSize, height: pm.sSize, srcFunc: null, dstFunc: null]
}
ELSE TRUSTED {
tZone: UNCOUNTED ZONE ~ UnsafeStorage.GetTransientPageUZone[];
buf: ImagerSample.UnsafeSamples ← tZone.NEW[ImagerSample.RawSamples[pm.fSize]];
base: LONG POINTER ~ pm.refRep.pointer;
wpl: NAT ~ pm.refRep.rast;
FOR line: NAT IN [0..pm.sSize) DO
ImagerPixelArray.UnsafeGetSamples[pa: pa, i: i, s: line, f: 0, samples: buf, count: pm.fSize];
ImagerSample.UnsafePutF[samples: buf, count: pm.fSize, s: line, f: 0, base: base, wordsPerLine: wpl, bitsPerSample: bitsPerSample, srcFunc: null, dstFunc: null];
ENDLOOP;
tZone.FREE[@buf];
};
RETURN [pm];
};
};
KindOf: PUBLIC PROC [context: Context] RETURNS [Kind] ~ {
WITH context.data SELECT FROM
rasterData: ImagerRasterPrivate.Data => {
device: ImagerDevice.Device ~ rasterData.device;
WITH device.data SELECT FROM
bitmap: ImagerBitmapDevicePrivate.Data => RETURN [$Bitmap];
dithered: ImagerDitheredDevicePrivate.Data => RETURN [$Dithered];
fullColor: ImagerColor24DevicePrivate.Data => RETURN [$Color24];
ENDCASE => NULL;
};
ENDCASE => NULL;
RETURN [NIL]
};
SurfaceToDevice: PUBLIC PROC [context: Context] RETURNS [Transformation] ~ {
WITH context.data SELECT FROM
rasterData: ImagerRasterPrivate.Data => {
device: ImagerDevice.Device ~ rasterData.device;
RETURN [device.surfaceToDevice];
};
ENDCASE => NULL;
RETURN [NIL];
};
knownKinds: LIST OF Kind ← LIST [$Bitmap, $Dithered, $Color24];
KnownKinds: PUBLIC PROC RETURNS [LIST OF Kind] ~ {RETURN [knownKinds]};
TerminalFromContext: PUBLIC PROC [context: Context] RETURNS [vt: Virtual, color: BOOL] ~ {
WITH context.data SELECT FROM
rasterData: ImagerRasterPrivate.Data => {
device: ImagerDevice.Device ~ rasterData.device;
WITH device.data SELECT FROM
bitmap: ImagerBitmapDevicePrivate.Data => {
vt: Virtual ~ Terminal.Current[];
fb: Terminal.FrameBuffer ~ Terminal.GetBWFrameBuffer[vt];
IF fb = NIL OR fb.base # bitmap.frame.pointer THEN RETURN [NIL, FALSE]
ELSE RETURN [vt, FALSE]
};
dithered: ImagerDitheredDevicePrivate.Data => RETURN [dithered.terminal, TRUE];
fullColor: ImagerColor24DevicePrivate.Data => RETURN [fullColor.terminal, TRUE];
ENDCASE => NULL;
};
ENDCASE => NULL;
RETURN [NIL, FALSE];
};
Lg: PROC [n: INT] RETURNS [lg: NAT ← 0] ~ {
nn: LONG CARDINAL ~ n;
k: LONG CARDINAL ← 1;
UNTIL k=0 OR k>= nn DO
lg ← lg + 1;
k ← k + k;
ENDLOOP;
};
PixelMapFromFrameBuffer: PUBLIC PROC [frameBuffer: FrameBuffer] RETURNS [PixelMap] ~ {
refRep: REF ImagerPixelMap.PixelMapRep ~ NEW[ImagerPixelMap.PixelMapRep ← [
ref: frameBuffer,
pointer: frameBuffer.base,
words: INT[frameBuffer.wordsPerLine]*INT[frameBuffer.height],
lgBitsPerPixel: Lg[frameBuffer.bitsPerPixel],
rast: frameBuffer.wordsPerLine,
lines: frameBuffer.height
]];
pixelMap: ImagerPixelMap.PixelMap ~ [
sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0,
sSize: frameBuffer.height, fSize: frameBuffer.width, refRep: refRep
];
RETURN[pixelMap];
};
PixelMapsFromContext: PUBLIC PROC [context: Context] RETURNS [LIST OF PixelMap] ~ {
WITH context.data SELECT FROM
rasterData: ImagerRasterPrivate.Data => {
device: ImagerDevice.Device ~ rasterData.device;
WITH device.data SELECT FROM
bitmap: ImagerBitmapDevicePrivate.Data => RETURN [LIST[[sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmap.sSizeFrame, fSize: bitmap.fSizeFrame, refRep: bitmap.frame]]];
dithered: ImagerDitheredDevicePrivate.Data => RETURN [LIST[dithered.frame]];
fullColor: ImagerColor24DevicePrivate.Data => RETURN [LIST[PixelMapFromFrameBuffer[fullColor.frame[A]], PixelMapFromFrameBuffer[fullColor.frame[B]]]];
ENDCASE => NULL;
};
ENDCASE => NULL;
RETURN [NIL];
};
Validate: PROC [context: Context, data: ImagerRasterPrivate.Data, needs: ImagerRasterPrivate.Flags] ~ INLINE {
state: ImagerState.State ~ context.state;
IF state.changed#ImagerState.notChanged THEN ImagerRasterPrivate.NoteStateChanges[data, state];
IF ImagerRasterPrivate.AndFlags[data.valid, needs]#needs THEN ImagerRasterPrivate.ValidateIfNeeded[data, state, needs];
};
DoSaveClipRectangleI: PROC [context: Context, action: PROC, x, y, w, h: INTEGER] ~ {
clippedAction: PROC ~ {
Imager.ClipRectangleI[context, x, y, w, h];
action[];
};
WITH context.data SELECT FROM
rasterData: ImagerRasterPrivate.Data => {
clientToDevice: Transformation;
Validate[context, rasterData, [clientToDevice: TRUE]];
clientToDevice ← rasterData.clientToDevice;
IF clientToDevice.form=9 AND clientToDevice.integerTrans AND NOT rasterData.specialClipPresent THEN {
smin: INTINT[clientToDevice.tx]-y; smax: INT ← smin-h;
fmin: INTINT[clientToDevice.ty]+x; fmax: INT ← fmin+w;
IF smax<smin THEN { temp: INT ~ smin; smin ← smax; smax ← temp };
IF fmax<fmin THEN { temp: INT ~ fmin; fmin ← fmax; fmax ← temp };
rasterData.specialClipRect ← [sMin: smin, fMin: fmin, sSize: smax-smin, fSize: fmax-fmin];
rasterData.specialClipPresent ← TRUE;
rasterData.valid.clientClipper ← FALSE;
Imager.DoSave[context, action ! UNWIND => rasterData.specialClipPresent ← rasterData.valid.clientClipper ← FALSE];
rasterData.specialClipPresent ← rasterData.valid.clientClipper ← FALSE;
RETURN;
};
};
ENDCASE => NULL;
Imager.DoSave[context, clippedAction];
};
DoWithBuffer: PUBLIC PROC [context: Context, action: PROC, x, y, w, h: INTEGER, backgroundColor: Color ← NIL] ~ {
paintBack: PROC ~ {
Imager.SetColor[context, backgroundColor];
Imager.MaskRectangleI[context, x, y, w, h];
};
clippedAction: PROC ~ {
IF bufferingOn THEN WITH context.data SELECT FROM
rasterData: ImagerRasterPrivate.Data => {
Validate[context, rasterData, [clientClipper: TRUE]];
IF rasterData.clientClipBoxOnly THEN {
device: ImagerDevice.Device ~ rasterData.device;
WITH device.data SELECT FROM
bitmapData: ImagerBitmapDevicePrivate.Data => {
IF DoWithBitmapBuffer[context, action, backgroundColor, rasterData, bitmapData] THEN RETURN;
};
ditheredData: ImagerDitheredDevicePrivate.Data => {
IF DoWithDitheredBuffer[context, action, backgroundColor, rasterData, ditheredData] THEN RETURN;
};
fullColorData: ImagerColor24DevicePrivate.Data => {
};
ENDCASE => NULL;
};
};
ENDCASE => NULL;
IF backgroundColor # NIL THEN Imager.DoSave[context, paintBack];
action[];
};
DoSaveClipRectangleI[context, clippedAction, x, y, w, h];
};
DeviceBoxFromDeviceRectangle: PROC [r: ImagerPixelMap.DeviceRectangle] RETURNS [ImagerDevice.DeviceBox] ~ {
RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]];
};
DoWithShift: PROC [rasterData: ImagerRasterPrivate.Data, action: PROC, sDelta, fDelta: INTEGER] ~ {
Performs action with the surface to device post-translated by [sDelta, fDelta].
delta: VEC ~ [sDelta, fDelta];
minusDelta: VEC ~ [-delta.x, -delta.y];
true: BOOL[TRUE..TRUE] ~ rasterData.valid.clientClipper;
savedClipMask: ImagerManhattan.Polygon ~ rasterData.viewClipMask;
savedDeviceBox: ImagerDevice.DeviceBox ~ rasterData.device.box;
Restore: PROC ~ {
minusDelta: VEC ~ [-delta.x, -delta.y];
ImagerTransformation.ApplyPostTranslate[rasterData.viewToDevice, minusDelta];
ImagerTransformation.ApplyPostTranslate[rasterData.device.surfaceToDevice, minusDelta];
rasterData.device.box ← savedDeviceBox;
ImagerManhattan.Destroy[rasterData.viewClipMask];
rasterData.viewClipMask ← savedClipMask;
rasterData.viewClipBox ← ImagerManhattan.BoundingBox[rasterData.viewClipMask];
rasterData.specialClipRect.sMin ← rasterData.specialClipRect.sMin-sDelta;
rasterData.specialClipRect.fMin ← rasterData.specialClipRect.fMin-fDelta;
rasterData.valid.clientClipper ← FALSE;
rasterData.valid.clientToDevice ← FALSE;
};
ImagerTransformation.ApplyPostTranslate[rasterData.viewToDevice, delta];
ImagerTransformation.ApplyPostTranslate[rasterData.device.surfaceToDevice, delta];
rasterData.viewClipMask ← ImagerManhattanExtras.DestructiveClip[
ImagerManhattan.Copy[rasterData.viewClipMask], rasterData.specialClipRect
];
ImagerManhattan.Shift[rasterData.viewClipMask, sDelta, fDelta];
rasterData.viewClipBox ← ImagerManhattan.BoundingBox[rasterData.viewClipMask];
rasterData.device.box ← DeviceBoxFromDeviceRectangle[rasterData.viewClipBox];
rasterData.specialClipRect.sMin ← rasterData.specialClipRect.sMin+sDelta;
rasterData.specialClipRect.fMin ← rasterData.specialClipRect.fMin+fDelta;
rasterData.valid.clientClipper ← FALSE;
rasterData.valid.clientToDevice ← FALSE;
action[ ! UNWIND => Restore[]];
Restore[];
};
nScratch: NAT ~ 2;
scratch: ARRAY [0..nScratch) OF REF ImagerPixelMap.PixelMapRep ← ALL[NIL];
ObtainScratch: ENTRY PROC RETURNS [r: REF ImagerPixelMap.PixelMapRep] ~ {
FOR i: NAT IN [0..nScratch) DO
IF (r ← scratch[i]) # NIL THEN {scratch[i] ← NIL; RETURN};
ENDLOOP;
RETURN [NIL];
};
ReleaseScratch: ENTRY PROC [r: REF ImagerPixelMap.PixelMapRep] ~ {
FOR i: NAT IN [0..nScratch) DO
IF scratch[i] = NIL THEN {scratch[i] ← r; RETURN}
ENDLOOP;
};
DoWithBitmapBuffer: PROC [context: Context, action: PROC, backgroundColor: Color, rasterData: ImagerRasterPrivate.Data, bitmapData: ImagerBitmapDevicePrivate.Data] RETURNS [ok: BOOLTRUE] ~ {
tightBox: ImagerDevice.DeviceBox ~ rasterData.clientClipBox;
box: ImagerDevice.DeviceBox ~ [
smin: tightBox.smin/4*4,
fmin: tightBox.fmin/4*4,
smax: tightBox.smax,
fmax: tightBox.fmax];
display: PixelMap ~ [sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmapData.sSizeFrame, fSize: bitmapData.fSizeFrame, refRep: bitmapData.frame];
buffer: PixelMap ~ ImagerPixelMap.Reshape[ObtainScratch[], 0, [box.smin, box.fmin, box.smax-box.smin, box.fmax-box.fmin]].Clip[[tightBox.smin, tightBox.fmin, tightBox.smax-tightBox.smin, tightBox.fmax-tightBox.fmin]];
frameAction: PROC ~ {
bitmapData.sSizeFrame ← buffer.sMin+buffer.sSize;
bitmapData.fSizeFrame ← buffer.fMin+buffer.fSize;
bitmapData.frame ← buffer.refRep;
IF backgroundColor # NIL THEN {
state: ImagerState.State ~ context.state;
oldColor: Color ~ state.color;
Imager.SetColor[context, backgroundColor];
Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]];
Imager.SetColor[context, oldColor];
};
Imager.DoSave[context, action ! UNWIND => {
bitmapData.sSizeFrame ← display.sSize;
bitmapData.fSizeFrame ← display.fSize;
bitmapData.frame ← display.refRep;
}];
display.Transfer[buffer];
bitmapData.sSizeFrame ← display.sSize;
bitmapData.fSizeFrame ← display.fSize;
bitmapData.frame ← display.refRep;
};
IF backgroundColor = NIL THEN {
buffer.Transfer[display];
}
ELSE IF backgroundColor = Imager.white THEN {
ImagerPixelMap.Clear[buffer];
backgroundColor ← NIL;
};
DoWithShift[rasterData, frameAction, -buffer.sOrigin, -buffer.fOrigin];
ReleaseScratch[buffer.refRep];
};
DoWithDitheredBuffer: PROC [context: Context, action: PROC, backgroundColor: Color, rasterData: ImagerRasterPrivate.Data, ditheredData: ImagerDitheredDevicePrivate.Data] RETURNS [ok: BOOLTRUE] ~ {
tightBox: ImagerDevice.DeviceBox ~ rasterData.clientClipBox;
box: ImagerDevice.DeviceBox ~ [
smin: tightBox.smin/12*12,
fmin: tightBox.fmin/12*12,
smax: tightBox.smax,
fmax: tightBox.fmax];
terminal: Terminal.Virtual ~ ditheredData.terminal;
display: PixelMap ~ ditheredData.frame;
buffer: PixelMap ~ ImagerPixelMap.Reshape[ObtainScratch[], display.refRep.lgBitsPerPixel, [box.smin, box.fmin, box.smax-box.smin, box.fmax-box.fmin]].Clip[[tightBox.smin, tightBox.fmin, tightBox.smax-tightBox.smin, tightBox.fmax-tightBox.fmin]];
frameAction: PROC ~ {
ditheredData.frame ← buffer;
ditheredData.frame.sOrigin ← ditheredData.frame.fOrigin ← 0;
IF backgroundColor # NIL THEN {
state: ImagerState.State ~ context.state;
oldColor: Color ~ state.color;
Imager.SetColor[context, backgroundColor];
Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]];
Imager.SetColor[context, oldColor];
};
Imager.DoSave[context, action ! UNWIND => {
ditheredData.frame ← display;
}];
IF terminal = NIL THEN display.Transfer[buffer]
ELSE {
action: PROC ~ {display.Transfer[buffer]};
Terminal.ModifyColorFrame[vt: terminal, action: action, xmin: box.fmin, ymin: box.smin, xmax: box.fmax, ymax: box.smax];
};
display.Transfer[buffer];
ditheredData.frame ← display;
};
IF backgroundColor = NIL THEN {
IF terminal = NIL THEN buffer.Transfer[display]
ELSE {
action: PROC ~ {buffer.Transfer[display]};
Terminal.ModifyColorFrame[vt: terminal, action: action, xmin: box.fmin, ymin: box.smin, xmax: box.fmax, ymax: box.smax];
};
buffer.Transfer[display];
};
ditheredData.terminal ← NIL;
DoWithShift[rasterData, frameAction, -buffer.sOrigin, -buffer.fOrigin ! UNWIND => {ditheredData.terminal ← terminal}];
ditheredData.terminal ← terminal;
ReleaseScratch[buffer.refRep];
};
bufferingOn: BOOLTRUE;
END.