Browse Source

fix formats + added sample for switching out encoders/decoders

pull/254/head
Scott Williams 9 years ago
parent
commit
fe61328183
  1. 19
      ImageSharp.sln
  2. 12
      samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj
  3. 35
      samples/ChangeDefaultEncoderOptions/Program.cs
  4. 59
      src/ImageSharp/Configuration.cs
  5. 7
      src/ImageSharp/Formats/Bmp/BmpImageFormatProvider.cs
  6. 6
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  7. 12
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  8. 13
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  9. 21
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  10. 7
      src/ImageSharp/Formats/Gif/GifImageFormatProvider.cs
  11. 4
      src/ImageSharp/Formats/IImageFormatProvider.cs
  12. 7
      src/ImageSharp/Formats/Jpeg/JpegImageFormatProvider.cs
  13. 8
      src/ImageSharp/Formats/Png/PngEncoder.cs
  14. 16
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  15. 7
      src/ImageSharp/Formats/Png/PngImageFormatProvider.cs
  16. 9
      src/ImageSharp/Image/Image.Decode.cs
  17. 5
      src/ImageSharp/Image/Image.FromStream.cs
  18. 23
      src/ImageSharp/Image/Image{TPixel}.cs
  19. 10
      tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs
  20. 22
      tests/ImageSharp.Tests/ConfigurationTests.cs
  21. 13
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  22. 2
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  23. 2
      tests/ImageSharp.Tests/Image/ImageSaveTests.cs
  24. 2
      tests/ImageSharp.Tests/TestFormat.cs

19
ImageSharp.sln

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.6
VisualStudioVersion = 15.0.26430.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
@ -47,7 +47,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Sandbox46", "tes
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7CC6D57E-B916-43B8-B315-A0BB92F260A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarWithRoundedCorner", "samples\AvatarWithRoundedCorner\AvatarWithRoundedCorner.csproj", "{844FC582-4E78-4371-847D-EFD4D1103578}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChangeDefaultEncoderOptions", "samples\ChangeDefaultEncoderOptions\ChangeDefaultEncoderOptions.csproj", "{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -143,6 +145,18 @@ Global
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x64.Build.0 = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.ActiveCfg = Release|Any CPU
{844FC582-4E78-4371-847D-EFD4D1103578}.Release|x86.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x64.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.ActiveCfg = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Debug|x86.Build.0 = Debug|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|Any CPU.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x64.Build.0 = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.ActiveCfg = Release|Any CPU
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -156,5 +170,6 @@ Global
{2BF743D8-2A06-412D-96D7-F448F00C5EA5} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{96188137-5FA6-4924-AB6E-4EFF79C6E0BB} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{844FC582-4E78-4371-847D-EFD4D1103578} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2}
{07EE511D-4BAB-4323-BAFC-3AF2BF9366F0} = {7CC6D57E-B916-43B8-B315-A0BB92F260A2}
EndGlobalSection
EndGlobal

12
samples/ChangeDefaultEncoderOptions/ChangeDefaultEncoderOptions.csproj

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ImageSharp\ImageSharp.csproj" />
</ItemGroup>
</Project>

35
samples/ChangeDefaultEncoderOptions/Program.cs

@ -0,0 +1,35 @@
using System;
using ImageSharp;
namespace ChangeDefaultEncoderOptions
{
class Program
{
static void Main(string[] args)
{
// lets switch out the default encoder for jpeg to one
// that saves at 90 quality and ignores the matadata
Configuration.Default.SetMimeTypeEncoder("image/jpeg", new ImageSharp.Formats.JpegEncoder()
{
Quality = 90,
IgnoreMetadata = true
});
// now lets say we don't want animated gifs, lets skip decoding the alternative frames
Configuration.Default.SetMimeTypeDecoder("image/gif", new ImageSharp.Formats.GifDecoder()
{
IgnoreFrames = true,
IgnoreMetadata = true
});
// and just to be douple sure we don't want animations lets disable them on encode too.
Configuration.Default.SetMimeTypeEncoder("image/gif", new ImageSharp.Formats.GifEncoder()
{
IgnoreFrames = true,
IgnoreMetadata = true
});
}
}
}

