~
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];
};
};