CKViewerOps.mesa
Last edited by Ken Pier, July 21, 1983 4:36 pm
DIRECTORY
BitBlt,
ViewerOps,
CKViewerButtons,
ColorDisplay,
MessageWindow,
Inline,
Process USING [MsecToTicks, Pause];
CKViewerOps: PROGRAM
IMPORTS BitBlt, ColorDisplay, Inline, Process, MessageWindow, ViewerOps
EXPORTS CKViewerButtons = BEGIN
OPEN MW: MessageWindow;
CKViewer: TYPE = CKViewerButtons.CKViewer;
CKViewerState: TYPE = CKViewerButtons.CKViewerState;
sym: BOOLEANFALSE;
bbspace: BitBlt.BBTableSpace;
bb: BitBlt.BBptr ← InitBB[@bbspace];
grayword: CARDINAL ← 0;
bitmap,bitmapB: LONG POINTERNIL;
wpl,wplB: CARDINAL ← 0;
width,height: CARDINAL ← 0;
lbpp,lbppB: CARDINAL ← 0; -- log (base 2) bits per pixel
bpp,bppB: CARDINAL ← 0; -- bits per pixel
full: BOOLEANFALSE;
ashow,bshow: BOOLEANTRUE;
splat: BOOLEANTRUE;
test: BOOLEANFALSE;
neg: BOOLEANFALSE;
cornerSize: CARDINAL ← 4;
cornerColor: CARDINAL ← 8;
Triple: TYPE = RECORD[r,g,b: [0..256)];
testmap: ARRAY[0..8] OF Triple ← [
[127,127,127], -- gray (background)
[ 0, 0, 0], -- black
[255, 0, 0], -- red
[ 0,255, 0], -- green
[255,255, 0], -- yellow
[ 0, 0,255], -- blue
[255, 0,255], -- magenta
[ 0,255,255], -- cyan
[255,255,255] -- white
];
ShowTPat: PROC = {
x: CARDINAL = 60;
h: CARDINAL = 60;
w: CARDINAL = 50;
gap: CARDINAL = 15;
IF NOT full THEN {
FOR i: CARDINAL IN[0..9) DO
ColorDisplay.SetColor[pixelA: i, r: testmap[i].r, g: testmap[i].g, b: testmap[i].b];
ENDLOOP;
ColorDisplay.Show[TRUE,FALSE,FALSE];
};
gray background
Rect[0,0,width,height,0];
four corners
{ s: CARDINAL = cornerSize;
c: CARDINAL = cornerColor; -- color
Rect[0,0,s,s,c];
Rect[width-s,0,s,s,c];
Rect[0,height-s,s,s,c];
Rect[width-s,height-s,s,s,c];
};
various colors at left edge
FOR i: CARDINAL IN[0..8) DO
r,g,b: [0..256);
r ← (i MOD 2)*255;
g ← ((i/2) MOD 2)*255;
b ← ((i/4) MOD 2)*255;
Rect[4,i*h+10,x,h-20,i+1];
ENDLOOP;
a bunch of stripes
FOR i: CARDINAL IN[0..8) DO
r,g,b: [0..256);
r ← (i MOD 2)*255;
g ← ((i/2) MOD 2)*255;
b ← ((i/4) MOD 2)*255;
Rect[x+i*w+gap,0,w-gap,8*h,i+1];
ENDLOOP;
};
Rect: PROC[x,y,w,h: CARDINAL, i: [0..8]] = {
IF NOT (x<width AND y<height) THEN RETURN;
w ← MIN[w,width-x]; h ← MIN[h,height-y];
IF full THEN FullRect[x,y,w,h,testmap[i].r,testmap[i].g,testmap[i].b]
ELSE ARect[x,y,w,h,i];
};
SetUpColors: PROCEDURE = BEGIN
k: CARDINAL ← Inline.BITSHIFT[1,bpp]; -- number of pixel values
FOR n: CARDINAL IN [0..k) DO
r,g,b: [0..256);
Alter: PROC[c: [0..256)] RETURNS[[0..256)] = INLINE { RETURN[255-(255-c)/2] };
r ← MyRandom[256]; g ← MyRandom[256]; b ← MyRandom[256];
ColorDisplay.SetColor[pixelA: n, pixelB: 0, r: r, g: g, b: b];
ColorDisplay.SetColor[pixelA: n, pixelB: 1, r: Alter[r], g: Alter[g], b: Alter[b]];
ENDLOOP;
END;
Positive: PROC = {
IF NOT full THEN RETURN;
ColorDisplay.TurnOff[];
FOR i: CARDINAL IN[0..256) DO
ColorDisplay.SetRedMap[i,i];
ColorDisplay.SetGreenMap[i,i];
ColorDisplay.SetBlueMap[i,i];
ENDLOOP;
ColorDisplay.TurnOn[];
};
Negative: PROC = {
IF NOT full THEN RETURN;
ColorDisplay.TurnOff[];
FOR i: CARDINAL IN[0..256) DO
ColorDisplay.SetRedMap[i,255-i];
ColorDisplay.SetGreenMap[i,255-i];
ColorDisplay.SetBlueMap[i,255-i];
ENDLOOP;
ColorDisplay.TurnOn[];
};
MyRandom: PROCEDURE [max: CARDINAL] RETURNS [CARDINAL] = INLINE BEGIN
RETURN[Random[] MOD max];
END;
SetGray: PROC[g: [0..256)] = { grayword ← MakeGray[g] };
MakeGray: PROC[g: [0..256)] RETURNS[CARDINAL] = {
grayword: CARDINAL ← 0;
IF bpp#0 THEN {
ppw: CARDINAL ← 16/bpp; -- pixels per word
mask: CARDINAL ← Inline.BITNOT[Inline.BITSHIFT[-1,bpp]];
g ← Inline.BITAND[g,mask];
THROUGH [0..ppw) DO grayword ← Inline.BITSHIFT[grayword,bpp] + g ENDLOOP;
};
RETURN[grayword];
};
InitBB: PROC[bbs: POINTER TO BitBlt.BBTableSpace] RETURNS[BitBlt.BBptr] = INLINE {
bb: BitBlt.BBptr ← BitBlt.AlignedBBTable[bbs];
bb^ ← [
dst: [word: NIL, bit: 0],
dstBpl: 0,
src: [word: @grayword, bit: 0],
srcDesc: [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]],
width: 0, height: 0,
flags: [disjoint: TRUE, gray: TRUE]
];
RETURN[bb];
};
FullRect: PROC[x,y,w,h: CARDINAL, r,g,b: [0..256)] = {
rgword: CARDINAL ← 256*r + g;
bbword: CARDINAL ← 256*b + b;
IF NOT full THEN RETURN;
bitmap A
bb.dst ← [word: ColorDisplay.baseA + LONG[y]*ColorDisplay.wplA + x, bit: 0];
bb.dstBpl ← 16*ColorDisplay.wplA;
bb.src ← [word: @rgword, bit: 0];
bb.width ← 16*w;
bb.height ← h;
bb.flags.dstFunc ← null;
BitBlt.BITBLT[bb];
bitmap B
bb.dst ← [word: ColorDisplay.baseB + LONG[y]*ColorDisplay.wplB + x/2, bit: 8*(x MOD 2)];
bb.dstBpl ← 16*ColorDisplay.wplB;
bb.src ← [word: @bbword, bit: bb.dst.bit];
bb.width ← 8*w;
bb.height ← h;
bb.flags.dstFunc ← null;
BitBlt.BITBLT[bb];
};
ARect: PROC[x,y,w,h: CARDINAL, i: [0..256), fn: BitBlt.DstFunc ← null] = {
iword: CARDINAL ← MakeGray[i];
xbit: CARDINAL ← Inline.BITSHIFT[x,lbpp];
bb.dst ← [word: ColorDisplay.baseA + LONG[y]*ColorDisplay.wplA + xbit/16, bit: xbit MOD 16];
bb.dstBpl ← 16*ColorDisplay.wplA;
bb.src ← [word: @iword, bit: bb.dst.bit];
bb.width ← Inline.BITSHIFT[w,lbpp];
bb.height ← h;
bb.flags.dstFunc ← fn;
BitBlt.BITBLT[bb];
};
index: [0..256) ← 0;
Rectangle: PROC[lx,ty,rx,by: CARDINAL, fn: BitBlt.DstFunc ← null] = {
IF full THEN {
r,g,b: [0..256);
r ← MyRandom[256]; g ← MyRandom[256]; b ← MyRandom[256];
FullRect[lx,ty,rx-lx,by-ty,r,g,b];
}
ELSE {
index ← (index + 1) MOD 256;
ARect[lx,ty,rx-lx,by-ty,index,fn];
};
};
BRectangle: PROC[lx,ty,rx,by: CARDINAL] = {
xbit: CARDINAL ← Inline.BITSHIFT[lx,lbppB];
black: CARDINAL ← 177777B;
bb.dst ← [word: bitmapB + LONG[ty]*wplB + xbit/16, bit: xbit MOD 16];
bb.dstBpl ← 16*wplB;
bb.src ← [word: @black, bit: 0];
bb.width ← Inline.BITSHIFT[(rx-lx),lbppB];
bb.height ← by-ty;
bb.flags.dstFunc ← xor;
BitBlt.BITBLT[bb];
};
RandomSplat: PROCEDURE [ckViewer: CKViewer] = {
screenX: CARDINAL = width;
screenY: CARDINAL = height;
XorRatio: CARDINAL = 3;
ty, ty2, lx, w, h: CARDINAL;
fn: BitBlt.DstFunc ← (IF MyRandom[XorRatio]=0 THEN xor ELSE null);
SetGray[MyRandom[256]];
IF sym THEN {
ty ← ty2 ← MyRandom[screenY/2];
lx ← MyRandom[screenX/2];
w ← MyRandom[screenX/2-lx];
h ← MyRandom[screenY/2-ty];
Rectangle[lx, ty, lx+w, ty+h, fn];
ty ← screenY/2+(screenY/2-ty)-h;
Rectangle[lx, ty, lx+w, ty+h, fn];
lx ← screenX/2+(screenX/2-lx)-w;
Rectangle[lx, ty, lx+w, ty+h, fn];
Rectangle[lx, ty2, lx+w, ty2+h, fn];
}
ELSE {
ty ← MyRandom[screenY];
lx ← MyRandom[screenX];
w ← MyRandom[screenX-lx];
h ← MyRandom[screenY-ty];
Rectangle[lx, ty, lx+w, ty+h, fn];
};
IF bitmapB#NIL THEN {
ty ← MyRandom[screenY];
lx ← MyRandom[screenX];
w ← MyRandom[screenX-lx];
h ← MyRandom[screenY-ty];
BRectangle[lx, ty, lx+w, ty+h];
};
IF ckViewer.state.slow THEN Wait[200]; -- slow mode
};
ShowConverge: PROCEDURE = {
black: [0..8] = 1; --testmap[1] is black
white: [0..8] = 8; --testmap[8] is white
hThick: CARDINAL ← height/300;
hStep: CARDINAL ← height/30;
vThick: CARDINAL ← height/300;
vStep: CARDINAL ← height/30;
IF NOT full THEN {
FOR i: CARDINAL IN[0..9) DO
ColorDisplay.SetColor[pixelA: i, r: testmap[i].r, g: testmap[i].g, b: testmap[i].b];
ENDLOOP;
ColorDisplay.Show[TRUE,FALSE,FALSE];
};
Rect[0,0,width,height,black]; --black background
FOR xx: CARDINAL ← 0, xx+hStep UNTIL xx > width DO
Rect[xx, 0, vThick, height, white];
ENDLOOP;
FOR v: CARDINAL ← 0, v+vStep UNTIL v > height DO
Rect[0, v, width, hThick ,white];
ENDLOOP;
};
ShowBigRed,ShowSmallRed: PROCEDURE [] = {
MW.Append[message: "RedScreen NOT IMPLEMENTED !!", clearFirst: TRUE];
MW.Blink[];
};
ShowBigGreen,ShowSmallGreen: PROCEDURE [] = {
MW.Append[message: "GreenScreen NOT IMPLEMENTED !!", clearFirst: TRUE];
MW.Blink[];
};
ShowBigBlue,ShowSmallBlue: PROCEDURE [] = {
MW.Append[message: "BlueScreen NOT IMPLEMENTED !!", clearFirst: TRUE];
MW.Blink[];
};
ShowCBars: PROCEDURE [] = {
MW.Append[message: "ShowCBars NOT IMPLEMENTED !!", clearFirst: TRUE];
MW.Blink[];
};
NextRoll: PROCEDURE [] = {
r,g,b,r0,g0,b0: [0..256);
[r0,g0,b0] ← ColorDisplay.GetColor[0];
FOR i: CARDINAL IN[0..255) DO
[r,g,b] ← ColorDisplay.GetColor[i+1];
ColorDisplay.SetColor[i,0,r,g,b];
ENDLOOP;
ColorDisplay.SetColor[255,0,r0,g0,b0];
Wait[100];
};
NextScramble: PROCEDURE [] = {
SetUpColors; Wait[200];
};
SetBackground: PROCEDURE [v: INTEGER] = BEGIN
background ← v;
ColorDisplay.SetColor[0, 0, v, v, v];
END;
background: INTEGER ← 255; -- initially white
ClearScreen: PROCEDURE = {
IF NOT full THEN {
ColorDisplay.SetColor[0, 0, background, background, background];
SetGray[0] };
Rectangle[0,0,width,height];
};
Wait: PROCEDURE[millisecs: CARDINAL] = INLINE {
Process.Pause[Process.MsecToTicks[millisecs]] };
Constant definitions (meanings described in InitRandom below)
defaultSeed: CARDINAL = 27183;
numCalls: INTEGER = 3;
Module state
a: ARRAY [0..55] OF CARDINAL;
Holds 55 random cardinals, to be returned by Random. (A[0] is wasted to make the
code generator produce better array accesses.)
p: INTEGER [0..55];
a[1..p-1] has not yet been returned by Random; p is [1..55] except within Random.
Procedures
InitRandom: PUBLIC PROC[seed: INTEGER] RETURNS[INTEGER] = {
The parameter seed determines the sequence generated by procedure Random below.
If seed=0, a default seed value is used to determine the starting point of the
sequence; if seed>0, seed is scaled if necessary and then used; if seed<0, a seed
value is derived from the system clock. In any case, the seed value actually used
(after scaling) is the integer value returned.
minSeed: CARDINAL = LAST[CARDINAL]/10;
g, gPrev, gSave: CARDINAL;
IF seed<=0 THEN seed ← defaultSeed;
Now scale the seed into the proper range (no log routine available...)
WHILE seed<minSeed DO seed ← seed*3 ENDLOOP;
Seed can't be too big since LAST[INTEGER] < LAST[CARDINAL]*(9/10)
The array a is initialized by placing seed in a[55], and scattering the values
(-1)**(i-1) * (F(i) - seed*F(i-1)) MOD maxRand, 0<i<55,
(where F(i) denotes the i-th Fibonacci number) throughout the rest of a. Then
the generating procedure RandomGen is called, numCalls times, to make things
sufficiently random.
a[55] ← gPrev ← seed;
g ← 1;
FOR i: INTEGER IN [1..54] DO
p ← (21*i) MOD 55;
a[p] ← gSave ← g; g ← gPrev-g; gPrev ← gSave;
ENDLOOP;
THROUGH [1..numCalls) DO
RandomGen[];
ENDLOOP;
Show a as being empty; first call to Random will call RandomGen again.
p ← 1;
RETURN[seed]
};--InitRandom
Random: PUBLIC PROC RETURNS[CARDINAL] = INLINE {
p ← p-1;
IF p=0 THEN { RandomGen[]; p ← 55 };
RETURN[a[p]]
};--Random
RandomGen: PROC = INLINE {
Additive random number generator using the recurrence y(n) = y(n-55) - y(n-24)
mod maxRand. See John F. Reiser, "Analysis of Additive Random Number Generators",
STAN-CS-77-601, March 1977, or Knuth Volume 2 (second edition.)
FOR i: INTEGER IN [1..24] DO
a[i] ← a[i] - a[i+31];
ENDLOOP;
FOR i: INTEGER IN [25..55] DO
a[i] ← a[i] - a[i-24];
ENDLOOP;
};--RandomGen
Choose: PUBLIC PROC[min, max: CARDINAL] RETURNS[CARDINAL--[min..max]--] = {
intervalLen: CARDINAL;
IF min > max THEN ERROR;
intervalLen ← max - min + 1; --is 0 when min=0, max=LAST[CARDINAL]
IF intervalLen = 0 THEN RETURN[Random[]];
DO
Draw a number in [0..LAST[CARDINAL]]. We want to reject this number if it lies in the
"odd interval" at the high end of this range (there is no odd interval if intervalLen
divides 2^16). The funny test below does it (claim). The average number of numbers drawn
is less than 2, and much closer to 1 for small intervalLen.
r, rem: CARDINAL;
Inline expansion of Random[];
p ← p-1;
IF p=0 THEN { RandomGen[]; p ← 55 };
r ← a[p];
rem ← r MOD intervalLen;
IF (r - rem) > LOOPHOLE[-LOOPHOLE[intervalLen,INTEGER],CARDINAL] THEN LOOP;
RETURN[min + rem];
ENDLOOP;
};--Choose
SetLogBitsPerPixel: PROC[n: [0..4)] RETURNS[BOOLEAN] = {
b: CARDINAL ← Inline.BITSHIFT[1,n];
mode: ColorDisplay.Mode ← [FALSE,b,1];
IF NOT ColorDisplay.HasMode[mode] THEN {
mode.bitsPerPixelB ← 0;
IF NOT ColorDisplay.HasMode[mode] THEN RETURN[FALSE];
};
IF NOT ColorDisplay.SetMode[mode] THEN ERROR;
lbpp ← n; bpp ← b; full ← FALSE;
lbppB ← 0; bppB ← 1;
ashow ← bshow ← TRUE;
width ← ColorDisplay.width; height ← ColorDisplay.height;
bitmap ← ColorDisplay.baseA; wpl ← ColorDisplay.wplA;
bitmapB ← ColorDisplay.baseB; wplB ← ColorDisplay.wplB;
ClearScreen;
SetUpColors;
ColorDisplay.TurnOn[];
RETURN[TRUE];
};
Set24BitsPerPixel: PROC = {
mode: ColorDisplay.Mode ← [TRUE,0,0];
IF NOT ColorDisplay.HasMode[mode] THEN RETURN;
IF NOT ColorDisplay.SetMode[mode] THEN ERROR;
lbpp ← 0; bpp ← 0; full ← TRUE;
bitmap ← NIL; bitmapB ← NIL; wpl ← 0;
ashow ← bshow ← TRUE;
width ← ColorDisplay.width; height ← ColorDisplay.height;
IF test THEN { ShowTPat[]; test ← FALSE } ELSE ClearScreen;
ColorDisplay.TurnOn[];
};
Go: PUBLIC PROC [ckViewer: CKViewer] = {
[] ← InitRandom[0];
SELECT TRUE FROM
SetLogBitsPerPixel[3] => NULL;
SetLogBitsPerPixel[2] => NULL;
ENDCASE => {
MW.Append[message: "NO COLOR DISPLAY !!", clearFirst: TRUE];
MW.Blink[];
RETURN;
};
DO
IF ckViewer.state.quit THEN {
ClearScreen; ViewerOps.DestroyViewer[ckViewer.container];
[]𡤌olorDisplay.SetMode[ColorDisplay.disconnected]; EXIT;
};
IF ckViewer.state.bpp=1 AND bpp#1 THEN { [] ← SetLogBitsPerPixel[0];
IF NOT splat THEN test ← TRUE };
IF ckViewer.state.bpp=2 AND bpp#2 THEN { [] ← SetLogBitsPerPixel[1];
IF NOT splat THEN test ← TRUE };
IF ckViewer.state.bpp=4 AND bpp#4 THEN { [] ← SetLogBitsPerPixel[2];
IF NOT splat THEN test ← TRUE };
IF ckViewer.state.bpp=8 AND bpp#8 THEN { [] ← SetLogBitsPerPixel[3];
IF NOT splat THEN test ← TRUE };
IF ckViewer.state.bpp=24 AND NOT full THEN { Set24BitsPerPixel[];
IF NOT splat THEN test ← TRUE };
IF ckViewer.state.aToggle THEN {
ashow ← NOT ashow;
ColorDisplay.Show[ashow,bshow,TRUE];
ckViewer.state.aToggle ← FALSE};
IF ckViewer.state.bToggle THEN {
bshow ← NOT bshow;
ColorDisplay.Show[ashow,bshow,TRUE];
ckViewer.state.bToggle ← FALSE};
IF ckViewer.state.random THEN {
sym ← FALSE; splat ← TRUE;
ClearScreen[];
ckViewer.state.random ← FALSE};
IF ckViewer.state.symmetric THEN {
sym ← TRUE; splat ← TRUE;
ClearScreen[];
ckViewer.state.symmetric ← FALSE};
IF NOT ckViewer.state.freeze THEN {
ckViewer.state.roll ← ckViewer.state.scramble ← FALSE; --only TRUE when frozen
SELECT TRUE FROM
test OR ckViewer.state.testPattern => {
test ← TRUE; splat ← FALSE;
ShowTPat[]; test ← ckViewer.state.testPattern ← FALSE;
};
ckViewer.state.cbars => {ShowCBars[]; splat ← ckViewer.state.cbars ← FALSE};
ckViewer.state.converge => {ShowConverge[]; splat ← ckViewer.state.converge ← FALSE};
ckViewer.state.bigRed => {ShowBigRed[]; splat ← ckViewer.state.bigRed ← FALSE};
ckViewer.state.bigGreen => {ShowBigGreen[]; splat ← ckViewer.state.bigGreen ← FALSE};
ckViewer.state.bigBlue => {ShowBigBlue[]; splat ← ckViewer.state.bigBlue ← FALSE};
ckViewer.state.smallRed => {ShowSmallRed[]; splat ← ckViewer.state.smallRed ← FALSE};
ckViewer.state.smallGreen => {ShowSmallGreen[]; splat ← ckViewer.state.smallGreen ← FALSE};
ckViewer.state.smallBlue => {ShowSmallBlue[]; splat ← ckViewer.state.smallBlue ← FALSE};
ENDCASE => IF splat THEN {ColorDisplay.Show[ashow,bshow,TRUE]; RandomSplat[ckViewer]; };
}
ELSE {
SELECT TRUE FROM
ckViewer.state.scramble => NextScramble[];
ckViewer.state.roll => NextRoll[];
ENDCASE => NULL;
};
ENDLOOP;
IF NOT ColorDisplay.SetMode[ColorDisplay.disconnected] THEN ERROR;
};
END.