A Beginners Guide To Euphoria


Introduction To Graphics Modes

Multimedia is the use of graphics, text, and sound by your program to interact with the person running it. It's a popular trend in both the business and home entertainment software industry. Euphoria's language set has a wealth of library routines that can allow you to handle powerful text and graphic output. Do not be intimidated by the idea of handling graphics in your programs. Euphoria remains true to its reputation of being an easy-to-understand language, even with graphics!

Before you are introduced to library routines that add colour and style to your screen output, you need to know how your computer screen works. When your computer screen shows information, it is presenting the data using a display format, otherwise known as graphics mode.

There are two types of graphics modes, a text mode and a pixel-graphics mode. Text mode is the default setting of your computer screen when your system is started up. It allows the display of characters at specific screen locations by using a paired co-ordinate system of row first, and column second. The default number of columns is 80 characters across, and the default number of rows is 25 rows down, though these can be changed to the needs of the program. Combining a row and a column position produces a screen location to start displaying text at. For example, the top left corner of the screen in text mode is 1,1 (row 1, column 1). The bottom left corner of the screen in text mode is 25,80 (row 25, column 80).

Characters in text mode can be displayed with colour as well. The colour the text is displayed in is called the foreground colour. The area around each character that is not being used can also have a colour different from the foreground colour. This is called the background colour. The default foreground colour for the text being displayed in Euphoria is white, and the default background colour black, in text mode.

Pixel-graphics mode also allows text to be displayed, and in a foreground colour. It also allows displaying what is known as a pixel. Pixels are tiny dots that can be used to assemble a graphics image. This tutorial program for example uses pixels to assemble the graphic images of the remote, the index screen, and the grey tiling around the viewport. This graphics mode also uses a paired co-ordinate system to display a pixel on the screen, but with two differences. First, the address pair to display a pixel is reversed in comparison to text screen addresses. Pixels are displayed using a pixel column first, pixel row last, address pair. Also, pixel row and column locations start at 0, not 1. This means the top left pixel corner is 0,0 (column 0, row 0). Because pixels are very small, the row and column positions can number into the hundreds, or in the case of high-resolution screens, the thousands.

But what is a high resolution? Well, a screen resolution is the number of pixels available on the screen. But how does one increase the number of pixels on a screen when the computer monitor itself remains the same size? This is accomplished by compressing the size of the pixel in each different screen resolution. For example, a high-resolution screen would have the pixels very small in order to fit a lot of them on one monitor. While this results in displaying images with very fine detail, the image itself would appear smaller than expected. On the other hand, a pixel-graphics mode in low resolution would use pixels that are a little larger, seeing that there would be fewer pixels available on the screen. This would make graphic images appear larger in size, but the quality of the picture would be more coarse and grainy. High-definition television screens for example use pixels that are much smaller than in conventional television sets, so the picture quality is much better.

Pixels, like text, can be displayed in a specific colour, so they also have a foreground colour. But the similarities end when discussing background colour in pixel-graphics modes. Any screen area not being occupied by a pixel is considered the background, so setting the background colour affects the entire screen area. As a result, any text shown in pixel graphics mode does not have a background colour. Text in this case is treated more like a group of pixels organized into a graphics shape than actual characters.

Each text and pixel graphics mode comes with a set of colours. this set of colours is called the palette. It works much like a painter's palette used in the creation of art. The painter's palette holds a number of tins of paint, each tin holding a different paint colour. If a new colour not on the palette is needed, one tin is switched with another tin containing the desired colour. The graphic mode palette works the same way, as each colour on the palette is identified by a number.

Each palette colour is created by mixing three elemental colours together. These three colours are red, blue, and green. By varying the intensities of each of the three colours differently, a new colour can be produced. The colours of the graphics mode palette are initially set to default levels of red, green, and blue, but as mentioned they can be changed to make a new colour. Because red, green and blue is used to generate the colours on a palette, each colour is said to have an RGB intensity level.

While the computer monitor can only show one screen display at a time, it is possible to maintain more than one virtual screen, called a screen page, in certain graphics modes. This means you can send screen output (using puts() for example) to one of many screen pages your Euphoria program is juggling. The screen page where all screen output is sent is called the active page. The screen page currently being shown on the computer monitor is called the display page.

The active page and the display page do not have to be the same screen page. For example, you can be sending screen output to one page while displaying a different page on the monitor. The number of screen pages available depends on the amount of memory on your video card in your computer.

Before any text or pixel-graphics images can be shown on the screen, the appropriate graphics mode must be selected. To do this, you use the graphics_mode() library routine:

   include graphics.e
   ri = graphics_mode(i)

graphics_mode() sets the screen to the appropriate graphics mode, an integer value of i. If successful, a value of 0 is returned to the receiving variable ri. If unsuccessful, a value of 1 is returned instead.

Each graphics mode is identified as a unique number, and is either a text mode or a pixel graphics mode. Within each of these two groups are variations on the number of pixels or characters available for each graphics mode. As of version 1.5, here are the accepted mode numbers you can pass to graphics_mode(), with a description for each:

   0 = 40×25 text, 16 grey
   1 = 40×25 text, 16/8 color
   2 = 80×25 text, 16 grey
   3 = 80×25 text, 16/8 color
   4 = 320×200 pixels, 4 color
   5 = 320×200 pixels, 4 grey
   6 = 640×200 pixels, BW
   7 = 80×25 text, BW
   11 = 720×350 pixels, BW
   13 = 320×200 pixels, 16 color
   14 = 640×200 pixels, 16 color
   15 = 640×350 pixels, BW
   16 = 640×350 pixels, 4 or 16 color
   17 = 640×480 pixels, BW
   18 = 640×480 pixels, 16 color
   19 = 320×200 pixels, 256 color
   256 = 640×400 pixels, 256 color
   257 = 640×480 pixels, 256 color
   258 = 800×600 pixels, 16 color
   259 = 800×600 pixels, 256 color
   260 = 1024×768 pixels, 16 color
   261 = 1024×768 pixels, 256 color
Not all video cards will support all of these modes. The older the video card, the least number of the listed modes that will be available to you.

To switch back to the previous video mode you were using, pass a value of -1 to graphics_mode() as a parameter. You should monitor for the return code generated by graphics_mode() to make sure the graphics mode was changed successfully before attempting any text or graphics mode operations. A demo program is available to list the graphics modes available on your computer. Do not be alarmed at the screen flickering or cracking while it runs.

demo program 45
include graphics.e
sequence screen_modes, good_modes, bad_modes
atom counter
integer screen_set_status
screen_modes = {0,1,2,3,4,5,6,7,11,13,14,15,16,17,18,19,256,257,258,259,260,
261,262,263,-999}
counter = 1
good_modes = {}
bad_modes = {}
while screen_modes[counter] != -999 do
     screen_set_status = graphics_mode(screen_modes[counter])
     if screen_set_status = 0 then
          good_modes = append(good_modes,screen_modes[counter])
     else
          bad_modes = append(bad_modes, screen_modes[counter])
     end if
     counter = counter + 1
end while
screen_set_status = graphics_mode(-1)
puts(1, "The modes that can be used for your video card are:\n")
print(1, good_modes)
puts(1, "\n\nThe modes that cannot be used for your video card are:\n")
print(1, bad_modes)

Whatever graphics mode you are in, it's important to know what you can and cannot do while in this graphics mode, because going over the limits defined in the graphics mode could lead to a program error. This library routine will give you information about the graphics mode you are in:

   include graphics.e
   rs = video_config()

When executed, video_config() will return a sequence value made up of eight atom elements, which is stored in receiving variable rs. Each element represents an attribute about the graphics mode. The structure is as follows:

 {colour monitor (1 means yes, 0 means no), graphics mode number,
  number of text rows, number of text columns, number of pixels across,
  number of pixels down, number of colours supported, number of pages}

If the value of number of pixels across and down are both zero, you are in a text mode. Only pixel-graphics modes can support the use of pixels. The next demo program will not only tell you which graphics modes your system can support, it will give you the details about each.