59
src/ImageSharp/Configuration.cs

@ -38,7 +38,7 @@ namespace ImageSharp
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to fiel extensions.
/// </summary>
private readonly ConcurrentDictionary<string, IImageEncoder> extensionsEncoders = new ConcurrentDictionary<string, IImageEncoder>(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, string> extensionsMap = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mimestypes.
@ -95,17 +95,17 @@ namespace ImageSharp
/// <summary>
/// Gets the typeof of all the current image decoders
/// </summary>
internal IEnumerable<Type> AllMimeImageDecoders => this.mimeTypeDecoders.Select(x => x.Value.GetType()).Distinct().ToList();
internal IEnumerable<KeyValuePair<string, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the typeof of all the current image decoders
/// </summary>
internal IEnumerable<Type> AllMimeImageEncoders => this.mimeTypeEncoders.Select(x => x.Value.GetType()).Distinct().ToList();
internal IEnumerable<KeyValuePair<string, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Gets the typeof of all the current image decoders
/// </summary>
internal IEnumerable<Type> AllExtImageEncoders => this.mimeTypeEncoders.Select(x => x.Value.GetType()).Distinct().ToList();
internal IEnumerable<KeyValuePair<string, string>> ImageExtensionToMimeTypeMapping => this.extensionsMap;
#if !NETSTANDARD1_1
/// <summary>
@ -133,11 +133,11 @@ namespace ImageSharp
}
/// <inheritdoc />
public void SetFileExtensionEncoder(string extension, IImageEncoder encoder)
public void SetFileExtensionToMimeTypeMapping(string extension, string mimetype)
{
Guard.NotNullOrEmpty(extension, nameof(extension));
Guard.NotNull(encoder, nameof(encoder));
this.extensionsEncoders.AddOrUpdate(extension?.Trim(), encoder, (s, e) => encoder);
Guard.NotNullOrEmpty(mimetype, nameof(mimetype));
this.extensionsMap.AddOrUpdate(extension?.Trim(), mimetype, (s, e) => mimetype);
}
/// <inheritdoc />
@ -151,7 +151,7 @@ namespace ImageSharp
/// <summary>
/// Removes all the registerd detectors
/// </summary>
public void ClearMimeTypeDetector()
public void ClearMimeTypeDetectors()
{
this.mimeTypeDetectors.Clear();
}
@ -164,12 +164,25 @@ namespace ImageSharp
this.SetMaxHeaderSize();
}
/// <summary>
/// Creates the default instance, with Png, Jpeg, Gif and Bmp preregisterd (if they have been referenced)
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/> </returns>
internal static Configuration CreateDefaultInstance()
{
return new Configuration(
new PngImageFormatProvider(),
new JpegImageFormatProvider(),
new GifImageFormatProvider(),
new BmpImageFormatProvider());
}
/// <summary>
/// For the specified mimetype find the decoder.
/// </summary>
/// <param name="mimeType">the mimetype to discover</param>
/// <returns>the IImageDecoder if found othersize null </returns>
public IImageDecoder FindMimeTypeDecoder(string mimeType)
internal IImageDecoder FindMimeTypeDecoder(string mimeType)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
if (this.mimeTypeDecoders.TryGetValue(mimeType, out IImageDecoder dec))
@ -185,7 +198,7 @@ namespace ImageSharp
/// </summary>
/// <param name="mimeType">the mimetype to discover</param>
/// <returns>the IImageEncoder if found othersize null </returns>
public IImageEncoder FindMimeTypeEncoder(string mimeType)
internal IImageEncoder FindMimeTypeEncoder(string mimeType)
{
Guard.NotNullOrEmpty(mimeType, nameof(mimeType));
if (this.mimeTypeEncoders.TryGetValue(mimeType, out IImageEncoder dec))
@ -201,29 +214,33 @@ namespace ImageSharp
/// </summary>
/// <param name="extensions">the extensions to discover</param>
/// <returns>the IImageEncoder if found othersize null </returns>
public IImageEncoder FindFileExtensionsEncoder(string extensions)
internal IImageEncoder FindFileExtensionsEncoder(string extensions)
{
extensions = extensions?.TrimStart('.');
Guard.NotNullOrEmpty(extensions, nameof(extensions));
if (this.extensionsEncoders.TryGetValue(extensions, out IImageEncoder dec))
if (this.extensionsMap.TryGetValue(extensions, out string mime))
{
return dec;
return this.FindMimeTypeEncoder(mime);
}
return null;
}
/// <summary>
/// Creates the default instance, with Png, Jpeg, Gif and Bmp preregisterd (if they have been referenced)
/// For the specified mimetype find the encoder.
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/> </returns>
internal static Configuration CreateDefaultInstance()
/// <param name="extensions">the extensions to discover</param>
/// <returns>the IImageEncoder if found othersize null </returns>
internal string FindFileExtensionsMimeType(string extensions)
{
return new Configuration(
new PngImageFormatProvider(),
new JpegImageFormatProvider(),
new GifImageFormatProvider(),
new BmpImageFormatProvider());
extensions = extensions?.TrimStart('.');
Guard.NotNullOrEmpty(extensions, nameof(extensions));
if (this.extensionsMap.TryGetValue(extensions, out string mime))
{
return mime;
}
return null;
}
/// <summary>

7
src/ImageSharp/Formats/Bmp/BmpImageFormatProvider.cs

@ -25,9 +25,12 @@ namespace ImageSharp.Formats
host.SetMimeTypeEncoder(mimeType, encoder);
}
foreach (string mimeType in BmpConstants.FileExtensions)
foreach (string ext in BmpConstants.FileExtensions)
{
host.SetFileExtensionEncoder(mimeType, encoder);
foreach (string mimeType in BmpConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var decoder = new BmpDecoder();

6
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -21,6 +21,11 @@ namespace ImageSharp.Formats
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether the additional frames should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreFrames { get; set; } = false;
/// <summary>
/// Gets or sets the encoding that should be used when reading comments.
/// </summary>
@ -32,6 +37,7 @@ namespace ImageSharp.Formats
{
var decoder = new GifDecoderCore<TPixel>(this.TextEncoding, configuration);
decoder.IgnoreMetadata = this.IgnoreMetadata;
decoder.IgnoreFrames = this.IgnoreFrames;
return decoder.Decode(stream);
}
}

12
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -96,6 +96,11 @@ namespace ImageSharp.Formats
/// </summary>
public Encoding TextEncoding { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether the additional frames should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreFrames { get; internal set; }
/// <summary>
/// Decodes the stream to the image.
/// </summary>
@ -357,6 +362,13 @@ namespace ImageSharp.Formats
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private unsafe void ReadFrameColors(byte[] indices, byte[] colorTable, int colorTableLength, GifImageDescriptor descriptor)
{
if (this.IgnoreFrames && this.image != null)
{
// we already have our images skip this
// TODO move this higher up the stack to prevent some of the data loading higher up.
return;
}
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;

13
src/ImageSharp/Formats/Gif/GifEncoder.cs

@ -22,16 +22,20 @@ namespace ImageSharp.Formats
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether the additional frames should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreFrames { get; set; } = false;
/// <summary>
/// Gets or sets the encoding that should be used when writing comments.
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Gets or sets the quality of output for images.
/// Gets or sets the size of the color palette to use. For gifs the value ranges from 1 to 256. Leave as zero for default size.
/// </summary>
/// <remarks>For gifs the value ranges from 1 to 256.</remarks>
public int Quality { get; set; }
public int PaletteSize { get; set; } = 0;
/// <summary>
/// Gets or sets the transparency threshold.
@ -50,8 +54,9 @@ namespace ImageSharp.Formats
GifEncoderCore encoder = new GifEncoderCore(this.TextEncoding);
encoder.Quantizer = this.Quantizer;
encoder.Threshold = this.Threshold;
encoder.Quality = this.Quality;
encoder.PaletteSize = this.PaletteSize;
encoder.IgnoreMetadata = this.IgnoreMetadata;
encoder.IgnoreFrames = this.IgnoreFrames;
encoder.Encode(image, stream);
}
}

21
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -62,13 +62,18 @@ namespace ImageSharp.Formats
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
public int Quality { get; internal set; }
public int PaletteSize { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether the additional frames should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreFrames { get; internal set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
/// </summary>
@ -87,20 +92,20 @@ namespace ImageSharp.Formats
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
// Ensure that quality can be set but has a fallback.
int quality = this.Quality;
quality = quality > 0 ? quality.Clamp(1, 256) : 256;
int paletteSize = this.PaletteSize;
paletteSize = paletteSize > 0 ? paletteSize.Clamp(1, 256) : 256;
// Get the number of bits.
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quality);
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(paletteSize);
// Quantize the image returning a palette.
this.hasFrames = image.Frames.Any();
this.hasFrames = !this.IgnoreFrames && image.Frames.Any();
// Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames.
var ditheredQuantizer = (IQuantizer<TPixel>)this.Quantizer;
ditheredQuantizer.Dither = !this.hasFrames;
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image, quality);
// Quantize the image returning a palette.
QuantizedImage<TPixel> quantized = ditheredQuantizer.Quantize(image, paletteSize);
int index = this.GetTransparentIndex(quantized);
@ -126,7 +131,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame<TPixel> frame = image.Frames[i];
QuantizedImage<TPixel> quantizedFrame = ditheredQuantizer.Quantize(frame, quality);
QuantizedImage<TPixel> quantizedFrame = ditheredQuantizer.Quantize(frame, paletteSize);
this.WriteGraphicalControlExtension(frame.MetaData, writer, this.GetTransparentIndex(quantizedFrame));
this.WriteImageDescriptor(frame, writer);

7
src/ImageSharp/Formats/Gif/GifImageFormatProvider.cs

@ -25,9 +25,12 @@ namespace ImageSharp.Formats
host.SetMimeTypeEncoder(mimeType, encoder);
}
foreach (string mimeType in GifConstants.FileExtensions)
foreach (string ext in GifConstants.FileExtensions)
{
host.SetFileExtensionEncoder(mimeType, encoder);
foreach (string mimeType in GifConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var decoder = new GifDecoder();

4
src/ImageSharp/Formats/IImageFormatProvider.cs

@ -37,8 +37,8 @@ namespace ImageSharp.Formats
/// Sets a specific image encoder as the encoder for a specific mimetype
/// </summary>
/// <param name="extension">the target mimetype</param>
/// <param name="encoder">the encoder to use</param>
void SetFileExtensionEncoder(string extension, IImageEncoder encoder);
/// <param name="mimetype">the mimetype this extenion equates to</param>
void SetFileExtensionToMimeTypeMapping(string extension, string mimetype);
/// <summary>
/// Sets a specific image decoder as the decoder for a specific mimetype

7
src/ImageSharp/Formats/Jpeg/JpegImageFormatProvider.cs

@ -25,9 +25,12 @@ namespace ImageSharp.Formats
host.SetMimeTypeEncoder(mimeType, pngEncoder);
}
foreach (string mimeType in JpegConstants.FileExtensions)
foreach (string ext in JpegConstants.FileExtensions)
{
host.SetFileExtensionEncoder(mimeType, pngEncoder);
foreach (string mimeType in JpegConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var pngDecoder = new JpegDecoder();

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

@ -17,14 +17,14 @@ namespace ImageSharp.Formats
public class PngEncoder : IImageEncoder
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <summary>
/// Gets or sets the quality of output for images.
/// Gets or sets the size of the color palette to use. Set to zero to leav png encoding to use pixel data.
/// </summary>
public int Quality { get; set; }
public int PaletteSize { get; set; } = 0;
/// <summary>
/// Gets or sets the png color type
@ -74,7 +74,7 @@ namespace ImageSharp.Formats
{
encode.IgnoreMetadata = this.IgnoreMetadata;
encode.Quality = this.Quality > 0 ? this.Quality.Clamp(1, int.MaxValue) : int.MaxValue;
encode.PaletteSize = this.PaletteSize > 0 ? this.PaletteSize.Clamp(1, int.MaxValue) : int.MaxValue;
encode.PngColorType = this.PngColorType;
encode.CompressionLevel = this.CompressionLevel;
encode.Gamma = this.Gamma;

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

@ -133,7 +133,7 @@ namespace ImageSharp.Formats
/// <summary>
/// Gets or sets the Quality value
/// </summary>
public int Quality { get; internal set; }
public int PaletteSize { get; internal set; }
/// <summary>
/// Gets or sets the Quality value
@ -196,19 +196,19 @@ namespace ImageSharp.Formats
this.quantizer = this.Quantizer;
// Set correct color type if the color count is 256 or less.
if (this.Quality <= 256)
if (this.PaletteSize <= 256)
{
this.pngColorType = PngColorType.Palette;
}
if (this.pngColorType == PngColorType.Palette && this.Quality > 256)
if (this.pngColorType == PngColorType.Palette && this.PaletteSize > 256)
{
this.Quality = 256;
this.PaletteSize = 256;
}
// Set correct bit depth.
this.bitDepth = this.Quality <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.Quality).Clamp(1, 8)
this.bitDepth = this.PaletteSize <= 256
? (byte)ImageMaths.GetBitsNeededForColorDepth(this.PaletteSize).Clamp(1, 8)
: (byte)8;
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
@ -538,7 +538,7 @@ namespace ImageSharp.Formats
private QuantizedImage<TPixel> WritePaletteChunk<TPixel>(Stream stream, PngHeader header, ImageBase<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (this.Quality > 256)
if (this.PaletteSize > 256)
{
return null;
}
@ -549,7 +549,7 @@ namespace ImageSharp.Formats
}
// Quantize the image returning a palette. This boxing is icky.
QuantizedImage<TPixel> quantized = ((IQuantizer<TPixel>)this.quantizer).Quantize(image, this.Quality);
QuantizedImage<TPixel> quantized = ((IQuantizer<TPixel>)this.quantizer).Quantize(image, this.PaletteSize);
// Grab the palette and write it to the stream.
TPixel[] palette = quantized.Palette;

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

@ -25,9 +25,12 @@ namespace ImageSharp.Formats
host.SetMimeTypeEncoder(mimeType, pngEncoder);
}
foreach (string mimeType in PngConstants.FileExtensions)
foreach (string ext in PngConstants.FileExtensions)
{
host.SetFileExtensionEncoder(mimeType, pngEncoder);
foreach (string mimeType in PngConstants.MimeTypes)
{
host.SetFileExtensionToMimeTypeMapping(ext, mimeType);
}
}
var pngDecoder = new PngDecoder();

9
src/ImageSharp/Image/Image.Decode.cs

@ -55,10 +55,13 @@ namespace ImageSharp
/// <returns>The image format or null if none found.</returns>
private static IImageDecoder DiscoverDecoder(Stream stream, Configuration config, out string mimeType)
{
format = config.FindMimeTypeDecoder(mimeType);
mimeType = InternalDiscoverMimeType(stream, config);
if (mimeType != null)
{
return config.FindMimeTypeDecoder(mimeType);
}
return format;
return null;
}
#pragma warning disable SA1008 // Opening parenthesis must be spaced correctly

5
src/ImageSharp/Image/Image.FromStream.cs

@ -6,6 +6,7 @@
namespace ImageSharp
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@ -202,9 +203,9 @@ namespace ImageSharp
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
foreach (Type format in config.AllMimeImageDecoders)
foreach (KeyValuePair<string, IImageDecoder> val in config.ImageDecoders)
{
stringBuilder.AppendLine(" - " + format.Name);
stringBuilder.AppendLine($" - {val.Key} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());

23
src/ImageSharp/Image/Image{TPixel}.cs

@ -165,9 +165,9 @@ namespace ImageSharp
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (Type format in this.Configuration.AllMimeImageEncoders)
foreach (KeyValuePair<string, IImageEncoder> val in this.Configuration.ImageEncoders)
{
stringBuilder.AppendLine(" - " + format);
stringBuilder.AppendLine($" - {val.Key} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
@ -211,11 +211,22 @@ namespace ImageSharp
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}'. Available encoded:");
foreach (Type format in this.Configuration.AllExtImageEncoders)
string mime = this.Configuration.FindFileExtensionsMimeType(ext);
if (mime == null)
{
stringBuilder.AppendLine($"Can't find a mime type for the file extention '{ext}'. Registerd File extension maps include:");
foreach (KeyValuePair<string, string> map in this.Configuration.ImageExtensionToMimeTypeMapping)
{
stringBuilder.AppendLine($" - {map.Key} : {map.Value}");
}
}
else
{
stringBuilder.AppendLine(" - " + format);
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using mime type '{mime}'. Registerd encoders include:");
foreach (KeyValuePair<string, IImageEncoder> enc in this.Configuration.ImageEncoders)
{
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
}
throw new NotSupportedException(stringBuilder.ToString());

10
tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs

@ -53,7 +53,7 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
PngEncoder encoder = new PngEncoder() { Quantizer = new OctreeQuantizer<Rgba32>(), Quality = 256 };
PngEncoder encoder = new PngEncoder() { Quantizer = new OctreeQuantizer<Rgba32>(), PaletteSize = 256 };
this.bmpCore.SaveAsPng(memoryStream, encoder);
}
@ -64,7 +64,7 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder { Quantizer = new OctreeQuantizer<Rgba32> { Dither = false }, Quality = 256 };
PngEncoder options = new PngEncoder { Quantizer = new OctreeQuantizer<Rgba32> { Dither = false }, PaletteSize = 256 };
this.bmpCore.SaveAsPng(memoryStream, options);
}
@ -75,7 +75,7 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder { Quantizer = new PaletteQuantizer<Rgba32>(), Quality = 256 };
PngEncoder options = new PngEncoder { Quantizer = new PaletteQuantizer<Rgba32>(), PaletteSize = 256 };
this.bmpCore.SaveAsPng(memoryStream, options);
}
@ -86,7 +86,7 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder { Quantizer = new PaletteQuantizer<Rgba32> { Dither = false }, Quality = 256 };
PngEncoder options = new PngEncoder { Quantizer = new PaletteQuantizer<Rgba32> { Dither = false }, PaletteSize = 256 };
this.bmpCore.SaveAsPng(memoryStream, options);
}
@ -97,7 +97,7 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
PngEncoder options = new PngEncoder() { Quantizer = new WuQuantizer<Rgba32>(), Quality = 256 };
PngEncoder options = new PngEncoder() { Quantizer = new WuQuantizer<Rgba32>(), PaletteSize = 256 };
this.bmpCore.SaveAsPng(memoryStream, options);
}

22
tests/ImageSharp.Tests/ConfigurationTests.cs

@ -40,8 +40,8 @@ namespace ImageSharp.Tests
[Fact]
public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded()
{
Assert.Equal(4, DefaultConfiguration.AllMimeImageDecoders.Count());
Assert.Equal(4, DefaultConfiguration.AllMimeImageDecoders.Count());
Assert.Equal(6, DefaultConfiguration.ImageEncoders.Count());
Assert.Equal(6, DefaultConfiguration.ImageDecoders.Count());
}
/// <summary>
@ -103,15 +103,15 @@ namespace ImageSharp.Tests
{
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetFileExtensionEncoder(null, new Mock<IImageEncoder>().Object);
DefaultConfiguration.SetFileExtensionToMimeTypeMapping(null, "str");
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetFileExtensionEncoder("sdsdsd", null);
DefaultConfiguration.SetFileExtensionToMimeTypeMapping("sdsdsd", null);
});
Assert.Throws<ArgumentNullException>(() =>
{
DefaultConfiguration.SetFileExtensionEncoder(null, null);
DefaultConfiguration.SetFileExtensionToMimeTypeMapping(null, null);
});
}
@ -150,14 +150,14 @@ namespace ImageSharp.Tests
[Fact]
public void RegisterFileExtEnecoderReplacesLast()
{
var encoder1 = new Mock<IImageEncoder>().Object;
ConfigurationEmpty.SetFileExtensionEncoder("TEST", encoder1);
var found = ConfigurationEmpty.FindFileExtensionsEncoder("test");
var encoder1 = "mime1";
ConfigurationEmpty.SetFileExtensionToMimeTypeMapping("TEST", encoder1);
var found = ConfigurationEmpty.FindFileExtensionsMimeType("test");
Assert.Equal(encoder1, found);
var encoder2 = new Mock<IImageEncoder>().Object;
ConfigurationEmpty.SetFileExtensionEncoder("test", encoder2);
var found2 = ConfigurationEmpty.FindFileExtensionsEncoder("TEST");
var encoder2 = "mime2";
ConfigurationEmpty.SetFileExtensionToMimeTypeMapping("test", encoder2);
var found2 = ConfigurationEmpty.FindFileExtensionsMimeType("TEST");
Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2);
}

