offscreen canvas rendering implementation

This commit is contained in:
ThaUnknown 2021-02-28 18:00:50 +01:00
parent c010b6459e
commit 7e675bc1e6
4 changed files with 9639 additions and 67 deletions

View file

@ -44,7 +44,7 @@ async function renderSubs(trackNumber) {
video: video,
targetFps: await playerData.fps,
subContent: trackNumber ? playerData.headers[trackNumber].header.slice(0, -1) + Array.from(playerData.subtitles[trackNumber]).join("\n") : playerData.headers[3].header.slice(0, -1),
lossyRender: true,
renderMode: "offscreenCanvas",
fonts: playerData.fonts?.length != 0 ? playerData.fonts : ["https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc4.woff2"],
workerUrl: 'js/subtitles-octopus-worker.js',
timeOffset: 0

File diff suppressed because one or more lines are too long

View file

@ -135,52 +135,69 @@ var SubtitlesOctopus = function (options) {
renderOnDemand: self.renderAhead > 0,
dropAllAnimations: self.dropAllAnimations
});
if (self.renderMode === "offscreenCanvas") {
self.pushOffscreenCanvas()
self.initDone = true
}
};
self.pushOffscreenCanvas = function () {
let canvasControl = self.canvas.transferControlToOffscreen()
self.worker.postMessage({
target: 'offscreenCanvas',
canvas: canvasControl
}, [canvasControl])
}
self.createCanvas = function () {
if (!self.canvas) {
if (self.video) {
self.isOurCanvas = true;
self.canvas = document.createElement('canvas');
self.canvas.className = 'libassjs-canvas';
self.canvas.style.display = 'none';
if (self.video) {
self.isOurCanvas = true;
self.canvas = document.createElement('canvas');
self.canvas.className = 'libassjs-canvas';
self.canvas.style.display = 'none';
self.canvasParent = document.createElement('div');
self.canvasParent.className = 'libassjs-canvas-parent';
self.canvasParent.appendChild(self.canvas);
self.canvasParent = document.createElement('div');
self.canvasParent.className = 'libassjs-canvas-parent';
self.canvasParent.appendChild(self.canvas);
if (self.video.nextSibling) {
self.video.parentNode.insertBefore(self.canvasParent, self.video.nextSibling);
}
else {
self.video.parentNode.appendChild(self.canvasParent);
}
if (self.video.nextSibling) {
self.video.parentNode.insertBefore(self.canvasParent, self.video.nextSibling);
}
else {
if (!self.canvas) {
self.workerError('Don\'t know where to render: you should give video or canvas in options.');
}
self.video.parentNode.appendChild(self.canvasParent);
}
}
self.ctx = self.canvas.getContext('2d');
self.bufferCanvas = document.createElement('canvas');
self.bufferCanvasCtx = self.bufferCanvas.getContext('2d');
else {
if (!self.canvas) {
self.workerError('Don\'t know where to render: you should give video or canvas in options.');
}
}
if (!self.renderMode === "offscreenCanvas") {
self.ctx = self.canvas.getContext('2d');
}
if (!typeof self.hasAlphaBug == "boolean") {
self.bufferCanvas = document.createElement('canvas');
self.bufferCanvasCtx = self.bufferCanvas.getContext('2d');
self.bufferCanvas2 = document.createElement('canvas');
self.bufferCanvasCtx2 = self.bufferCanvas.getContext('2d');
// test for alpha bug, where e.g. WebKit can render a transparent pixel
// (with alpha == 0) as non-black which then leads to visual artifacts
self.bufferCanvas.width = 1;
self.bufferCanvas.height = 1;
var testBuf = new Uint8ClampedArray([0, 255, 0, 0]);
var testImage = new ImageData(testBuf, 1, 1);
self.bufferCanvasCtx.clearRect(0, 0, 1, 1);
self.ctx.clearRect(0, 0, 1, 1);
var prePut = self.ctx.getImageData(0, 0, 1, 1).data;
self.bufferCanvasCtx.putImageData(testImage, 0, 0);
self.ctx.drawImage(self.bufferCanvas, 0, 0);
var postPut = self.ctx.getImageData(0, 0, 1, 1).data;
self.hasAlphaBug = prePut[1] != postPut[1];
if (self.hasAlphaBug) {
console.log("Detected a browser having issue with transparent pixels, applying workaround");
// test for alpha bug, where e.g. WebKit can render a transparent pixel
// (with alpha == 0) as non-black which then leads to visual artifacts
self.bufferCanvas.width = 1;
self.bufferCanvas.height = 1;
self.bufferCanvas2.width = 1;
self.bufferCanvas2.height = 1;
var testBuf = new Uint8ClampedArray([0, 255, 0, 0]);
var testImage = new ImageData(testBuf, 1, 1);
self.bufferCanvasCtx.clearRect(0, 0, 1, 1);
self.bufferCanvasCtx2.clearRect(0, 0, 1, 1);
var prePut = self.bufferCanvasCtx2.getImageData(0, 0, 1, 1).data;
self.bufferCanvasCtx.putImageData(testImage, 0, 0);
self.bufferCanvasCtx2.drawImage(self.bufferCanvas, 0, 0);
var postPut = self.bufferCanvasCtx2.getImageData(0, 0, 1, 1).data;
self.hasAlphaBug = prePut[1] != postPut[1];
if (self.hasAlphaBug) {
console.log("Detected a browser having issue with transparent pixels, applying workaround");
}
}
};
@ -544,7 +561,7 @@ var SubtitlesOctopus = function (options) {
case 'canvas': {
switch (data.op) {
case 'getContext': {
self.ctx = self.canvas.getContext(data.type, data.attributes);
if (!self.renderMode === "offscreenCanvas") self.ctx = self.canvas.getContext(data.type, data.attributes);
break;
}
case 'resize': {
@ -754,6 +771,10 @@ var SubtitlesOctopus = function (options) {
self.canvas.style.left != left
) {
if (self.renderMode == "offscreenCanvas" && self.initDone) {
self.canvasParent.remove()
self.createCanvas()
}
if (videoSize != null) {
self.canvasParent.style.position = 'relative';
self.canvas.style.display = 'block';
@ -770,6 +791,9 @@ var SubtitlesOctopus = function (options) {
self.canvas.style.width = videoSize.width + 'px';
self.canvas.style.height = videoSize.height + 'px';
}
if (self.renderMode == "offscreenCanvas" && self.initDone) {
self.pushOffscreenCanvas()
}
self.worker.postMessage({
target: 'canvas',
width: self.canvas.width,

View file

@ -6,42 +6,47 @@ Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour,
Style: Default,Roboto Medium,26,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,0,0,0,0,100,100,0,0,1,1.3,0,2,20,20,23,1`
let options = {
video: video,
lossyRender: true,
fonts: ["https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc4.woff2"],
workerUrl: 'js/subtitles-octopus-worker.js',
subContent: header,
renderMode: "offscreenCanvas",
onReady: ready,
debug: true,
onError: console.log
};
let octopusInstance = new SubtitlesOctopus(options);
let octopusInstance
setTimeout(() => {
octopusInstance = new SubtitlesOctopus(options);
}, 1000)
function ready() {
octopusInstance.createEvent({
Duration: 2960,
Effect: "",
Layer: 100,
MarginL: 0,
MarginR: 0,
MarginV: 0,
Name: "",
ReadOrder: 0,
Start: 180,
Style: 1,
Text: "Miss Kobayashi's Dragon Something!",
})
octopusInstance.createEvent({
Duration: 1750,
Effect: "",
Layer: 100,
MarginL: 0,
MarginR: 0,
MarginV: 0,
Name: "",
ReadOrder: 1,
Start: 1940,
Style: 1,
Text: "What's with the "
})
setTimeout(() => {
octopusInstance.createEvent({
Duration: 2960,
Effect: "",
Layer: 100,
MarginL: 0,
MarginR: 0,
MarginV: 0,
Name: "",
ReadOrder: 0,
Start: 180,
Style: 1,
Text: "Miss Kobayashi's Dragon Something!",
})
octopusInstance.createEvent({
Duration: 1750,
Effect: "",
Layer: 100,
MarginL: 0,
MarginR: 0,
MarginV: 0,
Name: "",
ReadOrder: 1,
Start: 1940,
Style: 1,
Text: "What's with the "
})
}, 1000)
}
</script>