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();