CPC-POWER : CPC-SOFTS - CPCArchives 
Options de Recherche :
 
 
 

ARTICLES

23 / 74

Sprites

Sprites

 

Types of sprites

Software sprite

A software sprite is a sprite which is positioned, clipped, drawn and erased by software functions.

 

Compound sprites

A compound sprite is constructed from more than one sprite.

Each sprite has an (x,y) position which is relative to an origin position, a width and a height.

Each sprite is drawn in a defined order to make the final compound sprite.

For software sprites the order of drawing defines the priority of the image so that an image which is drawn last has a higher priority over an image which is drawn first. The pixels of the last image will be shown in preference to the images from a sprite which is drawn before it.

For a CPC+ hardware sprite the order is pre-defined by the hardware.

An example of a compound sprite is an animating character. The character is constructed from two images. An image for the body, and a an animating image for the legs.

 

Hardware sprites

A hardware sprite is a sprite which is positioned, drawn, erased and clipped by the hardware.

The advantages:

  • The hardware performs all the functions (plotting, clipping and erasing), which means we do not need to use CPU time to do these functions. However, we do need to use CPU time to setup the sprite (initialising the pixel data and defining the position and size of the hardware sprite).

 

The disadvantages:

  • Hardware sprites often have fixed dimensions (for the CPC+ each sprite is 16 pixels wide and 16 pixels tall)
  • There is often a limited number of hardware sprites (for the CPC+, there are 16 hardware sprites)

 

The CPC+ has 16 hardware sprites with these properties:

  • the dimensions of a sprite are 16 pixels wide and 16 pixels tall,
  • the sprites use 16 colours (this colour palette is seperate from the palette used for the main screen)
  • the sprite can be magnified horizontally, vertically or both. Magnifiying the sprite will increase the size of the displayed pixels, and this can be used to match the resolution of the screen:
    • mode 0 (x magnification=4),
    • mode 1 (x magnification=2) or
    • mode 2 (x magnification=1) sized pixels.

 

The CPC and KC Compact do not have hardware sprites.

 

Compiled software sprites

A compiled sprite is a special form of the software sprite.

The pixel and mask data for the sprite image is contained within the instructions to plot the sprite, the original sprite pixel data is not stored. Each sprite drawing function is dedicated to a single sprite image, each function can therefore only draw one sprite.

Drawing a compiled software sprite is much faster than drawing a standard software sprite.

The draw function doesn't use as much CPU time as a standard software sprite because we know in advance which pixels are opaque and which are transparent.

Therefore, we can create a dedicated plot function which doesn't need to perform any unnecessary functions:

  • where a byte of sprite pixel data contains only opaque pixels, the screen pixels will be replaced. We do not require a mask byte and we do not need to read a byte of pixel data from the screen.
  • where a byte of sprite pixel data contains only transparent pixels, the screen pixels will remain unchanged. We do not need a byte of sprite pixel data or a mask byte.
  • where a byte of sprite pixel data contains contains a mixture of opaque and transparent pixels, we require a byte of screen pixel data, a mask byte and we must perform the masking operation and write the result to the screen.

 

In a standard software sprite plotting function, the masking is performed for every byte regardless of whether it is completely opaque or completely transparent, as a result some of the CPU time is being wasted by executing these unnecessary instructions. In contrast, the compiled sprite only executes the necessary instructions, therefore it is faster.

However, because we can only plot one sprite per function, we need one function for each sprite.

  • This can consume a lot of RAM if we use a lot of this type of sprite.
  • the plot routine is dedicated to drawing an entire sprite. If we wanted to clip the sprite we would need many functions for plotting all the results of the sprite being clipped.

 

The instructions for the compiled sprite are generated from the sprite pixel data itself using another function. This reads the byte of sprite pixel data, and outputs the instructions required to draw the pixels in that byte. The output of the function is either the opcodes themselves, or the assembler mneumonics which can then be assembled using an assembler.

 

Compressed/RLE software sprites

A compressed sprite is a special form of the software sprite. It can be drawn faster than a standard software sprite but slower than a compiled sprite.

But, in contrast to the compiled sprite, only one draw function is required for all compressed sprites.

The pixel data for the sprite is processed to identify which bytes of pixel data contain:

  1. all transparent pixels,
  2. all opaque pixels,
  3. a mixture of opaque and transparent pixels.

From this we generate an "instruction stream" which is stored in memory. The original sprite pixel data is not stored. The instruction stream is normally generated using a function which takes the original sprite pixel data as an input, and outputs the instruction stream.

