Compare commits

...

10 Commits

Author SHA1 Message Date
CJKmkp 578ffb7c56 improve:兼容性变更提示 2026-04-25 16:31:11 +08:00
CJKmkp feca1aa46d add:兼容性变更提示 2026-04-19 08:40:50 +08:00
CJKmkp b399429d9d improve:墨迹纠正 2026-04-19 08:38:27 +08:00
CJKmkp 5388bef3ea fix:墨迹纠正 2026-04-19 08:10:45 +08:00
CJKmkp cbb17ee03f add:兼容性变更提示 2026-04-18 17:53:13 +08:00
CJKmkp 51dcc374ce add:兼容性变更提示 2026-04-18 17:23:21 +08:00
CJKmkp 1640238728 improve:墨迹纠正 2026-04-18 17:10:27 +08:00
CJKmkp 0fa4010625 代码优化 2026-04-18 17:01:18 +08:00
CJKmkp e6d391e98b 更新版本号 2026-04-18 16:58:35 +08:00
CJKmkp 6659db651d add:禁用硬件加速 & improve:UI 2026-04-18 16:50:44 +08:00
21 changed files with 505 additions and 178 deletions
+2 -2
View File
@@ -43,5 +43,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.18.9")] [assembly: AssemblyVersion("1.7.18.10")]
[assembly: AssemblyFileVersion("1.7.18.9")] [assembly: AssemblyFileVersion("1.7.18.10")]
+6
View File
@@ -152,6 +152,9 @@ namespace Ink_Canvas.Helpers
var node = legacy.InkDrawingNode; var node = legacy.InkDrawingNode;
var shape = node.GetShape(); var shape = node.GetShape();
if (shape == null)
return InkShapeRecognitionResult.Empty;
var hot = ClonePointCollection(node.HotPoints); var hot = ClonePointCollection(node.HotPoints);
return new InkShapeRecognitionResult( return new InkShapeRecognitionResult(
node.GetShapeName(), node.GetShapeName(),
@@ -173,6 +176,9 @@ namespace Ink_Canvas.Helpers
public static bool IsContainShapeType(string name) public static bool IsContainShapeType(string name)
{ {
if (string.IsNullOrEmpty(name))
return false;
if (name.Contains("Triangle") || name.Contains("Circle") || if (name.Contains("Triangle") || name.Contains("Circle") ||
name.Contains("Rectangle") || name.Contains("Diamond") || name.Contains("Rectangle") || name.Contains("Diamond") ||
name.Contains("Parallelogram") || name.Contains("Square") name.Contains("Parallelogram") || name.Contains("Square")
@@ -29,31 +29,13 @@ namespace Ink_Canvas.Helpers
public static bool IsApiAvailable => public static bool IsApiAvailable =>
OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10; OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10;
/// <summary>
/// 启动阶段不再预热线程内 WinRT 手写管线。历史上曾用 <see cref="WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection"/> 跑全链路,
/// 会显著拖慢启动;与更早的「空 <see cref="StrokeCollection"/>」一样,此处不再在 Idle 上做任何工作。
/// 首次真正需要手写识别时由 <see cref="RecognizeHandwritingAsync"/> 承担冷启动成本。
/// </summary>
public static void Warmup() public static void Warmup()
{ {
if (!IsApiAvailable || !Environment.Is64BitProcess) return;
try
{
var d = Application.Current?.Dispatcher;
if (d == null) return;
d.BeginInvoke(new Action(async () =>
{
try
{
await RecognizeHandwritingAsync(
WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(),
verboseTrace: false).ConfigureAwait(true);
}
catch
{
// ignore
}
}));
}
catch
{
// ignore
}
} }
/// <summary> /// <summary>
@@ -124,6 +124,9 @@ namespace Ink_Canvas.Helpers
return null; return null;
var da = stroke.DrawingAttributes; var da = stroke.DrawingAttributes;
if (da == null)
return null;
var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes
{ {
PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle, PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle,
+70
View File
@@ -624,6 +624,67 @@
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</Border> </Border>
<Border x:Name="Net472CompatibilityWarningPanel"
Background="#FFFFF4C2"
BorderBrush="#FFF0C24B"
BorderThickness="1"
CornerRadius="10"
Padding="14,12"
Margin="0,14,0,0"
Visibility="Collapsed">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
TextWrapping="Wrap"
Foreground="#FF2A2A2A"
FontSize="14"
Text="此版本为最后一个基于.NET Framework 4.7.2的版本,您需要手动确认才能继续接受自动更新。" />
<Button Grid.Column="1"
Margin="14,0,0,0"
Padding="18,6"
MinWidth="92"
Click="ConfirmNetCompatibilityChangeButton_Click"
Background="#FFFFFFFF"
BorderBrush="#FFE5E7EB"
Foreground="#FF111827"
Content="确认">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8"
SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"
Margin="{TemplateBinding Padding}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#FFF9FAFB" />
<Setter TargetName="Bd" Property="BorderBrush" Value="#FFD1D5DB" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#FFF3F4F6" />
<Setter TargetName="Bd" Property="BorderBrush" Value="#FF9CA3AF" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Bd" Property="Background" Value="#FFE7E7E7" />
<Setter Property="Foreground" Value="#FF7A7A7A" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Border>
<GroupBox Name="GroupBoxModeSettings"> <GroupBox Name="GroupBoxModeSettings">
<GroupBox.Header> <GroupBox.Header>
<TextBlock Margin="0,12,0,0" Text="{i18n:I18n Key=Settings_Mode}" FontWeight="Bold" Foreground="#fafafa" <TextBlock Margin="0,12,0,0" Text="{i18n:I18n Key=Settings_Mode}" FontWeight="Bold" Foreground="#fafafa"
@@ -803,6 +864,15 @@
FontSize="26" /> FontSize="26" />
</GroupBox.Header> </GroupBox.Header>
<ikw:SimpleStackPanel Spacing="6"> <ikw:SimpleStackPanel Spacing="6">
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_DisableHardwareAcceleration}" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" />
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchDisableHardwareAcceleration"
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchDisableHardwareAcceleration_Toggled" />
</ikw:SimpleStackPanel>
<TextBlock Text="{i18n:I18n Key=Canvas_DisableHardwareAccelerationHint}" TextWrapping="Wrap" Foreground="#a1a1aa" />
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_ShowCursor}" VerticalAlignment="Center" <TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Canvas_ShowCursor}" VerticalAlignment="Center"
FontSize="14" Margin="0,0,16,0" /> FontSize="14" Margin="0,0,16,0" />
+153
View File
@@ -1180,6 +1180,7 @@ namespace Ink_Canvas
loadPenCanvas(); loadPenCanvas();
//加载设置 //加载设置
LoadSettings(true); LoadSettings(true);
ScheduleNetCompatibilityChangePromptAfterStartup();
ApplyLanguageFromSettings(); ApplyLanguageFromSettings();
AutoBackupManager.Initialize(Settings); AutoBackupManager.Initialize(Settings);
CheckUpdateChannelAndTelemetryConsistency(); CheckUpdateChannelAndTelemetryConsistency();
@@ -1501,6 +1502,149 @@ namespace Ink_Canvas
AddTouchSupportToSliders(); AddTouchSupportToSliders();
} }
private void ShowNetCompatibilityChangePromptIfNeeded()
{
try
{
if (IsNetCompatibilityChangePromptAcknowledged() || IsNetCompatibilityChangeConfirmed())
{
return;
}
MessageBox.Show(
"下个版本将更换.NET6,请前往设置确认以便继续使用自动更新",
"兼容性变更",
MessageBoxButton.OK,
MessageBoxImage.Warning);
PersistNetCompatibilityChangePromptAcknowledgement();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"兼容性变更提示弹窗失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void ScheduleNetCompatibilityChangePromptAfterStartup()
{
Dispatcher.BeginInvoke(new Action(async () =>
{
try
{
// 等待主窗口启动流程与初始渲染基本完成后再提示,避免“刚启动就弹窗”。
await Task.Delay(1000);
ShowNetCompatibilityChangePromptIfNeeded();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"延迟显示兼容性变更提示失败: {ex.Message}", LogHelper.LogType.Error);
}
}), DispatcherPriority.ApplicationIdle);
}
private string GetNetCompatibilityConfirmationFlagPath()
{
return Path.Combine(App.RootPath, "Configs", "NetCompatibilityConfirmed.flag");
}
private string GetNetCompatibilityPromptAcknowledgedFlagPath()
{
return Path.Combine(App.RootPath, "Configs", "NetCompatibilityPromptAcknowledged.flag");
}
private bool IsNetCompatibilityChangePromptAcknowledged()
{
try
{
return File.Exists(GetNetCompatibilityPromptAcknowledgedFlagPath());
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"读取兼容性提示弹窗标记失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
private void PersistNetCompatibilityChangePromptAcknowledgement()
{
try
{
var flagPath = GetNetCompatibilityPromptAcknowledgedFlagPath();
var dir = Path.GetDirectoryName(flagPath);
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
if (!File.Exists(flagPath))
{
File.WriteAllText(flagPath, "acknowledged=true");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"写入兼容性提示弹窗标记失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private bool IsNetCompatibilityChangeConfirmed()
{
if (Settings?.Startup?.HasConfirmedNetCompatibilityChange == true)
{
return true;
}
try
{
var flagPath = GetNetCompatibilityConfirmationFlagPath();
if (File.Exists(flagPath))
{
if (Settings?.Startup != null && !Settings.Startup.HasConfirmedNetCompatibilityChange)
{
Settings.Startup.HasConfirmedNetCompatibilityChange = true;
SaveSettingsToFile();
}
return true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"读取兼容性确认标记失败: {ex.Message}", LogHelper.LogType.Error);
}
return false;
}
private void PersistNetCompatibilityChangeConfirmation()
{
if (Settings?.Startup == null)
{
Settings.Startup = new Startup();
}
Settings.Startup.HasConfirmedNetCompatibilityChange = true;
try
{
var flagPath = GetNetCompatibilityConfirmationFlagPath();
var dir = Path.GetDirectoryName(flagPath);
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
if (!File.Exists(flagPath))
{
File.WriteAllText(flagPath, "confirmed=true");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"写入兼容性确认标记失败: {ex.Message}", LogHelper.LogType.Error);
}
SaveSettingsToFile();
}
private void ApplyLanguageFromSettings() private void ApplyLanguageFromSettings()
{ {
try try
@@ -1917,6 +2061,12 @@ namespace Ink_Canvas
private async void AutoUpdate() private async void AutoUpdate()
{ {
if (Settings?.Startup?.HasConfirmedNetCompatibilityChange != true)
{
LogHelper.WriteLogToFile("AutoUpdate | 自动更新已暂停:未确认兼容性变更(NET472 -> .NET6", LogHelper.LogType.Warning);
return;
}
if (!string.IsNullOrEmpty(Settings.Startup.AutoUpdatePauseUntilDate)) if (!string.IsNullOrEmpty(Settings.Startup.AutoUpdatePauseUntilDate))
{ {
if (DateTime.TryParse(Settings.Startup.AutoUpdatePauseUntilDate, out DateTime pauseUntilDate)) if (DateTime.TryParse(Settings.Startup.AutoUpdatePauseUntilDate, out DateTime pauseUntilDate))
@@ -4383,6 +4533,9 @@ namespace Ink_Canvas
{ {
try try
{ {
if (!IsLoaded)
return;
if (Settings.ModeSettings.IsPPTOnlyMode) if (Settings.ModeSettings.IsPPTOnlyMode)
{ {
if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value) if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value)
-2
View File
@@ -1,13 +1,11 @@
using Ink_Canvas.Helpers; using Ink_Canvas.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Ink; using System.Windows.Ink;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
namespace Ink_Canvas namespace Ink_Canvas
+57 -45
View File
@@ -617,6 +617,18 @@ namespace Ink_Canvas
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
{ {
if (!isLoaded) return; if (!isLoaded) return;
if (ToggleSwitchIsAutoUpdate.IsOn && Settings?.Startup?.HasConfirmedNetCompatibilityChange != true)
{
ToggleSwitchIsAutoUpdate.IsOn = false;
MessageBox.Show(
"此版本为最后一个NET472版本,您需要手动确认才能继续接受自动更新。",
"兼容性变更",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
Settings.Startup.IsAutoUpdate = ToggleSwitchIsAutoUpdate.IsOn; Settings.Startup.IsAutoUpdate = ToggleSwitchIsAutoUpdate.IsOn;
// 自动更新关闭时隐藏静默更新选项 // 自动更新关闭时隐藏静默更新选项
@@ -651,6 +663,18 @@ namespace Ink_Canvas
private void ToggleSwitchIsAutoUpdateWithSilence_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchIsAutoUpdateWithSilence_Toggled(object sender, RoutedEventArgs e)
{ {
if (!isLoaded) return; if (!isLoaded) return;
if (ToggleSwitchIsAutoUpdateWithSilence.IsOn && Settings?.Startup?.HasConfirmedNetCompatibilityChange != true)
{
ToggleSwitchIsAutoUpdateWithSilence.IsOn = false;
MessageBox.Show(
"此版本为最后一个NET472版本,您需要手动确认才能继续接受自动更新。",
"兼容性变更",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
Settings.Startup.IsAutoUpdateWithSilence = ToggleSwitchIsAutoUpdateWithSilence.IsOn; Settings.Startup.IsAutoUpdateWithSilence = ToggleSwitchIsAutoUpdateWithSilence.IsOn;
// 静默更新的时间设置区域只在静默更新开启时显示 // 静默更新的时间设置区域只在静默更新开启时显示
@@ -3331,55 +3355,16 @@ namespace Ink_Canvas
SaveSettingsToFile(); SaveSettingsToFile();
} }
// 注释掉这些方法,因为对应的UI控件还没有在XAML中定义 private void ToggleSwitchDisableHardwareAcceleration_Toggled(object sender, RoutedEventArgs e)
/* {
private void ToggleSwitchAsyncInkSmoothing_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return; if (!isLoaded) return;
Settings.Canvas.UseAsyncInkSmoothing = ToggleSwitchAsyncInkSmoothing.IsOn;
Settings.Canvas.UseHardwareAcceleration = !ToggleSwitchDisableHardwareAcceleration.IsOn;
_inkSmoothingManager?.UpdateConfig(); _inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile(); SaveSettingsToFile();
} }
private void ToggleSwitchHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.UseHardwareAcceleration = ToggleSwitchHardwareAcceleration.IsOn;
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
}
private void ComboBoxInkSmoothingQuality_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (!isLoaded) return;
Settings.Canvas.InkSmoothingQuality = ComboBoxInkSmoothingQuality.SelectedIndex;
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
}
private void SliderMaxConcurrentTasks_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
if (!isLoaded) return;
Settings.Canvas.MaxConcurrentSmoothingTasks = (int)SliderMaxConcurrentTasks.Value;
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
}
private void ButtonApplyRecommendedSettings_Click(object sender, RoutedEventArgs e) {
// 应用推荐的性能设置
Helpers.InkSmoothingManager.ApplyRecommendedSettings();
LoadSettings(false);
_inkSmoothingManager?.UpdateConfig();
SaveSettingsToFile();
ShowNotification("已应用推荐的性能设置");
}
private void ButtonShowPerformanceStats_Click(object sender, RoutedEventArgs e) {
if (_inkSmoothingManager != null)
{
var stats = _inkSmoothingManager.GetPerformanceStats();
ShowNotification($"性能统计: {stats}");
}
}
*/
private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e)
{ {
if (!isLoaded) return; if (!isLoaded) return;
@@ -5342,7 +5327,7 @@ namespace Ink_Canvas
SaveSettingsToFile(); SaveSettingsToFile();
// 如果启用了自动更新,立即执行完整的检查更新操作 // 如果启用了自动更新,立即执行完整的检查更新操作
if (Settings.Startup.IsAutoUpdate) if (Settings.Startup.IsAutoUpdate && Settings.Startup.HasConfirmedNetCompatibilityChange)
{ {
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check"); LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
ResetUpdateCheckRetry(); ResetUpdateCheckRetry();
@@ -5371,6 +5356,33 @@ namespace Ink_Canvas
} }
} }
private void ConfirmNetCompatibilityChangeButton_Click(object sender, RoutedEventArgs e)
{
try
{
PersistNetCompatibilityChangeConfirmation();
if (Net472CompatibilityWarningPanel != null)
{
Net472CompatibilityWarningPanel.Visibility = Visibility.Collapsed;
}
if (ToggleSwitchIsAutoUpdate != null)
{
ToggleSwitchIsAutoUpdate.IsEnabled = true;
}
if (ToggleSwitchIsAutoUpdateWithSilence != null)
{
ToggleSwitchIsAutoUpdateWithSilence.IsEnabled = true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"确认兼容性变更失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e) private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
{ {
ManualUpdateButton.IsEnabled = false; ManualUpdateButton.IsEnabled = false;
+24 -17
View File
@@ -267,7 +267,7 @@ namespace Ink_Canvas
ToggleSwitchIsAutoUpdate.IsOn = Settings.Startup.IsAutoUpdate; ToggleSwitchIsAutoUpdate.IsOn = Settings.Startup.IsAutoUpdate;
// 只有在启用了自动更新功能时才检查更新 // 只有在启用了自动更新功能时才检查更新
if (Settings.Startup.IsAutoUpdate && !skipAutoUpdateCheck) if (Settings.Startup.IsAutoUpdate && Settings.Startup.HasConfirmedNetCompatibilityChange && !skipAutoUpdateCheck)
{ {
if (isStartup) if (isStartup)
{ {
@@ -288,6 +288,8 @@ namespace Ink_Canvas
ToggleSwitchIsAutoUpdateWithSilence.IsOn = true; ToggleSwitchIsAutoUpdateWithSilence.IsOn = true;
} }
ApplyNetCompatibilityConfirmationGateToUpdateSettingsUi();
// 初始化更新通道选择 // 初始化更新通道选择
foreach (var radioButton in UpdateChannelSelector.Items) foreach (var radioButton in UpdateChannelSelector.Items)
{ {
@@ -954,22 +956,7 @@ namespace Ink_Canvas
drawingAttributes.FitToCurve = false; drawingAttributes.FitToCurve = false;
} }
// 注释掉新的墨迹平滑性能设置,因为UI控件还没有定义 ToggleSwitchDisableHardwareAcceleration.IsOn = !Settings.Canvas.UseHardwareAcceleration;
/*
// 初始化新的墨迹平滑性能设置
ToggleSwitchAsyncInkSmoothing.IsOn = Settings.Canvas.UseAsyncInkSmoothing;
ToggleSwitchHardwareAcceleration.IsOn = Settings.Canvas.UseHardwareAcceleration;
ComboBoxInkSmoothingQuality.SelectedIndex = Settings.Canvas.InkSmoothingQuality;
SliderMaxConcurrentTasks.Value = Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount;
// 检查硬件加速支持
if (!Helpers.InkSmoothingManager.IsHardwareAccelerationSupported())
{
ToggleSwitchHardwareAcceleration.IsEnabled = false;
// 可以添加提示文本说明硬件加速不可用
}
*/
// 初始化直线自动拉直相关设置 // 初始化直线自动拉直相关设置
ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine; ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine;
@@ -1332,6 +1319,26 @@ namespace Ink_Canvas
try { RefreshConfigProfileList(); } catch (Exception ex) { LogHelper.WriteLogToFile($"刷新配置文件列表失败: {ex.Message}", LogHelper.LogType.Warning); } try { RefreshConfigProfileList(); } catch (Exception ex) { LogHelper.WriteLogToFile($"刷新配置文件列表失败: {ex.Message}", LogHelper.LogType.Warning); }
} }
private void ApplyNetCompatibilityConfirmationGateToUpdateSettingsUi()
{
bool confirmed = Settings?.Startup?.HasConfirmedNetCompatibilityChange == true;
if (Net472CompatibilityWarningPanel != null)
{
Net472CompatibilityWarningPanel.Visibility = confirmed ? Visibility.Collapsed : Visibility.Visible;
}
if (ToggleSwitchIsAutoUpdate != null)
{
ToggleSwitchIsAutoUpdate.IsEnabled = confirmed;
}
if (ToggleSwitchIsAutoUpdateWithSilence != null)
{
ToggleSwitchIsAutoUpdateWithSilence.IsEnabled = confirmed;
}
}
/// <summary> /// <summary>
/// 将画笔自动恢复相关的设置应用到界面控件并在启用时初始化自动恢复定时器。 /// 将画笔自动恢复相关的设置应用到界面控件并在启用时初始化自动恢复定时器。
/// </summary> /// </summary>
@@ -718,6 +718,18 @@ namespace Ink_Canvas
var endP = new Point(result.Centroid.X + result.ShapeWidth / 2, var endP = new Point(result.Centroid.X + result.ShapeWidth / 2,
result.Centroid.Y + result.ShapeHeight / 2); result.Centroid.Y + result.ShapeHeight / 2);
// WinRT 返回的热点顺序/方向不稳定时,用点集反推 IACore 风格椭圆参数(中心/长短轴/方向/四个端点)
var hasEllipseParams = TryEstimateEllipseParamsFromStrokes(
result.StrokesToRemove,
out var ellipseCentroid,
out var ellipseA,
out var ellipseB,
out var ellipseThetaRad,
out var ellipseMajor0,
out var ellipseMajor1,
out var ellipseMinor0,
out var ellipseMinor1);
foreach (var circle in circles) foreach (var circle in circles)
//判断是否画同心椭圆 //判断是否画同心椭圆
if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 && if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 &&
@@ -805,13 +817,17 @@ namespace Ink_Canvas
} }
} }
//纠正垂直与水平关系 // 用反推参数替换中心与长短轴(比 WinRT 的包围盒更接近 IACore,且不会竖横翻转)
var newPoints = FixPointsDirection(p[0], p[2]); if (hasEllipseParams)
p[0] = newPoints[0]; {
p[2] = newPoints[1]; result.Centroid = ellipseCentroid;
newPoints = FixPointsDirection(p[1], p[3]); a = ellipseA;
p[1] = newPoints[0]; b = ellipseB;
p[3] = newPoints[1]; iniP = new Point(result.Centroid.X - a, result.Centroid.Y - b);
endP = new Point(result.Centroid.X + a, result.Centroid.Y + b);
// 用端点重写热点,保证后续回退分支也一致
p = new PointCollection { ellipseMajor0, ellipseMinor0, ellipseMajor1, ellipseMinor1 };
}
var pointList = GenerateEllipseGeometry(iniP, endP); var pointList = GenerateEllipseGeometry(iniP, endP);
var point = new StylusPointCollection(pointList); var point = new StylusPointCollection(pointList);
@@ -823,9 +839,8 @@ namespace Ink_Canvas
if (needRotation) if (needRotation)
{ {
var m = new Matrix(); var m = new Matrix();
var fe = e.Source as FrameworkElement; // 优先使用反推参数角度;否则用端点向量角度(使用 Atan2 避免斜率无穷)
var tanTheta = (p[2].Y - p[0].Y) / (p[2].X - p[0].X); var theta = hasEllipseParams ? ellipseThetaRad : Math.Atan2(p[2].Y - p[0].Y, p[2].X - p[0].X);
var theta = Math.Atan(tanTheta);
m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y); m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y);
stroke.Transform(m, false); stroke.Transform(m, false);
} }
@@ -2282,6 +2297,124 @@ namespace Ink_Canvas
return new Point[2] { p1, p2 }; return new Point[2] { p1, p2 };
} }
/// <summary>
/// 用点集拟合出 IACore 风格椭圆参数(中心/长短半轴/方向/四个端点)。
/// 解决 WinRT 返回热点顺序不稳定导致椭圆纠正角度翻转的问题。
/// </summary>
private static bool TryEstimateEllipseParamsFromStrokes(
StrokeCollection strokes,
out Point centroid,
out double a,
out double b,
out double thetaRad,
out Point major0,
out Point major1,
out Point minor0,
out Point minor1)
{
centroid = default;
a = b = 0;
thetaRad = 0;
major0 = major1 = minor0 = minor1 = default;
if (strokes == null || strokes.Count == 0) return false;
var pts = new List<Point>(256);
foreach (var s in strokes)
{
if (s?.StylusPoints == null) continue;
foreach (var sp in s.StylusPoints)
pts.Add(sp.ToPoint());
}
if (pts.Count < 12) return false;
double mx = 0, my = 0;
for (int i = 0; i < pts.Count; i++)
{
mx += pts[i].X;
my += pts[i].Y;
}
mx /= pts.Count;
my /= pts.Count;
centroid = new Point(mx, my);
double sxx = 0, syy = 0, sxy = 0;
for (int i = 0; i < pts.Count; i++)
{
var dx = pts[i].X - mx;
var dy = pts[i].Y - my;
sxx += dx * dx;
syy += dy * dy;
sxy += dx * dy;
}
if (sxx + syy < 1e-6) return false;
thetaRad = 0.5 * Math.Atan2(2.0 * sxy, sxx - syy);
if (double.IsNaN(thetaRad) || double.IsInfinity(thetaRad)) return false;
// 主轴单位向量 v1=(cos,sin),次轴 v2=(-sin,cos)
var cos = Math.Cos(thetaRad);
var sin = Math.Sin(thetaRad);
// 投影收集,用分位数抑制离群点
var us = new double[pts.Count];
var vs = new double[pts.Count];
double maxU = double.MinValue, minU = double.MaxValue;
double maxV = double.MinValue, minV = double.MaxValue;
for (int i = 0; i < pts.Count; i++)
{
var dx = pts[i].X - mx;
var dy = pts[i].Y - my;
var u = dx * cos + dy * sin;
var v = -dx * sin + dy * cos;
us[i] = u;
vs[i] = v;
if (u > maxU) maxU = u;
if (u < minU) minU = u;
if (v > maxV) maxV = v;
if (v < minV) minV = v;
}
Array.Sort(us);
Array.Sort(vs);
int hi = (int)Math.Round((pts.Count - 1) * 0.98);
int lo = (int)Math.Round((pts.Count - 1) * 0.02);
hi = Math.Max(0, Math.Min(pts.Count - 1, hi));
lo = Math.Max(0, Math.Min(pts.Count - 1, lo));
var uHi = us[hi];
var uLo = us[lo];
var vHi = vs[hi];
var vLo = vs[lo];
var aCandidate = Math.Max(Math.Abs(uHi), Math.Abs(uLo));
var bCandidate = Math.Max(Math.Abs(vHi), Math.Abs(vLo));
if (aCandidate < 1e-3) aCandidate = Math.Max(Math.Abs(maxU), Math.Abs(minU));
if (bCandidate < 1e-3) bCandidate = Math.Max(Math.Abs(maxV), Math.Abs(minV));
a = aCandidate;
b = bCandidate;
// 保证 a 为长半轴
if (b > a)
{
var t = a; a = b; b = t;
thetaRad += Math.PI / 2;
cos = Math.Cos(thetaRad);
sin = Math.Sin(thetaRad);
}
major0 = new Point(mx - a * cos, my - a * sin);
major1 = new Point(mx + a * cos, my + a * sin);
minor0 = new Point(mx + b * sin, my - b * cos);
minor1 = new Point(mx - b * sin, my + b * cos);
return a > 1e-2 && b > 1e-2;
}
public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points) public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points)
{ {
var newPoint = new StylusPointCollection(); var newPoint = new StylusPointCollection();
+2 -2
View File
@@ -43,5 +43,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.18.9")] [assembly: AssemblyVersion("1.7.18.10")]
[assembly: AssemblyFileVersion("1.7.18.9")] [assembly: AssemblyFileVersion("1.7.18.10")]
+12
View File
@@ -528,6 +528,12 @@
<data name="InkRecog_EnableInkRecognition" xml:space="preserve"> <data name="InkRecog_EnableInkRecognition" xml:space="preserve">
<value>Enable ink recognition</value> <value>Enable ink recognition</value>
</data> </data>
<data name="InkRecog_HandwritingBeautify" xml:space="preserve">
<value>Handwriting correction</value>
</data>
<data name="InkRecog_HandwritingBeautifyHint" xml:space="preserve">
<value># When enabled, strokes will be corrected to look smoother and neater (WinRT-based).</value>
</data>
<data name="InkRecog_BlockRectFakePressure" xml:space="preserve"> <data name="InkRecog_BlockRectFakePressure" xml:space="preserve">
<value>Block fake pressure on corrected rectangles</value> <value>Block fake pressure on corrected rectangles</value>
</data> </data>
@@ -957,6 +963,12 @@
<data name="Canvas_GroupTitle" xml:space="preserve"> <data name="Canvas_GroupTitle" xml:space="preserve">
<value>Canvas and ink</value> <value>Canvas and ink</value>
</data> </data>
<data name="Canvas_DisableHardwareAcceleration" xml:space="preserve">
<value>Disable hardware acceleration</value>
</data>
<data name="Canvas_DisableHardwareAccelerationHint" xml:space="preserve">
<value># Improves compatibility but may reduce performance; some effects may require an app restart to fully apply.</value>
</data>
<data name="Canvas_ShowCursor" xml:space="preserve"> <data name="Canvas_ShowCursor" xml:space="preserve">
<value>Show pen cursor</value> <value>Show pen cursor</value>
</data> </data>
+12
View File
@@ -549,6 +549,12 @@
<data name="InkRecog_ShapeEngineHint" xml:space="preserve"> <data name="InkRecog_ShapeEngineHint" xml:space="preserve">
<value> 自动:64 位进程使用 WinRTWindows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value> <value> 自动:64 位进程使用 WinRTWindows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value>
</data> </data>
<data name="InkRecog_HandwritingBeautify" xml:space="preserve">
<value>手写体纠正</value>
</data>
<data name="InkRecog_HandwritingBeautifyHint" xml:space="preserve">
<value># 开启后,将对手写笔画进行更平滑、更工整的纠正处理(基于 WinRT)。</value>
</data>
<data name="InkRecog_ShapeEngineAuto" xml:space="preserve"> <data name="InkRecog_ShapeEngineAuto" xml:space="preserve">
<value>自动</value> <value>自动</value>
</data> </data>
@@ -999,6 +1005,12 @@
<data name="Canvas_GroupTitle" xml:space="preserve"> <data name="Canvas_GroupTitle" xml:space="preserve">
<value>画板和墨迹</value> <value>画板和墨迹</value>
</data> </data>
<data name="Canvas_DisableHardwareAcceleration" xml:space="preserve">
<value>关闭硬件加速</value>
</data>
<data name="Canvas_DisableHardwareAccelerationHint" xml:space="preserve">
<value># 关闭后可提升兼容性,但可能降低性能;部分效果可能需要重启程序后完全生效。</value>
</data>
<data name="Canvas_ShowCursor" xml:space="preserve"> <data name="Canvas_ShowCursor" xml:space="preserve">
<value>显示画笔光标</value> <value>显示画笔光标</value>
</data> </data>
+2
View File
@@ -250,6 +250,8 @@ namespace Ink_Canvas
public bool HasAcceptedTelemetryPrivacy { get; set; } = false; public bool HasAcceptedTelemetryPrivacy { get; set; } = false;
[JsonProperty("hasShownOobe")] [JsonProperty("hasShownOobe")]
public bool HasShownOobe { get; set; } = false; public bool HasShownOobe { get; set; } = false;
[JsonProperty("hasConfirmedNetCompatibilityChange")]
public bool HasConfirmedNetCompatibilityChange { get; set; } = false;
} }
public class Appearance public class Appearance
@@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows; using System.Windows;
@@ -1,17 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,17 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,17 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,17 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Ink_Canvas.Windows.SettingsViews.Pages namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
@@ -2,11 +2,10 @@ using Ink_Canvas.Windows.SettingsViews.Pages;
using iNKORE.UI.WPF.Modern.Controls; using iNKORE.UI.WPF.Modern.Controls;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Interop;
using System.Windows.Input;
using System.Linq; using System.Linq;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Navigation;
using MessageBox = System.Windows.MessageBox; using MessageBox = System.Windows.MessageBox;
using Screen = System.Windows.Forms.Screen; using Screen = System.Windows.Forms.Screen;