diff --git a/Comic_Compressor/Comic_Compressor.csproj b/Comic_Compressor/Comic_Compressor.csproj index 0ddd65d..53a091a 100644 --- a/Comic_Compressor/Comic_Compressor.csproj +++ b/Comic_Compressor/Comic_Compressor.csproj @@ -11,21 +11,14 @@ - + - - - PreserveNewest - PreserveNewest - - PreserveNewest - PreserveNewest @@ -44,9 +37,6 @@ Never - - PreserveNewest - diff --git a/Comic_Compressor/JxlCompressor.cs b/Comic_Compressor/JxlCompressor.cs new file mode 100644 index 0000000..1d275ac --- /dev/null +++ b/Comic_Compressor/JxlCompressor.cs @@ -0,0 +1,116 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Webp; +using SixLabors.ImageSharp.Processing; +using ShellProgressBar; + +namespace Comic_Compressor +{ + internal class JxlCompressor + { + internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount) + { + // Step 1: Get all subdirectories and store them in a list + List subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); + + int totalFiles = 0; + foreach (string subdirectory in subdirectories) + { + totalFiles += GetImageFiles(subdirectory).Length; + } + + using var progressBar = new ShellProgressBar.ProgressBar(totalFiles, "Compressing images", new ProgressBarOptions + { + ProgressCharacter = '─', + ProgressBarOnBottom = true + }); + + // Step 2: Iterate through each subdirectory in order + foreach (string subdirectory in subdirectories) + { + // Step 3: Process each directory + ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath, progressBar, threadCount); + } + + Console.WriteLine("All directories processed successfully."); + } + + private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath, ShellProgressBar.ProgressBar progressBar, int threadCount) + { + // 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); + + // Set up ParallelOptions to limit the number of concurrent threads + ParallelOptions options = new() + { + MaxDegreeOfParallelism = threadCount // Adjust this value to set the number of concurrent threads + }; + + // Process each image file in parallel + Parallel.ForEach(imageFiles, options, imageFile => + { + // Set the target file path with the .webp extension + string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".webp"); + + // Check if the target file already exists + if (!File.Exists(targetFilePath)) + { + CompressImage(imageFile, targetFilePath); + + // Update progress bar safely + lock (progressBar) + { + progressBar.Tick($"Processed {Path.GetFileName(imageFile)}"); + } + } + else + { + lock (progressBar) { progressBar.Tick($"Skipped {Path.GetFileName(imageFile)}"); } + } + }); + } + + private static string[] GetImageFiles(string directoryPath) + { + // Get all image files supported by ImageSharp + string[] supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.gif"]; + 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 85 (for lossy compression) + var encoder = new WebpEncoder + { + Quality = 90, + FileFormat = WebpFileFormatType.Lossy + }; + + image.Save(targetFilePath, encoder); + } + } +} diff --git a/Comic_Compressor/LegacyFormatCompressor.cs b/Comic_Compressor/LegacyFormatCompressor.cs new file mode 100644 index 0000000..e8e3e52 --- /dev/null +++ b/Comic_Compressor/LegacyFormatCompressor.cs @@ -0,0 +1,116 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Webp; +using SixLabors.ImageSharp.Processing; +using ShellProgressBar; + +namespace Comic_Compressor +{ + internal class LegacyFormatCompressor + { + internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, int targetFormat) + { + // Step 1: Get all subdirectories and store them in a list + List subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); + + int totalFiles = 0; + foreach (string subdirectory in subdirectories) + { + totalFiles += GetImageFiles(subdirectory).Length; + } + + using var progressBar = new ShellProgressBar.ProgressBar(totalFiles, "Compressing images", new ProgressBarOptions + { + ProgressCharacter = '─', + ProgressBarOnBottom = true + }); + + // Step 2: Iterate through each subdirectory in order + foreach (string subdirectory in subdirectories) + { + // Step 3: Process each directory + ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath, progressBar, threadCount); + } + + Console.WriteLine("All directories processed successfully."); + } + + private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath, ShellProgressBar.ProgressBar progressBar, int threadCount) + { + // 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); + + // Set up ParallelOptions to limit the number of concurrent threads + ParallelOptions options = new() + { + MaxDegreeOfParallelism = threadCount // Adjust this value to set the number of concurrent threads + }; + + // Process each image file in parallel + Parallel.ForEach(imageFiles, options, imageFile => + { + // Set the target file path with the .webp extension + string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".webp"); + + // Check if the target file already exists + if (!File.Exists(targetFilePath)) + { + CompressImage(imageFile, targetFilePath); + + // Update progress bar safely + lock (progressBar) + { + progressBar.Tick($"Processed {Path.GetFileName(imageFile)}"); + } + } + else + { + lock (progressBar) { progressBar.Tick($"Skipped {Path.GetFileName(imageFile)}"); } + } + }); + } + + private static string[] GetImageFiles(string directoryPath) + { + // Get all image files supported by ImageSharp + string[] supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.gif"]; + 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 85 (for lossy compression) + var encoder = new WebpEncoder + { + Quality = 90, + FileFormat = WebpFileFormatType.Lossy + }; + + image.Save(targetFilePath, encoder); + } + } +} diff --git a/Comic_Compressor/MixProcessor.cs b/Comic_Compressor/MixProcessor.cs new file mode 100644 index 0000000..fe148fe --- /dev/null +++ b/Comic_Compressor/MixProcessor.cs @@ -0,0 +1,111 @@ +namespace Comic_Compressor +{ + internal class MixProcessor + { + internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, int targetFormat) + { + // Step 1: Get all subdirectories and store them in a list + List subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); + + int totalFiles = 0; + foreach (string subdirectory in subdirectories) + { + totalFiles += GetImageFiles(subdirectory).Length; + } + + using var progressBar = new ShellProgressBar.ProgressBar(totalFiles, "Compressing images", new ProgressBarOptions + { + ProgressCharacter = '─', + ProgressBarOnBottom = true + }); + + // Step 2: Iterate through each subdirectory in order + foreach (string subdirectory in subdirectories) + { + // Step 3: Process each directory + ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath, progressBar, threadCount); + } + + Console.WriteLine("All directories processed successfully."); + } + + private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath, ShellProgressBar.ProgressBar progressBar, int threadCount) + { + // 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); + + // Set up ParallelOptions to limit the number of concurrent threads + ParallelOptions options = new() + { + MaxDegreeOfParallelism = threadCount // Adjust this value to set the number of concurrent threads + }; + + // Process each image file in parallel + Parallel.ForEach(imageFiles, options, imageFile => + { + // Set the target file path with the .webp extension + string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".webp"); + + // Check if the target file already exists + if (!File.Exists(targetFilePath)) + { + CompressImage(imageFile, targetFilePath); + + // Update progress bar safely + lock (progressBar) + { + progressBar.Tick($"Processed {Path.GetFileName(imageFile)}"); + } + } + else + { + lock (progressBar) { progressBar.Tick($"Skipped {Path.GetFileName(imageFile)}"); } + } + }); + } + + private static string[] GetImageFiles(string directoryPath) + { + // Get all image files supported by ImageSharp + string[] supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.gif"]; + 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 85 (for lossy compression) + var encoder = new WebpEncoder + { + Quality = 90, + FileFormat = WebpFileFormatType.Lossy + }; + + image.Save(targetFilePath, encoder); + } + } +} diff --git a/Comic_Compressor/Program.cs b/Comic_Compressor/Program.cs index d8fcbf4..5f454e6 100644 --- a/Comic_Compressor/Program.cs +++ b/Comic_Compressor/Program.cs @@ -17,7 +17,7 @@ namespace Comic_Compressor return; } - Console.WriteLine("请输入目标存储位置:"); + Console.WriteLine("请选择保存位置:"); string? targetStoragePath = GetFolderPath(); if (string.IsNullOrEmpty(targetStoragePath)) { @@ -29,7 +29,7 @@ namespace Comic_Compressor int threadCount = int.Parse(Console.ReadLine() ?? "2"); Console.WriteLine($"处理线程数设定:{threadCount}"); - Console.WriteLine("请选择压缩模式:0 - 压缩成webp,1 - 压缩成avif"); + Console.WriteLine("目标格式:0 - webp, 1 - avif, 2 - JXL(JPEG-XL), 3 - JPG, 4 - PNG, 5 - 保留原格式(best effort)"); string? modeInput = Console.ReadLine(); if (modeInput == null) { @@ -47,12 +47,24 @@ namespace Comic_Compressor AvifCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount); GetCompressorResult(sourceImagePath, targetStoragePath); break; + case "2": + JxlCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount); + GetCompressorResult(sourceImagePath, targetStoragePath); + break; + case "3": + case "4": + LegacyFormatCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount,int.Parse(modeInput)); + GetCompressorResult(sourceImagePath, targetStoragePath); + break; + case "5": + throw new NotImplementedException(); default: - Console.WriteLine("不支持的模式"); + Console.WriteLine("不支持的格式"); break; } } + private static string? GetFolderPath() { using var dialog = new FolderBrowserDialog(); @@ -74,10 +86,8 @@ namespace Comic_Compressor { long size = 0; - // 遍历目录中的所有文件 foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) { - // 获取文件的大小并累加到总大小中 FileInfo fileInfo = new(file); size += fileInfo.Length; } @@ -85,15 +95,30 @@ namespace Comic_Compressor return size; } + private static string GetHumanReadableSize(long size) + { + string[] sizes = { "B", "KB", "MB", "GB", "TB" }; + double len = size; + int order = 0; + while (len >= 1024 && order < sizes.Length - 1) + { + order++; + len = len / 1024; + } + + return $"{len:0.##} {sizes[order]}"; + } + + private static void GetCompressorResult(string source, string target) { long sourceSize = GetDirectorySize(source); long targetSize = GetDirectorySize(target); double reduced = (sourceSize - targetSize) * 1.0 / sourceSize; - Console.WriteLine($"源目录大小:{sourceSize} 字节"); - Console.WriteLine($"目标目录大小:{targetSize} 字节"); - Console.WriteLine($"已减少:{reduced:P}的体积"); + Console.WriteLine($"压缩前大小:{GetHumanReadableSize(sourceSize)}"); + Console.WriteLine($"压缩后大小:{GetHumanReadableSize(targetSize)}"); + Console.WriteLine($"体积已减少{reduced:P}"); } } diff --git a/Comic_Compressor/aom.dll b/Comic_Compressor/aom.dll deleted file mode 100644 index e69a1e5..0000000 Binary files a/Comic_Compressor/aom.dll and /dev/null differ diff --git a/Comic_Compressor/dav1d.dll b/Comic_Compressor/dav1d.dll deleted file mode 100644 index c313084..0000000 Binary files a/Comic_Compressor/dav1d.dll and /dev/null differ diff --git a/Comic_Compressor/libde265.dll b/Comic_Compressor/libde265.dll deleted file mode 100644 index d557151..0000000 Binary files a/Comic_Compressor/libde265.dll and /dev/null differ diff --git a/Comic_Compressor/libheif.dll b/Comic_Compressor/libheif.dll deleted file mode 100644 index e8068fe..0000000 Binary files a/Comic_Compressor/libheif.dll and /dev/null differ diff --git a/Comic_Compressor/libx265.dll b/Comic_Compressor/libx265.dll deleted file mode 100644 index 6992a31..0000000 Binary files a/Comic_Compressor/libx265.dll and /dev/null differ