Skip to main content

Drawing Sprites

WASM-4 can blit sprites directly to the screen. A sprite is simply a pointer to raw bytes in memory.

blit (spritePtr, x, y, width, height, flags)

Sprites come in two formats: 1BPP and 2BPP.

1BPP Format#

1BPP sprites require 1 Bit Per Pixel. That means each pixel can be one of two colors. Bit value 0 uses DRAW_COLORS color 1, and bit value 1 is DRAW_COLORS color 2.

First let's define an 8x8 1BPP sprite called smiley at the top of our program. We do this by using the binary literal syntax for each row of 8 pixels. If you look closely you can see it's a picture of a smiley face: ๐Ÿ™‚

const smiley = memory.data<u8>([    0b11000011,    0b10000001,    0b00100100,    0b00100100,    0b00000000,    0b00100100,    0b10011001,    0b11000011,]);

Now that we have our sprite data, we can blit it to the screen at position (10, 10).

w4.blit(smiley, 10, 10, 8, 8, w4.BLIT_1BPP);

Just like any other drawing function, we can set DRAW_COLORS to change the colors.

The last parameter to blit() is a flags bitset. We can bitwise OR (|) flags together to modify the behavior of blit(). For example, to flip the sprite vertically: ๐Ÿ™ƒ

w4.blit(smiley, 10, 10, 8, 8, w4.BLIT_1BPP | w4.BLIT_FLIP_Y);

For a full list of blit flags, see the Functions reference.

2BPP Format#

2BPP sprites require 2 Bits Per Pixel. That means each pixel can be one of four colors.

Unlike with 1BPP, it's harder to "draw" the sprite directly in the source code with binary literal ASCII art. An alternative is to use an image editor with a palette of 4 colors and save an indexed PNG. We can then use w4 png2src to convert it to source code to paste into our program.

For example, if we have this 4-color image of a bunny:

Note that the RGB color of this sprite doesn't matter, w4 png2src only cares about a pixel's palette index.

w4 png2src --assemblyscript bunny.png

Will print out the following generated source:

const bunny_width = 16;const bunny_height = 16;const bunny_flags = 1; // BLIT_2BPPconst bunny = memory.data<u8>([ 0xaa,0x9e,0xac,0xaa,0xaa,0x57,0xbf,0x2a,0xaa,0x57,0xbf,0x2a,0xaa,0x17,0xbf,0x2a,0xaa,0x17,0x03,0x2a,0xaa,0x57,0x54,0x2a,0xa8,0x55,0x55,0x6a,0xa9,0x55,0x05,0x0a,0xaf,0xd5,0x55,0x4a,0xa8,0x75,0x55,0x4a,0xaa,0xd5,0x57,0x2a,0xaa,0x1d,0x7c,0xaa,0xa8,0x75,0x15,0x2a,0xa8,0x45,0x15,0x2a,0xaa,0x10,0x54,0xaa,0xaa,0x85,0x52,0xaa ]);

Pasting that into our program, we can draw a bunny like so:

*DRAW_COLORS = 0x2013;blit(bunny, 10, 10, bunny_width, bunny_height, bunny_flags);

DRAW_COLORS is used here to modify the 4 colors of the original sprite. Reading from right to left: color 1 becomes 3, color 2 becomes 1, color 3 becomes 0 (transparent), and color 4 becomes 2.

Custom template#

You can use a custom template for generating a image source. Use a --template filename for this.

Templates use the Mustache syntax. An array called sprites is provided.

Basic template:

{{#sprites}}// {{name}}#define {{name}}Width {{width}}#define {{name}}Height {{height}}#define {{name}}Flags {{flagsHumanReadable}}const uint8_t {{name}}[{{length}}] = { {{bytes}} };
{{/sprites}}

Where:

  • {{#sprites}}, {{/sprites}} - Start and end of the list of sprites
  • {{name}} - filename (i.e. wallTop; string),
  • {{rustName}} - Rust specific variable name (i.e. WALL_TOP; string)
  • {{odinName}} - Odin specific variable name (i.e. wall_top; string)
  • {{odinFlags}} - Odin specific flags (nil or .USE_2BPP)
  • {{width}}, {{height}} - image dimensions (integer)
  • {{flags}}, {{flagsHumanReadable}} - type flag as integer and enum name (BLIT_2BPP or BLIT_1BPP)
  • {{length}} - count of bytes (integer)
  • {{bytes}} - comma separated series of bytes (string)
  • {{firstByte}} - first byte of the sprite (string)
  • {{restBytes}} - comma-separated series of bytes excluding the first one (string)