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

136 lines
4.7 KiB
JavaScript

// 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
};