Browse Source

Merge pull request #8 from SixLabors/master

Changes from master
pull/1050/head
Sergio Pedri 7 years ago
committed by GitHub
parent
commit
bf84f98537
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 1
      src/ImageSharp.Drawing/Primitives/ShapeRegion.cs
  3. 2
      src/ImageSharp.Drawing/Processing/Brushes.cs
  4. 2
      src/ImageSharp.Drawing/Processing/ColorStop.cs
  5. 4
      src/ImageSharp.Drawing/Processing/DrawingHelpers.cs
  6. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs
  7. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs
  8. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs
  9. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs
  10. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs
  11. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs
  12. 1
      src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs
  13. 2
      src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs
  14. 1
      src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs
  15. 1
      src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs
  16. 1
      src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs
  17. 2
      src/ImageSharp.Drawing/Processing/IPen.cs
  18. 1
      src/ImageSharp.Drawing/Processing/ImageBrush.cs
  19. 1
      src/ImageSharp.Drawing/Processing/Pen.cs
  20. 2
      src/ImageSharp.Drawing/Processing/Pens.cs
  21. 5
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  22. 3
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  23. 1
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
  24. 6
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
  25. 27
      src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
  26. 40
      src/ImageSharp/Advanced/AdvancedImageExtensions.cs
  27. 108
      src/ImageSharp/Advanced/AotCompilerTools.cs
  28. 2
      src/ImageSharp/Color/Color.cs
  29. 4
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs
  30. 4
      src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs
  31. 14
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
  32. 2
      src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs
  33. 6
      src/ImageSharp/Common/Extensions/ConfigurationExtensions.cs
  34. 4
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  35. 105
      src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
  36. 14
      src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs
  37. 28
      src/ImageSharp/Common/Helpers/Guard.cs
  38. 13
      src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
  39. 6
      src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
  40. 9
      src/ImageSharp/Common/Helpers/SimdUtils.cs
  41. 8
      src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs
  42. 2
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  43. 20
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  44. 4
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  45. 2
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  46. 2
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  47. 23
      src/ImageSharp/Formats/Gif/GifConstants.cs
  48. 8
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  49. 24
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  50. 10
      src/ImageSharp/Formats/Gif/GifEncoder.cs
  51. 77
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  52. 4
      src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
  53. 17
      src/ImageSharp/Formats/Gif/GifMetaData.cs
  54. 8
      src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs
  55. 10
      src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs
  56. 6
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  57. 10
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  58. 4
      src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
  59. 2
      src/ImageSharp/Formats/IImageFormatDetector.cs
  60. 4
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  61. 12
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  62. 20
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  63. 8
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  64. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
  65. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  66. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
  67. 7
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
  68. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
  69. 1
      src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs
  70. 1
      src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs
  71. 1
      src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs
  72. 6
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  73. 32
      src/ImageSharp/Formats/Png/Adam7.cs
  74. 10
      src/ImageSharp/Formats/Png/Filters/SubFilter.cs
  75. 9
      src/ImageSharp/Formats/Png/IPngDecoderOptions.cs
  76. 20
      src/ImageSharp/Formats/Png/IPngEncoderOptions.cs
  77. 17
      src/ImageSharp/Formats/Png/PngChunkType.cs
  78. 41
      src/ImageSharp/Formats/Png/PngConstants.cs
  79. 9
      src/ImageSharp/Formats/Png/PngDecoder.cs
  80. 268
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  81. 23
      src/ImageSharp/Formats/Png/PngEncoder.cs
  82. 852
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  83. 57
      src/ImageSharp/Formats/Png/PngEncoderHelpers.cs
  84. 82
      src/ImageSharp/Formats/Png/PngEncoderOptions.cs
  85. 152
      src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs
  86. 2
      src/ImageSharp/Formats/Png/PngInterlaceMode.cs
  87. 62
      src/ImageSharp/Formats/Png/PngMetaData.cs
  88. 143
      src/ImageSharp/Formats/Png/PngTextData.cs
  89. 2
      src/ImageSharp/IO/IFileSystem.cs
  90. 8
      src/ImageSharp/Image.FromBytes.cs
  91. 22
      src/ImageSharp/Image.cs
  92. 13
      src/ImageSharp/ImageExtensions.cs
  93. 2
      src/ImageSharp/ImageFrameCollection{TPixel}.cs
  94. 11
      src/ImageSharp/ImageFrame{TPixel}.cs
  95. 2
      src/ImageSharp/ImageSharp.csproj
  96. 8
      src/ImageSharp/Image{TPixel}.cs
  97. 5
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  98. 24
      src/ImageSharp/Memory/Buffer2D{T}.cs
  99. 4
      src/ImageSharp/Memory/MemoryOwnerExtensions.cs
  100. 35
      src/ImageSharp/MetaData/ImageMetaData.cs

2
README.md

@ -92,7 +92,7 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
// Individual pixels
using (Image<Rgba32> image = new Image<Rgba32>(400, 400))
using (var image = new Image<Rgba32>(400, 400))
{
image[200, 200] = Rgba32.White;
}

1
src/ImageSharp.Drawing/Primitives/ShapeRegion.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;

2
src/ImageSharp.Drawing/Processing/Brushes.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>

2
src/ImageSharp.Drawing/Processing/ColorStop.cs

@ -3,8 +3,6 @@
using System.Diagnostics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>

4
src/ImageSharp.Drawing/Processing/DrawingHelpers.cs

@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Processing
public static DenseMatrix<TPixel> ToPixelMatrix<TPixel>(this DenseMatrix<Color> colorMatrix, Configuration configuration)
where TPixel : struct, IPixel<TPixel>
{
DenseMatrix<TPixel> result = new DenseMatrix<TPixel>(colorMatrix.Columns, colorMatrix.Rows);
var result = new DenseMatrix<TPixel>(colorMatrix.Columns, colorMatrix.Rows);
Color.ToPixel(configuration, colorMatrix.Span, result.Span);
return result;
}
}
}
}

1
src/ImageSharp.Drawing/Processing/Extensions/DrawBezierExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;

1
src/ImageSharp.Drawing/Processing/Extensions/DrawLineExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;

1
src/ImageSharp.Drawing/Processing/Extensions/DrawPathCollectionExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing

1
src/ImageSharp.Drawing/Processing/Extensions/DrawPathExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Shapes;

1
src/ImageSharp.Drawing/Processing/Extensions/DrawPolygonExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;

1
src/ImageSharp.Drawing/Processing/Extensions/DrawRectangleExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;

1
src/ImageSharp.Drawing/Processing/Extensions/DrawTextExtensions.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Text;
using SixLabors.Primitives;

2
src/ImageSharp.Drawing/Processing/Extensions/FillPathBuilderExtensions.cs

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing

1
src/ImageSharp.Drawing/Processing/Extensions/FillPathCollectionExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing

1
src/ImageSharp.Drawing/Processing/Extensions/FillPolygonExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;

1
src/ImageSharp.Drawing/Processing/Extensions/FillRectangleExtensions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using SixLabors.Shapes;

2
src/ImageSharp.Drawing/Processing/IPen.cs

@ -3,8 +3,6 @@
using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>

1
src/ImageSharp.Drawing/Processing/ImageBrush.cs

@ -7,7 +7,6 @@ using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing

1
src/ImageSharp.Drawing/Processing/Pen.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{

2
src/ImageSharp.Drawing/Processing/Pens.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>

5
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -1,12 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing

3
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -1,10 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{

1
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs

@ -3,7 +3,6 @@
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{

6
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs

@ -94,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
}
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
for (float subPixel = y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
if (pointsFound == 0)
@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
QuickSort.Sort(buffer.Slice(0, pointsFound));
for (int point = 0; point < pointsFound; point += 2)
for (int point = 0; point < pointsFound && point < buffer.Length - 1; point += 2)
{
// points will be paired up
float scanStart = buffer[point] - minX;
@ -192,4 +192,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
return this.definition.Options.IsOpaqueColorWithoutBlending(solidBrush.Color);
}
}
}
}

27
src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs

@ -135,21 +135,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
private readonly PathBuilder builder;
private Point currentRenderPosition = default;
private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default;
private readonly int offset = 0;
private PointF currentPoint = default(PointF);
private Point currentRenderPosition;
private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams;
private readonly int offset;
private PointF currentPoint;
private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>
glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>();
private readonly bool renderOutline = false;
private readonly bool renderFill = false;
private bool rasterizationRequired = false;
private readonly bool renderOutline;
private readonly bool renderFill;
private bool rasterizationRequired;
public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill)
{
this.MemoryAllocator = memoryAllocator;
this.currentRenderPosition = default;
this.Pen = pen;
this.renderFill = renderFill;
this.renderOutline = pen != null;
@ -191,7 +192,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple;
subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple;
// we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate
// we have offset our rendering origin a little bit down to prevent edge cropping, move the draw origin up to compensate
this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset);
this.currentGlyphRenderParams = (parameters, subPixelOffset);
@ -205,7 +206,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
// we check to see if we have a render cache and if we do then we render else
this.builder.Clear();
// ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back
// ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offset it back
this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset));
this.rasterizationRequired = true;
@ -244,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
{
GlyphRenderData renderData = default;
// has the glyoh been rendedered already????
// has the glyph been rendered already?
if (this.rasterizationRequired)
{
IPath path = this.builder.Build();
@ -303,7 +304,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
float offset = 0.5f;
if (this.Options.Antialias)
{
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4)
{
@ -326,7 +327,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
bool scanlineDirty = false;
float yPlusOne = y + 1;
for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
for (float subPixel = y; subPixel < yPlusOne; subPixel += subpixelFraction)
{
var start = new PointF(path.Bounds.Left - 1, subPixel);
var end = new PointF(path.Bounds.Right + 1, subPixel);
@ -443,4 +444,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
}
}
}
}
}

40
src/ImageSharp/Advanced/AdvancedImageExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Advanced
/// <summary>
/// Returns a reference to the 0th element of the Pixel buffer,
/// allowing direct manipulation of pixel data through unsafe operations.
/// The pixel buffer is a contigous memory area containing Width*Height TPixel elements layed out in row-major order.
/// The pixel buffer is a contiguous memory area containing Width*Height TPixel elements laid out in row-major order.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
@ -153,42 +153,6 @@ namespace SixLabors.ImageSharp.Advanced
internal static MemoryAllocator GetMemoryAllocator(this IConfigurable source)
=> GetConfiguration(source).MemoryAllocator;
/// <summary>
/// Gets the span to the backing buffer.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <returns>The span returned from Pixel source</returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.PixelBuffer.GetSpan();
/// <summary>
/// Gets the span to the backing buffer at the given row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>
/// The span returned from Pixel source
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(IPixelSource<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> GetSpan(source.PixelBuffer, row);
/// <summary>
/// Gets the span to the backing buffer at the given row.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="source">The source.</param>
/// <param name="row">The row.</param>
/// <returns>
/// The span returned from Pixel source.
/// </returns>
private static Span<TPixel> GetSpan<TPixel>(Buffer2D<TPixel> source, int row)
where TPixel : struct, IPixel<TPixel>
=> source.GetSpan().Slice(row * source.Width, source.Width);
/// <summary>
/// Gets the configuration.
/// </summary>

108
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -1,12 +1,15 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Dithering;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Advanced
{
@ -15,8 +18,10 @@ namespace SixLabors.ImageSharp.Advanced
/// compiled on demand by a JIT compiler. This means there are a few limitations with respect to generics,
/// these are caused because not every possible generic instantiation can be determined up front at compile time.
/// The Aot Compiler is designed to overcome the limitations of this compiler.
/// None of the methods in this class should ever be called, the code only has to exist at compile-time to be picked up by the AoT compiler.
/// (Very similar to the LinkerIncludes.cs technique used in Xamarin.Android projects.)
/// </summary>
public static class AotCompilerTools
internal static class AotCompilerTools
{
static AotCompilerTools()
{
@ -25,21 +30,59 @@ namespace SixLabors.ImageSharp.Advanced
System.Runtime.CompilerServices.Unsafe.SizeOf<float>();
System.Runtime.CompilerServices.Unsafe.SizeOf<double>();
System.Runtime.CompilerServices.Unsafe.SizeOf<byte>();
System.Runtime.CompilerServices.Unsafe.SizeOf<int>();
System.Runtime.CompilerServices.Unsafe.SizeOf<Block8x8>();
System.Runtime.CompilerServices.Unsafe.SizeOf<Vector4>();
}
/// <summary>
/// This is the method that seeds the AoT compiler.
/// None of these seed methods needs to actually be called to seed the compiler.
/// The calls just need to be present when the code is compiled, and each implementation will be built.
/// </summary>
private static void SeedEverything()
{
Seed<Alpha8>();
Seed<Argb32>();
Seed<Bgr24>();
Seed<Bgr565>();
Seed<Bgra32>();
Seed<Bgra4444>();
Seed<Bgra5551>();
Seed<Byte4>();
Seed<Gray16>();
Seed<Gray8>();
Seed<HalfSingle>();
Seed<HalfVector2>();
Seed<HalfVector4>();
Seed<NormalizedByte2>();
Seed<NormalizedByte4>();
Seed<NormalizedShort2>();
Seed<NormalizedShort4>();
Seed<Rg32>();
Seed<Rgb24>();
Seed<Rgb48>();
Seed<Rgba1010102>();
Seed<Rgba32>();
Seed<Rgba64>();
Seed<RgbaVector>();
Seed<Short2>();
Seed<Short4>();
}
/// <summary>
/// Seeds the compiler using the given pixel format.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public static void Seed<TPixel>()
private static void Seed<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
// This is we actually call all the individual methods you need to seed.
AotCompileOctreeQuantizer<TPixel>();
AotCompileWuQuantizer<TPixel>();
AotCompileDithering<TPixel>();
AotCompilePixelOperations<TPixel>();
AotCompileResizeOperations<TPixel>();
System.Runtime.CompilerServices.Unsafe.SizeOf<TPixel>();
@ -51,39 +94,11 @@ namespace SixLabors.ImageSharp.Advanced
// TODO: Do the discovery work to figure out what works and what doesn't.
}
/// <summary>
/// Seeds the compiler using the given pixel formats.
/// </summary>
/// <typeparam name="TPixel">The first pixel format.</typeparam>
/// <typeparam name="TPixel2">The second pixel format.</typeparam>
public static void Seed<TPixel, TPixel2>()
where TPixel : struct, IPixel<TPixel>
where TPixel2 : struct, IPixel<TPixel2>
{
Seed<TPixel>();
Seed<TPixel2>();
}
/// <summary>
/// Seeds the compiler using the given pixel formats.
/// </summary>
/// <typeparam name="TPixel">The first pixel format.</typeparam>
/// <typeparam name="TPixel2">The second pixel format.</typeparam>
/// <typeparam name="TPixel3">The third pixel format.</typeparam>
public static void Seed<TPixel, TPixel2, TPixel3>()
where TPixel : struct, IPixel<TPixel>
where TPixel2 : struct, IPixel<TPixel2>
where TPixel3 : struct, IPixel<TPixel3>
{
Seed<TPixel, TPixel2>();
Seed<TPixel3>();
}
/// <summary>
/// This method doesn't actually do anything but serves an important purpose...
/// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an excepion:
/// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an exception:
/// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode."
/// The reason this happens is the SaveAsGif method makes haevy use of generics, which are too confusing for the AoT
/// The reason this happens is the SaveAsGif method makes heavy use of generics, which are too confusing for the AoT
/// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on
/// iOS so it bombs out.
/// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the
@ -118,7 +133,7 @@ namespace SixLabors.ImageSharp.Advanced
{
var test = new FloydSteinbergDiffuser();
TPixel pixel = default;
test.Dither<TPixel>(new ImageFrame<TPixel>(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
test.Dither(new ImageFrame<TPixel>(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
}
/// <summary>
@ -146,5 +161,28 @@ namespace SixLabors.ImageSharp.Advanced
{
}
}
/// <summary>
/// This method pre-seeds the PixelOperations engine for the AoT compiler on iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompilePixelOperations<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var pixelOp = new PixelOperations<TPixel>();
pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear);
}
/// <summary>
/// This method pre-seeds the ResizeProcessor for the AoT compiler on iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileResizeOperations<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var resizeProcessor = new ResizeProcessor(new ResizeOptions(), default);
var genericResizeProcessor = new ResizeProcessor<TPixel>((ResizeProcessor)resizeProcessor.CreatePixelSpecificProcessor<TPixel>());
genericResizeProcessor.AotCreateDestination(new Image<TPixel>(0, 0), default);
}
}
}
}

2
src/ImageSharp/Color/Color.cs

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

4
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLab ToCieLab(in CieLch color)
{
// Conversion (perserving white point)
// Conversion (preserving white point)
CieLab unadapted = CieLchToCieLabConverter.Convert(color);
return this.Adapt(unadapted);
@ -446,4 +446,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
}
}
}
}

4
src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs

@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
/// <returns>The <see cref="CieLab"/></returns>
public CieLuv ToCieLuv(in CieLchuv color)
{
// Conversion (perserving white point)
// Conversion (preserving white point)
CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color);
// Adaptation
@ -438,4 +438,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
}
}
}
}
}

14
src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs

@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
/// </summary>
/// <param name="r">The chomaticity coordinates of the red channel.</param>
/// <param name="g">The chomaticity coordinates of the green channel.</param>
/// <param name="b">The chomaticity coordinates of the blue channel.</param>
/// <param name="r">The chromaticity coordinates of the red channel.</param>
/// <param name="g">The chromaticity coordinates of the green channel.</param>
/// <param name="b">The chromaticity coordinates of the blue channel.</param>
public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b)
{
this.R = r;
@ -25,17 +25,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
}
/// <summary>
/// Gets the chomaticity coordinates of the red channel.
/// Gets the chromaticity coordinates of the red channel.
/// </summary>
public CieXyChromaticityCoordinates R { get; }
/// <summary>
/// Gets the chomaticity coordinates of the green channel.
/// Gets the chromaticity coordinates of the green channel.
/// </summary>
public CieXyChromaticityCoordinates G { get; }
/// <summary>
/// Gets the chomaticity coordinates of the blue channel.
/// Gets the chromaticity coordinates of the blue channel.
/// </summary>
public CieXyChromaticityCoordinates B { get; }
@ -88,4 +88,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <inheritdoc />
public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B);
}
}
}

2
src/ImageSharp/Common/Exceptions/UnknownImageFormatException.cs

@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp
{
/// <summary>
/// The exception that is thrown when the library tries to load
/// an image which has an unkown format.
/// an image which has an unknown format.
/// </summary>
public sealed class UnknownImageFormatException : ImageFormatException
{

6
src/ImageSharp/Common/Extensions/ConfigurationExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Threading.Tasks;
@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp
/// </summary>
public static ParallelOptions GetParallelOptions(this Configuration configuration)
{
return new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism };
return new ParallelOptions { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism };
}
}
}
}

4
src/ImageSharp/Common/Extensions/StreamExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp
}
else
{
byte[] foo = new byte[count];
var foo = new byte[count];
while (count > 0)
{
int bytesRead = stream.Read(foo, 0, count);

105
src/ImageSharp/Common/Helpers/Buffer2DUtils.cs

@ -0,0 +1,105 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for <see cref="Buffer2D{T}"/>.
/// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
/// </summary>
internal static class Buffer2DUtils
{
/// <summary>
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="kernel">The 1D convolution kernel.</param>
/// <param name="sourcePixels">The source frame.</param>
/// <param name="targetRow">The target row.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
/// <param name="minRow">The minimum working area row.</param>
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="minColumn">The minimum working area column.</param>
/// <param name="maxColumn">The maximum working area column.</param>
public static void Convolve4<TPixel>(
Span<Complex64> kernel,
Buffer2D<TPixel> sourcePixels,
Span<ComplexVector4> targetRow,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
int maxColumn)
where TPixel : struct, IPixel<TPixel>
{
ComplexVector4 vector = default;
int kernelLength = kernel.Length;
int radiusY = kernelLength >> 1;
int sourceOffsetColumnBase = column + minColumn;
ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel);
for (int i = 0; i < kernelLength; i++)
{
int offsetY = (row + i - radiusY).Clamp(minRow, maxRow);
int offsetX = sourceOffsetColumnBase.Clamp(minColumn, maxColumn);
Span<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
var currentColor = sourceRowSpan[offsetX].ToVector4();
vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor);
}
targetRow[column] = vector;
}
/// <summary>
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
/// </summary>
/// <param name="kernel">The 1D convolution kernel.</param>
/// <param name="sourceValues">The source frame.</param>
/// <param name="targetRow">The target row.</param>
/// <param name="row">The current row.</param>
/// <param name="column">The current column.</param>
/// <param name="minRow">The minimum working area row.</param>
/// <param name="maxRow">The maximum working area row.</param>
/// <param name="minColumn">The minimum working area column.</param>
/// <param name="maxColumn">The maximum working area column.</param>
public static void Convolve4(
Span<Complex64> kernel,
Buffer2D<ComplexVector4> sourceValues,
Span<ComplexVector4> targetRow,
int row,
int column,
int minRow,
int maxRow,
int minColumn,
int maxColumn)
{
ComplexVector4 vector = default;
int kernelLength = kernel.Length;
int radiusX = kernelLength >> 1;
int sourceOffsetColumnBase = column + minColumn;
int offsetY = row.Clamp(minRow, maxRow);
ref ComplexVector4 sourceRef = ref MemoryMarshal.GetReference(sourceValues.GetRowSpan(offsetY));
ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel);
for (int x = 0; x < kernelLength; x++)
{
int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn);
vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX));
}
targetRow[column] = vector;
}
}
}

14
src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -45,8 +45,6 @@ namespace SixLabors.ImageSharp
int maxColumn)
where TPixel : struct, IPixel<TPixel>
{
Vector4 vector = default;
Convolve2DImpl(
in matrixY,
in matrixX,
@ -57,7 +55,7 @@ namespace SixLabors.ImageSharp
maxRow,
minColumn,
maxColumn,
ref vector);
out Vector4 vector);
ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
vector.W = target.W;
@ -95,8 +93,6 @@ namespace SixLabors.ImageSharp
int maxColumn)
where TPixel : struct, IPixel<TPixel>
{
Vector4 vector = default;
Convolve2DImpl(
in matrixY,
in matrixX,
@ -107,7 +103,7 @@ namespace SixLabors.ImageSharp
maxRow,
minColumn,
maxColumn,
ref vector);
out Vector4 vector);
ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
Vector4Utils.UnPremultiply(ref vector);
@ -125,7 +121,7 @@ namespace SixLabors.ImageSharp
int maxRow,
int minColumn,
int maxColumn,
ref Vector4 vector)
out Vector4 vector)
where TPixel : struct, IPixel<TPixel>
{
Vector4 vectorY = default;
@ -281,4 +277,4 @@ namespace SixLabors.ImageSharp
}
}
}
}
}

28
src/ImageSharp/Common/Helpers/Guard.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -51,28 +51,6 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Ensures that the enumeration is not null or empty.
/// </summary>
/// <typeparam name="T">The type of objects in the <paramref name="value"/></typeparam>
/// <param name="value">The target enumeration, which should be checked against being null or empty.</param>
/// <param name="parameterName">Name of the parameter.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="value"/> is empty.</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void NotNullOrEmpty<T>(ICollection<T> value, string parameterName)
{
if (value is null)
{
ThrowArgumentNullException(parameterName);
}
if (value.Count == 0)
{
ThrowArgumentException("Must not be empty.", parameterName);
}
}
/// <summary>
/// Ensures that the specified value is less than a maximum value.
/// </summary>
@ -253,7 +231,7 @@ namespace SixLabors.ImageSharp
{
if (destination.Length < source.Length)
{
ThrowArgumentException($"Destination span is too short!", destinationParamName);
ThrowArgumentException("Destination span is too short!", destinationParamName);
}
}
@ -273,7 +251,7 @@ namespace SixLabors.ImageSharp
{
if (destination.Length < source.Length)
{
ThrowArgumentException($"Destination span is too short!", destinationParamName);
ThrowArgumentException("Destination span is too short!", destinationParamName);
}
}

13
src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs

@ -1,8 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -139,8 +138,8 @@ namespace SixLabors.ImageSharp
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
var magick = new Vector<float>(32768.0f);
var scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
@ -188,8 +187,8 @@ namespace SixLabors.ImageSharp
ref Octet.OfByte destBase = ref Unsafe.As<byte, Octet.OfByte>(ref MemoryMarshal.GetReference(dest));
int n = source.Length / 8;
Vector<float> magick = new Vector<float>(32768.0f);
Vector<float> scale = new Vector<float>(255f) / new Vector<float>(256f);
var magick = new Vector<float>(32768.0f);
var scale = new Vector<float>(255f) / new Vector<float>(256f);
// need to copy to a temporary struct, because
// SimdUtils.Octet.OfUInt32 temp = Unsafe.As<Vector<float>, SimdUtils.Octet.OfUInt32>(ref x)
@ -212,4 +211,4 @@ namespace SixLabors.ImageSharp
}
}
}
}
}

6
src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector<uint> ConvertToUInt32(Vector<float> vf)
{
Vector<float> maxBytes = new Vector<float>(255f);
var maxBytes = new Vector<float>(255f);
vf *= maxBytes;
vf += new Vector<float>(0.5f);
vf = Vector.Min(Vector.Max(vf, Vector<float>.Zero), maxBytes);
@ -190,4 +190,4 @@ namespace SixLabors.ImageSharp
}
}
}
}
}

9
src/ImageSharp/Common/Helpers/SimdUtils.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -7,9 +7,6 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tuples;
namespace SixLabors.ImageSharp
{
/// <summary>
@ -46,7 +43,7 @@ namespace SixLabors.ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector<float> FastRound(this Vector<float> v)
{
Vector<int> magic0 = new Vector<int>(int.MinValue); // 0x80000000
var magic0 = new Vector<int>(int.MinValue); // 0x80000000
Vector<float> sgn0 = Vector.AsVectorSingle(magic0);
Vector<float> and0 = Vector.BitwiseAnd(sgn0, v);
Vector<float> or0 = Vector.BitwiseOr(and0, new Vector<float>(8388608.0f));
@ -182,4 +179,4 @@ namespace SixLabors.ImageSharp
$"length should be divisible by {shouldBeDivisibleBy}!");
}
}
}
}

8
src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps };
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
Parallel.For(
0,
@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.ParallelUtils
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps };
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
Parallel.For(
0,
@ -158,4 +158,4 @@ namespace SixLabors.ImageSharp.ParallelUtils
$"{nameof(rectangle)}.{nameof(rectangle.Height)}");
}
}
}
}

2
src/ImageSharp/Formats/Bmp/BmpCompression.cs

@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// rather than four or eight bits in size.
///
/// Note: Because compression value of 4 is ambiguous for BI_RGB for windows and RLE24 for OS/2, the enum value is remapped
/// to a different value.
/// to a different value, to be clearly separate from valid windows values.
/// </summary>
RLE24 = 100,
}

20
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -446,7 +446,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#if NETCOREAPP2_1
Span<byte> cmd = stackalloc byte[2];
#else
byte[] cmd = new byte[2];
var cmd = new byte[2];
#endif
int count = 0;
@ -485,7 +485,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int max = cmd[1];
int bytesToRead = (max + 1) / 2;
byte[] run = new byte[bytesToRead];
var run = new byte[bytesToRead];
this.stream.Read(run, 0, run.Length);
@ -557,7 +557,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#if NETCOREAPP2_1
Span<byte> cmd = stackalloc byte[2];
#else
byte[] cmd = new byte[2];
var cmd = new byte[2];
#endif
int count = 0;
@ -595,7 +595,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// Take this number of bytes from the stream as uncompressed data.
int length = cmd[1];
byte[] run = new byte[length];
var run = new byte[length];
this.stream.Read(run, 0, run.Length);
@ -640,7 +640,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#if NETCOREAPP2_1
Span<byte> cmd = stackalloc byte[2];
#else
byte[] cmd = new byte[2];
var cmd = new byte[2];
#endif
int uncompressedPixels = 0;
@ -678,7 +678,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// Take this number of bytes from the stream as uncompressed data.
int length = cmd[1];
byte[] run = new byte[length * 3];
var run = new byte[length * 3];
this.stream.Read(run, 0, run.Length);
@ -1214,7 +1214,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
#else
byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize];
var buffer = new byte[BmpInfoHeader.MaxHeaderSize];
#endif
// Read the header size.
@ -1252,7 +1252,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
// color masks for each color channel follow the info header.
if (this.infoHeader.Compression == BmpCompression.BitFields)
{
byte[] bitfieldsBuffer = new byte[12];
var bitfieldsBuffer = new byte[12];
this.stream.Read(bitfieldsBuffer, 0, 12);
Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
@ -1261,7 +1261,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
{
byte[] bitfieldsBuffer = new byte[16];
var bitfieldsBuffer = new byte[16];
this.stream.Read(bitfieldsBuffer, 0, 16);
Span<byte> data = bitfieldsBuffer.AsSpan();
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4));
@ -1340,7 +1340,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpFileHeader.Size];
#else
byte[] buffer = new byte[BmpFileHeader.Size];
var buffer = new byte[BmpFileHeader.Size];
#endif
this.stream.Read(buffer, 0, BmpFileHeader.Size);

4
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[infoHeaderSize];
#else
byte[] buffer = new byte[infoHeaderSize];
var buffer = new byte[infoHeaderSize];
#endif
fileHeader.WriteTo(buffer);

2
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
return MemoryMarshal.Cast<byte, BmpFileHeader>(data)[0];
}
public unsafe void WriteTo(Span<byte> buffer)
public void WriteTo(Span<byte> buffer)
{
ref BmpFileHeader dest = ref Unsafe.As<byte, BmpFileHeader>(ref MemoryMarshal.GetReference(buffer));

2
src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs

@ -447,7 +447,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes a complete Bitmap V4 header to a buffer.
/// </summary>
/// <param name="buffer">The buffer to write to.</param>
public unsafe void WriteV4Header(Span<byte> buffer)
public void WriteV4Header(Span<byte> buffer)
{
ref BmpInfoHeader dest = ref Unsafe.As<byte, BmpInfoHeader>(ref MemoryMarshal.GetReference(buffer));

23
src/ImageSharp/Formats/Gif/GifConstants.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
@ -7,7 +7,7 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
/// Constants that define specific points within a gif.
/// Constants that define specific points within a Gif.
/// </summary>
internal static class GifConstants
{
@ -67,14 +67,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
public const byte CommentLabel = 0xFE;
/// <summary>
/// The name of the property inside the image properties for the comments.
/// The maximum length of a comment data sub-block is 255.
/// </summary>
public const string Comments = "Comments";
/// <summary>
/// The maximum comment length.
/// </summary>
public const int MaxCommentLength = 1024 * 8;
public const int MaxCommentSubBlockLength = 255;
/// <summary>
/// The image descriptor label <value>,</value>.
@ -102,18 +97,18 @@ namespace SixLabors.ImageSharp.Formats.Gif
public const byte EndIntroducer = 0x3B;
/// <summary>
/// Gets the default encoding to use when reading comments.
/// The character encoding to use when reading and writing comments - (ASCII 7bit).
/// </summary>
public static readonly Encoding DefaultEncoding = Encoding.ASCII;
public static readonly Encoding Encoding = Encoding.ASCII;
/// <summary>
/// The list of mimetypes that equate to a gif.
/// The collection of mimetypes that equate to a Gif.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/gif" };
/// <summary>
/// The list of file extensions that equate to a gif.
/// The collection of file extensions that equate to a Gif.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "gif" };
}
}
}

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

@ -1,8 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Text;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
@ -18,11 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public bool IgnoreMetadata { get; set; } = false;
/// <summary>
/// Gets or sets the encoding that should be used when reading comments.
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Gets or sets the decoding mode for multi-frame images
/// </summary>

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -77,7 +77,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="options">The decoder options.</param>
public GifDecoderCore(Configuration configuration, IGifDecoderOptions options)
{
this.TextEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.IgnoreMetadata = options.IgnoreMetadata;
this.DecodingMode = options.DecodingMode;
this.configuration = configuration ?? Configuration.Default;
@ -88,11 +87,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public bool IgnoreMetadata { get; internal set; }
/// <summary>
/// Gets the text encoding
/// </summary>
public Encoding TextEncoding { get; }
/// <summary>
/// Gets the decoding mode for multi-frame images
/// </summary>
@ -317,11 +311,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int length;
var stringBuilder = new StringBuilder();
while ((length = this.stream.ReadByte()) != 0)
{
if (length > GifConstants.MaxCommentLength)
if (length > GifConstants.MaxCommentSubBlockLength)
{
throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentLength}'");
throw new ImageFormatException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block");
}
if (this.IgnoreMetadata)
@ -333,10 +328,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
using (IManagedByteBuffer commentsBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(length))
{
this.stream.Read(commentsBuffer.Array, 0, length);
string comments = this.TextEncoding.GetString(commentsBuffer.Array, 0, length);
this.metadata.Properties.Add(new ImageProperty(GifConstants.Comments, comments));
string commentPart = GifConstants.Encoding.GetString(commentsBuffer.Array, 0, length);
stringBuilder.Append(commentPart);
}
}
if (stringBuilder.Length > 0)
{
this.gifMetadata.Comments.Add(stringBuilder.ToString());
}
}
/// <summary>
@ -632,4 +632,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
}
}
}
}

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

@ -1,8 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -14,11 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public sealed class GifEncoder : IImageEncoder, IGifEncoderOptions
{
/// <summary>
/// Gets or sets the encoding that should be used when writing comments.
/// </summary>
public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding;
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// Defaults to the <see cref="OctreeQuantizer"/>
@ -38,4 +32,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
encoder.Encode(image, stream);
}
}
}
}

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -37,11 +37,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private readonly byte[] buffer = new byte[20];
/// <summary>
/// The text encoding used to write comments.
/// </summary>
private readonly Encoding textEncoding;
/// <summary>
/// The quantizer used to generate the color palette.
/// </summary>
@ -57,11 +52,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private int bitDepth;
/// <summary>
/// Gif specific metadata.
/// </summary>
private GifMetadata gifMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary>
@ -70,7 +60,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
public GifEncoderCore(MemoryAllocator memoryAllocator, IGifEncoderOptions options)
{
this.memoryAllocator = memoryAllocator;
this.textEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding;
this.quantizer = options.Quantizer;
this.colorTableMode = options.ColorTableMode;
}
@ -90,12 +79,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
this.gifMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
this.colorTableMode = this.colorTableMode ?? this.gifMetadata.ColorTableMode;
GifMetadata gifMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
this.colorTableMode = this.colorTableMode ?? gifMetadata.ColorTableMode;
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
// Quantize the image returning a palette.
IQuantizedFrame<TPixel> quantized = null;
IQuantizedFrame<TPixel> quantized;
using (IFrameQuantizer<TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()))
{
quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
@ -117,12 +106,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
// Write the comments.
this.WriteComments(metadata, stream);
this.WriteComments(gifMetadata, stream);
// Write application extension to allow additional frames.
if (image.Frames.Count > 1)
{
this.WriteApplicationExtension(stream, this.gifMetadata.RepeatCount);
this.WriteApplicationExtension(stream, gifMetadata.RepeatCount);
}
if (useGlobalTable)
@ -158,10 +147,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
else
{
using (IFrameQuantizer<TPixel> palleteFrameQuantizer =
using (IFrameQuantizer<TPixel> paletteFrameQuantizer =
new PaletteFrameQuantizer<TPixel>(this.quantizer.Diffuser, quantized.Palette))
{
using (IQuantizedFrame<TPixel> paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame))
using (IQuantizedFrame<TPixel> paletteQuantized = paletteFrameQuantizer.QuantizeFrame(frame))
{
this.WriteImageData(paletteQuantized, stream);
}
@ -333,25 +322,51 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <param name="metadata">The metadata to be extract the comment data.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteComments(ImageMetadata metadata, Stream stream)
private void WriteComments(GifMetadata metadata, Stream stream)
{
if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property)
|| string.IsNullOrEmpty(property.Value))
if (metadata.Comments.Count == 0)
{
return;
}
byte[] comments = this.textEncoding.GetBytes(property.Value);
foreach (string comment in metadata.Comments)
{
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.CommentLabel;
stream.Write(this.buffer, 0, 2);
// Comment will be stored in chunks of 255 bytes, if it exceeds this size.
ReadOnlySpan<char> commentSpan = comment.AsSpan();
int idx = 0;
for (; idx <= comment.Length - GifConstants.MaxCommentSubBlockLength; idx += GifConstants.MaxCommentSubBlockLength)
{
WriteCommentSubBlock(stream, commentSpan, idx, GifConstants.MaxCommentSubBlockLength);
}
int count = Math.Min(comments.Length, 255);
// Write the length bytes, if any, to another sub block.
if (idx < comment.Length)
{
int remaining = comment.Length - idx;
WriteCommentSubBlock(stream, commentSpan, idx, remaining);
}
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.CommentLabel;
this.buffer[2] = (byte)count;
stream.WriteByte(GifConstants.Terminator);
}
}
stream.Write(this.buffer, 0, 3);
stream.Write(comments, 0, count);
stream.WriteByte(GifConstants.Terminator);
/// <summary>
/// Writes a comment sub-block to the stream.
/// </summary>
/// <param name="stream">The stream to write to.</param>
/// <param name="commentSpan">Comment as a Span.</param>
/// <param name="idx">Current start index.</param>
/// <param name="length">The length of the string to write. Should not exceed 255 bytes.</param>
private static void WriteCommentSubBlock(Stream stream, ReadOnlySpan<char> commentSpan, int idx, int length)
{
string subComment = commentSpan.Slice(idx, length).ToString();
byte[] subCommentBytes = GifConstants.Encoding.GetBytes(subComment);
stream.WriteByte((byte)length);
stream.Write(subCommentBytes, 0, length);
}
/// <summary>
@ -458,4 +473,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
}
}
}
}

