272. Color index grid to framebuffer

Convert color-index grids into RGB framebuffer data.

Lesson 272 of 356 · tests/chapter_05_rendering_pipeline/test_272_color_index_grid_to_framebuffer.py

File to create

emulator/rendering/color_index_renderer.py

Why this step exists

Earlier PPU/CHR helpers produce color indexes, not RGB pixels. For example, decode_chr_tile() produces values like:

0, 1, 2, 3

Those values are palette indexes. They are not directly displayable colors yet.

This step creates a pure rendering helper that maps

color-index grid + RGB palette -> Framebuffer

What is a color-index grid? A color-index grid is a 2D grid where each number points into a palette.

Minimal example

grid = [
    [0, 1, 1, 0, ...],
    [2, 3, 3, 0, ...],
    ...
]

palette = [
    (0, 0, 0),
    (85, 85, 85),
    (170, 170, 170),
    (255, 255, 255),
]

Expected framebuffer pixels

(x=0, y=0) -> palette[0] -> (0, 0, 0)
(x=1, y=0) -> palette[1] -> (85, 85, 85)
(x=0, y=1) -> palette[2] -> (170, 170, 170)
(x=1, y=1) -> palette[3] -> (255, 255, 255)

Suggested implementation example

from emulator.rendering.framebuffer import Framebuffer, RGBColor

Grid = list[list[int]]


def color_index_grid_to_framebuffer(
    grid: Grid,
    palette: list[RGBColor],
) -> Framebuffer:
    height = len(grid)
    width = len(grid[0])

    framebuffer = Framebuffer(width=width, height=height)

    for y, row in enumerate(grid):
        for x, color_index in enumerate(row):
            framebuffer.set_pixel(x, y, palette[color_index])

    return framebuffer

Important simplification

For now, assume valid input

grid is not empty
grid is rectangular
palette contains every used index
color indexes are valid

Future validations can be added later if this becomes a debugging problem.

Architecture rule

This helper is pure data transformation. It should not import pygame, CPU, PPU timing, ROM loading, or frontend code.

Out of scope

  • NES palette memory lookup
  • rendering CHR tiles directly
  • rendering pattern tables directly
  • rendering nametables/backgrounds
  • pygame display

Run this lesson

uv run pytest tests/chapter_05_rendering_pipeline/test_272_color_index_grid_to_framebuffer.py -v