Files
web-dslr/ui/preview.js
Ingvar Stepanyan 6e84c3ead9 OSS boilterplate
2021-12-07 12:56:41 +00:00

122 lines
3.6 KiB
JavaScript

/*
* Copyright 2021 Google LLC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
import { h, Component, createRef } from 'preact';
import { rethrowIfCritical } from './ops.js';
export const isDebug = new URLSearchParams(location.search).has('debug');
const Stats = isDebug
? await import('stats.js').then(
res => /** @type {typeof import('stats.js')} */ (res['default'])
)
: null;
/** @extends Component<{ getPreview?: () => Promise<Blob> }, { error?: string }> */
export class Preview extends Component {
canvasHolderRef = createRef();
canvasRef = createRef();
/** @type {ResizeObserver} */
resizeObserver;
stats = isDebug ? new Stats() : null;
render() {
return h(
'div',
{ class: 'center-parent', ref: this.canvasHolderRef },
!this.props.getPreview
? h('div', { class: 'center' }, `Preview is unsupported`)
: h('canvas', { class: 'center', ref: this.canvasRef })
);
}
async componentDidMount() {
if (!this.props.getPreview) return;
let canvas = /** @type {HTMLCanvasElement} */ (this.canvasRef.current);
let canvasHolder = this.canvasHolderRef.current;
if (isDebug) {
canvasHolder.appendChild(this.stats.dom);
}
let canvasCtx = canvas.getContext('bitmaprenderer');
let ratio = 0;
let throttled = 0;
function updateCanvasSize() {
if (throttled) {
cancelAnimationFrame(throttled);
}
throttled = requestAnimationFrame(() => {
throttled = 0;
let width = canvasHolder.offsetWidth - 10;
let height = canvasHolder.offsetHeight;
if (height * ratio > width) {
height = width / ratio;
} else {
width = height * ratio;
}
Object.assign(canvas, { width, height });
});
}
(this.resizeObserver = new ResizeObserver(updateCanvasSize)).observe(
canvasHolder
);
// I have no idea why, but if we connect too soon, it just hangs...
await new Promise(resolve => setTimeout(resolve, 1500));
while (this.canvasRef.current) {
try {
let blob = await this.props.getPreview();
// If ratio is known; decode resized image right away - it's a bit faster.
// If it isn't known, retrieve entire image to calculate ratio from its dimensions.
let img = await createImageBitmap(
blob,
ratio
? {
resizeWidth: canvas.width,
resizeHeight: canvas.height
}
: {}
);
if (!ratio) {
ratio = img.width / img.height;
updateCanvasSize();
}
await new Promise(resolve => requestAnimationFrame(resolve));
canvasCtx.transferFromImageBitmap(img);
} catch (err) {
rethrowIfCritical(err);
console.error('Could not refresh preview:', err);
}
this.stats?.update();
}
}
componentWillUnmount() {
this.resizeObserver?.disconnect();
}
}