Browse Source

Implemented the IEncoderOptions inside the png encoder.

pull/22/merge
Dirk Lemstra 9 years ago
committed by Dirk Lemstra
parent
commit
031da70a15
  1. 54
      src/ImageSharp.Formats.Png/IPngEncoderOptions.cs
  2. 14
      src/ImageSharp.Formats.Png/ImageExtensions.cs
  3. 1
      src/ImageSharp.Formats.Png/PngDecoderCore.cs
  4. 68
      src/ImageSharp.Formats.Png/PngEncoder.cs
  5. 91
      src/ImageSharp.Formats.Png/PngEncoderCore.cs
  6. 88
      src/ImageSharp.Formats.Png/PngEncoderOptions.cs

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

@ -0,0 +1,54 @@
// <copyright file="IPngEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using Quantizers;
/// <summary>
/// Encapsulates the options for the <see cref="PngEncoder"/>.
/// </summary>
public interface IPngEncoderOptions : IEncoderOptions
{
/// <summary>
/// Gets the quality of output for images.
/// </summary>
int Quality { get; }
/// <summary>
/// Gets the png color type
/// </summary>
PngColorType PngColorType { get; }
/// <summary>
/// Gets the compression level 1-9.
/// </summary>
int CompressionLevel { get; }
/// <summary>
/// Gets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true.
/// </summary>
/// <value>The gamma value of the image.</value>
float Gamma { get; }
/// <summary>
/// Gets quantizer for reducing the color count.
/// </summary>
IQuantizer Quantizer { get; }
/// <summary>
/// Gets the transparency threshold.
/// </summary>
byte Threshold { get; }
/// <summary>
/// Gets a value indicating whether this instance should write
/// gamma information to the stream.
/// </summary>
bool WriteGamma { get; }
}
}

14
src/ImageSharp.Formats.Png/ImageExtensions.cs

@ -5,7 +5,6 @@
namespace ImageSharp
{
using System;
using System.IO;
using Formats;
@ -21,15 +20,18 @@ namespace ImageSharp
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors.
/// Anything equal to 256 and below will cause the encoder to save the image in an indexed format.
/// </param>
/// <param name="options">The options for the encoder.</param>
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
/// <returns>
/// The <see cref="Image{TColor}"/>.
/// </returns>
public static Image<TColor> SaveAsPng<TColor>(this Image<TColor> source, Stream stream, int quality = int.MaxValue)
public static Image<TColor> SaveAsPng<TColor>(this Image<TColor> source, Stream stream, IPngEncoderOptions options = null)
where TColor : struct, IPixel<TColor>
=> source.Save(stream, new PngEncoder { Quality = quality });
{
PngEncoder encoder = new PngEncoder();
encoder.Encode(source, stream, options);
return source;
}
}
}

1
src/ImageSharp.Formats.Png/PngDecoderCore.cs

@ -10,7 +10,6 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using static ComparableExtensions;

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

@ -5,72 +5,34 @@
namespace ImageSharp.Formats
{
using System;
using System.IO;
using ImageSharp.Quantizers;
/// <summary>
/// Image encoder for writing image data to a stream in png format.
/// </summary>
public class PngEncoder : IImageEncoder
{
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the png color type
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
public int CompressionLevel { get; set; } = 6;
/// <summary>
/// Gets or sets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true. The default value is 2.2F.
/// </summary>
/// <value>The gamma value of the image.</value>
public float Gamma { get; set; } = 2.2F;
/// <summary>
/// Gets or sets quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <inheritdoc/>
public void Encode<TColor>(Image<TColor> image, Stream stream, IEncoderOptions options)
where TColor : struct, IPixel<TColor>
{
IPngEncoderOptions pngOptions = PngEncoderOptions.Create(options);
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 0;
this.Encode(image, stream, pngOptions);
}
/// <summary>
/// Gets or sets a value indicating whether this instance should write
/// gamma information to the stream. The default value is false.
/// Encodes the image to the specified stream from the <see cref="Image{TColor}"/>.
/// </summary>
public bool WriteGamma { get; set; }
/// <inheritdoc/>
public void Encode<TColor>(Image<TColor> image, Stream stream, IEncoderOptions options)
/// <typeparam name="TColor">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TColor}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="options">The options for the encoder.</param>
public void Encode<TColor>(Image<TColor> image, Stream stream, IPngEncoderOptions options)
where TColor : struct, IPixel<TColor>
{
PngEncoderCore encoder = new PngEncoderCore
{
CompressionLevel = this.CompressionLevel,
Gamma = this.Gamma,
Quality = this.Quality,
PngColorType = this.PngColorType,
Quantizer = this.Quantizer,
WriteGamma = this.WriteGamma,
Threshold = this.Threshold
};
encoder.Encode(image, stream);
PngEncoderCore encode = new PngEncoderCore(options);
encode.Encode(image, stream);
}
}
}

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

