Blitz:Graphics-BuiltIn

From Amiga Coding
Jump to: navigation, search

Blitz's built-in graphics commands offer powerful and easy-to-use functions for the control, manipulation and use of graphics on the Amiga. Most of these commands access the hardware directly so you will run into problems if you try to use them on systems lacking the native Amiga chipset, or try to use them on classic hardware fitted with a graphics card. Also, these commands operate on graphics objects in Chip RAM, so be careful of using too much memory as Chip RAM is very limited. Fortunately, planar bitmaps only use up the amount of memory they actually need, whereas chunky bitmaps need a minimum of one byte per pixel, and usually 3. This means that only using the number of bitplanes that you actually need will use less memory and run faster than if everything was defined with the maximum number of bitplanes.

The descriptions below assume that you're familiar with the general concepts of Amiga graphics.

Bitmaps

Creating and Managing Bitmaps

Bitmaps are created in memory with the Bitmap command:

Bitmap 0, 320, 256, 6

This command creates a bitmap in memory with an ID of 0, a width of 320 pixels, a height of 256 pixels, and 6 bitplanes deep for a total of 64 possible colours. The bitmap is initialized with every pixel set to colour 0, and is ready to be used immediately. Any number of bitmaps can be created with unique IDs, but be careful of Chip RAM usage if creating large bitmaps!

Bitmap objects can also be created by loading a bitmap directly from an IFF image file on disc:

LoadBitmap 0, "Files:MyImage.iff"

This will load the image from the given filename, and put it in Bitmap object ID 0. The width, height and depth of the bitmap will match those of the file. Optionally, a Palette object ID can be specified, and if the iff file includes palette information, this will be loaded into the Palette object specified:

LoadBitmap 0, "Files:MyImage.iff", 3

This loads the given image as bitmap 0, and puts the palette information from the file in Palette object ID 3.


To select which bitmap to use for drawing commands, use the Use Bitmap statement:

Use Bitmap 1

This sets the bitmap object with ID 1 as the currently used bitmap.

When no longer required, a bitmap may be freed using the Free Bitmap statement:

Free Bitmap 1

This removes the bitmap object with ID 1 from memory, freeing the memory for use elsewhere. It's very important that the bitmap object is not used after being freed - drawing to it after freeing it will still write to the freed memory, which could since be used by another object or task!

Virtual Bitmaps

If you wish to draw directly on a screen, you can point a bitmap object directly at a screen's rastport using the ScreensBitmap command. This assigns a Blitz object number to the rastport, allowing you to "use" it and draw directly to the screen:

ScreensBitmap 0, 1

This line assigns the Bitmap ID 1 to the rastport of screen ID 0. This is good for speed and memory use since it avoids copying bitmaps around, but bad for making lots of changes to the graphics as the screen written to by each command directly, so no buffering takes place.

Similarly, to draw directly onto a shape object, a bitmap object can be created that points to the shape's image data using the ShapesBitmap command:

ShapesBitmap 1, 2

This assigns Bitmap ID 2 and points it at the image data of Shape object ID 1.

Displaying Bitmaps

Bitmaps in memory can be displayed either in a window or directly on a screen:

ShowBitMap 2 ; Displays Bitmap object ID 2 on the current screen
BitmapToWindow 3, 0 ; Copies Bitmap object 3 to the rastport of Window object 0

An alternative form of the BitmapToWindow command allows you to select an area of the bitmap and transfer that to an area of the window's rastport:

Bitmaptowindow 3, 0, 20, 30, 0, 0, 200, 100

This copies from bitmap 3 to window 0, but copies starting at coordinates 20x30 on the bitmap to coordinates 0x0 in the window (top left), and the area copied is 200 pixels wide and 100 pixels high.

General Drawing

The general drawing commands write pixels directly to the currently used bitmap and allow the creation of basic shapes, and will sound familiar to you if you've ever used graphics in BASIC before. In all cases it is up to you to ensure you do not draw outside the bounds of the bitmap as doing so will overwrite adjacent memory, which is very bad indeed! All coordinates start at 0, 0 in the top left corner. Where a colour parameter is required, it refers to a colour index, also known as a pen. The actual colour displayed will depend on the palette applied to the screen on which the graphics are displayed. Giving an index of -1 inverts the colour of all pixels affected by the command.

