commit 320550399d491fe5136248eb858fb81b79976779 Author: wucl12 Date: Mon Feb 2 21:56:45 2026 +0800 Init diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/App.config b/App.config new file mode 100644 index 0000000..193aecc --- /dev/null +++ b/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Compressor/CountProgress.cs b/Compressor/CountProgress.cs new file mode 100644 index 0000000..d3c6b1b --- /dev/null +++ b/Compressor/CountProgress.cs @@ -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 +{ + /// + /// 用来总的记录各个线程处理照片的进度,以及标记线程运行结果 + /// + class CountProgress + { + ImageCompressorForm owner; + public CountProgress(int totalCount, ImageCompressorForm owner) + { + this.TotalCount = totalCount; + this.owner = owner; + FailedInfos = new List(); + } + + + public ReaderWriterLock RWLock = new ReaderWriterLock(); + + /// + /// 总的处理了的照片数量 + /// + public int ProcessedCount => SuccessCount + FailedCount + SkipCount; + + /// + /// 处理过成功的照片数量 + /// + public int SuccessCount { get; set; } = 0; + + /// + /// 处理过失败的照片数量 + /// + public int FailedCount { get; set; } = 0; + + /// + /// 已经跳过的照片数量 + /// + public int SkipCount { get; set; } = 0; + + /// + /// 处理失败的信息 + /// + public List FailedInfos { get; set; } + /// + /// 需要处理的照片总数 + /// + public int TotalCount { get; private set; } + + /// + /// 是否已经处理完所有照片 + /// + public bool Finished => ProcessedCount == TotalCount; + + /// + /// 处理前照片的大小总和 + /// + public long PreSizeKBSum { get; set; } = 0; + /// + /// 压缩后照片的大小总和 + /// + public long PostSizeKBSum { get; set; } = 0; + /// + /// 计时器 + /// + public Stopwatch stopwatch { get; set; } = new Stopwatch(); + + /// + /// Update Data To Winform + /// + public void Update() + { + owner.SetProgressValue(ProcessedCount); + if (Finished) + { + stopwatch.Stop(); + owner.ShowReport(); + } + } + + } +} diff --git a/Compressor/FilesManager.cs b/Compressor/FilesManager.cs new file mode 100644 index 0000000..2009f73 --- /dev/null +++ b/Compressor/FilesManager.cs @@ -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 +{ + /// + /// 后台线程运行状态 + /// + enum RunningState + { + NoFile, + Ready, + Running, + WaitStop, + AllThreadStoped, + Finished, + } + + /// + /// 管理读取IMG文件,并且负责Invoke压缩多线程 + /// + class FilesManager + { + + public bool IncludeSubFolders { get; set; } = true; + + + ImageCompressorForm owner { get; set; } + + + + public FilesManager(ImageCompressorForm owner) + { + this.owner = owner; + } + + #region Files + + /// + /// 储存读取的所有照片的Size + /// + public long PreAllImageSizeinKB { get; private set; } + + + /// + /// 所有的照片的路径(打开路径和储存路径) + /// + List ImgPathList { get; set; } = new List(); + /// + /// 总共有多少张照片 + /// + public int FilesCount => ImgPathList.Count; + + /// + /// 开始读取照片 + /// 从输入的文件夹里,提取出所有的照片路径 + /// + /// + public void ReadAllFilePaths(IEnumerable 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; + } + /// + /// 设定跳过的文件大小 + /// + /// + public void SetSkipFileSizeInKB(string textInKB) + { + if (int.TryParse(textInKB, out int result)) + skipFileSize = result * 1024; + } + + public int CurrentSkipFileSizeInKB => (int)(skipFileSize / 1024); + + /// + /// 判断是否需要跳过该文件 + /// + /// + /// + public static bool IsSkip(FileInfo fileInfo) + { + return fileInfo == null || fileInfo.Length < skipFileSize; + } + /// + /// 格式化显示大小 + /// + /// + /// + 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 + + /// + /// 用于储存临时保存的预览压缩图片 + /// + string TempImgPath = Path.Combine(Path.GetTempPath(), "temp.jpg"); + + /// + /// 返回当前所有路径的随机序号,如果没有路径,return false + /// + /// + /// + /// + 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; + } + } + + /// + /// 将列表中Index序号的图像进行压缩 + /// 储存到临时文件里去 + /// 然后读取该临时图片作为输出 + /// 失败返回null + /// skip时返回null,fileSize = null; + /// + /// + /// + /// + /// + 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 + /// + /// 储存照片时是否保留源文件 + /// + public bool KeepOriginal { get; set; } = true; + int compressLevel = 8; + /// + /// 当前的压缩级数 + /// + public int CompressLevel + { + get + { + return compressLevel; + } + set + { + compressLevel = value; + MaxWidth = JPGCompressor.CompressionLevelToMaxWidth(compressLevel); + MaxHeight = JPGCompressor.CompressionLevelToMaxHeight(compressLevel); + } + } + /// + /// 当前的最大宽度 + /// + public int MaxWidth { get; set; } + /// + /// 当前的最大高度 + /// + public int MaxHeight { get; set; } + + + /// + /// 向Winform标记运行状态 + /// + 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; + } + } + } + + /// + /// 用来储存多线程运行过程数据和结果 + /// + internal CountProgress progress; + + /// + /// 所有线程运行数据 + /// + ImgsProcess[] processList; + + /// + /// 多线程常量,增加貌似并没有加速很多,猜测卡在JPG的IO读取上。 + /// + public const int ThreadCount = 16; + + /// + /// 正式压缩所有图片 + /// 返回线程数 + /// + 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(); + + } + } + + /// + /// 等待所有线程结束之后再弹出Report + /// + 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(); + } + /// + /// 停止所有计算。 + /// 先设定ToStop状态 + /// 然后等所有线程计算结束后再停止弹出Report + /// + public void Stop() + { + foreach (ImgsProcess process in processList) + { + if (process.ToStop) return; + process.ToStop = true; + } + owner.UpdateRunButtonText(); + // 等待所有线程结束之后再弹出Report + new Thread(StopAsync).Start(); + } + /// + /// 多线程分工方法 + /// + /// + private ImgsProcess[] DivideIntoMultiProcess() + { + List imgsProcess = new List(); + List> splitImgPath = new List>(); + for (int t = 0; t < ThreadCount; t++) + { + splitImgPath.Add(new List()); + } + 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 + } +} diff --git a/Compressor/ImgPaths.cs b/Compressor/ImgPaths.cs new file mode 100644 index 0000000..f30b900 --- /dev/null +++ b/Compressor/ImgPaths.cs @@ -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 +{ + /// + /// 一张照片的打开路径和储存路径 + /// + 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); + } + } +} diff --git a/Compressor/ImgsProcess.cs b/Compressor/ImgsProcess.cs new file mode 100644 index 0000000..d9f9f2e --- /dev/null +++ b/Compressor/ImgsProcess.cs @@ -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 +{ + + /// + /// 一个单独的线程,来批量压缩图片 + /// + class ImgsProcess + { + + /// + /// 这个压缩进程内的所有压缩图片的路径 + /// + ImgPaths[] ImgPaths { get; set; } + + /// + /// 计数当前线程编号,用于Debug + /// + public int threadIndex=-1; + + /// + /// 引用的进度总控 + /// + readonly CountProgress progress; + + int currentIndex = 0; + + /// + /// 设定是否要在过程中停止计算,标记为停止后,等待计算停止 + /// + public bool ToStop { get; set; } = false; + + /// + /// 判断是否所有计算已经停止 + /// + public bool Stoped { get; private set; } = false; + /// + /// 该线程处理的所有图片数量 + /// + public int AllCount { get; private set; } + + public ImgsProcess(int index, List imgPaths, CountProgress countProgress) + { + threadIndex = index; + ImgPaths = imgPaths.ToArray(); + AllCount = ImgPaths.Length; + this.progress = countProgress; + } + /// + /// 开始时设定所有的压缩参数 + /// + /// + /// + /// + public void SetCompressLevel(int compressLevel,int maxWidth, int maxHeight) + { + this.compressLevel = compressLevel; + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + } + + int compressLevel; + int maxWidth; + int maxHeight; + + /// + /// 用来传递每个Img压缩时候的数据 + /// + 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 failedPaths = new List(); + int skipCount = 0; + long preSizeKB = 0; + long saveSizeKB = 0; + + /// + /// 重置当前线程运行的结果变量 + /// + void ResetCountData() + { + successCount = 0; + failedCount = 0; + failedPaths.Clear(); + skipCount = 0; + preSizeKB = 0; + saveSizeKB = 0; + } + /// + /// 读照片到DataPack + /// + /// + 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); + } + } + + /// + /// 压缩得到文件的Bytes + /// + /// + 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); + } + } + } + + /// + /// 写Bytes到文件 + /// + /// + 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); + } + } + } + + /// + /// 将本线程结果更新到progress的结果 + /// + 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(); + } + + + } +} diff --git a/Compressor/JPGCompressor.cs b/Compressor/JPGCompressor.cs new file mode 100644 index 0000000..cdb588b --- /dev/null +++ b/Compressor/JPGCompressor.cs @@ -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 +{ + /// + /// 单张JPG的压缩运算 + /// + 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; + + } + /// + /// 设定压缩质量 + /// + 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 + + /// + /// 储存Bitmap到JPG文件。 + /// 之后释放 writeBitmap + /// + 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; + } + } + + /// + /// 打开一个Image文件,返回Bytes + /// + /// + 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 + /// + /// 储存Bitmap到JPG文件。 + /// + /// + /// + /// + 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(); + } + } + + /// + /// 打开一个Image文件 + /// + /// + /// + /// + /// + /// + 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(); + } + } + + + /// + /// 将图片压缩,不释放Image + /// + /// + /// + 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; + } + } +} diff --git a/ImageCompressor.csproj b/ImageCompressor.csproj new file mode 100644 index 0000000..a22b4cd --- /dev/null +++ b/ImageCompressor.csproj @@ -0,0 +1,131 @@ + + + + + Debug + AnyCPU + {8DB25786-04B5-4D20-AE2F-A3DB997E0DF0} + WinExe + ImageCompressor + ImageCompressor + v4.8 + 512 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + Resources\Icon.ico + + + + + + + + + + + + + + + + + + + + Form + + + ImageCompressorForm.cs + + + + + + Component + + + Component + + + + + + ImageCompressorForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + False + Microsoft .NET Framework 4.8 %28x86 和 x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + \ No newline at end of file diff --git a/ImageCompressor.sln b/ImageCompressor.sln new file mode 100644 index 0000000..e4dfa37 --- /dev/null +++ b/ImageCompressor.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32319.34 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageCompressor", "ImageCompressor.csproj", "{8DB25786-04B5-4D20-AE2F-A3DB997E0DF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8DB25786-04B5-4D20-AE2F-A3DB997E0DF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DB25786-04B5-4D20-AE2F-A3DB997E0DF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DB25786-04B5-4D20-AE2F-A3DB997E0DF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DB25786-04B5-4D20-AE2F-A3DB997E0DF0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D7BB7D20-838B-4464-999D-3F536B7F2291} + EndGlobalSection +EndGlobal diff --git a/MainForm/ImageCompressorForm.Designer.cs b/MainForm/ImageCompressorForm.Designer.cs new file mode 100644 index 0000000..1a0dc09 --- /dev/null +++ b/MainForm/ImageCompressorForm.Designer.cs @@ -0,0 +1,492 @@ +namespace ImageCompressor +{ + partial class ImageCompressorForm + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows 窗体设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImageCompressorForm)); + this.PathTextBox = new System.Windows.Forms.TextBox(); + this.PreviousPictureBox = new System.Windows.Forms.PictureBox(); + this.PostPictureBox = new System.Windows.Forms.PictureBox(); + this.SizeEstimateLabel = new System.Windows.Forms.Label(); + this.CompressLevelLabel = new System.Windows.Forms.Label(); + this.SkipFileSizeLabel = new System.Windows.Forms.Label(); + this.SkipFileSizeTextBox = new System.Windows.Forms.TextBox(); + this.SubFolderCheckBox = new System.Windows.Forms.CheckBox(); + this.RunButton = new System.Windows.Forms.Button(); + this.InfoLabel = new System.Windows.Forms.Label(); + this.CloseButton = new System.Windows.Forms.Button(); + this.CompressLevelDownButton = new System.Windows.Forms.Button(); + this.CompressLevelUpButton = new System.Windows.Forms.Button(); + this.TitleTextLabel = new System.Windows.Forms.Label(); + this.MaxmizeButton = new System.Windows.Forms.Button(); + this.MinimizeButton = new System.Windows.Forms.Button(); + this.ProgressPanel = new System.Windows.Forms.Panel(); + this.CompressLevelTextLabel = new System.Windows.Forms.Label(); + this.ZoomInPictureBox = new System.Windows.Forms.PictureBox(); + this.MaxWidthPixelTextBox = new System.Windows.Forms.TextBox(); + this.MaxPixelLimitLabel = new System.Windows.Forms.Label(); + this.MaxPixelMultiplyLabel = new System.Windows.Forms.Label(); + this.MaxHeightPixelTextBox = new System.Windows.Forms.TextBox(); + this.KeepOriginalCheckBox = new System.Windows.Forms.CheckBox(); + ((System.ComponentModel.ISupportInitialize)(this.PreviousPictureBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.PostPictureBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ZoomInPictureBox)).BeginInit(); + this.SuspendLayout(); + // + // PathTextBox + // + this.PathTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.PathTextBox.BackColor = System.Drawing.SystemColors.ControlText; + this.PathTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.PathTextBox.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.PathTextBox.ForeColor = System.Drawing.Color.White; + this.PathTextBox.Location = new System.Drawing.Point(12, 55); + this.PathTextBox.Multiline = true; + this.PathTextBox.Name = "PathTextBox"; + this.PathTextBox.ReadOnly = true; + this.PathTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.PathTextBox.Size = new System.Drawing.Size(962, 121); + this.PathTextBox.TabIndex = 1; + this.PathTextBox.Text = "Drag and Drop Folders Here To Start"; + this.PathTextBox.WordWrap = false; + // + // PreviousPictureBox + // + this.PreviousPictureBox.BackColor = System.Drawing.SystemColors.ControlText; + this.PreviousPictureBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.PreviousPictureBox.Location = new System.Drawing.Point(12, 189); + this.PreviousPictureBox.Name = "PreviousPictureBox"; + this.PreviousPictureBox.Size = new System.Drawing.Size(475, 423); + this.PreviousPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.PreviousPictureBox.TabIndex = 2; + this.PreviousPictureBox.TabStop = false; + this.PreviousPictureBox.Click += new System.EventHandler(this.PreviousPictureBox_Click); + // + // PostPictureBox + // + this.PostPictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.PostPictureBox.BackColor = System.Drawing.SystemColors.ControlText; + this.PostPictureBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.PostPictureBox.Location = new System.Drawing.Point(501, 189); + this.PostPictureBox.Name = "PostPictureBox"; + this.PostPictureBox.Size = new System.Drawing.Size(473, 423); + this.PostPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.PostPictureBox.TabIndex = 3; + this.PostPictureBox.TabStop = false; + this.PostPictureBox.Click += new System.EventHandler(this.AfterPictureBox_Click); + // + // SizeEstimateLabel + // + this.SizeEstimateLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SizeEstimateLabel.AutoSize = true; + this.SizeEstimateLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.SizeEstimateLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.SizeEstimateLabel.Location = new System.Drawing.Point(498, 625); + this.SizeEstimateLabel.Name = "SizeEstimateLabel"; + this.SizeEstimateLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.SizeEstimateLabel.Size = new System.Drawing.Size(188, 19); + this.SizeEstimateLabel.TabIndex = 4; + this.SizeEstimateLabel.Text = "New File Size Estimate: 0KB / 0KB"; + // + // CompressLevelLabel + // + this.CompressLevelLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.CompressLevelLabel.AutoSize = true; + this.CompressLevelLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.CompressLevelLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.CompressLevelLabel.Location = new System.Drawing.Point(12, 629); + this.CompressLevelLabel.Name = "CompressLevelLabel"; + this.CompressLevelLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.CompressLevelLabel.Size = new System.Drawing.Size(124, 19); + this.CompressLevelLabel.TabIndex = 4; + this.CompressLevelLabel.Text = "Compress Level (1-10)"; + // + // SkipFileSizeLabel + // + this.SkipFileSizeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.SkipFileSizeLabel.AutoSize = true; + this.SkipFileSizeLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.SkipFileSizeLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.SkipFileSizeLabel.Location = new System.Drawing.Point(12, 675); + this.SkipFileSizeLabel.Name = "SkipFileSizeLabel"; + this.SkipFileSizeLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.SkipFileSizeLabel.Size = new System.Drawing.Size(103, 19); + this.SkipFileSizeLabel.TabIndex = 4; + this.SkipFileSizeLabel.Text = "Skip File Size (KB)"; + // + // SkipFileSizeTextBox + // + this.SkipFileSizeTextBox.AcceptsReturn = true; + this.SkipFileSizeTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.SkipFileSizeTextBox.BackColor = System.Drawing.Color.Black; + this.SkipFileSizeTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.SkipFileSizeTextBox.Font = new System.Drawing.Font("Bahnschrift Condensed", 15F); + this.SkipFileSizeTextBox.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.SkipFileSizeTextBox.Location = new System.Drawing.Point(142, 667); + this.SkipFileSizeTextBox.Name = "SkipFileSizeTextBox"; + this.SkipFileSizeTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.SkipFileSizeTextBox.Size = new System.Drawing.Size(101, 32); + this.SkipFileSizeTextBox.TabIndex = 5; + this.SkipFileSizeTextBox.Text = "900"; + this.SkipFileSizeTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + // + // SubFolderCheckBox + // + this.SubFolderCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.SubFolderCheckBox.AutoSize = true; + this.SubFolderCheckBox.Checked = true; + this.SubFolderCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.SubFolderCheckBox.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.SubFolderCheckBox.ForeColor = System.Drawing.SystemColors.HighlightText; + this.SubFolderCheckBox.Location = new System.Drawing.Point(16, 717); + this.SubFolderCheckBox.Name = "SubFolderCheckBox"; + this.SubFolderCheckBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.SubFolderCheckBox.Size = new System.Drawing.Size(129, 23); + this.SubFolderCheckBox.TabIndex = 6; + this.SubFolderCheckBox.Text = "Include SubFolders"; + this.SubFolderCheckBox.UseVisualStyleBackColor = true; + this.SubFolderCheckBox.CheckedChanged += new System.EventHandler(this.SubFolderCheckBox_CheckedChanged); + // + // RunButton + // + this.RunButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.RunButton.BackColor = System.Drawing.Color.Black; + this.RunButton.CausesValidation = false; + this.RunButton.FlatAppearance.BorderColor = System.Drawing.Color.Gray; + this.RunButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.RunButton.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F, System.Drawing.FontStyle.Bold); + this.RunButton.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.RunButton.Location = new System.Drawing.Point(502, 702); + this.RunButton.Name = "RunButton"; + this.RunButton.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.RunButton.Size = new System.Drawing.Size(474, 33); + this.RunButton.TabIndex = 0; + this.RunButton.Text = "Waiting for image folders"; + this.RunButton.UseVisualStyleBackColor = false; + this.RunButton.Click += new System.EventHandler(this.RunButton_Click); + // + // InfoLabel + // + this.InfoLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.InfoLabel.AutoSize = true; + this.InfoLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.InfoLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.InfoLabel.Location = new System.Drawing.Point(498, 660); + this.InfoLabel.Name = "InfoLabel"; + this.InfoLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.InfoLabel.Size = new System.Drawing.Size(35, 19); + this.InfoLabel.TabIndex = 4; + this.InfoLabel.Text = "Info :"; + // + // CloseButton + // + this.CloseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.CloseButton.BackColor = System.Drawing.Color.Black; + this.CloseButton.FlatAppearance.BorderColor = System.Drawing.Color.Gray; + this.CloseButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.CloseButton.Font = new System.Drawing.Font("Arial Rounded MT Bold", 16F); + this.CloseButton.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.CloseButton.Location = new System.Drawing.Point(905, 10); + this.CloseButton.Name = "CloseButton"; + this.CloseButton.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.CloseButton.Size = new System.Drawing.Size(70, 35); + this.CloseButton.TabIndex = 10; + this.CloseButton.Text = "×"; + this.CloseButton.UseVisualStyleBackColor = false; + this.CloseButton.Click += new System.EventHandler(this.CloseButton_Click); + // + // CompressLevelDownButton + // + this.CompressLevelDownButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.CompressLevelDownButton.BackColor = System.Drawing.Color.Black; + this.CompressLevelDownButton.FlatAppearance.BorderColor = System.Drawing.Color.Gray; + this.CompressLevelDownButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.CompressLevelDownButton.Font = new System.Drawing.Font("Arial", 11F, System.Drawing.FontStyle.Bold); + this.CompressLevelDownButton.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.CompressLevelDownButton.Location = new System.Drawing.Point(142, 623); + this.CompressLevelDownButton.Margin = new System.Windows.Forms.Padding(0); + this.CompressLevelDownButton.Name = "CompressLevelDownButton"; + this.CompressLevelDownButton.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.CompressLevelDownButton.Size = new System.Drawing.Size(28, 35); + this.CompressLevelDownButton.TabIndex = 11; + this.CompressLevelDownButton.Text = "-"; + this.CompressLevelDownButton.UseVisualStyleBackColor = false; + this.CompressLevelDownButton.Click += new System.EventHandler(this.CompressLevelDownButton_Click); + // + // CompressLevelUpButton + // + this.CompressLevelUpButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.CompressLevelUpButton.BackColor = System.Drawing.Color.Black; + this.CompressLevelUpButton.FlatAppearance.BorderColor = System.Drawing.Color.Gray; + this.CompressLevelUpButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.CompressLevelUpButton.Font = new System.Drawing.Font("Arial", 11F, System.Drawing.FontStyle.Bold); + this.CompressLevelUpButton.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.CompressLevelUpButton.Location = new System.Drawing.Point(215, 623); + this.CompressLevelUpButton.Name = "CompressLevelUpButton"; + this.CompressLevelUpButton.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.CompressLevelUpButton.Size = new System.Drawing.Size(28, 35); + this.CompressLevelUpButton.TabIndex = 12; + this.CompressLevelUpButton.Text = "+"; + this.CompressLevelUpButton.UseVisualStyleBackColor = false; + this.CompressLevelUpButton.Click += new System.EventHandler(this.CompressLevelUpButton_Click); + // + // TitleTextLabel + // + this.TitleTextLabel.AutoSize = true; + this.TitleTextLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.TitleTextLabel.ForeColor = System.Drawing.SystemColors.ControlDark; + this.TitleTextLabel.Location = new System.Drawing.Point(13, 18); + this.TitleTextLabel.Name = "TitleTextLabel"; + this.TitleTextLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.TitleTextLabel.Size = new System.Drawing.Size(201, 19); + this.TitleTextLabel.TabIndex = 14; + this.TitleTextLabel.Text = "Image Compressor 2023.03.25 by WR"; + // + // MaxmizeButton + // + this.MaxmizeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.MaxmizeButton.BackColor = System.Drawing.Color.Black; + this.MaxmizeButton.FlatAppearance.BorderColor = System.Drawing.Color.Gray; + this.MaxmizeButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.MaxmizeButton.Font = new System.Drawing.Font("Arial Rounded MT Bold", 16F); + this.MaxmizeButton.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.MaxmizeButton.Location = new System.Drawing.Point(825, 10); + this.MaxmizeButton.Name = "MaxmizeButton"; + this.MaxmizeButton.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.MaxmizeButton.Size = new System.Drawing.Size(70, 35); + this.MaxmizeButton.TabIndex = 15; + this.MaxmizeButton.Text = "◻"; + this.MaxmizeButton.UseVisualStyleBackColor = false; + this.MaxmizeButton.Click += new System.EventHandler(this.MaxmizeButton_Click); + // + // MinimizeButton + // + this.MinimizeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.MinimizeButton.BackColor = System.Drawing.Color.Black; + this.MinimizeButton.FlatAppearance.BorderColor = System.Drawing.Color.Gray; + this.MinimizeButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.MinimizeButton.Font = new System.Drawing.Font("Arial Rounded MT Bold", 16F); + this.MinimizeButton.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.MinimizeButton.Location = new System.Drawing.Point(745, 10); + this.MinimizeButton.Name = "MinimizeButton"; + this.MinimizeButton.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.MinimizeButton.Size = new System.Drawing.Size(70, 35); + this.MinimizeButton.TabIndex = 16; + this.MinimizeButton.Text = "-"; + this.MinimizeButton.UseVisualStyleBackColor = false; + this.MinimizeButton.Click += new System.EventHandler(this.MinimizeButton_Click); + // + // ProgressPanel + // + this.ProgressPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.ProgressPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.ProgressPanel.ForeColor = System.Drawing.SystemColors.ControlDark; + this.ProgressPanel.Location = new System.Drawing.Point(502, 746); + this.ProgressPanel.Name = "ProgressPanel"; + this.ProgressPanel.Size = new System.Drawing.Size(474, 29); + this.ProgressPanel.TabIndex = 17; + // + // CompressLevelTextLabel + // + this.CompressLevelTextLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.CompressLevelTextLabel.AutoSize = true; + this.CompressLevelTextLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 15F); + this.CompressLevelTextLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.CompressLevelTextLabel.Location = new System.Drawing.Point(185, 629); + this.CompressLevelTextLabel.Name = "CompressLevelTextLabel"; + this.CompressLevelTextLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.CompressLevelTextLabel.Size = new System.Drawing.Size(18, 24); + this.CompressLevelTextLabel.TabIndex = 4; + this.CompressLevelTextLabel.Text = "8"; + // + // ZoomInPictureBox + // + this.ZoomInPictureBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.ZoomInPictureBox.Location = new System.Drawing.Point(298, 313); + this.ZoomInPictureBox.Name = "ZoomInPictureBox"; + this.ZoomInPictureBox.Size = new System.Drawing.Size(300, 150); + this.ZoomInPictureBox.TabIndex = 18; + this.ZoomInPictureBox.TabStop = false; + this.ZoomInPictureBox.Visible = false; + // + // MaxWidthPixelTextBox + // + this.MaxWidthPixelTextBox.AcceptsReturn = true; + this.MaxWidthPixelTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.MaxWidthPixelTextBox.BackColor = System.Drawing.Color.Black; + this.MaxWidthPixelTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.MaxWidthPixelTextBox.Font = new System.Drawing.Font("Bahnschrift Condensed", 15F); + this.MaxWidthPixelTextBox.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.MaxWidthPixelTextBox.Location = new System.Drawing.Point(355, 623); + this.MaxWidthPixelTextBox.Name = "MaxWidthPixelTextBox"; + this.MaxWidthPixelTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.MaxWidthPixelTextBox.Size = new System.Drawing.Size(52, 32); + this.MaxWidthPixelTextBox.TabIndex = 19; + this.MaxWidthPixelTextBox.Text = "1920"; + this.MaxWidthPixelTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + // + // MaxPixelLimitLabel + // + this.MaxPixelLimitLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.MaxPixelLimitLabel.AutoSize = true; + this.MaxPixelLimitLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.MaxPixelLimitLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.MaxPixelLimitLabel.Location = new System.Drawing.Point(264, 629); + this.MaxPixelLimitLabel.Name = "MaxPixelLimitLabel"; + this.MaxPixelLimitLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.MaxPixelLimitLabel.Size = new System.Drawing.Size(89, 19); + this.MaxPixelLimitLabel.TabIndex = 4; + this.MaxPixelLimitLabel.Text = "Max Pixel Limit"; + // + // MaxPixelMultiplyLabel + // + this.MaxPixelMultiplyLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.MaxPixelMultiplyLabel.AutoSize = true; + this.MaxPixelMultiplyLabel.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.MaxPixelMultiplyLabel.ForeColor = System.Drawing.SystemColors.HighlightText; + this.MaxPixelMultiplyLabel.Location = new System.Drawing.Point(409, 629); + this.MaxPixelMultiplyLabel.Name = "MaxPixelMultiplyLabel"; + this.MaxPixelMultiplyLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.MaxPixelMultiplyLabel.Size = new System.Drawing.Size(15, 19); + this.MaxPixelMultiplyLabel.TabIndex = 4; + this.MaxPixelMultiplyLabel.Text = "×"; + // + // MaxHeightPixelTextBox + // + this.MaxHeightPixelTextBox.AcceptsReturn = true; + this.MaxHeightPixelTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.MaxHeightPixelTextBox.BackColor = System.Drawing.Color.Black; + this.MaxHeightPixelTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.MaxHeightPixelTextBox.Font = new System.Drawing.Font("Bahnschrift Condensed", 15F); + this.MaxHeightPixelTextBox.ForeColor = System.Drawing.SystemColors.ButtonFace; + this.MaxHeightPixelTextBox.Location = new System.Drawing.Point(425, 623); + this.MaxHeightPixelTextBox.Name = "MaxHeightPixelTextBox"; + this.MaxHeightPixelTextBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.MaxHeightPixelTextBox.Size = new System.Drawing.Size(52, 32); + this.MaxHeightPixelTextBox.TabIndex = 19; + this.MaxHeightPixelTextBox.Text = "1080"; + this.MaxHeightPixelTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + // + // KeepOriginalCheckBox + // + this.KeepOriginalCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.KeepOriginalCheckBox.AutoSize = true; + this.KeepOriginalCheckBox.Checked = true; + this.KeepOriginalCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.KeepOriginalCheckBox.Font = new System.Drawing.Font("Bahnschrift Condensed", 12F); + this.KeepOriginalCheckBox.ForeColor = System.Drawing.SystemColors.HighlightText; + this.KeepOriginalCheckBox.Location = new System.Drawing.Point(16, 752); + this.KeepOriginalCheckBox.Name = "KeepOriginalCheckBox"; + this.KeepOriginalCheckBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.KeepOriginalCheckBox.Size = new System.Drawing.Size(233, 23); + this.KeepOriginalCheckBox.TabIndex = 6; + this.KeepOriginalCheckBox.Text = "Keep Original (Compress in new folder)"; + this.KeepOriginalCheckBox.UseVisualStyleBackColor = true; + this.KeepOriginalCheckBox.CheckedChanged += new System.EventHandler(this.KeepOriginalCheckBox_CheckedChanged); + // + // ImageCompressorForm + // + this.AllowDrop = true; + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); + this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; + this.ClientSize = new System.Drawing.Size(990, 800); + this.ControlBox = false; + this.Controls.Add(this.ZoomInPictureBox); + this.Controls.Add(this.MaxHeightPixelTextBox); + this.Controls.Add(this.MaxWidthPixelTextBox); + this.Controls.Add(this.MinimizeButton); + this.Controls.Add(this.MaxmizeButton); + this.Controls.Add(this.TitleTextLabel); + this.Controls.Add(this.CompressLevelUpButton); + this.Controls.Add(this.CompressLevelDownButton); + this.Controls.Add(this.CloseButton); + this.Controls.Add(this.SubFolderCheckBox); + this.Controls.Add(this.KeepOriginalCheckBox); + this.Controls.Add(this.SkipFileSizeTextBox); + this.Controls.Add(this.SkipFileSizeLabel); + this.Controls.Add(this.MaxPixelMultiplyLabel); + this.Controls.Add(this.CompressLevelTextLabel); + this.Controls.Add(this.MaxPixelLimitLabel); + this.Controls.Add(this.CompressLevelLabel); + this.Controls.Add(this.InfoLabel); + this.Controls.Add(this.SizeEstimateLabel); + this.Controls.Add(this.PostPictureBox); + this.Controls.Add(this.PreviousPictureBox); + this.Controls.Add(this.PathTextBox); + this.Controls.Add(this.RunButton); + this.Controls.Add(this.ProgressPanel); + this.Font = new System.Drawing.Font("等线", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(990, 800); + this.Name = "ImageCompressorForm"; + this.RightToLeft = System.Windows.Forms.RightToLeft.Yes; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Image Compressor 1.0 by WR "; + ((System.ComponentModel.ISupportInitialize)(this.PreviousPictureBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.PostPictureBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ZoomInPictureBox)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.TextBox PathTextBox; + private System.Windows.Forms.PictureBox PreviousPictureBox; + private System.Windows.Forms.PictureBox PostPictureBox; + private System.Windows.Forms.Label SizeEstimateLabel; + private System.Windows.Forms.Label CompressLevelLabel; + private System.Windows.Forms.Label SkipFileSizeLabel; + private System.Windows.Forms.TextBox SkipFileSizeTextBox; + private System.Windows.Forms.CheckBox SubFolderCheckBox; + private System.Windows.Forms.Button RunButton; + private System.Windows.Forms.Label InfoLabel; + private System.Windows.Forms.Button CloseButton; + private System.Windows.Forms.Button CompressLevelDownButton; + private System.Windows.Forms.Button CompressLevelUpButton; + private System.Windows.Forms.Label TitleTextLabel; + private System.Windows.Forms.Button MaxmizeButton; + private System.Windows.Forms.Button MinimizeButton; + private System.Windows.Forms.Panel ProgressPanel; + private System.Windows.Forms.Label CompressLevelTextLabel; + private System.Windows.Forms.PictureBox ZoomInPictureBox; + private System.Windows.Forms.TextBox MaxWidthPixelTextBox; + private System.Windows.Forms.Label MaxPixelLimitLabel; + private System.Windows.Forms.Label MaxPixelMultiplyLabel; + private System.Windows.Forms.TextBox MaxHeightPixelTextBox; + private System.Windows.Forms.CheckBox KeepOriginalCheckBox; + } +} + diff --git a/MainForm/ImageCompressorForm.cs b/MainForm/ImageCompressorForm.cs new file mode 100644 index 0000000..e3f3063 --- /dev/null +++ b/MainForm/ImageCompressorForm.cs @@ -0,0 +1,867 @@ +//#define InitialPath +using System; +using System.Collections.Generic; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Threading; +using System.Windows.Forms; +using System.IO; +using System.Diagnostics; + +namespace ImageCompressor +{ + public partial class ImageCompressorForm : Form + { + public ImageCompressorForm() + { + manager = new FilesManager(this); + } + /// + /// 后台读取图片,并且负责运行 + /// + FilesManager manager; + /// + /// 替换原有的Panel作为进度条 + /// + NewProgressPanel newProgressPanel; + protected override void OnLoad(EventArgs e) + { + this.newProgressPanel = new NewProgressPanel(new Font("Bahnschrift Condensed", 12F), SystemColors.ControlDark); + + InitializeComponent(); + SetLanguage(); + + newProgressPanel.Clone(ProgressPanel); + + ProgressPanel.Visible = false; + this.Controls.Add(this.newProgressPanel); + + OnSizeChanged(null); + + ZoomInPictureBox.Visible = false; + + PostPictureBox.MouseMove += OnPictureMouseMovePost; + PreviousPictureBox.MouseMove += OnPictureMouseMovePrevious; + ZoomInPictureBox.MouseMove += OnZoomMouseMove; + PostPictureBox.MouseWheel += OnZoomMouseWheelPost; + PreviousPictureBox.MouseWheel += OnZoomMouseWheelPre; + + MaxWidthPixelTextBox.Leave += MaxWidthPixelTextBox_FocusLeave; + MaxHeightPixelTextBox.Leave += MaxHeightPixelTextBox_FocusLeave; + SkipFileSizeTextBox.Leave += SkipFileSizeTextBox_FocusLeave; + MaxWidthPixelTextBox.KeyDown += TextBox_KeyDown; + MaxHeightPixelTextBox.KeyDown += TextBox_KeyDown; + SkipFileSizeTextBox.KeyDown += TextBox_KeyDown; + + ApplySettingsFromForm(); + UpdateDisplayInfo(); + UpdateRunButtonText(); + SetProgressValue(0); + + PathTextBox.DragDrop += Box_DragDrop; + PathTextBox.DragEnter += Box_DragEnter; + + this.DragDrop += Box_DragDrop; + this.DragEnter += Box_DragEnter; + + + +#if InitialPath && DEBUG + + //string path = @"C:\Users\wucl12\Downloads\Test\Italy"; + string path = @"D:\Users\MandO\Downloads\重庆照片"; + dragDropFolderPaths.Add(path); + UpdateDropFilePaths(); + UpdateRunButtonText(); +#endif + } + + /// + /// 启动时设置语言 + /// + void SetLanguage() + { + if(!Localize.IsEN) + { + + this.PathTextBox.Font = new System.Drawing.Font("等线", 12F); + this.SizeEstimateLabel.Font = new Font("等线", 12F); + this.KeepOriginalCheckBox.Font = new Font("等线", 12F); + this.MaxPixelMultiplyLabel.Font = new Font("等线", 12F); + this.MaxPixelLimitLabel.Font = new Font("等线", 12F); + //this.CompressLevelTextLabel.Font = new Font("等线", 15F); + this.TitleTextLabel.Font = new Font("等线", 12F); + this.InfoLabel.Font = new Font("等线", 12F); + this.RunButton.Font = new Font("等线", 12F, FontStyle.Bold); + this.SubFolderCheckBox.Font = new Font("等线", 12F); + this.SkipFileSizeLabel.Font = new Font("等线", 12F); + this.CompressLevelLabel.Font = new Font("等线", 12F); + } + + this.CompressLevelLabel.Text = Localize.Get("Compress Level (1-10)"); + this.SkipFileSizeLabel.Text = Localize.Get("Skip File Size (KB)"); + + this.KeepOriginalCheckBox.Text = Localize.Get("Keep Original (Compress in new folder)"); + this.SubFolderCheckBox.Text = Localize.Get("Include SubFolders"); + + this.MaxPixelLimitLabel.Text = Localize.Get("Max Pixel Limit"); + this.TitleTextLabel.Text = Localize.Get("Image Compressor") +" 2026.02.02 by WR"; + + this.Text = Localize.Get("Image Compressor"); + } + + + #region Resize + /// + /// 点击到左右边界 + /// + ClickResize clickResizeHorizen = ClickResize.None; + /// + /// 点击到上下边界 + /// + ClickResize clickResizeVertical = ClickResize.None; + /// + /// 记录点击边界值 + /// + enum ClickResize + { + None, + Left, + Right, + Top, + Buttom, + } + /// + /// 鼠标点击位置是移动还是缩放 + /// + enum SizeChangeState + { + None, + ChangeSize, + Move, + } + /// + /// 记录移动或者缩放状态 + /// + SizeChangeState sizeChangeState = SizeChangeState.None; + + /// + /// 绘制尺寸时的间距控制常数 + /// + const int sizePad = 15; + + /// + /// 减少卡顿 + /// + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.ExStyle |= 0x02000000; + return cp; + } + } + + /// + /// 动态缩放 + /// + /// + protected override void OnSizeChanged(EventArgs e) + { + SuspendLayout(); + this.CloseButton.Left = Width - sizePad - CloseButton.Width; + this.MaxmizeButton.Left = CloseButton.Left - sizePad - MaxmizeButton.Width; + this.MinimizeButton.Left = MaxmizeButton.Left - sizePad - MinimizeButton.Width; + + this.PathTextBox.Width = Width - sizePad - PathTextBox.Left; + + this.RunButton.Left = Width / 2 + sizePad / 2; + this.newProgressPanel.Top = Height - sizePad - newProgressPanel.Height; + this.newProgressPanel.Left = RunButton.Left; + this.newProgressPanel.Width = Width - sizePad - RunButton.Left; + this.ProgressPanel.Location = newProgressPanel.Location; + this.ProgressPanel.Width = newProgressPanel.Width; + + + this.RunButton.Top = this.ProgressPanel.Top - sizePad - RunButton.Height; + this.RunButton.Width = Width - sizePad - RunButton.Left; + + this.InfoLabel.Left = RunButton.Left; + this.InfoLabel.Top = this.RunButton.Top - sizePad - InfoLabel.Height; + + this.SizeEstimateLabel.Left = this.RunButton.Left; + this.SizeEstimateLabel.Top = this.InfoLabel.Top - sizePad - this.SizeEstimateLabel.Height; + + + this.PostPictureBox.Left = RunButton.Left; + this.PostPictureBox.Width = this.RunButton.Width; + this.PostPictureBox.Height = this.SizeEstimateLabel.Top - sizePad - this.PostPictureBox.Top; + this.PreviousPictureBox.Height = this.PostPictureBox.Height; + this.PreviousPictureBox.Width = this.PostPictureBox.Left- sizePad - this.PreviousPictureBox.Left; + + this.KeepOriginalCheckBox.Top = ProgressPanel.Top; + this.SubFolderCheckBox.Top = this.KeepOriginalCheckBox.Top - 10 - this.SubFolderCheckBox.Height; + + this.SkipFileSizeTextBox.Top = this.RunButton.Top - this.SkipFileSizeTextBox.Height; + this.CompressLevelDownButton.Top = this.PostPictureBox.Bottom + sizePad-5; + this.CompressLevelUpButton.Top = this.CompressLevelDownButton.Top; + this.CompressLevelTextLabel.Top = this.CompressLevelUpButton.Top + 7; + this.CompressLevelLabel.Top = this.CompressLevelTextLabel.Bottom - this.CompressLevelLabel.Height-3; + this.SkipFileSizeTextBox.Top = this.CompressLevelUpButton.Bottom + sizePad-5; + this.SkipFileSizeLabel.Top = this.SkipFileSizeTextBox.Top+5; + + this.MaxPixelLimitLabel.Top = this.CompressLevelLabel.Top; + this.MaxPixelMultiplyLabel.Top = this.MaxPixelLimitLabel.Top; + this.MaxWidthPixelTextBox.Top = this.CompressLevelUpButton.Top + 3; + this.MaxHeightPixelTextBox.Top = this.MaxWidthPixelTextBox.Top; + + + ResumeLayout(false); + PerformLayout(); + } + /// + /// 点击四边拖拽更改窗口大小的距离距离范围 + /// + const float ChangeSizeClickTolerance = 10; + bool ClickNear(float n1, float n2) + { + return Math.Abs(n1 - n2) < ChangeSizeClickTolerance; + } + Point moveLocation; + protected override void OnMouseDown(MouseEventArgs e) + { + + this.RunButton.Focus(); + sizeChangeState = SizeChangeState.None; + if(e.Y > ChangeSizeClickTolerance && e.Y < MinimizeButton.Bottom && e.X < MinimizeButton.Left) + { + sizeChangeState = SizeChangeState.Move; + moveLocation = e.Location; + return; + } + if (ClickNear(0, e.X)) + { + clickResizeHorizen = ClickResize.Left; + sizeChangeState = SizeChangeState.ChangeSize; + } + else if (ClickNear(Bounds.Width, e.X)) + { + clickResizeHorizen = ClickResize.Right; + sizeChangeState = SizeChangeState.ChangeSize; + } + else clickResizeHorizen = ClickResize.None; + + if (ClickNear(0, e.Y)) + { + clickResizeVertical = ClickResize.Top; + sizeChangeState = SizeChangeState.ChangeSize; + } + else if (ClickNear(Bounds.Height, e.Y)) + { + clickResizeVertical = ClickResize.Buttom; + sizeChangeState = SizeChangeState.ChangeSize; + } + else clickResizeVertical = ClickResize.None; + if (sizeChangeState == SizeChangeState.None) + { + + base.OnMouseDown(e); + } + } + + protected override void OnMouseMove(MouseEventArgs e) + { + ZoomInPictureBox.Visible = false; + + if (sizeChangeState == SizeChangeState.Move) + { + if(this.WindowState != FormWindowState.Normal) + { + this.WindowState = FormWindowState.Normal; + moveLocation = new Point(Width / 2, 10); + } + else + { + Size size = Bounds.Size; + Point location = new Point(Bounds.X + e.X - moveLocation.X, Bounds.Y + e.Y - moveLocation.Y); + Bounds = new Rectangle(location, size); + } + } + else if(sizeChangeState == SizeChangeState.ChangeSize) + { + this.WindowState = FormWindowState.Normal; + Point location = Bounds.Location; + Size size = Bounds.Size; + bool changed = false; + if (clickResizeHorizen == ClickResize.Left) + { + location.X = Bounds.X + e.X; + size.Width = Bounds.Width - e.X; + changed = true; + } + else if (clickResizeHorizen == ClickResize.Right) + { + size.Width = e.X; + changed = true; + } + if (clickResizeVertical == ClickResize.Top) + { + location.Y = Bounds.Y + e.Y; + size.Height = Bounds.Height - e.Y; + changed = true; + } + else if (clickResizeVertical == ClickResize.Buttom) + { + size.Height = e.Y; + changed = true; + } + if (changed) + { + Bounds = new Rectangle(location, size); + } + } + else + { + bool left = ClickNear(0, e.X); + bool onHorizon = left || ClickNear(Bounds.Width, e.X); + bool top = ClickNear(0, e.Y); + bool onVertical = top || ClickNear(Bounds.Height, e.Y); + if (onHorizon && onVertical) + { + if ((left && top) || ((!left) || (!top))) this.Cursor = Cursors.SizeNWSE; + else this.Cursor = Cursors.SizeNESW; + } + else if (onHorizon) + { + this.Cursor = Cursors.SizeWE; + } + else if (onVertical) + { + this.Cursor = Cursors.SizeNS; + } + else this.Cursor = Cursors.Default; + + + + base.OnMouseMove(e); + } + } + + + protected override void OnMouseUp(MouseEventArgs e) + { + if (sizeChangeState!= SizeChangeState.None) + { + clickResizeHorizen = ClickResize.None; + clickResizeVertical= ClickResize.None; + sizeChangeState = SizeChangeState.None; + OnSizeChanged(null); + } + else base.OnMouseUp(e); + } + #endregion + + #region Drag&Drop + + + List dragDropFolderPaths = new List(); + + /// + /// 改变路径Box的消息 + /// + /// + private void ChangePathTextBoxMessage(string messageText) + { + if (!string.IsNullOrEmpty(messageText)) + { + PathTextBox.Text = messageText; + PathTextBox.ForeColor = Color.Gray; + } + } + + private void Box_DragEnter(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop)) + { + e.Effect = DragDropEffects.Link; + } + else e.Effect = DragDropEffects.None; + } + + private void Box_DragDrop(object sender, DragEventArgs e) + { + dragDropFolderPaths.Clear(); + Array files = (Array)e.Data.GetData(DataFormats.FileDrop); + foreach (object file in files) + { + FileInfo info = new FileInfo(file.ToString()); + if (info != null && info.Attributes.HasFlag(FileAttributes.Directory)) + { + dragDropFolderPaths.Add(info.FullName); + } + } + if (dragDropFolderPaths.Count == 0) + { + ChangePathTextBoxMessage(Localize.Get("No Folders Droped")); + return; + } + UpdateDropFilePaths(); + UpdateRunButtonText(); + + } + /// + /// 当Drop时更新路径 + /// + void UpdateDropFilePaths() + { + manager.ReadAllFilePaths(dragDropFolderPaths); + SetProgressValue(0); + if (manager.FilesCount == 0) + { + MessageBox.Show(Localize.Get("No Images (JPG, PNG, BMP) Found in Folder Droped"), Localize.Get("Warning")); + PostPictureBox.Image = null; + PreviousPictureBox.Image = null; + UpdateDisplayInfo(); + return; + } + UpdateDisplayInfo(); + DrawPreviewImg(); + PathTextBox.Lines = dragDropFolderPaths.ToArray(); + } + + + #endregion + + #region Zoom Picture + + /// + /// 滚轮缩放Zoom框 + /// + void OnZoomMouseWheelPre(object sender, MouseEventArgs e) + { + + if (e.Delta > 0) ZoomFactor.ZoomPercent *= 0.8; + else ZoomFactor.ZoomPercent *= 1.25; + DrawZoomInImage(PreviousPictureBox, e); + } + + void OnZoomMouseWheelPost(object sender, MouseEventArgs e) + { + + if (e.Delta > 0) ZoomFactor.ZoomPercent *= 0.8; + else ZoomFactor.ZoomPercent *= 1.25; + DrawZoomInImage(PostPictureBox, e); + } + + void OnZoomMouseMove(object sender, MouseEventArgs e) + { + ZoomInPictureBox.Location = new Point(ZoomInPictureBox.Left + e.X + 5, ZoomInPictureBox.Top - ZoomInPictureBox.Height + e.Y - 5); + } + + void OnPictureMouseMovePost(object sender, MouseEventArgs e) + { + DrawZoomInImage(PostPictureBox, e); + } + void OnPictureMouseMovePrevious(object sender, MouseEventArgs e) + { + DrawZoomInImage(PreviousPictureBox, e); + } + long CpuTime => DateTime.Now.Ticks / 10000; + long wait = 0; + void DrawZoomInImage(PictureBox box, MouseEventArgs e) + { + long t = CpuTime; + if (t - wait > 100) wait = t; + else return; + + if (PreviousPictureBox.Image == null || PostPictureBox.Image == null) + { + ZoomInPictureBox.Visible = false; + return; + } + ZoomInPictureBox.Visible = true; + ZoomInPictureBox.Location = new Point(box.Left + e.X + 5, box.Top + e.Y - 155); + ZoomFactor zoomFactor = new ZoomFactor(box.Bounds, e.Location, box.Image.Width, box.Image.Height); + ZoomInPictureBox.Image = GetZoomImage(zoomFactor); + } + + Image GetZoomImage(ZoomFactor factor) + { + + Bitmap newBit = new Bitmap(300, 150); + Rectangle destRectPre = new Rectangle(0, 0, 150, 150); + Rectangle destRectPost = new Rectangle(150, 0, 150, 150); + Graphics graphics = Graphics.FromImage(newBit); + graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; + graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; + graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half; + graphics.SetClip(destRectPre); + Rectangle srcRectPre = factor.GetSourceImage(PreviousPictureBox.Image); + + graphics.DrawImage(PreviousPictureBox.Image, destRectPre, srcRectPre, GraphicsUnit.Pixel); + graphics.SetClip(destRectPost); + Rectangle srcRectPost = factor.GetSourceImage(PostPictureBox.Image); + graphics.DrawImage(PostPictureBox.Image, destRectPost, srcRectPost, GraphicsUnit.Pixel); + graphics.Dispose(); + //Debug.WriteLine("srcRectPre = {0}, srcRectPost = {1}", srcRectPre, srcRectPost); + return newBit; + } + + #endregion + + #region UI Event + /// + /// 防止内部修改文字的时候,调用了TextChange事件 + /// + bool internalUpdating = false; + /// + /// 在Drop文件之后,初始化Info显示 + /// + private void UpdateDisplayInfo() + { + internalUpdating = true; + CompressLevelTextLabel.Text = manager.CompressLevel.ToString(); + MaxHeightPixelTextBox.Text = manager.MaxHeight.ToString(); + MaxWidthPixelTextBox.Text = manager.MaxWidth.ToString(); + + int fileCount = manager.FilesCount; + if (fileCount > 0) + { + newProgressPanel.Text = $"0/{fileCount}"; + newProgressPanel.Refresh(); + manager.SetSkipFileSizeInKB(SkipFileSizeTextBox.Text); + InfoLabel.Text = string.Format(Localize.Get("Find {0} Imgs , Sum Size = {1}"), manager.FilesCount, manager.GetFileSizeString(manager.PreAllImageSizeinKB)); + + } + else + { + ChangePathTextBoxMessage(Localize.Get("Drag and Drop Folders Here To Start")); + newProgressPanel.Text = $"0/0"; + newProgressPanel.Refresh(); + SizeEstimateLabel.Text = Localize.Get("New Image Size Estimate: ") + "0KB / 0KB"; + InfoLabel.Text = ""; + } + internalUpdating = false; + } + /// + /// 将Form的设置运用到后台程序中去 + /// + void ApplySettingsFromForm(bool updateWidthHeight = false) + { + manager.SetSkipFileSizeInKB(SkipFileSizeTextBox.Text); + manager.IncludeSubFolders = SubFolderCheckBox.Checked; + manager.KeepOriginal = KeepOriginalCheckBox.Checked; + manager.CompressLevel = int.Parse(CompressLevelTextLabel.Text); + if(updateWidthHeight) + { + manager.MaxWidth = int.Parse(MaxWidthPixelTextBox.Text); + manager.MaxHeight = int.Parse(MaxHeightPixelTextBox.Text); + } + } + + + + /// + /// 根据Seed绘制一个压缩后的预览图 + /// + void DrawPreviewImg() + { + if (!manager.GetRandomIndex(seed, out int index)) + { + //manager没有文件 + PreviousPictureBox.Image = null; + PostPictureBox.Image = null; + SizeEstimateLabel.Text = Localize.Get("New Image Size Estimate: ") + "0KB / 0KB"; + return; + } + + var rawImage = manager.GetImageAtIndex(index, out string rawFileSize); + PreviousPictureBox.Image = rawImage; + + + var newImage = manager.GetCompressedImgAtIndex(index, out string newFileSize, out bool skip, out string errorMessage); + + + if (skip) + { + PostPictureBox.Image = rawImage; + SizeEstimateLabel.Text = Localize.Get("New Image Size Estimate: ")+$"{rawFileSize} / {rawFileSize}" + Localize.Get("(Skiped)"); + } + else + { + if (newImage == null) + { + SizeEstimateLabel.Text = errorMessage; + PostPictureBox.Image = null; + return; + } + PostPictureBox.Image = newImage; + SizeEstimateLabel.Text = Localize.Get("New Image Size Estimate: ") +$"{newFileSize} / {rawFileSize}"; + + } + + ZoomFactor.ZoomPercent = 0.1; + } + + + void SkipFileSizeTextBox_FocusLeave(object sender, EventArgs e) + { + if (!int.TryParse(SkipFileSizeTextBox.Text, out _)) + SkipFileSizeTextBox.Text = manager.CurrentSkipFileSizeInKB.ToString(); + ApplySettingsFromForm(); + } + + private void SubFolderCheckBox_CheckedChanged(object sender, EventArgs e) + { + ApplySettingsFromForm(); + } + + private void KeepOriginalCheckBox_CheckedChanged(object sender, EventArgs e) + { + ApplySettingsFromForm(); + } + int seed = 0; + private void PreviousPictureBox_Click(object sender, EventArgs e) + { + if (manager.FilesCount > 0) + { + seed++; + DrawPreviewImg(); + } + else seed = 0; + } + + private void AfterPictureBox_Click(object sender, EventArgs e) + { + if (manager.FilesCount > 0) + { + seed--; + DrawPreviewImg(); + } + else seed = 0; + } + + + + private void CompressLevelTextChanged(bool manuallWidthHeight=false) + { + ApplySettingsFromForm(manuallWidthHeight); + DrawPreviewImg(); + UpdateDisplayInfo(); + + } + + void MaxHeightPixelTextBox_FocusLeave(object sender, EventArgs e) + { + if (internalUpdating) return; + if (!int.TryParse(MaxHeightPixelTextBox.Text, out _)) + { + MaxHeightPixelTextBox.Text = manager.MaxHeight.ToString(); + } + CompressLevelTextChanged(true); + } + + + private void TextBox_KeyDown(object sender, KeyEventArgs e) + { + if(e.KeyCode == Keys.Enter) + { + this.RunButton.Focus(); + } + } + + + + void MaxWidthPixelTextBox_FocusLeave(object sender, EventArgs e) + { + if (internalUpdating) return; + if (!int.TryParse(MaxWidthPixelTextBox.Text, out _)) + { + MaxWidthPixelTextBox.Text = manager.MaxWidth.ToString(); + } + CompressLevelTextChanged(true); + } + + + private void CompressLevelDownButton_Click(object sender, EventArgs e) + { + if (manager.CompressLevel > 1) + { + this.CompressLevelTextLabel.Text = (manager.CompressLevel - 1).ToString(); + CompressLevelTextChanged(); + } + } + + private void CompressLevelUpButton_Click(object sender, EventArgs e) + { + if (manager.CompressLevel < 10) + { + this.CompressLevelTextLabel.Text = (manager.CompressLevel + 1).ToString(); + CompressLevelTextChanged(); + } + } + + private void CloseButton_Click(object sender, EventArgs e) + { + if (manager.State != RunningState.Running) + { + + this.Close(); + return; + } + else + { + //发送结束指令 + manager.Stop(); + while (true) + { + if (manager.State != RunningState.Running) + { + //确保运行结束后才关闭 + this.Close(); + return; + } + else Thread.Sleep(100); + } + } + } + + private void MaxmizeButton_Click(object sender, EventArgs e) + { + if (this.WindowState != FormWindowState.Maximized) this.WindowState = FormWindowState.Maximized; + else this.WindowState = FormWindowState.Normal; + } + + private void MinimizeButton_Click(object sender, EventArgs e) + { + this.WindowState = FormWindowState.Minimized; + } + + #endregion + + #region Run + + /// + /// 用于内部线程调用 + /// + /// + public delegate void SetProgressValueDelegate(int percent); + + /// + /// 设定进度条 + /// + /// + public void SetProgressValue(int fileCount) + { + if (InvokeRequired) + { + //从其他线程调用,则在此返回Form线程调用 + SetProgressValueDelegate setProgressValueDelegate = new SetProgressValueDelegate(SetProgressValue); + this.Invoke(setProgressValueDelegate, fileCount); + } + else + { + newProgressPanel.ProgressValue =(double)fileCount / manager.FilesCount; + this.newProgressPanel.Text = $"{fileCount} / {manager.FilesCount}"; + newProgressPanel.Refresh(); + } + } + /// + /// 根据RunningState更新按钮文字 + /// + public void UpdateRunButtonText() + { + switch(manager.State) + { + case RunningState.NoFile: + RunButton.Text = Localize.Get("Waiting for image folders"); + break; + case RunningState.Running: + RunButton.Text = Localize.Get("Running, Click to Pause"); + break; + case RunningState.WaitStop: + RunButton.Text = Localize.Get("Running, Wait to Pause"); + break; + case RunningState.AllThreadStoped: + RunButton.Text = Localize.Get("Paused, Click to Continue"); + break; + case RunningState.Finished: + RunButton.Text = Localize.Get("Done"); + break; + case RunningState.Ready: + RunButton.Text = Localize.Get("Ready"); + break; + default: + RunButton.Text = Localize.Get("Run"); + break; + } + } + + /// + /// 点击Run之后运行 + /// + /// + /// + void RunButton_Click(object sender, EventArgs e) + { + + if (manager.State == RunningState.Running) + { + manager.Stop(); + } + else if (manager.State == RunningState.Ready || manager.State == RunningState.AllThreadStoped) + { + manager.RunAllCompress(); + } + else //(manager.State == RunningState.Finished) + { + RunButton.Text = Localize.Get("Done !!!"); + } + + UpdateRunButtonText(); + } + + /// + /// 运行完后显示结束报告 + /// + internal void ShowReport() + { + if (InvokeRequired) + { + //从其他线程调用,则在此返回Form线程调用 + Action ac = new Action(ShowReport); + this.Invoke(ac); + } + else + { + CountProgress progress = manager.progress; + + int minutes = (int)progress.stopwatch.Elapsed.TotalMinutes; + float seconds = progress.stopwatch.Elapsed.Seconds + (progress.stopwatch.Elapsed.Milliseconds / 100) * 0.1f; + + string watchString; + if (minutes > 0) watchString = string.Format(Localize.Get("{0}min {1}s"),minutes,seconds); + else watchString = string.Format(Localize.Get("{0}s"),seconds); + + UpdateRunButtonText(); + InfoLabel.Text = string.Format(Localize.Get("Compressed {0}, Skiped {1}, Failed {2}, "), progress.SuccessCount, progress.SkipCount, progress.FailedCount) + + string.Format(Localize.Get("Time {0}, Compressed Result {1} / {2}"),watchString,manager.GetFileSizeString(progress.PostSizeKBSum), manager.GetFileSizeString(progress.PreSizeKBSum)); + if (progress.FailedInfos.Count > 0) + { + MessageBox.Show(string.Join("\n", progress.FailedInfos), Localize.Get("Failed Images Info")); + } + } + } + + + #endregion + + + } + + +} diff --git a/MainForm/ImageCompressorForm.resx b/MainForm/ImageCompressorForm.resx new file mode 100644 index 0000000..c8e7444 --- /dev/null +++ b/MainForm/ImageCompressorForm.resx @@ -0,0 +1,941 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + QmCC + + + \ No newline at end of file diff --git a/MainForm/NewPictureBox.cs b/MainForm/NewPictureBox.cs new file mode 100644 index 0000000..afc9051 --- /dev/null +++ b/MainForm/NewPictureBox.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.Threading.Tasks; + +namespace ImageCompressor +{ + internal class NewPictureBox:PictureBox + { + /// + /// 以不抗锯齿方式绘制原图像素 + /// + /// + protected override void OnPaint(PaintEventArgs pe) + { + pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; + pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; + pe.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half; + base.OnPaint(pe); + } + } +} diff --git a/MainForm/NewProgressPanel.cs b/MainForm/NewProgressPanel.cs new file mode 100644 index 0000000..fdcd07a --- /dev/null +++ b/MainForm/NewProgressPanel.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.Threading.Tasks; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Diagnostics; +using System.IO; + +namespace ImageCompressor +{ + /// + /// ProgressBar类有点问题,重写一个简单的ProgressBar。 + /// + internal class NewProgressPanel:Panel + { + public Font TextFont; + public Brush TextForeColor; + public NewProgressPanel(Font textFont,Color textForeColor) + { + this.TextFont = textFont; + this.TextForeColor = new SolidBrush(textForeColor); + SetStyle(ControlStyles.ResizeRedraw, true); + this.foreBrush = new SolidBrush(Color.Gray); + } + /// + /// 克隆原来的Panel以实现设计的参数传递。 + /// + /// + public void Clone(Panel raw) + { + this.Size = raw.Size; + this.Bounds = raw.Bounds; + this.Anchor = raw.Anchor; + this.BorderStyle = raw.BorderStyle; + this.Name = raw.Name; + this.TabIndex = raw.TabIndex; + this.BackColor= raw.BackColor; + this.ForeColor = raw.ForeColor; + } + public double ProgressValue { get; set; } + + Brush foreBrush; + + /// + /// 绘制背景,快但是不能耗时太久,会闪 + /// + /// + protected override void OnPaintBackground(PaintEventArgs e) + { + e.Graphics.Clear(this.BackColor); + e.Graphics.FillRectangle(foreBrush, 0, 0, (int)((double)e.ClipRectangle.Width * ProgressValue), e.ClipRectangle.Height); + } + + /// + /// 绘制耗时较多的部分 + /// + /// + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e);//控件必须通过重写它从 Control 继承的 OnPaint 方法来提供呈现逻辑。 + + + //测量string应该较为耗时,放在此处绘制 + SizeF size = e.Graphics.MeasureString(Text, TextFont); + if (!string.IsNullOrEmpty(Text)) + e.Graphics.DrawString(Text, TextFont, TextForeColor, Width / 2 - size.Width / 2, Height - size.Height - 2); + } + } +} diff --git a/MainForm/Program.cs b/MainForm/Program.cs new file mode 100644 index 0000000..b73d992 --- /dev/null +++ b/MainForm/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ImageCompressor +{ + internal static class Program + { + /// + /// 应用程序的主入口点。 + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ImageCompressorForm()); + } + } +} diff --git a/MainForm/ZoomFactor.cs b/MainForm/ZoomFactor.cs new file mode 100644 index 0000000..c725bf3 --- /dev/null +++ b/MainForm/ZoomFactor.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ImageCompressor +{ + + + /// + /// 计算保存Zoom的数据 + /// + class ZoomFactor + { + static double zoomPercent = 0.1; + /// + /// [0.1 - 1] + /// 对原图像的最短一边的像素 * value,作为新方形图像的一边像素值。 + /// + public static double ZoomPercent + { + get + { + return zoomPercent; + } + set + { + if (value < 0.01) zoomPercent = 0.01; + else if (value > 1) zoomPercent = 1; + else zoomPercent = value; + } + } + /// + /// 新图像宽度的百分比 + /// + double widthLocationPercent; + /// + /// 新图像高度的百分比 + /// + double heightLocationPercent; + + + public Rectangle GetSourceImage(Image image) + { + int rawImageHeight= image.Height; + int rawImageWidth= image.Width; + int NewImageX = (int)(rawImageWidth * widthLocationPercent); + int NewImageY = (int)(rawImageHeight * heightLocationPercent); + int NewImageSize = (int)(Math.Min(rawImageHeight, rawImageWidth) * zoomPercent); + return new Rectangle(NewImageX - NewImageSize/2, NewImageY - NewImageSize / 2, NewImageSize, NewImageSize); + } + + /// + /// 获取当前缩放数据 + /// + /// + /// + /// + /// + public ZoomFactor(Rectangle previewBoxRectangle, Point location, int imageWidth, int imageHeight) + { + double X = location.X; // - previewBoxRectangle.X; + double Y = location.Y;// - previewBoxRectangle.Y; + if ((double)imageWidth / (double)imageHeight > (double)previewBoxRectangle.Width / (double)previewBoxRectangle.Height) + { + double truePreviewBoxHeight = previewBoxRectangle.Width * ((double)imageHeight / (double)imageWidth); + double offsetY = (previewBoxRectangle.Height - truePreviewBoxHeight) * 0.5; + //图像比预览框更宽 + widthLocationPercent = X / (double)previewBoxRectangle.Width; + heightLocationPercent = (Y - offsetY) / truePreviewBoxHeight; + } + else + { + double truePreivewBoxWidth = previewBoxRectangle.Height * ((double)imageWidth / (double)imageHeight); + double offsetX = (previewBoxRectangle.Width - truePreivewBoxWidth) * 0.5; + widthLocationPercent = (X - offsetX) / truePreivewBoxWidth; + heightLocationPercent = Y / (double)previewBoxRectangle.Height; + } + + //Debug.WriteLine("On Move percent w = {0}, h = {1}", widthLocationPercent, heightLocationPercent); + } + } + +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7e9da3f --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("ImageCompressor")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ImageCompressor")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("8db25786-04b5-4d20-ae2f-a3db997e0df0")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..f221e72 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace ImageCompressor.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ImageCompressor.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..8dde3b3 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace ImageCompressor.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/Icon.ico b/Resources/Icon.ico new file mode 100644 index 0000000..13232d7 Binary files /dev/null and b/Resources/Icon.ico differ diff --git a/Utils/Localize.cs b/Utils/Localize.cs new file mode 100644 index 0000000..0cd78f6 --- /dev/null +++ b/Utils/Localize.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ImageCompressor +{ + internal class Localize + { + + public static bool IsEN = CultureInfo.CurrentCulture.Name != "zh-CN"; + public static string Get(string name) + { + if (IsEN) return name; + else return CN[name]; + } + static readonly Dictionary CN = new Dictionary() + { + {"(Skiped)", "(跳过)"}, + {"_Compressed", "_压缩"}, + {"{0}min {1}s", "{0}分{1}秒"}, + {"{0}s", "{0}秒"}, + {"Cannot Create JPG Comporessor", "无法初始化JPG压缩设置"}, + {"Cannot create save folder. ", "无法创建新的储存文件夹"}, + {"Cannot delete original image: ", "无法删除原文件: " }, + {"Compress Level (1-10)", "压缩质量(1-10)"}, + {"Compressed {0}, Skiped {1}, Failed {2}, ", "已压缩 {0}, 跳过 {1},失败 {2}, "}, + {"Done !!!", "结束"}, + {"Done", "结束"}, + {"Drag and Drop Folders Here To Start", "将包含图像的文件夹拖入此处"}, + {"Estimate Fail", "无法估算"}, + {"Failed Images Info", "压缩图片失败"}, + {"Find {0} Imgs , Sum Size = {1}", "找到 {0} 张图片 , 总共大小 = {1}"}, + {"Image Compressor", "图像压缩工具"}, + {"Include SubFolders", "包含子文件夹"}, + {"Keep Original (Compress in new folder)", "保留源文件(储存到新文件夹)"}, + {"Max Pixel Limit", "像素限制"}, + {"Missing", "丢失"}, + {"New Image Size Estimate: ", "压缩前后大小估计: "}, + {"No Folders Droped", "没有找到拖入的文件夹"}, + {"No Images (JPG, PNG, BMP) Found in Folder Droped", "拖入文件夹内没有发现图像 (JPG, PNG, BMP)"}, + {"Paused, Click to Continue", "压缩暂停,单机继续"}, + {"Read file error at ", "打开图像失败:"}, + {"Read file missing", "读取文件丢失"}, + {"Read file not exist. ", "要读取的图像不存在"}, + {"Read image larger than 256 MB", "不能处理大于256MB的图片"}, + {"Ready", "单击开始压缩"}, + {"Run", "压缩中"}, + {"Running, Click to Pause", "压缩中,单击以暂停"}, + {"Running, Wait to Pause", "结束压缩中,马上暂停"}, + {"Save file error at ", "储存图像失败:"}, + {"Save file is occupied. ", "要保存的图像被其他程序占用"}, + {"Save file is ReadOnly. ", "要保存的图像是只读的"}, + {"Skip File Size (KB)", "跳过小图片(KB)"}, + {"Time {0}, Compressed Result {1} / {2}", "耗时 {0}, 压缩前后总大小 {1} / {2}"}, + {"Waiting for image folders", "等待拖入图片文件夹"}, + {"Warning", "警告"}, + }; + } +}