website statistics

bitmapΒΆ

../../../_images/bitmap-screenshot.png

Although Gameduino’s graphics are sprite-based, with a little setup it can also output pixel-addressed graphics.

This program shows a way of creating a 256x256 pixel 4-color bitmap window. It does this by setting up all 256 sprites in a 16x16 grid, using a 4-color palette. A similar setup is used by the wireframe demo.

The function setpixel() computes the address of an (x,y) pixel in sprite image memory, and masks in the appropriate color value.

The rest of the program repeatedly draws a triangle, moving the corners at each step. Every once in a while it clears the screen and switches to a new four-color palette.

#include <SPI.h>
#include <GD.h>

// replicate a 2-bit color across the whole byte.  Optimization for setpixel
byte replicate(byte color)
{
  return (color << 6) | (color << 4) | (color << 2) | color;
}

// Set pixel at (x,y) to color.  (note that color is replicated).
void setpixel(byte x, byte y, byte color)
{
  /*
   Because of the way the sprites are laid out in setup(), it's not too
   hard to translate the pixel (x,y) to an address and mask.  Taking the
   two byte values as x7-x0 and y7-y0, the address of the pixel is:

      x5 x4 y7 y6 y5 y4 y3 y2 y1 y0 x3 x2 x1 x0

  (x6, x7) gives the value of the mask.
  */
  unsigned int addr = RAM_SPRIMG | (x & 0xf) | (y << 4) | ((x & 0x30) << 8);
  byte mask = 0xc0 >> ((x >> 5) & 6);
  GD.wr(addr, (GD.rd(addr) & ~mask) | (color & mask));
}

// Draw color line from (x0,y0) to (x1,y1).
void line(byte x0, byte y0, byte x1, byte y1, byte color)
{
  byte swap;
#define SWAP(a, b) (swap = (a), (a) = (b), (b) = swap)

  color = replicate(color);
  byte steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    SWAP(x0, y0);
    SWAP(x1, y1);
  }
  if (x0 > x1) {
    SWAP(x0, x1);
    SWAP(y0, y1);
  }
  int deltax = x1 - x0;
  int deltay = abs(y1 - y0);
  int error = deltax / 2;
  char ystep;
  if (y0 < y1)  
    ystep = 1;
  else
    ystep = -1;
  byte x;
  byte y = y0;
  for (x = x0; x < x1; x++) {
    if (steep)
      setpixel(y, x, color);
    else
      setpixel(x, y, color);
    error -= deltay;
    if (error < 0) {
      y += ystep;
      error += deltax;
    }
  }
}

struct point {
  int x, y;
  char xv, yv;
};
static struct point triangle[3];

#define RANDOM_RGB() RGB(random(256),random(256),random(256))

// Restart the drawing
static void restart()
{
  // Clear the screen
  GD.fill(RAM_SPRIMG, 0, 16384);

  // Position triangle at random
  byte i;
  for (i = 0; i < 3; i++) {
    triangle[i].x = 3 + random(250);
    triangle[i].y = 3 + random(250);
  }
  triangle[0].xv = 1;
  triangle[0].yv = 1;
  triangle[1].xv = -1;
  triangle[1].yv = 1;
  triangle[2].xv = 1;
  triangle[2].yv = -1;

  // Choose a random palette
  GD.wr16(PALETTE4A, RGB(0,0,0));
  GD.wr16(PALETTE4A + 2, RANDOM_RGB());
  GD.wr16(PALETTE4A + 4, RANDOM_RGB());
  GD.wr16(PALETTE4A + 6, RANDOM_RGB());
}

void setup()
{
  int i;

  GD.begin();
  GD.ascii();
  GD.putstr(0, 0,"Bitmap demonstration");

  // Draw 256 sprites left to right, top to bottom, all in 4-color
  // palette mode.  By doing them in column-wise order, the address
  // calculation in setpixel is made simpler.
  // First 64 use bits 0-1, next 64 use bits 2-4, etc.
  // This gives a 256 x 256 4-color bitmap.

  for (i = 0; i < 256; i++) {
    int x =     72 + 16 * ((i >> 4) & 15);
    int y =     22 + 16 * (i & 15);
    int image = i & 63;     /* image 0-63 */
    int pal =   3 - (i >> 6);   /* palettes bits in columns 3,2,1,0 */
    GD.sprite(i, x, y, image, 0x8 | (pal << 1), 0);
  }

  restart();
}

void loop()
{
  static byte color;

  if (random(1000) == 0)
    restart();

  line(triangle[0].x, triangle[0].y, triangle[1].x, triangle[1].y, color);
  line(triangle[1].x, triangle[1].y, triangle[2].x, triangle[2].y, color);
  line(triangle[2].x, triangle[2].y, triangle[0].x, triangle[0].y, color);
  color = (color + 1) & 3;
  byte i;
  for (i = 0; i < 3; i++) {
    triangle[i].x += triangle[i].xv;
    triangle[i].y += triangle[i].yv;
    if (triangle[i].x == 0 || triangle[i].x == 255)
      triangle[i].xv = -triangle[i].xv;
    if (triangle[i].y == 0 || triangle[i].y == 255)
      triangle[i].yv = -triangle[i].yv;
  }
}

Last modified $Date: 2011-05-13 11:32:42 -0700 (Fri, 13 May 2011) $