internal static void FromVector4 <TPixel>( Configuration configuration, PixelOperations <TPixel> pixelOperations, Span <Vector4> sourceVectors, Span <TPixel> destPixels, PixelConversionModifiers modifiers) where TPixel : struct, IPixel <TPixel> { Guard.NotNull(configuration, nameof(configuration)); Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); int count = sourceVectors.Length; // Not worth for small buffers: if (count < Vector4ConversionThreshold) { Default.UnsafeFromVector4(sourceVectors, destPixels, modifiers); return; } // TODO: Investigate optimized 1-pass approach! ApplyBackwardConversionModifiers(sourceVectors, modifiers); // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion, // so let's allocate a temporary buffer as usually: using (IMemoryOwner <Rgba32> tempBuffer = configuration.MemoryAllocator.Allocate <Rgba32>(count)) { Span <Rgba32> tempSpan = tempBuffer.Memory.Span; SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows( MemoryMarshal.Cast <Vector4, float>(sourceVectors), MemoryMarshal.Cast <Rgba32, byte>(tempSpan)); pixelOperations.FromRgba32(configuration, tempSpan, destPixels); } }
public static bool IsDefined(this PixelConversionModifiers modifiers, PixelConversionModifiers expected) => (modifiers & expected) == expected;
/// <summary> /// Initializes a new instance of the <see cref="PixelShaderProcessorBase{TPixel}"/> class. /// </summary> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param> protected PixelShaderProcessorBase(Configuration configuration, PixelConversionModifiers modifiers, Image <TPixel> source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.modifiers = modifiers; }
/// <inheritdoc /> internal override void ToVector4(Configuration configuration, ReadOnlySpan <Bgr24> sourcePixels, Span <Vector4> destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); }
/// <inheritdoc /> internal override void FromVector4Destructive(Configuration configuration, Span <Vector4> sourceVectors, Span <Bgr24> destPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, modifiers.Remove(PixelConversionModifiers.Scale | PixelConversionModifiers.Premultiply)); }
internal static void ApplyBackwardConversionModifiers(Span <Vector4> vectors, PixelConversionModifiers modifiers) { if (modifiers.HasFlag(PixelConversionModifiers.Premultiply)) { Numeric.UnPremultiply(vectors); } if (modifiers.HasFlag(PixelConversionModifiers.SRgbCompand)) { SRgbCompanding.Compress(vectors); } }
/// <summary> /// Applies a user defined pixel shader to the image. /// </summary> /// <param name="source">The image this method extends.</param> /// <param name="pixelShader">The user defined pixel shader to use to modify images.</param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PixelShader pixelShader, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PixelShaderProcessor(pixelShader, modifiers));
/// <inheritdoc /> public override void ToVector4(Configuration configuration, ReadOnlySpan <Argb32> sourcePixels, Span <Vector4> destVectors, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, modifiers.Remove(PixelConversionModifiers.Scale)); }
internal static void ApplyBackwardConversionModifiers(Span <Vector4> vectors, PixelConversionModifiers modifiers) { if (modifiers.IsDefined(PixelConversionModifiers.Premultiply)) { Vector4Utilities.UnPremultiply(vectors); } if (modifiers.IsDefined(PixelConversionModifiers.SRgbCompand)) { SRgbCompanding.Compress(vectors); } }
/// <summary> /// Applies the union of <see cref="PixelConversionModifiers.Scale"/> and <see cref="PixelConversionModifiers.SRgbCompand"/>, /// if <paramref name="compand"/> is true, returns unmodified <paramref name="originalModifiers"/> otherwise. /// </summary> /// <remarks> /// <see cref="PixelConversionModifiers.Scale"/> and <see cref="PixelConversionModifiers.SRgbCompand"/> /// should be always used together! /// </remarks> public static PixelConversionModifiers ApplyCompanding( this PixelConversionModifiers originalModifiers, bool compand) => compand ? originalModifiers | PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand : originalModifiers;
/// <summary> /// Initializes a new instance of the <see cref="PixelRowDelegateProcessor"/> class. /// </summary> /// <param name="pixelRowOperation">The user defined, row processing delegate.</param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> public PixelRowDelegateProcessor(PixelRowOperation pixelRowOperation, PixelConversionModifiers modifiers) { this.PixelRowOperation = pixelRowOperation; this.Modifiers = modifiers; }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration) { // Handle resize dimensions identical to the original if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle) { // The cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.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) { var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; ParallelHelper.IterateRows( workingRect, configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) { // Y coordinates of source points Span <TPixel> sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span <TPixel> targetRow = destination.GetPixelRowSpan(y); for (int x = minX; x < maxX; x++) { // X coordinates of source points targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; } } }); return; } int sourceHeight = source.Height; PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply; if (this.Compand) { conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand; } // 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. using (Buffer2D <Vector4> firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D <Vector4>(sourceHeight, width)) { firstPassPixelsTransposed.MemorySource.Clear(); var processColsRect = new Rectangle(0, 0, source.Width, sourceRectangle.Bottom); ParallelHelper.IterateRowsWithTempBuffer <Vector4>( processColsRect, configuration, (rows, tempRowBuffer) => { for (int y = rows.Min; y < rows.Max; y++) { Span <TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); Span <Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX); PixelOperations <TPixel> .Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers); ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; for (int x = minX; x < maxX; x++) { ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan); } } }); var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY); // Now process the rows. ParallelHelper.IterateRowsWithTempBuffer <Vector4>( processRowsRect, configuration, (rows, tempRowBuffer) => { Span <Vector4> tempRowSpan = tempRowBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { // Ensure offsets are normalized for cropping and padding. ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); for (int x = 0; x < width; x++) { Span <Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); // Destination color components Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); } Span <TPixel> targetRowSpan = destination.GetPixelRowSpan(y); PixelOperations <TPixel> .Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers); } }); } }
/// <summary> /// Initializes a new instance of the <see cref="PositionAwarePixelRowDelegateProcessor"/> class. /// </summary> /// <param name="pixelRowOperation">The user defined, position aware, row processing delegate.</param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> public PositionAwarePixelRowDelegateProcessor(PixelRowOperation <Point> pixelRowOperation, PixelConversionModifiers modifiers) { this.PixelRowOperation = pixelRowOperation; this.Modifiers = modifiers; }
public static PixelConversionModifiers Remove( this PixelConversionModifiers modifiers, PixelConversionModifiers removeThis) => modifiers & ~removeThis;
/// <summary> /// Applies a user defined pixel shader to the image. /// </summary> /// <param name="source">The image this method extends.</param> /// <param name="pixelShader">The user defined pixel shader to use to modify images.</param> /// <param name="rectangle"> /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// </param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> public static IImageProcessingContext ApplyPixelShaderProcessor(this IImageProcessingContext source, PositionAwarePixelShader pixelShader, Rectangle rectangle, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PositionAwarePixelShaderProcessor(pixelShader, modifiers), rectangle);
/// <inheritdoc /> public override void FromVector4Destructive(Configuration configuration, Span <Vector4> sourceVectors, Span <Argb32> destinationPixels, PixelConversionModifiers modifiers) { Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destinationPixels, modifiers.Remove(PixelConversionModifiers.Scale)); }
/// <summary> /// Applies a user defined processing delegate to the image. /// </summary> /// <param name="source">The image this method extends.</param> /// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation rowOperation, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PixelRowDelegateProcessor(rowOperation, modifiers));
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination) { Rectangle sourceRectangle = this.SourceRectangle; Configuration configuration = this.Configuration; // Handle resize dimensions identical to the original if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.targetRectangle) { // The cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } int width = this.targetWidth; int height = this.targetHeight; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; int startY = this.targetRectangle.Y; int startX = this.targetRectangle.X; var targetWorkingRect = Rectangle.Intersect( this.targetRectangle, new Rectangle(0, 0, width, height)); if (this.resampler is NearestNeighborResampler) { // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height; ParallelHelper.IterateRows( targetWorkingRect, configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) { // Y coordinates of source points Span <TPixel> sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span <TPixel> targetRow = destination.GetPixelRowSpan(y); for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) { // X coordinates of source points targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; } } }); return; } PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply.ApplyCompanding(this.compand); BufferArea <TPixel> sourceArea = source.PixelBuffer.GetArea(sourceRectangle); // To reintroduce parallel processing, we to launch multiple workers // for different row intervals of the image. using (var worker = new ResizeWorker <TPixel>( configuration, sourceArea, conversionModifiers, this.horizontalKernelMap, this.verticalKernelMap, width, targetWorkingRect, this.targetRectangle.Location)) { worker.Initialize(); var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom); worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); } }
/// <summary> /// Applies a user defined processing delegate to the image. /// </summary> /// <param name="source">The image this method extends.</param> /// <param name="rowOperation">The user defined processing delegate to use to modify image rows.</param> /// <param name="rectangle"> /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// </param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> /// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns> public static IImageProcessingContext ProcessPixelRowsAsVector4(this IImageProcessingContext source, PixelRowOperation <Point> rowOperation, Rectangle rectangle, PixelConversionModifiers modifiers) => source.ApplyProcessor(new PositionAwarePixelRowDelegateProcessor(rowOperation, modifiers), rectangle);
/// <summary> /// Initializes a new instance of the <see cref="PixelRowDelegateProcessorBase{TPixel}"/> class. /// </summary> /// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> /// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param> /// <param name="sourceRectangle">The source area to process for the current processor instance.</param> protected PixelRowDelegateProcessorBase(Configuration configuration, PixelConversionModifiers modifiers, Image <TPixel> source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) => this.modifiers = modifiers;
/// <summary> /// Initializes a new instance of the <see cref="PositionAwarePixelShaderProcessor"/> class. /// </summary> /// <param name="pixelShader"> /// The user defined pixel shader to use to modify images. /// </param> /// <param name="modifiers">The <see cref="PixelConversionModifiers"/> to apply during the pixel conversions.</param> public PositionAwarePixelShaderProcessor(PositionAwarePixelShader pixelShader, PixelConversionModifiers modifiers) { this.PixelShader = pixelShader; this.Modifiers = modifiers; }