public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType colorType) { // arrange var image = new Image <Rgba32>(50, 50); var encoder = new PngEncoder() { TransparentColorMode = PngTransparentColorMode.Clear, ColorType = colorType }; Rgba32 rgba32 = Color.Blue; for (int y = 0; y < image.Height; y++) { System.Span <Rgba32> rowSpan = image.GetPixelRowSpan(y); // Half of the test image should be transparent. if (y > 25) { rgba32.A = 0; } for (int x = 0; x < image.Width; x++) { rowSpan[x].FromRgba32(rgba32); } } // act using var memStream = new MemoryStream(); image.Save(memStream, encoder); // assert memStream.Position = 0; using var actual = Image.Load <Rgba32>(memStream); Rgba32 expectedColor = Color.Blue; if (colorType == PngColorType.Grayscale || colorType == PngColorType.GrayscaleWithAlpha) { var luminance = ImageMaths.Get8BitBT709Luminance(expectedColor.R, expectedColor.G, expectedColor.B); expectedColor = new Rgba32(luminance, luminance, luminance); } for (int y = 0; y < actual.Height; y++) { System.Span <Rgba32> rowSpan = actual.GetPixelRowSpan(y); if (y > 25) { expectedColor = Color.Transparent; } for (int x = 0; x < actual.Width; x++) { Assert.Equal(expectedColor, rowSpan[x]); } } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { byte threshold = (byte)MathF.Round(this.Definition.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; PixelPair <TPixel> pair = this.GetClosestPixelPair(ref sourcePixel); Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { Span <TPixel> row = source.GetPixelRowSpan(y); for (int x = startX; x < endX; x++) { sourcePixel = row[x]; // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimization. if (!previousPixel.Equals(sourcePixel)) { pair = this.GetClosestPixelPair(ref sourcePixel); // No error to spread, exact match. if (sourcePixel.Equals(pair.First)) { continue; } sourcePixel.ToRgba32(ref rgba); luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First; this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY); } } }
public void Gray8_FromRgba32(byte rgb) { // Arrange Gray8 gray = default; byte expected = ImageMaths.Get8BitBT709Luminance(rgb, rgb, rgb); // Act gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); byte actual = gray.PackedValue; // Assert Assert.Equal(expected, actual); }
public void La16_FromRgba32(byte rgb) { // Arrange La16 gray = default; byte expected = ImageMaths.Get8BitBT709Luminance(rgb, rgb, rgb); // Act gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); byte actual = gray.L; // Assert Assert.Equal(expected, actual); Assert.Equal(255, gray.A); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { IOrderedDither dither = this.definition.Dither; TPixel upperColor = this.definition.UpperColor.ToPixel <TPixel>(); TPixel lowerColor = this.definition.LowerColor.ToPixel <TPixel>(); bool isAlphaOnly = typeof(TPixel) == typeof(A8); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { Span <TPixel> row = source.GetPixelRowSpan(y); for (int x = startX; x < endX; x++) { sourcePixel = row[x]; // Check if this is the same as the last pixel. If so use that value // rather than calculating it again. This is an inexpensive optimization. if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; } dither.Dither(source, sourcePixel, upperColor, lowerColor, luminance, x, y); } } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { byte threshold = (byte)MathF.Round(this.definition.Threshold * 255F); TPixel upper = this.definition.UpperColor.ToPixel <TPixel>(); TPixel lower = this.definition.LowerColor.ToPixel <TPixel>(); Rectangle sourceRectangle = this.SourceRectangle; Configuration configuration = this.Configuration; var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startY = interest.Y; int endY = interest.Bottom; int startX = interest.X; int endX = interest.Right; bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); ParallelHelper.IterateRows( workingRect, configuration, rows => { Rgba32 rgba = default; for (int y = rows.Min; y < rows.Max; y++) { Span <TPixel> row = source.GetPixelRowSpan(y); for (int x = startX; x < endX; x++) { ref TPixel color = ref row[x]; color.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); color = luminance >= threshold ? upper : lower; } } }); }