Add files via upload

This commit is contained in:
Steve Seguin 2022-07-26 08:10:50 -04:00 committed by GitHub
parent 0081e0ec39
commit 29e63296ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 514 additions and 122 deletions

View File

@ -2,135 +2,34 @@
/* global chrome location ReadableStream define MessageChannel TransformStream */
;((name, definition) => {
typeof module !== 'undefined'
? module.exports = definition()
: typeof define === 'function' && typeof define.amd === 'object'
? define(definition)
: this[name] = definition()
})('streamSaver', () => {
function streamSaverFunction(){
'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')
let mitmTransporter = null
let supportsTransferable = false
const test = fn => { try { fn() } catch (e) {} }
const ponyfill = global.WebStreamsPolyfill || {}
const isSecureContext = global.isSecureContext
let mitmTransporter = null;
let supportsTransferable = false;
const test = fn => { try { fn() } catch (e) {} };
const ponyfill = global.WebStreamsPolyfill || {};
const isSecureContext = global.isSecureContext;
//console.log(ponyfill);
//console.log(isSecureContext);
// 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
? 'iframe'
: 'navigate'
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'
}
/**
* 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>}
*/
: 'navigate';
//console.log(downloadStrategy);
function createWriteStream (filename, stopStream){
//console.log("createWriteStream");
let opts = {
size: null,
pathname: null,
@ -200,6 +99,7 @@
}
channel.port1.onmessage = evt => {
console.log(evt);
// Service worker sent us a link that we should open.
if (evt.data.download) {
// Special treatment for popup...
@ -309,5 +209,109 @@
}, 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
})
};
var streamSaver = streamSaverFunction();

253
thirdparty/canvasFilters.js vendored Normal file
View 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
View 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
View 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;