Render helmet layer in skin preview. Fixed #456

This commit is contained in:
andylizi
2018-09-23 22:53:23 +08:00
parent 6e2696dea3
commit 6d346a3ad6
7 changed files with 65 additions and 35 deletions

View File

@@ -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 {

View File

@@ -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));
});
}
}

View File

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

View File

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

View File

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

View File

@@ -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;
}

View File

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