diff --git a/Other/Translator/TranslatorWindow.xaml.cs b/Other/Translator/TranslatorWindow.xaml.cs index 073fc7f3..56798c2a 100644 --- a/Other/Translator/TranslatorWindow.xaml.cs +++ b/Other/Translator/TranslatorWindow.xaml.cs @@ -26,11 +26,10 @@ namespace Translator; public partial class TranslatorWindow : Window { - private static string TempPath => Path.Combine(".", "ScreenToGif", "Resources"); - private readonly List _resourceList = new(); private IEnumerable _cultures; private ObservableCollection _translationList = new(); + private string _tempPath; private string _resourceTemplate; public TranslatorWindow() @@ -42,8 +41,7 @@ public TranslatorWindow() private async void Window_Loaded(object sender, RoutedEventArgs e) { - if (!Directory.Exists(TempPath)) - Directory.CreateDirectory(TempPath); + PrepareTempPath(); OpenButton.IsEnabled = false; RefreshButton.IsEnabled = false; @@ -220,17 +218,18 @@ private async void Load_Executed(object sender, ExecutedRoutedEventArgs e) CheckFileExists = true, Title = "Open a Resource Dictionary", Filter = "Resource Dictionay (*.xaml)|*.xaml;", - InitialDirectory = Path.GetFullPath(TempPath) + InitialDirectory = Path.GetFullPath(_tempPath) }; var result = ofd.ShowDialog(); - if (!result.HasValue || !result.Value) return; + if (!result.HasValue || !result.Value) + return; //Will save the file to other folder. - var tempFile = Path.Combine(TempPath, "Temp", Path.GetFileName(ofd.FileName)); + var tempFile = Path.Combine(_tempPath, "Temp", Path.GetFileName(ofd.FileName)); - Directory.CreateDirectory(Path.Combine(TempPath, "Temp")); + Directory.CreateDirectory(Path.Combine(_tempPath, "Temp")); //Replaces the special chars. var text = await Task.Factory.StartNew(() => File.ReadAllText(ofd.FileName, Encoding.UTF8).Replace("&#", "&#").Replace("-->", "-->")); @@ -290,7 +289,8 @@ private async void Export_Executed(object sender, ExecutedRoutedEventArgs e) var result = sfd.ShowDialog(); - if (!result.HasValue || !result.Value) return; + if (!result.HasValue || !result.Value) + return; BaseDataGrid.IsEnabled = false; StatusBand.Info("Exporting translation..."); @@ -321,6 +321,14 @@ private void Window_Closing(object sender, CancelEventArgs e) #region Methods + private void PrepareTempPath() + { + _tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenToGif", "Resources"); + + if (!Directory.Exists(_tempPath)) + Directory.CreateDirectory(_tempPath); + } + private async Task DownloadSingleResourceAsync(string culture) { try @@ -425,7 +433,7 @@ private async Task DownloadFileAsync2(Uri uri, string name) { try { - var file = Path.Combine(Dispatcher.Invoke(() => TempPath), name); + var file = Path.Combine(Dispatcher.Invoke(() => _tempPath), name); if (File.Exists(file)) File.Delete(file); @@ -464,7 +472,7 @@ private async Task DownloadFileAsync(Uri uri, string name) { try { - var file = Path.Combine(Dispatcher.Invoke(() => TempPath), name); + var file = Path.Combine(Dispatcher.Invoke(() => _tempPath), name); if (File.Exists(file)) File.Delete(file); @@ -503,7 +511,7 @@ private async Task LoadFilesAsync() { try { - var files = await Task.Factory.StartNew(() => Directory.EnumerateFiles(TempPath, "*.xaml")); + var files = await Task.Factory.StartNew(() => Directory.EnumerateFiles(_tempPath, "*.xaml")); foreach (var file in files) { diff --git a/ScreenToGif.Model/Enums/EncoderTypes.cs b/ScreenToGif.Model/Enums/EncoderTypes.cs index 830f1dee..e2f2f360 100644 --- a/ScreenToGif.Model/Enums/EncoderTypes.cs +++ b/ScreenToGif.Model/Enums/EncoderTypes.cs @@ -3,6 +3,7 @@ namespace ScreenToGif.Domain.Enums; public enum EncoderTypes { ScreenToGif, //Gif, Apng + KGySoft, // Gif System, //Gif, Video FFmpeg, //Gif, Webp, Apng, Video Gifski //Gif diff --git a/ScreenToGif.Model/Enums/PanelType.cs b/ScreenToGif.Model/Enums/PanelType.cs index 8509cd2f..41113f3a 100644 --- a/ScreenToGif.Model/Enums/PanelType.cs +++ b/ScreenToGif.Model/Enums/PanelType.cs @@ -69,9 +69,9 @@ public enum PanelTypes RemoveDuplicates = 13, /// - /// Mouse Clicks Panel. + /// Mouse Events Panel. /// - MouseClicks = 14, + MouseEvents = 14, /// /// Smooth Loop Panel. diff --git a/ScreenToGif.Model/Enums/TaskTypes.cs b/ScreenToGif.Model/Enums/TaskTypes.cs index f1b2f62e..8980c4b2 100644 --- a/ScreenToGif.Model/Enums/TaskTypes.cs +++ b/ScreenToGif.Model/Enums/TaskTypes.cs @@ -3,7 +3,7 @@ namespace ScreenToGif.Domain.Enums; public enum TaskTypes { NotDeclared = 0, - MouseClicks = 1, + MouseEvents = 1, KeyStrokes = 2, Delay = 3, Progress = 4, diff --git a/ScreenToGif.Model/Enums/VideoCodecPresets.cs b/ScreenToGif.Model/Enums/VideoCodecPresets.cs index 98b58e52..88d977e4 100644 --- a/ScreenToGif.Model/Enums/VideoCodecPresets.cs +++ b/ScreenToGif.Model/Enums/VideoCodecPresets.cs @@ -5,6 +5,7 @@ namespace ScreenToGif.Domain.Enums; [SuppressMessage("ReSharper", "InconsistentNaming")] public enum VideoCodecPresets { + Auto = -1, NotSelected, None, diff --git a/ScreenToGif.Model/Enums/VideoCodecs.cs b/ScreenToGif.Model/Enums/VideoCodecs.cs index 5ea12c1a..c27fcbb3 100644 --- a/ScreenToGif.Model/Enums/VideoCodecs.cs +++ b/ScreenToGif.Model/Enums/VideoCodecs.cs @@ -40,5 +40,14 @@ public enum VideoCodecs Vp8, [Description("libvpx-vp9")] - Vp9 + Vp9, + + [Description("libaom-av1")] + LibAom, + + [Description("libsvtav1")] + SvtAv1, + + [Description("librav1e")] + Rav1E, } \ No newline at end of file diff --git a/ScreenToGif.Model/Enums/VideoPixelFormats.cs b/ScreenToGif.Model/Enums/VideoPixelFormats.cs index b3ada0c0..1d4ffc2f 100644 --- a/ScreenToGif.Model/Enums/VideoPixelFormats.cs +++ b/ScreenToGif.Model/Enums/VideoPixelFormats.cs @@ -24,6 +24,7 @@ public enum VideoPixelFormats Gbrp12Le, Gray, Gray10Le, + Gray12Le, Gray16Be, MonoB, Nv12, diff --git a/ScreenToGif.Model/ScreenToGif.Domain.csproj b/ScreenToGif.Model/ScreenToGif.Domain.csproj index 5cb338fc..ca4c1756 100644 --- a/ScreenToGif.Model/ScreenToGif.Domain.csproj +++ b/ScreenToGif.Model/ScreenToGif.Domain.csproj @@ -7,7 +7,7 @@ embedded True AnyCPU;ARM64;x64;x86 - 2.36.0 + 2.37.0 diff --git a/ScreenToGif.Native/ScreenToGif.Native.csproj b/ScreenToGif.Native/ScreenToGif.Native.csproj index fa31d8dd..7ccb8474 100644 --- a/ScreenToGif.Native/ScreenToGif.Native.csproj +++ b/ScreenToGif.Native/ScreenToGif.Native.csproj @@ -7,7 +7,7 @@ True embedded AnyCPU;ARM64;x64;x86 - 2.36.0 + 2.37.0 diff --git a/ScreenToGif.Test/ScreenToGif.Test.csproj b/ScreenToGif.Test/ScreenToGif.Test.csproj index 222c26aa..e0acd1aa 100644 --- a/ScreenToGif.Test/ScreenToGif.Test.csproj +++ b/ScreenToGif.Test/ScreenToGif.Test.csproj @@ -12,13 +12,13 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/ScreenToGif.Util/Converters/TimeSpanToString.cs b/ScreenToGif.Util/Converters/TimeSpanToString.cs new file mode 100644 index 00000000..06e3025f --- /dev/null +++ b/ScreenToGif.Util/Converters/TimeSpanToString.cs @@ -0,0 +1,26 @@ +using System.Globalization; +using System.Windows.Data; + +namespace ScreenToGif.Util.Converters; + +public class TimeSpanToString : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is not TimeSpan time) + return Binding.DoNothing; + + if (time.Days > 0) + return time.ToString("d\\:hh\\:mm\\:ss", culture); + + if (time.Hours > 0) + return time.ToString("h\\:mm\\:ss", culture); + + return time.ToString("mm\\:ss", culture); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return Binding.DoNothing; + } +} \ No newline at end of file diff --git a/ScreenToGif.Util/Extensions/WriteableBitmapExtensions.cs b/ScreenToGif.Util/Extensions/WriteableBitmapExtensions.cs new file mode 100644 index 00000000..6d5dae64 --- /dev/null +++ b/ScreenToGif.Util/Extensions/WriteableBitmapExtensions.cs @@ -0,0 +1,62 @@ +#region Usings + +#region Used Namespaces + +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +using KGySoft.Drawing.Imaging; + +#endregion + +#region Used Aliases + +using PixelFormat = System.Drawing.Imaging.PixelFormat; +using Size = System.Drawing.Size; + +#endregion + +#endregion + +namespace ScreenToGif.Util.Extensions; + +/// +/// Contains extension methods for the type. +/// +public static class WriteableBitmapExtensions +{ + #region Methods + + /// + /// Gets a managed read-write accessor for a instance. + /// + /// The bitmap to get the managed accessor. + /// An instance that provides managed access to the specified . + public static IReadWriteBitmapData GetReadWriteBitmapData(this WriteableBitmap bitmap) + { + if (bitmap == null) + throw new ArgumentNullException(nameof(bitmap)); + if (bitmap.IsFrozen) + throw new ArgumentException("Bitmap must not be frozen"); + + var format = bitmap.Format; + + // Actually you can support any other formats, including non-natively supported ones by custom PixelFormatInfo and getter/setter delegates + var pixelFormat = format == PixelFormats.Bgra32 ? PixelFormat.Format32bppArgb + : format == PixelFormats.Pbgra32 ? PixelFormat.Format32bppPArgb + : format == PixelFormats.Bgr32 ? PixelFormat.Format32bppRgb + : format == PixelFormats.Bgr24 ? PixelFormat.Format24bppRgb + : throw new NotSupportedException(bitmap.Format.ToString()); + + bitmap.Lock(); + return BitmapDataFactory.CreateBitmapData(bitmap.BackBuffer, new Size(bitmap.PixelWidth, bitmap.PixelHeight), bitmap.BackBufferStride, pixelFormat, + disposeCallback: () => + { + bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight)); + bitmap.Unlock(); + }); + } + + #endregion +} diff --git a/ScreenToGif.Util/LogWritter.cs b/ScreenToGif.Util/LogWritter.cs index c24d5ab4..0514be58 100644 --- a/ScreenToGif.Util/LogWritter.cs +++ b/ScreenToGif.Util/LogWritter.cs @@ -8,6 +8,35 @@ namespace ScreenToGif.Util; /// public static class LogWriter { + private static void WriteDetails(TextWriter writer, Exception ex, int level) + { + writer.WriteLine(new string('▬', level) + $" Message - {Environment.NewLine}\t{ex.Message}"); + writer.WriteLine(new string('○', level) + $" Type - {Environment.NewLine}\t{ex.GetType()}"); + writer.WriteLine(new string('▲', level) + $" Source - {Environment.NewLine}\t{ex.Source}"); + writer.WriteLine(new string('▼', level) + $" TargetSite - {Environment.NewLine}\t{ex.TargetSite}"); + + if (ex is BadImageFormatException bad) + { + writer.WriteLine(new string('☼', level) + $" Filename - {Environment.NewLine}\t{bad.FileName}"); + writer.WriteLine(new string('►', level) + $" Fuslog - {Environment.NewLine}\t{bad.FusionLog}"); + } + else if (ex is ArgumentException arg) + { + writer.WriteLine(new string('☼', level) + $" ParamName - {Environment.NewLine}\t{arg.ParamName}"); + } + + if (ex.HelpLink != null) + writer.WriteLine(new string('◘', level) + $" Other - {Environment.NewLine}\t{ex.HelpLink}"); + + writer.WriteLine(new string('♠', level) + $" StackTrace - {Environment.NewLine}{ex.StackTrace}"); + + if (ex.InnerException == null || level >= 6) + return; + + writer.WriteLine(); + WriteDetails(writer, ex.InnerException, level + 1); + } + /// /// Writes the exception details to the error log on disk. /// @@ -58,52 +87,12 @@ public static void Log(Exception ex, string title, object aditional = null, bool using (var writer = new StreamWriter(fileStream)) { writer.WriteLine($"► Title - {Environment.NewLine}\t{title}"); - writer.WriteLine($"▬ Message - {Environment.NewLine}\t{ex.Message}"); - writer.WriteLine($"○ Type - {Environment.NewLine}\t{ex.GetType()}"); writer.WriteLine(FormattableString.Invariant($"♦ [Version] Date/Hour - {Environment.NewLine}\t[{UserSettings.All?.VersionText}] {DateTime.Now}")); - writer.WriteLine($"▲ Source - {Environment.NewLine}\t{ex.Source}"); - writer.WriteLine($"▼ TargetSite - {Environment.NewLine}\t{ex.TargetSite}"); - - if (ex is BadImageFormatException bad) - writer.WriteLine($"► Fuslog - {Environment.NewLine}\t{bad.FusionLog}"); if (aditional != null) writer.WriteLine($"◄ Aditional - {Environment.NewLine}\t{aditional}"); - if (ex.HelpLink != null) - writer.WriteLine($"◘ Other - {Environment.NewLine}\t{ex.HelpLink}"); - - writer.WriteLine($"♠ StackTrace - {Environment.NewLine}{ex.StackTrace}"); - - if (ex.InnerException != null) - { - writer.WriteLine(); - writer.WriteLine($"▬▬ Message - {Environment.NewLine}\t{ex.InnerException.Message}"); - writer.WriteLine($"○○ Type - {Environment.NewLine}\t{ex.InnerException.GetType()}"); - writer.WriteLine($"▲▲ Source - {Environment.NewLine}\t{ex.InnerException.Source}"); - writer.WriteLine($"▼▼ TargetSite - {Environment.NewLine}\t{ex.InnerException.TargetSite}"); - writer.WriteLine($"♠♠ StackTrace - {Environment.NewLine}{ex.InnerException.StackTrace}"); - - if (ex.InnerException.InnerException != null) - { - writer.WriteLine(); - writer.WriteLine($"▬▬▬ Message - {Environment.NewLine}\t{ex.InnerException.InnerException.Message}"); - writer.WriteLine($"○○○ Type - {Environment.NewLine}\t{ex.InnerException.InnerException.GetType()}"); - writer.WriteLine($"▲▲▲ Source - {Environment.NewLine}\t{ex.InnerException.InnerException.Source}"); - writer.WriteLine($"▼▼▼ TargetSite - {Environment.NewLine}\t{ex.InnerException.InnerException.TargetSite}"); - writer.WriteLine($"♠♠♠ StackTrace - {Environment.NewLine}\t{ex.InnerException.InnerException.StackTrace}"); - - if (ex.InnerException.InnerException.InnerException != null) - { - writer.WriteLine(); - writer.WriteLine($"▬▬▬▬ Message - {Environment.NewLine}\t{ex.InnerException.InnerException.InnerException.Message}"); - writer.WriteLine($"○○○○ Type - {Environment.NewLine}\t{ex.InnerException.InnerException.InnerException.GetType()}"); - writer.WriteLine($"▲▲▲▲ Source - {Environment.NewLine}\t{ex.InnerException.InnerException.InnerException.Source}"); - writer.WriteLine($"▼▼▼▼ TargetSite - {Environment.NewLine}\t{ex.InnerException.InnerException.InnerException.TargetSite}"); - writer.WriteLine($"♠♠♠♠ StackTrace - {Environment.NewLine}\t{ex.InnerException.InnerException.InnerException.StackTrace}"); - } - } - } + WriteDetails(writer, ex, 1); writer.WriteLine(); writer.WriteLine("----------------------------------"); diff --git a/ScreenToGif.Util/ScreenToGif.Util.csproj b/ScreenToGif.Util/ScreenToGif.Util.csproj index 574b82fd..8fb76cf3 100644 --- a/ScreenToGif.Util/ScreenToGif.Util.csproj +++ b/ScreenToGif.Util/ScreenToGif.Util.csproj @@ -1,4 +1,4 @@ - + net6.0-windows @@ -6,12 +6,13 @@ True embedded AnyCPU;ARM64;x64;x86 - 2.36.0 + 2.37.0 + diff --git a/ScreenToGif.Util/Settings/Migrations.cs b/ScreenToGif.Util/Settings/Migrations.cs index 10764235..234c65f6 100644 --- a/ScreenToGif.Util/Settings/Migrations.cs +++ b/ScreenToGif.Util/Settings/Migrations.cs @@ -46,6 +46,10 @@ public static bool Migrate(List properties, string version) case "2.35.3": case "2.35.4": Migration2_35_0To2_36_0.Up(properties); + goto case "2.36"; + + case "2.36": //To 2.37 + Migration2_36_0To2_37_0.Up(properties); goto default; default: diff --git a/ScreenToGif.Util/Settings/Migrations/Migration2_32_0To2_35_0.cs b/ScreenToGif.Util/Settings/Migrations/Migration2_32_0To2_35_0.cs index 94177716..f856a24f 100644 --- a/ScreenToGif.Util/Settings/Migrations/Migration2_32_0To2_35_0.cs +++ b/ScreenToGif.Util/Settings/Migrations/Migration2_32_0To2_35_0.cs @@ -91,21 +91,21 @@ private static void UpdateNamespaces(List properties) { child.NameSpace = "clr-namespace:ScreenToGif.ViewModel.UploadPresets.Imgur;assembly=ScreenToGif.ViewModel"; - UpdateNamespaces(child.Children); + UpdateNamespaces(child.Attributes); break; } case "GfycatPreset": { child.NameSpace = "clr-namespace:ScreenToGif.ViewModel.UploadPresets.Gfycat;assembly=ScreenToGif.ViewModel"; - UpdateNamespaces(child.Children); + UpdateNamespaces(child.Attributes); break; } case "YandexPreset": { child.NameSpace = "clr-namespace:ScreenToGif.ViewModel.UploadPresets.Yandex;assembly=ScreenToGif.ViewModel"; - UpdateNamespaces(child.Children); + UpdateNamespaces(child.Attributes); break; } case "ImgurAlbum": diff --git a/ScreenToGif.Util/Settings/Migrations/Migration2_36_0To2_37_0.cs b/ScreenToGif.Util/Settings/Migrations/Migration2_36_0To2_37_0.cs new file mode 100644 index 00000000..d00b9413 --- /dev/null +++ b/ScreenToGif.Util/Settings/Migrations/Migration2_36_0To2_37_0.cs @@ -0,0 +1,46 @@ +using ScreenToGif.Domain.Models; + +namespace ScreenToGif.Settings.Migrations; + +internal class Migration2_36_0To2_37_0 +{ + internal static bool Up(List properties) + { + //Rename properties. + var mouseClicksWidth = properties.FirstOrDefault(a => a.Key == "MouseClicksWidth"); + + if (mouseClicksWidth != null) + mouseClicksWidth.Key = "MouseEventsWidth"; + + var mouseClicksHeight = properties.FirstOrDefault(a => a.Key == "MouseClicksHeight"); + + if (mouseClicksHeight != null) + mouseClicksHeight.Key = "MouseEventsHeight"; + + UpdateTasks(properties); + + return true; + } + + private static void UpdateTasks(List properties) + { + //Update tasks. + var tasks = properties.FirstOrDefault(f => f.Key == "AutomatedTasksList"); + + if (tasks == null) + return; + + foreach (var task in tasks.Children) + { + if (task.Type == "MouseClicksViewModel") + { + task.Type = "MouseEventsViewModel"; + + var taskTypeAttribute = task.Attributes.FirstOrDefault(a => a.Key == "TaskType"); + + if (taskTypeAttribute != null) + taskTypeAttribute.Value = "MouseEvents"; + } + } + } +} \ No newline at end of file diff --git a/ScreenToGif.Util/Settings/UserSettings.cs b/ScreenToGif.Util/Settings/UserSettings.cs index d1f8022c..5876ace8 100644 --- a/ScreenToGif.Util/Settings/UserSettings.cs +++ b/ScreenToGif.Util/Settings/UserSettings.cs @@ -1,6 +1,6 @@ -using ScreenToGif.Domain.Enums; using System.Collections; using System.ComponentModel; +using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; @@ -13,16 +13,15 @@ using System.Windows.Media; using System.Xml; using System.Xml.Linq; + +using KGySoft.CoreLibraries; +using KGySoft.Reflection; + +using ScreenToGif.Domain.Enums; using ScreenToGif.Domain.Exceptions; using ScreenToGif.Domain.Models; using ScreenToGif.Util.Extensions; -using Application = System.Windows.Application; -using Color = System.Windows.Media.Color; -using ColorConverter = System.Windows.Media.ColorConverter; -using FontFamily = System.Windows.Media.FontFamily; -using FontStyle = System.Windows.FontStyle; -using HorizontalAlignment = System.Windows.HorizontalAlignment; -using VerticalAlignment = System.Windows.VerticalAlignment; + using Orientation = System.Windows.Controls.Orientation; using XamlWriter = System.Windows.Markup.XamlWriter; @@ -31,7 +30,7 @@ namespace ScreenToGif.Util.Settings; public class UserSettings : INotifyPropertyChanged { #region Variables - + public event PropertyChangedEventHandler PropertyChanged; public static readonly object Lock = new(); @@ -47,7 +46,7 @@ public class UserSettings : INotifyPropertyChanged private static readonly ResourceDictionary Default; #endregion - + static UserSettings() { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) @@ -103,13 +102,13 @@ private static ResourceDictionary Load(string path) #region Load settings from disk var doc = XDocument.Parse(File.ReadAllText(path)); - var properties = (doc.Root?.Descendants() ?? doc.Descendants()).Where(w => w.Parent == doc.Root).Select(GetProperty).ToList(); + var properties = doc.Root?.Elements().Select(GetProperty).ToList(); #endregion #region Migrate - var version = properties.FirstOrDefault(f => f.Key == "Version")?.Value ?? "0.0"; + var version = properties?.FirstOrDefault(f => f.Key == "Version")?.Value ?? "0.0"; Migration.Migrate(properties, version); @@ -144,13 +143,16 @@ private static ResourceDictionary Load(string path) public static Property GetProperty(XElement node) { - var attributes = node.Attributes().Select(s => new Property { Key = s.Name.LocalName, Value = s.Value }).ToList(); + var attributes = node.Attributes() + .Where(a => a.Name.LocalName != "Key" && !(a.Name.Namespace == XNamespace.Xml && a.Name.LocalName == "space")) + .Select(s => new Property { Key = s.Name.LocalName, Value = s.Value }) + .ToList(); var prop = new Property { NameSpace = node.Name.NamespaceName, Type = node.Name.LocalName, - Key = attributes.FirstOrDefault(f => f.Key == "Key")?.Value, + Key = node.Attributes().FirstOrDefault(a => a.Name.LocalName == "Key")?.Value, Attributes = attributes, Value = node.Value }; @@ -172,6 +174,7 @@ public static Property GetProperty(XElement node) prop.Type = inner.Type; prop.NameSpace = inner.NameSpace; prop.Children = inner.Children; + prop.Attributes = inner.Attributes; return prop; } } @@ -181,7 +184,13 @@ public static Property GetProperty(XElement node) var innerElement = GetProperty(element); if (innerElement != null) - prop.Children.Add(innerElement); + { + // Adding collection elements to Children and properties to Attributes + if (innerElement.Key == null) + prop.Children.Add(innerElement); + else + prop.Attributes.Add(innerElement); + } } return prop; @@ -191,141 +200,30 @@ private static object ParseProperty(Property property) { try { - //Do you know any other way to achieve this? Contact me via Github. + var type = ParseType(property); + + // Primitive/simple type, enum or type with TypeConverter + if (property.Attributes.Count == 0) + return ParseValue(property.Value, type); + + // Complex object with properties/items + var instance = Activator.CreateInstance(type); + + // Restoring properties + foreach (var prop in property.Attributes) + { + var info = type.GetProperty(prop.Key) ?? throw new ArgumentException($"Property not found: {type.Name}.{prop.Key}", nameof(property)); + PropertyAccessor.GetAccessor(info).Set(instance, prop.Type != null ? ParseProperty(prop) : ParseValue(prop.Value, info.PropertyType)); + } - switch (property.Type) + // Restoring collection items (in fact, list is always an ArrayList due to WPF serialization but in theory we support others, too) + if (instance is IList list) { - case "String": - property.Value = property.Value.StartsWith("{}") ? property.Value.Substring(2) : property.Value; - - if (property.Value == "{x:Null}") - return null; - - return property.Value; - case "Boolean": - return Convert.ToBoolean(property.Value); - case "Int32": - return Convert.ToInt32(property.Value, CultureInfo.InvariantCulture); - case "Int64": - return Convert.ToInt64(property.Value, CultureInfo.InvariantCulture); - case "Double": - return Convert.ToDouble(property.Value, CultureInfo.InvariantCulture); - case "Decimal": - return Convert.ToDecimal(property.Value, CultureInfo.InvariantCulture); - - case "ExportFormats": - return Enum.Parse(typeof(ExportFormats), property.Value); - case "Key": - return (property.Value ?? "").Length == 0 ? null : Enum.Parse(typeof(Key), property.Value); - case "ProgressTypes": - return Enum.Parse(typeof(ProgressTypes), property.Value); - case "StylusTip": - return Enum.Parse(typeof(StylusTip), property.Value); - case "AppThemes": - return Enum.Parse(typeof(AppThemes), property.Value); - case "WindowState": - return Enum.Parse(typeof(WindowState), property.Value); - case "CopyModes": - return Enum.Parse(typeof(CopyModes), property.Value); - case "HorizontalAlignment": - return Enum.Parse(typeof(HorizontalAlignment), property.Value); - case "VerticalAlignment": - return Enum.Parse(typeof(VerticalAlignment), property.Value); - case "CaptureFrequencies": - return Enum.Parse(typeof(CaptureFrequencies), property.Value); - case "ProxyTypes": - return Enum.Parse(typeof(ProxyTypes), property.Value); - case "PasteBehaviors": - return Enum.Parse(typeof(PasteBehaviors), property.Value); - case "ReduceDelayModes": - return Enum.Parse(typeof(ReduceDelayModes), property.Value); - case "DuplicatesRemovalModes": - return Enum.Parse(typeof(DuplicatesRemovalModes), property.Value); - case "DuplicatesDelayModes": - return Enum.Parse(typeof(DuplicatesDelayModes), property.Value); - case "SmoothLoopFromModes": - return Enum.Parse(typeof(SmoothLoopFromModes), property.Value); - case "Orientation": - return Enum.Parse(typeof(Orientation), property.Value); - case "ObfuscationModes": - return Enum.Parse(typeof(ObfuscationModes), property.Value); - case "FadeModes": - return Enum.Parse(typeof(FadeModes), property.Value); - case "CompressionLevel": - return Enum.Parse(typeof(CompressionLevel), property.Value); - case "TaskTypes": - return Enum.Parse(typeof(TaskTypes), property.Value); - case "DelayUpdateModes": - return Enum.Parse(typeof(DelayUpdateModes), property.Value); - case "UploadDestinations": - return Enum.Parse(typeof(UploadDestinations), property.Value); - case "EncoderTypes": - return Enum.Parse(typeof(EncoderTypes), property.Value); - case "PartialExportModes": - return Enum.Parse(typeof(PartialExportModes), property.Value); - case "VideoSettingsModes": - return Enum.Parse(typeof(VideoSettingsModes), property.Value); - case "VideoCodecs": - return Enum.Parse(typeof(VideoCodecs), property.Value); - case "DitherMethods": - return Enum.Parse(typeof(DitherMethods), property.Value); - case "PredictionMethods": - return Enum.Parse(typeof(PredictionMethods), property.Value); - case "VideoCodecPresets": - return Enum.Parse(typeof(VideoCodecPresets), property.Value); - case "HardwareAccelerationModes": - return Enum.Parse(typeof(HardwareAccelerationModes), property.Value); - case "RateUnits": - return Enum.Parse(typeof(RateUnits), property.Value); - case "VideoPixelFormats": - return Enum.Parse(typeof(VideoPixelFormats), property.Value); - case "Framerates": - return Enum.Parse(typeof(Framerates), property.Value); - case "Vsyncs": - return Enum.Parse(typeof(Vsyncs), property.Value); - case "BitmapScalingMode": - return Enum.Parse(typeof(BitmapScalingMode), property.Value); - case "ColorQuantizationTypes": - return Enum.Parse(typeof(ColorQuantizationTypes), property.Value); - case "SizeUnits": - return Enum.Parse(typeof(SizeUnits), property.Value); - case "OverwriteModes": - return Enum.Parse(typeof(OverwriteModes), property.Value); - - case "FontWeight": - return new FontWeightConverter().ConvertFrom(property.Value); - case "FontFamily": - return new FontFamilyConverter().ConvertFrom(property.Value); - case "FontStyle": - return new FontStyleConverter().ConvertFrom(property.Value); - case "ModifierKeys": - return new ModifierKeysConverter().ConvertFrom(property.Value); - case "Color": - return ColorConverter.ConvertFromString(property.Value); - case "DoubleCollection": - return DoubleCollection.Parse(property.Value); - case "Rect": - return Rect.Parse(property.Value); - case "DateTime": - return DateTime.Parse(property.Value); - case "TimeSpan": - return TimeSpan.Parse(property.Value); - case "TextAlignment": - return Enum.Parse(typeof(TextAlignment), property.Value); - - case "ArrayList": - { - var array = new ArrayList(); - - foreach (var child in property.Children) - array.Add(ParseProperty(child)); - - return array; - } - - default: - return DeserializeProperty(property); + foreach (var child in property.Children) + list.Add(ParseProperty(child)); } + + return instance; } catch (Exception e) { @@ -336,166 +234,47 @@ private static object ParseProperty(Property property) private static Type ParseType(Property property) { - if (string.IsNullOrWhiteSpace(property.NameSpace)) - return Type.GetType("System.Windows." + property.Type, true); - - var namespaceIndex = property.NameSpace?.IndexOf("clr-namespace:", StringComparison.Ordinal) ?? -1; + if (property.NameSpace is "clr-namespace:System;assembly=System.Private.CoreLib" or "clr-namespace:System;assembly=mscorlib") + return Type.GetType("System." + property.Type, true); - if (namespaceIndex == -1) - throw new Exception("Namespace not expected"); + // Using Reflector instead of Type.GetType because without an assembly Type.GetType works only for mscorlib/System.Private.CoreLib types + if (property.NameSpace.StartsWith("http", StringComparison.Ordinal)) + return Reflector.ResolveType("System.Windows." + property.Type, ResolveTypeOptions.None) + ?? Reflector.ResolveType("System.Windows.Media." + property.Type, ResolveTypeOptions.None) + ?? Reflector.ResolveType("System.Windows.Ink." + property.Type, ResolveTypeOptions.None) + ?? Reflector.ResolveType("System.Windows.Input." + property.Type, ResolveTypeOptions.None) + ?? Reflector.ResolveType("System.Windows.Controls." + property.Type, ResolveTypeOptions.ThrowError); + var namespaceIndex = property.NameSpace.IndexOf("clr-namespace:", StringComparison.Ordinal); var space = property.NameSpace.Substring(namespaceIndex + 14); var assemblyIndex = space.IndexOf(";assembly=", StringComparison.Ordinal); if (assemblyIndex == -1) - return Type.GetType(space + "." + property.Type, true); + return Reflector.ResolveType(space + "." + property.Type, ResolveTypeOptions.ThrowError); + // Alert: ResolveTypeOptions.TryToLoadAssemblies is a security risk but this is the compatible behavior with Type.GetType(assemblyQualifiedName), which also would happily load any assembly. var assembly = space.Substring(assemblyIndex + 10); space = space.Substring(0, space.Length - assembly.Length - 10); - - return Type.GetType(space + "." + property.Type + ", " + assembly, true); + return Reflector.ResolveType(space + "." + property.Type + ", " + assembly, ResolveTypeOptions.AllowPartialAssemblyMatch | ResolveTypeOptions.TryToLoadAssemblies | ResolveTypeOptions.ThrowError); } - private static object DeserializeProperty(Property property) + private static object ParseValue(string value, Type type) { - var type = ParseType(property); - - //Does not work with enums. - if (property.Children.Count == 0 && property.Attributes.Count(w => w.Key != "Key") == 0) - return Convert.ChangeType(property.Value, type); - - var instance = Activator.CreateInstance(type); - - //Sub-properties. - foreach (var att in property.Attributes.Where(w => w.Key != "Key")) - { - if (string.IsNullOrEmpty(att.Key)) - { - LogWriter.Log("Property not identified in children", att, property); - continue; - } - - var info = type.GetProperty(att.Key); - - if (info == null) - { - LogWriter.Log("Property not available in object", att, property); - continue; - } - - att.Type = info.PropertyType.Name; - - if (info.PropertyType == typeof(int?)) - { - if (int.TryParse(att.Value, out var intValue)) - info.SetValue(instance, intValue, null); - - continue; - } - - if (info.PropertyType == typeof(DateTime?)) - { - if (DateTime.TryParse(att.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var deteTimeValue)) - info.SetValue(instance, deteTimeValue, null); - - continue; - } - - if (info.PropertyType == typeof(bool?)) - { - if (bool.TryParse(att.Value, out var boolValue)) - info.SetValue(instance, boolValue, null); - - continue; - } - - if (info.PropertyType == typeof(TimeSpan?)) - { - if (TimeSpan.TryParse(att.Value, out var timeValue)) - info.SetValue(instance, timeValue, null); - - continue; - } - - if (att.Type.StartsWith("Nullable")) - { - LogWriter.Log("Property not identified.", att, property); - continue; - } - - var value = ParseProperty(att); - - if (value != null) - info.SetValue(instance, value, null); - } - - //Sub-properties that are in expanded tags. - foreach (var child in property.Children) - { - if (string.IsNullOrEmpty(child.Key)) - { - LogWriter.Log("Property not identified in children", child, property); - continue; - } - - var info = type.GetProperty(child.Key); - - if (info == null) - { - LogWriter.Log("Property not available in object in children", child, property); - continue; - } - - child.Type = info.PropertyType.Name; - - if (info.PropertyType == typeof(int?)) - { - if (int.TryParse(child.Value, out var intValue)) - info.SetValue(instance, intValue, null); - - continue; - } - - if (info.PropertyType == typeof(DateTime?)) - { - if (DateTime.TryParse(child.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var deteTimeValue)) - info.SetValue(instance, deteTimeValue, null); - - continue; - } - - if (info.PropertyType == typeof(bool?)) - { - if (bool.TryParse(child.Value, out var boolValue)) - info.SetValue(instance, boolValue, null); - - continue; - } - - if (info.PropertyType == typeof(TimeSpan?)) - { - if (TimeSpan.TryParse(child.Value, out var timeValue)) - info.SetValue(instance, timeValue, null); - - continue; - } - - if (child.Type.StartsWith("Nullable")) - { - LogWriter.Log("Property not identified in children.", child, property); - continue; - } + if (value is null or "{x:Null}") + return null; - var innerChild = ParseProperty(child); + if (type == typeof(string)) + return value.StartsWith("{}", StringComparison.Ordinal) ? value[2..] : value; - if (innerChild != null) - info.SetValue(instance, innerChild, null); - } + // This works for primitive types, enums, and types with TypeConverters + if (value.TryParse(type, CultureInfo.InvariantCulture, out var result)) + return result; - return instance; + // [Try]Parse fails for enums that should be parsed by TypeConverters rather than from their ToString value (eg. ModifierKeys) + // We could just use Convert even instead of the Parse above but it is faster to use this only as a fallback + return value.Convert(type, CultureInfo.InvariantCulture); } - public static void Save(bool canForce = false, bool saveToAppData = false) { //Only writes if non-default values were created. Should not write the default dictionary. @@ -507,7 +286,7 @@ public static void Save(bool canForce = false, bool saveToAppData = false) //Filename (Local or AppData). var folder = !saveToAppData && _local != null ? AppDomain.CurrentDomain.BaseDirectory : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ScreenToGif"); var filename = Path.Combine(folder, "Settings.xaml"); - + //Create folder. if (!string.IsNullOrWhiteSpace(folder) && !Directory.Exists(folder)) Directory.CreateDirectory(folder); @@ -525,10 +304,15 @@ public static void Save(bool canForce = false, bool saveToAppData = false) Encoding = Encoding.UTF8 }; + var dic = RemoveInvalidEntries(_local ?? _appData); + + if (dic == null) + return; + //Serialize and save to disk. using (var fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) using (var writer = XmlWriter.Create(fileStream, settings)) - XamlWriter.Save(_local ?? _appData, writer); + XamlWriter.Save(dic, writer); CheckIfSavedCorrectly(filename, backup, true); } @@ -604,6 +388,28 @@ private static void CheckIfSavedCorrectly(string filename, string backup, bool t } } + private static ResourceDictionary RemoveInvalidEntries(ResourceDictionary dictionary) + { + if (dictionary == null) + return null; + + var toRemove = dictionary.Cast().Where(entry => entry.Value == null).ToList(); + + foreach (var entry in toRemove) + { + LogWriter.Log("Setting removed: " + entry.Key); + dictionary.Remove(entry.Key); + } + + for (var i = 0; i < dictionary.Count; i++) + { + if (dictionary[i] is DictionaryEntry { Value: ArrayList list } entry) + entry.Value = list.OfType().Where(w => w != null); + } + + return dictionary; + } + public static void CreateLocalSettings() { @@ -1121,6 +927,15 @@ public int PlaybackDelayManual set => SetValue(value); } + /// + /// The synthetic delay after trigger, in the "manual" capture mode. + /// + public int TriggerDelayManual + { + get => (int)GetValue(); + set => SetValue(value); + } + /// /// The placyback speed of the capture frame, in the "manual" mode. /// @@ -1130,6 +945,15 @@ public int PlaybackDelayInteraction set => SetValue(value); } + /// + /// The synthetic delay after trigger, in the "interaction" capture mode. + /// + public int TriggerDelayInteraction + { + get => (int)GetValue(); + set => SetValue(value); + } + /// /// The placyback speed of the capture frame, in the "per minute" mode. /// @@ -1725,7 +1549,7 @@ public string GifskiLocation get => (string)GetValue(); set => SetValue(value); } - + #endregion @@ -2538,7 +2362,13 @@ public Orientation ProgressOrientation #endregion - #region Editor • Mouse Clicks + #region Editor • Mouse Events + + public Color MouseHighlightColor + { + get => (Color)GetValue(); + set => SetValue(value); + } public Color LeftMouseButtonClicksColor { @@ -2558,13 +2388,13 @@ public Color MiddleMouseButtonClicksColor set => SetValue(value); } - public double MouseClicksWidth + public double MouseEventsWidth { get => (double)GetValue(); set => SetValue(value); } - public double MouseClicksHeight + public double MouseEventsHeight { get => (double)GetValue(); set => SetValue(value); diff --git a/ScreenToGif.ViewModel/ExportPresets/AnimatedImage/Gif/KGySoftGifPreset.cs b/ScreenToGif.ViewModel/ExportPresets/AnimatedImage/Gif/KGySoftGifPreset.cs new file mode 100644 index 00000000..6fb36e2c --- /dev/null +++ b/ScreenToGif.ViewModel/ExportPresets/AnimatedImage/Gif/KGySoftGifPreset.cs @@ -0,0 +1,251 @@ +#region Usings + +using System.Windows.Media; + +using KGySoft.Drawing.Imaging; + +using ScreenToGif.Domain.Enums; + +#endregion + +namespace ScreenToGif.ViewModel.ExportPresets.AnimatedImage.Gif; + +/// +/// Represents the persistable settings for KGy SOFT GIF Encoder. +/// +public class KGySoftGifPreset : GifPreset +{ + #region Fields + + private string _quantizerId; + private string _dithererId; + private Color _backColor = Colors.Silver; + private byte _alphaThreshold = 128; + private byte _whiteThreshold = 128; + private bool _directMapping; + private int _paletteSize = 256; + private byte? _bitLevel; + private float _strength; + private int? _seed; + private bool _serpentine; + private bool _allowDeltaFrames = true; + private bool _allowClippedFrames = true; + private byte _deltaTolerance; + + #endregion + + #region Properties + + #region Static Properties + + /// + /// Gets the defaults presets for the type. + /// + public static KGySoftGifPreset[] Defaults => new[] + { + new KGySoftGifPreset + { + TitleKey = "S.Preset.Gif.KGySoft.Balanced.Title", + DescriptionKey = "S.Preset.Gif.KGySoft.Balanced.Description", + HasAutoSave = true, + IsSelected = true, + IsDefault = true, + IsSelectedForEncoder = true, + CreationDate = new DateTime(2021, 12, 15), + QuantizerId = $"{nameof(OptimizedPaletteQuantizer)}.{nameof(OptimizedPaletteQuantizer.Wu)}", + }, + new KGySoftGifPreset + { + TitleKey = "S.Preset.Gif.KGySoft.High.Title", + DescriptionKey = "S.Preset.Gif.KGySoft.High.Description", + HasAutoSave = true, + IsSelected = true, + IsDefault = true, + IsSelectedForEncoder = true, + CreationDate = new DateTime(2021, 12, 15), + QuantizerId = $"{nameof(OptimizedPaletteQuantizer)}.{nameof(OptimizedPaletteQuantizer.Wu)}", + DithererId = $"{nameof(ErrorDiffusionDitherer)}.{nameof(ErrorDiffusionDitherer.FloydSteinberg)}", + BitLevel = 7, + }, + new KGySoftGifPreset + { + TitleKey = "S.Preset.Gif.KGySoft.Fast.Title", + DescriptionKey = "S.Preset.Gif.KGySoft.Fast.Description", + HasAutoSave = true, + IsSelected = true, + IsDefault = true, + IsSelectedForEncoder = true, + CreationDate = new DateTime(2021, 12, 15), + QuantizerId = $"{nameof(PredefinedColorsQuantizer)}.{nameof(PredefinedColorsQuantizer.SystemDefault8BppPalette)}", + DithererId = $"{nameof(OrderedDitherer)}.{nameof(OrderedDitherer.Bayer8x8)}", + }, + }; + + #endregion + + #region Instance Properties + + #region Quantizer Settings + + /// + /// Gets or sets the quantizer identifier in {TypeName}.{MethodName} format. + /// + public string QuantizerId + { + get => _quantizerId; + set => SetProperty(ref _quantizerId, value); + } + + /// + /// Gets or sets the background color for alpha pixels that will not be transparent in the result. + /// + public Color BackColor + { + get => _backColor; + set => SetProperty(ref _backColor, value); + } + + /// + /// Gets or sets the alpha threshold under which a color is considered transparent. + /// This property is ignored by quantizers that do not support transparency. + /// + public byte AlphaThreshold + { + get => _alphaThreshold; + set => SetProperty(ref _alphaThreshold, value); + } + + /// + /// Gets or sets the lowest input brightness to consider the result color white. + /// This property is considered only by the black and white quantizer. + /// + public byte WhiteThreshold + { + get => _whiteThreshold; + set => SetProperty(ref _whiteThreshold, value); + } + + /// + /// Gets or sets whether the palette entries are mapped from the color directly. + /// This property is ignored by quantizers that do not support direct mapping. + /// + public bool DirectMapping + { + get => _directMapping; + set => SetProperty(ref _directMapping, value); + } + + /// + /// Gets or sets the maximum palette size per frame. + /// This property is ignored by predefined colors quantizers. + /// + public int PaletteSize + { + get => _paletteSize; + set => SetProperty(ref _paletteSize, value); + } + + /// + /// Gets or sets the bit level used by an optimized quantizer. + /// This property is ignored by predefined colors quantizers. + /// + public byte? BitLevel + { + get => _bitLevel; + set => SetProperty(ref _bitLevel, value); + } + + #endregion + + #region Ditherer Settings + + /// + /// Gets or sets the ditherer identifier in {TypeName}[.{PropertyName}] format. + /// + public string DithererId + { + get => _dithererId; + set => SetProperty(ref _dithererId, value); + } + + /// + /// Gets or sets the strength of the ditherer. + /// This property is ignored by error diffusion ditherers. + /// + public float Strength + { + get => _strength; + set => SetProperty(ref _strength, value); + } + + /// + /// Gets or sets the seed of ditherer. + /// This property is ignored by non-randomized ditherers. + /// + public int? Seed + { + get => _seed; + set => SetProperty(ref _seed, value); + } + + /// + /// Gets or sets whether the ditherer uses serpentine processing. + /// This property is used only by error diffusion ditherers. + /// + public bool IsSerpentineProcessing + { + get => _serpentine; + set => SetProperty(ref _serpentine, value); + } + + #endregion + + #region Animation Settings + + /// + /// Gets or sets whether the encoder is allowed to save the changed image parts. + /// + public bool AllowDeltaFrames + { + get => _allowDeltaFrames; + set => SetProperty(ref _allowDeltaFrames, value); + } + + /// + /// Gets or sets whether the encoder is allowed clip the transparent border of the frames. + /// + public bool AllowClippedFrames + { + get => _allowClippedFrames; + set => SetProperty(ref _allowClippedFrames, value); + } + + /// + /// If is , then gets or sets the allowed maximum tolerance when detecting changes. + /// + public byte DeltaTolerance + { + get => _deltaTolerance; + set => SetProperty(ref _deltaTolerance, value); + } + + #endregion + + #endregion + + #endregion + + #region Constructors + + /// + /// Instantiates a new instance of the class. + /// + public KGySoftGifPreset() + { + Encoder = EncoderTypes.KGySoft; + ImageId = "Vector.KGySoft"; + RepeatCount = 0; + } + + #endregion +} diff --git a/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/LibAom.cs b/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/LibAom.cs new file mode 100644 index 00000000..db466e31 --- /dev/null +++ b/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/LibAom.cs @@ -0,0 +1,46 @@ +using ScreenToGif.Domain.Enums; + +namespace ScreenToGif.ViewModel.ExportPresets.Video.Codecs; + +public class LibAom : VideoCodec +{ + /// + /// Alliance for Open Media AV1. + /// ffmpeg.exe -h encoder=libaom-av1 + /// + public LibAom() + { + Type = VideoCodecs.LibAom; + Name = "LibAOM AV1"; + Command = "libaom-av1"; + Parameters = ""; + + CanSetCrf = true; + MinimumCrf = 0; + MaximumCrf = 63; + CodecPresets = new List> + { + new(VideoCodecPresets.None, "S.SaveAs.VideoOptions.CodecPreset.None", "") + }; + PixelFormats = new List> + { + new(VideoPixelFormats.Auto, "S.SaveAs.VideoOptions.PixelFormat.Auto", ""), + new(VideoPixelFormats.Gbrp, "", "Gbrp", "gbrp"), + new(VideoPixelFormats.Gbrp10Le, "", "Gbrp10Le", "gbrp10le"), + new(VideoPixelFormats.Gbrp12Le, "", "Gbrp12Le", "gbrp12le"), + new(VideoPixelFormats.Gray, "", "Gray", "gray"), + new(VideoPixelFormats.Gray10Le, "", "Gray10LE", "gray10le"), + new(VideoPixelFormats.Gray12Le, "", "Gray12LE", "gray12le"), + new(VideoPixelFormats.Yuv420p, "", "Yuv420p", "yuv420p"), + new(VideoPixelFormats.Yuv422p, "", "Yuv422p", "yuv422p"), + new(VideoPixelFormats.Yuv420p10Le, "", "Yuv420p10Le", "yuv420p10le"), + new(VideoPixelFormats.Yuv420p12Le, "", "Yuv420p12Le", "yuv420p12le"), + new(VideoPixelFormats.Yuv422p10Le, "", "Yuv422p10Le", "yuv422p10le"), + new(VideoPixelFormats.Yuv422p12Le, "", "Yuv422p12Le", "yuv422p12le"), + new(VideoPixelFormats.Yuv444p10Le, "", "Yuv444p10Le", "yuv444p10le"), + new(VideoPixelFormats.Yuv444p12Le, "", "Yuv444p12Le", "yuv444p12le"), + new(VideoPixelFormats.Yuv444p, "", "Yuv444p", "yuv444p"), + new(VideoPixelFormats.Yuva420p, "", "Yuva420p", "yuva420p") + }; + } +} \ No newline at end of file diff --git a/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/Rav1E.cs b/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/Rav1E.cs new file mode 100644 index 00000000..ac0f55c5 --- /dev/null +++ b/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/Rav1E.cs @@ -0,0 +1,52 @@ +using ScreenToGif.Domain.Enums; + +namespace ScreenToGif.ViewModel.ExportPresets.Video.Codecs; + +public class Rav1E : VideoCodec +{ + /// + /// Rav1E. + /// ffmpeg.exe -h encoder=librav1e + /// + public Rav1E() + { + Type = VideoCodecs.Rav1E; + Name = "Rav1E"; + Command = "librav1e"; + Parameters = ""; + + CanSetCrf = true; + MinimumCrf = 0; + MaximumCrf = 63; + CodecPresets = new List> + { + new(VideoCodecPresets.Auto, "S.SaveAs.VideoOptions.CodecPreset.Default", "-1"), + new(VideoCodecPresets.None, "S.SaveAs.VideoOptions.CodecPreset.None", "0"), + new(VideoCodecPresets.VerySlow, "S.SaveAs.VideoOptions.CodecPreset.VerySlow", "1"), + new(VideoCodecPresets.Slower, "S.SaveAs.VideoOptions.CodecPreset.Slower", "2"), + new(VideoCodecPresets.Slow, "S.SaveAs.VideoOptions.CodecPreset.Slow", "3"), + new(VideoCodecPresets.Medium, "S.SaveAs.VideoOptions.CodecPreset.Medium", "4"), + new(VideoCodecPresets.Fast, "S.SaveAs.VideoOptions.CodecPreset.Fast", "5"), + new(VideoCodecPresets.Faster, "S.SaveAs.VideoOptions.CodecPreset.Faster", "6"), + new(VideoCodecPresets.VeryFast, "S.SaveAs.VideoOptions.CodecPreset.VeryFast", "7"), + new(VideoCodecPresets.SuperFast, "S.SaveAs.VideoOptions.CodecPreset.SuperFast", "8"), + new(VideoCodecPresets.UltraFast, "S.SaveAs.VideoOptions.CodecPreset.UltraFast", "9") + }; + PixelFormats = new List> + { + new(VideoPixelFormats.Auto, "S.SaveAs.VideoOptions.PixelFormat.Auto", ""), + new(VideoPixelFormats.Yuv420p, "", "Yuv420p", "yuv420p"), + new(VideoPixelFormats.Yuvj420p, "", "Yuvj420p", "yuvj420p"), + new(VideoPixelFormats.Yuv422p, "", "Yuv422p", "yuv422p"), + new(VideoPixelFormats.Yuvj422p, "", "Yuvj422p", "yuvj422p"), + new(VideoPixelFormats.Yuv444p, "", "Yuv444p", "yuv444p"), + new(VideoPixelFormats.Yuvj444p, "", "Yuvj444p", "yuvj444p"), + new(VideoPixelFormats.Yuv420p10Le, "", "Yuv420p10Le", "yuv420p10le"), + new(VideoPixelFormats.Yuv420p12Le, "", "Yuv420p12Le", "yuv420p12le"), + new(VideoPixelFormats.Yuv422p10Le, "", "Yuv422p10Le", "yuv422p10le"), + new(VideoPixelFormats.Yuv422p12Le, "", "Yuv422p12Le", "yuv422p12le"), + new(VideoPixelFormats.Yuv444p10Le, "", "Yuv444p10Le", "yuv444p10le"), + new(VideoPixelFormats.Yuv444p12Le, "", "Yuv444p12Le", "yuv444p12le"), + }; + } +} \ No newline at end of file diff --git a/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/SvtAv1.cs b/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/SvtAv1.cs new file mode 100644 index 00000000..5341fbaf --- /dev/null +++ b/ScreenToGif.ViewModel/ExportPresets/Video/Codecs/SvtAv1.cs @@ -0,0 +1,41 @@ +using ScreenToGif.Domain.Enums; + +namespace ScreenToGif.ViewModel.ExportPresets.Video.Codecs; + +public class SvtAv1 : VideoCodec +{ + /// + /// SVT-AV1, Scalable Video Technology for AV1. + /// ffmpeg.exe -h encoder=libsvtav1 + /// + public SvtAv1() + { + Type = VideoCodecs.SvtAv1; + Name = "SVT-AV1"; + Command = "libsvtav1"; + Parameters = ""; + + CanSetCrf = true; + MinimumCrf = 0; + MaximumCrf = 63; + CodecPresets = new List> + { + new(VideoCodecPresets.None, "S.SaveAs.VideoOptions.CodecPreset.None", "0"), + new(VideoCodecPresets.VerySlow, "S.SaveAs.VideoOptions.CodecPreset.VerySlow", "1"), + new(VideoCodecPresets.Slower, "S.SaveAs.VideoOptions.CodecPreset.Slower", "2"), + new(VideoCodecPresets.Slow, "S.SaveAs.VideoOptions.CodecPreset.Slow", "3"), + new(VideoCodecPresets.Medium, "S.SaveAs.VideoOptions.CodecPreset.Medium", "4"), + new(VideoCodecPresets.Fast, "S.SaveAs.VideoOptions.CodecPreset.Fast", "5"), + new(VideoCodecPresets.Faster, "S.SaveAs.VideoOptions.CodecPreset.Faster", "6"), + new(VideoCodecPresets.VeryFast, "S.SaveAs.VideoOptions.CodecPreset.VeryFast", "7"), + new(VideoCodecPresets.SuperFast, "S.SaveAs.VideoOptions.CodecPreset.SuperFast", "8"), + new(VideoCodecPresets.UltraFast, "S.SaveAs.VideoOptions.CodecPreset.UltraFast", "9") + }; + PixelFormats = new List> + { + new(VideoPixelFormats.Auto, "S.SaveAs.VideoOptions.PixelFormat.Auto", ""), + new(VideoPixelFormats.Yuv420p, "", "Yuv420p", "yuv420p"), + new(VideoPixelFormats.Yuv420p10Le, "", "Yuv420p10Le", "yuv420p10le") + }; + } +} \ No newline at end of file diff --git a/ScreenToGif.ViewModel/ScreenToGif.ViewModel.csproj b/ScreenToGif.ViewModel/ScreenToGif.ViewModel.csproj index 9e43a04a..aaae8c9d 100644 --- a/ScreenToGif.ViewModel/ScreenToGif.ViewModel.csproj +++ b/ScreenToGif.ViewModel/ScreenToGif.ViewModel.csproj @@ -6,9 +6,13 @@ disable embedded AnyCPU;ARM64;x64;x86 - 2.36.0 + 2.37.0 + + + + diff --git a/ScreenToGif.ViewModel/Tasks/BaseTaskViewModel.cs b/ScreenToGif.ViewModel/Tasks/BaseTaskViewModel.cs index 7163cebc..a5d622ac 100644 --- a/ScreenToGif.ViewModel/Tasks/BaseTaskViewModel.cs +++ b/ScreenToGif.ViewModel/Tasks/BaseTaskViewModel.cs @@ -30,8 +30,8 @@ public string Kind { switch (TaskType) { - case TaskTypes.MouseClicks: - return LocalizationHelper.Get("S.Editor.Image.Clicks", true); + case TaskTypes.MouseEvents: + return LocalizationHelper.Get("S.Editor.Image.MouseEvents", true); case TaskTypes.KeyStrokes: return LocalizationHelper.Get("S.Editor.Image.KeyStrokes", true); case TaskTypes.Delay: diff --git a/ScreenToGif.ViewModel/Tasks/MouseClicksViewModel.cs b/ScreenToGif.ViewModel/Tasks/MouseEventsViewModel.cs similarity index 64% rename from ScreenToGif.ViewModel/Tasks/MouseClicksViewModel.cs rename to ScreenToGif.ViewModel/Tasks/MouseEventsViewModel.cs index d127d0bc..349823b8 100644 --- a/ScreenToGif.ViewModel/Tasks/MouseClicksViewModel.cs +++ b/ScreenToGif.ViewModel/Tasks/MouseEventsViewModel.cs @@ -5,17 +5,24 @@ namespace ScreenToGif.ViewModel.Tasks; -public class MouseClicksViewModel : BaseTaskViewModel +public class MouseEventsViewModel : BaseTaskViewModel { private Color _leftButtonForegroundColor; private Color _rightButtonForegroundColor; private Color _middleButtonForegroundColor; + private Color _highlightForegroundColor; private double _width; private double _height; - public MouseClicksViewModel() + public MouseEventsViewModel() { - TaskType = TaskTypes.MouseClicks; + TaskType = TaskTypes.MouseEvents; + } + + public Color HighlightForegroundColor + { + get => _highlightForegroundColor; + set => SetProperty(ref _highlightForegroundColor, value); } public Color LeftButtonForegroundColor @@ -50,33 +57,36 @@ public double Height public override string ToString() { - return $"{LocalizationHelper.Get("S.MouseClicks.Color.Left")} #{LeftButtonForegroundColor.A:X2}{LeftButtonForegroundColor.R:X2}{LeftButtonForegroundColor.G:X2}{LeftButtonForegroundColor.B:X2}, "+ - $"{LocalizationHelper.Get("S.MouseClicks.Color.Middle")} #{MiddleButtonForegroundColor.A:X2}{MiddleButtonForegroundColor.R:X2}{MiddleButtonForegroundColor.G:X2}{MiddleButtonForegroundColor.B:X2}, "+ - $"{LocalizationHelper.Get("S.MouseClicks.Color.Right")} #{RightButtonForegroundColor.A:X2}{RightButtonForegroundColor.R:X2}{RightButtonForegroundColor.G:X2}{RightButtonForegroundColor.B:X2}, "+ + return $"{LocalizationHelper.Get("S.MouseHighlight.Color")} #{HighlightForegroundColor.A:X2}{HighlightForegroundColor.R:X2}{HighlightForegroundColor.G:X2}{HighlightForegroundColor.B:X2}, " + + $"{LocalizationHelper.Get("S.MouseClicks.Color.Left")} #{LeftButtonForegroundColor.A:X2}{LeftButtonForegroundColor.R:X2}{LeftButtonForegroundColor.G:X2}{LeftButtonForegroundColor.B:X2}, " + + $"{LocalizationHelper.Get("S.MouseClicks.Color.Middle")} #{MiddleButtonForegroundColor.A:X2}{MiddleButtonForegroundColor.R:X2}{MiddleButtonForegroundColor.G:X2}{MiddleButtonForegroundColor.B:X2}, " + + $"{LocalizationHelper.Get("S.MouseClicks.Color.Right")} #{RightButtonForegroundColor.A:X2}{RightButtonForegroundColor.R:X2}{RightButtonForegroundColor.G:X2}{RightButtonForegroundColor.B:X2}, " + $"{LocalizationHelper.Get("S.FreeDrawing.Width")} {Width}, {LocalizationHelper.Get("S.FreeDrawing.Height")} {Height}"; } - public static MouseClicksViewModel Default() + public static MouseEventsViewModel Default() { - return new MouseClicksViewModel + return new MouseEventsViewModel { + HighlightForegroundColor = Color.FromArgb(0, 0, 0, 0), LeftButtonForegroundColor = Color.FromArgb(120, 255, 255, 0), RightButtonForegroundColor = Color.FromArgb(120, 255, 0, 0), - MiddleButtonForegroundColor = Color.FromArgb(120, 0, 255,255), + MiddleButtonForegroundColor = Color.FromArgb(120, 0, 255, 255), Height = 12, Width = 12 }; } - public static MouseClicksViewModel FromSettings() + public static MouseEventsViewModel FromSettings() { - return new MouseClicksViewModel + return new MouseEventsViewModel { + HighlightForegroundColor = UserSettings.All.MouseHighlightColor, LeftButtonForegroundColor = UserSettings.All.LeftMouseButtonClicksColor, MiddleButtonForegroundColor = UserSettings.All.MiddleMouseButtonClicksColor, RightButtonForegroundColor = UserSettings.All.RightMouseButtonClicksColor, - Height = UserSettings.All.MouseClicksHeight, - Width = UserSettings.All.MouseClicksWidth + Height = UserSettings.All.MouseEventsHeight, + Width = UserSettings.All.MouseEventsWidth }; } } \ No newline at end of file diff --git a/ScreenToGif/Capture/DirectImageCapture.cs b/ScreenToGif/Capture/DirectImageCapture.cs index f75b015a..dfccb864 100644 --- a/ScreenToGif/Capture/DirectImageCapture.cs +++ b/ScreenToGif/Capture/DirectImageCapture.cs @@ -792,7 +792,7 @@ protected internal bool GetCursor(Texture2D screenTexture, OutputDuplicateFrameI PreviousPosition = info.PointerPosition; //TODO: In a future version, don't merge the cursor image in here, let the editor do that. - //Saves the position of the cursor, so the editor can add the mouse clicks overlay later. + //Saves the position of the cursor, so the editor can add the mouse events overlay later. frame.CursorX = PreviousPosition.Position.X - (Left - OffsetLeft); frame.CursorY = PreviousPosition.Position.Y - (Top - OffsetTop); diff --git a/ScreenToGif/Controls/BaseScreenRecorder.cs b/ScreenToGif/Controls/BaseScreenRecorder.cs index 24ad0569..269cf280 100644 --- a/ScreenToGif/Controls/BaseScreenRecorder.cs +++ b/ScreenToGif/Controls/BaseScreenRecorder.cs @@ -80,6 +80,19 @@ internal int GetFixedDelay() } } + internal int GetTriggerDelay() + { + switch (UserSettings.All.CaptureFrequency) + { + case CaptureFrequencies.Interaction: + return UserSettings.All.TriggerDelayInteraction; + case CaptureFrequencies.Manual: + return UserSettings.All.TriggerDelayManual; + default: + return 0; + } + } + internal int GetCaptureInterval() { switch (UserSettings.All.CaptureFrequency) diff --git a/ScreenToGif/Controls/NullableIntegerBox.cs b/ScreenToGif/Controls/NullableIntegerBox.cs index 20105cf9..72e9158f 100644 --- a/ScreenToGif/Controls/NullableIntegerBox.cs +++ b/ScreenToGif/Controls/NullableIntegerBox.cs @@ -39,8 +39,8 @@ public class NullableIntegerBox : ExtendedTextBox public static readonly DependencyProperty UpdateOnInputProperty = DependencyProperty.Register(nameof(UpdateOnInput), typeof(bool), typeof(NullableIntegerBox), new FrameworkPropertyMetadata(false, OnUpdateOnInputPropertyChanged)); - public static readonly DependencyProperty DefaultValueIfEmptyProperty = DependencyProperty.Register(nameof(DefaultValueIfEmpty), typeof(int), typeof(NullableIntegerBox), - new FrameworkPropertyMetadata(0)); + public static readonly DependencyProperty DefaultValueIfEmptyProperty = DependencyProperty.Register(nameof(DefaultValueIfEmpty), typeof(int?), typeof(NullableIntegerBox), + new FrameworkPropertyMetadata(null)); public static readonly DependencyProperty PropagateWheelEventProperty = DependencyProperty.Register(nameof(PropagateWheelEvent), typeof(bool), typeof(NullableIntegerBox), new PropertyMetadata(default(bool))); @@ -87,9 +87,9 @@ public bool UpdateOnInput } [Bindable(true), Category("Common")] - public int DefaultValueIfEmpty + public int? DefaultValueIfEmpty { - get => (int)GetValue(DefaultValueIfEmptyProperty); + get => (int?)GetValue(DefaultValueIfEmptyProperty); set => SetValue(DefaultValueIfEmptyProperty, value); } @@ -117,7 +117,7 @@ private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPrope private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - if (!(d is NullableIntegerBox box) || _ignore) + if (d is not NullableIntegerBox box || _ignore) return; _ignore = true; @@ -221,14 +221,14 @@ protected override void OnGotFocus(RoutedEventArgs e) protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { //Only sets the focus if not clicking on the Up/Down buttons of a IntegerUpDown. - if (e.OriginalSource is TextBlock || e.OriginalSource is Border) + if (e.OriginalSource is TextBlock or Border) return; - if (!IsKeyboardFocusWithin) - { - e.Handled = true; - Focus(); - } + if (IsKeyboardFocusWithin) + return; + + e.Handled = true; + Focus(); } protected override void OnPreviewTextInput(TextCompositionEventArgs e) @@ -253,7 +253,7 @@ protected override void OnTextChanged(TextChangedEventArgs e) if (!UpdateOnInput || string.IsNullOrEmpty(Text) || !IsTextAllowed(Text)) return; - Value = int.TryParse(Text, out int value) ? value : new int?(); + Value = int.TryParse(Text, out var value) ? value : new int?(); base.OnTextChanged(e); } @@ -270,7 +270,7 @@ protected override void OnLostFocus(RoutedEventArgs e) return; } - Value = int.TryParse(Text, out int value) ? value : new int?(); + Value = int.TryParse(Text, out var value) ? value : new int?(); return; } @@ -279,7 +279,7 @@ protected override void OnLostFocus(RoutedEventArgs e) protected override void OnKeyDown(KeyEventArgs e) { - if (e.Key == Key.Enter || e.Key == Key.Return) + if (e.Key is Key.Enter or Key.Return) { e.Handled = true; MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); @@ -292,10 +292,13 @@ protected override void OnMouseWheel(MouseWheelEventArgs e) { base.OnMouseWheel(e); - var step = Keyboard.Modifiers == (ModifierKeys.Shift | ModifierKeys.Control) - ? 50 : Keyboard.Modifiers == ModifierKeys.Shift - ? 10 : Keyboard.Modifiers == ModifierKeys.Control - ? 5 : StepValue; + var step = Keyboard.Modifiers switch + { + ModifierKeys.Shift | ModifierKeys.Control => 50, + ModifierKeys.Shift => 10, + ModifierKeys.Control => 5, + _ => StepValue + }; Value = e.Delta > 0 ? Math.Min(Maximum, (Value ?? 0) + step) : diff --git a/ScreenToGif/ImageUtil/ImageMethods.cs b/ScreenToGif/ImageUtil/ImageMethods.cs index 2d4e9163..72064fe7 100644 --- a/ScreenToGif/ImageUtil/ImageMethods.cs +++ b/ScreenToGif/ImageUtil/ImageMethods.cs @@ -21,10 +21,10 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Resources; -using Color = System.Windows.Media.Color; using Image = System.Drawing.Image; using PixelFormat = System.Windows.Media.PixelFormat; using Size = System.Drawing.Size; +using Color = System.Windows.Media.Color; namespace ScreenToGif.ImageUtil; diff --git a/ScreenToGif/Model/FrameInfo.cs b/ScreenToGif/Model/FrameInfo.cs index a108fc39..04028fdb 100644 --- a/ScreenToGif/Model/FrameInfo.cs +++ b/ScreenToGif/Model/FrameInfo.cs @@ -206,7 +206,7 @@ public FrameInfo(string path, int delay, int cursorX, int cursorY, MouseButtons /// - /// This works as a migration method for mouse clicks. Before storing the button + /// This works as a migration method for mouse events. Before storing the button /// type only bool was stored to mark the clicks. During opening old project it will /// be converted to Left mouse button click loosing some info unfortunately. /// diff --git a/ScreenToGif/Resources/Commands.xaml b/ScreenToGif/Resources/Commands.xaml index 3b887884..d2e8af27 100644 --- a/ScreenToGif/Resources/Commands.xaml +++ b/ScreenToGif/Resources/Commands.xaml @@ -297,7 +297,7 @@ - + Alt + I diff --git a/ScreenToGif/Resources/Localization/StringResources.en.xaml b/ScreenToGif/Resources/Localization/StringResources.en.xaml index bf41d756..5c5c5572 100644 --- a/ScreenToGif/Resources/Localization/StringResources.en.xaml +++ b/ScreenToGif/Resources/Localization/StringResources.en.xaml @@ -280,6 +280,8 @@ Frames will be captured in a 'per hour' basis (timelapse), given the framerate denominator set on the recorder screen. Playback delay: (In ms, each captured frame will be set to this delay) + Trigger delay: + (In ms, each frame capture will wait this amount of time before starting) Each frame will be captured in interval of {0}. Capture mode @@ -792,22 +794,28 @@ Supports saving the animation with a transparent background. High quality • Graphics Better for recordings with a lower quantity of colors. - FFmpeg - Higher quality + KGy SOFT • Balanced + Good quality for photo-like images using Wu's quantizer without dithering. + KGy SOFT • High quality + High quality for photo-like images using Wu's quantizer with higher bit level and Floyd-Steinberg error diffusion dithering. + KGy SOFT • Low quality, faster + Quantizing all frames with the same predefined 'web-safe' palette and Bayer 8x8 ordered dithering. + FFmpeg • Higher quality Higher image quality, but with a greater file size. - FFmpeg - Lower quality + FFmpeg • Lower quality Lower image quality, but with a smaller file size. - Gifski - Higher quality + Gifski • Higher quality Higher image quality, but with a greater file size. - Gifski - Lower quality + Gifski • Lower quality Lower image quality, but with a smaller file size. - Gifski - Lower quality and faster encoding + Gifski • Lower quality and faster encoding Even lower image quality, with a faster encoding, but with a smaller file size. - System - Low quality + System • Low quality Low quality but faster encoding. - FFmpeg - High quality + FFmpeg • High quality High image quality and small file size, but slower encoding. - FFmpeg - Lower quality + FFmpeg • Lower quality Lower image quality, small file size and faster encoding. High quality High image quality and small file size. @@ -1058,7 +1066,7 @@ Overlay Free Drawing Shapes - Mouse Clicks + Mouse Events Watermark Cinemagraph Border @@ -1373,12 +1381,12 @@ Remove Fill - - Mouse Clicks + + Mouse Events + Mouse Highlight Color: Left button color: Middle button color: Right button color: - There's no detected mouse clicks on your project. Image @@ -1534,6 +1542,8 @@ Encoder responsible for generating the output file. ScreenToGif Built-in encoder. + KGy SOFT + KGy SOFT GIF Encoder. System Encoder made available by the system. FFmpeg @@ -1541,7 +1551,7 @@ Gifski External encoder, made by Gif.ski. Encoder options - + Enable the advanced mode. Manually input the parameters to control the encoder. @@ -1593,7 +1603,110 @@ Chroma key: Dither: Bayer scale: - + + + Quantizer + Background Color: + Pixels with alpha (transparency) that are considered opaque by the selected quantizer will be blended with this color before getting the quantized color. + Alpha Threshold: + Determines the input alpha value under which the quantized color will be transparent. If 0, then the result will never have transparency. If 255, then only fully transparent pixels are considered transparent. + White Threshold: + Determines the lowest input brightness to consider the result color white. It affects the brightness of the result. An error diffusion dithering may compensate for the value of this parameter though. + Direct Mapping + When checked, the quantized color is determined by a direct mapping rather than looking up the nearest palette entry. It speeds up quantization but may end up in a higher contrast result. An error diffusion dithering may compensate for the value of this parameter though. + Palette Size: + Determines the maximum size of the palette per frame. + Custom bit level. + When checked, the bit level can be configured manually. ⚠️ Warning: The highest bit level might require a LOT of memory! + Higher value means more accuracy, larger target color space, slower processing and larger memory usage. For example, if 1, then the result can have no more than 8 colors, or when 2, no more than 64 colors. For Octree and Wu quantizers it affects also maximum number of monochromatic shades. For example, if 5 (which is the default for Wu quantizer), only 32 monochromatic shades can be differentiated. ⚠️ Caution: The Wu quantizer consumes at least 650 MB with the highest value. + Black and White + Fixed 1 bpp palette with the black and white colors. + Grayscale 4 colors + Fixed 2 bpp palette with 4 grayscale entries. + Grayscale 16 colors + Fixed 4 bpp palette with 16 grayscale entries. + Grayscale 256 colors + Fixed 8 bpp palette with 256 grayscale entries. + System default 4 bpp palette + Fixed 4 bpp palette using the standard 16 sRGB colors. + System default 8 bpp palette + Fixed 8 bpp palette including the 'web-safe' colors and transparency. + RGB 332 palette + Fixed 8 bpp palette using the RGB 332 color space. + Octree Quantizer + Optimizing palette for each frame with the Octree algorithm. + Median Cut Quantizer + Optimizing palette for each frame with the Median Cut algorithm. + Wu Quantizer + Optimizing palette for each frame with Xiaolin Wu's algorithm. + + Ditherer + Strength: + The strength of the ditherer, or 0 to auto calibrate strength. + Seed: + An integer seed to be used to produce a specific random dithering pattern. Leave empty to use a random seed for every frame. + None + Frames are quantized without dithering. + Serpentine Processing + When checked, the error propagation direction is altered from line to line. This helps to reduce the ripple effect of the error diffusion dithering. + Bayer 2x2 (Ordered) + The 2x2 Bayer matrix pattern. + Bayer 3x3 (Ordered) + The 3x3 Bayer matrix pattern. + Bayer 4x4 (Ordered) + The 4x4 Bayer matrix pattern. + Bayer 8x8 (Ordered) + The 8x8 Bayer matrix pattern. + Dotted Halftone (Ordered) + A 8x8 matrix with a dotted halftone pattern. + Blue Noise (Ordered) + A 64x64 matrix with a blue noise pattern. + Atkinson (Error Diffusion) + Bill Atkinson's 4x3 matrix with 6 effective values. + Burkes (Error Diffusion) + D. Burkes' 5x2 matrix. + Floyd-Steinberg (Error Diffusion) + The original 3x2 matrix by Floyd and Steinberg. + Jarvis-Judice-Ninke (Error Diffusion) + An 5x3 matrix by Jarvis, Judice and Ninke. + Sierra 3 (Error Diffusion) + Frankie Sierra's 5x3 matrix. + Sierra 2 (Error Diffusion) + Frankie Sierra's 5x2 matrix. + Sierra Lite (Error Diffusion) + Frankie Sierra's 3x2 matrix. + Stevenson-Arce (Error Diffusion) + A 7x4 hexagonal matrix by Stevenson and Arce. + Stucki (Error Diffusion) + Stucki's 5x3 matrix. + Random Noise + Random white noise. + Interleaved Gradient Noise + Nonrandom gradient noise generated by a formula. + + Preview + Show current frame. + When checked, the preview shows the current frame instead of a standard image example. + Auto preview is disabled due to the high memory requirement of the current settings. Click to refresh the preview. + Failed to generate preview: {0} Click to try to generate it again. + + Animation Settings + Endless loop. + When checked, the animation will be looped indefinitely. + Back and forth. + When checked, the animation will be played back and forth. This is achieved by duplicating the frames meaning larger file size and longer encoding time. + Repeat Count: + Specifies how many times the animation will be played. + Allow delta frames. + When checked, unchanged pixels are attempted to be detected during the encoding. When using with an optimized quantizer, this option makes possible for a frame to have more than 256 colors. This option is ignored if the quantizer does not use transparency and Allow Clipped Frames is unchecked. + Delta Tolerance: + Specifies the maximum tolerance when detecting changed pixels. If 0, then no difference is tolerated at all. If 255, then there might be frames (or even all of them) that are added with no content. Reasonable range is between 0 and 16 for an optimized quantizer. The ones with fixed colors can be used with somewhat larger values with dithering. + If Delta Tolerance is too high the result might have poor quality. Click to reset delta tolerance. + Allow clipped frames. + When checked, the encoder is allowed to add smaller frames than the actual resolution. If Allow Delta Frames is unchecked, then allows only clipping possible transparent borders. + Allow Delta Frames is ignored because the current configuration does not use alpha and Allow Clipped Frames is unchecked. Click to enable clipped frames. + Allow Clipped Frames has no effect because the current configuration does not use alpha and Allow Delta Frames is unchecked. Click to enable delta frames. + Apng options Prediction: diff --git a/ScreenToGif/Resources/Localization/StringResources.es.xaml b/ScreenToGif/Resources/Localization/StringResources.es.xaml index c7b62863..4a903902 100644 --- a/ScreenToGif/Resources/Localization/StringResources.es.xaml +++ b/ScreenToGif/Resources/Localization/StringResources.es.xaml @@ -758,16 +758,16 @@ ¿Te gustaria probar la nueva grabadora?, Ve a "Opciones > Grabador", para activarlo. - Grabando - Webcam Grabando - Pizarra Grabando + Grabación + Grabación de Webcam + Grabación de Pizarra En Blanco Nuevo - Grabando - Webcam Grabando - Pizarra Grabando + Grabación + Grabación de Webcam + Grabación de Pizarra Media Insertar diff --git a/ScreenToGif/Resources/Localization/StringResources.hu.xaml b/ScreenToGif/Resources/Localization/StringResources.hu.xaml index a013367b..ec95aa32 100644 --- a/ScreenToGif/Resources/Localization/StringResources.hu.xaml +++ b/ScreenToGif/Resources/Localization/StringResources.hu.xaml @@ -1,4 +1,4 @@ - @@ -792,6 +792,12 @@ Támogatja az animáció mentését átlátszó háttérrel. Kiváló minőség • Grafika Jobb a kisebb színmennyiségű felvételekhez. + KGy SOFT • Kiegyensúlyozott + Jó minőség fénykép jellegű képekhez Wu kvantáló használatával árnyalás nélkül. + KGy SOFT • Magas minőség + Jó minőség fénykép jellegű képekhez nagyobb bitmélységet használó Wu kvantálóval és Floyd-Steinberg hibadiffúziós árnyalással. + KGy SOFT • Alacsony minőség, gyorsabb + Minden képkocka azonos, "webbiztos" színeket tartalmazó palettával való kvantálása Bayer 8x8 rendezett mátrix árnyalással. FFmpeg - Magasabb minőség Magasabb képminőség, de nagyobb fájlméret. FFmpeg - Alacsonyabb minőség @@ -1533,6 +1539,8 @@ A kimeneti fájl létrehozásáért felelős kódoló. ScreenToGif Beépített kódoló. + KGy SOFT + KGy SOFT GIF kódoló. Rendszer A rendszer által rendelkezésre álló kódoló. FFmpeg @@ -1592,7 +1600,110 @@ Chroma kulcs: Árnyalás: Bayer-skála: - + + + Kvantáló + Háttérszín: + Azok az alfa komponenst (átlátszóságot) tartalmazó pixelek, melyeket a kiválasztott kvantáló átlátszannak ítél, ezzel a színnel lesznek összemosva a végleges kvantált szín megállapítása előtt. + Alfa küszöb: + Meghatározza azt a bemeneti alfa értéket, ami alatt a kvantált szín átlátszónak tekintendő. Ha 0, akkor az eredmény soha nem tartalmaz átlátszóságot. Ha 255, akkor csak a teljesen átlátszó pixelek lesznek a végeredményben átlátszóak. + Fehérküszöb: + Meghatározza azt a legalacsonyabb bemeneti fényességet, amitől kezdve a kimeneti érték fehérnek tekintendő. Hatással van az eredmény fényességére, bár egy hibadiffúziós árnyaló képes kompenzálni ennek a paraméternek az értékét. + Közvetlen leképezés + Ha ki van jelölve, a kvantált szín megállapítása a palettában való legközelebbi szín keresése helyett egy közvetlen leképezéssel történik. Ez gyorsabb kvantálás eredményez, de növelheti az eredmény kontrasztját. Egy hibadiffúziós árnyaló képes kompenzálni ennek a paraméternek az értékét. + Palettaméret: + Meghatározza a képkockánkénti maximális palettaméretet. + Egyedi bitmélység + Ha ki van jelölve, a színtér bitmélysége szabadon állítható ⚠️ Vigyázat: A legmagasabb érték NAGYON sok memóriát igényelhet! + A magasabb érték nagyobb pontosságot, nagyobb színteret, lassabb feldolgozást és nagyobb memóriahasználatot jelent. Pl. ha 1, akkor az eredmény legfeljebb 8 színű lehet, vagy 2 esetén legfeljebb 64 színű. Oktális fa és Wu kvantálók esetén meghatározza a maximálisan megkülönböztethető monokróm árnyalatok színét is. Pl. ha 5 (az alapérték Wu kvantáló esetén), akkor legfeljebb 32 monokróm árnyalat különböztethető meg. ⚠️ Vigyázat: A Wu kvantáló legalább 650 MB memóriát igényel a legmagasabb érték esetén. + Fekete-fehér + Fix 1 bites paletta fekete és fehér színekkel. + Szürkeárnyalatos 4 színű + Fix 2 bites paletta 4 szürkeárnyalattal. + Szürkeárnyalatos 16 színű + Fix 4 bites paletta 16 szürkeárnyalattal. + Szürkeárnyalatos 256 színű + Fix 8 bites paletta 256 szürkeárnyalattal. + Alapértelmezett 4 bites rendszerpaletta + Fix 4 bites paletta a szabvány 16 sRGB színnel. + Alapértelmezett 8 bites rendszerpaletta + Fix 8 bites paletta "webbiztos" színekkel és átlátszósággal. + RGB 332 paletta + Fix 8 bites paletta az RGB 332-es színtér színeivel. + Oktális fa kvantáló + Minden képkocka színeinek optimalizálása az oktális keresőfa algoritmus segítségével. + Medián vágás kvantáló + Minden képkocka színeinek optimalizálása a medián vágás algoritmus segítségével. + Wu kvantáló + Minden képkocka színeinek optimalizálása Xiaolin Wu algoritmusának segítségével. + + Árnyalás + Erősség: + Az árnyalás erőssége, vagy 0 az erősség automatikus kalibrációjához. + Magérték: + Egy egész szám, ami egy rögzített véletlenszerű mintázatot eredményez. Üresen hagyva minden képkocka véletlenszerű magértéket fog használni. + Nincs + A képkockák árnyalás nélkül lesznek kvantálva. + Szerpentin feldolgozás + Ha ki van jelölve, a kvantálási hiba propagálásának iránya sorról-sorra váltakozva történik. Ez segít enyhíteni a hibadiffúziós árnyalásra jellemző fodrozódást. + Bayer 2x2 (rendezett) + A 2x2-es Bayer mátrix mintázat. + Bayer 3x3 (rendezett) + A 2x2-as Bayer mátrix mintázat. + Bayer 4x4 (rendezett) + A 4x4-es Bayer mátrix mintázat. + Bayer 8x8 (rendezett) + A 8x8-as Bayer mátrix mintázat. + Féltónus (rendezett) + Egy féltónus árnyalást eredményező 8x8-as mátrix. + Kék zaj (rendezett) + Egy kék zaj mintát tartalmazó 64x64-es mátrix. + Atkinson (hiba diffúzió) + Bill Atkinson 4x3-as mátrixa 6 effektív értékkel. + Burkes (hiba diffúzió) + D. Burkes' 5x2-es mátrixa. + Floyd-Steinberg (hiba diffúzió) + Floyd és Steinberg eredeti 3x2-es mátrixa. + Jarvis-Judice-Ninke (hiba diffúzió) + Jarvis, Judice és Ninke 5x3-as mátrixa. + Sierra 3 (hiba diffúzió) + Frankie Sierra 5x3-as mátrixa. + Sierra 2 (hiba diffúzió) + Frankie Sierra 5x2-es mátrixa. + Sierra Lite (hiba diffúzió) + Frankie Sierra 3x2-es mátrixa. + Stevenson-Arce (hiba diffúzió) + Stevenson és Arce 7x4-es hexagonális mátrixa. + Stucki (hiba diffúzió) + Stucki 5x3-as mátrixa. + Véletlen zaj + Véletlen fehér zaj. + Átlapolt gradiens zaj + Egy képlet alapján generált nem véletlenszerű gradiens zaj. + + Előnézet + Aktuális képkocka + Ka ki van jelölve, az előnézet az aktuális képkockát mutatja egy állandó példa helyett. + A jelen beállítások magas memóriaigénye miatt az automatikus előnézet kikapcsolva. A linkre kattintással frissíthető az előnézet. + Az előnézet generálása során hiba történt: {0} A linkre kattintással megkísérelhető az előnézet újbóli generálása. + + Az animáció beállításai + Végtelen ismétlés + Ha ki van jelölve, az animáció vég nélkül fog ismétlődni. + Oda-vissza + Ha ki van jelölve, az animáció oda-vissza fog ismétlődni. Mivel ezzel közel duplázódik a kódolt képkockák száma, ez nagyobb fájlmérettel és hosszabb kódolási idővel jár. + Ismétlések száma: + Meghatározza, hányszor legyen az animáció lejátszva. + Delta képkockák engedélyezése + Ha ki van jelölve, a kódolás során kísérlet történik a változatlan pixelek detektálására. Optimalizált kvantáló esetén ez lehetővé teszi, hogy egy képkocka több mint 256 színt tartalmazzon. Ez a beállítás nincs figyelembe véve, ha a kvantáló nem használ átlátszóságot és nincs kijelölve a Levágott képkockák engedélyezése. + Delta tűréshatár: + Meghatározza a maximális tűréshatárt a változott pixelek detektálása során. Ha 0, akkor semmilyen különbség nincs tolerálva. Ha 255, akkor lehetséges, hogy egyes képkockák (vagy akár az összes) üres lesz. Optimalizált kvantáló esetén az észszerű tartomány 0 és 16 között van, míg a fix színeket alkalmazók használhatók valamivel nagyobb értékekkel, különösen árnyalás esetén. + A túl magas delta tűréshatár gyenge minőségű eredménnyel járhat. A linkre kattintással visszaállítható a delta tűréshatár. + Levágott képkockák engedélyezése + Ha ki van jelölve, a kódoló a tényleges felbontásnál kisebb képkockákat is hozzáadhat az animációhoz. Ha a delta képkockák nincsenek engedélyezve, akkor ez mindössze az esetlegesen átlátszó keretek levágását jelenti. + A delta képkockák engedélyezése figyelmen kívül lesz hagyva, mert a jelenlegi konfiguráció nem használ átlátszóságot, és a Levágott képkockák engedélyezése nincs kijelölve. A linkre kattintás engedélyezi a levágott képkockákat. + A levágott képkockák engedélyezésének nem lesz hatása, mert a jelenlegi konfiguráció nem használ átlátszóságot, és a Delta képkockák engedélyezése nincs kijelölve. A linkre kattintás engedélyezi a delta képkockákat. + Apng beállítások Előrejelzés: diff --git a/ScreenToGif/Resources/Localization/StringResources.pt.xaml b/ScreenToGif/Resources/Localization/StringResources.pt.xaml index c70e3288..68ae876c 100644 --- a/ScreenToGif/Resources/Localization/StringResources.pt.xaml +++ b/ScreenToGif/Resources/Localization/StringResources.pt.xaml @@ -280,6 +280,8 @@ Quadros serão capturados em base de 'por hora' (timelapse), considerando o denominador de taxa de captura definido na tela do gravador. Duração de reprodução: (Em ms, cada quadro capturado receberá essa duração) + Atraso: + (Em ms, tempo aguardado antes da captura de cada quadro) Cada quadro será capturado em intervalos de {0}. Modo de captura @@ -792,22 +794,28 @@ Suporta salvar a animação com um fundo transparente. Alta qualidade • Gráficos Melhor para gravações com menor quantidade de cores. - FFmpeg - Qualidade superior + KGy SOFT • Balanceado + Boa qualidade para imagens semelhantes a fotos usando o quantizador de Wu sem pontilhamento. + KGy SOFT • Alta qualidade + Alta qualidade para imagens semelhantes a fotos usando o quantizador de Wu com nível de bits mais alto e pontilhamento de difusão de erro Floyd-Steinberg. + KGy SOFT • Baixa qualidade, mais rápido + Quantização de todos os quadros com a mesma paleta 'web-safe' predefinida e pontilhamento ordenado Bayer 8x8. + FFmpeg • Qualidade superior Qualidade de imagem superior, mas com um tamanho de arquivo maior. - FFmpeg - Menor qualidade + FFmpeg • Menor qualidade Menor qualidade de imagem, mas com um tamanho de arquivo menor. - Gifski - Maior qualidade + Gifski • Maior qualidade Maior qualidade de imagem, mas com maior tamanho de arquivo. - Gifski - Menor qualidade + Gifski • Menor qualidade Menor qualidade de imagem, mas com um tamanho de arquivo menor. - Gifski - Menor qualidade e codificação mais rápida + Gifski • Menor qualidade e codificação mais rápida Qualidade de imagem ainda menor, com uma codificação mais rápida, mas com um tamanho de arquivo menor. - Sistema - Baixa qualidade + Sistema • Baixa qualidade Baixa qualidade, mas codificação mais rápida. - FFmpeg - Alta qualidade + FFmpeg • Alta qualidade Alta qualidade de imagem e tamanho de arquivo pequeno, mas codificação mais lenta. - FFmpeg - Menor qualidade + FFmpeg • Baixa qualidade Menor qualidade de imagem, tamanho de arquivo pequeno e codificação mais rápida. Alta qualidade Alta qualidade de imagem e tamanho do arquivo pequeno. @@ -1534,6 +1542,8 @@ Codificador responsável por gerar o arquivo final. ScreenToGif Codificador embutido. + KGy SOFT + Codificador de Gif do KGy SOFT. Sistema Codificador disponibilizado pelo sistema. FFmpeg @@ -1593,6 +1603,109 @@ Chroma key: Pontilhado: Escala Bayer: + + + Quantizador + Cor de Fundo: + Pixels com alfa (transparência) que são considerados opacos pelo quantizador selecionado serão misturados com esta cor antes de obter a cor quantizada. + Limiar Alfa: + Determina o valor alfa de entrada sob o qual a cor quantizada será transparente. Se 0, então o resultado nunca terá transparência. Se 255, somente pixels totalmente transparentes são considerados transparentes. + Limiar Branco: + Determina o brilho de entrada mais baixo para considerar o resultado da cor branca. Afeta o brilho do resultado. Um pontilhador de difusão de erro pode compensar o valor deste parâmetro. + Mapeamento Direto + Quando marcada, a cor quantizada é determinada por um mapeamento direto em vez de procurar a entrada da paleta mais próxima. Ele acelera a quantização, mas pode resultar em um resultado de contraste mais alto. Um pontilhamento de difusão de erro pode compensar o valor desse parâmetro. + Tamanho da Paleta: + Determina o tamanho máximo da paleta por quadro. + Nível de bits personalizado. + Quando marcado, o nível de bits pode ser configurado manualmente. ⚠️ Aviso: O nível de bit mais alto pode exigir MUITA memória! + Um valor mais alto significa mais precisão, maior espaço de cores de destino, processamento mais lento e maior uso de memória. Por exemplo, se 1, então o resultado não pode ter mais de 8 cores, ou quando 2, não mais de 64 cores. Para os quantizadores Octree e Wu, afeta também o número máximo de tons monocromáticos. Por exemplo, se 5 (que é o padrão para o quantizador Wu), apenas 32 tons monocromáticos podem ser diferenciados. ⚠️ Cuidado: O quantizador Wu consome pelo menos 650 MB com o valor mais alto. + Preto e Branco + Paleta fixa de 1 bpp com as cores preto e branco. + Tons de cinza com 4 cores + Paleta fixa de 2 bpp com 4 entradas em tons de cinza. + Escala de cinza com 16 cores + Paleta fixa de 4 bpp com 16 entradas em tons de cinza. + Escala de cinza com 256 cores + Paleta fixa de 8 bpp com 256 entradas em tons de cinza. + Paleta de 4 bpp padrão do sistema + Paleta fixa de 4 bpp usando as 16 cores padrão de sRGB. + Paleta de 8 bpp padrão do sistema + Paleta fixa de 8 bpp, incluindo as cores e transparência 'seguras para a web'. + Paleta RGB 332 + Paleta fixa de 8 bpp usando o espaço de cores RGB 332. + Quantizador Octree + Otimizando a paleta para cada quadro com o algoritmo Octree. + Quantizador de corte mediano + Otimizando a paleta para cada quadro com o algoritmo de corte mediano. + Quantizador Wu + Otimizando a paleta para cada quadro com o algoritmo de Xiaolin Wu. + + Pontilhador + Força: + A força do pontilhador, ou 0 para calibrar automaticamente a força. + Semente: + Um número inteiro a ser usado para produzir um padrão de pontilhamento aleatório específico. Deixe em branco para usar uma semente aleatória para cada quadro. + Nenhum + Os quadros são quantizados sem pontilhamento. + Processamento de Serpentina + Quando marcada, a direção de propagação do erro é alterada de linha para linha. Isso ajuda a reduzir o efeito cascata do pontilhamento de difusão de erro. + Bayer 2x2 (Ordenado) + O padrão de matriz 2x2 Bayer. + Bayer 3x3 (Ordenado) + O padrão de matriz 3x3 Bayer. + Bayer 4x4 (Ordenado) + O padrão de matriz 4x4 Bayer. + Bayer 8x8 (Ordenado) + O padrão de matriz 8x8 Bayer. + Meio-tom pontilhado (Ordenado) + Uma matriz 8x8 com um padrão de meio-tom pontilhado. + Ruído Azul (Ordenado) + Uma matriz 64x64 com um padrão de ruído azul. + Atkinson (Difusão de Erro) + Matriz 4x3 de Bill Atkinson com 6 valores efetivos. + Burkes (Difusão de Erro) + Matriz 5x2 de D. Burkes. + Floyd-Steinberg (Difusão de Erro) + A matriz 3x2 original de Floyd e Steinberg. + Jarvis-Judice-Ninke (Difusão de Erro) + Uma matriz 5x3 de Jarvis, Judice e Ninke. + Sierra 3 (Difusão de Erro) + Matriz 5x3 de Frankie Sierra. + Sierra 2 (Difusão de Erro) + Matriz 5x2 de Frankie Sierra. + Sierra Lite (Difusão de Erro) + Matriz 3x2 de Frankie Sierra. + Stevenson-Arce (Difusão de Erro) + Uma matriz hexagonal 7x4 por Stevenson e Arce. + Stucki (Difusão de Erro) + Matriz 5x3 de Stucki. + Ruído Aleatório + Ruído branco aleatório. + Ruído de Dradiente Intercalado + Ruído de gradiente não aleatório gerado por uma fórmula. + + Visualizar + Mostrar quadro atual. + Quando marcada, a visualização mostra o quadro atual em vez de um exemplo de imagem padrão. + A visualização automática está desativada devido ao alto requisito de memória das configurações atuais. Clique para atualizar a visualização. + Falha ao gerar visualização: {0} Clique para tentar gerá-lo novamente. + + Configurações de Animação + Loop infinito. + Quando marcada, a animação será repetida indefinidamente. + Vai e volta. + Quando marcada, a animação será reproduzida para frente e para trás. Isso é obtido duplicando os quadros, o que significa um tamanho de arquivo maior e um tempo de codificação mais longo. + Repetições: + Especifica quantas vezes a animação será reproduzida. + Permitir quadros delta. + Quando marcado, os pixels inalterados tentam ser detectados durante a codificação. Ao usar com um quantizador otimizado, esta opção possibilita que um quadro tenha mais de 256 cores. Esta opção é ignorada se o quantizador não usar transparência e a opção Permitir Quadros Recortados estiver desmarcada. + Tolerância Delta: + Especifica a tolerância máxima ao detectar pixels alterados. Se 0, então nenhuma diferença é tolerada. Se 255, então pode haver quadros (ou mesmo todos eles) que são adicionados sem conteúdo. O intervalo razoável está entre 0 e 16 para um quantizador otimizado. Aqueles com cores fixas podem ser usados com valores um pouco maiores com dithering. + Se a tolerância Delta for muito alta, o resultado pode ter baixa qualidade. Clique para redefinir a tolerância delta. + Permitir quadros recortados. + Quando marcado, o codificador pode adicionar quadros menores do que a resolução real. Se Permitir Quadros Delta estiver desmarcado, permite apenas o recorte de possíveis bordas transparentes. + Permitir Quadros Delta é ignorado porque a configuração atual não usa alfa e Permitir Quadros Recortados está desmarcada. Clique para habilitar quadros recortados. + Permitir quadros cortados não tem efeito porque a configuração atual não usa alfa e Permitir quadros delta está desmarcada. Clique para habilitar quadros delta. Opções do Apng diff --git a/ScreenToGif/Resources/Settings.xaml b/ScreenToGif/Resources/Settings.xaml index 86d794db..c475edd5 100644 --- a/ScreenToGif/Resources/Settings.xaml +++ b/ScreenToGif/Resources/Settings.xaml @@ -83,7 +83,9 @@ False PerSecond 1000 + 0 500 + 0 66 66 False @@ -144,7 +146,7 @@ - + @@ -349,12 +351,13 @@ Bottom Horizontal - + + #00000000 #78FFFF00 #7800FFFF #78FF0000 - 12 - 12 + 12 + 12 #FF000000 diff --git a/ScreenToGif/Resources/Vectors.xaml b/ScreenToGif/Resources/Vectors.xaml index 5e245493..e6fa6e3d 100644 --- a/ScreenToGif/Resources/Vectors.xaml +++ b/ScreenToGif/Resources/Vectors.xaml @@ -595,6 +595,12 @@ + + + + + + @@ -1226,7 +1232,19 @@ - + + + + + + + + + + + + + diff --git a/ScreenToGif/ScreenToGif.csproj b/ScreenToGif/ScreenToGif.csproj index 1b8836b9..e505e75f 100644 --- a/ScreenToGif/ScreenToGif.csproj +++ b/ScreenToGif/ScreenToGif.csproj @@ -15,7 +15,7 @@ false true 0 - 2.36.0.0 + 2.37.0.0 false true true @@ -104,11 +104,12 @@ + - + all - + @@ -139,7 +140,7 @@ OnBuildSuccess Nicke Manarin Nicke Manarin - 2.36.0 + 2.37.0 Copyright© Nicke Manarin 2022 https://www.screentogif.com Readme.md diff --git a/ScreenToGif/Themes/ComboBox.xaml b/ScreenToGif/Themes/ComboBox.xaml index 4dd94f51..d4470be2 100644 --- a/ScreenToGif/Themes/ComboBox.xaml +++ b/ScreenToGif/Themes/ComboBox.xaml @@ -434,6 +434,19 @@ + + + + + + + + + + + + diff --git a/ScreenToGif/Themes/Common.xaml b/ScreenToGif/Themes/Common.xaml index bf76aba2..b6511692 100644 --- a/ScreenToGif/Themes/Common.xaml +++ b/ScreenToGif/Themes/Common.xaml @@ -1,4 +1,4 @@ - @@ -91,5 +91,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ScreenToGif/Themes/Generic.xaml b/ScreenToGif/Themes/Generic.xaml index 8d4d498e..b0825d39 100644 --- a/ScreenToGif/Themes/Generic.xaml +++ b/ScreenToGif/Themes/Generic.xaml @@ -35,6 +35,7 @@ + - - + + @@ -507,7 +507,23 @@ + + + + + + + + + + + + @@ -1152,7 +1168,7 @@ - - + diff --git a/ScreenToGif/Windows/Other/AutomatedTask.xaml.cs b/ScreenToGif/Windows/Other/AutomatedTask.xaml.cs index d448eddb..53ab529a 100644 --- a/ScreenToGif/Windows/Other/AutomatedTask.xaml.cs +++ b/ScreenToGif/Windows/Other/AutomatedTask.xaml.cs @@ -51,8 +51,8 @@ private void TypeComboBox_Selected(object sender, RoutedEventArgs e) //Create a new model. switch ((TaskTypes)TypeComboBox.SelectedIndex) { - case TaskTypes.MouseClicks: - CurrentTask = MouseClicksViewModel.Default(); + case TaskTypes.MouseEvents: + CurrentTask = MouseEventsViewModel.Default(); break; case TaskTypes.KeyStrokes: CurrentTask = KeyStrokesViewModel.Default(); @@ -74,8 +74,8 @@ private void TypeComboBox_Selected(object sender, RoutedEventArgs e) switch ((TaskTypes)TypeComboBox.SelectedIndex) { - case TaskTypes.MouseClicks: - MainPresenter.Content = new MouseClicksPanel { DataContext = CurrentTask }; + case TaskTypes.MouseEvents: + MainPresenter.Content = new MouseEventsPanel { DataContext = CurrentTask }; break; case TaskTypes.KeyStrokes: MainPresenter.Content = new KeyStrokesPanel { DataContext = CurrentTask }; diff --git a/ScreenToGif/Windows/Other/Downloader.xaml.cs b/ScreenToGif/Windows/Other/Downloader.xaml.cs index bde7eb4f..4f91d70a 100644 --- a/ScreenToGif/Windows/Other/Downloader.xaml.cs +++ b/ScreenToGif/Windows/Other/Downloader.xaml.cs @@ -34,9 +34,10 @@ private string GetDownloadUrl() case "gifski": return "https://www.screentogif.com/downloads/Gifski.zip"; case "ffmpeg": - return $"https://www.screentogif.com/downloads/FFmpeg-4.3.1-x{(Environment.Is64BitProcess ? "64" : "86")}.zip"; - case "sharpdx": - return "https://www.screentogif.com/downloads/SharpDx.zip"; + { + return Environment.Is64BitProcess ? "https://www.screentogif.com/downloads/FFmpeg-4.4.1-x64.zip" : + "https://www.screentogif.com/downloads/FFmpeg-4.3.1-x86.zip"; + } } return null; @@ -114,6 +115,7 @@ private async Task Download() entry?.ExtractToFile(Path.Combine(DestinationPath, entry.Name), true); } + break; } } diff --git a/ScreenToGif/Windows/Other/Preset.xaml b/ScreenToGif/Windows/Other/Preset.xaml index 418faf58..9094079e 100644 --- a/ScreenToGif/Windows/Other/Preset.xaml +++ b/ScreenToGif/Windows/Other/Preset.xaml @@ -64,17 +64,20 @@ - - - - + + diff --git a/ScreenToGif/Windows/Other/Preset.xaml.cs b/ScreenToGif/Windows/Other/Preset.xaml.cs index e6852b0e..7f4d124c 100644 --- a/ScreenToGif/Windows/Other/Preset.xaml.cs +++ b/ScreenToGif/Windows/Other/Preset.xaml.cs @@ -45,12 +45,14 @@ private void Window_Loaded(object sender, RoutedEventArgs e) EncoderFfmpegItem.IsEnabled = true; EncoderGifskiItem.IsEnabled = Environment.Is64BitProcess; EncoderSystemItem.IsEnabled = true; + EncoderKGySoftItem.IsEnabled = true; break; case ExportFormats.Apng: EncoderScreenToGifItem.IsEnabled = true; EncoderFfmpegItem.IsEnabled = true; EncoderGifskiItem.IsEnabled = false; EncoderSystemItem.IsEnabled = false; + EncoderKGySoftItem.IsEnabled = false; break; case ExportFormats.Webp: case ExportFormats.Bpg: @@ -63,6 +65,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e) EncoderFfmpegItem.IsEnabled = true; EncoderGifskiItem.IsEnabled = false; EncoderSystemItem.IsEnabled = false; + EncoderKGySoftItem.IsEnabled = false; break; case ExportFormats.Jpeg: @@ -74,6 +77,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e) EncoderFfmpegItem.IsEnabled = false; EncoderGifskiItem.IsEnabled = false; EncoderSystemItem.IsEnabled = false; + EncoderKGySoftItem.IsEnabled = false; break; } @@ -154,6 +158,14 @@ private void Ok_Executed(object sender, System.Windows.Input.ExecutedRoutedEvent embeddedGifPreset.ImageId = "Vector.Logo"; Current = embeddedGifPreset; break; + case EncoderTypes.KGySoft: + var kgySoftGifPreset = new KGySoftGifPreset(); + + Current.CopyPropertiesTo(kgySoftGifPreset); + kgySoftGifPreset.Encoder = EncoderTypes.KGySoft; + kgySoftGifPreset.ImageId = "Vector.KGySoft"; + Current = kgySoftGifPreset; + break; case EncoderTypes.FFmpeg: var ffmpegGifPreset = new FfmpegGifPreset(); diff --git a/ScreenToGif/Windows/Recorder.xaml.cs b/ScreenToGif/Windows/Recorder.xaml.cs index 712034a5..f4e89bb0 100644 --- a/ScreenToGif/Windows/Recorder.xaml.cs +++ b/ScreenToGif/Windows/Recorder.xaml.cs @@ -960,6 +960,11 @@ internal async Task Record() /// private async Task Snap() { + var snapTriggerDelay = GetTriggerDelay(); + + if (snapTriggerDelay != 0) + await Task.Delay(snapTriggerDelay); + HideGuidelines(); if (Project == null || Project.Frames.Count == 0)