feat(web): wasm justified layout (#19150)

* wasm justified layout

* fix tests

* redundant layout generation

* raw position
This commit is contained in:
Mert 2025-06-17 10:20:14 -04:00 committed by GitHub
parent 8038ae1e7a
commit bc062da11b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 106 additions and 145 deletions

View file

@ -1,16 +1,13 @@
// import { TUNABLES } from '$lib/utils/tunables';
// note: it's important that this is not imported in more than one file due to https://github.com/sveltejs/kit/issues/7805
// import { JustifiedLayout, type LayoutOptions } from '@immich/justified-layout-wasm';
import { TUNABLES } from '$lib/utils/tunables';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import { getAssetRatio } from '$lib/utils/asset-utils';
import { isTimelineAsset } from '$lib/utils/timeline-util';
import { isTimelineAsset, isTimelineAssets } from '$lib/utils/timeline-util';
import { JustifiedLayout, type LayoutOptions } from '@immich/justified-layout-wasm';
import type { AssetResponseDto } from '@immich/sdk';
import createJustifiedLayout from 'justified-layout';
export type getJustifiedLayoutFromAssetsFunction = typeof getJustifiedLayoutFromAssets;
// let useWasm = TUNABLES.LAYOUT.WASM;
const useWasm = TUNABLES.LAYOUT.WASM;
export type CommonJustifiedLayout = {
containerWidth: number;
@ -19,6 +16,12 @@ export type CommonJustifiedLayout = {
getLeft(boxIdx: number): number;
getWidth(boxIdx: number): number;
getHeight(boxIdx: number): number;
getPosition(boxIdx: number): {
top: number;
left: number;
width: number;
height: number;
};
};
export type CommonLayoutOptions = {
@ -29,25 +32,32 @@ export type CommonLayoutOptions = {
};
export function getJustifiedLayoutFromAssets(
assets: (TimelineAsset | AssetResponseDto)[],
assets: AssetResponseDto[] | TimelineAsset[],
options: CommonLayoutOptions,
): CommonJustifiedLayout {
// if (useWasm) {
// return wasmJustifiedLayout(assets, options);
// }
) {
if (useWasm) {
return isTimelineAssets(assets) ? wasmLayoutFromTimeline(assets, options) : wasmLayoutFromDto(assets, options);
}
return justifiedLayout(assets, options);
}
// commented out until a solution for top level awaits on safari is fixed
// function wasmJustifiedLayout(assets: AssetResponseDto[], options: LayoutOptions) {
// const aspectRatios = new Float32Array(assets.length);
// // eslint-disable-next-line unicorn/no-for-loop
// for (let i = 0; i < assets.length; i++) {
// const { width, height } = getAssetRatio(assets[i]);
// aspectRatios[i] = width / height;
// }
// return new JustifiedLayout(aspectRatios, options);
// }
function wasmLayoutFromTimeline(assets: TimelineAsset[], options: LayoutOptions) {
const aspectRatios = new Float32Array(assets.length);
for (let i = 0; i < assets.length; i++) {
aspectRatios[i] = assets[i].ratio;
}
return new JustifiedLayout(aspectRatios, options);
}
function wasmLayoutFromDto(assets: AssetResponseDto[], options: LayoutOptions) {
const aspectRatios = new Float32Array(assets.length);
for (let i = 0; i < assets.length; i++) {
const { width, height } = getAssetRatio(assets[i]);
aspectRatios[i] = width / height;
}
return new JustifiedLayout(aspectRatios, options);
}
type Geometry = ReturnType<typeof createJustifiedLayout>;
class Adapter {
@ -88,9 +98,13 @@ class Adapter {
getHeight(boxIdx: number) {
return this.result.boxes[boxIdx]?.height;
}
getPosition(boxIdx: number): CommonPosition {
return this.result.boxes[boxIdx];
}
}
export function justifiedLayout(assets: (TimelineAsset | AssetResponseDto)[], options: CommonLayoutOptions) {
export function justifiedLayout(assets: TimelineAsset[] | AssetResponseDto[], options: CommonLayoutOptions) {
const adapter = {
targetRowHeight: options.rowHeight,
containerWidth: options.rowWidth,
@ -105,25 +119,9 @@ export function justifiedLayout(assets: (TimelineAsset | AssetResponseDto)[], op
return new Adapter(result);
}
export const emptyGeometry = () =>
new Adapter({
containerHeight: 0,
widowCount: 0,
boxes: [],
});
export type CommonPosition = {
top: number;
left: number;
width: number;
height: number;
};
export function getPosition(geometry: CommonJustifiedLayout, boxIdx: number): CommonPosition {
const top = geometry.getTop(boxIdx);
const left = geometry.getLeft(boxIdx);
const width = geometry.getWidth(boxIdx);
const height = geometry.getHeight(boxIdx);
return { top, left, width, height };
}