-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathGenerateThumbnails.java
More file actions
164 lines (134 loc) · 5.91 KB
/
GenerateThumbnails.java
File metadata and controls
164 lines (134 loc) · 5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/// usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 25+
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
void main(String... args) throws IOException, NoSuchAlgorithmException {
System.out.println("Starting thumbnail generation...");
Path imagesDir = Paths.get("images");
Path hashFile = imagesDir.resolve(".thumbnail-hashes");
if (!Files.exists(imagesDir)) {
System.out.println("Images directory not found!");
return;
}
// Load existing hashes
Map<String, String> existingHashes = loadHashes(hashFile);
Map<String, String> currentHashes = new HashMap<>();
int generated = 0;
int skipped = 0;
// Process all PNG files in the images directory
Files.list(imagesDir)
.filter(path -> path.toString().toLowerCase().endsWith(".png"))
.filter(path -> !path.getFileName().toString().startsWith("thumbnail-"))
.filter(path -> !path.getFileName().toString().equals(".thumbnail-hashes"))
.forEach(imagePath -> {
try {
String fileName = imagePath.getFileName().toString();
System.out.println("Processing: " + fileName);
// Calculate hash of the original image
String imageHash = calculateHash(imagePath);
currentHashes.put(fileName, imageHash);
// Determine thumbnail filename
String thumbnailBaseName = fileName.startsWith("ui-")
? fileName.substring("ui-".length())
: fileName.substring(fileName.lastIndexOf('-') + 1);
String thumbnailName = "thumbnail-" + thumbnailBaseName;
Path thumbnailPath = imagesDir.resolve(thumbnailName);
// Check if thumbnail needs to be generated
String existingHash = existingHashes.get(fileName);
if (existingHash != null && existingHash.equals(imageHash) && Files.exists(thumbnailPath)) {
System.out.println(" Skipped (unchanged): " + thumbnailName);
return;
}
// Generate thumbnail
generateThumbnail(imagePath, thumbnailPath, 540);
System.out.println(" Generated: " + thumbnailName);
} catch (Exception e) {
System.err.println("Error processing " + imagePath + ": " + e.getMessage());
e.printStackTrace();
}
});
// Save updated hashes
saveHashes(hashFile, currentHashes);
System.out.println("\nThumbnail generation completed!");
System.out.println("Check the images directory for generated thumbnails.");
}
/**
* Generate a thumbnail with a maximum height while maintaining aspect ratio
*/
private static void generateThumbnail(Path sourcePath, Path targetPath, int maxHeight) throws IOException {
BufferedImage originalImage = ImageIO.read(sourcePath.toFile());
if (originalImage == null) {
throw new IOException("Unable to read image: " + sourcePath);
}
int originalWidth = originalImage.getWidth();
int originalHeight = originalImage.getHeight();
// Calculate new dimensions maintaining aspect ratio
int newHeight = maxHeight;
int newWidth = (int) ((double) originalWidth / originalHeight * maxHeight);
// If the image is already smaller than maxHeight, use original dimensions
if (originalHeight <= maxHeight) {
newHeight = originalHeight;
newWidth = originalWidth;
}
// Create thumbnail with high-quality scaling
BufferedImage thumbnail = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = thumbnail.createGraphics();
// Enable high-quality rendering
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
g2d.dispose();
// Write thumbnail to file
ImageIO.write(thumbnail, "PNG", targetPath.toFile());
}
/**
* Calculate SHA-256 hash of a file
*/
private static String calculateHash(Path filePath) throws IOException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] fileBytes = Files.readAllBytes(filePath);
byte[] hashBytes = digest.digest(fileBytes);
// Convert to hex string
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
/**
* Load existing hashes from file
*/
private static Map<String, String> loadHashes(Path hashFile) throws IOException {
Map<String, String> hashes = new HashMap<>();
if (Files.exists(hashFile)) {
Files.readAllLines(hashFile).forEach(line -> {
String[] parts = line.split("=", 2);
if (parts.length == 2) {
hashes.put(parts[0], parts[1]);
}
});
}
return hashes;
}
/**
* Save hashes to file
*/
private static void saveHashes(Path hashFile, Map<String, String> hashes) throws IOException {
StringBuilder content = new StringBuilder();
hashes.forEach((fileName, hash) -> {
content.append(fileName).append("=").append(hash).append("\n");
});
Files.writeString(hashFile, content.toString());
}