Skip to content

Commit ab71b22

Browse files
committed
Add project files.
1 parent 05fa81c commit ab71b22

26 files changed

+2357
-0
lines changed

App.config

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
<configSections>
4+
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
5+
<section name="DecolorizerWindow.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
6+
</sectionGroup>
7+
</configSections>
8+
<userSettings>
9+
<DecolorizerWindow.Properties.Settings>
10+
<setting name="Width" serializeAs="String">
11+
<value>0</value>
12+
</setting>
13+
<setting name="Height" serializeAs="String">
14+
<value>0</value>
15+
</setting>
16+
</DecolorizerWindow.Properties.Settings>
17+
</userSettings>
18+
</configuration>

Core.cs

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data.Common;
4+
using System.Diagnostics;
5+
using System.Drawing;
6+
using System.Drawing.Imaging;
7+
using System.Linq;
8+
using System.Runtime.InteropServices;
9+
using System.Security.Policy;
10+
using System.Text;
11+
using System.Threading.Tasks;
12+
using System.Windows;
13+
14+
namespace DecolorizerWindow
15+
{
16+
public static partial class Core
17+
{
18+
public static MainForm MainForm { get; set; }
19+
20+
public static ushort Width
21+
{
22+
get => Properties.Settings.Default.Width;
23+
set => Properties.Settings.Default.Width = value;
24+
}
25+
public static ushort Height
26+
{
27+
get => Properties.Settings.Default.Height;
28+
set => Properties.Settings.Default.Height = value;
29+
}
30+
31+
private static bool updating = true;
32+
private static bool stopping = false;
33+
private static bool finished = false;
34+
private static bool toggleCooledDown = true;
35+
private static readonly ushort bytesPerPixel = (ushort)(Screen.PrimaryScreen.BitsPerPixel / 8);
36+
private static readonly byte lastByteSkip = (byte)(bytesPerPixel == 3 ? 1 : 2);
37+
38+
public static void ShowError(string message)
39+
{
40+
MessageBox.Show(message, "Обесцветыватель", MessageBoxButtons.OK, MessageBoxIcon.Error);
41+
}
42+
43+
public static void MakeFormInvisible(Form form)
44+
{
45+
// Сделать окно невидимым для "BitBlt()" (функции копирования пикселей)
46+
SetWindowDisplayAffinity(form.Handle, WDA_EXCLUDEFROMCAPTURE);
47+
}
48+
49+
public static async void StartUpdating()
50+
{
51+
await Task.Delay(1); // Чтобы компилятор думал, что этот метод по-настоящему асинхронен
52+
53+
finished = false;
54+
updating = true;
55+
56+
Size size = new(Width, Height);
57+
MainForm.Size = size;
58+
MainForm.displayPicture.Size = size;
59+
using Graphics g = MainForm.displayPicture.CreateGraphics();
60+
61+
IntPtr hScreen = GetDC((IntPtr)null);
62+
IntPtr srcHDC = CreateCompatibleDC(hScreen);
63+
IntPtr destHDC = g.GetHdc();
64+
65+
BITMAPINFO bmi = default;
66+
unsafe
67+
{
68+
bmi.bmiHeader.biSize = (uint)sizeof(BITMAPINFOHEADER);
69+
}
70+
bmi.bmiHeader.biWidth = Width;
71+
bmi.bmiHeader.biHeight = -Height; // top-down
72+
bmi.bmiHeader.biPlanes = 1;
73+
bmi.bmiHeader.biBitCount = (ushort)Screen.PrimaryScreen.BitsPerPixel;
74+
bmi.bmiHeader.biCompression = BI.RGB;
75+
int bufSize = ((((Width * bmi.bmiHeader.biBitCount) + 31) & ~31) >> 3) * Height;
76+
77+
IntPtr pBits;
78+
IntPtr hBitmap = CreateDIBSection(srcHDC, ref bmi, DIB_RGB_COLORS, out pBits, (IntPtr)null, 0);
79+
if (hBitmap == IntPtr.Zero)
80+
{
81+
ShowError("Ошибка при вызове GDI32.CreateDIBSection().");
82+
_ = ReleaseDC((IntPtr)null, hScreen);
83+
DeleteDC(srcHDC);
84+
DeleteDC(destHDC);
85+
86+
return;
87+
}
88+
89+
int halfWidth = Width / 2;
90+
int halfHeight = Height / 2;
91+
uint windowFlag = SWP_NOSIZE | SWP_NOACTIVATE;
92+
93+
SelectObject(srcHDC, hBitmap);
94+
95+
// Нельзя сделать "Visible = false" при первом запуске, поэтому я
96+
// проверяю "Tag" - если он не пуст, значит это первый запуск
97+
if (!MainForm.Visible || MainForm.Tag is not null)
98+
{
99+
MainForm.Tag = null;
100+
101+
// Показать окно когда уже начнётся сам цикл копирования
102+
_ = Task.Run(() =>
103+
{
104+
Thread.Sleep(30);
105+
MainForm.Invoke(() =>
106+
{
107+
// Чтобы не было белого кадра при запуске, приходится также
108+
// менять прозрачность окна, т.к. почему-то при выставлении размера окна
109+
// оно становится видимым не смотря на "TransparencyKey"
110+
MainForm.Opacity = 1;
111+
MainForm.Show();
112+
});
113+
});
114+
}
115+
116+
while (updating)
117+
{
118+
Point mousePos = default;
119+
GetCursorPos(ref mousePos);
120+
int x = mousePos.X - halfWidth;
121+
int y = mousePos.Y - halfHeight;
122+
123+
BitBlt(srcHDC, 0, 0, Width, Height, hScreen, x, y, SRCCOPY); // Копировать пиксели с экрана
124+
TransformAndCopy(pBits, bufSize, srcHDC, destHDC, hBitmap); // Сделать чёрно-белым и скопировать пиксели в окно
125+
126+
SetWindowPos(MainForm.Handle, HWND_TOPMOST, x, y, 0, 0, windowFlag);
127+
128+
Application.DoEvents(); // Предотвратить "Приложение не отвечает"
129+
}
130+
131+
_ = ReleaseDC((IntPtr)null, hScreen);
132+
DeleteDC(srcHDC);
133+
DeleteDC(destHDC);
134+
g.ReleaseHdc();
135+
DeleteObject(hBitmap);
136+
137+
finished = true;
138+
}
139+
private static unsafe void TransformAndCopy(IntPtr srcBufPtr, int bufSize, IntPtr srcHDC, IntPtr destHDC, IntPtr hBitmap)
140+
{
141+
byte* srcBuf = (byte*)srcBufPtr.ToPointer();
142+
for (int i = 0; i < bufSize; i += bytesPerPixel)
143+
{
144+
// "srcBuf++" - вернуть значение, а потом увеличить его на 1.
145+
byte* b = srcBuf++;
146+
byte* g = srcBuf++;
147+
byte* r = srcBuf;
148+
srcBuf += lastByteSkip;
149+
150+
byte gray = (byte)(*r * 0.3 + *g * 0.59 + *b * 0.11);
151+
*b = *g = *r = gray;
152+
}
153+
154+
BitBlt(destHDC, 0, 0, Width, Height, srcHDC, 0, 0, SRCCOPY);
155+
}
156+
157+
public static async Task<bool> StopUpdating(bool showError = true)
158+
{
159+
if (!updating)
160+
return true;
161+
162+
updating = false;
163+
164+
bool res = await Task.Run(() =>
165+
{
166+
if (finished)
167+
return true;
168+
169+
for (int i = 0; i < 50; i++) // ждать 5 секунд
170+
{
171+
Thread.Sleep(100);
172+
if (finished)
173+
return true;
174+
}
175+
176+
return false;
177+
});
178+
179+
if (!res)
180+
{
181+
if (showError)
182+
ShowError("Не получилось остановить процесс обновления изображения.\n" +
183+
"Приложение будет принудительно остановлено.");
184+
185+
Environment.Exit(1);
186+
}
187+
188+
return true;
189+
}
190+
191+
public static async Task ToggleProgramState()
192+
{
193+
if (!toggleCooledDown)
194+
return;
195+
196+
toggleCooledDown = false;
197+
_ = Task.Run(() =>
198+
{
199+
Thread.Sleep(200); // Не чаще, чем 5 раз в секунду
200+
toggleCooledDown = true;
201+
});
202+
203+
if (updating)
204+
{
205+
if (!stopping)
206+
{
207+
stopping = true;
208+
209+
MainForm.Hide();
210+
MainForm.Opacity = 0;
211+
await StopUpdating();
212+
213+
stopping = false;
214+
}
215+
}
216+
else
217+
{
218+
StartUpdating();
219+
}
220+
}
221+
}
222+
}

DecolorizerWindow.csproj

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<TargetFramework>net8.0-windows7</TargetFramework>
6+
<Nullable>disable</Nullable>
7+
<UseWindowsForms>true</UseWindowsForms>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
10+
<ApplicationIcon>mainIcon.ico</ApplicationIcon>
11+
<Title>Decolorizer</Title>
12+
<Authors>VladiStep</Authors>
13+
<Copyright>2024</Copyright>
14+
</PropertyGroup>
15+
16+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
17+
<DebugType>embedded</DebugType>
18+
</PropertyGroup>
19+
20+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
21+
<DebugType>embedded</DebugType>
22+
</PropertyGroup>
23+
24+
<ItemGroup>
25+
<Compile Remove="Resources\**" />
26+
<EmbeddedResource Remove="Resources\**" />
27+
<None Remove="Resources\**" />
28+
</ItemGroup>
29+
30+
<ItemGroup>
31+
<Content Include="mainIcon.ico" />
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<Compile Update="Properties\Resources.Designer.cs">
36+
<DesignTime>True</DesignTime>
37+
<AutoGen>True</AutoGen>
38+
<DependentUpon>Resources.resx</DependentUpon>
39+
</Compile>
40+
<Compile Update="Properties\Settings.Designer.cs">
41+
<DesignTimeSharedInput>True</DesignTimeSharedInput>
42+
<AutoGen>True</AutoGen>
43+
<DependentUpon>Settings.settings</DependentUpon>
44+
</Compile>
45+
</ItemGroup>
46+
47+
<ItemGroup>
48+
<EmbeddedResource Update="Properties\Resources.resx">
49+
<Generator>ResXFileCodeGenerator</Generator>
50+
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
51+
</EmbeddedResource>
52+
</ItemGroup>
53+
54+
<ItemGroup>
55+
<None Update="Properties\Settings.settings">
56+
<Generator>SettingsSingleFileGenerator</Generator>
57+
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
58+
</None>
59+
</ItemGroup>
60+
</Project>

DecolorizerWindow.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.8.34330.188
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DecolorizerWindow", "DecolorizerWindow.csproj", "{94F34B67-271D-4B56-922C-475F71F59D43}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{94F34B67-271D-4B56-922C-475F71F59D43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{94F34B67-271D-4B56-922C-475F71F59D43}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{94F34B67-271D-4B56-922C-475F71F59D43}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{94F34B67-271D-4B56-922C-475F71F59D43}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {AA7824E0-DD49-4B98-ABFE-47D3FDCF5BC7}
24+
EndGlobalSection
25+
EndGlobal

Icons/exitIcon.png

505 Bytes
Loading

Icons/mainIcon.ico

16.6 KB
Binary file not shown.

Icons/settingsIcon.png

2.15 KB
Loading

Icons/toggleDisplayIcon source.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://www.flaticon.com/free-icons/switch

Icons/toggleDisplayIcon.png

2.21 KB
Loading

0 commit comments

Comments
 (0)