demo program 46
include graphics.e
sequence screen_modes, video_settings
atom counter
integer screen_set_status
screen_modes = {0,1,2,3,4,5,6,7,11,13,14,15,16,17,18,19,256,257,258,259,260,
261,262,263,-999}
counter = 1
while screen_modes[counter] != -999 do
     screen_set_status = graphics_mode(screen_modes[counter])
     if screen_set_status = 0 then
          video_settings = video_config()
          printf(1, "Mode %d supports the following attributes:\n\n",
               {screen_modes[counter]})
          if video_settings[1] = 1 then
               puts(1, "     Has Colour\n")
          else
               puts(1, "     Has No Colour\n")
          end if
          printf(1, "     Has %d text rows and %d text columns\n",
               {video_settings[3], video_settings[4]})
          if video_settings[5] + video_settings[6] > 0 then
               printf(1, "     Has %d pixels across and %d pixels down\n",
                    {video_settings[5], video_settings[6]})
          end if
          printf(1, "     Has %d colours available\n", {video_settings[7]})
          printf(1, "     Has %d display pages accessible\n\n",
               {video_settings[8]})
          puts(1, "Press Any Key To Continue\n")
          while get_key() = -1 do
          end while
     end if
     counter = counter + 1
end while
if graphics_mode(-1) then
end if

Whenever you switch to any new graphics mode, the screen automatically clears any data off the screen. However, you may want to clear the screen without having to reset the graphics mode:

   clear_screen()

clear_screen() clears the screen using the current background colour. Later in the tutorial, you will learn how to set the background colour in both text and pixel graphics mode. clear_screen() works in any graphics mode. Because this is a relatively straightforward routine to use, no demo is required.

Now that you understand how to set the screen graphics mode to your liking, and are able to obtain information about the selected mode, let's move on to putting text mode displays to good use. Once you have finished reading the next chapter, you'll never consider text output as dull again.

ToC

Colouring And Animating Text

Most of the demos you have seen involved presenting text on the screen. While it is very easy to create a formatted text line using printf(), it's entirely another matter to have text, formatted or otherwise, to appear anywhere on the screen. The special characters '\t' and '\n', and generous usage of the spacebar isn't enough to present text in professional form, such as in columns, horizontally centered, or even in colours other than white on black. This chapter will change all that.

You can place text anywhere on the screen in both pixel-graphics or text modes using the position() library routine:

   position(i1,i2)

position() moves the screen cursor to any row (i1) and column (i2) location where you want the next screen print (by puts(), print(), or printf(), for example) to appear. For example, issuing position(15,40) will move the cursor to the 15th row and the 40th column from the top-left corner of the screen (row 1, column 1). Any attempt to go off the screen will result in a program error. A demo program is available to show one humourous use of position() in displaying text.

demo program 47
clear_screen()
position(3,15)
puts(1, "A Program Example To Demonstrate Text Positioning")
position(4,30)
puts(1, "Written By David Gay")
position(5,20)
puts(1, "Author, \"A Beginner's Guide To Euphoria II\"")
position(8,1)
puts(1, "Top Ten Reasons why you should purchase Euphoria:")
position(9,1)
puts(1, "==============================================")
position(11, 5)
puts(1, "Number 10: It's (thankfully) not a Microsoft product.")
position(12, 5)
puts(1, "Number 9: Because C is like tax laws: too complex to figure out.")
position(13, 5)
puts(1, "Number 8: Euphoria is more fun than this year's prime time TV season.")
position(14, 5)
puts(1, "Number 7: The money spent will go to BASIC's retirement home.")
position(15, 5)
puts(1, "Number 6: Because \"Ernest Learns Euphoria\" hits the theatres soon.")
position(16, 5)
puts(1, "Number 5: You sound very smart when you say you work with atoms.")
position(17, 5)
puts(1, "Number 4: You can write word games you already know the answers to.")
position(18, 5)
puts(1, "Number 3: You can declare things without cross border shopping.")
position(19, 5)
puts(1, "Number 2: At last! A reason to use the { and } keys on the keyboard!")
position(20, 5)
puts(1, "Number 1: It's a great product!!!!! :)\n\n")

If you need to know where the cursor is on the screen, you can receive the cursor location by using the library routine get_position():

   include graphics.e
   rs = get_position()

get_position returns a two-element long string, representing the current cursor position, to the receiving variable rs. The sequence value returned by get_position() is composed of two atoms, the first atom element is the current row the cursor is on, the second, the current column the cursor is on. The format of the sequence value is:
   {current row position, current column position}
The current cursor position is always updated whenever a puts(), print(), or printf() library routine sends any text output to the screen. A demo program is available to show how the current cursor position changes whenever text print is sent to the screen and even when position() is used.

demo program 48
include graphics.e
integer element, keystroke, update
sequence some_text, current_position

clear_screen()
element = 1
update = 'y'
some_text = "As the screen is continually updated, the cursor position\n" &
            "is updated automatically. get_position() will report the\n" &
            "current position of the cursor.\n\n"
position(1,1)
puts(1,"An example of get_position()")
position(2,1)
puts(1,"============================")
position(25,1)
puts(1,"Press Q to quit, or any other key to display a character")
position(5,1)
while element <= length(some_text) do
     if update = 'y' then
          update = 'n'
          puts(1,some_text[element])
          current_position = get_position()
          position(23,1)
          printf(1,"Cursor at row %2d, column %2d",current_position)
          position(current_position[1],current_position[2])
     end if
     keystroke = get_key()
     if keystroke = 'q' then
          element = length(some_text) + 1
     elsif keystroke != -1 then
          element = element + 1
          update = 'y'
     end if
end while

If text sent to the screen is longer than the number of columns per row, it is wrapped at the right margin of the screen to continue on the next row below. There may be times where you want any text that goes over the right margin to be simply truncated and therefore not seen. Euphoria has a library routine that allows you to choose whether to wrap long text strings that go past the right margin or to truncate:

   include graphics.e
   wrap(i)

If parameter i is equal to 1, any printed text that would go past the right margin would be wrapped and appear on the start of the next row below. If parameter i is 0, any printed text that would go past the right margin would be truncated. wrap() works in both text and pixel-graphics mode. A demo program shows how wrap() works based on the parameter passed to it.

demo program 49
include graphics.e
sequence some_string
some_string = "This is a text string that is longer than " &
              "the width of your screen. See how wrap() handles the " &
              "output of the text string when printed. "
for modes = 1 to 0 by -1 do
     wrap(modes)
     printf(1,"When wrap(%d) is used:\n\n", modes)
     puts(1,some_string & "\n\n")
end for

The standard 25 rows in most graphics modes is more than enough for displaying full-screen text. But if you need to show a lot of text data on the screen, having more rows per screen would help. For text modes only, Euphoria has a library routine that can change the number of rows that can be displayed on the screen:

   include graphics.e
   ri = text_rows(i)

i represents the number of text rows you want the screen to show. The accepted values are 25, 28, 43, and 50. If possible, text_rows() will change the number of rows on the text mode you are in. You will notice the rows will appear flattened. Regardless of whether or not the number of rows on your screen has been changed successfully, the number of rows on the screen will be returned to the receiving variable ri. Watch how the demo program assigned to this screen will change the number of rows in a text mode.

demo program 50
include graphics.e
integer current_rows
sequence text_length


puts(1, "The following will demonstrate how to use text_rows() to\n")
puts(1, "increase the number of rows available in this program. Note\n")
puts(1, "how the spacing between the lines narrows. For those\n")
puts(1, "text rows that are not supported, this program will simply skip\n")
puts(1, "them. You will be required to press any key in order for this\n")
puts(1, "program to continue. Press any key to start the demonstration\n")

while get_key() = -1 do
end while

text_length = {25,28,43,50}
for ix = 1 to 4 do
     current_rows = text_rows(text_length[ix])
     if current_rows = text_length[ix] then
          for iy = 1 to text_length[ix] do
               print(1, iy)
               if iy < text_length[ix] then
                    puts(1, "\n")
               end if
          end for
          while get_key() = -1 do
          end while
     end if
end for
current_rows = text_rows(25)

Another way to handle large amounts of text on the screen is by scrolling. Scrolling involves treating the screen as a movable window to view any part of the text. It's like riding in a glass elevator: as you go up or down, the view from your position will appear to vertically roll. Wordprocessors use this approach to handle documents larger than the size of the screen. While you could write some program code to perform text scrolling, Euphoria has a powerful library routine that can do this for you:

   include graphics.e
   scroll(i1,i2,i3)

scroll() makes a segment of screen text, starting from row i2 to row i3 inclusive, roll by i1 rows. The roll is towards the top (roll up) if i1 is negative, or towards the bottom (roll down) if i1 is positive. If the text scrolls towards the top of the screen, extra blank rows will appear starting at the bottom row of the scroll area. If the text scrolls towards the bottom of the screen, extra blank rows will appear starting at the top row of the scroll area. scroll() works in both text and pixel graphics modes. A demo program is available showing one example of how scroll() works.

demo program 51
include graphics.e

integer keystroke, current_position

clear_screen()

position(5,1)
puts(1, repeat('Í',80))

position(20,1)
puts(1, repeat('Í',80))

position(11,25)
puts(1, "ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»")

position(12,25)
puts(1, "º Welcome To The SCROLL ZONE º")

position(13,25)
puts(1, "ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ")

position(22,1)
puts(1, "Press 8 for up, 2 for down, or q to quit this program")

keystroke = get_key()
current_position = 11

while keystroke != 'q' do
     if keystroke = '8' and current_position > 6 then
          scroll(1,6,19)
          current_position = current_position - 1
     end if
     if keystroke = '2' and current_position < 17 then
          scroll(-1,6,19)
          current_position = current_position + 1
     end if

     keystroke = get_key()
end while

Perhaps the best way to present text is in colour. This tutorial, for example, uses colour to separate Euphoria statements and specific points apart from the regular text. There is a library routine you can use to present text in a certain colour.

   include graphics.e
   text_color(i)

text_color() causes any following screen text to be printed in the colour i. Any text already on the screen will not have its foreground colour changed. To start printing separate text in a new colour, you have to issue text_color() again. i is a number representing a colour in the palette of the text or pixel-graphic mode you are in. While some numbers may mean different colours in different graphic modes, the first 16 colours are as follows:

   1 = blue
   2 = green
   3 = cyan
   4 = red
   5 = magenta
   6 = brown
   7 = white
   8 = gray
   9 = bright blue
   10 = bright green
   11 = bright cyan
   12 = bright red
   13 = bright magenta
   14 = yellow
   15 = bright white

   (note: 0 = black)
When your program terminates, any screen text after will appear in the colour set by the last text_color() issued. As a result your program should issue a text_color(7) (white) followed by a puts("\n") before it completes. text_color() works in any text or pixel-graphics modes. A demo program is available to show one use of text_color().

demo program 52
include graphics.e
integer foreground_colour
sequence list_of_colours, already_used

clear_screen()

list_of_colours = {"blue","green","cyan","red","magenta","brown",
                   "white","gray","bright blue","bright green",
                   "bright cyan","bright red","bright magenta",
                   "yellow","bright white"}

already_used = {}

for ix = 1 to 10 do
     foreground_colour = rand(15)
     while find(foreground_colour,already_used) do
          foreground_colour = rand(15)
     end while
     already_used = already_used & foreground_colour
     text_color(foreground_colour)
     printf(1, "Text in %s\n",{list_of_colours[foreground_colour]})
end for

To set the background colour of any text printed in a text mode, or the entire background of a screen in pixel graphics mode, you use the following library routine:

   include graphics.e
   bk_color(i)

In text mode, issuing bk_color() will cause the next screen text to be printed in the background colour i. Any text already on the screen will not have its background colour changed. In pixel-graphics mode, the entire screen background will be changed instantaneously to the new colour i. This differs from text mode where only the background of any printed text following bk_color() is affected. In addition, any parts of graphic images on the screen that are in black (0) will show the background colour underneath when it is changed to any non-black colour. A demo program is available showing the differences between text and pixel-graphics modes where bk_colour() is concerned.

demo program 53
include graphics.e
clear_screen()
text_color(15)
bk_color(2)
position(13,9)
puts(1,"When using bk_color() in text mode, only the background colour")
position(14,10)
puts(1,"of the text itself is affected. Press any key to continue...")
while get_key() = -1 do
end while
if graphics_mode(18) = 0 then
     text_color(15)
     bk_color(2)
     position(14,10)
     puts(1,"When using bk_color() in pixel-graphics mode, the background")
     position(15,10)
     puts(1,"colour of the entire screen is changed. Press any key to end.")
     while get_key() = -1 do
     end while
     if graphics_mode(-1) then
          puts(1,"Resetting to the previous screen does not work for you!")
     end if
end if

Rather than have text sit motionless on the screen, you can make it more interesting to look at through animation, as in moving groups of text horizontally and vertically around the screen. To perform this trick of movement, you must be able to grab text right off the screen, then re-display it at a new location. Euphoria has two library routines that can do this for you.

   include image.e
   rs = save_text_image(s1,s2)

save_text_image() captures a rectangular area of text on your screen, storing the saved data in receiving variable rs. The rectangular area is created by specifying the top left corner (s1) and the bottom right corner (s2). s1 is a sequence made up of two atom elements, the first element being the top left row of the screen, the second being the top left column of the screen, so the format of s1 is:
   {top left row, top left column}
s2 is also a sequence made up of two atoms, the first being the bottom right row, the second being the bottom right column:
   {bottom right row, bottom right column}
The saved data in rs is a sequence made up of sequence elements. Each sequence element represents a row of text in the rectangular area of the screen that was saved. The sequence elements themselves are made up of atom values. The odd-numbered elements are the actual text characters, and the even-numbered elements are values representing the combined foreground and background colours of each text character. So saving a rectangular area containing the following text:
   Welcome to
   Euphora!
would retrieve the value:
   {{87,13,101,13,108,13,99,13,111,13,109,13,101,13,32,13,84,13,111,13},
    {69,13,117,13,112,13,104,13,111,13,114,13,105,13,97,13,33,13,32,13}}
The foreground/background colour value always FOLLOWS the text character it is associated with. This value, called the attribute byte, is created by multiplying the background number by 16, then adding the foreground colour number to the result. In the example above, the text foreground was 13 or bright magenta with a background of 0 or black, so the attribute byte was 0 times 16, giving 0, then we added 13 to the result, giving 13.

Now that you have saved the image, you want to redisplay it in a new location on the screen. The following library routine below will do this:

   include image.e
   display_text_image(s1,s2)

display_text_image() displays s2, a sequence of sequences containing text and colour attributes, on the screen at location s1. s1 is the top left corner on the screen where you would display the sequence s2, so the format of s1 would be:
   {top left row, top left column}
s2 would very likely be the saved data obtained by the save_text_image() library routine. You could, however, create your own sequences to be displayed by display_text_image() using the structure shown to you earlier. Both save_text_image() and display_text_image() only work in text_modes. A demo program is available to show how save_text_image() and display_text_image() moves a block of text around the screen.

demo program 54
include image.e
sequence stored_image
integer input_key, line, column
line = 1
column = 1
bk_color(2)
clear_screen()
text_color(14)
position(2,2)
puts(1, "******************************")
position(3,2)
puts(1, "* Use the left, right, up,   *")
position(4,2)
puts(1, "* and down arrow keys to     *")
position(5,2)
puts(1, "* move box, press Q to quit! *")
position(6,2)
puts(1, "******************************")
stored_image = save_text_image({1,1},{7,32})
input_key = get_key()
while input_key != 'q'do
     display_text_image({line, column},stored_image)
     if input_key = 328 and line > 1 then
          line = line - 1
     end if
     if input_key = 336 then
          line = line + 1
     end if
     if input_key = 331 and column > 1 then
          column = column - 1
     end if
     if input_key = 333 then
          column = column + 1
     end if
     input_key = get_key()