Plot

The first command is Plot, which is similar to all other BASICs:

Plot 20, 30, 1

This changes the pixel at coordinates 20 across and 30 down, to colour 1.

Line

Line does as you would expect, drawing a straight line from one point to another Normally two sets of coordinates are used, one for the start point and one for the end. If you only provide one set of coordinates however, it will use the end of that last line drawn as the start of the new line. For example:

Line 10, 20, 100, 20, 3 ; Draw a line from 10x20 to 100x20 in pen 3
Line 100, 50, 3 ; Draw a line from the end of the last line (100x20) to 100x50 in pen 3

Rectangles

Boxes can be drawn with the Box and Boxf commands. Both take the same parameters - two sets of coordinates defining opposite corners, and the colour to use. The difference is that Box draws an outline whereas Boxf draws a box filled with the colour:

Boxf 10, 10, 50, 50, 2 ; Draw a solid box with corners at 10x10, 50x10, 10x50 and 50x50 in pen 2
Box 9, 9, 51, 51, 3 ; Draw an outline around the previous box in pen 3

Circles

Circles can be drawn with the Circle and Circlef commands. Like Box and Boxf, Circle draws an outline while Circlef draws a filled circle. The centre coordinates, a radius in pixels and the colour are required to draw a circle. Optionally, a second radius can be included which will instead cause an ellipse to be drawn, the first radius being along the x axis, the second along the y:

Circlef 25, 25, 10, 1 ; Draws a filled circle centred at 25x25 with a radius of 10 in colour 1
Circle 50, 50, 20, 10, 2 ; Draws an outline ellipse at 50x50, with an x radius of 10 and a y radius of 10 in colour 2

Flood Fill

The FloodFill command lets you carry out a flood fill function at the given coordinates. The bitmap will be filled with the colour given, starting at the coordinates, until it finds a different colour or the edge of the bitmap:

Circlef 100, 100, 50, 1 ; Draw a solid circle in pen 1
Line 50, 100, 150, 100, 2 ; Draw a line across the circle in pen 2
FloodFill 100, 75, 3 ; Fills the top half of the circle with pen 3

An optional second colour can be provided to specify a border colour; in this case the flood will only stop when it meets the specified colour or the edge of the bitmap:

FloodFill 100, 75, 3, 4

This will flood over all graphics from 100x75 with pen 3 until it meets pen 4.

Polygons

Polygons can also be drawn, and again there is a Poly and Polyf pair of commands to draw an outline or a solid, filled polygon. Any number of points are allowed, and must be provided as a pointer to an array or NewType. The number of points is also required to be specified, as is the pen. You must ensure that the array or NewType contains at least as many points as you give the command.

NEWTYPE .points
  x1.w
  y1.w
  x2.w
  y2.w
  x3.w
  y3.w
End NEWTYPE

DEFTYPE .points *poly1

*poly1\x1 = 10
*poly1\y1 = 10
*poly1\x2 = 25
*poly1\y2 = 45
*poly1\x3 = 5
*poly1\y3 = 15

Poly 3, *poly1, 1

This draws a polygon outline with the coordinates listed. 3 points are provided and it is drawn in colour 1. The polygon will be closed with a straight line between the last point and the first. The Polyf command has the option for a second colour to be specified. If this is included, the polygon is drawn in the second colour if the points are in anti-clockwise order, otherwise it's drawn in the first colour. If the second colour is specified as -1, the polygon isn't drawn at all if the points are in anti-clockwise order.

Note: For all the filled commands (FloodFill, Circlef, Boxf and Polyf), a separate dummy bitmap is created in order to carry out the fill. This takes up precious Chip RAM, so if memory is tight, it's a good idea to free this dummy bitmap after you've finished drawing. This is done simply using the FreeFill command.

Clearing Bitmaps

Finally, the Cls command clears the current bitmap to pen 0, or optionally, to any pen specified:

Cls ; Clears to pen 0
Cls 2 ; Clears to colour 2

Palette Control

Blitz Palette objects can easily be edited using the built-in commands. More than one palette object can be created at any time, and swapped in to the display whenever required. Editing and setting palettes is fast and simple, and is a good way to produce simple animations over lots of graphics, for example, the traffic in Sim City. This technique is known as palette cycling.

