-
-
Notifications
You must be signed in to change notification settings - Fork 852
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
EXIF Support for PNG's #616
Conversation
…er reloading it, if the profile is still there (SixLabors#611)
… the ExifChunk - changed the ExifWriter GetData, to support the special case for PNG's not inlcluding the 'Exif' Code (SixLabors#611)
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
I'll leave this one for @dlemstra to review but I'm thinking we should make the |
/// <returns>The <see cref="T:byte[]"/></returns> | ||
public byte[] ToByteArray() | ||
public byte[] ToByteArray(bool includeExifIdCode = true) | ||
{ | ||
if (this.values == null) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 }; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
/// <returns>The <see cref="T:byte[]"/></returns> | ||
public byte[] ToByteArray() | ||
public byte[] ToByteArray(bool includeExifIdCode = true) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic, thanks!
# Conflicts: # src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
…er defined in Jpeg ProfileResolver
…rom one exifprofile to another
…r FontShapesAreRenderedCorrectlyAlongACirclePath"
@@ -5,6 +5,7 @@ | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
@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? |
int bytesToWrite = remaining > MaxBytesApp1 ? MaxBytesApp1 : remaining; | ||
int app1Length = bytesToWrite + 2; | ||
|
||
// write the app1 header |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
…is is jpeg specific and should be handled by the jpeg encoder
…e will be skipped when setting the ExifProfile
@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! 👍 |
Ok merging this now as it all looks good to me. Thanks @brianpopow for adding this, much appreciated! 👍 |
EXIF Support for PNG's
Prerequisites
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.