end while
bk_color(0)
text_color(7)

The next chapter promises to be more entertaining, as it shows you how to work with pixels and even graphic shapes in a pixel graphics mode.

ToC

Creating Pixel Graphics Images

Personal computers have undergone major changes since they were introduced in the early 1970's. The most significant change is the way the person works with the computer. Before the creation of Windows and Mac OS, you had to enter cryptic commands on a prompt. Today, we operate our computers using a graphics-based menu. Graphics are present in any software written these days because people relate to images easier than actual system commands.

The smallest element to work with in pixel-graphics modes is the pixel. Pixels can be placed anywhere on the screen, and can appear in any of the colours supported in the graphics mode you are in. Euphoria has a library routine that can set one or more pixels to any colour.

   include graphics.e
   pixel(o,s)

pixel() sets either one or a series of pixels starting at a position on the screen to a colour. s represents a sequence value, made up of two atom elements, that is the screen location of the pixel:

   {pixel column location, pixel row location}

if o is an atom value, this value represents the colour number to set one pixel to at position s. If o is a sequence, this value is a series of colour numbers to set a group of pixels to, beginning at s, and then advancing one pixel to the right onward. Let's look at some examples of pixel on the next page.

   pixel(6,{200,100})

This will set a pixel located at pixel column 200 and pixel row 100 to colour number 6, which is brown.

   pixel({9,2,14},{63,120})

This will:
  1. set a pixel located at pixel column 63 and pixel row 120 to 9, which is grey
  2. set a pixel located at pixel column 64 and pixel row 120 to 2, which is green.
  3. set a pixel located at pixel colomn 65 and pixel row 120 to 14, which is yellow.
If you are working with a series of pixels on the same pixel row,it is faster to issue pixel() once with a series of colours than to set them individually one at a time. pixel() works only in pixel-graphics modes. A demo is available showing how pixel() is used to create a shape on the screen.

Demo program 56
include graphics.e
integer x_or_y_work_variable
if graphics_mode(18) then
     puts(1, "640 X 480 mode, 16 colours, not supported!\n")
else
     text_color(15)
     position(1,1)
     puts(1,"Drawing A Shape Using pixel()")
     position(25,1)
     puts(1, "Press q to end this program")
     for ix = 100 to 300 do
          pixel(9, {ix , ix})
     end for
     pixel(repeat(12,400),{100,100})
     x_or_y_work_variable = 300
     for ix = 300 to 500 do
          pixel(10, (ix & x_or_y_work_variable))
          x_or_y_work_variable = x_or_y_work_variable - 1
     end for
     for ix = 100 to 300 do
          pixel(11, {100, ix})
          pixel(13, {500, ix})
     end for
     for ix = 100 to 500 do
          pixel(14, {ix, 300})
     end for
     while get_key() != 'q' do
     end while
     if graphics_mode(-1) then
          puts(1, "Unable To Reset\n")
     end if
end if

You can find out what colour one or a series of pixels on the screen is set to by using the get_pixel() library routine:

   include graphics.e
   ro = get_pixel(s)

If s is in the format:
{pixel column location, pixel row location}
then the colour of the pixel at that location is returned, and is stored in receiving variable ro. If s is in the format:
{pixel column location, pixel row location, number of pixels}
then a series of colours is returned as a sequence, and stored in receiving variable ro. This sequence is all the colours of the pixels starting at that pixel column and pixel row location, and continuing right for the specified number of pixels.

   one_colour = get_pixel({167,231})

This will return the colour of one pixel located at pixel column 167 and pixel row 231. The value is stored in variable "one_colour".

   series_of_colours = get_pixel({1500,10,15})

This will return the colours of 15 pixels, beginning at pixel column 500 and pixel row 10, and stopping at pixel column 514 and pixel row 10. The sequence is stored in variable "series_of_colours". get_pixel() only works in pixel-graphics modes. Do not attempt to obtain a pixel colour at a location that is off the screen. A demo program is available showing how to grab a series of pixels and then redisplay them several times to make a shape.

Demo program 57
include graphics.e
sequence copy_buffer
if graphics_mode(18) then
     puts(1, "Mode 18 not supported\n")
else
     pixel({10,10,10,10,10,10,10,10,10,10,10,10,15,15,15,15,15,15,15,15,
            15,15,15,15,12,12,12,12,12,12,12,12,12,12,12,12},{300,100})
     position(20,1)

     puts(1, "Press 1 to continue")

     while get_key() != '1' do
     end while

     copy_buffer = get_pixel({300,100,36})

     for ix = 205 to 220 do
          pixel(copy_buffer,{300,ix})
     end for

     position(20,1)

     puts(1, "Press 2 to end program")

     while get_key() != '2' do
     end while

     if graphics_mode(-1) then
          puts(1, "Reset failed!\n")
     end if
end if

Remember the demo program that created a shape using pixel()? While this worked well, it was awkward using pixel() to create even this simple shape. Euphoria has a library routine that can draw a line of pixels on the screen using a series of pixel screen locations:

   include graphics.e
   draw_line(i,s)

i represents the colour number to draw a line of pixels in. s is a sequence made up of two or more sequence elements, where each element is a pixel location on the screen. Here's the structure of the sequence used by draw_line() to make a line:

   {{pixel column location, pixel row location},
    {pixel column location, pixel row location},
    {pixel column location, pixel row location},...}

draw_line() draws the line starting with the first two elements. If there is a third pixel location as an element, the line continues to be drawn using the second and third elements. If there is a fourth pixel location as an element, then the line continues to be drawn using the third and fourth elements. This goes on until the entire sequence is processed. Here's how draw_line() draws a line that closes in on itself:

   draw_line(14,{{50,50},{150,50},{150,150},{50,150},{50,50}})

A demo is ready to show how to draw a shape using draw_line().

Demo program 58
include graphics.e
if graphics_mode(18) then
     puts(1, "640 X 480 mode, 16 colours, not supported!\n")
else
     text_color(15)
     position(1,1)
     puts(1, "Drawing A Shape Using draw_line()")
     position(25,1)
     puts(1, "Press q to end this program")
     draw_line(9,{{100,100},{300,300}})
     draw_line(12,{{100,100},{500,100}})
     draw_line(10,{{300,300},{500,100}})
     draw_line(11,{{100,100},{100,300}})
     draw_line(13,{{500,100},{500,300}})
     draw_line(14,{{100,300},{500,300}})
     while get_key() != 'q' do
     end while
     if graphics_mode(-1) then
          puts(1, "Unable To Reset\n")
     end if
end if


If you really want to draw a shape, Euphoria has a library routine that draws a polygon. A polygon is any 2-D plane figure with more than 4 sides, though with this library routine you can draw squares and triangles too:

   include graphics.e
   polygon(i1,i2,s)

Like draw_line(), polygon() uses the same pixel location format, s, to draw a shape on the screen, with the shape being drawn in a colour, i1. polygon() , however, uses a "fill" parameter (i2). Fill simply means to colour in the area bordered by the line that forms the shape. If it is 1, the area will be filled in using the same colour as the line. If it is 0, the area will be left empty. This way, you can control whether you have a wireframe shape or a solid shape. Also, to complete the shape of the polygon, you do not have to specify a pixel location to close the shape. polygon() automatically draws a line using the last element of s to the first element of s as pixel locations.
The order of the elements representing pixel locations in sequence s is important, as it dictates the shape of the polygon. when creating a shape using polygon(), you should order the elements in a circular pattern, unless your intention is to create a bizarrely-formed shape. polygon() only works in pixel-graphics modes. A demo program is available that demonstrates some examples of shapes drawn by polygon().

Demo program 59
include graphics.e
include image.e

if graphics_mode(18) = 0 then
     text_color(15)
     position(1,34)
     puts(1,"Polygon Fun!")
     position(30,30)
     puts(1,"Press any key to end")

     position(6,16)
     puts(1,"polygon(6,1,{{10,50},{100,50},{100,140},{10,140}})")
     polygon(6,1,{{10,50},{100,50},{100,140},{10,140}})

     position(13,16)
     puts(1,"polygon(13,0,{{55,160},{10,240},{100,240}})")
     polygon(13,0,{{55,160},{10,240},{100,240}})

     position(19,16)
     puts(1,"polygon(2,1,{{30,260},{100,260},{80,340},{10,340}})")
     polygon(2,1,{{30,260},{100,260},{80,340},{10,340}})

     position(25,16)
     puts(1,"polygon(12,0,{{10,360},{55,360},{55,400},{100,400},")
     position(26,16)
     puts(1,"              {100,440},{10,440}})")
     polygon(12,0,{{10,360},{55,360},{55,400},{100,400},
                   {100,440},{10,440}})

     while get_key() = -1 do
     end while
     if graphics_mode(-1) then
          puts(1,"Reset Failure")
     end if
end if

Drawing shapes with multiple sides is not too difficult, even without library routines like polygon() . Drawing oval shapes is a different matter entirely. A background in trigonometry was the only way you could draw any kind of circle or ellipse shape. Fortunately, Euphoria comes to the rescue with a library routine that makes drawing circles and ellipses easy:

   include graphics.e
   ellipse(i1,i2,s1,s2)

ellipse() draws an ellipse on the screen in colour i1. You have the option of having the area bordered by the circle to be filled (i2) using colour i1. A value of 1 means fill the area, while 0 means do not fill. The ellipse is drawn by defining a rectangular area on the screen, which will control the size and kind of ellipse that will be drawn. The area requires two pixel locations, an upper left corner (s1) and lower right corner (s2) in order for it to be defined. Both of these sequence values follow the format listed below:

   {pixel column location, pixel row location}

If you specify an area that is a perfect square, the oval drawn will be a circle. ellipse() only works in pixel-graphics modes. A demo program is ready to demonstrate how ellipse()'s defining of a rectangular area will dictate the shape and size of the circle.

Demo program 60
include graphics.e
integer input_key, tl1, tl2, br1, br2, update
tl1 = 200
tl2 = 100
br1 = 400
br2 = 300
update = 'y'
if graphics_mode(18) then
    puts(1, "Mode not available")
else
    position (28,1)
    puts(1, "Press 1 to widen horizontally, 2 to narrow horizontally")
    position (29,1)
    puts(1, "      3 to widen vertically,   4 to narrow vertically")
    position (30,1)
    puts(1, "      q to quit")
    input_key = get_key()
    while input_key != 'q' do
         input_key = get_key()
         if update = 'y' then
              update = 'n'
              ellipse(15, 0, {tl1,tl2},{br1,br2})
              polygon(3,0, {{tl1,tl2},{br1,tl2},{br1,br2},{tl1,br2}})
         end if
         if input_key = '1' then
              ellipse(0, 0, {tl1,tl2},{br1,br2})
              polygon(0,0, {{tl1,tl2},{br1,tl2},{br1,br2},{tl1,br2}})
              tl1 = tl1 - 3
              br1 = br1 + 3
              update = 'y'
         end if
         if input_key = '2' then
              ellipse(0, 0, {tl1,tl2},{br1,br2})
              polygon(0,0, {{tl1,tl2},{br1,tl2},{br1,br2},{tl1,br2}})
              tl1 = tl1 + 3
              br1 = br1 - 3
              update = 'y'
         end if
         if input_key = '3' then
              ellipse(0, 0, {tl1,tl2},{br1,br2})
              polygon(0,0, {{tl1,tl2},{br1,tl2},{br1,br2},{tl1,br2}})
              tl2 = tl2 - 3
              br2 = br2 + 3
              update = 'y'
         end if
         if input_key = '4' then
              ellipse(0, 0, {tl1,tl2},{br1,br2})
              polygon(0,0, {{tl1,tl2},{br1,tl2},{br1,br2},{tl1,br2}})
              tl2 = tl2 + 3
              br2 = br2 - 3
              update = 'y'
         end if
    end while
    if graphics_mode(-1) then
         puts(1, "Mode not available")
    end if
end if

The next chapter will show you how to make graphic images more animated instead of sitting motionless on the screen.

ToC

Pixel-Graphics Animation And Palette Handling

You remember from the chapters on text mode programming that you can create moving text in Euphoria. This is also possible with pixel graphic images. You can make images created with draw_line(), polygon() and ellipse() appear to move around the screen by redisplaying the images over and over while varying the screen location. Another form of animation is quickly changing one or more image colours without having to redisplay the image itself. Just as save_text_image() and display_text_image() are used to grab and redisplay text in text mode, Euphoria has library routines to grab and redisplay pixel images in pixel-graphics mode.

   include image.e
   rs = save_image(s1,s2)

save_image() saves an image within a rectangular area on the screen. The area is stored as a sequence made up of sequence elements that represent rows of pixel colours. The value is stored in receiving variable rs, and consists ofthe following structure:

   {{pixel colour, pixel colour, pixel colour, pixel colour,...}
    {pixel colour, pixel colour, pixel colour, pixel colour,...}
    {pixel colour, pixel colour, pixel colour, pixel colour,...},...}

Both the top left corner (s1) and the bottom right corner (s2) of the defined rectangular area being saved follow the format below:

   {pixel column location, pixel row location}

Once you have captured the image using save_image(), you can then redisplay it using display_image():

   include image.e
   display_image(s1,s2)

display_image() displays a sequence value, s2, containing pixel colours at location s1 on the screen. Most likely s2 is a sequence variable containing the data retrieved by the save_image() library routine. You could, however, create an image to display with this library routine by following the format on the previous page. s1 is the location on the screen to display the image, and follows the format below:

   {pixel column location, pixel row location}

The first pixel colour in s2 will be displayed at screen location s1. Because the data in s2 implies a rectangular shape, s1 is where the top left corner of the image is displayed. Both save_image() and display_image() only work in pixel-graphics modes. A demo program is available to show how to use both save_image() and display_image() to make a bouncing ball animation.

Demo program 61
include image.e
include graphics.e
sequence ball, bounce
bounce =
{200,200.125,200.375,200.75,201.25,201.875,202.625,203.5,204.5,205.625,
206.875,208.25,209.75,211.375,213.125,215,217,219.125,221.375,223.75,226.25,
228.875,231.625,234.5,237.5,240.625,243.875,247.25,250.75,254.375,258.125,
262,266,270.125,274.375,278.75,283.25,287.875,292.625,297.5,302.5,307.625,
312.875,318.25,323.75,329.375,335.125,341,347,353.125}
if graphics_mode(18) then
     puts(1, "Mode Set Failure")
else
     clear_screen()
     ellipse(5, 1, {200,200}, {300,300})
     for iy = 234 to 266 do
          for ix = 200 to 300 do
               if get_pixel({ix, iy}) = 5 then
                    pixel(15,{ix, iy})
               end if
          end for
     end for
     for iy = 267 to 300 do
          for ix = 200 to 300 do
               if get_pixel({ix, iy}) = 5 then
                    pixel(9,{ix, iy})
               end if
          end for
     end for
     ball = save_image({193,193},{308,308})
     clear_screen()
     position(1,1)
     puts(1, "Press q to end this bouncing ball demo!")
     while 1 = 1 do
          if get_key() = 'q' then
               exit
          end if
          for ix = 1 to length(bounce) do
               display_image({270,bounce[ix]},ball)
          end for
          for ix = length(bounce) to 1 by -1 do
               display_image({270,bounce[ix]},ball)
          end for
     end while
     if graphics_mode(-1) then
          puts(1, "Mode Set Failure")
     end if
end if

Another kind of animation is colour-shifting, where one or more colours that the text or graphic image is in is changed. One way of doing it is to perform a save_image() or save_text_image(), search through the retrieved sequence value for the colour to be changed, and change where a match is made. Once done, you redisplay using either display_image() or display_text_image(). However, that is a lot of work just to change the colour of something displayed on the screen. Can we change the image colour without having the image itself redisplayed?
The answer is yes, by changing the palette of the graphics mode you are in. Changing any of the numbers on the palette to another colour will be instantaneously shown on the screen in any text or image that uses the colour. To do this, you first need to get a copy of the palette:

   include image.e
   rs = get_all_palette()

