/// <summary> /// Copies the contents of the given surface to the upper left corner of this surface. /// </summary> /// <param name="source">The surface to copy pixels from.</param> /// <remarks> /// The source surface does not need to have the same dimensions as this surface. Clipping /// will be handled automatically. No resizing will be done. /// </remarks> private void CopySurface(MaskSurface source) { if (disposed) { throw new ObjectDisposedException("Surface"); } if (stride == source.stride && width == source.width && height == source.height) { unsafe { Memory.Copy(scan0.VoidStar, source.scan0.VoidStar, ((ulong)(height - 1) * (ulong)stride) + (ulong)width); } } else { int copyWidth = Math.Min(width, source.width); int copyHeight = Math.Min(height, source.height); unsafe { for (int y = 0; y < copyHeight; ++y) { Memory.Copy(GetRowAddressUnchecked(y), source.GetRowAddressUnchecked(y), (ulong)copyWidth); } } } }
/// <summary> /// Implements bicubic filtering with NO bounds checking at any pixel. /// </summary> private unsafe void BicubicFitSurfaceUnchecked(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 = 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); } rColCache = (double *)rColCacheIP.ToPointer(); byte *srcRowPtr = source.GetRowAddressUnchecked(srcRowInt - 1); 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; byte *srcPtr = srcRowPtr + srcColumnInt - 1; for (int n = 0; n <= 3; ++n) { double w0 = rColCache[0] * rRowCache[n]; double w1 = rColCache[1] * rRowCache[n]; double w2 = rColCache[2] * rRowCache[n]; double w3 = rColCache[3] * rRowCache[n]; double a0 = 255.0; double a1 = 255.0; double a2 = 255.0; double a3 = 255.0; alphaSum += (a0 * w0) + (a1 * w1) + (a2 * w2) + (a3 * w3); totalWeight += w0 + w1 + w2 + w3; graySum += (a0 * srcPtr[0] * w0) + (a1 * srcPtr[1] * w1) + (a2 * srcPtr[2] * w2) + (a3 * srcPtr[3] * w3); srcPtr = ((byte *)srcPtr + 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; rColCache += 4; } // for (dstX... } // for (dstY... Memory.Free(rColCacheIP); }