4 Commits

Author SHA1 Message Date
6e7a1bfbee Create PNG Image Compress 2026-09-20 22:58:46 +08:00
bc5bfe0d00 Create PNG Image Compress 2026-09-20 22:57:12 +08:00
341ec1b0f6 修复了一些BUG 2026-08-14 04:46:38 +08:00
9817a41801 INFO.md 2026-08-14 00:26:55 +08:00
6 changed files with 315 additions and 18 deletions

14
INFO.md
View File

@@ -1,4 +1,4 @@
1.3-SNAPSHOT
1.5-SNAPSHOT
此版本包含:
1. 压缩/解压 (`compress`)
@@ -6,11 +6,15 @@
2.1 预定义颜色打印 — `ColorPrintln`
2.2 自定义颜色打印 — `ColorHexPrintln`
2.3 Minecraft 风格多色打印 — `ColorPrintlnPlus`
3. 随机生成器 (`random`)
4. 硬件标识生成器 (`hwid`)
5. 版本信息 (`version`)
6. 运行时信息 (`runtime`)
3. 文件创建与保存 (`file/CreateFile`)
4. 文件加密/解密 (`file/EncryptionAndDecryption`)
5. 随机生成器 (`random`)
6. 硬件标识生成器 (`hwid`)
7. 版本信息 (`version`)
8. 运行时信息 (`runtime`)
6.1 获取运行时临时目录
6.2 新建临时目录
6.3 获取临时目录
6.4 获取当前运行的JAR文件路径
在 1.4-SNAPSHOT 的基础上调整了API接口修复了一些BUG

View File

@@ -6,7 +6,7 @@
<groupId>top.gtb520.java.gnu</groupId>
<artifactId>gnu_java_utils_library</artifactId>
<version>1.4-SNAPSHOT</version>
<version>1.6-SNAPSHOT</version>
<properties>
<maven.compiler.source>25</maven.compiler.source>

View File

@@ -1,6 +1,7 @@
package top.gtb520.java.gnu.java.utils.library;
import top.gtb520.java.gnu.java.utils.library.utils.print.ColorNameEnum;
import top.gtb520.java.gnu.java.utils.library.utils.compress.CompressPNG.PNGThreshold;
import java.io.File;
import java.nio.file.Path;
@@ -31,6 +32,11 @@ public interface main {
static void UnZip(String sourceFilePanth, String destFilePanth) throws Exception {
top.gtb520.java.gnu.java.utils.library.utils.compress.compress.UnZip(sourceFilePanth, destFilePanth);
}
// 压缩png图片
static File CompressPNG(File pngFile, PNGThreshold threshold) {
return top.gtb520.java.gnu.java.utils.library.utils.compress.CompressPNG.PNG(pngFile, threshold);
}
}
// 终端打印接口与方法
@@ -148,7 +154,6 @@ public interface main {
return top.gtb520.java.gnu.java.utils.library.utils.file.CreateFile.StreamSaveFile(file, TargePath, FileName);
}
}
}
// 文件加解密操作
interface EncryptionAndDecryption {
@@ -162,3 +167,4 @@ public interface main {
}
}
}
}

View File

@@ -0,0 +1,82 @@
package top.gtb520.java.gnu.java.utils.library.utils.compress;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.io.File;
import java.awt.image.BufferedImage;
import java.util.Iterator;
public class CompressPNG {
/***
* 压缩PNG图片
* 传入未压缩的PNG图片文件和压缩阈值返回压缩后的PNG图片文件
* 5个等级的压缩阈值从低到高THRESHOLD_1 最低THRESHOLD_5 最高
*
* @param pngFile 未压缩的PNG图片文件
* @param threshold 压缩阈值
* @return 压缩后的PNG图片文件
*/
public static File PNG(File pngFile, PNGThreshold threshold) {
if (!pngFile.exists()) {
System.out.println("文件不存在");
return null;
}
if (pngFile.getName().endsWith(".png") || pngFile.getName().endsWith(".PNG")) {
try {
BufferedImage image = ImageIO.read(pngFile);
if (image == null) {
System.out.println("无法读取PNG文件");
return null;
}
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("png");
if (!writers.hasNext()) {
System.out.println("未找到PNG写入器");
return null;
}
ImageWriter writer = writers.next();
try (ImageOutputStream ios = ImageIO.createImageOutputStream(pngFile)) {
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(getCompressionQuality(threshold));
writer.write(null, new IIOImage(image, null, null), param);
} finally {
writer.dispose();
}
return pngFile;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else {
System.out.println("文件不是PNG格式无法压缩");
}
return null;
}
private static float getCompressionQuality(PNGThreshold threshold) {
return switch (threshold) {
case THRESHOLD_1 -> 0.7f;
case THRESHOLD_2 -> 0.5f;
case THRESHOLD_3 -> 0.3f;
case THRESHOLD_4 -> 0.1f;
case THRESHOLD_5 -> 0.0f;
};
}
public enum PNGThreshold {
THRESHOLD_1,
THRESHOLD_2,
THRESHOLD_3,
THRESHOLD_4,
THRESHOLD_5;
}
}

View File

@@ -2,7 +2,7 @@ package top.gtb520.java.gnu.java.utils.library;
public class version {
// 项目的版本信息,不代表实时运行环境
public static final String VERSION = "1.4";
public static final String VERSION = "1.5";
public static final String JAVA_VERSION = "25";
public static final String OpenJDK_VERSION = "25.0.0.0.1";
public static final String OpenJDK_Runtime_Environment = "25.0.0.0.1+36-GA";

View File

@@ -0,0 +1,205 @@
package top.gtb520.java.gnu.java.utils.library.utils.compress;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class CompressPNGTest {
static int passed = 0;
static int failed = 0;
public static void main(String[] args) {
System.out.println("========== CompressPNG 测试开始 ==========");
testCompressWithEachThreshold();
testNonPNGFile();
testNonExistentFile();
testCompressedFileIsValidPNG();
testCompressedFileSmallerOrEqual();
System.out.println("========================================");
System.out.println("测试完成: 通过 " + passed + ", 失败 " + failed);
if (failed > 0) {
System.exit(1);
}
}
/**
* 测试每个压缩阈值均能正常压缩并返回原文件
*/
static void testCompressWithEachThreshold() {
String testName = "testCompressWithEachThreshold";
for (CompressPNG.PNGThreshold threshold : CompressPNG.PNGThreshold.values()) {
File pngFile = null;
try {
pngFile = createTestPNG("test_threshold_" + threshold.name());
long originalSize = pngFile.length();
File result = CompressPNG.PNG(pngFile, threshold);
boolean returnedSameFile = result != null && result.equals(pngFile);
assertCondition(testName + " [" + threshold.name() + "] - 应返回原文件引用", returnedSameFile);
boolean fileExists = result != null && result.exists() && result.length() > 0;
assertCondition(testName + " [" + threshold.name() + "] - 压缩后文件应存在且非空", fileExists);
System.out.println(" [" + threshold.name() + "] 原始: " + originalSize + " bytes, 压缩后: " + (result != null ? result.length() : "N/A") + " bytes");
} catch (Exception e) {
assertCondition(testName + " [" + threshold.name() + "] - 不应抛出异常: " + e.getMessage(), false);
} finally {
deleteTestFile(pngFile);
}
}
}
/**
* 测试传入非PNG文件应返回null
*/
static void testNonPNGFile() {
String testName = "testNonPNGFile";
File txtFile = null;
try {
txtFile = File.createTempFile("test_not_png", ".txt");
java.nio.file.Files.writeString(txtFile.toPath(), "this is not a png");
File result = CompressPNG.PNG(txtFile, CompressPNG.PNGThreshold.THRESHOLD_3);
assertCondition(testName + " - 非PNG文件应返回null", result == null);
} catch (Exception e) {
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
} finally {
deleteTestFile(txtFile);
}
}
/**
* 测试传入不存在的文件应返回null
*/
static void testNonExistentFile() {
String testName = "testNonExistentFile";
try {
File nonExistent = new File(System.getProperty("java.io.tmpdir"), "non_existent_file.png");
File result = CompressPNG.PNG(nonExistent, CompressPNG.PNGThreshold.THRESHOLD_3);
assertCondition(testName + " - 不存在的文件应返回null", result == null);
} catch (Exception e) {
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
}
}
/**
* 测试压缩后的文件仍然是有效的PNG图片
*/
static void testCompressedFileIsValidPNG() {
String testName = "testCompressedFileIsValidPNG";
File pngFile = null;
try {
pngFile = createTestPNG("test_valid_png");
CompressPNG.PNG(pngFile, CompressPNG.PNGThreshold.THRESHOLD_3);
BufferedImage compressed = ImageIO.read(pngFile);
boolean isValidPNG = compressed != null;
assertCondition(testName + " - 压缩后的文件应是有效的PNG图片", isValidPNG);
if (isValidPNG) {
assertCondition(testName + " - 压缩后图片宽度应一致", compressed.getWidth() == 100);
assertCondition(testName + " - 压缩后图片高度应一致", compressed.getHeight() == 100);
}
} catch (Exception e) {
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
} finally {
deleteTestFile(pngFile);
}
}
/**
* 测试压缩后文件大小不超过原始文件
*/
static void testCompressedFileSmallerOrEqual() {
String testName = "testCompressedFileSmallerOrEqual";
File pngFile = null;
try {
// 创建一个较大的、有压缩空间的测试图片(纯色+渐变)
pngFile = createLargeTestPNG("test_compression_ratio", 500, 500);
long originalSize = pngFile.length();
File result = CompressPNG.PNG(pngFile, CompressPNG.PNGThreshold.THRESHOLD_5);
if (result != null) {
long compressedSize = result.length();
assertCondition(testName + " - 压缩后文件大小(" + compressedSize + ")应不超过原始大小(" + originalSize + ")",
compressedSize <= originalSize);
System.out.println(" 原始: " + originalSize + " bytes, THRESHOLD_5压缩后: " + compressedSize + " bytes");
} else {
assertCondition(testName + " - 压缩不应返回null", false);
}
} catch (Exception e) {
assertCondition(testName + " - 不应抛出异常: " + e.getMessage(), false);
} finally {
deleteTestFile(pngFile);
}
}
// ========== 辅助方法 ==========
/**
* 创建一个100x100的测试PNG图片
*/
static File createTestPNG(String name) throws IOException {
File file = File.createTempFile(name, ".png");
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
// 绘制渐变图案,使图片有一定的压缩空间
for (int y = 0; y < 100; y++) {
for (int x = 0; x < 100; x++) {
image.setRGB(x, y, new Color(x * 2, y * 2, (x + y)).getRGB());
}
}
g2d.dispose();
ImageIO.write(image, "png", file);
return file;
}
/**
* 创建一个指定尺寸的测试PNG图片包含更多颜色变化以增加压缩效果差异
*/
static File createLargeTestPNG(String name, int width, int height) throws IOException {
File file = File.createTempFile(name, ".png");
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
// 绘制大面积纯色区域(压缩率高)
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, width / 2, height / 2);
g2d.setColor(Color.BLUE);
g2d.fillRect(width / 2, 0, width / 2, height / 2);
g2d.setColor(Color.GREEN);
g2d.fillRect(0, height / 2, width / 2, height / 2);
g2d.setColor(Color.YELLOW);
g2d.fillRect(width / 2, height / 2, width / 2, height / 2);
g2d.dispose();
ImageIO.write(image, "png", file);
return file;
}
/**
* 安全删除测试文件
*/
static void deleteTestFile(File file) {
if (file != null && file.exists()) {
file.delete();
}
}
static void assertCondition(String message, boolean condition) {
if (condition) {
System.out.println("[PASS] " + message);
passed++;
} else {
System.out.println("[FAIL] " + message);
failed++;
}
}
}