Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EXIF Support for PNG's #616

Merged
merged 45 commits into from
Aug 4, 2018

Conversation

brianpopow
Copy link
Collaborator

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

This MR adds reading and writing eXIf chunks for PNG files. The basic difference between JPG's and PNG's EXIF is, that PNG does not include the Exif code (e. g. "Exif00") in the exif chunk.

I had to change the ExifWriter a little for that. The GetData method now has a parameter, which indicates, if the Exif Code should be included (defaults to true).

Please review the changes and let me know what you think.

@codecov
Copy link

codecov bot commented Jun 16, 2018

Codecov Report

Merging #616 into master will increase coverage by <.01%.
The diff coverage is 96.05%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #616      +/-   ##
==========================================
+ Coverage   89.78%   89.79%   +<.01%     
==========================================
  Files         889      889              
  Lines       37529    37614      +85     
  Branches     2462     2468       +6     
==========================================
+ Hits        33697    33777      +80     
- Misses       3127     3133       +6     
+ Partials      705      704       -1
Impacted Files Coverage Δ
...rocessing/Processors/Transforms/AutoOrientTests.cs 100% <ø> (ø) ⬆️
...Formats/Jpeg/Components/Decoder/ProfileResolver.cs 100% <ø> (ø) ⬆️
...ts/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs 100% <ø> (ø) ⬆️
src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs 90% <ø> (-0.55%) ⬇️
.../ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs 100% <100%> (ø) ⬆️
...rc/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs 70.93% <100%> (-2.27%) ⬇️
...ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs 100% <100%> (ø) ⬆️
...c/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs 87.2% <100%> (ø) ⬆️
...rc/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs 84.82% <100%> (+0.32%) ⬆️
src/ImageSharp/Formats/Png/PngEncoderCore.cs 85.5% <100%> (+0.63%) ⬆️
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c2105e4...6c777d4. Read the comment docs.

@JimBobSquarePants
Copy link
Member

I'll leave this one for @dlemstra to review but I'm thinking we should make the EXIF00 bytes the sole responsibility of the jpeg format if it's unused in png.

/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray()
public byte[] ToByteArray(bool includeExifIdCode = true)
{
if (this.values == null)
Copy link
Member

@dlemstra dlemstra Jun 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also check if this.data starts with the exif id and skip the first 6 bytes when includeExifIdCode is false.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, i missed that one. I have added a unit test for that case.

int i = 0;
if (includeExifIdCode)
{
result[0] = (byte)'E';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also increment i when you set the values so you don't need the duplicate code block in the else part.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have changed that

}

// The EXIF ID code: ASCII "Exif" followed by two zeros.
byte[] exifCode = { 69, 120, 105, 102, 0, 0 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid this allocation per iteration. Do we know yet whether that identifier is specific to jpeg?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have changed that.

I am not sure if its jpeg specific or other formats which support Exif also use this. I only can tell for sure that PNG does not use it. Maybe @dlemstra knows more about this?

@tocsoft tocsoft added this to the 1.0.0-rc1 milestone Jun 17, 2018
@brianpopow brianpopow changed the title [WIP] EXIF Support for PNG's EXIF Support for PNG's Jun 20, 2018
/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray()
public byte[] ToByteArray(bool includeExifIdCode = true)
Copy link
Member

@JimBobSquarePants JimBobSquarePants Jun 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to move the Exif identifier out of the ExifProfile as that's defined as part of the App1 segment itself in section 4.7.2 of the specification.

http://www.exif.org/Exif2-2.PDF

Ideally we should pass the marker to the ToByteArray method when exporting. The ExifProfile constructor should have an overload accepting the marker.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i will do that on the weekend.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic, thanks!

@brianpopow brianpopow changed the title EXIF Support for PNG's [WIP] EXIF Support for PNG's Jun 21, 2018
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ExifProfile needs to be format agnostic.

@@ -239,7 +235,8 @@ public void SetValue(ExifTag tag, object value)
/// Converts this instance to a byte array.
/// </summary>
/// <param name="includeExifIdCode">Indicates, if the Exif ID code should be included.
/// This Exif ID code should not be included in case of PNG's. Defaults to true.</param>
/// The Exif Id Code is part of the JPEG APP1 segment. This Exif ID code should not be included in case of PNG's.
/// Defaults to true.</param>
/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray(bool includeExifIdCode = true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass through the code as a ReadonlySpan<bye> copying that to the output array.

# Conflicts:
#	tests/ImageSharp.Tests/Processing/Processors/Transforms/AutoOrientTests.cs
@JimBobSquarePants
Copy link
Member

@brianpopow Looks great to me 👍 thanks for persevering!

@dlemstra Would you be able to cast your eye over it just to be certain I haven't missed anything?

@brianpopow brianpopow changed the title [WIP] EXIF Support for PNG's EXIF Support for PNG's Jul 8, 2018
int bytesToWrite = remaining > MaxBytesApp1 ? MaxBytesApp1 : remaining;
int app1Length = bytesToWrite + 2;

// write the app1 header
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please cleanup these redundant comments? The method below the comment is self explanatory.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have removed the unnecessary comments

/// Extends the profile with additional data.
/// </summary>
/// <param name="bytes">The array containing addition profile data.</param>
public void Extend(byte[] bytes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this logic should be handled inside the ExifProfile class. It feels like this should be handled inside the decoder itself.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while this extending is also something jpeg specific and therefore should be handled by the decoder, i could not figure out a way to change it without making it more complicated. The problem is, that we do not know in advance how many APP1 marker there will be for very large EXIF data.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dlemstra: i have moved the extending of the exif data to the jpeg decoder now, please let me know if you think this is a viable solution.

/// <returns>The <see cref="T:byte[]"/></returns>
public byte[] ToByteArray()
public byte[] ToByteArray(ReadOnlySpan<byte> exifIdCode = default)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels a bit odd that we need to pass in the extra data. Can this not be handled inside the encoder/decoder?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes i think you are right, it feels a bit odd. This is jpeg specific and should be handled by the encoder/decoder

@@ -79,6 +79,7 @@ public List<ExifValue> ReadValues()

if (this.ReadString(4) == "Exif")
{
// two zeros are expected to follow the Exif Id code
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this comment?

@@ -639,7 +671,7 @@ private void WriteExifProfile(ExifProfile exifProfile)
/// </exception>
private void WriteIccProfile(IccProfile iccProfile)
{
// Just incase someone set the value to null by accident.
// Just in-case someone set the value to null by accident.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this comment instead? Not every JPEG file has an IccProfile.

@JimBobSquarePants
Copy link
Member

@brianpopow I'm going to have a look at this PR this evening and fix the merge conflicts and any final tweaks, thanks for being so patient with us and helping out! 👍

@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented Aug 4, 2018

Ok merging this now as it all looks good to me. Thanks @brianpopow for adding this, much appreciated! 👍

@JimBobSquarePants JimBobSquarePants merged commit 67b2e83 into SixLabors:master Aug 4, 2018
antonfirsov pushed a commit to antonfirsov/ImageSharp that referenced this pull request Nov 11, 2019
@brianpopow brianpopow deleted the feature/pngExif branch March 23, 2020 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants