Lesson 5: Graphics

BASIC originally came with a  primitive set of graphics commands to create dots, lines, boxes, circles and to allow painting of screen areas. Even though the commands are very simple a creative programmer could do very interesting things with them. In this lesson we'll cover these primitive commands and in a later lesson step it up with new fancy graphics commands offered by QB64.

A quick note about new QB64 commands first

In this lesson commands are going to be introduced that were not native to QuickBasic but instead introduced by QB64. These new commands enhance the BASIC language to take advantage of today's more powerful computers and  features that were not available many years ago. Most new commands introduced by QB64 start with an underscore ( _ ) character. This was done for compatibility reasons since QB64 was written to allow BASIC source code from the 1970's and later to compile and run.

One of the new commands you'll learn about is _NEWIMAGE(). Let's say someone wrote a program back in 1992 and created a variable in their code called NewImage%.  If QB64 did not precede these new commands with an underscore that variable created way back then would be interpreted as a command in QB64. By adding the underscore to new commands old source code has a better than 99% probability of compiling and running just fine in modern versions of QB64.

A programmer can also create custom subroutines and functions and name them anything they like (more on subroutines and functions in Lesson 6). If a programmer made a function called NewImage() then again QB64 would interpret this as a command rather than a custom function written many years ago.

There are literally hundreds of thousands, if not millions, of BASIC source code files scattered around the Internet. Because the developers of QB64 took compatibility into account you can be fairly certain that code from 1989 you found on the Internet will compile and run in QB64 today.

Colors

Before we get into graphics commands we need to learn about color use in QB64 first. Most graphics commands have a color option attached to them allowing for color customization. While there are options in QB64 to work with 256 color screens this course is only going to work with 32bit color (16 million) screens. This is just going to be a quick introduction to colors in QB64. We will delve into this topic in much greater detail in Lesson 14.

The _RGB32() statement is the most common way to set or create colors in QB64. There are a wide variety of color commands available in QB64 but this will be the one we use in this lesson_RGB32() returns an unsigned long integer data type. Unsigned long integers have a range of 0 through 4,294,967,295 which is also the maximum value of a 32bit number (232 = 4,294,967,296). The _RGB32() statement creates colors by mixing red, green, and blue values together.

Color~& = _RGB32(red%, green%, blue%)

There are 256 levels of each color available (0 through 255). By varying the levels for red, green, and blue you can make up to 16 million different color combinations (256 * 256 * 256 = 16,777,216). There is also a fourth optional parameter for changing the alpha layer, or transparency of a color, using the same 256 levels. This is why we need to use an unsigned long integer variable type to store color information (red(256) * green(256) * blue(256) * alpha(256) = 4,294,967,295). Unsigned long integers can be created with the use of ~& (the tilde character means "unsigned" and as we learned before the & character means long integer). Some examples are:

BrightRed~& = _RGB32(255, 0, 0) '   red set to full intensity
Red~& = _RGB32(127, 0, 0) '         red set to half intensity
BrightGreen~& = _RGB32(0, 255, 0) ' green set to full intensity
BrightBlue~& = _RGB32(0, 0, 255) '  blue set to full intensity
White~& = _RGB32(255, 255, 255) '   all three colors set to full intensity
Gray~& = _RGB32(127, 127, 127) '    all three colors set to half intensity

Many times you'll see programs that have constants with common colors used throughout the source code:

CONST WHITE~& = _RGB32(255, 255, 255)
CONST GRAY~& = _RGB32(127, 127, 127)
CONST DARKGRAY~& = _RGB32(63, 63, 63)
CONST PURPLE~& = _RGB32(255, 0, 255) ' red and blue full intensity
CONST YELLOW~& = _RGB32(255, 255, 0) ' red and green full intensity

We'll get much deeper into the workings of color and the other color statements available in Lesson 14.

The SCREEN Statement

Up to this point we have been doing everything in a text only screen (known as SCREEN 0) where graphics are not permitted. The first thing we need to do is get QB64 into a graphics screen mode. Think of a graphics screen as a blank sheet of graph paper with each individual little square at your command. Each one of those little squares is known as a pixel which is the smallest point that can be referenced on a graphics screen. A pixel is literally a single point of colored light that you can control. Your computer's monitor screen is made up of millions of pixels (2,073,600 for a 1920x1080 monitor). You can reference each and every one of those pixels using an X,Y coordinate system built into QB64.

Legacy Screens

The SCREEN statement is used to put QB64 into a graphics mode. SCREEN 0 is the text mode we have been working with up to this point and is considered the default SCREEN. Since QB64 was developed to support GWBASIC and QuickBasic source code from the 80's and 90's, SCREEN 1 through SCREEN 13 graphics modes are also available. However, these legacy SCREEN graphics modes are very limited in both size and colors available when working with today's graphics cards and monitors. As graphics cards evolved over time (MDA, CGA, EGA, VGA) the BASIC language had to add new SCREEN modes to take advantage of them. The syntax for the legacy SCREEN statement is (used in GWBasic, QuickBasic, and supported by QB64):

SCREEN mode%, , active%, visual%

mode% sets the screen into the desired mode (see list below)
active% is the page of video RAM currently being written to (more on this in a bit)
visual% is the page of video RAM currently being displayed on the monitor (again, more in a bit)

NOTE: there is a parameter missing between the first two commas in the syntax above. This was used for color switching (on and off) in GWBasic and QuickBasic and is no longer supported by QB64.

Here is a list of the legacy SCREEN modes supported by QB64:

You probably noticed the omission of SCREEN modes 3 through 6. These modes were available on systems with specialized video adapters such as the IBM PCjr computer, the Tandy 1000 series of computers (known as TGA), the Hercules video adapter, the Olivetti M series of computers, and the AT&T 6300 series systems (an Olivetti M24 in disguise). These systems shipped with a modified version of GWBasic to take advantage of the enhanced video adapters they included. QuickBasic never natively supported these specialized legacy SCREEN modes although there were third party libraries available if these modes were needed but it was still quite uncommon.

A program included with the tutorial named LegacyModes.BAS displays all of the above SCREEN legacy modes along with their color capabilities. Load the program into your IDE and run it now. Press the ENTER key to move through the SCREEN modes or the ESC key to exit at any time.

( This program can be found at .\tutorial\Lesson5\LegacyModes.bas )

Figure 0: The legacy SCREEN modes