4
src/ImageSharp/Formats/Gif/GifFrameMetaData.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Gif
@ -51,4 +51,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new GifFrameMetadata(this);
}
}
}

17
src/ImageSharp/Formats/Gif/GifMetaData.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
@ -24,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.RepeatCount = other.RepeatCount;
this.ColorTableMode = other.ColorTableMode;
this.GlobalColorTableLength = other.GlobalColorTableLength;
for (int i = 0; i < other.Comments.Count; i++)
{
this.Comments.Add(other.Comments[i]);
}
}
/// <summary>
@ -44,7 +51,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public int GlobalColorTableLength { get; set; }
/// <summary>
/// Gets or sets the the collection of comments about the graphics, credits, descriptions or any
/// other type of non-control and non-graphic data.
/// </summary>
public IList<string> Comments { get; set; } = new List<string>();
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new GifMetadata(this);
}
}
}

8
src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Text;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Gif
@ -16,11 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the text encoding that should be used when reading comments.
/// </summary>
Encoding TextEncoding { get; }
/// <summary>
/// Gets the decoding mode for multi-frame images.
/// </summary>

10
src/ImageSharp/Formats/Gif/IGifEncoderOptions.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Text;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Gif
@ -11,11 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
internal interface IGifEncoderOptions
{
/// <summary>
/// Gets the text encoding used to write comments.
/// </summary>
Encoding TextEncoding { get; }
/// <summary>
/// Gets the quantizer used to generate the color palette.
/// </summary>
@ -26,4 +20,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
GifColorTableMode? ColorTableMode { get; }
}
}
}

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
#if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[255];
#else
byte[] buffer = new byte[255];
var buffer = new byte[255];
#endif
while (xyz < length)
@ -253,4 +253,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.pixelStack.Dispose();
}
}
}
}

10
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -249,9 +249,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Compress the packets to the stream.
/// </summary>
/// <param name="indexedPixels">The span of indexed pixels.</param>
/// <param name="intialBits">The initial bits.</param>
/// <param name="initialBits">The initial bits.</param>
/// <param name="stream">The stream to write to.</param>
private void Compress(ReadOnlySpan<byte> indexedPixels, int intialBits, Stream stream)
private void Compress(ReadOnlySpan<byte> indexedPixels, int initialBits, Stream stream)
{
int fcode;
int c;
@ -260,14 +260,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
int hshift;
// Set up the globals: globalInitialBits - initial number of bits
this.globalInitialBits = intialBits;
this.globalInitialBits = initialBits;
// Set up the necessary values
this.clearFlag = false;
this.bitCount = this.globalInitialBits;
this.maxCode = GetMaxcode(this.bitCount);
this.clearCode = 1 << (intialBits - 1);
this.clearCode = 1 << (initialBits - 1);
this.eofCode = this.clearCode + 1;
this.freeEntry = this.clearCode + 2;
@ -450,4 +450,4 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.codeTable?.Dispose();
}
}
}
}

4
src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Application Data ----
buffer[12] = 3; // Application block length (always 3)
buffer[13] = 1; // Data sub-block indentity (always 1)
buffer[13] = 1; // Data sub-block identity (always 1)
// 0 means loop indefinitely. Count is set as play n + 1 times.
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14, 2), this.RepeatCount);

2
src/ImageSharp/Formats/IImageFormatDetector.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats
/// Detect mimetype
/// </summary>
/// <param name="header">The <see cref="T:byte[]"/> containing the file header.</param>
/// <returns>returns the mime type of detected othersie returns null</returns>
/// <returns>returns the mime type of detected otherwise returns null</returns>
IImageFormat DetectFormat(ReadOnlySpan<byte> header);
}
}

4
src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs

@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
public short[] ToArray()
{
short[] result = new short[Size];
var result = new short[Size];
this.CopyTo(result);
return result;
}
@ -297,4 +297,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return result;
}
}
}
}

12
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -95,9 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
public void NormalizeColorsInplace(float maximum)
{
Vector4 CMin4 = new Vector4(0F);
Vector4 CMax4 = new Vector4(maximum);
Vector4 COff4 = new Vector4(MathF.Ceiling(maximum / 2));
var CMin4 = new Vector4(0F);
var CMax4 = new Vector4(maximum);
var COff4 = new Vector4(MathF.Ceiling(maximum / 2));
this.V0L = Vector4.Clamp(this.V0L + COff4, CMin4, CMax4);
this.V0R = Vector4.Clamp(this.V0R + COff4, CMin4, CMax4);
@ -123,8 +123,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
Vector<float> off = new Vector<float>(MathF.Ceiling(maximum / 2));
Vector<float> max = new Vector<float>(maximum);
var off = new Vector<float>(MathF.Ceiling(maximum / 2));
var max = new Vector<float>(maximum);
ref Vector<float> row0 = ref Unsafe.As<Vector4, Vector<float>>(ref this.V0L);
row0 = NormalizeAndRound(row0, off, max);

20
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -1,4 +1,4 @@
<#
<#
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
#>
@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
char srcCoord = coordz[j % 4];
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n";
var expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n";
Write(expression);
}
}
@ -60,9 +60,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </summary>
public void NormalizeColorsInplace(float maximum)
{
Vector4 CMin4 = new Vector4(0F);
Vector4 CMax4 = new Vector4(maximum);
Vector4 COff4 = new Vector4(MathF.Ceiling(maximum / 2));
var CMin4 = new Vector4(0F);
var CMax4 = new Vector4(maximum);
var COff4 = new Vector4(MathF.Ceiling(maximum / 2));
<#
@ -86,8 +86,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
[MethodImpl(InliningOptions.ShortMethod)]
public void NormalizeColorsAndRoundInplaceAvx2(float maximum)
{
Vector<float> off = new Vector<float>(MathF.Ceiling(maximum / 2));
Vector<float> max = new Vector<float>(maximum);
var off = new Vector<float>(MathF.Ceiling(maximum / 2));
var max = new Vector<float>(maximum);
<#
for (int i = 0; i < 8; i++)
@ -117,7 +117,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
char destCoord = coordz[i % 4];
char destSide = (i / 4) % 2 == 0 ? 'L' : 'R';
if(j > 0 && i == 0){
WriteLine("");
}
@ -125,9 +125,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
char srcCoord = coordz[j % 4];
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
string expression = $"this.V{j}{destSide}.{destCoord} = Unsafe.Add(ref selfRef, {j*8+i});\r\n";
var expression = $"this.V{j}{destSide}.{destCoord} = Unsafe.Add(ref selfRef, {j*8+i});\r\n";
Write(expression);
}
}
PopIndent();

8
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
/// <summary>
/// Convert salars to byte-s and copy to dest,
/// Convert scalars to byte-s and copy to dest,
/// </summary>
/// <param name="blockPtr">Pointer to block</param>
/// <param name="dest">Destination</param>
@ -257,7 +257,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
fixed (Vector4* ptr = &this.V0L)
{
float* fp = (float*)ptr;
var fp = (float*)ptr;
for (int i = 0; i < Size; i++)
{
dest[i] = (int)fp[i];
@ -267,7 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public float[] ToArray()
{
float[] result = new float[Size];
var result = new float[Size];
this.CopyTo(result);
return result;
}

7
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs

@ -25,11 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
var maximum = 1 / this.MaximumValue;
var scale = new Vector4(maximum, maximum, maximum, 1F);
for (int i = 0; i < result.Length; i++)
{

7
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -19,11 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
var maximum = 1 / this.MaximumValue;
var scale = new Vector4(maximum, maximum, maximum, 1F);
ref float sBase = ref MemoryMarshal.GetReference(values.Component0);
ref Vector4 dBase = ref MemoryMarshal.GetReference(result);

7
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs

@ -24,11 +24,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
var maximum = 1 / this.MaximumValue;
var scale = new Vector4(maximum, maximum, maximum, 1F);
for (int i = 0; i < result.Length; i++)
{

7
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs

@ -25,11 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
var v = new Vector4(0, 0, 0, 1F);
var scale = new Vector4(
1 / this.MaximumValue,
1 / this.MaximumValue,
1 / this.MaximumValue,
1F);
var maximum = 1 / this.MaximumValue;
var scale = new Vector4(maximum, maximum, maximum, 1F);
for (int i = 0; i < result.Length; i++)
{

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
private unsafe void ParseBaselineDataInterleaved()
private void ParseBaselineDataInterleaved()
{
// Interleaved
int mcu = 0;
@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
private unsafe void ParseBaselineDataNonInterleaved()
private void ParseBaselineDataNonInterleaved()
{
JpegComponent component = this.components[this.frame.ComponentOrder[0]];
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
@ -209,7 +209,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
dcHuffmanTable.Configure();
acHuffmanTable.Configure();
int mcu = 0;
for (int j = 0; j < h; j++)
{
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(j);
@ -228,9 +227,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref dcHuffmanTable,
ref acHuffmanTable);
// Every data block is an MCU, so countdown the restart interval
mcu++;
this.HandleRestart();
}
}
@ -366,7 +362,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
private unsafe void ParseProgressiveDataNonInterleaved()
private void ParseProgressiveDataNonInterleaved()
{
JpegComponent component = this.components[this.frame.ComponentOrder[0]];
ref HuffmanScanBuffer buffer = ref this.scanBuffer;
@ -379,7 +375,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId];
dcHuffmanTable.Configure();
int mcu = 0;
for (int j = 0; j < h; j++)
{
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(j);
@ -397,8 +392,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref Unsafe.Add(ref blockRef, i),
ref dcHuffmanTable);
// Every data block is an MCU, so countdown the restart interval
mcu++;
this.HandleRestart();
}
}
@ -408,7 +401,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId];
acHuffmanTable.Configure();
int mcu = 0;
for (int j = 0; j < h; j++)
{
Span<Block8x8> blockSpan = component.SpectralBlocks.GetRowSpan(j);
@ -425,8 +417,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
ref Unsafe.Add(ref blockRef, i),
ref acHuffmanTable);
// Every data block is an MCU, so countdown the restart interval
mcu++;
this.HandleRestart();
}
}

1
src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using SixLabors.Primitives;

1
src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs

@ -4,7 +4,6 @@
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder

1
src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs

@ -7,7 +7,6 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components

6
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -538,7 +538,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return;
}
byte[] profile = new byte[remaining];
var profile = new byte[remaining];
this.InputStream.Read(profile, 0, remaining);
if (ProfileResolver.IsProfile(profile, ProfileResolver.ExifMarker))
@ -571,14 +571,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return;
}
byte[] identifier = new byte[Icclength];
var identifier = new byte[Icclength];
this.InputStream.Read(identifier, 0, Icclength);
remaining -= Icclength; // We have read it by this point
if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker))
{
this.isIcc = true;
byte[] profile = new byte[remaining];
var profile = new byte[remaining];
this.InputStream.Read(profile, 0, remaining);
if (this.iccData is null)

32
src/ImageSharp/Formats/Png/Adam7.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -31,6 +31,34 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public static readonly int[] RowIncrement = { 8, 8, 8, 4, 4, 2, 2 };
/// <summary>
/// Gets the width of the block.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="pass">The pass.</param>
/// <returns>
/// The <see cref="int" />
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ComputeBlockWidth(int width, int pass)
{
return (width + ColumnIncrement[pass] - 1 - FirstColumn[pass]) / ColumnIncrement[pass];
}
/// <summary>
/// Gets the height of the block.
/// </summary>
/// <param name="height">The height.</param>
/// <param name="pass">The pass.</param>
/// <returns>
/// The <see cref="int" />
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ComputeBlockHeight(int height, int pass)
{
return (height + RowIncrement[pass] - 1 - FirstRow[pass]) / RowIncrement[pass];
}
/// <summary>
/// Returns the correct number of columns for each interlaced pass.
/// </summary>
@ -53,4 +81,4 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
}
}
}

10
src/ImageSharp/Formats/Png/Filters/SubFilter.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -25,12 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters
ref byte scanBaseRef = ref MemoryMarshal.GetReference(scanline);
// Sub(x) + Raw(x-bpp)
int x = 1;
for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);
}
int x = bytesPerPixel + 1;
Unsafe.Add(ref scanBaseRef, x);
for (; x < scanline.Length; ++x)
{
ref byte scan = ref Unsafe.Add(ref scanBaseRef, x);

9
src/ImageSharp/Formats/Png/IPngDecoderOptions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Text;
@ -14,10 +14,5 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the encoding that should be used when reading text chunks.
/// </summary>
Encoding TextEncoding { get; }
}
}
}

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// The options available for manipulating the encoder pipeline
/// The options available for manipulating the encoder pipeline.
/// </summary>
internal interface IPngEncoderOptions
{
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Png
PngBitDepth? BitDepth { get; }
/// <summary>
/// Gets the color type
/// Gets the color type.
/// </summary>
PngColorType? ColorType { get; }
@ -33,7 +33,12 @@ namespace SixLabors.ImageSharp.Formats.Png
int CompressionLevel { get; }
/// <summary>
/// Gets the gamma value, that will be written the the image.
/// Gets the threshold of characters in text metadata, when compression should be used.
/// </summary>
int TextCompressionThreshold { get; }
/// <summary>
/// Gets the gamma value, that will be written the image.
/// </summary>
/// <value>The gamma value of the image.</value>
float? Gamma { get; }
@ -47,5 +52,10 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Gets the transparency threshold.
/// </summary>
byte Threshold { get; }
/// <summary>
/// Gets a value indicating whether this instance should write an Adam7 interlaced image.
/// </summary>
PngInterlaceMode? InterlaceMethod { get; }
}
}
}

17
src/ImageSharp/Formats/Png/PngChunkType.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Png
@ -55,6 +55,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
Text = 0x74455874U,
/// <summary>
/// Textual information that the encoder wishes to record with the image. The zTXt and tEXt chunks are semantically equivalent,
/// but the zTXt chunk is recommended for storing large blocks of text. Each zTXt chunk contains a (uncompressed) keyword and
/// a compressed text string.
/// </summary>
CompressedText = 0x7A545874U,
/// <summary>
/// The iTXt chunk contains International textual data. It contains a keyword, an optional language tag, an optional translated keyword
/// and the actual text string, which can be compressed or uncompressed.
/// </summary>
InternationalText = 0x69545874U,
/// <summary>
/// The tRNS chunk specifies that the image uses simple transparency:
/// either alpha values associated with palette entries (for indexed-color images)
@ -62,4 +75,4 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
Transparency = 0x74524E53U
}
}
}

41
src/ImageSharp/Formats/Png/PngConstants.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
@ -7,25 +7,38 @@ using System.Text;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Defines png constants defined in the specification.
/// Defines Png constants defined in the specification.
/// </summary>
internal static class PngConstants
{
/// <summary>
/// The default encoding for text metadata.
/// The character encoding to use when reading and writing textual data keywords and text - (Latin-1 ISO-8859-1).
/// </summary>
public static readonly Encoding DefaultEncoding = Encoding.ASCII;
public static readonly Encoding Encoding = Encoding.GetEncoding("ISO-8859-1");
/// <summary>
/// The list of mimetypes that equate to a png.
/// The character encoding to use when reading and writing language tags within iTXt chunks - (ASCII 7bit).
/// </summary>
public static readonly Encoding LanguageEncoding = Encoding.ASCII;
/// <summary>
/// The character encoding to use when reading and writing translated textual data keywords and text - (UTF8).
/// </summary>
public static readonly Encoding TranslatedEncoding = Encoding.UTF8;
/// <summary>
/// The list of mimetypes that equate to a Png.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/png" };
/// <summary>
/// The list of file extensions that equate to a png.
/// The list of file extensions that equate to a Png.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "png" };
/// <summary>
/// The header bytes identifying a Png.
/// </summary>
public static readonly byte[] HeaderBytes =
{
0x89, // Set the high bit.
@ -39,14 +52,14 @@ namespace SixLabors.ImageSharp.Formats.Png
};
/// <summary>
/// The header bytes as a big endian coded ulong.
/// The header bytes as a big-endian coded ulong.
/// </summary>
public const ulong HeaderValue = 0x89504E470D0A1A0AUL;
/// <summary>
/// The dictionary of available color types.
/// </summary>
public static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>()
public static readonly Dictionary<PngColorType, byte[]> ColorTypes = new Dictionary<PngColorType, byte[]>
{
[PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8, 16 },
[PngColorType.Rgb] = new byte[] { 8, 16 },
@ -54,5 +67,15 @@ namespace SixLabors.ImageSharp.Formats.Png
[PngColorType.GrayscaleWithAlpha] = new byte[] { 8, 16 },
[PngColorType.RgbWithAlpha] = new byte[] { 8, 16 }
};
/// <summary>
/// The maximum length of keyword in a text chunk is 79 bytes.
/// </summary>
public const int MaxTextKeywordLength = 79;
/// <summary>
/// The minimum length of a keyword in a text chunk is 1 byte.
/// </summary>
public const int MinTextKeywordLength = 1;
}
}
}

9
src/ImageSharp/Formats/Png/PngDecoder.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
@ -34,11 +34,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <summary>
/// Gets or sets the encoding that should be used when reading text chunks.
/// </summary>
public Encoding TextEncoding { get; set; } = PngConstants.DefaultEncoding;
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageFrame{TPixel}"/>.
/// </summary>
@ -63,4 +58,4 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}
}

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

