Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4e8de95
Refactored byte[] array in ZigZag type
Sergio0694 Feb 27, 2020
c83d066
Refactored byte[] array in DeflaterHuffman type
Sergio0694 Feb 27, 2020
aa65526
Refactored byte[] array in PngConstants type
Sergio0694 Feb 27, 2020
da79756
Refactored byte[] array in ExifConstants type
Sergio0694 Feb 27, 2020
4dbec4c
Refactored byte[] array in Octree type
Sergio0694 Feb 27, 2020
714c960
Refactored byte[] arrays in GifConstants type
Sergio0694 Feb 27, 2020
bb7b041
Refactored byte[] arrays in ProfileResolver type
Sergio0694 Feb 27, 2020
f907f90
Code style tweaks
Sergio0694 Feb 27, 2020
8ad8a59
Fixed a build error
Sergio0694 Feb 27, 2020
b251c00
Minor code changes to improve clarity
Sergio0694 Feb 28, 2020
0577690
Added input validation to DeflaterHuffman unsafe offsetting
Sergio0694 Feb 28, 2020
68387a7
Fixed a test in the DeflaterHuffman type
Sergio0694 Feb 28, 2020
f3f6d9c
Merge branch 'master' into sp/text-segment-initialization
Sergio0694 Feb 28, 2020
5b7ac75
Refactored DeflaterHuffman.BitLengthOrder to ReadOnlySpan<byte>
Sergio0694 Feb 28, 2020
70ed21e
Reintroduced some bounds checks for additional security
Sergio0694 Feb 28, 2020
4b0dfd1
Minor micro-optimization in DeflaterHuffman type
Sergio0694 Feb 28, 2020
719b5a3
Merge branch 'master' into sp/text-segment-initialization
JimBobSquarePants Feb 29, 2020
99b88c0
Merge branch 'master' into sp/text-segment-initialization
JimBobSquarePants Mar 4, 2020
b43a66c
tem remove submodule to reset dirty
JimBobSquarePants Mar 6, 2020
cbd1872
Fix module, import configs, automate T4 builds
JimBobSquarePants Mar 6, 2020
20bf5fa
Manually include compiled file in source via auto copy.
JimBobSquarePants Mar 6, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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 change: 1 addition & 0 deletions ImageSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@
<InternalsVisibleTo Include="SixLabors.ImageSharp.Tests" PublicKey="$(SixLaborsPublicKey)" />
</ItemGroup>



</Project>
16 changes: 16 additions & 0 deletions src/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,20 @@
<!-- 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" />
</ItemGroup>

<!--Ensures our config files are up to date.-->
<Target Name="CopyFiles" BeforeTargets="Build">
<Copy SourceFiles="@(ConfigFilesToCopy)" DestinationFolder="..\..\" />
</Target>

<!-- Allows regenerating T4-generated files at build time using MsBuild -->
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>

</Project>
4 changes: 3 additions & 1 deletion src/ImageSharp/Common/Extensions/StreamExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Common/Helpers/Guard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static void MustBeValueType<TValue>(TValue value, string parameterName)
{
if (!value.GetType().GetTypeInfo().IsValueType)
{
ThrowArgumentException("Type must be a struct.", parameterName);
ThrowHelper.ThrowArgumentException("Type must be a struct.", parameterName);
}
}
}
Expand Down
31 changes: 21 additions & 10 deletions src/ImageSharp/Formats/Gif/GifConstants.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -21,11 +22,6 @@ internal static class GifConstants
/// </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>
Expand All @@ -51,11 +47,6 @@ internal static class GifConstants
/// </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>
Expand Down Expand Up @@ -110,5 +101,25 @@ internal static class GifConstants
/// 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 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Gif/GifEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ private int GetTransparentIndex<TPixel>(QuantizedFrame<TPixel> quantized)
/// </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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public int WriteTo(Span<byte> buffer)
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)
Expand Down
35 changes: 24 additions & 11 deletions src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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.
Expand All @@ -43,4 +56,4 @@ public static bool IsProfile(ReadOnlySpan<byte> bytesToCheck, ReadOnlySpan<byte>
&& bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier);
}
}
}
}
16 changes: 9 additions & 7 deletions src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@ internal unsafe struct ZigZag
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,
Expand Down Expand Up @@ -75,8 +74,11 @@ public byte this[int idx]
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;
}

Expand Down
31 changes: 16 additions & 15 deletions src/ImageSharp/Formats/Png/PngConstants.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -36,21 +37,6 @@ internal static class PngConstants
/// </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>
Expand All @@ -77,5 +63,20 @@ internal static class PngConstants
/// 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 changes: 1 addition & 1 deletion src/ImageSharp/Formats/Png/PngEncoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream)
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);
Expand Down
43 changes: 33 additions & 10 deletions src/ImageSharp/Formats/Png/Zlib/DeflaterHuffman.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ internal sealed unsafe class DeflaterHuffman : IDisposable

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;
Expand Down Expand Up @@ -128,6 +122,13 @@ public DeflaterHuffman(MemoryAllocator memoryAllocator)
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>
Expand Down Expand Up @@ -158,6 +159,7 @@ public void SendAllTrees(int blTreeCodes)
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);
Expand Down Expand Up @@ -250,6 +252,7 @@ public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool l
this.blTree.BuildTree();

int blTreeCodes = 4;

for (int i = 18; i > blTreeCodes; i--)
{
if (this.blTree.Length[BitLengthOrder[i]] > 0)
Expand Down Expand Up @@ -363,10 +366,30 @@ public bool TallyDist(int distance, int length)
[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/>
Expand Down
Loading