|
|
@ -185,6 +185,7 @@ public class Photo { |
|
|
|
public string IsoSpeed = "<unk>"; |
|
|
|
public int Rating = 0; |
|
|
|
public ushort Orientation = 1; |
|
|
|
public Rectangle CropRectangle = Rectangle.Empty; |
|
|
|
|
|
|
|
private static long touchCounter = 0; |
|
|
|
private Texture texture; |
|
|
@ -237,6 +238,9 @@ public class Photo { |
|
|
|
// FIXME: warn if the file already exists?
|
|
|
|
using (Image<Rgba32> image = await Image.LoadAsync<Rgba32>(Filename)) { |
|
|
|
Util.RotateImageFromExif(image, Orientation); |
|
|
|
if (CropRectangle != Rectangle.Empty) { |
|
|
|
image.Mutate(x => x.Crop(CropRectangle)); |
|
|
|
} |
|
|
|
|
|
|
|
ExifProfile exif = image.Metadata.ExifProfile ?? new(); |
|
|
|
exif.SetValue<ushort>(ExifTag.Orientation, 1); |
|
|
@ -504,7 +508,7 @@ public class UiGeometry { |
|
|
|
ThumbnailBoxes.Add(box); |
|
|
|
} |
|
|
|
|
|
|
|
int statusBoxHeight = 20; |
|
|
|
int statusBoxHeight = 40; |
|
|
|
int statusBoxPadding = 4; |
|
|
|
PhotoBox = new Box2i(0, 0, WindowSize.X - thumbnailWidth, WindowSize.Y - statusBoxHeight - statusBoxPadding); |
|
|
|
StatusBox = new Box2i(0, WindowSize.Y - statusBoxHeight, WindowSize.X - thumbnailWidth, WindowSize.Y); |
|
|
@ -609,6 +613,9 @@ public static class Util { |
|
|
|
public class Game : GameWindow { |
|
|
|
public Game(GameWindowSettings gwSettings, NativeWindowSettings nwSettings) : base(gwSettings, nwSettings) {} |
|
|
|
|
|
|
|
private static string outputRoot = @"c:\users\colin\desktop\totte-output"; |
|
|
|
// private static string outputRoot = @"c:\users\colin\pictures\photos";
|
|
|
|
|
|
|
|
private static Texture TEXTURE_WHITE = new(new Image<Rgba32>(1, 1, new Rgba32(255, 255, 255))); |
|
|
|
private static Texture TEXTURE_BLACK = new(new Image<Rgba32>(1, 1, new Rgba32(0, 0, 0))); |
|
|
|
private static Texture STAR_FILLED = Util.RenderStar(20, true); |
|
|
@ -637,9 +644,11 @@ public class Game : GameWindow { |
|
|
|
readonly object loadedImagesLock = new(); |
|
|
|
int photoIndex = 0; |
|
|
|
int ribbonIndex = 0; |
|
|
|
Vector2i mousePosition; |
|
|
|
Vector2i mouseDragStart; |
|
|
|
Vector2i mouseDragEnd; |
|
|
|
float activeScale = 1f; |
|
|
|
Vector2i activeOffset; |
|
|
|
Shader shader = new(); |
|
|
|
Matrix4 projection; |
|
|
|
float zoomLevel = 0f; |
|
|
@ -662,7 +671,7 @@ public class Game : GameWindow { |
|
|
|
|
|
|
|
int lastPhotoIndex = photoIndex; |
|
|
|
|
|
|
|
Vector2i mousePosition = (Vector2i) MouseState.Position; |
|
|
|
mousePosition = (Vector2i) MouseState.Position; |
|
|
|
|
|
|
|
// Look for mouse clicks on thumbnails or stars.
|
|
|
|
//
|
|
|
@ -970,8 +979,6 @@ public class Game : GameWindow { |
|
|
|
// FIXME: show a progress bar or something.
|
|
|
|
private async void ExportPhotos() { |
|
|
|
JpegEncoder encoder = new JpegEncoder() { Quality = 100 }; |
|
|
|
string outputRoot = @"c:\users\colin\desktop\totte-output"; |
|
|
|
// string outputRoot = @"c:\users\colin\pictures\photos";
|
|
|
|
foreach (Photo p in photos) { |
|
|
|
await Task.Run( () => { p.SaveAsJpegAsync(outputRoot, encoder); }); |
|
|
|
} |
|
|
@ -1018,17 +1025,25 @@ public class Game : GameWindow { |
|
|
|
void ApplyCrop() { |
|
|
|
var (left, right, top, bottom) = GetCrop(); |
|
|
|
int area = (right - left) * (bottom - top); |
|
|
|
if (area < 100) { |
|
|
|
if (area == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
Vector2i leftTop = ScreenToImage(left, top); |
|
|
|
Vector2i rightBottom = ScreenToImage(right, bottom); |
|
|
|
Rectangle crop = Rectangle.FromLTRB(leftTop.X, leftTop.Y, rightBottom.X, rightBottom.Y); |
|
|
|
Photo photo = photos[photoIndex]; |
|
|
|
// FIXME: make sure this doesn't exceed image.Bounds.
|
|
|
|
// FIXME: once set, display it properly in the PhotoBox.
|
|
|
|
photo.CropRectangle = crop; |
|
|
|
Console.WriteLine(crop); |
|
|
|
} |
|
|
|
|
|
|
|
void DrawCropBox() { |
|
|
|
var (left, right, top, bottom) = GetCrop(); |
|
|
|
int area = (right - left) * (bottom - top); |
|
|
|
|
|
|
|
if (area < 100) { |
|
|
|
if (area == 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
Color4 shadeColor = new Color4(0, 0, 0, 0.75f); |
|
|
@ -1060,8 +1075,8 @@ public class Game : GameWindow { |
|
|
|
|
|
|
|
Vector2i renderSize = (Vector2i) (((Vector2) active.Size) * scale); |
|
|
|
Vector2i center = (Vector2i) geometry.PhotoBox.Center; |
|
|
|
// Box2i photoBox = Util.MakeBox(center.X - renderSize.X / 2, center.Y - renderSize.Y / 2, renderSize.X, renderSize.Y);
|
|
|
|
Box2i photoBox = Util.MakeBox(0, 0, renderSize.X, renderSize.Y); |
|
|
|
Box2i photoBox = Util.MakeBox(center.X - renderSize.X / 2, center.Y - renderSize.Y / 2, renderSize.X, renderSize.Y); |
|
|
|
activeOffset = new(photoBox.Min.X, photoBox.Min.Y); |
|
|
|
DrawTexture(active, photoBox); |
|
|
|
for (int i = 0; i < 5; i++) { |
|
|
|
Texture star = (activePhoto.Rating > i) ? STAR_FILLED : STAR_EMPTY; |
|
|
@ -1090,13 +1105,29 @@ public class Game : GameWindow { |
|
|
|
// Draw status box.
|
|
|
|
int statusPadding = 2; |
|
|
|
DrawFilledBox(geometry.StatusBox, Color4.Black); |
|
|
|
DrawText(String.Format("{0,4}/{1,-4}", photoIndex + 1, photos.Count), geometry.StatusBox.Min.X + 72, geometry.StatusBox.Min.Y + statusPadding); |
|
|
|
DrawText(activePhoto.Description(), geometry.StatusBox.Min.X + 160, geometry.StatusBox.Min.Y + statusPadding); |
|
|
|
DrawText(String.Format("FPS: {0,2}", fpsCounter.Fps), geometry.StatusBox.Max.X - 66, geometry.StatusBox.Min.Y + statusPadding); |
|
|
|
|
|
|
|
// First line.
|
|
|
|
int y = geometry.StatusBox.Min.Y + statusPadding; |
|
|
|
DrawText(activePhoto.Description(), geometry.StatusBox.Min.X, y); |
|
|
|
// Second line.
|
|
|
|
y += 20; |
|
|
|
DrawText(String.Format("{0,4}/{1,-4}", photoIndex + 1, photos.Count), geometry.StatusBox.Min.X + 72, y); |
|
|
|
DrawText(String.Format("FPS: {0,2}", fpsCounter.Fps), geometry.StatusBox.Max.X - 66, y); |
|
|
|
if (activePhoto.Loaded) { |
|
|
|
DrawText($"{(scale * 100):F1}%", geometry.StatusBox.Min.X, geometry.StatusBox.Min.Y + statusPadding); |
|
|
|
DrawText($"{(scale * 100):F1}%", geometry.StatusBox.Min.X, y); |
|
|
|
} |
|
|
|
DrawText($"({mousePosition.X}, {mousePosition.Y})", geometry.StatusBox.Min.X + 160, y); |
|
|
|
Vector2i imagePosition = ScreenToImage(mousePosition); |
|
|
|
DrawText($"({imagePosition.X}, {imagePosition.Y})", geometry.StatusBox.Min.X + 320, y); |
|
|
|
} |
|
|
|
|
|
|
|
Vector2i ScreenToImage(int x, int y) { |
|
|
|
return new( |
|
|
|
(int) ((x - activeOffset.X) / activeScale), |
|
|
|
(int) ((y - activeOffset.Y) / activeScale)); |
|
|
|
} |
|
|
|
|
|
|
|
Vector2i ScreenToImage(Vector2i position) { |
|
|
|
return ScreenToImage(position.X, position.Y); |
|
|
|
} |
|
|
|
|
|
|
|
void DrawTexture(Texture texture, int x, int y) { |
|
|
|