// Resize convolvers, pure JS implementation // 'use strict'; // Precision of fixed FP values //var FIXED_FRAC_BITS = 14; function clampTo8(i) { return i < 0 ? 0 : (i > 255 ? 255 : i); } // Convolve image in horizontal directions and transpose output. In theory, // transpose allow: // // - use the same convolver for both passes (this fails due different // types of input array and temporary buffer) // - making vertical pass by horisonltal lines inprove CPU cache use. // // But in real life this doesn't work :) // function convolveHorizontally(src, dest, srcW, srcH, destW, filters) { var r, g, b, a; var filterPtr, filterShift, filterSize; var srcPtr, srcY, destX, filterVal; var srcOffset = 0, destOffset = 0; // For each row for (srcY = 0; srcY < srcH; srcY++) { filterPtr = 0; // Apply precomputed filters to each destination row point for (destX = 0; destX < destW; destX++) { // Get the filter that determines the current output pixel. filterShift = filters[filterPtr++]; filterSize = filters[filterPtr++]; srcPtr = (srcOffset + (filterShift * 4))|0; r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a for (; filterSize > 0; filterSize--) { filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10) // Big thanks to @mraleph (Vyacheslav Egorov) for the tip. a = (a + filterVal * src[srcPtr + 3])|0; b = (b + filterVal * src[srcPtr + 2])|0; g = (g + filterVal * src[srcPtr + 1])|0; r = (r + filterVal * src[srcPtr])|0; srcPtr = (srcPtr + 4)|0; } // Bring this value back in range. All of the filter scaling factors // are in fixed point with FIXED_FRAC_BITS bits of fractional part. // // (!) Add 1/2 of value before clamping to get proper rounding. In other // case brightness loss will be noticeable if you resize image with white // border and place it on white background. // dest[destOffset + 3] = clampTo8((a + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); dest[destOffset + 2] = clampTo8((b + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); dest[destOffset + 1] = clampTo8((g + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); dest[destOffset] = clampTo8((r + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); destOffset = (destOffset + srcH * 4)|0; } destOffset = ((srcY + 1) * 4)|0; srcOffset = ((srcY + 1) * srcW * 4)|0; } } // Technically, convolvers are the same. But input array and temporary // buffer can be of different type (especially, in old browsers). So, // keep code in separate functions to avoid deoptimizations & speed loss. function convolveVertically(src, dest, srcW, srcH, destW, filters) { var r, g, b, a; var filterPtr, filterShift, filterSize; var srcPtr, srcY, destX, filterVal; var srcOffset = 0, destOffset = 0; // For each row for (srcY = 0; srcY < srcH; srcY++) { filterPtr = 0; // Apply precomputed filters to each destination row point for (destX = 0; destX < destW; destX++) { // Get the filter that determines the current output pixel. filterShift = filters[filterPtr++]; filterSize = filters[filterPtr++]; srcPtr = (srcOffset + (filterShift * 4))|0; r = g = b = a = 0; // Apply the filter to the row to get the destination pixel r, g, b, a for (; filterSize > 0; filterSize--) { filterVal = filters[filterPtr++]; // Use reverse order to workaround deopts in old v8 (node v.10) // Big thanks to @mraleph (Vyacheslav Egorov) for the tip. a = (a + filterVal * src[srcPtr + 3])|0; b = (b + filterVal * src[srcPtr + 2])|0; g = (g + filterVal * src[srcPtr + 1])|0; r = (r + filterVal * src[srcPtr])|0; srcPtr = (srcPtr + 4)|0; } // Bring this value back in range. All of the filter scaling factors // are in fixed point with FIXED_FRAC_BITS bits of fractional part. // // (!) Add 1/2 of value before clamping to get proper rounding. In other // case brightness loss will be noticeable if you resize image with white // border and place it on white background. // dest[destOffset + 3] = clampTo8((a + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); dest[destOffset + 2] = clampTo8((b + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); dest[destOffset + 1] = clampTo8((g + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); dest[destOffset] = clampTo8((r + (1 << 13)) >> 14/*FIXED_FRAC_BITS*/); destOffset = (destOffset + srcH * 4)|0; } destOffset = ((srcY + 1) * 4)|0; srcOffset = ((srcY + 1) * srcW * 4)|0; } } module.exports = { convolveHorizontally, convolveVertically };