From a9b9cf6bf50f08723896659634f2e6afc17bcd42 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 29 Apr 2026 00:37:56 +0200 Subject: chore: switch from biome to eslint + prettier; fix ton of lint issues --- apps/web/src/services/renderer/canvas-renderer.ts | 41 ++++------------- apps/web/src/services/renderer/canvas-utils.ts | 19 ++++---- .../renderer/compositor/frame-descriptor.ts | 23 +++------- apps/web/src/services/renderer/effect-preview.ts | 25 ++++------ apps/web/src/services/renderer/gpu-renderer.ts | 53 +++------------------- apps/web/src/services/renderer/mask-feather.ts | 6 +-- .../src/services/renderer/nodes/graphic-node.ts | 17 ++----- apps/web/src/services/renderer/nodes/image-node.ts | 11 +++-- apps/web/src/services/renderer/resolve.ts | 10 ++-- 9 files changed, 61 insertions(+), 144 deletions(-) (limited to 'apps/web/src/services/renderer') diff --git a/apps/web/src/services/renderer/canvas-renderer.ts b/apps/web/src/services/renderer/canvas-renderer.ts index 4a002675..58294e9c 100644 --- a/apps/web/src/services/renderer/canvas-renderer.ts +++ b/apps/web/src/services/renderer/canvas-renderer.ts @@ -1,5 +1,6 @@ import type { FrameRate } from "opencut-wasm"; import type { AnyBaseNode } from "./nodes/base-node"; +import { createCanvasSurface } from "./canvas-utils"; import { buildFrameDescriptor } from "./compositor/frame-descriptor"; import { wasmCompositor } from "./compositor/wasm-compositor"; import { resolveRenderTree } from "./resolve"; @@ -16,8 +17,8 @@ export type CanvasRendererParams = { }; export class CanvasRenderer { - canvas: OffscreenCanvas | HTMLCanvasElement; - context: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D; + canvas: OffscreenCanvas; + context: OffscreenCanvasRenderingContext2D; width: number; height: number; fps: FrameRate; @@ -27,22 +28,9 @@ export class CanvasRenderer { this.height = height; this.fps = fps; - try { - this.canvas = new OffscreenCanvas(width, height); - } catch { - this.canvas = document.createElement("canvas"); - this.canvas.width = width; - this.canvas.height = height; - } - - const context = this.canvas.getContext("2d"); - if (!context) { - throw new Error("Failed to get canvas context"); - } - - this.context = context as - | OffscreenCanvasRenderingContext2D - | CanvasRenderingContext2D; + const surface = createCanvasSurface({ width, height }); + this.canvas = surface.canvas; + this.context = surface.context; } getOutputCanvas(): HTMLCanvasElement { @@ -57,20 +45,9 @@ export class CanvasRenderer { this.width = width; this.height = height; - if (this.canvas instanceof OffscreenCanvas) { - this.canvas = new OffscreenCanvas(width, height); - } else { - this.canvas.width = width; - this.canvas.height = height; - } - - const context = this.canvas.getContext("2d"); - if (!context) { - throw new Error("Failed to get canvas context"); - } - this.context = context as - | OffscreenCanvasRenderingContext2D - | CanvasRenderingContext2D; + const surface = createCanvasSurface({ width, height }); + this.canvas = surface.canvas; + this.context = surface.context; } async render({ node, time }: { node: AnyBaseNode; time: number }) { diff --git a/apps/web/src/services/renderer/canvas-utils.ts b/apps/web/src/services/renderer/canvas-utils.ts index 093168e6..6981e6d5 100644 --- a/apps/web/src/services/renderer/canvas-utils.ts +++ b/apps/web/src/services/renderer/canvas-utils.ts @@ -1,16 +1,17 @@ -export function createOffscreenCanvas({ +export function createCanvasSurface({ width, height, }: { width: number; height: number; -}): OffscreenCanvas | HTMLCanvasElement { - try { - return new OffscreenCanvas(width, height); - } catch { - const canvas = document.createElement("canvas"); - canvas.width = width; - canvas.height = height; - return canvas; +}): { + canvas: OffscreenCanvas; + context: OffscreenCanvasRenderingContext2D; +} { + const canvas = new OffscreenCanvas(width, height); + const context = canvas.getContext("2d"); + if (!context) { + throw new Error("Failed to create 2D rendering context"); } + return { canvas, context }; } diff --git a/apps/web/src/services/renderer/compositor/frame-descriptor.ts b/apps/web/src/services/renderer/compositor/frame-descriptor.ts index 190a0237..1bc316ef 100644 --- a/apps/web/src/services/renderer/compositor/frame-descriptor.ts +++ b/apps/web/src/services/renderer/compositor/frame-descriptor.ts @@ -3,7 +3,7 @@ import { masksRegistry } from "@/masks"; import { incrementCounter } from "@/diagnostics/render-perf"; import type { AnyBaseNode } from "../nodes/base-node"; import type { CanvasRenderer } from "../canvas-renderer"; -import { createOffscreenCanvas } from "../canvas-utils"; +import { createCanvasSurface } from "../canvas-utils"; import { BlurBackgroundNode } from "../nodes/blur-background-node"; import { ColorNode } from "../nodes/color-node"; import { EffectLayerNode } from "../nodes/effect-layer-node"; @@ -414,15 +414,11 @@ function buildMaskArtifacts({ const { width: canvasWidth, height: canvasHeight } = renderer; const maskContentHash = `mask:${mask.type}:${JSON.stringify(mask.params)}:${transformHash(transform)}:${canvasWidth}x${canvasHeight}:direct=${shouldRenderMaskDirectly}`; const drawMask: TextureCanvasDrawFn = (ctx) => { - const elementMaskCanvas = createOffscreenCanvas({ - width: Math.round(transform.width), - height: Math.round(transform.height), - }); - const elementMaskCtx = elementMaskCanvas.getContext("2d") as - | CanvasRenderingContext2D - | OffscreenCanvasRenderingContext2D - | null; - if (!elementMaskCtx) return; + const { canvas: elementMaskCanvas, context: elementMaskCtx } = + createCanvasSurface({ + width: Math.round(transform.width), + height: Math.round(transform.height), + }); if (shouldRenderMaskDirectly && definition.renderer.renderMask) { definition.renderer.renderMask({ @@ -465,15 +461,10 @@ function buildMaskArtifacts({ const strokeTextureId = `${path}:mask-stroke`; const strokeContentHash = `stroke:${mask.type}:${JSON.stringify(mask.params)}:${transformHash(transform)}:${canvasWidth}x${canvasHeight}`; const drawStroke: TextureCanvasDrawFn = (ctx) => { - const strokeCanvas = createOffscreenCanvas({ + const { canvas: strokeCanvas, context: strokeCtx } = createCanvasSurface({ width: Math.round(transform.width), height: Math.round(transform.height), }); - const strokeCtx = strokeCanvas.getContext("2d") as - | CanvasRenderingContext2D - | OffscreenCanvasRenderingContext2D - | null; - if (!strokeCtx) return; if (definition.renderer.renderStroke) { definition.renderer.renderStroke({ diff --git a/apps/web/src/services/renderer/effect-preview.ts b/apps/web/src/services/renderer/effect-preview.ts index c4cacde0..cbfb1a31 100644 --- a/apps/web/src/services/renderer/effect-preview.ts +++ b/apps/web/src/services/renderer/effect-preview.ts @@ -1,4 +1,4 @@ -import { createOffscreenCanvas } from "./canvas-utils"; +import { createCanvasSurface } from "./canvas-utils"; import { effectsRegistry, resolveEffectPasses } from "@/effects"; import { buildDefaultParamValues } from "@/params/registry"; import type { ParamValues } from "@/params"; @@ -8,7 +8,7 @@ const PREVIEW_SIZE = 160; const PREVIEW_IMAGE_PATH = "/effects/preview.jpg"; class EffectPreviewService { - private testSourceCanvas: OffscreenCanvas | HTMLCanvasElement | null = null; + private testSourceCanvas: OffscreenCanvas | null = null; private previewImageElement: HTMLImageElement | null = null; private onReadyCallbacks = new Set<() => void>(); @@ -98,7 +98,7 @@ class EffectPreviewService { }: { width: number; height: number; - }): OffscreenCanvas | HTMLCanvasElement | null { + }): OffscreenCanvas | null { const isImageReady = this.previewImageElement?.complete && (this.previewImageElement.naturalWidth ?? 0) > 0; @@ -106,15 +106,8 @@ class EffectPreviewService { return null; } - const canvas = createOffscreenCanvas({ width, height }); - const ctx = canvas.getContext("2d") as - | CanvasRenderingContext2D - | OffscreenCanvasRenderingContext2D - | null; - if (!ctx) { - throw new Error("failed to get 2d context for test source"); - } - ctx.drawImage(this.previewImageElement, 0, 0, width, height); + const { canvas, context } = createCanvasSurface({ width, height }); + context.drawImage(this.previewImageElement, 0, 0, width, height); return canvas; } @@ -124,7 +117,7 @@ class EffectPreviewService { }: { width: number; height: number; - }): CanvasImageSource | null { + }): OffscreenCanvas | null { if ( !this.testSourceCanvas || this.testSourceCanvas.width !== width || @@ -141,17 +134,17 @@ class EffectPreviewService { height, passes, }: { - source: CanvasImageSource; + source: OffscreenCanvas; width: number; height: number; passes: ReturnType; - }): OffscreenCanvas | HTMLCanvasElement { + }): OffscreenCanvas { return gpuRenderer.applyEffect({ source, width, height, passes, - }) as OffscreenCanvas | HTMLCanvasElement; + }); } } diff --git a/apps/web/src/services/renderer/gpu-renderer.ts b/apps/web/src/services/renderer/gpu-renderer.ts index c61b6b98..c1be3f1a 100644 --- a/apps/web/src/services/renderer/gpu-renderer.ts +++ b/apps/web/src/services/renderer/gpu-renderer.ts @@ -34,23 +34,17 @@ export const gpuRenderer = { height, passes, }: { - source: CanvasImageSource; + source: OffscreenCanvas; width: number; height: number; passes: EffectPass[]; - }): CanvasImageSource { + }): OffscreenCanvas { if (passes.length === 0 || !gpuAvailable) { return source; } - const sourceCanvas = ensureOffscreenCanvas({ - source, - width, - height, - label: "effect source", - }); return applyEffectPasses({ - source: sourceCanvas, + source, width, height, passes: serializeEffectPasses(passes), @@ -63,23 +57,17 @@ export const gpuRenderer = { height, feather, }: { - maskCanvas: CanvasImageSource; + maskCanvas: OffscreenCanvas; width: number; height: number; feather: number; - }): CanvasImageSource { + }): OffscreenCanvas { if (!gpuAvailable) { return maskCanvas; } - const sourceCanvas = ensureOffscreenCanvas({ - source: maskCanvas, - width, - height, - label: "mask source", - }); return applyMaskFeatherWasm({ - mask: sourceCanvas, + mask: maskCanvas, width, height, feather, @@ -87,35 +75,6 @@ export const gpuRenderer = { }, }; -function ensureOffscreenCanvas({ - source, - width, - height, - label, -}: { - source: CanvasImageSource; - width: number; - height: number; - label: string; -}): OffscreenCanvas { - if (source instanceof OffscreenCanvas) { - return source; - } - - if (typeof OffscreenCanvas === "undefined") { - throw new Error(`OffscreenCanvas is required for the GPU ${label}`); - } - - const canvas = new OffscreenCanvas(width, height); - const context = canvas.getContext("2d"); - if (!context) { - throw new Error(`Failed to get 2d context for the GPU ${label}`); - } - context.clearRect(0, 0, width, height); - context.drawImage(source, 0, 0, width, height); - return canvas; -} - function serializeEffectPasses(passes: EffectPass[]) { return passes.map((pass) => ({ shader: pass.shader, diff --git a/apps/web/src/services/renderer/mask-feather.ts b/apps/web/src/services/renderer/mask-feather.ts index 4ea7eb2b..dde4decc 100644 --- a/apps/web/src/services/renderer/mask-feather.ts +++ b/apps/web/src/services/renderer/mask-feather.ts @@ -6,15 +6,15 @@ export function applyMaskFeather({ height, feather, }: { - maskCanvas: CanvasImageSource; + maskCanvas: OffscreenCanvas; width: number; height: number; feather: number; -}): OffscreenCanvas | HTMLCanvasElement { +}): OffscreenCanvas { return gpuRenderer.applyMaskFeather({ maskCanvas, width, height, feather, - }) as OffscreenCanvas | HTMLCanvasElement; + }); } diff --git a/apps/web/src/services/renderer/nodes/graphic-node.ts b/apps/web/src/services/renderer/nodes/graphic-node.ts index 2d022510..798b56a0 100644 --- a/apps/web/src/services/renderer/nodes/graphic-node.ts +++ b/apps/web/src/services/renderer/nodes/graphic-node.ts @@ -1,4 +1,4 @@ -import { createOffscreenCanvas } from "../canvas-utils"; +import { createCanvasSurface } from "../canvas-utils"; import { DEFAULT_GRAPHIC_SOURCE_SIZE, getGraphicDefinition, @@ -25,7 +25,7 @@ export class GraphicNode extends VisualNode< ResolvedGraphicNodeState > { private cachedKey: string | null = null; - private cachedSource: OffscreenCanvas | HTMLCanvasElement | null = null; + private cachedSource: OffscreenCanvas | null = null; constructor(params: GraphicNodeParams) { super(params); @@ -36,7 +36,7 @@ export class GraphicNode extends VisualNode< resolvedParams, }: { resolvedParams: ParamValues; - }): OffscreenCanvas | HTMLCanvasElement | null { + }): OffscreenCanvas { const definition = getGraphicDefinition({ definitionId: this.params.definitionId, }); @@ -48,20 +48,13 @@ export class GraphicNode extends VisualNode< return this.cachedSource; } - const canvas = createOffscreenCanvas({ + const { canvas, context } = createCanvasSurface({ width: DEFAULT_GRAPHIC_SOURCE_SIZE, height: DEFAULT_GRAPHIC_SOURCE_SIZE, }); - const ctx = canvas.getContext("2d") as - | CanvasRenderingContext2D - | OffscreenCanvasRenderingContext2D - | null; - if (!ctx) { - return null; - } definition.render({ - ctx, + ctx: context, params: resolvedParams, width: DEFAULT_GRAPHIC_SOURCE_SIZE, height: DEFAULT_GRAPHIC_SOURCE_SIZE, diff --git a/apps/web/src/services/renderer/nodes/image-node.ts b/apps/web/src/services/renderer/nodes/image-node.ts index d8c56803..4f475a2d 100644 --- a/apps/web/src/services/renderer/nodes/image-node.ts +++ b/apps/web/src/services/renderer/nodes/image-node.ts @@ -17,10 +17,13 @@ export interface CachedImageSource { const imageSourceCache = new Map>(); -export function loadImageSource( - url: string, - maxSourceSize?: number, -): Promise { +export function loadImageSource({ + url, + maxSourceSize, +}: { + url: string; + maxSourceSize?: number; +}): Promise { const cacheKey = `${url}::${maxSourceSize ?? "full"}`; const cached = imageSourceCache.get(cacheKey); diff --git a/apps/web/src/services/renderer/resolve.ts b/apps/web/src/services/renderer/resolve.ts index 36a6ecb5..4637df8d 100644 --- a/apps/web/src/services/renderer/resolve.ts +++ b/apps/web/src/services/renderer/resolve.ts @@ -235,10 +235,10 @@ async function resolveImageNode({ node: ImageNode; context: ResolveContext; }): Promise { - const source = await loadImageSource( - node.params.url, - node.params.maxSourceSize, - ); + const source = await loadImageSource({ + url: node.params.url, + maxSourceSize: node.params.maxSourceSize, + }); const visualState = resolveVisualState({ params: node.params, context, @@ -434,7 +434,7 @@ async function resolveBackdropSource({ }; } - const source = await loadImageSource(node.params.url); + const source = await loadImageSource({ url: node.params.url }); return { source: source.source, width: source.width, -- cgit v1.3.1