get_all_palette() returns the entire colour set of a palette as a sequence, which is then stored in receiving variable rs. The sequence is composed of sequence elements, where each element is the red, green, and blue intensities that make up a colour in the palette. The first element is the RGB intensity level for colour 0 (black), the second element is the RGB intensity level for colour 1 (blue), and so forth. These elements are made up of 3 atom values, the first being the level of red, the second being the level of green, the third being the level of blue. To help clarify, the format of the sequence representing the palette of the current graphics mode you are in is shown below:

   {{red level, green level, blue level},                <-----colour 0
    {red level, green level, blue level},                <-----colour 1
    {red level, green level, blue level},                <-----colour 2
    {red level, green level, blue level},...}            <-----colour 3

Each red, green, and blue level can have a value from 0 (no intensity) to 63 (maximum intensity). get_all_palette() works in any text or pixel graphics mode. A demo program is available from this page. It will show the RGB intensities for each colour in a palette of a given screen mode.

Demo program 62
include graphics.e
include image.e

sequence colour_list,rgb_levels

colour_list = {{"BLACK",0},{"BLUE",0},{"GREEN",0},{"CYAN",0},{"RED",0},
         {"MAGENTA",0},{"BROWN",0},{"WHITE",0},{"GRAY",0},
         {"BRIGHT BLUE",0},{"BRIGHT GREEN",0},{"BRIGHT CYAN",0},
         {"BRIGHT RED",0},{"BRIGHT MAGENTA",0},{"YELLOW",0},
         {"BRIGHT WHITE",0}}

if graphics_mode(3) = 0 then
     rgb_levels = get_all_palette()
     if graphics_mode(-1) then
     puts(1,"Reset Failure!")
     end if
end if

if graphics_mode(3) = 0 then
     for rgb = 1 to length(colour_list) do
   colour_list[rgb][2] = rgb_levels[rgb]
     end for
     position(1,10)
     puts(1,"                                   RGB Intensity Makeup")
     position(2,10)
     puts(1,"                                   --------------------")
     position(3,10)
     puts(1,"Colour                             Red    Green    Blue")
     position(4,10)
     puts(1,"==============                     ===    =====    ====")
     for colours = 1 to length(colour_list) do
     position(5+colours,10)
     printf(1,"%-14s                     %2d       %2d       %2d",
    {colour_list[colours][1],colour_list[colours][2][1],
                                   colour_list[colours][2][2],
                                   colour_list[colours][2][3]})
     end for
     position(25,10)
     puts(1,"Press any key to end")
     while get_key() = -1 do
     end while
     if graphics_mode(-1) then
     puts(1,"Reset Failure!")
     end if
end if

Now that you have a copy of the palette, you can start changing colour numbers to mean a different colour. This is done by changing either one or more of the RGB intensity levels assigned to that colour. Here is the library routine that can change a colour's RGB intensity:

   include graphics.e
   ro = palette(i,s)

palette() changes the colour number i to another colour by assigning it new RGB intensity levels stored in s. If successful, the previous RGB intensity levels will be returned as a sequence, to be stored in receiving variable ro, and any image or text on the screen that uses the colour i will immediately show the change. If unsuccessful, -1 is returned. s is a sequence composed of 3 atom elements, namely the red, green, and blue intensity levels of what colour i is being set to:

   {red intensity level, green intensity level, blue intensity level}

The returned sequence value representing the old RGB intensity of the colour being changed (if successful) is also made up of threee atom elements, and is in the same format shown on the previous page. The atom elements in the sequence passed to palette() must be between the values of 0 and 63 inclusive. palette() works in both text and pixel-graphics modes. It's always a good idea to save the returned value from palette(), or even using get_palette() to save an old copy of all the colours of the palette, before attempting to change any colour. A demo program is available from this screen, showing how to change the colour number associated with yellow to a new colour.

Demo program 62
include graphics.e
object changed_palette
integer Red_, Green_, Blue_, input
if graphics_mode(18) then
     puts(1, "Mode Set Failure")
else
     for ix = 0 to 60 do
          ellipse(14, 1, {250-ix, 180-ix}, {350+ix, 280+ix})
          ellipse(15, 0, {249-ix, 179-ix}, {351+ix, 281+ix})
     end for
     Red_ = 0
     Green_ = 0
     Blue_ = 0

     changed_palette = palette(14, {Red_, Green_, Blue_})

     Red_ = changed_palette[1]
     Green_ = changed_palette[2]
     Blue_ = changed_palette[3]
     input = get_key()
     position(1,9)
     puts(1,"This program will demonstrate how to modify the palette")
     position(2,9)
     puts(1,"colour code for yellow into another colour by manipulating")
     position(3,9)
     puts(1,"the RGB colour code. Use 1 and 2 to change the red tint,")
     position(4,9)
     puts(1,"3 and 4 to change the green tint, and 5 and 6 to change the")
     position(5,9)
     puts(1,"blue tint. Press q when you are finished using this program.")
     while input != 'q' do
          if input = '1' and Red_ > 0 then
               Red_ = Red_ - 1
          end if
          if input = '2' and Red_ < 63 then
               Red_ = Red_ + 1
          end if
          if input = '3' and Green_ > 0 then
               Green_ = Green_ - 1
          end if
          if input = '4' and Green_ < 63 then
               Green_ = Green_ + 1
          end if
          if input = '5' and Blue_ > 0 then
               Blue_ = Blue_ - 1
          end if
          if input = '6' and Blue_ < 63 then
               Blue_ = Blue_ + 1
          end if
          changed_palette = palette(14, {Red_, Green_, Blue_})
          position(28,19)
          printf(1, "Red: %.2d        Green: %.2d        Blue: %.2d",
                 {Red_,Green_,Blue_})
          input = get_key()
     end while
     if graphics_mode(-1) then
          puts(1, "Mode Set Failure")
     end if
end if

If you want to change all the colours in the palette, you can either issue palette() once for every colour, or use a Euphoria library routine created to change all colours at once.

   include graphics.e
   all_palette(s)

all_palette() is used to change all colours of a palette in the current graphics mode. s is a list of new RGB intensities for the entire palette to be set to, and is a sequence made up of sequence elements. The element position represents a colour, starting with 0 (black) for the first element, 1 (blue) for the second element, 2 (green) for the third element, and so forth. Each element of s is made up of three atom elements, the first being the red intensity level, the second being the green intensity level, and the third being the blue intensity level:

   {{red level, green level, blue level},         <---- new colour for 0
    {red level, green level, blue level},         <---- new colour for 1
    {red level, green level, blue level},         <---- new colour for 2
    {red level, green level, blue level},...}     <---- new colour for 3

As with palette(), each red, green, and blue intensity must be between the values of 0 and 63 inclusive. Any text or image on the screen that is using the colours changed by all_palette() will instantaneously show the changes on the screen once this library routine is executed. This is handy if you are performing a large scale of colour changing involving many images or text on the screen. Using all_palette() to change a series of colours is much faster than using palette() to change each colour individually. all_palette() works in both text and pixel-graphics modes. A demo program is available to show how colours 1 through 5 are changed using all_palette().

Demo program 63
include graphics.e
include image.e

sequence colours_one_to_five, original

