fix cache

add font propertie to rust text paint
layout improvements
This commit is contained in:
Enrico Bühler 2023-05-28 01:10:53 +02:00
parent b671f9ee47
commit 1baa3ae736
18 changed files with 176 additions and 84 deletions

Binary file not shown.

View File

@ -30,6 +30,7 @@ pub struct Paint {
pub struct TextPaint { pub struct TextPaint {
pub style: PaintStyle, pub style: PaintStyle,
pub align: TextAlign, pub align: TextAlign,
pub fontName: String,
pub size: f32, pub size: f32,
} }

View File

@ -154,6 +154,7 @@ pub fn test_timeline_entities_at_frame(
color: Color::new(0, 0, 0, 1.0), color: Color::new(0, 0, 0, 1.0),
width: 10.0, width: 10.0,
}), }),
fontName: "Arial".to_string(),
align: TextAlign::Center, align: TextAlign::Center,
size: 20.0, size: 20.0,
}; };
@ -162,6 +163,7 @@ pub fn test_timeline_entities_at_frame(
style: PaintStyle::Fill(FillStyle { style: PaintStyle::Fill(FillStyle {
color: Color::new(0, 0, 0, 1.0), color: Color::new(0, 0, 0, 1.0),
}), }),
fontName: "Arial".to_string(),
align: TextAlign::Center, align: TextAlign::Center,
size: 10.0, size: 10.0,
}; };

View File

@ -20,12 +20,12 @@ export default function App() {
}, []); }, []);
return ( return (
<div className="bg-gray-950 h-full w-full flex flex-col"> <div className="bg-gray-950 overflow-y-hidden h-full w-full flex flex-col">
<MenuBar /> <MenuBar />
<div className="flex flex-row w-full h-full"> <div className="flex flex-row w-full h-full">
<ToolBar /> <ToolBar />
<div className="flex flex-col ml-4 w-full h-full"> <div className="flex flex-col ml-4 mr-4 mt-4 w-full h-full overflow-x-hidden">
<div className="flex gap-4 flex-row mb-4 justify-center items-center"> <div className="flex gap-4 flex-col lg:flex-row mb-4 justify-center items-center">
<Canvas /> <Canvas />
<PropertiesContainer> <PropertiesContainer>
<Properties /> <Properties />

View File

@ -11,6 +11,7 @@ import { FC } from "react";
import { z } from "zod"; import { z } from "zod";
import { AnimatedVec2Properties, ColorProperties } from "./Values"; import { AnimatedVec2Properties, ColorProperties } from "./Values";
import { PropertiesProps } from "./common"; import { PropertiesProps } from "./common";
import { useFontsStore } from "stores/fonts.store";
type TextPropertiesProps = PropertiesProps<z.input<typeof AnimatedTextEntity>>; type TextPropertiesProps = PropertiesProps<z.input<typeof AnimatedTextEntity>>;
type StaggeredTextPropertiesProps = PropertiesProps< type StaggeredTextPropertiesProps = PropertiesProps<
@ -66,6 +67,8 @@ export const TextProperties: FC<TextPropertiesProps> = ({
entity, entity,
onUpdate, onUpdate,
}) => { }) => {
const { fonts } = useFontsStore();
return ( return (
<motion.div <motion.div
variants={{ enter: { opacity: 1, y: 0 }, from: { opacity: 0, y: 50 } }} variants={{ enter: { opacity: 1, y: 0 }, from: { opacity: 0, y: 50 } }}
@ -92,6 +95,38 @@ export const TextProperties: FC<TextPropertiesProps> = ({
} }
></input> ></input>
</label> </label>
<label className="flex flex-col items-start">
<span className="label">Font</span>
<select
onChange={(e) =>
onUpdate({
...entity,
cache: { valid: false },
paint: { ...entity.paint, fontName: e.target.value },
})
}
value={entity.paint.fontName}
>
{fonts.map((font) => (
<option value={font} key={font}>
{font}
</option>
))}
</select>
</label>
<label>
<span className="label">Size</span>
<input
value={entity.paint.size}
onChange={(e) =>
onUpdate({
...entity,
cache: { valid: false },
paint: { ...entity.paint, size: Number(e.target.value) },
})
}
></input>
</label>
<AnimatedVec2Properties <AnimatedVec2Properties
onUpdate={(updatedEntity) => onUpdate={(updatedEntity) =>
onUpdate({ ...entity, origin: updatedEntity }) onUpdate({ ...entity, origin: updatedEntity })
@ -107,6 +142,8 @@ export const StaggeredTextProperties: FC<StaggeredTextPropertiesProps> = ({
entity, entity,
onUpdate, onUpdate,
}) => { }) => {
const { fonts } = useFontsStore();
return ( return (
<motion.div <motion.div
variants={{ enter: { opacity: 1, y: 0 }, from: { opacity: 0, y: 50 } }} variants={{ enter: { opacity: 1, y: 0 }, from: { opacity: 0, y: 50 } }}
@ -127,6 +164,28 @@ export const StaggeredTextProperties: FC<StaggeredTextPropertiesProps> = ({
} }
/> />
</label> </label>
<label className="flex flex-col items-start">
<span className="label">Font</span>
<select
onChange={(e) => {
onUpdate({
...entity,
cache: { valid: false },
letter: {
...entity.letter,
paint: { ...entity.letter.paint, fontName: e.target.value },
},
});
}}
value={entity.letter.paint.fontName}
>
{fonts.map((font) => (
<option value={font} key={font}>
{font}
</option>
))}
</select>
</label>
<label className="flex flex-col items-start"> <label className="flex flex-col items-start">
<span className="label">Size</span> <span className="label">Size</span>
<input <input
@ -134,6 +193,7 @@ export const StaggeredTextProperties: FC<StaggeredTextPropertiesProps> = ({
onChange={(e) => onChange={(e) =>
onUpdate({ onUpdate({
...entity, ...entity,
cache: { valid: false },
letter: { letter: {
...entity.letter, ...entity.letter,
paint: { paint: {

View File

@ -11,7 +11,7 @@ import {
const PropertiesContainer: FC<{ children: ReactNode }> = ({ children }) => { const PropertiesContainer: FC<{ children: ReactNode }> = ({ children }) => {
return ( return (
<div className="w-full rounded-md h-[500px] overflow-auto border transition-colors focus-within:border-gray-400 border-gray-600 flex flex-col items-start p-4"> <div className="w-full rounded-md lg:h-[500px] overflow-auto border transition-colors focus-within:border-gray-400 border-gray-600 flex flex-col items-start p-4">
{children} {children}
</div> </div>
); );

View File

@ -72,12 +72,12 @@ const Track: FC<TrackProps> = ({
value={entity} value={entity}
dragListener={false} dragListener={false}
dragControls={controls} dragControls={controls}
className="h-8 w-full flex flex-row gap-1 select-none" className="h-8 w-96 flex flex-1 flex-row gap-1 select-none"
> >
<div <div
onMouseDown={(e) => e.preventDefault()} onMouseDown={(e) => e.preventDefault()}
onPointerDown={(e) => controls.start(e)} onPointerDown={(e) => controls.start(e)}
className={`h-full transition-all rounded-sm flex-shrink-0 w-96 p-1 px-2 flex flex-row ${ className={`h-full transition-all rounded-sm min-w-[200px] p-1 px-2 flex flex-row ${
selectedEntity === index ? "bg-gray-800" : "bg-gray-900" selectedEntity === index ? "bg-gray-800" : "bg-gray-900"
}`} }`}
> >
@ -87,7 +87,7 @@ const Track: FC<TrackProps> = ({
? deselectEntity() ? deselectEntity()
: selectEntity(index) : selectEntity(index)
} }
className="text-white-800 select-none pointer-events-none" className="text-white-800 select-none cursor-pointer"
> >
{name} {name}
</h3> </h3>
@ -230,11 +230,11 @@ const Timeline: FC<TimelineProps> = () => {
</div> </div>
<div className="gap-1 flex flex-col overflow-y-hidden"> <div className="gap-1 flex flex-col overflow-y-hidden">
<div className="z-20 flex flex-row gap-2"> <div className="z-20 flex flex-row gap-2">
<div className="flex-shrink-0 w-96" /> <div className="flex-shrink-0 min-w-[200px]" />
<TimePicker /> <TimePicker />
</div> </div>
<Reorder.Group <Reorder.Group
className="gap-1 flex flex-col" className="gap-1 flex-1 flex flex-col overflow-scroll"
values={entities} values={entities}
onReorder={setEntities} onReorder={setEntities}
> >

View File

@ -1,3 +1,4 @@
import { C } from "@tauri-apps/api/event-30ea0228";
import { BaseEntity } from "primitives/Entities"; import { BaseEntity } from "primitives/Entities";
import { z } from "zod"; import { z } from "zod";
@ -20,7 +21,12 @@ export function handleEntityCache<
if (cached) { if (cached) {
cache.cleanup(cached); cache.cleanup(cached);
} }
return cache.build();
const nextCache = cache.build();
cache.set(entity.id, nextCache);
return nextCache;
} else { } else {
if (!cached) { if (!cached) {
const nextCache = cache.build(); const nextCache = cache.build();

View File

@ -124,7 +124,7 @@ export class Drawer {
animatedEntities: z.input<typeof AnimatedEntities>, animatedEntities: z.input<typeof AnimatedEntities>,
prepareDependencies: boolean prepareDependencies: boolean
) { ) {
console.time("calculate"); // console.time("calculate");
if (this.didLoad) { if (this.didLoad) {
const renderState = useRenderStateStore.getState().renderState; const renderState = useRenderStateStore.getState().renderState;
@ -145,20 +145,20 @@ export class Drawer {
} }
); );
} else { } else {
console.timeEnd("calculate"); // console.timeEnd("calculate");
} }
} }
requestRedraw(rebuild: boolean) { requestRedraw(rebuild: boolean) {
if (this.didLoad && this.surface) { if (this.didLoad && this.surface && !this.isLocked) {
if (rebuild && this.raf !== undefined) { if (rebuild && this.raf !== undefined) {
cancelAnimationFrame(this.raf); cancelAnimationFrame(this.raf);
this.surface.flush(); // this.surface.flush();
this.raf = this.surface.requestAnimationFrame((canvas) => this.raf = this.surface.requestAnimationFrame((canvas) =>
this.draw(canvas) this.draw(canvas)
); );
} else { } else {
this.surface.flush(); // this.surface.flush();
this.raf = this.surface.requestAnimationFrame((canvas) => this.raf = this.surface.requestAnimationFrame((canvas) =>
this.draw(canvas) this.draw(canvas)
); );
@ -169,7 +169,7 @@ export class Drawer {
draw(canvas: Canvas) { draw(canvas: Canvas) {
if (this.CanvasKit && this.entities && !this.isLocked) { if (this.CanvasKit && this.entities && !this.isLocked) {
this.isLocked = true; this.isLocked = true;
console.time("draw"); //console.time("draw");
const CanvasKit = this.CanvasKit; const CanvasKit = this.CanvasKit;
canvas.clear(CanvasKit.WHITE); canvas.clear(CanvasKit.WHITE);
@ -191,12 +191,19 @@ export class Drawer {
TextCache, TextCache,
TextEntityCache TextEntityCache
>(entity, { >(entity, {
build: () => build: () => {
buildTextCache( const cache = buildTextCache(
CanvasKit, CanvasKit,
entity, entity,
this.dependenciesService.dependencies this.dependenciesService.dependencies
), );
useEntitiesStore
.getState()
.updateEntityById(entity.id, { cache: { valid: true } });
return cache;
},
get: () => this.cache.text.get(entity.id), get: () => this.cache.text.get(entity.id),
set: (id, cache) => this.cache.text.set(id, cache), set: (id, cache) => this.cache.text.set(id, cache),
cleanup: (cache) => { cleanup: (cache) => {
@ -245,7 +252,7 @@ export class Drawer {
} }
}); });
this.isLocked = false; this.isLocked = false;
console.timeEnd("draw"); //console.timeEnd("draw");
} }
} }
} }

View File

@ -203,6 +203,7 @@ export default function drawStaggeredText(
buildPaintStyle(CanvasKit, paint, entity.letter.paint); buildPaintStyle(CanvasKit, paint, entity.letter.paint);
if (glyphs) {
// Draw all those runs. // Draw all those runs.
for (let i = 0; i < measuredLetters.length; i++) { for (let i = 0; i < measuredLetters.length; i++) {
const measuredLetter = measuredLetters[i]; const measuredLetter = measuredLetters[i];
@ -249,7 +250,9 @@ export default function drawStaggeredText(
// Center the origin // Center the origin
origin[0] = origin[0] =
origin[0] + measuredLetter.bounds.width / 2 + measuredLetter.offset.x; origin[0] +
measuredLetter.bounds.width / 2 +
measuredLetter.offset.x;
origin[1] = origin[1] - metrics.descent + lineOffset; origin[1] = origin[1] - metrics.descent + lineOffset;
//console.log(measuredLetter.bounds); //console.log(measuredLetter.bounds);
@ -282,3 +285,4 @@ export default function drawStaggeredText(
} }
} }
} }
}

View File

@ -138,6 +138,7 @@ function buildText(
type: "Fill", type: "Fill",
color, color,
}, },
fontName: "Arial",
size, size,
align: "Center", align: "Center",
}, },
@ -202,6 +203,7 @@ function buildStaggeredText(
stagger: 0.05, stagger: 0.05,
letter: { letter: {
paint: { paint: {
fontName: "Arial",
style: { style: {
type: "Fill", type: "Fill",
color, color,

View File

@ -35,7 +35,7 @@ export const Paint = z.object({
export const TextPaint = z.object({ export const TextPaint = z.object({
style: PaintStyle, style: PaintStyle,
align: TextAlign, align: TextAlign,
fontName: z.string().default("Helvetica-Bold"), fontName: z.string(),
size: z.number().min(0), size: z.number().min(0),
}); });

View File

@ -28,10 +28,9 @@ export class DependenciesService {
) { ) {
const fontNames = new Set<string>(); const fontNames = new Set<string>();
entities.forEach((entity) => { console.log(entities);
if (entity.type === EntityType.Enum.Text) {
}
entities.forEach((entity) => {
switch (entity.type) { switch (entity.type) {
case EntityType.Enum.Text: case EntityType.Enum.Text:
fontNames.add(entity.paint.fontName); fontNames.add(entity.paint.fontName);
@ -62,22 +61,26 @@ export class DependenciesService {
async loadFonts(fontNames: Set<string>) { async loadFonts(fontNames: Set<string>) {
const resolveFonts: Array<Promise<void>> = []; const resolveFonts: Array<Promise<void>> = [];
fontNames.forEach((fontName) => { const loadFont = async (fontName: string) => {
if (!this.dependencies.fonts.has(fontName)) { return invoke("get_system_font", { fontName }).then((data) => {
resolveFonts.push(
invoke("get_system_font", { fontName }).then((data) => {
if (Array.isArray(data)) { if (Array.isArray(data)) {
const u8 = new Uint8Array(data as any); const u8 = new Uint8Array(data as any);
const buffer = typedArrayToBuffer(u8); const buffer = typedArrayToBuffer(u8);
this.dependencies.fonts.set(fontName, buffer); this.dependencies.fonts.set(fontName, buffer);
} }
}) });
); };
fontNames.forEach((fontName) => {
if (!this.dependencies.fonts.has(fontName)) {
resolveFonts.push(loadFont(fontName));
} }
}); });
await Promise.all(resolveFonts); await Promise.all(resolveFonts);
console.log(this.dependencies); console.log(fontNames);
// console.log(this.dependencies);
} }
} }

View File

@ -36,6 +36,12 @@ export class PlaybackService {
} }
}); });
useEntitiesStore.subscribe((state) => {
if (!this.playing) {
this.seek();
}
});
this.seek(); this.seek();
} }

View File

@ -7,7 +7,7 @@ interface FontsStore {
const useFontsStore = create<FontsStore>((set) => ({ const useFontsStore = create<FontsStore>((set) => ({
fonts: [], fonts: [],
setFonts: (fonts) => ({ fonts }), setFonts: (fonts) => set({ fonts }),
})); }));
export { useFontsStore }; export { useFontsStore };

View File

@ -7,7 +7,7 @@ interface TimelineStore {
} }
const useTimelineStore = create<TimelineStore>((set) => ({ const useTimelineStore = create<TimelineStore>((set) => ({
fps: 120, fps: 30,
size: [1280, 720], size: [1280, 720],
duration: 10.0, duration: 10.0,
})); }));

View File

@ -72,6 +72,7 @@ body,
#root { #root {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden;
} }
.SliderRoot { .SliderRoot {