UNPKG

@beenotung/tslib

Version:
390 lines 13.8 kB
"use strict"; 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