public unsafe bool GetUnchecked(int x, int y) { int cx = x / 32; int sx = x % 32; uint mask = surface.GetPointAddressUnchecked(cx, y)->Bgra; return(0 != (mask & (1 << sx))); }
public unsafe void DrawScansBilinear(object cpuNumberObj) { int cpuNumber = (int)cpuNumberObj; int start = (this.dstScans.Length * cpuNumber) / Processor.LogicalCpuCount; int end = (this.dstScans.Length * (cpuNumber + 1)) / Processor.LogicalCpuCount; PointF[] pts2 = new PointF[1]; for (int i = start; i < end; ++i) { Rectangle dstRect = this.dstScans[i]; dstRect.Intersect(dst.Bounds); pts2[0] = new PointF(dstRect.Left, dstRect.Top); this.inverses[cpuNumber].TransformPoints(pts2); pts2[0].X -= this.boundsX; pts2[0].Y -= this.boundsY; // Sometimes pts2 ends up being infintessimally small (1 x 10^-5 or -6) but negative // This throws off GetBilinearSample and it returns transparent colors, which looks horribly wrong. // So we fix that! if (pts2[0].X < 0) { pts2[0].X = 0; } if (pts2[0].Y < 0) { pts2[0].Y = 0; } PointF srcPtRow = pts2[0]; for (int dstY = dstRect.Top; dstY < dstRect.Bottom; ++dstY) { PointF srcPtCol = srcPtRow; srcPtRow.X += this.dsxddy; srcPtRow.Y += this.dsyddy; if (dstY >= 0) { int dstX = dstRect.Left; while (dstX < dstRect.Right) { ColorBgra srcPixel = this.src.GetBilinearSample(srcPtCol.X, srcPtCol.Y); ColorBgra *dstPtr = dst.GetPointAddressUnchecked(dstX, dstY); * dstPtr = srcPixel; srcPtCol.X += this.dsxddx; srcPtCol.Y += this.dsyddx; ++dstX; } } } } }
protected override unsafe void AddSurfaceRectangleToHistogram(Surface surface, Rectangle rect) { long[] histogramLuminosity = histogram[0]; for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra *ptr = surface.GetPointAddressUnchecked(rect.Left, y); for (int x = rect.Left; x < rect.Right; ++x) { ++histogramLuminosity[ptr->GetIntensityByte()]; ++ptr; } } }
protected override unsafe void AddSurfaceRectangleToHistogram(Surface surface, Rectangle rect) { long[] histogramLuminosity = histogram[0]; for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra* ptr = surface.GetPointAddressUnchecked(rect.Left, y); for (int x = rect.Left; x < rect.Right; ++x) { ++histogramLuminosity[ptr->GetIntensityByte()]; ++ptr; } } }
protected override unsafe void AddSurfaceRectangleToHistogram(Surface surface, Rectangle rect) { long[] histogramB = histogram[0]; long[] histogramG = histogram[1]; long[] histogramR = histogram[2]; for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra *ptr = surface.GetPointAddressUnchecked(rect.Left, y); for (int x = rect.Left; x < rect.Right; ++x) { ++histogramB[ptr->B]; ++histogramG[ptr->G]; ++histogramR[ptr->R]; ++ptr; } } }
protected override unsafe void AddSurfaceRectangleToHistogram(Surface surface, Rectangle rect) { long[] histogramB = histogram[0]; long[] histogramG = histogram[1]; long[] histogramR = histogram[2]; for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra* ptr = surface.GetPointAddressUnchecked(rect.Left, y); for (int x = rect.Left; x < rect.Right; ++x) { ++histogramB[ptr->B]; ++histogramG[ptr->G]; ++histogramR[ptr->R]; ++ptr; } } }
public static void RenderOneToOne(Surface dst, Surface source, Point offset) { unsafe { Rectangle srcRect = new Rectangle(offset, dst.Size); srcRect.Intersect(source.Bounds); for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow) { ColorBgra* dstRowPtr = dst.GetRowAddressUnchecked(dstRow); ColorBgra* srcRowPtr = source.GetPointAddressUnchecked(offset.X, dstRow + offset.Y); int dstCol = offset.X; int dstColEnd = offset.X + srcRect.Width; int checkerY = dstRow + offset.Y; while (dstCol < dstColEnd) { int b = srcRowPtr->B; int g = srcRowPtr->G; int r = srcRowPtr->R; int a = srcRowPtr->A; // Blend it over the checkerboard background int v = (((dstCol ^ checkerY) & 8) << 3) + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstRowPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24); ++dstRowPtr; ++srcRowPtr; ++dstCol; } } } }
public static void RenderOneToOne(Surface dst, Surface source, Point offset) { unsafe { Rectangle srcRect = new Rectangle(offset, dst.Size); srcRect.Intersect(source.Bounds); for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow) { ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dstRow); ColorBgra *srcRowPtr = source.GetPointAddressUnchecked(offset.X, dstRow + offset.Y); int dstCol = offset.X; int dstColEnd = offset.X + srcRect.Width; int checkerY = dstRow + offset.Y; while (dstCol < dstColEnd) { int b = srcRowPtr->B; int g = srcRowPtr->G; int r = srcRowPtr->R; int a = srcRowPtr->A; // Blend it over the checkerboard background int v = (((dstCol ^ checkerY) & 8) << 3) + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstRowPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24); ++dstRowPtr; ++srcRowPtr; ++dstCol; } } } }
/// <summary> /// Clears a portion of a surface to transparent. /// </summary> /// <param name="surface">The surface to partially clear</param> /// <param name="roi">The rectangle to clear</param> private unsafe void ClearBackground(Surface surface, Rectangle roi) { roi.Intersect(surface.Bounds); for (int y = roi.Top; y < roi.Bottom; y++) { ColorBgra *ptr = surface.GetPointAddressUnchecked(roi.Left, y); Memory.SetToZero(ptr, (ulong)roi.Width * ColorBgra.SizeOf); } }
public unsafe void SuperSampleFitSurface(Surface source) { Rectangle dstRoi2 = Rectangle.Intersect(source.Bounds, this.Bounds); for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY) { double srcTop = (double)(dstY * source.height) / (double)height; double srcTopFloor = Math.Floor(srcTop); double srcTopWeight = 1 - (srcTop - srcTopFloor); int srcTopInt = (int)srcTopFloor; double srcBottom = (double)((dstY + 1) * source.height) / (double)height; double srcBottomFloor = Math.Floor(srcBottom - 0.00001); double srcBottomWeight = srcBottom - srcBottomFloor; int srcBottomInt = (int)srcBottomFloor; ColorBgra *dstPtr = this.GetPointAddressUnchecked(dstRoi2.Left, dstY); for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX) { double srcLeft = (double)(dstX * source.width) / (double)width; double srcLeftFloor = Math.Floor(srcLeft); double srcLeftWeight = 1 - (srcLeft - srcLeftFloor); int srcLeftInt = (int)srcLeftFloor; double srcRight = (double)((dstX + 1) * source.width) / (double)width; double srcRightFloor = Math.Floor(srcRight - 0.00001); double srcRightWeight = srcRight - srcRightFloor; int srcRightInt = (int)srcRightFloor; double blueSum = 0; double greenSum = 0; double redSum = 0; double alphaSum = 0; // left fractional edge ColorBgra *srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { double a = srcLeftPtr->A; blueSum += srcLeftPtr->B * srcLeftWeight * a; greenSum += srcLeftPtr->G * srcLeftWeight * a; redSum += srcLeftPtr->R * srcLeftWeight * a; alphaSum += srcLeftPtr->A * srcLeftWeight; srcLeftPtr = (ColorBgra *)((byte *)srcLeftPtr + source.stride); } // right fractional edge ColorBgra *srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { double a = srcRightPtr->A; blueSum += srcRightPtr->B * srcRightWeight * a; greenSum += srcRightPtr->G * srcRightWeight * a; redSum += srcRightPtr->R * srcRightWeight * a; alphaSum += srcRightPtr->A * srcRightWeight; srcRightPtr = (ColorBgra *)((byte *)srcRightPtr + source.stride); } // top fractional edge ColorBgra *srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { double a = srcTopPtr->A; blueSum += srcTopPtr->B * srcTopWeight * a; greenSum += srcTopPtr->G * srcTopWeight * a; redSum += srcTopPtr->R * srcTopWeight * a; alphaSum += srcTopPtr->A * srcTopWeight; ++srcTopPtr; } // bottom fractional edge ColorBgra *srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { double a = srcBottomPtr->A; blueSum += srcBottomPtr->B * srcBottomWeight * a; greenSum += srcBottomPtr->G * srcBottomWeight * a; redSum += srcBottomPtr->R * srcBottomWeight * a; alphaSum += srcBottomPtr->A * srcBottomWeight; ++srcBottomPtr; } // center area for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { ColorBgra *srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { double a = srcPtr->A; blueSum += (double)srcPtr->B * a; greenSum += (double)srcPtr->G * a; redSum += (double)srcPtr->R * a; alphaSum += (double)srcPtr->A; ++srcPtr; } } // four corner pixels ColorBgra srcTL = source.GetPoint(srcLeftInt, srcTopInt); double srcTLA = srcTL.A; blueSum += srcTL.B * (srcTopWeight * srcLeftWeight) * srcTLA; greenSum += srcTL.G * (srcTopWeight * srcLeftWeight) * srcTLA; redSum += srcTL.R * (srcTopWeight * srcLeftWeight) * srcTLA; alphaSum += srcTL.A * (srcTopWeight * srcLeftWeight); ColorBgra srcTR = source.GetPoint(srcRightInt, srcTopInt); double srcTRA = srcTR.A; blueSum += srcTR.B * (srcTopWeight * srcRightWeight) * srcTRA; greenSum += srcTR.G * (srcTopWeight * srcRightWeight) * srcTRA; redSum += srcTR.R * (srcTopWeight * srcRightWeight) * srcTRA; alphaSum += srcTR.A * (srcTopWeight * srcRightWeight); ColorBgra srcBL = source.GetPoint(srcLeftInt, srcBottomInt); double srcBLA = srcBL.A; blueSum += srcBL.B * (srcBottomWeight * srcLeftWeight) * srcBLA; greenSum += srcBL.G * (srcBottomWeight * srcLeftWeight) * srcBLA; redSum += srcBL.R * (srcBottomWeight * srcLeftWeight) * srcBLA; alphaSum += srcBL.A * (srcBottomWeight * srcLeftWeight); ColorBgra srcBR = source.GetPoint(srcRightInt, srcBottomInt); double srcBRA = srcBR.A; blueSum += srcBR.B * (srcBottomWeight * srcRightWeight) * srcBRA; greenSum += srcBR.G * (srcBottomWeight * srcRightWeight) * srcBRA; redSum += srcBR.R * (srcBottomWeight * srcRightWeight) * srcBRA; alphaSum += srcBR.A * (srcBottomWeight * srcRightWeight); double area = (srcRight - srcLeft) * (srcBottom - srcTop); double alpha = alphaSum / area; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; } // add 0.5 so that rounding goes in the direction we want it to blue += 0.5; green += 0.5; red += 0.5; alpha += 0.5; dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstPtr; } } }
/// <summary> /// Implements bicubic filtering with bounds checking at every pixel. /// </summary> private unsafe void BicubicFitSurfaceChecked(Surface source, Rectangle dstRoi) { Rectangle roi = Rectangle.Intersect(dstRoi, this.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; ColorBgra *dstPtr = this.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 blueSum = 0; double greenSum = 0; double redSum = 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); ColorBgra *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; blueSum += srcPtr->B * w * srcPtr->A; greenSum += srcPtr->G * w * srcPtr->A; redSum += srcPtr->R * w * srcPtr->A; alphaSum += srcPtr->A * w; totalWeight += w; } ++srcPtr; } srcPtr = (ColorBgra *)((byte *)(srcPtr - 4) + source.stride); } double alpha = alphaSum / totalWeight; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; // add 0.5 to ensure truncation to uint results in rounding alpha += 0.5; blue += 0.5; green += 0.5; red += 0.5; } dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstPtr; } // for (dstX... } // for (dstY... Memory.Free(rColCacheIP); }
/// <summary> /// Copies a region of the given surface to this surface. /// </summary> /// <param name="source">The surface to copy pixels from.</param> /// <param name="region">The region to clip copying to.</param> /// <remarks> /// The upper left corner of the source surface will be mapped to the upper left of this /// surface, and only those pixels that are defined by the region will be copied. /// 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> public void CopySurface(Surface source, Rectangle[] region, int startIndex, int length) { if (disposed) { throw new ObjectDisposedException("Surface"); } for (int i = startIndex; i < startIndex + length; ++i) { Rectangle rect = region[i]; rect.Intersect(this.Bounds); rect.Intersect(source.Bounds); if (rect.Width == 0 || rect.Height == 0) { continue; } unsafe { for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra* dst = this.GetPointAddressUnchecked(rect.Left, y); ColorBgra* src = source.GetPointAddressUnchecked(rect.Left, y); Memory.Copy(dst, src, (ulong)rect.Width * (ulong)ColorBgra.SizeOf); } } } }
/// <summary> /// Copies a region of the given surface to this surface. /// </summary> /// <param name="source">The surface to copy pixels from.</param> /// <param name="region">The region to clip copying to.</param> /// <remarks> /// The upper left corner of the source surface will be mapped to the upper left of this /// surface, and only those pixels that are defined by the region will be copied. /// 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> public void CopySurface(Surface source, PdnRegion region) { if (disposed) { throw new ObjectDisposedException("Surface"); } Rectangle[] scans = region.GetRegionScansReadOnlyInt(); for (int i = 0; i < scans.Length; ++i) { Rectangle rect = scans[i]; rect.Intersect(this.Bounds); rect.Intersect(source.Bounds); if (rect.Width == 0 || rect.Height == 0) { continue; } unsafe { for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra *dst = this.GetPointAddressUnchecked(rect.Left, y); ColorBgra *src = source.GetPointAddressUnchecked(rect.Left, y); Memory.Copy(dst, src, (ulong)rect.Width * (ulong)ColorBgra.SizeOf); } } } }
/// <summary> /// Implements bicubic filtering with bounds checking at every pixel. /// </summary> private void BicubicFitSurfaceChecked(Surface source, Rectangle dstRoi) { if (this.width < 2 || this.height < 2 || source.width < 2 || source.height < 2) { SuperSamplingFitSurface(source, dstRoi); } else { unsafe { Rectangle roi = Rectangle.Intersect(dstRoi, this.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; int srcColumnInt = (int)srcColumn; 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; ColorBgra *dstPtr = this.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 blueSum = 0; double greenSum = 0; double redSum = 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); ColorBgra *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; blueSum += srcPtr->B * w * srcPtr->A; greenSum += srcPtr->G * w * srcPtr->A; redSum += srcPtr->R * w * srcPtr->A; alphaSum += srcPtr->A * w; totalWeight += w; } ++srcPtr; } srcPtr = (ColorBgra *)((byte *)(srcPtr - 4) + source.stride); } double alpha = alphaSum / totalWeight; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; // add 0.5 to ensure truncation to uint results in rounding alpha += 0.5; blue += 0.5; green += 0.5; red += 0.5; } dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstPtr; } // for (dstX... } // for (dstY... Memory.Free(rColCacheIP); } // unsafe } }
/// <summary> /// Fits the source surface to this surface using super sampling. If the source surface is less wide /// or less tall than this surface (i.e. magnification), bicubic resampling is used instead. If either /// the source or destination has a dimension that is only 1 pixel, nearest neighbor is used. /// </summary> /// <param name="source">The surface to read pixels from.</param> /// <param name="dstRoi">The rectangle to clip rendering to.</param> /// <remarks>This method was implemented with correctness, not performance, in mind.</remarks> public void SuperSamplingFitSurface(Surface source, Rectangle dstRoi) { if (source.Width == Width && source.Height == Height) { CopySurface(source); } else if (source.Width <= Width || source.Height <= Height) { if (source.width < 2 || source.height < 2 || this.width < 2 || this.height < 2) { this.NearestNeighborFitSurface(source, dstRoi); } else { this.BicubicFitSurface(source, dstRoi); } } else unsafe { Rectangle dstRoi2 = Rectangle.Intersect(dstRoi, this.Bounds); for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY) { double srcTop = (double)(dstY * source.height) / (double)height; double srcTopFloor = Math.Floor(srcTop); double srcTopWeight = 1 - (srcTop - srcTopFloor); int srcTopInt = (int)srcTopFloor; double srcBottom = (double)((dstY + 1) * source.height) / (double)height; double srcBottomFloor = Math.Floor(srcBottom - 0.00001); double srcBottomWeight = srcBottom - srcBottomFloor; int srcBottomInt = (int)srcBottomFloor; ColorBgra *dstPtr = this.GetPointAddressUnchecked(dstRoi2.Left, dstY); for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX) { double srcLeft = (double)(dstX * source.width) / (double)width; double srcLeftFloor = Math.Floor(srcLeft); double srcLeftWeight = 1 - (srcLeft - srcLeftFloor); int srcLeftInt = (int)srcLeftFloor; double srcRight = (double)((dstX + 1) * source.width) / (double)width; double srcRightFloor = Math.Floor(srcRight - 0.00001); double srcRightWeight = srcRight - srcRightFloor; int srcRightInt = (int)srcRightFloor; double blueSum = 0; double greenSum = 0; double redSum = 0; double alphaSum = 0; // left fractional edge ColorBgra *srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { double a = srcLeftPtr->A; blueSum += srcLeftPtr->B * srcLeftWeight * a; greenSum += srcLeftPtr->G * srcLeftWeight * a; redSum += srcLeftPtr->R * srcLeftWeight * a; alphaSum += srcLeftPtr->A * srcLeftWeight; srcLeftPtr = (ColorBgra*)((byte*)srcLeftPtr + source.stride); } // right fractional edge ColorBgra *srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1); for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { double a = srcRightPtr->A; blueSum += srcRightPtr->B * srcRightWeight * a; greenSum += srcRightPtr->G * srcRightWeight * a; redSum += srcRightPtr->R * srcRightWeight * a; alphaSum += srcRightPtr->A * srcRightWeight; srcRightPtr = (ColorBgra*)((byte*)srcRightPtr + source.stride); } // top fractional edge ColorBgra *srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { double a = srcTopPtr->A; blueSum += srcTopPtr->B * srcTopWeight * a; greenSum += srcTopPtr->G * srcTopWeight * a; redSum += srcTopPtr->R * srcTopWeight * a; alphaSum += srcTopPtr->A * srcTopWeight; ++srcTopPtr; } // bottom fractional edge ColorBgra *srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { double a = srcBottomPtr->A; blueSum += srcBottomPtr->B * srcBottomWeight * a; greenSum += srcBottomPtr->G * srcBottomWeight * a; redSum += srcBottomPtr->R * srcBottomWeight * a; alphaSum += srcBottomPtr->A * srcBottomWeight; ++srcBottomPtr; } // center area for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY) { ColorBgra *srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY); for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX) { double a = srcPtr->A; blueSum += (double)srcPtr->B * a; greenSum += (double)srcPtr->G * a; redSum += (double)srcPtr->R * a; alphaSum += (double)srcPtr->A; ++srcPtr; } } // four corner pixels ColorBgra srcTL = source.GetPoint(srcLeftInt, srcTopInt); double srcTLA = srcTL.A; blueSum += srcTL.B * (srcTopWeight * srcLeftWeight) * srcTLA; greenSum += srcTL.G * (srcTopWeight * srcLeftWeight) * srcTLA; redSum += srcTL.R * (srcTopWeight * srcLeftWeight) * srcTLA; alphaSum += srcTL.A * (srcTopWeight * srcLeftWeight); ColorBgra srcTR = source.GetPoint(srcRightInt, srcTopInt); double srcTRA = srcTR.A; blueSum += srcTR.B * (srcTopWeight * srcRightWeight) * srcTRA; greenSum += srcTR.G * (srcTopWeight * srcRightWeight) * srcTRA; redSum += srcTR.R * (srcTopWeight * srcRightWeight) * srcTRA; alphaSum += srcTR.A * (srcTopWeight * srcRightWeight); ColorBgra srcBL = source.GetPoint(srcLeftInt, srcBottomInt); double srcBLA = srcBL.A; blueSum += srcBL.B * (srcBottomWeight * srcLeftWeight) * srcBLA; greenSum += srcBL.G * (srcBottomWeight * srcLeftWeight) * srcBLA; redSum += srcBL.R * (srcBottomWeight * srcLeftWeight) * srcBLA; alphaSum += srcBL.A * (srcBottomWeight * srcLeftWeight); ColorBgra srcBR = source.GetPoint(srcRightInt, srcBottomInt); double srcBRA = srcBR.A; blueSum += srcBR.B * (srcBottomWeight * srcRightWeight) * srcBRA; greenSum += srcBR.G * (srcBottomWeight * srcRightWeight) * srcBRA; redSum += srcBR.R * (srcBottomWeight * srcRightWeight) * srcBRA; alphaSum += srcBR.A * (srcBottomWeight * srcRightWeight); double area = (srcRight - srcLeft) * (srcBottom - srcTop); double alpha = alphaSum / area; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; } // add 0.5 so that rounding goes in the direction we want it to blue += 0.5; green += 0.5; red += 0.5; alpha += 0.5; dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstPtr; } } } }
private unsafe void DrawText(Surface dst, Font textFont, string text, Point pt, Size measuredSize, bool antiAliasing, Surface brush8x8) { Point pt2 = pt; Size measuredSize2 = measuredSize; int offset = (int)textFont.Height; pt.X -= offset; measuredSize.Width += 2 * offset; Rectangle dstRect = new Rectangle(pt, measuredSize); Rectangle dstRectClipped = Rectangle.Intersect(dstRect, ScratchSurface.Bounds); if (dstRectClipped.Width == 0 || dstRectClipped.Height == 0) { return; } // We only use the first 8,8 of brush using (RenderArgs renderArgs = new RenderArgs(this.ScratchSurface)) { renderArgs.Graphics.FillRectangle(Brushes.White, pt.X, pt.Y, measuredSize.Width, measuredSize.Height); if (measuredSize.Width > 0 && measuredSize.Height > 0) { using (Surface s2 = renderArgs.Surface.CreateWindow(dstRectClipped)) { using (RenderArgs renderArgs2 = new RenderArgs(s2)) { SystemLayer.Fonts.DrawText( renderArgs2.Graphics, this.font, text, new Point(dstRect.X - dstRectClipped.X + offset, dstRect.Y - dstRectClipped.Y), AppEnvironment.AntiAliasing, AppEnvironment.FontSmoothing); } } } // Mask out anything that isn't within the user's clip region (selected region) using (PdnRegion clip = Selection.CreateRegion()) { clip.Xor(renderArgs.Surface.Bounds); // invert clip.Intersect(new Rectangle(pt, measuredSize)); renderArgs.Graphics.FillRegion(Brushes.White, clip.GetRegionReadOnly()); } int skipX; if (pt.X < 0) { skipX = -pt.X; } else { skipX = 0; } int xEnd = Math.Min(dst.Width, pt.X + measuredSize.Width); bool blending = AppEnvironment.AlphaBlending; if (dst.IsColumnVisible(pt.X + skipX)) { for (int y = pt.Y; y < pt.Y + measuredSize.Height; ++y) { if (!dst.IsRowVisible(y)) { continue; } ColorBgra *dstPtr = dst.GetPointAddressUnchecked(pt.X + skipX, y); ColorBgra *srcPtr = ScratchSurface.GetPointAddress(pt.X + skipX, y); ColorBgra *brushPtr = brush8x8.GetRowAddressUnchecked(y & 7); for (int x = pt.X + skipX; x < xEnd; ++x) { ColorBgra srcPixel = *srcPtr; ColorBgra dstPixel = *dstPtr; ColorBgra brushPixel = brushPtr[x & 7]; int alpha = ((255 - srcPixel.R) * brushPixel.A) / 255; // we could use srcPixel.R, .G, or .B -- the choice here is arbitrary brushPixel.A = (byte)alpha; if (srcPtr->R == 255) // could use R, G, or B -- arbitrary choice { // do nothing -- leave dst alone } else if (alpha == 255 || !blending) { // copy it straight over *dstPtr = brushPixel; } else { // do expensive blending *dstPtr = UserBlendOps.NormalBlendOp.ApplyStatic(dstPixel, brushPixel); } ++dstPtr; ++srcPtr; } } } } }