As you can tell after running the program these legacy screens are quite small and limited in their color capabilities. SCREEN 13, the 320x200 256 color mode, was by far the most popular when writing games in QuickBasic but it would be much too small to be usable on today's large monitors. You can literally fit 30 SCREEN 13s onto a standard 1920x1080 wide screen monitor! Heck, all of them fit into a nice little image as Figure 0 illustrates above. It's also important to note that while some of these SCREEN modes are extremely rectangular, on a CRT monitor they would have been stretched to a 4:3 aspect ratio creating pixels greater in height than in width.

The _NEWIMAGE Statement

SVGA (Super Video Graphics Array) and XGA (eXtended Graphics Array) added the screen modes 800x600, 1024x768, and 1280x1024. Other standards followed to offer even higher resolutions to accommodate wide screen and high definition formats. QB64 allows the creation of these formats by pairing the SCREEN statement with the _NEWIMAGE() statement. _NEWIMAGE creates an image canvas in RAM identified by a numeric handle value that can be used as the output screen. QB64 has modified the SCREEN syntax as follows to allow this:

SCREEN ImageHandle&, ,active%, visual%

ImageHandle& is any image created through the use of QB64's image statements
active% is the page of video RAM currently being written to (more on this in a bit)
visual% is the page of video RAM currently being displayed on the monitor (again, more in a bit)

The syntax for the _NEWIMAGE statement is:

Handle& = _NEWIMAGE(Width%, Height%, Mode% {0|1|2|7|8|9|10|11|12|13|256|32})

Width% is the width of the image in pixels to be created
Height% is the height of the image in pixels to be created
Mode% is the image mode to emulate (0 for text, 1 through 13 for legacy, 256 for 256 color, and 32 for 32 bit color)

To create an SVGA screen of 1024x768 that supports 32 bit color (16+ million colors) the following line of code would be used:

SCREEN _NEWIMAGE(1024, 768, 32) ' create a 1024x768 32 bit color graphics output screen

_NEWIMAGE(1024, 768, 32) creates an image canvas in RAM and assigns a numeric value to it. The SCREEN statement uses that numeric value to identify the output screen. When _NEWIMAGE is paired with the SCREEN statement that numeric value will always be zero (0). Statements that you'll learn about in later lessons allow you to identify the image canvas to draw on. Image zero (0) will always be the output screen.

It's even possible to create a text only screen with custom text character dimensions:


SCREEN _NEWIMAGE(132, 66, 0) ' create a text only output screen of character dimension 132x66

By choosing a Mode% of zero (0) the _NEWIMAGE statement will emulate the text only legacy SCREEN 0.  Here we've chosen to make the screen 132 characters wide by 66 characters high. These are the exact dimensions of a wide format sheet of printer paper. Note that when emulating SCREEN 0 the dimensions supplied are in characters and not pixels. Emulating screen modes 1, 2, 7, 8, 9, 10, 11, 12, and 13 need dimensions supplied in pixels. The same is true for modes 256 (256 colors) and 32 (32 bit color).

The _PIXELSIZE statement

At some point in your program you may need to know which mode was used to create an image or SCREEN. The _PIXELSIZE statement returns one of three values that identify the mode your SCREEN or an image is in. The syntax for the _PIXELSIZE statement is:

Psize% = _PIXELSIZE(ImageHandle&)

ImageHandle& is the numeric handle value of the image you wish to test. Psize% will contain one of three values:

Remember, the current output screen will always be zero (0) so just supply a value of zero (0) for ImageHandle& to identify the current SCREEN mode.

SCREEN _NEWIMAGE(640, 480, 32) ' 32 bit color mode

PRINT _PIXELSIZE(0) '            will print the value of 4

While _PIXELSIZE may not seem to be a handy statement at the moment it will become useful later on as your programming skills progress. The value _PIXELSIZE returns is actually the number of bytes needed to store a single pixel's color information in RAM. This information is required when dealing with direct image memory manipulation or routines that need to create new images that match the current mode of existing images or the output screen.

The Graphics SCREEN

Lesson 13 will introduce the different ways that QB64 can create images but in this lesson we'll focus on the _NEWIMAGE() statement to create an image that can be used as an output screen. Type in the following program that illustrates how a graphics screen is created and laid out as shown in Figure 1.

( This code can be found at .\tutorial\Lesson5\ScreenDemo.bas )

'------------------------
'- SCREEN demonstration -
'------------------------

CONST WHITE = _RGB32(255, 255, 255) ' set colors
CONST YELLOW = _RGB32(255, 255, 0)

SCREEN _NEWIMAGE(640, 480, 32) '      switch into a 640x480 graphics screen
CLS '                                 clear the graphics screen
PSET (0, 0), WHITE '                  turn on white pixels
PSET (319, 239), WHITE
PSET (639, 479), WHITE
LINE (5, 5)-(50, 50), YELLOW '        draw yellow lines
LINE (5, 5)-(5, 15), YELLOW
LINE (5, 5)-(15, 5), YELLOW
LINE (319, 234)-(319, 189), YELLOW
LINE (319, 234)-(309, 224), YELLOW
LINE (319, 234)-(329, 224), YELLOW
LINE (634, 474)-(584, 424), YELLOW
LINE (634, 474)-(624, 474), YELLOW
LINE (634, 474)-(634, 464), YELLOW
LINE (0, 279)-(639, 279), YELLOW
LINE (0, 279)-(10, 269), YELLOW
LINE (0, 279)-(10, 289), YELLOW
LINE (639, 279)-(629, 269), YELLOW
LINE (639, 279)-(629, 289), YELLOW
LINE (150, 0)-(150, 479), YELLOW
LINE (150, 0)-(160, 10), YELLOW
LINE (150, 0)-(140, 10), YELLOW
LINE (150, 479)-(160, 469), YELLOW
LINE (150, 479)-(140, 469), YELLOW
LOCATE 4, 2 '                         place text on screen
PRINT "Coordinate 0,0"
LOCATE 5, 3
PRINT "(White dot)"
LOCATE 11, 34
PRINT "Center 319,239"
LOCATE 12, 35
PRINT "(White dot)"
LOCATE 26, 62
PRINT "Coordinate 639,479"
LOCATE 27, 66
PRINT "(White dot)"
LOCATE 18, 23
PRINT "Width of screen 640 pixels (0-639)"
LOCATE 25, 3
PRINT "Height of screen 480 pixels (0-479)"
SLEEP '                               wait for user to press a key
SYSTEM '                              return to Windows