@ -1,8 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.Png
private readonly byte[] buffer = new byte[4];
/// <summary>
/// Reusable crc for validating chunks.
/// Reusable CRC for validating chunks.
/// </summary>
private readonly Crc32 crc = new Crc32();
@ -39,11 +40,6 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Gets the encoding to use
/// </summary>
private readonly Encoding textEncoding;
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
@ -70,22 +66,22 @@ namespace SixLabors.ImageSharp.Formats.Png
private int bytesPerPixel;
/// <summary>
/// The number of bytes per sample
/// The number of bytes per sample.
/// </summary>
private int bytesPerSample;
/// <summary>
/// The number of bytes per scanline
/// The number of bytes per scanline.
/// </summary>
private int bytesPerScanline;
/// <summary>
/// The palette containing color information for indexed png's
/// The palette containing color information for indexed png's.
/// </summary>
private byte[] palette;
/// <summary>
/// The palette containing alpha channel color information for indexed png's
/// The palette containing alpha channel color information for indexed png's.
/// </summary>
private byte[] paletteAlpha;
@ -95,37 +91,32 @@ namespace SixLabors.ImageSharp.Formats.Png
private bool isEndChunkReached;
/// <summary>
/// Previous scanline processed
/// Previous scanline processed.
/// </summary>
private IManagedByteBuffer previousScanline;
/// <summary>
/// The current scanline that is being processed
/// The current scanline that is being processed.
/// </summary>
private IManagedByteBuffer scanline;
/// <summary>
/// The index of the current scanline being processed
/// The index of the current scanline being processed.
/// </summary>
private int currentRow = Adam7.FirstRow[0];
/// <summary>
/// The current pass for an interlaced PNG
/// </summary>
private int pass;
/// <summary>
/// The current number of bytes read in the current scanline
/// </summary>
private int currentRowBytesRead;
/// <summary>
/// Gets or sets the png color type
/// Gets or sets the png color type.
/// </summary>
private PngColorType pngColorType;
/// <summary>
/// The next chunk of data to return
/// The next chunk of data to return.
/// </summary>
private PngChunk? nextChunk;
@ -138,7 +129,6 @@ namespace SixLabors.ImageSharp.Formats.Png
{
this.configuration = configuration ?? Configuration.Default;
this.memoryAllocator = this.configuration.MemoryAllocator;
this.textEncoding = options.TextEncoding ?? PngConstants.DefaultEncoding;
this.ignoreMetadata = options.IgnoreMetadata;
}
@ -193,23 +183,29 @@ namespace SixLabors.ImageSharp.Formats.Png
break;
case PngChunkType.Palette:
byte[] pal = new byte[chunk.Length];
var pal = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal;
break;
case PngChunkType.Transparency:
byte[] alpha = new byte[chunk.Length];
var alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha, pngMetadata);
break;
case PngChunkType.Text:
this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length));
this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.CompressedText:
this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.InternationalText:
this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.Exif:
if (!this.ignoreMetadata)
{
byte[] exifData = new byte[chunk.Length];
var exifData = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
metadata.ExifProfile = new ExifProfile(exifData);
}
@ -271,7 +267,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.SkipChunkDataAndCrc(chunk);
break;
case PngChunkType.Text:
this.ReadTextChunk(metadata, chunk.Data.Array.AsSpan(0, chunk.Length));
this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.End:
this.isEndChunkReached = true;
@ -550,13 +546,15 @@ namespace SixLabors.ImageSharp.Formats.Png
private void DecodeInterlacedPixelData<TPixel>(Stream compressedStream, ImageFrame<TPixel> image, PngMetadata pngMetadata)
where TPixel : struct, IPixel<TPixel>
{
int pass = 0;
int width = this.header.Width;
while (true)
{
int numColumns = Adam7.ComputeColumns(this.header.Width, this.pass);
int numColumns = Adam7.ComputeColumns(width, pass);
if (numColumns == 0)
{
this.pass++;
pass++;
// This pass contains no data; skip to next pass
continue;
@ -604,23 +602,23 @@ namespace SixLabors.ImageSharp.Formats.Png
}
Span<TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]);
this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetadata, Adam7.FirstColumn[pass], Adam7.ColumnIncrement[pass]);
this.SwapBuffers();
this.currentRow += Adam7.RowIncrement[this.pass];
this.currentRow += Adam7.RowIncrement[pass];
}
this.pass++;
pass++;
this.previousScanline.Clear();
if (this.pass < 7)
if (pass < 7)
{
this.currentRow = Adam7.FirstRow[this.pass];
this.currentRow = Adam7.FirstRow[pass];
}
else
{
this.pass = 0;
pass = 0;
break;
}
}
@ -653,7 +651,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.header,
scanlineSpan,
rowSpan,
pngMetadata.HasTrans,
pngMetadata.HasTransparency,
pngMetadata.TransparentGray16.GetValueOrDefault(),
pngMetadata.TransparentGray8.GetValueOrDefault());
@ -687,7 +685,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan,
this.bytesPerPixel,
this.bytesPerSample,
pngMetadata.HasTrans,
pngMetadata.HasTransparency,
pngMetadata.TransparentRgb48.GetValueOrDefault(),
pngMetadata.TransparentRgb24.GetValueOrDefault());
@ -737,7 +735,7 @@ namespace SixLabors.ImageSharp.Formats.Png
rowSpan,
pixelOffset,
increment,
pngMetadata.HasTrans,
pngMetadata.HasTransparency,
pngMetadata.TransparentGray16.GetValueOrDefault(),
pngMetadata.TransparentGray8.GetValueOrDefault());
@ -776,7 +774,7 @@ namespace SixLabors.ImageSharp.Formats.Png
increment,
this.bytesPerPixel,
this.bytesPerSample,
pngMetadata.HasTrans,
pngMetadata.HasTransparency,
pngMetadata.TransparentRgb48.GetValueOrDefault(),
pngMetadata.TransparentRgb24.GetValueOrDefault());
@ -816,7 +814,7 @@ namespace SixLabors.ImageSharp.Formats.Png
ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2));
pngMetadata.TransparentRgb48 = new Rgb48(rc, gc, bc);
pngMetadata.HasTrans = true;
pngMetadata.HasTransparency = true;
return;
}
@ -824,7 +822,7 @@ namespace SixLabors.ImageSharp.Formats.Png
byte g = ReadByteLittleEndian(alpha, 2);
byte b = ReadByteLittleEndian(alpha, 4);
pngMetadata.TransparentRgb24 = new Rgb24(r, g, b);
pngMetadata.HasTrans = true;
pngMetadata.HasTransparency = true;
}
}
else if (this.pngColorType == PngColorType.Grayscale)
@ -840,7 +838,7 @@ namespace SixLabors.ImageSharp.Formats.Png
pngMetadata.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0));
}
pngMetadata.HasTrans = true;
pngMetadata.HasTransparency = true;
}
}
}
@ -858,6 +856,7 @@ namespace SixLabors.ImageSharp.Formats.Png
pngMetadata.BitDepth = (PngBitDepth)this.header.BitDepth;
pngMetadata.ColorType = this.header.ColorType;
pngMetadata.InterlaceMethod = this.header.InterlaceMethod;
this.pngColorType = this.header.ColorType;
}
@ -867,7 +866,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <param name="metadata">The metadata to decode to.</param>
/// <param name="data">The <see cref="T:Span"/> containing the data.</param>
private void ReadTextChunk(ImageMetadata metadata, ReadOnlySpan<byte> data)
private void ReadTextChunk(PngMetadata metadata, ReadOnlySpan<byte> data)
{
if (this.ignoreMetadata)
{
@ -876,10 +875,151 @@ namespace SixLabors.ImageSharp.Formats.Png
int zeroIndex = data.IndexOf((byte)0);
string name = this.textEncoding.GetString(data.Slice(0, zeroIndex));
string value = this.textEncoding.GetString(data.Slice(zeroIndex + 1));
// Keywords are restricted to 1 to 79 bytes in length.
if (zeroIndex < PngConstants.MinTextKeywordLength || zeroIndex > PngConstants.MaxTextKeywordLength)
{
return;
}
ReadOnlySpan<byte> keywordBytes = data.Slice(0, zeroIndex);
if (!this.TryReadTextKeyword(keywordBytes, out string name))
{
return;
}
string value = PngConstants.Encoding.GetString(data.Slice(zeroIndex + 1));
metadata.Properties.Add(new ImageProperty(name, value));
metadata.TextData.Add(new PngTextData(name, value, string.Empty, string.Empty));
}
/// <summary>
/// Reads the compressed text chunk. Contains a uncompressed keyword and a compressed text string.
/// </summary>
/// <param name="metadata">The metadata to decode to.</param>
/// <param name="data">The <see cref="T:Span"/> containing the data.</param>
private void ReadCompressedTextChunk(PngMetadata metadata, ReadOnlySpan<byte> data)
{
if (this.ignoreMetadata)
{
return;
}
int zeroIndex = data.IndexOf((byte)0);
if (zeroIndex < PngConstants.MinTextKeywordLength || zeroIndex > PngConstants.MaxTextKeywordLength)
{
return;
}
byte compressionMethod = data[zeroIndex + 1];
if (compressionMethod != 0)
{
// Only compression method 0 is supported (zlib datastream with deflate compression).
return;
}
ReadOnlySpan<byte> keywordBytes = data.Slice(0, zeroIndex);
if (!this.TryReadTextKeyword(keywordBytes, out string name))
{
return;
}
ReadOnlySpan<byte> compressedData = data.Slice(zeroIndex + 2);
metadata.TextData.Add(new PngTextData(name, this.UncompressTextData(compressedData, PngConstants.Encoding), string.Empty, string.Empty));
}
/// <summary>
/// Reads a iTXt chunk, which contains international text data. It contains:
/// - A uncompressed keyword.
/// - Compression flag, indicating if a compression is used.
/// - Compression method.
/// - Language tag (optional).
/// - A translated keyword (optional).
/// - Text data, which is either compressed or uncompressed.
/// </summary>
/// <param name="metadata">The metadata to decode to.</param>
/// <param name="data">The <see cref="T:Span"/> containing the data.</param>
private void ReadInternationalTextChunk(PngMetadata metadata, ReadOnlySpan<byte> data)
{
if (this.ignoreMetadata)
{
return;
}
int zeroIndexKeyword = data.IndexOf((byte)0);
if (zeroIndexKeyword < PngConstants.MinTextKeywordLength || zeroIndexKeyword > PngConstants.MaxTextKeywordLength)
{
return;
}
byte compressionFlag = data[zeroIndexKeyword + 1];
if (!(compressionFlag == 0 || compressionFlag == 1))
{
return;
}
byte compressionMethod = data[zeroIndexKeyword + 2];
if (compressionMethod != 0)
{
// Only compression method 0 is supported (zlib datastream with deflate compression).
return;
}
int langStartIdx = zeroIndexKeyword + 3;
int languageLength = data.Slice(langStartIdx).IndexOf((byte)0);
if (languageLength < 0)
{
return;
}
string language = PngConstants.LanguageEncoding.GetString(data.Slice(langStartIdx, languageLength));
int translatedKeywordStartIdx = langStartIdx + languageLength + 1;
int translatedKeywordLength = data.Slice(translatedKeywordStartIdx).IndexOf((byte)0);
string translatedKeyword = PngConstants.TranslatedEncoding.GetString(data.Slice(translatedKeywordStartIdx, translatedKeywordLength));
ReadOnlySpan<byte> keywordBytes = data.Slice(0, zeroIndexKeyword);
if (!this.TryReadTextKeyword(keywordBytes, out string keyword))
{
return;
}
int dataStartIdx = translatedKeywordStartIdx + translatedKeywordLength + 1;
if (compressionFlag == 1)
{
ReadOnlySpan<byte> compressedData = data.Slice(dataStartIdx);
metadata.TextData.Add(new PngTextData(keyword, this.UncompressTextData(compressedData, PngConstants.TranslatedEncoding), language, translatedKeyword));
}
else
{
string value = PngConstants.TranslatedEncoding.GetString(data.Slice(dataStartIdx));
metadata.TextData.Add(new PngTextData(keyword, value, language, translatedKeyword));
}
}
/// <summary>
/// Decompresses a byte array with zlib compressed text data.
/// </summary>
/// <param name="compressedData">Compressed text data bytes.</param>
/// <param name="encoding">The string encoding to use.</param>
/// <returns>A string.</returns>
private string UncompressTextData(ReadOnlySpan<byte> compressedData, Encoding encoding)
{
using (var memoryStream = new MemoryStream(compressedData.ToArray()))
using (var inflateStream = new ZlibInflateStream(memoryStream, () => 0))
{
inflateStream.AllocateNewBytes(compressedData.Length);
var uncompressedBytes = new List<byte>();
// Note: this uses the a buffer which is only 4 bytes long to read the stream, maybe allocating a larger buffer makes sense here.
int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
while (bytesRead != 0)
{
uncompressedBytes.AddRange(this.buffer.AsSpan().Slice(0, bytesRead).ToArray());
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
}
return encoding.GetString(uncompressedBytes.ToArray());
}
}
/// <summary>
@ -1048,7 +1188,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Attempts to read the length of the next chunk.
/// </summary>
/// <returns>
/// Whether the the length was read.
/// Whether the length was read.
/// </returns>
private bool TryReadChunkLength(out int result)
{
@ -1060,10 +1200,40 @@ namespace SixLabors.ImageSharp.Formats.Png
}
result = default;
return false;
}
/// <summary>
/// Tries to reads a text chunk keyword, which have some restrictions to be valid:
/// Keywords shall contain only printable Latin-1 characters and should not have leading or trailing whitespace.
/// See: https://www.w3.org/TR/PNG/#11zTXt
/// </summary>
/// <param name="keywordBytes">The keyword bytes.</param>
/// <param name="name">The name.</param>
/// <returns>True, if the keyword could be read and is valid.</returns>
private bool TryReadTextKeyword(ReadOnlySpan<byte> keywordBytes, out string name)
{
name = string.Empty;
// Keywords shall contain only printable Latin-1.
foreach (byte c in keywordBytes)
{
if (!((c >= 32 && c <= 126) || (c >= 161 && c <= 255)))
{
return false;
}
}
// Keywords should not be empty or have leading or trailing whitespace.
name = PngConstants.Encoding.GetString(keywordBytes);
if (string.IsNullOrWhiteSpace(name) || name.StartsWith(" ") || name.EndsWith(" "))
{
return false;
}
return true;
}
private void SwapBuffers()
{
IManagedByteBuffer temp = this.previousScanline;
@ -1071,4 +1241,4 @@ namespace SixLabors.ImageSharp.Formats.Png
this.scanline = temp;
}
}
}
}

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
@ -36,20 +36,31 @@ namespace SixLabors.ImageSharp.Formats.Png
public int CompressionLevel { get; set; } = 6;
/// <summary>
/// Gets or sets the gamma value, that will be written the the image.
/// Gets or sets the threshold of characters in text metadata, when compression should be used.
/// Defaults to 1024.
/// </summary>
public int TextCompressionThreshold { get; set; } = 1024;
/// <summary>
/// Gets or sets the gamma value, that will be written the image.
/// </summary>
public float? Gamma { get; set; }
/// <summary>
/// Gets or sets quantizer for reducing the color count.
/// Defaults to the <see cref="WuQuantizer"/>
/// Defaults to the <see cref="WuQuantizer"/>.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Gets or sets the transparency threshold.
/// </summary>
public byte Threshold { get; set; } = 255;
public byte Threshold { get; set; } = byte.MaxValue;
/// <summary>
/// Gets or sets a value indicating whether this instance should write an Adam7 interlaced image.
/// </summary>
public PngInterlaceMode? InterlaceMethod { get; set; }
/// <summary>
/// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>.
@ -60,10 +71,10 @@ namespace SixLabors.ImageSharp.Formats.Png
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new PngEncoderCore(image.GetMemoryAllocator(), this))
using (var encoder = new PngEncoderCore(image.GetMemoryAllocator(), image.GetConfiguration(), new PngEncoderOptions(this)))
{
encoder.Encode(image, stream);
}
}
}
}
}

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

File diff suppressed because it is too large

57
src/ImageSharp/Formats/Png/PngEncoderHelpers.cs

@ -0,0 +1,57 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// The helper methods for <see cref="PngEncoderCore"/> class.
/// </summary>
internal static class PngEncoderHelpers
{
/// <summary>
/// Packs the given 8 bit array into and array of <paramref name="bits"/> depths.
/// </summary>
/// <param name="source">The source span in 8 bits.</param>
/// <param name="result">The resultant span in <paramref name="bits"/>.</param>
/// <param name="bits">The bit depth.</param>
/// <param name="scale">The scaling factor.</param>
public static void ScaleDownFrom8BitArray(ReadOnlySpan<byte> source, Span<byte> result, int bits, float scale = 1)
{
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
ref byte resultRef = ref MemoryMarshal.GetReference(result);
int shift = 8 - bits;
byte mask = (byte)(0xFF >> shift);
byte shift0 = (byte)shift;
int v = 0;
int resultOffset = 0;
for (int i = 0; i < source.Length; i++)
{
int value = ((int)MathF.Round(Unsafe.Add(ref sourceRef, i) / scale)) & mask;
v |= value << shift;
if (shift == 0)
{
shift = shift0;
Unsafe.Add(ref resultRef, resultOffset) = (byte)v;
resultOffset++;
v = 0;
}
else
{
shift -= bits;
}
}
if (shift != shift0)
{
Unsafe.Add(ref resultRef, resultOffset) = (byte)v;
}
}
}
}

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

@ -0,0 +1,82 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// The options structure for the <see cref="PngEncoderCore"/>.
/// </summary>
internal class PngEncoderOptions : IPngEncoderOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="PngEncoderOptions"/> class.
/// </summary>
/// <param name="source">The source.</param>
public PngEncoderOptions(IPngEncoderOptions source)
{
this.BitDepth = source.BitDepth;
this.ColorType = source.ColorType;
// Specification recommends default filter method None for paletted images and Paeth for others.
this.FilterMethod = source.FilterMethod ?? (source.ColorType == PngColorType.Palette
? PngFilterMethod.None
: PngFilterMethod.Paeth);
this.CompressionLevel = source.CompressionLevel;
this.TextCompressionThreshold = source.TextCompressionThreshold;
this.Gamma = source.Gamma;
this.Quantizer = source.Quantizer;
this.Threshold = source.Threshold;
this.InterlaceMethod = source.InterlaceMethod;
}
/// <summary>
/// Gets or sets the number of bits per sample or per palette index (not per pixel).
/// Not all values are allowed for all <see cref="P:SixLabors.ImageSharp.Formats.Png.IPngEncoderOptions.ColorType" /> values.
/// </summary>
public PngBitDepth? BitDepth { get; set; }
/// <summary>
/// Gets or sets the color type.
/// </summary>
public PngColorType? ColorType { get; set; }
/// <summary>
/// Gets the filter method.
/// </summary>
public PngFilterMethod? FilterMethod { get; }
/// <summary>
/// Gets the compression level 1-9.
/// <remarks>Defaults to 6.</remarks>
/// </summary>
public int CompressionLevel { get; }
/// <inheritdoc/>
public int TextCompressionThreshold { get; }
/// <summary>
/// Gets or sets the gamma value, that will be written the image.
/// </summary>
/// <value>
/// The gamma value of the image.
/// </value>
public float? Gamma { get; set; }
/// <summary>
/// Gets or sets the quantizer for reducing the color count.
/// </summary>
public IQuantizer Quantizer { get; set; }
/// <summary>
/// Gets the transparency threshold.
/// </summary>
public byte Threshold { get; }
/// <summary>
/// Gets or sets a value indicating whether this instance should write an Adam7 interlaced image.
/// </summary>
public PngInterlaceMode? InterlaceMethod { get; set; }
}
}

152
src/ImageSharp/Formats/Png/PngEncoderOptionsHelpers.cs

@ -0,0 +1,152 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// The helper methods for the PNG encoder options.
/// </summary>
internal static class PngEncoderOptionsHelpers
{
/// <summary>
/// Adjusts the options.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="pngMetadata">The PNG metadata.</param>
/// <param name="use16Bit">if set to <c>true</c> [use16 bit].</param>
/// <param name="bytesPerPixel">The bytes per pixel.</param>
public static void AdjustOptions(
PngEncoderOptions options,
PngMetadata pngMetadata,
out bool use16Bit,
out int bytesPerPixel)
{
// Always take the encoder options over the metadata values.
options.Gamma = options.Gamma ?? pngMetadata.Gamma;
options.ColorType = options.ColorType ?? pngMetadata.ColorType;
options.BitDepth = options.BitDepth ?? pngMetadata.BitDepth;
options.InterlaceMethod = options.InterlaceMethod ?? pngMetadata.InterlaceMethod;
use16Bit = options.BitDepth == PngBitDepth.Bit16;
bytesPerPixel = CalculateBytesPerPixel(options.ColorType, use16Bit);
// Ensure we are not allowing impossible combinations.
if (!PngConstants.ColorTypes.ContainsKey(options.ColorType.Value))
{
throw new NotSupportedException("Color type is not supported or not valid.");
}
}
/// <summary>
/// Creates the quantized frame.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="options">The options.</param>
/// <param name="image">The image.</param>
public static IQuantizedFrame<TPixel> CreateQuantizedFrame<TPixel>(
PngEncoderOptions options,
Image<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
if (options.ColorType != PngColorType.Palette)
{
return null;
}
byte bits = (byte)options.BitDepth;
if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bits) == -1)
{
throw new NotSupportedException("Bit depth is not supported or not valid.");
}
// Use the metadata to determine what quantization depth to use if no quantizer has been set.
if (options.Quantizer is null)
{
options.Quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits));
}
// Create quantized frame returning the palette and set the bit depth.
using (IFrameQuantizer<TPixel> frameQuantizer = options.Quantizer.CreateFrameQuantizer<TPixel>(image.GetConfiguration()))
{
return frameQuantizer.QuantizeFrame(image.Frames.RootFrame);
}
}
/// <summary>
/// Calculates the bit depth value.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="options">The options.</param>
/// <param name="image">The image.</param>
/// <param name="quantizedFrame">The quantized frame.</param>
public static byte CalculateBitDepth<TPixel>(
PngEncoderOptions options,
Image<TPixel> image,
IQuantizedFrame<TPixel> quantizedFrame)
where TPixel : struct, IPixel<TPixel>
{
byte bitDepth;
if (options.ColorType == PngColorType.Palette)
{
byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantizedFrame.Palette.Length).Clamp(1, 8);
byte bits = Math.Max((byte)options.BitDepth, quantizedBits);
// Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk
// We check again for the bit depth as the bit depth of the color palette from a given quantizer might not
// be within the acceptable range.
if (bits == 3)
{
bits = 4;
}
else if (bits >= 5 && bits <= 7)
{
bits = 8;
}
bitDepth = bits;
}
else
{
bitDepth = (byte)options.BitDepth;
}
if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bitDepth) == -1)
{
throw new NotSupportedException("Bit depth is not supported or not valid.");
}
return bitDepth;
}
/// <summary>
/// Calculates the correct number of bytes per pixel for the given color type.
/// </summary>
/// <returns>Bytes per pixel.</returns>
private static int CalculateBytesPerPixel(PngColorType? pngColorType, bool use16Bit)
{
switch (pngColorType)
{
case PngColorType.Grayscale:
return use16Bit ? 2 : 1;
case PngColorType.GrayscaleWithAlpha:
return use16Bit ? 4 : 2;
case PngColorType.Palette:
return 1;
case PngColorType.Rgb:
return use16Bit ? 6 : 3;
// PngColorType.RgbWithAlpha
default:
return use16Bit ? 8 : 4;
}
}
}
}

2
src/ImageSharp/Formats/Png/PngInterlaceMode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Provides enumeration of available PNG interlace modes.
/// </summary>
internal enum PngInterlaceMode : byte
public enum PngInterlaceMode : byte
{
/// <summary>
/// Non interlaced

62
src/ImageSharp/Formats/Png/PngMetaData.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Png
@ -26,11 +27,17 @@ namespace SixLabors.ImageSharp.Formats.Png
this.BitDepth = other.BitDepth;
this.ColorType = other.ColorType;
this.Gamma = other.Gamma;
this.HasTrans = other.HasTrans;
this.InterlaceMethod = other.InterlaceMethod;
this.HasTransparency = other.HasTransparency;
this.TransparentGray8 = other.TransparentGray8;
this.TransparentGray16 = other.TransparentGray16;
this.TransparentRgb24 = other.TransparentRgb24;
this.TransparentRgb48 = other.TransparentRgb48;
for (int i = 0; i < other.TextData.Count; i++)
{
this.TextData.Add(other.TextData[i]);
}
}
/// <summary>
@ -44,37 +51,74 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
public PngColorType ColorType { get; set; } = PngColorType.RgbWithAlpha;
/// <summary>
/// Gets or sets a value indicating whether this instance should write an Adam7 interlaced image.
/// </summary>
public PngInterlaceMode? InterlaceMethod { get; set; } = PngInterlaceMode.None;
/// <summary>
/// Gets or sets the gamma value for the image.
/// </summary>
public float Gamma { get; set; }
/// <summary>
/// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent
/// Gets or sets the Rgb24 transparent color.
/// This represents any color in an 8 bit Rgb24 encoded png that should be transparent.
/// </summary>
public Rgb24? TransparentRgb24 { get; set; }
/// <summary>
/// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent
/// Gets or sets the Rgb48 transparent color.
/// This represents any color in a 16 bit Rgb24 encoded png that should be transparent.
/// </summary>
public Rgb48? TransparentRgb48 { get; set; }
/// <summary>
/// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent
/// Gets or sets the 8 bit grayscale transparent color.
/// This represents any color in an 8 bit grayscale encoded png that should be transparent.
/// </summary>
public Gray8? TransparentGray8 { get; set; }
/// <summary>
/// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent
/// Gets or sets the 16 bit grayscale transparent color.
/// This represents any color in a 16 bit grayscale encoded png that should be transparent.
/// </summary>
public Gray16? TransparentGray16 { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded
/// Gets or sets a value indicating whether the image contains a transparency chunk and markers were decoded.
/// </summary>
public bool HasTransparency { get; set; }
/// <summary>
/// Gets or sets the collection of text data stored within the iTXt, tEXt, and zTXt chunks.
/// Used for conveying textual information associated with the image.
/// </summary>
public IList<PngTextData> TextData { get; set; } = new List<PngTextData>();
/// <summary>
/// Gets the list of png text properties for storing meta information about this image.
/// </summary>
public bool HasTrans { get; set; }
public IList<PngTextData> PngTextProperties { get; } = new List<PngTextData>();
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new PngMetadata(this);
internal bool TryGetPngTextProperty(string keyword, out PngTextData result)
{
for (int i = 0; i < this.TextData.Count; i++)
{
if (this.TextData[i].Keyword == keyword)
{
result = this.TextData[i];
return true;
}
}
result = default;
return false;
}
}
}
}

143
src/ImageSharp/Formats/Png/PngTextData.cs

@ -0,0 +1,143 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.Png
{
/// <summary>
/// Stores text data contained in the iTXt, tEXt, and zTXt chunks.
/// Used for conveying textual information associated with the image, like the name of the author,
/// the copyright information, the date, where the image was created, or some other information.
/// </summary>
public readonly struct PngTextData : IEquatable<PngTextData>
{
/// <summary>
/// Initializes a new instance of the <see cref="PngTextData"/> struct.
/// </summary>
/// <param name="keyword">The keyword of the property.</param>
/// <param name="value">The value of the property.</param>
/// <param name="languageTag">An optional language tag.</param>
/// <param name="translatedKeyword">A optional translated keyword.</param>
public PngTextData(string keyword, string value, string languageTag, string translatedKeyword)
{
Guard.NotNullOrWhiteSpace(keyword, nameof(keyword));
// No leading or trailing whitespace is allowed in keywords.
this.Keyword = keyword.Trim();
this.Value = value;
this.LanguageTag = languageTag;
this.TranslatedKeyword = translatedKeyword;
}
/// <summary>
/// Gets the keyword of this <see cref="PngTextData"/> which indicates
/// the type of information represented by the text string as described in https://www.w3.org/TR/PNG/#11keywords.
/// </summary>
/// <example>
/// Typical properties are the author, copyright information or other meta information.
/// </example>
public string Keyword { get; }
/// <summary>
/// Gets the value of this <see cref="PngTextData"/>.
/// </summary>
public string Value { get; }
/// <summary>
/// Gets an optional language tag defined in https://www.w3.org/TR/PNG/#2-RFC-3066 indicates the human language used by the translated keyword and the text.
/// If the first word is two or three letters long, it is an ISO language code https://www.w3.org/TR/PNG/#2-ISO-639.
/// </summary>
/// <example>
/// Examples: cn, en-uk, no-bok, x-klingon, x-KlInGoN.
/// </example>
public string LanguageTag { get; }
/// <summary>
/// Gets an optional translated keyword, should contain a translation of the keyword into the language indicated by the language tag.
/// </summary>
public string TranslatedKeyword { get; }
/// <summary>
/// Compares two <see cref="PngTextData"/> objects. The result specifies whether the values
/// of the properties of the two <see cref="PngTextData"/> objects are equal.
/// </summary>
/// <param name="left">
/// The <see cref="PngTextData"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="PngTextData"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator ==(PngTextData left, PngTextData right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="PngTextData"/> objects. The result specifies whether the values
/// of the properties of the two <see cref="PngTextData"/> objects are unequal.
/// </summary>
/// <param name="left">
/// The <see cref="PngTextData"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="PngTextData"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
public static bool operator !=(PngTextData left, PngTextData right)
{
return !(left == right);
}
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
/// <param name="obj">
/// The object to compare with the current instance.
/// </param>
/// <returns>
/// true if <paramref name="obj"/> and this instance are the same type and represent the
/// same value; otherwise, false.
/// </returns>
public override bool Equals(object obj)
{
return obj is PngTextData other && this.Equals(other);
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>
/// A 32-bit signed integer that is the hash code for this instance.
/// </returns>
public override int GetHashCode() => HashCode.Combine(this.Keyword, this.Value, this.LanguageTag, this.TranslatedKeyword);
/// <summary>
/// Returns the fully qualified type name of this instance.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> containing a fully qualified type name.
/// </returns>
public override string ToString() => $"PngTextData [ Name={this.Keyword}, Value={this.Value} ]";
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// True if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(PngTextData other)
{
return this.Keyword.Equals(other.Keyword)
&& this.Value.Equals(other.Value)
&& this.LanguageTag.Equals(other.LanguageTag)
&& this.TranslatedKeyword.Equals(other.TranslatedKeyword);
}
}
}

2
src/ImageSharp/IO/IFileSystem.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.IO
Stream OpenRead(string path);
/// <summary>
/// Creates or opens a file and returns it as a writeable stream as defined by the path.
/// Creates or opens a file and returns it as a writable stream as defined by the path.
/// </summary>
/// <param name="path">Path to the file to open.</param>
/// <returns>A stream representing the file to open.</returns>

8
src/ImageSharp/Image.FromBytes.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp
/// <param name="config">The configuration.</param>
/// <param name="data">The byte array containing encoded image data to read the header from.</param>
/// <returns>The mime type or null if none found.</returns>
public static unsafe IImageFormat DetectFormat(Configuration config, ReadOnlySpan<byte> data)
public static IImageFormat DetectFormat(Configuration config, ReadOnlySpan<byte> data)
{
int maxHeaderSize = config.MaxHeaderSize;
if (maxHeaderSize <= 0)
@ -351,7 +351,7 @@ namespace SixLabors.ImageSharp
/// <param name="config">The configuration options.</param>
/// <param name="data">The byte span containing image data.</param>
/// <returns>A new <see cref="Image"/>.</returns>
public static unsafe Image Load(Configuration config, ReadOnlySpan<byte> data) => Load(config, data, out _);
public static Image Load(Configuration config, ReadOnlySpan<byte> data) => Load(config, data, out _);
/// <summary>
/// Load a new instance of <see cref="Image"/> from the given encoded byte span.
@ -395,4 +395,4 @@ namespace SixLabors.ImageSharp
}
}
}
}
}

22
src/ImageSharp/Image.cs

@ -80,8 +80,22 @@ namespace SixLabors.ImageSharp
/// </summary>
Configuration IConfigurable.Configuration => this.Configuration;
/// <summary>
/// Gets a value indicating whether the image instance is disposed.
/// </summary>
public bool IsDisposed { get; private set; }
/// <inheritdoc />
public abstract void Dispose();
public void Dispose()
{
if (this.IsDisposed)
{
return;
}
this.IsDisposed = true;
this.DisposeImpl();
}
/// <summary>
/// Saves the image to the given stream using the given image encoder.
@ -93,6 +107,7 @@ namespace SixLabors.ImageSharp
{
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(encoder, nameof(encoder));
this.EnsureNotDisposed();
EncodeVisitor visitor = new EncodeVisitor(encoder, stream);
this.AcceptVisitor(visitor);
@ -128,6 +143,11 @@ namespace SixLabors.ImageSharp
/// <param name="size">The <see cref="Size"/>.</param>
protected void UpdateSize(Size size) => this.size = size;
/// <summary>
/// Implements the Dispose logic.
/// </summary>
protected abstract void DisposeImpl();
private class EncodeVisitor : IImageVisitor
{
private readonly IImageEncoder encoder;

13
src/ImageSharp/ImageExtensions.cs

@ -119,5 +119,16 @@ namespace SixLabors.ImageSharp
return $"data:{format.DefaultMimeType};base64,{Convert.ToBase64String(stream.ToArray())}";
}
}
/// <summary>
/// Throws <see cref="ObjectDisposedException"/> if the image is disposed.
/// </summary>
internal static void EnsureNotDisposed(this Image image)
{
if (image.IsDisposed)
{
throw new ObjectDisposedException(nameof(image), "Trying to execute an operation on a disposed image.");
}
}
}
}
}

2
src/ImageSharp/ImageFrameCollection{TPixel}.cs

@ -347,7 +347,7 @@ namespace SixLabors.ImageSharp
private ImageFrame<TPixel> CopyNonCompatibleFrame(ImageFrame source)
{
ImageFrame<TPixel> result = new ImageFrame<TPixel>(
var result = new ImageFrame<TPixel>(
this.parent.GetConfiguration(),
source.Size(),
source.Metadata.DeepClone());

11
src/ImageSharp/ImageFrame{TPixel}.cs

@ -1,10 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp
Guard.MustBeGreaterThan(height, 0, nameof(height));
this.PixelBuffer = this.MemoryAllocator.Allocate2D<TPixel>(width, height);
this.Clear(configuration.GetParallelOptions(), backgroundColor);
this.Clear(backgroundColor);
}
/// <summary>
@ -267,7 +267,7 @@ namespace SixLabors.ImageSharp
ParallelHelper.IterateRows(
this.Bounds(),
configuration,
(rows) =>
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
@ -283,9 +283,8 @@ namespace SixLabors.ImageSharp
/// <summary>
/// Clears the bitmap.
/// </summary>
/// <param name="parallelOptions">The parallel options.</param>
/// <param name="value">The value to initialize the bitmap with.</param>
internal void Clear(ParallelOptions parallelOptions, TPixel value)
internal void Clear(TPixel value)
{
Span<TPixel> span = this.GetPixelSpan();

2
src/ImageSharp/ImageSharp.csproj

@ -14,11 +14,9 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp</AssemblyName>
<PackageId>SixLabors.ImageSharp</PackageId>
<PackageTags>Image Resize Crop Gif Jpg Jpeg Bitmap Png Core</PackageTags>
<RootNamespace>SixLabors.ImageSharp</RootNamespace>
<TargetFrameworks>netcoreapp2.1;netstandard1.3;netstandard2.0;net472</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' OR '$(TargetFramework)' == 'net472' ">

8
src/ImageSharp/Image{TPixel}.cs

@ -162,6 +162,8 @@ namespace SixLabors.ImageSharp
/// <returns>Returns a new <see cref="Image{TPixel}"/> with all the same pixel data as the original.</returns>
public Image<TPixel> Clone(Configuration configuration)
{
this.EnsureNotDisposed();
IEnumerable<ImageFrame<TPixel>> clonedFrames =
this.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(x => x.Clone(configuration));
return new Image<TPixel>(configuration, this.Metadata.DeepClone(), clonedFrames);
@ -175,17 +177,21 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel2}"/>.</returns>
public override Image<TPixel2> CloneAs<TPixel2>(Configuration configuration)
{
this.EnsureNotDisposed();
IEnumerable<ImageFrame<TPixel2>> clonedFrames =
this.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel2>>(x => x.CloneAs<TPixel2>(configuration));
return new Image<TPixel2>(configuration, this.Metadata.DeepClone(), clonedFrames);
}
/// <inheritdoc/>
public override void Dispose() => this.Frames.Dispose();
protected override void DisposeImpl() => this.Frames.Dispose();
/// <inheritdoc />
internal override void AcceptVisitor(IImageVisitor visitor)
{
this.EnsureNotDisposed();
visitor.Visit(this);
}

5
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -42,7 +41,7 @@ namespace SixLabors.ImageSharp.Memory
fixed (byte* ptr = span)
{
byte* basePtr = (byte*)ptr;
byte* basePtr = ptr;
for (int y = 0; y < buffer.Height; y++)
{
byte* sPtr = basePtr + sOffset;
@ -185,4 +184,4 @@ namespace SixLabors.ImageSharp.Memory
}
}
}
}
}

24
src/ImageSharp/Memory/Buffer2D{T}.cs

@ -61,13 +61,31 @@ namespace SixLabors.ImageSharp.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
ImageSharp.DebugGuard.MustBeLessThan(x, this.Width, nameof(x));
ImageSharp.DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
DebugGuard.MustBeLessThan(x, this.Width, nameof(x));
DebugGuard.MustBeLessThan(y, this.Height, nameof(y));
Span<T> span = this.Span;
return ref span[(this.Width * y) + x];
}
}
/// <summary>
/// Creates a new <see cref="Buffer2D{T}"/> instance that maps to a target rows interval from the current instance.
/// </summary>
/// <param name="y">The target vertical offset for the rows interval to retrieve.</param>
/// <param name="h">The desired number of rows to extract.</param>
/// <returns>The new <see cref="Buffer2D{T}"/> instance with the requested rows interval.</returns>
public Buffer2D<T> Slice(int y, int h)
{
DebugGuard.MustBeGreaterThanOrEqualTo(y, 0, nameof(y));
DebugGuard.MustBeGreaterThan(h, 0, nameof(h));
DebugGuard.MustBeLessThanOrEqualTo(y + h, this.Height, nameof(h));
Memory<T> slice = this.Memory.Slice(y * this.Width, h * this.Width);
var memory = new MemorySource<T>(slice);
return new Buffer2D<T>(memory, this.Width, h);
}
/// <summary>
/// Disposes the <see cref="Buffer2D{T}"/> instance
/// </summary>
@ -98,4 +116,4 @@ namespace SixLabors.ImageSharp.Memory
a.Height = bSize.Height;
}
}
}
}

4
src/ImageSharp/Memory/MemoryOwnerExtensions.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Memory
=> buffer.GetSpan().Length;
/// <summary>
/// Gets a <see cref="Span{T}"/> to an offseted position inside the buffer.
/// Gets a <see cref="Span{T}"/> to an offsetted position inside the buffer.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="start">The start</param>
@ -60,4 +60,4 @@ namespace SixLabors.ImageSharp.Memory
where T : struct =>
ref MemoryMarshal.GetReference(buffer.GetSpan());
}
}
}

35
src/ImageSharp/MetaData/ImageMetaData.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
@ -63,11 +63,6 @@ namespace SixLabors.ImageSharp.Metadata
this.formatMetadata.Add(meta.Key, meta.Value.DeepClone());
}
foreach (ImageProperty property in other.Properties)
{
this.Properties.Add(property);
}
this.ExifProfile = other.ExifProfile?.DeepClone();
this.IccProfile = other.IccProfile?.DeepClone();
}
@ -127,11 +122,6 @@ namespace SixLabors.ImageSharp.Metadata
/// </summary>
public IccProfile IccProfile { get; set; }
/// <summary>
/// Gets the list of properties for storing meta information about this image.
/// </summary>
public IList<ImageProperty> Properties { get; } = new List<ImageProperty>();
/// <summary>
/// Gets the metadata value associated with the specified key.
/// </summary>
@ -156,29 +146,6 @@ namespace SixLabors.ImageSharp.Metadata
/// <inheritdoc/>
public ImageMetadata DeepClone() => new ImageMetadata(this);
/// <summary>
/// Looks up a property with the provided name.
/// </summary>
/// <param name="name">The name of the property to lookup.</param>
/// <param name="result">The property, if found, with the provided name.</param>
/// <returns>Whether the property was found.</returns>
internal bool TryGetProperty(string name, out ImageProperty result)
{
foreach (ImageProperty property in this.Properties)
{
if (property.Name == name)
{
result = property;
return true;
}
}
result = default;
return false;
}
/// <summary>
/// Synchronizes the profiles with the current metadata.
/// </summary>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save