DIRECTORY
SampleArrayDisplay,
Basics USING [bitsPerByte, bitsPerWord, RawWords],
ImagerPixelMap USING [DeviceRectangle, Intersect, PixelMap],
ImagerSample USING [UnsafeSamples],
PrincOpsUtils USING [BITAND, BITOR, LongCopy],
Real USING [FixLI],
RealOps USING [RoundC],
SampleArrays USING [GetSamples, SampleArray],
SampleMapOps USING [Buffer, Create, Fill, Get, GetPointer, ObtainBuffer, Put, SampleMap, UnsafeCreate],
Terminal USING [Current, FrameBuffer, GetColorFrameBufferA, GetColorFrameBufferB, GetColorMode, ModifyColorFrame, Virtual],
Vector2 USING [VEC],
ViewerClasses USING [Viewer],
ViewerOps USING [UserToScreenCoords];
SampleArrayDisplayImpl:
CEDAR
MONITOR
IMPORTS SampleArrays, ImagerPixelMap, PrincOpsUtils, Real, RealOps, SampleMapOps, Terminal, ViewerOps
EXPORTS SampleArrayDisplay
= BEGIN
OPEN SampleArrayDisplay;
SampleArray: TYPE ~ SampleArrays.SampleArray;
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
SampleMap: TYPE ~ SampleMapOps.SampleMap;
UnsafeSamples: TYPE ~ ImagerSample.UnsafeSamples;
VEC: TYPE ~ Vector2.VEC;
bitsPerWord: NAT ~ Basics.bitsPerWord;
bitsPerByte: NAT ~ Basics.bitsPerByte;
term: Terminal.Virtual ~ Terminal.Current[];
sb1: SampleMapOps.Buffer ~ SampleMapOps.ObtainBuffer[4096];
sbRed: SampleMapOps.Buffer ~ SampleMapOps.ObtainBuffer[4096];
sbGreen: SampleMapOps.Buffer ~ SampleMapOps.ObtainBuffer[4096];
sbBlue: SampleMapOps.Buffer ~ SampleMapOps.ObtainBuffer[4096];
sbMask: SampleMapOps.Buffer ~ SampleMapOps.ObtainBuffer[4096];
scratch: SampleMap ~ SampleMapOps.Create[1, 1, 1];
Display:
PUBLIC
PROC [screen: Screen, rect: DeviceRectangle, sa: SampleArray ←
NIL, sm: SampleMap ←
NIL, slow, fast: Sampler, picRect, onlyPaintIn: DeviceRectangle ← nullRectangle] ~ {
vt: Terminal.Virtual ~ Terminal.Current[];
FullColor: PROC RETURNS [BOOL] ~ INLINE {RETURN [Terminal.GetColorMode[vt].full]};
SELECT
TRUE
FROM
screen=bw => DisplayLF[rect, sa, sm, slow, fast, picRect, onlyPaintIn];
FullColor[] => Display24Bit[rect, sa, sm, slow, fast, picRect, onlyPaintIn];
ENDCASE => DisplayNBit[rect, sa, sm, slow, fast, picRect, onlyPaintIn];
};
ViewerInformation:
PUBLIC PROC [v: ViewerClasses.Viewer]
RETURNS [screen: Screen, rect: DeviceRectangle] ~ {
x1, x2, y1, y2: INTEGER;
vt: Terminal.Virtual ~ Terminal.Current[];
screenHeight: INT;
screen ← IF v.column=color THEN color ELSE bw;
screenHeight ← IF screen=bw THEN vt.bwHeight ELSE vt.colorHeight;
[x1, y1] ← ViewerOps.UserToScreenCoords[self: v];
[x2, y2] ← ViewerOps.UserToScreenCoords[self: v, vx: v.cw-1, vy: v.ch-1];
y1 ← screenHeight - y1;
y2 ← screenHeight - y2;
rect ← [sMin: MIN[y1, y2], fMin: MIN[x1, x2], sSize: ABS[y1-y2]+1, fSize: ABS[x1-x2]+1];
};
fillValue: CARDINAL ← 100200B;
Display24Bit:
ENTRY
PROC [rect: DeviceRectangle, sa: SampleArray ←
NIL, sm: SampleMap ←
NIL, slow, fast: Sampler, picRect, onlyPaintIn: DeviceRectangle] ~
TRUSTED {
The basic attack is to set up pointer to the two channels (A channel has red and green, and B channel has blue). Then the three colors are put on the screen.
paintPixels: BOOL ~ sa#NIL;
paintMask: BOOL ~ sm#NIL;
red, green, blue: NAT;
frameA: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferA[term];
frameB: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferB[term];
smB: SampleMap ~ SampleMapOps.UnsafeCreate[sSize: frameB.height, fSize: frameB.width, bitsPerSample: 8, bitsPerLine: frameB.wordsPerLine*Basics.bitsPerWord, base: [word: frameB.base, bit: 0], nWords: LONG[frameB.wordsPerLine]*LONG[frameB.height], ref: NIL];
smRG: SampleMap ~ SampleMapOps.UnsafeCreate[sSize: frameA.height, fSize: frameA.width*2, bitsPerSample: 8, bitsPerLine: frameA.wordsPerLine*Basics.bitsPerWord, base: [word: frameA.base, bit: 0], nWords: LONG[frameA.wordsPerLine]*LONG[frameA.height], ref: NIL];
smBMask: SampleMap ~ SampleMapOps.UnsafeCreate[sSize: frameB.height, fSize: frameB.width*8, bitsPerSample: 1, bitsPerLine: frameB.wordsPerLine*Basics.bitsPerWord, base: [word: frameB.base, bit: 0], nWords: LONG[frameB.wordsPerLine]*LONG[frameB.height], ref: NIL];
smRGMask: SampleMap ~ SampleMapOps.UnsafeCreate[sSize: frameA.height, fSize: frameA.width*16, bitsPerSample: 1, bitsPerLine: frameA.wordsPerLine*Basics.bitsPerWord, base: [word: frameA.base, bit: 0], nWords: LONG[frameA.wordsPerLine]*LONG[frameA.height], ref: NIL];
ClearScreen:
PROC ~
TRUSTED {
red ← 0;
green ← MIN[1, sa.n-1];
blue ← MIN[2, sa.n-1];
SampleMapOps.Fill[dest: [smB, [onlyPaintIn.sMin, onlyPaintIn.fMin], [onlyPaintIn.sSize, onlyPaintIn.fSize]], value: fillValue, function: [null, null]];
SampleMapOps.Fill[dest: [smRG, [onlyPaintIn.sMin, 2*onlyPaintIn.fMin], [onlyPaintIn.sSize, 2*onlyPaintIn.fSize]], value: fillValue, function: [null, null]];
};
PaintScreen:
PROC ~
TRUSTED {
lastLine: CARDINAL ← CARDINAL.LAST; --Line # of line currently buffered
fastOffset: INTEGER ~ onlyPaintIn.fMin - picRect.fMin;
size: NAT ~ onlyPaintIn.fSize;
rMin, gMin, and bMin are the places in each scan line where we need to blast the bits
rMin: NAT ~ 16*onlyPaintIn.fMin+7;
gMin: NAT ~ rMin+8;
bMin: NAT ~ 8*onlyPaintIn.fMin+7;
fMin, fMax, and fCount are used as follows: fMin represents the lowest f position in the pixel which will be examined during the subsampling operation. fMax, correspondingly is the highest. Thus, if we get the buffer correct for values in the range [fMin .. fMax] then we need not worry about the rest of the buffer. fCount is a convenience.
fMin: NAT ~ MIN[fast.samples[fastOffset], fast.samples[fastOffset+size-1]];
fMax: NAT ~ MAX[fast.samples[fastOffset], fast.samples[fastOffset+size-1]];
fCount: NAT ~ fMax-fMin+1;
FOR scan:
NAT
IN [onlyPaintIn.sMin .. onlyPaintIn.sMin+onlyPaintIn.sSize)
DO
line: CARDINAL ~ slow.samples[scan - picRect.sMin];
IF line#lastLine
THEN {
--Load the buffer
sb1Ptr: UnsafeSamples ~ SampleMapOps.GetPointer[buffer: sb1, start: fMin, count: fCount];
lastLine ← line;
IF paintMask
THEN {
SampleMapOps.Get[buffer: sb1, start: fMin, count: fCount, sampleMap: sm, s: line, f: fMin];
SubsampleSamples[from: sb1, to: sbMask, s: fast, min: fastOffset, size: size];
};
IF paintPixels
THEN {
SampleArrays.GetSamples[buffer: sb1, start: fMin, count: fCount, sa: sa, i: red, s: line, f: fMin]; --Red
IF paintMask THEN SubsampleSamplesWithMask[from: sb1, mask: sbMask, to: sbRed, s: fast, min: fastOffset, size: size] ELSE SubsampleSamples[from: sb1, to: sbRed, s: fast, min: fastOffset, size: size];
SampleArrays.GetSamples[buffer: sb1, start: fMin, count: fCount, sa: sa, i: green, s: line, f: fMin]; --Green
IF paintMask THEN SubsampleSamplesWithMask[from: sb1, mask: sbMask, to: sbGreen, s: fast, min: fastOffset, size: size] ELSE SubsampleSamples[from: sb1, to: sbGreen, s: fast, min: fastOffset, size: size];
SampleArrays.GetSamples[buffer: sb1, start: fMin, count: fCount, sa: sa, i: blue, s: line, f: fMin]; --Blue
IF paintMask THEN SubsampleSamplesWithMask[from: sb1, mask: sbMask, to: sbBlue, s: fast, min: fastOffset, size: size] ELSE SubsampleSamples[from: sb1, to: sbBlue, s: fast, min: fastOffset, size: size];
};
};
IF paintPixels
THEN {
SampleMapOps.Put[buffer: sbRed, count: onlyPaintIn.fSize, sampleMap: smRG, s: scan, f: onlyPaintIn.fMin+onlyPaintIn.fMin, df: 2, function: [null, null]];
SampleMapOps.Put[buffer: sbGreen, count: onlyPaintIn.fSize, sampleMap: smRG, s: scan, f: onlyPaintIn.fMin+onlyPaintIn.fMin+1, df: 2, function: [null, null]];
SampleMapOps.Put[buffer: sbBlue, count: onlyPaintIn.fSize, sampleMap: smB, s: scan, f: onlyPaintIn.fMin, function: [null, null]];
};
IF paintMask
AND ~paintPixels
THEN {
SampleMapOps.Put[buffer: sbMask, count: onlyPaintIn.fSize, sampleMap: smRGMask, s: scan, f: rMin, df: 16, function: [null, null]];
SampleMapOps.Put[buffer: sbMask, count: onlyPaintIn.fSize, sampleMap: smRGMask, s: scan, f: gMin, df: 16, function: [null, null]];
SampleMapOps.Put[buffer: sbMask, count: onlyPaintIn.fSize, sampleMap: smBMask, s: scan, f: bMin, df: 8, function: [null, null]];
};
ENDLOOP;
};
onlyPaintIn ← IF onlyPaintIn=nullRectangle THEN rect ELSE ImagerPixelMap.Intersect[rect, onlyPaintIn];
IF paintPixels AND onlyPaintIn.sSize>0 AND onlyPaintIn.fSize>0 THEN Terminal.ModifyColorFrame[vt: term, action: ClearScreen, xmin: onlyPaintIn.fMin, ymin: onlyPaintIn.sMin, xmax: onlyPaintIn.fMin+onlyPaintIn.fSize, ymax: onlyPaintIn.sMin+onlyPaintIn.sSize]; --Only clear the screen if we're repainting the bits
picRect ← ImagerPixelMap.Intersect[rect, picRect];
onlyPaintIn ← ImagerPixelMap.Intersect[picRect, onlyPaintIn];
IF onlyPaintIn.sSize>0 AND onlyPaintIn.fSize>0 THEN Terminal.ModifyColorFrame[vt: term, action: PaintScreen, xmin: onlyPaintIn.fMin, ymin: onlyPaintIn.sMin, xmax: onlyPaintIn.fMin+onlyPaintIn.fSize, ymax: onlyPaintIn.sMin+onlyPaintIn.sSize];
};
DisplayNBit:
PROC [rect: DeviceRectangle, sa: SampleArray ←
NIL, sm: SampleMap ←
NIL, slow, fast: Sampler, picRect, onlyPaintIn: DeviceRectangle] ~ {
};
DisplayLF:
PROC [rect: DeviceRectangle, sa: SampleArray ←
NIL, sm: SampleMap ←
NIL, slow, fast: Sampler, picRect, onlyPaintIn: DeviceRectangle] ~ {
};
SubsampleSamples:
UNSAFE
PROC [from, to: SampleMapOps.Buffer, s: Sampler, min, size:
NAT] ~
UNCHECKED {
SELECT s.form
FROM
offset => {
--> s.samples[i] = s.offset+i
So, we need to set to[k] ← from[s.offset+k+min]], k IN [0..size).
PrincOpsUtils.LongCopy[
from: SampleMapOps.GetPointer[from, s.offset+min, size],
to: SampleMapOps.GetPointer[to, 0, size],
nwords: size
]
};
reduce => {
--> s.samples[i] = s.offset + reduce*i
So, we need to set to[k] ← from[s.offset + reduce*min + reduce*k], k IN [0..size).
CopyWords[
src: SampleMapOps.GetPointer[buffer: from, start: s.offset + s.reduce*min, count: size],
dest: SampleMapOps.GetPointer[buffer: to, start: 0, count: 0],
count: size,
delta: s.reduce
];
};
ENDCASE => {
FOR sample:
NAT
IN [0 .. size)
DO
to[sample] ← from[s.samples[sample+min]];
ENDLOOP;
};
};
SubsampleSamplesWithMask:
UNSAFE
PROC [from, mask, to: SampleMapOps.Buffer, s: Sampler, min, size:
NAT] ~
UNCHECKED {
SELECT s.form
FROM
offset =>
TRUSTED {
--> s.samples[i] = s.offset+i
So, we need to set to[k] ← from[s.offset+k+min]], k IN [0..size).
sm: SampleMap ~ SampleMapOps.UnsafeCreate[sSize: 1, fSize: 16*size, bitsPerSample: 1, bitsPerLine: 16*size, base: [word: SampleMapOps.GetPointer[buffer: to, start: 0, count: size], bit: 0], nWords: size, ref: NIL, scratchDescriptor: scratch];
PrincOpsUtils.LongCopy[
from: SampleMapOps.GetPointer[from, s.offset+min, size],
to: SampleMapOps.GetPointer[to, 0, size],
nwords: size
];
SampleMapOps.Put[buffer: mask, count: size, sampleMap: sm, f: 15, df: 16];
};
reduce =>
TRUSTED {
--> s.samples[i] = s.offset + reduce*i
So, we need to set to[k] ← from[s.offset + reduce*min + reduce*k], k IN [0..size).
sm: SampleMap ~ SampleMapOps.UnsafeCreate[sSize: 1, fSize: 16*size, bitsPerSample: 1, bitsPerLine: 16*size, base: [word: SampleMapOps.GetPointer[buffer: to, start: 0, count: size], bit: 0], nWords: size, ref: NIL, scratchDescriptor: scratch];
CopyWords[
src: SampleMapOps.GetPointer[buffer: from, start: s.offset + s.reduce*min, count: size],
dest: SampleMapOps.GetPointer[buffer: to, start: 0, count: 0],
count: size,
delta: s.reduce
];
SampleMapOps.Put[buffer: mask, count: size, sampleMap: sm, f: 15, df: 16];
};
ENDCASE => {
FOR sample:
NAT
IN [0 .. size)
DO
to[sample] ← PrincOpsUtils.
BITOR[
PrincOpsUtils.BITAND[376B, from[s.samples[sample+min]]],
mask[sample]
];
ENDLOOP;
};
};
CopyWords:
UNSAFE
PROC [src, dest:
LONG
POINTER
TO Basics.RawWords, count, delta:
CARDINAL] ~
UNCHECKED {
Has the following effect:
FOR k: NAT IN [0 .. count) DO
dest[k] ← src[k*delta];
ENDLOOP;
delta2: CARDINAL ← delta+delta;
delta3: CARDINAL ← delta2+delta;
delta4: CARDINAL ← delta3+delta;
delta5: CARDINAL ← delta4+delta;
delta6: CARDINAL ← delta5+delta;
delta7: CARDINAL ← delta6+delta;
delta8: CARDINAL ← delta7+delta;
THROUGH [0 .. count/8)
DO
dest[0] ← src[0];
dest[1] ← src[delta];
dest[2] ← src[delta2];
dest[3] ← src[delta3];
dest[4] ← src[delta4];
dest[5] ← src[delta5];
dest[6] ← src[delta6];
dest[7] ← src[delta7];
dest ← dest+8;
src ← src+delta8;
ENDLOOP;
FOR k:
NAT
IN [0 .. count
MOD 8)
DO
dest[k] ← src[0];
src ← src+delta;
ENDLOOP;
};
BuildSampler:
PUBLIC
PROC [min, size:
NAT, m, b:
REAL]
RETURNS [s: Sampler] ~ {
Round:
PROC [arg:
REAL]
RETURNS [
CARDINAL] ~
INLINE {
RETURN [RealOps.RoundC[arg+0.5, [round: rm]]]
};
s ← NEW[SamplerRep[size]];
s.min ← min;
FOR sample:
NAT
IN [min .. min+size)
DO
i: NAT ← sample-min;
s.samples[i] ← Round[m*sample + b];
ENDLOOP;
[numerator: s.reduce, denominator: s.expand] ← RealToRational[m ! ANY => {s.reduce ← s.expand ← 0; CONTINUE}];
s.offset ← s.samples[0];
SELECT
TRUE
FROM
s.reduce=1
AND s.expand=1 => {
--An offset type
s.form ← offset;
};
s.expand=1 => {
--A reduce type
s.form ← reduce;
};
s.reduce=1 => {
--An expand type
s.form ← expand;
};
ENDCASE;
};
RealToRational:
PROC [r:
REAL]
RETURNS [numerator, denominator:
INT] ~ {
b: REAL ← 0;
c: REAL ← 1;
d: REAL ← ABS[r - Real.FixLI[r]];
UNTIL r*c = Real.FixLI[r*c]
DO
e: REAL ← 1/d;
oldC: REAL ← c;
c ← Real.FixLI[e]*c+b;
b ← oldC;
d ← e - Real.FixLI[e];
ENDLOOP;
RETURN [numerator: Real.FixLI[r*c], denominator: Real.FixLI[c]];
};
END.