Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use more accurate bit for alpha lookup. #2473

Merged
merged 2 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,13 @@ private static int DistanceSquared(Rgba32 a, Rgba32 b)
/// The granularity of the cache has been determined based upon the current
/// suite of test images and provides the lowest possible memory usage while
/// providing enough match accuracy.
/// Entry count is currently limited to 1185921 entries (2371842 bytes ~2.26MB).
/// Entry count is currently limited to 2335905 entries (4671810 bytes ~4.45MB).
/// </para>
/// </remarks>
private unsafe struct ColorDistanceCache : IDisposable
{
private const int IndexBits = 5;
private const int IndexAlphaBits = 5;
private const int IndexAlphaBits = 6;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be zero when TPixel doesn't have an alpha. It might be worth to eventually implement some trick to have two versions of EuclideanPixelMap, so we save memory when working with RGB pngs and gifs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. We could easily preserve much more accurate matches in less space.

private const int IndexCount = (1 << IndexBits) + 1;
private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1;
private const int RgbShift = 8 - IndexBits;
Expand Down
2 changes: 1 addition & 1 deletion tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
Expand Down Expand Up @@ -537,7 +538,6 @@ public void Issue2209_Decode_HasTransparencyIsTrue<TPixel>(TestImageProvider<TPi
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
image.DebugSave(provider);
PngMetadata metadata = image.Metadata.GetPngMetadata();
Assert.True(metadata.HasTransparency);
}
Expand Down
64 changes: 39 additions & 25 deletions tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ public void IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provid
PngFilterMethod.Adaptive,
PngBitDepth.Bit8,
interlaceMode,
appendPixelType: true,
appendPngColorType: true);
appendPngColorType: true,
appendPixelType: true);
}
}

Expand Down Expand Up @@ -321,7 +321,7 @@ public void WritesFileMarker<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
using var ms = new MemoryStream();
using MemoryStream ms = new();
image.Save(ms, PngEncoder);

