From 08d736ecdcd1c05e21fd37a879aa057f22122b88 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alexander.rose@weirdbyte.de> Date: Sat, 20 Aug 2022 11:54:51 -0700 Subject: [PATCH] image loading error handling and other tweaks --- src/extensions/backgrounds/typings.d.ts | 2 +- src/mol-canvas3d/passes/background.ts | 33 ++++++++++++++----------- src/mol-canvas3d/passes/image.ts | 2 +- src/mol-gl/webgl/texture.ts | 23 ++++++++++------- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/extensions/backgrounds/typings.d.ts b/src/extensions/backgrounds/typings.d.ts index 94ad23bc1..83e439357 100644 --- a/src/extensions/backgrounds/typings.d.ts +++ b/src/extensions/backgrounds/typings.d.ts @@ -5,6 +5,6 @@ */ declare module '*.jpg' { - const value: any; + const value: string; export = value; } diff --git a/src/mol-canvas3d/passes/background.ts b/src/mol-canvas3d/passes/background.ts index f1b323d71..d4bfb3e59 100644 --- a/src/mol-canvas3d/passes/background.ts +++ b/src/mol-canvas3d/passes/background.ts @@ -137,20 +137,20 @@ export class BackgroundPass { const f = props.faces.params; if (!f.nx || !f.ny || !f.nz || !f.px || !f.py || !f.pz) { this.clearSkybox(); - if (onload) onload(false); + onload?.(false); return; } if (!this.skybox || !tf || !areSkyboxTexturePropsEqual(props.faces, this.skybox.props.faces)) { this.clearSkybox(); - const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, () => { - if (this.skybox) this.skybox.loaded = true; - if (onload) onload(true); + const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, errored => { + if (this.skybox) this.skybox.loaded = !errored; + onload?.(true); }); this.skybox = { texture, props: { ...props }, assets, loaded: false }; ValueCell.update(this.renderable.values.tSkybox, texture); this.renderable.update(); } else { - if (onload) onload(false); + onload?.(false); } if (!this.skybox) return; @@ -188,20 +188,20 @@ export class BackgroundPass { private updateImage(props: ImageProps, onload?: (loaded: boolean) => void) { if (!props.source.params) { this.clearImage(); - if (onload) onload(false); + onload?.(false); return; } if (!this.image || !this.image.props.source.params || !areImageTexturePropsEqual(props.source, this.image.props.source)) { this.clearImage(); - const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, () => { - if (this.image) this.image.loaded = true; - if (onload) onload(true); + const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, errored => { + if (this.image) this.image.loaded = !errored; + onload?.(true); }); this.image = { texture, props: { ...props }, asset, loaded: false }; ValueCell.update(this.renderable.values.tImage, texture); this.renderable.update(); } else { - if (onload) onload(false); + onload?.(false); } if (!this.image) return; @@ -248,7 +248,7 @@ export class BackgroundPass { if (props.variant.name === 'off') { this.clearSkybox(); this.clearImage(); - if (onload) onload(false); + onload?.(false); return; } else if (props.variant.name === 'skybox') { this.clearImage(); @@ -260,12 +260,12 @@ export class BackgroundPass { this.clearSkybox(); this.clearImage(); this.updateGradient(props.variant.params.topColor, props.variant.params.bottomColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false); - if (onload) onload(false); + onload?.(false); } else if (props.variant.name === 'radialGradient') { this.clearSkybox(); this.clearImage(); this.updateGradient(props.variant.params.centerColor, props.variant.params.edgeColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false); - if (onload) onload(false); + onload?.(false); } const { x, y, width, height } = camera.viewport; @@ -363,7 +363,7 @@ function areSkyboxTexturePropsEqual(facesA: SkyboxProps['faces'], facesB: Skybox return getSkyboxHash(facesA) === getSkyboxHash(facesB); } -function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: () => void): { texture: Texture, assets: Asset[] } { +function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: (errored?: boolean) => void): { texture: Texture, assets: Asset[] } { const cubeAssets = getCubeAssets(assetManager, faces); const cubeFaces = getCubeFaces(assetManager, cubeAssets); const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz]; @@ -387,13 +387,16 @@ function areImageTexturePropsEqual(sourceA: ImageProps['source'], sourceB: Image return getImageHash(sourceA) === getImageHash(sourceB); } -function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: () => void): { texture: Texture, asset: Asset } { +function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: (errored?: boolean) => void): { texture: Texture, asset: Asset } { const texture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear'); const img = new Image(); img.onload = () => { texture.load(img); onload?.(); }; + img.onerror = () => { + onload?.(true); + }; const asset = source.name === 'url' ? Asset.getUrlAsset(assetManager, source.params) : source.params!; diff --git a/src/mol-canvas3d/passes/image.ts b/src/mol-canvas3d/passes/image.ts index 2cdc9fee3..e78aca0a3 100644 --- a/src/mol-canvas3d/passes/image.ts +++ b/src/mol-canvas3d/passes/image.ts @@ -64,7 +64,7 @@ export class ImagePass { this.setSize(1024, 768); } - async updateBackground() { + updateBackground() { return new Promise<void>(resolve => { this.drawPass.postprocessing.background.update(this.camera, this.props.postprocessing.background, () => { resolve(); diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts index f2371edcf..26d4a2f1d 100644 --- a/src/mol-gl/webgl/texture.ts +++ b/src/mol-gl/webgl/texture.ts @@ -441,7 +441,7 @@ export function getCubeTarget(gl: GLRenderingContext, side: CubeSide): number { } } -export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipmaps: boolean, onload?: () => void): Texture { +export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipmaps: boolean, onload?: (errored?: boolean) => void): Texture { const target = gl.TEXTURE_CUBE_MAP; const filter = gl.LINEAR; const internalFormat = gl.RGBA; @@ -482,17 +482,22 @@ export function createCubeTexture(gl: GLRenderingContext, faces: CubeFaces, mipm gl.texImage2D(cubeTarget, level, internalFormat, format, type, image); loadedCount += 1; - if (loadedCount === 6 && !destroyed) { - if (mipmaps) { - gl.generateMipmap(target); - gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); - } else { - gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter); + if (loadedCount === 6) { + if (!destroyed) { + if (mipmaps) { + gl.generateMipmap(target); + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + } else { + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter); + } + gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter); } - gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter); - if (onload) onload(); + onload?.(destroyed); } }); + image.addEventListener('error', () => { + onload?.(true); + }); }); let destroyed = false; -- GitLab