Compare commits

..

6 Commits

10 changed files with 125 additions and 47 deletions

View File

@ -3,24 +3,30 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.11.35219.272 VisualStudioVersion = 17.11.35219.272
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Comic_Compressor", "Comic_Compressor\Comic_Compressor.csproj", "{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Comic_Compressor", "Comic_Compressor\Comic_Compressor.csproj", "{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64 Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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|Any CPU.Build.0 = Debug|Any CPU
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|x64.ActiveCfg = Debug|x64 {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}.Debug|x64.Build.0 = Debug|x64
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|x86.ActiveCfg = Debug|x86
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Debug|x86.Build.0 = Debug|x86
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {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|Any CPU.Build.0 = Release|Any CPU
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x64.ActiveCfg = Release|x64 {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x64.ActiveCfg = Release|x64
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x64.Build.0 = Release|x64 {B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x64.Build.0 = Release|x64
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x86.ActiveCfg = Release|x86
{B6B0E3D8-DE3D-4A7D-AAE5-34953ABFEA2A}.Release|x86.Build.0 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -5,12 +5,11 @@ namespace Comic_Compressor
{ {
internal class AvifCompressor : Utils internal class AvifCompressor : Utils
{ {
internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount) internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, bool usePresetQuality, int Quality)
{ {
//config
MagickFormat targetFormat = MagickFormat.Avif; MagickFormat targetFormat = MagickFormat.Avif;
string targetExtension = ".avif"; string targetExtension = ".avif";
int targetQuality = 80; int targetQuality = usePresetQuality ? 80 : Quality;
List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));

View File

@ -5,7 +5,7 @@
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64;x86</Platforms>
<UseWindowsForms>True</UseWindowsForms> <UseWindowsForms>True</UseWindowsForms>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>

View File

@ -5,12 +5,11 @@ namespace Comic_Compressor
{ {
internal class JxlCompressor : Utils internal class JxlCompressor : Utils
{ {
internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount) internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, bool usePresetQuality, int Quality)
{ {
//config
MagickFormat targetFormat = MagickFormat.Jxl; MagickFormat targetFormat = MagickFormat.Jxl;
string targetExtension = ".jxl"; string targetExtension = ".jxl";
int targetQuality = 90; int targetQuality = usePresetQuality ? 90 : Quality;
List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));

View File

@ -5,14 +5,25 @@ namespace Comic_Compressor
//Process images in legacy format(JPG,PNG) //Process images in legacy format(JPG,PNG)
internal class LegacyFormatCompressor : Utils internal class LegacyFormatCompressor : Utils
{ {
internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, int format) internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, bool usePresetQuality, int Quality, int format)
{ {
//check format MagickFormat targetFormat = format switch
throw new NotImplementedException(); {
//config 3 => MagickFormat.Jpeg,
MagickFormat targetFormat = MagickFormat.Jxl; 4 => MagickFormat.Png,
string targetExtension = ".jxl"; 5 => MagickFormat.Bmp,
int targetQuality = 90; _ => throw new Exception(),
};
string targetExtension = format switch
{
3 => ".jpg",
4 => ".png",
5 => ".bmp",
_ => throw new Exception(),
};
int targetQuality = usePresetQuality ? 90 : Quality;
List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));

View File

@ -1,17 +1,11 @@
using ImageMagick; using ShellProgressBar;
using ShellProgressBar;
namespace Comic_Compressor namespace Comic_Compressor
{ {
internal class MixProcessor : Utils internal class MixProcessor : Utils
{ {
internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, int format) internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, bool usePresetQuality, int Quality)
{ {
//detect mu-config int targetQuality = usePresetQuality ? 90 : Quality;
throw new NotImplementedException();
//config
MagickFormat targetFormat = MagickFormat.Jxl;
string targetExtension = ".jxl";
int targetQuality = 90;
List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));
@ -29,7 +23,7 @@ namespace Comic_Compressor
foreach (string subdirectory in subdirectories) foreach (string subdirectory in subdirectories)
{ {
ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath, progressBar, threadCount, targetExtension, targetFormat, targetQuality); ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath, progressBar, threadCount, targetQuality);
} }
Console.WriteLine("All directories processed successfully."); Console.WriteLine("All directories processed successfully.");

View File

@ -1,6 +1,4 @@
using ImageMagick; using System.Text;
using System.Text;
using System.Windows.Forms;
namespace Comic_Compressor namespace Comic_Compressor
{ {
internal class Program internal class Program
@ -29,9 +27,13 @@ namespace Comic_Compressor
Console.WriteLine("处理线程数:"); Console.WriteLine("处理线程数:");
int threadCount = int.Parse(Console.ReadLine() ?? "2"); int threadCount = int.Parse(Console.ReadLine() ?? "2");
Console.WriteLine($"处理线程数设定:{threadCount}"); if (threadCount < 1)
{
Console.WriteLine("无效线程数");
return;
}
Console.WriteLine("目标格式0 - webp, 1 - avif, 2 - JXL(JPEG-XL), 3 - JPG, 4 - PNG, 5 - BMP, 6 - 保留原格式(best effort)"); Console.WriteLine("目标格式0 - webp, 1 - avif, 2 - JXL(JPEG-XL), 3 - JPG, 4 - PNG, 5 - BMP, 6 - 保留原格式");
string? modeInput = Console.ReadLine(); string? modeInput = Console.ReadLine();
if (modeInput == null) if (modeInput == null)
{ {
@ -39,32 +41,51 @@ namespace Comic_Compressor
return; return;
} }
Console.WriteLine("使用预设质量(默认使用)(y/n)");
string? input = Console.ReadLine()?.Trim().ToLower();
bool usePresetQuality = input == null || input == "" || input == "y" || input == "yes";
int targetQuality = -1;
if (!usePresetQuality)
{
Console.WriteLine("Quality (0-100 INT):");
string? targetQualityStr = Console.ReadLine();
if (targetQualityStr == null)
{
Console.WriteLine("无效输入");
return;
}
targetQuality = int.Parse(targetQualityStr);
if (targetQuality < 0 || targetQuality > 100)
{
Console.WriteLine("invalid image quality");
return;
}
}
switch (modeInput) switch (modeInput)
{ {
case "0": case "0":
WebpCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount); WebpCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount, usePresetQuality, targetQuality);
Utils.GetCompressorResult(sourceImagePath, targetStoragePath);
break; break;
case "1": case "1":
AvifCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount); AvifCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount, usePresetQuality, targetQuality);
Utils.GetCompressorResult(sourceImagePath, targetStoragePath);
break; break;
case "2": case "2":
JxlCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount); JxlCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount, usePresetQuality, targetQuality);
Utils.GetCompressorResult(sourceImagePath, targetStoragePath);
break; break;
case "3": case "3":
case "4": case "4":
case "5": case "5":
LegacyFormatCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount,int.Parse(modeInput)); LegacyFormatCompressor.CompressImages(sourceImagePath, targetStoragePath, threadCount, usePresetQuality, targetQuality, int.Parse(modeInput));
Utils.GetCompressorResult(sourceImagePath, targetStoragePath);
break; break;
case "6": case "6":
throw new NotImplementedException(); MixProcessor.CompressImages(sourceImagePath, targetStoragePath, threadCount, usePresetQuality, targetQuality);
break;
default: default:
Console.WriteLine("不支持的格式"); Console.WriteLine("不支持的格式");
break; return;
} }
Utils.GetCompressorResult(sourceImagePath, targetStoragePath);
} }
} }
} }

View File

@ -28,7 +28,7 @@ namespace Comic_Compressor
// Get all image files in a directory with supported extensions // Get all image files in a directory with supported extensions
public static string[] GetImageFiles(string directoryPath) public static string[] GetImageFiles(string directoryPath)
{ {
string[] supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.jxl", "*.avif", "*.webp"]; string[] supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.tif", "*.jxl", "*.avif", "*.webp"];
ConcurrentBag<string> allFiles = []; ConcurrentBag<string> allFiles = [];
foreach (string extension in supportedExtensions) foreach (string extension in supportedExtensions)
@ -76,6 +76,56 @@ namespace Comic_Compressor
}); });
} }
//Process all image files in a directory, save as origin format
public static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath, ShellProgressBar.ProgressBar progressBar, int threadCount, int quality)
{
string relativePath = Path.GetRelativePath(sourceImagePath, subdirectory);
string targetSubdirectory = Path.Combine(targetStoragePath, relativePath);
Directory.CreateDirectory(targetSubdirectory);
string[] imageFiles = GetImageFiles(subdirectory);
ParallelOptions options = new()
{
MaxDegreeOfParallelism = threadCount // Adjust this value to set the number of concurrent threads
};
Parallel.ForEach(imageFiles, options, imageFile =>
{
string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileName(imageFile));
//detect file format
//supportedExtensions = ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.tiff", "*.jxl", "*.avif", "*.webp"];
string extension = Path.GetExtension(targetFilePath).ToLower();
MagickFormat mFormat = extension switch
{
".jpg" or ".jpeg" => MagickFormat.Jpeg,
".png" => MagickFormat.Png,
".bmp" => MagickFormat.Bmp,
".gif" => MagickFormat.Gif,
".tiff" or ".tif" => MagickFormat.Tiff,
".jxl" => MagickFormat.Jxl,
".avif" => MagickFormat.Avif,
".webp" => MagickFormat.WebP,
_ => throw new Exception()//这个位置怎么还会有意外情况
};
if (!File.Exists(targetFilePath))
{
CompressImage(imageFile, targetFilePath, mFormat, quality);
lock (progressBar)
{
progressBar.Tick($"Processed {Path.GetFileName(imageFile)}");
}
}
else
{
lock (progressBar) { progressBar.Tick($"Skipped {Path.GetFileName(imageFile)}"); }
}
});
}
// Display the compression result // Display the compression result
public static void GetCompressorResult(string source, string target) public static void GetCompressorResult(string source, string target)
{ {

View File

@ -5,12 +5,11 @@ namespace Comic_Compressor
{ {
internal class WebpCompressor : Utils internal class WebpCompressor : Utils
{ {
internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount) internal static void CompressImages(string sourceImagePath, string targetStoragePath, int threadCount, bool usePresetQuality, int Quality)
{ {
//config
MagickFormat targetFormat = MagickFormat.WebP; MagickFormat targetFormat = MagickFormat.WebP;
string targetExtension = ".webp"; string targetExtension = ".webp";
int targetQuality = 90; int targetQuality = usePresetQuality ? 90 : Quality;
List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories)); List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));

View File

@ -1,5 +1,4 @@
这是一个自用的图像压缩工具 这是一个自用的图像压缩工具
支持对一整个文件夹内的图像进行压缩处理,支持输出webp、avif、jxl(jpeg xl)格式图像(传统格式尚未完工 支持对一整个文件夹内的图像进行压缩处理,支持输出webp、avif、jxl(jpeg xl)、jpg、bmp、png等格式
*转换avif性能很差?
*转换avif性能很差