/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { float brightness = this.Value / 100F; 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; } 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; // TODO: Check this with other formats. Vector4 vector = sourcePixels[offsetX, offsetY].ToVector4().Expand(); Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness); vector = new Vector4(transformed, vector.W); TColor packed = default(TColor); packed.PackFromVector4(vector.Compress()); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { float contrast = (100F + this.Value) / 100F; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Vector4 contrastVector = new Vector4(contrast, contrast, contrast, 1); Vector4 shiftVector = new Vector4(.5F, .5F, .5F, 1); // 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 <TPixel> 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 vector = sourcePixels[offsetX, offsetY].ToVector4().Expand(); vector -= shiftVector; vector *= contrastVector; vector += shiftVector; TPixel packed = default(TPixel); packed.PackFromVector4(vector.Compress()); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { float contrast = (100F + this.Value) / 100F; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; var contrastVector = new Vector4(contrast, contrast, contrast, 1); var shiftVector = new Vector4(.5F, .5F, .5F, 1); // 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; } Parallel.For( minY, maxY, configuration.ParallelOptions, y => { Span <TPixel> row = source.GetPixelRowSpan(y - startY); for (int x = minX; x < maxX; x++) { ref TPixel pixel = ref row[x - startX]; Vector4 vector = pixel.ToVector4().Expand(); vector -= shiftVector; vector *= contrastVector; vector += shiftVector; pixel.PackFromVector4(vector.Compress()); } }); }
/// <summary> /// Applies the color matrix against the given color. /// </summary> /// <param name="color">The source color.</param> /// <param name="matrix">The matrix.</param> /// <param name="compand">Whether to compand the color during processing.</param> /// <returns> /// The <see cref="Color"/>. /// </returns> private TColor ApplyMatrix(TColor color, Matrix4x4 matrix, bool compand) { Vector4 vector = color.ToVector4(); if (compand) { vector = vector.Expand(); } vector = Vector4.Transform(vector, matrix); TColor packed = default(TColor); packed.PackFromVector4(compand ? vector.Compress() : vector); return(packed); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { float brightness = this.Value / 100F; 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; } Parallel.For( minY, maxY, this.ParallelOptions, y => { Span <TPixel> row = source.GetRowSpan(y - startY); for (int x = minX; x < maxX; x++) { ref TPixel pixel = ref row[x - startX]; // TODO: Check this with other formats. Vector4 vector = pixel.ToVector4().Expand(); Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness); vector = new Vector4(transformed, vector.W); pixel.PackFromVector4(vector.Compress()); } }); }
/// <summary> /// Applies the color matrix against the given color. /// </summary> /// <param name="color">The source color.</param> /// <param name="matrix">The matrix.</param> /// <param name="compand">Whether to compand the color during processing.</param> /// <returns> /// The <see cref="Color"/>. /// </returns> private TColor ApplyMatrix(TColor color, Matrix4x4 matrix, bool compand) { Vector4 vector = color.ToVector4(); if (compand) { vector = vector.Expand(); } Vector3 transformed = Vector3.Transform(new Vector3(vector.X, vector.Y, vector.Z), matrix); vector = new Vector4(transformed, vector.W); TColor packed = default(TColor); packed.PackFromVector4(compand ? vector.Compress() : vector); return(packed); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { // Jump out, we'll deal with that later. if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) { return; } int width = this.Width; int height = this.Height; int startY = this.ResizeRectangle.Y; int endY = this.ResizeRectangle.Bottom; int startX = this.ResizeRectangle.X; int endX = this.ResizeRectangle.Right; int minX = Math.Max(0, startX); int maxX = Math.Min(width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); TColor[] target = new TColor[width * height]; if (this.Sampler is NearestNeighborResampler) { // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PixelAccessor <TColor> targetPixels = target.Lock <TColor>(width, height)) { Parallel.For( minY, maxY, this.ParallelOptions, y => { // Y coordinates of source points int originY = (int)((y - startY) * heightFactor); for (int x = minX; x < maxX; x++) { // X coordinates of source points targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; } }); } // Break out now. source.SetPixels(width, height, target); return; } // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. TColor[] firstPass = new TColor[width * source.Height]; using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PixelAccessor <TColor> firstPassPixels = firstPass.Lock <TColor>(width, source.Height)) using (PixelAccessor <TColor> targetPixels = target.Lock <TColor>(width, height)) { Parallel.For( 0, sourceRectangle.Height, this.ParallelOptions, y => { for (int x = minX; x < maxX; x++) { // Ensure offsets are normalised for cropping and padding. Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; // Destination color components Vector4 destination = Vector4.Zero; for (int i = 0; i < horizontalValues.Length; i++) { Weight xw = horizontalValues[i]; destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; } TColor d = default(TColor); d.PackFromVector4(destination.Compress()); firstPassPixels[x, y] = d; } }); // Now process the rows. Parallel.For( minY, maxY, this.ParallelOptions, y => { // Ensure offsets are normalised for cropping and padding. Weight[] verticalValues = this.VerticalWeights[y - startY].Values; for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = Vector4.Zero; for (int i = 0; i < verticalValues.Length; i++) { Weight yw = verticalValues[i]; destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; } TColor d = default(TColor); d.PackFromVector4(destination.Compress()); targetPixels[x, y] = d; } }); } source.SetPixels(width, height, target); }
/// <inheritdoc/> protected override unsafe void OnApply(ImageFrame <TPixel> source, ImageFrame <TPixel> cloned, Rectangle sourceRectangle, Configuration configuration) { // Jump out, we'll deal with that later. if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle) { // the cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(cloned.GetPixelSpan()); return; } int width = this.Width; int height = this.Height; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; int startY = this.ResizeRectangle.Y; int endY = this.ResizeRectangle.Bottom; int startX = this.ResizeRectangle.X; int endX = this.ResizeRectangle.Right; int minX = Math.Max(0, startX); int maxX = Math.Min(width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); if (this.Sampler is NearestNeighborResampler) { // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; Parallel.For( minY, maxY, configuration.ParallelOptions, y => { // Y coordinates of source points Span <TPixel> sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span <TPixel> targetRow = cloned.GetPixelRowSpan(y); for (int x = minX; x < maxX; x++) { // X coordinates of source points targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; } }); return; } // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! using (var firstPassPixels = new Buffer2D <Vector4>(width, source.Height)) { firstPassPixels.Clear(); Parallel.For( 0, sourceRectangle.Bottom, configuration.ParallelOptions, y => { // TODO: Without Parallel.For() this buffer object could be reused: using (var tempRowBuffer = new Buffer <Vector4>(source.Width)) { Span <Vector4> firstPassRow = firstPassPixels.GetRowSpan(y); Span <TPixel> sourceRow = source.GetPixelRowSpan(y); PixelOperations <TPixel> .Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length); if (this.Compand) { for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; firstPassRow[x] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); } } else { for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; firstPassRow[x] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); } } } }); // Now process the rows. Parallel.For( minY, maxY, configuration.ParallelOptions, y => { // Ensure offsets are normalised for cropping and padding. WeightsWindow window = this.VerticalWeights.Weights[y - startY]; Span <TPixel> targetRow = cloned.GetPixelRowSpan(y); if (this.Compand) { for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); destination = destination.Compress(); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(destination); } } else { for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); ref TPixel pixel = ref targetRow[x]; pixel.PackFromVector4(destination); } } });
/// <inheritdoc/> protected override unsafe void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { // Jump out, we'll deal with that later. if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) { return; } int width = this.Width; int height = this.Height; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; int startY = this.ResizeRectangle.Y; int endY = this.ResizeRectangle.Bottom; int startX = this.ResizeRectangle.X; int endX = this.ResizeRectangle.Right; int minX = Math.Max(0, startX); int maxX = Math.Min(width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(height, endY); if (this.Sampler is NearestNeighborResampler) { // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; using (PixelAccessor <TColor> targetPixels = new PixelAccessor <TColor>(width, height)) { using (PixelAccessor <TColor> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { // Y coordinates of source points int originY = (int)(((y - startY) * heightFactor) + sourceY); for (int x = minX; x < maxX; x++) { // X coordinates of source points targetPixels[x, y] = sourcePixels[(int)(((x - startX) * widthFactor) + sourceX), originY]; } }); } // Break out now. source.SwapPixelsBuffers(targetPixels); return; } } // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! using (PixelAccessor <TColor> targetPixels = new PixelAccessor <TColor>(width, height)) { using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (Buffer2D <Vector4> firstPassPixels = new Buffer2D <Vector4>(width, source.Height)) { firstPassPixels.Clear(); Parallel.For( 0, sourceRectangle.Bottom, this.ParallelOptions, y => { // TODO: Without Parallel.For() this buffer object could be reused: using (Buffer <Vector4> tempRowBuffer = new Buffer <Vector4>(sourcePixels.Width)) { BufferSpan <TColor> sourceRow = sourcePixels.GetRowSpan(y); BulkPixelOperations <TColor> .Instance.ToVector4( sourceRow, tempRowBuffer, sourceRow.Length); if (this.Compand) { for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer); } } else { for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer); } } } }); // Now process the rows. Parallel.For( minY, maxY, this.ParallelOptions, y => { // Ensure offsets are normalised for cropping and padding. WeightsWindow window = this.VerticalWeights.Weights[y - startY]; if (this.Compand) { for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); destination = destination.Compress(); TColor d = default(TColor); d.PackFromVector4(destination); targetPixels[x, y] = d; } } else { for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); TColor d = default(TColor); d.PackFromVector4(destination); targetPixels[x, y] = d; } } }); } source.SwapPixelsBuffers(targetPixels); } }