~
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;
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: INT ← INT[clientToDevice.tx]-y; smax: INT ← smin-h;
fmin: INT ← INT[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:
BOOL ←
TRUE] ~ {
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:
BOOL ←
TRUE] ~ {
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: BOOL ← TRUE;