import { ActionResize } from '../../ImageManipulator.types'; import { getContext } from '../utils.web'; /** * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version! * https://stackoverflow.com/a/18320662/4047926 * * @param {HTMLCanvasElement} canvas * @param {int} width * @param {int} height * @param {boolean} resizeCanvas if true, canvas will be resized. Optional. */ function resampleSingle( canvas: HTMLCanvasElement, width: number, height: number, resizeCanvas: boolean = false ): HTMLCanvasElement { const result = document.createElement('canvas'); result.width = canvas.width; result.height = canvas.height; const widthSource = canvas.width; const heightSource = canvas.height; width = Math.round(width); height = Math.round(height); const wRatio = widthSource / width; const hRatio = heightSource / height; const wRatioHalf = Math.ceil(wRatio / 2); const hRatioHalf = Math.ceil(hRatio / 2); const ctx = getContext(canvas); const img = ctx.getImageData(0, 0, widthSource, heightSource); const img2 = ctx.createImageData(width, height); const data = img.data; const data2 = img2.data; for (let j = 0; j < height; j++) { for (let i = 0; i < width; i++) { const x2 = (i + j * width) * 4; let weight = 0; let weights = 0; let weightsAlpha = 0; let gx_r = 0; let gx_g = 0; let gx_b = 0; let gx_a = 0; const yCenter = (j + 0.5) * hRatio; const yy_start = Math.floor(j * hRatio); const yy_stop = Math.ceil((j + 1) * hRatio); for (let yy = yy_start; yy < yy_stop; yy++) { const dy = Math.abs(yCenter - (yy + 0.5)) / hRatioHalf; const center_x = (i + 0.5) * wRatio; const w0 = dy * dy; //pre-calc part of w const xx_start = Math.floor(i * wRatio); const xx_stop = Math.ceil((i + 1) * wRatio); for (let xx = xx_start; xx < xx_stop; xx++) { const dx = Math.abs(center_x - (xx + 0.5)) / wRatioHalf; const w = Math.sqrt(w0 + dx * dx); if (w >= 1) { //pixel too far continue; } //hermite filter weight = 2 * w * w * w - 3 * w * w + 1; const xPosition = 4 * (xx + yy * widthSource); //alpha gx_a += weight * data[xPosition + 3]; weightsAlpha += weight; //colors if (data[xPosition + 3] < 255) { weight = (weight * data[xPosition + 3]) / 250; } gx_r += weight * data[xPosition]; gx_g += weight * data[xPosition + 1]; gx_b += weight * data[xPosition + 2]; weights += weight; } } data2[x2] = gx_r / weights; data2[x2 + 1] = gx_g / weights; data2[x2 + 2] = gx_b / weights; data2[x2 + 3] = gx_a / weightsAlpha; } } //resize canvas if (resizeCanvas) { result.width = width; result.height = height; } //draw const context = getContext(result); context.putImageData(img2, 0, 0); return result; } export default (canvas: HTMLCanvasElement, { width, height }: ActionResize['resize']) => { const imageRatio = canvas.width / canvas.height; let requestedWidth: number = 0; let requestedHeight: number = 0; if (width !== undefined) { requestedWidth = width; requestedHeight = requestedWidth / imageRatio; } if (height !== undefined) { requestedHeight = height; if (requestedWidth === 0) { requestedWidth = requestedHeight * imageRatio; } } return resampleSingle(canvas, requestedWidth, requestedHeight, true); };