Skip to content

Commit fbe101c

Browse files
author
Carl Chang
committed
additional subPath column in thumb db;
improve thumb load speed over large number of containers;
1 parent 267d50d commit fbe101c

File tree

7 files changed

+104
-64
lines changed

7 files changed

+104
-64
lines changed

BlockWindow.xaml

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,27 @@
2424
<Binding RelativeSource="{RelativeSource Self}" Path="Height"/>
2525
</MultiBinding>
2626
</Window.Top>-->
27-
<Grid x:Name="grid" RenderTransformOrigin="0.5,0.5">
28-
<TextBlock x:Name="T_Title" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,15,0,0" FontWeight="Bold"
27+
<Grid x:Name="grid" RenderTransformOrigin="0.5,0.5" Margin="0 0 0 20">
28+
<Grid.RowDefinitions>
29+
<RowDefinition Height="Auto"/>
30+
<RowDefinition/>
31+
<RowDefinition Height="Auto"/>
32+
</Grid.RowDefinitions>
33+
<TextBlock x:Name="T_Title" HorizontalAlignment="Center" VerticalAlignment="Top" FontWeight="Bold"
2934
Text="{Binding MessageTitle, ElementName=BlockWin, Mode=OneWay}"/>
30-
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
31-
<TextBlock x:Name="TB_Message" HorizontalAlignment="Center" TextWrapping="Wrap" FontSize="10"
32-
Text="{Binding MessageBody, ElementName=BlockWin, Mode=OneWay}"/>
33-
<ProgressBar x:Name="PB_Message" Height="{Binding FontSize, ElementName=BlockWin, Mode=OneWay}" Width="200" Margin="0,10,0,0"
35+
<TextBlock Grid.Row="1" x:Name="TB_Message" HorizontalAlignment="Center" TextWrapping="Wrap" FontSize="10" Margin="0 10"
36+
Text="{Binding MessageBody, ElementName=BlockWin, Mode=OneWay}"/>
37+
<ProgressBar Grid.Row="2" x:Name="PB_Message" Height="{Binding FontSize, ElementName=BlockWin, Mode=OneWay}" Width="200"
3438
Value="{Binding Percentage, ElementName=BlockWin, Mode=OneWay}">
35-
<ProgressBar.Style>
36-
<Style TargetType="ProgressBar">
37-
<Style.Triggers>
38-
<DataTrigger Binding="{Binding Percentage, ElementName=BlockWin, Mode=OneWay}" Value="-1">
39-
<Setter Property="IsIndeterminate" Value="True" />
40-
</DataTrigger>
41-
</Style.Triggers>
42-
</Style>
43-
</ProgressBar.Style>
44-
</ProgressBar>
45-
</StackPanel>
39+
<ProgressBar.Style>
40+
<Style TargetType="ProgressBar">
41+
<Style.Triggers>
42+
<DataTrigger Binding="{Binding Percentage, ElementName=BlockWin, Mode=OneWay}" Value="-1">
43+
<Setter Property="IsIndeterminate" Value="True" />
44+
</DataTrigger>
45+
</Style.Triggers>
46+
</Style>
47+
</ProgressBar.Style>
48+
</ProgressBar>
4649
</Grid>
4750
</local:RoundedWindow>

Helpers/LoadHelper.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,9 @@ private static bool extractZip(LoadOptions options, ObjectInfo objInfo, HashSet<
179179

180180
ImageSource source = null;
181181
if (options.LoadImage) {
182-
var thumbPathInDb = Path.Combine(options.FilePath, fileName);
183182
if (options.TryCache && isThumb) {
184183
//try load from cache
185-
source = SQLiteHelper.GetFromThumbDB(thumbPathInDb, options.DecodeSize);
184+
source = SQLiteHelper.GetFromThumbDB(options.FilePath, options.DecodeSize, fileName)?.Item1;
186185
}
187186
if (source == null) {
188187
#if DEBUG
@@ -196,7 +195,7 @@ private static bool extractZip(LoadOptions options, ObjectInfo objInfo, HashSet<
196195
success = true; //if the task is cancelled, success info is still returned correctly.
197196
source = GetImageSource(ms, options.DecodeSize);
198197
}
199-
if (isThumb && source != null) SQLiteHelper.AddToThumbDB(source, thumbPathInDb, options.DecodeSize);
198+
if (isThumb && source != null) SQLiteHelper.AddToThumbDB(source, options.FilePath, fileName, options.DecodeSize);
200199
}
201200
}
202201

@@ -446,11 +445,13 @@ public static async Task<BitmapSource> GetImageSourceAsync(string path, SizeInt
446445
/// </summary>
447446
public static BitmapSource GetImageSource(string path, SizeInt decodeSize = default, bool tryCache = true) {
448447
BitmapSource bs = null;
448+
var basePath = Path.GetDirectoryName(path);
449+
var subPath = Path.GetFileName(path);
449450
try {
450451
var isThumb = decodeSize == (SizeInt)Setting.ThumbnailSize;
451452
if (tryCache && isThumb) {
452453
//try load from cache when decodeSize is non-zero
453-
bs = SQLiteHelper.GetFromThumbDB(path, decodeSize);
454+
bs = SQLiteHelper.GetFromThumbDB(basePath, decodeSize, subPath)?.Item1;
454455
if (bs != null) return bs;
455456
}
456457
#if DEBUG
@@ -460,7 +461,7 @@ public static BitmapSource GetImageSource(string path, SizeInt decodeSize = defa
460461
bs = GetImageSource(fs, decodeSize);
461462
}
462463
if (isThumb && bs != null)
463-
SQLiteHelper.AddToThumbDB(bs, path, decodeSize);
464+
SQLiteHelper.AddToThumbDB(bs, basePath, subPath, decodeSize);
464465
}
465466
catch { }
466467

@@ -634,8 +635,7 @@ public static void CacheFolder(string path, ref CancellationTokenSource tknSrc,
634635
try {
635636
objInfo.SourcePaths = GetSourcePaths(objInfo);
636637
if (objInfo.SourcePaths?.Length > 0) {
637-
var p = Path.Combine(objInfo.ContainerPath, objInfo.SourcePaths[0]);
638-
if (!SQLiteHelper.ThumbExistInDB(p, decodeSize)) {
638+
if (!SQLiteHelper.ThumbExistInDB(objInfo.ContainerPath, objInfo.SourcePaths[0], decodeSize)) {
639639
GetImageSource(objInfo, 0, decodeSize, false);
640640
}
641641
}

Helpers/SQLiteHelper.cs

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Windows.Media.Imaging;
1111
using SizeInt = System.Drawing.Size;
1212
using static ZipImageViewer.TableHelper;
13+
using System.Threading.Tasks;
1314

1415
namespace ZipImageViewer
1516
{
@@ -60,10 +61,13 @@ internal static void CheckThumbsDB() {
6061
using (var r = cmd.ExecuteReader()) {
6162
while (r.Read()) {
6263
switch (r["name"]) {
63-
case nameof(Column.VirtualPath):
64+
case nameof(Column.BasePath):
6465
if (r["type"].ToString() == "TEXT" &&
65-
r["notnull"].ToString() == "1" &&
66-
r["pk"].ToString() == "1") goodColumns += 1;
66+
r["notnull"].ToString() == "1") goodColumns += 1;
67+
break;
68+
case nameof(Column.SubPath):
69+
if (r["type"].ToString() == "TEXT" &&
70+
r["notnull"].ToString() == "1") goodColumns += 1;
6771
break;
6872
case nameof(Column.DecodeWidth):
6973
case nameof(Column.DecodeHeight):
@@ -78,7 +82,7 @@ internal static void CheckThumbsDB() {
7882
}
7983
}
8084
});
81-
if (goodColumns == 4) return;
85+
if (goodColumns == 5) return;
8286

8387
//recreate thumbs table
8488
if (File.Exists(Tables[Table.Thumbs].FullPath))
@@ -90,17 +94,17 @@ internal static void CheckThumbsDB() {
9094
using (var cmd = new SQLiteCommand(con)) {
9195
cmd.CommandText =
9296
$@"create table if not exists [{table.Name}] (
93-
[{Column.VirtualPath}] TEXT NOT NULL,
97+
[{Column.BasePath}] TEXT NOT NULL,
98+
[{Column.SubPath}] TEXT NOT NULL,
9499
[{Column.DecodeWidth}] INTEGER,
95100
[{Column.DecodeHeight}] INTEGER,
96-
[{Column.ThumbData}] BLOB,
97-
PRIMARY KEY({Column.VirtualPath}))";
101+
[{Column.ThumbData}] BLOB)";
98102
return cmd.ExecuteNonQuery();
99103
}
100104
});
101105
}
102106

103-
internal static int AddToThumbDB(ImageSource source, string path, SizeInt decodeSize) {
107+
internal static int AddToThumbDB(ImageSource source, string basePath, string subPath, SizeInt decodeSize) {
104108
if (!(source is BitmapSource bs)) throw new NotSupportedException();
105109

106110
object[] affected = null;
@@ -116,13 +120,18 @@ internal static int AddToThumbDB(ImageSource source, string path, SizeInt decode
116120
affected = Execute(Table.Thumbs, (table, con) => {
117121
using (var cmd = new SQLiteCommand(con)) {
118122
//remove existing
119-
cmd.CommandText = $@"delete from {table.Name} where {Column.VirtualPath} = @path";
120-
cmd.Parameters.Add(new SQLiteParameter("@path", DbType.String) { Value = path });
123+
cmd.CommandText =
124+
$@"delete from {table.Name} where
125+
{Column.BasePath} = @basePath and
126+
{Column.SubPath} = @subPath";
127+
cmd.Parameters.Add(new SQLiteParameter("@basePath", DbType.String) { Value = basePath });
128+
cmd.Parameters.Add(new SQLiteParameter("@subPath", DbType.String) { Value = subPath });
121129
cmd.ExecuteNonQuery();
122130
//insert new
123-
cmd.CommandText = $@"insert into {table.Name}
124-
({Column.VirtualPath}, {Column.DecodeWidth}, {Column.DecodeHeight}, {Column.ThumbData}) values
125-
(@path, {decodeSize.Width}, {decodeSize.Height}, @png)";
131+
cmd.CommandText =
132+
$@"insert into {table.Name}
133+
({Column.BasePath}, {Column.SubPath}, {Column.DecodeWidth}, {Column.DecodeHeight}, {Column.ThumbData}) values
134+
(@basePath, @subPath, {decodeSize.Width}, {decodeSize.Height}, @png)";
126135
cmd.Parameters.Add(new SQLiteParameter("@png", DbType.Binary) { Value = png });
127136
return cmd.ExecuteNonQuery();
128137
}
@@ -131,22 +140,35 @@ internal static int AddToThumbDB(ImageSource source, string path, SizeInt decode
131140
return (int)affected[0];
132141
}
133142

143+
/// <summary>
144+
/// Async version of <see cref="GetFromThumbDB(string, string, SizeInt)"/>
145+
/// </summary>
146+
internal static Task<Tuple<BitmapSource, string>> GetFromThumbDBAsync(string basePath, SizeInt decodeSize, string subPath = null) {
147+
return Task.Run(() => GetFromThumbDB(basePath, decodeSize, subPath));
148+
}
149+
134150
/// <summary>
135151
/// Returns null if thumb either does not exist in DB or has different size.
152+
/// If <paramref name="subPath"/> is null, the first match by <paramref name="basePath"/> will be returned.
136153
/// </summary>
137-
internal static BitmapSource GetFromThumbDB(string path, SizeInt decodeSize) {
154+
internal static Tuple<BitmapSource, string> GetFromThumbDB(string basePath, SizeInt decodeSize, string subPath = null) {
138155
var png = Execute(Table.Thumbs, (table, con) => {
139156
byte[] pngByte = null;
140157
using (var cmd = new SQLiteCommand(con)) {
141158
cmd.CommandText =
142-
$@"select {Column.ThumbData} from {table.Name} where
143-
{Column.VirtualPath} = @path and
159+
$@"select * from {table.Name} where
160+
{Column.BasePath} = @basePath
161+
{(subPath == null ? "" : $@"and {Column.SubPath} = @subPath")} and
144162
{Column.DecodeWidth} = {decodeSize.Width} and
145-
{Column.DecodeHeight} = {decodeSize.Height}";
146-
cmd.Parameters.Add(new SQLiteParameter("@path", DbType.String) { Value = path });
163+
{Column.DecodeHeight} = {decodeSize.Height} limit 1";
164+
cmd.Parameters.Add(new SQLiteParameter("@basePath", DbType.String) { Value = basePath });
165+
if (subPath != null)
166+
cmd.Parameters.Add(new SQLiteParameter("@subPath", DbType.String) { Value = subPath });
147167
using (var reader = cmd.ExecuteReader()) {
148168
while (reader.Read()) {
149-
pngByte = (byte[])reader[0];
169+
pngByte = (byte[])reader[nameof(Column.ThumbData)];
170+
if (subPath == null)
171+
subPath = (string)reader[nameof(Column.SubPath)];
150172
break;
151173
}
152174
}
@@ -162,19 +184,22 @@ internal static BitmapSource GetFromThumbDB(string path, SizeInt decodeSize) {
162184
bi.StreamSource = ms;
163185
bi.EndInit();
164186
bi.Freeze();
165-
return bi;
187+
return new Tuple<BitmapSource, string>(bi, subPath);
166188
}
167189
}
168190

169-
internal static bool ThumbExistInDB(string path, SizeInt decodeSize) {
191+
internal static bool ThumbExistInDB(string basePath, string subPath, SizeInt decodeSize) {
170192
return (bool)Execute(Table.Thumbs, (table, con) => {
171193
using (var cmd = new SQLiteCommand(con)) {
172194
cmd.CommandText =
173195
$@"select count({Column.ThumbData}) from {table.Name} where
174-
{Column.VirtualPath} = @path and
196+
{Column.BasePath} = @basePath
197+
{(subPath == null ? "" : $@"and {Column.SubPath} = @subPath")} and
175198
{Column.DecodeWidth} = {decodeSize.Width} and
176199
{Column.DecodeHeight} = {decodeSize.Height}";
177-
cmd.Parameters.Add(new SQLiteParameter("@path", DbType.String) { Value = path });
200+
cmd.Parameters.Add(new SQLiteParameter("@basePath", DbType.String) { Value = basePath });
201+
if (subPath != null)
202+
cmd.Parameters.Add(new SQLiteParameter("@subPath", DbType.String) { Value = subPath });
178203
using (var r = cmd.ExecuteReader()) {
179204
r.Read();
180205
return (long)r[0] > 0;

Helpers/SlideshowHelper.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,6 @@ private static void Anim_KBE(DpiImage tgtImg, Size frameSize, SlideAnimConfig cf
236236
//zoon animation
237237
var animZoom = zoomIn ? new DoubleAnimation(1d, 1.2d, imageDur) : new DoubleAnimation(1.2d, 1d, imageDur);
238238

239-
//transform center point
240-
//tgtImg.RenderTransformOrigin = new Point(ran.NextDouble(), ran.NextDouble());
241-
//tgtImg.RenderTransformOrigin = new Point(0.5, 0d);
242-
243239
//pan animation
244240
DoubleAnimation animPanPri;
245241
DependencyProperty transEdgePri;

Helpers/TableHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public enum Table
1515

1616
public enum Column
1717
{
18-
VirtualPath, DecodeWidth, DecodeHeight, ThumbData,
19-
Path, Password,
18+
BasePath, SubPath, DecodeWidth, DecodeHeight, ThumbData,//Thumbs table
19+
Path, Password,//MappedPasswords table
2020
}
2121

2222
public class TableInfo

UserControls/Thumbnail.xaml.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ public ObjectInfo ObjectInfo {
2626

2727
public event PropertyChangedEventHandler PropertyChanged;
2828

29-
private int thumbIndex = -1;
29+
private int sourcePathIdx;
30+
private string sourcePathName;
3031

3132
private int thumbTransAnimCount;
3233
private string thumbTransAnimName;
@@ -116,17 +117,17 @@ private void TN_Loaded(object sender, RoutedEventArgs e) {
116117

117118
mainWin = (MainWindow)Window.GetWindow(this);
118119

119-
thumbIndex = -1;
120+
sourcePathIdx = -2;
120121
cycleImageSource(null, null);
121122
}
122123

123124
private void TN_Unloaded(object sender, RoutedEventArgs e) {
124-
//ThumbImageSource = null;
125+
ThumbImageSource = null;
125126
//if (ObjectInfo != null) {
126127
// ObjectInfo.ImageSources = null;
127128
// ObjectInfo = null;
128129
//}
129-
//nextSource = null;
130+
nextSource = null;
130131
//if (IM1 != null) {
131132
// IM1.Source = null;
132133
// IM1.ToolTip = null;
@@ -149,6 +150,21 @@ private async void cycleImageSource(object sender, EventArgs e) {
149150
return;
150151
}
151152

153+
var thumbSize = (SizeInt)Setting.ThumbnailSize;
154+
//load from db if exists (only the first time)
155+
if ((tn.ObjectInfo.Flags == FileFlags.Directory || tn.ObjectInfo.Flags == FileFlags.Archive) && tn.sourcePathIdx == -2) {
156+
tn.sourcePathIdx = -1;
157+
var cached = await SQLiteHelper.GetFromThumbDBAsync(tn.ObjectInfo.ContainerPath, thumbSize);
158+
if (cached != null) {
159+
tn.ThumbImageSource = cached.Item1;
160+
tn.sourcePathName = cached.Item2;
161+
tn.cycleTimer.Interval = TimeSpan.FromMilliseconds(mainWin.ThumbChangeDelay);
162+
tn.cycleTimer.Start();
163+
return;
164+
}
165+
}
166+
if (tn.sourcePathIdx == -2) tn.sourcePathIdx = -1;
167+
152168
//wait to get image
153169
if (tn.ObjectInfo.ImageSource == null && !Setting.ImmersionMode &&
154170
(mainWin.tknSrc_LoadThumb != null || Interlocked.CompareExchange(ref workingThreads, 0, 0) >= MaxLoadThreads)) {
@@ -170,14 +186,15 @@ private async void cycleImageSource(object sender, EventArgs e) {
170186
objInfo.SourcePaths = await GetSourcePathsAsync(objInfo);
171187
}
172188
//get the next path index to use
173-
var thumbSize = (SizeInt)Setting.ThumbnailSize;
174189
if (tn.ObjectInfo.SourcePaths?.Length > 1) {
175-
tn.thumbIndex = tn.thumbIndex == tn.ObjectInfo.SourcePaths.Length - 1 ? 0 : tn.thumbIndex + 1;
190+
do {
191+
tn.sourcePathIdx = tn.sourcePathIdx == tn.ObjectInfo.SourcePaths.Length - 1 ? 0 : tn.sourcePathIdx + 1;
192+
} while (tn.ObjectInfo.SourcePaths[tn.sourcePathIdx] == tn.sourcePathName);
176193
cycle = true;
177194
}
178195
else
179-
tn.thumbIndex = 0;
180-
tn.ThumbImageSource = await GetImageSourceAsync(tn.ObjectInfo, sourcePathIdx: tn.thumbIndex, decodeSize: thumbSize);
196+
tn.sourcePathIdx = 0;
197+
tn.ThumbImageSource = await GetImageSourceAsync(tn.ObjectInfo, sourcePathIdx: tn.sourcePathIdx, decodeSize: thumbSize);
181198
}
182199
catch { }
183200
finally {
@@ -188,8 +205,7 @@ private async void cycleImageSource(object sender, EventArgs e) {
188205
if (!tn.IsLoaded || !mainWin.IsLoaded || !cycle) return;
189206

190207
//plan for the next run
191-
var delay = mainWin.ThumbChangeDelay;
192-
tn.cycleTimer.Interval = TimeSpan.FromMilliseconds(delay);
208+
tn.cycleTimer.Interval = TimeSpan.FromMilliseconds(mainWin.ThumbChangeDelay);
193209
tn.cycleTimer.Start();
194210
}
195211
}

packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<package id="Microsoft.WindowsAPICodePack-Core" version="1.1.0.2" targetFramework="net461" />
66
<package id="Microsoft.WindowsAPICodePack-Shell" version="1.1.0.0" targetFramework="net461" />
77
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
8-
<package id="SevenZipSharp.Net45" version="1.0.19" targetFramework="net461" />
8+
<package id="SevenZipSharp.Net45" version="1.0.19" targetFramework="net472" />
99
<package id="System.Data.SQLite.Core" version="1.0.112.0" targetFramework="net472" />
1010
<package id="System.Data.SQLite.Linq" version="1.0.112.0" targetFramework="net472" />
1111
<package id="VirtualizingWrapPanel" version="1.4.2" targetFramework="net472" />

0 commit comments

Comments
 (0)