refactor drawing

implement shareable cache logic
improve caching
fix drawing bug
This commit is contained in:
2023-05-24 20:02:44 +02:00
parent 330fa6a7f0
commit 60c8bb5877
8 changed files with 231 additions and 150 deletions

View File

@@ -1,4 +1,4 @@
import { FC } from "react";
import { FC, useMemo } from "react";
import { useEffect, useRef, useState } from "react";
import { invoke } from "@tauri-apps/api";
import { useTimelineStore } from "stores/timeline.store";
@@ -16,6 +16,7 @@ import drawStaggeredText, {
calculateLetters,
} from "drawers/staggered-text";
import useMap from "hooks/useMap";
import { Drawer } from "drawers/draw";
type CanvasProps = {};
@@ -28,13 +29,7 @@ function typedArrayToBuffer(array: Uint8Array): ArrayBuffer {
const CanvasComponent: FC<CanvasProps> = () => {
const canvas = useRef<HTMLCanvasElement>(null);
const [loading, setLoading] = useState(true);
const [canvasKit, setCanvasKit] = useState<CanvasKit>();
const [fontData, setFontData] = useState<ArrayBuffer>();
const surface = useRef<Surface>();
const staggeredTextCache = useMap<string, StaggeredTextCache>();
const isLocked = useRef<boolean>(false);
const [didInit, setDidInit] = useState(false);
const renderState = useRenderStateStore((store) => store.renderState);
const { fps, size, duration } = useTimelineStore((store) => ({
fps: store.fps,
@@ -46,126 +41,24 @@ const CanvasComponent: FC<CanvasProps> = () => {
updateEntityById: store.updateEntityById,
}));
const drawer = useMemo(() => new Drawer(), []);
useEffect(() => {
InitCanvasKit({
locateFile: (file) =>
"https://unpkg.com/canvaskit-wasm@latest/bin/" + file,
}).then((CanvasKit) => {
setCanvasKit(CanvasKit);
/* fetch("https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf")
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => {
setLoading(false);
setFontData(arrayBuffer);
}); */
invoke("get_system_font", { fontName: "Helvetica-Bold" }).then((data) => {
console.log(data);
if (Array.isArray(data)) {
const u8 = new Uint8Array(data as any);
const buffer = typedArrayToBuffer(u8);
setFontData(buffer);
setLoading(false);
if (canvas.current) {
const CSurface = CanvasKit.MakeWebGLCanvasSurface(canvas.current);
if (CSurface) {
surface.current = CSurface;
}
}
}
});
});
if (canvas.current && !didInit) {
drawer
.init(canvas.current)
.then(() => {
setDidInit(true);
})
.catch((e) => console.error(e));
}
}, []);
useEffect(() => {
//console.time("calculation");
const parsedEntities = AnimatedEntities.parse(entities);
if (!loading && !isLocked.current) {
isLocked.current = true;
invoke("calculate_timeline_entities_at_frame", {
timeline: {
entities: parsedEntities,
render_state: renderState,
fps,
size,
duration,
},
}).then((data) => {
const entitiesResult = Entities.safeParse(data);
if (canvasKit && canvas.current && surface.current && fontData) {
surface.current.flush();
surface.current.requestAnimationFrame((skCanvas) => {
skCanvas.clear(canvasKit.WHITE);
if (entitiesResult.success) {
const entities = entitiesResult.data;
entities.reverse().forEach((entity) => {
switch (entity.type) {
case EntityType.Enum.Rect:
drawRect(canvasKit, skCanvas, entity);
break;
case EntityType.Enum.Ellipse:
drawEllipse(canvasKit, skCanvas, entity);
break;
case EntityType.Enum.Text:
drawText(canvasKit, skCanvas, entity, fontData);
break;
case EntityType.Enum.StaggeredText:
{
let cache: StaggeredTextCache;
if (!entity.cache.valid) {
const _cache = staggeredTextCache[0].get(entity.id);
if (_cache !== undefined) {
canvasKit.Free(_cache.glyphs);
}
cache = calculateLetters(canvasKit, entity, fontData);
staggeredTextCache[1].set(entity.id, cache);
updateEntityById(entity.id, { cache: { valid: true } });
} else {
const _cache = staggeredTextCache[0].get(entity.id);
if (_cache) {
cache = _cache;
} else {
cache = calculateLetters(canvasKit, entity, fontData);
}
}
drawStaggeredText(
canvasKit,
skCanvas,
entity,
cache.font,
cache.letterMeasures,
cache.metrics
);
}
break;
default:
break;
}
isLocked.current = false;
});
} else {
isLocked.current = false;
console.log(entitiesResult.error);
}
});
}
//console.timeEnd("draw");
});
if (didInit) {
drawer.update(entities);
}
}, [entities, loading, renderState.curr_frame]);
}, [entities, renderState.curr_frame, didInit]);
return (
<div>