Update Texture (#2037)

* Update Texture

* fix checkstyle
This commit is contained in:
Glavo 2023-02-02 21:25:05 +08:00 committed by GitHub
parent fb573d8205
commit d4398764a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 45 deletions

View File

@ -141,9 +141,9 @@ public class OfflineAccountSkinPane extends StackPane {
return;
}
canvas.updateSkin(
result.getSkin() != null ? new Image(result.getSkin().getInputStream()) : getDefaultTexture(),
result.getSkin() != null ? result.getSkin().getImage() : getDefaultTexture(),
result.getModel() == TextureModel.ALEX,
result.getCape() != null ? new Image(result.getCape().getInputStream()) : null);
result.getCape() != null ? result.getCape().getImage() : null);
}
}).start();
}, skinItem.selectedDataProperty(), cslApiField.textProperty(), skinSelector.valueProperty(), capeSelector.valueProperty());

View File

@ -3,6 +3,7 @@ plugins {
}
dependencies {
api("org.glavo:simple-png-javafx:0.3.0")
api("com.google.code.gson:gson:2.8.1")
api("com.moandjiezana.toml:toml4j:0.7.2")
api("org.tukaani:xz:1.8")

View File

@ -17,15 +17,12 @@
*/
package org.jackhuang.hmcl.auth.offline;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import javafx.scene.image.Image;
import javafx.scene.image.PixelReader;
import org.jackhuang.hmcl.util.Hex;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
@ -33,29 +30,21 @@ import java.util.Map;
import static java.util.Objects.requireNonNull;
public class Texture {
public final class Texture {
private final String hash;
private final byte[] data;
private final Image image;
public Texture(String hash, byte[] data) {
public Texture(String hash, Image image) {
this.hash = requireNonNull(hash);
this.data = requireNonNull(data);
}
public byte[] getData() {
return data;
this.image = requireNonNull(image);
}
public String getHash() {
return hash;
}
public InputStream getInputStream() {
return new ByteArrayInputStream(data);
}
public int getLength() {
return data.length;
public Image getImage() {
return image;
}
private static final Map<String, Texture> textures = new HashMap<>();
@ -68,15 +57,17 @@ public class Texture {
return textures.get(hash);
}
private static String computeTextureHash(BufferedImage img) {
private static String computeTextureHash(Image img) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
int width = img.getWidth();
int height = img.getHeight();
PixelReader reader = img.getPixelReader();
int width = (int) img.getWidth();
int height = (int) img.getHeight();
byte[] buf = new byte[4096];
putInt(buf, 0, width);
@ -84,7 +75,7 @@ public class Texture {
int pos = 8;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
putInt(buf, pos, img.getRGB(x, y));
putInt(buf, pos, reader.getArgb(x, y));
if (buf[pos + 0] == 0) {
buf[pos + 1] = buf[pos + 2] = buf[pos + 3] = 0;
}
@ -99,8 +90,7 @@ public class Texture {
digest.update(buf, 0, pos);
}
byte[] sha256 = digest.digest();
return String.format("%0" + (sha256.length << 1) + "x", new BigInteger(1, sha256));
return Hex.encodeHex(digest.digest());
}
private static void putInt(byte[] array, int offset, int x) {
@ -112,25 +102,28 @@ public class Texture {
public static Texture loadTexture(InputStream in) throws IOException {
if (in == null) return null;
BufferedImage img;
Image img;
try (InputStream is = in) {
img = ImageIO.read(is);
}
if (img == null) {
throw new IIOException("No image found");
img = new Image(is);
}
String hash = computeTextureHash(img);
if (img.isError()) {
throw new IOException("No image found", img.getException());
}
return loadTexture(img);
}
public static Texture loadTexture(Image image) {
if (image == null) return null;
String hash = computeTextureHash(image);
Texture existent = textures.get(hash);
if (existent != null) {
return existent;
}
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ImageIO.write(img, "png", buf);
Texture texture = new Texture(hash, buf.toByteArray());
Texture texture = new Texture(hash, image);
existent = textures.putIfAbsent(hash, texture);
if (existent != null) {
@ -139,9 +132,4 @@ public class Texture {
return texture;
}
public static Texture loadTexture(String url) throws IOException {
if (url == null) return null;
return loadTexture(new URL(url).openStream());
}
}

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.auth.offline;
import com.google.gson.reflect.TypeToken;
import org.glavo.png.javafx.PNGJavaFXUtils;
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
import org.jackhuang.hmcl.util.KeyUtils;
@ -26,6 +27,7 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
import org.jackhuang.hmcl.util.io.HttpServer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.*;
import java.util.*;
@ -127,7 +129,8 @@ public class YggdrasilServer extends HttpServer {
if (Texture.hasTexture(hash)) {
Texture texture = Texture.getTexture(hash);
Response response = newFixedLengthResponse(Response.Status.OK, "image/png", texture.getInputStream(), texture.getLength());
byte[] data = PNGJavaFXUtils.writeImageToArray(texture.getImage());
Response response = newFixedLengthResponse(Response.Status.OK, "image/png", new ByteArrayInputStream(data), data.length);
response.addHeader("Etag", String.format("\"%s\"", hash));
response.addHeader("Cache-Control", "max-age=2592000, public");
return response;