Skip to content

Images & drawing

An image is a grid of palette indices; a palette (u32[]) maps indices to 0x00RRGGBB colors. Palette index 0 is always transparent. All drawing is client-side.

Constructing images

A Munos mod ships as one self-contained file, so every image is inlined as data in the source.

image_literal

munos
image_literal(w: i32, h: i32, data: string): image

Builds a w × h image from base64-encoded palette indices (one byte per pixel, row-major), decoded at compile time. Good for simple/hand-built art.

munos
var block: image = image_literal(32, 32, "AQEBAQEB…")

image_from_bytes

munos
image_from_bytes(data: u8[]): image

Builds an image from a byte stream whose first byte is width, second byte is height, and remaining bytes are pixel indices in row-major order. Useful when image data arrives over the network (from event receive).

image_from_bitmap_bytes · client

munos
image_from_bitmap_bytes(data: u8[], dx: i32, dy: i32): image

Decodes a compact indexed-bitmap blob (the format carries its own width, height, and bit depth in a 3-byte header) into an image. Preferred for sprites — a few times smaller than PNG for low-color art, with no decoder overhead at runtime.

dx / dy are a per-image alignment nudge added to the draw position every time this image is drawn — use them to keep an animation's anchor steady as frame bounding-boxes shift. Pass 0, 0 when not needed. Under a flip the nudge is negated automatically so one tuned value stays correct in both facings.

munos
var bytes: u8[] = base64_decode("FRgD…")
var sprite: image = image_from_bitmap_bytes(bytes, 0, 0)

image_from_png_bytes · client

munos
image_from_png_bytes(data: u8[], x: i32, y: i32, w: i32, h: i32): image

Decodes a PNG blob and takes the (x, y, w, h) sub-rectangle as a palette-indexed image — ideal for slicing one frame from a sprite sheet. The palette is generated automatically: pixels with alpha < 128 become index 0 (transparent); opaque colors are sorted by luminance. Every slice of the same data shares one palette.

palette_from_png_bytes · client

munos
palette_from_png_bytes(data: u8[]): u32[]

Returns the auto-generated palette for a PNG blob (slot 0 transparent). Pair with image_from_png_bytes.

munos
var bytes: u8[] = base64_decode("iVBORw0K…")
var sprite: image = image_from_png_bytes(bytes, 0, 0, 16, 16)
var pal:    u32[] = palette_from_png_bytes(bytes)

base64_decode

All three byte-based loaders pair with base64_decode(s: string): u8[] to inline a binary asset directly in the .munos source. See Networking & packing.

Drawing

munos
draw_image(img: image, pal: u32[], x: i32, y: i32,
          flip_x: bool = false, flip_y: bool = false,
          clip_x: i32 = 0, clip_y: i32 = 0, clip_w: i32 = -1, clip_h: i32 = -1,
          clip_rect_x1: i32 = -1, clip_rect_y1: i32 = -1,
          clip_rect_x2: i32 = -1, clip_rect_y2: i32 = -1)

Client-side. Paints img with its top-left at screen (x, y), resolving indices through pal. Index-0 pixels are skipped; an out-of-range index is skipped too.

The common call is just the first four arguments:

munos
draw_image(sprite, pal, x, y)

Flipping

flip_x / flip_y mirror the image horizontally / vertically.

munos
draw_image(sprite, pal, x, y, flip_x = true)

Source-space clip (clip_x/y/w/h)

Picks a sub-rectangle of the image to draw. clip_w = -1 / clip_h = -1 mean "use the full remaining width/height from clip_x/clip_y."

munos
// draw only the 16×16 top-left tile of a larger sheet image
draw_image(sheet, pal, x, y, clip_x = 0, clip_y = 0, clip_w = 16, clip_h = 16)

Screen-space scissor (clip_rect_x1/y1/x2/y2)

A screen rectangle outside which no pixel is drawn. Any pixel landing outside [clip_rect_x1, clip_rect_x2) × [clip_rect_y1, clip_rect_y2) is suppressed. Each side defaults to -1 = "unbounded on this side," so you specify only the edges you need.

munos
// keep a ghost below a 32px-tall HUD strip at the top of the screen
draw_image(ghost, pal, gx, gy, clip_rect_y1 = 32)

The two clips are independent: source-space chooses what part of the image to read; screen-space chooses where on screen it's allowed to land.

Palette swap

Because the palette is a separate argument, the same image draws in different colors with a different palette — recoloring is free.

munos
draw_image(player, marioPal, 100, 100)   // red
draw_image(player, luigiPal, 140, 100)   // green, identical pixels

Part of the MultiNostalgia project.