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
4 changed files with 37 additions and 45 deletions

View File

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

View File

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

View File

@@ -17,15 +17,12 @@
*/ */
package org.jackhuang.hmcl.auth.offline; package org.jackhuang.hmcl.auth.offline;
import javax.imageio.IIOException; import javafx.scene.image.Image;
import javax.imageio.ImageIO; import javafx.scene.image.PixelReader;
import java.awt.image.BufferedImage; import org.jackhuang.hmcl.util.Hex;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigInteger;
import java.net.URL;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
@@ -33,29 +30,21 @@ import java.util.Map;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
public class Texture { public final class Texture {
private final String hash; 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.hash = requireNonNull(hash);
this.data = requireNonNull(data); this.image = requireNonNull(image);
}
public byte[] getData() {
return data;
} }
public String getHash() { public String getHash() {
return hash; return hash;
} }
public InputStream getInputStream() { public Image getImage() {
return new ByteArrayInputStream(data); return image;
}
public int getLength() {
return data.length;
} }
private static final Map<String, Texture> textures = new HashMap<>(); private static final Map<String, Texture> textures = new HashMap<>();
@@ -68,15 +57,17 @@ public class Texture {
return textures.get(hash); return textures.get(hash);
} }
private static String computeTextureHash(BufferedImage img) { private static String computeTextureHash(Image img) {
MessageDigest digest; MessageDigest digest;
try { try {
digest = MessageDigest.getInstance("SHA-256"); digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new RuntimeException(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]; byte[] buf = new byte[4096];
putInt(buf, 0, width); putInt(buf, 0, width);
@@ -84,7 +75,7 @@ public class Texture {
int pos = 8; int pos = 8;
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) { 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) { if (buf[pos + 0] == 0) {
buf[pos + 1] = buf[pos + 2] = buf[pos + 3] = 0; buf[pos + 1] = buf[pos + 2] = buf[pos + 3] = 0;
} }
@@ -99,8 +90,7 @@ public class Texture {
digest.update(buf, 0, pos); digest.update(buf, 0, pos);
} }
byte[] sha256 = digest.digest(); return Hex.encodeHex(digest.digest());
return String.format("%0" + (sha256.length << 1) + "x", new BigInteger(1, sha256));
} }
private static void putInt(byte[] array, int offset, int x) { 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 { public static Texture loadTexture(InputStream in) throws IOException {
if (in == null) return null; if (in == null) return null;
BufferedImage img; Image img;
try (InputStream is = in) { try (InputStream is = in) {
img = ImageIO.read(is); img = new Image(is);
}
if (img == null) {
throw new IIOException("No image found");
} }
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); Texture existent = textures.get(hash);
if (existent != null) { if (existent != null) {
return existent; return existent;
} }
ByteArrayOutputStream buf = new ByteArrayOutputStream(); Texture texture = new Texture(hash, image);
ImageIO.write(img, "png", buf);
Texture texture = new Texture(hash, buf.toByteArray());
existent = textures.putIfAbsent(hash, texture); existent = textures.putIfAbsent(hash, texture);
if (existent != null) { if (existent != null) {
@@ -139,9 +132,4 @@ public class Texture {
return 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; package org.jackhuang.hmcl.auth.offline;
import com.google.gson.reflect.TypeToken; 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.GameProfile;
import org.jackhuang.hmcl.auth.yggdrasil.TextureModel; import org.jackhuang.hmcl.auth.yggdrasil.TextureModel;
import org.jackhuang.hmcl.util.KeyUtils; 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.gson.UUIDTypeAdapter;
import org.jackhuang.hmcl.util.io.HttpServer; import org.jackhuang.hmcl.util.io.HttpServer;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.security.*; import java.security.*;
import java.util.*; import java.util.*;
@@ -127,7 +129,8 @@ public class YggdrasilServer extends HttpServer {
if (Texture.hasTexture(hash)) { if (Texture.hasTexture(hash)) {
Texture texture = Texture.getTexture(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("Etag", String.format("\"%s\"", hash));
response.addHeader("Cache-Control", "max-age=2592000, public"); response.addHeader("Cache-Control", "max-age=2592000, public");
return response; return response;