save rating in XMP data. also change default window size

This commit is contained in:
Colin McMillen 2023-07-31 20:50:53 -04:00
parent 9e03d8f2c5
commit 58a19d061d

View File

@ -15,6 +15,7 @@ using SixLabors.ImageSharp.Formats.Jpeg;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
namespace SemiColinGames; namespace SemiColinGames;
@ -199,8 +200,8 @@ public class Photo {
DateTimeOriginal = creationTime; DateTimeOriginal = creationTime;
ImageInfo info = Image.Identify(filename); ImageInfo info = Image.Identify(filename);
Size = new(info.Size.Width, info.Size.Height); Size = new(info.Size.Width, info.Size.Height);
Rating = ParseRating(info.Metadata.XmpProfile);
ParseExif(info.Metadata.ExifProfile); ParseExif(info.Metadata.ExifProfile);
TryParseRating(info.Metadata.XmpProfile, out Rating);
} }
public async void LoadAsync() { public async void LoadAsync() {
@ -221,7 +222,7 @@ public class Photo {
} }
} }
public async void SaveAsJpeg(string outputRoot, JpegEncoder encoder) { public async void SaveAsJpegAsync(string outputRoot, JpegEncoder encoder) {
// FIXME: if nothing was changed about this image, just copy the file bytes directly, possibly with metadata changed? // FIXME: if nothing was changed about this image, just copy the file bytes directly, possibly with metadata changed?
string directory = System.IO.Path.Combine( string directory = System.IO.Path.Combine(
outputRoot, outputRoot,
@ -231,7 +232,7 @@ public class Photo {
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
string filename = System.IO.Path.Combine(directory, System.IO.Path.GetFileName(Filename)); string filename = System.IO.Path.Combine(directory, System.IO.Path.GetFileName(Filename));
Console.WriteLine("saving " + filename); Console.WriteLine("saving " + filename);
// FIXME: update Rating data. // FIXME: what if we also saved Exif rating?
// FIXME: add comments / captions as ImageDescription? // FIXME: add comments / captions as ImageDescription?
// FIXME: strip some Exif tags for privacy reasons? // FIXME: strip some Exif tags for privacy reasons?
// FIXME: warn if the file already exists? // FIXME: warn if the file already exists?
@ -249,31 +250,50 @@ public class Photo {
now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second);
exif.SetValue<string>(ExifTag.DateTime, datetime); exif.SetValue<string>(ExifTag.DateTime, datetime);
image.Metadata.XmpProfile = UpdateXmp(image.Metadata.XmpProfile);
await image.SaveAsync(filename, encoder); await image.SaveAsync(filename, encoder);
} }
} }
private bool TryParseRating(XmpProfile? xmp, out int rating) { private XElement? GetXmpRoot(XmpProfile? xmp) {
rating = 0;
if (xmp == null) { if (xmp == null) {
return false; return null;
} }
XDocument? doc = xmp.GetDocument(); XDocument? doc = xmp.GetDocument();
if (doc == null) { if (doc == null) {
return false; return null;
} }
XElement? root = doc.Root; return doc.Root;
}
private int ParseRating(XmpProfile? xmp) {
XElement? root = GetXmpRoot(xmp);
if (root == null) { if (root == null) {
return false; return 0;
} }
foreach (XElement elt in root.Descendants()) { foreach (XElement elt in root.Descendants()) {
if (elt.Name == "{http://ns.adobe.com/xap/1.0/}Rating") { if (elt.Name == "{http://ns.adobe.com/xap/1.0/}Rating") {
int rating = 0;
if (int.TryParse(elt.Value, out rating)) { if (int.TryParse(elt.Value, out rating)) {
return true; return rating;
} }
} }
} }
return false; return 0;
}
private XmpProfile? UpdateXmp(XmpProfile? xmp) {
if (xmp == null) {
return null;
}
string xmlIn = Encoding.UTF8.GetString(xmp.ToByteArray());
int index = xmlIn.IndexOf("</xmp:Rating>");
if (index == -1) {
return xmp;
}
string xmlOut = xmlIn.Substring(0, index - 1) + Rating.ToString() + xmlIn.Substring(index);
return new XmpProfile(Encoding.UTF8.GetBytes(xmlOut));
} }
// Exif (and other image metadata) reference, from the now-defunct Metadata Working Group: // Exif (and other image metadata) reference, from the now-defunct Metadata Working Group:
@ -627,6 +647,7 @@ public class Game : GameWindow {
bool ctrlIsDown = input.IsKeyDown(Keys.LeftControl) || input.IsKeyDown(Keys.RightControl); bool ctrlIsDown = input.IsKeyDown(Keys.LeftControl) || input.IsKeyDown(Keys.RightControl);
// FIXME: add a confirm dialog before closing. (Also for the window-close button.) // FIXME: add a confirm dialog before closing. (Also for the window-close button.)
// FIXME: don't quit if there's pending file-write operations.
// Close when Escape is pressed. // Close when Escape is pressed.
if (input.IsKeyPressed(Keys.Escape)) { if (input.IsKeyPressed(Keys.Escape)) {
Close(); Close();
@ -826,7 +847,7 @@ public class Game : GameWindow {
// string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\"); // string[] files = Directory.GetFiles(@"c:\users\colin\desktop\photos-test\");
// string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\"); // string[] files = Directory.GetFiles(@"c:\users\colin\pictures\photos\2023\07\14\");
string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\"); string[] files = Directory.GetFiles(@"G:\DCIM\100EOSR6\");
// string[] files = Directory.GetFiles(@"c:\users\colin\desktop\totte-output\2023\07\28"); // string[] files = Directory.GetFiles(@"c:\users\colin\desktop\totte-output\2023\07\31");
// string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Pictures\photos\2018\06\23");
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\Germany all\104D7000");
// string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\many-birds\"); // string[] files = Directory.GetFiles(@"C:\Users\colin\Desktop\many-birds\");
@ -913,9 +934,10 @@ public class Game : GameWindow {
// FIXME: show a progress bar or something. // FIXME: show a progress bar or something.
private async void ExportPhotos() { private async void ExportPhotos() {
JpegEncoder encoder = new JpegEncoder() { Quality = 100 }; JpegEncoder encoder = new JpegEncoder() { Quality = 100 };
string outputRoot = @"c:\users\colin\pictures\photos\"; string outputRoot = @"c:\users\colin\desktop\totte-output";
// string outputRoot = @"c:\users\colin\pictures\photos";
foreach (Photo p in photos) { foreach (Photo p in photos) {
await Task.Run( () => { p.SaveAsJpeg(outputRoot, encoder); }); await Task.Run( () => { p.SaveAsJpegAsync(outputRoot, encoder); });
} }
} }
@ -1088,7 +1110,8 @@ static class Program {
nwSettings.WindowState = WindowState.Normal; nwSettings.WindowState = WindowState.Normal;
nwSettings.CurrentMonitor = bestMonitor.Handle; nwSettings.CurrentMonitor = bestMonitor.Handle;
nwSettings.Location = new Vector2i(bestMonitor.WorkArea.Min.X + 1, bestMonitor.WorkArea.Min.Y + 31); nwSettings.Location = new Vector2i(bestMonitor.WorkArea.Min.X + 1, bestMonitor.WorkArea.Min.Y + 31);
nwSettings.Size = new Vector2i(bestMonitor.WorkArea.Size.X - 2, bestMonitor.WorkArea.Size.Y - 32); // nwSettings.Size = new Vector2i(bestMonitor.WorkArea.Size.X - 2, bestMonitor.WorkArea.Size.Y - 32);
nwSettings.Size = new Vector2i(1600, 900);
nwSettings.MinimumSize = UiGeometry.MIN_WINDOW_SIZE; nwSettings.MinimumSize = UiGeometry.MIN_WINDOW_SIZE;
nwSettings.Title = "Totte"; nwSettings.Title = "Totte";
nwSettings.IsEventDriven = false; nwSettings.IsEventDriven = false;