Browse Source

to make ExifProfile format agnostic again: passing ExifIdCode as ReadonlySpan to the ToByteArray method

af/merge-core
popow 8 years ago
parent
commit
cc4a203130
  1. 3
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  2. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 40
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  4. 29
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  5. 2
      tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs
  6. 10
      tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
  7. 3
      tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs

3
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -6,6 +6,7 @@ using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc; using SixLabors.ImageSharp.MetaData.Profiles.Icc;
@ -608,7 +609,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
private void WriteExifProfile(ExifProfile exifProfile) private void WriteExifProfile(ExifProfile exifProfile)
{ {
const int Max = 65533; const int Max = 65533;
byte[] data = exifProfile?.ToByteArray(); byte[] data = exifProfile?.ToByteArray(ProfileResolver.ExifMarker);
if (data == null || data.Length == 0) if (data == null || data.Length == 0)
{ {
return; return;

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

@ -536,7 +536,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
if (image.MetaData.ExifProfile?.Values.Count > 0) if (image.MetaData.ExifProfile?.Values.Count > 0)
{ {
this.WriteChunk(stream, PngChunkType.Exif, image.MetaData.ExifProfile.ToByteArray(includeExifIdCode: false)); this.WriteChunk(stream, PngChunkType.Exif, image.MetaData.ExifProfile.ToByteArray());
} }
} }

40
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
@ -234,20 +233,13 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Converts this instance to a byte array. /// Converts this instance to a byte array.
/// </summary> /// </summary>
/// <param name="includeExifIdCode">Indicates, if the Exif ID code should be included. /// <param name="exifIdCode">The Exif Id Code is part of the JPEG APP1 segment (Exif00). Those bytes will be written at
/// The Exif Id Code is part of the JPEG APP1 segment. This Exif ID code should not be included in case of PNG's. /// the beginning of the array. This Exif ID code should not be included in case of PNG's.</param>
/// Defaults to true.</param>
/// <returns>The <see cref="T:byte[]"/></returns> /// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray(bool includeExifIdCode = true) public byte[] ToByteArray(ReadOnlySpan<byte> exifIdCode = default)
{ {
if (this.values == null) if (this.values == null)
{ {
if (!includeExifIdCode && this.StartsWithExifIdCode(this.data))
{
// skip the first 6 bytes (the Exif Code)
return this.data.Skip(6).ToArray();
}
return this.data; return this.data;
} }
@ -257,31 +249,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
} }
var writer = new ExifWriter(this.values, this.Parts); var writer = new ExifWriter(this.values, this.Parts);
return writer.GetData(includeExifIdCode); return writer.GetData(exifIdCode);
}
/// <summary>
/// Checks if a byte array start with the Exif Code: ASCII "Exif" followed by two zeros.
/// </summary>
/// <param name="exifBytes">The byte array to check for the Exif Code.</param>
/// <returns>True, if the byte array starts with the Exif Code</returns>
private bool StartsWithExifIdCode(byte[] exifBytes)
{
if (exifBytes.Length < 6)
{
return false;
}
int exifLength = ProfileResolver.ExifMarker.Length;
for (int i = 0; i < ProfileResolver.ExifMarker.Length; i++)
{
if (exifBytes[i] != ProfileResolver.ExifMarker[i])
{
return false;
}
}
return true;
} }
/// <summary> /// <summary>

29
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -5,7 +5,6 @@ using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
@ -42,15 +41,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary> /// <summary>
/// Returns the EXIF data. /// Returns the EXIF data.
/// </summary> /// </summary>
/// <param name="includeExifIdCode">Indicates, if the Exif ID code should be included. /// <param name="exifIdCode">The Exif Id Code is part of the JPEG APP1 segment (Exif00). Those bytes will be written at
/// The Exif Id Code is part of the JPEG APP1 segment. This Exif ID code should not be included in case of PNG's. /// the beginning of the array. This Exif ID code should not be included in case of PNG's.</param>
/// Defaults to true.</param>
/// <returns> /// <returns>
/// The <see cref="T:byte[]"/>. /// The <see cref="T:byte[]"/>.
/// </returns> /// </returns>
public byte[] GetData(bool includeExifIdCode = true) public byte[] GetData(ReadOnlySpan<byte> exifIdCode)
{ {
uint startIndex = (uint)ProfileResolver.ExifMarker.Length; uint exifIdCodeLength = exifIdCode.IsEmpty ? 0 : (uint)exifIdCode.Length;
uint startIndex = exifIdCodeLength;
uint length; uint length;
int exifIndex = -1; int exifIndex = -1;
int gpsIndex = -1; int gpsIndex = -1;
@ -86,17 +85,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null; return null;
} }
if (includeExifIdCode) length += exifIdCodeLength;
{
// Exif Id Code "Exif00" (6 bytes)
length += (uint)ProfileResolver.ExifMarker.Length;
}
else
{
// special case for PNG eXIf Chunk:
// if the Exif Code ("Exif00") is not included, the start index is 0 instead of 6
startIndex = 0;
}
// two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total // two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total
length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length; length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length;
@ -106,10 +95,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
byte[] result = new byte[length]; byte[] result = new byte[length];
int i = 0; int i = 0;
if (includeExifIdCode) if (!exifIdCode.IsEmpty)
{ {
ProfileResolver.ExifMarker.AsSpan().CopyTo(result); // 0-5 exifIdCode.CopyTo(result); // 0-5
i += ProfileResolver.ExifMarker.Length; i += exifIdCode.Length;
} }
// the byte order marker for little-endian, followed by the number 42 and a 0 // the byte order marker for little-endian, followed by the number 42 and a 0

