mstone January 26, 1983 6:23 pm
Low level screen functions
Last Edited by: Stone, February 15, 1984 11:43:26 am PST
Last Edited by: Pier, February 14, 1984 6:50:55 pm PST
DIRECTORY
ScreenDefs: FROM "ScreenDefs",
Graphics USING[Context, PaintMode, Color, SetPaintMode, SetCP, white, black, Box, FontRef, MakeFont, FontBox, Translate, ClipBox, DrawRope, Save, Restore, RopeBox],
Rope USING [ROPE],
ScreenDefsExtras,
GraphicsBasic USING [Vec],
CGArea USING [InsertBox, New,Ref],
CGSource USING [Ref, Rep],
CGDevice USING [Ref],
CGContext USING [Ref],
CGMatrix USING [Ref,Map],
GraphicsOps USING [MoveDeviceRectangle],
PointDefs: FROM "PointDefs" USING [ IntegerSequence,IntegerSequenceRec,ScrPt,X,Y],
Font USING [FontRec, FontGraphicsClassRec, FONT],
ImagerTransform USING [Transformation, Create, Concat],
GriffinMemoryDefs USING [CZone],
Real USING [RoundI],
GriffinDefs: FROM "GriffinDefs",
GriffinViewer USING[GetViewer, PaintProc, DoPaint],
ViewerOps USING [Top2Bottom],
ViewerClasses USING[Viewer];
ScreenFns: PROGRAM
IMPORTS GraphicsOps, GriffinDefs, Graphics, GriffinMemoryDefs, CGArea, CGMatrix, Real, GriffinViewer, ViewerOps, ImagerTransform
EXPORTS ScreenDefs, ScreenDefsExtras =
BEGIN OPEN ScreenDefs,PointDefs,GriffinMemoryDefs;
TY,BY,LX,RX: INTEGER;
initTY,initBY,initLX,initRX: INTEGER;
CLIP: BOOLEAN ← TRUE;
CULL: BOOLEAN ← FALSE;
FNC: Graphics.PaintMode;
white: Graphics.Color=Graphics.white;
black: Graphics.Color=Graphics.black;
opaque: Graphics.PaintMode = opaque;
transparent: Graphics.PaintMode = transparent;
clears the current screen
ClearScreen: PUBLIC PROCEDURE [dc: Context] =
BEGIN
tl: ScrPt ← [LX,TY];
br: ScrPt ← [RX,BY];
EraseBox[tl,br, dc];
END;
defines the current screen
SetViewport: PUBLIC PROCEDURE [width,height: INTEGER] =
BEGIN
LX ← initLX ← 0;
RX ← initRX ← width;
TY ← initTY ← 0;
BY ← initBY ← height;
END;
changes clipping edges of current screen. Must stay inside initial clip window
SetClipEdges: PUBLIC PROCEDURE [tl,br: PointDefs.ScrPt] = {
clip clipping window
[tl,br] ← ClipToScreen[tl,br];
IF br[X]<tl[X] OR br[Y]<tl[Y] THEN CULL ← TRUE
ELSE {
CULL ←
FALSE;
LX ← tl[X];
RX ← br[X];
TY ← tl[Y];
BY ← br[Y];
};
};
ClipToScreen: PUBLIC PROC [tl,br: ScrPt] RETURNS [ScrPt, ScrPt] =
BEGIN
IF tl[
X]>
RX
OR br[
X]<
LX
OR tl[
Y]>
BY
OR br[
Y]<
TY
THEN {RETURN[[0,0],[-1,-1]]};
IF tl[X] < initLX THEN tl[X] ← initLX;
IF br[X] >initRX THEN br[X] ← initRX;
IF tl[Y] < initTY THEN tl[Y] ← initTY;
IF br[Y] >initBY THEN br[Y] ← initBY;
RETURN[tl,br];
END;
ClipPointToScreen: PUBLIC PROC [pt: ScrPt] RETURNS [ScrPt] =
BEGIN
IF pt[X] < initLX THEN pt[X] ← initLX;
IF pt[X] >initRX THEN pt[X] ← initRX;
IF pt[Y] < initTY THEN pt[Y] ← initTY;
IF pt[Y] >initBY THEN pt[Y] ← initBY;
RETURN[pt];
END;
ResetClipEdges: PUBLIC PROCEDURE =
BEGIN OPEN PointDefs;
tl: PointDefs.ScrPt ← [initLX,initTY];
br: PointDefs.ScrPt ← [initRX,initBY];
SetClipEdges[tl,br];
END;
ClipOn: PUBLIC PROCEDURE =
BEGIN
CLIP ← TRUE;
END;
PrimeClipper: PUBLIC PROC [tl,br: PointDefs.ScrPt] RETURNS [ClipperState] =
BEGIN
IF tl[X] > RX OR tl[Y] > BY OR br[X] < LX OR br[Y] < TY THEN RETURN[cull];
IF tl[
X]
IN [
LX..
RX]
AND tl[
Y]
IN [
TY..
BY]
AND br[
X]
IN [
LX..
RX]
AND br[
Y]
IN [
TY..
BY]
THEN {CLIP ← FALSE; RETURN[inside]};
CLIP ← TRUE;
RETURN[clip];
END;
SetFunction: PUBLIC PROCEDURE [fnc: Graphics.PaintMode] =
BEGIN
FNC ← fnc;
END;
This routine "scan converts" a chain encoding and draws
Width: INTEGER;
Grey: GreyType;
SetLineParms: PUBLIC PROCEDURE [ width: INTEGER, grey: GreyType]=
BEGIN
Width ← width;
CurrentBrush ← CodedBrushes[Width];
Grey ← grey;
END;
left: INTEGER = 0;
right: INTEGER = 1;
CodedBrush: TYPE = ARRAY [left..right] OF BrushSide;
BrushSide: TYPE = PointDefs.IntegerSequence;
CodedBrushes: ARRAY [1..8] OF CodedBrush;
CurrentBrush: CodedBrush;
xleft, xright: ARRAY [0..7] OF INTEGER;
leftBrush: BrushSide;
rightBrush: BrushSide;
brushSize: INTEGER;
topIndex: INTEGER;
topY: INTEGER; -- top of brush
centerX: INTEGER;
StartChain:
PUBLIC
PROCEDURE[pt: ScrPt, dc: Context] = {
ref: CGContext.Ref ← NARROW[dc.data];
newPt: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[pt[X],YFlip[pt[Y]]]];
brushIndex: INTEGER;
cgSource.color ← Grey;
cgSource.mode ← FNC;
cgDevice ← ref.device;
DeviceClipper[ref.matrix];
leftBrush ← CurrentBrush[left];
rightBrush ← CurrentBrush[right];
brushSize ← leftBrush.length;
topIndex ← 0;
topY ← Real.RoundI[newPt.y] - brushSize/2; -- top of brush
centerX ← Real.RoundI[newPt.x];
FOR brushIndex
IN [0..brushSize)
DO
xleft[brushIndex] ← centerX + leftBrush[brushIndex];
xright[brushIndex] ← centerX + rightBrush[brushIndex];
ENDLOOP;
};
NextChainPoint: PUBLIC PROCEDURE[dx,dy: INTEGER, dc: Context]=
BEGIN OPEN PointDefs;
brushIndex, rangeIndex, temp: INTEGER;
main loop for processing a point
centerX ← centerX + dx;
SELECT dy
FROM
-1 =>
BEGIN
-- move brush up
IF (topIndex ← topIndex -1) < 0
THEN topIndex ← topIndex + brushSize;
topIndex now points to bottom of xRanges
now write out lowest scan line.
NextScanLine[(topY + brushSize -1), xleft[topIndex], xright[topIndex], dc];
and reset for new entries
xleft[topIndex] ← 606;
xright[topIndex] ← 0;
topY ← topY - 1;
END;
1 =>
BEGIN
-- move brush down
first write out top scan line
NextScanLine[topY, xleft[topIndex], xright[topIndex], dc];
and reset for new entries
xleft[topIndex] ← 606;
xright[topIndex] ← 0;
IF (topIndex ← topIndex +1) >= brushSize THEN topIndex ← 0;
topY ← topY + 1;
END;
0 => NULL;
ENDCASE => BEGIN
EndChain[dc]; blt whole brush for strictly horizontal movements
DrawNextDot[0,0,dc];
RETURN;
END;
rangeIndex ← topIndex;
FOR brushIndex
IN [0..brushSize)
DO
IF xleft[rangeIndex] > (temp ← centerX + leftBrush[brushIndex])
THEN xleft[rangeIndex] ← temp;
IF xright[rangeIndex] < (temp ← centerX + rightBrush[brushIndex])
THEN xright[rangeIndex] ← temp;
IF (rangeIndex ← rangeIndex + 1) >= brushSize
THEN rangeIndex ← 0;
ENDLOOP;
END;
EndChain: PUBLIC PROCEDURE [dc: Context] =
BEGIN
cleanup remaining brush lines when finished
brushIndex: INTEGER;
FOR brushIndex
IN [0..brushSize)
DO
NextScanLine[topY, xleft[topIndex], xright[topIndex], dc];
topY ← topY + 1;
IF (topIndex ← topIndex +1) >= brushSize
THEN topIndex ← 0;
ENDLOOP;
END;
SetFillParms: PUBLIC PROCEDURE [grey: GreyType] = {fillGrey ← grey};
fillGrey: GreyType ← black;
StartArea:
PUBLIC
PROCEDURE[y:
INTEGER, dc: Context]
RETURNS[yScan,xOffset:
INTEGER]= {
ref: CGContext.Ref ← NARROW[dc.data];
newPt: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[0,YFlip[y]]];
yScan ← Real.RoundI[newPt.y];
xOffset ← Real.RoundI[newPt.x];
cgSource.color ← fillGrey;
cgSource.mode ← FNC;
cgDevice ← ref.device;
DeviceClipper[ref.matrix];
RETURN[yScan,xOffset];
};
cgDevice: CGDevice.Ref ← NIL;
cgBox: Graphics.Box;
dLX,dRX,dTY,dBY: INTEGER;
DeviceClipper:
PROC [cgMatrix: CGMatrix.Ref] = {
newPt: GraphicsBasic.Vec ← CGMatrix.Map[cgMatrix,[LX,YFlip[TY]]];
dLX ← Real.RoundI[newPt.x];
dTY ← Real.RoundI[newPt.y];
newPt ← CGMatrix.Map[cgMatrix,[RX,YFlip[BY]]];
dRX ← Real.RoundI[newPt.x];
dBY ← Real.RoundI[newPt.y];
};
These parameters are in DEVICE coordinates
NextScanLine:
PUBLIC
PROCEDURE[y,lx,rx:
INTEGER, dc: Context] = {
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF lx>dRX OR rx<dLX OR y>dBY OR y<dTY THEN RETURN; --cull
IF lx < dLX THEN lx ← dLX;
IF rx >dRX THEN rx ← dRX;
END;
cgBox ← [xmin: lx, ymin: y, xmax: rx, ymax: y];
CGArea.InsertBox[cgArea,cgBox];
cgDevice.Show[cgDevice,cgArea,cgSource,NIL];
};
HLine: PROCEDURE[y,lx,rx: INTEGER, grey: GreyType, dc: Context] =
BEGIN
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF lx>RX OR rx<LX OR y>BY OR y<TY THEN RETURN; --cull
IF lx < LX THEN lx ← LX;
IF rx >RX THEN rx ← RX;
END;
cgSource.color ← grey;
cgSource.mode ← FNC;
DrawBox[xmin: lx, ymin: y, xmax: rx, ymax: y, dc: dc];
END;
InvertBox: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt, dc: Context] =
BEGIN OPEN PointDefs;
lx: INTEGER ← tl[X]; ty: INTEGER ← tl[Y]; --simpler this way on a number of counts
rx: INTEGER ← br[X]; by: INTEGER ← br[Y];
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN; --cull
IF lx < LX THEN lx ← LX;
IF rx >RX THEN rx ← RX;
IF ty < TY THEN ty ← TY;
IF by >BY THEN by ← BY;
END;
cgSource.mode ← invert;
cgSource.color ← black;
DrawBox[xmin: lx,ymin: ty,xmax: rx,ymax: by, dc: dc];
END;
EraseBox: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt, dc: Context] =
BEGIN OPEN PointDefs;
lx: INTEGER ← tl[X]; ty: INTEGER ← tl[Y]; --simpler this way on a number of counts
rx: INTEGER ← br[X]; by: INTEGER ← br[Y];
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN; --cull
IF lx < LX THEN lx ← LX;
IF rx >RX THEN rx ← RX;
IF ty < TY THEN ty ← TY;
IF by >BY THEN by ← BY;
END;
cgSource.mode ← opaque;
cgSource.color ← white;
DrawBox[xmin: lx,ymin: ty,xmax: rx,ymax: by, dc: dc];
END;
BoxFill: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt, dc: Context] =
BEGIN OPEN PointDefs;
lx: INTEGER ← tl[X]; ty: INTEGER ← tl[Y]; --simpler this way on a number of counts
rx: INTEGER ← br[X]; by: INTEGER ← br[Y];
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN; --cull
IF lx < LX THEN lx ← LX;
IF rx >RX THEN rx ← RX;
IF ty < TY THEN ty ← TY;
IF by >BY THEN by ← BY;
END;
cgSource.color ← fillGrey;
cgSource.mode ← FNC;
DrawBox[xmin: lx,ymin: ty,xmax: rx,ymax: by, dc: dc];
END;
ngridpieces: INTEGER ← 0;
gridT: INTEGER ← 3;
GetGridParameters:
PUBLIC
PROCEDURE
RETURNS[center: PointDefs.ScrPt,hgridlx,hgridrx,vgridty,vgridby,gridt: INTEGER]=
BEGIN
pt: PointDefs.ScrPt;
ngridpieces ← 1+(initBY-initTY)/8;
pt ← [(initRX-initLX)/2, (initBY-initTY)/2];
pt ← GriffinDefs.Grid[pt];
RETURN[pt,initLX,initRX,initTY,initBY,gridT];
END;
BltVGrid: PUBLIC PROCEDURE[lx: INTEGER, dc: Context]=
BEGIN
ty: INTEGER ← GriffinDefs.Grid[[0,initTY]][Y];
THROUGH [0..ngridpieces]
DO
BLTBlockInScreen[vgridBlock,[lx,ty],transparent,dc];
ty ← ty+8;
ENDLOOP;
END;
BltHGrid: PUBLIC PROCEDURE[ty: INTEGER, dc: Context]=
BEGIN
lx: INTEGER ← GriffinDefs.Grid[[0,initLX]][X];
THROUGH [0..ngridpieces]
DO
BLTBlockInScreen[hgridBlock,[lx,ty],transparent, dc];
lx ← lx+8;
ENDLOOP;
END;
actual calls to the GraphicsPackage
cgSource: CGSource.Ref ← CZone.NEW[CGSource.Rep ← [type: const, mode: opaque, fat: TRUE, color: black, bps: 0, xbase: NIL, xrast: 0]];
cgArea: CGArea.Ref ← CGArea.New[];
viewer: ViewerClasses.Viewer ← GriffinViewer.GetViewer[];
YFlip:
PROC [y:
INTEGER]
RETURNS [
INTEGER] =
INLINE {
RETURN[ViewerOps.Top2Bottom[viewer,y]];
};
menuFont: Graphics.FontRef ← Graphics.MakeFont["Cream10"];
ShowMenuString:
PUBLIC
PROCEDURE [origin: PointDefs.ScrPt, rope: Rope.
ROPE, dc: Context] = {
width,ascent: REAL;
paintProc: GriffinViewer.PaintProc = {
we've got to change y for a bottomUp coordinate system
Graphics.ClipBox[self: dc,
box: [xmin: LX, xmax: RX+1,ymin: YFlip[BY] , ymax: YFlip[TY]+1] ];
Graphics.Translate[dc,origin[X],YFlip[origin[Y]]];
Graphics.SetCP[dc,0,0];
[] ← Graphics.SetPaintMode[dc,opaque];
Graphics.DrawRope[self: dc,rope: rope,font: menuFont];
};
[,,,ascent] ← Graphics.FontBox[menuFont];
[,,width,] ← Graphics.RopeBox[font: menuFont, rope: rope];
origin[X] ← origin[X]-Real.RoundI[width/2];
origin[Y] ← origin[Y]+Real.RoundI[ascent];
IF dc=NIL THEN GriffinViewer.DoPaint[paintProc]
ELSE {[] ← Graphics.Save[dc]; paintProc[dc]; Graphics.Restore[dc]};
};
flip: ImagerTransform.Transformation ← ImagerTransform.Create[0,-1,0,1,0,0];
DrawChar:
PUBLIC
PROCEDURE [font: Font.
FONT, origin: PointDefs.ScrPt, char:
CHAR, dc: Context] = {
transformation: ImagerTransform.Transformation ←
ImagerTransform.Concat[pre: font.actualTransformation, post: flip];
paintProc: GriffinViewer.PaintProc = {
ref: CGContext.Ref ← NARROW[dc.data];
dorg: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[origin[X],YFlip[origin[Y]]]];
orgX: INTEGER ← Real.RoundI[dorg.x]; --origin in device coordinates
orgY: INTEGER ← Real.RoundI[dorg.y];
lx,rx,y: INTEGER;
fontProc: SAFE PROC [sMin, fMin: INTEGER, fSize: NAT] = TRUSTED {
y,xmin,xsize relative to the character origin
lx ← fMin+orgX;
rx ← lx+fSize;
y ← sMin+orgY;
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF lx>dRX OR rx<dLX OR y>dBY OR y<dTY THEN RETURN; --cull
IF lx < dLX THEN lx ← dLX;
IF rx >dRX THEN rx ← dRX;
END;
cgBox ← [xmin: lx, ymin: y, xmax: rx, ymax: y];
CGArea.InsertBox[cgArea,cgBox];
};
DeviceClipper[ref.matrix]; --set up dRX, etc.
cgSource.color ← black;
cgSource.mode ← opaque;
font.fontGraphicsClass.maskProc[font,transformation,char,fontProc];
ref.device.Show[ref.device,cgArea,cgSource,NIL];
};
IF dc=NIL THEN GriffinViewer.DoPaint[paintProc] ELSE paintProc[dc];
};
BLTBlockInScreen: PUBLIC PROCEDURE[src: BlockPtr, tl: PointDefs.ScrPt, fnc: Graphics.PaintMode, dc: Context]=
BEGIN OPEN PointDefs;
paintProc: GriffinViewer.PaintProc = {
[] ← Graphics.SetPaintMode[dc,fnc];
Graphics.SetCP[dc, dlx,YFlip[dty]]; --flip y
dc.procs.DrawBits[self: dc,
base: src.base, raster: src.raster, bitsPerPixel: 0,
w: drx-dlx+1, h: dby - dty +1, x: x, y: y, xorigin: x, yorigin: y];
};
dlx,dty,drx,dby,x,y: INTEGER;
I want edges
dlx ← tl[X]; dty ← tl[Y];
drx ← dlx + src.w -1;
dby ← dty + src.h -1;
IF
CLIP
THEN
BEGIN
IF CULL THEN RETURN;
IF dlx>RX OR drx<LX OR dty>BY OR dby<TY THEN RETURN; --cull
IF dlx < LX THEN dlx ← LX;
IF drx >RX THEN drx ← RX;
IF dty < TY THEN dty ← TY;
IF dby >BY THEN dby ← BY;
END;
x ← src.lx+(dlx-tl[X]); y ← src.ty+(dty-tl[Y]);
IF dc=NIL THEN GriffinViewer.DoPaint[paintProc] ELSE paintProc[dc];
END;
DrawBox:
PROC[xmin,ymin,xmax,ymax:
INTEGER, dc: Context] = {
paintProc:
PROC[dc: Graphics.Context] = {
ref: CGContext.Ref ← NARROW[dc.data];
newMin: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[xmin,YFlip[ymin]]];
newMax: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[xmax,YFlip[ymax]]];
cgBox: Graphics.Box ← [xmin: newMin.x, ymin: newMin.y,
xmax: newMax.x, ymax: newMax.y];
CGArea.InsertBox[cgArea,cgBox];
ref.device.Show[ref.device,cgArea,cgSource,NIL];
};
IF dc=NIL THEN GriffinViewer.DoPaint[paintProc] ELSE paintProc[dc];
};
MoveScreenBox: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt,dx,dy: INTEGER] =
BEGIN
paintProc:
PROC[dc: Graphics.Context] = {
ref: CGContext.Ref ← NARROW[dc.data];
from: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[tl[X],YFlip[tl[Y]]]];
to: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[tl[X]+dx,YFlip[tl[Y]+dy]]];
xbr: GraphicsBasic.Vec ← CGMatrix.Map[ref.matrix,[br[
X],YFlip[br[
Y]]]];
lx: INTEGER ← Real.RoundI[to.x]; ty: INTEGER ← Real.RoundI[to.y];
rx: INTEGER ← Real.RoundI[lx+xbr.x-from.x]; by: INTEGER ← Real.RoundI[ty+xbr.y-from.y];
IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN; --cull
IF lx < LX THEN lx ← LX;
IF rx >RX THEN rx ← RX;
IF ty < TY THEN ty ← TY;
IF by >BY THEN by ← BY;
GraphicsOps.MoveDeviceRectangle[self: dc,
width: rx-lx+1, height: by-ty+1, --trust we're not scaled
fromX: MAX[LX,Real.RoundI[from.x]], fromY: MAX[TY,Real.RoundI[from.y]],
toX: lx, toY: ty];
};
GriffinViewer.DoPaint[paintProc];
END;
vgridBlock,hgridBlock: ScreenDefs.BlockPtr;
vgridbits: PACKED ARRAY [0..8) OF CARDINAL ← ALL[040000B];
hgridbits: PACKED ARRAY [0..4) OF CARDINAL ← ALL[100200B];
vgridBlock ← NEW[Block ← [@vgridbits,1,0,0,3,8]];
hgridBlock ← NEW[Block ← [@hgridbits,1,0,0,8,3]];
vgridbits[0] ← 120000B; --rest are "040000B"
hgridbits[1] ← 77577B; --rest are "100200B"
Initialize coded brushes
CodedBrushes[1] ←[NEW[IntegerSequenceRec[1]],NEW[IntegerSequenceRec[1]]];
CodedBrushes[1][left][0]← 0;
CodedBrushes[1][right][0]← 0;
CodedBrushes[2] ←[NEW[IntegerSequenceRec[2]],NEW[IntegerSequenceRec[2]]];
CodedBrushes[2][left][0]← -1;
CodedBrushes[2][left][1]← -1;
CodedBrushes[2][right][0]← 0;
CodedBrushes[2][right][1]← 0;
CodedBrushes[3] ←[NEW[IntegerSequenceRec[3]],NEW[IntegerSequenceRec[3]]];
CodedBrushes[3][left][0]← 0;
CodedBrushes[3][left][1]← -1;
CodedBrushes[3][left][2]← 0;
CodedBrushes[3][right][0]← 0;
CodedBrushes[3][right][1]← 1;
CodedBrushes[3][right][2]← 0;
CodedBrushes[4] ←[NEW[IntegerSequenceRec[4]],NEW[IntegerSequenceRec[4]]];
CodedBrushes[4][left][0]← -1;
CodedBrushes[4][left][1]← -2;
CodedBrushes[4][left][2]← -2;
CodedBrushes[4][left][3]← -1;
CodedBrushes[4][right][0]← 0;
CodedBrushes[4][right][1]← 1;
CodedBrushes[4][right][2]← 1;
CodedBrushes[4][right][3]← 0;
END.