Browse Source

Merge branch 'master' into dp/jpeg-grayscale-fix

pull/1979/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
4fd6a29bb2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      .github/workflows/build-and-test.yml
  2. 7
      src/ImageSharp/ImageSharp.csproj
  3. 24
      src/ImageSharp/Metadata/Profiles/Exif/Values/ExifOrientationMode.cs
  4. 36
      src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs
  5. 71
      tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs

19
.github/workflows/build-and-test.yml

@ -98,15 +98,6 @@ jobs:
- name: Git Pull LFS - name: Git Pull LFS
run: git lfs pull run: git lfs pull
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v1
with:
dotnet-version: |
6.0.x
5.0.x
3.1.x
2.1.x
- name: NuGet Install - name: NuGet Install
uses: NuGet/setup-nuget@v1 uses: NuGet/setup-nuget@v1
@ -118,12 +109,14 @@ jobs:
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: ${{ runner.os }}-nuget- restore-keys: ${{ runner.os }}-nuget-
- name: DotNet Setup Preview - name: DotNet Setup
if: ${{ matrix.options.sdk-preview == true }}
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: ${{ matrix.options.sdk }} dotnet-version: |
include-prerelease: true 6.0.x
5.0.x
3.1.x
2.1.x
- name: DotNet Build - name: DotNet Build
if: ${{ matrix.options.sdk-preview != true }} if: ${{ matrix.options.sdk-preview != true }}

7
src/ImageSharp/ImageSharp.csproj

@ -58,13 +58,6 @@
<PackageReference Include="System.Memory" Version="4.5.4" /> <PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs"> <Compile Update="Formats\Jpeg\Components\Block8x8F.Generated.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>

24
src/ImageSharp/Processing/OrientationMode.cs → src/ImageSharp/Metadata/Profiles/Exif/Values/ExifOrientationMode.cs

@ -1,56 +1,56 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{ {
/// <summary> /// <summary>
/// Enumerates the available orientation values supplied by EXIF metadata. /// Enumerates the available orientation values supplied by EXIF metadata.
/// </summary> /// </summary>
internal enum OrientationMode : ushort public static class ExifOrientationMode
{ {
/// <summary> /// <summary>
/// Unknown rotation. /// Unknown rotation.
/// </summary> /// </summary>
Unknown = 0, public const ushort Unknown = 0;
/// <summary> /// <summary>
/// The 0th row at the top, the 0th column on the left. /// The 0th row at the top, the 0th column on the left.
/// </summary> /// </summary>
TopLeft = 1, public const ushort TopLeft = 1;
/// <summary> /// <summary>
/// The 0th row at the top, the 0th column on the right. /// The 0th row at the top, the 0th column on the right.
/// </summary> /// </summary>
TopRight = 2, public const ushort TopRight = 2;
/// <summary> /// <summary>
/// The 0th row at the bottom, the 0th column on the right. /// The 0th row at the bottom, the 0th column on the right.
/// </summary> /// </summary>
BottomRight = 3, public const ushort BottomRight = 3;
/// <summary> /// <summary>
/// The 0th row at the bottom, the 0th column on the left. /// The 0th row at the bottom, the 0th column on the left.
/// </summary> /// </summary>
BottomLeft = 4, public const ushort BottomLeft = 4;
/// <summary> /// <summary>
/// The 0th row on the left, the 0th column at the top. /// The 0th row on the left, the 0th column at the top.
/// </summary> /// </summary>
LeftTop = 5, public const ushort LeftTop = 5;
/// <summary> /// <summary>
/// The 0th row at the right, the 0th column at the top. /// The 0th row at the right, the 0th column at the top.
/// </summary> /// </summary>
RightTop = 6, public const ushort RightTop = 6;
/// <summary> /// <summary>
/// The 0th row on the right, the 0th column at the bottom. /// The 0th row on the right, the 0th column at the bottom.
/// </summary> /// </summary>
RightBottom = 7, public const ushort RightBottom = 7;
/// <summary> /// <summary>
/// The 0th row on the left, the 0th column at the bottom. /// The 0th row on the left, the 0th column at the bottom.
/// </summary> /// </summary>
LeftBottom = 8 public const ushort LeftBottom = 8;
} }
} }

36
src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs

@ -28,42 +28,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeImageApply() protected override void BeforeImageApply()
{ {
OrientationMode orientation = GetExifOrientation(this.Source); ushort orientation = GetExifOrientation(this.Source);
Size size = this.SourceRectangle.Size; Size size = this.SourceRectangle.Size;
switch (orientation) switch (orientation)
{ {
case OrientationMode.TopRight: case ExifOrientationMode.TopRight:
new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle); new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.BottomRight: case ExifOrientationMode.BottomRight:
new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Configuration, this.Source, this.SourceRectangle); new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.BottomLeft: case ExifOrientationMode.BottomLeft:
new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle); new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.LeftTop: case ExifOrientationMode.LeftTop:
new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle); new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle); new FlipProcessor(FlipMode.Horizontal).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.RightTop: case ExifOrientationMode.RightTop:
new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle); new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.RightBottom: case ExifOrientationMode.RightBottom:
new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle); new FlipProcessor(FlipMode.Vertical).Execute(this.Configuration, this.Source, this.SourceRectangle);
new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle); new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.LeftBottom: case ExifOrientationMode.LeftBottom:
new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle); new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Configuration, this.Source, this.SourceRectangle);
break; break;
case OrientationMode.Unknown: case ExifOrientationMode.Unknown:
case OrientationMode.TopLeft: case ExifOrientationMode.TopLeft:
default: default:
break; break;
} }
@ -81,32 +81,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Returns the current EXIF orientation /// Returns the current EXIF orientation
/// </summary> /// </summary>
/// <param name="source">The image to auto rotate.</param> /// <param name="source">The image to auto rotate.</param>
/// <returns>The <see cref="OrientationMode"/></returns> /// <returns>The <see cref="ushort"/></returns>
private static OrientationMode GetExifOrientation(Image<TPixel> source) private static ushort GetExifOrientation(Image<TPixel> source)
{ {
if (source.Metadata.ExifProfile is null) if (source.Metadata.ExifProfile is null)
{ {
return OrientationMode.Unknown; return ExifOrientationMode.Unknown;
} }
IExifValue<ushort> value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); IExifValue<ushort> value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation);
if (value is null) if (value is null)
{ {
return OrientationMode.Unknown; return ExifOrientationMode.Unknown;
} }
OrientationMode orientation; ushort orientation;
if (value.DataType == ExifDataType.Short) if (value.DataType == ExifDataType.Short)
{ {
orientation = (OrientationMode)value.Value; orientation = value.Value;
} }
else else
{ {
orientation = (OrientationMode)Convert.ToUInt16(value.Value); orientation = Convert.ToUInt16(value.Value);
source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation);
} }
source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, ExifOrientationMode.TopLeft);
return orientation; return orientation;
} }

