1
0
Fork 0

Graphics library for wasm

This commit is contained in:
Pabloader 2025-05-20 07:23:42 +00:00
parent 2484b82873
commit 1aa082a156
13 changed files with 215 additions and 70 deletions

View File

@ -0,0 +1,37 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef union {
uint32_t color;
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
} image_color_t;
typedef struct {
uint16_t width;
uint16_t height;
image_color_t* pixels;
} image_data_t;
image_data_t image_create(uint16_t width, uint16_t height);
void image_free(image_data_t image);
void image_draw_point(image_data_t image, uint16_t x, uint16_t y, image_color_t color);
void image_draw_hline(image_data_t image, uint16_t x1, uint16_t x2, uint16_t y, image_color_t color);
void image_draw_vline(image_data_t image, uint16_t x, uint16_t y1, uint16_t y2, image_color_t color);
void image_draw_line(image_data_t image, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, image_color_t color);
void image_draw_rect(image_data_t image, uint16_t x, uint16_t y, uint16_t w, uint16_t h, image_color_t color);
void image_fill_rect(image_data_t image, uint16_t x, uint16_t y, uint16_t w, uint16_t h, image_color_t color);
#ifdef __cplusplus
}
#endif

View File

@ -1,22 +0,0 @@
#pragma once
#include <stdint.h>
typedef union {
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
uint32_t color;
} image_pixel_t;
typedef struct {
uint16_t width;
uint16_t height;
image_pixel_t* pixels;
} image_data_t;
image_data_t create_image(uint16_t width, uint16_t height);
void free_image(image_data_t image);

View File

@ -0,0 +1,5 @@
#pragma once
#define abs(x) (((x) < 0) ? -(x): (x))
#define max(a, b) (((a) > (b)) ? (a): (b))
#define min(a, b) (((a) < (b)) ? (a): (b))

106
build/assets/lib/graphics.c Normal file
View File

@ -0,0 +1,106 @@
#include <graphics.h>
#include <math.h>
#include <stdlib.h>
image_data_t image_create(uint16_t width, uint16_t height) {
image_data_t data;
data.width = width;
data.height = height;
size_t length = width * height * sizeof(image_color_t);
data.pixels = (image_color_t*)malloc(length);
memset(data.pixels, 255, length);
return data;
}
void image_free(image_data_t image) {
if (image.pixels) {
free(image.pixels);
}
}
static inline void set_point(image_data_t image, uint16_t x, uint16_t y, image_color_t color) {
image.pixels[x + y * image.width] = color;
}
void image_draw_point(image_data_t image, uint16_t x, uint16_t y, image_color_t color) {
if (x >= image.width || y >= image.height) {
return;
}
set_point(image, x, y, color);
}
void image_draw_hline(image_data_t image, uint16_t x1, uint16_t x2, uint16_t y, image_color_t color) {
if (x1 > x2) {
uint16_t temp = x1;
x1 = x2;
x2 = temp;
}
if (x1 > image.width) {
return;
}
x2 = min(x2, image.width);
do {
image_draw_point(image, x1++, y, color);
} while (x1 < x2);
}
void image_draw_vline(image_data_t image, uint16_t x, uint16_t y1, uint16_t y2, image_color_t color) {
if (y1 > y2) {
uint16_t temp = y1;
y1 = y2;
y2 = temp;
}
if (y1 > image.width) {
return;
}
y2 = min(y2, image.height);
do {
image_draw_point(image, x, y1++, color);
} while (y1 < y2);
}
void image_draw_line(image_data_t image, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, image_color_t color) {
int dx = abs(x2 - x1);
int dy = abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
int e2;
while (1) {
if (x1 >= image.width || y1 >= image.height) {
break;
}
set_point(image, x1, y1, color);
if (x1 == x2 && y1 == y2) {
break;
}
e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x1 += sx;
}
if (e2 < dx) {
err += dx;
y1 += sy;
}
}
}
void image_draw_rect(image_data_t image, uint16_t x, uint16_t y, uint16_t w, uint16_t h, image_color_t color) {
image_draw_hline(image, x, x + w - 1, y, color);
image_draw_hline(image, x, x + w, y + h - 1, color);
image_draw_vline(image, x, y, y + h - 1, color);
image_draw_vline(image, x + w - 1, y, y + h - 1, color);
}
void image_fill_rect(image_data_t image, uint16_t x, uint16_t y, uint16_t w, uint16_t h, image_color_t color) {
while (h--) {
image_draw_hline(image, x, x + w, y++, color);
};
}

View File

@ -1,20 +0,0 @@
#include <stdlib.h>
#include <image_data.h>
image_data_t create_image(uint16_t width, uint16_t height) {
image_data_t data;
data.width = width;
data.height = height;
size_t length = width * height * sizeof(image_pixel_t);
data.pixels = (image_pixel_t*)malloc(length);
memset(data.pixels, 255, length);
return data;
}
void free_image(image_data_t image) {
if (image.pixels) {
free(image.pixels);
}
}

View File

