29 | AutoDrawer is a program that allows users to draw imported images using their cursor. The program supports Windows and Linux. The program controls the movement of the cursor, allowing for precise tracing of an imported image. AutoDrawer is often used as a utility in drawing games, providing an easy and intuitive way for players to draw images.
30 |
31 |
37 | Latest build can be found here.
38 |
39 |
40 |
Version 1
41 |
42 | AutoDrawer was originally created as a fork of [rainzhao2000](https://github.com/rainzhao2000/autodrawer)'s software and
43 | has since been modified to support automatic drawing in more drawing games. This version is coded in C# and uses the WPF
44 | user interface framework. AutoDrawer is open source and only supports the Windows operating systems.
45 |
51 |
52 | Join [the Discord](https://discord.gg/rwvUFraDnb) for help, as we are active on there!
53 |
54 |
55 |
Operating Systems
56 |
57 |
58 | | Operating System | Version 2 | Version 1 |
59 | |------------------|----------------------|--------------------|
60 | | Windows | :white_check_mark: | :white_check_mark: |
61 | | Linux | :x:* | :x: |
62 | | Mac | :x:** | :x: |
63 | * Internally we have had success with Linux on X11, however latest builds crash. Do not try to build for Linux, it won't work.
64 | We intend on moving to our own input service to support both Wayland and X11.
65 | ** MacOS is likely in a similar state ^, had previously worked, additionally requires signatures for proper release.
66 |
67 |
68 |
Documentation and FAQ
69 |
70 | Documentation and FAQ can [be found here](https://auto-draw.com/wiki)
71 |
72 |
--------------------------------------------------------------------------------
/DevTest.axaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
36 | This is for testing features upon implimentation, don't be surprised by the lack of stuff.
37 |
38 |
39 |
40 |
42 | Results:
43 |
44 |
45 | DALL-E Image Generation
46 | Prompt
47 | A painting of a beautiful starlit sky, with mountains cascading across the landscape.
48 | Image size and model
49 | 256x256
50 | dall-e-2
51 |
55 | (Note: Make sure to set your OpenAI Developer Key in Settings > Dev. Settings!)
56 |
57 |
--------------------------------------------------------------------------------
/DevTest.axaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 | using Avalonia.Controls;
6 | using Avalonia.Interactivity;
7 | using Avalonia.Threading;
8 | using Newtonsoft.Json.Linq;
9 | using RestSharp;
10 | using RestSharp.Authenticators.OAuth2;
11 | using SkiaSharp;
12 |
13 | namespace Autodraw;
14 |
15 | public partial class DevTest : Window
16 | {
17 | public DevTest()
18 | {
19 | InitializeComponent();
20 | TestBenchmarking.Click += (sender, e) => Benchmark();
21 | TestPopup.Click += TestPopup_Click;
22 | GenerateImage.Click += GenerateImage_ClickAsync;
23 | }
24 |
25 | private void GenerateImage_ClickAsync(object? sender, RoutedEventArgs e)
26 | {
27 | new MessageBox().ShowMessageBox("Depreciated!","This is depreciated, please use the context menu prompt instead.\nRight click the image box and select AI Generation");
28 | }
29 |
30 | private void TestPopup_Click(object? sender, RoutedEventArgs e)
31 | {
32 | new MessageBox().ShowMessageBox("Hi", "Loser");
33 | }
34 |
35 | public static unsafe SKBitmap TestImage(int width, int height)
36 | {
37 | SKBitmap returnbtmp = new(width, height);
38 |
39 | var srcPtr = (byte*)returnbtmp.GetPixels().ToPointer();
40 |
41 | Random rng = new();
42 |
43 | for (var row = 0; row < height; row++)
44 | for (var col = 0; col < width; col++)
45 | {
46 | *srcPtr++ = (byte)rng.Next(0, 255);
47 | *srcPtr++ = (byte)rng.Next(0, 255);
48 | *srcPtr++ = (byte)rng.Next(0, 255);
49 | *srcPtr++ = (byte)rng.Next(0, 255);
50 | }
51 |
52 | return returnbtmp;
53 | }
54 |
55 | private void Benchmark()
56 | {
57 | var sw = Stopwatch.StartNew();
58 |
59 | SKBitmap small = new(64, 64);
60 | sw.Restart();
61 | ImageProcessing.Process(small, new ImageProcessing.Filters { Invert = true });
62 | var TimeTookSmall = sw.ElapsedMilliseconds;
63 |
64 | BenchmarkResults.Text = "Results:\n64x64: " + TimeTookSmall;
65 |
66 | SKBitmap avg = new(384, 384);
67 | sw.Restart();
68 | ImageProcessing.Process(avg, new ImageProcessing.Filters { Invert = true });
69 | var TimeTookAvg = sw.ElapsedMilliseconds;
70 |
71 | BenchmarkResults.Text = "Results:\n64x64: " + TimeTookSmall + "\n384x384: " + TimeTookAvg;
72 |
73 | SKBitmap med = new(1024, 1024);
74 | sw.Restart();
75 | ImageProcessing.Process(med, new ImageProcessing.Filters { Invert = true });
76 | var TimeTookMed = sw.ElapsedMilliseconds;
77 |
78 | BenchmarkResults.Text = "Results:\n64x64: " + TimeTookSmall + "\n384x384: " + TimeTookAvg + "\n1024x1024: " +
79 | TimeTookMed;
80 |
81 | SKBitmap large = new(3072, 3072);
82 | sw.Restart();
83 | ImageProcessing.Process(large, new ImageProcessing.Filters { Invert = true });
84 | var TimeTookLarge = sw.ElapsedMilliseconds;
85 |
86 | sw.Reset();
87 |
88 | BenchmarkResults.Text = "Results:\n64x64: " + TimeTookSmall + "\n384x384: " + TimeTookAvg + "\n1024x1024: " +
89 | TimeTookMed + "\n4096x4096: " + TimeTookLarge;
90 |
91 | small.Dispose();
92 | avg.Dispose();
93 | med.Dispose();
94 | large.Dispose();
95 | }
96 | }
--------------------------------------------------------------------------------
/Preview.axaml:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Auto,*
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 480
40 |
41 |
42 |
43 | 360
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/MessageBox.axaml:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | Info!
78 |
80 | Info Content!
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/Styles/universal.axaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
12 |
13 |
16 |
17 |
20 |
21 |
24 |
27 |
30 |
31 |
35 |
36 |
37 |
42 |
43 |
46 |
47 |
51 |
52 |
53 |
56 |
59 |
62 |
63 |
64 |
67 |
68 |
71 |
72 |
73 |
78 |
79 |
82 |
83 |
86 |
87 |
88 |
91 |
92 |
93 |
100 |
101 |
102 |
103 |
106 |
109 |
110 |
111 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/OpenAIPrompt.axaml:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | DALL-E 2
76 |
77 |
78 | DALL-E 3 SD
79 |
80 |
81 | DALL-E 3 HD
82 |
83 |
84 |
85 |
86 |
87 | A large landscape with mountains towering across, with a large reflective lake.
88 |
89 |
90 |
91 |
92 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/Input.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Numerics;
3 | using System.Runtime.InteropServices;
4 | using Avalonia;
5 | using Avalonia.Controls;
6 | using SharpHook;
7 | using SharpHook.Native;
8 | #if WINDOWS
9 | using SimWinInput;
10 | #endif
11 |
12 | namespace Autodraw;
13 |
14 | public class Input
15 | {
16 | //// Variables
17 |
18 | // Private
19 | private static readonly EventSimulator eventSim = new();
20 |
21 | // Public
22 | public static TaskPoolGlobalHook taskHook = new();
23 | public static Vector2 mousePos;
24 | public static bool forceUio = false;
25 | public static event EventHandler? MousePosUpdate;
26 | public static PixelPoint primaryScreenBounds;
27 |
28 | //// Functions
29 |
30 | // Core
31 |
32 | private static bool isUio()
33 | {
34 | var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
35 | return !isWindows || forceUio;
36 | }
37 |
38 | public static void Start()
39 | {
40 | if (taskHook.IsRunning) return;
41 | if (taskHook.IsDisposed) return; // Avalonia Preview Fix.
42 | primaryScreenBounds = MainWindow.CurrentMainWindow.Screens.Primary.Bounds.TopLeft; // updates if main screen orientation changes
43 |
44 | taskHook.MouseMoved += (sender, e) =>
45 | {
46 | mousePos = new Vector2(e.Data.X, e.Data.Y);
47 |
48 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) // || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
49 | mousePos = new Vector2(mousePos.X + primaryScreenBounds.X, mousePos.Y + primaryScreenBounds.Y);
50 | MousePosUpdate?.Invoke(null, EventArgs.Empty);
51 | };
52 |
53 | taskHook.RunAsync();
54 | }
55 |
56 | public static void Stop()
57 | {
58 | // Never really need to call this UNLESS, we are closing the software.
59 | taskHook.Dispose();
60 | }
61 |
62 | // Movement
63 |
64 | public static void MoveTo(short x, short y)
65 | {
66 | #if WINDOWS
67 | SimMouse.Act(SimMouse.Action.MoveOnly, x, y);
68 | mousePos = new Vector2(x, y);
69 | #else
70 | eventSim.SimulateMouseMovement(x, y);
71 | #endif
72 | }
73 |
74 | public static void MoveBy(short xOffset, short yOffset)
75 | {
76 | #if WINDOWS
77 | SimMouse.Act(SimMouse.Action.MoveOnly, xOffset + (short)mousePos.X, yOffset + (short)mousePos.Y);
78 | mousePos = new Vector2(xOffset + (short)mousePos.X, yOffset + (short)mousePos.Y);
79 | #else
80 | eventSim.SimulateMouseMovementRelative(xOffset, yOffset);
81 | #endif
82 | }
83 |
84 | // Click Handling
85 |
86 | public static void SendClick(byte mouseType)
87 | {
88 | #if WINDOWS
89 | var buttonDown = mouseType == MouseTypes.MouseLeft
90 | ? SimMouse.Action.LeftButtonDown
91 | : SimMouse.Action.RightButtonDown;
92 | var buttonUp = mouseType == MouseTypes.MouseLeft
93 | ? SimMouse.Action.LeftButtonUp
94 | : SimMouse.Action.RightButtonUp;
95 | SimMouse.Act(buttonDown, (int)mousePos.X, (int)mousePos.Y);
96 | SimMouse.Act(buttonUp, (int)mousePos.X, (int)mousePos.Y);
97 | #else
98 | var button = mouseType == MouseTypes.MouseLeft ? MouseButton.Button1 : MouseButton.Button2;
99 | eventSim.SimulateMousePress(button);
100 | eventSim.SimulateMouseRelease(button);
101 | #endif
102 | }
103 |
104 | public static void SendClickDown(byte mouseType)
105 | {
106 | #if WINDOWS
107 | var buttonDown = mouseType == MouseTypes.MouseLeft
108 | ? SimMouse.Action.LeftButtonDown
109 | : SimMouse.Action.RightButtonDown;
110 | SimMouse.Act(buttonDown, (int)mousePos.X, (int)mousePos.Y);
111 | #else
112 | var button = mouseType == MouseTypes.MouseLeft ? MouseButton.Button1 : MouseButton.Button2;
113 | eventSim.SimulateMousePress(button);
114 | #endif
115 | }
116 |
117 | public static void SendClickUp(byte mouseType)
118 | {
119 | #if WINDOWS
120 | var buttonUp = mouseType == MouseTypes.MouseLeft
121 | ? SimMouse.Action.LeftButtonUp
122 | : SimMouse.Action.RightButtonUp;
123 | SimMouse.Act(buttonUp, (int)mousePos.X, (int)mousePos.Y);
124 | #else
125 | var button = mouseType == MouseTypes.MouseLeft ? MouseButton.Button1 : MouseButton.Button2;
126 | eventSim.SimulateMouseRelease(button);
127 | #endif
128 | }
129 |
130 | public static void SendKeyDown(KeyCode keyCode)
131 | {
132 | eventSim.SimulateKeyPress(keyCode);
133 | }
134 | public static void SendKeyUp(KeyCode keyCode)
135 | {
136 | eventSim.SimulateKeyRelease(keyCode);
137 | }
138 | public static void SendText(string text)
139 | {
140 | eventSim.SimulateTextEntry(text);
141 | }
142 |
143 | public static class MouseTypes
144 | {
145 | public static byte MouseLeft = 1;
146 | public static byte MouseRight = 2;
147 | }
148 | }
--------------------------------------------------------------------------------
/OpenAIPrompt.axaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Runtime.InteropServices;
5 | using System.Text.RegularExpressions;
6 | using System.Threading.Tasks;
7 | using Avalonia;
8 | using Avalonia.Controls;
9 | using Avalonia.Input;
10 | using Avalonia.Interactivity;
11 | using Avalonia.Markup.Xaml;
12 | using Avalonia.Threading;
13 | using Newtonsoft.Json.Linq;
14 | using RestSharp;
15 | using RestSharp.Authenticators.OAuth2;
16 |
17 | namespace Autodraw;
18 |
19 | public partial class OpenAIPrompt : Window
20 | {
21 | public static OpenAIPrompt? current;
22 |
23 | public OpenAIPrompt()
24 | {
25 | InitializeComponent();
26 |
27 | if (Config.GetEntry("OpenAIKey") is null)
28 | {
29 | Warning1.Opacity = 1;
30 | Warning2.Opacity = 1;
31 | }
32 | else
33 | {
34 | Warning1.Opacity = 0;
35 | Warning2.Opacity = 0;
36 | }
37 |
38 | Model.SelectedIndex = 0;
39 | Resolution.Items.Clear();
40 | Resolution.Items.Add("1024x1024");
41 | Resolution.Items.Add("512x512");
42 | Resolution.Items.Add("256x256");
43 | Resolution.SelectedIndex = 0;
44 |
45 | Model.SelectionChanged += ModelOnSelectionChanged;
46 |
47 | CloseAppButton.Click += QuitAppOnClick;
48 | Generate.Click += GenerateOnClick;
49 | }
50 |
51 | private void ModelOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
52 | {
53 | var rawModel = ((ComboBoxItem)this.Model.SelectedItem).Content.ToString();
54 | var Model = rawModel == "DALL-E 2" ? "dall-e-2" : "dall-e-3";
55 |
56 | // For some reason the traditional way, Resolution.Items = new List {} doesn't work. Really annoying.
57 | if (Model == "dall-e-2")
58 | {
59 | Resolution.Items.Clear();
60 | Resolution.Items.Add("1024x1024");
61 | Resolution.Items.Add("512x512");
62 | Resolution.Items.Add("256x256");
63 | Resolution.SelectedIndex = 0;
64 | }
65 | else
66 | {
67 | Resolution.Items.Clear();
68 | Resolution.Items.Add("1024x1024");
69 | Resolution.Items.Add("1024x1792");
70 | Resolution.Items.Add("1792x1024");
71 | Resolution.SelectedIndex = 0;
72 | }
73 | }
74 |
75 | private void GenerateOnClick(object? sender, RoutedEventArgs e)
76 | {
77 | var OpenAIKey = Config.GetEntry("OpenAIKey");
78 | if (OpenAIKey is null)
79 | {
80 | new MessageBox().ShowMessageBox("Error!", "You have not set up an API key!", "error");
81 | return;
82 | }
83 |
84 | var options = new RestClientOptions("https://api.openai.com/v1/images/generations")
85 | {
86 | Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(OpenAIKey, "Bearer")
87 | };
88 |
89 | Generate.IsEnabled = false;
90 | Generate.Content = "Generating...";
91 |
92 | var client = new RestClient(options);
93 |
94 | var request = new RestRequest
95 | {
96 | Method = Method.Post,
97 | RequestFormat = DataFormat.Json
98 | };
99 |
100 | var rawModel = ((ComboBoxItem)this.Model.SelectedItem).Content.ToString();
101 | var model = rawModel == "DALL-E 2" ? "dall-e-2" : "dall-e-3";
102 | var size = Resolution.SelectedItem.ToString();
103 | var quality = rawModel.EndsWith(" HD") ? "hd" : "standard";
104 |
105 | var param = new
106 | {
107 | prompt = Prompt.Text,
108 | model = model,
109 | size = size,
110 | n = 1,
111 | quality = quality
112 | };
113 | Task.Run(async () =>
114 | {
115 | try
116 | {
117 | request.AddJsonBody(param);
118 |
119 | var jsonResponse = JObject.Parse(client.Execute(request).Content);
120 | if (jsonResponse["error"] is not null)
121 | {
122 | Dispatcher.UIThread.Invoke(() =>
123 | {
124 | new MessageBox().ShowMessageBox($"Error! ({jsonResponse["error"]["type"]})",
125 | jsonResponse["error"]["message"].ToString(), "warn");
126 | Generate.IsEnabled = true;
127 | Generate.Content = "Generate";
128 | });
129 | return;
130 | }
131 |
132 | var url = jsonResponse["data"][0]["url"].ToString();
133 |
134 | using var httpClient = new HttpClient();
135 | var response = await httpClient.GetAsync(url);
136 | response.EnsureSuccessStatusCode();
137 |
138 | Dispatcher.UIThread.Invoke(new Action (async() =>
139 | {
140 | Generate.IsEnabled = true;
141 | Generate.Content = "Generate";
142 | MainWindow.CurrentMainWindow.ImportImage("", await response.Content.ReadAsByteArrayAsync());
143 | }));
144 | }
145 | catch
146 | {
147 | Generate.IsEnabled = true;
148 | Generate.Content = "Generate";
149 | Utils.Log("Error occured within the AI Generation");
150 | }
151 | });
152 | }
153 |
154 | private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
155 | {
156 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
157 | if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && e.GetPosition(this).Y <= 20)
158 | BeginMoveDrag(e);
159 | }
160 |
161 | private void QuitAppOnClick(object? sender, RoutedEventArgs e)
162 | {
163 | Close();
164 | }
165 | }
--------------------------------------------------------------------------------
/ActionPrompt.axaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Numerics;
4 | using Avalonia;
5 | using Avalonia.Controls;
6 | using Avalonia.Input;
7 | using Avalonia.Interactivity;
8 | using Avalonia.Markup.Xaml;
9 | using SharpHook;
10 | using MouseButton = SharpHook.Native.MouseButton;
11 |
12 | namespace Autodraw;
13 |
14 |
15 | public partial class ActionPrompt : Window, INotifyPropertyChanged
16 | {
17 | public event PropertyChangedEventHandler? PropertyChanged;
18 |
19 | private string actionData;
20 | public string ActionData
21 | {
22 | get => actionData;
23 | set
24 | {
25 | if (actionData != value)
26 | {
27 | actionData = value;
28 | OnPropertyChanged(nameof(ActionData));
29 | }
30 | }
31 | }
32 |
33 | private void OnPropertyChanged(string propertyName)
34 | {
35 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
36 | }
37 |
38 | public ActionPrompt()
39 | {
40 | DataContext = this; // Still stupid.
41 |
42 | InitializeComponent();
43 | CloseAppButton.Click += CloseAppButton_Click;
44 | ActionType.SelectionChanged += ActionTypeOnSelectionChanged;
45 | }
46 |
47 | public String Speed { get; set; }
48 | public String Delay { get; set; }
49 | public String InputData { get; set; }
50 | public int Selection { get; set; }
51 | public Action Callback { get; set; }
52 | public InputAction? Action { get; set; }
53 | private object? _rawActionData;
54 |
55 | private void CloseAppButton_Click(object? sender, RoutedEventArgs e)
56 | {
57 | Close();
58 | }
59 |
60 | private bool isTracking = false;
61 | public void StartTracking()
62 | {
63 | if (isTracking)
64 | {
65 | // Stop tracking if it was already active
66 | Input.taskHook.MouseClicked -= OnGlobalMouseClick;
67 | Input.taskHook.KeyPressed -= OnGlobalKeyPress;
68 | isTracking = false;
69 | return;
70 | }
71 |
72 | // Start global input hook
73 | Input.Start();
74 | isTracking = true;
75 |
76 | switch (Selection)
77 | {
78 | // Left Click
79 | case 0:
80 | // Right Click
81 | case 1:
82 | // General Position
83 | case 2:
84 | Input.taskHook.MouseClicked += OnGlobalMouseClick;
85 | ActionData = "Awaiting Mouse Input";
86 | break;
87 | // Key Down
88 | case 4:
89 | Input.taskHook.KeyPressed += OnGlobalKeyPress;
90 | ActionData = "Awaiting Key Down...";
91 | break;
92 | // Key Up
93 | case 5:
94 | Input.taskHook.KeyReleased += OnGlobalKeyPress;
95 | ActionData = "Awaiting Key Up...";
96 | break;
97 | default:
98 | Console.WriteLine($"Selection type '{Selection}' is not supported for global tracking.");
99 | break;
100 | }
101 | }
102 |
103 | // Handle global mouse click events
104 | private void OnGlobalMouseClick(object? sender, MouseHookEventArgs e)
105 | {
106 | if (e.Data.Button is MouseButton.Button1 or MouseButton.Button2)
107 | {
108 | var x = e.Data.X;
109 | var y = e.Data.Y;
110 | Console.WriteLine($"Global Mouse Click: Button {e.Data.Button}, Position: ({x}, {y})");
111 |
112 | ActionData = $"Click at X:{x}, Y:{y}";
113 | _rawActionData = new Vector2(x, y);
114 |
115 | // Stop tracking after the action
116 | Input.taskHook.MouseClicked -= OnGlobalMouseClick;
117 | isTracking = false;
118 | }
119 | }
120 |
121 | // Handle global key press events
122 | private void OnGlobalKeyPress(object? sender, KeyboardHookEventArgs e)
123 | {
124 | var keyEvent = Selection == 4 ? "Key Down" : "Key Up";
125 |
126 | Console.WriteLine($"{keyEvent}: {e.Data.KeyCode}");
127 |
128 | ActionData = $"{keyEvent}: {e.Data.KeyCode}";
129 | _rawActionData = e.Data.KeyCode.ToString();
130 |
131 | // Stop tracking after the action
132 | if (Selection == 4)
133 | {
134 | Input.taskHook.KeyPressed -= OnGlobalKeyPress;
135 | }
136 | else if (Selection == 5)
137 | {
138 | Input.taskHook.KeyReleased -= OnGlobalKeyPress;
139 | }
140 |
141 | isTracking = false;
142 | }
143 |
144 | private void ActionTypeOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
145 | {
146 | _rawActionData = null;
147 | ActionData = "";
148 | if (Selection == 3)
149 | {
150 | TrackAction.IsVisible = false;
151 | InputBox.IsVisible = true;
152 | }
153 | else
154 | {
155 | TrackAction.IsVisible = true;
156 | InputBox.IsVisible = false;
157 | }
158 | }
159 |
160 | private void InputBox_OnTextChanged(object? sender, TextChangedEventArgs e)
161 | {
162 | if (Selection != 3) return;
163 | Console.WriteLine(InputData);
164 | _rawActionData = InputData;
165 | }
166 |
167 | public void Confirm()
168 | {
169 | if (_rawActionData is null) return;
170 | // Generate a Gift for Mothership!
171 | var _ActionType = Selection switch
172 | {
173 | 1 => InputAction.ActionType.RightClick,
174 | 2 => InputAction.ActionType.MoveTo,
175 | 3 => InputAction.ActionType.WriteString,
176 | 4 => InputAction.ActionType.KeyDown,
177 | 5 => InputAction.ActionType.KeyUp,
178 | _ => InputAction.ActionType.LeftClick
179 | };
180 | var _ActionData = _rawActionData;
181 |
182 | Action = new InputAction(_ActionType, _ActionData);
183 |
184 | Callback(); // Alien Cat: "Hello? Mothership, can you beam me up?"
185 | }
186 |
187 | public void Cancel()
188 | {
189 | Close();
190 | }
191 | }
--------------------------------------------------------------------------------
/ActionPrompt.axaml:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Auto,4,Auto
72 |
73 | 128,Auto,*
74 |
76 | Left Click
77 | Right Click
78 | Move To
79 |
80 | Write Text
81 |
82 | Key Down
83 | Key Up
84 |
85 |
86 |
87 |
91 |
92 |
93 |
95 |
96 |
97 |
98 |
99 | Auto,4,Auto
100 | *,4,*
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | *,Auto,Auto
113 |
114 |
115 |
117 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/Properties/Resources.zh.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 | text/microsoft-resx
4 |
5 |
6 | 1.3
7 |
8 |
9 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
10 |
11 |
12 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
13 |
14 |
15 | 自动画画 (测试版)
16 |
17 |
18 | 常规
19 |
20 |
21 | 过滤器
22 |
23 |
24 | 配置
25 |
26 |
27 | 图像
28 |
29 |
30 | 速度间隔
31 |
32 |
33 | 点击延迟
34 |
35 |
36 | 亮度阈值
37 |
38 |
39 | 最小值
40 |
41 |
42 | 最大值
43 |
44 |
45 | 阿尔法阈值
46 |
47 |
48 | 绘画算法
49 |
50 |
51 | FREE DRAW™ 绕过
52 |
53 |
54 | 常规过滤器
55 |
56 |
57 | 图案过滤器
58 |
59 |
60 | 过滤器
61 |
62 |
63 | 倒置
64 |
65 |
66 | 轮廓
67 |
68 |
69 | 交叉影线
70 |
71 |
72 | 对角交叉影线
73 |
74 |
75 | 水平条纹
76 |
77 |
78 | 垂直条纹
79 |
80 |
81 | 边框 (半着色器)
82 |
83 |
84 | 轮廓 (着色器)
85 |
86 |
87 | 内联 (着色器)
88 |
89 |
90 | 内联边框 (半着色器)
91 |
92 |
93 | 腐蚀 (着色器)
94 |
95 |
96 | 选择的配置
97 |
98 |
99 | 加载配置
100 |
101 |
102 | 处理图像
103 |
104 |
105 | 开始画
106 |
107 |
108 | 高
109 |
110 |
111 | 宽
112 |
113 |
114 | 这是绘制图像的速度。一个速度间隔等于100纳秒
115 |
116 |
117 | 这是自动画画在每个图像部分之间等待的延迟 (在毫秒里)
118 |
119 |
120 | 照片部分在阈值里的话,这会选择把哪个照片部分画。阈值在0-255里
121 |
122 |
123 | 设置
124 |
125 |
126 | 常规
127 |
128 |
129 | 主题
130 |
131 |
132 | 许可
133 |
134 |
135 | 市场
136 |
137 |
138 | 开发人员选项
139 |
140 |
141 | 这不完整的,几个功能就可能不行
142 |
143 |
144 | 替代鼠标控制 (坏了)
145 |
146 |
147 | 给看最近图画的弹出窗口
148 |
149 |
150 | 启用文件日志
151 |
152 |
153 | 自动跳过再扫描
154 |
155 |
156 | 暗/亮
157 |
158 |
159 | 新
160 |
161 |
162 | 打开
163 |
164 |
165 | 保存
166 |
167 |
168 | 加载主题
169 |
170 |
171 | 暗主题
172 |
173 |
174 | OpenAI开源人工智能API匙
175 |
176 |
177 | 你可能想要向开发汇报
178 |
179 |
180 | 普通话
181 |
182 |
183 | 选择语言
184 |
185 |
186 | 语言
187 |
188 |
189 | 比例尺
190 |
191 |
192 | 当前软件版本:v2.2b
193 |
194 |
195 | 语言版本: C# 10.0
196 |
197 |
198 | .NET Framework 版本:net6.0
199 |
200 |
201 | 解锁宽高比
202 |
203 |
204 | 2.2b版
205 |
206 |
207 | 边缘跟踪
208 |
209 |
210 | DFS
211 |
212 |
--------------------------------------------------------------------------------
/Autodraw.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net8.0-windows
5 | enable
6 | true
7 | app.manifest
8 | en
9 | Assets\autodraw.ico
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | en
18 |
19 | True
20 | AnyCPU
21 | 2.2.0.0
22 | 7.1.2024
23 |
24 |
25 |
26 | portable
27 |
28 |
29 |
30 | portable
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | Always
106 |
107 |
108 |
109 |
110 |
111 | Always
112 |
113 |
114 | Always
115 |
116 |
117 | Always
118 |
119 |
120 | Always
121 |
122 |
123 |
124 |
125 |
126 | MSBuild:Compile
127 |
128 |
129 | Designer
130 |
131 |
132 | Designer
133 |
134 |
135 |
136 |
137 |
138 | True
139 | True
140 | Resources.resx
141 |
142 |
143 |
144 |
145 |
146 | PublicResXFileCodeGenerator
147 | Resources.Designer.cs
148 |
149 |
150 |
151 |
152 |
153 | MSBuild:Compile
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/Preview.axaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Numerics;
6 | using System.Runtime.InteropServices;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Avalonia;
10 | using Avalonia.Controls;
11 | using Avalonia.Controls.ApplicationLifetimes;
12 | using Avalonia.Input;
13 | using Avalonia.Interactivity;
14 | using Avalonia.Media.Imaging;
15 | using Avalonia.Threading;
16 | using SharpHook;
17 | using SharpHook.Native;
18 | using SkiaSharp;
19 |
20 | namespace Autodraw;
21 |
22 | public partial class Preview : Window
23 | {
24 | public bool hasStarted = false;
25 | public SKBitmap? inputBitmap;
26 | public long lastMovement;
27 | public Bitmap? renderedBitmap;
28 | private double scale = 1;
29 | public PixelPoint primaryScreenBounds;
30 |
31 |
32 | private bool drawingStack;
33 | private List actions = new();
34 | private List stack = new();
35 |
36 | public Preview()
37 | {
38 | InitializeComponent();
39 | Position = new PixelPoint((int)Drawing.LastPos.X, (int)Drawing.LastPos.Y);
40 | // For the eventual case where someone's display settings change, especially in dual monitor cases.
41 | if (Screens.ScreenFromPoint(Position) is null)
42 | {
43 | Position = new PixelPoint(0, 0);
44 | }
45 | var currScreen = Screens.ScreenFromWindow(this);
46 | // For the eventual case where someone puts it out of bounds.
47 | Position = new PixelPoint(Math.Min(Position.X, currScreen.Bounds.Width-64), Math.Min(Position.Y, currScreen.Bounds.Height-64));
48 |
49 | scale = currScreen.Scaling;
50 | _isUpdatingPosition = true;
51 | XPos.Text = Position.X.ToString();
52 | YPos.Text = Position.Y.ToString();
53 | _isUpdatingPosition = false;
54 | Closing += OnClosing;
55 |
56 | primaryScreenBounds = Screens.Primary.Bounds.TopLeft;
57 | }
58 |
59 | private void Control_OnLoaded(object? sender, RoutedEventArgs e)
60 | {
61 | }
62 |
63 | private void OnClosing(object? sender, WindowClosingEventArgs e)
64 | {
65 | renderedBitmap.Dispose();
66 | Closing -= OnClosing;
67 | }
68 |
69 | private void Keybind(object? sender, KeyboardHookEventArgs e)
70 | {
71 | if (e.Data.KeyCode == Config.Keybind_StartDrawing)
72 | {
73 | if (inputBitmap is null && !drawingStack) return;
74 | Thread drawThread = new(async () =>
75 | {
76 | if (drawingStack)
77 | {
78 | await Drawing.DrawStack(stack,actions,Drawing.LastPos);
79 | }
80 | else
81 | {
82 | await Drawing.Draw(inputBitmap,Drawing.LastPos);
83 | }
84 | });
85 | drawThread.Start();
86 | Dispatcher.UIThread.Invoke(Close);
87 |
88 | Input.taskHook.KeyReleased -= Keybind;
89 | }
90 |
91 | if (e.Data.KeyCode == Config.Keybind_StopDrawing)
92 | {
93 | Dispatcher.UIThread.Invoke(Close);
94 | Dispatcher.UIThread.Invoke(() =>
95 | {
96 | if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
97 | {
98 | desktop.MainWindow.WindowState = WindowState.Normal;
99 | desktop.MainWindow.BringIntoView();
100 | }
101 | });
102 | Input.taskHook.KeyReleased -= Keybind;
103 | }
104 | }
105 |
106 | public void ReadyStackDraw(SKBitmap bitmap, List _stack, List _actions)
107 | {
108 | drawingStack = true;
109 | renderedBitmap?.Dispose();
110 | renderedBitmap = bitmap.ConvertToAvaloniaBitmap();
111 | PreviewImage.Source = renderedBitmap;
112 |
113 | Width = (bitmap.Width) / scale;
114 | Height = (bitmap.Height) / scale;
115 |
116 | Show();
117 |
118 | actions.Clear();
119 | actions = _actions;
120 | stack.Clear();
121 | stack = _stack;
122 |
123 | Input.taskHook.KeyReleased += Keybind;
124 | }
125 |
126 | public void ReadyDraw(SKBitmap bitmap)
127 | {
128 | drawingStack = false;
129 | renderedBitmap?.Dispose();
130 | renderedBitmap = bitmap.ConvertToAvaloniaBitmap();
131 | PreviewImage.Source = renderedBitmap;
132 |
133 | Width = (bitmap.Width) / scale;
134 | Height = (bitmap.Height) / scale;
135 |
136 | Show();
137 |
138 | inputBitmap = bitmap;
139 | Input.taskHook.KeyReleased += Keybind;
140 | }
141 |
142 | private bool isMoving = false;
143 | private PointerPoint _originalPoint;
144 | private void PreviewImage_OnPointerPressed(object? sender, PointerPressedEventArgs e)
145 | {
146 | isMoving = true;
147 | _originalPoint = e.GetCurrentPoint(this);
148 | }
149 |
150 | private void PreviewImage_OnPointerReleased(object? sender, PointerReleasedEventArgs e)
151 | {
152 | isMoving = false;
153 | Drawing.LastPos = new Vector2(Position.X,Position.Y);
154 | Config.SetEntry("Preview_LastLockedX", Drawing.LastPos.X.ToString());
155 | Config.SetEntry("Preview_LastLockedY", Drawing.LastPos.Y.ToString());
156 | }
157 |
158 | // Thanks GMas0124816 on GitHub. Previous method was too buggy, and BeginMoveDrag doesnt allow locking of X / Y axis.
159 | private void InputElement_OnPointerMoved(object? sender, PointerEventArgs e)
160 | {
161 | if (!isMoving) return;
162 | PointerPoint currentPoint = e.GetCurrentPoint(this);
163 | Position = new PixelPoint(
164 | Position.X + (XLock.IsChecked == false ? (int)(currentPoint.Position.X - _originalPoint.Position.X) : 0),
165 | Position.Y + (YLock.IsChecked == false ? (int)(currentPoint.Position.Y - _originalPoint.Position.Y) : 0)
166 | );
167 | Drawing.LastPos = new Vector2(Position.X,Position.Y);
168 | _isUpdatingPosition = true;
169 | XPos.Text = Position.X.ToString();
170 | YPos.Text = Position.Y.ToString();
171 | _isUpdatingPosition = false;
172 | }
173 | private bool _isUpdatingPosition; // Prevent recursive updates
174 | private void XPos_OnTextChanged(object? sender, TextChangedEventArgs e)
175 | {
176 | if (_isUpdatingPosition)
177 | return;
178 | _isUpdatingPosition = true;
179 |
180 | float xValue = 0f;
181 | if (!string.IsNullOrWhiteSpace(XPos.Text))
182 | {
183 | string filteredInput = new string(XPos.Text.Where(char.IsDigit).ToArray());
184 | _ = float.TryParse(filteredInput, out xValue);
185 | }
186 |
187 | Drawing.LastPos = new Vector2(xValue, Position.Y);
188 | Position = new PixelPoint((int)Drawing.LastPos.X, (int)Drawing.LastPos.Y);
189 |
190 | _isUpdatingPosition = false;
191 | }
192 |
193 | private void YPos_OnTextChanged(object? sender, TextChangedEventArgs e)
194 | {
195 | if (_isUpdatingPosition)
196 | return;
197 | _isUpdatingPosition = true;
198 |
199 | float yValue = 0f;
200 | if (!string.IsNullOrWhiteSpace(YPos.Text))
201 | {
202 | string filteredInput = new string(YPos.Text.Where(char.IsDigit).ToArray());
203 | _ = float.TryParse(filteredInput, out yValue);
204 | }
205 |
206 | Drawing.LastPos = new Vector2(Position.X, yValue);
207 | Position = new PixelPoint((int)Drawing.LastPos.X, (int)Drawing.LastPos.Y);
208 |
209 | _isUpdatingPosition = false;
210 | }
211 |
212 | private void XLock_OnIsCheckedChanged(object? sender, RoutedEventArgs e)
213 | {
214 | XPos.IsEnabled = XLock.IsChecked == false;
215 | }
216 |
217 | private void YLock_OnIsCheckedChanged(object? sender, RoutedEventArgs e)
218 | {
219 | YPos.IsEnabled = YLock.IsChecked == false;
220 | }
221 |
222 | private void Button_OnClick(object? sender, RoutedEventArgs e)
223 | {
224 | EditPanel.IsVisible = !EditPanel.IsVisible;
225 | }
226 | }
--------------------------------------------------------------------------------
/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Net;
5 | using System.Net.Http;
6 | using System.Numerics;
7 | using System.Threading.Tasks;
8 | using Avalonia;
9 | using Avalonia.Media.Imaging;
10 | using Avalonia.Platform;
11 | using Newtonsoft.Json;
12 | using Newtonsoft.Json.Linq;
13 | using SharpHook.Native;
14 | using SkiaSharp;
15 | using Vector = Avalonia.Vector;
16 |
17 | namespace Autodraw;
18 |
19 | public static class ImageExtensions
20 | {
21 | public static Bitmap ConvertToAvaloniaBitmap(this SKBitmap bitmap)
22 | {
23 | using var encodedStream = new MemoryStream();
24 | bitmap.Encode(encodedStream, SKEncodedImageFormat.Png, 100);
25 | encodedStream.Seek(0, SeekOrigin.Begin);
26 | return new Bitmap(encodedStream);
27 | }
28 | }
29 |
30 | public class Config
31 | {
32 | public static string FolderPath =
33 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutoDraw");
34 |
35 | public static KeyCode Keybind_StartDrawing = KeyCode.VcLeftShift;
36 | public static KeyCode Keybind_StopDrawing = KeyCode.VcLeftAlt;
37 | public static KeyCode Keybind_PauseDrawing = KeyCode.VcBackslash;
38 | public static KeyCode Keybind_SkipRescan = KeyCode.VcBackspace;
39 | public static KeyCode Keybind_LockPreview = KeyCode.VcLeftControl;
40 | public static KeyCode Keybind_ClearLock = KeyCode.VcBackQuote;
41 |
42 | public static Vector2 Preview_LastLockPos = new(0,0);
43 |
44 | public static string ConfigPath = Path.Combine(FolderPath, "config.json");
45 | public static string ThemesPath = Path.Combine(FolderPath, "Themes");
46 | public static string CachePath = Path.Combine(FolderPath, "Cache");
47 |
48 | public static void init()
49 | {
50 | Directory.CreateDirectory(FolderPath);
51 | if (!File.Exists(ConfigPath))
52 | {
53 | JObject obj = new();
54 | // Migrates old directory list path (from AutoDraw v1) to the new config file
55 | var OldPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
56 | "AutoDraw");
57 | if (File.Exists(Path.Combine(OldPath, "dir.txt")) &&
58 | File.ReadAllText(Path.Combine(OldPath, "dir.txt")).Length != 0)
59 | obj.Add("ConfigFolder", File.ReadAllText(Path.Combine(OldPath, "dir.txt")));
60 | var emptyJObject = JsonConvert.SerializeObject(obj);
61 | File.WriteAllText(ConfigPath, emptyJObject);
62 |
63 | }
64 |
65 | Utils.Copy(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Styles"), ThemesPath);
66 |
67 | // Check Configuration Path for Themes
68 | if (GetEntry("SavedThemesPath") is null || !Directory.Exists(GetEntry("SavedPath")))
69 | {
70 | Directory.CreateDirectory(ThemesPath);
71 | SetEntry("SavedThemesPath", ThemesPath);
72 | }
73 | else
74 | {
75 | ThemesPath = GetEntry("SavedThemesPath")!;
76 | }
77 |
78 | // Check Configuration Path for Cache
79 | if (GetEntry("SavedCachePath") is null || !Directory.Exists(GetEntry("SavedPath")))
80 | {
81 | Directory.CreateDirectory(CachePath);
82 | SetEntry("SavedCachePath", CachePath);
83 | }
84 | else
85 | {
86 | CachePath = GetEntry("SavedCachePath")!;
87 | }
88 |
89 | // Get Keybinds
90 | if (GetEntry("Keybind_StartDrawing") is not null)
91 | {
92 | Keybind_StartDrawing = (KeyCode)Enum.Parse(typeof(KeyCode), GetEntry("Keybind_StartDrawing")!);
93 | }
94 | if (GetEntry("Keybind_StopDrawing") is not null)
95 | {
96 | Keybind_StopDrawing = (KeyCode)Enum.Parse(typeof(KeyCode), GetEntry("Keybind_StopDrawing")!);
97 | }
98 | if (GetEntry("Keybind_PauseDrawing") is not null)
99 | {
100 | Keybind_PauseDrawing = (KeyCode)Enum.Parse(typeof(KeyCode), GetEntry("Keybind_PauseDrawing")!);
101 | }
102 | if (GetEntry("Keybind_SkipRescan") is not null)
103 | {
104 | Keybind_SkipRescan = (KeyCode)Enum.Parse(typeof(KeyCode), GetEntry("Keybind_SkipRescan")!);
105 | }
106 | if (GetEntry("Keybind_LockPreview") is not null)
107 | {
108 | Keybind_LockPreview = (KeyCode)Enum.Parse(typeof(KeyCode), GetEntry("Keybind_LockPreview")!);
109 | }
110 | if (GetEntry("Keybind_ClearLock") is not null)
111 | {
112 | Keybind_ClearLock = (KeyCode)Enum.Parse(typeof(KeyCode), GetEntry("Keybind_ClearLock")!);
113 | }
114 |
115 | if (GetEntry("Preview_LastLockedX") is not null && GetEntry("Preview_LastLockedY") is not null )
116 | {
117 | Preview_LastLockPos = new Vector2(int.Parse(GetEntry("Preview_LastLockedX")!),int.Parse(GetEntry("Preview_LastLockedY")!));
118 | }
119 |
120 | }
121 |
122 | public static string? GetEntry(string entry)
123 | {
124 | if (!File.Exists(ConfigPath)) return null;
125 | var json = File.ReadAllText(ConfigPath);
126 | var parse = JObject.Parse(json);
127 | return (string?)parse[entry];
128 | }
129 |
130 | public static bool SetEntry(string entry, string data)
131 | {
132 | if (!File.Exists(ConfigPath)) return false;
133 | var json = File.ReadAllText(ConfigPath);
134 | var jsonFile = JObject.Parse(json);
135 | jsonFile[entry] = data;
136 | File.WriteAllText(ConfigPath, JsonConvert.SerializeObject(jsonFile, Formatting.Indented));
137 | return true;
138 | }
139 | }
140 |
141 | public class Utils
142 | {
143 | public static string LogFolder = Path.Combine(Config.FolderPath, "logs");
144 | public static string LogsPath = Path.Combine(LogFolder, $"{DateTime.Now:dd.MM.yyyy}.txt");
145 | public static bool LoggingEnabled = Config.GetEntry("logsEnabled") == "True";
146 | public static StreamWriter? LogObject;
147 |
148 | public static void Log(object data)
149 | {
150 | string text = data.ToString() ?? "null";
151 | Console.WriteLine(text);
152 | if (!LoggingEnabled) return;
153 | if (!Directory.Exists(LogFolder)) Directory.CreateDirectory(LogFolder);
154 | if (LogObject == null) LogObject = new StreamWriter(LogsPath);
155 | LogObject.WriteLine(text);
156 | LogObject.Flush();
157 | }
158 |
159 | public static void Copy(string sourceDirectory, string targetDirectory)
160 | {
161 | var diSource = new DirectoryInfo(sourceDirectory);
162 | var diTarget = new DirectoryInfo(targetDirectory);
163 | CopyAll(diSource, diTarget);
164 | }
165 |
166 | public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
167 | {
168 | Directory.CreateDirectory(target.FullName);
169 |
170 | // Copy each file into the new directory.
171 | foreach (var fi in source.GetFiles())
172 | {
173 | Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
174 | fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
175 | }
176 |
177 | // Copy each subdirectory using recursion.
178 | foreach (var diSourceSubDir in source.GetDirectories())
179 | {
180 | var nextTargetSubDir =
181 | target.CreateSubdirectory(diSourceSubDir.Name);
182 | CopyAll(diSourceSubDir, nextTargetSubDir);
183 | }
184 | }
185 | }
186 |
187 |
188 | public class Marketplace
189 | {
190 | public static string API = "https://auto-draw.com/api/";
191 | private static readonly HttpClient client = new HttpClient();
192 |
193 | public async static Task List(string type) // 'type' can be either "theme" or "config"
194 | {
195 | HttpResponseMessage response = await client.GetAsync($"{API}list?page=1&filter={type}");
196 | if (!response.IsSuccessStatusCode) return null!;
197 | var JSONResponse = await response.Content.ReadAsStringAsync();
198 | dynamic Response = (JObject)JsonConvert.DeserializeObject(JSONResponse);
199 | return Response.items;
200 | }
201 |
202 | public async static Task Download(int id) // filename should be in the format of "{theme name}.{file extension}"
203 | {
204 | HttpResponseMessage response = await client.GetAsync($"{API}download?id={id}");
205 | if (!response.IsSuccessStatusCode) return null!;
206 | var FileResponse = await response.Content.ReadAsStringAsync();
207 | File.WriteAllText(Path.Combine(Config.ThemesPath, response.Content.Headers.ContentDisposition?.FileName.Trim('"')), FileResponse);
208 | return Path.Combine(Config.ThemesPath, response.Content.Headers.ContentDisposition?.FileName.Trim('"'));
209 | }
210 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.tlog
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.)
298 | *.vbp
299 |
300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
301 | *.dsw
302 | *.dsp
303 |
304 | # Visual Studio 6 technical files
305 | *.ncb
306 | *.aps
307 |
308 | # Visual Studio LightSwitch build output
309 | **/*.HTMLClient/GeneratedArtifacts
310 | **/*.DesktopClient/GeneratedArtifacts
311 | **/*.DesktopClient/ModelManifest.xml
312 | **/*.Server/GeneratedArtifacts
313 | **/*.Server/ModelManifest.xml
314 | _Pvt_Extensions
315 |
316 | # Paket dependency manager
317 | .paket/paket.exe
318 | paket-files/
319 |
320 | # FAKE - F# Make
321 | .fake/
322 |
323 | # CodeRush personal settings
324 | .cr/personal
325 |
326 | # Python Tools for Visual Studio (PTVS)
327 | __pycache__/
328 | *.pyc
329 |
330 | # Cake - Uncomment if you are using it
331 | # tools/**
332 | # !tools/packages.config
333 |
334 | # Tabs Studio
335 | *.tss
336 |
337 | # Telerik's JustMock configuration file
338 | *.jmconfig
339 |
340 | # BizTalk build output
341 | *.btp.cs
342 | *.btm.cs
343 | *.odx.cs
344 | *.xsd.cs
345 |
346 | # OpenCover UI analysis results
347 | OpenCover/
348 |
349 | # Azure Stream Analytics local run output
350 | ASALocalRun/
351 |
352 | # MSBuild Binary and Structured Log
353 | *.binlog
354 |
355 | # NVidia Nsight GPU debugger configuration file
356 | *.nvuser
357 |
358 | # MFractors (Xamarin productivity tool) working folder
359 | .mfractor/
360 |
361 | # Local History for Visual Studio
362 | .localhistory/
363 |
364 | # Visual Studio History (VSHistory) files
365 | .vshistory/
366 |
367 | # BeatPulse healthcheck temp database
368 | healthchecksdb
369 |
370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
371 | MigrationBackup/
372 |
373 | # Ionide (cross platform F# VS Code tools) working folder
374 | .ionide/
375 |
376 | # Fody - auto-generated XML schema
377 | FodyWeavers.xsd
378 |
379 | # VS Code files for those working on multiple tools
380 | .vscode/*
381 | !.vscode/settings.json
382 | !.vscode/tasks.json
383 | !.vscode/launch.json
384 | !.vscode/extensions.json
385 | *.code-workspace
386 |
387 | # Local History for Visual Studio Code
388 | .history/
389 |
390 | # Windows Installer files from build outputs
391 | *.cab
392 | *.msi
393 | *.msix
394 | *.msm
395 | *.msp
396 |
397 | # JetBrains Rider
398 | *.sln.iml
399 |
400 | WindowsShortcutCreator/*.sln
401 | WindowsShortcutCreator/*.vcxproj
402 | WindowsShortcutCreator/*.vcxproj.filters
403 |
404 | build/
405 | .gradle/
406 | .project
407 | .classpath
408 | .settings/
409 |
410 | offline-repository/
411 | buildSrc/libs
412 | .vscode/
413 |
414 | bin/
415 | obj/
416 | Debug/
417 | Release/
418 | **/Debug/
419 | **/Release/
420 | .vs/
421 | Builds/
422 | .idea/.idea.Autodraw/.idea/avalonia.xml
423 |
--------------------------------------------------------------------------------
/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Text.RegularExpressions;
5 | using System.Threading.Tasks;
6 | using Avalonia;
7 | using Avalonia.Controls;
8 | using Avalonia.Controls.ApplicationLifetimes;
9 | using Avalonia.Data;
10 | using Avalonia.Markup.Xaml;
11 | using Avalonia.Media;
12 | using Avalonia.Styling;
13 | using Avalonia.Threading;
14 | using static Avalonia.Application;
15 |
16 | namespace Autodraw;
17 |
18 | public class App : Application
19 | {
20 | public static string CurrentTheme = Config.GetEntry("theme") ?? "avares://Autodraw/Styles/dark.axaml";
21 |
22 | public static bool SavedIsDark =
23 | Config.GetEntry("isDarkTheme") == null || bool.Parse(Config.GetEntry("isDarkTheme") ?? "true");
24 |
25 | private static int themeIndex = 5;
26 |
27 | private static void ThemeFailed()
28 | {
29 | Console.WriteLine("Theme Failed.");
30 | // This function is for if it failed to load a theme, will revert to previous, or will decide to use darkmode if all else fails.
31 | try
32 | {
33 | // Tries loading back to our original loaded theme.
34 | var Resource = (IStyle)AvaloniaXamlLoader.Load(
35 | new Uri(CurrentTheme)
36 | );
37 | Current.RequestedThemeVariant = SavedIsDark ? ThemeVariant.Dark : ThemeVariant.Light;
38 | if (Current.Styles.Count > themeIndex)
39 | Current.Styles.Remove(Current.Styles[themeIndex]);
40 | Current.Styles.Add(Resource);
41 | Config.SetEntry("theme", CurrentTheme);
42 | Config.SetEntry("isDarkTheme", SavedIsDark.ToString());
43 | }
44 | catch
45 | {
46 | Console.WriteLine("Theme Failed 2");
47 | // Tries loading our default theme. Purpose of this is if a theme somehow vanished.
48 | var Resource = (IStyle)AvaloniaXamlLoader.Load(
49 | new Uri("avares://Autodraw/Styles/dark.axaml")
50 | );
51 | Current.RequestedThemeVariant = ThemeVariant.Dark;
52 | if (Current.Styles.Count > themeIndex)
53 | Current.Styles.Remove(Current.Styles[themeIndex]);
54 | Current.Styles.Add(Resource);
55 | CurrentTheme = "avares://Autodraw/Styles/dark.axaml";
56 | SavedIsDark = true;
57 | Config.SetEntry("theme", "avares://Autodraw/Styles/dark.axaml");
58 | Config.SetEntry("isDarkTheme", true.ToString());
59 | }
60 | }
61 |
62 | public static string? LoadThemeFromString(string themeText, bool isDark = true, string themeUri = "")
63 | {
64 | var OutputMessage = "";
65 | try
66 | {
67 | // Tries loading as runtime uncompiled.
68 | var TextInput = themeText;
69 | TextInput = Regex.Replace(TextInput, @"file:./", AppDomain.CurrentDomain.BaseDirectory);
70 | if (themeUri != "")
71 | {
72 | TextInput = Regex.Replace(TextInput, @"style:./",
73 | Regex.Replace(themeUri, @"\\(?:.(?!\\))+$", "") + "\\");
74 | }
75 | else
76 | {
77 | OutputMessage += "- You have not saved this theme, so it won't parse style:./.\n\n";
78 | }
79 | Match isCodeDark = Regex.Match(TextInput, @"");
80 | Match isCodeLight = Regex.Match(TextInput, @"");
81 | if (isCodeDark.Success && isCodeLight.Success) throw new Exception("My brother in christ, you cannot have both DarkTheme and LightTheme.");
82 | if (isCodeDark.Success) isDark = true;
83 | if (isCodeLight.Success) isDark = false;
84 |
85 | // This is a bugfix, no joke. Somehow fixes any sort of avalonia parsing errors.
86 | // Please push to github.com/Avalonia, its revolutionary.
87 | Console.WriteLine(typeof(Binding));
88 |
89 | var Resource = AvaloniaRuntimeXamlLoader.Parse(
90 | TextInput
91 | );
92 | Current.RequestedThemeVariant = isDark ? ThemeVariant.Dark : ThemeVariant.Light;
93 | Current.Styles.Remove(Current.Styles[themeIndex]);
94 | Current.Styles.Add(Resource);
95 | if (themeUri != "")
96 | {
97 | CurrentTheme = themeUri;
98 | SavedIsDark = isDark;
99 | Config.SetEntry("theme", themeUri);
100 | Config.SetEntry("isDarkTheme", isDark.ToString());
101 | }
102 | }
103 | catch (Exception ex)
104 | {
105 | ThemeFailed();
106 | OutputMessage += "# Theme has failed to load successfully due to an error.\n" + ex.Message;
107 | OutputMessage += "\n Full message:\n" + ex;
108 | Console.WriteLine(OutputMessage);
109 | return OutputMessage;
110 | }
111 |
112 | OutputMessage += "# Theme loaded successfully!\n";
113 | return OutputMessage;
114 | }
115 |
116 | public static string? LoadTheme(string themeUri, bool isDark = true)
117 | {
118 | // Behold, terrible bruteforce-ey code! Performance be damned!
119 | try
120 | {
121 | Console.WriteLine("Loading Compiled Code");
122 | // Tries loading as Compiled.
123 | var Resource = (IStyle)AvaloniaXamlLoader.Load(
124 | new Uri(themeUri)
125 | );
126 | Current.RequestedThemeVariant = isDark ? ThemeVariant.Dark : ThemeVariant.Light;
127 | Current.Styles.Remove(Current.Styles[themeIndex]);
128 | Current.Styles.Add(Resource);
129 | CurrentTheme = themeUri;
130 | SavedIsDark = isDark;
131 | Config.SetEntry("theme", themeUri);
132 | Config.SetEntry("isDarkTheme", isDark.ToString());
133 | }
134 | catch
135 | {
136 | try
137 | {
138 | // Tries loading as runtime uncompiled.
139 | Console.WriteLine("Loading Uncompiled Runtime Code");
140 | var TextInput = File.ReadAllText(themeUri);
141 | return LoadThemeFromString(TextInput, isDark, themeUri);
142 | }
143 | catch (Exception ex)
144 | {
145 | Console.WriteLine(ex);
146 | Console.WriteLine(ex.Message);
147 | Console.WriteLine("Exception Caught.");
148 | ThemeFailed();
149 | return ex.Message;
150 | }
151 | }
152 |
153 | return null;
154 | }
155 |
156 | public override void Initialize()
157 | {
158 | TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
159 | AvaloniaXamlLoader.Load(this);
160 | LoadTheme(CurrentTheme, SavedIsDark);
161 |
162 |
163 |
164 | RegisterGlobalExceptionHandlers();
165 | }
166 |
167 | private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
168 | {
169 | Utils.Log(e.Exception.ToString());
170 | Utils.Log(e.Exception.Message);
171 | }
172 |
173 | public override void OnFrameworkInitializationCompleted()
174 | {
175 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
176 | desktop.MainWindow = new MainWindow();
177 |
178 | base.OnFrameworkInitializationCompleted();
179 | }
180 |
181 | private void RegisterGlobalExceptionHandlers()
182 | {
183 | // Handle UI thread exceptions
184 | Dispatcher.UIThread.UnhandledException += (sender, e) =>
185 | {
186 | e.Handled = true;
187 | HandleException(e.Exception);
188 | };
189 |
190 | // Handle non-UI thread unhandled exceptions
191 | AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
192 | {
193 | if (e.ExceptionObject is Exception ex)
194 | {
195 | HandleException(ex);
196 | }
197 | };
198 |
199 | // Handle unobserved task exceptions
200 | TaskScheduler.UnobservedTaskException += (sender, e) =>
201 | {
202 | e.SetObserved();
203 | HandleException(e.Exception);
204 | };
205 | }
206 |
207 | private async void HandleException(Exception exception)
208 | {
209 | await ShowExceptionDialog(exception);
210 | }
211 |
212 | private async Task ShowExceptionDialog(Exception exception)
213 | {
214 | // Extremely barebones UI, no theming, no functions, for exceptions.
215 |
216 | Window messageBox = null;
217 | messageBox = new Window
218 | {
219 | Title = "An Error Occurred",
220 | Width = 400,
221 | Height = 200,
222 | Content = new StackPanel
223 | {
224 | Children =
225 | {
226 | new TextBlock { Text = "An unexpected error has occurred:", Margin = new Thickness(10) },
227 | new TextBlock { Text = exception.Message, Margin = new Thickness(10), TextWrapping = TextWrapping.Wrap },
228 | new Button
229 | {
230 | Content = "OK",
231 | HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
232 | Margin = new Thickness(10),
233 | Command = ReactiveUI.ReactiveCommand.Create(() => messageBox.Close())
234 | }
235 | }
236 | }
237 | };
238 |
239 | var mainWindow = (Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
240 | ?.MainWindow;
241 | if (mainWindow != null)
242 | await messageBox.ShowDialog(mainWindow);
243 | }
244 |
245 | }
--------------------------------------------------------------------------------
/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | A list of assembly names to exclude from the default action of
13 | "embed all Copy Local references", delimited with line breaks
14 |
15 |
16 |
17 |
18 |
19 | A list of assembly names to include from the default action of
20 | "embed all Copy Local references", delimited with line breaks.
21 |
22 |
23 |
24 |
25 |
26 | A list of runtime assembly names to exclude from the default
27 | action of "embed all Copy Local references", delimited with line breaks
28 |
29 |
30 |
31 |
32 |
33 | A list of runtime assembly names to include from the default
34 | action of "embed all Copy Local references", delimited with line breaks.
35 |
36 |
37 |
38 |
39 |
40 | A list of unmanaged 32 bit assembly names to include, delimited
41 | with line breaks.
42 |
43 |
44 |
45 |
46 |
47 | A list of unmanaged 64 bit assembly names to include, delimited
48 | with line breaks.
49 |
50 |
51 |
52 |
53 |
54 | The order of preloaded assemblies, delimited with line breaks.
55 |
56 |
57 |
58 |
59 |
60 |
61 | This will copy embedded files to disk before loading them into memory.
62 | This is helpful for some scenarios that expected an assembly to be loaded from a
63 | physical file.
64 |
65 |
66 |
67 |
68 |
69 | Controls if .pdbs for reference assemblies are also embedded.
70 |
71 |
72 |
73 |
74 |
75 | Controls if runtime assemblies are also embedded.
76 |
77 |
78 |
79 |
80 | Controls whether the runtime assemblies are embedded with their full
81 | path or only with their assembly name.
82 |
83 |
84 |
85 |
86 |
87 | Embedded assemblies are compressed by default, and uncompressed when
88 | they are loaded. You can turn compression off with this option.
89 |
90 |
91 |
92 |
93 |
94 | As part of Costura, embedded assemblies are no longer included as part
95 | of the build. This cleanup can be turned off.
96 |
97 |
98 |
99 |
100 |
101 | Costura by default will load as part of the module initialization.
102 | This flag disables that behavior. Make sure you call CosturaUtility.Initialize()
103 | somewhere in your code.
104 |
105 |
106 |
107 |
108 |
109 | Costura will by default use assemblies with a name like
110 | 'resources.dll' as a satellite resource and prepend the output path. This flag
111 | disables that behavior.
112 |
113 |
114 |
115 |
116 |
117 | A list of assembly names to exclude from the default action of "embed
118 | all Copy Local references", delimited with |
119 |
120 |
121 |
122 |
123 |
124 | A list of assembly names to include from the default action of "embed
125 | all Copy Local references", delimited with |.
126 |
127 |
128 |
129 |
130 |
131 | A list of runtime assembly names to exclude from the default action of
132 | "embed all Copy Local references", delimited with |
133 |
134 |
135 |
136 |
137 |
138 | A list of runtime assembly names to include from the default action of
139 | "embed all Copy Local references", delimited with |.
140 |
141 |
142 |
143 |
144 |
145 | A list of unmanaged 32 bit assembly names to include, delimited with
146 | |.
147 |
148 |
149 |
150 |
151 |
152 | A list of unmanaged 64 bit assembly names to include, delimited with
153 | |.
154 |
155 |
156 |
157 |
158 |
159 | The order of preloaded assemblies, delimited with |.
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | 'true' to run assembly verification (PEVerify) on the target assembly after all
169 | weavers have been executed.
170 |
171 |
172 |
173 |
174 |
175 | A comma-separated list of error codes that can be safely ignored in assembly
176 | verification.
177 |
178 |
179 |
180 |
181 |
182 | 'false' to turn off automatic generation of the XML Schema file.
183 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/Styles/Overrides/CheckBox.axaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 | Item 1
9 | Item 2
10 |
11 |
15 | Item 1
16 | Item 2
17 |
18 |
19 |
20 |
21 |
22 | Error
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 0,0,0,4
32 | 15
33 | 7
34 |
35 | 12,5,0,7
36 | 11,5,32,6
37 | 32
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
68 |
76 |
84 |
91 |
92 |
99 |
100 |
111 |
112 |
120 |
126 |
129 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
145 |
146 |
147 |
151 |
152 |
153 |
156 |
157 |
158 |
162 |
165 |
168 |
169 |
170 |
171 |
176 |
179 |
182 |
185 |
186 |
187 |
188 |
193 |
196 |
199 |
202 |
203 |
204 |
205 |
208 |
209 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | text/microsoft-resx
112 |
113 |
114 | 2.0
115 |
116 |
117 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
118 | PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
123 | PublicKeyToken=b77a5c561934e089
124 |
125 |
126 |
127 | AutoDraw Open Beta
128 |
129 |
130 | v2.2b
131 |
132 |
133 | General
134 |
135 |
136 | Filters
137 |
138 |
139 | Configs
140 |
141 |
142 | Images
143 |
144 |
145 | Interval
146 |
147 |
148 | Click Delay
149 |
150 |
151 | Luminance Threshold
152 |
153 |
154 | Minimum
155 |
156 |
157 | Maximum
158 |
159 |
160 | Alpha Threshold
161 |
162 |
163 | Draw Algorithm
164 |
165 |
166 | Free Draw Bypass
167 |
168 |
169 | General Filters
170 |
171 |
172 | Pattern Filters
173 |
174 |
175 | Experimental Filters
176 |
177 |
178 | Invert
179 |
180 |
181 | Outline
182 |
183 |
184 | Crosshatch
185 |
186 |
187 | Diagonal Crosshatch
188 |
189 |
190 | Horizontal Stripes
191 |
192 |
193 | Vertical Stripes
194 |
195 |
196 | Border (Semi-Shader)
197 |
198 |
199 | Outline (Shader)
200 |
201 |
202 | Inline (Shader)
203 |
204 |
205 | Inline Border (Semi-Shader)
206 |
207 |
208 | Erosion (Shader)
209 |
210 |
211 | Selected Config
212 |
213 |
214 | Load Config
215 |
216 |
217 | Process Image
218 |
219 |
220 | Begin Drawing
221 |
222 |
223 | Height
224 |
225 |
226 | Width
227 |
228 |
229 | This is the speed at which the image will be drawn. One interval equates to 100 nanoseconds
230 |
231 |
232 | This is the delay (in ms) which AutoDraw will wait in-between each image chunk.
233 |
234 |
235 | This decides what portions of the image will be drawn, based on if it falls within the threshold. Threshold is based on 0-255
236 |
237 |
238 | Settings
239 |
240 |
241 | General
242 |
243 |
244 | Themes
245 |
246 |
247 | Licenses
248 |
249 |
250 | Marketplace
251 |
252 |
253 | Dev. Options
254 |
255 |
256 | This is incomplete, some functions here may not work.
257 |
258 |
259 | Alternate Mouse Control (Buggy)
260 |
261 |
262 | Show Finished Drawing Popup
263 |
264 |
265 | Enable Log files
266 |
267 |
268 | Auto-Skip Rescan
269 |
270 |
271 | Dark/Light
272 |
273 |
274 | New
275 |
276 |
277 | Open
278 |
279 |
280 | Save
281 |
282 |
283 | Load Theme
284 |
285 |
286 | Dark theme?
287 |
288 |
289 | OpenAI API Key
290 |
291 |
292 | You may want to report this to the developers.
293 |
294 |
295 | Scale
296 |
297 |
298 | English
299 |
300 |
301 | Select your language
302 |
303 |
304 | Language
305 |
306 |
307 | Current Version: v2.2.0
308 |
309 |
310 | Language Version: C# 10.0
311 |
312 |
313 | .NET Framework Version: net6.0
314 |
315 |
316 | Unlock Aspect Ratio
317 |
318 |
319 | Edge-Following
320 |
321 |
322 | DFS
323 |
324 |
--------------------------------------------------------------------------------
/ImageProcessing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using Avalonia.Controls;
6 | using Avalonia.Media;
7 | using SkiaSharp;
8 |
9 | namespace Autodraw;
10 |
11 | public static class ImageProcessing
12 | {
13 | private static long Process_MemPressure;
14 |
15 | private static readonly Pattern patternCrosshatch = new()
16 | {
17 | Pat =
18 | "0 0 0 1 0 0 0\n0 0 0 1 0 0 0\n0 0 0 1 0 0 0\n1 1 1 1 1 1 1\n0 0 0 1 0 0 0\n0 0 0 1 0 0 0\n0 0 0 1 0 0 0\n",
19 | Width = 7, Height = 7
20 | };
21 |
22 | private static readonly Pattern patternDiagCross = new()
23 | {
24 | Pat =
25 | "1 0 0 0 0 0 1\n0 1 0 0 0 1 0\n0 0 1 0 1 0 0\n0 0 0 1 0 0 0\n0 0 1 0 1 0 0\n0 1 0 0 0 1 0\n1 0 0 0 0 0 1\n",
26 | Width = 6,
27 | Height = 6
28 | };
29 |
30 | private static readonly List listCrosshatch = ReadPattern(patternCrosshatch.Pat);
31 | private static readonly List listDiagCross = ReadPattern(patternDiagCross.Pat);
32 |
33 | public static Filters _currentFilters = new()
34 | { Invert = false, MaxThreshold = 127, AlphaThreshold = 200 };
35 |
36 | public static List ReadPattern(string pat)
37 | {
38 | var lines = pat.Split('\n');
39 |
40 | List positions = new();
41 |
42 | for (var i = 0; i < lines.Length; i++)
43 | {
44 | var numbers = lines[i].Split(' ');
45 |
46 | for (var j = 0; j < numbers.Length; j++)
47 | if (numbers[j] == "1")
48 | positions.Add(new[] { j, i });
49 | }
50 |
51 | return positions;
52 | }
53 |
54 | // TODO: Rewrite allat to be image based patterns 🙄
55 |
56 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
57 | private static uint MakePixel(byte red, byte green, byte blue, byte alpha)
58 | {
59 | return (uint)((alpha << 24) | (blue << 16) | (green << 8) | red);
60 | }
61 |
62 | private static unsafe void GetPixel(uint* input, out byte red, out byte green, out byte blue, out byte alpha)
63 | {
64 | red = (byte)(*input & 0xFF);
65 | green = (byte)((*input >> 8) & 0xFF);
66 | blue = (byte)((*input >> 16) & 0xFF);
67 | alpha = (byte)((*input >> 24) & 0xFF);
68 | }
69 |
70 | public static byte AdjustThresh(byte rByte, byte gByte, byte bByte, byte aByte, bool doinvert, float maxthresh, float minthresh, byte athresh)
71 | {
72 | float lumen = (rByte + gByte + bByte) / 3;
73 | byte localThresh = (byte)(lumen > maxthresh || lumen < minthresh || aByte < athresh ? 255 : 0);
74 | localThresh = doinvert == false ? localThresh : (byte)(255 - localThresh);
75 | return localThresh;
76 | }
77 |
78 | private static byte GetThreshold(float luminosity, byte alphaByte, Filters filterSettings)
79 | {
80 | byte threshByte =
81 | (byte)(luminosity > filterSettings.MaxThreshold || luminosity < filterSettings.MinThreshold || alphaByte < filterSettings.AlphaThreshold
82 | ? 255
83 | : 0); // Thresholds Filter
84 | return filterSettings.Invert == false ? threshByte : (byte)(255 - threshByte); // Invert
85 | }
86 |
87 | private static unsafe byte GetOutlineAlpha(int y, int x, uint* basePtr, int width, int height, byte threshByte, Filters filterSettings)
88 | {
89 | byte returnByte = 255;
90 | var doOutline = false;
91 | foreach (var i in Enumerable.Range(0, 4))
92 | {
93 | uint* pixelAddress;
94 | var outOfBounds = i switch
95 | {
96 | 0 => y - 1 < 0,
97 | 1 => x - 1 < 0,
98 | 2 => x + 1 >= width,
99 | 3 => y + 1 >= height,
100 | _ => false
101 | };
102 | if (outOfBounds)
103 | {
104 | doOutline = true;
105 | }
106 | else
107 | {
108 | pixelAddress = i switch
109 | {
110 | 0 => basePtr + width * (y - 1) + x,
111 | 1 => basePtr + width * y + (x - 1),
112 | 2 => basePtr + width * y + (x + 1),
113 | 3 => basePtr + width * (y + 1) + x,
114 | _ => throw new ArgumentException($"Invalid value {i}")
115 | };
116 | GetPixel(pixelAddress, out var rByte, out var gByte, out var bByte, out var aByte);
117 | byte localThresh = AdjustThresh(rByte, gByte, bByte, aByte, filterSettings.Invert, filterSettings.MaxThreshold, filterSettings.MinThreshold, filterSettings.AlphaThreshold);
118 | if (localThresh == 255) doOutline = true;
119 | }
120 | }
121 | if (doOutline) returnByte = 0;
122 | return returnByte;
123 | }
124 |
125 | private static byte GetPatternAlpha(int y, int x, byte returnByte, Filters filterSettings)
126 | {
127 | if (returnByte != 0 && (filterSettings.Crosshatch || filterSettings.DiagCrosshatch || filterSettings.HorizontalLines > 0 || filterSettings.VerticalLines > 0))
128 | {
129 | if (filterSettings.DiagCrosshatch) // Diag Crosshatch
130 | foreach (var patPoint in listDiagCross)
131 | if (x % patternDiagCross.Width == patPoint[0] && y % patternDiagCross.Height == patPoint[1])
132 | returnByte = 0;
133 | if (filterSettings.Crosshatch) // Crosshatch
134 | foreach (var patPoint in listCrosshatch)
135 | if (x % patternCrosshatch.Width == patPoint[0] && y % patternCrosshatch.Height == patPoint[1])
136 | returnByte = 0;
137 | }
138 | return returnByte;
139 | }
140 |
141 | private static bool IsAnyPatternSet(this Filters filters)
142 | {
143 | return filters.Crosshatch || filters.DiagCrosshatch;
144 | }
145 |
146 | private static unsafe SKBitmap GenerateBorder(int thickness, int width, int height)
147 | {
148 | // Could move this to SkiaSharp SKPaint stuff but nahhh.
149 |
150 | SKBitmap borderBitmap = new SKBitmap(width, height);
151 |
152 | var srcPtr = (byte*)borderBitmap.GetPixels().ToPointer();
153 |
154 | for (var y = 0; y < height; y++)
155 | for (var x = 0; x < width; x++)
156 | {
157 | byte returnByte = 255;
158 | if (x < thickness || x >= width - thickness ||
159 | y < thickness || y >= height - thickness) returnByte = 0;
160 |
161 | *srcPtr++ = returnByte;
162 | *srcPtr++ = returnByte;
163 | *srcPtr++ = returnByte;
164 | *srcPtr++ = 255;
165 | }
166 |
167 | return borderBitmap;
168 | }
169 |
170 | private static unsafe SKBitmap GeneratePattern(decimal horizontal, decimal vertical, int width, int height)
171 | {
172 | SKBitmap patternBitmap = new SKBitmap(width, height);
173 |
174 | var srcPtr = (byte*)patternBitmap.GetPixels().ToPointer();
175 |
176 | for (var y = 0; y < height; y++)
177 | for (var x = 0; x < width; x++)
178 | {
179 | byte returnByte = 255;
180 | if (horizontal > 0)
181 | if (y % horizontal == 0)
182 | returnByte = 0;
183 | if (vertical > 0)
184 | if (x % vertical == 0)
185 | returnByte = 0;
186 |
187 | *srcPtr++ = returnByte;
188 | *srcPtr++ = returnByte;
189 | *srcPtr++ = returnByte;
190 | *srcPtr++ = 255;
191 | }
192 |
193 | return patternBitmap;
194 | }
195 |
196 | public static unsafe SKBitmap Process(SKBitmap sourceBitmap, Filters filterSettings)
197 | {
198 | if(Process_MemPressure>0) GC.RemoveMemoryPressure(Process_MemPressure);
199 | Process_MemPressure = sourceBitmap.BytesPerPixel * sourceBitmap.Width * sourceBitmap.Height;
200 | var height = sourceBitmap.Height;
201 | var width = sourceBitmap.Width;
202 |
203 | SKBitmap outputBitmap = new(width, height);
204 | var basePtr = (uint*)sourceBitmap.GetPixels().ToPointer();
205 | var returnPtr = (uint*)outputBitmap.GetPixels().ToPointer();
206 | var isPatSet = filterSettings.IsAnyPatternSet();
207 | var outline = filterSettings.Outline;
208 |
209 | for (var y = 0; y < height; y++)
210 | for (var x = 0; x < width; x++)
211 | {
212 | var srcPtr = basePtr + width * y + x;
213 | GetPixel(srcPtr, out var redByte, out var greenByte, out var blueByte, out var alphaByte);
214 | var luminosity = (redByte + greenByte + blueByte) / 3;
215 | byte threshByte = GetThreshold(luminosity, alphaByte, filterSettings);
216 |
217 | if (threshByte == 255)
218 | {
219 | *returnPtr++ = 0xffffffff;
220 | continue;
221 | }
222 |
223 | byte returnByte = 255;
224 | if (outline)
225 | {
226 | returnByte = GetOutlineAlpha(y, x, basePtr, width, height, threshByte, filterSettings);
227 | }
228 |
229 | if (isPatSet)
230 | {
231 | returnByte = GetPatternAlpha(y, x, returnByte, filterSettings);
232 | }
233 | else if (!outline)
234 | {
235 | returnByte = threshByte;
236 | }
237 | *returnPtr++ = MakePixel(returnByte, returnByte, returnByte, 255);
238 | }
239 |
240 | var invert = new float[20] {
241 | -1f, 0f, 0f, 0f, 1f,
242 | 0f, -1f, 0f, 0f, 1f,
243 | 0f, 0f, -1f, 0f, 1f,
244 | 0f, 0f, 0f, 1f, 0f
245 | };
246 |
247 | SKImage InvertImage(SKImage image)
248 | {
249 | return image.ApplyImageFilter(SKImageFilter.CreateColorFilter(SKColorFilter.CreateColorMatrix(invert)), new SKRectI(0, 0, width, height), new SKRectI(0, 0, width, height), out _, out SKPoint _);
250 | }
251 |
252 | var OutImage = SKImage.FromBitmap(outputBitmap);
253 | if (filterSettings.ErosionAdvanced > 0)
254 | {
255 | var OutImageAnti = OutImage.ApplyImageFilter(
256 | SKImageFilter.CreateDilate(filterSettings.ErosionAdvanced, filterSettings.ErosionAdvanced),
257 | new SKRectI(0, 0, width, height),
258 | new SKRectI(filterSettings.ErosionAdvanced, filterSettings.ErosionAdvanced, width - filterSettings.ErosionAdvanced, height - filterSettings.ErosionAdvanced),
259 | out _,
260 | out SKPoint _
261 | );
262 | OutImage = OutImageAnti;
263 | }
264 |
265 | if (filterSettings.InlineAdvanced > 0 || filterSettings.InlineBorderAdvanced > 0 || filterSettings.OutlineAdvanced > 0 ||
266 | filterSettings.HorizontalLines > 0 || filterSettings.VerticalLines > 0 || filterSettings.BorderAdvanced > 0)
267 | {
268 | SKImageInfo imageInfo = new SKImageInfo(OutImage.Width, OutImage.Height);
269 | using SKSurface primarySurface = SKSurface.Create(imageInfo);
270 |
271 | SKPaint lightenPaint = new SKPaint();
272 | lightenPaint.BlendMode = SKBlendMode.Lighten;
273 | SKPaint darkenPaint = new SKPaint();
274 | darkenPaint.BlendMode = SKBlendMode.Darken;
275 |
276 | bool hasAppliedAnother = false;
277 |
278 | if (filterSettings.InlineAdvanced > 0)
279 | {
280 | using SKPaint inlinePaint = new SKPaint();
281 | inlinePaint.ImageFilter = SKImageFilter.CreateDilate(filterSettings.InlineAdvanced, filterSettings.InlineAdvanced);
282 | primarySurface.Canvas.DrawImage(OutImage, 0, 0, inlinePaint);
283 | primarySurface.Canvas.DrawImage(InvertImage(primarySurface.Snapshot()), 0, 0);
284 | primarySurface.Canvas.DrawImage(OutImage, 0, 0, lightenPaint);
285 | hasAppliedAnother = true;
286 | }
287 |
288 | if (filterSettings.OutlineAdvanced > 0)
289 | {
290 | using SKPaint outlinePaint = new SKPaint();
291 | outlinePaint.ImageFilter = SKImageFilter.CreateDilate(filterSettings.OutlineAdvanced, filterSettings.OutlineAdvanced);
292 | using SKSurface secondarySurface = SKSurface.Create(imageInfo);
293 | secondarySurface.Canvas.DrawImage(InvertImage(OutImage), 0, 0, outlinePaint);
294 | secondarySurface.Canvas.DrawImage(InvertImage(secondarySurface.Snapshot()),0,0);
295 | secondarySurface.Canvas.DrawImage(InvertImage(OutImage),0,0,lightenPaint);
296 | primarySurface.Canvas.DrawImage(secondarySurface.Snapshot(),0,0,darkenPaint);
297 | hasAppliedAnother = true;
298 | }
299 |
300 | if (filterSettings.InlineBorderAdvanced > 0)
301 | {
302 | using SKSurface secondarySurface = SKSurface.Create(imageInfo);
303 | secondarySurface.Canvas.DrawImage(OutImage, 0, 0);
304 | secondarySurface.Canvas.DrawImage(SKImage.FromBitmap(GenerateBorder(filterSettings.InlineBorderAdvanced, width, height)),0,0,lightenPaint);
305 | primarySurface.Canvas.DrawImage(secondarySurface.Snapshot(),0,0,darkenPaint);
306 | hasAppliedAnother = true;
307 | }
308 |
309 | if (filterSettings.HorizontalLines > 0 || filterSettings.VerticalLines > 0)
310 | {
311 | filterSettings.HorizontalLines = Math.Min(4096, filterSettings.HorizontalLines);
312 | filterSettings.VerticalLines = Math.Min(4096, filterSettings.VerticalLines);
313 |
314 | SKBitmap patternBitmap = GeneratePattern(filterSettings.HorizontalLines, filterSettings.VerticalLines, width, height);
315 | using SKSurface secondarySurface = SKSurface.Create(imageInfo);
316 |
317 | secondarySurface.Canvas.DrawBitmap(patternBitmap, 0, 0);
318 | secondarySurface.Canvas.DrawImage(OutImage, 0, 0, lightenPaint);
319 |
320 | primarySurface.Canvas.DrawImage(secondarySurface.Snapshot(),0,0,darkenPaint);
321 | hasAppliedAnother = true;
322 | }
323 |
324 | if (filterSettings.BorderAdvanced > 0)
325 | {
326 | if(!hasAppliedAnother)
327 | {
328 | primarySurface.Canvas.DrawImage(OutImage,0,0);
329 | }
330 | primarySurface.Canvas.DrawImage(SKImage.FromBitmap(GenerateBorder(filterSettings.BorderAdvanced, width, height)),0,0,darkenPaint);
331 | }
332 |
333 | SKImage MergeImage = primarySurface.Snapshot();
334 | OutImage = MergeImage;
335 | }
336 |
337 | outputBitmap = SKBitmap.FromImage(OutImage);
338 |
339 | GC.AddMemoryPressure(Process_MemPressure);
340 | return outputBitmap;
341 | }
342 |
343 | public static unsafe Color GetColor(SKBitmap sourceBitmap)
344 | {
345 | // Assume its Bgra8888
346 | var width = sourceBitmap.Width;
347 | var height = sourceBitmap.Height;
348 | // I've always found it so weird how specifying this as a variable, instead of just chucking it into a for loop, improves performance so much.
349 | // Guess the width/height variable could change in the middle of the function technically, so it has to re-read. Silly tho.
350 | var srcPtr = (byte*)sourceBitmap.GetPixels().ToPointer();
351 |
352 | for (var row = 0; row < height; row++)
353 | for (var col = 0; col < width; col++)
354 | {
355 | var b = *srcPtr++;
356 | var g = *srcPtr++;
357 | var r = *srcPtr++;
358 | var a = *srcPtr++;
359 |
360 | if (a == 255) // Look for only opaque pixels, transparent pixels sometimes are black or pink?! No clue why pink, but it happens often.
361 | {
362 | return Color.FromArgb(a, r, g, b);
363 | }
364 | }
365 | return Color.FromArgb(0, 0, 0, 0);
366 | }
367 |
368 | public static unsafe SKBitmap NormalizeColor(this SKBitmap SourceBitmap)
369 | {
370 | var srcColor = SourceBitmap.ColorType;
371 | var srcAlpha = SourceBitmap.AlphaType;
372 |
373 | if (srcColor == SKColorType.Bgra8888) return SourceBitmap;
374 | // Ensure we don't need to normalize it.
375 |
376 | SKBitmap OutputBitmap = new(SourceBitmap.Width, SourceBitmap.Height);
377 |
378 | var srcPtr = (byte*)SourceBitmap.GetPixels().ToPointer();
379 | var dstPtr = (byte*)OutputBitmap.GetPixels().ToPointer();
380 |
381 | var width = OutputBitmap.Width;
382 | var height = OutputBitmap.Height;
383 |
384 | var outColor = OutputBitmap.ColorType;
385 |
386 | for (var row = 0; row < height; row++)
387 | for (var col = 0; col < width; col++)
388 | if (srcColor == SKColorType.Gray8 || srcColor == SKColorType.Alpha8)
389 | {
390 | var b = *srcPtr++;
391 | *dstPtr++ = b;
392 | *dstPtr++ = b;
393 | *dstPtr++ = b;
394 | *dstPtr++ = 255;
395 | }
396 | else if (srcColor == SKColorType.Rgba8888)
397 | {
398 | var r = *srcPtr++;
399 | var g = *srcPtr++;
400 | var b = *srcPtr++;
401 | var a = *srcPtr++;
402 | *dstPtr++ = b;
403 | *dstPtr++ = g;
404 | *dstPtr++ = r;
405 | *dstPtr++ = a;
406 | }
407 | else if (srcColor == SKColorType.Argb4444)
408 | {
409 | var r = *srcPtr++;
410 | var g = *srcPtr++;
411 | var b = *srcPtr++;
412 | var a = *srcPtr++;
413 | *dstPtr++ = (byte)(b * 2);
414 | *dstPtr++ = (byte)(g * 2);
415 | *dstPtr++ = (byte)(r * 2);
416 | *dstPtr++ = (byte)(a * 2);
417 | }
418 |
419 | SourceBitmap.Dispose();
420 | SourceBitmap = OutputBitmap;
421 |
422 | return SourceBitmap;
423 | }
424 |
425 | public class Filters
426 | {
427 | public byte AlphaThreshold = 127;
428 | public byte MaxThreshold = 127;
429 | public byte MinThreshold = 0;
430 |
431 | public bool Crosshatch = false;
432 | public bool DiagCrosshatch = false;
433 | public decimal HorizontalLines = 0;
434 | public decimal VerticalLines = 0;
435 |
436 | public bool Invert = false;
437 | public bool Outline = false;
438 |
439 | public int BorderAdvanced = 5;
440 | public int OutlineAdvanced = 0;
441 | public int InlineAdvanced = 0;
442 | public int InlineBorderAdvanced = 0;
443 | public int ErosionAdvanced = 0;
444 | }
445 |
446 | public class Pattern
447 | {
448 | public int Height = 2;
449 | public string Pat = "0 0\n0 0";
450 | public int Width = 2;
451 | }
452 | }
--------------------------------------------------------------------------------
/Assets/LICENSES.txt:
--------------------------------------------------------------------------------
1 | ## AutoDraw
2 |
3 | MIT License
4 |
5 | Copyright (c) 2023 AlexDalas and Siydge
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
25 | ## Avalonia
26 |
27 | The MIT License (MIT)
28 |
29 | Copyright (c) .NET Foundation and Contributors
30 | All Rights Reserved
31 |
32 | Permission is hereby granted, free of charge, to any person obtaining a copy
33 | of this software and associated documentation files (the "Software"), to deal
34 | in the Software without restriction, including without limitation the rights
35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36 | copies of the Software, and to permit persons to whom the Software is
37 | furnished to do so, subject to the following conditions:
38 |
39 | The above copyright notice and this permission notice shall be included in all
40 | copies or substantial portions of the Software.
41 |
42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
48 | SOFTWARE.
49 |
50 | ## AvaloniaEdit
51 |
52 | MIT License
53 |
54 | Copyright (c) 2017 Eli Arbel
55 |
56 | Permission is hereby granted, free of charge, to any person obtaining a copy
57 | of this software and associated documentation files (the "Software"), to deal
58 | in the Software without restriction, including without limitation the rights
59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
60 | copies of the Software, and to permit persons to whom the Software is
61 | furnished to do so, subject to the following conditions:
62 |
63 | The above copyright notice and this permission notice shall be included in all
64 | copies or substantial portions of the Software.
65 |
66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
72 | SOFTWARE.
73 |
74 | ## SharpHook
75 |
76 | MIT License
77 |
78 | Copyright (c) 2021 Anatoliy Pylypchuk
79 |
80 | Permission is hereby granted, free of charge, to any person obtaining a copy
81 | of this software and associated documentation files (the "Software"), to deal
82 | in the Software without restriction, including without limitation the rights
83 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
84 | copies of the Software, and to permit persons to whom the Software is
85 | furnished to do so, subject to the following conditions:
86 |
87 | The above copyright notice and this permission notice shall be included in all
88 | copies or substantial portions of the Software.
89 |
90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
96 | SOFTWARE.
97 |
98 | ## AsyncImageLoader.Avalonia
99 |
100 | MIT License
101 |
102 | Copyright (c) 2021 SKProCH
103 |
104 | Permission is hereby granted, free of charge, to any person obtaining a copy
105 | of this software and associated documentation files (the "Software"), to deal
106 | in the Software without restriction, including without limitation the rights
107 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
108 | copies of the Software, and to permit persons to whom the Software is
109 | furnished to do so, subject to the following conditions:
110 |
111 | The above copyright notice and this permission notice shall be included in all
112 | copies or substantial portions of the Software.
113 |
114 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
115 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
116 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
117 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
118 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
119 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
120 | SOFTWARE.
121 |
122 | ## Newtonsoft.Json
123 |
124 | The MIT License (MIT)
125 |
126 | Copyright (c) 2007 James Newton-King
127 |
128 | Permission is hereby granted, free of charge, to any person obtaining a copy of
129 | this software and associated documentation files (the "Software"), to deal in
130 | the Software without restriction, including without limitation the rights to
131 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
132 | the Software, and to permit persons to whom the Software is furnished to do so,
133 | subject to the following conditions:
134 |
135 | The above copyright notice and this permission notice shall be included in all
136 | copies or substantial portions of the Software.
137 |
138 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
139 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
140 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
141 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
142 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
143 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
144 |
145 | ## SimWinMouse
146 |
147 | MIT License
148 |
149 | Copyright (c) 2017 David Rieman
150 |
151 | Permission is hereby granted, free of charge, to any person obtaining a copy
152 | of this software and associated documentation files (the "Software"), to deal
153 | in the Software without restriction, including without limitation the rights
154 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
155 | copies of the Software, and to permit persons to whom the Software is
156 | furnished to do so, subject to the following conditions:
157 |
158 | The above copyright notice and this permission notice shall be included in all
159 | copies or substantial portions of the Software.
160 |
161 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
162 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
163 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
164 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
165 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
166 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
167 | SOFTWARE.
168 |
169 | ## TextMateSharp.Grammars
170 |
171 | MIT License
172 |
173 | Copyright (c) 2021 Daniel Peñalba
174 |
175 | Permission is hereby granted, free of charge, to any person obtaining a copy
176 | of this software and associated documentation files (the "Software"), to deal
177 | in the Software without restriction, including without limitation the rights
178 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
179 | copies of the Software, and to permit persons to whom the Software is
180 | furnished to do so, subject to the following conditions:
181 |
182 | The above copyright notice and this permission notice shall be included in all
183 | copies or substantial portions of the Software.
184 |
185 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
186 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
188 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
189 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
190 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
191 | SOFTWARE.
192 |
193 | ## Markdown.Avalonia
194 |
195 | MIT License
196 |
197 | Copyright (c) 2010 Bevan Arps, 2020 Whistyun
198 |
199 | Permission is hereby granted, free of charge, to any person obtaining a copy
200 | of this software and associated documentation files (the "Software"), to deal
201 | in the Software without restriction, including without limitation the rights
202 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
203 | copies of the Software, and to permit persons to whom the Software is
204 | furnished to do so, subject to the following conditions:
205 |
206 | The above copyright notice and this permission notice shall be included in all
207 | copies or substantial portions of the Software.
208 |
209 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
210 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
211 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
212 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
213 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
214 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
215 | SOFTWARE.
216 |
217 |
218 | ## RestSharp
219 |
220 | Copyright (c) .NET Foundation and Contributors
221 | All Rights Reserved
222 |
223 | Apache License
224 | Version 2.0, January 2004
225 | http://www.apache.org/licenses/
226 |
227 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
228 |
229 | 1. Definitions.
230 |
231 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
232 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
233 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
234 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
235 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
236 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
237 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
238 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
239 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
240 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
241 |
242 | 2. Grant of Copyright License.
243 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
244 |
245 | 3. Grant of Patent License.
246 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
247 |
248 | 4. Redistribution.
249 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
250 | You must give any other recipients of the Work or Derivative Works a copy of this License; and
251 | You must cause any modified files to carry prominent notices stating that You changed the files; and
252 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
253 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
254 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
255 |
256 | 5. Submission of Contributions.
257 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
258 |
259 | 6. Trademarks.
260 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
261 |
262 | 7. Disclaimer of Warranty.
263 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
264 |
265 | 8. Limitation of Liability.
266 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
267 |
268 | 9. Accepting Warranty or Additional Liability.
269 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
270 |
271 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------