diff --git a/AboutForm.cs b/AboutForm.cs index bed6efd..025acec 100644 --- a/AboutForm.cs +++ b/AboutForm.cs @@ -50,7 +50,7 @@ namespace AppStore // 初始化并添加应用信息 infoLabel = new Label(); - infoLabel.Text = "kortapp-z\n版本: 1.0.3\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C++的软件"; + infoLabel.Text = "kortapp-z\n版本: 1.0.4\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C++的软件"; infoLabel.Font = new Font("Microsoft YaHei", 12); infoLabel.AutoSize = false; infoLabel.Width = 300; diff --git a/ImageCompressor.png b/ImageCompressor.png new file mode 100644 index 0000000..bf01c64 Binary files /dev/null and b/ImageCompressor.png differ diff --git a/ImageCompressorForm.cs b/ImageCompressorForm.cs new file mode 100644 index 0000000..ebe3aef --- /dev/null +++ b/ImageCompressorForm.cs @@ -0,0 +1,190 @@ +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +namespace AppStore +{ + public class ImageCompressorForm : Form + { + private Button btnSelectInput = new Button(); + private Button btnSelectOutput = new Button(); + private Button btnCompress = new Button(); + private TextBox txtInput = new TextBox(); + private TextBox txtOutput = new TextBox(); + private RadioButton rbLossy = new RadioButton(); + private RadioButton rbLossless = new RadioButton(); + private TrackBar tbQuality = new TrackBar(); + private Label lblQuality = new Label(); + private CheckBox cbKeepExif = new CheckBox(); + private ProgressBar progressBar = new ProgressBar(); + + public ImageCompressorForm() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + this.Text = "图片压缩工具"; + this.Size = new Size(500, 350); + this.StartPosition = FormStartPosition.CenterScreen; + this.FormBorderStyle = FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + + // 输入文件选择 + btnSelectInput.Text = "选择..."; + btnSelectInput.Location = new Point(400, 20); + btnSelectInput.Click += (s, e) => SelectFile(txtInput); + this.Controls.Add(btnSelectInput); + + txtInput.Location = new Point(20, 20); + txtInput.Size = new Size(370, 20); + txtInput.ReadOnly = true; + this.Controls.Add(txtInput); + + Label lblInput = new Label(); + lblInput.Text = "输入文件:"; + lblInput.Location = new Point(20, 0); + this.Controls.Add(lblInput); + + // 输出文件选择 + btnSelectOutput.Text = "选择..."; + btnSelectOutput.Location = new Point(400, 70); + btnSelectOutput.Click += (s, e) => SelectFile(txtOutput, true); + this.Controls.Add(btnSelectOutput); + + txtOutput.Location = new Point(20, 70); + txtOutput.Size = new Size(370, 20); + this.Controls.Add(txtOutput); + + Label lblOutput = new Label(); + lblOutput.Text = "输出文件:"; + lblOutput.Location = new Point(20, 50); + this.Controls.Add(lblOutput); + + // 压缩类型 + rbLossy.Text = "有损压缩 (JPEG)"; + rbLossy.Location = new Point(20, 110); + rbLossy.Checked = true; + this.Controls.Add(rbLossy); + + rbLossless.Text = "无损压缩 (PNG)"; + rbLossless.Location = new Point(20, 135); + this.Controls.Add(rbLossless); + + // 质量设置 + tbQuality.Minimum = 1; + tbQuality.Maximum = 1000; + tbQuality.Value = 800; + tbQuality.Location = new Point(20, 190); + tbQuality.Size = new Size(300, 50); + tbQuality.Scroll += (s, e) => lblQuality.Text = $"压缩质量: {tbQuality.Value}"; + this.Controls.Add(tbQuality); + + lblQuality.Text = $"压缩质量: {tbQuality.Value}"; + lblQuality.Location = new Point(20, 170); + this.Controls.Add(lblQuality); + + // EXIF选项 + cbKeepExif.Text = "保留EXIF信息"; + cbKeepExif.Location = new Point(20, 240); + this.Controls.Add(cbKeepExif); + + // 压缩按钮 + btnCompress.Text = "开始压缩"; + btnCompress.Location = new Point(20, 280); + btnCompress.Size = new Size(460, 30); + btnCompress.Click += BtnCompress_Click; + this.Controls.Add(btnCompress); + + // 调整窗体大小 + this.Size = new Size(500, 370); + } + + private void SelectFile(TextBox target, bool isSave = false) + { + var dialog = isSave ? new SaveFileDialog() : new OpenFileDialog() as FileDialog; + dialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp|所有文件|*.*"; + + if (dialog.ShowDialog() == DialogResult.OK) + { + target.Text = dialog.FileName; + } + } + + private void BtnCompress_Click(object sender, EventArgs e) + { + if (string.IsNullOrEmpty(txtInput.Text) || !File.Exists(txtInput.Text)) + { + MessageBox.Show("请选择有效的输入文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (string.IsNullOrEmpty(txtOutput.Text)) + { + MessageBox.Show("请指定输出文件", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + btnCompress.Enabled = false; + + try + { + string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe"); + if (!File.Exists(toolPath)) + { + MessageBox.Show("图片压缩工具未找到", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + string args = $"\"{txtInput.Text}\" \"{txtOutput.Text}\""; + args += $" -t {(rbLossy.Checked ? "lossy" : "lossless")}"; + args += $" -q {tbQuality.Value}"; + if (cbKeepExif.Checked) args += " -e"; + + var process = new Process(); + process.StartInfo.FileName = toolPath; + process.StartInfo.Arguments = args; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + + process.OutputDataReceived += (s, ev) => { + if (!string.IsNullOrEmpty(ev.Data)) + Console.WriteLine(ev.Data); + }; + + process.ErrorDataReceived += (s, ev) => { + if (!string.IsNullOrEmpty(ev.Data)) + Console.Error.WriteLine(ev.Data); + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + + if (process.ExitCode == 0) + { + MessageBox.Show("图片压缩完成", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show("图片压缩失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + catch (Exception ex) + { + MessageBox.Show($"压缩过程中发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + finally + { + btnCompress.Enabled = true; + progressBar.Visible = false; + } + } + } +} diff --git a/MainForm.cs b/MainForm.cs index eb6810e..6472bba 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -325,6 +325,45 @@ namespace AppStore } }; flowPanel.Controls.Add(qrCard); + + // 图片压缩卡片 + var imageCompressorCard = new ToolCard(); + imageCompressorCard.ToolName = "图片压缩"; + + try + { + string iconPath = Path.Combine(Application.StartupPath, "img", "resource", "png","ImageCompressor.png"); + if (File.Exists(iconPath)) + { + imageCompressorCard.ToolIcon = Image.FromFile(iconPath); + } + else + { + imageCompressorCard.ToolIcon = SystemIcons.Application.ToBitmap(); + } + } + catch + { + imageCompressorCard.ToolIcon = SystemIcons.Application.ToBitmap(); + } + + imageCompressorCard.UpdateDisplay(); + imageCompressorCard.ToolCardClicked += (s, e) => { + try { + string toolPath = Path.Combine(Application.StartupPath, "resource", "image_compressor.exe"); + if (File.Exists(toolPath)) { + var form = new ImageCompressorForm(); + form.ShowDialog(); + } else { + MessageBox.Show("图片压缩工具未找到,请确保已正确安装", "错误", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } catch (Exception ex) { + MessageBox.Show($"启动图片压缩工具失败: {ex.Message}", "错误", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + }; + flowPanel.Controls.Add(imageCompressorCard); } catch (Exception ex) { @@ -697,7 +736,7 @@ namespace AppStore "https://download-ssl.firefox.com.cn/releases-sha2/full/116.0/zh-CN/Firefox-full-latest-win64.exe", "img/jpg/firefox.jpg")); - //这应该是为数不多的国产软件了 + //这应该是为数不多的国产软件了 flowPanel.Controls.Add(CreateAppCard( "星愿浏览器", "https://d1.twinkstar.com/win/Twinkstar_v10.7.1000.2505_Release.exe", diff --git a/tools/QrCodeGenerator.cs b/tools/QrCodeGenerator.cs index 2c7616a..d845005 100644 --- a/tools/QrCodeGenerator.cs +++ b/tools/QrCodeGenerator.cs @@ -20,6 +20,8 @@ namespace AppStore this.Text = "二维码生成器"; this.Size = new Size(500, 600); this.StartPosition = FormStartPosition.CenterScreen; + this.FormBorderStyle = FormBorderStyle.FixedDialog; + this.MaximizeBox = false; } private void InitializeComponent() diff --git a/tools/image_compressor.cpp b/tools/image_compressor.cpp new file mode 100644 index 0000000..73ee5bc --- /dev/null +++ b/tools/image_compressor.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + +namespace fs = std::filesystem; + +void ShowHelp() { + std::cout << "图片压缩工具 v1.0\n"; + std::cout << "用法: image_compressor [选项] 输入文件 输出文件\n"; + std::cout << "选项:\n"; + std::cout << " -t 压缩类型: lossy(有损)/lossless(无损) (默认: lossy)\n"; + std::cout << " -q 压缩质量 (1-1000) (默认: 80)\n"; + std::cout << " -e 保留EXIF信息 (默认: 不保留)\n"; + std::cout << " -h 显示帮助信息\n"; +} + +int main(int argc, char** argv) { + // 默认参数 + std::string compressType = "lossy"; + int quality = 80; + bool keepExif = false; + std::string inputFile; + std::string outputFile; + + // 解析命令行参数 + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "-h") { + ShowHelp(); + return 0; + } else if (arg == "-t" && i + 1 < argc) { + compressType = argv[++i]; + } else if (arg == "-q" && i + 1 < argc) { + quality = std::stoi(argv[++i]); + } else if (arg == "-e") { + keepExif = true; + } else if (inputFile.empty()) { + inputFile = arg; + } else { + outputFile = arg; + } + } + + // 验证参数 + if (inputFile.empty() || outputFile.empty()) { + std::cerr << "错误: 必须指定输入文件和输出文件\n"; + ShowHelp(); + return 1; + } + + if (quality < 1 || quality > 1000) { + std::cerr << "错误: 压缩质量必须在1-1000之间\n"; + return 1; + } + + try { + std::cout << "正在压缩: " << inputFile << " -> " << outputFile << std::endl; + std::cout << "类型: " << compressType << std::endl; + std::cout << "质量: " << quality << std::endl; + std::cout << "保留EXIF: " << (keepExif ? "是" : "否") << std::endl; + + // 加载图片 + int width, height, channels; + unsigned char* image = stbi_load(inputFile.c_str(), &width, &height, &channels, 0); + if (!image) { + std::cerr << "错误: 无法加载图片 " << inputFile << " - " << stbi_failure_reason() << std::endl; + return 1; + } + + // 压缩图片 + int result = 0; + if (compressType == "lossy") { + // 有损压缩 (JPEG) + int jpeg_quality = quality / 10; // 将1-1000映射到1-100 + jpeg_quality = std::max(1, std::min(jpeg_quality, 100)); + result = stbi_write_jpg(outputFile.c_str(), width, height, channels, image, jpeg_quality); + } else { + // 无损压缩 (PNG) + result = stbi_write_png(outputFile.c_str(), width, height, channels, image, width * channels); + } + + // 释放内存 + stbi_image_free(image); + + if (!result) { + std::cerr << "错误: 无法保存图片 " << outputFile << std::endl; + return 1; + } + + std::cout << "压缩完成\n"; + return 0; + + } catch (const std::exception& e) { + std::cerr << "发生错误: " << e.what() << std::endl; + return 1; + } +}