Render helmet layer in skin preview. Fixed #456
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> image = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Rectangle2D> 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<Rectangle2D> 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();
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ public class AccountListItemSkin extends SkinBase<AccountListItem> {
|
||||
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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -30,7 +30,6 @@ import javafx.scene.image.Image;
|
||||
|
||||
public class AdvancedListItem extends Control {
|
||||
private final ObjectProperty<Image> image = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Rectangle2D> 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<Rectangle2D> viewportProperty() {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
public StringProperty titleProperty() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ public class AdvancedListItemSkin extends SkinBase<AdvancedListItem> {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user