Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 17ea185

Browse files
authored
enhance: image diff (#1797)
* Removed decimals from % values shown for ImageBlendSlider * Moved OLD text at ImageBlendSlider, for symmetry and non-jumpiness * Added ticks on ImageBlendSlider, at 0%, 50% and 100% * Added new image-diff mode DIFFERENCE Similar to the BLEND image-diff mode, but instead blends towards total DIFFERENCE at 50%. (This way, we can look at the difference in the middle, and slide towards OLD or NEW to view their respective contributions to the diff.) * Selected image-diff mode is now persistent
1 parent c3aea15 commit 17ea185

File tree

5 files changed

+195
-6
lines changed

5 files changed

+195
-6
lines changed

‎src/Converters/DoubleConverters.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ public static class DoubleConverters
1212
new FuncValueConverter<double, double>(v => v - 1.0);
1313

1414
public static readonly FuncValueConverter<double, string> ToPercentage =
15-
new FuncValueConverter<double, string>(v => (v * 100).ToString("F3") + "%");
15+
new FuncValueConverter<double, string>(v => (v * 100).ToString("F0") + "%");
1616

1717
public static readonly FuncValueConverter<double, string> OneMinusToPercentage =
18-
new FuncValueConverter<double, string>(v => ((1.0 - v) * 100).ToString("F3") + "%");
18+
new FuncValueConverter<double, string>(v => ((1.0 - v) * 100).ToString("F0") + "%");
1919

2020
public static readonly FuncValueConverter<double, Thickness> ToLeftMargin =
2121
new FuncValueConverter<double, Thickness>(v => new Thickness(v, 0, 0, 0));

‎src/Resources/Locales/en_US.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@
319319
<x:String x:Key="Text.Diff.First" xml:space="preserve">First Difference</x:String>
320320
<x:String x:Key="Text.Diff.IgnoreWhitespace" xml:space="preserve">Ignore All Whitespace Changes</x:String>
321321
<x:String x:Key="Text.Diff.Image.Blend" xml:space="preserve">BLEND</x:String>
322+
<x:String x:Key="Text.Diff.Image.Difference" xml:space="preserve">DIFFERENCE</x:String>
322323
<x:String x:Key="Text.Diff.Image.SideBySide" xml:space="preserve">SIDE-BY-SIDE</x:String>
323324
<x:String x:Key="Text.Diff.Image.Swipe" xml:space="preserve">SWIPE</x:String>
324325
<x:String x:Key="Text.Diff.Last" xml:space="preserve">Last Difference</x:String>

‎src/ViewModels/Preferences.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,12 @@ public int LFSImageActiveIdx
279279
set => SetProperty(ref _lfsImageActiveIdx, value);
280280
}
281281