if graphics_mode(18) = 0 then
     original = get_all_palette()

     clear_screen()

     text_color(7)

     puts(1,"This program will attempt to demonstrate all_palette() by\n")
     puts(1,"changing 5 colour numbers without having to redisplay\n")
     puts(1,"the text again.\n\n")

     colours_one_to_five = original
     colours_one_to_five[2..6] = {{0,63,0},{0,63,0},{0,63,0},{0,63,0},
                                  {0,63,0}}

     for ix = 0 to 15 do
          text_color(ix)
          print(1,ix)
          puts(1," ")
     end for

     for retry = 1 to 2 do
          text_color(7)
          position(20,1)
          if retry = 1 then
               puts(1,
               "These are the colours present in the default palette.\n")
               puts(1,
               "Press any key to change the first 5 colours to green.\n")
          end if
          if retry = 2 then
               all_palette(colours_one_to_five)
               puts(1,
               "Colour numbers from 1 to 5 now have an RGB setting   \n")
               puts(1,
               "of {0,63,0}, or pure green. Press any key to end.    \n")
          end if
          while get_key() = -1 do
          end while
     end for

     all_palette(original)

end if

if graphics_mode(-1) then
     puts(1,"reset failure\n")
end if

It is important to understand how to change palettes in graphics modes, as you will need to do so when dealing with .BMP files, or Windows bitmaps. The next chapter will introduce you to bitmaps, as well as how to direct screen output (text or pixel graphics images) to multiple screen pages.

ToC

Bitmaps And Screen Pages

This final chapter on text and pixel-graphics output will focus on bitmaps and screen pages. Euphoria allows the programmer to use bitmapped (.BMP) files created by popular paint programs like Neopaint and Windows Paintbrush, without needing to write any code to read in the data. This saves a lot of time in graphics programming. The programmer also has the option of redirecting screen output to more than one virtual screen page, and deciding which page to show on the screen. Before using any .BMP files in your program, you have to read them in. This is easily done using the read_bitmap() library routine.

   include image.e
   ro = read_bitmap(s) 

read_bitmap() reads in the data stored in a .BMP file, s,and stores that data in receiving variable ro as a sequence value. The sequence value is composed of two elements. The first element is the palette, a sequence containing the RGB instensity levels for colours the .BMP file used. The seconde element is a sequence containing the pixels themselves, arranged in a format that display_image() can use to show on the screen:

   {{{red level, green level, blue level},...},
    {{pixel colour, pixel colour, pixel colour,...},
     {pixel colour, pixel colour, pixel colour,...},
     {pixel colour, pixel colour, pixel colour,...},...}}

If the palette of the .BMP file uses the same colour scheme as the palette of the graphics mode you are in, it's no hassle. You just store the second element of the sequence value returned by read_bitmap() in a variable, and then use the library routine display_image() to display the contents of that variable on the screen. If the .BMP file uses a palette colouring scheme different from the palette of the graphics mode you are in, you have to do some extra steps before displaying the .BMP file on the screen. First you take the first element of the sequence returned by read_bitmap() (the .BMP's palette), and divide it by four. You then pass the adjusted palette to all_palette(), to change the graphic mode palette to match that of the .BMP file. Once this is done, you then take the second element of the sequence returned by read_bitmap(), store it in a variable, then use display_image() to present it on the screen.

Why use all_palette() to have the graphic mode's palette adjusted to match that of the .BMP file's palette? Pretend you are attempting to display a .BMP file of a flower, and petals use colour 2 to show a faded pink. In any of the graphic modes, colour 2 is green. So when you try to display this picture of the flower, the petals will appear...you guessed it...GREEN, not PINK. By adjusting the the palette to match that used by the .BMP file, the picture shows up properly.

Why divide the .BMP file palette by four before using it with all_palette()? The red, green, and blue intensity levels of a .BMP file palette go from 0 to 255. In Euphoria, the graphic mode palette for red, green, and blue intensities go from 0 to 63. This means the .BMP file palette uses an intensity scheme that is four times the size of the graphics mode palette. To properly adjust for this, you must divide by four to bring it in line with what the graphic mode can use.

.BMP files using 2,4 16 or 256 colours are supported. If something goes wrong during the reading of the .BMP file data by read_bitmap(), the library routine will return any one of the following error codes:

   1 = open failed (probably spelt the name of the file wrong)
   2 = unexpected end of file (the end of the file was reached before all the
       required data was read in)
   3 = unsupported format (Euphoria may not recognize that format even though other
       paint programs can load it with no problems)

A demo program shows how to load a .BMP file and display it, with a note about adjusting the palette first before displaying it.

Demo program 64
include graphics.e
include image.e
sequence demo_bitmap
if graphics_mode(18) then
     puts(1, "Mode Failure")
else
     demo_bitmap = read_bitmap("d2105a.bmp")
     position(3,10)
     puts(1, "This image was made using Windows Paintbrush program.")
     position(4,10)
     puts(1, "It uses a different palette numbering scheme than what")
     position(5,10)
     puts(1, "Euphoria has, so the colours come out wrong. But if you")
     position(6,10)
     puts(1, "press 'n'..........")
     display_image({200,200}, demo_bitmap[2])
     while get_key() != 'n' do
     end while
     position(3,10)
     puts(1, "......we apply the palette read in with the bitmap after   ")
     position(4,10)
     puts(1, "dividing it by four to scale it for use with Euphoria.     ")
     position(5,10)
     puts(1, "The all_palette() statement is handy for displaying bitmaps")
     position(6,10)
     puts(1, "with a different colour palette. Press 'q' to quit now.    ")
     all_palette((demo_bitmap[1]/4))
     while get_key() != 'q' do
     end while
     if graphics_mode(-1) then
          puts(1, "Mode Failure")
     end if
end if

Your Euphoria programs can take screen images and save them into a .BMP file to be viewed and edited by a paint program, or loaded and displayed by another Euphoria program using read_bitmap().

   include image.e
   ri = save_bitmap(s1,s2)

save_bitmap() creates a Windows .BMP file, s2, using data stored in s1. s1 is a two element sequence, the first being a sequence representing the palette of the .BMP file being created, and the second being a sequence representing the coloured pixels that make up the .BMP file data. s1 matches the format introduced in read_bitmap(). The palette for the .BMP file you are reading is easily obtained using get_all_palette(). Because .BMP file palettes use red, green, and blue intensities between 0 and 255, you must multiply the palette returned by get_all_palette() by 4. Getting the .BMP file data is even easier. You use save_image() to grab whatever image on the screen you want saved in the .BMP file.

Once the .BMP file palette and data are obtained, you use append() to join them together to make a new sequence, which save_bitmap() uses to create your new BMP file. If the .BMP file data sequence was created other than by using save_image(), make sure all sequence elements are of the same length. Note that some paint programs do not support a 4-colour .BMP file, even though save_bitmap() produces 2, 4, 16, and 256 colour .BMP files.

save_bitmap() returns an integer value to report how well things went:

   0 = .BMP file created successfully (it works!)
   1 = .BMP file open failed (probably mis-spelled the name)
   4 = .BMP file invalid mode (invalid graphic mode or parameters received)

A demo is available, showing the steps to create a .BMP file.

Demo program 65
include graphics.e
include image.e
atom blue, increment, status
sequence palette_mode,new_blue, previous_blue, bitmap_data, bitmap_file
blue = 30
increment = 1
if graphics_mode(18) then
     puts(1,"Unable to go into mode 18!")
