/// <inheritdoc /> internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); using (Buffer <float> buffer = new Buffer <float>(scanlineBuffer)) { BufferSpan <float> slice = buffer.Slice(offset); for (int xPos = 0; xPos < scanlineWidth; xPos++) { int targetX = xPos + x; int targetY = y; float opacity = slice[xPos]; if (opacity > Constants.Epsilon) { Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); Vector4 sourceVector = this.colorVector; Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); TColor packed = default(TColor); packed.PackFromVector4(finalColor); this.Target[targetX, targetY] = packed; } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } Vector4 backgroundColor = this.Value.ToVector4(); using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); float a = color.W; if (a < 1 && a > 0) { color = Vector4BlendTransforms.PremultipliedLerp(backgroundColor, color, .5F); } if (Math.Abs(a) < Epsilon) { color = backgroundColor; } TColor packed = default(TColor); packed.PackFromVector4(color); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } // we could possibly do some optermising by having knowledge about the individual brushes operate // for example If brush is SolidBrush<TColor, TPacked> then we could just get the color upfront // and skip using the IBrushApplicator<TColor, TPacked>?. using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (IBrushApplicator <TColor, TPacked> applicator = this.brush.CreateApplicator(sourceRectangle)) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; Vector2 currentPoint = default(Vector2); for (int x = minX; x < maxX; x++) { int offsetX = x - startX; int offsetColorX = x - minX; currentPoint.X = offsetX; currentPoint.Y = offsetY; Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceVector = applicator.GetColor(currentPoint).ToVector4(); var finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, 1); TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TColor vignetteColor = this.VignetteColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float rX = this.RadiusX > 0 ? Math.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; float rY = this.RadiusY > 0 ? Math.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; float maxDistance = (float)Math.Sqrt((rX * rX) + (rY * rY)); // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } using (PixelAccessor <TColor> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); TColor packed = default(TColor); packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, vignetteColor.ToVector4(), .9F * (distance / maxDistance))); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <summary> /// Gets the color for a single pixel. /// </summary> /// <param name="point">The point.</param> /// <returns> /// The color /// </returns> public override TColor GetColor(Vector2 point) { // Offset the requested pixel by the value in the rectangle (the shapes position) TColor result = this.source[(int)point.X, (int)point.Y]; Vector4 background = result.ToVector4(); float distance = Vector4.DistanceSquared(background, this.sourceColor); if (distance <= this.threshold) { var lerpAmount = (this.threshold - distance) / this.threshold; Vector4 blended = Vector4BlendTransforms.PremultipliedLerp(background, this.targetColor, lerpAmount); result.PackFromVector4(blended); } return(result); }
/// <inheritdoc/> protected override void Apply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TColor glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float maxDistance = this.Radius > 0 ? Math.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; Ellipse ellipse = new Ellipse(new Point(centre), maxDistance, maxDistance); // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); TColor packed = default(TColor); packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance)))); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { if (this.Image.Bounds.Size != this.Size) { // should Resize be moved to core? this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); } // Align start/end positions. Rectangle bounds = this.Image.Bounds; int minX = Math.Max(this.Location.X, sourceRectangle.X); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); float alpha = this.Alpha / 100F; using (PixelAccessor <TColor> toBlendPixels = this.Image.Lock()) using (PixelAccessor <TColor> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { for (int x = minX; x < maxX; x++) { Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4(); // Lerping colors is dependent on the alpha of the blended color backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha); TColor packed = default(TColor); packed.PackFromVector4(backgroundVector); sourcePixels[x, y] = packed; } }); } }
/// <summary> /// Gets the color for a single pixel. /// </summary> /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <returns> /// The color /// </returns> internal override TColor this[int x, int y] { get { // Offset the requested pixel by the value in the rectangle (the shapes position) TColor result = this.Target[x, y]; Vector4 background = result.ToVector4(); float distance = Vector4.DistanceSquared(background, this.sourceColor); if (distance <= this.threshold) { float lerpAmount = (this.threshold - distance) / this.threshold; Vector4 blended = Vector4BlendTransforms.PremultipliedLerp( background, this.targetColor, lerpAmount); result.PackFromVector4(blended); } return(result); } }
/// <inheritdoc /> internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); using (PinnedBuffer <float> buffer = new PinnedBuffer <float>(scanlineBuffer)) { BufferPointer <float> slice = buffer.Slice(offset); for (int xPos = 0; xPos < scanlineWidth; xPos++) { int targetX = xPos + x; int targetY = y; float opacity = slice[xPos]; if (opacity > Constants.Epsilon) { Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); Vector4 sourceVector = backgroundVector; float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor); if (distance <= this.threshold) { float lerpAmount = (this.threshold - distance) / this.threshold; sourceVector = Vector4BlendTransforms.PremultipliedLerp( sourceVector, this.targetColor, lerpAmount); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); TColor packed = default(TColor); packed.PackFromVector4(finalColor); this.Target[targetX, targetY] = packed; } } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PenApplicator <TColor> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds)) { Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; int startX = rect.X - PaddingFactor; int endX = rect.Right + PaddingFactor; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right, endX); int minY = Math.Max(sourceRectangle.Top, polyStartY); int maxY = Math.Min(sourceRectangle.Bottom, polyEndY); // Align start/end positions. minX = Math.Max(0, minX); maxX = Math.Min(source.Width, maxX); minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { polyStartY = 0; } Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - polyStartY; for (int x = minX; x < maxX; x++) { // TODO add find intersections code to skip and scan large regions of this. int offsetX = x - startX; PointInfo info = this.Path.GetPointInfo(offsetX, offsetY); ColoredPointInfo <TColor> color = applicator.GetColor(offsetX, offsetY, info); float opacity = this.Opacity(color.DistanceFromElement); if (opacity > Constants.Epsilon) { Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceVector = color.Color.ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); finalColor.W = backgroundVector.W; TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[offsetX, offsetY] = packed; } } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { Rectangle rect = this.Region.Bounds; int polyStartY = sourceRectangle.Y - DrawPadding; int polyEndY = sourceRectangle.Bottom + DrawPadding; int startX = sourceRectangle.X - DrawPadding; int endX = sourceRectangle.Right + DrawPadding; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right - 1, endX); int minY = Math.Max(sourceRectangle.Top, polyStartY); int maxY = Math.Min(sourceRectangle.Bottom - 1, polyEndY); // Align start/end positions. minX = Math.Max(0, minX); maxX = Math.Min(source.Width, maxX); minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); ArrayPool <float> arrayPool = ArrayPool <float> .Shared; int maxIntersections = this.Region.MaxIntersections; using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (BrushApplicator <TColor> applicator = this.Brush.CreateApplicator(sourcePixels, rect)) { Parallel.For( minY, maxY, this.ParallelOptions, (int y) => { float[] buffer = arrayPool.Rent(maxIntersections); try { float right = endX; // foreach line we get all the points where this line crosses the polygon int pointsFound = this.Region.ScanY(y, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothing on this line skip return; } QuickSort(buffer, pointsFound); int currentIntersection = 0; float nextPoint = buffer[0]; float lastPoint = float.MinValue; bool isInside = false; for (int x = minX; x < maxX; x++) { if (!isInside) { if (x < (nextPoint - DrawPadding) && x > (lastPoint + DrawPadding)) { if (nextPoint == right) { // we are in the ends run skip it x = maxX; continue; } // lets just jump forward x = (int)Math.Floor(nextPoint) - DrawPadding; } } bool onCorner = false; // there seems to be some issue with this switch. if (x >= nextPoint) { currentIntersection++; lastPoint = nextPoint; if (currentIntersection == pointsFound) { nextPoint = right; } else { nextPoint = buffer[currentIntersection]; // double point from a corner flip the bit back and move on again if (nextPoint == lastPoint) { onCorner = true; isInside ^= true; currentIntersection++; if (currentIntersection == pointsFound) { nextPoint = right; } else { nextPoint = buffer[currentIntersection]; } } } isInside ^= true; } float opacity = 1; if (!isInside && !onCorner) { if (this.Options.Antialias) { float distance = float.MaxValue; if (x == lastPoint || x == nextPoint) { // we are to far away from the line distance = 0; } else if (nextPoint - AntialiasFactor < x) { // we are near the left of the line distance = nextPoint - x; } else if (lastPoint + AntialiasFactor > x) { // we are near the right of the line distance = x - lastPoint; } else { // we are to far away from the line continue; } opacity = 1 - (distance / AntialiasFactor); } else { continue; } } if (opacity > Constants.Epsilon) { Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); Vector4 sourceVector = applicator[x, y].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[x, y] = packed; } } } finally { arrayPool.Return(buffer); } }); if (this.Options.Antialias) { // we only need to do the X can for antialiasing purposes Parallel.For( minX, maxX, this.ParallelOptions, (int x) => { float[] buffer = arrayPool.Rent(maxIntersections); try { float left = polyStartY; float right = polyEndY; // foreach line we get all the points where this line crosses the polygon int pointsFound = this.Region.ScanX(x, buffer, maxIntersections, 0); if (pointsFound == 0) { // nothign on this line skip return; } QuickSort(buffer, pointsFound); int currentIntersection = 0; float nextPoint = buffer[0]; float lastPoint = left; bool isInside = false; for (int y = minY; y < maxY; y++) { if (!isInside) { if (y < (nextPoint - DrawPadding) && y > (lastPoint + DrawPadding)) { if (nextPoint == right) { // we are in the ends run skip it y = maxY; continue; } // lets just jump forward y = (int)Math.Floor(nextPoint) - DrawPadding; } } else { if (y < nextPoint - DrawPadding) { if (nextPoint == right) { // we are in the ends run skip it y = maxY; continue; } // lets just jump forward y = (int)Math.Floor(nextPoint); } } bool onCorner = false; if (y >= nextPoint) { currentIntersection++; lastPoint = nextPoint; if (currentIntersection == pointsFound) { nextPoint = right; } else { nextPoint = buffer[currentIntersection]; // double point from a corner flip the bit back and move on again if (nextPoint == lastPoint) { onCorner = true; isInside ^= true; currentIntersection++; if (currentIntersection == pointsFound) { nextPoint = right; } else { nextPoint = buffer[currentIntersection]; } } } isInside ^= true; } float opacity = 1; if (!isInside && !onCorner) { if (this.Options.Antialias) { float distance = float.MaxValue; if (y == lastPoint || y == nextPoint) { // we are to far away from the line distance = 0; } else if (nextPoint - AntialiasFactor < y) { // we are near the left of the line distance = nextPoint - y; } else if (lastPoint + AntialiasFactor > y) { // we are near the right of the line distance = y - lastPoint; } else { // we are to far away from the line continue; } opacity = 1 - (distance / AntialiasFactor); } else { continue; } } // don't set full opactiy color as it will have been gotten by the first scan if (opacity > Constants.Epsilon && opacity < 1) { Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); Vector4 sourceVector = applicator[x, y].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); finalColor.W = backgroundVector.W; TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[x, y] = packed; } } } finally { arrayPool.Return(buffer); } }); } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { using (IPenApplicator <TColor, TPacked> applicator = this.pen.CreateApplicator(this.region)) { var rect = RectangleF.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; int startX = rect.X - PaddingFactor; int endX = rect.Right + PaddingFactor; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right, endX); int minY = Math.Max(sourceRectangle.Top, polyStartY); int maxY = Math.Min(sourceRectangle.Bottom, polyEndY); // Align start/end positions. minX = Math.Max(0, minX); maxX = Math.Min(source.Width, maxX); minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { polyStartY = 0; } using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - polyStartY; var currentPoint = default(Vector2); for (int x = minX; x < maxX; x++) { int offsetX = x - startX; currentPoint.X = offsetX; currentPoint.Y = offsetY; var dist = Closest(currentPoint); var color = applicator.GetColor(dist); var opacity = this.Opacity(color.DistanceFromElement); if (opacity > Epsilon) { int offsetColorX = x - minX; Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceVector = color.Color.ToVector4(); var finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); finalColor.W = backgroundVector.W; TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[offsetX, offsetY] = packed; } } }); } } }