save rating in XMP data. also change default window size
This commit is contained in:
parent
9e03d8f2c5
commit
58a19d061d
53
Program.cs
53
Program.cs
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user