Like the compiled sprite, the compressed sprite does not perform unnecessary instructions to plot the sprite. The "instruction stream" guides the plot function, so that unnecessary instructions are not required.

Example instructions:

"Leave screen pixel data unchanged"
for a byte of sprite pixel data which contains all transparent pixels
"Write sprite pixel data to screen"
for a byte of sprite pixel data which contains all opaque pixels
"Mask screen pixel data, combine with sprite pixel data and write to screen"
for a byte of sprite pixel data which contains a mixture of opaque and transparent pixels
"End of Line/Go to next Line"
where the remainder of the line in the sprite contains only transparent pixels, no plotting is necessary for those pixels. Therefore to reduce CPU time furthur we can ignore those pixels and plot the next line of the sprite.

We can extend the instructions furthur to include a count parameter. This would define the number of continuous bytes which would be processed by the instruction. e.g. "Leave screen pixel data unchanged" with a count of 4, would indicate 4 bytes that would remain unchanged.

Using this method, the "instruction stream" would be smaller than the equivalent uncompressed sprite pixel data. This is ideal for large sprites which would otherwise consume a lot of memory.

 

Pre-shifted software sprites

Pre-shifted sprites are a special form of the software sprite. They are used to draw sprites at a pixel position.

Generally sprites are plotted byte-by-byte.

The image is duplicated, and each image represents the

Each image is "shifted"/offset by one pixel to the right compared to the previous image.

Image 0 is located at pixel offset 0, Image 1 is located at pixel offset 1... Image n is located at pixel offset n.

The number of images is defined by the number of pixels per byte:

  • In mode 0, there are 2 pixels per byte, therefore there would be 2 images,
  • In mode 1, there are 4 pixels per byte, therefore there would be 4 images,
  • In mode 2, there are 8 pixels per byte, therefore there would be 8 images.

In order to draw the sprite to a pixel position we now do the following.

  1. From our pixel based coordinate We now use a pixel based coordinate scheme. This is converted into a byte coordinate and a "pixel offset&quot. The pixel offset is a number between 0 and "pixels per byte"-1. Using the pixel offset we can choose the appropiate image to draw, so that the image is located at the correct pixel position.

Disadvantages:

  • For every sprite which we wish to plot at a pixel position, we require n duplicate images. Where n is the number of pixels in a byte. Therefore, pre-shifted sprites can consume a lot of memory

The alternative is to use a custom plot routine which shifts the data before it is plotted to the screen

 

RLE hardware sprites

One of the disadvantages of the CPC+ hardware sprites is that you must copy the sprite pixels into the ASIC's sprite RAM. If the sprite is animating, then you must do this for every frame of the animation.

RLE hardware sprites is one method to speed up the update of the sprite pixel data.

This method assumes that a sprite is animating, and there is little difference between the current frame and the previous frame of the animation. Therefore it is not necessary to update the entire sprite each frame, we only need to write the pixels where the new frame is different from the previous frame.

The pixel data for each frame is stored in a special form, it is a sequence of instructions and data which is processed by the transfer function.

 

Drawing a software sprite

Generally a sprite image is drawn from top to bottom one line at a time. Each line is drawn from left to right.

The general method is:

  1. Calculate an initial memory address,
  2. Store the current memory address so we can use it later to calculate the memory address of the next line.
  3. Drawing a line:
    1. Plot a byte to the screen,
    2. Calculate the memory address of the byte immediatly to the right of the current byte
    3. Repeat steps 1 and 2 to draw the entire line, plotting the number of bytes for the width of the sprite
  4. Retrieve the stored memory address. Use this to calculate the memory address of the next line.
  5. Repeat steps 2 and 3 to plot each line of the sprite, plotting the number of lines for the height of the sprite

The next section will describe the various plot methods.

Another document will describe the various methods to calculate a memory address and to manipulate it to calculate the next plot position (to the right, or the next line).

 

Plot methods

Replace

With this method, each byte of sprite pixel data is written to the screen. The pixels on the screen are completely replaced.

In Z80 assembly language:

 

 
;; DE = current memory address of sprite pixel data
 
;; HL = current screen memory address
ld a,(de)
;; read byte of sprite pixel data
ld (hl),a
;; write byte to screen

 

This plot method is the fastest, however if this method is used for all of the sprite for both transparent and opaque pixels, the pixels on the screen will be erased where the sprite is drawn.

 

bitwise XOR

With this method, each byte of sprite pixel data is combined with each byte of screen pixel data using a bitwise XOR logical operation.

