diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..aeae5c6ca
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,24 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "0.1.0",
+ "command": "dotnet",
+ "isShellCommand": true,
+ "args": [],
+ "tasks": [
+ {
+ "taskName": "build",
+ "args": [ "src/*/project.json", "-f", "netstandard1.1" ],
+ "isBuildCommand": true,
+ "showOutput": "always",
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "taskName": "test",
+ "args": ["tests/ImageSharp.Tests/project.json", "-f", "netcoreapp1.1"],
+ "isTestCommand": true,
+ "showOutput": "always",
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index cfbd18de6..9d5c9788a 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-# ImageSharp
+#
**ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`.
@@ -56,6 +56,11 @@ If you prefer, you can compile ImageSharp yourself (please do and help!), you'll
- [Visual Studio 2015 with Update 3 (or above)](https://www.visualstudio.com/news/releasenotes/vs2015-update3-vs)
- The [.NET Core 1.0 SDK Installer](https://www.microsoft.com/net/core#windows) - Non VSCode link.
+Alternatively on Linux you can use:
+
+- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp)
+- [.Net Core 1.1](https://www.microsoft.com/net/core#linuxubuntu)
+
To clone it locally click the "Clone in Windows" button above or run the following git commands.
```bash
diff --git a/build/icons/imagesharp-logo-128.png b/build/icons/imagesharp-logo-128.png
index 07b040547..6d05c222c 100644
Binary files a/build/icons/imagesharp-logo-128.png and b/build/icons/imagesharp-logo-128.png differ
diff --git a/build/icons/imagesharp-logo-256.png b/build/icons/imagesharp-logo-256.png
index 082859f24..fc59b03e1 100644
Binary files a/build/icons/imagesharp-logo-256.png and b/build/icons/imagesharp-logo-256.png differ
diff --git a/build/icons/imagesharp-logo-32.png b/build/icons/imagesharp-logo-32.png
index 868a8713f..0fd4411ce 100644
Binary files a/build/icons/imagesharp-logo-32.png and b/build/icons/imagesharp-logo-32.png differ
diff --git a/build/icons/imagesharp-logo-512.png b/build/icons/imagesharp-logo-512.png
index d89ac3fca..5d5fb854e 100644
Binary files a/build/icons/imagesharp-logo-512.png and b/build/icons/imagesharp-logo-512.png differ
diff --git a/build/icons/imagesharp-logo-64.png b/build/icons/imagesharp-logo-64.png
index cfe391e5c..4e97906e5 100644
Binary files a/build/icons/imagesharp-logo-64.png and b/build/icons/imagesharp-logo-64.png differ
diff --git a/build/icons/imagesharp-logo-heading.png b/build/icons/imagesharp-logo-heading.png
new file mode 100644
index 000000000..b10d367bf
Binary files /dev/null and b/build/icons/imagesharp-logo-heading.png differ
diff --git a/build/icons/imagesharp-logo.png b/build/icons/imagesharp-logo.png
index eb9fc6d17..ed1c36c5e 100644
Binary files a/build/icons/imagesharp-logo.png and b/build/icons/imagesharp-logo.png differ
diff --git a/build/icons/imagesharp-logo.svg b/build/icons/imagesharp-logo.svg
index 9638e9785..2df3cc80c 100644
--- a/build/icons/imagesharp-logo.svg
+++ b/build/icons/imagesharp-logo.svg
@@ -1,59 +1 @@
-
+
\ No newline at end of file
diff --git a/features.md b/features.md
index 764d7c4a6..6bc5630ee 100644
--- a/features.md
+++ b/features.md
@@ -10,13 +10,23 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway
- [x] Bmp (Read: 32bit, 24bit, 16 bit. Write: 32bit, 24bit just now)
- [x] Png (Read: Rgb, Rgba, Grayscale, Grayscale + alpha, Palette. Write: Rgb, Rgba, Grayscale, Grayscale + alpha, Palette) Supports interlaced decoding
- [x] Gif (Includes animated)
- - [ ] Tiff
+ - [ ] Tiff (Help needed)
- **Metadata**
- [x] EXIF Read/Write (Jpeg just now)
-- **Quantizers (IQuantizer with alpha channel support + thresholding)**
+- **Quantizers (IQuantizer with alpha channel support, dithering, and thresholding)**
- [x] Octree
- [x] Xiaolin Wu
- [x] Palette
+- **DIthering (Error diffusion and Ordered)**
+ - [x] Atkinson
+ - [x] Burks
+ - [x] FloydSteinburg
+ - [x] JarvisJudiceNinke
+ - [x] Sieera2
+ - [x] Sierra3
+ - [x] SerraLite
+ - [x] Bayer
+ - [x] Ordered
- **Basic color structs with implicit operators.**
- [x] Color - 32bit color in RGBA order (IPackedPixel\).
- [x] Bgra32
@@ -133,5 +143,5 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway
- [x] DrawImage
- [ ] Gradient brush (Need help)
- **DrawingText**
- - [x] DrawString (Single variant support just now, no italic,bold)
+ - [ ] DrawString (In-progress. Single variant support just now, no italic,bold)
- Other stuff I haven't thought of.
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing.Paths/project.json b/src/ImageSharp.Drawing.Paths/project.json
index bf6b1fae8..b761233c3 100644
--- a/src/ImageSharp.Drawing.Paths/project.json
+++ b/src/ImageSharp.Drawing.Paths/project.json
@@ -44,7 +44,7 @@
"ImageSharp.Drawing": {
"target": "project"
},
- "SixLabors.Shapes": "0.1.0-alpha0005",
+ "SixLabors.Shapes": "0.1.0-alpha0006",
"StyleCop.Analyzers": {
"version": "1.0.0",
"type": "build"
diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
index 913293ff3..95f4ab472 100644
--- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs
@@ -110,7 +110,6 @@ namespace ImageSharp.Drawing.Processors
Vector4 sourceVector = color.Color.ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
- finalColor.W = backgroundVector.W;
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
index fed97275d..4f468c707 100644
--- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
@@ -202,7 +202,6 @@ namespace ImageSharp.Drawing.Processors
Vector4 sourceVector = applicator[x, y].ToVector4();
Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity);
- finalColor.W = backgroundVector.W;
TColor packed = default(TColor);
packed.PackFromVector4(finalColor);
diff --git a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs
index 4b7da38af..9f490a3a9 100644
--- a/src/ImageSharp.Formats.Bmp/BmpDecoder.cs
+++ b/src/ImageSharp.Formats.Bmp/BmpDecoder.cs
@@ -26,7 +26,7 @@ namespace ImageSharp.Formats
public class BmpDecoder : IImageDecoder
{
///
- public void Decode(Image image, Stream stream)
+ public void Decode(Image image, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel
{
Guard.NotNull(image, "image");
diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs
index 6edaf178b..d0a3550f6 100644
--- a/src/ImageSharp.Formats.Bmp/BmpEncoder.cs
+++ b/src/ImageSharp.Formats.Bmp/BmpEncoder.cs
@@ -14,17 +14,27 @@ namespace ImageSharp.Formats
/// The encoder can currently only write 24-bit rgb images to streams.
public class BmpEncoder : IImageEncoder
{
+ ///
+ public void Encode(Image image, Stream stream, IEncoderOptions options)
+ where TColor : struct, IPixel
+ {
+ IBmpEncoderOptions bmpOptions = BmpEncoderOptions.Create(options);
+
+ this.Encode(image, stream, bmpOptions);
+ }
+
///
- /// Gets or sets the number of bits per pixel.
+ /// Encodes the image to the specified stream from the .
///
- public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
-
- ///
- public void Encode(Image image, Stream stream)
+ /// The pixel format.
+ /// The to encode from.
+ /// The to encode the image data to.
+ /// The options for the encoder.
+ public void Encode(Image image, Stream stream, IBmpEncoderOptions options)
where TColor : struct, IPixel
{
- BmpEncoderCore encoder = new BmpEncoderCore();
- encoder.Encode(image, stream, this.BitsPerPixel);
+ BmpEncoderCore encoder = new BmpEncoderCore(options);
+ encoder.Encode(image, stream);
}
}
}
diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs
index 02d270a0a..df62fb6f4 100644
--- a/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp.Formats.Bmp/BmpEncoderCore.cs
@@ -16,34 +16,40 @@ namespace ImageSharp.Formats
internal sealed class BmpEncoderCore
{
///
- /// The number of bits per pixel.
+ /// The options for the encoder.
///
- private BmpBitsPerPixel bmpBitsPerPixel;
+ private readonly IBmpEncoderOptions options;
///
/// The amount to pad each row by.
///
private int padding;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ public BmpEncoderCore(IBmpEncoderOptions options)
+ {
+ this.options = options ?? new BmpEncoderOptions();
+ }
+
///
/// Encodes the image to the specified stream from the .
///
/// The pixel format.
/// The to encode from.
/// The to encode the image data to.
- /// The
- public void Encode(ImageBase image, Stream stream, BmpBitsPerPixel bitsPerPixel)
+ public void Encode(ImageBase image, Stream stream)
where TColor : struct, IPixel
{
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- this.bmpBitsPerPixel = bitsPerPixel;
-
// Cast to int will get the bytes per pixel
- short bpp = (short)(8 * (int)bitsPerPixel);
+ short bpp = (short)(8 * (int)this.options.BitsPerPixel);
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
- this.padding = bytesPerLine - (image.Width * (int)bitsPerPixel);
+ this.padding = bytesPerLine - (image.Width * (int)this.options.BitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
@@ -128,7 +134,7 @@ namespace ImageSharp.Formats
{
using (PixelAccessor pixels = image.Lock())
{
- switch (this.bmpBitsPerPixel)
+ switch (this.options.BitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
this.Write32Bit(writer, pixels);
diff --git a/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs
new file mode 100644
index 000000000..a0f9ff8e0
--- /dev/null
+++ b/src/ImageSharp.Formats.Bmp/BmpEncoderOptions.cs
@@ -0,0 +1,45 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public sealed class BmpEncoderOptions : EncoderOptions, IBmpEncoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BmpEncoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ private BmpEncoderOptions(IEncoderOptions options)
+ : base(options)
+ {
+ }
+
+ ///
+ /// Gets or sets the number of bits per pixel.
+ ///
+ public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
+
+ ///
+ /// Converts the options to a instance with a cast
+ /// or by creating a new instance with the specfied options.
+ ///
+ /// The options for the encoder.
+ /// The options for the .
+ internal static IBmpEncoderOptions Create(IEncoderOptions options)
+ {
+ return options as IBmpEncoderOptions ?? new BmpEncoderOptions(options);
+ }
+ }
+}
diff --git a/src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs b/src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs
new file mode 100644
index 000000000..6cf37cbae
--- /dev/null
+++ b/src/ImageSharp.Formats.Bmp/IBmpEncoderOptions.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public interface IBmpEncoderOptions : IEncoderOptions
+ {
+ ///
+ /// Gets the number of bits per pixel.
+ ///
+ BmpBitsPerPixel BitsPerPixel { get; }
+ }
+}
diff --git a/src/ImageSharp.Formats.Gif/GifConstants.cs b/src/ImageSharp.Formats.Gif/GifConstants.cs
index 5334bcba3..4af291c2b 100644
--- a/src/ImageSharp.Formats.Gif/GifConstants.cs
+++ b/src/ImageSharp.Formats.Gif/GifConstants.cs
@@ -5,10 +5,12 @@
namespace ImageSharp.Formats
{
+ using System.Text;
+
///
/// Constants that define specific points within a gif.
///
- internal sealed class GifConstants
+ internal static class GifConstants
{
///
/// The file type.
@@ -50,6 +52,11 @@ namespace ImageSharp.Formats
///
public const byte CommentLabel = 0xFE;
+ ///
+ /// The name of the property inside the image properties for the comments.
+ ///
+ public const string Comments = "Comments";
+
///
/// The maximum comment length.
///
@@ -79,5 +86,10 @@ namespace ImageSharp.Formats
/// The end introducer trailer ;.
///
public const byte EndIntroducer = 0x3B;
+
+ ///
+ /// Gets the default encoding to use when reading comments.
+ ///
+ public static Encoding DefaultEncoding { get; } = Encoding.GetEncoding("ASCII");
}
}
diff --git a/src/ImageSharp.Formats.Gif/GifDecoder.cs b/src/ImageSharp.Formats.Gif/GifDecoder.cs
index 76530dc50..b1e8ba928 100644
--- a/src/ImageSharp.Formats.Gif/GifDecoder.cs
+++ b/src/ImageSharp.Formats.Gif/GifDecoder.cs
@@ -14,10 +14,25 @@ namespace ImageSharp.Formats
public class GifDecoder : IImageDecoder
{
///
- public void Decode(Image image, Stream stream)
+ public void Decode(Image image, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel
{
- new GifDecoderCore().Decode(image, stream);
+ IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options);
+
+ this.Decode(image, stream, gifOptions);
+ }
+
+ ///
+ /// Decodes the image from the specified stream to the .
+ ///
+ /// The pixel format.
+ /// The to decode to.
+ /// The containing image data.
+ /// The options for the decoder.
+ public void Decode(Image image, Stream stream, IGifDecoderOptions options)
+ where TColor : struct, IPixel
+ {
+ new GifDecoderCore(options).Decode(image, stream);
}
}
}
diff --git a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs
index 5812b9f29..ab1edc2c7 100644
--- a/src/ImageSharp.Formats.Gif/GifDecoderCore.cs
+++ b/src/ImageSharp.Formats.Gif/GifDecoderCore.cs
@@ -8,6 +8,7 @@ namespace ImageSharp.Formats
using System;
using System.Buffers;
using System.IO;
+ using System.Text;
///
/// Performs the gif decoding operation.
@@ -21,6 +22,11 @@ namespace ImageSharp.Formats
///
private readonly byte[] buffer = new byte[16];
+ ///
+ /// The decoder options.
+ ///
+ private readonly IGifDecoderOptions options;
+
///
/// The image to decode the information to.
///
@@ -61,6 +67,15 @@ namespace ImageSharp.Formats
///
private GifGraphicsControlExtension graphicsControlExtension;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The decoder options.
+ public GifDecoderCore(IGifDecoderOptions options)
+ {
+ this.options = options ?? new GifDecoderOptions();
+ }
+
///
/// Decodes the stream to the image.
///
@@ -225,25 +240,32 @@ namespace ImageSharp.Formats
///
private void ReadComments()
{
- int flag;
+ int length;
- while ((flag = this.currentStream.ReadByte()) != 0)
+ while ((length = this.currentStream.ReadByte()) != 0)
{
- if (flag > GifConstants.MaxCommentLength)
+ if (length > GifConstants.MaxCommentLength)
+ {
+ throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentLength}'");
+ }
+
+ if (this.options.IgnoreMetadata)
{
- throw new ImageFormatException($"Gif comment length '{flag}' exceeds max '{GifConstants.MaxCommentLength}'");
+ this.currentStream.Seek(length, SeekOrigin.Current);
+ continue;
}
- byte[] flagBuffer = ArrayPool.Shared.Rent(flag);
+ byte[] commentsBuffer = ArrayPool.Shared.Rent(length);
try
{
- this.currentStream.Read(flagBuffer, 0, flag);
- this.decodedImage.MetaData.Properties.Add(new ImageProperty("Comments", BitConverter.ToString(flagBuffer, 0, flag)));
+ this.currentStream.Read(commentsBuffer, 0, length);
+ string comments = this.options.TextEncoding.GetString(commentsBuffer, 0, length);
+ this.decodedImage.MetaData.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
}
finally
{
- ArrayPool.Shared.Return(flagBuffer);
+ ArrayPool.Shared.Return(commentsBuffer);
}
}
}
diff --git a/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs
new file mode 100644
index 000000000..bc7709f75
--- /dev/null
+++ b/src/ImageSharp.Formats.Gif/GifDecoderOptions.cs
@@ -0,0 +1,47 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System.Text;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public sealed class GifDecoderOptions : DecoderOptions, IGifDecoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GifDecoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the decoder.
+ private GifDecoderOptions(IDecoderOptions options)
+ : base(options)
+ {
+ }
+
+ ///
+ /// Gets or sets the encoding that should be used when reading comments.
+ ///
+ public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
+
+ ///
+ /// Converts the options to a instance with a cast
+ /// or by creating a new instance with the specfied options.
+ ///
+ /// The options for the decoder.
+ /// The options for the .
+ internal static IGifDecoderOptions Create(IDecoderOptions options)
+ {
+ return options as IGifDecoderOptions ?? new GifDecoderOptions(options);
+ }
+ }
+}
diff --git a/src/ImageSharp.Formats.Gif/GifEncoder.cs b/src/ImageSharp.Formats.Gif/GifEncoder.cs
index de7e03322..cc8516ed9 100644
--- a/src/ImageSharp.Formats.Gif/GifEncoder.cs
+++ b/src/ImageSharp.Formats.Gif/GifEncoder.cs
@@ -8,40 +8,31 @@ namespace ImageSharp.Formats
using System;
using System.IO;
- using ImageSharp.Quantizers;
-
///
/// Image encoder for writing image data to a stream in gif format.
///
public class GifEncoder : IImageEncoder
{
- ///
- /// Gets or sets the quality of output for images.
- ///
- /// For gifs the value ranges from 1 to 256.
- public int Quality { get; set; }
+ ///
+ public void Encode(Image image, Stream stream, IEncoderOptions options)
+ where TColor : struct, IPixel
+ {
+ IGifEncoderOptions gifOptions = GifEncoderOptions.Create(options);
- ///
- /// Gets or sets the transparency threshold.
- ///
- public byte Threshold { get; set; } = 128;
+ this.Encode(image, stream, gifOptions);
+ }
///
- /// Gets or sets the quantizer for reducing the color count.
+ /// Encodes the image to the specified stream from the .
///
- public IQuantizer Quantizer { get; set; }
-
- ///
- public void Encode(Image image, Stream stream)
+ /// The pixel format.
+ /// The to encode from.
+ /// The to encode the image data to.
+ /// The options for the encoder.
+ public void Encode(Image image, Stream stream, IGifEncoderOptions options)
where TColor : struct, IPixel
{
- GifEncoderCore encoder = new GifEncoderCore
- {
- Quality = this.Quality,
- Quantizer = this.Quantizer,
- Threshold = this.Threshold
- };
-
+ GifEncoderCore encoder = new GifEncoderCore(options);
encoder.Encode(image, stream);
}
}
diff --git a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs
index 36cf614d9..38cbba850 100644
--- a/src/ImageSharp.Formats.Gif/GifEncoderCore.cs
+++ b/src/ImageSharp.Formats.Gif/GifEncoderCore.cs
@@ -24,20 +24,23 @@ namespace ImageSharp.Formats
private readonly byte[] buffer = new byte[16];
///
- /// The number of bits requires to store the image palette.
+ /// The options for the encoder.
///
- private int bitDepth;
+ private readonly IGifEncoderOptions options;
///
- /// Gets or sets the quality of output for images.
+ /// The number of bits requires to store the image palette.
///
- /// For gifs the value ranges from 1 to 256.
- public int Quality { get; set; }
+ private int bitDepth;
///
- /// Gets or sets the transparency threshold.
+ /// Initializes a new instance of the class.
///
- public byte Threshold { get; set; } = 128;
+ /// The options for the encoder.
+ public GifEncoderCore(IGifEncoderOptions options)
+ {
+ this.options = options ?? new GifEncoderOptions();
+ }
///
/// Gets or sets the quantizer for reducing the color count.
@@ -56,23 +59,20 @@ namespace ImageSharp.Formats
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- if (this.Quantizer == null)
- {
- this.Quantizer = new OctreeQuantizer();
- }
+ this.Quantizer = this.options.Quantizer ?? new OctreeQuantizer();
// Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
// 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, 256) : 256;
+ int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality;
+ quality = quality > 0 ? quality.Clamp(1, 256) : 256;
// Get the number of bits.
- this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(this.Quality);
+ this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quality);
// Quantize the image returning a palette.
- QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, this.Quality);
+ QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, quality);
int index = this.GetTransparentIndex(quantized);
@@ -84,6 +84,7 @@ namespace ImageSharp.Formats
// Write the first frame.
this.WriteGraphicalControlExtension(image, writer, index);
+ this.WriteComments(image, writer);
this.WriteImageDescriptor(image, writer);
this.WriteColorTable(quantized, writer);
this.WriteImageData(quantized, writer);
@@ -97,7 +98,7 @@ namespace ImageSharp.Formats
for (int i = 0; i < image.Frames.Count; i++)
{
ImageFrame frame = image.Frames[i];
- QuantizedImage quantizedFrame = ((IQuantizer)this.Quantizer).Quantize(frame, this.Quality);
+ QuantizedImage quantizedFrame = ((IQuantizer)this.Quantizer).Quantize(frame, quality);
this.WriteGraphicalControlExtension(frame, writer, this.GetTransparentIndex(quantizedFrame));
this.WriteImageDescriptor(frame, writer);
@@ -106,7 +107,7 @@ namespace ImageSharp.Formats
}
}
- // TODO: Write Comments extension etc
+ // TODO: Write extension etc
writer.Write(GifConstants.EndIntroducer);
}
@@ -229,6 +230,39 @@ namespace ImageSharp.Formats
}
}
+ ///
+ /// Writes the image comments to the stream.
+ ///
+ /// The pixel format.
+ /// The to be encoded.
+ /// The stream to write to.
+ private void WriteComments(Image image, EndianBinaryWriter writer)
+ where TColor : struct, IPixel
+ {
+ if (this.options.IgnoreMetadata == true)
+ {
+ return;
+ }
+
+ ImageProperty property = image.MetaData.Properties.FirstOrDefault(p => p.Name == GifConstants.Comments);
+ if (property == null || string.IsNullOrEmpty(property.Value))
+ {
+ return;
+ }
+
+ byte[] comments = this.options.TextEncoding.GetBytes(property.Value);
+
+ int count = Math.Min(comments.Length, 255);
+
+ this.buffer[0] = GifConstants.ExtensionIntroducer;
+ this.buffer[1] = GifConstants.CommentLabel;
+ this.buffer[2] = (byte)count;
+
+ writer.Write(this.buffer, 0, 3);
+ writer.Write(comments, 0, count);
+ writer.Write(GifConstants.Terminator);
+ }
+
///
/// Writes the graphics control extension to the stream.
///
diff --git a/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs b/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs
new file mode 100644
index 000000000..5d7c6e40b
--- /dev/null
+++ b/src/ImageSharp.Formats.Gif/GifEncoderOptions.cs
@@ -0,0 +1,65 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System.Text;
+
+ using Quantizers;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public sealed class GifEncoderOptions : EncoderOptions, IGifEncoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GifEncoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ private GifEncoderOptions(IEncoderOptions options)
+ : base(options)
+ {
+ }
+
+ ///
+ /// Gets or sets the encoding that should be used when writing comments.
+ ///
+ public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
+
+ ///
+ /// Gets or sets the quality of output for images.
+ ///
+ /// For gifs the value ranges from 1 to 256.
+ public int Quality { get; set; }
+
+ ///
+ /// Gets or sets the transparency threshold.
+ ///
+ public byte Threshold { get; set; } = 128;
+
+ ///
+ /// Gets or sets the quantizer for reducing the color count.
+ ///
+ public IQuantizer Quantizer { get; set; }
+
+ ///
+ /// Converts the options to a instance with a
+ /// cast or by creating a new instance with the specfied options.
+ ///
+ /// The options for the encoder.
+ /// The options for the .
+ internal static IGifEncoderOptions Create(IEncoderOptions options)
+ {
+ return options as IGifEncoderOptions ?? new GifEncoderOptions(options);
+ }
+ }
+}
diff --git a/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs b/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs
new file mode 100644
index 000000000..729bf1d11
--- /dev/null
+++ b/src/ImageSharp.Formats.Gif/IGifDecoderOptions.cs
@@ -0,0 +1,20 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System.Text;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public interface IGifDecoderOptions : IDecoderOptions
+ {
+ ///
+ /// Gets the encoding that should be used when reading comments.
+ ///
+ Encoding TextEncoding { get; }
+ }
+}
diff --git a/src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs b/src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs
new file mode 100644
index 000000000..c1d6b7ad8
--- /dev/null
+++ b/src/ImageSharp.Formats.Gif/IGifEncoderOptions.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System.Text;
+
+ using Quantizers;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public interface IGifEncoderOptions : IEncoderOptions
+ {
+ ///
+ /// Gets the encoding that should be used when writing comments.
+ ///
+ Encoding TextEncoding { get; }
+
+ ///
+ /// Gets the quality of output for images.
+ ///
+ /// For gifs the value ranges from 1 to 256.
+ int Quality { get; }
+
+ ///
+ /// Gets the transparency threshold.
+ ///
+ byte Threshold { get; }
+
+ ///
+ /// Gets the quantizer for reducing the color count.
+ ///
+ IQuantizer Quantizer { get; }
+ }
+}
diff --git a/src/ImageSharp.Formats.Gif/ImageExtensions.cs b/src/ImageSharp.Formats.Gif/ImageExtensions.cs
index e0ec2e8c8..1ba03ed35 100644
--- a/src/ImageSharp.Formats.Gif/ImageExtensions.cs
+++ b/src/ImageSharp.Formats.Gif/ImageExtensions.cs
@@ -21,13 +21,34 @@ namespace ImageSharp
/// The pixel format.
/// The image this method extends.
/// The stream to save the image to.
- /// The quality to save the image to representing the number of colors. Between 1 and 256.
/// Thrown if the stream is null.
///
/// The .
///
- public static Image SaveAsGif(this Image source, Stream stream, int quality = 256)
+ public static Image SaveAsGif(this Image source, Stream stream)
where TColor : struct, IPixel
- => source.Save(stream, new GifEncoder { Quality = quality });
+ {
+ return SaveAsGif(source, stream, null);
+ }
+
+ ///
+ /// Saves the image to the given stream with the gif format.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// The options for the encoder.
+ /// Thrown if the stream is null.
+ ///
+ /// The .
+ ///
+ public static Image SaveAsGif(this Image source, Stream stream, IGifEncoderOptions options)
+ where TColor : struct, IPixel
+ {
+ GifEncoder encoder = new GifEncoder();
+ encoder.Encode(source, stream, options);
+
+ return source;
+ }
}
}
diff --git a/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs
new file mode 100644
index 000000000..a54517965
--- /dev/null
+++ b/src/ImageSharp.Formats.Jpeg/IJpegEncoderOptions.cs
@@ -0,0 +1,26 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public interface IJpegEncoderOptions : IEncoderOptions
+ {
+ ///
+ /// Gets the quality, that will be used to encode the image. Quality
+ /// index must be between 0 and 100 (compression from max to min).
+ ///
+ /// The quality of the jpg image from 0 to 100.
+ int Quality { get; }
+
+ ///
+ /// Gets the subsample ration, that will be used to encode the image.
+ ///
+ /// The subsample ratio of the jpg image.
+ JpegSubsample? Subsample { get; }
+ }
+}
diff --git a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs
index 2cbba02a9..351275ebb 100644
--- a/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs
+++ b/src/ImageSharp.Formats.Jpeg/ImageExtensions.cs
@@ -21,13 +21,34 @@ namespace ImageSharp
/// The pixel format.
/// The image this method extends.
/// The stream to save the image to.
- /// The quality to save the image to. Between 1 and 100.
/// Thrown if the stream is null.
///
/// The .
///
- public static Image SaveAsJpeg(this Image source, Stream stream, int quality = 75)
+ public static Image SaveAsJpeg(this Image source, Stream stream)
where TColor : struct, IPixel
- => source.Save(stream, new JpegEncoder { Quality = quality });
+ {
+ return SaveAsJpeg(source, stream, null);
+ }
+
+ ///
+ /// Saves the image to the given stream with the jpeg format.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// The options for the encoder.
+ /// Thrown if the stream is null.
+ ///
+ /// The .
+ ///
+ public static Image SaveAsJpeg(this Image source, Stream stream, IJpegEncoderOptions options)
+ where TColor : struct, IPixel
+ {
+ JpegEncoder encoder = new JpegEncoder();
+ encoder.Encode(source, stream, options);
+
+ return source;
+ }
}
}
diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs
index 435ae51cf..eeb371d1e 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegDecoder.cs
@@ -14,13 +14,13 @@ namespace ImageSharp.Formats
public class JpegDecoder : IImageDecoder
{
///
- public void Decode(Image image, Stream stream)
+ public void Decode(Image image, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
- using (JpegDecoderCore decoder = new JpegDecoderCore())
+ using (JpegDecoderCore decoder = new JpegDecoderCore(options))
{
decoder.Decode(image, stream, false);
}
diff --git a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
index 657787682..f1b85fa0b 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegDecoderCore.cs
@@ -37,6 +37,11 @@ namespace ImageSharp.Formats
public InputProcessor InputProcessor;
#pragma warning restore SA401
+ ///
+ /// The decoder options.
+ ///
+ private readonly IDecoderOptions options;
+
///
/// The App14 marker color-space
///
@@ -85,8 +90,10 @@ namespace ImageSharp.Formats
///
/// Initializes a new instance of the class.
///
- public JpegDecoderCore()
+ /// The decoder options.
+ public JpegDecoderCore(IDecoderOptions options)
{
+ this.options = options ?? new DecoderOptions();
this.HuffmanTrees = HuffmanTree.CreateHuffmanTrees();
this.QuantizationTables = new Block8x8F[MaxTq + 1];
this.Temp = new byte[2 * Block8x8F.ScalarCount];
@@ -958,7 +965,7 @@ namespace ImageSharp.Formats
private void ProcessApp1Marker(int remaining, Image image)
where TColor : struct, IPixel
{
- if (remaining < 6)
+ if (remaining < 6 || this.options.IgnoreMetadata)
{
this.InputProcessor.Skip(remaining);
return;
diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs
index 07d9b24cd..2f2823fa2 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegEncoder.cs
@@ -5,7 +5,6 @@
namespace ImageSharp.Formats
{
- using System;
using System.IO;
///
@@ -13,73 +12,27 @@ namespace ImageSharp.Formats
///
public class JpegEncoder : IImageEncoder
{
- ///
- /// The quality used to encode the image.
- ///
- private int quality = 75;
-
- ///
- /// The subsamples scheme used to encode the image.
- ///
- private JpegSubsample subsample = JpegSubsample.Ratio420;
-
- ///
- /// Whether subsampling has been specifically set.
- ///
- private bool subsampleSet;
-
- ///
- /// Gets or sets the quality, that will be used to encode the image. Quality
- /// index must be between 0 and 100 (compression from max to min).
- ///
- ///
- /// If the quality is less than or equal to 80, the subsampling ratio will switch to
- ///
- /// The quality of the jpg image from 0 to 100.
- public int Quality
+ ///
+ public void Encode(Image image, Stream stream, IEncoderOptions options)
+ where TColor : struct, IPixel
{
- get { return this.quality; }
- set { this.quality = value.Clamp(1, 100); }
+ IJpegEncoderOptions gifOptions = JpegEncoderOptions.Create(options);
+
+ this.Encode(image, stream, gifOptions);
}
///
- /// Gets or sets the subsample ration, that will be used to encode the image.
+ /// Encodes the image to the specified stream from the .
///
- /// The subsample ratio of the jpg image.
- public JpegSubsample Subsample
- {
- get
- {
- return this.subsample;
- }
-
- set
- {
- this.subsample = value;
- this.subsampleSet = true;
- }
- }
-
- ///
- public void Encode(Image image, Stream stream)
+ /// The pixel format.
+ /// The to encode from.
+ /// The to encode the image data to.
+ /// The options for the encoder.
+ public void Encode(Image image, Stream stream, IJpegEncoderOptions options)
where TColor : struct, IPixel
{
- // Ensure that quality can be set but has a fallback.
- if (image.MetaData.Quality > 0)
- {
- this.Quality = image.MetaData.Quality;
- }
-
- JpegEncoderCore encode = new JpegEncoderCore();
- if (this.subsampleSet)
- {
- encode.Encode(image, stream, this.Quality, this.Subsample);
- }
- else
- {
- // Use 4:2:0 Subsampling at quality < 91% for reduced filesize.
- encode.Encode(image, stream, this.Quality, this.Quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
- }
+ JpegEncoderCore encode = new JpegEncoderCore(options);
+ encode.Encode(image, stream);
}
}
}
diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
index 0309af129..66f400c01 100644
--- a/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderCore.cs
@@ -118,6 +118,11 @@ namespace ImageSharp.Formats
///
private readonly byte[] huffmanBuffer = new byte[179];
+ ///
+ /// The options for the encoder.
+ ///
+ private readonly IJpegEncoderOptions options;
+
///
/// The accumulated bits to write to the stream.
///
@@ -148,15 +153,22 @@ namespace ImageSharp.Formats
///
private JpegSubsample subsample;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ public JpegEncoderCore(IJpegEncoderOptions options)
+ {
+ this.options = options ?? new JpegEncoderOptions();
+ }
+
///
/// Encode writes the image to the jpeg baseline format with the given options.
///
/// The pixel format.
/// The image to write from.
/// The stream to write to.
- /// The quality.
- /// The subsampling mode.
- public void Encode(Image image, Stream stream, int quality, JpegSubsample sample)
+ public void Encode(Image image, Stream stream)
where TColor : struct, IPixel
{
Guard.NotNull(image, nameof(image));
@@ -168,18 +180,17 @@ namespace ImageSharp.Formats
throw new ImageFormatException($"Image is too large to encode at {image.Width}x{image.Height}.");
}
- this.outputStream = stream;
- this.subsample = sample;
-
- if (quality < 1)
+ // Ensure that quality can be set but has a fallback.
+ int quality = this.options.Quality > 0 ? this.options.Quality : image.MetaData.Quality;
+ if (quality == 0)
{
- quality = 1;
+ quality = 75;
}
- if (quality > 100)
- {
- quality = 100;
- }
+ quality = quality.Clamp(1, 100);
+
+ this.outputStream = stream;
+ this.subsample = this.options.Subsample ?? (quality >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
// Convert from a quality rating to a scaling factor.
int scale;
@@ -706,6 +717,11 @@ namespace ImageSharp.Formats
private void WriteProfiles(Image image)
where TColor : struct, IPixel
{
+ if (this.options.IgnoreMetadata)
+ {
+ return;
+ }
+
image.MetaData.SyncProfiles();
this.WriteProfile(image.MetaData.ExifProfile);
}
diff --git a/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs
new file mode 100644
index 000000000..73e483164
--- /dev/null
+++ b/src/ImageSharp.Formats.Jpeg/JpegEncoderOptions.cs
@@ -0,0 +1,56 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public sealed class JpegEncoderOptions : EncoderOptions, IJpegEncoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public JpegEncoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ private JpegEncoderOptions(IEncoderOptions options)
+ : base(options)
+ {
+ }
+
+ ///
+ /// Gets or sets the quality, that will be used to encode the image. Quality
+ /// index must be between 0 and 100 (compression from max to min).
+ ///
+ ///
+ /// If the quality is less than or equal to 90, the subsampling ratio will switch to
+ ///
+ /// The quality of the jpg image from 0 to 100.
+ public int Quality { get; set; }
+
+ ///
+ /// Gets or sets the subsample ration, that will be used to encode the image.
+ ///
+ /// The subsample ratio of the jpg image.
+ public JpegSubsample? Subsample { get; set; }
+
+ ///
+ /// Converts the options to a instance with a
+ /// cast or by creating a new instance with the specfied options.
+ ///
+ /// The options for the encoder.
+ /// The options for the .
+ internal static IJpegEncoderOptions Create(IEncoderOptions options)
+ {
+ return options as IJpegEncoderOptions ?? new JpegEncoderOptions(options);
+ }
+ }
+}
diff --git a/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs b/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs
index d5f810708..b4ec49946 100644
--- a/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs
+++ b/src/ImageSharp.Formats.Png/Filters/AverageFilter.cs
@@ -5,6 +5,8 @@
namespace ImageSharp.Formats
{
+ using System.Runtime.CompilerServices;
+
///
/// The Average filter uses the average of the two neighboring pixels (left and above) to predict
/// the value of a pixel.
@@ -19,6 +21,7 @@ namespace ImageSharp.Formats
/// The previous scanline.
/// The number of bytes per scanline
/// The bytes per pixel.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{
// Average(x) + floor((Raw(x-bpp)+Prior(x))/2)
@@ -42,6 +45,7 @@ namespace ImageSharp.Formats
/// The previous scanline.
/// The filtered scanline result.
/// The bytes per pixel.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel)
{
// Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2)
@@ -67,6 +71,7 @@ namespace ImageSharp.Formats
/// The left byte
/// The above byte
/// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Average(byte left, byte above)
{
return (left + above) >> 1;
diff --git a/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs b/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs
index e5787a944..5abd89296 100644
--- a/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs
+++ b/src/ImageSharp.Formats.Png/Filters/NoneFilter.cs
@@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
+ using System.Runtime.CompilerServices;
///
/// The None filter, the scanline is transmitted unmodified; it is only necessary to
@@ -19,6 +20,7 @@ namespace ImageSharp.Formats
///
/// The scanline to decode
/// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] Decode(byte[] scanline)
{
// No change required.
@@ -30,6 +32,7 @@ namespace ImageSharp.Formats
///
/// The scanline to encode
/// The filtered scanline result.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(byte[] scanline, byte[] result)
{
// Insert a byte before the data.
diff --git a/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs b/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs
index ff208f3d7..a43d4f080 100644
--- a/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs
+++ b/src/ImageSharp.Formats.Png/Filters/PaethFilter.cs
@@ -6,6 +6,7 @@
namespace ImageSharp.Formats
{
using System;
+ using System.Runtime.CompilerServices;
///
/// The Paeth filter computes a simple linear function of the three neighboring pixels (left, above, upper left),
@@ -22,6 +23,7 @@ namespace ImageSharp.Formats
/// The previous scanline.
/// The number of bytes per scanline
/// The bytes per pixel.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel)
{
// Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp))
@@ -46,6 +48,7 @@ namespace ImageSharp.Formats
/// The previous scanline.
/// The filtered scanline result.
/// The bytes per pixel.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel)
{
// Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp))
@@ -76,6 +79,7 @@ namespace ImageSharp.Formats
///
/// The .
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte PaethPredicator(byte left, byte above, byte upperLeft)
{
int p = left + above - upperLeft;
diff --git a/src/ImageSharp.Formats.Png/Filters/SubFilter.cs b/src/ImageSharp.Formats.Png/Filters/SubFilter.cs
index 65e86f4f1..4610ed0ae 100644
--- a/src/ImageSharp.Formats.Png/Filters/SubFilter.cs
+++ b/src/ImageSharp.Formats.Png/Filters/SubFilter.cs
@@ -5,6 +5,8 @@
namespace ImageSharp.Formats
{
+ using System.Runtime.CompilerServices;
+
///
/// The Sub filter transmits the difference between each byte and the value of the corresponding byte
/// of the prior pixel.
@@ -18,6 +20,7 @@ namespace ImageSharp.Formats
/// The scanline to decode
/// The number of bytes per scanline
/// The bytes per pixel.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(byte[] scanline, int bytesPerScanline, int bytesPerPixel)
{
// Sub(x) + Raw(x-bpp)
@@ -37,6 +40,7 @@ namespace ImageSharp.Formats
/// The scanline to encode
/// The filtered scanline result.
/// The bytes per pixel.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(byte[] scanline, byte[] result, int bytesPerPixel)
{
// Sub(x) = Raw(x) - Raw(x-bpp)
diff --git a/src/ImageSharp.Formats.Png/Filters/UpFilter.cs b/src/ImageSharp.Formats.Png/Filters/UpFilter.cs
index 036862ddc..525f50d0f 100644
--- a/src/ImageSharp.Formats.Png/Filters/UpFilter.cs
+++ b/src/ImageSharp.Formats.Png/Filters/UpFilter.cs
@@ -5,6 +5,8 @@
namespace ImageSharp.Formats
{
+ using System.Runtime.CompilerServices;
+
///
/// The Up filter is just like the Sub filter except that the pixel immediately above the current pixel,
/// rather than just to its left, is used as the predictor.
@@ -18,6 +20,7 @@ namespace ImageSharp.Formats
/// The scanline to decode
/// The previous scanline.
/// The number of bytes per scanline
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline)
{
// Up(x) + Prior(x)
@@ -39,6 +42,7 @@ namespace ImageSharp.Formats
/// The scanline to encode
/// The previous scanline.
/// The filtered scanline result.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result)
{
// Up(x) = Raw(x) - Prior(x)
diff --git a/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs b/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs
new file mode 100644
index 000000000..cc6d194bf
--- /dev/null
+++ b/src/ImageSharp.Formats.Png/IPngDecoderOptions.cs
@@ -0,0 +1,20 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System.Text;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public interface IPngDecoderOptions : IDecoderOptions
+ {
+ ///
+ /// Gets the encoding that should be used when reading text chunks.
+ ///
+ Encoding TextEncoding { get; }
+ }
+}
diff --git a/src/ImageSharp.Formats.Png/IPngEncoderOptions.cs b/src/ImageSharp.Formats.Png/IPngEncoderOptions.cs
new file mode 100644
index 000000000..0008080d3
--- /dev/null
+++ b/src/ImageSharp.Formats.Png/IPngEncoderOptions.cs
@@ -0,0 +1,54 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using Quantizers;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public interface IPngEncoderOptions : IEncoderOptions
+ {
+ ///
+ /// Gets the quality of output for images.
+ ///
+ int Quality { get; }
+
+ ///
+ /// Gets the png color type
+ ///
+ PngColorType PngColorType { get; }
+
+ ///
+ /// Gets the compression level 1-9.
+ ///
+ int CompressionLevel { get; }
+
+ ///
+ /// Gets the gamma value, that will be written
+ /// the the stream, when the property
+ /// is set to true.
+ ///
+ /// The gamma value of the image.
+ float Gamma { get; }
+
+ ///
+ /// Gets quantizer for reducing the color count.
+ ///
+ IQuantizer Quantizer { get; }
+
+ ///
+ /// Gets the transparency threshold.
+ ///
+ byte Threshold { get; }
+
+ ///
+ /// Gets a value indicating whether this instance should write
+ /// gamma information to the stream.
+ ///
+ bool WriteGamma { get; }
+ }
+}
diff --git a/src/ImageSharp.Formats.Png/ImageExtensions.cs b/src/ImageSharp.Formats.Png/ImageExtensions.cs
index dcb1c988b..79e96175c 100644
--- a/src/ImageSharp.Formats.Png/ImageExtensions.cs
+++ b/src/ImageSharp.Formats.Png/ImageExtensions.cs
@@ -5,7 +5,6 @@
namespace ImageSharp
{
- using System;
using System.IO;
using Formats;
@@ -21,15 +20,34 @@ namespace ImageSharp
/// The pixel format.
/// The image this method extends.
/// The stream to save the image to.
- /// 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.
- ///
/// Thrown if the stream is null.
///
/// The .
///
- public static Image SaveAsPng(this Image source, Stream stream, int quality = int.MaxValue)
+ public static Image SaveAsPng(this Image source, Stream stream)
where TColor : struct, IPixel
- => source.Save(stream, new PngEncoder { Quality = quality });
+ {
+ return SaveAsPng(source, stream, null);
+ }
+
+ ///
+ /// Saves the image to the given stream with the png format.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The stream to save the image to.
+ /// The options for the encoder.
+ /// Thrown if the stream is null.
+ ///
+ /// The .
+ ///
+ public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options)
+ where TColor : struct, IPixel
+ {
+ PngEncoder encoder = new PngEncoder();
+ encoder.Encode(source, stream, options);
+
+ return source;
+ }
}
}
diff --git a/src/ImageSharp.Formats.Png/PngDecoder.cs b/src/ImageSharp.Formats.Png/PngDecoder.cs
index 088bea591..d527e1654 100644
--- a/src/ImageSharp.Formats.Png/PngDecoder.cs
+++ b/src/ImageSharp.Formats.Png/PngDecoder.cs
@@ -30,16 +30,26 @@ namespace ImageSharp.Formats
///
public class PngDecoder : IImageDecoder
{
+ ///
+ public void Decode(Image image, Stream stream, IDecoderOptions options)
+ where TColor : struct, IPixel
+ {
+ IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options);
+
+ this.Decode(image, stream, pngOptions);
+ }
+
///
/// Decodes the image from the specified stream to the .
///
/// The pixel format.
/// The to decode to.
/// The containing image data.
- public void Decode(Image image, Stream stream)
+ /// The options for the decoder.
+ public void Decode(Image image, Stream stream, IPngDecoderOptions options)
where TColor : struct, IPixel
{
- new PngDecoderCore().Decode(image, stream);
+ new PngDecoderCore(options).Decode(image, stream);
}
}
}
diff --git a/src/ImageSharp.Formats.Png/PngDecoderCore.cs b/src/ImageSharp.Formats.Png/PngDecoderCore.cs
index 47b09d5ff..a7765342e 100644
--- a/src/ImageSharp.Formats.Png/PngDecoderCore.cs
+++ b/src/ImageSharp.Formats.Png/PngDecoderCore.cs
@@ -10,7 +10,7 @@ namespace ImageSharp.Formats
using System.Collections.Generic;
using System.IO;
using System.Linq;
- using System.Text;
+ using System.Runtime.CompilerServices;
using static ComparableExtensions;
@@ -64,6 +64,11 @@ namespace ImageSharp.Formats
///
private readonly char[] chars = new char[4];
+ ///
+ /// The decoder options.
+ ///
+ private readonly IPngDecoderOptions options;
+
///
/// Reusable crc for validating chunks.
///
@@ -120,6 +125,15 @@ namespace ImageSharp.Formats
ColorTypes.Add((int)PngColorType.RgbWithAlpha, new byte[] { 8 });
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The decoder options.
+ public PngDecoderCore(IPngDecoderOptions options)
+ {
+ this.options = options ?? new PngDecoderOptions();
+ }
+
///
/// Gets or sets the png color type
///
@@ -763,6 +777,11 @@ namespace ImageSharp.Formats
private void ReadTextChunk(Image image, byte[] data, int length)
where TColor : struct, IPixel
{
+ if (this.options.IgnoreMetadata)
+ {
+ return;
+ }
+
int zeroIndex = 0;
for (int i = 0; i < length; i++)
@@ -774,8 +793,8 @@ namespace ImageSharp.Formats
}
}
- string name = Encoding.Unicode.GetString(data, 0, zeroIndex);
- string value = Encoding.Unicode.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
+ string name = this.options.TextEncoding.GetString(data, 0, zeroIndex);
+ string value = this.options.TextEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1);
image.MetaData.Properties.Add(new ImageProperty(name, value));
}
@@ -927,12 +946,7 @@ namespace ImageSharp.Formats
private void ReadChunkLength(PngChunk chunk)
{
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
- if (numBytes > 1 && numBytes <= 3)
- {
- throw new ImageFormatException("Image stream is not valid!");
- }
-
- if (numBytes <= 1)
+ if (numBytes < 4)
{
chunk.Length = -1;
return;
@@ -948,6 +962,7 @@ namespace ImageSharp.Formats
///
/// Th current pass index
/// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ComputeColumnsAdam7(int pass)
{
int width = this.header.Width;
diff --git a/src/ImageSharp.Formats.Png/PngDecoderOptions.cs b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs
new file mode 100644
index 000000000..e8990ec45
--- /dev/null
+++ b/src/ImageSharp.Formats.Png/PngDecoderOptions.cs
@@ -0,0 +1,49 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using System.Text;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public sealed class PngDecoderOptions : DecoderOptions, IPngDecoderOptions
+ {
+ private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII");
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PngDecoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the decoder.
+ private PngDecoderOptions(IDecoderOptions options)
+ : base(options)
+ {
+ }
+
+ ///
+ /// Gets or sets the encoding that should be used when reading text chunks.
+ ///
+ public Encoding TextEncoding { get; set; } = DefaultEncoding;
+
+ ///
+ /// Converts the options to a instance with a cast
+ /// or by creating a new instance with the specfied options.
+ ///
+ /// The options for the decoder.
+ /// The options for the .
+ internal static IPngDecoderOptions Create(IDecoderOptions options)
+ {
+ return options as IPngDecoderOptions ?? new PngDecoderOptions(options);
+ }
+ }
+}
diff --git a/src/ImageSharp.Formats.Png/PngEncoder.cs b/src/ImageSharp.Formats.Png/PngEncoder.cs
index ba790f4ae..e583f381f 100644
--- a/src/ImageSharp.Formats.Png/PngEncoder.cs
+++ b/src/ImageSharp.Formats.Png/PngEncoder.cs
@@ -5,72 +5,34 @@
namespace ImageSharp.Formats
{
- using System;
using System.IO;
- using ImageSharp.Quantizers;
-
///
/// Image encoder for writing image data to a stream in png format.
///
public class PngEncoder : IImageEncoder
{
- ///
- /// Gets or sets the quality of output for images.
- ///
- public int Quality { get; set; }
-
- ///
- /// Gets or sets the png color type
- ///
- public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
-
- ///
- /// Gets or sets the compression level 1-9.
- /// Defaults to 6.
- ///
- public int CompressionLevel { get; set; } = 6;
-
- ///
- /// Gets or sets the gamma value, that will be written
- /// the the stream, when the property
- /// is set to true. The default value is 2.2F.
- ///
- /// The gamma value of the image.
- public float Gamma { get; set; } = 2.2F;
-
- ///
- /// Gets or sets quantizer for reducing the color count.
- ///
- public IQuantizer Quantizer { get; set; }
+ ///
+ public void Encode(Image image, Stream stream, IEncoderOptions options)
+ where TColor : struct, IPixel
+ {
+ IPngEncoderOptions pngOptions = PngEncoderOptions.Create(options);
- ///
- /// Gets or sets the transparency threshold.
- ///
- public byte Threshold { get; set; } = 0;
+ this.Encode(image, stream, pngOptions);
+ }
///
- /// 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 .
///
- public bool WriteGamma { get; set; }
-
- ///
- public void Encode(Image image, Stream stream)
+ /// The pixel format.
+ /// The to encode from.
+ /// The to encode the image data to.
+ /// The options for the encoder.
+ public void Encode(Image image, Stream stream, IPngEncoderOptions options)
where TColor : struct, IPixel
{
- 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);
}
}
}
diff --git a/src/ImageSharp.Formats.Png/PngEncoderCore.cs b/src/ImageSharp.Formats.Png/PngEncoderCore.cs
index 2324853cb..7950d260c 100644
--- a/src/ImageSharp.Formats.Png/PngEncoderCore.cs
+++ b/src/ImageSharp.Formats.Png/PngEncoderCore.cs
@@ -40,6 +40,11 @@ namespace ImageSharp.Formats
///
private readonly Crc32 crc = new Crc32();
+ ///
+ /// The options for the encoder.
+ ///
+ private readonly IPngEncoderOptions options;
+
///
/// Contains the raw pixel data from an indexed image.
///
@@ -86,44 +91,28 @@ namespace ImageSharp.Formats
private byte[] paeth;
///
- /// Gets or sets the quality of output for images.
- ///
- public int Quality { get; set; }
-
- ///
- /// Gets or sets the png color type
+ /// The quality of output for images.
///
- public PngColorType PngColorType { get; set; }
+ private int quality;
///
- /// Gets or sets the compression level 1-9.
- /// Defaults to 6.
+ /// The png color type.
///
- public int CompressionLevel { get; set; } = 6;
+ private PngColorType pngColorType;
///
- /// 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.
///
- public bool WriteGamma { get; set; }
+ private IQuantizer quantizer;
///
- /// Gets or sets the gamma value, that will be written
- /// the the stream, when the property
- /// is set to true. The default value is 2.2F.
+ /// Initializes a new instance of the class.
///
- /// The gamma value of the image.
- public float Gamma { get; set; } = 2.2F;
-
- ///
- /// Gets or sets the quantizer for reducing the color count.
- ///
- public IQuantizer Quantizer { get; set; }
-
- ///
- /// Gets or sets the transparency threshold.
- ///
- public byte Threshold { get; set; }
+ /// The options for the encoder.
+ public PngEncoderCore(IPngEncoderOptions options)
+ {
+ this.options = options ?? new PngEncoderOptions();
+ }
///
/// Encodes the image to the specified stream from the .
@@ -153,23 +142,26 @@ 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;
+ this.quantizer = this.options.Quantizer;
// 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 +180,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 +190,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 +326,7 @@ namespace ImageSharp.Formats
private byte[] EncodePixelRow(PixelAccessor pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result)
where TColor : struct, IPixel
{
- switch (this.PngColorType)
+ switch (this.pngColorType)
{
case PngColorType.Palette:
Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length);
@@ -362,7 +354,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 +428,7 @@ namespace ImageSharp.Formats
/// The
private int CalculateBytesPerPixel()
{
- switch (this.PngColorType)
+ switch (this.pngColorType)
{
case PngColorType.Grayscale:
return 1;
@@ -488,18 +480,18 @@ namespace ImageSharp.Formats
private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image)
where TColor : struct, IPixel
{
- if (this.Quality > 256)
+ if (this.quality > 256)
{
return null;
}
- if (this.Quantizer == null)
+ if (this.quantizer == null)
{
- this.Quantizer = new OctreeQuantizer();
+ this.quantizer = new OctreeQuantizer();
}
// Quantize the image returning a palette. This boxing is icky.
- QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, this.Quality);
+ QuantizedImage quantized = ((IQuantizer)this.quantizer).Quantize(image, this.quality);
// Grab the palette and write it to the stream.
TColor[] palette = quantized.Palette;
@@ -524,7 +516,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 +570,9 @@ namespace ImageSharp.Formats
/// The containing image data.
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 +600,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 +614,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++)
{
diff --git a/src/ImageSharp.Formats.Png/PngEncoderOptions.cs b/src/ImageSharp.Formats.Png/PngEncoderOptions.cs
new file mode 100644
index 000000000..2891f1974
--- /dev/null
+++ b/src/ImageSharp.Formats.Png/PngEncoderOptions.cs
@@ -0,0 +1,82 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Formats
+{
+ using Quantizers;
+
+ ///
+ /// Encapsulates the options for the .
+ ///
+ public sealed class PngEncoderOptions : EncoderOptions, IPngEncoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PngEncoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options for the encoder.
+ private PngEncoderOptions(IEncoderOptions options)
+ : base(options)
+ {
+ }
+
+ ///
+ /// Gets or sets the quality of output for images.
+ ///
+ public int Quality { get; set; }
+
+ ///
+ /// Gets or sets the png color type
+ ///
+ public PngColorType PngColorType { get; set; } = PngColorType.RgbWithAlpha;
+
+ ///
+ /// Gets or sets the compression level 1-9.
+ /// Defaults to 6.
+ ///
+ public int CompressionLevel { get; set; } = 6;
+
+ ///
+ /// Gets or sets the gamma value, that will be written
+ /// the the stream, when the property
+ /// is set to true. The default value is 2.2F.
+ ///
+ /// The gamma value of the image.
+ public float Gamma { get; set; } = 2.2F;
+
+ ///
+ /// Gets or sets quantizer for reducing the color count.
+ ///
+ public IQuantizer Quantizer { get; set; }
+
+ ///
+ /// Gets or sets the transparency threshold.
+ ///
+ public byte Threshold { get; set; } = 0;
+
+ ///
+ /// Gets or sets a value indicating whether this instance should write
+ /// gamma information to the stream. The default value is false.
+ ///
+ public bool WriteGamma { get; set; }
+
+ ///
+ /// Converts the options to a instance with a
+ /// cast or by creating a new instance with the specfied options.
+ ///
+ /// The options for the encoder.
+ /// The options for the .
+ internal static IPngEncoderOptions Create(IEncoderOptions options)
+ {
+ return options as IPngEncoderOptions ?? new PngEncoderOptions(options);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/DecoderOptions.cs b/src/ImageSharp/Formats/DecoderOptions.cs
new file mode 100644
index 000000000..5257b07b3
--- /dev/null
+++ b/src/ImageSharp/Formats/DecoderOptions.cs
@@ -0,0 +1,37 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Encapsulates the shared decoder options.
+ ///
+ public class DecoderOptions : IDecoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DecoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The decoder options
+ protected DecoderOptions(IDecoderOptions options)
+ {
+ if (options != null)
+ {
+ this.IgnoreMetadata = options.IgnoreMetadata;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
+ ///
+ public bool IgnoreMetadata { get; set; } = false;
+ }
+}
diff --git a/src/ImageSharp/Formats/EncoderOptions.cs b/src/ImageSharp/Formats/EncoderOptions.cs
new file mode 100644
index 000000000..27a7e9781
--- /dev/null
+++ b/src/ImageSharp/Formats/EncoderOptions.cs
@@ -0,0 +1,37 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Encapsulates the shared encoder options.
+ ///
+ public class EncoderOptions : IEncoderOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EncoderOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The encoder options
+ protected EncoderOptions(IEncoderOptions options)
+ {
+ if (options != null)
+ {
+ this.IgnoreMetadata = options.IgnoreMetadata;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the metadata should be ignored when the image is being encoded.
+ ///
+ public bool IgnoreMetadata { get; set; } = false;
+ }
+}
diff --git a/src/ImageSharp/Formats/IDecoderOptions.cs b/src/ImageSharp/Formats/IDecoderOptions.cs
new file mode 100644
index 000000000..cdfd90d5e
--- /dev/null
+++ b/src/ImageSharp/Formats/IDecoderOptions.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Encapsulates the shared decoder options.
+ ///
+ public interface IDecoderOptions
+ {
+ ///
+ /// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
+ ///
+ bool IgnoreMetadata { get; }
+ }
+}
diff --git a/src/ImageSharp/Formats/IEncoderOptions.cs b/src/ImageSharp/Formats/IEncoderOptions.cs
new file mode 100644
index 000000000..0fd3d1c43
--- /dev/null
+++ b/src/ImageSharp/Formats/IEncoderOptions.cs
@@ -0,0 +1,18 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ ///
+ /// Encapsulates the shared encoder options.
+ ///
+ public interface IEncoderOptions
+ {
+ ///
+ /// Gets a value indicating whether the metadata should be ignored when the image is being encoded.
+ ///
+ bool IgnoreMetadata { get; }
+ }
+}
diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs
index 28bda7837..df98870dd 100644
--- a/src/ImageSharp/Formats/IImageDecoder.cs
+++ b/src/ImageSharp/Formats/IImageDecoder.cs
@@ -19,7 +19,8 @@ namespace ImageSharp.Formats
/// The pixel format.
/// The to decode to.
/// The containing image data.
- void Decode(Image image, Stream stream)
+ /// The options for the decoder.
+ void Decode(Image image, Stream stream, IDecoderOptions options)
where TColor : struct, IPixel;
}
}
diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs
index 0ba56477a..918f0d273 100644
--- a/src/ImageSharp/Formats/IImageEncoder.cs
+++ b/src/ImageSharp/Formats/IImageEncoder.cs
@@ -19,7 +19,8 @@ namespace ImageSharp.Formats
/// The pixel format.
/// The to encode from.
/// The to encode the image data to.
- void Encode(Image image, Stream stream)
+ /// The options for the encoder.
+ void Encode(Image image, Stream stream, IEncoderOptions options)
where TColor : struct, IPixel;
}
}
diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs
index bdb6b49a9..af31eff79 100644
--- a/src/ImageSharp/Image.cs
+++ b/src/ImageSharp/Image.cs
@@ -8,6 +8,8 @@ namespace ImageSharp
using System.Diagnostics;
using System.IO;
+ using Formats;
+
///
/// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha
/// packed into a single unsigned integer value.
@@ -29,6 +31,33 @@ namespace ImageSharp
{
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The stream containing image information.
+ ///
+ /// Thrown if the is null.
+ public Image(Stream stream)
+ : base(stream, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The stream containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ /// Thrown if the is null.
+ public Image(Stream stream, IDecoderOptions options)
+ : base(stream, options, null)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -39,12 +68,57 @@ namespace ImageSharp
/// The configuration providing initialization code which allows extending the library.
///
/// Thrown if the is null.
- public Image(Stream stream, Configuration configuration = null)
- : base(stream, configuration)
+ public Image(Stream stream, Configuration configuration)
+ : base(stream, null, configuration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The stream containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ ///
+ /// The configuration providing initialization code which allows extending the library.
+ ///
+ /// Thrown if the is null.
+ public Image(Stream stream, IDecoderOptions options, Configuration configuration)
+ : base(stream, options, configuration)
{
}
#if !NETSTANDARD1_1
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A file path to read image information.
+ ///
+ /// Thrown if the is null.
+ public Image(string filePath)
+ : base(filePath, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A file path to read image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ /// Thrown if the is null.
+ public Image(string filePath, IDecoderOptions options)
+ : base(filePath, options, null)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -55,8 +129,26 @@ namespace ImageSharp
/// The configuration providing initialization code which allows extending the library.
///
/// Thrown if the is null.
- public Image(string filePath, Configuration configuration = null)
- : base(filePath, configuration)
+ public Image(string filePath, Configuration configuration)
+ : base(filePath, null, configuration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A file path to read image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ ///
+ /// The configuration providing initialization code which allows extending the library.
+ ///
+ /// Thrown if the is null.
+ public Image(string filePath, IDecoderOptions options, Configuration configuration)
+ : base(filePath, options, configuration)
{
}
#endif
@@ -67,12 +159,57 @@ namespace ImageSharp
///
/// The byte array containing image information.
///
+ /// Thrown if the is null.
+ public Image(byte[] bytes)
+ : base(bytes, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The byte array containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ /// Thrown if the is null.
+ public Image(byte[] bytes, IDecoderOptions options)
+ : base(bytes, options, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The byte array containing image information.
+ ///
+ ///
+ /// The configuration providing initialization code which allows extending the library.
+ ///
+ /// Thrown if the is null.
+ public Image(byte[] bytes, Configuration configuration)
+ : base(bytes, null, configuration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The byte array containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
///
/// The configuration providing initialization code which allows extending the library.
///
/// Thrown if the is null.
- public Image(byte[] bytes, Configuration configuration = null)
- : base(bytes, configuration)
+ public Image(byte[] bytes, IDecoderOptions options, Configuration configuration)
+ : base(bytes, options, configuration)
{
}
diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TColor}.cs
index bbd839d16..27dee5434 100644
--- a/src/ImageSharp/Image/Image{TColor}.cs
+++ b/src/ImageSharp/Image/Image{TColor}.cs
@@ -46,6 +46,33 @@ namespace ImageSharp
this.CurrentImageFormat = this.Configuration.ImageFormats.First();
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The stream containing image information.
+ ///
+ /// Thrown if the is null.
+ public Image(Stream stream)
+ : this(stream, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The stream containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ /// Thrown if the is null.
+ public Image(Stream stream, IDecoderOptions options)
+ : this(stream, options, null)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -56,14 +83,59 @@ namespace ImageSharp
/// The configuration providing initialization code which allows extending the library.
///
/// Thrown if the is null.
- public Image(Stream stream, Configuration configuration = null)
+ public Image(Stream stream, Configuration configuration)
+ : this(stream, null, configuration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The stream containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ ///
+ /// The configuration providing initialization code which allows extending the library.
+ ///
+ /// Thrown if the is null.
+ public Image(Stream stream, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(stream, nameof(stream));
- this.Load(stream);
+ this.Load(stream, options);
}
#if !NETSTANDARD1_1
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The file containing image information.
+ ///
+ /// Thrown if the is null.
+ public Image(string filePath)
+ : this(filePath, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The file containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ /// Thrown if the is null.
+ public Image(string filePath, IDecoderOptions options)
+ : this(filePath, options, null)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -74,17 +146,62 @@ namespace ImageSharp
/// The configuration providing initialization code which allows extending the library.
///
/// Thrown if the is null.
- public Image(string filePath, Configuration configuration = null)
+ public Image(string filePath, Configuration configuration)
+ : this(filePath, null, configuration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The file containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ ///
+ /// The configuration providing initialization code which allows extending the library.
+ ///
+ /// Thrown if the is null.
+ public Image(string filePath, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(filePath, nameof(filePath));
using (var fs = File.OpenRead(filePath))
{
- this.Load(fs);
+ this.Load(fs, options);
}
}
#endif
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The byte array containing image information.
+ ///
+ /// Thrown if the is null.
+ public Image(byte[] bytes)
+ : this(bytes, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The byte array containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ /// Thrown if the is null.
+ public Image(byte[] bytes, IDecoderOptions options)
+ : this(bytes, options, null)
+ {
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -95,14 +212,32 @@ namespace ImageSharp
/// The configuration providing initialization code which allows extending the library.
///
/// Thrown if the is null.
- public Image(byte[] bytes, Configuration configuration = null)
+ public Image(byte[] bytes, Configuration configuration)
+ : this(bytes, null, configuration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The byte array containing image information.
+ ///
+ ///
+ /// The options for the decoder.
+ ///
+ ///
+ /// The configuration providing initialization code which allows extending the library.
+ ///
+ /// Thrown if the is null.
+ public Image(byte[] bytes, IDecoderOptions options, Configuration configuration)
: base(configuration)
{
Guard.NotNull(bytes, nameof(bytes));
using (MemoryStream stream = new MemoryStream(bytes, false))
{
- this.Load(stream);
+ this.Load(stream, options);
}
}
@@ -200,9 +335,21 @@ namespace ImageSharp
/// Thrown if the stream is null.
/// The
public Image Save(Stream stream)
+ {
+ return this.Save(stream, (IEncoderOptions)null);
+ }
+
+ ///
+ /// Saves the image to the given stream using the currently loaded image format.
+ ///
+ /// The stream to save the image to.
+ /// The options for the encoder.
+ /// Thrown if the stream is null.
+ /// The
+ public Image Save(Stream stream, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
- this.CurrentImageFormat.Encoder.Encode(this, stream);
+ this.CurrentImageFormat.Encoder.Encode(this, stream, options);
return this;
}
@@ -211,13 +358,24 @@ namespace ImageSharp
///
/// The stream to save the image to.
/// The format to save the image as.
- /// Thrown if the stream or format is null.
/// The
public Image Save(Stream stream, IImageFormat format)
+ {
+ return this.Save(stream, format, null);
+ }
+
+ ///
+ /// Saves the image to the given stream using the given image format.
+ ///
+ /// The stream to save the image to.
+ /// The format to save the image as.
+ /// The options for the encoder.
+ /// The
+ public Image Save(Stream stream, IImageFormat format, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(format, nameof(format));
- format.Encoder.Encode(this, stream);
+ format.Encoder.Encode(this, stream, options);
return this;
}
@@ -231,10 +389,25 @@ namespace ImageSharp
/// The .
///
public Image Save(Stream stream, IImageEncoder encoder)
+ {
+ return this.Save(stream, encoder, null);
+ }
+
+ ///
+ /// Saves the image to the given stream using the given image encoder.
+ ///
+ /// The stream to save the image to.
+ /// The encoder to save the image with.
+ /// The options for the encoder.
+ /// Thrown if the stream or encoder is null.
+ ///
+ /// The .
+ ///
+ public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options)
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
- encoder.Encode(this, stream);
+ encoder.Encode(this, stream, options);
// Reset to the start of the stream.
if (stream.CanSeek)
@@ -253,6 +426,18 @@ namespace ImageSharp
/// Thrown if the stream is null.
/// The
public Image Save(string filePath)
+ {
+ return this.Save(filePath, (IEncoderOptions)null);
+ }
+
+ ///
+ /// Saves the image to the given stream using the currently loaded image format.
+ ///
+ /// The file path to save the image to.
+ /// The options for the encoder.
+ /// Thrown if the stream is null.
+ /// The
+ public Image Save(string filePath, IEncoderOptions options)
{
string ext = Path.GetExtension(filePath).Trim('.');
IImageFormat format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase));
@@ -272,6 +457,19 @@ namespace ImageSharp
/// Thrown if the format is null.
/// The
public Image Save(string filePath, IImageFormat format)
+ {
+ return this.Save(filePath, format, null);
+ }
+
+ ///
+ /// Saves the image to the given stream using the currently loaded image format.
+ ///
+ /// The file path to save the image to.
+ /// The format to save the image as.
+ /// The options for the encoder.
+ /// Thrown if the format is null.
+ /// The
+ public Image Save(string filePath, IImageFormat format, IEncoderOptions options)
{
Guard.NotNull(format, nameof(format));
using (FileStream fs = File.Create(filePath))
@@ -288,6 +486,19 @@ namespace ImageSharp
/// Thrown if the encoder is null.
/// The
public Image Save(string filePath, IImageEncoder encoder)
+ {
+ return this.Save(filePath, encoder, null);
+ }
+
+ ///
+ /// Saves the image to the given stream using the currently loaded image format.
+ ///
+ /// The file path to save the image to.
+ /// The encoder to save the image with.
+ /// The options for the encoder.
+ /// Thrown if the encoder is null.
+ /// The
+ public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options)
{
Guard.NotNull(encoder, nameof(encoder));
using (FileStream fs = File.Create(filePath))
@@ -397,10 +608,11 @@ namespace ImageSharp
/// Loads the image from the given stream.
///
/// The stream containing image information.
+ /// The options for the decoder.
///
/// Thrown if the stream is not readable nor seekable.
///
- private void Load(Stream stream)
+ private void Load(Stream stream, IDecoderOptions options)
{
if (!this.Configuration.ImageFormats.Any())
{
@@ -414,7 +626,7 @@ namespace ImageSharp
if (stream.CanSeek)
{
- if (this.Decode(stream))
+ if (this.Decode(stream, options))
{
return;
}
@@ -427,7 +639,7 @@ namespace ImageSharp
stream.CopyTo(ms);
ms.Position = 0;
- if (this.Decode(ms))
+ if (this.Decode(ms, options))
{
return;
}
@@ -449,10 +661,11 @@ namespace ImageSharp
/// Decodes the image stream to the current image.
///
/// The stream.
+ /// The options for the decoder.
///
/// The .
///
- private bool Decode(Stream stream)
+ private bool Decode(Stream stream, IDecoderOptions options)
{
int maxHeaderSize = this.Configuration.MaxHeaderSize;
if (maxHeaderSize <= 0)
@@ -479,7 +692,7 @@ namespace ImageSharp
return false;
}
- format.Decoder.Decode(this, stream);
+ format.Decoder.Decode(this, stream, options);
this.CurrentImageFormat = format;
return true;
}
diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
index a41afd333..1d3ead81f 100644
--- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
@@ -32,7 +32,6 @@ namespace ImageSharp.Tests.Drawing
using (FileStream output = File.OpenWrite($"{path}/Simple.png"))
{
image
- .BackgroundColor(Color.Blue)
.FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true))
.Save(output);
}
@@ -45,7 +44,7 @@ namespace ImageSharp.Tests.Drawing
Assert.Equal(Color.HotPink, sourcePixels[50, 50]);
- Assert.Equal(Color.Blue, sourcePixels[2, 2]);
+ Assert.NotEqual(Color.HotPink, sourcePixels[2, 2]);
}
}
}
diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs
index b147e97e8..765ff3a42 100644
--- a/tests/ImageSharp.Tests/FileTestBase.cs
+++ b/tests/ImageSharp.Tests/FileTestBase.cs
@@ -30,7 +30,8 @@ namespace ImageSharp.Tests
TestFile.Create(TestImages.Bmp.Car),
// TestFile.Create(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only
TestFile.Create(TestImages.Png.Splash),
- // TestFile.Create(TestImages.Png.ChunkLength), // Perf: Enable for local testing only
+ // TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only
+ // TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Blur), // Perf: Enable for local testing only
// TestFile.Create(TestImages.Png.Indexed), // Perf: Enable for local testing only
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
similarity index 80%
rename from tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs
rename to tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
index c1275335d..497abb7d5 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BitmapTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
@@ -7,16 +7,14 @@ using ImageSharp.Formats;
namespace ImageSharp.Tests
{
- using System.IO;
-
using Xunit;
- public class BitmapTests : FileTestBase
+ public class BmpEncoderTests : FileTestBase
{
public static readonly TheoryData BitsPerPixel
= new TheoryData
{
- BmpBitsPerPixel.Pixel24 ,
+ BmpBitsPerPixel.Pixel24,
BmpBitsPerPixel.Pixel32
};
@@ -31,7 +29,7 @@ namespace ImageSharp.Tests
string filename = file.GetFileNameWithoutExtension(bitsPerPixel);
using (Image image = file.CreateImage())
{
- image.Save($"{path}/{filename}.bmp", new BmpEncoder { BitsPerPixel = bitsPerPixel });
+ image.Save($"{path}/{filename}.bmp", new BmpEncoderOptions { BitsPerPixel = bitsPerPixel });
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
new file mode 100644
index 000000000..b874a1585
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
@@ -0,0 +1,66 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System.Text;
+ using Xunit;
+
+ using ImageSharp.Formats;
+
+ public class GifDecoderTests
+ {
+ [Fact]
+ public void Decode_IgnoreMetadataIsFalse_CommentsAreRead()
+ {
+ var options = new DecoderOptions()
+ {
+ IgnoreMetadata = false
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Gif.Rings);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Equal(1, image.MetaData.Properties.Count);
+ Assert.Equal("Comments", image.MetaData.Properties[0].Name);
+ Assert.Equal("ImageSharp", image.MetaData.Properties[0].Value);
+ }
+ }
+
+ [Fact]
+ public void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored()
+ {
+ var options = new DecoderOptions()
+ {
+ IgnoreMetadata = true
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Gif.Rings);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Equal(0, image.MetaData.Properties.Count);
+ }
+ }
+
+ [Fact]
+ public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding()
+ {
+ var options = new GifDecoderOptions()
+ {
+ TextEncoding = Encoding.Unicode
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Gif.Rings);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Equal(1, image.MetaData.Properties.Count);
+ Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value);
+ }
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
new file mode 100644
index 000000000..da1323627
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
@@ -0,0 +1,90 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System.IO;
+ using Xunit;
+
+ using ImageSharp.Formats;
+
+ public class GifEncoderTests
+ {
+ [Fact]
+ public void Encode_IgnoreMetadataIsFalse_CommentsAreWritten()
+ {
+ var options = new EncoderOptions()
+ {
+ IgnoreMetadata = false
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Gif.Rings);
+
+ using (Image input = testFile.CreateImage())
+ {
+ using (MemoryStream memStream = new MemoryStream())
+ {
+ input.Save(memStream, new GifFormat(), options);
+
+ memStream.Position = 0;
+ using (Image output = new Image(memStream))
+ {
+ Assert.Equal(1, output.MetaData.Properties.Count);
+ Assert.Equal("Comments", output.MetaData.Properties[0].Name);
+ Assert.Equal("ImageSharp", output.MetaData.Properties[0].Value);
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void Encode_IgnoreMetadataIsTrue_CommentsAreNotWritten()
+ {
+ var options = new GifEncoderOptions()
+ {
+ IgnoreMetadata = true
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Gif.Rings);
+
+ using (Image input = testFile.CreateImage())
+ {
+ using (MemoryStream memStream = new MemoryStream())
+ {
+ input.SaveAsGif(memStream, options);
+
+ memStream.Position = 0;
+ using (Image output = new Image(memStream))
+ {
+ Assert.Equal(0, output.MetaData.Properties.Count);
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void Encode_CommentIsToLong_CommentIsTrimmed()
+ {
+ using (Image input = new Image(1, 1))
+ {
+ string comments = new string('c', 256);
+ input.MetaData.Properties.Add(new ImageProperty("Comments", comments));
+
+ using (MemoryStream memStream = new MemoryStream())
+ {
+ input.Save(memStream, new GifFormat());
+
+ memStream.Position = 0;
+ using (Image output = new Image(memStream))
+ {
+ Assert.Equal(1, output.MetaData.Properties.Count);
+ Assert.Equal("Comments", output.MetaData.Properties[0].Name);
+ Assert.Equal(255, output.MetaData.Properties[0].Value.Length);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
index 71ce4e165..7cb9a7cf2 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
@@ -61,12 +61,13 @@ namespace ImageSharp.Tests
byte[] data;
using (Image image = provider.GetImage())
{
- JpegEncoder encoder = new JpegEncoder() { Subsample = subsample, Quality = quality };
+ JpegEncoder encoder = new JpegEncoder();
+ JpegEncoderOptions options = new JpegEncoderOptions { Subsample = subsample, Quality = quality };
data = new byte[65536];
using (MemoryStream ms = new MemoryStream(data))
{
- image.Save(ms, encoder);
+ image.Save(ms, encoder, options);
}
}
@@ -90,7 +91,7 @@ namespace ImageSharp.Tests
ms.Seek(0, SeekOrigin.Begin);
Image mirror = provider.Factory.CreateImage(1, 1);
- using (JpegDecoderCore decoder = new JpegDecoderCore())
+ using (JpegDecoderCore decoder = new JpegDecoderCore(null))
{
decoder.Decode(mirror, ms, true);
@@ -120,5 +121,37 @@ namespace ImageSharp.Tests
Assert.Equal(72, image.MetaData.VerticalResolution);
}
}
+
+ [Fact]
+ public void Decode_IgnoreMetadataIsFalse_ExifProfileIsRead()
+ {
+ var options = new DecoderOptions()
+ {
+ IgnoreMetadata = false
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.NotNull(image.MetaData.ExifProfile);
+ }
+ }
+
+ [Fact]
+ public void Decode_IgnoreMetadataIsTrue_ExifProfileIgnored()
+ {
+ var options = new DecoderOptions()
+ {
+ IgnoreMetadata = true
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Null(image.MetaData.ExifProfile);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
index 6518c3e6b..741e785c0 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
@@ -38,11 +38,12 @@ namespace ImageSharp.Tests
{
image.MetaData.Quality = quality;
image.MetaData.ExifProfile = null; // Reduce the size of the file
- JpegEncoder encoder = new JpegEncoder { Subsample = subsample, Quality = quality };
+ JpegEncoder encoder = new JpegEncoder();
+ JpegEncoderOptions options = new JpegEncoderOptions { Subsample = subsample, Quality = quality };
provider.Utility.TestName += $"{subsample}_Q{quality}";
provider.Utility.SaveTestOutputFile(image, "png");
- provider.Utility.SaveTestOutputFile(image, "jpg", encoder);
+ provider.Utility.SaveTestOutputFile(image, "jpg", encoder, options);
}
}
@@ -59,13 +60,63 @@ namespace ImageSharp.Tests
using (FileStream outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg")))
{
- JpegEncoder encoder = new JpegEncoder()
- {
- Subsample = subSample,
- Quality = quality
- };
+ JpegEncoder encoder = new JpegEncoder();
- image.Save(outputStream, encoder);
+ image.Save(outputStream, encoder, new JpegEncoderOptions()
+ {
+ Subsample = subSample,
+ Quality = quality
+ });
+ }
+ }
+ }
+
+ [Fact]
+ public void Encode_IgnoreMetadataIsFalse_ExifProfileIsWritten()
+ {
+ var options = new EncoderOptions()
+ {
+ IgnoreMetadata = false
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan);
+
+ using (Image input = testFile.CreateImage())
+ {
+ using (MemoryStream memStream = new MemoryStream())
+ {
+ input.Save(memStream, new JpegFormat(), options);
+
+ memStream.Position = 0;
+ using (Image output = new Image(memStream))
+ {
+ Assert.NotNull(output.MetaData.ExifProfile);
+ }
+ }
+ }
+ }
+
+ [Fact]
+ public void Encode_IgnoreMetadataIsTrue_ExifProfileIgnored()
+ {
+ var options = new JpegEncoderOptions()
+ {
+ IgnoreMetadata = true
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan);
+
+ using (Image input = testFile.CreateImage())
+ {
+ using (MemoryStream memStream = new MemoryStream())
+ {
+ input.SaveAsJpeg(memStream, options);
+
+ memStream.Position = 0;
+ using (Image output = new Image(memStream))
+ {
+ Assert.Null(output.MetaData.ExifProfile);
+ }
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs
index bfe1f1e76..50e678bf0 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs
@@ -81,8 +81,9 @@ namespace ImageSharp.Tests
{
foreach (Image img in testImages)
{
- JpegEncoder encoder = new JpegEncoder() { Quality = quality, Subsample = subsample };
- img.Save(ms, encoder);
+ JpegEncoder encoder = new JpegEncoder();
+ JpegEncoderOptions options = new JpegEncoderOptions { Quality = quality, Subsample = subsample };
+ img.Save(ms, encoder, options);
ms.Seek(0, SeekOrigin.Begin);
}
},
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
new file mode 100644
index 000000000..921530806
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -0,0 +1,66 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests
+{
+ using System.Text;
+ using Xunit;
+
+ using ImageSharp.Formats;
+
+ public class PngDecoderTests
+ {
+ [Fact]
+ public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead()
+ {
+ var options = new PngDecoderOptions()
+ {
+ IgnoreMetadata = false
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Png.Blur);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Equal(1, image.MetaData.Properties.Count);
+ Assert.Equal("Software", image.MetaData.Properties[0].Name);
+ Assert.Equal("paint.net 4.0.6", image.MetaData.Properties[0].Value);
+ }
+ }
+
+ [Fact]
+ public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored()
+ {
+ var options = new PngDecoderOptions()
+ {
+ IgnoreMetadata = true
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Png.Blur);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Equal(0, image.MetaData.Properties.Count);
+ }
+ }
+
+ [Fact]
+ public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding()
+ {
+ var options = new PngDecoderOptions()
+ {
+ TextEncoding = Encoding.Unicode
+ };
+
+ TestFile testFile = TestFile.Create(TestImages.Png.Blur);
+
+ using (Image image = testFile.CreateImage(options))
+ {
+ Assert.Equal(1, image.MetaData.Properties.Count);
+ Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
similarity index 91%
rename from tests/ImageSharp.Tests/Formats/Png/PngTests.cs
rename to tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
index 5ba00eb4d..49be75139 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
@@ -10,11 +10,9 @@ namespace ImageSharp.Tests
using System.IO;
using System.Threading.Tasks;
- using Formats;
-
using Xunit;
- public class PngTests : FileTestBase
+ public class PngEncoderTests : FileTestBase
{
[Fact]
public void ImageCanSaveIndexedPng()
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs
index aea4330c6..0ed724fad 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.cs
@@ -59,7 +59,7 @@ namespace ImageSharp.Tests
ArgumentNullException ex = Assert.Throws(
() =>
{
- new Image(null);
+ new Image((string) null);
});
}
diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs
index 891a45cec..0c9cc5f47 100644
--- a/tests/ImageSharp.Tests/TestFile.cs
+++ b/tests/ImageSharp.Tests/TestFile.cs
@@ -130,6 +130,18 @@ namespace ImageSharp.Tests
return new Image(this.image);
}
+ ///
+ /// Creates a new image.
+ ///
+ /// The options for the decoder.
+ ///
+ /// The .
+ ///
+ public Image CreateImage(IDecoderOptions options)
+ {
+ return new Image(this.Bytes, options);
+ }
+
///
/// Gets the correct path to the formats directory.
///
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 4924cc1de..f0a0e8dd8 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -25,18 +25,19 @@ namespace ImageSharp.Tests
public const string SplashInterlaced = "Png/splash-interlaced.png";
public const string Interlaced = "Png/interlaced.png";
- // filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
+ // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png";
public const string Filter1 = "Png/filter1.png";
public const string Filter2 = "Png/filter2.png";
public const string Filter3 = "Png/filter3.png";
public const string Filter4 = "Png/filter4.png";
- // filter changing per scanline
+ // Filter changing per scanline
public const string FilterVar = "Png/filterVar.png";
- // Chunk length of 1 by end marker
- public const string ChunkLength = "Png/chunklength1.png";
+ // Odd chunk lengths
+ public const string ChunkLength1 = "Png/chunklength1.png";
+ public const string ChunkLength2 = "Png/chunklength2.png";
}
public static class Jpeg
diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif b/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif
index 53f2890fa..76f093a20 100644
Binary files a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif and b/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif differ
diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png
new file mode 100644
index 000000000..0d14abdc4
Binary files /dev/null and b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png differ
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
index 54be62d37..38429b278 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
@@ -75,7 +75,8 @@ namespace ImageSharp.Tests
/// The image instance
/// The requested extension
/// Optional encoder
- public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null)
+ /// Optional encoder options
+ public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null)
where TColor : struct, IPixel
{
string path = this.GetTestOutputFileName(extension);
@@ -86,7 +87,7 @@ namespace ImageSharp.Tests
using (var stream = File.OpenWrite(path))
{
- image.Save(stream, encoder);
+ image.Save(stream, encoder, options);
}
}