From 6d346a3ad6aa8bc158295cab1f2adef88f381b00 Mon Sep 17 00:00:00 2001 From: andylizi Date: Sun, 23 Sep 2018 22:53:23 +0800 Subject: [PATCH 1/3] Render helmet layer in skin preview. Fixed #456 --- .../jackhuang/hmcl/game/AccountHelper.java | 52 +++++++++++++++++-- .../ui/account/AccountAdvancedListItem.java | 10 ++-- .../hmcl/ui/account/AccountListItem.java | 23 +++----- .../hmcl/ui/account/AccountListItemSkin.java | 1 - .../hmcl/ui/account/AddAccountPane.java | 8 +-- .../hmcl/ui/construct/AdvancedListItem.java | 5 -- .../ui/construct/AdvancedListItemSkin.java | 1 - 7 files changed, 65 insertions(+), 35 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java index 0a137e70f..29d65925b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java @@ -17,7 +17,7 @@ */ package org.jackhuang.hmcl.game; -import javafx.geometry.Rectangle2D; +import javafx.embed.swing.SwingFXUtils; import javafx.scene.image.Image; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.auth.Account; @@ -32,6 +32,7 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.DialogController; import org.jackhuang.hmcl.util.io.NetworkUtils; +import java.awt.image.BufferedImage; import java.io.File; import java.util.*; @@ -91,9 +92,52 @@ public final class AccountHelper { return scale(url, scaleRatio); } - public static Rectangle2D getViewport(double scaleRatio) { - double size = 8.0 * scaleRatio; - return new Rectangle2D(size, size, size, size); + public static Image getHead(Image skin, int scaleRatio) { + int size = 8 * scaleRatio; + BufferedImage image = SwingFXUtils.fromFXImage(skin, null); + BufferedImage head = image.getSubimage(size, size, size, size); + if (image.getHeight() > 32) { + int[] face = head.getRGB(0, 0, size, size, null, 0, size); + int[] helmet = image.getRGB(40 * scaleRatio, 8 * scaleRatio, size, size, null, 0, size); + int[] result = blendColor(face, helmet); + head.setRGB(0, 0, size, size, result, 0, size); + } + return SwingFXUtils.toFXImage(head, null); + } + + private static int[] blendColor(int[] bottom, int[] top) { + if (bottom.length != top.length) throw new IllegalArgumentException(); + int[] result = new int[bottom.length]; + for (int i = 0; i < bottom.length; i++) { + int b = bottom[i]; + int t = top[i]; + result[i] = blendColor(b, t); + } + return result; + } + + private static int blendColor(int bottom, int top) { + int tAlpha = (top >> 24) & 0xFF; + if (tAlpha == 0) return bottom | 0xFF000000; // set alpha to 255 + if (tAlpha == 255) return top; + + int tRed = (top >> 16) & 0xFF, + tGreen = (top >> 8) & 0xFF, + tBlue = (top) & 0xFF; + int bRed = (bottom >> 16) & 0xFF, + bGreen = (bottom >> 8) & 0xFF, + bBlue = (bottom) & 0xFF; + int cRed = blendColorChannel(bRed, tRed, tAlpha), + cGreen = blendColorChannel(bGreen, tGreen, tAlpha), + cBlue = blendColorChannel(bBlue, tBlue, tAlpha); + return 0xFF000000 | // set alpha to 255 + ((cRed & 0xFF) << 16) | + ((cGreen & 0xFF) << 8) | + ((cBlue & 0xFF)); + } + + private static int blendColorChannel(int bottom, int top, int alpha) { + return (top * alpha + bottom * (255 - alpha)) / 255; } private static class SkinLoadTask extends Task { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java index f78f30212..cbabb6de7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountAdvancedListItem.java @@ -39,18 +39,18 @@ public class AccountAdvancedListItem extends AdvancedListItem { titleProperty().set(i18n("account.missing")); subtitleProperty().set(i18n("account.missing.add")); imageProperty().set(new Image("/assets/img/craft_table.png")); - viewportProperty().set(null); } else { titleProperty().set(account.getCharacter()); subtitleProperty().set(accountSubtitle(account)); - imageProperty().set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); - viewportProperty().set(AccountHelper.getViewport(4)); + final int scaleRatio = 4; + Image defaultSkin = AccountHelper.getDefaultSkin(account.getUUID(), scaleRatio); + imageProperty().set(AccountHelper.getHead(defaultSkin, scaleRatio)); if (account instanceof YggdrasilAccount) { AccountHelper.loadSkinAsync((YggdrasilAccount) account).subscribe(Schedulers.javafx(), () -> { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - imageProperty().set(image); + Image image = AccountHelper.getSkin((YggdrasilAccount) account, scaleRatio); + imageProperty().set(AccountHelper.getHead(image, scaleRatio)); }); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java index fe46ed324..6f721b084 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.ui.account; import javafx.beans.property.*; -import javafx.geometry.Rectangle2D; import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.control.ToggleGroup; @@ -41,7 +40,6 @@ public class AccountListItem extends Control { private final StringProperty subtitle = new SimpleStringProperty(); private final BooleanProperty selected = new SimpleBooleanProperty(); private final ObjectProperty image = new SimpleObjectProperty<>(); - private final ObjectProperty viewport = new SimpleObjectProperty<>(); public AccountListItem(ToggleGroup toggleGroup, Account account) { this.account = account; @@ -60,13 +58,11 @@ public class AccountListItem extends Control { subtitle.set(subtitleString.toString()); selected.set(Accounts.selectedAccountProperty().get() == account); - viewport.set(AccountHelper.getViewport(4)); - if (account instanceof YggdrasilAccount) { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - this.image.set(image); - } else { - this.image.set(AccountHelper.getDefaultSkin(account.getUUID(), 4)); - } + final int scaleRatio = 4; + Image image = account instanceof YggdrasilAccount ? + AccountHelper.getSkin((YggdrasilAccount) account, scaleRatio) : + AccountHelper.getDefaultSkin(account.getUUID(), scaleRatio); + this.image.set(AccountHelper.getHead(image, scaleRatio)); } @Override @@ -98,10 +94,6 @@ public class AccountListItem extends Control { return image; } - public ObjectProperty viewportProperty() { - return viewport; - } - public void refresh() { if (account instanceof YggdrasilAccount) { // progressBar.setVisible(true); @@ -110,8 +102,9 @@ public class AccountListItem extends Control { // progressBar.setVisible(false); if (isDependentsSucceeded) { - Image image = AccountHelper.getSkin((YggdrasilAccount) account, 4); - this.image.set(image); + final int scaleRatio = 4; + Image image = AccountHelper.getSkin((YggdrasilAccount) account, scaleRatio); + this.image.set(AccountHelper.getHead(image, scaleRatio)); } }).start(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java index 26911fce3..b4713b3dc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItemSkin.java @@ -59,7 +59,6 @@ public class AccountListItemSkin extends SkinBase { ImageView imageView = new ImageView(); FXUtils.limitSize(imageView, 32, 32); imageView.imageProperty().bind(skinnable.imageProperty()); - imageView.viewportProperty().bind(skinnable.viewportProperty()); imageViewContainer.getChildren().setAll(imageView); TwoLineListItem item = new TwoLineListItem(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java index aaf04f7ce..c4aeb0b3a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java @@ -221,17 +221,17 @@ public class AddAccountPane extends StackPane { for (GameProfile profile : names) { Image image; + final int scaleRatio = 4; try { - image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, 4); + image = AccountHelper.getSkinImmediately(yggdrasilAccount, profile, scaleRatio); } catch (Exception e) { Logging.LOG.log(Level.WARNING, "Failed to get skin for " + profile.getName(), e); - image = AccountHelper.getDefaultSkin(profile.getId(), 4); + image = AccountHelper.getDefaultSkin(profile.getId(), scaleRatio); } ImageView portraitView = new ImageView(); portraitView.setSmooth(false); - portraitView.setImage(image); - portraitView.setViewport(AccountHelper.getViewport(4)); + portraitView.setImage(AccountHelper.getHead(image, scaleRatio)); FXUtils.limitSize(portraitView, 32, 32); IconedItem accountItem = new IconedItem(portraitView, profile.getName()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java index a7141c3ad..6fb1ad8b5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItem.java @@ -30,7 +30,6 @@ import javafx.scene.image.Image; public class AdvancedListItem extends Control { private final ObjectProperty image = new SimpleObjectProperty<>(); - private final ObjectProperty viewport = new SimpleObjectProperty<>(); private final StringProperty title = new SimpleStringProperty(); private final StringProperty subtitle = new SimpleStringProperty(); @@ -38,10 +37,6 @@ public class AdvancedListItem extends Control { return image; } - public ObjectProperty viewportProperty() { - return viewport; - } - public StringProperty titleProperty() { return title; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java index e97311143..f743f99b3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/AdvancedListItemSkin.java @@ -54,7 +54,6 @@ public class AdvancedListItemSkin extends SkinBase { FXUtils.limitSize(imageView, 32, 32); imageView.setPreserveRatio(true); imageView.imageProperty().bind(skinnable.imageProperty()); - imageView.viewportProperty().bind(skinnable.viewportProperty()); imageViewContainer.getChildren().setAll(imageView); VBox vbox = new VBox(); From 92cfc3c1d3e187677235bab53cf7d7063509bcd2 Mon Sep 17 00:00:00 2001 From: andylizi Date: Mon, 24 Sep 2018 13:47:59 +0800 Subject: [PATCH 2/3] Make skin preview more accurate --- .../jackhuang/hmcl/game/AccountHelper.java | 50 ++++--------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java index 29d65925b..751e1ba78 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java @@ -32,6 +32,7 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.DialogController; import org.jackhuang.hmcl.util.io.NetworkUtils; +import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.util.*; @@ -93,53 +94,20 @@ public final class AccountHelper { } public static Image getHead(Image skin, int scaleRatio) { - int size = 8 * scaleRatio; + final int size = 8 * scaleRatio; + final int faceOffset = (int) Math.ceil(0.25 * scaleRatio); BufferedImage image = SwingFXUtils.fromFXImage(skin, null); - BufferedImage head = image.getSubimage(size, size, size, size); + BufferedImage head = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = head.createGraphics(); + g2d.drawImage(image, faceOffset, faceOffset, size - faceOffset, size - faceOffset, + size, size, size + size, size + size, null); if (image.getHeight() > 32) { - int[] face = head.getRGB(0, 0, size, size, null, 0, size); - int[] helmet = image.getRGB(40 * scaleRatio, 8 * scaleRatio, size, size, null, 0, size); - int[] result = blendColor(face, helmet); - head.setRGB(0, 0, size, size, result, 0, size); + g2d.drawImage(image, 0, 0, size, size, + 40 * scaleRatio, 8 * scaleRatio, 48 * scaleRatio, 16 * scaleRatio, null); } return SwingFXUtils.toFXImage(head, null); } - private static int[] blendColor(int[] bottom, int[] top) { - if (bottom.length != top.length) throw new IllegalArgumentException(); - int[] result = new int[bottom.length]; - for (int i = 0; i < bottom.length; i++) { - int b = bottom[i]; - int t = top[i]; - result[i] = blendColor(b, t); - } - return result; - } - - private static int blendColor(int bottom, int top) { - int tAlpha = (top >> 24) & 0xFF; - if (tAlpha == 0) return bottom | 0xFF000000; // set alpha to 255 - if (tAlpha == 255) return top; - - int tRed = (top >> 16) & 0xFF, - tGreen = (top >> 8) & 0xFF, - tBlue = (top) & 0xFF; - int bRed = (bottom >> 16) & 0xFF, - bGreen = (bottom >> 8) & 0xFF, - bBlue = (bottom) & 0xFF; - int cRed = blendColorChannel(bRed, tRed, tAlpha), - cGreen = blendColorChannel(bGreen, tGreen, tAlpha), - cBlue = blendColorChannel(bBlue, tBlue, tAlpha); - return 0xFF000000 | // set alpha to 255 - ((cRed & 0xFF) << 16) | - ((cGreen & 0xFF) << 8) | - ((cBlue & 0xFF)); - } - - private static int blendColorChannel(int bottom, int top, int alpha) { - return (top * alpha + bottom * (255 - alpha)) / 255; - } - private static class SkinLoadTask extends Task { private final YggdrasilAccount account; private final boolean refresh; From c8e8b25c858eb0106745ffaf495b49c94c7040ef Mon Sep 17 00:00:00 2001 From: andylizi Date: Mon, 24 Sep 2018 15:34:48 +0800 Subject: [PATCH 3/3] Adjust `faceOffset` in skin preview --- HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java index 751e1ba78..193f036ce 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/AccountHelper.java @@ -95,7 +95,7 @@ public final class AccountHelper { public static Image getHead(Image skin, int scaleRatio) { final int size = 8 * scaleRatio; - final int faceOffset = (int) Math.ceil(0.25 * scaleRatio); + final int faceOffset = (int) Math.floor(scaleRatio * 4d / 9d); BufferedImage image = SwingFXUtils.fromFXImage(skin, null); BufferedImage head = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = head.createGraphics();