├── .idea └── .idea.Autodraw │ └── .idea │ ├── .name │ ├── encodings.xml │ ├── vcs.xml │ ├── indexLayout.xml │ ├── .gitignore │ └── avalonia.xml ├── autodraw-compat.bat ├── .gitattributes ├── Assets ├── gear.png ├── reload.png ├── remove.png ├── save.png ├── autodraw.ico ├── open-doc.png ├── Dark │ ├── close.png │ ├── lock.png │ └── unlock.png ├── Light │ ├── close.png │ ├── lock.png │ └── unlock.png ├── Message │ ├── pog.png │ ├── error.png │ ├── info.png │ ├── nerd.png │ ├── warn.png │ └── privacy-warn.png ├── ProggyTiny.ttf ├── add-image.png ├── open-folder.png ├── Sounds │ ├── Music.mp3 │ └── alert.wav ├── test-pattern.jpg ├── AIGenerationTab.md ├── autodraw.md └── LICENSES.txt ├── .avalonia-build-tasks └── id ├── Styles ├── Anime │ ├── character.png │ └── anime.axaml ├── Landscape │ ├── landscape.jpg │ └── landscape.axaml ├── dark.axaml ├── DefaultTheme.txt ├── light.axaml ├── universal.axaml └── Overrides │ └── CheckBox.axaml ├── global.json ├── FodyWeavers.xml ├── DrawDataDisplay.axaml.cs ├── .editorconfig ├── app.manifest ├── DrawDataDisplay.axaml ├── LICENSE ├── MessageBox.axaml.cs ├── App.axaml ├── Autodraw.sln ├── Program.cs ├── README.md ├── DevTest.axaml ├── DevTest.axaml.cs ├── Preview.axaml ├── MessageBox.axaml ├── OpenAIPrompt.axaml ├── Input.cs ├── OpenAIPrompt.axaml.cs ├── ActionPrompt.axaml.cs ├── ActionPrompt.axaml ├── Properties ├── Resources.zh.resx └── Resources.resx ├── Autodraw.csproj ├── Preview.axaml.cs ├── Utils.cs ├── .gitignore ├── App.axaml.cs ├── FodyWeavers.xsd └── ImageProcessing.cs /.idea/.idea.Autodraw/.idea/.name: -------------------------------------------------------------------------------- 1 | Autodraw -------------------------------------------------------------------------------- /autodraw-compat.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | start autodraw.exe compat -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Assets/gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/gear.png -------------------------------------------------------------------------------- /Assets/reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/reload.png -------------------------------------------------------------------------------- /Assets/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/remove.png -------------------------------------------------------------------------------- /Assets/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/save.png -------------------------------------------------------------------------------- /Assets/autodraw.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/autodraw.ico -------------------------------------------------------------------------------- /Assets/open-doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/open-doc.png -------------------------------------------------------------------------------- /Assets/Dark/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Dark/close.png -------------------------------------------------------------------------------- /Assets/Dark/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Dark/lock.png -------------------------------------------------------------------------------- /Assets/Dark/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Dark/unlock.png -------------------------------------------------------------------------------- /Assets/Light/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Light/close.png -------------------------------------------------------------------------------- /Assets/Light/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Light/lock.png -------------------------------------------------------------------------------- /Assets/Message/pog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Message/pog.png -------------------------------------------------------------------------------- /Assets/ProggyTiny.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/ProggyTiny.ttf -------------------------------------------------------------------------------- /Assets/add-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/add-image.png -------------------------------------------------------------------------------- /Assets/open-folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/open-folder.png -------------------------------------------------------------------------------- /.avalonia-build-tasks/id: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/.avalonia-build-tasks/id -------------------------------------------------------------------------------- /Assets/Light/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Light/unlock.png -------------------------------------------------------------------------------- /Assets/Message/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Message/error.png -------------------------------------------------------------------------------- /Assets/Message/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Message/info.png -------------------------------------------------------------------------------- /Assets/Message/nerd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Message/nerd.png -------------------------------------------------------------------------------- /Assets/Message/warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Message/warn.png -------------------------------------------------------------------------------- /Assets/Sounds/Music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Sounds/Music.mp3 -------------------------------------------------------------------------------- /Assets/Sounds/alert.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Sounds/alert.wav -------------------------------------------------------------------------------- /Assets/test-pattern.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/test-pattern.jpg -------------------------------------------------------------------------------- /Styles/Anime/character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Styles/Anime/character.png -------------------------------------------------------------------------------- /Assets/Message/privacy-warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Assets/Message/privacy-warn.png -------------------------------------------------------------------------------- /Styles/Landscape/landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/auto-draw/autodraw/HEAD/Styles/Landscape/landscape.jpg -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.0", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": true 6 | } 7 | } -------------------------------------------------------------------------------- /FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/.idea.Autodraw/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.Autodraw/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DrawDataDisplay.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace Autodraw; 4 | 5 | public partial class DrawDataDisplay : Window 6 | { 7 | public DrawDataDisplay() 8 | { 9 | InitializeComponent(); 10 | } 11 | } -------------------------------------------------------------------------------- /.idea/.idea.Autodraw/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.Autodraw/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /projectSettingsUpdater.xml 6 | /.idea.Autodraw.iml 7 | /modules.xml 8 | /contentModel.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS8602: Dereference of a possibly null reference. 4 | dotnet_diagnostic.CS8602.severity = none 5 | 6 | # CS8600: Converting null literal or possible null value to non-nullable type. 7 | dotnet_diagnostic.CS8600.severity = none 8 | 9 | # CS8601: Possible null reference assignment. 10 | dotnet_diagnostic.CS8601.severity = suggestion 11 | 12 | # CS8604: Possible null reference argument. 13 | dotnet_diagnostic.CS8604.severity = suggestion 14 | -------------------------------------------------------------------------------- /Assets/AIGenerationTab.md: -------------------------------------------------------------------------------- 1 | ## Welcome to AutoDraws AI Tab ## 2 | 3 | ### You'll need an OpenAI API Key for this! ### 4 | 5 | You can fetch your API Key from [OpenAI](https://openai.com/api-keys/), we use OpenAI's DallE Integration. 6 | 7 | ### Current Prices [as of writing](https://openai.com/pricing) are as follow: 8 | 9 | 10 | Dall-E 3 Standard 11 | - 1024x1024 = 0.040 / Image 12 | - 1024×1792, 1792×1024 = 0.080 / Image 13 | 14 | Dall-E 3 High Definition 15 | - 1024x1024 = 0.080 / Image 16 | - 1024×1792, 1792×1024 = 0.120 / Image 17 | 18 | Dall-E 2: 19 | - 1024x1024 = 0.020 / Image 20 | - 512x512 = 0.018 / Image 21 | - 256x256 = 0.016 / Image -------------------------------------------------------------------------------- /Assets/autodraw.md: -------------------------------------------------------------------------------- 1 | ## AutoDraw, developed by AlexDalas and Siydge. 2 | ### "Easily trace any picture with your cursor and impress your friends in drawing games." 3 | #### This software is licensed under the [MIT License](https://github.com/auto-draw/autodraw/blob/main/LICENSE). 4 | 5 | 6 | "This project first started by Siydge as a passion project from v3rmillion. It first begun as a fork of an AutoIt script, fixed to work on more Roblox games, before I shortly moved to instead fixing a .NET WinForm app by Rainzhao2000 to work better for Roblox and other games as a whole. During this development, AlexDalas, also known as gz9 joined me with the development, and later on down the line we fully re-wrote all of the code to move from .NET WinForms to Avalonia, to aim to one day support all Desktop Systems." - Siydge 7 | 8 | 9 | **Discord:** [https://discord.gg/jXU97W8zaK](https://discord.gg/jXU97W8zaK) 10 | 11 | AutoDraw icons are provided by Icons8 with love <3 -------------------------------------------------------------------------------- /Styles/dark.axaml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 11 | 12 | 16 | 17 | 20 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /DrawDataDisplay.axaml: -------------------------------------------------------------------------------- 1 | 19 | 21 | -------------------------------------------------------------------------------- /.idea/.idea.Autodraw/.idea/avalonia.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 AlexDalas and Siydge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MessageBox.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Avalonia.Controls; 4 | using Avalonia.Input; 5 | using Avalonia.Interactivity; 6 | using Avalonia.Media.Imaging; 7 | using Avalonia.Platform; 8 | 9 | namespace Autodraw; 10 | 11 | public partial class MessageBox : Window 12 | { 13 | public MessageBox() 14 | { 15 | InitializeComponent(); 16 | CloseAppButton.Click += CloseAppButton_Click; 17 | } 18 | 19 | public void ShowMessageBox(string title, string description, string icon = "info", string sound = "alert.wav") 20 | { 21 | Bitmap bmp = new(AssetLoader.Open(new Uri($"avares://Autodraw/Assets/Message/{icon}.png"))); 22 | MessageIcon.Source = bmp; 23 | MessageTitle.Text = title; 24 | MessageContent.Text = description; 25 | 26 | Show(); 27 | } 28 | 29 | private void OnPointerPressed(object? sender, PointerPressedEventArgs e) 30 | { 31 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; 32 | if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && e.GetPosition(this).Y <= 20) 33 | BeginMoveDrag(e); 34 | } 35 | 36 | private void CloseAppButton_Click(object? sender, RoutedEventArgs e) 37 | { 38 | Close(); 39 | } 40 | } -------------------------------------------------------------------------------- /App.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | avares://Autodraw/Assets#ProggyTiny 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Autodraw.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33815.320 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autodraw", "Autodraw.csproj", "{3BD9DC45-60DC-4871-A5BA-74B4946EF029}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B634ACB7-11EF-4A95-9B59-8978BEC133DD}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {3BD9DC45-60DC-4871-A5BA-74B4946EF029}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {3BD9DC45-60DC-4871-A5BA-74B4946EF029}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {3BD9DC45-60DC-4871-A5BA-74B4946EF029}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {3BD9DC45-60DC-4871-A5BA-74B4946EF029}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {5F5178CE-5538-44F1-A9DD-EF3BB0A7CEB3} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | 4 | namespace Autodraw; 5 | 6 | internal class Program 7 | { 8 | private static bool compatability = false; 9 | 10 | // Initialization code. Don't use any Avalonia, third-party APIs or any 11 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 12 | // yet and stuff might break. 13 | [STAThread] 14 | public static void Main(string[] args) 15 | { 16 | try 17 | { 18 | compatability = args?.Length > 0 && args[0].Equals("compat", StringComparison.OrdinalIgnoreCase); 19 | BuildAvaloniaApp() 20 | .StartWithClassicDesktopLifetime(args); 21 | } 22 | catch (Exception e) 23 | { 24 | Utils.Log(e.ToString()); 25 | Utils.Log(e.Message); 26 | throw; 27 | } 28 | } 29 | 30 | // Avalonia configuration, don't remove; also used by visual designer. 31 | public static AppBuilder BuildAvaloniaApp() 32 | { 33 | var app = AppBuilder.Configure() 34 | .UsePlatformDetect(); 35 | 36 | if (compatability) 37 | { 38 | app = app.With(new Win32PlatformOptions 39 | { 40 | RenderingMode = [Win32RenderingMode.Wgl,Win32RenderingMode.Vulkan,Win32RenderingMode.Software], 41 | CompositionMode = [Win32CompositionMode.WinUIComposition,Win32CompositionMode.DirectComposition,Win32CompositionMode.RedirectionSurface] 42 | }); 43 | } 44 | 45 | app.WithInterFont() 46 | .LogToTrace(); 47 | return app; 48 | } 49 | } -------------------------------------------------------------------------------- /Styles/Landscape/landscape.axaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 14 | 15 | 18 | 21 | 22 | 27 | 28 | 31 | 32 | 35 | 36 | 39 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /Styles/Anime/anime.axaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 14 | 15 | 18 | 19 | 22 | 23 | 28 | 29 | 32 | 33 | 36 | 37 | 40 | 41 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /Styles/DefaultTheme.txt: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 24 | 25 | 26 | 30 | 31 | 32 | 36 | 37 | 38 | 41 | 42 | 43 | 46 | 47 | 48 | 51 | 54 | 55 | 56 | 59 | -------------------------------------------------------------------------------- /Styles/light.axaml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 11 | 14 | 15 | 18 | 19 | 24 | 25 | 29 | 33 | 34 | 37 | 40 | 41 | 44 | 47 | 48 | 51 | 52 | 56 | 59 | 60 | 63 | 64 | 67 | 70 | 71 | 74 | 77 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | AutoDraw 3 |
4 | 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 6 | 7 | chat on Discord 9 | 10 | 11 | 12 | 13 |

14 | 15 | ##### Table of Contents 16 | 17 | - [Introduction](#Introduction) 18 | - [Installation](#Installation) 19 | - [Version 2](#Version2) 20 | - [Version 1](#Version1) 21 | - [Support](#Support) 22 | - [FAQ](#FAQ) 23 | - [Operating Systems](#OperatingSystems) 24 | - [Documentation](#Documentation) 25 | 26 |

27 | 28 |

Introduction

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 |

Installation

32 | 33 | Follow [this guide](https://github.com/auto-draw/autodraw/releases) to install AutoDrawer. 34 | 35 |
36 |

Version 2

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 |

46 | 47 | Install v1.4 [through the V1 repository](https://github.com/AlexDalas/autodrawer/). 48 | 49 |
50 |

Support

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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 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 | 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 --------------------------------------------------------------------------------