Multi-page graphics

by Richard Russell, November 2007

Some dialects of BASIC provide the ability to have multiple pages of graphics, whereby you can draw text and/or graphics into each page and then switch 'instantly' between them. Such a facility could be used for simple animation, or simply to avoid having to redraw the screen (possibly taking a significant time) each time a particular layout is needed by your program. It also has the advantage of 'remembering' the contents of each page without any extra effort by you.

Although BBC BASIC for Windows doesn't provide this facility as a built-in feature, it is relatively easy to achieve by means of calls to the Windows API. The routine PROCpage listed below allows you to create, and subsequently select, up to 15 extra pages (you can increase the number if necessary); it also allows you to delete the new pages when you have finished with them (by passing the parameter -1).

To avoid a resource leak you will probably want your program to incorporate ON CLOSE and ON ERROR routines so that the additional pages can be deleted on exit. For example these routines might be as follows:

        ON CLOSE PROCpage(-1) : QUIT
        ON ERROR PROCpage(-1) : SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT

Of course you may need to perform other 'cleanup' operations as well as deleting the extra graphics pages, so in that case you can replace the PROCpage(-1) with a call to a more general routine which itself then calls PROCpage.

No specific initialisation is required; when you first select a page it will be created. However for the purposes of demonstration you could initialise and label each of the pages as follows:

        FOR Page% = 0 TO 15
          PROCpage(Page%)
          VDU 30
          PRINT "This is page ";Page%
        NEXT

As it stands the PROCpage routine doesn't keep independent text cursor positions for each page, so the VDU 30 is used to home the cursor each time. Neither does the routine keep independent settings for colours, fonts, graphics pointers etc. so you must remember to reset these as required whenever a new page is selected. With some extra complication this could be made automatic (PROCpage would need to store the settings for each page).

Selecting each page as required will obviously depend on the specific requirements of your program, but again for the purposes of demonstration the following code uses the function keys to select the different pages:

        REPEAT
          K% = INKEY(10)
          CASE TRUE OF
            WHEN K%>=136 AND K%<=139: VDU K%-128
            WHEN K%>=145 AND K%<=159: PROCpage(K%-144)
            WHEN K%=13: VDU 13,10
            WHEN K%=-1:
            OTHERWISE: VDU K%
          ENDCASE
        UNTIL FALSE

This code also allows you to write text into each page, to demonstrate that they are truly independent. Note that page 0, which is the normal default screen, cannot be selected using this code. The page currently being displayed is also the page into which text and/or graphics will be drawn, but if you need to draw into one page whilst displaying another this is easily arranged by means of the *REFRESH command.

Finally, here is a listing of the PROCpage routine:

        DEF PROCpage(Page%)
        PRIVATE hdc%()
        DIM hdc%(15) : REM. Maximum number of pages
        IF hdc%(0) = 0 hdc%(0) = @memhdc% : REM. Page 0 is default
        IF Page% >= 0 THEN
          IF hdc%(Page%) THEN
            @memhdc% = hdc%(Page%)
          ELSE
            LOCAL bitmap%, font%, X%, Y%
            SYS "GetSystemMetrics", 0 TO X%
            SYS "GetSystemMetrics", 1 TO Y%
            SYS "CreateCompatibleBitmap", hdc%(0), X%, Y% TO bitmap%
            SYS "CreateCompatibleDC", 0 TO @memhdc%
            SYS "SelectObject", @memhdc%, bitmap%
            SYS "PatBlt", @memhdc%, 0, 0, X%, Y%, &FF0062
            SYS "GetStockObject", 16 TO font%
            SYS "SelectObject", @memhdc%, font%
            SYS "SelectPalette", @memhdc%, @hpal%
            hdc%(Page%) = @memhdc%
          ENDIF
        ELSE
          LOCAL newbm%, oldbm%
          @memhdc% = hdc%(0)
          FOR Page% = 1 TO DIM(hdc%(),1)
            IF hdc%(Page%) THEN
              SYS "CreateCompatibleBitmap", @memhdc%, 0, 0 TO newbm%
              SYS "SelectObject", hdc%(Page%), newbm% TO oldbm%
              SYS "DeleteObject", oldbm%
              SYS "DeleteDC", hdc%(Page%)
            ENDIF
          NEXT
        ENDIF
        SYS "InvalidateRect", @hwnd%, 0, 0
        ENDPROC