@ -40,6 +40,11 @@ namespace ImageSharp.Formats
/// </summary>
private readonly Crc32 crc = new Crc32();
/// <summary>
/// The options for the encoder.
/// </summary>
private readonly IPngEncoderOptions options;
/// <summary>
/// Contains the raw pixel data from an indexed image.
/// </summary>
@ -86,44 +91,28 @@ namespace ImageSharp.Formats
private byte[] paeth;
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the png color type
/// The quality of output for images.
/// </summary>
public PngColorType PngColorType { get; set; }
private int quality;
/// <summary>
/// Gets or sets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// The png color type.
/// </summary>
public int CompressionLevel { get; set; } = 6;
private PngColorType pngColorType;
/// <summary>
/// Gets or sets a value indicating whether this instance should write
/// gamma information to the stream. The default value is false.
/// The quantizer for reducing the color count.
/// </summary>
public bool WriteGamma { get; set; }
private IQuantizer quantizer;
/// <summary>
/// Gets or sets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true. The default value is 2.2F.
/// Initializes a new instance of the <see cref="PngEncoderCore"/> class.
/// </summary>
/// <value>The gamma value of the image.</value>
public float Gamma { get; set; } = 2.2F;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; }
/// <param name="options">The options for the encoder.</param>
public PngEncoderCore(IPngEncoderOptions options)
{
this.options = options ?? new PngEncoderOptions();
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TColor}"/>.
@ -153,23 +142,25 @@ namespace ImageSharp.Formats
stream.Write(this.chunkDataBuffer, 0, 8);
// Ensure that quality can be set but has a fallback.
int quality = this.Quality > 0 ? this.Quality : image.MetaData.Quality;
this.Quality = quality > 0 ? quality.Clamp(1, int.MaxValue) : int.MaxValue;
this.quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality;
this.quality = this.quality > 0 ? this.quality.Clamp(1, int.MaxValue) : int.MaxValue;
this.pngColorType = this.options.PngColorType;
// Set correct color type if the color count is 256 or less.
if (this.Quality <= 256)
if (this.quality <= 256)
{
this.PngColorType = PngColorType.Palette;
this.pngColorType = PngColorType.Palette;
}
if (this.PngColorType == PngColorType.Palette && this.Quality > 256)
if (this.pngColorType == PngColorType.Palette && this.quality > 256)
{
this.Quality = 256;
this.quality = 256;
}
// Set correct bit depth.
this.bitDepth = this.Quality <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.Quality).Clamp(1, 8)
this.bitDepth = this.quality <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.quality).Clamp(1, 8)
: (byte)8;
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
@ -188,7 +179,7 @@ namespace ImageSharp.Formats
{
Width = image.Width,
Height = image.Height,
ColorType = (byte)this.PngColorType,
ColorType = (byte)this.pngColorType,
BitDepth = this.bitDepth,
FilterMethod = 0, // None
CompressionMethod = 0,
@ -198,7 +189,7 @@ namespace ImageSharp.Formats
this.WriteHeaderChunk(stream, header);
// Collect the indexed pixel data
if (this.PngColorType == PngColorType.Palette)
if (this.pngColorType == PngColorType.Palette)
{
this.CollectIndexedBytes(image, stream, header);
}
@ -334,7 +325,7 @@ namespace ImageSharp.Formats
private byte[] EncodePixelRow<TColor>(PixelAccessor<TColor> pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result)
where TColor : struct, IPixel<TColor>
{
switch (this.PngColorType)
switch (this.pngColorType)
{
case PngColorType.Palette:
Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length);
@ -362,7 +353,7 @@ namespace ImageSharp.Formats
private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result)
{
// Palette images don't compress well with adaptive filtering.
if (this.PngColorType == PngColorType.Palette || this.bitDepth < 8)
if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
{
NoneFilter.Encode(rawScanline, result);
return result;
@ -436,7 +427,7 @@ namespace ImageSharp.Formats
/// <returns>The <see cref="int"/></returns>
private int CalculateBytesPerPixel()
{
switch (this.PngColorType)
switch (this.pngColorType)
{
case PngColorType.Grayscale:
return 1;
@ -488,18 +479,18 @@ namespace ImageSharp.Formats
private QuantizedImage<TColor> WritePaletteChunk<TColor>(Stream stream, PngHeader header, ImageBase<TColor> image)
where TColor : struct, IPixel<TColor>
{
if (this.Quality > 256)
if (this.quality > 256)
{
return null;
}
if (this.Quantizer == null)
if (this.quantizer == null)
{
this.Quantizer = new OctreeQuantizer<TColor>();
this.quantizer = new OctreeQuantizer<TColor>();
}
// Quantize the image returning a palette. This boxing is icky.
QuantizedImage<TColor> quantized = ((IQuantizer<TColor>)this.Quantizer).Quantize(image, this.Quality);
QuantizedImage<TColor> quantized = ((IQuantizer<TColor>)this.quantizer).Quantize(image, this.quality);
// Grab the palette and write it to the stream.
TColor[] palette = quantized.Palette;
@ -524,7 +515,7 @@ namespace ImageSharp.Formats
colorTable[offset + 1] = bytes[1];
colorTable[offset + 2] = bytes[2];
if (alpha <= this.Threshold)
if (alpha <= this.options.Threshold)
{
transparentPixels.Add((byte)offset);
}
@ -578,9 +569,9 @@ namespace ImageSharp.Formats
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
private void WriteGammaChunk(Stream stream)
{
if (this.WriteGamma)
if (this.options.WriteGamma)
{
int gammaValue = (int)(this.Gamma * 100000F);
int gammaValue = (int)(this.options.Gamma * 100000F);
byte[] size = BitConverter.GetBytes(gammaValue);
@ -608,7 +599,7 @@ namespace ImageSharp.Formats
int resultLength = bytesPerScanline + 1;
byte[] result = new byte[resultLength];
if (this.PngColorType != PngColorType.Palette)
if (this.pngColorType != PngColorType.Palette)
{
this.sub = new byte[resultLength];
this.up = new byte[resultLength];
@ -622,7 +613,7 @@ namespace ImageSharp.Formats
try
{
memoryStream = new MemoryStream();
using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.CompressionLevel))
using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel))
{
for (int y = 0; y < this.height; y++)
{

88
src/ImageSharp.Formats.Png/PngEncoderOptions.cs

@ -0,0 +1,88 @@
// <copyright file="PngEncoderOptions.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Formats
{
using Quantizers;
/// <summary>
/// Encapsulates the options for the <see cref="PngEncoder"/>.
/// </summary>
public sealed class PngEncoderOptions : EncoderOptions, IPngEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderOptions"/> class.
/// </summary>
public PngEncoderOptions()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderOptions"/> class.
/// </summary>
/// <param name="options">The options for the encoder.</param>
private PngEncoderOptions(IEncoderOptions options)
: base(options)
{
}
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
public int Quality { get; set; }
/// <summary>
/// Gets or sets the png color type
/// </summary>
public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
public int CompressionLevel { get; set; } = 6;
/// <summary>
/// Gets or sets the gamma value, that will be written
/// the the stream, when the <see cref="WriteGamma"/> property
/// is set to true. The default value is 2.2F.
/// </summary>
/// <value>The gamma value of the image.</value>
public float Gamma { get; set; } = 2.2F;
/// <summary>
/// Gets or sets quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 0;
/// <summary>
/// Gets or sets a value indicating whether this instance should write
/// gamma information to the stream. The default value is false.
/// </summary>
public bool WriteGamma { get; set; }
/// <summary>
/// Converts the options to a <see cref="IPngEncoderOptions"/> instance with a
/// cast or by creating a new instance with the specfied options.
/// </summary>
/// <param name="options">The options for the encoder.</param>
/// <returns>The options for the <see cref="PngEncoder"/>.</returns>
internal static IPngEncoderOptions Create(IEncoderOptions options)
{
IPngEncoderOptions pngOptions = options as IPngEncoderOptions;
if (pngOptions != null)
{
return pngOptions;
}
return new PngEncoderOptions(options);
}
}
}
Loading…
Cancel
Save