Figure 1: A 640x480 graphics screen

A graphics screen of 640 pixels wide by 480 pixels high was created using this line of code:

SCREEN _NEWIMAGE(640, 480, 32) '      switch into a 640x480 graphics screen

It's important to remember that to a computer zero is a number like any other and most operations start with zero instead of one. In the example program we created a 640 by 480 screen but Figure 1 points out that the coordinate system is actually referenced as 0-639 for width and 0-479 for height. That's because of the computer starting with zero. If you count zero as a number then 0 through 639 is actually 640 pixels and 0 through 479 is 480 pixels. That's also why in Figure 1 the center point is referenced as 319, 239 instead of 320, 240. Because the computer starts at zero everything seems "off" by one pixel. You'll need to remember this when creating graphics using QB64. Note that this is not something QB64 alone does, all programming languages use zero as a starting point for their graphics screen.

SCREEN Pages and Flipping

An output screen can have multiple pages associated with it. Remember these two methods of creating output screens?

Legacy SCREEN:

SCREEN mode%, , active%, visual%

mode% sets the screen into the desired mode
active% is the page of video RAM currently being written to
visual% is the page of video RAM currently being displayed on the monitor

QB64 modified SCREEN:

SCREEN ImageHandle&, ,active%, visual%

ImageHandle& is any image created through the use of QB64's image statements
active% is the page of video RAM currently being written to
visual% is the page of video RAM currently being displayed on the monitor

It's time to discuss the active% and visual% video RAM pages associated with them. Legacy SCREENs were created in the video card's RAM. In many cases an output screen would only use a portion of that video RAM. That extra video RAM could be used to create multiple instances of the output screen that could be called upon.

For instance, If you had an EGA video card that contained 256K of RAM and created a game using SCREEN 7 (320x200 with 16 color) that output screen only required 32K of video RAM. The remaining video RAM could be broken into 32K chunks containing more output screens for a total of 8 screens (256K divided by 32K). Those 7 extra screens could then be written to and viewed at will. Many GWBasic programmers took advantage of this and used these hidden pages to hold sprite images instead of using the precious little system RAM that early computers had. Through the use of the GET and PUT graphics statements these sprite images could be copied from a hidden screen and written to the output screen.

I used this to my advantage while writing my Connect Four game back in 1992 (more on that here). When the game was first started page 0, the output screen, remained blank until page 1 was finished drawing the title screen. Page 1 was then copied to page 0 to reveal the title screen while page 2 drew the sprites needed for the game. When that was finished an image of the playing field was drawn on page 3. The whole time this drawing was going on in the background page 0 was still showing the title screen. Page 3 now contained a clean image of the playing field whenever I needed it, page 1 held a copy of the title screen, and page 2 a copy of the sprites. Once all the drawing was finished page 3 was copied to page 0 and the game began. When the game finished I could simply copy page 3 back to page 0 to clear the screen with a fresh playing field to start a new game. Or, if the player decided to exit the game back the title screen I could simply copy page 1 which contains a copy of the title screen back to page 0. Drawing graphics on a Tandy 1000EX 8088 computer was painfully slow in GWBasic but I took advantage of that by using that time to display just the title screen. The title screen would display for about 5 seconds, the time needed to draw all the graphics assets needed in the background. If you were to run that same program in QB64 today the title screen would blip by so fast you wouldn't even see it!

Traditional animation, film, and video is achieved by flipping images very quickly in front of the eye. This same "page flipping" technique was very common in QuickBasic using one page to draw on while the other was being shown, then the pages were flipped, with the opposite pages being drawn and shown. Here is a quick example of page flipping:

'---------------
' Page flipping
'---------------

x% = 159 ' x coordinate
y% = 99 '  y coordinate
a% = 1 '   active page being written to
v% = 0 '   visual page being shown

DO '                                               begin animation loop
   SCREEN 7, , a%, v% '                            set active and visual pages
    IF y% > 0 THEN '                               draw another square?
       LINE (x%, y%)-(319 - x%, 199 - y%), 14, B ' yes, draw yellow square
        x% = x% - 3 '                              decrement x coordinate
        y% = y% - 3 '                              decrement y coordinate
   END IF
    a% = 1 - a% '                                  flip active page
    v% = 1 - v% '                                  flip visual page
   _DELAY .1 '                                     1/10th second delay (10 FPS)
LOOP UNTIL _KEYDOWN(27) '                          leave when ESC key pressed

While some legacy screens had more pages than others, and some of them only having one, QB64 has removed these limitations completely. All SCREENs, regardless of mode (this includes legacy SCREENs), can have as many video pages as available system RAM allows. This even includes SCREEN 0 and an emulated SCREEN 0 created with _NEWIMAGE.

The PCOPY Statement

The PCOPY statement allows the programmer to take advantage of the multiple pages associated with a SCREEN as discussed in the section above. The PCOPY statement allows any SCREEN page to be copied to a different SCREEN page. The syntax for the PCOPY statement is:

PCOPY SourcePage%, DesinationPage%

SourcePage% is the SCREEN page you wish to copy and DestinationPage% is the SCREEN page to receive the copy. Let's look at an example of this in code first.

Type the following program into your IDE.

( This code can be found at .\tutorial\Lesson5\PCOPYexample.bas )

'---------------------------------------------------
' PCOPY Example
'
' by Terry Ritchie  04/22/24
'
' A simple game using PCOPY to update the screen
'
' Page 0 - the main viewing page
' Page 1 - a copy of the title screen (animation 1)
' Page 2 - a copy of the title screen (animation 2)
' Page 3 - a copy of the game screen
'---------------------------------------------------

CONST WHITE~& = _RGB32(255, 255, 255) ' define the color white
CONST BLACK~& = _RGB32(0, 0, 0) '       define the color black

DIM ScreenImage& ' the main graphics screen
DIM ActivePage% '  page being written to
DIM ViewPage% '    page being viewed
DIM KeyPress$ '    any key press on keyboard
DIM x% '           player x coordinate
DIM y% '           player y coordinate
DIM ex! '          enemy x coordinate
DIM ey! '          enemy y coordinate
DIM Distance! '    distance from player to enemy

ScreenImage& = _NEWIMAGE(640, 640, 32) '              create main graphics screen
SCREEN ScreenImage&, , 1, 0 '                         page 1 active and viewing page 0

'+-----------------------------------------------------+
'| Create an animated title screen using pages 1 and 2 |
'+-----------------------------------------------------+

LOCATE 15, 21 '                                       position text cursor
PRINT "WELCOME TO MY AWESOME MONOCHROME GAME!" '      print title screen text
LOCATE 18, 24
PRINT "PRESS ANY KEY TO BEGIN THE GAME!" '            print instructions
LOCATE 21, 33
PRINT "(ESC to Exit)"
PCOPY 1, 2 '                                          copy this page to page 2
ActivePage% = 0 '                                     set active page
FOR x% = 0 TO 640 STEP 64 '                           cycle horizontally in steps of 64
   SCREEN ScreenImage&, , ActivePage% + 1, 0 '        set active screen page
   LINE (x%, 0)-(x% + 63, 63), WHITE, BF '            draw a filled box at top of screen
   LINE (639 - x%, 575)-(575 - x%, 639), WHITE, BF '  draw a filled box at bottom of screen
    ActivePage% = 1 - ActivePage% '                   flip active page
NEXT x%

'+------------------------------+
'| Draw a game screen on page 3 |
'+------------------------------+

SCREEN ScreenImage&, , 3, 0 '                         make page 3 the active page
CLS , WHITE '                                         clear the page white
LINE (10, 10)-(629, 629), BLACK, BF '                 draw black box (creates border)
LOCATE 2, 18: PRINT "DON'T LET THE SOLID CIRCLE CATCH YOUR CIRCLE"
LOCATE 3, 27: PRINT "USE THE ARROW KEYS TO MOVE"
LINE (0, 50)-(639, 59), WHITE, BF '                   draw border line under text

'+-----------------+
'| Begin main code |
'+-----------------+

SCREEN ScreenImage&, , 0, 0 '                                   active and view page set to 0
DO '                                                            begin main program loop
    ViewPage% = 0 '                                             set view page
   DO '                                                         begin title screen loop
       _LIMIT 5 '                                               5 frames per second
       PCOPY ViewPage% + 1, 0 '                                 copy page 1 or 2 to page 0
        ViewPage% = 1 - ViewPage% '                             flip view page
        KeyPress$ = INKEY$ '                                    get any key that was pressed
       _DISPLAY '                                               update screen (remove flicker)
   LOOP UNTIL KeyPress$ <> "" '                                 leave loop when key pressed
   IF KeyPress$ = CHR$(27) THEN SYSTEM '                        return to OS if ESC pressed
    x% = 539 '                                                  initialize player position
    y% = 539
    ex! = 99 '                                                  initialize enemy position
    ey! = 99
   DO '                                                         begin game play loop
       _LIMIT 240 '                                             240 frames per second
       PCOPY 3, 0 '                                             clear screen with page 3 image
       CIRCLE (x%, y%), 10, WHITE '                             draw player
       CIRCLE (ex!, ey!), 10, WHITE '                           draw enemy
       PAINT (ex!, ey!), WHITE, WHITE '                         paint enemy
       IF _KEYDOWN(19712) THEN x% = x% + 1 '                    move player right
       IF _KEYDOWN(19200) THEN x% = x% - 1 '                    move player left
       IF _KEYDOWN(18432) THEN y% = y% - 1 '                    move player up
       IF _KEYDOWN(20480) THEN y% = y% + 1 '                    move player down
       IF x% < 20 THEN x% = 20 ELSE IF x% > 619 THEN x% = 619 ' keep player x coordinate within limits
       IF y% < 70 THEN y% = 70 ELSE IF y% > 619 THEN y% = 619 ' keep player y coordinate within limits
        Distance! = _HYPOT(x% - ex!, y% - ey!) '                distance between player and enemy
        ex! = ex! + (x% - ex!) / Distance! '                    add normalized x vector to enemy
        ey! = ey! + (y% - ey!) / Distance! '                    add normalized y vector to enemy
       SOUND (1660 - Distance! * 2), .03 '                      higher pitch as enemy gets closer
       _DISPLAY '                                               update screen (remove flicker)
   LOOP UNTIL Distance! <= 20 '                                 leave when enemy catches player
   PLAY "O4L8CO3L16GGL8A-GRBO4C" '                              shave and a haircut 2 bits
   _KEYCLEAR '                                                  clear the keyboard buffer
LOOP '                                                          end main program loop

Lines 34 through 39 of the code print the text instructions to the active page 1 as set in line 28. Since the title screen is going to be animated through the use of pages 1 and 2 line 40 of the code copies the text currently on page 1 to page 2.

SCREEN ScreenImage&, , 1, 0 '                         page 1 active and viewing page 0

'+-----------------------------------------------------+
'| Create an animated title screen using pages 1 and 2 |
'+-----------------------------------------------------+

LOCATE 15, 21 '                                       position text cursor
PRINT "WELCOME TO MY AWESOME MONOCHROME GAME!" '      print title screen text
LOCATE 18, 24
PRINT "PRESS ANY KEY TO BEGIN THE GAME!" '            print instructions
LOCATE 21, 33
PRINT "(ESC to Exit)"
PCOPY 1, 2 '                                          copy this page to page 2

Lines 42 through 47 of the code draws solid boxes across the top and bottom of the screen. While these boxes are being drawn the active page is alternated between page 1 and page 2. This causes the boxes to be drawn in alternate positions on each active page and creates the animation effect. We now have our two title screen pages that can be flipped back and forth.

FOR x% = 0 TO 640 STEP 64 '                           cycle horizontally in steps of 64
   SCREEN ScreenImage&, , ActivePage% + 1, 0 '        set active screen page
   LINE (x%, 0)-(x% + 63, 63), WHITE, BF '            draw a filled box at top of screen
   LINE (639 - x%, 575)-(575 - x%, 639), WHITE, BF '  draw a filled box at bottom of screen
    ActivePage% = 1 - ActivePage% '                   flip active page
NEXT x%

Lines 53 through 58 of the code draws the game play screen on page 3. Page 3 can now be used to clear the screen during the game updates and retain the look of the playing field without having to redraw it over and over again.

SCREEN ScreenImage&, , 3, 0 '                         make page 3 the active page
CLS , WHITE '                                         clear the page white
LINE (10, 10)-(629, 629), BLACK, BF '                 draw black box (creates border)
LOCATE 2, 18: PRINT "DON'T LET THE SOLID CIRCLE CATCH YOUR CIRCLE"
LOCATE 3, 27: PRINT "USE THE ARROW KEYS TO MOVE"
LINE (0, 50)-(639, 59), WHITE, BF '                   draw border line under text