In Z80 assembly language:

 

 
;; DE = current memory address of sprite pixel data
 
;; HL = current screen memory address
ld a,(de)
;; read byte of sprite pixel data
xor (hl)
;; combine with a byte of screen pixel data
ld (hl),a
;; write result byte to screen

 

The advantages:

  • Where a sprite should be transparent, the scren pixels will show through. This method doesn't require much CPU time,
  • the sprite can be erased, and the background restored simply by re-drawing the sprite at the same position.

 

The disadvantages:

  • where the sprite is plotted, the pixels of the sprite/screen will change colour. This is often undesirable.

 

Truth table for the logical XOR operation:

ABResult
0 0 0
0 1 1
1 0 1
1 1 0

 

Transparency/Masked pixels

The mask can either define:

  • The pixels of the screen to *keep*,
  • The pixels of the screen to *replace*

 

If the mask is stored with the sprite pixel data, we can use this plot method for each byte:

 

 
;; HL = current screen memory address
 
;; DE = current memory address of sprite pixel and mask data
   
 
;; data for sprite:
 
;; sprite pixel data, mask, sprite pixel data, mask ...
ld a,(de)
;; get byte of sprite pixel data
ld c,a
;; store sprite pixel data temporarily in C
ld a,(de)
;; get mask byte
and (hl)
;; mask pixels on screen (remove pixels which will be replaced)
or c
;; combine with sprite pixel data
ld (hl),a
;; write result to screen
 

However we can reduce the size of the memory used to store the sprite pixel and mask data.

A byte can have 256 possible values: 0 to 255 inclusive.

If the same pen, or combination of pens, is always used to define a transparent pixel within the image, then the mask bytes will always be the same, and there will always be exactly one mask value for each byte value: there will be 256 possible mask values.

We have now reduced the number of pens which can be used to define the sprite image:

e.g. If one pen is used to define a transparent pixel then:

  • In mode 0 there will be 15 pens which can be used to define the sprite image,
  • In mode 1 there will be 3 pens which can be used to define the sprite image,
  • In mode 2 there will be 1 pen which can be used to define the sprite image

 

But we no longer need to store a mask for each byte of the sprite pixel data, instead we only need to store a look-up table containing the masks corresponding to each byte value. As a result we can reduce the size of the memory required for the sprite and mask data.

We now use the value of the pixel data to look-up the corresponding mask.

Example source code to generate the mask look-up table.

Plotting each byte of sprite pixel data:

  1. Read byte of sprite pixel data
  2. Using the value of the byte, lookup the corresponding mask byte in the table. Table entry 0 will contain the mask

This now gives us the following advantages:

  • We no longer need to store the mask for each sprite, thus reducing the amount of memory for sprite and mask data

 

If we store the look-up table so that it starts on a 256 byte boundary, we can use the following plot function:

 

 
;; HL = current screen memory address
 
;; DE = current memory address of sprite pixel data
 
;; B = upper 8-bits (bits 15..8) of mask table memory address
   
 
;; data for sprite:
 
;; sprite pixel data, sprite pixel data ...
ld a,(de)
;; get byte of sprite pixel data
ld c,a
;; C = byte of sprite pixel data/look-up table value
 
;; BC = address (in look-up table) of mask corresponding to this sprite pixel data
ld a,(bc)
;; lookup mask from table
and (hl)
;; mask pixels on screen (remove pixels which will be replaced)
or c
;; combine with sprite pixel data
ld (hl),a
;; write result to screen

 

OR:

 

 
;; DE = pixel data
 
;; BC = screen address
 
;; H = upper 8-bits of mask table
pop de
;; get two bytes of sprite pixel data. E is the first byte
 
;; D is the second byte
ld l,e
;; L = first byte. HL = address of mask
ld a,(bc)
;; get byte of screen pixel data
and (hl)
;; mask pixels
or e
;; combine with a byte of screen pixel data
ld (bc),a
;; write result to screen
inc bc
 
ld l,d
 
ld a,(bc)
 
and (hl)
 
or d
 
ld (bc),a
 
inc bc
 

 

 

Article créé le : Dimanche 22 Novembre 2009 à 11 h 51
Dernière mise à jour le : Samedi 03 Mars 2012 à 11 h 44
 
 

CPC-POWER/CPCArchives, projet maintenu par Fredouille.
Programmation par Kukulcan © 2007-2024 tous droits réservés.
Reproduction sans autorisation interdite. Tous les titres utilisées appartiennent à leurs propriétaires respectifs.