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

63
.gitattributes vendored Normal file
View File

@@ -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

363
.gitignore vendored Normal file
View File

@@ -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

6
App.config Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>

View File

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

424
Compressor/FilesManager.cs Normal file
View File

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

29
Compressor/ImgPaths.cs Normal file
View File

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

239
Compressor/ImgsProcess.cs Normal file
View File

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

366
Compressor/JPGCompressor.cs Normal file
View File

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

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

131
ImageCompressor.csproj Normal file
View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8DB25786-04B5-4D20-AE2F-A3DB997E0DF0}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>ImageCompressor</RootNamespace>
<AssemblyName>ImageCompressor</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\Icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Compressor\CountProgress.cs" />
<Compile Include="Compressor\ImgPaths.cs" />
<Compile Include="Compressor\FilesManager.cs" />
<Compile Include="MainForm\ImageCompressorForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainForm\ImageCompressorForm.Designer.cs">
<DependentUpon>ImageCompressorForm.cs</DependentUpon>
</Compile>
<Compile Include="Compressor\JPGCompressor.cs" />
<Compile Include="Compressor\ImgsProcess.cs" />
<Compile Include="Utils\Localize.cs" />
<Compile Include="MainForm\NewPictureBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="MainForm\NewProgressPanel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="MainForm\Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MainForm\ZoomFactor.cs" />
<EmbeddedResource Include="MainForm\ImageCompressorForm.resx">
<DependentUpon>ImageCompressorForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\Icon.ico" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.8 %28x86 和 x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

25
ImageCompressor.sln Normal file
View File

@@ -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

492
MainForm/ImageCompressorForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,492 @@
namespace ImageCompressor
{
partial class ImageCompressorForm
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
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;
}
}

View File

@@ -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);
}
/// <summary>
/// 后台读取图片,并且负责运行
/// </summary>
FilesManager manager;
/// <summary>
/// 替换原有的Panel作为进度条
/// </summary>
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
}
/// <summary>
/// 启动时设置语言
/// </summary>
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
/// <summary>
/// 点击到左右边界
/// </summary>
ClickResize clickResizeHorizen = ClickResize.None;
/// <summary>
/// 点击到上下边界
/// </summary>
ClickResize clickResizeVertical = ClickResize.None;
/// <summary>
/// 记录点击边界值
/// </summary>
enum ClickResize
{
None,
Left,
Right,
Top,
Buttom,
}
/// <summary>
/// 鼠标点击位置是移动还是缩放
/// </summary>
enum SizeChangeState
{
None,
ChangeSize,
Move,
}
/// <summary>
/// 记录移动或者缩放状态
/// </summary>
SizeChangeState sizeChangeState = SizeChangeState.None;
/// <summary>
/// 绘制尺寸时的间距控制常数
/// </summary>
const int sizePad = 15;
/// <summary>
/// 减少卡顿
/// </summary>
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
/// <summary>
/// 动态缩放
/// </summary>
/// <param name="e"></param>
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();
}
/// <summary>
/// 点击四边拖拽更改窗口大小的距离距离范围
/// </summary>
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<string> dragDropFolderPaths = new List<string>();
/// <summary>
/// 改变路径Box的消息
/// </summary>
/// <param name="messageText"></param>
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();
}
/// <summary>
/// 当Drop时更新路径
/// </summary>
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
/// <summary>
/// 滚轮缩放Zoom框
/// </summary>
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
/// <summary>
/// 防止内部修改文字的时候调用了TextChange事件
/// </summary>
bool internalUpdating = false;
/// <summary>
/// 在Drop文件之后初始化Info显示
/// </summary>
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;
}
/// <summary>
/// 将Form的设置运用到后台程序中去
/// </summary>
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);
}
}
/// <summary>
/// 根据Seed绘制一个压缩后的预览图
/// </summary>
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
/// <summary>
/// 用于内部线程调用
/// </summary>
/// <param name="percent"></param>
public delegate void SetProgressValueDelegate(int percent);
/// <summary>
/// 设定进度条
/// </summary>
/// <param name="value"></param>
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();
}
}
/// <summary>
/// 根据RunningState更新按钮文字
/// </summary>
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;
}
}
/// <summary>
/// 点击Run之后运行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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();
}
/// <summary>
/// 运行完后显示结束报告
/// </summary>
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
}
}

View File

@@ -0,0 +1,941 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
QmCC
</value>
</data>
</root>

23
MainForm/NewPictureBox.cs Normal file
View File

@@ -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
{
/// <summary>
/// 以不抗锯齿方式绘制原图像素
/// </summary>
/// <param name="pe"></param>
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);
}
}
}

View File

@@ -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
{
/// <summary>
/// ProgressBar类有点问题重写一个简单的ProgressBar。
/// </summary>
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);
}
/// <summary>
/// 克隆原来的Panel以实现设计的参数传递。
/// </summary>
/// <param name="raw"></param>
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;
/// <summary>
/// 绘制背景,快但是不能耗时太久,会闪
/// </summary>
/// <param name="e"></param>
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);
}
/// <summary>
/// 绘制耗时较多的部分
/// </summary>
/// <param name="e"></param>
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);
}
}
}

22
MainForm/Program.cs Normal file
View File

@@ -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
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ImageCompressorForm());
}
}
}

87
MainForm/ZoomFactor.cs Normal file
View File

@@ -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
{
/// <summary>
/// 计算保存Zoom的数据
/// </summary>
class ZoomFactor
{
static double zoomPercent = 0.1;
/// <summary>
/// [0.1 - 1]
/// 对原图像的最短一边的像素 * value作为新方形图像的一边像素值。
/// </summary>
public static double ZoomPercent
{
get
{
return zoomPercent;
}
set
{
if (value < 0.01) zoomPercent = 0.01;
else if (value > 1) zoomPercent = 1;
else zoomPercent = value;
}
}
/// <summary>
/// 新图像宽度的百分比
/// </summary>
double widthLocationPercent;
/// <summary>
/// 新图像高度的百分比
/// </summary>
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);
}
/// <summary>
/// 获取当前缩放数据
/// </summary>
/// <param name="previewBoxRectangle"></param>
/// <param name="location"></param>
/// <param name="imageWidth"></param>
/// <param name="imageHeight"></param>
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);
}
}
}

View File

@@ -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")]

63
Properties/Resources.Designer.cs generated Normal file
View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace ImageCompressor.Properties {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 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() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[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;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

120
Properties/Resources.resx Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

26
Properties/Settings.Designer.cs generated Normal file
View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

BIN
Resources/Icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

62
Utils/Localize.cs Normal file
View File

@@ -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<string, string> CN = new Dictionary<string, string>()
{
{"(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", "警告"},
};
}
}