/// <summary> /// Provides a default implementation for performing dst = F(dst, src) or F(src) over some rectangle /// of interest. May be slightly faster than calling the other multi-parameter Apply method, as less /// variables are used in the implementation, thus inducing less register pressure. /// </summary> /// <param name="dst">The Surface to write pixels to, and from which pixels are read and used as the lhs parameter for calling the method <b>ColorBgra Apply(ColorBgra, ColorBgra)</b>.</param> /// <param name="dstOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the dst Surface.</param> /// <param name="src">The Surface to read pixels from for the rhs parameter given to the method <b>ColorBgra Apply(ColorBgra, ColorBgra)</b>b>.</param></param> /// <param name="srcOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the src Surface.</param> /// <param name="roiSize">The size of the rectangles-of-interest for all Surfaces.</param> public void ApplyBase(Surface dst, Point dstOffset, Surface src, Point srcOffset, Size roiSize) { // Create bounding rectangles for each Surface Rectangle dstRect = new Rectangle(dstOffset, roiSize); if (dstRect.Width == 0 || dstRect.Height == 0) { return; } Rectangle srcRect = new Rectangle(srcOffset, roiSize); if (srcRect.Width == 0 || srcRect.Height == 0) { return; } // Clip those rectangles to those Surface's bounding rectangles Rectangle dstClip = Rectangle.Intersect(dstRect, dst.Bounds); Rectangle srcClip = Rectangle.Intersect(srcRect, src.Bounds); // If any of those Rectangles actually got clipped, then throw an exception if (dstRect != dstClip) { throw new ArgumentOutOfRangeException ( "roiSize", "Destination roi out of bounds" + ", dst.Size=" + dst.Size.ToString() + ", dst.Bounds=" + dst.Bounds.ToString() + ", dstOffset=" + dstOffset.ToString() + ", src.Size=" + src.Size.ToString() + ", srcOffset=" + srcOffset.ToString() + ", roiSize=" + roiSize.ToString() + ", dstRect=" + dstRect.ToString() + ", dstClip=" + dstClip.ToString() + ", srcRect=" + srcRect.ToString() + ", srcClip=" + srcClip.ToString() ); } if (srcRect != srcClip) { throw new ArgumentOutOfRangeException("roiSize", "Source roi out of bounds"); } // Cache the width and height properties int width = roiSize.Width; int height = roiSize.Height; // Do the work. unsafe { for (int row = 0; row < roiSize.Height; ++row) { ColorBgra *dstPtr = dst.GetPointAddress(dstOffset.X, dstOffset.Y + row); ColorBgra *srcPtr = src.GetPointAddress(srcOffset.X, srcOffset.Y + row); Apply(dstPtr, srcPtr, width); } } }
private unsafe void ApplyRectangle(Surface surface, Rectangle rect) { for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra *ptr = surface.GetPointAddress(rect.Left, y); Apply(ptr, rect.Width); } }
public void Apply(Surface dst, Surface src, Rectangle roi) { for (int y = roi.Top; y < roi.Bottom; ++y) { ColorBgra *dstPtr = dst.GetPointAddress(roi.Left, y); ColorBgra *srcPtr = src.GetPointAddress(roi.Left, y); Apply(dstPtr, srcPtr, roi.Width); } }
/// <summary> /// Provides a default implementation for performing dst = F(lhs, rhs) over some rectangle of interest. /// </summary> /// <param name="dst">The Surface to write pixels to.</param> /// <param name="dstOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the dst Surface.</param> /// <param name="lhs">The Surface to read pixels from for the lhs parameter given to the method <b>ColorBgra Apply(ColorBgra, ColorBgra)</b>b>.</param></param> /// <param name="lhsOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the lhs Surface.</param> /// <param name="rhs">The Surface to read pixels from for the rhs parameter given to the method <b>ColorBgra Apply(ColorBgra, ColorBgra)</b></param> /// <param name="rhsOffset">The pixel offset that defines the upper-left of the rectangle-of-interest for the rhs Surface.</param> /// <param name="roiSize">The size of the rectangles-of-interest for all Surfaces.</param> public void Apply(Surface dst, Point dstOffset, Surface lhs, Point lhsOffset, Surface rhs, Point rhsOffset, Size roiSize) { // Bounds checking only enabled in Debug builds. #if DEBUG // Create bounding rectangles for each Surface Rectangle dstRect = new Rectangle(dstOffset, roiSize); Rectangle lhsRect = new Rectangle(lhsOffset, roiSize); Rectangle rhsRect = new Rectangle(rhsOffset, roiSize); // Clip those rectangles to those Surface's bounding rectangles Rectangle dstClip = Rectangle.Intersect(dstRect, dst.Bounds); Rectangle lhsClip = Rectangle.Intersect(lhsRect, lhs.Bounds); Rectangle rhsClip = Rectangle.Intersect(rhsRect, rhs.Bounds); // If any of those Rectangles actually got clipped, then throw an exception if (dstRect != dstClip) { throw new ArgumentOutOfRangeException("roiSize", "Destination roi out of bounds"); } if (lhsRect != lhsClip) { throw new ArgumentOutOfRangeException("roiSize", "lhs roi out of bounds"); } if (rhsRect != rhsClip) { throw new ArgumentOutOfRangeException("roiSize", "rhs roi out of bounds"); } #endif // Cache the width and height properties int width = roiSize.Width; int height = roiSize.Height; // Do the work. unsafe { for (int row = 0; row < height; ++row) { ColorBgra *dstPtr = dst.GetPointAddress(dstOffset.X, dstOffset.Y + row); ColorBgra *lhsPtr = lhs.GetPointAddress(lhsOffset.X, lhsOffset.Y + row); ColorBgra *rhsPtr = rhs.GetPointAddress(rhsOffset.X, rhsOffset.Y + row); Apply(dstPtr, lhsPtr, rhsPtr, width); } } }
public unsafe void DrawScansNearestNeighbor(object cpuNumberObj) { int cpuNumber = (int)cpuNumberObj; int inc = Processor.LogicalCpuCount; void *scan0 = src.Scan0.VoidStar; int stride = src.Stride; PointF[] pts = new PointF[1]; for (int i = cpuNumber; i < this.dstScans.Length; i += inc) { Rectangle dstRect = this.dstScans[i]; dstRect.Intersect(dst.Bounds); if (dstRect.Width == 0 || dstRect.Height == 0) { continue; } pts[0] = new PointF(dstRect.Left, dstRect.Top); this.inverses[cpuNumber].TransformPoints(pts); pts[0].X -= this.boundsX; pts[0].Y -= this.boundsY; int fp_srcPtRowX = (int)(pts[0].X * fp_MultFactor); int fp_srcPtRowY = (int)(pts[0].Y * fp_MultFactor); for (int dstY = dstRect.Top; dstY < dstRect.Bottom; ++dstY) { int fp_srcPtColX = fp_srcPtRowX; int fp_srcPtColY = fp_srcPtRowY; fp_srcPtRowX += this.fp_dsxddy; fp_srcPtRowY += this.fp_dsyddy; if (dstY >= 0) { // We render the left side, then the right side, then the in-between pixels. // The reason for this is that the left and right sides have the chance that, // due to lack of enough precision, we will dive off the end of the surface // and into memory that we can't actually read from. To solve this, the left // and right sides will clamp the pixel coordinates that they read from, // and then keep track of when they were able to stomp clamping these values. // Then, rendering the middle part of the scanline is able to be completed // without the expensive clamping operation. int dstX = dstRect.Left; ColorBgra *dstPtr = dst.GetPointAddress(dstX, dstY); ColorBgra *dstPtrEnd = dstPtr + dstRect.Width; int fp_srcPtColLastX = fp_srcPtColX + (this.fp_dsxddx * (dstRect.Width - 1)); int fp_srcPtColLastY = fp_srcPtColY + (this.fp_dsyddx * (dstRect.Width - 1)); // Left side while (dstPtr < dstPtrEnd) { int srcPtColX = (fp_srcPtColX + fp_RoundFactor) >> fp_ShiftFactor; int srcPtColY = (fp_srcPtColY + fp_RoundFactor) >> fp_ShiftFactor; int srcX = Clamp(srcPtColX, 0, src.Width - 1); int srcY = Clamp(srcPtColY, 0, src.Height - 1); * dstPtr = this.src.GetPointUnchecked(srcX, srcY); ++dstPtr; fp_srcPtColX += this.fp_dsxddx; fp_srcPtColY += this.fp_dsyddx; if (srcX == srcPtColX && srcY == srcPtColY) { break; } } ColorBgra *startFastPtr = dstPtr; dstPtr = dstPtrEnd - 1; // Right side while (dstPtr >= startFastPtr) { int srcPtColX = (fp_srcPtColLastX + fp_RoundFactor) >> fp_ShiftFactor; int srcPtColY = (fp_srcPtColLastY + fp_RoundFactor) >> fp_ShiftFactor; int srcX = Clamp(srcPtColX, 0, src.Width - 1); int srcY = Clamp(srcPtColY, 0, src.Height - 1); * dstPtr = this.src.GetPointUnchecked(srcX, srcY); if (srcX == srcPtColX && srcY == srcPtColY) { break; } --dstPtr; fp_srcPtColLastX -= this.fp_dsxddx; fp_srcPtColLastY -= this.fp_dsyddx; } ColorBgra *endFastPtr = dstPtr; // Middle while (startFastPtr < endFastPtr) { int srcPtColX = (fp_srcPtColX + fp_RoundFactor) >> fp_ShiftFactor; int srcPtColY = (fp_srcPtColY + fp_RoundFactor) >> fp_ShiftFactor; // This is GetPointUnchecked inlined -- avoid the overhead, especially, of the call to MemoryBlock.VoidStar // which has the potential to throw an exception which is then NOT inlined. startFastPtr->Bgra = (unchecked (srcPtColX + (ColorBgra *)(((byte *)scan0) + (srcPtColY * stride))))->Bgra; ++startFastPtr; fp_srcPtColX += this.fp_dsxddx; fp_srcPtColY += this.fp_dsyddx; } } } } }
public virtual void Apply(Surface dst, Point dstOffset, Surface src, Point srcOffset, int scanLength) { Apply(dst.GetPointAddress(dstOffset), src.GetPointAddress(srcOffset), scanLength); }
public unsafe override void Render(Surface dst, System.Drawing.Point offset) { if (OwnerList.ScaleFactor < new ScaleFactor(2, 1)) { return; } int[] d2SLookupX = OwnerList.Dst2SrcLookupX; int[] d2SLookupY = OwnerList.Dst2SrcLookupY; int[] s2DLookupX = OwnerList.Src2DstLookupX; int[] s2DLookupY = OwnerList.Src2DstLookupY; ColorBgra[] blackAndWhite = new ColorBgra[2] { ColorBgra.White, ColorBgra.Black }; // draw horizontal lines int sTop = d2SLookupY[offset.Y]; int sBottom = d2SLookupY[offset.Y + dst.Height]; for (int srcY = sTop; srcY <= sBottom; ++srcY) { int dstY = s2DLookupY[srcY]; int dstRow = dstY - offset.Y; if (dst.IsRowVisible(dstRow)) { ColorBgra *dstRowPtr = dst.GetRowAddress(dstRow); ColorBgra *dstRowEndPtr = dstRowPtr + dst.Width; dstRowPtr += offset.X & 1; while (dstRowPtr < dstRowEndPtr) { *dstRowPtr = ColorBgra.Black; dstRowPtr += 2; } } } // draw vertical lines int sLeft = d2SLookupX[offset.X]; int sRight = d2SLookupX[offset.X + dst.Width]; for (int srcX = sLeft; srcX <= sRight; ++srcX) { int dstX = s2DLookupX[srcX]; int dstCol = dstX - offset.X; if (dst.IsColumnVisible(dstX - offset.X)) { byte *dstColPtr = (byte *)dst.GetPointAddress(dstCol, 0); byte *dstColEndPtr = dstColPtr + dst.Stride * dst.Height; dstColPtr += (offset.Y & 1) * dst.Stride; while (dstColPtr < dstColEndPtr) { *((ColorBgra *)dstColPtr) = ColorBgra.Black; dstColPtr += 2 * dst.Stride; } } } }
public override void Apply(Surface dst, Point dstOffset, Surface src, Point srcOffset, int roiLength) { Apply(dst.GetPointAddress(dstOffset), src.GetPointAddress(srcOffset), roiLength); }
public void Apply(Surface surface, Scanline scan) { Apply(surface.GetPointAddress(scan.X, scan.Y), scan.Length); }
public unsafe void Render(Surface surface, Rectangle[] rois, int startIndex, int length) { byte startAlpha; byte endAlpha; if (this.alphaOnly) { ComputeAlphaOnlyValuesFromColors(this.startColor, this.endColor, out startAlpha, out endAlpha); } else { startAlpha = this.startColor.A; endAlpha = this.endColor.A; } for (int ri = startIndex; ri < startIndex + length; ++ri) { Rectangle rect = rois[ri]; if (this.startPoint == this.endPoint) { // Start and End point are the same ... fill with solid color. for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra* pixelPtr = surface.GetPointAddress(rect.Left, y); for (int x = rect.Left; x < rect.Right; ++x) { ColorBgra result; if (this.alphaOnly && this.alphaBlending) { byte resultAlpha = (byte)Utility.FastDivideShortByByte((ushort)(pixelPtr->A * endAlpha), 255); result = *pixelPtr; result.A = resultAlpha; } else if (this.alphaOnly && !this.alphaBlending) { result = *pixelPtr; result.A = endAlpha; } else if (!this.alphaOnly && this.alphaBlending) { result = this.normalBlendOp.Apply(*pixelPtr, this.endColor); } else //if (!this.alphaOnly && !this.alphaBlending) { result = this.endColor; } *pixelPtr = result; ++pixelPtr; } } } else { for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra* pixelPtr = surface.GetPointAddress(rect.Left, y); if (this.alphaOnly && this.alphaBlending) { for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); byte lerpAlpha = this.lerpAlphas[lerpByte]; byte resultAlpha = Utility.FastScaleByteByByte(pixelPtr->A, lerpAlpha); pixelPtr->A = resultAlpha; ++pixelPtr; } } else if (this.alphaOnly && !this.alphaBlending) { for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); byte lerpAlpha = this.lerpAlphas[lerpByte]; pixelPtr->A = lerpAlpha; ++pixelPtr; } } else if (!this.alphaOnly && (this.alphaBlending && (startAlpha != 255 || endAlpha != 255))) { // If we're doing all color channels, and we're doing alpha blending, and if alpha blending is necessary for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); ColorBgra lerpColor = this.lerpColors[lerpByte]; ColorBgra result = this.normalBlendOp.Apply(*pixelPtr, lerpColor); *pixelPtr = result; ++pixelPtr; } } else //if (!this.alphaOnly && !this.alphaBlending) // or sC.A == 255 && eC.A == 255 { for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); ColorBgra lerpColor = this.lerpColors[lerpByte]; *pixelPtr = lerpColor; ++pixelPtr; } } } } } AfterRender(); }
public unsafe void Render(Surface surface, Rectangle[] rois, int startIndex, int length) { byte startAlpha; byte endAlpha; if (this.alphaOnly) { ComputeAlphaOnlyValuesFromColors(this.startColor, this.endColor, out startAlpha, out endAlpha); } else { startAlpha = this.startColor.A; endAlpha = this.endColor.A; } for (int ri = startIndex; ri < startIndex + length; ++ri) { Rectangle rect = rois[ri]; if (this.startPoint == this.endPoint) { // Start and End point are the same ... fill with solid color. for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra *pixelPtr = surface.GetPointAddress(rect.Left, y); for (int x = rect.Left; x < rect.Right; ++x) { ColorBgra result; if (this.alphaOnly && this.alphaBlending) { byte resultAlpha = (byte)Utility.FastDivideShortByByte((ushort)(pixelPtr->A * endAlpha), 255); result = *pixelPtr; result.A = resultAlpha; } else if (this.alphaOnly && !this.alphaBlending) { result = *pixelPtr; result.A = endAlpha; } else if (!this.alphaOnly && this.alphaBlending) { result = this.normalBlendOp.Apply(*pixelPtr, this.endColor); } else //if (!this.alphaOnly && !this.alphaBlending) { result = this.endColor; } *pixelPtr = result; ++pixelPtr; } } } else { for (int y = rect.Top; y < rect.Bottom; ++y) { ColorBgra *pixelPtr = surface.GetPointAddress(rect.Left, y); if (this.alphaOnly && this.alphaBlending) { for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); byte lerpAlpha = this.lerpAlphas[lerpByte]; byte resultAlpha = (byte)Utility.FastScaleByteByByte(pixelPtr->A, lerpAlpha); pixelPtr->A = resultAlpha; ++pixelPtr; } } else if (this.alphaOnly && !this.alphaBlending) { for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); byte lerpAlpha = this.lerpAlphas[lerpByte]; pixelPtr->A = lerpAlpha; ++pixelPtr; } } else if (!this.alphaOnly && (this.alphaBlending && (startAlpha != 255 || endAlpha != 255))) { // If we're doing all color channels, and we're doing alpha blending, and if alpha blending is necessary for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); ColorBgra lerpColor = this.lerpColors[lerpByte]; ColorBgra result = this.normalBlendOp.Apply(*pixelPtr, lerpColor); * pixelPtr = result; ++pixelPtr; } } else //if (!this.alphaOnly && !this.alphaBlending) // or sC.A == 255 && eC.A == 255 { for (int x = rect.Left; x < rect.Right; ++x) { float lerpUnbounded = ComputeUnboundedLerp(x, y); float lerpBounded = BoundLerp(lerpUnbounded); byte lerpByte = (byte)(lerpBounded * 255.0f); ColorBgra lerpColor = this.lerpColors[lerpByte]; * pixelPtr = lerpColor; ++pixelPtr; } } } } } AfterRender(); }