/// Build a BMP image by iterating over each pixel with some state. /// /// The callback gets the X and Y position of the pixel, where 0,0 is /// bottom-left. /// /// Rendering starts at the top and goes over each row, left to right, top to /// bottom. /// pub fn render( width width: Int, height height: Int, state state: state, pixel plot: fn(state, Int, Int) -> #(state, Pixel), ) -> #(state, BitArray) { let #(bmp, padding_size) = file_header(width, height) let bmp = dib_header(bmp, width, height) let padding_bits = padding_size * 8 let padding = <<0:size(padding_bits)>> render_loop(bmp, width, 0, height - 1, padding, state, plot) } /// Build a BMP image by iterating over each pixel. /// /// The callback gets the X and Y position of the pixel, where 0,0 is /// bottom-left. /// /// Rendering starts at the top and goes over each row, left to right, top to /// bottom. /// pub fn simple_render( width width: Int, height height: Int, pixel plot: fn(Int, Int) -> Pixel, ) -> BitArray { render(width, height, Nil, fn(state, x, y) { #(state, plot(x, y)) }).1 } pub opaque type Pixel { Pixel(data: BitArray) } pub fn rgb(red red: Int, green green: Int, blue blue: Int) -> Pixel { Pixel(<>) } fn file_header(width: Int, height: Int) -> #(BitArray, Int) { let padding = { 4 - { width * 3 } % 4 } % 4 let row_size = width * 3 + padding let file_size = 54 + row_size * height let pixel_offset = 54 let reserved = 0 let bmp = << "BM", file_size:little-size(32), reserved:little-size(16), reserved:little-size(16), pixel_offset:little-size(32), >> #(bmp, padding) } fn dib_header(bmp: BitArray, width: Int, height: Int) -> BitArray { let dib_header_size = 40 let planes = 1 // RGB: 8 * 3 let bits_per_pixel = 24 let compression = 0 let image_size = 0 let x_pixels_per_meter = 0 let y_pixels_per_meter = 0 let colours_in_palette = 0 let important_colours = 0 // Negative height is used in order to render from top to bottom let height = -height << bmp:bits, dib_header_size:little-32, width:little-32, height:little-32, planes:little-16, bits_per_pixel:little-16, compression:little-32, image_size:little-32, x_pixels_per_meter:little-32, y_pixels_per_meter:little-32, colours_in_palette:little-32, important_colours:little-32, >> } fn render_loop( bmp: BitArray, width: Int, x: Int, y: Int, padding: BitArray, state: state, render: fn(state, Int, Int) -> #(state, Pixel), ) -> #(state, BitArray) { let #(state, Pixel(pixel)) = render(state, x, y) let bmp = <> let x2 = { x + 1 } % width case x2, y { 0, 0 -> #(state, <>) 0, _ -> { let bmp = <> render_loop(bmp, width, 0, y - 1, padding, state, render) } _, _ -> render_loop(bmp, width, x2, y, padding, state, render) } }