Refactor component foreach loop of bilinear scaler

This commit is contained in:
Elias Batek 2025-02-01 07:30:21 +01:00
parent 2804f426c4
commit c3beff155c
1 changed files with 113 additions and 66 deletions

View File

@ -3041,6 +3041,29 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
static if ((filter == ScalingFilter.bilinear) || (filter == ScalingFilter.fauxLinear)) { static if ((filter == ScalingFilter.bilinear) || (filter == ScalingFilter.fauxLinear)) {
void scaleToLinearImpl(ScalingDirection directionX, ScalingDirection directionY)() { void scaleToLinearImpl(ScalingDirection directionX, ScalingDirection directionY)() {
alias InterPixel = ulong[4];
static Pixel toPixel(const InterPixel ipx) @safe pure nothrow @nogc {
pragma(inline, true);
return Pixel(
clamp255(ipx[0]),
clamp255(ipx[1]),
clamp255(ipx[2]),
clamp255(ipx[3]),
);
}
static InterPixel toInterPixel(const Pixel ipx) @safe pure nothrow @nogc {
pragma(inline, true);
InterPixel result = [
ipx.r,
ipx.g,
ipx.b,
ipx.a,
];
return result;
}
int[2] posSrcCenterToInterpolationTargets( int[2] posSrcCenterToInterpolationTargets(
ScalingDirection direction, ScalingDirection direction,
)( )(
@ -3048,6 +3071,8 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
UDecimal ratioHalf, UDecimal ratioHalf,
int sourceMax, int sourceMax,
) { ) {
pragma(inline, true);
int[2] result; int[2] result;
static if (direction == none) { static if (direction == none) {
const value = posSrcCenter.castTo!int; const value = posSrcCenter.castTo!int;
@ -3171,18 +3196,19 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
} }
// ======== Interpolate X ======== // ======== Interpolate X ========
auto sampleX(SamplingMode mode)(const size_t ib) { auto sampleX(SamplingMode mode)() {
pragma(inline, true); pragma(inline, true);
static if (mode == SamplingMode.multi) { static if (mode == SamplingMode.multi) {
const nLines = 1 + posSrcY[idxB] - posSrcY[idxT]; const nLines = 1 + posSrcY[idxB] - posSrcY[idxT];
alias ForeachLineCallback = ulong delegate(const Point posLine) @safe pure nothrow @nogc; alias ForeachLineCallback = InterPixel delegate(const Point posLine) @safe pure nothrow @nogc;
ulong foreachLine(scope ForeachLineCallback apply) { InterPixel foreachLine(scope ForeachLineCallback apply) {
ulong linesSum = 0; InterPixel linesSum = 0;
foreach (lineY; posSrcY[idxT] .. (1 + posSrcY[idxB])) { foreach (lineY; posSrcY[idxT] .. (1 + posSrcY[idxB])) {
const posLine = Point(posSrcX[idxL], lineY); const posLine = Point(posSrcX[idxL], lineY);
linesSum += apply(posLine); const lineValues = apply(posLine);
linesSum[] += lineValues[];
} }
return linesSum; return linesSum;
} }
@ -3191,25 +3217,26 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
// ========== None ========== // ========== None ==========
static if (directionX == none) { static if (directionX == none) {
static if (mode == SamplingMode.single) { static if (mode == SamplingMode.single) {
return (() @trusted => pxNeighs[idxTL].components.ptr[ib])(); return pxNeighs[idxTL];
} }
static if (mode == SamplingMode.dual) { static if (mode == SamplingMode.dual) {
return () @trusted { return () @trusted {
ulong[2] result = [ InterPixel[2] result = [
pxNeighs[idxTL].components.ptr[ib], toInterPixel(pxNeighs[idxTL]),
pxNeighs[idxBL].components.ptr[ib], toInterPixel(pxNeighs[idxBL]),
]; ];
return result; return result;
}(); }();
} }
static if (mode == SamplingMode.multi) { static if (mode == SamplingMode.multi) {
const ySum = foreachLine(delegate(const Point posLine) { auto ySum = foreachLine(delegate(const Point posLine) {
const pxSrc = source.getPixel(posLine); const pxSrc = source.getPixel(posLine);
return ulong((() @trusted => pxSrc.components.ptr[ib])()); return toInterPixel(pxSrc);
}); });
return (ySum / nLines); ySum[] /= nLines;
return ySum;
} }
} }
@ -3223,13 +3250,16 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
return source.data.ptr[samplingOffset .. (samplingOffset + nSamples)]; return source.data.ptr[samplingOffset .. (samplingOffset + nSamples)];
}(); }();
ulong xSum = 0; InterPixel xSum = [0, 0, 0, 0];
foreach (srcSample; srcSamples) { foreach (srcSample; srcSamples) {
xSum += (() @trusted => srcSample.components.ptr[ib])(); foreach (immutable ib, c; srcSample.components) {
() @trusted { xSum.ptr[ib] += c; }();
}
} }
return (xSum / nSamples); xSum[] /= nSamples;
return toPixel(xSum);
} }
static if (mode == SamplingMode.dual) { static if (mode == SamplingMode.dual) {
@ -3252,15 +3282,19 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
return result; return result;
}(); }();
ulong[2] xSums = [0, 0]; InterPixel[2] xSums = [[0, 0, 0, 0], [0, 0, 0, 0]];
foreach (idx, srcSamples; srcSamples2) { foreach (idx, srcSamples; srcSamples2) {
foreach (srcSample; srcSamples) { foreach (srcSample; srcSamples) {
() @trusted { xSums.ptr[idx] += srcSample.components.ptr[ib]; }(); foreach (immutable ib, c; srcSample.components)
() @trusted { xSums.ptr[idx].ptr[ib] += c; }();
} }
} }
xSums[] /= nSamples; foreach (xSum; xSums) {
xSum[] /= nSamples;
}
return xSums; return xSums;
} }
@ -3273,17 +3307,20 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
return source.data.ptr[samplingOffset .. (samplingOffset + nSamples)]; return source.data.ptr[samplingOffset .. (samplingOffset + nSamples)];
}(); }();
ulong xSum = 0; InterPixel xSum = 0;
foreach (srcSample; srcSamples) { foreach (srcSample; srcSamples) {
xSum += (() @trusted => srcSample.components.ptr[ib])(); foreach (immutable ib, c; srcSample.components) {
() @trusted { xSum.ptr[ib] += c; }();
}
} }
return xSum; return xSum;
}); });
ySum /= nSamples; ySum[] /= nSamples;
return (ySum / nLines); ySum[] /= nLines;
return ySum;
} }
} }
@ -3292,26 +3329,26 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
if (posSrcX[0] == posSrcX[1]) { if (posSrcX[0] == posSrcX[1]) {
static if (mode == SamplingMode.single) { static if (mode == SamplingMode.single) {
return (() @trusted => pxNeighs[idxTL].components.ptr[ib])(); return pxNeighs[idxTL];
} }
static if (mode == SamplingMode.dual) { static if (mode == SamplingMode.dual) {
return () @trusted { return () @trusted {
ulong[2] result = [ InterPixel[2] result = [
pxNeighs[idxTL].components.ptr[ib], toInterPixel(pxNeighs[idxTL]),
pxNeighs[idxBL].components.ptr[ib], toInterPixel(pxNeighs[idxBL]),
]; ];
return result; return result;
}(); }();
} }
static if (mode == SamplingMode.multi) { static if (mode == SamplingMode.multi) {
const ySum = foreachLine(delegate(const Point posLine) { auto ySum = foreachLine(delegate(const Point posLine) {
ulong xSum = 0;
const samplingOffset = source.scanTo(posLine); const samplingOffset = source.scanTo(posLine);
return (() @trusted return toInterPixel(
=> source.data.ptr[samplingOffset].components.ptr[ib] (() @trusted => source.data.ptr[samplingOffset])()
)(); );
}); });
return (ySum / nLines); ySum[] /= nLines;
return ySum;
} }
} }
@ -3323,80 +3360,90 @@ private void scaleToImpl(ScalingFilter filter)(const Pixmap source, Pixmap targe
}(); }();
static if (mode == SamplingMode.single) { static if (mode == SamplingMode.single) {
ulong xSum = 0; InterPixel xSum = [0, 0, 0, 0];
() @trusted { foreach (immutable ib, ref c; xSum) {
xSum += (pxNeighs[idxTL].components.ptr[ib] * weightsX[0]); c += ((() @trusted => pxNeighs[idxTL].components.ptr[ib])() * weightsX[0]);
xSum += (pxNeighs[idxTR].components.ptr[ib] * weightsX[1]); c += ((() @trusted => pxNeighs[idxTR].components.ptr[ib])() * weightsX[1]);
}(); }
xSum >>= 32; foreach (ref c; xSum) {
return xSum; c >>= 32;
}
return toPixel(xSum);
} }
static if (mode == SamplingMode.dual) { static if (mode == SamplingMode.dual) {
ulong[2] xSums = [0, 0]; InterPixel[2] xSums = [[0, 0, 0, 0], [0, 0, 0, 0]];
() @trusted { () @trusted {
xSums[0] += (pxNeighs[idxTL].components.ptr[ib] * weightsX[0]); foreach (immutable ib, ref c; xSums[0]) {
xSums[0] += (pxNeighs[idxTR].components.ptr[ib] * weightsX[1]); c += (pxNeighs[idxTL].components.ptr[ib] * weightsX[idxL]);
c += (pxNeighs[idxTR].components.ptr[ib] * weightsX[idxR]);
}
xSums[1] += (pxNeighs[idxBL].components.ptr[ib] * weightsX[0]); foreach (immutable ib, ref c; xSums[1]) {
xSums[1] += (pxNeighs[idxBR].components.ptr[ib] * weightsX[1]); c += (pxNeighs[idxBL].components.ptr[ib] * weightsX[idxL]);
c += (pxNeighs[idxBR].components.ptr[ib] * weightsX[idxR]);
}
}(); }();
foreach (ref sum; xSums) { foreach (ref sum; xSums) {
sum >>= 32; foreach (ref c; sum) {
c >>= 32;
}
} }
return xSums; return xSums;
} }
static if (mode == SamplingMode.multi) { static if (mode == SamplingMode.multi) {
const ySum = foreachLine(delegate(const Point posLine) { auto ySum = foreachLine(delegate(const Point posLine) {
ulong xSum = 0; InterPixel xSum = [0, 0, 0, 0];
const samplingOffset = source.scanTo(posLine); const samplingOffset = source.scanTo(posLine);
ubyte[2] pxcLR = () @trusted { Pixel[2] pxcLR = () @trusted {
ubyte[2] result = [ Pixel[2] result = [
source.data.ptr[samplingOffset].components.ptr[ib], source.data.ptr[samplingOffset],
source.data.ptr[samplingOffset + 1].components.ptr[ib], source.data.ptr[samplingOffset + 1],
]; ];
return result; return result;
}(); }();
xSum += (pxcLR[idxL] * weightsX[idxL]); foreach (immutable ib, ref c; xSum) {
xSum += (pxcLR[idxR] * weightsX[idxR]); c += ((() @trusted => pxcLR[idxL].components.ptr[ib])() * weightsX[idxL]);
c += ((() @trusted => pxcLR[idxR].components.ptr[ib])() * weightsX[idxR]);
}
return (xSum >> 32); foreach (ref c; xSum) {
c >>= 32;
}
return xSum;
}); });
return (ySum / nLines); ySum[] /= nLines;
return ySum;
} }
} }
} }
// ======== Interpolate Y ======== // ======== Interpolate Y ========
static if (directionY == none) { static if (directionY == none) {
foreach (immutable ib, ref c; pxInt.components) { const Pixel tmp = sampleX!(SamplingMode.single)();
c = clamp255(sampleX!(SamplingMode.single)(ib)); pxInt = tmp;
}
} }
static if (directionY == down) { static if (directionY == down) {
foreach (immutable ib, ref c; pxInt.components) { const InterPixel tmp = sampleX!(SamplingMode.multi)();
c = clamp255(sampleX!(SamplingMode.multi)(ib)); pxInt = toPixel(tmp);
}
} }
static if (directionY == up) { static if (directionY == up) {
const InterPixel[2] xSums = sampleX!(SamplingMode.dual)();
foreach (immutable ib, ref c; pxInt.components) { foreach (immutable ib, ref c; pxInt.components) {
const xSums = sampleX!(SamplingMode.dual)(ib);
ulong ySum = 0; ulong ySum = 0;
ySum += (xSums[idxT] * weightsY[idxT]); ySum += ((() @trusted => xSums[idxT].ptr[ib])() * weightsY[idxT]);
ySum += (xSums[idxB] * weightsY[idxB]); ySum += ((() @trusted => xSums[idxB].ptr[ib])() * weightsY[idxB]);
const xySum = (ySum >> 32); const xySum = (ySum >> 32);
c = clamp255(xySum); c = clamp255(xySum);
} }
} }