mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(core): graded media honors object-position vertically instead of mirroring it (#6691)
* fix(core): graded media honors object-position vertically instead of mirroring it * fix(core): name the bottom-up position instead of commenting it
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
} from "../colorGrading";
|
||||
import { HF_COLOR_GROUP_ATTR, HF_COLOR_GROUP_TAG } from "../colorGroups";
|
||||
import {
|
||||
calculateObjectFitUv,
|
||||
createColorGradingRuntime,
|
||||
installAuthoredOpacityCapture,
|
||||
type RuntimeColorGradingApi,
|
||||
@@ -2619,3 +2620,49 @@ describe("installAuthoredOpacityCapture", () => {
|
||||
el.remove();
|
||||
});
|
||||
});
|
||||
|
||||
describe("calculateObjectFitUv", () => {
|
||||
// The shader reads uv = (v_uv - offset) / scale; v_uv.y and the flipped source
|
||||
// texture both run bottom-up, so this reports the CSS-facing fraction from the top.
|
||||
function sampledFromSourceTop(
|
||||
uv: ReturnType<typeof calculateObjectFitUv>,
|
||||
boxEdgeY: 0 | 1,
|
||||
): number {
|
||||
return 1 - (boxEdgeY - uv.offsetY) / uv.scaleY;
|
||||
}
|
||||
|
||||
function sampledFromSourceLeft(
|
||||
uv: ReturnType<typeof calculateObjectFitUv>,
|
||||
boxEdgeX: 0 | 1,
|
||||
): number {
|
||||
return (boxEdgeX - uv.offsetX) / uv.scaleX;
|
||||
}
|
||||
|
||||
it("shows the top of a covered portrait source at object-position 50% 6%", () => {
|
||||
const uv = calculateObjectFitUv(1920, 1080, 1080, 1920, "cover", "50% 6%");
|
||||
|
||||
expect(sampledFromSourceTop(uv, 1)).toBeCloseTo(0.041, 3);
|
||||
expect(sampledFromSourceTop(uv, 0)).toBeCloseTo(0.3574, 3);
|
||||
});
|
||||
|
||||
it("shows the bottom of a covered portrait source at object-position 50% 94%", () => {
|
||||
const uv = calculateObjectFitUv(1920, 1080, 1080, 1920, "cover", "50% 94%");
|
||||
|
||||
expect(sampledFromSourceTop(uv, 1)).toBeCloseTo(0.6426, 3);
|
||||
expect(sampledFromSourceTop(uv, 0)).toBeCloseTo(0.959, 3);
|
||||
});
|
||||
|
||||
it("shows the left of a covered landscape source at object-position 6% 50%", () => {
|
||||
const uv = calculateObjectFitUv(1080, 1920, 1920, 1080, "cover", "6% 50%");
|
||||
|
||||
expect(sampledFromSourceLeft(uv, 0)).toBeCloseTo(0.041, 3);
|
||||
expect(sampledFromSourceLeft(uv, 1)).toBeCloseTo(0.3574, 3);
|
||||
});
|
||||
|
||||
it("centers a covered source when no object-position is set", () => {
|
||||
const uv = calculateObjectFitUv(1920, 1080, 1080, 1920, "cover", "");
|
||||
|
||||
expect(sampledFromSourceTop(uv, 1)).toBeCloseTo(0.3415, 3);
|
||||
expect(sampledFromSourceTop(uv, 0)).toBeCloseTo(0.6585, 3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2851,7 +2851,7 @@ function parseObjectPosition(value: string): { x: number; y: number } {
|
||||
}
|
||||
|
||||
// fallow-ignore-next-line complexity
|
||||
function calculateObjectFitUv(
|
||||
export function calculateObjectFitUv(
|
||||
boxWidth: number,
|
||||
boxHeight: number,
|
||||
sourceWidth: number,
|
||||
@@ -2882,7 +2882,8 @@ function calculateObjectFitUv(
|
||||
}
|
||||
const position = parseObjectPosition(objectPosition || "center");
|
||||
const offsetX = ((boxWidth - drawWidth) * position.x) / boxWidth;
|
||||
const offsetY = ((boxHeight - drawHeight) * position.y) / boxHeight;
|
||||
const positionFromBottom = 1 - position.y;
|
||||
const offsetY = ((boxHeight - drawHeight) * positionFromBottom) / boxHeight;
|
||||
return {
|
||||
scaleX: drawWidth / boxWidth,
|
||||
scaleY: drawHeight / boxHeight,
|
||||
|
||||
Reference in New Issue
Block a user