Compare commits
2 Commits
190dda46d4
...
473fac7a6f
Author | SHA1 | Date | |
---|---|---|---|
473fac7a6f | |||
e5c1b01806 |
79
Program.cs
79
Program.cs
@ -185,6 +185,7 @@ public class Photo {
|
|||||||
public string IsoSpeed = "<unk>";
|
public string IsoSpeed = "<unk>";
|
||||||
public int Rating = 0;
|
public int Rating = 0;
|
||||||
public ushort Orientation = 1;
|
public ushort Orientation = 1;
|
||||||
|
public Rectangle CropRectangle = Rectangle.Empty;
|
||||||
|
|
||||||
private static long touchCounter = 0;
|
private static long touchCounter = 0;
|
||||||
private Texture texture;
|
private Texture texture;
|
||||||
@ -237,6 +238,9 @@ public class Photo {
|
|||||||
// FIXME: warn if the file already exists?
|
// FIXME: warn if the file already exists?
|
||||||
using (Image<Rgba32> image = await Image.LoadAsync<Rgba32>(Filename)) {
|
using (Image<Rgba32> image = await Image.LoadAsync<Rgba32>(Filename)) {
|
||||||
Util.RotateImageFromExif(image, Orientation);
|
Util.RotateImageFromExif(image, Orientation);
|
||||||
|
if (CropRectangle != Rectangle.Empty) {
|
||||||
|
image.Mutate(x => x.Crop(CropRectangle));
|
||||||
|
}
|
||||||
|
|
||||||
ExifProfile exif = image.Metadata.ExifProfile ?? new();
|
ExifProfile exif = image.Metadata.ExifProfile ?? new();
|
||||||
exif.SetValue<ushort>(ExifTag.Orientation, 1);
|
exif.SetValue<ushort>(ExifTag.Orientation, 1);
|
||||||
@ -504,7 +508,7 @@ public class UiGeometry {
|
|||||||
ThumbnailBoxes.Add(box);
|
ThumbnailBoxes.Add(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
int statusBoxHeight = 20;
|
int statusBoxHeight = 40;
|
||||||
int statusBoxPadding = 4;
|
int statusBoxPadding = 4;
|
||||||
PhotoBox = new Box2i(0, 0, WindowSize.X - thumbnailWidth, WindowSize.Y - statusBoxHeight - statusBoxPadding);
|
PhotoBox = new Box2i(0, 0, WindowSize.X - thumbnailWidth, WindowSize.Y - statusBoxHeight - statusBoxPadding);
|
||||||
StatusBox = new Box2i(0, WindowSize.Y - statusBoxHeight, WindowSize.X - thumbnailWidth, WindowSize.Y);
|
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 class Game : GameWindow {
|
||||||
public Game(GameWindowSettings gwSettings, NativeWindowSettings nwSettings) : base(gwSettings, nwSettings) {}
|
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_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 TEXTURE_BLACK = new(new Image<Rgba32>(1, 1, new Rgba32(0, 0, 0)));
|
||||||
private static Texture STAR_FILLED = Util.RenderStar(20, true);
|
private static Texture STAR_FILLED = Util.RenderStar(20, true);
|
||||||
@ -637,8 +644,11 @@ public class Game : GameWindow {
|
|||||||
readonly object loadedImagesLock = new();
|
readonly object loadedImagesLock = new();
|
||||||
int photoIndex = 0;
|
int photoIndex = 0;
|
||||||
int ribbonIndex = 0;
|
int ribbonIndex = 0;
|
||||||
|
Vector2i mousePosition;
|
||||||
Vector2i mouseDragStart;
|
Vector2i mouseDragStart;
|
||||||
Vector2i mouseDragEnd;
|
Vector2i mouseDragEnd;
|
||||||
|
float activeScale = 1f;
|
||||||
|
Vector2i activeOffset;
|
||||||
Shader shader = new();
|
Shader shader = new();
|
||||||
Matrix4 projection;
|
Matrix4 projection;
|
||||||
float zoomLevel = 0f;
|
float zoomLevel = 0f;
|
||||||
@ -661,7 +671,7 @@ public class Game : GameWindow {
|
|||||||
|
|
||||||
int lastPhotoIndex = photoIndex;
|
int lastPhotoIndex = photoIndex;
|
||||||
|
|
||||||
Vector2i mousePosition = (Vector2i) MouseState.Position;
|
mousePosition = (Vector2i) MouseState.Position;
|
||||||
|
|
||||||
// Look for mouse clicks on thumbnails or stars.
|
// Look for mouse clicks on thumbnails or stars.
|
||||||
//
|
//
|
||||||
@ -737,6 +747,11 @@ public class Game : GameWindow {
|
|||||||
photoIndex -= 5;
|
photoIndex -= 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: crop should be a modal tool that starts with C and ends with Enter or Escape.
|
||||||
|
if (input.IsKeyPressed(Keys.C)) {
|
||||||
|
ApplyCrop();
|
||||||
|
}
|
||||||
|
|
||||||
if (input.IsKeyPressed(Keys.P) && altIsDown) {
|
if (input.IsKeyPressed(Keys.P) && altIsDown) {
|
||||||
ExportPhotos();
|
ExportPhotos();
|
||||||
}
|
}
|
||||||
@ -964,8 +979,6 @@ 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\desktop\totte-output";
|
|
||||||
// string outputRoot = @"c:\users\colin\pictures\photos";
|
|
||||||
foreach (Photo p in photos) {
|
foreach (Photo p in photos) {
|
||||||
await Task.Run( () => { p.SaveAsJpegAsync(outputRoot, encoder); });
|
await Task.Run( () => { p.SaveAsJpegAsync(outputRoot, encoder); });
|
||||||
}
|
}
|
||||||
@ -993,7 +1006,11 @@ public class Game : GameWindow {
|
|||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawCropBox() {
|
// left, right, top, bottom
|
||||||
|
(int, int, int, int) GetCrop() {
|
||||||
|
// FIXME: this expects the start point in the top left and the end point
|
||||||
|
// in the bottom right; some sign flipping needs to occur to make anchors
|
||||||
|
// in other direction work well.
|
||||||
Vector2i start = mouseDragStart;
|
Vector2i start = mouseDragStart;
|
||||||
Vector2i end = mouseDragEnd;
|
Vector2i end = mouseDragEnd;
|
||||||
end.Y = Math.Min(end.Y, start.Y + (end.X - start.X) * 4 / 6);
|
end.Y = Math.Min(end.Y, start.Y + (end.X - start.X) * 4 / 6);
|
||||||
@ -1002,9 +1019,31 @@ public class Game : GameWindow {
|
|||||||
int right = Math.Max(start.X, end.X);
|
int right = Math.Max(start.X, end.X);
|
||||||
int top = Math.Min(start.Y, end.Y);
|
int top = Math.Min(start.Y, end.Y);
|
||||||
int bottom = Math.Max(start.Y, end.Y);
|
int bottom = Math.Max(start.Y, end.Y);
|
||||||
|
return (left, right, top, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyCrop() {
|
||||||
|
var (left, right, top, bottom) = GetCrop();
|
||||||
|
int area = (right - left) * (bottom - top);
|
||||||
|
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);
|
int area = (right - left) * (bottom - top);
|
||||||
|
|
||||||
if (area < 100) {
|
if (area == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Color4 shadeColor = new Color4(0, 0, 0, 0.75f);
|
Color4 shadeColor = new Color4(0, 0, 0, 0.75f);
|
||||||
@ -1032,10 +1071,12 @@ public class Game : GameWindow {
|
|||||||
if (zoomLevel > 0f) {
|
if (zoomLevel > 0f) {
|
||||||
scale = zoomLevel;
|
scale = zoomLevel;
|
||||||
}
|
}
|
||||||
|
activeScale = scale;
|
||||||
|
|
||||||
Vector2i renderSize = (Vector2i) (((Vector2) active.Size) * scale);
|
Vector2i renderSize = (Vector2i) (((Vector2) active.Size) * scale);
|
||||||
Vector2i center = (Vector2i) geometry.PhotoBox.Center;
|
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(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);
|
DrawTexture(active, photoBox);
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
Texture star = (activePhoto.Rating > i) ? STAR_FILLED : STAR_EMPTY;
|
Texture star = (activePhoto.Rating > i) ? STAR_FILLED : STAR_EMPTY;
|
||||||
@ -1064,13 +1105,29 @@ public class Game : GameWindow {
|
|||||||
// Draw status box.
|
// Draw status box.
|
||||||
int statusPadding = 2;
|
int statusPadding = 2;
|
||||||
DrawFilledBox(geometry.StatusBox, Color4.Black);
|
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);
|
// First line.
|
||||||
DrawText(activePhoto.Description(), geometry.StatusBox.Min.X + 160, geometry.StatusBox.Min.Y + statusPadding);
|
int y = geometry.StatusBox.Min.Y + statusPadding;
|
||||||
DrawText(String.Format("FPS: {0,2}", fpsCounter.Fps), geometry.StatusBox.Max.X - 66, 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) {
|
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) {
|
void DrawTexture(Texture texture, int x, int y) {
|
||||||
|
Loading…
Reference in New Issue
Block a user