282+
public int ImageDiffActiveIdx
283+
{
284+
get => _imageDiffActiveIdx;
285+
set => SetProperty(ref _imageDiffActiveIdx, value);
286+
}
287+
282288
public bool EnableCompactFoldersInChangesTree
283289
{
284290
get => _enableCompactFoldersInChangesTree;
@@ -736,6 +742,7 @@ private bool RemoveInvalidRepositoriesRecursive(List<RepositoryNode> collection)
736742
private bool _showHiddenSymbolsInDiffView = false;
737743
private bool _useFullTextDiff = false;
738744
private int _lfsImageActiveIdx = 0;
745+
private int _imageDiffActiveIdx = 0;
739746
private bool _enableCompactFoldersInChangesTree = false;
740747

741748
private Models.ChangeViewMode _unstagedChangeViewMode = Models.ChangeViewMode.List;

‎src/Views/ImageContainer.cs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,119 @@ private void RenderSingleSide(DrawingContext context, Bitmap img, double w, doub
361361
private static readonly RenderOptions RO_SRC = new RenderOptions() { BitmapBlendingMode = BitmapBlendingMode.Source, BitmapInterpolationMode = BitmapInterpolationMode.HighQuality };
362362
private static readonly RenderOptions RO_DST = new RenderOptions() { BitmapBlendingMode = BitmapBlendingMode.Plus, BitmapInterpolationMode = BitmapInterpolationMode.HighQuality };
363363
}
364+
365+
public class ImageDifferenceControl : ImageContainer
366+
{
367+
public static readonly StyledProperty<double> AlphaProperty =
368+
AvaloniaProperty.Register<ImageDifferenceControl, double>(nameof(Alpha), 1.0);
369+
370+
public double Alpha
371+
{
372+
get => GetValue(AlphaProperty);
373+
set => SetValue(AlphaProperty, value);
374+
}
375+
376+
public static readonly StyledProperty<Bitmap> OldImageProperty =
377+
AvaloniaProperty.Register<ImageDifferenceControl, Bitmap>(nameof(OldImage));
378+
379+
public Bitmap OldImage
380+
{
381+
get => GetValue(OldImageProperty);
382+
set => SetValue(OldImageProperty, value);
383+
}
384+
385+
public static readonly StyledProperty<Bitmap> NewImageProperty =
386+
AvaloniaProperty.Register<ImageDifferenceControl, Bitmap>(nameof(NewImage));
387+
388+
public Bitmap NewImage
389+
{
390+
get => GetValue(NewImageProperty);
391+
set => SetValue(NewImageProperty, value);
392+
}
393+
394+
static ImageDifferenceControl()
395+
{
396+
AffectsMeasure<ImageDifferenceControl>(OldImageProperty, NewImageProperty);
397+
AffectsRender<ImageDifferenceControl>(AlphaProperty);
398+
}
399+
400+
public override void Render(DrawingContext context)
401+
{
402+
base.Render(context);
403+
404+
var alpha = Alpha;
405+
var left = OldImage;
406+
var right = NewImage;
407+
var drawLeft = left != null && alpha < 1.0;
408+
var drawRight = right != null && alpha > 0.0;
409+
410+
if (drawLeft && drawRight)
411+
{
412+
using (var rt = new RenderTargetBitmap(new PixelSize((int)Bounds.Width, (int)Bounds.Height), right.Dpi))
413+
{
414+
using (var dc = rt.CreateDrawingContext())
415+
{
416+
using (dc.PushRenderOptions(RO_SRC))
417+
RenderSingleSide(dc, left, rt.Size.Width, rt.Size.Height, Math.Min(1.0, 2.0 - 2.0 * alpha));
418+
419+
using (dc.PushRenderOptions(RO_DST))
420+
RenderSingleSide(dc, right, rt.Size.Width, rt.Size.Height, Math.Min(1.0, 2.0 * alpha));
421+
}
422+
423+
context.DrawImage(rt, new Rect(0, 0, Bounds.Width, Bounds.Height));
424+
}
425+
}
426+
else if (drawLeft)
427+
{
428+
RenderSingleSide(context, left, Bounds.Width, Bounds.Height, 1 - alpha);
429+
}
430+
else if (drawRight)
431+
{
432+
RenderSingleSide(context, right, Bounds.Width, Bounds.Height, alpha);
433+
}
434+
}
435+
436+
protected override Size MeasureOverride(Size availableSize)
437+
{
438+
var left = OldImage;
439+
var right = NewImage;
440+
441+
if (left == null)
442+
return right == null ? availableSize : GetDesiredSize(right.Size, availableSize);
443+
444+
if (right == null)
445+
return GetDesiredSize(left.Size, availableSize);
446+
447+
var ls = GetDesiredSize(left.Size, availableSize);
448+
var rs = GetDesiredSize(right.Size, availableSize);
449+
return ls.Width > rs.Width ? ls : rs;
450+
}
451+
452+
private Size GetDesiredSize(Size img, Size available)
453+
{
454+
var sw = available.Width / img.Width;
455+
var sh = available.Height / img.Height;
456+
var scale = Math.Min(1, Math.Min(sw, sh));
457+
return new Size(scale * img.Width, scale * img.Height);
458+
}
459+
460+
private void RenderSingleSide(DrawingContext context, Bitmap img, double w, double h, double alpha)
461+
{
462+
var imgW = img.Size.Width;
463+
var imgH = img.Size.Height;
464+
var scale = Math.Min(1, Math.Min(w / imgW, h / imgH));
465+
466+
var scaledW = img.Size.Width * scale;
467+
var scaledH = img.Size.Height * scale;
468+
469+
var src = new Rect(0, 0, imgW, imgH);
470+
var dst = new Rect((w - scaledW) * 0.5, (h - scaledH) * 0.5, scaledW, scaledH);
471+
472+
using (context.PushOpacity(alpha))
473+
context.DrawImage(img, src, dst);
474+
}
475+
476+
private static readonly RenderOptions RO_SRC = new RenderOptions() { BitmapBlendingMode = BitmapBlendingMode.Source, BitmapInterpolationMode = BitmapInterpolationMode.HighQuality };
477+
private static readonly RenderOptions RO_DST = new RenderOptions() { BitmapBlendingMode = BitmapBlendingMode.Difference, BitmapInterpolationMode = BitmapInterpolationMode.HighQuality };
478+
}
364479
}

‎src/Views/ImageDiffView.axaml

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
44
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
55
xmlns:m="using:SourceGit.Models"
6+
xmlns:vm="using:SourceGit.ViewModels"
67
xmlns:v="using:SourceGit.Views"
78
xmlns:c="using:SourceGit.Converters"
89
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
910
x:Class="SourceGit.Views.ImageDiffView"
1011
x:DataType="m:ImageDiff">
11-
<TabControl Margin="0,0,0,8" TabStripPlacement="Bottom">
12+
<TabControl SelectedIndex="{Binding Source={x:Static vm:Preferences.Instance}, Path=ImageDiffActiveIdx, Mode=TwoWay}"Margin="0,0,0,8" TabStripPlacement="Bottom">
1213
<TabControl.Styles>
1314
<Style Selector="TabControl /template/ ItemsPresenter#PART_ItemsPresenter > WrapPanel">
1415
<Setter Property="HorizontalAlignment" Value="Center"/>
@@ -129,18 +130,19 @@
129130

130131
<Grid Grid.Row="2" ColumnDefinitions="100,200,100" Margin="0,12,0,0" HorizontalAlignment="Center">
131132
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,8,0">
132-
<TextBlock Classes="primary" Text="{DynamicResource Text.Diff.Old}"/>
133133
<TextBlock Classes="primary"
134-
Margin="8,0,0,0"
134+
Margin="0,0,8,0"
135135
Text="{Binding #ImageBlendSlider.Value, Converter={x:Static c:DoubleConverters.OneMinusToPercentage}}"
136136
Foreground="{DynamicResource Brush.FG2}"/>
137+
<TextBlock Classes="primary" Text="{DynamicResource Text.Diff.Old}"/>
137138
</StackPanel>
138139

