Test/node_modules/pica/lib/utils.js
2026-04-09 22:54:00 +07:00

203 lines
5.9 KiB
JavaScript

'use strict';
function objClass(obj) { return Object.prototype.toString.call(obj); }
module.exports.isCanvas = function isCanvas(element) {
let cname = objClass(element);
return cname === '[object HTMLCanvasElement]'/* browser */ ||
cname === '[object OffscreenCanvas]' ||
cname === '[object Canvas]'/* node-canvas */;
};
module.exports.isImage = function isImage(element) {
return objClass(element) === '[object HTMLImageElement]';
};
module.exports.isImageBitmap = function isImageBitmap(element) {
return objClass(element) === '[object ImageBitmap]';
};
module.exports.limiter = function limiter(concurrency) {
let active = 0,
queue = [];
function roll() {
if (active < concurrency && queue.length) {
active++;
queue.shift()();
}
}
return function limit(fn) {
return new Promise((resolve, reject) => {
queue.push(() => {
fn().then(
result => {
resolve(result);
active--;
roll();
},
err => {
reject(err);
active--;
roll();
}
);
});
roll();
});
};
};
module.exports.cib_quality_name = function cib_quality_name(num) {
switch (num) {
case 0: return 'pixelated';
case 1: return 'low';
case 2: return 'medium';
}
return 'high';
};
module.exports.cib_support = function cib_support(createCanvas) {
return Promise.resolve().then(() => {
if (typeof createImageBitmap === 'undefined') {
return false;
}
let c = createCanvas(100, 100);
return createImageBitmap(c, 0, 0, 100, 100, {
resizeWidth: 10,
resizeHeight: 10,
resizeQuality: 'high'
})
.then(bitmap => {
let status = (bitmap.width === 10);
// Branch below is filtered on upper level. We do not call resize
// detection for basic ImageBitmap.
//
// https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap
// old Crome 51 has ImageBitmap without .close(). Then this code
// will throw and return 'false' as expected.
//
bitmap.close();
c = null;
return status;
});
})
.catch(() => false);
};
module.exports.worker_offscreen_canvas_support = function worker_offscreen_canvas_support() {
return new Promise((resolve, reject) => {
if (typeof OffscreenCanvas === 'undefined') {
// if OffscreenCanvas is present, we assume browser supports Worker and built-in Promise as well
resolve(false);
return;
}
function workerPayload(self) {
if (typeof createImageBitmap === 'undefined') {
self.postMessage(false);
return;
}
Promise.resolve()
.then(() => {
let canvas = new OffscreenCanvas(10, 10);
// test that 2d context can be used in worker
let ctx = canvas.getContext('2d');
ctx.rect(0, 0, 1, 1);
// test that cib can be used to return image bitmap from worker
return createImageBitmap(canvas, 0, 0, 1, 1);
})
.then(
() => self.postMessage(true),
() => self.postMessage(false)
);
}
let code = btoa(`(${workerPayload.toString()})(self);`);
let w = new Worker(`data:text/javascript;base64,${code}`);
w.onmessage = ev => resolve(ev.data);
w.onerror = reject;
}).then(result => result, () => false);
};
// Check if canvas.getContext('2d').getImageData can be used,
// FireFox randomizes the output of that function in `privacy.resistFingerprinting` mode
module.exports.can_use_canvas = function can_use_canvas(createCanvas) {
let usable = false;
try {
let canvas = createCanvas(2, 1);
let ctx = canvas.getContext('2d');
let d = ctx.createImageData(2, 1);
d.data[0] = 12; d.data[1] = 23; d.data[2] = 34; d.data[3] = 255;
d.data[4] = 45; d.data[5] = 56; d.data[6] = 67; d.data[7] = 255;
ctx.putImageData(d, 0, 0);
d = null;
d = ctx.getImageData(0, 0, 2, 1);
if (d.data[0] === 12 && d.data[1] === 23 && d.data[2] === 34 && d.data[3] === 255 &&
d.data[4] === 45 && d.data[5] === 56 && d.data[6] === 67 && d.data[7] === 255) {
usable = true;
}
} catch (err) {}
return usable;
};
// Check if createImageBitmap(img, sx, sy, sw, sh) signature works correctly
// with JPEG images oriented with Exif;
// https://bugs.chromium.org/p/chromium/issues/detail?id=1220671
// TODO: remove after it's fixed in chrome for at least 2 releases
module.exports.cib_can_use_region = function cib_can_use_region() {
return new Promise(resolve => {
if (typeof createImageBitmap === 'undefined') {
resolve(false);
return;
}
let image = new Image();
image.src = 'data:image/jpeg;base64,' +
'/9j/4QBiRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAYAAAEaAAUAAAABAAAASgEbAAUAA' +
'AABAAAAUgEoAAMAAAABAAIAAAITAAMAAAABAAEAAAAAAAAAAABIAAAAAQAAAEgAAAAB/9' +
'sAQwAEAwMEAwMEBAMEBQQEBQYKBwYGBgYNCQoICg8NEBAPDQ8OERMYFBESFxIODxUcFRc' +
'ZGRsbGxAUHR8dGh8YGhsa/9sAQwEEBQUGBQYMBwcMGhEPERoaGhoaGhoaGhoaGhoaGhoa' +
'GhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoa/8IAEQgAAQACAwERAAIRAQMRA' +
'f/EABQAAQAAAAAAAAAAAAAAAAAAAAf/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAA' +
'IQAxAAAAF/P//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAA' +
'AAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIB' +
'AT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAA' +
'AAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQH//EABQRAQAAAAAAAAAAAAAAAA' +
'AAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQ' +
'QAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z';
image.onload = () => {
createImageBitmap(image, 0, 0, image.width, image.height).then(bitmap => {
if (bitmap.width === image.width && bitmap.height === image.height) {
resolve(true);
} else {
resolve(false);
}
}, () => resolve(false));
};
image.onerror = () => resolve(false);
});
};