Creating Palettes

Palettes can be created in a number of ways - by initializing a blank palette object, by loading them from an IFF palette file on disc, or by loading as part of a bitmap or shape.

To create a palette object, use the InitPalette command:

InitPalette 0, 32

This sets up a new Palette object with the ID 0, and allocates it to hold 32 individual pens. This makes it suitable for a 5 bitplane screen.

To load a palette from disc, use the LoadPalette command:

LoadPalette 0, "MyColours.iff"

This loads the given filename into Palette object ID 0. Palette IFF files can be saved from most Amiga paint programs, such as Deluxe Paint and Personal Paint.

Palette information can also be saved in IFF image files, and these can also be loaded when loading the file itself, for example with the LoadShape and LoadBitmap commands

Modifying Colours

Colours in a palette can be modified using the PalRGB command:

PalRGB 0, 25, 15, 15, 0

The first parameter is the Palette object ID to modify, 0 in this case. The 25 is the colour register to modify; in the InitPalette example above there are 32 colours allocated and these are numbered from 0 to 31.

The last three parameters are the red, green and blue component values respectively of the colour to create. PalRGB is for use on OCS and ECS systems, so the colour component values range from 0 to 15 (4 bits per channel). In the example above, this gives 15 red, 15 green and 0 blue which creates a bright yellow colour.

For AGA machines, a separate command is available that uses 8 bits per channel for a range of 0-255 in each component. This command only makes sense for programs designed to work on the AGA chipset; palettes created with AGA values will lose the lower 4 bits from each channel when used on an OCS or ECS machine. To set the same colour as the example above on AGA machines:

AGAPalRGB 0, 25, 255, 255, 0

Again, maximum value in red and green channels and 0 in blue gives bright yellow.


Modifying a palette does not automatically update any display using that palette; first the palette needs to be copied to that display. How this is done depends on the display technique. If you're using a system-friendly screen, or a Blitz mode Slice, the ShowPalette command is used to update the screen's colours. If you're using the Display library in Blitz mode, use the DisplayPalette command. Further details are available on the relevant pages.


It's also possible to directly modify a display's colours, however this does not update any Palette objects. Modifying colours in this way takes effect on the display instantly. To do this, the RGB command is used:

RGB 25, 15, 15, 0

where 25 is the colour register to set, and the other three values are the red, green and blue channel values. AGA screens can use the AGA equivalent of the command:

AGARGB 25, 255, 255, 0

Shapes

Creating Shapes

Shapes can be initialized a number of ways. InitShape can be used to create a blank shape to be drawn on later (possibly using the ShapesBitMap command):

InitShape 0, 50, 25, 4

This creates a new Shape object with ID 0, 50 pixels wide, 25 pixels high and 4 bitplanes deep (16 colours).

Shapes can also be created by loading a standard ILBM image from disc. The shape created will match the image format for width, height and depth:

LoadShape 2, "Files:Shapes/Shape2.iff"

Loads the graphics in the specified file into shape object 2. This lets you easily use graphics created in Deluxe Paint, Personal Paint or any other Amiga paint package, in your program. An optional Palette object number can be provided which lets you read the palette information from the file (if it contains any) into the given Palette object:

LoadShape 2, "Files:Shapes/Shape2.iff", 1

Loads the given shape as Shape object ID 2, and also loads its palette into Palette object ID 1.

The third method for creating shapes is to copy a section from an existing bitmap into a new shape object. This is very useful for loading shapes from one file, where the individual graphics objects are evenly spaced in one large bitmap. This is generally known as a "sprite sheet", and is faster to load than many individual shape files. To create a shape in this way, use the GetaShape command:

GetaShape 1, 200, 120, 40, 20

This line creates Shape Object 1, and copies the image information from the currently used bitmap starting at 200 pixels across and 120 pixels down. The area copied is 40 pixels wide and 20 pixels high. The created shape will match this size (40x20) and the depth of the currently used bitmap.

Manipulating Shapes

All shapes have a "handle", which is the point at which the shape is considered to be for drawing/blitting. Normally this is at 0x0, so the top left corner of the shape. This means that the shape placed at coordinates 20x10 will have its top left pixel placed at 20x10, with the rest of the shape to the right and below that point. In some cases it is useful to move this handle, which can be done using the Handle command:

Handle 2, 5, 5

This sets the handle for shape 2 to the coordinates 5x5. For convenience, there is also a command for setting the handle to the middle of a shape:

MidHandle 2

Shapes can be scaled, flipped and rotated using simple commands for each. Some operations will be quite slow however so for speed reasons it might be best to provide pre-transformed versions as extra shape objects.

XFlip 2 ; Flip shape 2 in the horizontal direction
YFlip 3 ; Flip shape 3 in the vertical direction

Scaling shapes requires a scale factor for the X and Y directions. To keep the proportions the same, simply use the same value for both. A factor of 1 is 100%, 2 doubles the size, 0.5 halves the size, etc.

Scale 2, 0.5, 0.25

This scales Shape object 2 to half the original width and 1/4 the original height. An optional Palette object ID can be supplied also - if this is the case, the colours from the palette will be used to determine the more important pixels and therefore preserve as much detail as possible:

Scale 2, 0.5, 0.25, 0 ; Scale with palette 0 colours to reduce the amount of detail lost

Rotating a shape is similar - a rotation factor is required which determines how much clockwise rotation to apply. A full rotation is 1, so 90 degrees clockwise is 0.25, 180 degrees is 0.5, 270 degrees (same as 90 degrees anti-clockwise) is 0.75:

Rotate 2, 0.33 ; Rotate 120 degrees clockwise

Shapes can also be copied:

CopyShape 1, 2

This creates a copy of Shape object ID 1 as a new Shape object with ID 2.

Blitting

Blitting is the process of drawing a shape's graphics onto a bitmap. The Amiga's custom chips include a hardware blitter that provides a very efficient mechanism for moving and copying graphics around in Chip RAM. Blitz makes use of this mechanism to enable fast, efficient drawing of graphics. Several methods are provided for blitting; in most cases it is up to the programmer to ensure that no part of the shape is blitted outside the target bitmap. Bad things will happen if this happens! A simple technique to avoid problems is to make the bitmap larger than required by the size of your largest shape. That way your shape can be almost entirely off the screen but still within the bitmap's limit. Colour pen 0 is taken as transparent, so when a shape is blitted, the graphics already on the bitmap will show through wherever pen 0 is used.

Normal Blitting

The basic method for blitting simply draws the shape and that's it. Simple and quick:

Blit 2, 100, 50

This draws Shape object ID 2 at coordinates 100x50 on the currently used bitmap.

Clipped Blitting

This version of the Blit command works in the same way, except it will clip the shape to fit inside the boundaries of the current bitmap. This makes it safer to use in situations where shapes might extend beyond the edges of the bitmap, but is slightly slower as a result:

ClipBlit 2, 100, 50

Queued Blitting

This blitting method remembers the blits carried out in a queue, allowing them to later be erased to the background colour (pen 0). More than one queue may be defined and used independently, but you must have at least one Queue object.

To define a Queue object, use the Queue statement, providing a unique ID number for that queue and the maximum number of items it will have to remember:

Queue 0, 10

This sets up a Queue object with ID 0 that can hold up to 10 items. Once the queue is set up, shapes can be blitted and added to the queue automatically using the QBlit command:

QBlit 0, 2, 100, 50

This blits shape 2 at the coordinates 100x50 on the current bitmap, and adds the shape's details to Queue object ID 0. Further shapes can be blitted and added to the queue in this way, up to the maximum for that queue.

Once all the required shapes have been blitted, your program can do other things until it's time to redraw the items in the queue. When this is the case, use the UnQueue command to erase all objects in the queue from the bitmap:

UnQueue 0

This erases all items in Queue 0. Once erased, you can start the process again by redrawing the items with QBlit. A full example might look like this:

Queue 0, 10
Repeat
  UnQueue 0
  For i=1 To 10
    QBlit 0, i, enemy(i)\x, enemy(i)\y
  Next i

  ; Do the game logic, movement calculations etc. here

Until quit

In this example, there are 10 enemies to move, with their graphics stored as Shape objects 1-10 and their positions stored in the array enemies() as NewType fields x and y. Each time this loop repeats, all ten enemies will be erased from the current bitmap, and are then redrawn at their new coordinates. If you need to flush objects from the queue, for example when redrawing the enemies without an UnQueue command, use the FlushQueue command:

FlushQueue 0

This will flush all objects in Queue 0, allowing you to fill it again with a new set of blits. This will also have the effect of making any items in the queue permanent, that is, if you then execute an UnQueue command, any items currently drawn in the flushed queue will remain drawn and will not be erased - just like a normal Blit command.

Buffered Blitting

This is similar to queued blitting above, except that the graphics underneath the blitted shape are preserved by the queue and replaced when the shape is erased. This makes it ideal for situations where the shape is moving on top of the graphics already drawn on the bitmap. The general concept of buffered blitting is the same as queued blitting, however a buffer is used instead of a queue. It works the same way, but instead of setting a maximum number of items which can be viewed, you must allocate a certain amount of memory in which to store the preserved graphics. The amount of memory required will depend on the maximum number of objects required to be buffered, and on the size of each shape placed in the buffer. To set up a buffer:

Buffer 0, 16384

This allocates 16384 bytes (16KB) of memory for the buffer, which should be enough for most situations (for example, 16 shapes at 1KB each, or 4 shapes at 4KB each). Beware though, that if you run out of buffer space and try to store more graphics in it, your program will crash. The debugger should catch this for you, but your stand-alone program will have problems!

Instead of QBlit, buffered blits use the BBlit command:

BBlit 0, 2, 100, 50

As for the QBlit example, this blits Shape object 2 to the currently used bitmap at the coordinates 100x50. The existing graphics on the bitmap that the shape will cover are stored in Buffer object 0.

Erasing the buffered shapes and replacing them with the original graphics is handled by the UnBuffer command:

UnBuffer 0

This replaces all the graphics covered by the blits with those from the buffer.

The following example shows some simple usage of buffered blits:

Buffer 0, 16384
Repeat
  UnBuffer 0
  For i=1 To 10
    BBlit 0, i, enemy(i)\x, enemy(i)\y
  Next i
 
  ; Do the game logic, movement calculations etc. here
 
Until quit

You can see that the operation of the buffered blits is basically the same as for queued blits above.

To flush the buffer, and effectively make any blits currently in the buffer permanent, use the FlushBuffer command:

FlushBuffer 0

Block Blitting

Block Blitting is a super quick method of blitting that is intended for blitting tiled displays, such as backgrounds and maps. It works the same way as the Blit command, but with some restrictions as a trade-off for its speed. These restrictions are that it can only blit shapes that are multiples of 16 pixels wide, and it can only blit them at X coordinates a multiple of 16 pixels across. There are no restrictions on the height or y position of the blit. An example of its use for a tile display is as follows:

For i=0 To 9 ; 10 tiles wide
  For j=0 To 9 ; and 10 tiles high
    Block map(i, j), i*32, j*20
  Next j
Next i

The shapes required for each square of the grid are stored in the map() array, which can be read in from a file, for example. Each shape is blitted at a position multiples of 32 pixels across, starting at 0. This will fill a 320x200 bitmap with a map of tiles very quickly indeed.

Stencilled Blitting

Stencilled blitting is a technique for blanking out certain areas of buffered blits, as if the blits were applied through an invisible stencil. This is particularly useful for creating foreground graphics - making the buffered shapes appear behind certain areas but in front of the background. Effectively this gives three layers of graphics: the background at the back, then the moving, buffered blits in the middle, with the stencilled foreground at the front.

To use this technique, the following process should be followed:

  • The background bitmap is created and background graphics drawn to it using any preferred drawing method
  • The Stencil object is created based on the bitmap
  • The foreground graphics are drawn to the bitmap using the special SBlit command
  • The main loop is performed:
    • The moving graphics are drawn with the BBlit command
    • The stencil is applied, removing the sections of the BBlits that are behind the foreground graphics
    • The game logic is performed - new locations for moving graphics determined
    • The BBlits are UnBuffered and the loop starts again

A Stencil object is essentially a simple bitmap that determines where the buffered bilts should not be shown. To set up a Stencil object, use the Stencil command:

Stencil 5, 1

This creates a new Stencil object with the ID 0, based on the main Bitmap object with ID 2. Drawing foreground graphics to the bitmap is performed with the SBlit command:

SBlit 5, 2, 100, 50

This blits Shape object ID 2 to the currently used bitmap, and also updates Stencil object ID 0 with the information required for keeping the shape in the foreground. Once your main loop is running using standard buffered blitting as described above, an extra step is required to apply the stencil to the graphics just buffered and remove the parts that shouldn't be shown. This is carried out by the ShowStencil command:

ShowStencil 0, 5

This applies the Stencil object ID 5 to all objects in Buffer ID 0, automatically erasing the areas that shouldn't be visible, and thus giving the impression of the buffered objects appearing behind the foreground graphics.

Sprites

Sprites are similar to shapes, but they are handled totally separately to bitmaps by the Amiga hardware, and can be moved and manipulated independently to be combined with normal bitmap graphics later by the hardware. This means that, unlike blitting shapes, sprites do not interfere with the graphics on the bitmap, so no redrawing is necessary. There are many limitations to their use however, as follows:

  • Sprites can only be used in Blitz mode
  • A maximum of 8 sprites can be displayed at one time
  • Sprite colours are taken from specific pens on the display
  • Three pens (plus transparent) are allocated to each sprite, so the sprite's graphics can only use 3 colours
  • The width of sprites is limited to 16 pixels on OCS and ECS Amigas, and 64 pixels on AGA Amigas
  • Sprites reduce the bandwidth available for the display itself, which means that AGA display modes must be reduced in width if many sprites are used.

Some of these limitations can be expanded by combining sprites, however this reduces the number of sprites available for use. Two sprites can be combined to create one 15-colour sprite for example, and two 16 pixel width sprites can be combined to create a 32-pixel wide sprite (although this is not necessary on AGA machines).

Note: In Blitz, there are Sprite objects. These contain the graphics to be shown as a sprite, but are separate from hardware sprites, which are referred to as sprite channels. This means you can have as many Sprite objects as you like, but only display one sprite object per sprite channel at a time, giving you your limit. This does allow for animation of your sprites by having the different frames loaded as different Sprite objects, and then switching which Sprite object is displayed in a particular channel.

Sprite Colours

Sprites use fixed pens to get their colours from the current display's palette. The first pen of each sprite's colours is always transparent regardless of what colour it is set to, leaving 3 colours for use in the sprite itself, or 15 colours in the case of combined sprites. On OCS/ECS machines (and by default on AGA machines), these pens are all in the range of pens 17-31, so if you have more than 16 colours in your display, you should be aware that these pens will be shared with the sprites, and any changes to the palette will affect both the display itself and the sprites.

The actual pens used are:

Sprite Channel Pens
0 & 1 17-19
2 & 3 21-23
4 & 5 25-27
6 & 7 29-31

15-colour sprites use the entire range of pens from 17 to 31. When creating sprites however, remember to just use pens 0-3 with 0 being transparent, or 0-15 for 15-colour sprites, again with 0 being transparent. These pens in the image will be mapped in hardware to the pens listed above, e.g. colour 2 on sprite channel 0 will use pen 18 from the display's palette, and on sprite channel 4 will use pen 26.

On AGA machines it's possible to set the range of pens used by sprites to any bank of 16 colours across the 256 pen range of AGA. Also, even and odd-numbered sprite channels can be set to use different banks, so it is possibly for each channel to have a unique 3-colour palette. 15-colour sprites will all use the bank assigned for odd-numbered sprite channels. The banks used are determined by the BPLCON4 register, bits 0-3 selecting the bank for odd channels and bits 4-7 selecting the bank for even channels, so setting the lower byte of the register to %01101011 ($6B) will select bank 11 ($B, pens 176-191) for odd channels and 15-colour sprites, and bank 6 (pens 96-111) for even channels. See the DisplayControls command for further details on setting the contents of BPLCON4.

Creating Sprites

Perhaps the simplest way of creating sprites is to use the GetaSprite command. This creates a sprite by copying the graphics data from an already existing Shape object:

GetaSprite 1, 0

This creates Sprite object ID 1 from the graphics contained in Shape object ID 0.

Displaying Sprites

Sprites can only be displayed in Blitz mode, and the commands for displaying them differ depending on the display method used. See Blitz mode for more details.