Skip to content

Commit

Permalink
Add scanning QR code from image
Browse files Browse the repository at this point in the history
  • Loading branch information
2dust committed Oct 18, 2024
1 parent b74ddc0 commit 5c0fba8
Show file tree
Hide file tree
Showing 16 changed files with 208 additions and 89 deletions.
75 changes: 75 additions & 0 deletions v2rayN/ServiceLib/Common/QRCodeHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using QRCoder;
using SkiaSharp;
using ZXing.SkiaSharp;

namespace ServiceLib.Common
{
Expand All @@ -11,5 +13,78 @@ public class QRCodeHelper
using PngByteQRCode qrCode = new(qrCodeData);
return qrCode.GetGraphic(20);
}

public static string? ParseBarcode(string? fileName)
{
if (fileName == null || !File.Exists(fileName))
{
return null;
}

try
{
var image = SKImage.FromEncodedData(fileName);
var bitmap = SKBitmap.FromImage(image);

return ReaderBarcode(bitmap);
}
catch
{
// ignored
}

return null;
}

public static string? ParseBarcode(byte[]? bytes)
{
try
{
var bitmap = SKBitmap.Decode(bytes);
//using var stream = new FileStream("test2.png", FileMode.Create, FileAccess.Write);
//using var image = SKImage.FromBitmap(bitmap);
//using var encodedImage = image.Encode();
//encodedImage.SaveTo(stream);
return ReaderBarcode(bitmap);
}
catch
{
// ignored
}

return null;
}

private static string? ReaderBarcode(SKBitmap? bitmap)
{
var reader = new BarcodeReader();
var result = reader.Decode(bitmap);

if (result != null && Utils.IsNotEmpty(result.Text))
{
return result.Text;
}

//FlipBitmap
var result2 = reader.Decode(FlipBitmap(bitmap));
return result2?.Text;
}

private static SKBitmap FlipBitmap(SKBitmap bmp)
{
// Create a bitmap (to return)
var flipped = new SKBitmap(bmp.Width, bmp.Height, bmp.Info.ColorType, bmp.Info.AlphaType);

// Create a canvas to draw into the bitmap
using var canvas = new SKCanvas(flipped);

// Set a transform matrix which moves the bitmap to the right,
// and then "scales" it by -1, which just flips the pixels
// horizontally
canvas.Translate(bmp.Width, 0);
canvas.Scale(-1, 1);
canvas.DrawBitmap(bmp, 0, 0);
return flipped;
}
}
}
1 change: 1 addition & 0 deletions v2rayN/ServiceLib/Enums/EViewAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum EViewAction
ShareServer,
ShowHideWindow,
ScanScreenTask,
ScanImageTask,
Shutdown,
BrowseServer,
ImportRulesFromFile,
Expand Down
9 changes: 9 additions & 0 deletions v2rayN/ServiceLib/Resx/ResUI.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions v2rayN/ServiceLib/Resx/ResUI.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1351,4 +1351,7 @@
<data name="TbSettingsChinaUserTip" xml:space="preserve">
<value>Users in China region can ignore this item</value>
</data>
<data name="menuAddServerViaImage" xml:space="preserve">
<value>Scan QR code in the image</value>
</data>
</root>
3 changes: 3 additions & 0 deletions v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1348,4 +1348,7 @@
<data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>俄罗斯</value>
</data>
<data name="menuAddServerViaImage" xml:space="preserve">
<value>扫描图片中的二维码</value>
</data>
</root>
3 changes: 3 additions & 0 deletions v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1228,4 +1228,7 @@
<data name="menuRegionalPresetsRussia" xml:space="preserve">
<value>俄羅斯</value>
</data>
<data name="menuAddServerViaImage" xml:space="preserve">
<value>掃描圖片中的二維碼</value>
</data>
</root>
4 changes: 3 additions & 1 deletion v2rayN/ServiceLib/ServiceLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
<PackageReference Include="WebDav.Client" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="16.1.3" />
<PackageReference Include="QRCoder" Version="1.6.0" />
<PackageReference Include="CliWrap" Version="3.6.6" />
<PackageReference Include="CliWrap" Version="3.6.6" />
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
<PackageReference Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup>

<ItemGroup>
Expand Down
36 changes: 32 additions & 4 deletions v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class MainWindowViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
public ReactiveCommand<Unit, Unit> AddServerViaImageCmd { get; }

//Subscription
public ReactiveCommand<Unit, Unit> SubSettingCmd { get; }
Expand All @@ -46,6 +47,7 @@ public class MainWindowViewModel : MyReactiveObject

//Presets
public ReactiveCommand<Unit, Unit> RegionalPresetDefaultCmd { get; }