2
tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.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. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -32,6 +33,7 @@ namespace SixLabors.ImageSharp.Tests
ImageMetaData clone = metaData.Clone(); ImageMetaData clone = metaData.Clone();
Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray()); Assert.Equal(exifProfile.ToByteArray(), clone.ExifProfile.ToByteArray());
Assert.Equal(exifProfile.ToByteArray(ProfileResolver.ExifMarker), clone.ExifProfile.ToByteArray(ProfileResolver.ExifMarker));
Assert.Equal(4, clone.HorizontalResolution); Assert.Equal(4, clone.HorizontalResolution);
Assert.Equal(2, clone.VerticalResolution); Assert.Equal(2, clone.VerticalResolution);
Assert.Equal(imageProperty, clone.Properties[0]); Assert.Equal(imageProperty, clone.Properties[0]);

10
tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs

@ -324,7 +324,7 @@ namespace SixLabors.ImageSharp.Tests
// Force parsing of the profile. // Force parsing of the profile.
Assert.Equal(24, profile.Values.Count); Assert.Equal(24, profile.Values.Count);
byte[] bytes = profile.ToByteArray(); byte[] bytes = profile.ToByteArray(ProfileResolver.ExifMarker);
Assert.Equal(495, bytes.Length); Assert.Equal(495, bytes.Length);
} }
@ -360,21 +360,21 @@ namespace SixLabors.ImageSharp.Tests
// arrange // arrange
byte[] exifBytesWithExifCode = ProfileResolver.ExifMarker.Concat(ExifConstants.LittleEndianByteOrderMarker).ToArray(); byte[] exifBytesWithExifCode = ProfileResolver.ExifMarker.Concat(ExifConstants.LittleEndianByteOrderMarker).ToArray();
byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker; byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
var profile = new ExifProfile(exifBytesWithExifCode); ExifProfile profile = CreateExifProfile();
// act // act
byte[] actual = profile.ToByteArray(includeExifIdCode); byte[] actual = profile.ToByteArray(includeExifIdCode ? ProfileResolver.ExifMarker : default(ReadOnlySpan<byte>));
// assert // assert
Assert.NotNull(actual); Assert.NotNull(actual);
Assert.NotEmpty(actual); Assert.NotEmpty(actual);
if (includeExifIdCode) if (includeExifIdCode)
{ {
Assert.Equal(exifBytesWithExifCode, actual); Assert.Equal(exifBytesWithExifCode, actual.Take(exifBytesWithExifCode.Length).ToArray());
} }
else else
{ {
Assert.Equal(exifBytesWithoutExifCode, actual); Assert.Equal(exifBytesWithoutExifCode, actual.Take(exifBytesWithoutExifCode.Length).ToArray());
} }
} }

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

@ -9,6 +9,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{ {
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Processing.Transforms; using SixLabors.ImageSharp.Processing.Transforms;
public class AutoOrientTests : FileTestBase public class AutoOrientTests : FileTestBase
@ -65,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
var profile = new ExifProfile(); var profile = new ExifProfile();
profile.SetValue(ExifTag.JPEGTables, orientation); profile.SetValue(ExifTag.JPEGTables, orientation);
byte[] bytes = profile.ToByteArray(); byte[] bytes = profile.ToByteArray(ProfileResolver.ExifMarker);
// Change the tag into ExifTag.Orientation // Change the tag into ExifTag.Orientation
bytes[16] = 18; bytes[16] = 18;
bytes[17] = 1; bytes[17] = 1;

Loading…
Cancel
Save