diff --git a/Comic_Compressor.sln b/Comic_Compressor.sln
new file mode 100644
index 0000000..8bcfa2c
--- /dev/null
+++ b/Comic_Compressor.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35219.272
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comic_Compressor", "Comic_Compressor\Comic_Compressor.csproj", "{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|x64.ActiveCfg = Debug|x64
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|x64.Build.0 = Debug|x64
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x64.ActiveCfg = Release|x64
+ {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FA1DD3CC-0702-404F-9D7F-40EB2736F8AD}
+ EndGlobalSection
+EndGlobal
diff --git a/Comic_Compressor/AvifCompressor.cs b/Comic_Compressor/AvifCompressor.cs
new file mode 100644
index 0000000..68c4cff
--- /dev/null
+++ b/Comic_Compressor/AvifCompressor.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Comic_Compressor
+{
+ internal class AvifCompressor
+ {
+ internal static void CompressImages(string sourceImagePath, string targetStoragePath)
+ {
+ //尚未实现
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Comic_Compressor/Comic_Compressor.csproj b/Comic_Compressor/Comic_Compressor.csproj
new file mode 100644
index 0000000..b3aedb2
--- /dev/null
+++ b/Comic_Compressor/Comic_Compressor.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net8.0-windows
+ enable
+ enable
+ AnyCPU;x64
+ True
+
+
+
+
+
+
+
+
diff --git a/Comic_Compressor/Program.cs b/Comic_Compressor/Program.cs
new file mode 100644
index 0000000..bd039c4
--- /dev/null
+++ b/Comic_Compressor/Program.cs
@@ -0,0 +1,67 @@
+using System.Text;
+using System.Windows.Forms;
+namespace Comic_Compressor
+{
+ internal class Program
+ {
+ [STAThread]
+ static void Main()
+ {
+ Console.OutputEncoding = Encoding.UTF8;
+
+ Console.WriteLine("请选择源图像所在位置:");
+ string? sourceImagePath = GetFolderPath();
+ if (string.IsNullOrEmpty(sourceImagePath))
+ {
+ Console.WriteLine("未选择文件夹,程序将退出。");
+ return;
+ }
+
+ Console.WriteLine("请输入目标存储位置:");
+ string? targetStoragePath = GetFolderPath();
+ if (string.IsNullOrEmpty(targetStoragePath))
+ {
+ Console.WriteLine("未选择文件夹,程序将退出。");
+ return;
+ }
+
+ Console.WriteLine("请选择压缩模式:0 - 压缩成webp,1 - 压缩成avif");
+ string? modeInput = Console.ReadLine();
+ if (modeInput == null)
+ {
+ Console.WriteLine("无效输入");
+ return;
+ }
+
+ switch (modeInput)
+ {
+ case "0":
+ WebpCompressor.CompressImages(sourceImagePath, targetStoragePath);
+ break;
+ case "1":
+ AvifCompressor.CompressImages(sourceImagePath, targetStoragePath);
+ break;
+ default:
+ Console.WriteLine("不支持的模式");
+ break;
+ }
+ }
+
+ private static string? GetFolderPath()
+ {
+ using var dialog = new FolderBrowserDialog();
+ dialog.ShowNewFolderButton = false;
+
+ DialogResult result = dialog.ShowDialog();
+
+ if (result == DialogResult.OK && !string.IsNullOrWhiteSpace(dialog.SelectedPath))
+ {
+ return dialog.SelectedPath;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/Comic_Compressor/WebpCompressor.cs b/Comic_Compressor/WebpCompressor.cs
new file mode 100644
index 0000000..7eee2c0
--- /dev/null
+++ b/Comic_Compressor/WebpCompressor.cs
@@ -0,0 +1,82 @@
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Formats.Webp;
+using SixLabors.ImageSharp.Processing;
+
+namespace Comic_Compressor
+{
+ internal class WebpCompressor
+ {
+ internal static void CompressImages(string sourceImagePath, string targetStoragePath)
+ {
+ // Step 1: Get all subdirectories and store them in a list
+ List subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));
+
+ // Step 2: Iterate through each subdirectory in order
+ foreach (string subdirectory in subdirectories)
+ {
+ // Step 3: Process each directory
+ ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath);
+ }
+ }
+
+ private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath)
+ {
+ // Get the relative path of the subdirectory
+ string relativePath = Path.GetRelativePath(sourceImagePath, subdirectory);
+
+ // Create the corresponding subdirectory in the target storage path
+ string targetSubdirectory = Path.Combine(targetStoragePath, relativePath);
+ Directory.CreateDirectory(targetSubdirectory);
+
+ // Get all image files in the subdirectory (jpg and png)
+ string[] imageFiles = GetImageFiles(subdirectory);
+
+ // Iterate through each image file
+ foreach (string imageFile in imageFiles)
+ {
+ // Set the target file path with the .webp extension
+ string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".webp");
+ CompressImage(imageFile, targetFilePath);
+ }
+
+ Console.WriteLine($"{Path.GetFileName(subdirectory)} processed successfully.");
+ }
+
+ private static string[] GetImageFiles(string directoryPath)
+ {
+ // Get all image files supported by ImageSharp
+ string[] supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff"];
+ List allFiles = [];
+
+ foreach (string extension in supportedExtensions)
+ {
+ allFiles.AddRange(Directory.GetFiles(directoryPath, extension, SearchOption.TopDirectoryOnly));
+ }
+
+ return [.. allFiles];
+ }
+
+ private static void CompressImage(string sourceFilePath, string targetFilePath)
+ {
+ using SixLabors.ImageSharp.Image image = SixLabors.ImageSharp.Image.Load(sourceFilePath);
+ // Check the longest side of the image and resize if necessary
+ int maxDimension = Math.Max(image.Width, image.Height);
+ if (maxDimension > 1200)
+ {
+ double scaleFactor = 1200.0 / maxDimension;
+ int newWidth = (int)(image.Width * scaleFactor);
+ int newHeight = (int)(image.Height * scaleFactor);
+ image.Mutate(x => x.Resize(newWidth, newHeight));
+ }
+
+ // Save the image as WebP with a quality level of 70 (for lossy compression)
+ var encoder = new WebpEncoder
+ {
+ Quality = 70,
+ FileFormat = WebpFileFormatType.Lossy
+ };
+
+ image.Save(targetFilePath, encoder);
+ }
+ }
+}