diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000000..aeae5c6ca9
--- /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 cfbd18de6e..9d5c9788a7 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 81e29fee5b..d9ae997bad 100644
--- a/build/icons/imagesharp-logo-128.png
+++ b/build/icons/imagesharp-logo-128.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:01f79400a4a77c764273a97fbc76982546e88510fa4cc64a7b2e83e265b0e141
-size 2490
+oid sha256:47f14bb7d24f7228cd8833d8d1881a72750b2c7813f391bd2a0dd0eeea936841
+size 6569
diff --git a/build/icons/imagesharp-logo-256.png b/build/icons/imagesharp-logo-256.png
index 5b78542cba..f1e67dd78b 100644
--- a/build/icons/imagesharp-logo-256.png
+++ b/build/icons/imagesharp-logo-256.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5598d4cb5bad33aefc1084c4f389b071143a59505f00f7b7831c81254f1140f8
-size 4225
+oid sha256:757ec2f45cc5f9c2083fc65a236100f1a7776eee16bd1095a550e05783106a9f
+size 13949
diff --git a/build/icons/imagesharp-logo-32.png b/build/icons/imagesharp-logo-32.png
index 31e32300da..80435989ad 100644
--- a/build/icons/imagesharp-logo-32.png
+++ b/build/icons/imagesharp-logo-32.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:536e75abeaa2c35f34a95d34bee4f8bd13cf5514a979960566945da4ec8827d1
-size 979
+oid sha256:0f3a5375ce20321c2cfdc888a21dcb629d3e6a85641df5cca7c66e5b2a5f70f6
+size 1439
diff --git a/build/icons/imagesharp-logo-512.png b/build/icons/imagesharp-logo-512.png
index ee02fb650d..a5f880e3af 100644
--- a/build/icons/imagesharp-logo-512.png
+++ b/build/icons/imagesharp-logo-512.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c8288a69f4182b25e04ba6419b452c6cdbca0013856352c0aeb08abc67f67e4d
-size 7951
+oid sha256:0e4cd18406375999c2bee1c39ad439b37f9524485d6e247ab0f14d2eb90a65f3
+size 31256
diff --git a/build/icons/imagesharp-logo-64.png b/build/icons/imagesharp-logo-64.png
index 9919f97d6c..f59e202bf9 100644
--- a/build/icons/imagesharp-logo-64.png
+++ b/build/icons/imagesharp-logo-64.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6ab6d98a7a55caf570016a62a2f4d72842e22cbe03460ad60a4dc196b7b1d303
-size 1698
+oid sha256:fa25e5dbe84f942107a1c29f4f68ff2a73f497412ae91b6e60fc5464bc9b5f05
+size 3132
diff --git a/build/icons/imagesharp-logo-heading.png b/build/icons/imagesharp-logo-heading.png
new file mode 100644
index 0000000000..20779215fd
--- /dev/null
+++ b/build/icons/imagesharp-logo-heading.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bf2335642c6fd291befa0b203dbfb3387d99434369399b35aeea037c0f9eba45
+size 10474
diff --git a/build/icons/imagesharp-logo.png b/build/icons/imagesharp-logo.png
index 0f80ceeaa6..e0f1854ccb 100644
--- a/build/icons/imagesharp-logo.png
+++ b/build/icons/imagesharp-logo.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2f20a3b2613811efa3455ee65b532284d88636d8796ee2279c08a584aa01744e
-size 19129
+oid sha256:e4217fe820af06a593903441f0719cab1ca650fd4de795f0e6808c4240a89819
+size 59646
diff --git a/build/icons/imagesharp-logo.svg b/build/icons/imagesharp-logo.svg
index 9638e9785b..2df3cc80ce 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 764d7c4a62..6bc5630eed 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 bf6b1fae8f..b761233c37 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 913293ff34..95f4ab4726 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 fed97275d6..4f468c7070 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 4b7da38afc..9f490a3a9b 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 6edaf178bc..d0a3550f6f 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 02d270a0ad..df62fb6f40 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 0000000000..a0f9ff8e05
--- /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 0000000000..6cf37cbae3
--- /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 5334bcba37..4af291c2ba 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 76530dc504..b1e8ba928d 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 5812b9f297..ab1edc2c76 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 0000000000..bc7709f759
--- /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 de7e03322d..cc8516ed9d 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 36cf614d94..38cbba8500 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 0000000000..5d7c6e40b6
--- /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 0000000000..729bf1d111
--- /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 0000000000..c1d6b7ad86
--- /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 e0ec2e8c84..1ba03ed351 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 0000000000..a545179653
--- /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 2cbba02a90..351275ebb7 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 435ae51cfd..eeb371d1e7 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 6577876828..f1b85fa0bf 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 07d9b24cd9..2f2823fa28 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 0309af1299..66f400c017 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 0000000000..73e483164c
--- /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 d5f8107082..b4ec499469 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 e5787a9442..5abd892964 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 ff208f3d77..a43d4f0802 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 65e86f4f10..4610ed0ae4 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 036862ddce..525f50d0ff 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 0000000000..cc6d194bfc
--- /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 0000000000..0008080d3f
--- /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 dcb1c988b7..79e96175c1 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 088bea5919..d527e1654d 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 47b09d5ffb..a7765342e9 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 0000000000..e8990ec456
--- /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 ba790f4ae9..e583f381fb 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 2324853cba..7950d260c7 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 0000000000..2891f1974e
--- /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 0000000000..5257b07b39
--- /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 0000000000..27a7e9781d
--- /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 0000000000..cdfd90d5e2
--- /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 0000000000..0fd3d1c438
--- /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 28bda7837e..df98870ddd 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 0ba56477ac..918f0d273c 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 bdb6b49a92..af31eff792 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 bbd839d168..27dee54342 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 a41afd3334..1d3ead81f2 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 b147e97e88..765ff3a423 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 c1275335d2..497abb7d56 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 0000000000..b874a1585f
--- /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 0000000000..da1323627f
--- /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 71ce4e1652..7cb9a7cf23 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 6518c3e6b6..741e785c0c 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 bfe1f1e76c..50e678bf08 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 0000000000..921530806c
--- /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 5ba00eb4d3..49be751391 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 aea4330c68..0ed724fadc 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 891a45cec9..0c9cc5f47c 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 4924cc1dee..f0a0e8dd81 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 acd5d6339c..a714dbfbbf 100644
--- a/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif
+++ b/tests/ImageSharp.Tests/TestImages/Formats/Gif/rings.gif
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:716448da88152225767c024aac498f5b7562b6e8391907cefc9d03dba40050fd
-size 53435
+oid sha256:a7b9b5f5056a8d90134d72b462bf37675ab37aa2551ffeae0072037a0a27ad74
+size 53457
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 0000000000..85929cb1ef
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/chunklength2.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b508f05da9f50edd87ed387abc8222620682ef2f59f036fd66ac7cbaf95ffe44
+size 12181
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
index 54be62d37d..38429b2786 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);
}
}