Graphics library for wasm
This commit is contained in:
parent
2484b82873
commit
1aa082a156
|
|
@ -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
|
||||||
|
|
@ -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);
|
|
||||||
|
|
@ -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))
|
||||||
|
|
@ -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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -87,7 +87,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
||||||
const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`);
|
const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`);
|
||||||
const stdlib = await Array.fromAsync(glob.scan());
|
const stdlib = await Array.fromAsync(glob.scan());
|
||||||
const opt = production ? '-O3' : '-O0';
|
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) {
|
if (result.exitCode !== 0) {
|
||||||
throw new Error('Compile failed, check output');
|
throw new Error('Compile failed, check output');
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { loadImageData } from "@common/display/canvas";
|
||||||
import type { HTMLAttributes } from "preact/compat";
|
import type { HTMLAttributes } from "preact/compat";
|
||||||
import { useEffect, useMemo, useRef } from "preact/hooks";
|
import { useEffect, useMemo, useRef } from "preact/hooks";
|
||||||
|
|
||||||
|
|
@ -38,14 +39,3 @@ export const PointerCanvas = ({ dataView, pointer, ...otherProps }: Props) => {
|
||||||
<canvas ref={canvasRef} {...otherProps} />
|
<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;
|
|
||||||
}
|
|
||||||
|
|
@ -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) {
|
export function createCanvas(width: number, height: number) {
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
|
|
||||||
|
|
@ -13,6 +24,24 @@ export function createCanvas(width: number, height: number) {
|
||||||
return canvas;
|
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 {
|
export function getImageData(image: HTMLImageElement): ImageData {
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = image.naturalWidth;
|
canvas.width = image.naturalWidth;
|
||||||
|
|
@ -20,6 +49,6 @@ export function getImageData(image: HTMLImageElement): ImageData {
|
||||||
const ctx = canvas.getContext('2d')!;
|
const ctx = canvas.getContext('2d')!;
|
||||||
ctx.drawImage(image, 0, 0);
|
ctx.drawImage(image, 0, 0);
|
||||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
return imageData;
|
return imageData;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,4 +77,4 @@ export const throttle = function <T, A extends unknown[], R, F extends (this: T,
|
||||||
} as F;
|
} as F;
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { createCanvas } from "@common/display/canvas";
|
import { createCanvas, loadImageData } from "@common/display/canvas";
|
||||||
import life from "./life.c";
|
import life from "./life.c";
|
||||||
import { loadImageData } from "@common/components/PointerCanvas";
|
|
||||||
|
|
||||||
let context: CanvasRenderingContext2D | null;
|
let context: CanvasRenderingContext2D | null;
|
||||||
let imageData: ImageData;
|
let imageData: ImageData;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
#include <graphics.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <image_data.h>
|
|
||||||
|
|
||||||
#define WIDTH 512
|
#define WIDTH 512
|
||||||
#define HEIGHT 512
|
#define HEIGHT 512
|
||||||
|
|
@ -17,12 +17,13 @@ EXPORT(step) void step(void) {
|
||||||
int count = count_neighbours(x, y);
|
int count = count_neighbours(x, y);
|
||||||
uint8_t current_cell = field[x + y * WIDTH];
|
uint8_t current_cell = field[x + y * WIDTH];
|
||||||
int next_cell = current_cell;
|
int next_cell = current_cell;
|
||||||
if (current_cell && count < 2)
|
if (current_cell && count < 2) {
|
||||||
next_cell = 0;
|
next_cell = 0;
|
||||||
else if (current_cell && count > 3)
|
} else if (current_cell && count > 3) {
|
||||||
next_cell = 0;
|
next_cell = 0;
|
||||||
else if (!current_cell && count == 3)
|
} else if (!current_cell && count == 3) {
|
||||||
next_cell = 1;
|
next_cell = 1;
|
||||||
|
}
|
||||||
|
|
||||||
next_field[x + y * WIDTH] = next_cell;
|
next_field[x + y * WIDTH] = next_cell;
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +43,9 @@ static int count_neighbours(int x, int y) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = -1; i <= 1; i++) {
|
for (int i = -1; i <= 1; i++) {
|
||||||
for (int j = -1; j <= 1; j++) {
|
for (int j = -1; j <= 1; j++) {
|
||||||
if (i == 0 && j == 0)
|
if (i == 0 && j == 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int xx = (x + i + WIDTH) % WIDTH;
|
int xx = (x + i + WIDTH) % WIDTH;
|
||||||
int yy = (y + j + HEIGHT) % HEIGHT;
|
int yy = (y + j + HEIGHT) % HEIGHT;
|
||||||
|
|
@ -56,11 +58,10 @@ static int count_neighbours(int x, int y) {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EXPORT(initField) image_data_t* init_field(void) {
|
EXPORT(initField) image_data_t* init_field(void) {
|
||||||
field = malloc(WIDTH * HEIGHT);
|
field = malloc(WIDTH * HEIGHT);
|
||||||
next_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++) {
|
for (int i = 0; i < WIDTH * HEIGHT; i++) {
|
||||||
field[i] = rand() & 1;
|
field[i] = rand() & 1;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,23 @@
|
||||||
|
#include <graphics.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
EXPORT(cpptest) void cpptest() {
|
static image_data_t image;
|
||||||
printf("Awoo! %t%%\n");
|
|
||||||
}
|
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 ℑ
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
import { createWasmCanvas } from "@common/display/canvas";
|
||||||
import awoo from "./awoo.cpp";
|
import awoo from "./awoo.cpp";
|
||||||
|
|
||||||
export default function main() {
|
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);
|
console.log(awoo);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue