Skip to content

Commit 70e9b9a

Browse files
authored
Merge pull request #427 from ionite34/fast-png
Fast png
2 parents bd9abd7 + 90932c4 commit 70e9b9a

File tree

5 files changed

+182
-102
lines changed

5 files changed

+182
-102
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2
88
## v2.7.5
99
### Fixed
1010
- Fixed Python Packages manager crash when pip list returns warnings in json
11+
- Fixed slowdown when loading PNGs with large amounts of metadata
12+
- Fixed crash when scanning directories for missing metadata
1113

1214
## v2.7.4
1315
### Changed

StabilityMatrix.Avalonia/FallbackRamCachedWebImageLoader.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using AsyncImageLoader.Loaders;
88
using Avalonia.Media.Imaging;
99
using StabilityMatrix.Core.Extensions;
10+
using StabilityMatrix.Core.Helper;
1011

1112
namespace StabilityMatrix.Avalonia;
1213

@@ -42,7 +43,11 @@ protected void OnLoadFailed(string url, Exception exception) =>
4243
{
4344
try
4445
{
45-
return new Bitmap(url);
46+
if (!url.EndsWith("png", StringComparison.OrdinalIgnoreCase))
47+
return new Bitmap(url);
48+
49+
using var stream = ImageMetadata.BuildImageWithoutMetadata(url);
50+
return stream == null ? new Bitmap(url) : new Bitmap(stream);
4651
}
4752
catch (Exception e)
4853
{

StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs

+28-40
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,9 @@ public partial class OutputsPageViewModel : PageViewModelBase
5454
private readonly ILogger<OutputsPageViewModel> logger;
5555
public override string Title => Resources.Label_OutputsPageTitle;
5656

57-
public override IconSource IconSource =>
58-
new SymbolIconSource { Symbol = Symbol.Grid, IsFilled = true };
57+
public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Grid, IsFilled = true };
5958

60-
public SourceCache<LocalImageFile, string> OutputsCache { get; } =
61-
new(file => file.AbsolutePath);
59+
public SourceCache<LocalImageFile, string> OutputsCache { get; } = new(file => file.AbsolutePath);
6260

6361
public IObservableCollection<OutputImageViewModel> Outputs { get; set; } =
6462
new ObservableCollectionExtended<OutputImageViewModel>();
@@ -88,8 +86,7 @@ public partial class OutputsPageViewModel : PageViewModelBase
8886
[ObservableProperty]
8987
private bool isConsolidating;
9088

91-
public bool CanShowOutputTypes =>
92-
SelectedCategory?.Name?.Equals("Shared Output Folder") ?? false;
89+
public bool CanShowOutputTypes => SelectedCategory?.Name?.Equals("Shared Output Folder") ?? false;
9390

9491
public string NumImagesSelected =>
9592
NumItemsSelected == 1
@@ -138,7 +135,7 @@ ILogger<OutputsPageViewModel> logger
138135
delay: TimeSpan.FromMilliseconds(250)
139136
);
140137

141-
RefreshCategories();
138+
RefreshCategories(false);
142139
}
143140

144141
public override void OnLoaded()
@@ -163,10 +160,7 @@ public override void OnLoaded()
163160
GetOutputs(path);
164161
}
165162