71
tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs

@ -18,42 +18,41 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public const string FlipTestFile = TestImages.Bmp.F; public const string FlipTestFile = TestImages.Bmp.F;
public static readonly TheoryData<ExifDataType, byte[]> InvalidOrientationValues public static readonly TheoryData<ExifDataType, byte[]> InvalidOrientationValues
= new TheoryData<ExifDataType, byte[]> = new()
{ {
{ ExifDataType.Byte, new byte[] { 1 } }, { ExifDataType.Byte, new byte[] { 1 } },
{ ExifDataType.SignedByte, new byte[] { 2 } }, { ExifDataType.SignedByte, new byte[] { 2 } },
{ ExifDataType.SignedShort, BitConverter.GetBytes((short)3) }, { ExifDataType.SignedShort, BitConverter.GetBytes((short)3) },
{ ExifDataType.Long, BitConverter.GetBytes(4U) }, { ExifDataType.Long, BitConverter.GetBytes(4U) },
{ ExifDataType.SignedLong, BitConverter.GetBytes(5) } { ExifDataType.SignedLong, BitConverter.GetBytes(5) }
}; };
public static readonly TheoryData<ushort> ExifOrientationValues = new TheoryData<ushort> public static readonly TheoryData<ushort> ExifOrientationValues
{ = new()
0, {
1, ExifOrientationMode.Unknown,
2, ExifOrientationMode.TopLeft,
3, ExifOrientationMode.TopRight,
4, ExifOrientationMode.BottomRight,
5, ExifOrientationMode.BottomLeft,
6, ExifOrientationMode.LeftTop,
7, ExifOrientationMode.RightTop,
8 ExifOrientationMode.RightBottom,
}; ExifOrientationMode.LeftBottom
};
[Theory] [Theory]
[WithFile(FlipTestFile, nameof(ExifOrientationValues), PixelTypes.Rgba32)] [WithFile(FlipTestFile, nameof(ExifOrientationValues), PixelTypes.Rgba32)]
public void AutoOrient_WorksForAllExifOrientations<TPixel>(TestImageProvider<TPixel> provider, ushort orientation) public void AutoOrient_WorksForAllExifOrientations<TPixel>(TestImageProvider<TPixel> provider, ushort orientation)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage()) using Image<TPixel> image = provider.GetImage();
{ image.Metadata.ExifProfile = new ExifProfile();
image.Metadata.ExifProfile = new ExifProfile(); image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, orientation);
image.Metadata.ExifProfile.SetValue(ExifTag.Orientation, orientation);
image.Mutate(x => x.AutoOrient()); image.Mutate(x => x.AutoOrient());
image.DebugSave(provider, orientation, appendPixelTypeToFileName: false); image.DebugSave(provider, orientation, appendPixelTypeToFileName: false);
image.CompareToReferenceOutput(provider, orientation, appendPixelTypeToFileName: false); image.CompareToReferenceOutput(provider, orientation, appendPixelTypeToFileName: false);
}
} }
[Theory] [Theory]
@ -76,19 +75,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
// Change the number of components // Change the number of components
bytes[20] = 1; bytes[20] = 1;
var orientationCodeData = new byte[8]; byte[] orientationCodeData = new byte[8];
Array.Copy(orientation, orientationCodeData, orientation.Length); Array.Copy(orientation, orientationCodeData, orientation.Length);
ulong orientationCode = BitConverter.ToUInt64(orientationCodeData, 0); ulong orientationCode = BitConverter.ToUInt64(orientationCodeData, 0);
using (Image<TPixel> image = provider.GetImage()) using Image<TPixel> image = provider.GetImage();
using (Image<TPixel> reference = image.Clone()) using Image<TPixel> reference = image.Clone();
{ image.Metadata.ExifProfile = new ExifProfile(bytes);
image.Metadata.ExifProfile = new ExifProfile(bytes); image.Mutate(x => x.AutoOrient());
image.Mutate(x => x.AutoOrient()); image.DebugSave(provider, $"{dataType}-{orientationCode}", appendPixelTypeToFileName: false);
image.DebugSave(provider, $"{dataType}-{orientationCode}", appendPixelTypeToFileName: false); ImageComparer.Exact.VerifySimilarity(image, reference);
ImageComparer.Exact.VerifySimilarity(image, reference);
}
} }
} }
} }

Loading…
Cancel
Save