From 6c7af0c4a6cf208dc0fa4f4dfb06037fb838b90d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 25 Sep 2017 08:27:32 +1000 Subject: [PATCH] Testable code --- .../Jpeg/Common/Decoder/ProfileResolver.cs | 59 ++++++++++++++ .../Jpeg/GolangPort/OrigJpegConstants.cs | 69 ---------------- .../Jpeg/GolangPort/OrigJpegDecoderCore.cs | 21 +---- .../Formats/Jpg/ProfileResolverTests.cs | 79 +++++++++++++++++++ 4 files changed, 142 insertions(+), 86 deletions(-) create mode 100644 src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs create mode 100644 tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs new file mode 100644 index 0000000000..7ea0f9215e --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/ProfileResolver.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder +{ + /// + /// Provides methods for identifying metadata and color profiles within jpeg images. + /// + internal static class ProfileResolver + { + /// + /// Describes the EXIF specific markers + /// + public static readonly byte[] JFifMarker = ToAsciiBytes("JFIF\0"); + + /// + /// Describes the EXIF specific markers + /// + public static readonly byte[] IccMarker = ToAsciiBytes("ICC_PROFILE\0"); + + /// + /// Describes the ICC specific markers + /// + public static readonly byte[] ExifMarker = ToAsciiBytes("Exif\0\0"); + + /// + /// Describes Adobe specific markers + /// + public static readonly byte[] AdobeMarker = ToAsciiBytes("Adobe"); + + /// + /// Returns a value indicating whether the passed bytes are a match to the profile identifer + /// + /// The bytes to check + /// The profile identifier + /// The + public static bool IsProfile(Span bytesToCheck, Span profileIdentifier) + { + return bytesToCheck.Length >= profileIdentifier.Length + && bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier); + } + + // No Encoding.ASCII nor Linq.Select on NetStandard 1.1 + private static byte[] ToAsciiBytes(string str) + { + int length = str.Length; + byte[] bytes = new byte[length]; + char[] chars = str.ToCharArray(); + for (int i = 0; i < length; i++) + { + bytes[i] = (byte)chars[i]; + } + + return bytes; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs index 51c14c285f..be383d2120 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegConstants.cs @@ -25,49 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort /// public static readonly IEnumerable FileExtensions = new[] { "jpg", "jpeg", "jfif" }; - /// - /// Descibes the various header identifers for metadata profiles - /// - /// - /// Encoding ASCII isn't available for NetStandard 1.1 - /// - internal static class ProfileIdentifiers - { - /// - /// Describes the EXIF specific markers - /// - public static readonly byte[] JFifMarker = ToAsciiBytes("JFIF\0"); - - /// - /// Describes the EXIF specific markers - /// - public static readonly byte[] IccMarker = ToAsciiBytes("ICC_PROFILE\0"); - - /// - /// Describes the ICC specific markers - /// - public static readonly byte[] ExifMarker = ToAsciiBytes("Exif\0\0"); - - /// - /// Describes Adobe specific markers - /// - public static readonly byte[] AdobeMarker = ToAsciiBytes("Adobe"); - - // No Linq Select on NetStandard 1.1 - private static byte[] ToAsciiBytes(string str) - { - int length = str.Length; - byte[] bytes = new byte[length]; - char[] chars = str.ToCharArray(); - for (int i = 0; i < length; i++) - { - bytes[i] = (byte)chars[i]; - } - - return bytes; - } - } - /// /// Describes common Jpeg markers /// @@ -208,32 +165,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort public const byte APP15 = 0xef; } - /// - /// Contains JFIF specific markers - /// - public static class JFif - { - /// - /// Represents J in ASCII - /// - public const byte J = 0x4A; - - /// - /// Represents F in ASCII - /// - public const byte F = 0x46; - - /// - /// Represents I in ASCII - /// - public const byte I = 0x49; - - /// - /// Represents the null "0" marker - /// - public const byte Null = 0x0; - } - /// /// Describes Adobe specific markers /// diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs index 538e56d910..d68a901551 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.IO; @@ -395,18 +394,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.InitDerivedMetaDataProperties(); } - /// - /// Returns a value indicating whether the passed bytes are a match to the profile identifer - /// - /// The bytes to check - /// The profile identifier - /// The - private static bool IsProfile(Span bytesToCheck, Span profileIdentifier) - { - return bytesToCheck.Length >= profileIdentifier.Length - && bytesToCheck.Slice(0, profileIdentifier.Length).SequenceEqual(profileIdentifier); - } - /// /// Assigns derived metadata properties to , eg. horizontal and vertical resolution if it has a JFIF header. /// @@ -447,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.InputProcessor.ReadFull(this.Temp, 0, 13); remaining -= 13; - if (IsProfile(this.Temp, OrigJpegConstants.ProfileIdentifiers.JFifMarker)) + if (ProfileResolver.IsProfile(this.Temp, ProfileResolver.JFifMarker)) { this.isJFif = true; this.jFifHorizontalResolution = (short)((this.Temp[8] << 8) | this.Temp[9]); @@ -475,7 +462,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort byte[] profile = new byte[remaining]; this.InputProcessor.ReadFull(profile, 0, remaining); - if (IsProfile(profile, OrigJpegConstants.ProfileIdentifiers.ExifMarker)) + if (ProfileResolver.IsProfile(profile, ProfileResolver.ExifMarker)) { this.isExif = true; this.MetaData.ExifProfile = new ExifProfile(profile); @@ -500,7 +487,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.InputProcessor.ReadFull(identifier, 0, Icclength); remaining -= Icclength; // We have read it by this point - if (IsProfile(identifier, OrigJpegConstants.ProfileIdentifiers.IccMarker)) + if (ProfileResolver.IsProfile(identifier, ProfileResolver.IccMarker)) { byte[] profile = new byte[remaining]; this.InputProcessor.ReadFull(profile, 0, remaining); @@ -538,7 +525,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort this.InputProcessor.ReadFull(this.Temp, 0, 12); remaining -= 12; - if (IsProfile(this.Temp, OrigJpegConstants.ProfileIdentifiers.AdobeMarker)) + if (ProfileResolver.IsProfile(this.Temp, ProfileResolver.AdobeMarker)) { this.isAdobe = true; this.adobeColorTransform = this.Temp[11]; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs new file mode 100644 index 0000000000..1d368d1f5b --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Jpg/ProfileResolverTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Tests.Formats.Jpg +{ + using System.Text; + + using SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder; + + using Xunit; + + public class ProfileResolverTests + { + private static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0"); + private static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0"); + private static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0"); + private static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe"); + + [Fact] + public void ProfileResolverHasCorrectJFifMarker() + { + Assert.Equal(JFifMarker, ProfileResolver.JFifMarker); + } + + [Fact] + public void ProfileResolverHasCorrectExifMarker() + { + Assert.Equal(ExifMarker, ProfileResolver.ExifMarker); + } + + [Fact] + public void ProfileResolverHasCorrectIccMarker() + { + Assert.Equal(IccMarker, ProfileResolver.IccMarker); + } + + [Fact] + public void ProfileResolverHasCorrectAdobeMarker() + { + Assert.Equal(AdobeMarker, ProfileResolver.AdobeMarker); + } + + [Fact] + public void ProfileResolverCanResolveJFifMarker() + { + Assert.True(ProfileResolver.IsProfile(JFifMarker, ProfileResolver.JFifMarker)); + } + + [Fact] + public void ProfileResolverCanResolveExifMarker() + { + Assert.True(ProfileResolver.IsProfile(ExifMarker, ProfileResolver.ExifMarker)); + } + + [Fact] + public void ProfileResolverCanResolveIccMarker() + { + Assert.True(ProfileResolver.IsProfile(IccMarker, ProfileResolver.IccMarker)); + } + + [Fact] + public void ProfileResolverCanResolveAdobeMarker() + { + Assert.True(ProfileResolver.IsProfile(AdobeMarker, ProfileResolver.AdobeMarker)); + } + + [Fact] + public void ProfileResolverCorrectlyReportsNonMarker() + { + Assert.False(ProfileResolver.IsProfile(IccMarker, ProfileResolver.AdobeMarker)); + } + + [Fact] + public void ProfileResolverCanHandleIncorrectLength() + { + Assert.False(ProfileResolver.IsProfile(AdobeMarker, ProfileResolver.IccMarker)); + } + } +} \ No newline at end of file