166-
partial void OnSelectedCategoryChanged(
167-
PackageOutputCategory? oldValue,
168-
PackageOutputCategory? newValue
169-
)
163+
partial void OnSelectedCategoryChanged(PackageOutputCategory? oldValue, PackageOutputCategory? newValue)
170164
{
171165
if (oldValue == newValue || newValue == null)
172166
return;
@@ -223,8 +217,8 @@ public async Task ShowImageDialog(OutputImageViewModel item)
223217
)
224218
.Subscribe(ctx =>
225219
{
226-
Dispatcher.UIThread
227-
.InvokeAsync(async () =>
220+
Dispatcher
221+
.UIThread.InvokeAsync(async () =>
228222
{
229223
var sender = (ImageViewerViewModel)ctx.Sender!;
230224
var newIndex = currentIndex + (ctx.EventArgs.IsNext ? 1 : -1);
@@ -261,7 +255,7 @@ public Task CopyImage(string imagePath)
261255

262256
public void Refresh()
263257
{
264-
Dispatcher.UIThread.Post(RefreshCategories);
258+
Dispatcher.UIThread.Post(() => RefreshCategories());
265259
Dispatcher.UIThread.Post(OnLoaded);
266260
}
267261

@@ -430,9 +424,7 @@ public async Task ConsolidateImages()
430424

431425
Directory.CreateDirectory(settingsManager.ConsolidatedImagesDirectory);
432426

433-
foreach (
434-
var category in stackPanel.Children.OfType<CheckBox>().Where(c => c.IsChecked == true)
435-
)
427+
foreach (var category in stackPanel.Children.OfType<CheckBox>().Where(c => c.IsChecked == true))
436428
{
437429
if (
438430
string.IsNullOrWhiteSpace(category.Tag?.ToString())
@@ -442,13 +434,7 @@ var category in stackPanel.Children.OfType<CheckBox>().Where(c => c.IsChecked ==
442434

443435
var directory = category.Tag.ToString();
444436

445-
foreach (
446-
var path in Directory.EnumerateFiles(
447-
directory,
448-
"*.png",
449-
SearchOption.AllDirectories
450-
)
451-
)
437+
foreach (var path in Directory.EnumerateFiles(directory, "*.png", SearchOption.AllDirectories))
452438
{
453439
try
454440
{
@@ -524,7 +510,7 @@ private void GetOutputs(string directory)
524510
}
525511
}
526512

527-
private void RefreshCategories()
513+
private void RefreshCategories(bool updateProperty = true)
528514
{
529515
if (Design.IsDesignMode)
530516
return;
@@ -534,15 +520,11 @@ private void RefreshCategories()
534520

535521
var previouslySelectedCategory = SelectedCategory;
536522

537-
var packageCategories = settingsManager.Settings.InstalledPackages
538-
.Where(x => !x.UseSharedOutputFolder)
523+
var packageCategories = settingsManager
524+
.Settings.InstalledPackages.Where(x => !x.UseSharedOutputFolder)
539525
.Select(packageFactory.GetPackagePair)
540526
.WhereNotNull()
541-
.Where(
542-
p =>
543-
p.BasePackage.SharedOutputFolders != null
544-
&& p.BasePackage.SharedOutputFolders.Any()
545-
)
527+
.Where(p => p.BasePackage.SharedOutputFolders != null && p.BasePackage.SharedOutputFolders.Any())
546528
.Select(
547529
pair =>
548530
new PackageOutputCategory
@@ -567,16 +549,22 @@ private void RefreshCategories()
567549

568550
packageCategories.Insert(
569551
1,
570-
new PackageOutputCategory
571-
{
572-
Path = settingsManager.ImagesInferenceDirectory,
573-
Name = "Inference"
574-
}
552+
new PackageOutputCategory { Path = settingsManager.ImagesInferenceDirectory, Name = "Inference" }
575553
);
576554

577555
Categories = new ObservableCollection<PackageOutputCategory>(packageCategories);
578-
SelectedCategory =
579-
Categories.FirstOrDefault(x => x.Name == previouslySelectedCategory?.Name)
580-
?? Categories.First();
556+
557+
if (updateProperty)
558+
{
559+
SelectedCategory =
560+
Categories.FirstOrDefault(x => x.Name == previouslySelectedCategory?.Name)
561+
?? Categories.First();
562+
}
563+
else
564+
{
565+
selectedCategory =
566+
Categories.FirstOrDefault(x => x.Name == previouslySelectedCategory?.Name)
567+
?? Categories.First();
568+
}
581569
}
582570
}

StabilityMatrix.Core/Helper/ImageMetadata.cs

+48
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,52 @@ public static string ReadTextChunk(BinaryReader byteStream, string key)
179179

180180
return string.Empty;
181181
}
182+
183+
public static MemoryStream? BuildImageWithoutMetadata(FilePath imagePath)
184+
{
185+
using var byteStream = new BinaryReader(File.OpenRead(imagePath));
186+
byteStream.BaseStream.Position = 0;
187+
188+
if (!byteStream.ReadBytes(8).SequenceEqual(PngHeader))
189+
{
190+
return null;
191+
}
192+
193+
var memoryStream = new MemoryStream();
194+
memoryStream.Write(PngHeader);
195+
196+
// add the IHDR chunk
197+
var ihdrStuff = byteStream.ReadBytes(25);
198+
memoryStream.Write(ihdrStuff);
199+
200+
// find IDATs
201+
while (byteStream.BaseStream.Position < byteStream.BaseStream.Length - 4)
202+
{
203+
var chunkSizeBytes = byteStream.ReadBytes(4);
204+
var chunkSize = BitConverter.ToInt32(chunkSizeBytes.Reverse().ToArray());
205+
var chunkTypeBytes = byteStream.ReadBytes(4);
206+
var chunkType = Encoding.UTF8.GetString(chunkTypeBytes);
207+
208+
if (chunkType != Encoding.UTF8.GetString(Idat))
209+
{
210+
// skip chunk data
211+
byteStream.BaseStream.Position += chunkSize;
212+
// skip crc
213+
byteStream.BaseStream.Position += 4;
214+
continue;
215+
}
216+
217+
memoryStream.Write(chunkSizeBytes);
218+
memoryStream.Write(chunkTypeBytes);
219+
var idatBytes = byteStream.ReadBytes(chunkSize);
220+
memoryStream.Write(idatBytes);
221+
var crcBytes = byteStream.ReadBytes(4);
222+
memoryStream.Write(crcBytes);
223+
}
224+
225+
// Add IEND chunk
226+
memoryStream.Write([0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82]);
227+
memoryStream.Position = 0;
228+
return memoryStream;
229+
}
182230
}

0 commit comments

Comments
 (0)