public void SRgbCompanding_IsCorrect() { const float input = .667F; float e = SRgbCompanding.Expand(input); float c = SRgbCompanding.Compress(e); CompandingIsCorrectImpl(e, c, .40242353F, input); }
public void SRgbCompanding_Compress_VectorSpan(int length) { var rnd = new Random(42); Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); Vector4[] expected = source.Select(v => SRgbCompanding.Compress(v)).ToArray(); SRgbCompanding.Compress(source); Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); }
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); } }
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); } }
public override float Compress(float channel) => SRgbCompanding.Compress(channel);
/// <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; // 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); Span <Vector4> tempRowSpan = tempRowBuffer.Span; PixelOperations <TPixel> .Instance.ToVector4(sourceRow, tempRowSpan, sourceRow.Length); Vector4Utils.Premultiply(tempRowSpan); ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; if (this.Compand) { SRgbCompanding.Expand(tempRowSpan); } for (int x = minX; x < maxX; x++) { ResizeKernel window = this.horizontalKernelMap.Kernels[x - startX]; Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = window.Convolve(tempRowSpan, sourceX); } } }); 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 window = this.verticalKernelMap.Kernels[y - startY]; ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); for (int x = 0; x < width; x++) { Span <Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x); // Destination color components Unsafe.Add(ref tempRowBase, x) = window.Convolve(firstPassColumn, sourceY); } Vector4Utils.UnPremultiply(tempRowSpan); if (this.Compand) { SRgbCompanding.Compress(tempRowSpan); } Span <TPixel> targetRowSpan = destination.GetPixelRowSpan(y); PixelOperations <TPixel> .Instance.PackFromVector4(tempRowSpan, targetRowSpan, tempRowSpan.Length); } }); } }