public ReactiveCommand<Unit, Unit> RegionalPresetRussiaCmd { get; }

public ReactiveCommand<Unit, Unit> ReloadCmd { get; }
Expand Down Expand Up @@ -121,7 +123,11 @@ public MainWindowViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
});
AddServerViaScanCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerViaScanTaskAsync();
await AddServerViaScanAsync();
});
AddServerViaImageCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerViaImageAsync();
});

//Subscription
Expand Down Expand Up @@ -386,12 +392,34 @@ public async Task AddServerViaClipboardAsync(string? clipboardData)
}
}

public async Task AddServerViaScanTaskAsync()
public async Task AddServerViaScanAsync()
{
_updateView?.Invoke(EViewAction.ScanScreenTask, null);
}

public void ScanScreenResult(string result)
public async Task ScanScreenResult(byte[]? bytes)
{
var result = QRCodeHelper.ParseBarcode(bytes);
await AddScanResultAsync(result);
}

public async Task AddServerViaImageAsync()
{
_updateView?.Invoke(EViewAction.ScanImageTask, null);
}

public async Task ScanImageResult(string fileName)
{
if (Utils.IsNullOrEmpty(fileName))
{
return;
}

var result = QRCodeHelper.ParseBarcode(fileName);
await AddScanResultAsync(result);
}

private async Task AddScanResultAsync(string? result)
{
if (Utils.IsNullOrEmpty(result))
{
Expand Down Expand Up @@ -571,6 +599,6 @@ public async Task ApplyRegionalPreset(EPresetType type)
Reload();
}

#endregion
#endregion Presets
}
}
2 changes: 1 addition & 1 deletion v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private async Task AddServerViaClipboard()
private async Task AddServerViaScan()
{
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) await service.AddServerViaScanTaskAsync();
if (service != null) await service.AddServerViaScanAsync();
}

private async Task UpdateSubscriptionProcess(bool blProxy)
Expand Down
6 changes: 2 additions & 4 deletions v2rayN/v2rayN.Desktop/Views/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,8 @@
</StackPanel>
</MenuItem.Header>
<MenuItem x:Name="menuAddServerViaClipboard" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<MenuItem
x:Name="menuAddServerViaScan"
Header="{x:Static resx:ResUI.menuAddServerViaScan}"
IsVisible="False" />
<MenuItem x:Name="menuAddServerViaScan" Header="{x:Static resx:ResUI.menuAddServerViaScan}" />
<MenuItem x:Name="menuAddServerViaImage" Header="{x:Static resx:ResUI.menuAddServerViaImage}" />
<MenuItem x:Name="menuAddCustomServer" Header="{x:Static resx:ResUI.menuAddCustomServer}" />
<Separator />
<MenuItem x:Name="menuAddVmessServer" Header="{x:Static resx:ResUI.menuAddVmessServer}" />
Expand Down
17 changes: 16 additions & 1 deletion v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Avalonia.Controls.Notifications;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
using DialogHostAvalonia;
Expand Down Expand Up @@ -60,6 +61,7 @@ public MainWindow()
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
//sub
this.BindCommand(ViewModel, vm => vm.SubSettingCmd, v => v.menuSubSetting).DisposeWith(disposables);
Expand Down Expand Up @@ -224,6 +226,10 @@ private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
await ScanScreenTaskAsync();
break;

case EViewAction.ScanImageTask:
await ScanImageTaskAsync();
break;

case EViewAction.AddServerViaClipboard:
var clipboardData = await AvaUtils.GetClipboardData(this);
ViewModel?.AddServerViaClipboardAsync(clipboardData);
Expand Down Expand Up @@ -324,7 +330,16 @@ public async Task ScanScreenTaskAsync()

ShowHideWindow(true);

//ViewModel?.ScanScreenTaskAsync(result);
//ViewModel?.ScanScreenResult(result);
}
private async Task ScanImageTaskAsync()
{
var fileName = await UI.OpenFileDialog(this,null );
if (fileName.IsNullOrEmpty())
{
return;
}
await ViewModel?.ScanImageResult(fileName);
}

private void MenuCheckUpdate_Click(object? sender, RoutedEventArgs e)
Expand Down
5 changes: 2 additions & 3 deletions v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
<Image
Name="imgQrcode"
Width="300"
Height="300"
Source="/Assets/close.png" />
Height="300" />

<TextBox
x:Name="txtContent"
Expand All @@ -29,6 +28,6 @@
IsReadOnly="True"
MaxLines="1" />


</Grid>
</UserControl>
Loading

0 comments on commit 5c0fba8

Please sign in to comment.