@ -87,7 +87,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`);
const stdlib = await Array.fromAsync(glob.scan());
const opt = production ? '-O3' : '-O0';
const result = await $`clang --target=wasm32 ${opt} -flto -fno-builtin --no-standard-libraries -I ${include} -Wall -Wextra -Wpedantic -Werror -Wl,--lto-O3 -Wl,--no-entry -Wl,--import-memory -o ${wasmPath} ${args.path} ${stdlib}`;
const result = await $`clang --target=wasm32 ${opt} -flto -fno-builtin --no-standard-libraries -I ${include} -Wall -Wextra -Wpedantic -Werror -Wno-gnu-anonymous-struct -Wl,--lto-O3 -Wl,--no-entry -Wl,--import-memory -o ${wasmPath} ${args.path} ${stdlib}`;
if (result.exitCode !== 0) {
throw new Error('Compile failed, check output');

View File

@ -1,3 +1,4 @@
import { loadImageData } from "@common/display/canvas";
import type { HTMLAttributes } from "preact/compat";
import { useEffect, useMemo, useRef } from "preact/hooks";
@ -38,14 +39,3 @@ export const PointerCanvas = ({ dataView, pointer, ...otherProps }: Props) => {
<canvas ref={canvasRef} {...otherProps} />
</>;
};
export const loadImageData = (dataView: DataView, pointer: number) => {
const width = dataView.getUint16(pointer + 0, true);
const height = dataView.getUint16(pointer + 2, true);
const dataPtr = dataView.getUint32(pointer + 4, true);
const imageBuffer = new Uint8ClampedArray(dataView.buffer, dataPtr, width * height * 4);
const imageData = new ImageData(imageBuffer, width, height);
return imageData;
}

View File

@ -1,3 +1,14 @@
export function loadImageData(dataView: DataView, pointer: number) {
const width = dataView.getUint16(pointer + 0, true);
const height = dataView.getUint16(pointer + 2, true);
const dataPtr = dataView.getUint32(pointer + 4, true);
const imageBuffer = new Uint8ClampedArray(dataView.buffer, dataPtr, width * height * 4);
const imageData = new ImageData(imageBuffer, width, height);
return imageData;
}
export function createCanvas(width: number, height: number) {
const canvas = document.createElement('canvas');
@ -13,6 +24,24 @@ export function createCanvas(width: number, height: number) {
return canvas;
}
export function createWasmCanvas(dataView: DataView, pointer: number, onFrame?: () => void) {
const imageData = loadImageData(dataView, pointer);
const canvas = createCanvas(imageData.width, imageData.height);
const context = canvas.getContext('bitmaprenderer');
if (context) {
const frame = async () => {
onFrame?.();
const imageBitmap = await createImageBitmap(imageData);
context.transferFromImageBitmap(imageBitmap);
requestAnimationFrame(frame);
};
frame();
}
return canvas;
}
export function getImageData(image: HTMLImageElement): ImageData {
const canvas = document.createElement('canvas');
canvas.width = image.naturalWidth;

View File

@ -1,6 +1,5 @@
import { createCanvas } from "@common/display/canvas";
import { createCanvas, loadImageData } from "@common/display/canvas";
import life from "./life.c";
import { loadImageData } from "@common/components/PointerCanvas";
let context: CanvasRenderingContext2D | null;
let imageData: ImageData;

View File

@ -1,6 +1,6 @@
#include <graphics.h>
#include <stdint.h>
#include <stdlib.h>
#include <image_data.h>
#define WIDTH 512
#define HEIGHT 512
@ -17,12 +17,13 @@ EXPORT(step) void step(void) {
int count = count_neighbours(x, y);
uint8_t current_cell = field[x + y * WIDTH];
int next_cell = current_cell;
if (current_cell && count < 2)
if (current_cell && count < 2) {
next_cell = 0;
else if (current_cell && count > 3)
} else if (current_cell && count > 3) {
next_cell = 0;
else if (!current_cell && count == 3)
} else if (!current_cell && count == 3) {
next_cell = 1;
}
next_field[x + y * WIDTH] = next_cell;
}
@ -42,8 +43,9 @@ static int count_neighbours(int x, int y) {
int count = 0;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (i == 0 && j == 0)
if (i == 0 && j == 0) {
continue;
}
int xx = (x + i + WIDTH) % WIDTH;
int yy = (y + j + HEIGHT) % HEIGHT;
@ -56,11 +58,10 @@ static int count_neighbours(int x, int y) {
return count;
}
EXPORT(initField) image_data_t* init_field(void) {
field = malloc(WIDTH * HEIGHT);
next_field = malloc(WIDTH * HEIGHT);
image_data = create_image(WIDTH, HEIGHT);
image_data = image_create(WIDTH, HEIGHT);
for (int i = 0; i < WIDTH * HEIGHT; i++) {
field[i] = rand() & 1;

View File

@ -1,5 +1,23 @@
#include <graphics.h>
#include <stdlib.h>
EXPORT(cpptest) void cpptest() {
printf("Awoo! %t%%\n");
static image_data_t image;
EXPORT(step) void step() {
// for (int i = 0; i < 1; i++) {
// int x1 = rand() % image.width;
// int y1 = rand() % image.height;
// int x2 = rand() % image.width;
// int y2 = rand() % image.height;
// image_fill_rect(image, x1, y1, x2 - x1, y2 - y1, {(uint32_t)(rand() | 0xFF000000)});
// }
}
EXPORT(cpptest) image_data_t* cpptest(uint16_t w, uint16_t h) {
image = image_create(w, h);
image_fill_rect(image, 0, 0, w, h, {0xFF000000});
return &image;
}

View File

@ -1,6 +1,8 @@
import { createWasmCanvas } from "@common/display/canvas";
import awoo from "./awoo.cpp";
export default function main() {
awoo.cpptest();
const data = awoo.cpptest(window.innerWidth >> 2, window.innerHeight >> 2);
createWasmCanvas(awoo.data, data, awoo.step);
console.log(awoo);
}