refactor(image-pipeline): add backend router seam
This commit is contained in:
83
lib/image-pipeline/backend/backend-router.ts
Normal file
83
lib/image-pipeline/backend/backend-router.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { applyPipelineStep, applyPipelineSteps } from "@/lib/image-pipeline/render-core";
|
||||
|
||||
import {
|
||||
CPU_BACKEND_ID,
|
||||
type BackendHint,
|
||||
type BackendRouter,
|
||||
type FullBackendRequest,
|
||||
type ImagePipelineBackend,
|
||||
type PreviewBackendRequest,
|
||||
} from "@/lib/image-pipeline/backend/backend-types";
|
||||
|
||||
const cpuBackend: ImagePipelineBackend = {
|
||||
id: CPU_BACKEND_ID,
|
||||
runPreviewStep(request) {
|
||||
applyPipelineStep(
|
||||
request.pixels,
|
||||
request.step,
|
||||
request.width,
|
||||
request.height,
|
||||
request.executionOptions,
|
||||
);
|
||||
},
|
||||
runFullPipeline(request) {
|
||||
applyPipelineSteps(
|
||||
request.pixels,
|
||||
request.steps,
|
||||
request.width,
|
||||
request.height,
|
||||
request.executionOptions,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
function normalizeBackendHint(value: BackendHint): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalized = value.trim().toLowerCase();
|
||||
return normalized.length > 0 ? normalized : null;
|
||||
}
|
||||
|
||||
export function createBackendRouter(options?: {
|
||||
backends?: readonly ImagePipelineBackend[];
|
||||
defaultBackendId?: string;
|
||||
}): BackendRouter {
|
||||
const configuredBackends = options?.backends?.length ? [...options.backends] : [cpuBackend];
|
||||
const byId = new Map(configuredBackends.map((backend) => [backend.id.toLowerCase(), backend]));
|
||||
const defaultBackend =
|
||||
byId.get(options?.defaultBackendId?.toLowerCase() ?? "") ??
|
||||
byId.get(CPU_BACKEND_ID) ??
|
||||
configuredBackends[0] ??
|
||||
cpuBackend;
|
||||
|
||||
return {
|
||||
resolveBackend(backendHint) {
|
||||
const normalizedHint = normalizeBackendHint(backendHint);
|
||||
if (!normalizedHint) {
|
||||
return defaultBackend;
|
||||
}
|
||||
|
||||
return byId.get(normalizedHint) ?? defaultBackend;
|
||||
},
|
||||
runPreviewStep(request) {
|
||||
const backend = this.resolveBackend(request.backendHint);
|
||||
backend.runPreviewStep(request);
|
||||
},
|
||||
runFullPipeline(request) {
|
||||
const backend = this.resolveBackend(request.backendHint);
|
||||
backend.runFullPipeline(request);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const defaultRouter = createBackendRouter();
|
||||
|
||||
export function runPreviewStepWithBackendRouter(request: PreviewBackendRequest): void {
|
||||
defaultRouter.runPreviewStep(request);
|
||||
}
|
||||
|
||||
export function runFullPipelineWithBackendRouter(request: FullBackendRequest): void {
|
||||
defaultRouter.runFullPipeline(request);
|
||||
}
|
||||
42
lib/image-pipeline/backend/backend-types.ts
Normal file
42
lib/image-pipeline/backend/backend-types.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { PipelineStep } from "@/lib/image-pipeline/contracts";
|
||||
|
||||
export const CPU_BACKEND_ID = "cpu" as const;
|
||||
|
||||
export type BackendHint = string | undefined;
|
||||
|
||||
export type BackendExecutionOptions = {
|
||||
shouldAbort?: () => boolean;
|
||||
};
|
||||
|
||||
export type PreviewBackendRequest = {
|
||||
pixels: Uint8ClampedArray;
|
||||
step: PipelineStep<string, unknown>;
|
||||
width: number;
|
||||
height: number;
|
||||
backendHint?: BackendHint;
|
||||
executionOptions?: BackendExecutionOptions;
|
||||
};
|
||||
|
||||
export type FullBackendRequest = {
|
||||
pixels: Uint8ClampedArray;
|
||||
steps: readonly PipelineStep[];
|
||||
width: number;
|
||||
height: number;
|
||||
backendHint?: BackendHint;
|
||||
executionOptions?: BackendExecutionOptions;
|
||||
};
|
||||
|
||||
export type BackendStepRequest = Omit<PreviewBackendRequest, "backendHint">;
|
||||
export type BackendPipelineRequest = Omit<FullBackendRequest, "backendHint">;
|
||||
|
||||
export type ImagePipelineBackend = {
|
||||
id: string;
|
||||
runPreviewStep: (request: BackendStepRequest) => void;
|
||||
runFullPipeline: (request: BackendPipelineRequest) => void;
|
||||
};
|
||||
|
||||
export type BackendRouter = {
|
||||
resolveBackend: (backendHint?: BackendHint) => ImagePipelineBackend;
|
||||
runPreviewStep: (request: PreviewBackendRequest) => void;
|
||||
runFullPipeline: (request: FullBackendRequest) => void;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { applyPipelineSteps } from "@/lib/image-pipeline/render-core";
|
||||
import { runFullPipelineWithBackendRouter } from "@/lib/image-pipeline/backend/backend-router";
|
||||
import { resolveRenderSize } from "@/lib/image-pipeline/render-size";
|
||||
import {
|
||||
RENDER_FORMAT_TO_MIME,
|
||||
@@ -108,15 +108,15 @@ export async function renderFull(options: RenderFullOptions): Promise<RenderFull
|
||||
context.drawImage(bitmap, 0, 0, resolvedSize.width, resolvedSize.height);
|
||||
|
||||
const imageData = context.getImageData(0, 0, resolvedSize.width, resolvedSize.height);
|
||||
applyPipelineSteps(
|
||||
imageData.data,
|
||||
options.steps,
|
||||
resolvedSize.width,
|
||||
resolvedSize.height,
|
||||
{
|
||||
runFullPipelineWithBackendRouter({
|
||||
pixels: imageData.data,
|
||||
steps: options.steps,
|
||||
width: resolvedSize.width,
|
||||
height: resolvedSize.height,
|
||||
executionOptions: {
|
||||
shouldAbort: () => Boolean(signal?.aborted),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (signal?.aborted) {
|
||||
throw new DOMException("The operation was aborted.", "AbortError");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PipelineStep } from "@/lib/image-pipeline/contracts";
|
||||
import { runPreviewStepWithBackendRouter } from "@/lib/image-pipeline/backend/backend-router";
|
||||
import { computeHistogram, emptyHistogram, type HistogramData } from "@/lib/image-pipeline/histogram";
|
||||
import { applyPipelineStep } from "@/lib/image-pipeline/render-core";
|
||||
import { loadSourceBitmap } from "@/lib/image-pipeline/source-loader";
|
||||
|
||||
export type PreviewRenderResult = {
|
||||
@@ -77,8 +77,14 @@ export async function renderPreview(options: {
|
||||
const imageData = context.getImageData(0, 0, width, height);
|
||||
|
||||
for (let index = 0; index < options.steps.length; index += 1) {
|
||||
applyPipelineStep(imageData.data, options.steps[index]!, width, height, {
|
||||
shouldAbort: () => Boolean(options.signal?.aborted),
|
||||
runPreviewStepWithBackendRouter({
|
||||
pixels: imageData.data,
|
||||
step: options.steps[index]!,
|
||||
width,
|
||||
height,
|
||||
executionOptions: {
|
||||
shouldAbort: () => Boolean(options.signal?.aborted),
|
||||
},
|
||||
});
|
||||
await yieldToMainOrWorkerLoop();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user