Meet the pixels on your Christmas tree
By MICHAEL NOELS
THIS MONTH we are going to use the graphic methods we've already
discussed, plus some new ones, to produce a seasonal offering
— a Christmas tree with an appropriate greeting.
The finished product is Program VII. Before that, though, we'd
better look at a few of the techniques it uses, the most important
of which concern user defined characters.
These characters, as their name implies, allow you to use the
PRINT statement (or VDU) to print out your own special characters
on the screen.
In the BBC Micro's graphics modes, all the characters are drawn
on an 8 x 8 grid, each cell of the grid being known as a pixel.
Figure I. Pixel
pattern for the letter A
Figure I illustrates how the grid for the letter A is filled
out. To print this letter by using its Ascii code simply enter:
PRINT CHR$(65)
Now the BBC Micro has several blank grids ready for you to fill.
These grids have the Ascii codes 224 to 255.
Once you've filled, say, 224 with the required pattern of cells,
to print out this user defined character, you type:
PRINT CHR$(224)
and it appears where the cursor is, with the appropriate foreground
and background colours.
Figure I illustrates how you can define your own character.
You simply fill in the cells of the 8 x 8 grid until you get the
pattern required. Then all that's needed is a few simple sums.
Each column of the grid has a value. The value of the rightmost
column is 1, the one to its left 2, the one to the left of that
4, and so on, doubling each time until the leftmost column has
a value of 128.
You may have noticed that these are the column values of a binary
number.
Each row of the grid has a number calculated for it and you
add together the column values of each cell shaded in that row.
Figure II.
User defined characters
Figure II shows the idea. Each row has its number. To teach the
computer this pattern we use:
VDU 23
followed by the Ascii code we're going to use for it followed
by the eight numbers we've calculated for the rows, starting with
the top and working down. All these are separated by commas.
So, to define character 224 to be the pattern shown in Figure
II, we use:
VDU 23,224,153,66,36,153,153,36,66,153
Program I makes use of this character.
10 REM *** PROGRAM I ***
20 MODE 4
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 FOR I%=0 TO 19
50 PRINT TAB(I%,I%)CHR$(224)
60 NEXT I%
Try altering line 20 to:
20 MODE 5
and notice the difference — the character is spread sideways.
This is because the shape of the pixels in Mode 5 differs from
that in Mode 4.
A pixel is the smallest point you can plot on a screen. Its
size limits the resolution, or fineness, available.
Figure III.
Pixel sizes in each graphic mode
As Figure III shows, in Modes 1 and 4, the pixel is four graphic
units (gu) square, whereas in Modes 2,5 and 0 it is variously
oblong.
So when you print out an 8 x 8 pixel grid in Mode 4 it comes
out square, whereas in Mode 5 the wider pixels stretch the figure.
Figure IV.
Character sizes in each graphic mode
Figure IV shows the size of a character in each graphic mode
in terms of graphical units.
You have to allow for this alteration in width when you are
visualising your user defined character. I find it easier to see
the character I want in terms of square pixels, so I've stuck
to Mode 1 in Program VII even though it limits the range of available
colours.
Let's try to print a pattern with our user defined character.
We'll simply print the character three times, in a vertical line.
We can use the cursor control codes to ensure that the characters
actually end up vertically aligned.
VDU 8 moves the cursor left
VDU 9 moves the cursor right
VDU 10 moves the cursor down
VDU 11 moves the cursor up
Program II illustrates the technique.
10 REM *** PROGRAM II ***
20 MODE 4
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 VDU 224,8,10,224,8,10,224
Remember that we can string VDU statements together. Line 40
works like this: 224 prints the character, leaving the cursor
directly to its right. 8 moves the cursor back left and 10 moves
the cursor vertically down, leaving it directly under the first
character. 224 prints the character again. 8 moves the cursor
left and 10 moves the cursor down so that 224 prints the character
vertically under the other two.
We can also use:
PRINT CHR$(8)
to move the cursor left, and so on for the other cursor movements.
Although slightly longer, this has the advantage that we can add
all the characters, user defined and cursor movement, into one
string and then simply print out that string.
This results in the combined characters appearing far more quickly
than with the VDU method. (See Program III.)
10 REM *** PROGRAM III ***
20 MODE 4
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 character$=CHR$(224)+CHR$(8)+CHR $(10)+CHR$(224)+CHR$(8)+CHR$(10)+CHR$(
224)
50 PRINT character$
We use this technique in Program VII. Lines 210 to 290 each
add together the user defined characters required to make the
various shapes. Line 210, for example, defines a candle.
Notice how I use the name candle$ for the combined character
string - it's far more meaningful than, say, shapel$.
In Program VII, the actual user defined characters are defined
in lines 170 to 210, which read in the values for each VDU 23
from the DATA statements in lines 930 to 1060, 'VDUing' each parameter
at a time (line 190).
Notice how -1 is used in line 1060's DATA statement to mark
the end of the parameters.
When we print a user defined character, it prints, as we have
seen, in the currently defined foreground and background colour.
Suppose, however, that we want a two-colour foreground, such
that the centre pixels of our previous character and those in
the middle of the outer edges are yellow, while the other pixels
are red. (We'll leave the background black.)
You might think that we could do this by defining our character
(224) as before and printing it in red, then overprinting it in
yellow with another user defined character (225) containing only
the pixels we want to be yellow. The red pixels should then show
through.
Figure V. Overprinting
user defined characters
Figure V illustrates the idea. However, it isn't as straightforward
as this, as Program IV shows.
10 REM *** PROGRAM IV ***
20 MODE 1
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 VDU 23,225,24,0,0,153,153,0,0,24
50 COLOUR 1
60 PRINT TAB(19,15)CHR$(224)
70 COLOUR 2
80 PRINT TAB(19,15)CHRI(225)
What happens is that the second character's background is printed,
as well as its foreground, completely overwriting the first character!
We need some way of writing just the foreground of the characters
- making the background transparent.
We can do this by using the command VDU 5. After such a command,
the micro only prints the foreground of characters. This works
not only for user defined characters, but for the whole character
set as well.
There is one snag, though. VDU 5 also causes the characters
to be printed not at the text cursor, but where the graphics cursor
is.
Technically, we say that VDU 5 joins the text and graphics cursors.
However, it's the graphics commands that are used to position
the joined cursors - and to set colours - so I consider that VDU
5 causes characters to be printed at the graphics cursor.
This means that, after a VDU 5, to print a character at a certain
location on the screen you have to place the graphics cursor there
with the MOVE statement before PRINTing.
The numbers involved might take a bit more thought, but you
gain two advantages. Firstly, you can position your character
to the resolution of the screen, that is, within one pixel. With
the TAB statement, you are limited to the eight pixel resolution
of the character grid.
Secondly, of course, you only print the foreground of the character-there's
no background to overwrite things.
Another useful aspect of operations after VDU 5 is that the
cursor movement characters still work. For example, CHRS(8) still
moves the graphics/text cursor one whole character (eight pixels)
left from its previous position. Try Program V.
10 REM *** PROGRAM V ***
20 MODE 1
30 VDU 5
40 VDU 23,224,153,66,36,153,153,36, 66,153
50 VDU 23,225,24,0,0,153,153,0,0,24
60 GCOL0,1
70 MOVE 640,512:PRINT CHR$(224)
80 GCOL0,2
90 MOVE 640,512:PRINT CHR$(225)
100 VDU 4
Notice the use of VDU 4 at the end -this turns off the VDU 5.
You should always tidy up this way - leaving VDU 5 in operation
can cause problems. Try listing a long program with VDU 5 on to
see what I mean.
Also, the relevant colours are the graphics colours, not the
text colours, so we use GCOLO instead of COLOUR.
A word of warning. Don't use TAB with VDU 5 on. Although you
can get away with it in OS 0.1, your program won't work when you
upgrade to later systems.
Now run Program VI.
10 REM *** PROGRAM VI ***
20 MODE 1
30 VDU 5
40 VDU 23,224,153,66,36,153,153,36, 66,153
50 VDU 23,225,24,0,0,153,153,0,0,24
60 character$=CHR$(18)+CHR$(0)+CHR$ (1)+CHR$(224)+CHR$(18)+CHR$(0)+CHR$(2)
+CHR$(8)+CHR$(225)
70 MOVE 640,512
80 PRINT character$
90 VDU 4
100 VDU 4
This has the same effect as Program V. In this case we define
a string to print out our multi coloured character (line 50).
The latter might need some explanation.
Instead of using GCOL0,1 we can:
PRINT CHR$(l8) + CHR$(0) + CHR$(1) where CHR$(18) is in place
of GCOL
CHR$(0) is in place of 0 and CHR$(1) is in place of 1 So, the
first four CHR$ statements of line 60 give the foreground colour
1 (red), followed by the user defined character (224). The next
three CHR$ statements change the graphics colour to logical colour
2 (yellow), then CHR$(8) performs a backspace, ensuring that CHR$(225)
overprints the first character.
We use this technique in Program VII to obtain our fairy. Line
270 adds together the string fairy$, consisting of its body. This
is printed over wfairy$ which is the fairy's body plus wings (PROCfairy,
710-780).
fairy$ and wfairy$ each consist of nine user defined characters,
in a 3 x 3 grid. Actually, two of the characters of fairy$ (235
and 237) are completely blank. Since one variable overprints the
other, I decided to keep the grid standard for easier handling.
Similarly, we print a filled-in ball, fball$, followed by its
outer rim, uball$, on top of it (PROCballs, 590-690).
The candles are printed out as a combined string (candle$) in
PROC-candles (490-570).
The actual tree shape is drawn by printing three green triangles,
the apexes of which overlap (PROCtriangles, 320-390). The base
of the tree is drawn in PROCbase (410-470).
PROCmessage simply prints out a seasonal greeting, using VDU
5 to give a sort of 3D effect. Program VIII illustrates the technique.
What it does is to print out 'A WORD' in yellow several times,
each time moving it down four graphic units to the left and down
(line 60). This is because, each time through the loop, the number
we reduce the X and Y coordinates by, 1%, is increased by 4.
The reason we move four graphic units at a time is because this
is the minimum resolution in Mode 1.
When you exit a loop, the loop variable is still incremented
before the check to see if the loop should be terminated is made.
So, when line 90 is reached, 1% is four greater than when the
loop last printed 'A WORD'. Also the colour has changed to red
(line 100) so line 110 prints out 'A WORD' in red, one pixel down
and to the left of the last 'A WORD'.
Try to decipher Program VII -working through other people's
programs is one of the best ways to improve your own programming.
Next month, after this seasonal diversion, we'll return to the
graphics straight and narrow with the GCOL statements I promised!