fix: align background blur with effect blur intensity scale
This commit is contained in:
parent
b048b739bd
commit
301e89df89
|
|
@ -16,12 +16,12 @@ export const FPS_PRESETS = [
|
|||
] as const;
|
||||
|
||||
export const BLUR_INTENSITY_PRESETS: { label: string; value: number }[] = [
|
||||
{ label: "Light", value: 10 },
|
||||
{ label: "Medium", value: 50 },
|
||||
{ label: "Heavy", value: 100 },
|
||||
{ label: "Light", value: 50 },
|
||||
{ label: "Medium", value: 250 },
|
||||
{ label: "Heavy", value: 500 },
|
||||
] as const;
|
||||
|
||||
export const DEFAULT_CANVAS_SIZE: TCanvasSize = { width: 1920, height: 1080 };
|
||||
export const DEFAULT_FPS = 30;
|
||||
export const DEFAULT_BLUR_INTENSITY = 10;
|
||||
export const DEFAULT_BLUR_INTENSITY = 50;
|
||||
export const DEFAULT_COLOR = "#000000";
|
||||
|
|
|
|||
|
|
@ -6,11 +6,6 @@ const MAX_STEP = 4;
|
|||
const MAX_EFFECTIVE_SIGMA = MAX_SINGLE_PASS_SIGMA * MAX_STEP;
|
||||
const MAX_ITERATIONS = 8;
|
||||
|
||||
/**
|
||||
* Builds multi-pass gaussian blur passes for a given sigma.
|
||||
* Shared by the blur effect and background blur — they each
|
||||
* compute their own sigma from their own intensity scale.
|
||||
*/
|
||||
export function buildGaussianBlurPasses({
|
||||
sigmaX,
|
||||
sigmaY,
|
||||
|
|
@ -58,8 +53,10 @@ export function buildGaussianBlurPasses({
|
|||
return passes;
|
||||
}
|
||||
|
||||
function intensityToSigma(intensity: number, resolution: number, reference: number): number {
|
||||
return (intensity / 5) * (resolution / reference);
|
||||
export const INTENSITY_TO_SIGMA_DIVISOR = 5;
|
||||
|
||||
export function intensityToSigma({ intensity, resolution, reference }: { intensity: number; resolution: number; reference: number }): number {
|
||||
return (intensity / INTENSITY_TO_SIGMA_DIVISOR) * (resolution / reference);
|
||||
}
|
||||
|
||||
function parseIntensity(effectParams: Record<string, unknown>): number {
|
||||
|
|
@ -88,7 +85,7 @@ export const blurEffectDefinition: EffectDefinition = {
|
|||
{
|
||||
fragmentShader: blurFragmentShader,
|
||||
uniforms: ({ effectParams, width }) => ({
|
||||
u_sigma: Math.max(intensityToSigma(parseIntensity(effectParams), width, 1920), 0.001),
|
||||
u_sigma: Math.max(intensityToSigma({ intensity: parseIntensity(effectParams), resolution: width, reference: 1920 }), 0.001),
|
||||
u_step: 1,
|
||||
u_direction: [1, 0],
|
||||
}),
|
||||
|
|
@ -96,7 +93,7 @@ export const blurEffectDefinition: EffectDefinition = {
|
|||
{
|
||||
fragmentShader: blurFragmentShader,
|
||||
uniforms: ({ effectParams, height }) => ({
|
||||
u_sigma: Math.max(intensityToSigma(parseIntensity(effectParams), height, 1080), 0.001),
|
||||
u_sigma: Math.max(intensityToSigma({ intensity: parseIntensity(effectParams), resolution: height, reference: 1080 }), 0.001),
|
||||
u_step: 1,
|
||||
u_direction: [0, 1],
|
||||
}),
|
||||
|
|
@ -104,9 +101,9 @@ export const blurEffectDefinition: EffectDefinition = {
|
|||
],
|
||||
buildPasses: ({ effectParams, width, height }) => {
|
||||
const intensity = parseIntensity(effectParams);
|
||||
return buildGaussianBlurPasses({
|
||||
sigmaX: intensityToSigma(intensity, width, 1920),
|
||||
sigmaY: intensityToSigma(intensity, height, 1080),
|
||||
return buildGaussianBlurPasses({
|
||||
sigmaX: intensityToSigma({ intensity, resolution: width, reference: 1920 }),
|
||||
sigmaY: intensityToSigma({ intensity, resolution: height, reference: 1080 }),
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
|
||||
import { buildGaussianBlurPasses } from "@/lib/effects/definitions/blur";
|
||||
import { buildGaussianBlurPasses, intensityToSigma } from "@/lib/effects/definitions/blur";
|
||||
import { getSourceTimeAtClipTime } from "@/lib/retime";
|
||||
import { videoCache } from "@/services/video-cache/service";
|
||||
import type { RetimeConfig } from "@/lib/timeline";
|
||||
|
|
@ -128,8 +128,8 @@ export class BlurBackgroundNode extends BaseNode<BlurBackgroundNodeParams> {
|
|||
);
|
||||
|
||||
const passes = buildGaussianBlurPasses({
|
||||
sigmaX: this.params.blurIntensity * (renderer.width / 1920),
|
||||
sigmaY: this.params.blurIntensity * (renderer.height / 1080),
|
||||
sigmaX: intensityToSigma({ intensity: this.params.blurIntensity, resolution: renderer.width, reference: 1920 }),
|
||||
sigmaY: intensityToSigma({ intensity: this.params.blurIntensity, resolution: renderer.height, reference: 1080 }),
|
||||
});
|
||||
const effectResult = webglEffectRenderer.applyEffect({
|
||||
source: offscreen as CanvasImageSource,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import { DEFAULT_BLUR_INTENSITY } from "@/constants/project-constants";
|
||||
import { transformProjectV20ToV21 } from "../transformers/v20-to-v21";
|
||||
|
||||
describe("V20 to V21 Migration", () => {
|
||||
test("multiplies blur background intensity by 5", () => {
|
||||
const result = transformProjectV20ToV21({
|
||||
project: {
|
||||
id: "project-v20-blur",
|
||||
version: 20,
|
||||
metadata: {
|
||||
id: "project-v20-blur",
|
||||
name: "Project",
|
||||
createdAt: "2024-01-01T00:00:00.000Z",
|
||||
updatedAt: "2024-01-01T00:00:00.000Z",
|
||||
},
|
||||
settings: {
|
||||
fps: 30,
|
||||
canvasSize: { width: 1920, height: 1080 },
|
||||
background: { type: "blur", blurIntensity: 100 },
|
||||
},
|
||||
currentSceneId: "scene-main",
|
||||
scenes: [],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.skipped).toBe(false);
|
||||
expect(result.project.version).toBe(21);
|
||||
const settings = result.project.settings as Record<string, unknown>;
|
||||
const background = settings.background as Record<string, unknown>;
|
||||
expect(background.blurIntensity).toBe(500);
|
||||
});
|
||||
|
||||
test("uses default blur intensity when blur type but intensity missing", () => {
|
||||
const result = transformProjectV20ToV21({
|
||||
project: {
|
||||
id: "project-v20-blur-no-intensity",
|
||||
version: 20,
|
||||
settings: {
|
||||
background: { type: "blur" },
|
||||
},
|
||||
scenes: [],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.skipped).toBe(false);
|
||||
const settings = result.project.settings as Record<string, unknown>;
|
||||
const background = settings.background as Record<string, unknown>;
|
||||
expect(background.blurIntensity).toBe(DEFAULT_BLUR_INTENSITY);
|
||||
});
|
||||
|
||||
test("leaves color background unchanged", () => {
|
||||
const result = transformProjectV20ToV21({
|
||||
project: {
|
||||
id: "project-v20-color",
|
||||
version: 20,
|
||||
settings: {
|
||||
background: { type: "color", color: "#000000" },
|
||||
},
|
||||
scenes: [],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.skipped).toBe(false);
|
||||
const settings = result.project.settings as Record<string, unknown>;
|
||||
expect(settings.background).toEqual({ type: "color", color: "#000000" });
|
||||
});
|
||||
|
||||
test("skips projects already on v21", () => {
|
||||
const result = transformProjectV20ToV21({
|
||||
project: {
|
||||
id: "project-v21",
|
||||
version: 21,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.skipped).toBe(true);
|
||||
expect(result.reason).toBe("already v21");
|
||||
});
|
||||
|
||||
test("skips projects not on v20", () => {
|
||||
const result = transformProjectV20ToV21({
|
||||
project: {
|
||||
id: "project-v19",
|
||||
version: 19,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.skipped).toBe(true);
|
||||
expect(result.reason).toBe("not v20");
|
||||
});
|
||||
});
|
||||
|
|
@ -19,10 +19,11 @@ import { V16toV17Migration } from "./v16-to-v17";
|
|||
import { V17toV18Migration } from "./v17-to-v18";
|
||||
import { V18toV19Migration } from "./v18-to-v19";
|
||||
import { V19toV20Migration } from "./v19-to-v20";
|
||||
import { V20toV21Migration } from "./v20-to-v21";
|
||||
export { runStorageMigrations } from "./runner";
|
||||
export type { MigrationProgress } from "./runner";
|
||||
|
||||
export const CURRENT_PROJECT_VERSION = 20;
|
||||
export const CURRENT_PROJECT_VERSION = 21;
|
||||
|
||||
export const migrations = [
|
||||
new V0toV1Migration(),
|
||||
|
|
@ -45,4 +46,5 @@ export const migrations = [
|
|||
new V17toV18Migration(),
|
||||
new V18toV19Migration(),
|
||||
new V19toV20Migration(),
|
||||
new V20toV21Migration(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
import { DEFAULT_BLUR_INTENSITY } from "@/constants/project-constants";
|
||||
import { INTENSITY_TO_SIGMA_DIVISOR } from "@/lib/effects/definitions/blur";
|
||||
import type { MigrationResult, ProjectRecord } from "./types";
|
||||
import { getProjectId, isRecord } from "./utils";
|
||||
|
||||
export function transformProjectV20ToV21({
|
||||
project,
|
||||
}: {
|
||||
project: ProjectRecord;
|
||||
}): MigrationResult<ProjectRecord> {
|
||||
if (!getProjectId({ project })) {
|
||||
return { project, skipped: true, reason: "no project id" };
|
||||
}
|
||||
|
||||
const version = project.version;
|
||||
if (typeof version !== "number") {
|
||||
return { project, skipped: true, reason: "invalid version" };
|
||||
}
|
||||
if (version >= 21) {
|
||||
return { project, skipped: true, reason: "already v21" };
|
||||
}
|
||||
if (version !== 20) {
|
||||
return { project, skipped: true, reason: "not v20" };
|
||||
}
|
||||
|
||||
return {
|
||||
project: {
|
||||
...migrateBackgroundBlurScale({ project }),
|
||||
version: 21,
|
||||
},
|
||||
skipped: false,
|
||||
};
|
||||
}
|
||||
|
||||
function migrateBackgroundBlurScale({
|
||||
project,
|
||||
}: {
|
||||
project: ProjectRecord;
|
||||
}): ProjectRecord {
|
||||
if (!isRecord(project.settings)) {
|
||||
return project;
|
||||
}
|
||||
|
||||
const settings = { ...project.settings };
|
||||
const background = settings.background;
|
||||
if (!isRecord(background) || background.type !== "blur") {
|
||||
return { ...project, settings };
|
||||
}
|
||||
|
||||
const raw = background.blurIntensity;
|
||||
const blurIntensity =
|
||||
typeof raw === "number" && Number.isFinite(raw)
|
||||
? raw * INTENSITY_TO_SIGMA_DIVISOR
|
||||
: DEFAULT_BLUR_INTENSITY;
|
||||
|
||||
return {
|
||||
...project,
|
||||
settings: {
|
||||
...settings,
|
||||
background: {
|
||||
...background,
|
||||
blurIntensity,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { StorageMigration } from "./base";
|
||||
import type { ProjectRecord } from "./transformers/types";
|
||||
import { transformProjectV20ToV21 } from "./transformers/v20-to-v21";
|
||||
|
||||
export class V20toV21Migration extends StorageMigration {
|
||||
from = 20;
|
||||
to = 21;
|
||||
|
||||
async transform(project: ProjectRecord): Promise<{
|
||||
project: ProjectRecord;
|
||||
skipped: boolean;
|
||||
reason?: string;
|
||||
}> {
|
||||
return transformProjectV20ToV21({ project });
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue