Browse Source

Merge pull request #1133 from SixLabors/sp/text-segment-initialization

Text segment ReadOnlySpan<byte> initialization
pull/1140/head
James Jackson-South 6 years ago
committed by GitHub
parent
commit
a49ebb08f6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .editorconfig
  2. 1
      ImageSharp.sln
  3. 2
      shared-infrastructure
  4. 2
      src/Directory.Build.props
  5. 33
      src/Directory.Build.targets
  6. 4
      src/ImageSharp/Common/Extensions/StreamExtensions.cs
  7. 1154
      src/ImageSharp/Common/Helpers/Guard.Numeric.cs
  8. 2
      src/ImageSharp/Common/Helpers/Guard.cs
  9. 31
      src/ImageSharp/Formats/Gif/GifConstants.cs
  10. 2
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  11. 2
      src/ImageSharp/Formats/Gif/Sections/GifNetscapeLoopingApplicationExtension.cs
  12. 35
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
  13. 16
      src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
  14. 31
      src/ImageSharp/Formats/Png/PngConstants.cs
  15. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  16. 43
      src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
  17. 15
      src/ImageSharp/ImageSharp.csproj
  18. 10
      src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
  19. 2
      src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
  20. 9
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  21. 37
      src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs
  22. 12
      tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs
  23. 8
      tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs
  24. 4
      tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs
  25. 2
      tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

4
.editorconfig

@ -368,8 +368,6 @@ csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_for_built_in_types = never
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = false:warning
csharp_prefer_simple_using_statement = false:silent

1
ImageSharp.sln

@ -334,6 +334,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests.ProfilingS
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5
shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{68a8cc40-6aed-4e96-b524-31b1158fdeea}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution

2
shared-infrastructure

@ -1 +1 @@
Subproject commit 36b2d55f5bb0d91024955bd26ba220ee41cc96e5
Subproject commit 8dfef29f1838da76be9596f1a2f1be6d93e453d3

2
src/Directory.Build.props

@ -34,4 +34,6 @@
<InternalsVisibleTo Include="SixLabors.ImageSharp.Tests" PublicKey="$(SixLaborsPublicKey)" />
</ItemGroup>
</Project>

33
src/Directory.Build.targets

@ -52,4 +52,37 @@
<!-- https://github.com/Microsoft/vstest/issues/411 -->
<Target Name="VSTest" Condition="'$(IsTestProject)' == 'true'"/>
<ItemGroup>
<!--Shared config files that have to exist at root level.-->
<ConfigFilesToCopy Include="..\..\shared-infrastructure\.editorconfig;..\..\shared-infrastructure\.gitattributes" />
<!--
Copy submodule files generated via T4 templates.
We cannot include the generated files in the submodule as any solution wide T4 regeneration
could potentially cause the module to be marked dirty which is a headache.
This, instead, combined with using 'Compile Update' over 'Include' allows us to copy the file
across from the submodule and place it directly in the src folder for compilation.
-->
<SubmoduleSourceFilesToCopy Include="..\..\shared-infrastructure\src\SharedInfrastructure\Guard.Numeric.cs" />
</ItemGroup>
<!--Ensures our config files are up to date.-->
<Target Name="CopyFiles" BeforeTargets="Build">
<Copy SourceFiles="@(ConfigFilesToCopy)"
SkipUnchangedFiles = "true"
DestinationFolder="..\..\" />
<Copy SourceFiles="@(SubmoduleSourceFilesToCopy)"
DestinationFolder="..\..\src\ImageSharp\Common\Helpers"
SkipUnchangedFiles="true"
ContinueOnError="true" />
</Target>
<!-- Allows regenerating T4-generated files at build time using MsBuild -->
<!-- Enable on Windows OS to build all T4 templates. TODO: XPlat
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
-->
</Project>

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

