@beenotung/tslib
Version:
utils library in Typescript
390 lines • 13.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.compressMobilePhoto = exports.toImage = exports.compressImageToBlob = exports.canvasToBlob = exports.compressImageToBase64 = exports.compressImage = exports.dataURItoBlob = exports.dataURItoMimeType = exports.flipImage = exports.rotateImage = exports.transformCentered = exports.resizeImage = exports.resizeBase64WithRatio = exports.resizeWithRatio = exports.ResizeType = exports.getWidthHeightFromBase64 = exports.resizeBase64Image = exports.base64ToCanvas = exports.checkBase64ImagePrefix = exports.base64ToImage = exports.imageToBase64 = exports.imageToCanvas = void 0;
const tslib_1 = require("tslib");
const enum_1 = require("./enum");
const file_1 = require("./file");
const result_1 = require("./result");
const size_1 = require("./size");
/**
* reference : https://stackoverflow.com/questions/20958078/resize-a-base-64-image-in-javascript-without-using-canvas
* */
function imageToCanvas(img, width, height) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx === null) {
throw new Error('unsupported');
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
return canvas;
}
exports.imageToCanvas = imageToCanvas;
function imageToBase64(img, width, height) {
return imageToCanvas(img, width, height).toDataURL();
}
exports.imageToBase64 = imageToBase64;
function base64ToImage(data) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(image);
image.onerror = e => reject(e);
image.src = data;
});
});
}
exports.base64ToImage = base64ToImage;
/**
* TODO check if there are exceptions
* */
function checkBase64ImagePrefix(s) {
return typeof s === 'string' && s.startsWith('/9j/')
? 'data:image/jpeg;base64,' + s
: s;
}
exports.checkBase64ImagePrefix = checkBase64ImagePrefix;
/**
* data type conversion
* also work for resizing
* FIXME wrap width and height into options object
* */
function base64ToCanvas(data, width, height) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const image = yield base64ToImage(data);
let w;
let h;
if (width && height) {
w = width;
h = height;
}
else if (!width && !height) {
w = image.naturalWidth;
h = image.naturalHeight;
}
else if (width) {
// height is not defined
w = width;
h = (image.naturalHeight / image.naturalWidth) * width;
}
else if (height) {
// width is not defined
w = (image.naturalWidth / image.naturalHeight) * height;
h = height;
}
else {
throw new Error('logic error, missing edge case:' + JSON.stringify({ width, height }));
}
return imageToCanvas(image, w, h);
});
}
exports.base64ToCanvas = base64ToCanvas;
function resizeBase64Image(data, targetWidth, targetHeight) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (yield base64ToCanvas(data, targetWidth, targetHeight)).toDataURL();
});
}
exports.resizeBase64Image = resizeBase64Image;
function getWidthHeightFromBase64(data) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const image = yield base64ToImage(data);
return {
width: image.naturalWidth,
height: image.naturalHeight,
};
});
}
exports.getWidthHeightFromBase64 = getWidthHeightFromBase64;
var ResizeType;
(function (ResizeType) {
/* with-in the given area, maybe smaller */
ResizeType[ResizeType["with_in"] = 0] = "with_in";
/* at least as large as the given area, maybe larger */
ResizeType[ResizeType["at_least"] = 1] = "at_least";
})(ResizeType = exports.ResizeType || (exports.ResizeType = {}));
enum_1.enum_only_string(ResizeType);
function resizeWithRatio(oriSize, targetSize, mode) {
const widthRate = targetSize.width / oriSize.width;
const heightRate = targetSize.height / oriSize.height;
let rate;
switch (mode) {
case ResizeType.with_in:
rate = Math.min(widthRate, heightRate);
break;
case ResizeType.at_least:
rate = Math.max(widthRate, heightRate);
break;
default:
throw new TypeError(`unsupported type: ${mode}`);
}
return {
width: oriSize.width * rate,
height: oriSize.height * rate,
};
}
exports.resizeWithRatio = resizeWithRatio;
function resizeBase64WithRatio(data, preferredSize, mode) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const image = yield base64ToImage(data);
const targetSize = resizeWithRatio({
width: image.naturalWidth,
height: image.naturalHeight,
}, preferredSize, mode);
return imageToBase64(image, targetSize.width, targetSize.height);
});
}
exports.resizeBase64WithRatio = resizeBase64WithRatio;
// reference: image-file-to-base64-exif
function getNewScale(image, maxWidth, maxHeight) {
if (image.width <= maxWidth && image.height <= maxHeight) {
return 1;
}
if (image.width > image.height) {
return image.width / maxWidth;
}
else {
return image.height / maxHeight;
}
}
function resizeImage(image, maxWidth = image.width, maxHeight = image.height, mimeType, quality) {
const scale = getNewScale(image, maxWidth, maxHeight);
const scaledWidth = image.width / scale;
const scaledHeight = image.height / scale;
const canvas = document.createElement('canvas');
canvas.width = scaledWidth;
canvas.height = scaledHeight;
const context = canvas.getContext('2d');
if (context === null) {
throw new Error('not supported');
}
context.drawImage(image, 0, 0, scaledWidth, scaledHeight);
if (mimeType) {
return canvas.toDataURL(mimeType, quality || 1);
}
else {
return canvas.toDataURL();
}
}
exports.resizeImage = resizeImage;
function transformCentered(image, flipXY, f) {
const canvas = document.createElement('canvas');
canvas.width = flipXY ? image.height : image.width;
canvas.height = flipXY ? image.width : image.height;
const ctx = canvas.getContext('2d');
if (ctx === null) {
throw new Error('not supported');
}
ctx.translate(canvas.width * 0.5, canvas.height * 0.5);
f(ctx);
ctx.translate(-image.width * 0.5, -image.height * 0.5);
ctx.drawImage(image, 0, 0);
// return canvas.toDataURL();
return canvas;
}
exports.transformCentered = transformCentered;
function rotateImage(image) {
return transformCentered(image, true, ctx => ctx.rotate(0.5 * Math.PI));
}
exports.rotateImage = rotateImage;
function flipImage(image) {
return transformCentered(image, false, ctx => ctx.scale(-1, 1));
}
exports.flipImage = flipImage;
/**
* extract mime type from base64/URLEncoded data component
* e.g. data:image/jpeg;base64,... -> image/jpeg
* */
function dataURItoMimeType(dataURI) {
const idx = dataURI.indexOf(',');
if (idx === -1) {
throw new Error('data uri prefix not found');
}
const prefix = dataURI.substr(0, idx);
const [mimeType] = prefix.replace(/^data:/, '').split(';');
return mimeType;
}
exports.dataURItoMimeType = dataURItoMimeType;
/**
* convert base64/URLEncoded data component to raw binary data held in a string
* e.g. data:image/jpeg;base64,...
* */
function dataURItoBlob(dataURI) {
const [format, payload] = dataURI.split(',');
const [mimeType /*, encodeType*/] = format.replace(/^data:/, '').split(';');
let byteString;
if (dataURI.startsWith('data:')) {
byteString = atob(payload);
}
else {
byteString = unescape(payload);
}
const n = byteString.length;
const buffer = new Uint8Array(n);
for (let i = 0; i < n; i++) {
buffer[i] = byteString.charCodeAt(i);
}
return new Blob([buffer], { type: mimeType });
}
exports.dataURItoBlob = dataURItoBlob;
/**@deprecated use compressImageToBase64() compressImageToBlob() instead */
function compressImage(image, mimeType, quality = 0.8) {
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext('2d');
if (ctx === null) {
throw new Error('not supported');
}
ctx.drawImage(image, 0, 0);
if (mimeType) {
return canvas.toDataURL(mimeType, quality);
}
const png = canvas.toDataURL('image/png', quality);
const jpeg = canvas.toDataURL('image/jpeg', quality);
return jpeg.length < png.length ? jpeg : png;
}
exports.compressImage = compressImage;
function populateCompressArgs(args) {
const image = args.image;
const canvas = args.canvas || document.createElement('canvas');
const ctx = args.ctx ||
canvas.getContext('2d') ||
(() => {
throw new Error('not supported');
})();
let maximumSize = args.maximumSize;
let quality = args.quality;
if (!maximumSize && !quality) {
maximumSize = 768 * size_1.KB; // 768KB
quality = 0.8;
}
return {
image,
canvas,
ctx,
maximumSize,
quality,
};
}
function compressImageToBase64(args) {
const { image, canvas, ctx, maximumSize, quality } = populateCompressArgs(Object.assign(Object.assign({}, args), { maximumSize: args.maximumLength }));
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
let mimeType;
let dataURL;
if (args.mimeType) {
mimeType = args.mimeType;
dataURL = canvas.toDataURL(mimeType, quality);
}
else {
const png = canvas.toDataURL('image/png', quality);
const jpeg = canvas.toDataURL('image/jpeg', quality);
if (jpeg < png) {
mimeType = 'image/jpeg';
dataURL = jpeg;
}
else {
mimeType = 'image/png';
dataURL = png;
}
}
if (!maximumSize) {
return dataURL;
}
for (; dataURL.length > maximumSize;) {
const ratio = Math.sqrt(maximumSize / dataURL.length);
const new_width = Math.round(canvas.width * ratio);
const new_height = Math.round(canvas.height * ratio);
if (new_width === canvas.width && new_height === canvas.height) {
break;
}
canvas.width = new_width;
canvas.height = new_height;
ctx.drawImage(image, 0, 0, new_width, new_height);
dataURL = canvas.toDataURL(mimeType, quality);
}
return dataURL;
}
exports.compressImageToBase64 = compressImageToBase64;
function canvasToBlob(canvas, mimeType, quality) {
return new Promise((resolve, reject) => canvas.toBlob(blob => {
if (blob) {
resolve(blob);
}
else {
reject('not supported');
}
}, mimeType, quality));
}
exports.canvasToBlob = canvasToBlob;
function compressImageToBlob(args) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const { image, canvas, ctx, maximumSize, quality } = populateCompressArgs(args);
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
let mimeType;
let blob;
if (args.mimeType) {
mimeType = args.mimeType;
blob = yield canvasToBlob(canvas, mimeType, quality);
}
else {
const [png, jpeg] = yield Promise.all([
canvasToBlob(canvas, 'image/png', quality),
canvasToBlob(canvas, 'image/jpeg', quality),
]);
if (jpeg.size < png.size) {
mimeType = 'image/jpeg';
blob = jpeg;
}
else {
mimeType = 'image/png';
blob = png;
}
}
if (!maximumSize) {
return blob;
}
for (; blob.size > maximumSize;) {
const ratio = Math.sqrt(maximumSize / blob.size);
const new_width = Math.round(canvas.width * ratio);
const new_height = Math.round(canvas.height * ratio);
if (new_width === canvas.width && new_height === canvas.height) {
break;
}
canvas.width = new_width;
canvas.height = new_height;
ctx.drawImage(image, 0, 0, new_width, new_height);
blob = yield canvasToBlob(canvas, mimeType, quality);
}
return blob;
});
}
exports.compressImageToBlob = compressImageToBlob;
function toImage(image) {
if (typeof image === 'string') {
// base64
return base64ToImage(image);
}
if (image instanceof File) {
return file_1.fileToBase64String(image).then(base64 => toImage(base64));
}
if (image instanceof HTMLImageElement) {
return image;
}
console.error('unknown image type:', image);
throw new TypeError('unknown image type');
}
exports.toImage = toImage;
const DefaultMaximumMobilePhotoSize = 300 * size_1.KB; // 300KB
function compressMobilePhoto(args) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const maximumLength = args.maximumSize || DefaultMaximumMobilePhotoSize;
return result_1.then(toImage(args.image), image => compressImageToBase64({ image, maximumLength }));
});
}
exports.compressMobilePhoto = compressMobilePhoto;
//# sourceMappingURL=image.js.map