Browse Source

Merge branch 'master' into js/projective-transforms

pull/546/head
James Jackson-South 8 years ago
parent
commit
003debc6e2
  1. 5
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  2. 7
      src/ImageSharp/Formats/Png/PngEncoder.cs
  3. 38
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  4. 46
      src/ImageSharp/Formats/PngFilterMethod.cs
  5. 44
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  6. 2
      tests/Images/External

5
src/ImageSharp/Formats/Png/IPngEncoderOptions.cs

@ -15,6 +15,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
PngColorType PngColorType { get; }
/// <summary>
/// Gets the png filter method.
/// </summary>
PngFilterMethod PngFilterMethod { get; }
/// <summary>
/// Gets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>

7
src/ImageSharp/Formats/Png/PngEncoder.cs

@ -14,10 +14,15 @@ namespace SixLabors.ImageSharp.Formats.Png
public sealed class PngEncoder : IImageEncoder, IPngEncoderOptions
{
/// <summary>
/// Gets or sets the png color type
/// Gets or sets the png color type.
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the png filter method.
/// </summary>
public PngFilterMethod PngFilterMethod { get; set; } = PngFilterMethod.Adaptive;
/// <summary>
/// Gets or sets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>

38
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -46,6 +46,11 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly PngColorType pngColorType;
/// <summary>
/// The png filter method.
/// </summary>
private readonly PngFilterMethod pngFilterMethod;
/// <summary>
/// The quantizer for reducing the color count.
/// </summary>
@ -145,6 +150,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
this.memoryManager = memoryManager;
this.pngColorType = options.PngColorType;
this.pngFilterMethod = options.PngFilterMethod;
this.compressionLevel = options.CompressionLevel;
this.gamma = options.Gamma;
this.quantizer = options.Quantizer;
@ -272,7 +278,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="rowSpan">The row span.</param>
private void CollecTPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
private void CollectTPixelBytes<TPixel>(ReadOnlySpan<TPixel> rowSpan)
where TPixel : struct, IPixel<TPixel>
{
if (this.bytesPerPixel == 4)
@ -292,7 +298,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="rowSpan">The row span.</param>
/// <param name="row">The row.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
/// <returns>The <see cref="IManagedByteBuffer"/></returns>
private IManagedByteBuffer EncodePixelRow<TPixel>(ReadOnlySpan<TPixel> rowSpan, int row)
where TPixel : struct, IPixel<TPixel>
{
@ -307,11 +313,35 @@ namespace SixLabors.ImageSharp.Formats.Png
this.CollectGrayscaleBytes(rowSpan);
break;
default:
this.CollecTPixelBytes(rowSpan);
this.CollectTPixelBytes(rowSpan);
break;
}
return this.GetOptimalFilteredScanline();
switch (this.pngFilterMethod)
{
case PngFilterMethod.None:
NoneFilter.Encode(this.rawScanline.Span, this.result.Span);
return this.result;
case PngFilterMethod.Sub:
SubFilter.Encode(this.rawScanline.Span, this.sub.Span, this.bytesPerPixel, out int _);
return this.sub;
case PngFilterMethod.Up:
UpFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.up.Span, out int _);
return this.up;
case PngFilterMethod.Average:
AverageFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.average.Span, this.bytesPerPixel, out int _);
return this.average;
case PngFilterMethod.Paeth:
PaethFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.paeth.Span, this.bytesPerPixel, out int _);
return this.paeth;
default:
return this.GetOptimalFilteredScanline();
}
}
/// <summary>

46
src/ImageSharp/Formats/PngFilterMethod.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Provides enumeration of available PNG filter methods.
/// </summary>
public enum PngFilterMethod
{
/// <summary>
/// With the None filter, the scanline is transmitted unmodified.
/// </summary>
None,
/// <summary>
/// The Sub filter transmits the difference between each byte and the value of the corresponding
/// byte of the prior pixel.
/// </summary>
Sub,
/// <summary>
/// The Up filter is just like the <see cref="Sub"/> filter except that the pixel immediately above the current pixel,
/// rather than just to its left, is used as the predictor.
/// </summary>
Up,
/// <summary>
/// The Average filter uses the average of the two neighboring pixels (left and above) to predict the value of a pixel.
/// </summary>
Average,
/// <summary>
/// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left),
/// then chooses as predictor the neighboring pixel closest to the computed value.
/// </summary>
Paeth,
/// <summary>
/// Computes the output scanline using all five filters, and selects the filter that gives the smallest sum of
/// absolute values of outputs.
/// This method usually outperforms any single fixed filter choice.
/// </summary>
Adaptive,
}
}