Lines 67 through 73 in the main code section simply copies page 1 and page 2 using PCOPY repeatedly while waiting for the player to press a key. Since page 1 and page 2 have alternating graphics the view screen appears animated. These 2 pages eliminate the need to constantly redraw the animated squares while waiting for the player.

   DO '                                                         begin title screen loop
       _LIMIT 5 '                                               5 frames per second
       PCOPY ViewPage% + 1, 0 '                                 copy page 1 or 2 to page 0
        ViewPage% = 1 - ViewPage% '                             flip view page
        KeyPress$ = INKEY$ '                                    get any key that was pressed
       _DISPLAY '                                               update screen (remove flicker)
   LOOP UNTIL KeyPress$ <> "" '                                 leave loop when key pressed

Finally, line 81 of the code uses the image of the game playing field on page 3 to clear the screen in preparation for player and enemy movement and object redraws. Once again, no need to redraw the static parts of the playing field over and over again.

     PCOPY 3, 0 '                                             clear screen with page 3 image

The PSET Statement

The PSET() statement (Pixel Set) is used to turn a single pixel on with a given color. PSET() requires the following parameters:

PSET(x%, y%), color~&

The x% and y% coordinate pair refer to the location on the screen where the pixel is to be manipulated. Color~& is provided by the _RGB32() statement explained above. In the previous example program PSET() was used to turn the upper left (0, 0), middle (319, 239), and lower right pixel (639, 479) on with a color of WHITE.

PSET (0, 0), WHITE '                  turn on white pixels
PSET (319, 239), WHITE
PSET (639, 479), WHITE

The LINE Statement

The LINE statement is used to draw lines, boxes, and filled boxes. The LINE statement takes the following parameters:

LINE(x1%, y1%) - (x2%, y2%), color~&, BF, style~%

The LINE statement needs a start coordinate (x1%, y1%), an end coordinate (x2%, y2%), a color~&, an optional B to create a box or an optional BF to create a filled box. An optional style~% can also be defined giving the line a pattern. Type the following program into your IDE to see all of the different ways LINE can be utilized.

( This code can be found at .\tutorial\Lesson5\LineDemo.bas )

'----------------------
'- LINE demonstration -
'----------------------

CONST YELLOW = _RGB32(255, 255, 0)

SCREEN _NEWIMAGE(640, 480, 32)
CLS
LOCATE 2, 30
PRINT "Line Demonstration"
LOCATE 4, 16
PRINT "LINE (69, 89)-(569, 429), _RGB32(255, 255, 0)"
LINE (69, 89)-(569, 429), YELLOW
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP
CLS
LOCATE 2, 30
PRINT "Line Demonstration"
LOCATE 4, 16
PRINT "LINE (69, 89)-(569, 429), _RGB32(255, 255, 0), B"
LINE (69, 89)-(569, 429), YELLOW, B
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP
CLS
LOCATE 2, 30
PRINT "Line Demonstration"
LOCATE 4, 16
PRINT "LINE (69, 89)-(569, 429), _RGB32(255, 255, 0), BF"
LINE (69, 89)-(569, 429), YELLOW, BF

As the above example code illustrates the B and BF parameters are optional. (x1%, y1%) is also optional. If you omit the start coordinates (x1%, y1%) from the LINE statement it uses the last known position as a starting point. Type in the next example to see how this works.

( This code can be found at .\tutorial\Lesson5\LineDemo2.bas )

'-------------------------
'- LINE demonstration #2 -
'-------------------------

CONST YELLOW = _RGB32(255, 255, 0) ' define yellow

SCREEN _NEWIMAGE(640, 480, 32) '     switch into a 640x480 graphics screen
CLS '                                clear the graphics screen
LINE (299, 219)-(319, 0), YELLOW '   draw a line
LINE -(339, 219), YELLOW '           draw a line from endpoint of last line
LINE -(639, 239), YELLOW '           draw a line from endpoint of last line
LINE -(339, 259), YELLOW '           ""
LINE -(319, 479), YELLOW '           ""
LINE -(299, 259), YELLOW '           etc..
LINE -(0, 239), YELLOW
LINE -(299, 219), YELLOW
LOCATE 16, 36
PRINT "PRESS A KEY"
SLEEP '                              pause computer until key pressed
SYSTEM '                             return control to OS

This is possible because a graphics cursor is kept track of by QB64. The graphics cursor resides at the endpoint of each line that is drawn by the LINE statement.

You can even add a little style to the lines you draw. Type in the next example program.

( This code can be found at .\tutorial\Lesson5\LineDemo3.bas )

'-------------------------
'- LINE demonstration #3 -
'-------------------------

CONST YELLOW = _RGB32(255, 255, 0) ' define yellow
DIM Style& '                         the line's style
DIM Bit% '                           the current bit in style to draw
DIM Dir% '                           style direction

Dir% = -1
SCREEN _NEWIMAGE(640, 480, 32) '                   switch into a 640x480 graphics screen
DO '                                               begin loop
   CLS '                                           clear the graphics screen
   _LIMIT 30 '                                     limit loop to 30 frames per second
   LOCATE 2, 33 '                                  print directions
   PRINT "A Stylized Line"
   LOCATE 4, 21
   PRINT "Press the Space Bar to change direction."
   LOCATE 6, 23
   PRINT "Press the Escape key to exit program."
   IF _KEYHIT = 32 THEN Dir% = -Dir% '             change bit counter direction when space bar pressed
    Bit% = Bit% + Dir% '                           increment bit counter
   IF Bit% = 16 THEN '                             keep bit counter between 0 and 15
        Bit% = 0
   ELSEIF Bit% = -1 THEN
        Bit% = 15
   END IF
    Style& = 2 ^ Bit% '                            calculate the line style
   LINE (99, 129)-(539, 409), YELLOW, B , Style& ' draw the line as a stylized box
   _DISPLAY '                                      update screen with changes
LOOP UNTIL _KEYDOWN(27) '                          leave loop when ESC key pressed

Figure 2: A line with style

Setting up line styles is done by creating a value that contains a 16 bit pattern. For example, if you were to create a line using the number 43690 you would get a line with every other pixel turned on. Using the value of 65280 you would produce a dashed line. Type in the following code and execute it to see different style values being used and how they affect the line's output.

( This code can be found at .\tutorial\Lesson5\LineStyle.bas )

'---------------
'- Line Styles -
'---------------

CONST YELLOW = _RGB32(255, 255, 0) '         define yellow
SCREEN _NEWIMAGE(640, 480, 32) '             switch into a 640x480 graphics screen
CLS
LINE (10, 10)-(629, 10), YELLOW, , 43690 '   a dotted line 1010101010101010
LINE (10, 50)-(629, 50), YELLOW, , 52428 '   a dotted line 1100110011001100
LINE (10, 90)-(629, 90), YELLOW, , 61680 '   a dashed line 1111000011110000
LINE (10, 130)-(629, 130), YELLOW, , 65280 ' a dashed line 1111111100000000

Decimal numbers ranging in value from 0 to 65535 translate to binary numbers containing 16 places (16 bits). The decimal number 43690 can be converted to the binary number 1010101010101010 creating a pattern that can be used in the line's style. Where ever a 1 resides the line's pixel is turned on and a 0 means the pixel is turned off. This pattern is repeated over and over throughout the length of the line. Likewise, 65280 converts to 1111111100000000 in binary so you get a dashed line, 8 pixels on, 8 pixels off, 8 pixels on, 8 pixels off, and so on.

We're not going to get into decimal to binary conversion here. You can simply use a calculator that supports programmer functions, such as the Windows calculator, to type in the binary pattern you wish and then have the calculator convert that pattern to decimal for you.

In the hands of a creative programmer the LINE statement can do some pretty wild things with very little code. Type the next program in to see the LINE statement on digital drugs.

( This code can be found at .\tutorial\Lesson5\Illusion.bas )

CONST CYAN = _RGB32(0, 255, 255) '         define cyan color
DIM x1%, y1% '                             upper left coordinates
DIM x2%, y2% '                             lower right coordinates
DIM Count% '                               FOR...NEXT loop counter
DIM Bit% '                                 pixel to turn on in style
DIM Style& '                               calculated style of line

SCREEN _NEWIMAGE(640, 480, 32) '           640 x 480 graphics screen
DO '                                       begin animation loop
   CLS '                                   clear the screen
   _LIMIT 60 '                             limit animation to 60 frames per second
    x1% = 0 '                              set starting coordinates
    y1% = 0
    x2% = 639
    y2% = 479
   PSET (0, 0), CYAN '                     set pixel to start with
    Bit% = Bit% + 1 '                      increment style bit counter
   IF Bit% = 16 THEN Bit% = 0 '            keep bit between 0 and 15
    Style& = 2 ^ Bit% '                    calculate line style
   FOR Count% = 1 TO 60 '                  cycle through line corkscrew 60 times
       LINE -(x2%, y1%), CYAN, , Style& '  draw lines with style
       LINE -(x2%, y2%), CYAN, , Style&
       LINE -(x1%, y2%), CYAN, , Style&
        y1% = y1% + 4 '                    update coordinates
        x2% = x2% - 4
        y2% = y2% - 4
       LINE -(x1%, y1%), CYAN, , Style& '  draw next line
        x1% = x1% + 4 '                    update coordinate
   NEXT Count%
   _DISPLAY '                              update screen with changes
LOOP UNTIL _KEYHIT '                       leave animation loop when key pressed

Figure 3: An animated illusion made with lines

And here is the code with straight lines.

Warning: This code creates an effect that may induce an epileptic seizure. If you are prone to such seizures do not execute this code.

( This code can be found at .\tutorial\Lesson5\Illusion2.bas )

CONST CYAN = _RGB32(0, 255, 255) ' define cyan color
DIM x1%, y1% '                     upper left coordinates
DIM x2%, y2% '                     lower right coordinates

SCREEN _NEWIMAGE(640, 480, 32) '   640 x 480 graphics screen
x2% = 639 '                        set starting coordinates
y2% = 479
PSET (0, 0), CYAN '                set pixel to start with
FOR Count% = 1 TO 60 '             cycle through line corkscrew 60 times
   LINE -(x2%, y1%), CYAN '        draw lines
   LINE -(x2%, y2%), CYAN
   LINE -(x1%, y2%), CYAN
    y1% = y1% + 4 '                update coordinates
    x2% = x2% - 4
    y2% = y2% - 4
   LINE -(x1%, y1%), CYAN '        draw next line
    x1% = x1% + 4 '                update coordinate
NEXT Count%
SLEEP '                            leave when key pressed
SYSTEM '                           return control to OS

Figure 4: Makes the eyes go doink!

The CIRCLE Statement

The CIRCLE statement is used to draw full or partial circles and ellipses onto a graphics screen. Let's start with a simple circle. The CIRCLE statement requires a minimum of three parameters:

CIRCLE (x%, y%), radius%, color~&

The (x%, y%) pair are coordinates located on the graphics screen at the center of the circle. radius% is the distance from the center point to draw the circle's outer boundary. Remember that the radius of a circle is half the diameter. color~& is optional but will probably always be supplied as the default white can get boring. Type the following code into your IDE.

( This code can be found at .\tutorial\Lesson5\Circle.bas )

'-- Circle demonstration

CONST YELLOW~& = _RGB32(255, 255, 0)

SCREEN _NEWIMAGE(640, 480, 32)
CLS
CIRCLE (319, 239), 100, YELLOW ' a yellow circle with a radius of 100 pixels
LOCATE 2, 33
PRINT "A Yellow Circle"

Figure 5: A yellow circle

By default the CIRCLE statement draws a full 360 degree arc with an aspect ratio of one. You can override these values to draw only part of a circle, or an arc, by specifying the start and stop radian values of the arc. You can also supply an aspect ratio to change the circle into an ellipse.

CIRCLE (x%, y%), radius%, color~&, start_radian!, stop_radian!, aspect!

To better illustrate this type in the following program and execute it.

Note: Don't be intimidated by the length of the code. Type in one line at a time until you're finished. If you are unwilling or highly hesitant to type this tiny bit of code in you may want to rethink your programmer ambitions.

( This code can be found at .\tutorial\Lesson5\Radians.bas )

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

CONST PI = 3.1415926
CONST RED = _RGB32(255, 0, 0)
CONST YELLOW = _RGB32(255, 255, 0)

DIM StartRadian! ' starting point of circle arc
DIM StopRadian! '  ending point of circle arc
DIM Aspect! '      aspect ratio of circle (ellipses)
DIM Sign% '        holds the sign (-1 or +1) of arc radians

'----------------------------
'- Main Program Begins Here -
'----------------------------

SCREEN _NEWIMAGE(640, 480, 32) '                initiate a graphics screen
StartRadian! = 2 * PI '                         set initial arc starting point
StopRadian! = 0 '                               set initial arc ending point
Aspect! = 1 '                                   set initial aspect ratio of circle
Sign% = 1 '                                     set radian sign

DO '                                            main loop begins here
   CLS '                                        clear the screen
    StopRadian! = StopRadian! + 2 * PI / 360 '  increment stop radian in 360ths
   IF StopRadian! > 2 * PI THEN '               has arc done a full sweep?
        StopRadian! = 0 '                       yes, reset the arc end point
   END IF
   LINE (319, 119)-(319, 359), RED '            draw vertical red line
   LINE (199, 239)-(439, 239), RED '            draw horizontal red line
   CIRCLE (319, 239), 100, YELLOW, Sign% * StartRadian!, Sign% * StopRadian!, Aspect! ' draw yellow arc
   LOCATE 2, 18 '                               display instructions
   PRINT "CIRCLE radian demonstration for creating arcs"
   LOCATE 4, 30
   IF Sign% = 1 THEN
       PRINT "Stop radian ="; StopRadian!
   ELSE
       PRINT "Stop radian ="; -StopRadian!
   END IF
   LOCATE 5, 29
   PRINT "Aspect ratio ="; Aspect!
   LOCATE 7, 32
   PRINT "Half PI 1.5707963"
   LOCATE 24, 32
   PRINT "1.5x PI 4.7123889"
   LOCATE 15, 57
   PRINT "0 or 2x PI 6.2831852"
   LOCATE 15, 13
   PRINT "PI 3.1415926"
   LOCATE 27, 3
   PRINT "Press SPACEBAR to change arc type, hold UP/DOWN arrow keys to change ellipse"
   LOCATE 29, 26
   PRINT "Press ESC key to exit program";
   IF _KEYHIT = 32 THEN '                       did user press space bar?
        Sign% = -Sign% '                        yes, change the sign
   END IF
   IF _KEYDOWN(18432) THEN '                    is user holding UP arrow?
        Aspect! = Aspect! + .005 '              yes, increment aspect ratio
       IF Aspect! > 3 THEN '                    is aspect ratio greater than 3?
            Aspect! = 3 '                       yes, limit its value to 3
       END IF
   END IF
   IF _KEYDOWN(20480) THEN '                    is user holding DOWN arrow?
        Aspect! = Aspect! - .005 '              yes, decrement aspect ratio
       IF Aspect! < .25 THEN '                  is aspect ratio less than .25?
            Aspect! = .25 '                     yes, limit its value to .25
       END IF
   END IF
   _LIMIT 60 '                                  limit loop to 60 FPS
   _DISPLAY '                                   update screen with changes
LOOP UNTIL _KEYDOWN(27) '                       leave loop when ESC key pressed
SYSTEM '                                        return to Windows

Figure 6: Radian demonstration

Radians fall in the range of 0 to 6.2831852 or 2 times Pi. Supplying the CIRCLE statement with positive radian values produces an arc from start_radian! to stop_radian!. Supplying negative radian values to CIRCLE produces the same arc but this time with lines drawn from the radian endpoints back to the center of the arc. This creates a pie looking structure as seen in Figure 6  above. Arcs are always drawn in a counter-clockwise direction. If you were to designate Pi as the start radian value and half Pi as the end radian value the end result would not be an arc drawn between those two points in the upper left quadrant. The arc would include the entire circle except for this quadrant because of the arc being drawn counter-clockwise.

This Wikipedia article on radians does a very good job of explaining the relationship between a circle's radius and circumference.

The example program also illustrates how you can affect the CIRCLE statement's aspect ratio. The default aspect ratio for the CIRCLE statement is 1 resulting in a perfectly round circle. Smaller aspect values squash the ellipse while larger values make the ellipse taller. If you simply want to change the aspect ratio of a circle without having to give radians you can simply skip over them like so:

CIRCLE (319, 239), 100, _RGB32(255, 255, 0), , , 2 ' create a tall yellow ellipse

By supplying no values for the start and end radian the CIRCLE statement uses the default values that create a closed arc, also known as a circle.

Here is a program that shows the power of the CIRCLE statement and how creative programming can use statements in ways that seem to defy their purpose. This example uses the CIRCLE statement to draw straight lines creating the hands of a clock and the tick marks going around the clock face.

( This code can be found at .\tutorial\Lesson5\CircleClock.bas )

'-------------------------------------------------------
'Description: Draws a functioning clock using circles
'Author     : Terry Ritchie
'Date       : 11/12/13
'Updated    : 04/09/20
'Version    : 2.1
'Rights     : Open source, free to modify and distribute
'Terms      : Original author's name must remain intact
'-------------------------------------------------------

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

CONST GRAY = _RGB32(127, 127, 127) '    define colors
CONST DARKGRAY = _RGB32(63, 63, 63)
CONST LIGHTGRAY = _RGB32(191, 191, 191)
CONST BLACK = _RGB32(0, 0, 0)
CONST WHITE = _RGB32(255, 255, 255)
CONST RED = _RGB32(127, 0, 0)
CONST YELLOW = _RGB32(255, 255, 0)

DIM Clock!(60) ' 60 radian points around circle
DIM Tick% '      counter to keep track of tick marks
DIM Tc~& '       tick mark color
DIM Radian! '    FOR...NEXT radian counter
DIM h% '         value of hour extracted from TIME$
DIM m% '         value of minute extracted from TIME$
DIM s% '         value of second extracted from TIME$
DIM Tm$ '        current TIME$ value

'----------------------------
'- Main Program Begins Here -
'----------------------------

SCREEN _NEWIMAGE(640, 480, 32) '                                     initiate graphics screen
Tick% = 15 '                                                         first position is 15 seconds
FOR Radian! = 6.2831852 TO 0 STEP -.10471975 '                       clockwise from 2*Pi steps of -60ths
    Clock!(Tick%) = Radian! '                                        save circle radian seconds position
    Tick% = Tick% + 1 '                                              move to next seconds position
   IF Tick% = 60 THEN Tick% = 0 '                                    reset to 0 seconds position if needed
NEXT Radian!
DO '                                                                 begin main loop
   CLS '                                                             clear the screen
   _LIMIT 5 '                                                        5 updates per second
   CIRCLE (319, 239), 120, GRAY '                                    draw outer circle of clock
   PAINT (319, 239), DARKGRAY, GRAY '                                paint circle dark gray
   CIRCLE (319, 239), 110, BLACK '                                   draw inner circle of clock
   PAINT (319, 239), WHITE, BLACK '                                  paint face bright white
   FOR Tick% = 0 TO 59 '                                             cycle through radian seconds positions
       IF Tick% MOD 5 = 0 THEN Tc~& = BLACK ELSE Tc~& = LIGHTGRAY '  5 second ticks are black in color
       CIRCLE (319, 239), 109, Tc~&, -Clock!(Tick%), Clock!(Tick%) ' draw radian line from center of circle
   NEXT Tick%
   CIRCLE (319, 239), 102, DARKGRAY '                                draw circle to cut off radian lines
   PAINT (319, 239), WHITE, DARKGRAY '                               paint face again to remove radian lines
   CIRCLE (319, 239), 102, WHITE '                                   fill in cut off circle
   CIRCLE (319, 239), 4, BLACK '                                     draw small black circle in center
   PAINT (319, 239), 0, BLACK '                                      paint small black circle
    Tm$ = TIME$ '                                                    get current time from TIME$
    s% = VAL(RIGHT$(Tm$, 2)) '                                       get numeric value of seconds
    m% = VAL(MID$(Tm$, 4, 2)) '                                      get numeric value of minutes
    h% = VAL(LEFT$(Tm$, 2)) '                                        get numeric value of hours
   IF h% >= 12 THEN h% = h% - 12 '                                   convert from military time
   COLOR BLACK, WHITE '                                              black text on bright white background
   LOCATE 19, 37 '                                                   position cursor on screen
   PRINT RIGHT$("0" + LTRIM$(STR$(h%)), 2) + RIGHT$(Tm$, 6) '        print current time in face of clock
   COLOR GRAY, BLACK '                                               white text on black background
    Tick% = (h% * 5) + (m% \ 12) '                                   calculate which hour hand radian to use
   CIRCLE (319, 239), 80, BLACK, -Clock(Tick%), Clock(Tick%) '       display hour hand
    h% = h% + 6 '                                                    move to opposite hour on clock face
   IF h% >= 12 THEN h% = h% - 12 '                                   adjust hour if necessary
    Tick% = (h% * 5) + (m% \ 12) '                                   calculate which hour hand radian to use
   CIRCLE (319, 239), 15, BLACK, -Clock(Tick%), Clock(Tick%) '       display small opposite tail of hour hand
   CIRCLE (319, 239), 95, BLACK, -Clock(m%), Clock(m%) '             display minute hand
    m% = m% + 30 '                                                   move to opposite minute on clock face
   IF m% > 59 THEN m% = m% - 60 '                                    adjust minute if necessary
   CIRCLE (319, 239), 15, BLACK, -Clock(m%), Clock(m%) '             display small opposite tail of min hand
   CIRCLE (319, 239), 2, RED '                                       draw small red circle in center
   PAINT (319, 239), RED, RED '                                      paint small red circle
   CIRCLE (319, 239), 100, RED, -Clock(s%), Clock(s%) '              draw second hand
    s% = s% + 30 '                                                   move to opposite second on clock face
   IF s% > 59 THEN s% = s% - 60 '                                    adjust second if necessary
   CIRCLE (319, 239), 25, RED, -Clock(s%), Clock(s%) '               display small opposite tail of sec hand
   CIRCLE (319, 239), 1, YELLOW '                                    draw small yellow circle in center
   PSET (319, 239), YELLOW '                                         fill in small yellow circle
   _DISPLAY '                                                        update screen with loop's changes
LOOP UNTIL INKEY$ <> "" '                                            end program when key pressed
SYSTEM '                                                             return to Windows

Figure 7: A clock made with CIRCLEs

The PAINT Statement

While the LINE statement offers a way to create a filled box the CIRCLE statement does not offer a method of creating a filled circle. There also needs to be a mechanism for painting irregular shaped objects created with multiple LINE statements. This is where the PAINT statement comes in. Type in the following program to see the PAINT statement work its magic.

( This code can be found at .\tutorial\Lesson5\Paint.bas )

CONST RED = _RGB32(127, 0, 0) '           define colors
CONST BRIGHTRED = _RGB32(255, 0, 0)
CONST GREEN = _RGB32(0, 127, 0)
CONST BRIGHTGREEN = _RGB32(0, 255, 0)

SCREEN _NEWIMAGE(640, 480, 32) '          initiate a graphics screen
CLS '                                     clear the screen
LINE (50, 50)-(300, 300), BRIGHTRED, BF ' draw a bright red filled box
LINE (50, 50)-(300, 300), RED, B '        draw a darker red border around box
CIRCLE (319, 239), 100, GREEN '           draw a green circle
PAINT (319, 239), BRIGHTGREEN, GREEN '    paint bright green until darker green is encountered

Figure 8: A PAINTing

The PAINT statement is used to fill in an area with a color until another color is encountered. In the example program this line:

PAINT (319, 239), BRIGHTGREEN, GREEN '    paint bright green until darker green is encountered

is instructing the computer to fill in a color at coordinate (319, 239) (the center of the circle) with the color bright green, _RGB32(0, 255, 0), until the color green, _RGB32(0, 127, 0) is encountered. The green color of the circle creates a border that the PAINT statement stays within. Since the portion of the red box inside the circle is not green the PAINT statement will paint right over top of it.

Because of the way the PAINT statement operates it's important to remember the order in which objects are drawn. Let's take the previous example and simply swap the LINE and CIRCLE statements so the circle is drawn first to see what happens.

( This code can be found at .\tutorial\Lesson5\Paint2.bas )

CONST RED = _RGB32(127, 0, 0) '           define colors
CONST BRIGHTRED = _RGB32(255, 0, 0)
CONST GREEN = _RGB32(0, 127, 0)
CONST BRIGHTGREEN = _RGB32(0, 255, 0)

SCREEN _NEWIMAGE(640, 480, 32) '          initiate a graphics screen
CLS '                                     clear the screen
CIRCLE (319, 239), 100, GREEN '           draw a green circle
LINE (50, 50)-(300, 300), BRIGHTRED, BF ' draw a bright red filled box
LINE (50, 50)-(300, 300), RED, B '        draw a darker red border around box
PAINT (319, 239), BRIGHTGREEN, GREEN '    paint bright green until darker green is encountered