139140
<Slider Grid.Column="1"
140141
x:Name="ImageBlendSlider"
141142
Minimum="0" Maximum="1"
142143
VerticalAlignment="Top"
143-
TickPlacement="None"
144+
TickFrequency="0.5"
145+
TickPlacement="BottomRight"
144146
Margin="0"
145147
MinHeight="0"
146148
Foreground="{DynamicResource Brush.Border1}"
@@ -156,5 +158,69 @@
156158
</Grid>
157159
</Grid>
158160
</TabItem>
161+
162+
<TabItem>
163+
<TabItem.Header>
164+
<TextBlock Text="{DynamicResource Text.Diff.Image.Difference}" FontSize="11"/>
165+
</TabItem.Header>
166+
167+
<Grid RowDefinitions="Auto,*,Auto" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="8,16,8,0">
168+
<Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto" HorizontalAlignment="Center">
169+
<Border Grid.Column="0" Height="16" Background="{DynamicResource Brush.Badge}" CornerRadius="8" VerticalAlignment="Center">
170+
<TextBlock Classes="primary" Text="{DynamicResource Text.Diff.Old}" Margin="8,0" FontSize="10" Foreground="{DynamicResource Brush.BadgeFG}"/>
171+
</Border>
172+
173+
<TextBlock Grid.Column="1" Classes="primary" Text="{Binding OldImageSize}" Margin="8,0,0,0"/>
174+
<TextBlock Grid.Column="2" Classes="primary" Text="{Binding OldFileSize, Converter={x:Static c:LongConverters.ToFileSize}}" Foreground="{DynamicResource Brush.FG2}" Margin="16,0,0,0"/>
175+
176+
<Border Grid.Column="3" Height="16" Background="Green" CornerRadius="8" VerticalAlignment="Center" Margin="32,0,0,0">
177+
<TextBlock Classes="primary" Text="{DynamicResource Text.Diff.New}" Margin="8,0" FontSize="10" Foreground="White"/>
178+
</Border>
179+
180+
<TextBlock Grid.Column="4" Classes="primary" Text="{Binding NewImageSize}" Margin="8,0,0,0"/>
181+
<TextBlock Grid.Column="5" Classes="primary" Text="{Binding NewFileSize, Converter={x:Static c:LongConverters.ToFileSize}}" Foreground="{DynamicResource Brush.FG2}" Margin="16,0,0,0"/>
182+
</Grid>
183+
184+
<Border Grid.Row="1" Margin="0,12,0,0" HorizontalAlignment="Center" Effect="drop-shadow(0 0 8 #A0000000)">
185+
<Border Background="{DynamicResource Brush.Window}">
186+
<Border BorderThickness="1" BorderBrush="{DynamicResource Brush.Border1}" Margin="8">
187+
<v:ImageDifferenceControl Alpha="{Binding #ImageDifferenceSlider.Value}"
188+
OldImage="{Binding Old}"
189+
NewImage="{Binding New}"/>
190+
</Border>
191+
</Border>
192+
</Border>
193+
194+
<Grid Grid.Row="2" ColumnDefinitions="100,200,100" Margin="0,12,0,0" HorizontalAlignment="Center">
195+
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,8,0">
196+
<TextBlock Classes="primary"
197+
Margin="0,0,8,0"
198+
Text="{Binding #ImageDifferenceSlider.Value, Converter={x:Static c:DoubleConverters.OneMinusToPercentage}}"
199+
Foreground="{DynamicResource Brush.FG2}"/>
200+
<TextBlock Classes="primary" Text="{DynamicResource Text.Diff.Old}"/>
201+
</StackPanel>
202+
203+
<Slider Grid.Column="1"
204+
x:Name="ImageDifferenceSlider"
205+
Minimum="0" Maximum="1"
206+
VerticalAlignment="Top"
207+
TickFrequency="0.5"
208+
TickPlacement="BottomRight"
209+
Margin="0"
210+
MinHeight="0"
211+
Foreground="{DynamicResource Brush.Border1}"
212+
Value="0.5"/>
213+
214+
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Top" Margin="8,0,0,0">
215+
<TextBlock Classes="primary" Text="{DynamicResource Text.Diff.New}"/>
216+
<TextBlock Classes="primary"
217+
Margin="8,0,0,0"
218+
Text="{Binding #ImageDifferenceSlider.Value, Converter={x:Static c:DoubleConverters.ToPercentage}}"
219+
Foreground="{DynamicResource Brush.FG2}"/>
220+
</StackPanel>
221+
</Grid>
222+
</Grid>
223+
</TabItem>
224+
159225
</TabControl>
160226
</UserControl>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /