update
This commit is contained in:
parent
3860b1bd54
commit
3c645dc893
@ -1,8 +1,9 @@
|
|||||||
using System;
|
using LibHeifSharp;
|
||||||
using System.Collections.Generic;
|
using SixLabors.ImageSharp;
|
||||||
using System.Linq;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System.Text;
|
using ShellProgressBar;
|
||||||
using System.Threading.Tasks;
|
using SixLabors.ImageSharp.Metadata;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
namespace Comic_Compressor
|
namespace Comic_Compressor
|
||||||
{
|
{
|
||||||
@ -10,8 +11,319 @@ namespace Comic_Compressor
|
|||||||
{
|
{
|
||||||
internal static void CompressImages(string sourceImagePath, string targetStoragePath)
|
internal static void CompressImages(string sourceImagePath, string targetStoragePath)
|
||||||
{
|
{
|
||||||
//尚未实现
|
LibHeifSharpDllImportResolver.Register();
|
||||||
throw new NotImplementedException();
|
// Step 1: Get all subdirectories and store them in a list
|
||||||
|
List<string> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("All directories processed successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath, ShellProgressBar.ProgressBar progressBar)
|
||||||
|
{
|
||||||
|
// 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 = 2 // 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 .avif extension
|
||||||
|
string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".avif");
|
||||||
|
|
||||||
|
// 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"];
|
||||||
|
List<string> allFiles = [];
|
||||||
|
|
||||||
|
foreach (string extension in supportedExtensions)
|
||||||
|
{
|
||||||
|
allFiles.AddRange(Directory.GetFiles(directoryPath, extension, SearchOption.TopDirectoryOnly));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. allFiles];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CompressImage(string sourceFilePath, string targetFilePath)
|
||||||
|
{
|
||||||
|
int quality = 80;
|
||||||
|
//int quality = 70;
|
||||||
|
var format = HeifCompressionFormat.Av1;
|
||||||
|
bool saveAlphaChannel = false;
|
||||||
|
bool writeTwoProfiles = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load the image and ensure it's in Rgb24 format
|
||||||
|
using var image = SixLabors.ImageSharp.Image.Load(sourceFilePath);
|
||||||
|
var rgbImage = image.CloneAs<Rgb24>();
|
||||||
|
|
||||||
|
//// Check the longest side of the image and resize if necessary
|
||||||
|
//int maxDimension = Math.Max(rgbImage.Width, rgbImage.Height);
|
||||||
|
//if (maxDimension > 1200)
|
||||||
|
//{
|
||||||
|
// double scaleFactor = 1200.0 / maxDimension;
|
||||||
|
// int newWidth = (int)(rgbImage.Width * scaleFactor);
|
||||||
|
// int newHeight = (int)(rgbImage.Height * scaleFactor);
|
||||||
|
// rgbImage.Mutate(x => x.Resize(newWidth, newHeight));
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Save as AVIF format
|
||||||
|
using var context = new HeifContext();
|
||||||
|
HeifEncoderDescriptor? encoderDescriptor = null;
|
||||||
|
|
||||||
|
if (LibHeifInfo.HaveEncoder(format))
|
||||||
|
{
|
||||||
|
var encoderDescriptors = context.GetEncoderDescriptors(format);
|
||||||
|
encoderDescriptor = encoderDescriptors[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("No AV1 encoder available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using HeifEncoder encoder = context.GetEncoder(encoderDescriptor);
|
||||||
|
if (writeTwoProfiles && !LibHeifInfo.CanWriteTwoColorProfiles)
|
||||||
|
{
|
||||||
|
writeTwoProfiles = false;
|
||||||
|
Console.WriteLine($"Warning: LibHeif version {LibHeifInfo.Version} cannot write two color profiles.");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var heifImage = CreateHeifImage(rgbImage, writeTwoProfiles, out var metadata);
|
||||||
|
encoder.SetLossyQuality(quality);
|
||||||
|
|
||||||
|
var encodingOptions = new HeifEncodingOptions
|
||||||
|
{
|
||||||
|
SaveAlphaChannel = saveAlphaChannel,
|
||||||
|
WriteTwoColorProfiles = writeTwoProfiles
|
||||||
|
};
|
||||||
|
context.EncodeImage(heifImage, encoder, encodingOptions);
|
||||||
|
context.WriteToFile(targetFilePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static HeifImage CreateHeifImage(Image<Rgb24> image,
|
||||||
|
bool writeTwoColorProfiles,
|
||||||
|
out ImageMetadata metadata)
|
||||||
|
{
|
||||||
|
HeifImage? heifImage = null;
|
||||||
|
HeifImage? temp = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
metadata = image.Metadata;
|
||||||
|
temp = ConvertToHeifImage(image);
|
||||||
|
|
||||||
|
if (writeTwoColorProfiles && metadata.IccProfile != null)
|
||||||
|
{
|
||||||
|
temp.IccColorProfile = new HeifIccColorProfile(metadata.IccProfile.ToByteArray());
|
||||||
|
|
||||||
|
temp.NclxColorProfile = new HeifNclxColorProfile(ColorPrimaries.BT709,
|
||||||
|
TransferCharacteristics.Srgb,
|
||||||
|
MatrixCoefficients.BT601,
|
||||||
|
fullRange: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (metadata.IccProfile != null)
|
||||||
|
{
|
||||||
|
temp.IccColorProfile = new HeifIccColorProfile(metadata.IccProfile.ToByteArray());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp.NclxColorProfile = new HeifNclxColorProfile(ColorPrimaries.BT709,
|
||||||
|
TransferCharacteristics.Srgb,
|
||||||
|
MatrixCoefficients.BT601,
|
||||||
|
fullRange: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heifImage = temp;
|
||||||
|
temp = null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
temp?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return heifImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HeifImage ConvertToHeifImage(Image<Rgb24> image)
|
||||||
|
{
|
||||||
|
bool isGrayscale = IsGrayscale(image);
|
||||||
|
|
||||||
|
var colorspace = isGrayscale ? HeifColorspace.Monochrome : HeifColorspace.Rgb;
|
||||||
|
var chroma = colorspace == HeifColorspace.Monochrome ? HeifChroma.Monochrome : HeifChroma.InterleavedRgb24;
|
||||||
|
|
||||||
|
HeifImage? heifImage = null;
|
||||||
|
HeifImage? temp = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
temp = new HeifImage(image.Width, image.Height, colorspace, chroma);
|
||||||
|
|
||||||
|
if (colorspace == HeifColorspace.Monochrome)
|
||||||
|
{
|
||||||
|
temp.AddPlane(HeifChannel.Y, image.Width, image.Height, 8);
|
||||||
|
CopyGrayscale(image, temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp.AddPlane(HeifChannel.Interleaved, image.Width, image.Height, 8);
|
||||||
|
CopyRgb(image, temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
heifImage = temp;
|
||||||
|
temp = null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
temp?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return heifImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void CopyGrayscale(Image<Rgb24> image, HeifImage heifImage)
|
||||||
|
{
|
||||||
|
var grayPlane = heifImage.GetPlane(HeifChannel.Y);
|
||||||
|
|
||||||
|
byte* grayPlaneScan0 = (byte*)grayPlane.Scan0;
|
||||||
|
int grayPlaneStride = grayPlane.Stride;
|
||||||
|
|
||||||
|
image.ProcessPixelRows(accessor =>
|
||||||
|
{
|
||||||
|
for (int y = 0; y < accessor.Height; y++)
|
||||||
|
{
|
||||||
|
var src = accessor.GetRowSpan(y);
|
||||||
|
byte* dst = grayPlaneScan0 + (y * grayPlaneStride);
|
||||||
|
|
||||||
|
for (int x = 0; x < accessor.Width; x++)
|
||||||
|
{
|
||||||
|
ref var pixel = ref src[x];
|
||||||
|
|
||||||
|
dst[0] = pixel.R;
|
||||||
|
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void CopyRgb(Image<Rgb24> image, HeifImage heifImage)
|
||||||
|
{
|
||||||
|
var interleavedData = heifImage.GetPlane(HeifChannel.Interleaved);
|
||||||
|
|
||||||
|
byte* srcScan0 = (byte*)interleavedData.Scan0;
|
||||||
|
int srcStride = interleavedData.Stride;
|
||||||
|
|
||||||
|
image.ProcessPixelRows(accessor =>
|
||||||
|
{
|
||||||
|
for (int y = 0; y < accessor.Height; y++)
|
||||||
|
{
|
||||||
|
var src = accessor.GetRowSpan(y);
|
||||||
|
byte* dst = srcScan0 + (y * srcStride);
|
||||||
|
|
||||||
|
for (int x = 0; x < accessor.Width; x++)
|
||||||
|
{
|
||||||
|
ref var pixel = ref src[x];
|
||||||
|
|
||||||
|
dst[0] = pixel.R;
|
||||||
|
dst[1] = pixel.G;
|
||||||
|
dst[2] = pixel.B;
|
||||||
|
|
||||||
|
dst += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsGrayscale(Image<Rgb24> image)
|
||||||
|
{
|
||||||
|
bool isGrayscale = true;
|
||||||
|
|
||||||
|
image.ProcessPixelRows(accessor =>
|
||||||
|
{
|
||||||
|
for (int y = 0; y < accessor.Height; y++)
|
||||||
|
{
|
||||||
|
var src = accessor.GetRowSpan(y);
|
||||||
|
|
||||||
|
for (int x = 0; x < accessor.Width; x++)
|
||||||
|
{
|
||||||
|
ref var pixel = ref src[x];
|
||||||
|
|
||||||
|
if (!(pixel.R == pixel.G && pixel.G == pixel.B))
|
||||||
|
{
|
||||||
|
isGrayscale = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isGrayscale)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return isGrayscale;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,46 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<UseWindowsForms>True</UseWindowsForms>
|
<UseWindowsForms>True</UseWindowsForms>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LibHeifSharp" Version="3.2.0" />
|
<PackageReference Include="LibHeifSharp" Version="3.2.0" />
|
||||||
|
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="aom.dll">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="dav1d.dll">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libde265.dll">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libheif.dll">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libs\aom.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libs\dav1d.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libs\libde265.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libs\libheif.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libs\libx265.dll">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libx265.dll">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
105
Comic_Compressor/LibHeifSharpDllImportResolver.cs
Normal file
105
Comic_Compressor/LibHeifSharpDllImportResolver.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of libheif-sharp-samples, a collection of example applications
|
||||||
|
* for libheif-sharp
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020, 2021, 2022, 2023 Nicholas Hayes
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Comic_Compressor
|
||||||
|
{
|
||||||
|
internal static class LibHeifSharpDllImportResolver
|
||||||
|
{
|
||||||
|
private static IntPtr cachedLibHeifModule = IntPtr.Zero;
|
||||||
|
private static bool firstRequestForLibHeif = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the <see cref="DllImportResolver"/> for the LibHeifSharp assembly.
|
||||||
|
/// </summary>
|
||||||
|
public static void Register()
|
||||||
|
{
|
||||||
|
// The runtime will execute the specified callback when it needs to resolve a native library
|
||||||
|
// import for the LibHeifSharp assembly.
|
||||||
|
NativeLibrary.SetDllImportResolver(typeof(LibHeifSharp.LibHeifInfo).Assembly, Resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||||
|
{
|
||||||
|
// We only care about a native library named libheif, the runtime will use
|
||||||
|
// its default behavior for any other native library.
|
||||||
|
if (string.Equals(libraryName, "libheif", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
// Because the DllImportResolver will be called multiple times we load libheif once
|
||||||
|
// and cache the module handle for future requests.
|
||||||
|
if (firstRequestForLibHeif)
|
||||||
|
{
|
||||||
|
firstRequestForLibHeif = false;
|
||||||
|
cachedLibHeifModule = LoadNativeLibrary(libraryName, assembly, searchPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedLibHeifModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to default import resolver.
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static nint LoadNativeLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
// On Windows the libheif DLL name defaults to heif.dll, so we try to load that if
|
||||||
|
// libheif.dll was not found.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return NativeLibrary.Load(libraryName, assembly, searchPath);
|
||||||
|
}
|
||||||
|
catch (DllNotFoundException)
|
||||||
|
{
|
||||||
|
if (NativeLibrary.TryLoad("heif.dll", assembly, searchPath, out IntPtr handle))
|
||||||
|
{
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS() || OperatingSystem.IsWatchOS())
|
||||||
|
{
|
||||||
|
// The Apple mobile/embedded platforms statically link libheif into the AOT compiled main program binary.
|
||||||
|
return NativeLibrary.GetMainProgramHandle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use the default runtime behavior for all other platforms.
|
||||||
|
return NativeLibrary.Load(libraryName, assembly, searchPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats.Webp;
|
using SixLabors.ImageSharp.Formats.Webp;
|
||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using ShellProgressBar;
|
||||||
|
|
||||||
namespace Comic_Compressor
|
namespace Comic_Compressor
|
||||||
{
|
{
|
||||||
@ -11,15 +12,29 @@ namespace Comic_Compressor
|
|||||||
// Step 1: Get all subdirectories and store them in a list
|
// Step 1: Get all subdirectories and store them in a list
|
||||||
List<string> subdirectories = new(Directory.GetDirectories(sourceImagePath, "*", SearchOption.AllDirectories));
|
List<string> 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
|
// Step 2: Iterate through each subdirectory in order
|
||||||
foreach (string subdirectory in subdirectories)
|
foreach (string subdirectory in subdirectories)
|
||||||
{
|
{
|
||||||
// Step 3: Process each directory
|
// Step 3: Process each directory
|
||||||
ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath);
|
ProcessDirectory(subdirectory, sourceImagePath, targetStoragePath, progressBar);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath)
|
Console.WriteLine("All directories processed successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessDirectory(string subdirectory, string sourceImagePath, string targetStoragePath, ShellProgressBar.ProgressBar progressBar)
|
||||||
{
|
{
|
||||||
// Get the relative path of the subdirectory
|
// Get the relative path of the subdirectory
|
||||||
string relativePath = Path.GetRelativePath(sourceImagePath, subdirectory);
|
string relativePath = Path.GetRelativePath(sourceImagePath, subdirectory);
|
||||||
@ -31,15 +46,34 @@ namespace Comic_Compressor
|
|||||||
// Get all image files in the subdirectory (jpg and png)
|
// Get all image files in the subdirectory (jpg and png)
|
||||||
string[] imageFiles = GetImageFiles(subdirectory);
|
string[] imageFiles = GetImageFiles(subdirectory);
|
||||||
|
|
||||||
// Iterate through each image file
|
// Set up ParallelOptions to limit the number of concurrent threads
|
||||||
foreach (string imageFile in imageFiles)
|
ParallelOptions options = new()
|
||||||
|
{
|
||||||
|
MaxDegreeOfParallelism = 2 // 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
|
// Set the target file path with the .webp extension
|
||||||
string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".webp");
|
string targetFilePath = Path.Combine(targetSubdirectory, Path.GetFileNameWithoutExtension(imageFile) + ".webp");
|
||||||
CompressImage(imageFile, targetFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"{Path.GetFileName(subdirectory)} processed successfully.");
|
// 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)
|
private static string[] GetImageFiles(string directoryPath)
|
||||||
@ -69,10 +103,10 @@ namespace Comic_Compressor
|
|||||||
image.Mutate(x => x.Resize(newWidth, newHeight));
|
image.Mutate(x => x.Resize(newWidth, newHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the image as WebP with a quality level of 70 (for lossy compression)
|
// Save the image as WebP with a quality level of 85 (for lossy compression)
|
||||||
var encoder = new WebpEncoder
|
var encoder = new WebpEncoder
|
||||||
{
|
{
|
||||||
Quality = 70,
|
Quality = 90,
|
||||||
FileFormat = WebpFileFormatType.Lossy
|
FileFormat = WebpFileFormatType.Lossy
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BIN
Comic_Compressor/aom.dll
Normal file
BIN
Comic_Compressor/aom.dll
Normal file
Binary file not shown.
BIN
Comic_Compressor/dav1d.dll
Normal file
BIN
Comic_Compressor/dav1d.dll
Normal file
Binary file not shown.
BIN
Comic_Compressor/libde265.dll
Normal file
BIN
Comic_Compressor/libde265.dll
Normal file
Binary file not shown.
BIN
Comic_Compressor/libheif.dll
Normal file
BIN
Comic_Compressor/libheif.dll
Normal file
Binary file not shown.
BIN
Comic_Compressor/libx265.dll
Normal file
BIN
Comic_Compressor/libx265.dll
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user