load thumbnails for everything, async'ly on load

This commit is contained in:
Colin McMillen 2023-08-24 22:36:52 -04:00
parent cbea050334
commit 50b0e8d8e0
2 changed files with 31 additions and 12 deletions

View File

@ -29,12 +29,15 @@ public class Photo {
private static long touchCounter = 0;
private Texture texture;
private Texture placeholder;
private Texture thumbnailTexture;
private Image<Rgba32>? image = null;
private Image<Rgba32>? thumbnail = null;
public Photo(string filename, Texture placeholder) {
Filename = filename;
this.placeholder = placeholder;
texture = placeholder;
thumbnailTexture = placeholder;
DateTime creationTime = File.GetCreationTime(filename); // Local time.
DateTimeOriginal = creationTime;
@ -49,12 +52,18 @@ public class Photo {
// edit the image due to rotation (etc) and don't want to try generating
// a texture for it until that's already happened.
LastTouch = touchCounter++;
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(Filename);
Util.RotateImageFromExif(tmp, Orientation);
image = tmp;
}
public async void LoadThumbnailAsync() {
DecoderOptions options = new DecoderOptions {
TargetSize = new Size(256, 256)
};
Image<Rgba32> tmp = await Image.LoadAsync<Rgba32>(options, Filename);
Util.RotateImageFromExif(tmp, Orientation);
image = tmp;
thumbnail = tmp;
}
public void Unload() {
@ -273,16 +282,21 @@ public class Photo {
public Texture Texture() {
LastTouch = touchCounter++;
if (thumbnailTexture == placeholder && thumbnail != null) {
thumbnailTexture = new(thumbnail);
thumbnail.Dispose();
thumbnail = null;
}
if (texture == placeholder && image != null) {
// 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
// image is ready to go.
texture = new Texture(image);
texture = new(image);
image.Dispose();
image = null;
Loaded = true;
}
return texture;
return texture != placeholder ? texture : thumbnailTexture;
}
public string Description() {

View File

@ -652,6 +652,8 @@ public class Game : GameWindow {
allPhotos.Sort(ComparePhotosByDate);
photos = allPhotos;
LoadThumbnailsAsync();
}
private static int ComparePhotosByDate(Photo x, Photo y) {
@ -669,11 +671,9 @@ public class Game : GameWindow {
}
private void UnloadImages() {
return;
// Unload images that haven't been touched in a while.
// FIXME: keep around thumbnail-sized textures?
lock (loadedImagesLock) {
while (loadedImages.Count > 100) {
while (loadedImages.Count > 30) {
long earliestTime = long.MaxValue;
Photo? earliest = null;
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.
int minLoadedImage = Math.Max(0, photoIndex - 30);
// int maxLoadedImage = Math.Min(photoIndex + 30, photos.Count - 1);
int maxLoadedImage = photos.Count - 1;
int minLoadedImage = Math.Max(0, photoIndex - 10);
int maxLoadedImage = Math.Min(photoIndex + 10, photos.Count - 1);
List<Photo> toLoad = new();
for (int i = minLoadedImage; i <= maxLoadedImage; i++) {
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:
// $ identify -verbose image.jpg | grep Quality:
// FIXME: don't ExportPhotos() if another export is already active.
@ -988,9 +993,9 @@ static class Program {
nwSettings.CurrentMonitor = bestMonitor.Handle;
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(1600, 900);
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.Title = "Totte";
nwSettings.IsEventDriven = true;