byte[] data = ms.ToArray().Take(8).ToArray();
Expand All @@ -344,13 +344,13 @@ public void WritesFileMarker<TPixel>(TestImageProvider<TPixel> provider)
[MemberData(nameof(RatioFiles))]
public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit)
{
var testFile = TestFile.Create(imagePath);
TestFile testFile = TestFile.Create(imagePath);
using Image<Rgba32> input = testFile.CreateRgba32Image();
using var memStream = new MemoryStream();
using MemoryStream memStream = new();
input.Save(memStream, PngEncoder);

memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
ImageMetadata meta = output.Metadata;
Assert.Equal(xResolution, meta.HorizontalResolution);
Assert.Equal(yResolution, meta.VerticalResolution);
Expand All @@ -361,13 +361,13 @@ public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolut
[MemberData(nameof(PngBitDepthFiles))]
public void Encode_PreserveBits(string imagePath, PngBitDepth pngBitDepth)
{
var testFile = TestFile.Create(imagePath);
TestFile testFile = TestFile.Create(imagePath);
using Image<Rgba32> input = testFile.CreateRgba32Image();
using var memStream = new MemoryStream();
using MemoryStream memStream = new();
input.Save(memStream, PngEncoder);

memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
PngMetadata meta = output.Metadata.GetPngMetadata();

Assert.Equal(pngBitDepth, meta.BitDepth);
Expand All @@ -380,8 +380,8 @@ public void Encode_PreserveBits(string imagePath, PngBitDepth pngBitDepth)
public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType colorType)
{
// arrange
var image = new Image<Rgba32>(50, 50);
var encoder = new PngEncoder()
Image<Rgba32> image = new(50, 50);
PngEncoder encoder = new()
{
TransparentColorMode = PngTransparentColorMode.Clear,
ColorType = colorType
Expand All @@ -391,7 +391,7 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color
{
for (int y = 0; y < image.Height; y++)
{
System.Span<Rgba32> rowSpan = accessor.GetRowSpan(y);
Span<Rgba32> rowSpan = accessor.GetRowSpan(y);

// Half of the test image should be transparent.
if (y > 25)
Expand All @@ -407,12 +407,12 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color
});

// act
using var memStream = new MemoryStream();
using MemoryStream memStream = new();
image.Save(memStream, encoder);

// assert
memStream.Position = 0;
using var actual = Image.Load<Rgba32>(memStream);
using Image<Rgba32> actual = Image.Load<Rgba32>(memStream);
Rgba32 expectedColor = Color.Blue;
if (colorType is PngColorType.Grayscale or PngColorType.GrayscaleWithAlpha)
{
Expand All @@ -424,7 +424,7 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color
{
for (int y = 0; y < accessor.Height; y++)
{
System.Span<Rgba32> rowSpan = accessor.GetRowSpan(y);
Span<Rgba32> rowSpan = accessor.GetRowSpan(y);

if (y > 25)
{
Expand All @@ -443,15 +443,15 @@ public void Encode_WithPngTransparentColorBehaviorClear_Works(PngColorType color
[MemberData(nameof(PngTrnsFiles))]
public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType)
{
var testFile = TestFile.Create(imagePath);
TestFile testFile = TestFile.Create(imagePath);
using Image<Rgba32> input = testFile.CreateRgba32Image();
PngMetadata inMeta = input.Metadata.GetPngMetadata();
Assert.True(inMeta.HasTransparency);

using var memStream = new MemoryStream();
using MemoryStream memStream = new();
input.Save(memStream, PngEncoder);
memStream.Position = 0;
using var output = Image.Load<Rgba32>(memStream);
using Image<Rgba32> output = Image.Load<Rgba32>(memStream);
PngMetadata outMeta = output.Metadata.GetPngMetadata();
Assert.True(outMeta.HasTransparency);

Expand Down Expand Up @@ -501,8 +501,8 @@ public void Encode_WorksWithDiscontiguousBuffers<TPixel>(TestImageProvider<TPixe
PngFilterMethod.Adaptive,
PngBitDepth.Bit8,
interlaceMode,
appendPixelType: true,
appendPngColorType: true);
appendPngColorType: true,
appendPixelType: true);
}
}

Expand All @@ -523,8 +523,8 @@ static void RunTest(string serialized)
PngFilterMethod.Adaptive,
PngBitDepth.Bit8,
interlaceMode,
appendPixelType: true,
appendPngColorType: true);
appendPngColorType: true,
appendPixelType: true);
}
}

Expand All @@ -538,13 +538,27 @@ static void RunTest(string serialized)
public void EncodeFixesInvalidOptions()
{
// https://github.com/SixLabors/ImageSharp/issues/935
using var ms = new MemoryStream();
var testFile = TestFile.Create(TestImages.Png.Issue935);
using MemoryStream ms = new();
TestFile testFile = TestFile.Create(TestImages.Png.Issue935);
using Image<Rgba32> image = testFile.CreateRgba32Image(PngDecoder.Instance);

image.Save(ms, new PngEncoder { ColorType = PngColorType.RgbWithAlpha });
}

// https://github.com/SixLabors/ImageSharp/issues/2469
[Theory]
[WithFile(TestImages.Png.Issue2469, PixelTypes.Rgba32)]
public void Issue2469_Quantized_Encode_Artifacts<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(PngDecoder.Instance);
PngEncoder encoder = new() { BitDepth = PngBitDepth.Bit8, ColorType = PngColorType.Palette };

string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder);
using Image<Rgba32> encoded = Image.Load<Rgba32>(actualOutputFile);
encoded.CompareToReferenceOutput(ImageComparer.Exact, provider);
}

private static void TestPngEncoderCore<TPixel>(
TestImageProvider<TPixel> provider,
PngColorType pngColorType,
Expand All @@ -563,7 +577,7 @@ private static void TestPngEncoderCore<TPixel>(
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
var encoder = new PngEncoder
PngEncoder encoder = new()
{
ColorType = pngColorType,
FilterMethod = pngFilterMethod,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@ static void LeakPoolInstance()
public static readonly bool Is32BitProcess = !Environment.Is64BitProcess;
private static readonly List<byte[]> PressureArrays = new();

[ConditionalFact(nameof(Is32BitProcess))]
[Fact]
public static void GC_Collect_OnHighLoad_TrimsEntirePool()
{
if (!Is32BitProcess)
{
// This test is only relevant for 32-bit processes.
return;
}

RemoteExecutor.Invoke(RunTest).Dispose();

static void RunTest()
Expand Down
5 changes: 4 additions & 1 deletion tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,12 @@ public static class Png
// Issue 2209: https://github.com/SixLabors/ImageSharp/issues/2209
public const string Issue2209IndexedWithTransparency = "Png/issues/Issue_2209.png";

// Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2259
// Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2469
public const string Issue2259 = "Png/issues/Issue_2259.png";

// Issue 2259: https://github.com/SixLabors/ImageSharp/issues/2469
public const string Issue2469 = "Png/issues/issue_2469.png";

// Issue 2447: https://github.com/SixLabors/ImageSharp/issues/2447
public const string Issue2447 = "Png/issues/issue_2447.png";

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading