Image data c api
This commit is contained in:
parent
b47fbca76d
commit
2484b82873
|
|
@ -1,3 +1,12 @@
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
ColumnLimit: 240
|
ColumnLimit: 240
|
||||||
PointerAlignment: Left
|
PointerAlignment: Left
|
||||||
|
AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortCompoundRequirementOnASingleLine: true
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: None
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
InsertBraces: true
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#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);
|
||||||
|
|
@ -16,9 +16,9 @@ void* malloc(size_t);
|
||||||
void free(void*);
|
void free(void*);
|
||||||
void* realloc(void*, size_t);
|
void* realloc(void*, size_t);
|
||||||
|
|
||||||
void* memset(void* s, uint8_t c, uint32_t n);
|
void* memset(void* d, uint8_t c, size_t n);
|
||||||
void* memcpy(void* dest, const void* src, uint32_t n);
|
void* memcpy(void* dest, const void* src, size_t n);
|
||||||
int memcmp(const void* s1, const void* s2, uint32_t n);
|
int memcmp(const void* s1, const void* s2, size_t n);
|
||||||
|
|
||||||
IMPORT(log) void printf(const char* format, ...);
|
IMPORT(log) void printf(const char* format, ...);
|
||||||
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -137,15 +137,15 @@ void* realloc(void* block, size_t size) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* memset(void* s, uint8_t c, uint32_t n) {
|
void* memset(void* d, uint8_t c, size_t n) {
|
||||||
uint8_t* p = (uint8_t*)s;
|
uint8_t* p = (uint8_t*)d;
|
||||||
while (n--) {
|
while (n--) {
|
||||||
*p++ = c;
|
*p++ = c;
|
||||||
}
|
}
|
||||||
return s;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* memcpy(void* dest, const void* src, uint32_t n) {
|
void* memcpy(void* dest, const void* src, size_t n) {
|
||||||
uint8_t* d = (uint8_t*)dest;
|
uint8_t* d = (uint8_t*)dest;
|
||||||
const uint8_t* s = (const uint8_t*)src;
|
const uint8_t* s = (const uint8_t*)src;
|
||||||
while (n--) {
|
while (n--) {
|
||||||
|
|
@ -154,7 +154,7 @@ void* memcpy(void* dest, const void* src, uint32_t n) {
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
int memcmp(const void* s1, const void* s2, uint32_t n) {
|
int memcmp(const void* s1, const void* s2, size_t n) {
|
||||||
const uint8_t* p1 = (const uint8_t*)s1;
|
const uint8_t* p1 = (const uint8_t*)s1;
|
||||||
const uint8_t* p2 = (const uint8_t*)s2;
|
const uint8_t* p2 = (const uint8_t*)s2;
|
||||||
|
|
||||||
|
|
@ -83,9 +83,11 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
||||||
wasmPath = args.path;
|
wasmPath = args.path;
|
||||||
} else {
|
} else {
|
||||||
const buildAssets = path.resolve(import.meta.dir, 'assets');
|
const buildAssets = path.resolve(import.meta.dir, 'assets');
|
||||||
const stdlib = `${buildAssets}/stdlib.c`;
|
const include = `${buildAssets}/include`;
|
||||||
|
const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`);
|
||||||
|
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 ${buildAssets} -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 -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,8 +1,8 @@
|
||||||
--target=wasm32
|
--target=wasm32
|
||||||
-fno-builtin
|
-fno-builtin
|
||||||
--no-standard-libraries
|
--no-standard-libraries
|
||||||
-I
|
-I
|
||||||
build/assets/
|
build/assets/include
|
||||||
-Wall
|
-Wall
|
||||||
-Wextra
|
-Wextra
|
||||||
-Wpedantic
|
-Wpedantic
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import type { HTMLAttributes } from "preact/compat";
|
||||||
|
import { useEffect, useMemo, useRef } from "preact/hooks";
|
||||||
|
|
||||||
|
interface Props extends HTMLAttributes<HTMLCanvasElement> {
|
||||||
|
dataView: DataView;
|
||||||
|
pointer: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PointerCanvas = ({ dataView, pointer, ...otherProps }: Props) => {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
const ctx = useMemo(() => canvasRef.current?.getContext('2d'), [canvasRef.current]);
|
||||||
|
|
||||||
|
const imageData = useMemo(() => loadImageData(dataView, pointer), [dataView, pointer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (canvasRef.current) {
|
||||||
|
canvasRef.current.width = imageData.width;
|
||||||
|
canvasRef.current.height = imageData.height;
|
||||||
|
}
|
||||||
|
}, [imageData, canvasRef.current]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let raf: number;
|
||||||
|
const frame = () => {
|
||||||
|
if (ctx) {
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
}
|
||||||
|
raf = requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
|
raf = requestAnimationFrame(frame);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(raf);
|
||||||
|
};
|
||||||
|
}, [ctx, imageData]);
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<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,7 +1,7 @@
|
||||||
import type { ComponentChildren } from "preact";
|
import type { ComponentChildren } from "preact";
|
||||||
import { useCallback, useEffect, useRef } from "preact/hooks";
|
import { useCallback, useEffect, useRef } from "preact/hooks";
|
||||||
|
|
||||||
import styles from './modal.module.css';
|
import styles from './Modal.module.css';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
|
import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
|
||||||
import { useBool } from "@common/hooks/useBool";
|
import { useBool } from "@common/hooks/useBool";
|
||||||
import { Modal } from "@common/components/modal/modal";
|
import { Modal } from "@common/components/modal/Modal";
|
||||||
|
|
||||||
import { StateContext } from "../../contexts/state";
|
import { StateContext } from "../../contexts/state";
|
||||||
import { LLMContext } from "../../contexts/llm";
|
import { LLMContext } from "../../contexts/llm";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { MessageTools, type IMessage } from "../../tools/messages"
|
import { MessageTools, type IMessage } from "../../tools/messages"
|
||||||
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||||
import { Modal } from "@common/components/modal/modal";
|
import { Modal } from "@common/components/modal/Modal";
|
||||||
import { DOMTools } from "../../tools/dom";
|
import { DOMTools } from "../../tools/dom";
|
||||||
|
|
||||||
import styles from './minichat.module.css';
|
import styles from './minichat.module.css';
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,18 @@
|
||||||
import { createCanvas } from "@common/display/canvas";
|
import { createCanvas } from "@common/display/canvas";
|
||||||
import life from "./life.c";
|
import life from "./life.c";
|
||||||
|
import { loadImageData } from "@common/components/PointerCanvas";
|
||||||
|
|
||||||
const width = life.getWidth();
|
let context: CanvasRenderingContext2D | null;
|
||||||
const height = life.getHeight();
|
|
||||||
|
|
||||||
const canvas = createCanvas(width, height);
|
|
||||||
const context = canvas.getContext('2d')!;
|
|
||||||
let imageData: ImageData;
|
let imageData: ImageData;
|
||||||
|
|
||||||
const step = life.step as CallableFunction;
|
|
||||||
|
|
||||||
export default function main() {
|
export default function main() {
|
||||||
const pixelsPtr = life.initField();
|
const imageDataPtr = life.initField();
|
||||||
const pixels = new Uint8ClampedArray(life.memory.buffer, pixelsPtr, width * height * 4);
|
imageData = loadImageData(life.data, imageDataPtr);
|
||||||
imageData = new ImageData(pixels, width, height);
|
|
||||||
|
|
||||||
console.log(life, pixels.length);
|
const canvas = createCanvas(imageData.width, imageData.height);
|
||||||
|
context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
console.log(life, imageData);
|
||||||
|
|
||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
}
|
}
|
||||||
|
|
@ -26,16 +23,16 @@ let count = 0;
|
||||||
async function loop() {
|
async function loop() {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
|
|
||||||
step();
|
life.step();
|
||||||
|
|
||||||
context.putImageData(imageData, 0, 0);
|
context?.putImageData(imageData, 0, 0);
|
||||||
context.clearRect(0, 0, 35, 15);
|
context?.clearRect(0, 0, 35, 15);
|
||||||
|
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
|
||||||
sum += end - start;
|
sum += end - start;
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
context.fillText(`${(sum / count).toFixed(1)} ms`, 2, 10);
|
context?.fillText(`${(sum / count).toFixed(1)} ms`, 2, 10);
|
||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
}
|
}
|
||||||
|
|
@ -1,63 +1,54 @@
|
||||||
#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
|
||||||
|
|
||||||
static uint8_t* field;
|
static uint8_t* field;
|
||||||
static uint8_t* nextField;
|
static uint8_t* next_field;
|
||||||
static uint8_t* pixels;
|
static image_data_t image_data;
|
||||||
|
|
||||||
static int countNeighbours(int x, int y);
|
static int count_neighbours(int x, int y);
|
||||||
|
|
||||||
EXPORT(getWidth) int get_width(void) { return width; }
|
|
||||||
EXPORT(getHeight) int get_height(void) { return height; }
|
|
||||||
|
|
||||||
EXPORT(step) void step(void) {
|
EXPORT(step) void step(void) {
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < HEIGHT; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < WIDTH; x++) {
|
||||||
int count = countNeighbours(x, y);
|
int count = count_neighbours(x, y);
|
||||||
uint8_t currentCell = field[x + y * width];
|
uint8_t current_cell = field[x + y * WIDTH];
|
||||||
int nextCell = currentCell;
|
int next_cell = current_cell;
|
||||||
if (currentCell && count < 2)
|
if (current_cell && count < 2)
|
||||||
nextCell = 0;
|
next_cell = 0;
|
||||||
else if (currentCell && count > 3)
|
else if (current_cell && count > 3)
|
||||||
nextCell = 0;
|
next_cell = 0;
|
||||||
else if (!currentCell && count == 3)
|
else if (!current_cell && count == 3)
|
||||||
nextCell = 1;
|
next_cell = 1;
|
||||||
|
|
||||||
nextField[x + y * width] = nextCell;
|
next_field[x + y * WIDTH] = next_cell;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < width * height; i++) {
|
for (int i = 0; i < WIDTH * HEIGHT; i++) {
|
||||||
field[i] = nextField[i];
|
field[i] = next_field[i];
|
||||||
uint8_t px = field[i];
|
uint8_t px = (!field[i]) * 255;
|
||||||
|
|
||||||
if (px) {
|
image_data.pixels[i].r = px;
|
||||||
pixels[(i << 2) + 0] = 0;
|
image_data.pixels[i].g = px;
|
||||||
pixels[(i << 2) + 1] = 0;
|
image_data.pixels[i].b = px;
|
||||||
pixels[(i << 2) + 2] = 0;
|
|
||||||
} else {
|
|
||||||
pixels[(i << 2) + 0] = 255;
|
|
||||||
pixels[(i << 2) + 1] = 255;
|
|
||||||
pixels[(i << 2) + 2] = 255;
|
|
||||||
}
|
|
||||||
pixels[(i << 2) + 3] = 255;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int countNeighbours(int x, int y) {
|
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;
|
||||||
|
|
||||||
if (field[xx + yy * width]) {
|
if (field[xx + yy * WIDTH]) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,14 +57,14 @@ static int countNeighbours(int x, int y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EXPORT(initField) uint8_t* init_field(void) {
|
EXPORT(initField) image_data_t* init_field(void) {
|
||||||
field = malloc(width * height);
|
field = malloc(WIDTH * HEIGHT);
|
||||||
nextField = malloc(width * height);
|
next_field = malloc(WIDTH * HEIGHT);
|
||||||
pixels = malloc(width * height * 4);
|
image_data = create_image(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pixels;
|
return &image_data;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue