load thumbnails for everything, async'ly on load
This commit is contained in:
parent
cbea050334
commit
50b0e8d8e0
20
Photo.cs
20
Photo.cs
@ -29,12 +29,15 @@ public class Photo {
|
|||||||
private static long touchCounter = 0;
|
private static long touchCounter = 0;
|
||||||
private Texture texture;
|
private Texture texture;
|
||||||
private Texture placeholder;
|
private Texture placeholder;
|
||||||
|
private Texture thumbnailTexture;
|
||||||
private Image<Rgba32>? image = null;
|
private Image<Rgba32>? image = null;
|
||||||
|
private Image<Rgba32>? thumbnail = null;
|
||||||
|
|
||||||
public Photo(string filename, Texture placeholder) {
|
public Photo(string filename, Texture placeholder) {
|
||||||
Filename = filename;
|
Filename = filename;
|
||||||
this.placeholder = placeholder;
|
this.placeholder = placeholder;
|
||||||
texture = placeholder;
|
texture = placeholder;
|
||||||
|
thumbnailTexture = placeholder;
|
||||||
|
|
||||||
DateTime creationTime = File.GetCreationTime(filename); // Local time.
|
DateTime creationTime = File.GetCreationTime(filename); // Local time.
|
||||||
DateTimeOriginal = creationTime;
|
DateTimeOriginal = creationTime;
|
||||||
@ -49,12 +52,18 @@ public class Photo {
|
|||||||
// edit the image due to rotation (etc) and don't want to try generating
|
// edit the image due to rotation (etc) and don't want to try generating
|
||||||
// a texture for it until that's already happened.
|
// a texture for it until that's already happened.
|
||||||
LastTouch = touchCounter++;
|
LastTouch = touchCounter++;
|
||||||
|
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(Filename);
|
||||||
|
Util.RotateImageFromExif(tmp, Orientation);
|
||||||
|
image = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void LoadThumbnailAsync() {
|
||||||
DecoderOptions options = new DecoderOptions {
|
DecoderOptions options = new DecoderOptions {
|
||||||
TargetSize = new Size(256, 256)
|
TargetSize = new Size(256, 256)
|
||||||
};
|
};
|
||||||
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(options, Filename);
|
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(options, Filename);
|
||||||
Util.RotateImageFromExif(tmp, Orientation);
|
Util.RotateImageFromExif(tmp, Orientation);
|
||||||
image = tmp;
|
thumbnail = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unload() {
|
public void Unload() {
|
||||||
@ -273,16 +282,21 @@ public class Photo {
|
|||||||
|
|
||||||
public Texture Texture() {
|
public Texture Texture() {
|
||||||
LastTouch = touchCounter++;
|
LastTouch = touchCounter++;
|
||||||
|
if (thumbnailTexture == placeholder && thumbnail != null) {
|
||||||
|
thumbnailTexture = new(thumbnail);
|
||||||
|
thumbnail.Dispose();
|
||||||
|
thumbnail = null;
|
||||||
|
}
|
||||||
if (texture == placeholder && image != null) {
|
if (texture == placeholder && image != null) {
|
||||||
// The texture needs to be created on the GL thread, so we instantiate
|
// The texture needs to be created on the GL thread, so we instantiate
|
||||||
// it here (since this is called from OnRenderFrame), as long as the
|
// it here (since this is called from OnRenderFrame), as long as the
|
||||||
// image is ready to go.
|
// image is ready to go.
|
||||||
texture = new Texture(image);
|
texture = new(image);
|
||||||
image.Dispose();
|
image.Dispose();
|
||||||
image = null;
|
image = null;
|
||||||
Loaded = true;
|
Loaded = true;
|
||||||
}
|
}
|
||||||
return texture;
|
return texture != placeholder ? texture : thumbnailTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Description() {
|
public string Description() {
|
||||||
|
23
Program.cs
23
Program.cs
@ -652,6 +652,8 @@ public class Game : GameWindow {
|
|||||||
|
|
||||||
allPhotos.Sort(ComparePhotosByDate);
|
allPhotos.Sort(ComparePhotosByDate);
|
||||||
photos = allPhotos;
|
photos = allPhotos;
|
||||||
|
|
||||||
|
LoadThumbnailsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int ComparePhotosByDate(Photo x, Photo y) {
|
private static int ComparePhotosByDate(Photo x, Photo y) {
|
||||||
@ -669,11 +671,9 @@ public class Game : GameWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void UnloadImages() {
|
private void UnloadImages() {
|
||||||
return;
|
|
||||||
// Unload images that haven't been touched in a while.
|
// Unload images that haven't been touched in a while.
|
||||||
// FIXME: keep around thumbnail-sized textures?
|
|
||||||
lock (loadedImagesLock) {
|
lock (loadedImagesLock) {
|
||||||
while (loadedImages.Count > 100) {
|
while (loadedImages.Count > 30) {
|
||||||
long earliestTime = long.MaxValue;
|
long earliestTime = long.MaxValue;
|
||||||
Photo? earliest = null;
|
Photo? earliest = null;
|
||||||
foreach (Photo photo in loadedImages) {
|
foreach (Photo photo in loadedImages) {
|
||||||
@ -704,9 +704,8 @@ public class Game : GameWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Start loading any images that are in our window but not yet loaded.
|
// Start loading any images that are in our window but not yet loaded.
|
||||||
int minLoadedImage = Math.Max(0, photoIndex - 30);
|
int minLoadedImage = Math.Max(0, photoIndex - 10);
|
||||||
// int maxLoadedImage = Math.Min(photoIndex + 30, photos.Count - 1);
|
int maxLoadedImage = Math.Min(photoIndex + 10, photos.Count - 1);
|
||||||
int maxLoadedImage = photos.Count - 1;
|
|
||||||
List<Photo> toLoad = new();
|
List<Photo> toLoad = new();
|
||||||
for (int i = minLoadedImage; i <= maxLoadedImage; i++) {
|
for (int i = minLoadedImage; i <= maxLoadedImage; i++) {
|
||||||
lock (loadedImagesLock) {
|
lock (loadedImagesLock) {
|
||||||
@ -722,6 +721,12 @@ public class Game : GameWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void LoadThumbnailsAsync() {
|
||||||
|
foreach (Photo p in allPhotos) {
|
||||||
|
await Task.Run( () => { p.LoadThumbnailAsync(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// To find the JPEG compression level of a file from the command line:
|
// To find the JPEG compression level of a file from the command line:
|
||||||
// $ identify -verbose image.jpg | grep Quality:
|
// $ identify -verbose image.jpg | grep Quality:
|
||||||
// FIXME: don't ExportPhotos() if another export is already active.
|
// FIXME: don't ExportPhotos() if another export is already active.
|
||||||
@ -988,9 +993,9 @@ static class Program {
|
|||||||
nwSettings.CurrentMonitor = bestMonitor.Handle;
|
nwSettings.CurrentMonitor = bestMonitor.Handle;
|
||||||
nwSettings.Location = new Vector2i(bestMonitor.WorkArea.Min.X + 1,
|
nwSettings.Location = new Vector2i(bestMonitor.WorkArea.Min.X + 1,
|
||||||
bestMonitor.WorkArea.Min.Y + 31);
|
bestMonitor.WorkArea.Min.Y + 31);
|
||||||
// nwSettings.Size = new Vector2i(bestMonitor.WorkArea.Size.X - 2,
|
nwSettings.Size = new Vector2i(bestMonitor.WorkArea.Size.X - 2,
|
||||||
// bestMonitor.WorkArea.Size.Y - 32);
|
bestMonitor.WorkArea.Size.Y - 32);
|
||||||
nwSettings.Size = new Vector2i(1600, 900);
|
// 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 = true;
|
nwSettings.IsEventDriven = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user