SlideShow: use LayerDrawing only to generate image
Signed-off-by: Szymon Kłos <szymon.klos@collabora.com> Change-Id: Ib4ffcdba419a2561cb1eca9162297be7f49f1042
This commit is contained in:
parent
a15d56a0d1
commit
e791f10c54
5 changed files with 91 additions and 270 deletions
|
@ -70,10 +70,8 @@ class LayerDrawing {
|
|||
private map: any = null;
|
||||
private helper: LayersCompositor;
|
||||
|
||||
private presentInWindow: boolean = false;
|
||||
private slideShowWindowProxy: any = null;
|
||||
private renderedSlides: Map<string, ImageBitmap> = new Map();
|
||||
private requestedSlideHash: string = null;
|
||||
private displayedSlideHash: string = null;
|
||||
private prefetchedSlideHash: string = null;
|
||||
private resolutionWidth: number = 960;
|
||||
private resolutionHeight: number = 540;
|
||||
|
@ -88,18 +86,35 @@ class LayerDrawing {
|
|||
new Map();
|
||||
private offscreenCanvas: OffscreenCanvas = null;
|
||||
private offscreenContext: OffscreenCanvasRenderingContext2D = null;
|
||||
private currentCanvas: HTMLCanvasElement = null;
|
||||
private currentCanvas: OffscreenCanvas = null;
|
||||
private currentCanvasContext: ImageBitmapRenderingContext = null;
|
||||
private onSlideRenderingCompleteCallback: VoidFunction = null;
|
||||
|
||||
private startSlideNumber: number = 0;
|
||||
private initialSlide: boolean;
|
||||
private slideShow: HTMLIFrameElement;
|
||||
|
||||
private fullscreen: Element;
|
||||
|
||||
constructor(mapObj: any, helper: LayersCompositor) {
|
||||
constructor(
|
||||
mapObj: any,
|
||||
helper: LayersCompositor
|
||||
) {
|
||||
this.map = mapObj;
|
||||
this.helper = helper;
|
||||
|
||||
this.currentCanvas = new OffscreenCanvas(
|
||||
this.canvasWidth,
|
||||
this.canvasHeight,
|
||||
);
|
||||
if (!this.currentCanvas) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing: no canvas element found',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentCanvasContext = this.currentCanvas.getContext('bitmaprenderer');
|
||||
if (!this.currentCanvasContext) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing: can not get a valid context for current canvas',
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addHooks() {
|
||||
|
@ -112,35 +127,22 @@ class LayerDrawing {
|
|||
this.map.off('sliderenderingcomplete', this.onSlideRenderingComplete, this);
|
||||
}
|
||||
|
||||
isFullscreen(): boolean {
|
||||
return !!this.fullscreen;
|
||||
}
|
||||
|
||||
private getSlideInfo(slideHash: string) {
|
||||
return this.helper.getSlideInfo(slideHash);
|
||||
}
|
||||
|
||||
startPresentation(startSlideNumber: number, presentInWindow: boolean) {
|
||||
if (this.checkPresentationDisabled()) {
|
||||
this.notifyPresentationDisabled();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isPresenting()) {
|
||||
this.notifyAlreadyPresenting();
|
||||
return;
|
||||
}
|
||||
|
||||
this.presentInWindow = presentInWindow;
|
||||
this.startSlideNumber = startSlideNumber;
|
||||
|
||||
this.initializeSlideShow();
|
||||
public getSlide(slideNumber: number): ImageBitmap {
|
||||
const startSlideHash = this.helper.getSlideHash(slideNumber);
|
||||
return this.renderedSlides.get(startSlideHash);
|
||||
}
|
||||
|
||||
initializeSlideShow() {
|
||||
public requestSlide(slideNumber: number, callback: VoidFunction) {
|
||||
this.onSlideRenderingCompleteCallback = callback;
|
||||
this.computeInitialResolution();
|
||||
this.initializeCanvas();
|
||||
this.requestInitialSlide();
|
||||
|
||||
const startSlideHash = this.helper.getSlideHash(slideNumber);
|
||||
this.requestSlideImpl(startSlideHash);
|
||||
}
|
||||
|
||||
private initializeCanvas() {
|
||||
|
@ -156,17 +158,11 @@ class LayerDrawing {
|
|||
this.offscreenContext = this.offscreenCanvas.getContext('2d');
|
||||
}
|
||||
|
||||
requestInitialSlide() {
|
||||
this.initialSlide = true;
|
||||
const startSlideHash = this.helper.getSlideHash(this.startSlideNumber);
|
||||
this.requestSlide(startSlideHash);
|
||||
}
|
||||
|
||||
requestSlide(slideHash: string, prefetch: boolean = false) {
|
||||
private requestSlideImpl(slideHash: string, prefetch: boolean = false) {
|
||||
const slideInfo = this.getSlideInfo(slideHash);
|
||||
if (!slideInfo) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing.requestSlide: No info for requested slide: hash: ' +
|
||||
'LayerDrawing.requestSlideImpl: No info for requested slide: hash: ' +
|
||||
slideHash,
|
||||
);
|
||||
return;
|
||||
|
@ -176,8 +172,10 @@ class LayerDrawing {
|
|||
slideHash === this.prefetchedSlideHash
|
||||
)
|
||||
return;
|
||||
|
||||
// TODO: queue
|
||||
if (this.requestedSlideHash || this.prefetchedSlideHash) {
|
||||
setTimeout(this.requestSlide.bind(this), 500, slideHash, prefetch);
|
||||
setTimeout(this.requestSlideImpl.bind(this), 500, slideHash, prefetch);
|
||||
}
|
||||
|
||||
if (prefetch) {
|
||||
|
@ -240,8 +238,6 @@ class LayerDrawing {
|
|||
}
|
||||
|
||||
handleTextFieldMsg(info: LayerInfo, img: any) {
|
||||
const slideInfo = this.getSlideInfo(info.slideHash);
|
||||
|
||||
const textFieldInfo = info.content as TextFieldInfo;
|
||||
const imageInfo = textFieldInfo.content;
|
||||
if (!this.checkAndAttachImageData(imageInfo, img)) return;
|
||||
|
@ -459,150 +455,24 @@ class LayerDrawing {
|
|||
}
|
||||
|
||||
onSlideRenderingComplete() {
|
||||
if (this.initialSlide) {
|
||||
this.initialSlide = false;
|
||||
this.startPlaying();
|
||||
}
|
||||
this.showRenderedSlide();
|
||||
}
|
||||
|
||||
private startPlaying() {
|
||||
// Windowed Presentation
|
||||
if (this.presentInWindow) {
|
||||
const popupTitle =
|
||||
_('Windowed Presentation: ') + this.map['wopi'].BaseFileName;
|
||||
const htmlContent = this.generateSlideWindowHtml(popupTitle);
|
||||
|
||||
this.slideShowWindowProxy = window.open('', '_blank', 'popup');
|
||||
|
||||
// this.slideShowWindowProxy.addEventListener('load', this._handleSlideWindowLoaded.bind(this), false);
|
||||
|
||||
if (!this.slideShowWindowProxy) {
|
||||
this.map.uiManager.showInfoModal(
|
||||
'popup-blocked-modal',
|
||||
_('Windowed Presentation Blocked'),
|
||||
_(
|
||||
'Presentation was blocked. Please allow pop-ups in your browser. This lets slide shows to be displayed in separated windows, allowing for easy screen sharing.',
|
||||
),
|
||||
'',
|
||||
_('OK'),
|
||||
null,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
this.slideShowWindowProxy.document.documentElement.innerHTML =
|
||||
htmlContent;
|
||||
this.slideShowWindowProxy.document.close();
|
||||
this.slideShowWindowProxy.focus();
|
||||
|
||||
const canvas = this.slideShowWindowProxy.document.getElementById(
|
||||
'canvas',
|
||||
) as HTMLCanvasElement;
|
||||
if (!canvas) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing.startPlaying: no canvas element found',
|
||||
);
|
||||
return;
|
||||
}
|
||||
canvas.width = this.canvasWidth;
|
||||
canvas.height = this.canvasHeight;
|
||||
|
||||
const ctx = canvas.getContext('bitmaprenderer');
|
||||
if (!ctx) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing.startPlaying: can not get a valid context for current canvas',
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.currentCanvas = canvas;
|
||||
this.currentCanvasContext = ctx;
|
||||
|
||||
this.slideShowWindowProxy.addEventListener(
|
||||
'keydown',
|
||||
this.onSlideWindowKeyPress.bind(this),
|
||||
);
|
||||
this.slideShowWindowProxy.addEventListener(
|
||||
'resize',
|
||||
this.onSlideWindowResize.bind(this),
|
||||
);
|
||||
|
||||
this.centerCanvasInSlideWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private showRenderedSlide() {
|
||||
if (!this.offscreenCanvas) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing.showRenderedSlide: no offscreen canvas available.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.currentCanvasContext) {
|
||||
window.app.console.log(
|
||||
'LayerDrawing.showRenderedSlide: no valid context for current canvas',
|
||||
'LayerDrawing.onSlideRenderingComplete: no offscreen canvas available.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.requestedSlideHash) {
|
||||
this.displayedSlideHash = this.requestedSlideHash;
|
||||
const renderedSlide = this.offscreenCanvas.transferToImageBitmap();
|
||||
this.renderedSlides.set(this.requestedSlideHash, renderedSlide);
|
||||
|
||||
if (this.requestedSlideHash)
|
||||
this.requestedSlideHash = null;
|
||||
}
|
||||
const bitmap = this.offscreenCanvas.transferToImageBitmap();
|
||||
this.currentCanvasContext.transferFromImageBitmap(bitmap);
|
||||
}
|
||||
|
||||
private generateSlideWindowHtml(title: string) {
|
||||
const sanitizer = document.createElement('div');
|
||||
sanitizer.innerText = title;
|
||||
const sanitizedTitle = sanitizer.innerHTML;
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${sanitizedTitle}</title>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden; /* Prevent scrollbars */
|
||||
}
|
||||
.vertical-center {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 50%;
|
||||
/*-ms-transform: translateY(-50%);*/
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.horizontal-center {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 50%;
|
||||
/*-ms-transform: translateY(-50%);*/
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
const oldCallback = this.onSlideRenderingCompleteCallback;
|
||||
this.onSlideRenderingCompleteCallback = null;
|
||||
oldCallback.call(this.helper);
|
||||
}
|
||||
|
||||
/*
|
||||
handleSlideWindowLoaded () {
|
||||
window.app.console.log('LayerDrawing._handleSlideWindowLoaded');
|
||||
}
|
||||
*/
|
||||
|
||||
private checkAndAttachImageData(imageInfo: ImageInfo, img: any): boolean {
|
||||
if (!img || (imageInfo.type === 'png' && !img.src)) {
|
||||
window.app.console.log(
|
||||
|
@ -615,12 +485,8 @@ class LayerDrawing {
|
|||
}
|
||||
|
||||
private computeInitialResolution() {
|
||||
let viewWidth = window.innerWidth;
|
||||
let viewHeight = window.innerHeight;
|
||||
if (!this.presentInWindow) {
|
||||
viewWidth = window.screen.width;
|
||||
viewHeight = window.screen.height;
|
||||
}
|
||||
const viewWidth = window.screen.width;
|
||||
const viewHeight = window.screen.height;
|
||||
this.computeResolution(viewWidth, viewHeight);
|
||||
}
|
||||
|
||||
|
@ -635,69 +501,6 @@ class LayerDrawing {
|
|||
resHeight,
|
||||
);
|
||||
}
|
||||
|
||||
private centerCanvasInSlideWindow() {
|
||||
const winWidth = this.slideShowWindowProxy.innerWidth;
|
||||
const winHeight = this.slideShowWindowProxy.innerHeight;
|
||||
if (winWidth * this.canvasHeight < winHeight * this.canvasWidth) {
|
||||
this.currentCanvas.className = 'vertical-center';
|
||||
} else {
|
||||
this.currentCanvas.className = 'horizontal-center';
|
||||
}
|
||||
}
|
||||
|
||||
onSlideWindowResize() {
|
||||
this.centerCanvasInSlideWindow();
|
||||
}
|
||||
|
||||
isPresenting() {
|
||||
if (this.slideShowWindowProxy && !this.slideShowWindowProxy.closed)
|
||||
return true;
|
||||
if (this.slideShow) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
notifyAlreadyPresenting() {
|
||||
this.map.uiManager.showInfoModal(
|
||||
'already-presenting-modal',
|
||||
_('Already presenting'),
|
||||
_('You are already presenting this document'),
|
||||
'',
|
||||
_('OK'),
|
||||
null,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
checkPresentationDisabled() {
|
||||
return this.map['wopi'].DisablePresentation;
|
||||
}
|
||||
|
||||
notifyPresentationDisabled() {
|
||||
this.map.uiManager.showInfoModal(
|
||||
'presentation-disabled-modal',
|
||||
_('Presentation disabled'),
|
||||
_('Presentation mode has been disabled for this document'),
|
||||
'',
|
||||
_('OK'),
|
||||
null,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
onSlideWindowKeyPress(e: any) {
|
||||
if (e.code === 'Escape') {
|
||||
this.slideShowWindowProxy.opener.focus();
|
||||
this.slideShowWindowProxy.close();
|
||||
this.map.uiManager.closeSnackbar();
|
||||
} else if (e.code === 'ArrowRight') {
|
||||
const slideInfo = this.getSlideInfo(this.displayedSlideHash);
|
||||
this.requestSlide(slideInfo.next);
|
||||
} else if (e.code === 'ArrowLeft') {
|
||||
const slideInfo = this.getSlideInfo(this.displayedSlideHash);
|
||||
this.requestSlide(slideInfo.prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SlideShow.LayerDrawing = LayerDrawing;
|
||||
|
|
|
@ -34,7 +34,10 @@ class LayersCompositor extends SlideShow.SlideCompositor {
|
|||
|
||||
protected _addHooks() {
|
||||
app.map.on('slidebackground', this.onSlideBackground, this);
|
||||
this.layerDrawing = new SlideShow.LayerDrawing(app.map, this);
|
||||
this.layerDrawing = new SlideShow.LayerDrawing(
|
||||
app.map,
|
||||
this
|
||||
);
|
||||
this.layerDrawing.addHooks();
|
||||
}
|
||||
|
||||
|
@ -43,6 +46,15 @@ class LayersCompositor extends SlideShow.SlideCompositor {
|
|||
this.layerDrawing.removeHooks();
|
||||
}
|
||||
|
||||
public fetchAndRun(slideNumber: number, callback: VoidFunction) {
|
||||
super.fetchAndRun(slideNumber, callback);
|
||||
this.layerDrawing.requestSlide(this._initialSlideNumber, () => {
|
||||
const oldCallback = this._onGotSlideCallback;
|
||||
this._onGotSlideCallback = null;
|
||||
oldCallback.call(this._slideShowPresenter);
|
||||
});
|
||||
}
|
||||
|
||||
private onSlideBackground(e: any) {
|
||||
if (!e.data) {
|
||||
window.app.console.log(
|
||||
|
@ -60,7 +72,6 @@ class LayersCompositor extends SlideShow.SlideCompositor {
|
|||
public updatePresentationInfo(presentationInfo: PresentationInfo) {
|
||||
this._presentationInfo = presentationInfo;
|
||||
this.onSlidesInfo(presentationInfo);
|
||||
this.layerDrawing.startPresentation(0, false);
|
||||
}
|
||||
|
||||
private onSlidesInfo(data: any) {
|
||||
|
@ -83,8 +94,6 @@ class LayersCompositor extends SlideShow.SlideCompositor {
|
|||
|
||||
this.docWidth = data.docWidth;
|
||||
this.docHeight = data.docHeight;
|
||||
|
||||
app.map.fire('presentationinfoupdated');
|
||||
}
|
||||
|
||||
private handleBackgroundLayer(data: any, img: any) {
|
||||
|
@ -214,8 +223,8 @@ class LayersCompositor extends SlideShow.SlideCompositor {
|
|||
return this.computeLayerSize(resolution[0], resolution[1]);
|
||||
}
|
||||
|
||||
public getSlide(slideNumber: number): HTMLImageElement {
|
||||
return null;
|
||||
public getSlide(slideNumber: number): ImageBitmap {
|
||||
return this.layerDrawing.getSlide(slideNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
declare var SlideShow: any;
|
||||
|
||||
class PreviewsCompositor extends SlideShow.SlideCompositor {
|
||||
_slides: Array<HTMLImageElement> = null;
|
||||
_slides: Array<ImageBitmap> = null;
|
||||
_FETCH_ID_: number = 1000; // TODO
|
||||
|
||||
constructor(
|
||||
|
@ -25,7 +25,7 @@ class PreviewsCompositor extends SlideShow.SlideCompositor {
|
|||
) {
|
||||
super(slideShowPresenter, presentationInfo, width, height);
|
||||
const numberOfSlides = this._getSlidesCount();
|
||||
this._slides = new Array<HTMLImageElement>(numberOfSlides);
|
||||
this._slides = new Array<ImageBitmap>(numberOfSlides);
|
||||
}
|
||||
|
||||
protected _addHooks() {
|
||||
|
@ -53,13 +53,18 @@ class PreviewsCompositor extends SlideShow.SlideCompositor {
|
|||
console.debug('PreviewsCompositor: received slide: ' + e.part);
|
||||
const received = new Image();
|
||||
received.src = e.tile.src;
|
||||
this._slides[e.part] = received;
|
||||
|
||||
if (e.part === this._initialSlideNumber && this._onGotSlideCallback) {
|
||||
const callback = this._onGotSlideCallback; // allow nesting
|
||||
this._onGotSlideCallback = null;
|
||||
callback.call(this._slideShowPresenter);
|
||||
}
|
||||
received.onload = () => {
|
||||
createImageBitmap(received).then((result: ImageBitmap) => {
|
||||
this._slides[e.part] = result;
|
||||
|
||||
if (e.part === this._initialSlideNumber && this._onGotSlideCallback) {
|
||||
const callback = this._onGotSlideCallback; // allow nesting
|
||||
this._onGotSlideCallback = null;
|
||||
callback.call(this._slideShowPresenter);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private _requestPreview(slideNumber: number) {
|
||||
|
@ -76,7 +81,7 @@ class PreviewsCompositor extends SlideShow.SlideCompositor {
|
|||
);
|
||||
}
|
||||
|
||||
public getSlide(slideNumber: number): HTMLImageElement {
|
||||
public getSlide(slideNumber: number): ImageBitmap {
|
||||
// use cache if possible
|
||||
const slide = this._slides[slideNumber];
|
||||
|
||||
|
|
|
@ -48,12 +48,6 @@ abstract class SlideCompositor {
|
|||
this._onGotSlideCallback = callback;
|
||||
}
|
||||
|
||||
public getSlideInfo(slideNumber: number): SlideInfo | null {
|
||||
return this._presentationInfo
|
||||
? this._presentationInfo.slides[slideNumber]
|
||||
: null;
|
||||
}
|
||||
|
||||
protected _getSlidesCount() {
|
||||
return this._presentationInfo ? this._presentationInfo.slides.length : 0;
|
||||
}
|
||||
|
@ -66,7 +60,7 @@ abstract class SlideCompositor {
|
|||
return this._height;
|
||||
}
|
||||
|
||||
public abstract getSlide(slideNumber: number): HTMLImageElement;
|
||||
public abstract getSlide(slideNumber: number): ImageBitmap;
|
||||
}
|
||||
|
||||
SlideShow.SlideCompositor = SlideCompositor;
|
||||
|
|
|
@ -59,6 +59,12 @@ class SlideShowPresenter {
|
|||
this._map.off('newfullscreen', this._onStart, this);
|
||||
}
|
||||
|
||||
public getSlideInfo(slideNumber: number): SlideInfo | null {
|
||||
return this._presentationInfo
|
||||
? this._presentationInfo.slides[slideNumber]
|
||||
: null;
|
||||
}
|
||||
|
||||
_getSlidesCount() {
|
||||
return this._presentationInfo ? this._presentationInfo.slides.length : 0;
|
||||
}
|
||||
|
@ -67,6 +73,10 @@ class SlideShowPresenter {
|
|||
return !!this._fullscreen;
|
||||
}
|
||||
|
||||
public getCanvas(): HTMLCanvasElement {
|
||||
return this._slideShowCanvas;
|
||||
}
|
||||
|
||||
_onFullScreenChange() {
|
||||
this._fullscreen = document.fullscreenElement;
|
||||
if (!this._fullscreen) this._stopFullScreen();
|
||||
|
@ -133,10 +143,10 @@ class SlideShowPresenter {
|
|||
return canvas;
|
||||
}
|
||||
|
||||
_doTransition(previousSlide: HTMLImageElement, nextSlideNumber: number) {
|
||||
_doTransition(previousSlide: ImageBitmap, nextSlideNumber: number) {
|
||||
this._slideCompositor.fetchAndRun(nextSlideNumber, () => {
|
||||
const nextSlide = this._slideCompositor.getSlide(nextSlideNumber);
|
||||
const slideInfo = this._slideCompositor.getSlideInfo(nextSlideNumber);
|
||||
const slideInfo = this.getSlideInfo(nextSlideNumber);
|
||||
if (
|
||||
slideInfo.transitionType == undefined ||
|
||||
slideInfo.transitionType.length == 0
|
||||
|
|
Loading…
Reference in a new issue