13
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -18,6 +18,19 @@ namespace ImageSharp.Tests
public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans };
[Fact]
public void SkipDecodingFrames()
{
var file = TestFile.GetPath(TestImages.Gif.Giphy);
using (Image<Rgba32> image = Image.Load(file, new GifDecoder() { IgnoreFrames = true }))
using (Image<Rgba32> imageWithFrames = Image.Load(file, new GifDecoder() { IgnoreFrames = false }))
{
Assert.NotEmpty(imageWithFrames.Frames);
Assert.Empty(image.Frames);
}
}
[Theory]
[WithFileCollection(nameof(TestFiles), PixelTypes)]
public void DecodeAndReSave<TPixel>(TestImageProvider<TPixel> imageProvider)

2
tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs

@ -50,7 +50,7 @@ namespace ImageSharp.Tests.Formats.Png
using (MemoryStream ms = new MemoryStream())
{
// image.Save(provider.Utility.GetTestOutputFileName("bmp"));
image.Save(ms, new PngEncoder() { Quality = 256 });
image.Save(ms, new PngEncoder() { PaletteSize = 256 });
ms.Position = 0;
using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
{

2
tests/ImageSharp.Tests/Image/ImageSaveTests.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests
};
config.AddMimeTypeDetector(this.localMimeTypeDetector.Object);
config.SetMimeTypeEncoder("img/test", this.encoder.Object);
config.SetFileExtensionEncoder("png", this.encoder.Object);
config.SetFileExtensionToMimeTypeMapping("png", "img/test");
this.Image = new Image<Rgba32>(config, 1, 1);
}

2
tests/ImageSharp.Tests/TestFormat.cs

@ -114,7 +114,7 @@ namespace ImageSharp.Tests
host.AddMimeTypeDetector(new TestHeader(this));
foreach (var ext in this.SupportedExtensions)
{
host.SetFileExtensionEncoder(ext, new TestEncoder(this));
host.SetFileExtensionToMimeTypeMapping(ext, this.MimeType);
}
host.SetMimeTypeEncoder(this.MimeType, new TestEncoder(this));

Loading…
Cancel
Save