mirror of
https://github.com/SrIzan10/vdo.ninja.git
synced 2026-05-01 11:05:24 +00:00
Add files via upload
This commit is contained in:
248
thirdparty/StreamSaver.js
vendored
248
thirdparty/StreamSaver.js
vendored
@@ -2,135 +2,34 @@
|
|||||||
|
|
||||||
/* global chrome location ReadableStream define MessageChannel TransformStream */
|
/* global chrome location ReadableStream define MessageChannel TransformStream */
|
||||||
|
|
||||||
;((name, definition) => {
|
function streamSaverFunction(){
|
||||||
typeof module !== 'undefined'
|
|
||||||
? module.exports = definition()
|
|
||||||
: typeof define === 'function' && typeof define.amd === 'object'
|
|
||||||
? define(definition)
|
|
||||||
: this[name] = definition()
|
|
||||||
})('streamSaver', () => {
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const global = typeof window === 'object' ? window : this
|
const global = typeof window === 'object' ? window : this;
|
||||||
if (!global.HTMLElement) console.warn('streamsaver is meant to run on browsers main thread')
|
if (!global.HTMLElement) console.warn('streamsaver is meant to run on browsers main thread')
|
||||||
|
|
||||||
let mitmTransporter = null
|
let mitmTransporter = null;
|
||||||
let supportsTransferable = false
|
let supportsTransferable = false;
|
||||||
const test = fn => { try { fn() } catch (e) {} }
|
const test = fn => { try { fn() } catch (e) {} };
|
||||||
const ponyfill = global.WebStreamsPolyfill || {}
|
const ponyfill = global.WebStreamsPolyfill || {};
|
||||||
const isSecureContext = global.isSecureContext
|
const isSecureContext = global.isSecureContext;
|
||||||
|
|
||||||
|
//console.log(ponyfill);
|
||||||
|
//console.log(isSecureContext);
|
||||||
|
|
||||||
// TODO: Must come up with a real detection test (#69)
|
// TODO: Must come up with a real detection test (#69)
|
||||||
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint
|
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint;
|
||||||
|
|
||||||
|
//console.log(useBlobFallback);
|
||||||
|
|
||||||
const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
|
const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
|
||||||
? 'iframe'
|
? 'iframe'
|
||||||
: 'navigate'
|
: 'navigate';
|
||||||
|
|
||||||
const streamSaver = {
|
//console.log(downloadStrategy);
|
||||||
createWriteStream,
|
|
||||||
WritableStream: global.WritableStream || ponyfill.WritableStream,
|
|
||||||
supported: true,
|
|
||||||
version: { full: '2.0.7', major: 2, minor: 0, dot: 7 },
|
|
||||||
mitm: './thirdparty/mitm.html?v=2'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a hidden iframe and append it to the DOM (body)
|
|
||||||
*
|
|
||||||
* @param {string} src page to load
|
|
||||||
* @return {HTMLIFrameElement} page to load
|
|
||||||
*/
|
|
||||||
function makeIframe (src) {
|
|
||||||
if (!src) throw new Error('meh')
|
|
||||||
const iframe = document.createElement('iframe')
|
|
||||||
iframe.hidden = true
|
|
||||||
iframe.src = src
|
|
||||||
iframe.loaded = false
|
|
||||||
iframe.name = 'iframe'
|
|
||||||
iframe.isIframe = true
|
|
||||||
iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
|
|
||||||
iframe.addEventListener('load', () => {
|
|
||||||
iframe.loaded = true
|
|
||||||
}, { once: true })
|
|
||||||
document.body.appendChild(iframe)
|
|
||||||
return iframe
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create a popup that simulates the basic things
|
|
||||||
* of what a iframe can do
|
|
||||||
*
|
|
||||||
* @param {string} src page to load
|
|
||||||
* @return {object} iframe like object
|
|
||||||
*/
|
|
||||||
function makePopup (src) {
|
|
||||||
const options = 'width=200,height=100'
|
|
||||||
const delegate = document.createDocumentFragment()
|
|
||||||
const popup = {
|
|
||||||
frame: global.open(src, 'popup', options),
|
|
||||||
loaded: false,
|
|
||||||
isIframe: false,
|
|
||||||
isPopup: true,
|
|
||||||
remove () { popup.frame.close() },
|
|
||||||
addEventListener (...args) { delegate.addEventListener(...args) },
|
|
||||||
dispatchEvent (...args) { delegate.dispatchEvent(...args) },
|
|
||||||
removeEventListener (...args) { delegate.removeEventListener(...args) },
|
|
||||||
postMessage (...args) { popup.frame.postMessage(...args) }
|
|
||||||
}
|
|
||||||
|
|
||||||
const onReady = evt => {
|
|
||||||
if (evt.source === popup.frame) {
|
|
||||||
popup.loaded = true
|
|
||||||
global.removeEventListener('message', onReady)
|
|
||||||
popup.dispatchEvent(new Event('load'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
global.addEventListener('message', onReady)
|
|
||||||
|
|
||||||
return popup
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// We can't look for service worker since it may still work on http
|
|
||||||
new Response(new ReadableStream())
|
|
||||||
if (isSecureContext && !('serviceWorker' in navigator)) {
|
|
||||||
useBlobFallback = true
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
useBlobFallback = true
|
|
||||||
}
|
|
||||||
|
|
||||||
test(() => {
|
|
||||||
// Transferable stream was first enabled in chrome v73 behind a flag
|
|
||||||
const { readable } = new TransformStream()
|
|
||||||
const mc = new MessageChannel()
|
|
||||||
mc.port1.postMessage(readable, [readable])
|
|
||||||
mc.port1.close()
|
|
||||||
mc.port2.close()
|
|
||||||
supportsTransferable = true
|
|
||||||
// Freeze TransformStream object (can only work with native)
|
|
||||||
Object.defineProperty(streamSaver, 'TransformStream', {
|
|
||||||
configurable: false,
|
|
||||||
writable: false,
|
|
||||||
value: TransformStream
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function loadTransporter () {
|
|
||||||
if (!mitmTransporter) {
|
|
||||||
mitmTransporter = isSecureContext
|
|
||||||
? makeIframe(streamSaver.mitm)
|
|
||||||
: makePopup(streamSaver.mitm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} filename filename that should be used
|
|
||||||
* @param {object} options [description]
|
|
||||||
* @param {number} size deprecated
|
|
||||||
* @return {WritableStream<Uint8Array>}
|
|
||||||
*/
|
|
||||||
function createWriteStream (filename, stopStream){
|
function createWriteStream (filename, stopStream){
|
||||||
|
//console.log("createWriteStream");
|
||||||
let opts = {
|
let opts = {
|
||||||
size: null,
|
size: null,
|
||||||
pathname: null,
|
pathname: null,
|
||||||
@@ -200,6 +99,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel.port1.onmessage = evt => {
|
channel.port1.onmessage = evt => {
|
||||||
|
console.log(evt);
|
||||||
// Service worker sent us a link that we should open.
|
// Service worker sent us a link that we should open.
|
||||||
if (evt.data.download) {
|
if (evt.data.download) {
|
||||||
// Special treatment for popup...
|
// Special treatment for popup...
|
||||||
@@ -309,5 +209,109 @@
|
|||||||
}, opts.writableStrategy)
|
}, opts.writableStrategy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const streamSaver = {
|
||||||
|
createWriteStream,
|
||||||
|
WritableStream: global.WritableStream || ponyfill.WritableStream,
|
||||||
|
supported: true,
|
||||||
|
version: { full: '2.0.7', major: 2, minor: 0, dot: 7 },
|
||||||
|
mitm: './thirdparty/mitm.html?v=2'
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log(streamSaver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a hidden iframe and append it to the DOM (body)
|
||||||
|
*
|
||||||
|
* @param {string} src page to load
|
||||||
|
* @return {HTMLIFrameElement} page to load
|
||||||
|
*/
|
||||||
|
function makeIframe (src) {
|
||||||
|
if (!src) throw new Error('meh')
|
||||||
|
const iframe = document.createElement('iframe')
|
||||||
|
iframe.hidden = true
|
||||||
|
iframe.src = src
|
||||||
|
iframe.loaded = false
|
||||||
|
iframe.name = 'iframe'
|
||||||
|
iframe.isIframe = true
|
||||||
|
iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
|
||||||
|
iframe.addEventListener('load', () => {
|
||||||
|
iframe.loaded = true
|
||||||
|
}, { once: true })
|
||||||
|
document.body.appendChild(iframe)
|
||||||
|
return iframe
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a popup that simulates the basic things
|
||||||
|
* of what a iframe can do
|
||||||
|
*
|
||||||
|
* @param {string} src page to load
|
||||||
|
* @return {object} iframe like object
|
||||||
|
*/
|
||||||
|
function makePopup (src) {
|
||||||
|
const options = 'width=200,height=100'
|
||||||
|
const delegate = document.createDocumentFragment()
|
||||||
|
const popup = {
|
||||||
|
frame: global.open(src, 'popup', options),
|
||||||
|
loaded: false,
|
||||||
|
isIframe: false,
|
||||||
|
isPopup: true,
|
||||||
|
remove () { popup.frame.close() },
|
||||||
|
addEventListener (...args) { delegate.addEventListener(...args) },
|
||||||
|
dispatchEvent (...args) { delegate.dispatchEvent(...args) },
|
||||||
|
removeEventListener (...args) { delegate.removeEventListener(...args) },
|
||||||
|
postMessage (...args) { popup.frame.postMessage(...args) }
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReady = evt => {
|
||||||
|
if (evt.source === popup.frame) {
|
||||||
|
popup.loaded = true
|
||||||
|
global.removeEventListener('message', onReady)
|
||||||
|
popup.dispatchEvent(new Event('load'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global.addEventListener('message', onReady)
|
||||||
|
|
||||||
|
return popup
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// We can't look for service worker since it may still work on http
|
||||||
|
new Response(new ReadableStream())
|
||||||
|
if (isSecureContext && !('serviceWorker' in navigator)) {
|
||||||
|
useBlobFallback = true
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
useBlobFallback = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log("useBlobFallback: "+useBlobFallback);
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
// Transferable stream was first enabled in chrome v73 behind a flag
|
||||||
|
const { readable } = new TransformStream()
|
||||||
|
const mc = new MessageChannel()
|
||||||
|
mc.port1.postMessage(readable, [readable])
|
||||||
|
mc.port1.close()
|
||||||
|
mc.port2.close()
|
||||||
|
supportsTransferable = true
|
||||||
|
// Freeze TransformStream object (can only work with native)
|
||||||
|
Object.defineProperty(streamSaver, 'TransformStream', {
|
||||||
|
configurable: false,
|
||||||
|
writable: false,
|
||||||
|
value: TransformStream
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function loadTransporter () {
|
||||||
|
if (!mitmTransporter) {
|
||||||
|
mitmTransporter = isSecureContext
|
||||||
|
? makeIframe(streamSaver.mitm)
|
||||||
|
: makePopup(streamSaver.mitm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return streamSaver
|
return streamSaver
|
||||||
})
|
};
|
||||||
|
var streamSaver = streamSaverFunction();
|
||||||
253
thirdparty/canvasFilters.js
vendored
Normal file
253
thirdparty/canvasFilters.js
vendored
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
// Modified copy obtained from https://github.com/timotgl/inspector-bokeh/tree/main/demo - MIT Lic
|
||||||
|
// Original file based on https://github.com/kig/canvasfilters/blob/master/filters.js
|
||||||
|
// I reduced the modified code to a few core functions; standard convolve/blur matrix functions.
|
||||||
|
|
||||||
|
const Filters = {};
|
||||||
|
|
||||||
|
if (typeof Float32Array == 'undefined') { // good
|
||||||
|
Filters.getFloat32Array = Filters.getUint8Array = function (len) {
|
||||||
|
if (len.length) {
|
||||||
|
return len.slice(0);
|
||||||
|
}
|
||||||
|
return new Array(len);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
Filters.getFloat32Array = function (len) {
|
||||||
|
return new Float32Array(len);
|
||||||
|
};
|
||||||
|
Filters.getUint8Array = function (len) {
|
||||||
|
return new Uint8Array(len);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof document != 'undefined') {
|
||||||
|
Filters.tmpCanvas = document.createElement('canvas');
|
||||||
|
Filters.tmpCtx = Filters.tmpCanvas.getContext('2d');
|
||||||
|
Filters.createImageData = function (w, h) {
|
||||||
|
return this.tmpCtx.createImageData(w, h);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
onmessage = function (e) {
|
||||||
|
var ds = e.data;
|
||||||
|
if (!ds.length) {
|
||||||
|
ds = [ds];
|
||||||
|
}
|
||||||
|
postMessage(Filters.runPipeline(ds));
|
||||||
|
};
|
||||||
|
Filters.createImageData = function (w, h) {
|
||||||
|
return { width: w, height: h, data: this.getFloat32Array(w * h * 4) };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Filters.convolve = function (pixels, weights, opaque) { // good
|
||||||
|
var side = Math.round(Math.sqrt(weights.length));
|
||||||
|
var halfSide = Math.floor(side / 2);
|
||||||
|
|
||||||
|
var src = pixels.data;
|
||||||
|
var sw = pixels.width;
|
||||||
|
var sh = pixels.height;
|
||||||
|
|
||||||
|
var w = sw;
|
||||||
|
var h = sh;
|
||||||
|
var output = Filters.createImageData(w, h);
|
||||||
|
var dst = output.data;
|
||||||
|
|
||||||
|
var alphaFac = opaque ? 1 : 0;
|
||||||
|
|
||||||
|
for (var y = 0; y < h; y++) {
|
||||||
|
for (var x = 0; x < w; x++) {
|
||||||
|
var sy = y;
|
||||||
|
var sx = x;
|
||||||
|
var dstOff = (y * w + x) * 4;
|
||||||
|
var r = 0,
|
||||||
|
g = 0,
|
||||||
|
b = 0,
|
||||||
|
a = 0;
|
||||||
|
for (var cy = 0; cy < side; cy++) {
|
||||||
|
for (var cx = 0; cx < side; cx++) {
|
||||||
|
var scy = Math.min(sh - 1, Math.max(0, sy + cy - halfSide));
|
||||||
|
var scx = Math.min(sw - 1, Math.max(0, sx + cx - halfSide));
|
||||||
|
var srcOff = (scy * sw + scx) * 4;
|
||||||
|
var wt = weights[cy * side + cx];
|
||||||
|
r += src[srcOff] * wt;
|
||||||
|
g += src[srcOff + 1] * wt;
|
||||||
|
b += src[srcOff + 2] * wt;
|
||||||
|
a += src[srcOff + 3] * wt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst[dstOff] = r;
|
||||||
|
dst[dstOff + 1] = g;
|
||||||
|
dst[dstOff + 2] = b;
|
||||||
|
dst[dstOff + 3] = a + alphaFac * (255 - a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Filters.luminance = function (pixels, args) { // good
|
||||||
|
var output = Filters.createImageData(pixels.width, pixels.height);
|
||||||
|
var dst = output.data;
|
||||||
|
var d = pixels.data;
|
||||||
|
for (var i = 0; i < d.length; i += 4) {
|
||||||
|
var r = d[i];
|
||||||
|
var g = d[i + 1];
|
||||||
|
var b = d[i + 2];
|
||||||
|
// CIE luminance for the RGB
|
||||||
|
var v = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||||
|
dst[i] = dst[i + 1] = dst[i + 2] = v;
|
||||||
|
dst[i + 3] = d[i + 3];
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
Filters.runPipeline = function (ds) {
|
||||||
|
var res = null;
|
||||||
|
res = this[ds[0].name].apply(this, ds[0].args);
|
||||||
|
for (var i = 1; i < ds.length; i++) {
|
||||||
|
var d = ds[i];
|
||||||
|
var args = d.args.slice(0);
|
||||||
|
args.unshift(res);
|
||||||
|
res = this[d.name].apply(this, args);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Filters.identity = function (pixels, args) {
|
||||||
|
var output = Filters.createImageData(pixels.width, pixels.height);
|
||||||
|
var dst = output.data;
|
||||||
|
var d = pixels.data;
|
||||||
|
for (var i = 0; i < d.length; i++) {
|
||||||
|
dst[i] = d[i];
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Filters.horizontalConvolve = function (pixels, weightsVector, opaque) {
|
||||||
|
var side = weightsVector.length;
|
||||||
|
var halfSide = Math.floor(side / 2);
|
||||||
|
|
||||||
|
var src = pixels.data;
|
||||||
|
var sw = pixels.width;
|
||||||
|
var sh = pixels.height;
|
||||||
|
|
||||||
|
var w = sw;
|
||||||
|
var h = sh;
|
||||||
|
var output = Filters.createImageData(w, h);
|
||||||
|
var dst = output.data;
|
||||||
|
|
||||||
|
var alphaFac = opaque ? 1 : 0;
|
||||||
|
|
||||||
|
for (var y = 0; y < h; y++) {
|
||||||
|
for (var x = 0; x < w; x++) {
|
||||||
|
var sy = y;
|
||||||
|
var sx = x;
|
||||||
|
var dstOff = (y * w + x) * 4;
|
||||||
|
var r = 0,
|
||||||
|
g = 0,
|
||||||
|
b = 0,
|
||||||
|
a = 0;
|
||||||
|
for (var cx = 0; cx < side; cx++) {
|
||||||
|
var scy = sy;
|
||||||
|
var scx = Math.min(sw - 1, Math.max(0, sx + cx - halfSide));
|
||||||
|
var srcOff = (scy * sw + scx) * 4;
|
||||||
|
var wt = weightsVector[cx];
|
||||||
|
r += src[srcOff] * wt;
|
||||||
|
g += src[srcOff + 1] * wt;
|
||||||
|
b += src[srcOff + 2] * wt;
|
||||||
|
a += src[srcOff + 3] * wt;
|
||||||
|
}
|
||||||
|
dst[dstOff] = r;
|
||||||
|
dst[dstOff + 1] = g;
|
||||||
|
dst[dstOff + 2] = b;
|
||||||
|
dst[dstOff + 3] = a + alphaFac * (255 - a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
Filters.separableConvolve = function (
|
||||||
|
pixels,
|
||||||
|
horizWeights,
|
||||||
|
vertWeights,
|
||||||
|
opaque
|
||||||
|
) {
|
||||||
|
return this.horizontalConvolve(
|
||||||
|
this.verticalConvolveFloat32(pixels, vertWeights, opaque),
|
||||||
|
horizWeights,
|
||||||
|
opaque
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Filters.gaussianBlur = function (pixels, diameter) { // good
|
||||||
|
diameter = Math.abs(diameter);
|
||||||
|
if (diameter <= 1) return Filters.identity(pixels);
|
||||||
|
var radius = diameter / 2;
|
||||||
|
var len = Math.ceil(diameter) + (1 - (Math.ceil(diameter) % 2));
|
||||||
|
var weights = this.getFloat32Array(len);
|
||||||
|
var rho = (radius + 0.5) / 3;
|
||||||
|
var rhoSq = rho * rho;
|
||||||
|
var gaussianFactor = 1 / Math.sqrt(2 * Math.PI * rhoSq);
|
||||||
|
var rhoFactor = -1 / (2 * rho * rho);
|
||||||
|
var wsum = 0;
|
||||||
|
var middle = Math.floor(len / 2);
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
var x = i - middle;
|
||||||
|
var gx = gaussianFactor * Math.exp(x * x * rhoFactor);
|
||||||
|
weights[i] = gx;
|
||||||
|
wsum += gx;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < weights.length; i++) {
|
||||||
|
weights[i] /= wsum;
|
||||||
|
}
|
||||||
|
return Filters.separableConvolve(pixels, weights, weights, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Filters.verticalConvolveFloat32 = function (pixels, weightsVector, opaque) {
|
||||||
|
var side = weightsVector.length;
|
||||||
|
var halfSide = Math.floor(side / 2);
|
||||||
|
|
||||||
|
var src = pixels.data;
|
||||||
|
var sw = pixels.width;
|
||||||
|
var sh = pixels.height;
|
||||||
|
|
||||||
|
var w = sw;
|
||||||
|
var h = sh;
|
||||||
|
var output = { width: w, height: h, data: this.getFloat32Array(w * h * 4) };
|
||||||
|
var dst = output.data;
|
||||||
|
|
||||||
|
var alphaFac = opaque ? 1 : 0;
|
||||||
|
|
||||||
|
for (var y = 0; y < h; y++) {
|
||||||
|
for (var x = 0; x < w; x++) {
|
||||||
|
var sy = y;
|
||||||
|
var sx = x;
|
||||||
|
var dstOff = (y * w + x) * 4;
|
||||||
|
var r = 0,
|
||||||
|
g = 0,
|
||||||
|
b = 0,
|
||||||
|
a = 0;
|
||||||
|
for (var cy = 0; cy < side; cy++) {
|
||||||
|
var scy = Math.min(sh - 1, Math.max(0, sy + cy - halfSide));
|
||||||
|
var scx = sx;
|
||||||
|
var srcOff = (scy * sw + scx) * 4;
|
||||||
|
var wt = weightsVector[cy];
|
||||||
|
r += src[srcOff] * wt;
|
||||||
|
g += src[srcOff + 1] * wt;
|
||||||
|
b += src[srcOff + 2] * wt;
|
||||||
|
a += src[srcOff + 3] * wt;
|
||||||
|
}
|
||||||
|
dst[dstOff] = r;
|
||||||
|
dst[dstOff + 1] = g;
|
||||||
|
dst[dstOff + 2] = b;
|
||||||
|
dst[dstOff + 3] = a + alphaFac * (255 - a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Filters;
|
||||||
11
thirdparty/focus_worker.js
vendored
Normal file
11
thirdparty/focus_worker.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Part of Inspector Bokeh by @timotgl
|
||||||
|
// MIT License - Copyright (c) 2016 Timo Taglieber <github@timotaglieber.de>
|
||||||
|
// https://github.com/timotgl/inspector-bokeh
|
||||||
|
|
||||||
|
import measureBlur from './measureBlur.js';
|
||||||
|
|
||||||
|
onmessage = (messageEvent) => {
|
||||||
|
postMessage({
|
||||||
|
score: measureBlur(messageEvent.data.imageData),
|
||||||
|
});
|
||||||
|
};
|
||||||
124
thirdparty/measureBlur.js
vendored
Normal file
124
thirdparty/measureBlur.js
vendored
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Inspector Bokeh by @timotgl
|
||||||
|
// MIT License - Copyright (c) 2016 Timo Taglieber <github@timotaglieber.de>
|
||||||
|
// https://github.com/timotgl/inspector-bokeh
|
||||||
|
|
||||||
|
// This is just a copy of ../src/measureBlur.js that has been edited
|
||||||
|
// to assume that canvasFilters is already an ES module
|
||||||
|
// TODO: solve with bundling somehow
|
||||||
|
|
||||||
|
import Filters from './canvasFilters.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I forgot why exactly I was doing this.
|
||||||
|
* It somehow improves edge detection to blur the image a bit beforehand.
|
||||||
|
* But we don't want to do this for very small images.
|
||||||
|
*/
|
||||||
|
const BLUR_BEFORE_EDGE_DETECTION_MIN_WIDTH = 360; // pixels
|
||||||
|
const BLUR_BEFORE_EDGE_DETECTION_DIAMETER = 5.0; // pixels
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only count edges that reach a certain intensity.
|
||||||
|
* I forgot which unit this was. But it's not pixels.
|
||||||
|
*/
|
||||||
|
const MIN_EDGE_INTENSITY = 20;
|
||||||
|
|
||||||
|
const detectEdges = (imageData) => {
|
||||||
|
const preBlurredImageData =
|
||||||
|
imageData.width >= BLUR_BEFORE_EDGE_DETECTION_MIN_WIDTH
|
||||||
|
? Filters.gaussianBlur(imageData, BLUR_BEFORE_EDGE_DETECTION_DIAMETER)
|
||||||
|
: imageData;
|
||||||
|
|
||||||
|
const greyscaled = Filters.luminance(preBlurredImageData);
|
||||||
|
const sobelKernel = Filters.getFloat32Array([1, 0, -1, 2, 0, -2, 1, 0, -1]);
|
||||||
|
return Filters.convolve(greyscaled, sobelKernel, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce imageData from RGBA to only one channel (Y/luminance after conversion
|
||||||
|
* to greyscale) since RGB all have the same values and Alpha was ignored.
|
||||||
|
*/
|
||||||
|
const reducedPixels = (imageData) => {
|
||||||
|
const { data: pixels, width } = imageData;
|
||||||
|
const rowLen = width * 4;
|
||||||
|
let i,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
row,
|
||||||
|
rows = [];
|
||||||
|
|
||||||
|
for (y = 0; y < pixels.length; y += rowLen) {
|
||||||
|
row = new Uint8ClampedArray(imageData.width);
|
||||||
|
x = 0;
|
||||||
|
for (i = y; i < y + rowLen; i += 4) {
|
||||||
|
row[x] = pixels[i];
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param pixels Array of Uint8ClampedArrays (row in original image)
|
||||||
|
*/
|
||||||
|
const detectBlur = (pixels) => {
|
||||||
|
const width = pixels[0].length;
|
||||||
|
const height = pixels.length;
|
||||||
|
|
||||||
|
let x,
|
||||||
|
y,
|
||||||
|
value,
|
||||||
|
oldValue,
|
||||||
|
edgeStart,
|
||||||
|
edgeWidth,
|
||||||
|
bm,
|
||||||
|
percWidth,
|
||||||
|
numEdges = 0,
|
||||||
|
sumEdgeWidths = 0;
|
||||||
|
|
||||||
|
for (y = 0; y < height; y += 1) {
|
||||||
|
// Reset edge marker, none found yet
|
||||||
|
edgeStart = -1;
|
||||||
|
for (x = 0; x < width; x += 1) {
|
||||||
|
value = pixels[y][x];
|
||||||
|
// Edge is still open
|
||||||
|
if (edgeStart >= 0 && x > edgeStart) {
|
||||||
|
oldValue = pixels[y][x - 1];
|
||||||
|
// Value stopped increasing => edge ended
|
||||||
|
if (value < oldValue) {
|
||||||
|
// Only count edges that reach a certain intensity
|
||||||
|
if (oldValue >= MIN_EDGE_INTENSITY) {
|
||||||
|
edgeWidth = x - edgeStart - 1;
|
||||||
|
numEdges += 1;
|
||||||
|
sumEdgeWidths += edgeWidth;
|
||||||
|
}
|
||||||
|
edgeStart = -1; // Reset edge marker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Edge starts
|
||||||
|
if (value == 0) {
|
||||||
|
edgeStart = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numEdges === 0) {
|
||||||
|
bm = 0;
|
||||||
|
percWidth = 0;
|
||||||
|
} else {
|
||||||
|
bm = sumEdgeWidths / numEdges;
|
||||||
|
percWidth = (bm / width) * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
num_edges: numEdges,
|
||||||
|
avg_edge_width: bm,
|
||||||
|
avg_edge_width_perc: percWidth,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const measureBlur = (imageData) => detectBlur(reducedPixels(detectEdges(imageData)));
|
||||||
|
|
||||||
|
export default measureBlur;
|
||||||
Reference in New Issue
Block a user