Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbf4a2bc2d | ||
|
|
9269032315 | ||
|
|
3c6117933c | ||
|
|
59c6ae623e | ||
|
|
6ff1506bc2 | ||
|
|
b8b8b03713 | ||
|
|
104aba01da | ||
|
|
a93e4d67f4 | ||
|
|
d1c62f4488 | ||
|
|
f4f4d86faa | ||
|
|
4c13fe0008 | ||
|
|
6a931e9a85 | ||
|
|
916c944a90 | ||
|
|
996d73f9fe | ||
|
|
d4afc5a5e7 | ||
|
|
a445bbdc2d | ||
|
|
0e90195f9a | ||
|
|
11df47ff2e | ||
|
|
7d87d9be7e | ||
|
|
5530c6413d | ||
|
|
b79db2e6bd | ||
|
|
6056c984c0 | ||
|
|
2395c13b60 | ||
|
|
6624cee1a3 | ||
|
|
165703575b | ||
|
|
efc1498a25 | ||
|
|
481fc24735 | ||
|
|
bebd804b7a | ||
|
|
4202013265 | ||
|
|
d26ac95dd5 | ||
|
|
0432c5fa42 | ||
|
|
94da204ca9 | ||
|
|
9770c58166 | ||
|
|
c006f78693 | ||
|
|
ab6080cdd0 | ||
|
|
4b77884d32 | ||
|
|
d45e3029b7 | ||
|
|
41b056c074 | ||
|
|
6955655e64 | ||
|
|
4310557659 | ||
|
|
510362688a | ||
|
|
34962bba3f | ||
|
|
0bab6ccaab | ||
|
|
973d984c55 | ||
|
|
280a9122b9 | ||
|
|
837aba38ba | ||
|
|
92c2a57773 | ||
|
|
43027d7953 | ||
|
|
6d2711da08 | ||
|
|
e47f905a8c | ||
|
|
6899e4767f | ||
|
|
d5a0564847 | ||
|
|
525c823397 | ||
|
|
7db2d8813e | ||
|
|
7216f62cef | ||
|
|
a777991b8c | ||
|
|
e2c6c52b32 | ||
|
|
33089c39b6 | ||
|
|
45805178cc | ||
|
|
08a11f025a | ||
|
|
5bb3886bc5 | ||
|
|
ce5f964776 | ||
|
|
f52c7908d7 | ||
|
|
abcbf06493 | ||
|
|
f56bcb3627 | ||
|
|
f789c7904a | ||
|
|
e04709637c | ||
|
|
e39f976607 | ||
|
|
0d9ec0ad44 | ||
|
|
122ada92d9 | ||
|
|
d571729c89 | ||
|
|
351e1e97c3 | ||
|
|
cff4c39e8c | ||
|
|
5f41f57e8d | ||
|
|
f2756ddbd8 | ||
|
|
d7a90ca422 | ||
|
|
7404bdfb5d | ||
|
|
5e8de310df | ||
|
|
f3cca9b3a1 | ||
|
|
2154f465b7 | ||
|
|
99bbda4668 | ||
|
|
d1d69da3e3 | ||
|
|
4c8cb807d9 | ||
|
|
1ad64feab9 | ||
|
|
ef7c582c50 | ||
|
|
ee65689048 | ||
|
|
4ef8099054 | ||
|
|
6dd8819f22 | ||
|
|
59900081da | ||
|
|
2c60d0b970 | ||
|
|
815ba41bdc | ||
|
|
459c0bc9d7 | ||
|
|
7c78a118a9 | ||
|
|
d5f944280e | ||
|
|
1cd722bf89 | ||
|
|
e34a954777 | ||
|
|
125bf6b0d4 | ||
|
|
983a0d5bf4 | ||
|
|
259b075541 |
115
.github/workflows/dotnet-desktop.yml
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
# This workflow will build, test, sign and package a WPF or Windows Forms desktop application
|
||||
# built on .NET Core.
|
||||
# To learn how to migrate your existing application to .NET Core,
|
||||
# refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework
|
||||
#
|
||||
# To configure this workflow:
|
||||
#
|
||||
# 1. Configure environment variables
|
||||
# GitHub sets default environment variables for every workflow run.
|
||||
# Replace the variables relative to your project in the "env" section below.
|
||||
#
|
||||
# 2. Signing
|
||||
# Generate a signing certificate in the Windows Application
|
||||
# Packaging Project or add an existing signing certificate to the project.
|
||||
# Next, use PowerShell to encode the .pfx file using Base64 encoding
|
||||
# by running the following Powershell script to generate the output string:
|
||||
#
|
||||
# $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte
|
||||
# [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt'
|
||||
#
|
||||
# Open the output file, SigningCertificate_Encoded.txt, and copy the
|
||||
# string inside. Then, add the string to the repo as a GitHub secret
|
||||
# and name it "Base64_Encoded_Pfx."
|
||||
# For more information on how to configure your signing certificate for
|
||||
# this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing
|
||||
#
|
||||
# Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key".
|
||||
# See "Build the Windows Application Packaging project" below to see how the secret is used.
|
||||
#
|
||||
# For more information on GitHub Actions, refer to https://github.com/features/actions
|
||||
# For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications,
|
||||
# refer to https://github.com/microsoft/github-actions-for-desktop-apps
|
||||
|
||||
name: .NET Core Desktop
|
||||
|
||||
on:
|
||||
push:
|
||||
分支: [ "main" ]
|
||||
pull_request:
|
||||
分支: [ "main" ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [Debug, Release]
|
||||
|
||||
runs-on: windows-latest # For a list of available runner types, refer to
|
||||
# https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
|
||||
|
||||
env:
|
||||
Solution_Name: your-solution-name # Replace with your solution name, i.e. MyWpfApp.sln.
|
||||
Test_Project_Path: your-test-project-path # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj.
|
||||
Wap_Project_Directory: your-wap-project-directory-name # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package.
|
||||
Wap_Project_Path: your-wap-project-path # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj.
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Install the .NET Core workload
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
# Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
# Execute all unit tests in the solution
|
||||
- name: Execute unit tests
|
||||
run: dotnet test
|
||||
|
||||
# Restore the application to populate the obj folder with RuntimeIdentifiers
|
||||
- name: Restore the application
|
||||
run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
|
||||
env:
|
||||
Configuration: ${{ matrix.configuration }}
|
||||
|
||||
# Decode the base 64 encoded pfx and save the Signing_Certificate
|
||||
- name: Decode the pfx
|
||||
run: |
|
||||
$pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}")
|
||||
$certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx
|
||||
[IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte)
|
||||
|
||||
# Create the app package by building and packaging the Windows Application Packaging project
|
||||
- name: Create the app package
|
||||
run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }}
|
||||
env:
|
||||
Appx_Bundle: Always
|
||||
Appx_Bundle_Platforms: x86|x64
|
||||
Appx_Package_Build_Mode: StoreUpload
|
||||
Configuration: ${{ matrix.configuration }}
|
||||
|
||||
# Remove the pfx
|
||||
- name: Remove the pfx
|
||||
run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx
|
||||
|
||||
# Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MSIX Package
|
||||
path: ${{ env.Wap_Project_Directory }}\AppPackages
|
||||
28
.github/workflows/dotnet.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# This workflow will build a .NET project
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
|
||||
|
||||
name: .NET
|
||||
|
||||
on:
|
||||
push:
|
||||
分支: [ "main" ]
|
||||
pull_request:
|
||||
分支: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal
|
||||
@@ -51,7 +51,7 @@ namespace AppStore
|
||||
|
||||
// 初始化并添加应用信息
|
||||
infoLabel = new Label();
|
||||
infoLabel.Text = "kortapp-z\n版本: 1.1.3\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C/C++的软件";
|
||||
infoLabel.Text = "kortapp-z\n版本: 1.3.5\n作者: zs-yg\n一个简单、开源的应用商店\nkortapp-z是完全免费\n基于.NET8和C/C++的软件";
|
||||
infoLabel.Font = new Font("Microsoft YaHei", 12);
|
||||
infoLabel.AutoSize = false;
|
||||
infoLabel.Width = 300;
|
||||
|
||||
294
AppCard.cs
@@ -15,6 +15,7 @@ namespace AppStore
|
||||
private Label nameLabel;
|
||||
private Panel namePanel;
|
||||
private Button downloadBtn;
|
||||
private ToolTip? toolTip;
|
||||
private Color borderColor = SystemColors.ControlDark;
|
||||
private static readonly ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath> PathCache =
|
||||
new ConcurrentDictionary<string, System.Drawing.Drawing2D.GraphicsPath>();
|
||||
@@ -22,6 +23,7 @@ namespace AppStore
|
||||
public string AppName { get; set; } = string.Empty;
|
||||
public Image AppIcon { get; set; } = SystemIcons.Application.ToBitmap();
|
||||
public string DownloadUrl { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public bool ShowDownloadButton { get; set; } = true;
|
||||
|
||||
public AppCard()
|
||||
@@ -48,33 +50,72 @@ namespace AppStore
|
||||
this.Padding = new Padding(10);
|
||||
|
||||
// 异步初始化卡片路径和边框
|
||||
// 预加载边框路径
|
||||
Task.Run(() => {
|
||||
InitializeCardPath();
|
||||
InitializeBorder();
|
||||
|
||||
// 确保在主线程注册事件
|
||||
this.Invoke((MethodInvoker)(() => {
|
||||
this.Paint += (sender, e) => {
|
||||
if (BorderCache.IsEmpty)
|
||||
{
|
||||
Task.Run(() => {
|
||||
InitializeBorder();
|
||||
this.Invoke((MethodInvoker)(() => this.Invalidate()));
|
||||
});
|
||||
}
|
||||
};
|
||||
}));
|
||||
});
|
||||
|
||||
// 应用图标
|
||||
iconBox = new PictureBox();
|
||||
iconBox.Size = new Size(80, 80);
|
||||
iconBox.Location = new Point((Width - 80) / 2, 15);
|
||||
iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
|
||||
this.Controls.Add(iconBox);
|
||||
// 应用图标 - 添加null检查
|
||||
if (iconBox != null && this != null && this.Controls != null)
|
||||
{
|
||||
iconBox.Size = new Size(80, 80);
|
||||
iconBox.Location = new Point((Width - 80) / 2, 15);
|
||||
iconBox.SizeMode = PictureBoxSizeMode.StretchImage;
|
||||
this.Controls.Add(iconBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("iconBox或Controls为null");
|
||||
}
|
||||
|
||||
// 应用名称 - 使用Panel包裹Label实现边框颜色
|
||||
// namePanel已在构造函数中初始化
|
||||
namePanel.Size = new Size(Width - 20, 40);
|
||||
namePanel.Location = new Point(10, 100);
|
||||
namePanel.Paint += (sender, e) => {
|
||||
ControlPaint.DrawBorder(e.Graphics, namePanel.ClientRectangle,
|
||||
borderColor, ButtonBorderStyle.Solid);
|
||||
};
|
||||
if (namePanel != null)
|
||||
{
|
||||
namePanel.Size = new Size(Width - 20, 40);
|
||||
namePanel.Location = new Point(10, 100);
|
||||
namePanel.Paint += (sender, e) => {
|
||||
try
|
||||
{
|
||||
if (e != null && e.Graphics != null && namePanel != null)
|
||||
{
|
||||
var rect = namePanel.ClientRectangle;
|
||||
if (rect.Width > 0 && rect.Height > 0)
|
||||
{
|
||||
ControlPaint.DrawBorder(e.Graphics, rect,
|
||||
borderColor, ButtonBorderStyle.Solid);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"绘制namePanel边框失败: {ex.Message}");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
nameLabel = new Label();
|
||||
nameLabel.Dock = DockStyle.Fill;
|
||||
nameLabel.Font = new Font("Microsoft YaHei", 10, FontStyle.Bold);
|
||||
nameLabel.TextAlign = ContentAlignment.MiddleCenter;
|
||||
|
||||
namePanel.Controls.Add(nameLabel);
|
||||
if (namePanel != null && nameLabel != null)
|
||||
{
|
||||
namePanel.Controls.Add(nameLabel);
|
||||
}
|
||||
|
||||
// 初始主题设置
|
||||
UpdateLabelTheme();
|
||||
@@ -82,32 +123,54 @@ namespace AppStore
|
||||
// 订阅主题变化事件
|
||||
ThemeManager.ThemeChanged += (theme) => UpdateLabelTheme();
|
||||
|
||||
this.Controls.Add(namePanel);
|
||||
if (this != null && this.Controls != null && namePanel != null)
|
||||
{
|
||||
this.Controls.Add(namePanel);
|
||||
}
|
||||
|
||||
// 下载按钮
|
||||
downloadBtn = new Button();
|
||||
downloadBtn.Text = "下载";
|
||||
downloadBtn.Size = new Size(100, 32);
|
||||
downloadBtn.Location = new Point((Width - 100) / 2, 150);
|
||||
downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
|
||||
downloadBtn.ForeColor = Color.White;
|
||||
downloadBtn.FlatStyle = FlatStyle.Flat;
|
||||
downloadBtn.FlatAppearance.BorderSize = 0;
|
||||
downloadBtn.Cursor = Cursors.Hand;
|
||||
downloadBtn.Font = new Font("Microsoft YaHei", 9);
|
||||
// 初始化ToolTip控件
|
||||
toolTip = new ToolTip();
|
||||
toolTip.AutoPopDelay = 5000;
|
||||
toolTip.InitialDelay = 500;
|
||||
toolTip.ReshowDelay = 500;
|
||||
toolTip.ShowAlways = true;
|
||||
|
||||
// 按钮悬停效果
|
||||
downloadBtn.MouseEnter += (s, e) => {
|
||||
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
|
||||
};
|
||||
|
||||
downloadBtn.MouseLeave += (s, e) => {
|
||||
// 下载按钮 - 添加null检查
|
||||
if (downloadBtn != null)
|
||||
{
|
||||
downloadBtn.Text = "下载";
|
||||
downloadBtn.Size = new Size(100, 32);
|
||||
downloadBtn.Location = new Point((Width - 100) / 2, 150);
|
||||
downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
|
||||
};
|
||||
downloadBtn.ForeColor = Color.White;
|
||||
downloadBtn.FlatStyle = FlatStyle.Flat;
|
||||
downloadBtn.FlatAppearance.BorderSize = 0;
|
||||
downloadBtn.Cursor = Cursors.Hand;
|
||||
downloadBtn.Font = new Font("Microsoft YaHei", 9);
|
||||
|
||||
downloadBtn.Click += DownloadBtn_Click;
|
||||
this.Controls.Add(downloadBtn);
|
||||
downloadBtn.Visible = ShowDownloadButton;
|
||||
// 按钮悬停效果 - 添加null检查
|
||||
downloadBtn.MouseEnter += (s, e) => {
|
||||
if (downloadBtn != null)
|
||||
{
|
||||
downloadBtn.BackColor = Color.FromArgb(0, 150, 255);
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
toolTip.SetToolTip(downloadBtn, Description);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
downloadBtn.MouseLeave += (s, e) => {
|
||||
if (downloadBtn != null)
|
||||
{
|
||||
downloadBtn.BackColor = Color.FromArgb(0, 120, 215);
|
||||
}
|
||||
};
|
||||
|
||||
downloadBtn.Click += DownloadBtn_Click;
|
||||
this.Controls.Add(downloadBtn);
|
||||
downloadBtn.Visible = ShowDownloadButton;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLabelTheme()
|
||||
@@ -126,7 +189,14 @@ namespace AppStore
|
||||
namePanel.BackColor = Color.White;
|
||||
borderColor = SystemColors.ControlDark;
|
||||
}
|
||||
namePanel.Invalidate(); // 触发重绘
|
||||
if (namePanel != null && !namePanel.IsDisposed)
|
||||
{
|
||||
namePanel.Invalidate(); // 触发重绘
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("namePanel为null或已释放");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -138,52 +208,58 @@ namespace AppStore
|
||||
// 使用卡片尺寸作为缓存键
|
||||
string cacheKey = $"{Width}_{Height}_10";
|
||||
|
||||
// 检查缓存中是否已有路径
|
||||
// 双重检查锁模式确保线程安全
|
||||
if (!BorderCache.TryGetValue(cacheKey, out var borderPath))
|
||||
{
|
||||
// 创建临时文件存储路径数据
|
||||
string tempFile = Path.GetTempFileName();
|
||||
try
|
||||
lock (BorderCache)
|
||||
{
|
||||
// 配置C++程序启动参数
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
if (!BorderCache.TryGetValue(cacheKey, out borderPath))
|
||||
{
|
||||
FileName = Path.Combine(Application.StartupPath, "resource", "border_renderer.exe"),
|
||||
Arguments = $"{Width} {Height} 10 \"{tempFile}\"", // 传递宽高和圆角半径
|
||||
UseShellExecute = false, // 不显示命令行窗口
|
||||
CreateNoWindow = true // 静默运行
|
||||
};
|
||||
|
||||
// 启动C++程序计算路径
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
process.WaitForExit();
|
||||
|
||||
// 检查计算结果
|
||||
if (process.ExitCode == 0 && File.Exists(tempFile))
|
||||
// 创建临时文件存储路径数据
|
||||
string tempFile = Path.GetTempFileName();
|
||||
try
|
||||
{
|
||||
// 读取C++程序生成的路径点
|
||||
var lines = File.ReadAllLines(tempFile);
|
||||
PointF[] points = lines.Select(line => {
|
||||
var parts = line.Split(','); // 解析坐标点
|
||||
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
|
||||
}).ToArray();
|
||||
// 配置C++程序启动参数
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(Application.StartupPath, "resource", "border_renderer.exe"),
|
||||
Arguments = $"{Width} {Height} 10 \"{tempFile}\"", // 传递宽高和圆角半径
|
||||
UseShellExecute = false, // 不显示命令行窗口
|
||||
CreateNoWindow = true // 静默运行
|
||||
};
|
||||
|
||||
// 创建GraphicsPath对象
|
||||
borderPath = new System.Drawing.Drawing2D.GraphicsPath();
|
||||
borderPath.AddLines(points); // 添加路径点
|
||||
// 启动C++程序计算路径
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
process.WaitForExit();
|
||||
|
||||
// 缓存路径对象
|
||||
BorderCache.TryAdd(cacheKey, borderPath);
|
||||
// 检查计算结果
|
||||
if (process.ExitCode == 0 && File.Exists(tempFile))
|
||||
{
|
||||
// 读取C++程序生成的路径点
|
||||
var lines = File.ReadAllLines(tempFile);
|
||||
PointF[] points = lines.Select(line => {
|
||||
var parts = line.Split(','); // 解析坐标点
|
||||
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
|
||||
}).ToArray();
|
||||
|
||||
// 创建GraphicsPath对象
|
||||
borderPath = new System.Drawing.Drawing2D.GraphicsPath();
|
||||
borderPath.AddLines(points); // 添加路径点
|
||||
|
||||
// 缓存路径对象
|
||||
BorderCache.TryAdd(cacheKey, borderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 确保临时文件被删除
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 确保临时文件被删除
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,6 +270,12 @@ namespace AppStore
|
||||
{
|
||||
base.OnPaint(e);
|
||||
|
||||
// 确保边框已初始化
|
||||
if (BorderCache.IsEmpty)
|
||||
{
|
||||
InitializeBorder();
|
||||
}
|
||||
|
||||
// 绘制背景
|
||||
using (var brush = new SolidBrush(this.BackColor)) {
|
||||
e.Graphics.FillRectangle(brush, this.ClientRectangle);
|
||||
@@ -256,22 +338,37 @@ namespace AppStore
|
||||
};
|
||||
|
||||
// 启动C++程序计算路径
|
||||
using (var process = Process.Start(startInfo)) {
|
||||
process.WaitForExit();
|
||||
if (startInfo != null)
|
||||
{
|
||||
using (var process = Process.Start(startInfo))
|
||||
{
|
||||
if (process != null)
|
||||
{
|
||||
process.WaitForExit();
|
||||
|
||||
// 检查计算结果
|
||||
if (process.ExitCode == 0 && File.Exists(tempFile)) {
|
||||
// 读取生成的路径点
|
||||
var lines = File.ReadAllLines(tempFile);
|
||||
PointF[] points = lines.Select(line => {
|
||||
var parts = line.Split(','); // 解析坐标
|
||||
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
|
||||
}).ToArray();
|
||||
// 检查计算结果
|
||||
if (process.ExitCode == 0 && File.Exists(tempFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取生成的路径点
|
||||
var lines = File.ReadAllLines(tempFile);
|
||||
PointF[] points = lines.Select(line => {
|
||||
var parts = line.Split(','); // 解析坐标
|
||||
return new PointF(float.Parse(parts[0]), float.Parse(parts[1]));
|
||||
}).ToArray();
|
||||
|
||||
// 创建并缓存路径对象
|
||||
path = new System.Drawing.Drawing2D.GraphicsPath();
|
||||
path.AddLines(points);
|
||||
PathCache.TryAdd(cacheKey, path);
|
||||
// 创建并缓存路径对象
|
||||
path = new System.Drawing.Drawing2D.GraphicsPath();
|
||||
path.AddLines(points);
|
||||
PathCache.TryAdd(cacheKey, path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"读取路径点失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -289,7 +386,7 @@ namespace AppStore
|
||||
try
|
||||
{
|
||||
var safePath = path ?? CalculatePathFallback(Width, Height, 10);
|
||||
// 更严格的null检查,包括路径和控件状态
|
||||
// 更严格的null检查,包括路径和控件状态
|
||||
if (safePath != null &&
|
||||
safePath.PointCount > 0 &&
|
||||
this.IsHandleCreated &&
|
||||
@@ -347,10 +444,14 @@ namespace AppStore
|
||||
|
||||
public void UpdateDisplay()
|
||||
{
|
||||
if (nameLabel != null)
|
||||
if (nameLabel != null && AppName != null)
|
||||
{
|
||||
nameLabel.Text = AppName;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("nameLabel或AppName为null");
|
||||
}
|
||||
if (iconBox != null && AppIcon != null)
|
||||
{
|
||||
iconBox.Image = AppIcon;
|
||||
@@ -362,16 +463,17 @@ namespace AppStore
|
||||
try
|
||||
{
|
||||
// 更严格的null检查
|
||||
// 更严格的null检查,包括DownloadManager.Instance和其方法
|
||||
// 更严格的null检查,包括DownloadManager.Instance和其方法
|
||||
// 全面的null和状态检查
|
||||
var downloadManager = DownloadManager.Instance;
|
||||
if (sender == null || e == null ||
|
||||
string.IsNullOrWhiteSpace(DownloadUrl) ||
|
||||
string.IsNullOrWhiteSpace(AppName) ||
|
||||
!this.IsHandleCreated ||
|
||||
this.IsDisposed ||
|
||||
DownloadManager.Instance == null ||
|
||||
DownloadManager.Instance.DownloadItems == null ||
|
||||
DownloadManager.Instance.StartDownload == null)
|
||||
downloadManager == null ||
|
||||
downloadManager.DownloadItems == null ||
|
||||
downloadManager.StartDownload == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -379,7 +481,7 @@ namespace AppStore
|
||||
string safeAppName = AppName ?? "未知应用";
|
||||
string fileName = $"{safeAppName.Replace(" ", "_")}.exe";
|
||||
|
||||
DownloadManager.Instance.StartDownload(fileName, DownloadUrl);
|
||||
downloadManager.StartDownload(fileName, DownloadUrl);
|
||||
|
||||
string message = $"已开始下载: {safeAppName}";
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<WarningsAsErrors>CS8618</WarningsAsErrors>
|
||||
<ApplicationIcon>img\ico\icon.ico</ApplicationIcon>
|
||||
<Platforms>x86;x64</Platforms>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -34,6 +35,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SunnyUI" Version="3.8.6" />
|
||||
<PackageReference Include="ZXing.Net" Version="0.16.9" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -53,10 +53,11 @@ namespace AppStore
|
||||
this.BackColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
|
||||
? Color.White
|
||||
: Color.Black;
|
||||
this.BorderStyle = BorderStyle.FixedSingle;
|
||||
this.BorderStyle = BorderStyle.None; // 禁用默认边框
|
||||
this.ForeColor = ThemeManager.CurrentTheme == ThemeManager.ThemeMode.Light
|
||||
? Color.Black
|
||||
: Color.White;
|
||||
this.Paint += DownloadItem_Paint; // 添加自定义绘制
|
||||
|
||||
// 文件名标签
|
||||
nameLabel = new Label();
|
||||
@@ -98,6 +99,17 @@ namespace AppStore
|
||||
nameLabel.Text = FileName;
|
||||
progressBar.Value = Progress;
|
||||
statusLabel.Text = Status;
|
||||
this.Invalidate(); // 触发重绘
|
||||
}
|
||||
|
||||
private void DownloadItem_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
// 自定义边框绘制
|
||||
using (var pen = new Pen(ThemeManager.BorderColor, 1))
|
||||
{
|
||||
e.Graphics.DrawRectangle(pen,
|
||||
new Rectangle(0, 0, this.Width - 1, this.Height - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelBtn_Click(object sender, EventArgs e)
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace AppStore
|
||||
private ProcessResult GetProcessResult(Process? process)
|
||||
{
|
||||
var result = new ProcessResult();
|
||||
if (process == null) return result;
|
||||
if (process == null || process.StartInfo == null) return result;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -59,7 +59,7 @@ namespace AppStore
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略所有异常,使用默认值
|
||||
// 忽略所有异常,使用默认值
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -115,7 +115,7 @@ namespace AppStore
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"Downloads");
|
||||
|
||||
Logger.LogError($"下载路径{downloadsDir}不可用,将使用默认路径: {defaultPath}", ex);
|
||||
Logger.LogError($"下载路径{downloadsDir}不可用,将使用默认路径: {defaultPath}", ex);
|
||||
downloadsDir = defaultPath;
|
||||
Directory.CreateDirectory(downloadsDir);
|
||||
}
|
||||
@@ -357,6 +357,7 @@ namespace AppStore
|
||||
|
||||
private string GetDownloadPath()
|
||||
{
|
||||
string fallbackPath = string.Empty;
|
||||
// 1. 优先读取用户设置的下载路径
|
||||
try
|
||||
{
|
||||
@@ -449,12 +450,16 @@ namespace AppStore
|
||||
var downloadsFolderGuid = new Guid("374DE290-123F-4565-9164-39C4925E467B");
|
||||
if (SHGetKnownFolderPath(downloadsFolderGuid, 0, IntPtr.Zero, out pathPtr) == 0)
|
||||
{
|
||||
string defaultPath = Marshal.PtrToStringUni(pathPtr);
|
||||
string? defaultPath = Marshal.PtrToStringUni(pathPtr);
|
||||
if (!string.IsNullOrEmpty(defaultPath))
|
||||
{
|
||||
Directory.CreateDirectory(defaultPath);
|
||||
return defaultPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("获取到的系统下载路径为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -471,16 +476,28 @@ namespace AppStore
|
||||
|
||||
// 3. 最终回退到相对路径 ~/Downloads
|
||||
string relativePath = "~/Downloads";
|
||||
string fallbackPath = relativePath.Replace("~",
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
|
||||
fallbackPath = Path.GetFullPath(fallbackPath);
|
||||
string userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ??
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop) ??
|
||||
AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
if (!string.IsNullOrEmpty(userProfile))
|
||||
{
|
||||
fallbackPath = relativePath.Replace("~", userProfile);
|
||||
if (!string.IsNullOrEmpty(fallbackPath))
|
||||
{
|
||||
fallbackPath = Path.GetFullPath(fallbackPath);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Directory.CreateDirectory(fallbackPath);
|
||||
// 测试路径可写性
|
||||
string testFile = Path.Combine(fallbackPath, "write_test.tmp");
|
||||
File.WriteAllText(testFile, "test");
|
||||
File.Delete(testFile);
|
||||
if (!string.IsNullOrEmpty(testFile))
|
||||
{
|
||||
File.WriteAllText(testFile, "test");
|
||||
File.Delete(testFile);
|
||||
}
|
||||
return fallbackPath;
|
||||
}
|
||||
catch {
|
||||
|
||||
821
MainForm.cs
24
README.md
@@ -1,20 +1,20 @@
|
||||
# Kortapp-z - Windows应用商店 --主张软件开源、免费,拒绝广告
|
||||
# Kortapp-z - Windows应用商店 --主张软件开源、免费,拒绝广告
|
||||
|
||||
小立一个flag:从不接受广告,不停更新
|
||||
小立一个flag:从不接受广告,不停更新
|
||||
|
||||
## 项目开源行为
|
||||
|
||||
1. 项目代码开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
|
||||
2. 项目图标、截图等资源开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
|
||||
1. 项目代码开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
|
||||
2. 项目图标、截图等资源开源,允许任何人使用、修改、分发、商用,但必须注明原作者。
|
||||
3. 项目的任何衍生品(包括但不限于网站、APP、插件等)必须遵循以上开源协议。
|
||||
4. 项目不接受任何形式的广告,不得在任何地方投放广告。
|
||||
4. 项目不接受任何形式的广告,不得在任何地方投放广告。
|
||||
5. 项目不接受任何形式的捐赠、赞助
|
||||
6. 项目可以进行PR,欢迎任何形式的PR,不提交issue也可以
|
||||
7. 本项目可以PR一些你自己的项目,如果star数量不到1k,都会被删除
|
||||
6. 项目可以进行PR,欢迎任何形式的PR,不提交issue也可以
|
||||
7. 本项目可以PR一些你自己的项目,如果star数量不到1k,都会被删除
|
||||
|
||||
## 项目简介
|
||||
|
||||
一个简单的Windows应用商店应用,提供软件下载和管理功能。
|
||||
一个简单的Windows应用商店应用,提供软件下载和管理功能。
|
||||
提供软件管理、下载管理、内置工具使用等功能
|
||||
|
||||
## 功能特点
|
||||
@@ -44,7 +44,7 @@ dotnet publish AppStore.csproj -c Release -r win-x86 --self-contained false /p:O
|
||||
dotnet publish AppStore.csproj -c Release -r win-x64 --self-contained false /p:Optimize=true /p:DebugType=None
|
||||
```
|
||||
|
||||
打包后的可执行文件将包含指定的应用程序图标,输出路径为:
|
||||
打包后的可执行文件将包含指定的应用程序图标,输出路径为:
|
||||
```
|
||||
bin\Release\net8.0-windows\[platform]\publish
|
||||
```
|
||||
@@ -87,9 +87,9 @@ gitee镜像仓库:https://gitee.com/chr_super/kortapp-z (目前已经停止维
|
||||
|
||||
## 维护
|
||||
|
||||
由于gitee我没怎么用,而且操作麻烦,gitee镜像将不会继续同步,有懂得人可以帮我搞下镜像吗,qq: 3872006562,也可以b站直接私信,我会在readme中鸣谢的,谢谢各位
|
||||
由于和Daye发生了矛盾,所以windowscleaner将永远不上架,我要自己努力
|
||||
提示:由于github上传文件的限制,img/png/NET.png,请改名为.NET.png,否则程序可能出现无法预料的问题
|
||||
由于gitee我没怎么用,而且操作麻烦,gitee镜像将不会继续同步,有懂得人可以帮我搞下镜像吗,qq: 3872006562,也可以b站直接私信,我会在readme中鸣谢的,谢谢各位
|
||||
由于和Daye发生了矛盾,所以windowscleaner将永远不上架,我要自己努力
|
||||
提示:由于github上传文件的限制,img/png/NET.png,请改名为.NET.png,否则程序可能出现无法预料的问题
|
||||
|
||||
每一个人都可以通过PR添加属于自己的合法软件
|
||||
|
||||
|
||||
@@ -203,9 +203,9 @@ namespace AppStore
|
||||
{
|
||||
string jsonString = File.ReadAllText(jsonPath);
|
||||
var jsonData = JsonSerializer.Deserialize<JsonElement>(jsonString);
|
||||
string customPath = jsonData.GetProperty("DownloadPath").GetString();
|
||||
string customPath = jsonData.GetProperty("DownloadPath").GetString() ?? "";
|
||||
|
||||
// 如果自定义路径有效则显示,否则显示默认路径
|
||||
// 如果自定义路径有效则显示,否则显示默认路径
|
||||
txtBox.Text = !string.IsNullOrWhiteSpace(customPath) ? customPath : defaultPath;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
首先,如果希望编译程序,那么必须安装.NET8.0 SDK
|
||||
首先,如果希望编译程序,那么必须安装.NET8.0 SDK
|
||||
下载链接:https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-8.0.411-windows-x64-installer
|
||||
|
||||
使用一下指令编译
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
如果希望运行,那么必须安装.NET8.0 SDK
|
||||
如果希望运行,那么必须安装.NET8.0 SDK
|
||||
|
||||
下载链接:https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-8.0.411-windows-x64-installer
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace AppStore
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略错误,使用默认主题
|
||||
// 忽略错误,使用默认主题
|
||||
}
|
||||
return ThemeMode.Light;
|
||||
}
|
||||
@@ -58,18 +58,26 @@ namespace AppStore
|
||||
}
|
||||
|
||||
// 浅色主题颜色
|
||||
private static readonly Color LightBackground = Color.FromArgb(255, 255, 255);
|
||||
private static readonly Color LightControlBackground = Color.FromArgb(240, 240, 240);
|
||||
private static readonly Color LightText = Color.FromArgb(30, 30, 30);
|
||||
private static readonly Color LightButtonHover = Color.FromArgb(230, 230, 230);
|
||||
private static readonly Color LightButtonActive = Color.FromArgb(220, 220, 220);
|
||||
private static readonly Color LightBackground = Color.FromArgb(250, 250, 250);
|
||||
private static readonly Color LightControlBackground = Color.FromArgb(245, 245, 245);
|
||||
private static readonly Color LightText = Color.FromArgb(40, 40, 40);
|
||||
private static readonly Color LightButtonHover = Color.FromArgb(235, 235, 235);
|
||||
private static readonly Color LightButtonActive = Color.FromArgb(225, 225, 225);
|
||||
private static readonly Color LightAccent = Color.FromArgb(0, 120, 215);
|
||||
private static readonly Color LightAccentLight = Color.FromArgb(0, 150, 245);
|
||||
|
||||
// 深色主题颜色
|
||||
private static readonly Color DarkBackground = Color.FromArgb(30, 30, 30);
|
||||
private static readonly Color DarkControlBackground = Color.FromArgb(45, 45, 45);
|
||||
private static readonly Color DarkText = Color.FromArgb(240, 240, 240);
|
||||
private static readonly Color DarkButtonHover = Color.FromArgb(60, 60, 60);
|
||||
private static readonly Color DarkButtonActive = Color.FromArgb(70, 70, 70);
|
||||
private static readonly Color DarkBackground = Color.FromArgb(25, 25, 25);
|
||||
private static readonly Color DarkControlBackground = Color.FromArgb(40, 40, 40);
|
||||
private static readonly Color DarkText = Color.FromArgb(245, 245, 245);
|
||||
private static readonly Color DarkButtonHover = Color.FromArgb(55, 55, 55);
|
||||
private static readonly Color DarkButtonActive = Color.FromArgb(65, 65, 65);
|
||||
private static readonly Color DarkBorder = Color.FromArgb(70, 70, 70);
|
||||
private static readonly Color DarkAccent = Color.FromArgb(0, 150, 245);
|
||||
private static readonly Color DarkAccentLight = Color.FromArgb(0, 180, 255);
|
||||
|
||||
// 浅色主题边框颜色
|
||||
private static readonly Color LightBorder = Color.FromArgb(200, 200, 200);
|
||||
|
||||
public static event Action<ThemeMode> ThemeChanged = delegate {};
|
||||
|
||||
@@ -102,6 +110,19 @@ namespace AppStore
|
||||
public static Color ButtonActiveColor =>
|
||||
_currentTheme == ThemeMode.Light ? LightButtonActive : DarkButtonActive;
|
||||
|
||||
public static Color BorderColor =>
|
||||
_currentTheme == ThemeMode.Light ? LightBorder : DarkBorder;
|
||||
|
||||
public static Color AccentColor =>
|
||||
_currentTheme == ThemeMode.Light ? LightAccent : DarkAccent;
|
||||
|
||||
public static Color AccentLightColor =>
|
||||
_currentTheme == ThemeMode.Light ? LightAccentLight : DarkAccentLight;
|
||||
|
||||
public static int ControlRadius => 8;
|
||||
|
||||
public static int FormRadius => 12;
|
||||
|
||||
public static void ApplyTheme(Control control)
|
||||
{
|
||||
ApplyThemeToControl(control);
|
||||
@@ -118,6 +139,9 @@ namespace AppStore
|
||||
button.FlatAppearance.BorderSize = 0;
|
||||
button.FlatAppearance.MouseOverBackColor = ButtonHoverColor;
|
||||
button.FlatAppearance.MouseDownBackColor = ButtonActiveColor;
|
||||
button.BackColor = ControlBackgroundColor;
|
||||
button.Font = new Font(button.Font, FontStyle.Bold);
|
||||
button.Padding = new Padding(10, 5, 10, 5);
|
||||
}
|
||||
|
||||
foreach (Control childControl in control.Controls)
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
<main>
|
||||
<section class="about-project">
|
||||
<h2>项目简介</h2>
|
||||
<p>kortapp-z是一个专为Windows系统设计的实用工具集合,旨在提供一站式的系统优化和管理解决方案。</p>
|
||||
<p>项目始于2025年,由热爱效率工具的开发者创建。</p>
|
||||
<p>kortapp-z是一个专为Windows系统设计的实用工具集合,旨在提供一站式的系统优化和管理解决方案。</p>
|
||||
<p>项目始于2025年,由热爱效率工具的开发者创建。</p>
|
||||
</section>
|
||||
|
||||
<section class="team">
|
||||
<h2>开发团队</h2>
|
||||
<p>我们的团队由经验丰富的开发人员组成,专注于创建高质量的系统工具。</p>
|
||||
<p>我们的团队由经验丰富的开发人员组成,专注于创建高质量的系统工具。</p>
|
||||
<ul>
|
||||
<li>核心开发者: 1人</li>
|
||||
<li>UI设计师: 1人</li>
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<section class="contact">
|
||||
<h2>联系我们</h2>
|
||||
<p>如有任何问题或建议,欢迎通过GitHub提交issue或pull request。</p>
|
||||
<p>如有任何问题或建议,欢迎通过GitHub提交issue或pull request。</p>
|
||||
<a href="https://github.com/zs-yg/kortapp-z/issues" target="_blank">提交反馈</a>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -18,20 +18,20 @@
|
||||
|
||||
<article class="feature">
|
||||
<h3>应用程序管理</h3>
|
||||
<p>批量安装、卸载(目前没有)和更新应用程序(目前没有),管理启动项(目前没有)。</p>
|
||||
<p>优势:集中管理所有应用,节省时间,避免系统臃肿。</p>
|
||||
<p>批量安装、卸载(目前没有)和更新应用程序(目前没有),管理启动项(目前没有)。</p>
|
||||
<p>优势:集中管理所有应用,节省时间,避免系统臃肿。</p>
|
||||
</article>
|
||||
|
||||
<article class="feature">
|
||||
<h3>资源监控(之后可能在内置工具里有)</h3>
|
||||
<p>实时监控CPU、内存、磁盘和网络使用情况。</p>
|
||||
<p>优势:直观的图表展示,及时发现资源瓶颈。</p>
|
||||
<p>优势:直观的图表展示,及时发现资源瓶颈。</p>
|
||||
</article>
|
||||
|
||||
<article class="feature">
|
||||
<h3>文件管理(之后可能在内置工具里有)</h3>
|
||||
<p>高级文件搜索、批量重命名和快速文件分类。</p>
|
||||
<p>优势:提升文件管理效率,支持正则表达式搜索。</p>
|
||||
<p>优势:提升文件管理效率,支持正则表达式搜索。</p>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
BIN
img/png/Azul_JDKs.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
img/png/Cataclysm-DDA.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
img/png/ClamAV.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
img/png/CodeBlocks.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
img/png/Dev-C++.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
img/png/Final2x.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
img/png/FreeMove.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
img/png/NoteGen.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
img/png/ReactOS.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
img/png/Ubuntu.png
Normal file
|
After Width: | Height: | Size: 853 B |
BIN
img/png/VideoCaptioner.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
img/png/edit.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
img/png/github_cli.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
img/png/gophish.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
img/png/hashcat.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
img/png/notepad--.png
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
img/png/ollama.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
img/png/pixpin.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
img/png/pocketbase.png
Normal file
|
After Width: | Height: | Size: 660 B |
BIN
img/png/powershell.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
img/resource/png/Image_format_converter.png
Normal file
|
After Width: | Height: | Size: 332 KiB |
BIN
img/resource/png/Self_starting_management.png
Normal file
|
After Width: | Height: | Size: 608 KiB |
BIN
img/resource/png/ico_extractor.png
Normal file
|
After Width: | Height: | Size: 523 KiB |
|
Before Width: | Height: | Size: 946 KiB |
@@ -1,18 +1,18 @@
|
||||
; 脚本由 Inno Setup 脚本向导生成。
|
||||
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
|
||||
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
|
||||
|
||||
#define MyAppName "kortapp-z"
|
||||
#define MyAppVersion "1.1.3"
|
||||
#define MyAppVersion "1.3.5"
|
||||
#define MyAppPublisher "zsyg"
|
||||
#define MyAppURL "https://github.com/zs-yg/kortapp-z"
|
||||
#define MyAppExeName "kortapp.exe"
|
||||
#define MyAppExeName "kortapp-z.exe"
|
||||
#define MyAppAssocName MyAppName + ""
|
||||
#define MyAppAssocExt ".exe"
|
||||
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
|
||||
|
||||
[Setup]
|
||||
; 注意:AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。
|
||||
; (若要生成新的 GUID,请在 IDE 中单击 "工具|生成 GUID"。)
|
||||
; (若要生成新的 GUID,请在 IDE 中单击 "工具|生成 GUID"。)
|
||||
AppId={{8020EC01-6133-40BB-8B8B-0EB71E49696C}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
@@ -27,7 +27,7 @@ UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
; 除 Arm 上的 x64 和 Windows 11 之外的任何平台上。
|
||||
ArchitecturesAllowed=x64compatible
|
||||
; "ArchitecturesInstallIn64BitMode=x64compatible" 要求
|
||||
; 安装可以在 x64 或 Arm 上的 Windows 11 上以“64 位模式”完成,
|
||||
; 安装可以在 x64 或 Arm 上的 Windows 11 上以“64 位模式”完成,
|
||||
; 这意味着它应该使用本机 64 位 Program Files 目录和
|
||||
; 注册表的 64 位视图。
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
; 脚本由 Inno Setup 脚本向导生成。
|
||||
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
|
||||
; 有关创建 Inno Setup 脚本文件的详细信息,请参阅帮助文档!
|
||||
|
||||
#define MyAppName "kortapp-z"
|
||||
#define MyAppVersion "1.1.3"
|
||||
#define MyAppVersion "1.3.5"
|
||||
#define MyAppPublisher "zsyg"
|
||||
#define MyAppURL "https://github.com/zs-yg/kortapp-z"
|
||||
#define MyAppExeName "kortapp.exe"
|
||||
#define MyAppExeName "kortapp-z.exe"
|
||||
#define MyAppAssocName MyAppName + ""
|
||||
#define MyAppAssocExt ".exe"
|
||||
#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
|
||||
|
||||
[Setup]
|
||||
; 注意:AppId 的值唯一标识此应用程序。不要在其他应用程序的安装程序中使用相同的 AppId 值。
|
||||
; (若要生成新的 GUID,请在 IDE 中单击 "工具|生成 GUID"。)
|
||||
; (若要生成新的 GUID,请在 IDE 中单击 "工具|生成 GUID"。)
|
||||
AppId={{BF1944C3-CD0C-4119-A340-49C54961D48B}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
|
||||
@@ -44,7 +44,7 @@ int main() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cout << "日志目录不存在,无需清理" << std::endl;
|
||||
std::cout << "日志目录不存在,无需清理" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
30
logger.cs
@@ -59,5 +59,35 @@ namespace AppStore
|
||||
}
|
||||
Log(errorMessage);
|
||||
}
|
||||
|
||||
public static void LogWarning(string message, Exception? ex = null)
|
||||
{
|
||||
string warningMessage = $"WARNING: {message}";
|
||||
if (ex != null)
|
||||
{
|
||||
warningMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
|
||||
}
|
||||
Log(warningMessage);
|
||||
}
|
||||
|
||||
public static void LogDebug(string message, Exception? ex = null)
|
||||
{
|
||||
string debugMessage = $"DEBUG: {message}";
|
||||
if (ex != null)
|
||||
{
|
||||
debugMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
|
||||
}
|
||||
Log(debugMessage);
|
||||
}
|
||||
|
||||
public static void LogTip(string message, Exception? ex = null)
|
||||
{
|
||||
string tipMessage = $"TIP: {message}";
|
||||
if (ex != null)
|
||||
{
|
||||
tipMessage += $"\nException: {ex}\nStackTrace: {ex.StackTrace}";
|
||||
}
|
||||
Log(tipMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
83
others/C++/Image_format_converter/CMakeLists.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(ImageFormatConverter)
|
||||
|
||||
# 设置静态编译
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-static")
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
|
||||
|
||||
# 设置C++标准
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# 设置MSYS2库搜索路径
|
||||
set(MSYS2_LIB_DIR "C:/msys64/ucrt64/lib")
|
||||
|
||||
# 查找依赖库(静态版本)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(TIFF REQUIRED libtiff-4)
|
||||
find_library(TIFF_STATIC_LIB NAMES libtiff.a PATHS ${TIFF_LIBRARY_DIRS})
|
||||
|
||||
pkg_check_modules(WEBP REQUIRED libwebp)
|
||||
find_library(WEBP_STATIC_LIB NAMES libwebp.a PATHS ${WEBP_LIBRARY_DIRS})
|
||||
|
||||
pkg_check_modules(AVIF REQUIRED libavif)
|
||||
find_library(AVIF_STATIC_LIB NAMES libavif.a PATHS ${AVIF_LIBRARY_DIRS})
|
||||
|
||||
# 使用find_package查找FLTK(静态版本)
|
||||
find_package(FLTK REQUIRED)
|
||||
find_library(FLTK_STATIC_LIB NAMES libfltk.a PATHS ${FLTK_LIBRARY_DIRS})
|
||||
|
||||
# 查找cairo导入库
|
||||
find_library(CAIRO_IMPORT_LIB NAMES libcairo.dll.a PATHS "C:/msys64/ucrt64/lib")
|
||||
|
||||
# 查找其他依赖库
|
||||
find_library(JPEG_STATIC_LIB NAMES libjpeg.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(ZLIB_STATIC_LIB NAMES libz.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(ZSTD_STATIC_LIB NAMES libzstd.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(SHARPYUV_STATIC_LIB NAMES libsharpyuv.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(JBIG_STATIC_LIB NAMES libjbig.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(LZMA_STATIC_LIB NAMES liblzma.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(LIBDEFLATE_STATIC_LIB NAMES libdeflate.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(LERC_STATIC_LIB NAMES liblerc.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(AOM_STATIC_LIB NAMES libaom.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(YUV_STATIC_LIB NAMES libyuv.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(SVTAV1_ENC_STATIC_LIB NAMES libSvtAv1Enc.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(RAV1E_STATIC_LIB NAMES librav1e.a PATHS ${MSYS2_LIB_DIR})
|
||||
find_library(DAV1D_STATIC_LIB NAMES libdav1d.a PATHS ${MSYS2_LIB_DIR})
|
||||
|
||||
# 包含头文件目录
|
||||
include_directories(include ${TIFF_INCLUDE_DIRS} ${WEBP_INCLUDE_DIRS} ${FLTK_INCLUDE_DIRS} ${AVIF_INCLUDE_DIRS} "C:/msys64/ucrt64/include/cairo")
|
||||
|
||||
# 收集所有源文件
|
||||
file(GLOB SOURCES "src/*.cpp")
|
||||
|
||||
# 创建可执行文件(设置为WIN32应用程序避免控制台窗口)
|
||||
add_executable(ImageFormatConverter WIN32 ${SOURCES})
|
||||
|
||||
# 链接静态库
|
||||
target_link_libraries(ImageFormatConverter
|
||||
${TIFF_STATIC_LIB}
|
||||
${WEBP_STATIC_LIB}
|
||||
${FLTK_STATIC_LIB}
|
||||
${JPEG_STATIC_LIB}
|
||||
${ZLIB_STATIC_LIB}
|
||||
${ZSTD_STATIC_LIB}
|
||||
${SHARPYUV_STATIC_LIB}
|
||||
${JBIG_STATIC_LIB}
|
||||
${LZMA_STATIC_LIB}
|
||||
${LIBDEFLATE_STATIC_LIB}
|
||||
${LERC_STATIC_LIB}
|
||||
${AVIF_STATIC_LIB}
|
||||
${AOM_STATIC_LIB}
|
||||
${YUV_STATIC_LIB}
|
||||
${SVTAV1_ENC_STATIC_LIB}
|
||||
${RAV1E_STATIC_LIB}
|
||||
${DAV1D_STATIC_LIB}
|
||||
${CAIRO_IMPORT_LIB}
|
||||
"C:/msys64/ucrt64/lib/libcairo.a"
|
||||
-lgdi32 -lmsimg32 # 添加Windows图形库
|
||||
-lstdc++ -lgcc -lwinpthread -lcomctl32 -lole32 -luuid -lws2_32 -lntdll)
|
||||
|
||||
# 设置输出目录
|
||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||
11
others/C++/Image_format_converter/include/avif_to_bmp.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class AvifToBmpConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/avif_to_jpeg.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class AvifToJpegConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/avif_to_jpg.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class AvifToJpgConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/avif_to_png.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class AvifToPngConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/avif_to_tiff.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class AvifToTiffConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/avif_to_webp.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class AvifToWebpConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/bmp_to_avif.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class BmpToAvifConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/bmp_to_jpeg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class BmpToJpegConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/bmp_to_jpg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class BmpToJpgConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/bmp_to_png.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class BmpToPngConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int compression_level = 6);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/bmp_to_tiff.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class BmpToTiffConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/bmp_to_webp.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class BmpToWebpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
25
others/C++/Image_format_converter/include/common.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
struct ImageData {
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
std::unique_ptr<unsigned char, void(*)(void*)> pixels;
|
||||
|
||||
ImageData() : pixels(nullptr, stbi_image_free) {}
|
||||
};
|
||||
|
||||
enum class ImageFormat {
|
||||
PNG,
|
||||
JPG,
|
||||
TIFF,
|
||||
WEBP,
|
||||
AVIF,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
ImageFormat get_format_from_extension(const std::string& path);
|
||||
13
others/C++/Image_format_converter/include/converter_base.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class ConverterBase {
|
||||
public:
|
||||
virtual ~ConverterBase() = default;
|
||||
virtual bool convert(const std::string& input,
|
||||
const std::string& output) = 0;
|
||||
|
||||
protected:
|
||||
virtual bool validate(const ImageData& data) = 0;
|
||||
};
|
||||
23
others/C++/Image_format_converter/include/gui_interface.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
#include <FL/Fl_Input.H>
|
||||
#include <FL/Fl_Output.H>
|
||||
#include <FL/Fl_Choice.H>
|
||||
#include <string>
|
||||
|
||||
class MainWindow : public Fl_Window {
|
||||
public:
|
||||
MainWindow(int w, int h, const char* title);
|
||||
|
||||
private:
|
||||
Fl_Input* input_path;
|
||||
Fl_Output* output_path;
|
||||
Fl_Choice* format_choice;
|
||||
Fl_Button* convert_btn;
|
||||
|
||||
static void input_file_cb(Fl_Widget* w, void* data);
|
||||
static void output_file_cb(Fl_Widget* w, void* data);
|
||||
static void convert_cb(Fl_Widget* w, void* data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/image_loader.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include <string>
|
||||
|
||||
class ImageLoader {
|
||||
public:
|
||||
static ImageData load(const std::string& path);
|
||||
static bool save_png(const std::string& path, const ImageData& data);
|
||||
static bool save_jpg(const std::string& path, const ImageData& data, int quality = 90);
|
||||
static void validate_image(const unsigned char* data, int width, int height);
|
||||
|
||||
private:
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/jpeg_to_avif.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class JpegToAvifConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/jpeg_to_bmp.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpegToBmpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/jpeg_to_png.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpegToPngConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/jpeg_to_tiff.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpegToTiffConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/jpeg_to_webp.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpegToWebpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/jpg_to_avif.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class JpgToAvifConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/jpg_to_bmp.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpgToBmpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/jpg_to_png.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpgToPngConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int compression_level = 6);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/jpg_to_tiff.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpgToTiffConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/jpg_to_webp.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class JpgToWebpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/png_to_avif.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class PngToAvifConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/png_to_bmp.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class PngToBmpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/png_to_jpeg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class PngToJpegConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/png_to_jpg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class PngToJpgConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/png_to_tiff.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class PngToTiffConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/png_to_webp.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class PngToWebpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/tiff_to_avif.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class TiffToAvifConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/tiff_to_bmp.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class TiffToBmpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/tiff_to_jpeg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class TiffToJpegConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/tiff_to_jpg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class TiffToJpgConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/tiff_to_png.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class TiffToPngConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int compression_level = 6);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/tiff_to_webp.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class TiffToWebpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
11
others/C++/Image_format_converter/include/webp_to_avif.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "converter_base.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
class WebpToAvifConverter : public ConverterBase {
|
||||
public:
|
||||
bool convert(const std::string& input, const std::string& output) override;
|
||||
|
||||
protected:
|
||||
bool validate(const ImageData& data) override;
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/webp_to_bmp.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class WebpToBmpConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/webp_to_jpeg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class WebpToJpegConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
13
others/C++/Image_format_converter/include/webp_to_jpg.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class WebpToJpgConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality = 90);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/webp_to_png.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class WebpToPngConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
12
others/C++/Image_format_converter/include/webp_to_tiff.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "common.hpp"
|
||||
|
||||
class WebpToTiffConverter {
|
||||
public:
|
||||
static bool convert(const std::string& input_path,
|
||||
const std::string& output_path);
|
||||
|
||||
private:
|
||||
static bool validate_input(const ImageData& data);
|
||||
};
|
||||
31
others/C++/Image_format_converter/src/avif_to_bmp.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "avif_to_bmp.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
bool AvifToBmpConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载AVIF图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为BMP
|
||||
return stbi_write_bmp(output_path.c_str(),
|
||||
data.width,
|
||||
data.height,
|
||||
data.channels,
|
||||
data.pixels.get());
|
||||
}
|
||||
|
||||
bool AvifToBmpConverter::validate(const ImageData& data) {
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
32
others/C++/Image_format_converter/src/avif_to_jpeg.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "avif_to_jpeg.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
bool AvifToJpegConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载AVIF图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为JPEG(默认质量90)
|
||||
return stbi_write_jpg(output_path.c_str(),
|
||||
data.width,
|
||||
data.height,
|
||||
data.channels,
|
||||
data.pixels.get(),
|
||||
90);
|
||||
}
|
||||
|
||||
bool AvifToJpegConverter::validate(const ImageData& data) {
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
32
others/C++/Image_format_converter/src/avif_to_jpg.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "avif_to_jpg.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
bool AvifToJpgConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载AVIF图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为JPG(默认质量90)
|
||||
return stbi_write_jpg(output_path.c_str(),
|
||||
data.width,
|
||||
data.height,
|
||||
data.channels,
|
||||
data.pixels.get(),
|
||||
90);
|
||||
}
|
||||
|
||||
bool AvifToJpgConverter::validate(const ImageData& data) {
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
33
others/C++/Image_format_converter/src/avif_to_png.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "avif_to_png.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
|
||||
bool AvifToPngConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载AVIF图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为PNG
|
||||
return stbi_write_png(output_path.c_str(),
|
||||
data.width,
|
||||
data.height,
|
||||
data.channels,
|
||||
data.pixels.get(),
|
||||
data.width * data.channels);
|
||||
}
|
||||
|
||||
bool AvifToPngConverter::validate(const ImageData& data) {
|
||||
// 确保是有效的图像数据
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
65
others/C++/Image_format_converter/src/avif_to_tiff.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "avif_to_tiff.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <tiffio.h>
|
||||
|
||||
bool AvifToTiffConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载AVIF图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建TIFF文件
|
||||
TIFF* tif = TIFFOpen(output_path.c_str(), "w");
|
||||
if (!tif) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置TIFF标签
|
||||
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, data.width);
|
||||
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, data.height);
|
||||
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, data.channels);
|
||||
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
|
||||
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,
|
||||
(data.channels == 1) ? PHOTOMETRIC_MINISBLACK :
|
||||
(data.channels == 3) ? PHOTOMETRIC_RGB : PHOTOMETRIC_RGB);
|
||||
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
|
||||
|
||||
// 写入图像数据
|
||||
tsize_t linebytes = data.width * data.channels;
|
||||
unsigned char* buf = nullptr;
|
||||
if (TIFFScanlineSize(tif) == linebytes) {
|
||||
buf = (unsigned char*)_TIFFmalloc(linebytes);
|
||||
} else {
|
||||
buf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(tif));
|
||||
}
|
||||
|
||||
for (uint32_t row = 0; row < data.height; row++) {
|
||||
memcpy(buf, &data.pixels.get()[row * linebytes], linebytes);
|
||||
if (TIFFWriteScanline(tif, buf, row, 0) < 0) {
|
||||
_TIFFfree(buf);
|
||||
TIFFClose(tif);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
_TIFFfree(buf);
|
||||
TIFFClose(tif);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AvifToTiffConverter::validate(const ImageData& data) {
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 1 || data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
50
others/C++/Image_format_converter/src/avif_to_webp.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "avif_to_webp.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <webp/encode.h>
|
||||
|
||||
bool AvifToWebpConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载AVIF图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为WEBP
|
||||
FILE* f = fopen(output_path.c_str(), "wb");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* output = nullptr;
|
||||
size_t size = 0;
|
||||
if (data.channels == 3) {
|
||||
size = WebPEncodeRGB(data.pixels.get(), data.width, data.height,
|
||||
data.width * 3, 90, &output);
|
||||
} else if (data.channels == 4) {
|
||||
size = WebPEncodeRGBA(data.pixels.get(), data.width, data.height,
|
||||
data.width * 4, 90, &output);
|
||||
}
|
||||
|
||||
if (size == 0 || !output) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fwrite(output, 1, size, f);
|
||||
fclose(f);
|
||||
WebPFree(output);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AvifToWebpConverter::validate(const ImageData& data) {
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
71
others/C++/Image_format_converter/src/bmp_to_avif.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "bmp_to_avif.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <avif/avif.h>
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
bool BmpToAvifConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path) {
|
||||
// 加载BMP图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建AVIF编码器
|
||||
avifEncoder* encoder = avifEncoderCreate();
|
||||
encoder->maxThreads = 4;
|
||||
encoder->minQuantizer = 20;
|
||||
encoder->maxQuantizer = 20;
|
||||
encoder->speed = 6;
|
||||
|
||||
// 创建AVIF图像
|
||||
avifImage* image = avifImageCreate(data.width, data.height, 8, AVIF_PIXEL_FORMAT_YUV420);
|
||||
avifRGBImage rgbImage;
|
||||
avifRGBImageSetDefaults(&rgbImage, image);
|
||||
rgbImage.pixels = data.pixels.get();
|
||||
rgbImage.rowBytes = data.width * data.channels;
|
||||
rgbImage.format = (data.channels == 3) ? AVIF_RGB_FORMAT_RGB : AVIF_RGB_FORMAT_RGBA;
|
||||
|
||||
// 转换RGB到YUV
|
||||
if (avifImageRGBToYUV(image, &rgbImage) != AVIF_RESULT_OK) {
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 编码AVIF图像
|
||||
avifRWData output = AVIF_DATA_EMPTY;
|
||||
if (avifEncoderWrite(encoder, image, &output) != AVIF_RESULT_OK) {
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存AVIF文件
|
||||
FILE* f = fopen(output_path.c_str(), "wb");
|
||||
if (!f) {
|
||||
avifRWDataFree(&output);
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
return false;
|
||||
}
|
||||
fwrite(output.data, 1, output.size, f);
|
||||
fclose(f);
|
||||
|
||||
// 清理资源
|
||||
avifRWDataFree(&output);
|
||||
avifEncoderDestroy(encoder);
|
||||
avifImageDestroy(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BmpToAvifConverter::validate(const ImageData& data) {
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
35
others/C++/Image_format_converter/src/bmp_to_jpeg.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "bmp_to_jpeg.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <stb/stb_image.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
#include <string>
|
||||
|
||||
bool BmpToJpegConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality) {
|
||||
// 加载BMP图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate_input(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为JPEG
|
||||
return stbi_write_jpg(output_path.c_str(),
|
||||
data.width,
|
||||
data.height,
|
||||
data.channels,
|
||||
data.pixels.get(),
|
||||
quality);
|
||||
}
|
||||
|
||||
bool BmpToJpegConverter::validate_input(const ImageData& data) {
|
||||
// 确保是有效的图像数据
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||
35
others/C++/Image_format_converter/src/bmp_to_jpg.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "bmp_to_jpg.hpp"
|
||||
#include "common.hpp"
|
||||
#include "image_loader.hpp"
|
||||
#include <stb/stb_image.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
#include <string>
|
||||
|
||||
bool BmpToJpgConverter::convert(const std::string& input_path,
|
||||
const std::string& output_path,
|
||||
int quality) {
|
||||
// 加载BMP图像
|
||||
ImageData data = ImageLoader::load(input_path);
|
||||
if (!data.pixels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证输入格式
|
||||
if (!validate_input(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存为JPG
|
||||
return stbi_write_jpg(output_path.c_str(),
|
||||
data.width,
|
||||
data.height,
|
||||
data.channels,
|
||||
data.pixels.get(),
|
||||
quality);
|
||||
}
|
||||
|
||||
bool BmpToJpgConverter::validate_input(const ImageData& data) {
|
||||
// 确保是有效的图像数据
|
||||
return data.width > 0 && data.height > 0 &&
|
||||
(data.channels == 3 || data.channels == 4);
|
||||
}
|
||||