Skip to content

Commit

Permalink
feat: 加载的图片支持使用 ImageBitmap 格式
Browse files Browse the repository at this point in the history
  • Loading branch information
yiiqii committed Dec 27, 2024
1 parent 98a2c9c commit a9c1319
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 14 deletions.
32 changes: 28 additions & 4 deletions packages/effects-core/src/asset-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { passRenderLevel } from './pass-render-level';
import type { PrecompileOptions } from './plugin-system';
import { PluginSystem } from './plugin-system';
import type { JSONValue } from './downloader';
import { Downloader, loadWebPOptional, loadImage, loadVideo, loadMedia, loadAVIFOptional } from './downloader';
import { Downloader, loadWebPOptional, loadImage, loadVideo, loadMedia, loadAVIFOptional, closeImageBitMap } from './downloader';
import type { ImageLike, SceneLoadOptions } from './scene';
import { Scene } from './scene';
import type { Disposable } from './utils';
Expand Down Expand Up @@ -37,6 +37,10 @@ export class AssetManager implements Disposable {
* TextureSource 来源
*/
private sourceFrom: Record<string, { url: string, type: TextureSourceType }> = {};
/**
* Texture 选项,用于创建 ImageBitmap
*/
private imageBitmapOptions: Record<string, ImageBitmapOptions> = {};
/**
* 自定义文本缓存,随页面销毁而销毁
*/
Expand Down Expand Up @@ -217,11 +221,13 @@ export class AssetManager implements Disposable {

private async processJSON (json: JSONValue) {
const jsonScene = getStandardJSON(json);
const { plugins = [] } = jsonScene;
const { plugins = [], textures } = jsonScene;
const pluginSystem = new PluginSystem(plugins);

await pluginSystem.processRawJSON(jsonScene, this.options);

this.assignImageBitmapOptions(textures);

return {
jsonScene,
pluginSystem,
Expand Down Expand Up @@ -356,8 +362,8 @@ export class AssetManager implements Disposable {
}

const { url, image } = avifURL
? await loadAVIFOptional(imageURL, avifURL)
: await loadWebPOptional(imageURL, webpURL);
? await loadAVIFOptional(imageURL, avifURL, this.imageBitmapOptions[id])
: await loadWebPOptional(imageURL, webpURL, this.imageBitmapOptions[id]);

this.sourceFrom[id] = { url, type: TextureSourceType.image };

Expand Down Expand Up @@ -471,6 +477,21 @@ export class AssetManager implements Disposable {
}
}

private assignImageBitmapOptions (textures: spec.SerializedTextureSource[] = []) {
textures.forEach(texture => {
if (!(texture instanceof Texture || 'mipmaps' in texture)) {
const { source } = texture;

if (isObject(source)) {
this.imageBitmapOptions[source.id as string] = {
imageOrientation: texture.flipY ? 'flipY' : 'none',
premultiplyAlpha: texture.premultiplyAlpha ? 'premultiply' : 'none',
};
}
}
});
}

private removeTimer (id: number) {
const index = this.timers.indexOf(id);

Expand All @@ -485,6 +506,8 @@ export class AssetManager implements Disposable {
if (this.timers.length) {
this.timers.map(id => window.clearTimeout(id));
}

closeImageBitMap(Object.keys(this.assets).map(key => this.assets[key]));
this.assets = {};
this.sourceFrom = {};
this.timers = [];
Expand All @@ -508,6 +531,7 @@ function createTextureOptionsBySource (
};
} else if (
image instanceof HTMLImageElement ||
image instanceof ImageBitmap ||
isCanvas(image as HTMLCanvasElement)
) {
return {
Expand Down
2 changes: 2 additions & 0 deletions packages/effects-core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export const RENDER_PREFER_LOOKUP_TEXTURE = 'lookup_texture';
export const TEMPLATE_USE_OFFSCREEN_CANVAS = 'offscreen_canvas';
// 后处理配置相关
export const POST_PROCESS_SETTINGS = 'post_process_settings';
// 加载图片时是否使用 ImageBitmap
export const LOAD_PREFER_IMAGE_BITMAP = 'load_image_bitmap';

const config: Record<string, number | boolean | string | Record<string, any>> = {};

Expand Down
60 changes: 52 additions & 8 deletions packages/effects-core/src/downloader.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LOAD_PREFER_IMAGE_BITMAP, getConfig } from './config';
import { isAndroid } from './utils';

type SuccessHandler<T> = (data: T) => void;
Expand Down Expand Up @@ -109,20 +110,20 @@ let avifFailed = false;
* @param png - PNG 图片文件的 URL
* @param webp - WebP 图片文件的 URL
*/
export async function loadWebPOptional (png: string, webp?: string) {
export async function loadWebPOptional (png: string, webp?: string, options?: ImageBitmapOptions) {
if (webPFailed || !webp) {
const image = await loadImage(png);
const image = await loadImageBitmap(png, options);

return { image, url: png };
}

try {
const image = await loadImage(webp);
const image = await loadImageBitmap(webp, options);

return { image, url: webp };
} catch (_) {
webPFailed = true;
const image = await loadImage(png);
const image = await loadImageBitmap(png, options);

return { image, url: png };
}
Expand All @@ -133,20 +134,20 @@ export async function loadWebPOptional (png: string, webp?: string) {
* @param png - PNG 图片文件的 URL
* @param avif - AVIF 图片文件的 URL
*/
export async function loadAVIFOptional (png: string, avif?: string) {
export async function loadAVIFOptional (png: string, avif?: string, options?: ImageBitmapOptions) {
if (avifFailed || !avif) {
const image = await loadImage(png);
const image = await loadImageBitmap(png, options);

return { image, url: png };
}

try {
const image = await loadImage(avif);
const image = await loadImageBitmap(avif, options);

return { image, url: avif };
} catch (_) {
avifFailed = true;
const image = await loadImage(png);
const image = await loadImageBitmap(png, options);

return { image, url: png };
}
Expand Down Expand Up @@ -284,3 +285,46 @@ export async function loadMedia (url: string | string[], loadFn: (url: string) =

return loadFn(url);
}

const imageBitMapAvailable = typeof createImageBitmap === 'function';

/**
* 异步加载一个图片文件,如果支持 ImageBitmap 则返回 ImageBitmap 对象
* @param source
* @param options
* @returns
*/
export async function loadImageBitmap (
source: string | Blob | HTMLImageElement,
options?: ImageBitmapOptions,
): Promise<ImageBitmap | HTMLImageElement> {
if (imageBitMapAvailable && getConfig(LOAD_PREFER_IMAGE_BITMAP)) {
let blob: Blob | HTMLImageElement;

if (typeof source === 'string') {
blob = await loadBlob(source);
} else if (source instanceof Blob) {
blob = source;
} else {
return loadImage(source);
}

return createImageBitmap(blob, options);
} else {
return loadImage(source);
}
}

/**
* 关闭 ImageBitMap,释放内存
* @param imgs
*/
export function closeImageBitMap (imgs: any) {
if (imageBitMapAvailable) {
if (imgs instanceof ImageBitmap) {
imgs.close();
} else if (imgs instanceof Array) {
imgs.forEach(closeImageBitMap);
}
}
}
2 changes: 1 addition & 1 deletion packages/effects-core/src/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { PluginSystem } from './plugin-system';
import type { PickEnum } from './utils';
import { isObject } from './utils';

export type ImageLike = spec.HTMLImageLike | ArrayBuffer | Texture;
export type ImageLike = spec.HTMLImageLike | ArrayBuffer | Texture | ImageBitmap;
export type SceneRenderLevel = PickEnum<spec.RenderLevel, spec.RenderLevel.A | spec.RenderLevel.B | spec.RenderLevel.S>;

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/effects-core/src/template-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function combineImageTemplate (
template?: spec.TemplateContent,
variables?: spec.TemplateVariables,
) {
let image;
let image: HTMLImageElement;

if (typeof url === 'string') {
image = await loadImage(url);
Expand Down

0 comments on commit a9c1319

Please sign in to comment.