diff --git a/packages/core/src/canvas/renderer.ts b/packages/core/src/canvas/renderer.ts index 5bf6e88cc..17df39c2f 100644 --- a/packages/core/src/canvas/renderer.ts +++ b/packages/core/src/canvas/renderer.ts @@ -72,6 +72,8 @@ export class SkiaRenderer { fontsLoaded = false imageCache = new Map() vectorPathCache = new Map() + vectorStrokePathCache = new Map() + vectorStrokeOutlineCache = new Map() fillGeometryCache = new Map() strokeGeometryCache = new Map() scenePicture: SkPicture | null = null @@ -485,10 +487,16 @@ export class SkiaRenderer { } invalidateVectorPath(nodeId: string): void { - const old = this.vectorPathCache.get(nodeId) - if (old) { + for (const cache of [this.vectorPathCache, this.vectorStrokePathCache]) { + const old = cache.get(nodeId) + if (!old) continue for (const p of old) p.delete() - this.vectorPathCache.delete(nodeId) + cache.delete(nodeId) + } + for (const [key, paths] of this.vectorStrokeOutlineCache) { + if (!key.startsWith(`${nodeId}|`)) continue + for (const p of paths) p.delete() + this.vectorStrokeOutlineCache.delete(key) } for (const cache of [this.fillGeometryCache, this.strokeGeometryCache]) { const oldGeom = cache.get(nodeId) diff --git a/packages/core/src/canvas/renderer/lifecycle.ts b/packages/core/src/canvas/renderer/lifecycle.ts index a967d32be..7decbf45d 100644 --- a/packages/core/src/canvas/renderer/lifecycle.ts +++ b/packages/core/src/canvas/renderer/lifecycle.ts @@ -7,7 +7,13 @@ export function destroyRenderer(r: SkiaRenderer): void { for (const img of r.imageCache.values()) img.delete() r.imageCache.clear() - for (const cache of [r.vectorPathCache, r.fillGeometryCache, r.strokeGeometryCache]) { + for (const cache of [ + r.vectorPathCache, + r.vectorStrokePathCache, + r.vectorStrokeOutlineCache, + r.fillGeometryCache, + r.strokeGeometryCache + ]) { for (const paths of cache.values()) { for (const p of paths) p.delete() } diff --git a/packages/core/src/canvas/scene.ts b/packages/core/src/canvas/scene.ts index 0073c97dd..1a22c8deb 100644 --- a/packages/core/src/canvas/scene.ts +++ b/packages/core/src/canvas/scene.ts @@ -322,6 +322,8 @@ function drawVectorStrokeGeometry( function vectorStrokePaths(r: SkiaRenderer, node: SceneNode): Path[] | null { if (!node.vectorNetwork) return null + const cached = r.vectorStrokePathCache.get(node.id) + if (cached) return cached const paths: Path[] = [] for (const segment of node.vectorNetwork.segments) { @@ -350,7 +352,9 @@ function vectorStrokePaths(r: SkiaRenderer, node: SceneNode): Path[] | null { paths.push(path) } - return paths.length > 0 ? paths : null + if (paths.length === 0) return null + r.vectorStrokePathCache.set(node.id, paths) + return paths } function drawVectorPathStrokes( @@ -358,7 +362,8 @@ function drawVectorPathStrokes( canvas: Canvas, vectorPaths: Path[], stroke: SceneNode['strokes'][0], - sc: Color + sc: Color, + outlineCacheKey?: string ): void { const dash = stroke.dashPattern if (dash && dash.length > 0) { @@ -384,13 +389,17 @@ function drawVectorPathStrokes( r.fillPaint.setColor(r.ck.Color4f(sc.r, sc.g, sc.b, sc.a)) r.fillPaint.setAlphaf(stroke.opacity) r.fillPaint.setShader(null) - for (const vp of vectorPaths) { - const outline = vp.copy().stroke(strokeOpts) - if (outline) { - canvas.drawPath(outline, r.fillPaint) - outline.delete() + + let outlines = outlineCacheKey ? r.vectorStrokeOutlineCache.get(outlineCacheKey) : undefined + if (!outlines) { + outlines = [] + for (const vp of vectorPaths) { + const outline = vp.copy().stroke(strokeOpts) + if (outline) outlines.push(outline) } + if (outlineCacheKey) r.vectorStrokeOutlineCache.set(outlineCacheKey, outlines) } + for (const outline of outlines) canvas.drawPath(outline, r.fillPaint) } function drawRegularStroke( @@ -438,7 +447,8 @@ function drawNodeStroke( vectorStroke: Path[] | null ): void { if (vectorStroke && stroke.align === 'CENTER' && node.cornerRadius === 0) { - drawVectorPathStrokes(r, canvas, vectorStroke, stroke, sc) + const outlineKey = `${node.id}|${stroke.weight}|${stroke.cap ?? 'NONE'}|${stroke.join ?? 'MITER'}` + drawVectorPathStrokes(r, canvas, vectorStroke, stroke, sc, outlineKey) return } if (!sg) { @@ -498,10 +508,6 @@ export function renderShapeUncached( } drawNodeStroke(r, canvas, node, rect, hasRadius, stroke, color, sg, vectorPaths, vectorStroke) }) - if (vectorStroke) { - for (const path of vectorStroke) path.delete() - } - r.renderEffects(canvas, node, rect, hasRadius, 'front', shadowChild) }