44
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -32,21 +32,31 @@ namespace SixLabors.ImageSharp.Tests
PngColorType.GrayscaleWithAlpha,
};
public static readonly TheoryData<PngFilterMethod> PngFilterMethods = new TheoryData<PngFilterMethod>
{
PngFilterMethod.None,
PngFilterMethod.Sub,
PngFilterMethod.Up,
PngFilterMethod.Average,
PngFilterMethod.Paeth,
PngFilterMethod.Adaptive
};
/// <summary>
/// All types except Palette
/// </summary>
public static readonly TheoryData<int> CompressionLevels = new TheoryData<int>
{
{
1, 2, 3, 4, 5, 6, 7, 8, 9
};
public static readonly TheoryData<int> PaletteSizes = new TheoryData<int>
{
{
30, 55, 100, 201, 255
};
public static readonly TheoryData<int> PaletteLargeOnly = new TheoryData<int>
{
{
80, 100, 120, 230
};
@ -60,7 +70,7 @@ namespace SixLabors.ImageSharp.Tests
public void WorksWithDifferentSizes<TPixel>(TestImageProvider<TPixel> provider, PngColorType pngColorType)
where TPixel : struct, IPixel<TPixel>
{
TestPngEncoderCore(provider, pngColorType, appendPngColorType: true);
TestPngEncoderCore(provider, pngColorType, PngFilterMethod.Adaptive, appendPngColorType: true);
}
[Theory]
@ -68,7 +78,15 @@ namespace SixLabors.ImageSharp.Tests
public void IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider, PngColorType pngColorType)
where TPixel : struct, IPixel<TPixel>
{
TestPngEncoderCore(provider, pngColorType, appendPixelType: true, appendPngColorType: true);
TestPngEncoderCore(provider, pngColorType, PngFilterMethod.Adaptive, appendPixelType: true, appendPngColorType: true);
}
[Theory]
[WithTestPatternImages(nameof(PngFilterMethods), 24, 24, PixelTypes.Rgba32)]
public void WorksWithAllFilterMethods<TPixel>(TestImageProvider<TPixel> provider, PngFilterMethod pngFilterMethod)
where TPixel : struct, IPixel<TPixel>
{
TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, pngFilterMethod, appendPngFilterMethod: true);
}
[Theory]
@ -76,7 +94,7 @@ namespace SixLabors.ImageSharp.Tests
public void WorksWithAllCompressionLevels<TPixel>(TestImageProvider<TPixel> provider, int compressionLevel)
where TPixel : struct, IPixel<TPixel>
{
TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true);
TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, PngFilterMethod.Adaptive, compressionLevel, appendCompressionLevel: true);
}
[Theory]
@ -84,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests
public void PaletteColorType_WuQuantizer<TPixel>(TestImageProvider<TPixel> provider, int paletteSize)
where TPixel : struct, IPixel<TPixel>
{
TestPngEncoderCore(provider, PngColorType.Palette, paletteSize: paletteSize, appendPaletteSize: true);
TestPngEncoderCore(provider, PngColorType.Palette, PngFilterMethod.Adaptive, paletteSize: paletteSize, appendPaletteSize: true);
}
private static bool HasAlpha(PngColorType pngColorType) =>
@ -93,9 +111,11 @@ namespace SixLabors.ImageSharp.Tests
private static void TestPngEncoderCore<TPixel>(
TestImageProvider<TPixel> provider,
PngColorType pngColorType,
PngFilterMethod pngFilterMethod,
int compressionLevel = 6,
int paletteSize = 255,
bool appendPngColorType = false,
bool appendPngFilterMethod = false,
bool appendPixelType = false,
bool appendCompressionLevel = false,
bool appendPaletteSize = false)
@ -111,14 +131,16 @@ namespace SixLabors.ImageSharp.Tests
var encoder = new PngEncoder
{
PngColorType = pngColorType,
PngFilterMethod = pngFilterMethod,
CompressionLevel = compressionLevel,
Quantizer = new WuQuantizer(paletteSize)
};
string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : "";
string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : "";
string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : "";
string debugInfo = $"{pngColorTypeInfo}{compressionLevelInfo}{paletteSizeInfo}";
string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty;
string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty;
string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty;
string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty;
string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}";
//string referenceInfo = $"{pngColorTypeInfo}";
// Does DebugSave & load reference CompareToReferenceInput():

2
tests/Images/External

@ -1 +1 @@
Subproject commit 71635787778ba442087f326ec49a116ba19c7f60
Subproject commit 558729ec87bcf52f22362175842f88a81ccfc483
Loading…
Cancel
Save