This commit is contained in:
2026-02-02 21:56:45 +08:00
commit 320550399d
24 changed files with 4551 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ImageCompressor
{
/// <summary>
/// 用来总的记录各个线程处理照片的进度,以及标记线程运行结果
/// </summary>
class CountProgress
{
ImageCompressorForm owner;
public CountProgress(int totalCount, ImageCompressorForm owner)
{
this.TotalCount = totalCount;
this.owner = owner;
FailedInfos = new List<string>();
}
public ReaderWriterLock RWLock = new ReaderWriterLock();
/// <summary>
/// 总的处理了的照片数量
/// </summary>
public int ProcessedCount => SuccessCount + FailedCount + SkipCount;
/// <summary>
/// 处理过成功的照片数量
/// </summary>
public int SuccessCount { get; set; } = 0;
/// <summary>
/// 处理过失败的照片数量
/// </summary>
public int FailedCount { get; set; } = 0;
/// <summary>
/// 已经跳过的照片数量
/// </summary>
public int SkipCount { get; set; } = 0;
/// <summary>
/// 处理失败的信息
/// </summary>
public List<string> FailedInfos { get; set; }
/// <summary>
/// 需要处理的照片总数
/// </summary>
public int TotalCount { get; private set; }
/// <summary>
/// 是否已经处理完所有照片
/// </summary>
public bool Finished => ProcessedCount == TotalCount;
/// <summary>
/// 处理前照片的大小总和
/// </summary>
public long PreSizeKBSum { get; set; } = 0;
/// <summary>
/// 压缩后照片的大小总和
/// </summary>
public long PostSizeKBSum { get; set; } = 0;
/// <summary>
/// 计时器
/// </summary>
public Stopwatch stopwatch { get; set; } = new Stopwatch();
/// <summary>
/// Update Data To Winform
/// </summary>
public void Update()
{
owner.SetProgressValue(ProcessedCount);
if (Finished)
{
stopwatch.Stop();
owner.ShowReport();
}
}
}
}

424
Compressor/FilesManager.cs Normal file
View File

@@ -0,0 +1,424 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ImageCompressor
{
/// <summary>
/// 后台线程运行状态
/// </summary>
enum RunningState
{
NoFile,
Ready,
Running,
WaitStop,
AllThreadStoped,
Finished,
}
/// <summary>
/// 管理读取IMG文件并且负责Invoke压缩多线程
/// </summary>
class FilesManager
{
public bool IncludeSubFolders { get; set; } = true;
ImageCompressorForm owner { get; set; }
public FilesManager(ImageCompressorForm owner)
{
this.owner = owner;
}
#region Files
/// <summary>
/// 储存读取的所有照片的Size
/// </summary>
public long PreAllImageSizeinKB { get; private set; }
/// <summary>
/// 所有的照片的路径(打开路径和储存路径)
/// </summary>
List<ImgPaths> ImgPathList { get; set; } = new List<ImgPaths>();
/// <summary>
/// 总共有多少张照片
/// </summary>
public int FilesCount => ImgPathList.Count;
/// <summary>
/// 开始读取照片
/// 从输入的文件夹里,提取出所有的照片路径
/// </summary>
/// <param name="folders"></param>
public void ReadAllFilePaths(IEnumerable<string> folders)
{
ImgPathList.Clear();
processList = null;
PreAllImageSizeinKB = 0;
foreach (string folderPath in folders)
{
string[] files;
if (IncludeSubFolders)
files = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories).Where(x => IsImg(x)).ToArray();
else
files = Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly).Where(x => IsImg(x)).ToArray();
foreach (string file in files) { PreAllImageSizeinKB += new FileInfo(file).Length / 1024; }
DirectoryInfo info = new DirectoryInfo(folderPath);
string newFolder = Path.Combine(info.Parent.FullName, info.Name + Localize.Get("_Compressed"));
ImgPathList.AddRange(files.Select(x => new ImgPaths(x, x.Replace(folderPath, newFolder))));
}
progress = new CountProgress(ImgPathList.Count, owner);
}
static long skipFileSize = 900 * 1024;
bool IsImg(string fileName)
{
string extension = Path.GetExtension(fileName);
if (Path.GetFileName(fileName).StartsWith("._")) return false;
switch (extension.ToLower())
{
case ".jpg":
case ".png":
case ".jpeg":
case ".bmp":
return true;
}
return false;
}
/// <summary>
/// 设定跳过的文件大小
/// </summary>
/// <param name="textInKB"></param>
public void SetSkipFileSizeInKB(string textInKB)
{
if (int.TryParse(textInKB, out int result))
skipFileSize = result * 1024;
}
public int CurrentSkipFileSizeInKB => (int)(skipFileSize / 1024);
/// <summary>
/// 判断是否需要跳过该文件
/// </summary>
/// <param name="fileInfo"></param>
/// <returns></returns>
public static bool IsSkip(FileInfo fileInfo)
{
return fileInfo == null || fileInfo.Length < skipFileSize;
}
/// <summary>
/// 格式化显示大小
/// </summary>
/// <param name="sizeInKB"></param>
/// <returns></returns>
public string GetFileSizeString(long sizeInKB)
{
if (sizeInKB > 1048576)
return string.Format("{0:f2}GB", sizeInKB / 1048576.0);
else if (sizeInKB > 1024)
return string.Format("{0:f2}MB", sizeInKB / 1024.0);
else return string.Format("{0:f2}KB", sizeInKB);
}
#endregion
#region Preview
/// <summary>
/// 用于储存临时保存的预览压缩图片
/// </summary>
string TempImgPath = Path.Combine(Path.GetTempPath(), "temp.jpg");
/// <summary>
/// 返回当前所有路径的随机序号如果没有路径return false
/// </summary>
/// <param name="seed"></param>
/// <param name="index"></param>
/// <returns></returns>
public bool GetRandomIndex(int seed, out int index)
{
if (FilesCount > 0)
{
Random random = new Random(seed);
index = random.Next(0, FilesCount);
return true;
}
index = -1;
return false;
}
public Image GetImageAtIndex(int index, out string fileSize)
{
FileInfo rawFile = ImgPathList[index].OriginalFileInfo;
if (rawFile.Exists)
{
try
{
using (FileStream stream = rawFile.OpenRead())
{
Image raw = Image.FromStream(stream);
fileSize = GetFileSizeString(stream.Length / 1024);
stream.Close();
return raw;
}
}
catch (Exception ex)
{
fileSize = $"Cannot preview Image {rawFile.FullName}, {ex.Message}";
return null;
}
}
else
{
fileSize = Localize.Get("Missing");
return null;
}
}
/// <summary>
/// 将列表中Index序号的图像进行压缩
/// 储存到临时文件里去
/// 然后读取该临时图片作为输出
/// 失败返回null
/// skip时返回null,fileSize = null;
/// </summary>
/// <param name="index"></param>
/// <param name="fileSize"></param>
/// <param name="skip"></param>
/// <returns></returns>
public Image GetCompressedImgAtIndex(int index, out string fileSize, out bool skip, out string errorMessage)
{
fileSize = null;
FileInfo rawFile = ImgPathList[index].OriginalFileInfo;
skip = IsSkip(rawFile);
if (skip)
{
errorMessage = null;
return null;
}
JPGCompressor compressor = new JPGCompressor(this.CompressLevel, this.MaxWidth, this.MaxHeight);
if (!compressor.ReadFileToImage(rawFile, out Bitmap image, out _, out errorMessage))
{
return null;
}
Bitmap bitmap = compressor.CompressImgToBitmap(image);
//image.Dispose();
if (compressor.SaveCompressBitmap(bitmap, new FileInfo(TempImgPath), out long saveSizeInKB, out errorMessage))
{
fileSize = GetFileSizeString(saveSizeInKB);
}
else fileSize = Localize.Get("Estimate Fail");
return bitmap;
}
#endregion
#region MultiThread
/// <summary>
/// 储存照片时是否保留源文件
/// </summary>
public bool KeepOriginal { get; set; } = true;
int compressLevel = 8;
/// <summary>
/// 当前的压缩级数
/// </summary>
public int CompressLevel
{
get
{
return compressLevel;
}
set
{
compressLevel = value;
MaxWidth = JPGCompressor.CompressionLevelToMaxWidth(compressLevel);
MaxHeight = JPGCompressor.CompressionLevelToMaxHeight(compressLevel);
}
}
/// <summary>
/// 当前的最大宽度
/// </summary>
public int MaxWidth { get; set; }
/// <summary>
/// 当前的最大高度
/// </summary>
public int MaxHeight { get; set; }
/// <summary>
/// 向Winform标记运行状态
/// </summary>
public RunningState State
{
get
{
if(FilesCount == 0)
{
return RunningState.NoFile;
}
else if (processList == null)
{
return RunningState.Ready;
}
else if (progress.Finished) return RunningState.Finished;
else
{
foreach (ImgsProcess process in processList)
{
if (!process.Stoped)
{
if(process.ToStop)return RunningState.WaitStop;
return RunningState.Running;
}
}
return RunningState.AllThreadStoped;
}
}
}
/// <summary>
/// 用来储存多线程运行过程数据和结果
/// </summary>
internal CountProgress progress;
/// <summary>
/// 所有线程运行数据
/// </summary>
ImgsProcess[] processList;
/// <summary>
/// 多线程常量增加貌似并没有加速很多猜测卡在JPG的IO读取上。
/// </summary>
public const int ThreadCount = 16;
/// <summary>
/// 正式压缩所有图片
/// 返回线程数
/// </summary>
public void RunAllCompress()
{
//运行后不能修改KeepOriginal,CompressLevel
foreach (ImgPaths path in ImgPathList) path.KeepOriginal = KeepOriginal;
//将所有的图片Path平均分配到【线程数】个ProcessImgs里
if (State == RunningState.Ready)
{
progress.stopwatch.Restart();
processList = DivideIntoMultiProcess();
}
else progress.stopwatch.Start();
//另一种情况是Stoped就继续上次计算
foreach(ImgsProcess process in processList)
{
//一旦开始了运行,再修改的参数就不影响了
process.SetCompressLevel(this.CompressLevel, this.MaxWidth, this.MaxHeight);
}
Thread[] threads = processList.Select(x => new Thread(x.Start)).ToArray();
foreach (Thread thread in threads)
{
thread.Start();
}
}
/// <summary>
/// 等待所有线程结束之后再弹出Report
/// </summary>
void StopAsync()
{
while (true)
{
int stopCount = 0;
foreach (ImgsProcess process in processList)
{
if (process.Stoped) stopCount++;
else Thread.Sleep(100);
}
if (stopCount == processList.Length) break;
}
progress.stopwatch.Stop();
owner.ShowReport();
}
/// <summary>
/// 停止所有计算。
/// 先设定ToStop状态
/// 然后等所有线程计算结束后再停止弹出Report
/// </summary>
public void Stop()
{
foreach (ImgsProcess process in processList)
{
if (process.ToStop) return;
process.ToStop = true;
}
owner.UpdateRunButtonText();
// 等待所有线程结束之后再弹出Report
new Thread(StopAsync).Start();
}
/// <summary>
/// 多线程分工方法
/// </summary>
/// <returns></returns>
private ImgsProcess[] DivideIntoMultiProcess()
{
List<ImgsProcess> imgsProcess = new List<ImgsProcess>();
List<List<ImgPaths>> splitImgPath = new List<List<ImgPaths>>();
for (int t = 0; t < ThreadCount; t++)
{
splitImgPath.Add(new List<ImgPaths>());
}
int count = ImgPathList.Count;
for (int i = 0; i < count; i++)
{
int threadIndex = i % ThreadCount;
splitImgPath[threadIndex].Add(ImgPathList[i]);
}
int index = 0;
for (int t = 0; t < ThreadCount; t++)
{
if (splitImgPath[t].Count > 0)
{
imgsProcess.Add(new ImgsProcess(index++, splitImgPath[t], progress));
}
}
return imgsProcess.ToArray();
}
#endregion
}
}

29
Compressor/ImgPaths.cs Normal file
View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ImageCompressor
{
/// <summary>
/// 一张照片的打开路径和储存路径
/// </summary>
class ImgPaths
{
public bool KeepOriginal { get; set; } = true;
public FileInfo OriginalFileInfo { get; private set; }
public FileInfo DuplicatedFileInfo { get; private set; }
public ImgPaths(string readPath, string writePath)
{
OriginalFileInfo = new FileInfo(readPath);
string extension = Path.GetExtension(writePath).ToLower();
if(extension != ".jpg" || extension !=".jpeg")
{
Path.ChangeExtension(writePath, ".jpg");
}
this.DuplicatedFileInfo = new FileInfo(writePath);
}
}
}

239
Compressor/ImgsProcess.cs Normal file
View File

@@ -0,0 +1,239 @@
//#define NoOutput
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Dynamic;
namespace ImageCompressor
{
/// <summary>
/// 一个单独的线程,来批量压缩图片
/// </summary>
class ImgsProcess
{
/// <summary>
/// 这个压缩进程内的所有压缩图片的路径
/// </summary>
ImgPaths[] ImgPaths { get; set; }
/// <summary>
/// 计数当前线程编号用于Debug
/// </summary>
public int threadIndex=-1;
/// <summary>
/// 引用的进度总控
/// </summary>
readonly CountProgress progress;
int currentIndex = 0;
/// <summary>
/// 设定是否要在过程中停止计算,标记为停止后,等待计算停止
/// </summary>
public bool ToStop { get; set; } = false;
/// <summary>
/// 判断是否所有计算已经停止
/// </summary>
public bool Stoped { get; private set; } = false;
/// <summary>
/// 该线程处理的所有图片数量
/// </summary>
public int AllCount { get; private set; }
public ImgsProcess(int index, List<ImgPaths> imgPaths, CountProgress countProgress)
{
threadIndex = index;
ImgPaths = imgPaths.ToArray();
AllCount = ImgPaths.Length;
this.progress = countProgress;
}
/// <summary>
/// 开始时设定所有的压缩参数
/// </summary>
/// <param name="compressLevel"></param>
/// <param name="maxWidth"></param>
/// <param name="maxHeight"></param>
public void SetCompressLevel(int compressLevel,int maxWidth, int maxHeight)
{
this.compressLevel = compressLevel;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
}
int compressLevel;
int maxWidth;
int maxHeight;
/// <summary>
/// 用来传递每个Img压缩时候的数据
/// </summary>
class DataPack
{
public DataPack(ImgPaths paths, JPGCompressor compressor)
{
this.compressor = compressor;
this.imgPaths = paths;
}
public JPGCompressor compressor;
public ImgPaths imgPaths;
public byte[] readBytes = null;
public byte[] saveBytes = null;
}
#if DEBUG
double Tick => Math.Round(DateTime.Now.TimeOfDay.TotalSeconds % 1000, 3);
#endif
public void Start()
{
Stoped = false;
ToStop = false;
ResetCountData();
while (currentIndex < AllCount)
{
#if DEBUG
long t1 = progress.stopwatch.ElapsedMilliseconds;
#endif
DataPack dataPack = new DataPack(ImgPaths[currentIndex],new JPGCompressor(compressLevel,maxWidth,maxHeight));
ReadOneImage(dataPack);
CompressCurrent(dataPack);
WriteOneImage(dataPack);
#if DEBUG
long t2 = progress.stopwatch.ElapsedMilliseconds;
#endif
progress.RWLock.AcquireWriterLock(-1);
UpdateCountProgress();
ResetCountData();
Thread.Sleep(25);
progress.RWLock.ReleaseWriterLock();
#if DEBUG
Debug.WriteLine($"thread {threadIndex} count {currentIndex}, from {t1}, used {t2 - t1}");
#endif
currentIndex++;
if (ToStop)
{
Stoped = true;
return;
}
}
Stoped = true;
}
//当前线程运行的结果变量
int successCount = 0;
int failedCount = 0;
List<string> failedPaths = new List<string>();
int skipCount = 0;
long preSizeKB = 0;
long saveSizeKB = 0;
/// <summary>
/// 重置当前线程运行的结果变量
/// </summary>
void ResetCountData()
{
successCount = 0;
failedCount = 0;
failedPaths.Clear();
skipCount = 0;
preSizeKB = 0;
saveSizeKB = 0;
}
/// <summary>
/// 读照片到DataPack
/// </summary>
/// <param name="dataPack"></param>
void ReadOneImage(DataPack dataPack)
{
ImgPaths imgPaths = dataPack.imgPaths;
if(!imgPaths.OriginalFileInfo.Exists)
{
failedCount++;
failedPaths.Add(Localize.Get("Read file missing") + imgPaths.OriginalFileInfo.FullName);
return;
}
preSizeKB += imgPaths.OriginalFileInfo.Length / 1024;
if (FilesManager.IsSkip(imgPaths.OriginalFileInfo))
{
skipCount++;
return;
}
dataPack.readBytes = dataPack.compressor.ReadFileToBytes(imgPaths.OriginalFileInfo, out string error);
if (dataPack.readBytes == null)
{
failedCount++;
failedPaths.Add(error + imgPaths.OriginalFileInfo.FullName);
}
}
/// <summary>
/// 压缩得到文件的Bytes
/// </summary>
/// <param name="dataPack"></param>
void CompressCurrent(DataPack dataPack)
{
if (dataPack.readBytes != null)
{
dataPack.saveBytes = dataPack.compressor.CompressBytes(dataPack.readBytes,out string error);
if(dataPack.saveBytes == null)
{
failedCount++;
failedPaths.Add(error + dataPack.imgPaths.OriginalFileInfo.FullName);
}
}
}
/// <summary>
/// 写Bytes到文件
/// </summary>
/// <param name="dataPack"></param>
void WriteOneImage(DataPack dataPack)
{
if (dataPack.saveBytes != null)
{
bool keepOriginal = dataPack.imgPaths.KeepOriginal;
FileInfo saveFile = keepOriginal ? dataPack.imgPaths.DuplicatedFileInfo : dataPack.imgPaths.OriginalFileInfo;
bool re = dataPack.compressor.SaveCompressBytes(dataPack.saveBytes, saveFile, out string error);
if (re)
{
saveSizeKB += dataPack.saveBytes.LongLength / 1024;
successCount++;
}
else
{
failedCount++;
failedPaths.Add(error + dataPack.imgPaths.DuplicatedFileInfo.FullName);
}
}
}
/// <summary>
/// 将本线程结果更新到progress的结果
/// </summary>
void UpdateCountProgress()
{
if (progress.Finished) return;
progress.SuccessCount += successCount;
progress.FailedCount+= failedCount;
progress.SkipCount += skipCount;
progress.FailedInfos.AddRange(failedPaths);
progress.PreSizeKBSum+= preSizeKB;
progress.PostSizeKBSum += saveSizeKB;
progress.Update();
}
}
}

366
Compressor/JPGCompressor.cs Normal file
View File

@@ -0,0 +1,366 @@

using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Diagnostics;
namespace ImageCompressor
{
/// <summary>
/// 单张JPG的压缩运算
/// </summary>
class JPGCompressor
{
int compressLevel;
int MaxWidth;
int MaxHeight;
long Quality;
ImageCodecInfo jpegEncoder;
EncoderParameters jpeParameters;
public JPGCompressor(int compressLevel, int maxWidth,int maxHeight)
{
CompressLevel= compressLevel;
MaxWidth = maxWidth;
MaxHeight = maxHeight;
}
/// <summary>
/// 设定压缩质量
/// </summary>
public int CompressLevel
{
get { return compressLevel; }
private set
{
compressLevel = value;
Quality = compressLevel * 5 + 50; //(50-100)
if (!SetJpegCompressor()) throw new Exception("Cannot Create JPG Comporessor");
}
}
public static int CompressionLevelToMaxWidth(int level)
{
return Math.Max((int)(1920 * (level * 0.2)), 480);//480-3840
}
public static int CompressionLevelToMaxHeight(int level)
{
return Math.Max((int)(1080 * (level * 0.2)), 360);//360-2160
}
#region CompressBytes
/// <summary>
/// 储存Bitmap到JPG文件。
/// 之后释放 writeBitmap
/// </summary>
public bool SaveCompressBytes(byte[] saveBytes, FileInfo saveFile, out string errorMessage)
{
errorMessage = null;
if (saveFile.Exists)
{
if (saveFile.IsReadOnly)
{
errorMessage = Localize.Get("Save file is ReadOnly. ");
return false;
}
}
else
{
DirectoryInfo directory = saveFile.Directory;
if (!directory.Exists)
{
try
{
directory.Create();
}
catch
{
errorMessage = Localize.Get("Cannot create save folder. ");
return false;
}
}
}
try
{
File.WriteAllBytes(saveFile.FullName, saveBytes);
return true;
}
catch (Exception ex)
{
if (ex is IOException)
{
errorMessage = Localize.Get("Save file is occupied. ");
return false;
}
errorMessage = Localize.Get("Save file error at ")+$"{ex.Message}";
return false;
}
}
/// <summary>
/// 打开一个Image文件,返回Bytes
/// </summary>
/// <returns></returns>
public byte[] ReadFileToBytes(FileInfo readFile, out string errorMessage)
{
errorMessage = null;
if (!readFile.Exists)
{
errorMessage = Localize.Get("Read file not exist. ");
return null;
}
if(readFile.Length > 1024 * 1024 * 256)
{
errorMessage = Localize.Get("Read image larger than 256 MB");
return null;
}
try
{
return File.ReadAllBytes(readFile.FullName);
}
catch (Exception ex)
{
errorMessage = Localize.Get("Read file error at ") + $"{ex.Message}";
return null;
}
}
public byte[] CompressBytes(byte[] readBytes, out string errorMessage)
{
MemoryStream memoryStream = null;
MemoryStream saveMemoryStream = null;
Bitmap rawBit = null;
Graphics graphics = null;
Bitmap newBit = null;
errorMessage = null;
try
{
memoryStream = new MemoryStream(readBytes, false);
saveMemoryStream = new MemoryStream();
rawBit = new Bitmap(memoryStream);
memoryStream.Close();
memoryStream.Dispose();
double percentW = (double)MaxWidth / rawBit.Width;
double percentH = (double)MaxHeight / rawBit.Height;
double percent = Math.Min(percentW, percentH);
percent = Math.Min(1, percent);
if (percent < 1 || compressLevel < 10)
{
int newW = (int)(percent * rawBit.Width);
int newH = (int)(percent * rawBit.Height);
newBit = new Bitmap(newW, newH);
graphics = Graphics.FromImage(newBit);
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
graphics.DrawImage(rawBit, 0, 0, newW, newH);
graphics.Dispose();
rawBit.Dispose();
newBit.Save(saveMemoryStream, jpegEncoder, jpeParameters);
newBit.Dispose();
}
else
{
//Level为10则直接储存JPG
rawBit.Save(saveMemoryStream, jpegEncoder, jpeParameters);
}
int length = (int)saveMemoryStream.Length;
saveMemoryStream.Position = 0;
byte[] savedBytes = new byte[length];
saveMemoryStream.Read(savedBytes, 0, length);
saveMemoryStream.Close();
saveMemoryStream.Dispose();
return savedBytes;
}
catch (Exception ex)
{
errorMessage = Localize.Get("Save file error at ")+$"{ex.Message}";
return null;
}
finally
{
memoryStream?.Close();
memoryStream?.Dispose();
saveMemoryStream?.Close();
saveMemoryStream?.Dispose();
rawBit?.Dispose();
newBit?.Dispose();
graphics?.Dispose();
}
}
#endregion
#region CompressBitmap
/// <summary>
/// 储存Bitmap到JPG文件。
/// </summary>
/// <param name="bitmap"></param>
/// <param name="savePath"></param>
/// <param name="savedSizeInKB"></param>
public bool SaveCompressBitmap(Bitmap bitmap, FileInfo saveFile, out long savedSizeInKB, out string errorMessage)
{
savedSizeInKB = 0;
errorMessage = null;
if (saveFile.Exists)
{
if (saveFile.IsReadOnly)
{
errorMessage = Localize.Get("Save file is ReadOnly. ");
return false;
}
}
else
{
DirectoryInfo directory = saveFile.Directory;
if (!directory.Exists)
{
try
{
directory.Create();
}
catch
{
errorMessage = Localize.Get("Cannot create save folder. ");
return false;
}
}
}
FileStream stream = null;
try
{
stream = saveFile.Open(FileMode.Create, FileAccess.Write, FileShare.None);
bitmap.Save(stream, jpegEncoder, jpeParameters);
savedSizeInKB = stream.Length / 1024;
return true;
}
catch (Exception ex)
{
if (ex is IOException)
{
errorMessage = Localize.Get("Save file is occupied. ");
return false;
}
errorMessage = Localize.Get("Save file error at ") + $"{ex.Message}";
return false;
}
finally
{
stream?.Close();
stream?.Dispose();
}
}
/// <summary>
/// 打开一个Image文件
/// </summary>
/// <param name="readFile"></param>
/// <param name="image"></param>
/// <param name="readSizeInKB"></param>
/// <param name="errorMessage"></param>
/// <returns></returns>
public bool ReadFileToImage(FileInfo readFile, out Bitmap image, out long readSizeInKB, out string errorMessage)
{
image = null;
readSizeInKB = 0;
errorMessage = null;
if (!readFile.Exists)
{
errorMessage = Localize.Get("Read file not exist. ");
return false;
}
FileStream stream = null;
try
{
stream = readFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
Image im = Image.FromStream(stream);
image = new Bitmap(im);
im.Dispose();
readSizeInKB = stream.Length / 1024;
return true;
}
catch (Exception ex)
{
readSizeInKB = readFile.Length / 1024;
errorMessage = Localize.Get("Save file error at ") + $"{ex.Message}";
return false;
}
finally
{
stream?.Close();
stream?.Dispose();
}
}
/// <summary>
/// 将图片压缩不释放Image
/// </summary>
/// <param name="raw"></param>
/// <returns></returns>
public Bitmap CompressImgToBitmap(Bitmap raw)
{
double percentW = (double)MaxWidth / raw.Width;
double percentH = (double)MaxHeight / raw.Height;
double percent = Math.Min(percentW, percentH);
percent = Math.Min(1, percent);
if (percent < 1 || compressLevel < 10)
{
int newW = (int)(percent * raw.Width);
int newH = (int)(percent * raw.Height);
Bitmap newBit = new Bitmap(newW, newH);
using (Graphics g = Graphics.FromImage(newBit))
{
//g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
g.DrawImage(raw, 0, 0, newW, newH);
return newBit;
}
}
else
{
Bitmap bitmap = new Bitmap(raw);
return bitmap;
}
}
#endregion
bool SetJpegCompressor()
{
var temp = ImageCodecInfo.GetImageDecoders();
jpegEncoder = temp.FirstOrDefault(x => x.MimeType == "image/jpeg");
if (jpegEncoder == null) return false;
System.Drawing.Imaging.Encoder encoder = System.Drawing.Imaging.Encoder.Quality;
jpeParameters = new EncoderParameters(1);
EncoderParameter parameter = new EncoderParameter(encoder, Quality);
jpeParameters.Param[0] = parameter;
return true;
}
}
}