ImagerImageWorksImpl.mesa
Copyright Ó 1993 by Xerox Corporation. All rights reserved.
Michael Plass, July 1, 1993 12:47 pm PDT
DIRECTORY Basics, ImagerBox, ImagerColor, ImagerDevice, ImagerDeviceWorks, ImagerImageWorks, ImagerPixel, ImagerPixelArray, ImagerPixelArrayPrivate, ImagerSample, ImagerTransformation, IO, SF;
ImagerImageWorksImpl: CEDAR MONITOR
IMPORTS Basics, ImagerColor, ImagerDeviceWorks, ImagerPixelArrayPrivate, ImagerSample, ImagerTransformation, IO, SF
EXPORTS ImagerImageWorks
~ BEGIN
SampleMap: PRIVATE TYPE = ImagerSample.SampleMap;
PixelMap: PRIVATE TYPE = ImagerPixel.PixelMap;
RasterSampleMap: PRIVATE TYPE = ImagerSample.RasterSampleMap;
PixelArray: PRIVATE TYPE = ImagerPixelArray.PixelArray;
ColorOperator: PRIVATE TYPE = ImagerColor.ColorOperator;
Device: PRIVATE TYPE = ImagerDevice.Device;
Transformation: PRIVATE TYPE = ImagerTransformation.Transformation;
bufferedClass: ImagerPixelArrayPrivate.PixelArrayClass ~ ImagerPixelArrayPrivate.NewClass[
type: $BufferedPixelMap,
MaxSampleValue: BufferedMaxSampleValue,
GetSamples: BufferedGetSamples
];
BufferedData: TYPE ~ RECORD [
map: PixelMap,
borderSize: NAT
];
BufferedMaxSampleValue: PROC [pa: ImagerPixelArray.PixelArray, i: NAT] RETURNS [CARDINAL] ~ {
data: REF BufferedData ~ NARROW[pa.data];
map: SampleMap ~ data.map[i];
RETURN[Basics.BITSHIFT[1, ImagerSample.GetBitsPerSample[map]]-1];
};
BufferedGetSamples: PROC [pa: ImagerPixelArray.PixelArray, i: NAT, s, f: INT,
buffer: ImagerSample.SampleBuffer, start: NAT, count: NAT] ~ {
data: REF BufferedData ~ NARROW[pa.data];
map: SampleMap ~ data.map[i];
loFill: NAT = MAX[(data.borderSize - f), 0];
hiFill: NAT = MAX[(f + count) - (pa.fSize - data.borderSize), 0];
dataCount: INT = count-loFill-hiFill;
sSize: NAT ~ data.map.box.max.s; -- should be (pa.sSize-2*data.borderSize)
fSize: NAT ~ data.map.box.max.f; -- should be (pa.fSize-2*data.borderSize)
s ¬ s - data.borderSize;
IF s < 0 THEN s ¬ 0 ELSE IF s >= sSize THEN s ¬ sSize-1;
IF dataCount > 0 THEN {
ImagerSample.GetSamples[map: map, initIndex: [s, f-data.borderSize+loFill], buffer: buffer, start: start+loFill, count: dataCount];
};
IF loFill # 0 THEN {
ImagerSample.FillSamples[buffer: buffer, value: ImagerSample.Get[map, [s, 0]], start: start, count: loFill];
};
IF hiFill # 0 THEN {
ImagerSample.FillSamples[buffer: buffer, value: ImagerSample.Get[map, [s, fSize-1]], start: start+count-hiFill, count: hiFill];
};
};
MakeBufferedPixelArray: PROC [pixelMap: PixelMap, borderSize: NAT ¬ 0] RETURNS [ImagerPixelArray.PixelArray] ~ {
samplesPerPixel: NAT ~ pixelMap.samplesPerPixel;
delta: SF.Vec ~ SF.Neg[pixelMap.box.min];
size: SF.Vec ~ SF.Size[pixelMap.box];
m: Transformation ~ ImagerTransformation.Translate[[pixelMap.box.min.s-borderSize, pixelMap.box.min.f-borderSize]];
new: PixelMap ¬ pixelMap;
IF delta # [0, 0] THEN {
new ¬ NEW[ImagerPixel.PixelMapRep[samplesPerPixel]];
new.box ¬ [max: size];
FOR j: NAT IN [0..samplesPerPixel) DO
new[j] ¬ ImagerSample.ReIndex[map: pixelMap[j], delta: delta, box: [max: size]];
ENDLOOP;
};
RETURN[ImagerPixelArrayPrivate.New[class: bufferedClass, data: NEW[BufferedData ¬ [map: new, borderSize: borderSize]], immutable: FALSE, samplesPerPixel: samplesPerPixel, sSize: size.s+2*borderSize, fSize: size.f+2*borderSize, m: m]];
};
black: ImagerColor.Color = ImagerColor.ColorFromGray[1];
DrawBufferedImage: PUBLIC PROC [device: Device, buffer: ImagerPixel.PixelMap, box: SF.Box, colorOperator: ColorOperator, pixelToView: Transformation, viewToDevice: Transformation] ~ {
borderSize: NAT ¬ 2;
sSize: INTEGER = box.max.s-box.min.s;
fSize: INTEGER = box.max.f-box.min.f;
IF sSize > 0 AND fSize > 0 THEN {
pixelToDevice: Transformation = ImagerTransformation.Concat[pixelToView, viewToDevice];
pa: ImagerPixelArray.PixelArray = MakeBufferedPixelArray[buffer, borderSize];
color: ImagerColor.Color = ImagerColor.MakeSampledColor[pa: pa, um: pixelToView, colorOperator: colorOperator];
device.class.SetColor[device, color, viewToDevice];
ImagerDeviceWorks.MaskRectangle[device, [box.min.s, box.min.f, sSize, fSize], pixelToDevice];
device.class.SetColor[device, black, viewToDevice];
};
};
interleavedSamplesClass: ImagerPixelArrayPrivate.PixelArrayClass ~ ImagerPixelArrayPrivate.NewClass[
type: $InterleavedSamples,
MaxSampleValue: InterleavedSamplesMaxSampleValue,
GetSamples: InterleavedSamplesGetSamples
];
InterleavedSamplesDataRep: TYPE ~ RECORD [
map: ImagerSample.SampleMap,
borderSize: NAT,
samplesPerPixel: NAT,
cachedBuffer: REF CachedBufferRep ¬ NIL
];
InterleavedSamplesMaxSampleValue: PROC [pa: ImagerPixelArray.PixelArray, i: NAT] RETURNS [CARDINAL] ~ {
data: REF InterleavedSamplesDataRep ~ NARROW[pa.data];
IF i > data.samplesPerPixel THEN Basics.RaiseBoundsFault[];
RETURN [Basics.BITSHIFT[1, ImagerSample.GetBitsPerSample[data.map]] - 1];
};
CachedBufferRep: TYPE ~ RECORD [
s: NAT,
buffer: ImagerSample.SampleBuffer
];
ObtainBuffer: ENTRY PROC [data: REF InterleavedSamplesDataRep] RETURNS [b: REF CachedBufferRep] ~ {
b ¬ data.cachedBuffer;
data.cachedBuffer ¬ NIL;
};
ReleaseBuffer: ENTRY PROC [data: REF InterleavedSamplesDataRep, b: REF CachedBufferRep] ~ { data.cachedBuffer ¬ b };
SalvageBuffer: PROC [pa: ImagerPixelArray.PixelArray] RETURNS [scratch: REF ¬ NIL] ~ {
WITH pa.data SELECT FROM
data: REF InterleavedSamplesDataRep => scratch ¬ ObtainBuffer[data];
ENDCASE;
};
InterleavedSamplesGetSamples: PROC [pa: ImagerPixelArray.PixelArray, i: NAT, s, f: INT,
buffer: ImagerSample.SampleBuffer, start: NAT, count: NAT] ~ {
Since ImagerSample.GetSamples works best for contiguous source data, we fetch a whole scan line at a time, and then pick out the elements we need, keeping a one-line cache around for the next time (which is likely to be real soon).
data: REF InterleavedSamplesDataRep ~ NARROW[pa.data];
fSize: NAT ~ ImagerSample.GetBox[data.map].max.f; -- should be (pa.fSize-2*data.borderSize)*data.samplesPerPixel
sSize: NAT ~ ImagerSample.GetBox[data.map].max.s; -- should be (pa.sSize-2*data.borderSize)
s ¬ s - data.borderSize;
IF s < 0 THEN s ¬ 0 ELSE IF s >= sSize THEN s ¬ sSize-1;
IF i > data.samplesPerPixel THEN Basics.RaiseBoundsFault[] ELSE {
b: REF CachedBufferRep ¬ ObtainBuffer[data];
IF b = NIL THEN b ¬ NEW[CachedBufferRep];
IF b.buffer = NIL OR b.buffer.maxLength # fSize THEN {
b.buffer ¬ ImagerSample.NewSamples[fSize];
b.buffer.length ¬ 0;
};
IF b.s # s OR b.buffer.length # fSize THEN {
b.buffer.length ¬ fSize;
ImagerSample.GetSamples[map: data.map, initIndex: [s, 0], buffer: b.buffer];
b.s ¬ s;
};
TRUSTED {
loFill: NAT = MAX[(data.borderSize - f), 0];
hiFill: NAT = MAX[(f + count) - (pa.fSize - data.borderSize), 0];
dataCount: INT = count-loFill-hiFill;
IF dataCount > 0 THEN {
src: POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: b.buffer, start: (f-data.borderSize+loFill) * data.samplesPerPixel + i, count: (dataCount) * data.samplesPerPixel - (data.samplesPerPixel-1)];
srcP: POINTER TO Basics.RawWords ¬ src;
srcDelta: CARDINAL ¬ UNITS[WORD] * data.samplesPerPixel;
dst: POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: buffer, start: start+loFill, count: dataCount];
FOR k: CARDINAL IN [0..count) DO
dst[k] ¬ srcP[0];
srcP ¬ srcP + srcDelta;
ENDLOOP;
};
IF loFill # 0 THEN {
ImagerSample.FillSamples[buffer: buffer, value: b.buffer[i], start: start, count: loFill];
};
IF hiFill # 0 THEN {
ImagerSample.FillSamples[buffer: buffer, value: b.buffer[fSize-data.samplesPerPixel+i], start: start+count-hiFill, count: hiFill];
};
};
ReleaseBuffer[data, b];
};
};
MakeInterleavedPixelArray: PROC [sampleMap: ImagerSample.SampleMap, samplesPerPixel: NAT, borderSize: NAT ¬ 0, scratch: REF ¬ NIL] RETURNS [ImagerPixelArray.PixelArray] ~ {
The borderSize here is the number of pixels of border to include; this data will get filled in (on the fly) during InterleavedSamplesGetSamples by replicating the data at the edge. This is to prevent edge effects in the sampler.
box: SF.Box ~ ImagerSample.GetBox[sampleMap];
data: REF InterleavedSamplesDataRep ~ NEW [InterleavedSamplesDataRep ¬ [
map: ImagerSample.ZeroOrigin[sampleMap],
borderSize: borderSize,
samplesPerPixel: samplesPerPixel,
cachedBuffer: NIL
]];
pa: ImagerPixelArray.PixelArray ~ ImagerPixelArrayPrivate.New[class: interleavedSamplesClass, data: data, immutable: FALSE, samplesPerPixel: samplesPerPixel, sSize: box.max.s-box.min.s+2*borderSize, fSize: (box.max.f-box.min.f)/samplesPerPixel+2*borderSize, m: ImagerTransformation.Translate[[box.min.s-borderSize, box.min.f-borderSize]]];
WITH scratch SELECT FROM
b: REF CachedBufferRep => {
IF b.buffer # NIL AND b.buffer.maxLength = box.max.f-box.min.f THEN {
b.buffer.length ¬ 0;
data.cachedBuffer ¬ b;
};
};
ENDCASE;
RETURN [pa]
};
DrawInterleavedImage: PUBLIC PROC [device: Device, buffer: ImagerSample.SampleMap, samplesPerPixel: NAT, box: SF.Box, colorOperator: ColorOperator, pixelToView: Transformation, viewToDevice: Transformation] ~ {
borderSize: NAT ¬ 2;
sSize: INTEGER = box.max.s-box.min.s;
fSize: INTEGER = box.max.f-box.min.f;
IF sSize > 0 AND fSize > 0 THEN {
pixelToDevice: Transformation = ImagerTransformation.Concat[pixelToView, viewToDevice];
pa: ImagerPixelArray.PixelArray = MakeInterleavedPixelArray[buffer, samplesPerPixel, borderSize];
color: ImagerColor.Color = ImagerColor.MakeSampledColor[pa: pa, um: pixelToView, colorOperator: colorOperator];
device.class.SetColor[device, color, viewToDevice];
ImagerDeviceWorks.MaskRectangle[device, [box.min.s, box.min.f, sSize, fSize], pixelToDevice];
device.class.SetColor[device, black, viewToDevice];
};
};
GetUnsafeBlock: PROC [map: RasterSampleMap] RETURNS [Basics.UnsafeBlock] ~ {
Like ImagerSample.GetUnsafeBlock, but allows up to 31 pad bits per scan line.
size: SF.Vec ~ ImagerSample.GetSize[map];
base: POINTER TO Basics.RawBytes = LOOPHOLE[map.GetBase.word];
startIndex: NAT = map.GetBase.bit/8;
IF size.s = 0 OR size.f = 0
THEN RETURN [[base: base, startIndex: startIndex, count: 0]]
ELSE {
bitsPerLine: CARD ~ map.GetBitsPerLine;
dataBitsPerLine: CARD ~ CARD[map.GetBitsPerSample]*CARD[size.f];
fillBitsPerLine: [0..32) ~ bitsPerLine - dataBitsPerLine;
padStart: [0..0] ~ map.GetBase.bit MOD 8;
nBytes: NAT ~ (CARD[size.s]*bitsPerLine + 7)/8;
RETURN [[base: LOOPHOLE[map.GetBase.word], startIndex: map.GetBase.bit/8, count: nBytes]]
};
};
ChooseOverlap: PROC [device: Device, pixelToDevice: Transformation, padMod: NAT] RETURNS [NAT] = {
overlap: NAT ¬ 1;
DO -- bump overlap until it spans at least 2 device pixels.
d: ImagerBox.Rectangle = ImagerTransformation.TransformRectangle[pixelToDevice, [0, 0, overlap, overlap]];
IF d.h >= 2 AND d.w >= 2 THEN EXIT;
overlap ¬ overlap+1;
ENDLOOP;
UNTIL (overlap * padMod) MOD 8 = 0 DO -- ensure scanlines are byte-aligned.
overlap ¬ overlap+1;
ENDLOOP;
RETURN [overlap] -- ensure scanlines are byte-aligned.
};
ChooseBandSize: PROC [sSize: NAT, fSize: NAT, bitsPerSample: NAT, samplesPerPixel: NAT] RETURNS [NAT] = {
RETURN [MIN[sSize, 64]]
};
StreamImage: PUBLIC PROC [device: Device, sSize: NAT, fSize: NAT, bitsPerSample: NAT, samplesPerPixel: NAT ¬ 1, padMod: NAT ¬ 8, pixelToView: Transformation, viewToDevice: Transformation, co: ColorOperator, source: IO.STREAM] = {
IF sSize # 0 AND fSize # 0 THEN {
pixelToDevice: Transformation = ImagerTransformation.Concat[pixelToView, viewToDevice];
bandSize: NAT ~ ChooseBandSize[sSize: sSize, fSize: fSize, bitsPerSample: bitsPerSample, samplesPerPixel: samplesPerPixel];
overlapSize: NAT ~ IF bandSize = sSize THEN 0 ELSE ChooseOverlap[device, pixelToDevice, padMod];
fSizeBand: NAT ~ fSize*samplesPerPixel;
padModPred: NAT = padMod-1;
bitsPerLine: NAT ~ (bitsPerSample*samplesPerPixel*fSize+padModPred)/padMod*padMod;
bandStore: ImagerSample.RasterSampleMap ~ ImagerSample.ObtainScratchMap[box: [min: [-overlapSize, 0], max: [s: bandSize+overlapSize, f: fSizeBand]], bitsPerSample: bitsPerSample, bitsPerLine: bitsPerLine];
band: ImagerSample.RasterSampleMap ¬ NIL; -- valid band data, including overlap
box: SF.Box ¬ []; -- the part to image this time around.
DO
newBand: ImagerSample.RasterSampleMap ¬ NIL;
readBand: ImagerSample.RasterSampleMap ¬ NIL;
box.min.s ¬ box.max.s;
box.min.f ¬ 0;
box.max.s ¬ box.max.s+bandSize;
box.max.f ¬ fSize;
IF box.max.s > sSize THEN box.max.s ¬ sSize;
IF box.min.s >= box.max.s THEN EXIT;
readBand ¬ newBand ¬ NARROW[ImagerSample.ReIndex[map: bandStore, delta: [s: box.min.s, f: 0], box: [min: [0, 0], max: [sSize, fSizeBand]]]];
IF band # NIL THEN {
ImagerSample.Transfer[dst: newBand, src: band];
readBand ¬ NARROW[ImagerSample.Clip[readBand, [min: [band.GetBox.max.s, 0], max: SF.maxVec]]];
};
band ¬ newBand;
TRUSTED {
block: Basics.UnsafeBlock ~ GetUnsafeBlock[readBand];
bytes: INT ~ IO.UnsafeGetBlock[source, block];
IF bytes < block.count THEN {
Premature end; chop off the image, as per section 4.10.2
box.max.s ¬ sSize ¬ readBand.GetBox.min.s + ((bytes*8)/bitsPerLine);
band ¬ NARROW[ImagerSample.Clip[band, [max: [sSize, fSizeBand]]]];
};
};
DrawInterleavedImage[device: device, buffer: band, samplesPerPixel: samplesPerPixel, box: box, colorOperator: co, pixelToView: pixelToView, viewToDevice: viewToDevice];
ENDLOOP;
ImagerSample.ReleaseScratchMap[bandStore];
};
};
END.