1
0
Fork 0

Image data c api

This commit is contained in:
Pabloader 2025-05-19 14:48:03 +00:00
parent b47fbca76d
commit 2484b82873
14 changed files with 166 additions and 74 deletions

View File

@ -1,3 +1,12 @@
IndentWidth: 4
ColumnLimit: 240
PointerAlignment: Left
PointerAlignment: Left
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AllowShortLoopsOnASingleLine: false
InsertBraces: true

View File

@ -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);

View File

@ -16,9 +16,9 @@ void* malloc(size_t);
void free(void*);
void* realloc(void*, size_t);
void* memset(void* s, uint8_t c, uint32_t n);
void* memcpy(void* dest, const void* src, uint32_t n);
int memcmp(const void* s1, const void* s2, uint32_t n);
void* memset(void* d, uint8_t c, size_t n);
void* memcpy(void* dest, const void* src, size_t n);
int memcmp(const void* s1, const void* s2, size_t n);
IMPORT(log) void printf(const char* format, ...);

View File

@ -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);
}
}

View File

@ -137,15 +137,15 @@ void* realloc(void* block, size_t size) {
return ret;
}
void* memset(void* s, uint8_t c, uint32_t n) {
uint8_t* p = (uint8_t*)s;
void* memset(void* d, uint8_t c, size_t n) {
uint8_t* p = (uint8_t*)d;
while (n--) {
*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;
const uint8_t* s = (const uint8_t*)src;
while (n--) {
@ -154,7 +154,7 @@ void* memcpy(void* dest, const void* src, uint32_t n) {
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* p2 = (const uint8_t*)s2;

View File

@ -83,9 +83,11 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
wasmPath = args.path;
} else {
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 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) {
throw new Error('Compile failed, check output');

View File

@ -1,8 +1,8 @@
--target=wasm32
--target=wasm32
-fno-builtin
--no-standard-libraries
-I
build/assets/
build/assets/include
-Wall
-Wextra
-Wpedantic

View File

@ -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;
}

View File

@ -1,7 +1,7 @@
import type { ComponentChildren } from "preact";
import { useCallback, useEffect, useRef } from "preact/hooks";
import styles from './modal.module.css';
import styles from './Modal.module.css';
interface IProps {
open: boolean;

View File

@ -1,6 +1,6 @@
import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
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 { LLMContext } from "../../contexts/llm";

View File

@ -1,6 +1,6 @@
import { MessageTools, type IMessage } from "../../tools/messages"
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 styles from './minichat.module.css';

View File

@ -1,21 +1,18 @@
import { createCanvas } from "@common/display/canvas";
import life from "./life.c";
import { loadImageData } from "@common/components/PointerCanvas";
const width = life.getWidth();
const height = life.getHeight();
const canvas = createCanvas(width, height);
const context = canvas.getContext('2d')!;
let context: CanvasRenderingContext2D | null;
let imageData: ImageData;
const step = life.step as CallableFunction;
export default function main() {
const pixelsPtr = life.initField();
const pixels = new Uint8ClampedArray(life.memory.buffer, pixelsPtr, width * height * 4);
imageData = new ImageData(pixels, width, height);
const imageDataPtr = life.initField();
imageData = loadImageData(life.data, imageDataPtr);
console.log(life, pixels.length);
const canvas = createCanvas(imageData.width, imageData.height);
context = canvas.getContext('2d');
console.log(life, imageData);
requestAnimationFrame(loop);
}
@ -26,16 +23,16 @@ let count = 0;
async function loop() {
const start = performance.now();
step();
life.step();
context.putImageData(imageData, 0, 0);
context.clearRect(0, 0, 35, 15);
context?.putImageData(imageData, 0, 0);
context?.clearRect(0, 0, 35, 15);
const end = performance.now();
sum += end - start;
count++;
context.fillText(`${(sum / count).toFixed(1)} ms`, 2, 10);
context?.fillText(`${(sum / count).toFixed(1)} ms`, 2, 10);
requestAnimationFrame(loop);
}

View File

@ -1,63 +1,54 @@
#include <stdint.h>
#include <stdlib.h>
#include <image_data.h>
#define width 512
#define height 512
#define WIDTH 512
#define HEIGHT 512
static uint8_t* field;
static uint8_t* nextField;
static uint8_t* pixels;
static uint8_t* next_field;
static image_data_t image_data;
static int countNeighbours(int x, int y);
EXPORT(getWidth) int get_width(void) { return width; }
EXPORT(getHeight) int get_height(void) { return height; }
static int count_neighbours(int x, int y);
EXPORT(step) void step(void) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int count = countNeighbours(x, y);
uint8_t currentCell = field[x + y * width];
int nextCell = currentCell;
if (currentCell && count < 2)
nextCell = 0;
else if (currentCell && count > 3)
nextCell = 0;
else if (!currentCell && count == 3)
nextCell = 1;
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int count = count_neighbours(x, y);
uint8_t current_cell = field[x + y * WIDTH];
int next_cell = current_cell;
if (current_cell && count < 2)
next_cell = 0;
else if (current_cell && count > 3)
next_cell = 0;
else if (!current_cell && count == 3)
next_cell = 1;
nextField[x + y * width] = nextCell;
next_field[x + y * WIDTH] = next_cell;
}
}
for (int i = 0; i < width * height; i++) {
field[i] = nextField[i];
uint8_t px = field[i];
for (int i = 0; i < WIDTH * HEIGHT; i++) {
field[i] = next_field[i];
uint8_t px = (!field[i]) * 255;
if (px) {
pixels[(i << 2) + 0] = 0;
pixels[(i << 2) + 1] = 0;
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;
image_data.pixels[i].r = px;
image_data.pixels[i].g = px;
image_data.pixels[i].b = px;
}
}
static int countNeighbours(int x, int y) {
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)
continue;
int xx = (x + i + width) % width;
int yy = (y + j + height) % height;
int xx = (x + i + WIDTH) % WIDTH;
int yy = (y + j + HEIGHT) % HEIGHT;
if (field[xx + yy * width]) {
if (field[xx + yy * WIDTH]) {
count++;
}
}
@ -66,14 +57,14 @@ static int countNeighbours(int x, int y) {
}
EXPORT(initField) uint8_t* init_field(void) {
field = malloc(width * height);
nextField = malloc(width * height);
pixels = malloc(width * height * 4);
EXPORT(initField) image_data_t* init_field(void) {
field = malloc(WIDTH * HEIGHT);
next_field = malloc(WIDTH * HEIGHT);
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;
}
return pixels;
return &image_data;
}