@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using SixLabors.ImageSharp.Memory;
#if !SUPPORTS_SPAN_STREAM
using System.Buffers;
#endif
namespace SixLabors.ImageSharp
{

1154
src/ImageSharp/Common/Helpers/Guard.Numeric.cs

File diff suppressed because it is too large

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

@ -22,7 +22,7 @@ namespace SixLabors
{
if (!value.GetType().GetTypeInfo().IsValueType)
{
ThrowArgumentException("Type must be a struct.", parameterName);
ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName);
}
}
}

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Text;
@ -21,11 +22,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public const string FileVersion = "89a";
/// <summary>
/// The ASCII encoded bytes used to identify the GIF file.
/// </summary>
internal static readonly byte[] MagicNumber = Encoding.ASCII.GetBytes(FileType + FileVersion);
/// <summary>
/// The extension block introducer <value>!</value>.
/// </summary>
@ -51,11 +47,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public const string NetscapeApplicationIdentification = "NETSCAPE2.0";
/// <summary>
/// The ASCII encoded application identification bytes.
/// </summary>
internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.ASCII.GetBytes(NetscapeApplicationIdentification);
/// <summary>
/// The Netscape looping application sub block size.
/// </summary>
@ -110,5 +101,25 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The collection of file extensions that equate to a Gif.
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "gif" };
/// <summary>
/// Gets the ASCII encoded bytes used to identify the GIF file (combining <see cref="FileType"/> and <see cref="FileVersion"/>).
/// </summary>
internal static ReadOnlySpan<byte> MagicNumber => new[]
{
(byte)'G', (byte)'I', (byte)'F',
(byte)'8', (byte)'9', (byte)'a'
};
/// <summary>
/// Gets the ASCII encoded application identification bytes (representing <see cref="NetscapeApplicationIdentification"/>).
/// </summary>
internal static ReadOnlySpan<byte> NetscapeApplicationIdentificationBytes => new[]
{
(byte)'N', (byte)'E', (byte)'T',
(byte)'S', (byte)'C', (byte)'A',
(byte)'P', (byte)'E',
(byte)'2', (byte)'.', (byte)'0'
};
}
}

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

@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <param name="stream">The stream to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber, 0, GifConstants.MagicNumber.Length);
private void WriteHeader(Stream stream) => stream.Write(GifConstants.MagicNumber);
/// <summary>
/// Writes the logical screen descriptor to the stream.

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

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
buffer[0] = GifConstants.ApplicationBlockSize;
// Write NETSCAPE2.0
GifConstants.NetscapeApplicationIdentificationBytes.AsSpan().CopyTo(buffer.Slice(1, 11));
GifConstants.NetscapeApplicationIdentificationBytes.CopyTo(buffer.Slice(1, 11));
// Application Data ----
buffer[12] = 3; // Application block length (always 3)

35
src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.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.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@ -12,24 +11,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
internal static class ProfileResolver
{
/// <summary>
/// Describes the JFIF specific markers.
/// Gets the JFIF specific markers.
/// </summary>
public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0");
public static ReadOnlySpan<byte> JFifMarker => new[]
{
(byte)'J', (byte)'F', (byte)'I', (byte)'F', (byte)'\0'
};
/// <summary>
/// Describes the ICC specific markers.
/// Gets the ICC specific markers.
/// </summary>
public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0");
public static ReadOnlySpan<byte> IccMarker => new[]
{
(byte)'I', (byte)'C', (byte)'C', (byte)'_',
(byte)'P', (byte)'R', (byte)'O', (byte)'F',
(byte)'I', (byte)'L', (byte)'E', (byte)'\0'
};
/// <summary>
/// Describes the EXIF specific markers.
/// Gets the EXIF specific markers.
/// </summary>
public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0");
public static ReadOnlySpan<byte> ExifMarker => new[]
{
(byte)'E', (byte)'x', (byte)'i', (byte)'f', (byte)'\0', (byte)'\0'
};
/// <summary>
/// Describes Adobe specific markers <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe"/>.
/// Gets the Adobe specific markers <see href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html#Adobe"/>.
/// </summary>
public static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe");
public static ReadOnlySpan<byte> AdobeMarker => new[]
{
(byte)'A', (byte)'d', (byte)'o', (byte)'b', (byte)'e'
};
/// <summary>
/// Returns a value indicating whether the passed bytes are a match to the profile identifier.
@ -43,4 +56,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier);
}
}
}
}

16
src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs

@ -34,12 +34,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public fixed byte Data[Size];
/// <summary>
/// Unzig maps from the zigzag ordering to the natural ordering. For example,
/// unzig[3] is the column and row of the fourth element in zigzag order. The
/// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
/// Gets the unzigs map, which maps from the zigzag ordering to the natural ordering.
/// For example, unzig[3] is the column and row of the fourth element in zigzag order.
/// The value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
/// </summary>
private static readonly byte[] Unzig =
new byte[Size]
private static ReadOnlySpan<byte> Unzig => new byte[]
{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
@ -75,8 +74,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public static ZigZag CreateUnzigTable()
{
ZigZag result = default;
byte* unzigPtr = result.Data;
Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, Size);
ref byte sourceRef = ref MemoryMarshal.GetReference(Unzig);
ref byte destinationRef = ref Unsafe.AsRef<byte>(result.Data);
Unzig.CopyTo(new Span<byte>(result.Data, Size));
return result;
}

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Text;
@ -36,21 +37,6 @@ namespace SixLabors.ImageSharp.Formats.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.
0x50, // P
0x4E, // N
0x47, // G
0x0D, // Line ending CRLF
0x0A, // Line ending CRLF
0x1A, // EOF
0x0A // LF
};
/// <summary>
/// The header bytes as a big-endian coded ulong.
/// </summary>
@ -77,5 +63,20 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The minimum length of a keyword in a text chunk is 1 byte.
/// </summary>
public const int MinTextKeywordLength = 1;
/// <summary>
/// Gets the header bytes identifying a Png.
/// </summary>
public static ReadOnlySpan<byte> HeaderBytes => new byte[]
{
0x89, // Set the high bit.
0x50, // P
0x4E, // N
0x47, // G
0x0D, // Line ending CRLF
0x0A, // Line ending CRLF
0x1A, // EOF
0x0A // LF
};
}
}

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

@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Png
QuantizedFrame<TPixel> quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image);
this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized);
stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length);
stream.Write(PngConstants.HeaderBytes);
this.WriteHeaderChunk(stream);
this.WritePaletteChunk(stream, quantized);

43
src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs

@ -36,12 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private const int EofSymbol = 256;
// The lengths of the bit length codes are sent in order of decreasing
// probability, to avoid transmitting the lengths for unused bit length codes.
private static readonly int[] BitLengthOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
private static readonly byte[] Bit4Reverse = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
private static readonly short[] StaticLCodes;
private static readonly byte[] StaticLLength;
private static readonly short[] StaticDCodes;
@ -128,6 +122,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.pinnedLiteralBuffer = (short*)this.literalBufferHandle.Pointer;
}
/// <summary>
/// Gets the lengths of the bit length codes are sent in order of decreasing probability, to avoid transmitting the lengths for unused bit length codes.
/// </summary>
private static ReadOnlySpan<byte> BitLengthOrder => new byte[] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
private static ReadOnlySpan<byte> Bit4Reverse => new byte[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 };
/// <summary>
/// Gets the pending buffer to use.
/// </summary>
@ -158,6 +159,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.Pending.WriteBits(this.literalTree.NumCodes - 257, 5);
this.Pending.WriteBits(this.distTree.NumCodes - 1, 5);
this.Pending.WriteBits(blTreeCodes - 4, 4);
for (int rank = 0; rank < blTreeCodes; rank++)
{
this.Pending.WriteBits(this.blTree.Length[BitLengthOrder[rank]], 3);
@ -250,6 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.blTree.BuildTree();
int blTreeCodes = 4;
for (int i = 18; i > blTreeCodes; i--)
{
if (this.blTree.Length[BitLengthOrder[i]] > 0)
@ -363,10 +366,30 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
[MethodImpl(InliningOptions.ShortMethod)]
public static short BitReverse(int toReverse)
{
return (short)(Bit4Reverse[toReverse & 0xF] << 12
| Bit4Reverse[(toReverse >> 4) & 0xF] << 8
| Bit4Reverse[(toReverse >> 8) & 0xF] << 4
| Bit4Reverse[toReverse >> 12]);
/* Use unsafe offsetting and manually validate the input index to reduce the
* total number of conditional branches. There are two main cases to test here:
* 1. In the first 3, the input value (or some combination of it) is combined
* with & 0xF, which results in a maximum value of 0xF no matter what the
* input value was. That is 15, which is always in range for the target span.
* As a result, no input validation is needed at all in this case.
* 2. There are two cases where the input value might cause an invalid access:
* when it is either negative, or greater than 15 << 12. We can test both
* conditions in a single pass by casting the input value to uint and right
* shifting it by 12, which also preserves the sign. If it is a negative
* value (2-complement), the test will fail as the uint cast will result
* in a much larger value. If the value was simply too high, the test will
* fail as expected. We can't simply check whether the value is lower than
* 15 << 12, because higher values are acceptable in the first 3 accesses.
* Doing this reduces the total number of index checks from 4 down to just 1. */
int toReverseRightShiftBy12 = toReverse >> 12;
Guard.MustBeLessThanOrEqualTo<uint>((uint)toReverseRightShiftBy12, 15, nameof(toReverse));
ref byte bit4ReverseRef = ref MemoryMarshal.GetReference(Bit4Reverse);
return (short)(Unsafe.Add(ref bit4ReverseRef, toReverse & 0xF) << 12
| Unsafe.Add(ref bit4ReverseRef, (toReverse >> 4) & 0xF) << 8
| Unsafe.Add(ref bit4ReverseRef, (toReverse >> 8) & 0xF) << 4
| Unsafe.Add(ref bit4ReverseRef, toReverseRightShiftBy12));
}
/// <inheritdoc/>

15
src/ImageSharp/ImageSharp.csproj

@ -37,6 +37,16 @@
</ItemGroup>
<ItemGroup>
<!--
Note: Generating all templates via Visual Studio causes a duplicate entry that requires deleting
post operation. MSBuild target leaves the csproj file intact.
-->
<Compile Update="..\..\shared-infrastructure\src\SharedInfrastructure\Guard.Numeric.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Guard.Numeric.tt</DependentUpon>
</Compile>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
@ -202,6 +212,10 @@
<LastGenOutput>DefaultPixelBlenders.Generated.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
<None Update="..\..\shared-infrastructure\src\SharedInfrastructure\Guard.Numeric.tt">
<LastGenOutput>Guard.Numeric.cs</LastGenOutput>
<Generator>TextTemplatingFileGenerator</Generator>
</None>
</ItemGroup>
<ItemGroup>
@ -209,5 +223,4 @@
</ItemGroup>
<Import Project="..\..\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems" Label="Shared" />
</Project>

10
src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs

@ -1,11 +1,13 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
internal static class ExifConstants
{
public static readonly byte[] LittleEndianByteOrderMarker =
public static ReadOnlySpan<byte> LittleEndianByteOrderMarker => new byte[]
{
(byte)'I',
(byte)'I',
@ -13,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
0x00,
};
public static readonly byte[] BigEndianByteOrderMarker =
public static ReadOnlySpan<byte> BigEndianByteOrderMarker => new byte[]
{
(byte)'M',
(byte)'M',
@ -21,4 +23,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
0x2A
};
}
}
}

2
src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs

@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
int i = 0;
// The byte order marker for little-endian, followed by the number 42 and a 0
ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i));
ExifConstants.LittleEndianByteOrderMarker.CopyTo(result.AsSpan(start: i));
i += ExifConstants.LittleEndianByteOrderMarker.Length;
uint ifdOffset = ((uint)i - startIndex) + 4U;

9
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs

@ -13,7 +13,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
/// <summary>
/// Returns the result of the "NormalSrc" compositing equation.
/// </summary>
@ -419,7 +418,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "MultiplySrc" compositing equation.
/// </summary>
@ -825,7 +823,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "AddSrc" compositing equation.
/// </summary>
@ -1231,7 +1228,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "SubtractSrc" compositing equation.
/// </summary>
@ -1637,7 +1633,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "ScreenSrc" compositing equation.
/// </summary>
@ -2043,7 +2038,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "DarkenSrc" compositing equation.
/// </summary>
@ -2449,7 +2443,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "LightenSrc" compositing equation.
/// </summary>
@ -2855,7 +2848,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "OverlaySrc" compositing equation.
/// </summary>
@ -3261,7 +3253,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return dest;
}
/// <summary>
/// Returns the result of the "HardLightSrc" compositing equation.
/// </summary>

37
src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs

@ -6,6 +6,7 @@ using System.Buffers;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -111,21 +112,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
private sealed class Octree
{
/// <summary>
/// Mask used when getting the appropriate pixels for a given node.
/// </summary>
private static readonly byte[] Mask = new byte[]
{
0b10000000,
0b1000000,
0b100000,
0b10000,
0b1000,
0b100,
0b10,
0b1
};
/// <summary>
/// The root of the Octree
/// </summary>
@ -162,6 +148,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.previousNode = null;
}
/// <summary>
/// Gets the mask used when getting the appropriate pixels for a given node.
/// </summary>
private static ReadOnlySpan<byte> Mask => new byte[]
{
0b10000000,
0b1000000,
0b100000,
0b10000,
0b1000,
0b100,
0b10,
0b1
};
/// <summary>
/// Gets or sets the number of leaves in the tree
/// </summary>
@ -513,8 +514,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
[MethodImpl(InliningOptions.ShortMethod)]
private static int GetColorIndex(ref Rgba32 color, int level)
{
DebugGuard.MustBeLessThan(level, Mask.Length, nameof(level));
int shift = 7 - level;
byte mask = Mask[level];
ref byte maskRef = ref MemoryMarshal.GetReference(Mask);
byte mask = Unsafe.Add(ref maskRef, level);
return ((color.R & mask) >> shift)
| ((color.G & mask) >> (shift - 1))
| ((color.B & mask) >> (shift - 2));

12
tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.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;
@ -19,25 +19,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Fact]
public void ProfileResolverHasCorrectJFifMarker()
{
Assert.Equal(JFifMarker, ProfileResolver.JFifMarker);
Assert.Equal(JFifMarker, ProfileResolver.JFifMarker.ToArray());
}
[Fact]
public void ProfileResolverHasCorrectExifMarker()
{
Assert.Equal(ExifMarker, ProfileResolver.ExifMarker);
Assert.Equal(ExifMarker, ProfileResolver.ExifMarker.ToArray());
}
[Fact]
public void ProfileResolverHasCorrectIccMarker()
{
Assert.Equal(IccMarker, ProfileResolver.IccMarker);
Assert.Equal(IccMarker, ProfileResolver.IccMarker.ToArray());
}
[Fact]
public void ProfileResolverHasCorrectAdobeMarker()
{
Assert.Equal(AdobeMarker, ProfileResolver.AdobeMarker);
Assert.Equal(AdobeMarker, ProfileResolver.AdobeMarker.ToArray());
}
[Fact]
@ -76,4 +76,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.False(ProfileResolver.IsProfile(AdobeMarker, ProfileResolver.IccMarker));
}
}
}
}

8
tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.Generic.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests
this.Collection.AddFrame((ImageFrame<Rgba32>)null);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message);
}
[Fact]
@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests
this.Collection.AddFrame(data);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message);
}
[Fact]
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests
this.Collection.AddFrame(new Rgba32[0]);
});
Assert.StartsWith("Value 0 must be greater than or equal to 100.", ex.Message);
Assert.StartsWith($"Parameter \"data\" ({typeof(int)}) must be greater than or equal to {100}, was {0}", ex.Message);
}
[Fact]
@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Tests
this.Collection.InsertFrame(1, null);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
Assert.StartsWith("Parameter \"frame\" must be not null.", ex.Message);
}
[Fact]

4
tests/ImageSharp.Tests/Image/ImageFrameCollectionTests.NonGeneric.cs

@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests
this.Collection.AddFrame(null);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message);
}
[Fact]
@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests
this.Collection.InsertFrame(1, null);
});
Assert.StartsWith("Value cannot be null.", ex.Message);
Assert.StartsWith("Parameter \"source\" must be not null.", ex.Message);
}
[Fact]

2
tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs

@ -394,7 +394,7 @@ namespace SixLabors.ImageSharp.Tests
public void ProfileToByteArray()
{
// Arrange
byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker.ToArray();
ExifProfile expectedProfile = CreateExifProfile();
var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();

Loading…
Cancel
Save