Skip to content

Commit

Permalink
Offload generation of blurred backgrounds to a worker
Browse files Browse the repository at this point in the history
Until now the generation of the blurred backgrounds was done in a normal
canvas, so it happened in the main thread and made the UI unresponsive
while the image was being generated. Now, to solve that, the blurred
background is generated in an OffscreenCanvas in a worker (if
supported by the browser).

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
  • Loading branch information
danxuliu authored and backportbot[bot] committed Dec 17, 2020
1 parent 3617c4b commit ab231ac
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 2 deletions.
2 changes: 2 additions & 0 deletions lib/Listener/CSPListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public function handle(Event $event): void {
$csp->addAllowedConnectDomain($server);
}

$csp->addAllowedWorkerSrcDomain('\'self\'');

$event->addPolicy($csp);
}
}
4 changes: 3 additions & 1 deletion src/components/CallView/shared/VideoBackground.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ export default {
const image = new Image()
image.onload = () => {
this.blurredBackgroundImageSource = image
createImageBitmap(image).then(imageBitmap => {
this.blurredBackgroundImageSource = imageBitmap
})
}
image.src = this.backgroundImageUrl
},
Expand Down
44 changes: 43 additions & 1 deletion src/utils/imageBlurrer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,27 @@
*
*/

export default function blur(image, width, height, blurRadius) {
import { generateFilePath } from '@nextcloud/router'

const worker = new Worker(generateFilePath('spreed', '', 'js/image-blurrer-worker.js'))

const pendingResults = {}
let pendingResultsNextId = 0

worker.onmessage = function(message) {
const pendingResult = pendingResults[message.data.id]
if (!pendingResult) {
console.debug('No pending result for blurring image with id ' + message.data.id)

return
}

pendingResult(message.data.blurredImageAsDataUrl)

delete pendingResults[message.data.id]
}

function blurSync(image, width, height, blurRadius) {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas')
canvas.width = width
Expand All @@ -32,3 +52,25 @@ export default function blur(image, width, height, blurRadius) {
resolve(canvas.toDataURL())
})
}

export default function blur(image, width, height, blurRadius) {
if (typeof OffscreenCanvas === 'undefined') {
return blurSync(image, width, height, blurRadius)
}

const id = pendingResultsNextId

pendingResultsNextId++

return new Promise((resolve, reject) => {
pendingResults[id] = resolve

worker.postMessage({
id: id,
image: image,
width: width,
height: height,
blurRadius: blurRadius,
})
})
}
37 changes: 37 additions & 0 deletions src/utils/imageBlurrerWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
*
* @copyright Copyright (c) 2020, Daniel Calviño Sánchez (danxuliu@gmail.com)
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

const fileReaderSync = new global.FileReaderSync()

onmessage = function(message) {
const offscreenCanvas = new OffscreenCanvas(message.data.width, message.data.height)

const context = offscreenCanvas.getContext('2d')
context.filter = `blur(${message.data.blurRadius}px)`
context.drawImage(message.data.image, 0, 0, offscreenCanvas.width, offscreenCanvas.height)

offscreenCanvas.convertToBlob().then(blob => {
postMessage({
id: message.data.id,
blurredImageAsDataUrl: fileReaderSync.readAsDataURL(blob),
})
})
}
5 changes: 5 additions & 0 deletions webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ module.exports = {
entry: {
'admin-settings': path.join(__dirname, 'src', 'mainAdminSettings.js'),
'collections': path.join(__dirname, 'src', 'collections.js'),
// There is a "worker-loader" plugin for Webpack, but I was not able to
// get it to work ("publicPath" uses "output.publicPath" rather than the
// one set in the plugin
// https://github.com/webpack-contrib/worker-loader/issues/281).
'image-blurrer-worker': path.join(__dirname, 'src', 'utils/imageBlurrerWorker.js'),
'talk': path.join(__dirname, 'src', 'main.js'),
'talk-files-sidebar': [
path.join(__dirname, 'src', 'mainFilesSidebar.js'),
Expand Down

0 comments on commit ab231ac

Please sign in to comment.