/// <summary> /// Implements bicubic filtering with bounds checking at every pixel. /// </summary> private unsafe void BicubicFitSurfaceChecked(MaskSurface source, Rectangle dstRoi) { Rectangle roi = Rectangle.Intersect(dstRoi, Bounds); Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1)); IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double)); double *rColCache = (double *)rColCacheIP.ToPointer(); // Precompute and then cache the value of R() for each column for (int dstX = roi.Left; dstX < roi.Right; ++dstX) { double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1); double srcColumnFloor = Math.Floor(srcColumn); double srcColumnFrac = srcColumn - srcColumnFloor; for (int m = -1; m <= 2; ++m) { int index = (m + 1) + ((dstX - roi.Left) * 4); double x = m - srcColumnFrac; rColCache[index] = R(x); } } // Set this up so we can cache the R()'s for every row double *rRowCache = stackalloc double[4]; for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY) { double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1); double srcRowFloor = (double)Math.Floor(srcRow); double srcRowFrac = srcRow - srcRowFloor; int srcRowInt = (int)srcRow; byte * dstPtr = GetPointAddressUnchecked(roi.Left, dstY); // Compute the R() values for this row for (int n = -1; n <= 2; ++n) { double x = srcRowFrac - n; rRowCache[n + 1] = R(x); } // See Perf Note below //int nFirst = Math.Max(-srcRowInt, -1); //int nLast = Math.Min(source.height - srcRowInt - 1, 2); for (int dstX = roi.Left; dstX < roi.Right; dstX++) { double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1); double srcColumnFloor = Math.Floor(srcColumn); double srcColumnFrac = srcColumn - srcColumnFloor; int srcColumnInt = (int)srcColumn; double graySum = 0; double alphaSum = 0; double totalWeight = 0; // See Perf Note below //int mFirst = Math.Max(-srcColumnInt, -1); //int mLast = Math.Min(source.width - srcColumnInt - 1, 2); byte *srcPtr = source.GetPointAddressUnchecked(srcColumnInt - 1, srcRowInt - 1); for (int n = -1; n <= 2; ++n) { int srcY = srcRowInt + n; for (int m = -1; m <= 2; ++m) { // Perf Note: It actually benchmarks faster on my system to do // a bounds check for every (m,n) than it is to limit the loop // to nFirst-Last and mFirst-mLast. // I'm leaving the code above, albeit commented out, so that // benchmarking between these two can still be performed. if (source.IsVisible(srcColumnInt + m, srcY)) { double w0 = rColCache[(m + 1) + (4 * (dstX - roi.Left))]; double w1 = rRowCache[n + 1]; double w = w0 * w1; graySum += *srcPtr * w * 255.0; alphaSum += 255.0 * w; totalWeight += w; } ++srcPtr; } srcPtr = ((byte *)(srcPtr - 4) + source.stride); } double alpha = alphaSum / totalWeight; double gray; if (alpha == 0) { gray = 0; } else { gray = graySum / alphaSum; // add 0.5 to ensure truncation to uint results in rounding gray += 0.5; } *dstPtr = (byte)gray; ++dstPtr; } // for (dstX... } // for (dstY... Memory.Free(rColCacheIP); }
public unsafe void SuperSampleFitSurface(MaskSurface source) { Rectangle dstRoi2 = Rectangle.Intersect(source.Bounds, Bounds); int srcHeight = source.Height; int srcWidth = source.Width; long srcStride = source.Stride; for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY) { double srcTop = (double)(dstY * srcHeight) / (double)height; double srcTopFloor = Math.Floor(srcTop); double srcTopWeight = 1 - (srcTop - srcTopFloor); int srcTopInt = (int)srcTopFloor; double srcBottom = (double)((dstY + 1) * srcHeight) / (double)height; double srcBottomFloor = Math.Floor(srcBottom - 0.00001); double srcBottomWeight = srcBottom - srcBottomFloor; int srcBottomInt = (int)srcBottomFloor; byte *dstPtr = GetPointAddressUnchecked(dstRoi2.Left, dstY); for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX) { double srcLeft = (double)(dstX * srcWidth) / (double)width; double srcLeftFloor = Math.Floor(srcLeft); double srcLeftWeight = 1 - (srcLeft - srcLeftFloor); int srcLeftInt = (int)srcLeftFloor; double srcRight = (double)((dstX + 1) * srcWidth) / (double)width; double srcRightFloor = Math.Floor(srcRight - 0.00001); double srcRightWeight = srcRight - srcRightFloor; int srcRightInt = (int)srcRightFloor; double graySum = 0; double alphaSum = 0; // left fractional edge byte *srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { graySum += *srcLeftPtr * srcLeftWeight * 255.0; srcLeftPtr = (byte *)((byte *)srcLeftPtr + srcStride); } // right fractional edge byte *srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { graySum += *srcLeftPtr * srcLeftWeight * 255.0; srcRightPtr = (byte *)((byte *)srcRightPtr + srcStride); } // top fractional edge byte *srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { graySum += *srcLeftPtr * srcLeftWeight * 255.0; ++srcTopPtr; } // bottom fractional edge byte *srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { graySum += *srcLeftPtr * srcLeftWeight * 255.0; ++srcBottomPtr; } // center area for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { byte *srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { graySum += *srcLeftPtr * srcLeftWeight * 255.0; ++srcPtr; } } // four corner pixels byte srcTL = source.GetPoint(srcLeftInt, srcTopInt); graySum += srcTL * (srcTopWeight * srcLeftWeight) * 255.0; alphaSum += 255.0 * (srcTopWeight * srcLeftWeight); byte srcTR = source.GetPoint(srcRightInt, srcTopInt); graySum += srcTR * (srcTopWeight * srcRightWeight) * 255.0; alphaSum += 255.0 * (srcTopWeight * srcRightWeight); byte srcBL = source.GetPoint(srcLeftInt, srcBottomInt); graySum += srcBL * (srcBottomWeight * srcLeftWeight) * 255.0; alphaSum += 255.0 * (srcBottomWeight * srcLeftWeight); byte srcBR = source.GetPoint(srcRightInt, srcBottomInt); graySum += srcBR * (srcBottomWeight * srcRightWeight) * 255.0; alphaSum += 255.0 * (srcBottomWeight * srcRightWeight); double area = (srcRight - srcLeft) * (srcBottom - srcTop); double alpha = 255.0 / area; double gray; if (alpha == 0) { gray = 0; } else { gray = graySum / alphaSum; } // add 0.5 so that rounding goes in the direction we want it to gray += 0.5; *dstPtr = (byte)gray; ++dstPtr; } } }