else
     palette_mode = get_all_palette()
     position(1,3)
     text_color(11)
     puts(1,"How to build a bitmap using Euphoria, Part I")
     position(24,5)
     puts(1,"Press any key to continue demo")
     for ix = 0 to 50 by 4 do
         ellipse(2,0,{100+ix,100-ix},{199-ix,199+ix})
         ellipse(2,0,{100-ix,100+ix},{199+ix,199-ix})
         polygon(1,0,{{50,50},{50,249},{249,249},{249,50}})
     end for
     position(20,1)
     puts(1,"1) Define an area on the screen you want to save")
     while get_key() = -1 do
          if blue = 63 then
               increment = -3
          elsif blue = 30 then
               increment = 3
          end if
          blue = blue + increment
          new_blue = {0,0,0}
          new_blue[3] = blue
          previous_blue = palette(1,new_blue)
     end while
     polygon(0,0,{{50,50},{50,249},{249,249},{249,50}})
     bitmap_data = save_image({50,50},{249,249})
     polygon(0,1,{{50,50},{50,249},{249,249},{249,50}})
     for ix = 3 to 18 do
          position(ix,10)
          print(1,palette_mode[-2+ix])
     end for
     position(20,1)
     puts(1,"2) Get the current mode screen palette..........")
     while get_key() = -1 do
     end while
     palette_mode = palette_mode * 4
     for ix = 3 to 18 do
          position(ix,10)
          print(1,palette_mode[-2+ix])
     end for
     position(20,1)
     puts(1,"3) ....and scale it for use as a bitmap palette. ")
     while get_key() = -1 do
     end while
     clear_screen()
     puts(1,"You then use both the palette and the bitmap data to\n")
     puts(1,"construct the bitmap using save_image..\n")
     bitmap_file = {}
     bitmap_file = append(bitmap_file,palette_mode)
     bitmap_file = append(bitmap_file,bitmap_data)
     while get_key() = -1 do
     end while
     if graphics_mode(-1) then
          puts(1,"Unable to reset mode!")
     end if
     status = save_bitmap(bitmap_file,"d2107a.BMP")
     if status != 0 then
           puts(1,"Bitmap creation failure!")
     end if
end if

If you find the steps taken to assemble a sequence used by save_bitmap() to create a .BMP file a little daunting, Euphoria has another approach that is somewhat simpler:

   include image.e
   ri = save_image(o,s)

save_screen() saves either the entire screen or a rectangular area of the screen to a Windows .BMP file, named s. If o is equal to 0, everything displayed on the screen is saved to .BMP file s. If o is a sequence value, it defines a rectangular area on the screen that is to be saved to .BMP file s. o follows the structure format listed below:

   {{top left pixel colmn, top left pixel row},
    {bottom right pixel column, bottom right pixel row}}

save_screen() uses the palette of the current graphics mode you are in as the .BMP file's palette. The defined rectangular area on the screen is the data that make up the .BMP file image. When o is 0, the recangulare area is from the top left and the bottom right corners of the screen, and is defined automatically for you.

Like save_bitmap(), save_screen() returns and integer value based on the whether or not the .BMP file was created successfully. They are the same as those values returned by save_bitmap(). Also, save_screen() creates .BMP files that have 2, 4, 16, or 256 colours. Some paint programs cannot read 4 colour .BMP files, though any image created by save_bitmap() and save_screen() can be read by read_bitmap().

save_screen() only works in pixel-graphics modes. The demo program is a remake of the save_bitmap() demo program, using save_screen() instead to create a .BMP file.

Demo program 66
include graphics.e
include image.e
atom blue, increment, status
sequence palette_mode,new_blue, previous_blue
blue = 30
increment = 1
if graphics_mode(18) then
     puts(1,"Unable to go into mode 18!")
else
     palette_mode = get_all_palette()
     position(1,3)
     text_color(11)
     puts(1,"How to build a bitmap using Euphoria, Part II")
     position(24,5)
     puts(1,"Press any key to continue demo")
     for ix = 0 to 50 by 4 do
         ellipse(2,0,{100+ix,100-ix},{199-ix,199+ix})
         ellipse(2,0,{100-ix,100+ix},{199+ix,199-ix})
         polygon(1,0,{{50,50},{50,249},{249,249},{249,50}})
     end for
     position(20,1)
     puts(1,"Define an area on the screen you want to save")
     while get_key() = -1 do
          if blue = 63 then
               increment = -3
          elsif blue = 30 then
               increment = 3
          end if
          blue = blue + increment
          new_blue = {0,0,0}
          new_blue[3] = blue
          previous_blue = palette(1,new_blue)
     end while
     all_palette(palette_mode)
     polygon(0,0,{{50,50},{50,249},{249,249},{249,50}})
     status = save_screen({{50,50},{249,249}},"d2109a.bmp")
     polygon(0,1,{{50,50},{50,249},{249,249},{249,50}})
     clear_screen()
     puts(1,"save_screen() produces the same result as save_bitmap()\n")
     puts(1,"but without the steps shown in the save_bitmap() demo.\n")
     while get_key() = -1 do
     end while
     if graphics_mode(-1) then
          puts(1,"Unable to reset mode!")
     end if
end if

With handling .BMP files now explored, let's move on to handling screen pages. Each screen page is assigned a number, beginning with screen page 0. The default active page (where screen output is sent) and the default display page (the page that you want displayed on the screen) are both screen page 0. To change the screen page where all screen output is sent to, you use the following library routine below:

   include image.e
   set_active_page(i)

i is the screen page number you want screen output to be sent to. The number of pages available depends on the graphics mode you are in. If the new active page is not the same as the display page, you will see no changes on the screen when you perform any kind of screen output. Only when you change the display page to the new active page will you see what was sent. You cannot change the active page if you are in partial screen window under Windows. Only if you are in DOS or a full screen window under Windows. If you are uncertain on how many screen pages you have in the graphics mode you are in, use video_config().

To select a screen page you want displayed on the screen, you use the following library routine:

   include image.e
   set_display_page(i)

i is the screen page number you want displayed on the screen. The number of pages you can display depends on the graphics mode you are in. If the new display page is not the same as the active page, you will see no changes on the screen when you perform any kind of screen output. Only when you change the active page to the new display page will you see what was sent. Like set_active_page(), set_display_page() only works in DOS or in a full screen window under Windows.

A demo program will use set_active_page() and set_display_page() to first send screen output to each separate screen page, then to display each screen page one at a time.

Demo program 67
include graphics.e
include image.e
include get.e
integer file_id
sequence capture_buffer, video_data

video_data = video_config()

clear_screen()
printf(1, "%d pages available, 5 pages required\n", {video_data[8]})
if video_data[8] < 5 then
     puts(1, "Sorry, you have insufficient pages on your video card\n")
else
     puts(1, "Stand By, Loading Each Virtual Page\n")

     set_active_page(1)
     file_id = open("now.bin","rb")
     capture_buffer = get(file_id)
     close(file_id)
     display_text_image({1,1}, capture_buffer[2])

     set_active_page(2)
     file_id = open("this.bin","rb")
     capture_buffer = get(file_id)
     close(file_id)
     display_text_image({1,1}, capture_buffer[2])

     set_active_page(3)
     file_id = open("is.bin","rb")
     capture_buffer = get(file_id)
     close(file_id)
     display_text_image({1,1}, capture_buffer[2])

     set_active_page(4)
     file_id = open("ppower.bin","rb")
     capture_buffer = get(file_id)
     close(file_id)
     display_text_image({1,1}, capture_buffer[2])

     set_active_page(0)

     clear_screen()
     puts(1, "Done....to cycle through all the four pages, press 'n'.\n")
     puts(1, "Press any key to start cycling now.\n")
     while get_key() = -1 do
     end while

     for ix = 1 to 4 do
          set_display_page(ix)
          while get_key() != 'n' do
          end while
     end for

     clear_screen()
end if

set_active_page(0)
set_display_page(0)

If you want to know what screen page you have made either an active page or a display page, there are two library routines that can help you find out. To determine which screen page is the current active page, you use:

   include image.e
   ri = get_active_page()

get_active_page() returns the screen page number that screen output is being sent to. The screen page number is stored in receiving variable ri. To determine which screen page is the current display page, you use:

   include image.e
   ri = get_display_page()

get_display_page() returns the screen page number that is being displayed. The screen page number is stored in receiving variable ri.

You now know how to create programs that use colourful text and graphic images. But just in case you do run into some difficulty using graphics, particularly in SVGA (Super Video Graphics Adaptor) mode, where you use colours of 256 and higher, you may need to use a video graphics standard called VESA instead of Euphoria's method of working with the video card:

   include machine.e
   use_vesa(i)

If i is 1, Euphoria uses the VESA graphics standard to generate SVGA mode graphics. Otherwise, 0 means Euphoria uses its own methods. You should issue use_vesa() before using the graphics_mode() library routine. However, it's rare that you need to use this library routine.

Euphoria programs on their own can do a lot. But what if you can access the features of your operating system? The next chapter shows you how!

ToC