Refactor cards and token images code (#10139):

- removed outdated token store format (example: BIRD.W.BIRD.CREATURE.1.1.full.jpg)
 - removed duplicated cache images code;
 - removed duplicated set code fields;
 - removed outdated auto-generated thumb files (also all *.thumb.zip files will be deleted on startup);
This commit is contained in:
Oleg Agafonov 2023-03-24 06:44:11 +04:00
parent d93e533c75
commit 66d0ef4b35
25 changed files with 333 additions and 634 deletions

View file

@ -293,7 +293,11 @@ public class CardIconsPanel extends JPanel {
//BufferedImage iconImage = ImageManagerImpl.instance.getCardIcon(icon.getIconType().getResourceName(), this.halfSize * 2); //BufferedImage iconImage = ImageManagerImpl.instance.getCardIcon(icon.getIconType().getResourceName(), this.halfSize * 2);
// cached call // cached call
BufferedImage iconImageCached = ImageCache.getCardIconImage(icon.getIconType().getResourceName(), this.halfSize * 2, color.toString()); BufferedImage iconImageCached = ImageCache.getCardIconImage(
icon.getIconType().getResourceName(),
this.halfSize * 2,
color.toString()
).getImage();
if (iconImageCached != null && this.font != null) { if (iconImageCached != null && this.font != null) {
BufferedImage iconImageWithText = ImageManagerImpl.deepCopy(iconImageCached); // must copy cached value before modify BufferedImage iconImageWithText = ImageManagerImpl.deepCopy(iconImageCached); // must copy cached value before modify

View file

@ -51,7 +51,6 @@ public final class Constants {
// cards render // cards render
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149); public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
public static final Rectangle THUMBNAIL_SIZE_FULL = new Rectangle(102, 146);
// resources - default images // resources - default images
public static final String RESOURCE_PATH_DEFAULT_IMAGES = File.separator + "default"; public static final String RESOURCE_PATH_DEFAULT_IMAGES = File.separator + "default";

View file

@ -677,7 +677,7 @@ public class MageActionCallback implements ActionCallback {
switch (enlargeMode) { switch (enlargeMode) {
case COPY: case COPY:
if (cardView instanceof PermanentView) { if (cardView instanceof PermanentView) {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()); image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()).getImage();
} }
break; break;
case ALTERNATE: case ALTERNATE:
@ -686,9 +686,9 @@ public class MageActionCallback implements ActionCallback {
&& !cardView.isFlipCard() && !cardView.isFlipCard()
&& !cardView.canTransform() && !cardView.canTransform()
&& ((PermanentView) cardView).isCopy()) { && ((PermanentView) cardView).isCopy()) {
image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()); image = ImageCache.getImageOriginal(((PermanentView) cardView).getOriginal()).getImage();
} else { } else {
image = ImageCache.getImageOriginalAlternateName(cardView); image = ImageCache.getImageOriginalAlternateName(cardView).getImage();
displayCard = displayCard.getSecondCardFace(); displayCard = displayCard.getSecondCardFace();
} }
} }

View file

@ -3,7 +3,6 @@ package org.mage.card.arcane;
import mage.MageInt; import mage.MageInt;
import mage.cards.MageCardLocation; import mage.cards.MageCardLocation;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.client.util.ImageCaches; import mage.client.util.ImageCaches;
import mage.client.util.ImageHelper; import mage.client.util.ImageHelper;
@ -19,6 +18,7 @@ import mage.view.PermanentView;
import mage.view.StackAbilityView; import mage.view.StackAbilityView;
import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache; import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.images.ImageCacheData;
import org.mage.plugins.card.utils.impl.ImageManagerImpl; import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import javax.swing.*; import javax.swing.*;
@ -466,9 +466,9 @@ public class CardPanelRenderModeImage extends CardPanel {
public Image getImage() { public Image getImage() {
if (this.hasImage) { if (this.hasImage) {
if (getGameCard().isFaceDown()) { if (getGameCard().isFaceDown()) {
return getFaceDownImage(); return getFaceDownImage().getImage();
} else { } else {
return ImageCache.getImageOriginal(getGameCard()); return ImageCache.getImageOriginal(getGameCard()).getImage();
} }
} }
return null; return null;
@ -629,27 +629,27 @@ public class CardPanelRenderModeImage extends CardPanel {
setTappedAngle(isTapped() ? CardPanel.TAPPED_ANGLE : 0); setTappedAngle(isTapped() ? CardPanel.TAPPED_ANGLE : 0);
setFlippedAngle(isFlipped() ? CardPanel.FLIPPED_ANGLE : 0); setFlippedAngle(isFlipped() ? CardPanel.FLIPPED_ANGLE : 0);
//final CardView gameCard = this.gameCard;
final int stamp = ++updateArtImageStamp; final int stamp = ++updateArtImageStamp;
Util.threadPool.submit(() -> { Util.threadPool.submit(() -> {
try { try {
final BufferedImage srcImage; final ImageCacheData data;
if (getGameCard().isFaceDown()) { if (getGameCard().isFaceDown()) {
srcImage = getFaceDownImage(); data = getFaceDownImage();
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
} else { } else {
srcImage = ImageCache.getThumbnail(getGameCard()); data = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
} }
if (srcImage == null) {
setFullPath(ImageCache.getFilePath(getGameCard(), getCardWidth())); // show path on miss image
if (data.getImage() == null) {
setFullPath(data.getPath());
} }
UI.invokeLater(() -> { UI.invokeLater(() -> {
if (stamp == updateArtImageStamp) { if (stamp == updateArtImageStamp) {
hasImage = srcImage != null; hasImage = data.getImage() != null;
setTitle(getGameCard()); setTitle(getGameCard());
setImage(srcImage); setImage(data.getImage());
} }
}); });
} catch (Exception | Error e) { } catch (Exception | Error e) {
@ -658,7 +658,7 @@ public class CardPanelRenderModeImage extends CardPanel {
}); });
} }
private BufferedImage getFaceDownImage() { private ImageCacheData getFaceDownImage() {
// TODO: add download default images // TODO: add download default images
if (isPermanent() && getGameCard() instanceof PermanentView) { if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) { if (((PermanentView) getGameCard()).isMorphed()) {

View file

@ -3,7 +3,6 @@ package org.mage.card.arcane;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.client.constants.Constants;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.SuperType; import mage.constants.SuperType;
@ -13,6 +12,7 @@ import mage.view.PermanentView;
import mage.view.StackAbilityView; import mage.view.StackAbilityView;
import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache; import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.images.ImageCacheData;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -194,9 +194,9 @@ public class CardPanelRenderModeMTGO extends CardPanel {
return null; return null;
} }
if (getGameCard().isFaceDown()) { if (getGameCard().isFaceDown()) {
return getFaceDownImage(); return getFaceDownImage().getImage();
} else { } else {
return ImageCache.getImageOriginal(getGameCard()); return ImageCache.getImageOriginal(getGameCard()).getImage();
} }
} }
@ -319,12 +319,9 @@ public class CardPanelRenderModeMTGO extends CardPanel {
// Nothing to do // Nothing to do
srcImage = null; srcImage = null;
faceArtSrcImage = null; faceArtSrcImage = null;
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight());
} else { } else {
srcImage = ImageCache.getThumbnail(getGameCard()); srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight()).getImage();
faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight()); faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight()).getImage();
} }
UI.invokeLater(() -> { UI.invokeLater(() -> {
@ -352,7 +349,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
return new CardPanelAttributes(getCardWidth(), getCardHeight(), isChoosable(), isSelected(), isTransformed()); return new CardPanelAttributes(getCardWidth(), getCardHeight(), isChoosable(), isSelected(), isTransformed());
} }
private BufferedImage getFaceDownImage() { private ImageCacheData getFaceDownImage() {
// TODO: add download default images // TODO: add download default images
if (isPermanent() && getGameCard() instanceof PermanentView) { if (isPermanent() && getGameCard() instanceof PermanentView) {
if (((PermanentView) getGameCard()).isMorphed()) { if (((PermanentView) getGameCard()).isMorphed()) {

View file

@ -710,6 +710,6 @@ public class CardPluginImpl implements CardPlugin {
@Override @Override
public BufferedImage getOriginalImage(CardView card) { public BufferedImage getOriginalImage(CardView card) {
return ImageCache.getImageOriginal(card); return ImageCache.getImageOriginal(card).getImage();
} }
} }

View file

@ -2,7 +2,6 @@ package org.mage.plugins.card.images;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
/** /**
@ -14,8 +13,6 @@ public class CardDownloadData {
private String downloadName; private String downloadName;
private String fileName = ""; private String fileName = "";
private String set; private String set;
private String tokenSetCode;
private String tokenDescriptor;
private final String collectorId; private final String collectorId;
private final Integer type; private final Integer type;
private boolean token; private boolean token;
@ -28,55 +25,51 @@ public class CardDownloadData {
private String tokenClassName; private String tokenClassName;
private boolean isType2; private boolean isType2;
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor) { public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, false, ""); this(name, setCode, collectorId, usesVariousArt, type, false, "");
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token) { public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, false, false, ""); this(name, setCode, collectorId, usesVariousArt, type, token, false, false, "");
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, String fileName) { public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token, String fileName) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, false, false, ""); this(name, setCode, collectorId, usesVariousArt, type, token, false, false, "");
this.fileName = fileName; this.fileName = fileName;
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide) { public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token, boolean twoFacedCard, boolean secondSide) {
this(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor, token, twoFacedCard, secondSide, ""); this(name, setCode, collectorId, usesVariousArt, type, token, twoFacedCard, secondSide, "");
} }
public CardDownloadData(String name, String set, String collectorId, boolean usesVariousArt, Integer type, String tokenSetCode, String tokenDescriptor, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) { public CardDownloadData(String name, String setCode, String collectorId, boolean usesVariousArt, Integer type, boolean token, boolean twoFacedCard, boolean secondSide, String tokenClassName) {
this.name = name; this.name = name;
this.set = set; this.set = setCode;
this.collectorId = collectorId; this.collectorId = collectorId;
this.usesVariousArt = usesVariousArt; this.usesVariousArt = usesVariousArt;
this.type = type; this.type = type;
this.token = token; this.token = token;
this.twoFacedCard = twoFacedCard; this.twoFacedCard = twoFacedCard;
this.secondSide = secondSide; this.secondSide = secondSide;
this.tokenSetCode = tokenSetCode;
this.tokenDescriptor = tokenDescriptor;
this.tokenClassName = tokenClassName; this.tokenClassName = tokenClassName;
if (this.tokenDescriptor == null || this.tokenDescriptor.equalsIgnoreCase("")) {
this.tokenDescriptor = lastDitchTokenDescriptor();
}
} }
public CardDownloadData(final CardDownloadData card) { public CardDownloadData(final CardDownloadData card) {
this.name = card.name; this.name = card.name;
this.downloadName = card.downloadName;
this.fileName = card.fileName;
this.set = card.set; this.set = card.set;
this.collectorId = card.collectorId; this.collectorId = card.collectorId;
this.type = card.type;
this.token = card.token; this.token = card.token;
this.twoFacedCard = card.twoFacedCard; this.twoFacedCard = card.twoFacedCard;
this.secondSide = card.secondSide; this.secondSide = card.secondSide;
this.type = card.type; this.flipCard = card.flipCard;
this.flippedSide = card.flippedSide;
this.splitCard = card.splitCard;
this.usesVariousArt = card.usesVariousArt; this.usesVariousArt = card.usesVariousArt;
this.tokenSetCode = card.tokenSetCode;
this.tokenDescriptor = card.tokenDescriptor;
this.tokenClassName = card.tokenClassName; this.tokenClassName = card.tokenClassName;
this.fileName = card.fileName; this.isType2 = card.isType2;
} }
@Override @Override
@ -94,7 +87,7 @@ public class CardDownloadData {
if (!Objects.equals(this.set, other.set)) { if (!Objects.equals(this.set, other.set)) {
return false; return false;
} }
if (!Objects.equals(this.collectorId, other.collectorId) && (this.collectorId == null || !this.collectorId.equals(other.collectorId))) { if (!Objects.equals(this.collectorId, other.collectorId)) {
return false; return false;
} }
if (this.token != other.token) { if (this.token != other.token) {
@ -123,6 +116,11 @@ public class CardDownloadData {
return hash; return hash;
} }
@Override
public String toString() {
return String.format("%s - %s", this.getSet(), this.getName());
}
public String getCollectorId() { public String getCollectorId() {
return collectorId; return collectorId;
} }
@ -171,18 +169,6 @@ public class CardDownloadData {
this.set = set; this.set = set;
} }
public String getTokenSetCode() {
return tokenSetCode;
}
public void setTokenSetCode(String tokenSetCode) {
this.tokenSetCode = tokenSetCode;
}
public String getTokenDescriptor() {
return tokenDescriptor;
}
public void setTokenClassName(String tokenClassName) { public void setTokenClassName(String tokenClassName) {
this.tokenClassName = tokenClassName; this.tokenClassName = tokenClassName;
} }
@ -191,17 +177,6 @@ public class CardDownloadData {
return tokenClassName; return tokenClassName;
} }
public void setTokenDescriptor(String tokenDescriptor) {
this.tokenDescriptor = tokenDescriptor;
}
private String lastDitchTokenDescriptor() {
String tmpName = this.name.replaceAll("[^a-zA-Z0-9]", "");
String descriptor = tmpName + "....";
descriptor = descriptor.toUpperCase(Locale.ENGLISH);
return descriptor;
}
public boolean isToken() { public boolean isToken() {
return token; return token;
} }

View file

@ -444,7 +444,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) { if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) {
String cardName = card.getName(); String cardName = card.getName();
boolean isType2 = type2SetsFilter.contains(card.getSetCode()); boolean isType2 = type2SetsFilter.contains(card.getSetCode());
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), card.isNightCard()); CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, false, card.isDoubleFaced(), card.isNightCard());
// variations must have diff file names with additional postfix // variations must have diff file names with additional postfix
if (url.getUsesVariousArt()) { if (url.getUsesVariousArt()) {
@ -475,7 +475,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(), card.getSetCode(),
secondSideCard.getCardNumber(), secondSideCard.getCardNumber(),
card.usesVariousArt(), card.usesVariousArt(),
0, "", "", false, card.isDoubleFaced(), true); 0, false, card.isDoubleFaced(), true);
url.setType2(isType2); url.setType2(isType2);
allCardsUrls.add(url); allCardsUrls.add(url);
} }
@ -488,7 +488,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(), card.getSetCode(),
card.getCardNumber(), card.getCardNumber(),
card.usesVariousArt(), card.usesVariousArt(),
0, "", "", false, card.isDoubleFaced(), card.isNightCard()); 0, false, card.isDoubleFaced(), card.isNightCard());
cardDownloadData.setFlipCard(true); cardDownloadData.setFlipCard(true);
cardDownloadData.setFlippedSide(true); cardDownloadData.setFlippedSide(true);
cardDownloadData.setType2(isType2); cardDownloadData.setType2(isType2);
@ -510,7 +510,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(), card.getSetCode(),
meldsToCard.getCardNumber(), meldsToCard.getCardNumber(),
card.usesVariousArt(), card.usesVariousArt(),
0, "", "", false, false, false); 0, false, false, false);
url.setType2(isType2); url.setType2(isType2);
allCardsUrls.add(url); allCardsUrls.add(url);
} }
@ -523,7 +523,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
card.getSetCode(), card.getSetCode(),
card.getCardNumber(), card.getCardNumber(),
card.usesVariousArt(), card.usesVariousArt(),
0, "", "", false, true, true); 0, false, true, true);
cardDownloadData.setType2(isType2); cardDownloadData.setType2(isType2);
allCardsUrls.add(cardDownloadData); allCardsUrls.add(cardDownloadData);
} }
@ -549,7 +549,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
cardsToDownload.add(card); cardsToDownload.add(card);
} else { } else {
// need missing cards // need missing cards
File file = new TFile(CardImageUtils.buildImagePathToCard(card)); File file = new TFile(CardImageUtils.buildImagePathToCardOrToken(card));
if (!file.exists()) { if (!file.exists()) {
cardsToDownload.add(card); cardsToDownload.add(card);
} }
@ -592,32 +592,32 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("TOK:")) { if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("TOK:")) {
String set = params[2].substring(4); String set = params[2].substring(4);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true); CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type,true);
card.setTokenClassName(tokenClassName); card.setTokenClassName(tokenClassName);
list.add(card); list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM:")) { } else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM:")) {
String set = params[2].substring(7); String set = params[2].substring(7);
CardDownloadData card = new CardDownloadData("Emblem " + params[3], set, "0", false, type, "", "", true, fileName); CardDownloadData card = new CardDownloadData("Emblem " + params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName); card.setTokenClassName(tokenClassName);
list.add(card); list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM-:")) { } else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM-:")) {
String set = params[2].substring(8); String set = params[2].substring(8);
CardDownloadData card = new CardDownloadData(params[3] + " Emblem", set, "0", false, type, "", "", true, fileName); CardDownloadData card = new CardDownloadData(params[3] + " Emblem", set, "0", false, type,true, fileName);
card.setTokenClassName(tokenClassName); card.setTokenClassName(tokenClassName);
list.add(card); list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM!:")) { } else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("EMBLEM!:")) {
String set = params[2].substring(8); String set = params[2].substring(8);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true, fileName); CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName); card.setTokenClassName(tokenClassName);
list.add(card); list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("PLANE:")) { } else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("PLANE:")) {
String set = params[2].substring(6); String set = params[2].substring(6);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true, fileName); CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName); card.setTokenClassName(tokenClassName);
list.add(card); list.add(card);
} else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("DUNGEON:")) { } else if (params[1].toLowerCase(Locale.ENGLISH).equals("generate") && params[2].startsWith("DUNGEON:")) {
String set = params[2].substring(8); String set = params[2].substring(8);
CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, "", "", true, fileName); CardDownloadData card = new CardDownloadData(params[3], set, "0", false, type, true, fileName);
card.setTokenClassName(tokenClassName); card.setTokenClassName(tokenClassName);
list.add(card); list.add(card);
} else { } else {
@ -833,7 +833,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
destFile = new TFile(CardImageUtils.buildImagePathToTokens() + actualFilename + ".jpg"); destFile = new TFile(CardImageUtils.buildImagePathToTokens() + actualFilename + ".jpg");
} }
} else { } else {
destFile = new TFile(CardImageUtils.buildImagePathToCard(card)); destFile = new TFile(CardImageUtils.buildImagePathToCardOrToken(card));
} }
// FILE already exists (in zip or in dir) // FILE already exists (in zip or in dir)
@ -1008,7 +1008,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
// finished // finished
List<CardDownloadData> downloadedCards = Collections.synchronizedList(new ArrayList<>()); List<CardDownloadData> downloadedCards = Collections.synchronizedList(new ArrayList<>());
DownloadPicturesService.this.cardsMissing.parallelStream().forEach(cardDownloadData -> { DownloadPicturesService.this.cardsMissing.parallelStream().forEach(cardDownloadData -> {
TFile file = new TFile(CardImageUtils.buildImagePathToCard(cardDownloadData)); TFile file = new TFile(CardImageUtils.buildImagePathToCardOrToken(cardDownloadData));
if (file.exists()) { if (file.exists()) {
downloadedCards.add(cardDownloadData); downloadedCards.add(cardDownloadData);
} }

View file

@ -1,16 +1,13 @@
package org.mage.plugins.card.images; package org.mage.plugins.card.images;
import com.google.common.base.Function;
import com.google.common.collect.ComputationException; import com.google.common.collect.ComputationException;
import mage.abilities.icon.CardIconColor; import mage.abilities.icon.CardIconColor;
import mage.client.constants.Constants; import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.SoftValuesLoadingCache; import mage.client.util.SoftValuesLoadingCache;
import mage.client.util.TransformedImageCache; import mage.client.util.TransformedImageCache;
import mage.view.CardView; import mage.view.CardView;
import net.java.truevfs.access.TFile; import net.java.truevfs.access.TFile;
import net.java.truevfs.access.TFileInputStream; import net.java.truevfs.access.TFileInputStream;
import net.java.truevfs.access.TFileOutputStream;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.mage.plugins.card.dl.sources.DirectLinksForDownload; import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
import org.mage.plugins.card.utils.CardImageUtils; import org.mage.plugins.card.utils.CardImageUtils;
@ -20,7 +17,6 @@ import javax.imageio.ImageIO;
import java.awt.*; import java.awt.*;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -29,165 +25,113 @@ import java.util.regex.Pattern;
* that the images may be garbage collected when they are not needed any more, * that the images may be garbage collected when they are not needed any more,
* but will be kept as long as possible. * but will be kept as long as possible.
* <p> * <p>
* Key format: "[cardname]#[setname]#[type]#[collectorID]#[param]" * Key format: "[cardname]#[setname]#[type]#[collectorID]#[image size]#[additional data]"
* <p>
* where param is:
* <ul>
* <li>size of image</li>
* *
* <li>#Normal: request for unrotated image</li> * <li>#Normal: request for unrotated image</li>
* <li>#Tapped: request for rotated image</li> * <li>#Tapped: request for rotated image</li>
* <li>#Cropped: request for cropped image that is used for Shandalar like card * <li>#Cropped: request for cropped image that is used for Shandalar like card
* look</li> * look</li>
* </ul> * </ul>
*
* @author JayDi85
*/ */
public final class ImageCache { public final class ImageCache {
private static final Logger LOGGER = Logger.getLogger(ImageCache.class); private static final Logger LOGGER = Logger.getLogger(ImageCache.class);
private static final SoftValuesLoadingCache<String, BufferedImage> IMAGE_CACHE; private static final SoftValuesLoadingCache<String, ImageCacheData> IMAGE_CACHE; // cards and tokens
private static final SoftValuesLoadingCache<String, BufferedImage> FACE_IMAGE_CACHE; private static final SoftValuesLoadingCache<String, ImageCacheData> FACE_IMAGE_CACHE;
private static final SoftValuesLoadingCache<String, BufferedImage> CARD_ICONS_CACHE; private static final SoftValuesLoadingCache<String, ImageCacheData> CARD_ICONS_CACHE;
/** /**
* Common pattern for keys. See ImageCache.getKey for structure info * Common pattern for keys. See ImageCache.getKey for structure info
*/ */
private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)#(.*)"); private static final Pattern KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)#(.*)#(.*)");
private static final Pattern CARD_ICON_KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)"); private static final Pattern CARD_ICON_KEY_PATTERN = Pattern.compile("(.*)#(.*)#(.*)");
static { static {
// softValues() = Specifies that each value (not key) stored in the map should be wrapped in a SoftReference // softValues() = Specifies that each value (not key) stored in the map should be wrapped in a SoftReference
// (by default, strong references are used). Softly-referenced objects will be garbage-collected in a // (by default, strong references are used). Softly-referenced objects will be garbage-collected in a
// globally least-recently-used manner, in response to memory demand. // globally least-recently-used manner, in response to memory demand.
IMAGE_CACHE = SoftValuesLoadingCache.from(new Function<String, BufferedImage>() { IMAGE_CACHE = SoftValuesLoadingCache.from(key -> {
@Override try {
public BufferedImage apply(String key) { boolean usesVariousArt = false;
try { if (key.matches(".*#usesVariousArt.*")) {
boolean usesVariousArt = false; usesVariousArt = true;
if (key.matches(".*#usesVariousArt.*")) { key = key.replace("#usesVariousArt", "");
usesVariousArt = true; }
key = key.replace("#usesVariousArt", ""); Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String setCode = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
} }
boolean thumbnail = false;
if (key.matches(".*#thumb.*")) {
thumbnail = true;
key = key.replace("#thumb", "");
}
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) { CardDownloadData info = new CardDownloadData(name, setCode, collectorId, usesVariousArt, type);
String name = m.group(1);
String set = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
}
String tokenSetCode = m.group(5);
String tokenDescriptor = m.group(6);
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor); boolean cardback = false;
String path;
if (collectorId.isEmpty() || "0".equals(collectorId)) {
// TOKEN
// can be a normal token or a token from card (see embalm ability)
info.setToken(true);
TFile tokenFile;
boolean cardback = false; // try normal token
String path; path = CardImageUtils.buildImagePathToCardOrToken(info);
if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability tokenFile = getTFile(path);
info.setToken(true);
path = CardImageUtils.generateTokenImagePath(info); // try token from card
if (path == null) { if (tokenFile == null || !tokenFile.exists()) {
cardback = true; CardDownloadData tempInfo = new CardDownloadData(info);
// try token image from card tempInfo.setToken(false);
CardDownloadData newInfo = new CardDownloadData(info); path = CardImageUtils.buildImagePathToCardOrToken(info);
newInfo.setToken(false); tokenFile = getTFile(path);
path = CardImageUtils.buildImagePathToCard(newInfo);
TFile tokenFile = getTFile(path);
if (tokenFile == null || !tokenFile.exists()) {
// token empty token image
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
}
} else {
path = CardImageUtils.buildImagePathToCard(info);
} }
if (path == null) { // try unknown token image
return null; if (tokenFile == null || !tokenFile.exists()) {
} // TODO: replace empty token by other default card, not cardback
TFile file = getTFile(path); cardback = true;
if (file == null) { path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
return null;
}
if (thumbnail && path.endsWith(".jpg")) {
// need thumbnail image
String thumbnailPath = buildThumbnailPath(path);
TFile thumbnailFile = null;
try {
thumbnailFile = new TFile(thumbnailPath);
} catch (Exception ex) {
}
boolean exists = false;
if (thumbnailFile != null) {
try {
exists = thumbnailFile.exists();
} catch (Exception ex) {
exists = false;
}
}
if (exists) {
LOGGER.debug("loading thumbnail for " + key + ", path=" + thumbnailPath);
BufferedImage thumbnailImage = loadImage(thumbnailFile);
if (thumbnailImage == null) { // thumbnail exists but broken for some reason
LOGGER.warn("failed loading thumbnail for " + key + ", path=" + thumbnailPath
+ ", thumbnail file is probably broken, attempting to recreate it...");
thumbnailImage = makeThumbnailByFile(key, file, thumbnailPath);
}
if (cardback) {
// unknown tokens on opponent desk
thumbnailImage = getRoundCorner(thumbnailImage);
}
return thumbnailImage;
} else {
return makeThumbnailByFile(key, file, thumbnailPath);
}
} else {
if (cardback) {
// need cardback image
BufferedImage image = loadImage(file);
image = getRoundCorner(image);
return image;
} else {
// need normal card image
BufferedImage image = loadImage(file);
image = getWizardsCard(image);
image = getRoundCorner(image);
return image;
}
} }
} else { } else {
throw new RuntimeException( // CARD
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key); path = CardImageUtils.buildImagePathToCardOrToken(info);
} }
} catch (Exception ex) {
if (ex instanceof ComputationException) {
throw (ComputationException) ex;
} else {
throw new ComputationException(ex);
}
}
}
public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) { TFile file = getTFile(path);
BufferedImage image = loadImage(file); if (file == null) {
image = getWizardsCard(image); return new ImageCacheData(path, null);
image = getRoundCorner(image); }
if (image == null) {
return null; if (cardback) {
// TODO: is there any different in images styles? Cardback must be from scryfall, not wizards
// need cardback image
BufferedImage image = loadImage(file);
image = getRoundCorner(image);
return new ImageCacheData(path, image);
} else {
// need normal card image
BufferedImage image = loadImage(file);
image = getWizardsCard(image);
image = getRoundCorner(image);
return new ImageCacheData(path, image);
}
} else {
throw new RuntimeException(
"Requested image doesn't fit the requirement for key (<cardname>#<setname>#<collectorID>): " + key);
}
} catch (Exception ex) {
if (ex instanceof ComputationException) {
throw (ComputationException) ex;
} else {
throw new ComputationException(ex);
} }
LOGGER.debug("creating thumbnail for " + key);
return makeThumbnail(image, thumbnailPath);
} }
}); });
@ -197,22 +141,18 @@ public final class ImageCache {
if (m.matches()) { if (m.matches()) {
String name = m.group(1); String name = m.group(1);
String set = m.group(2); String setCode = m.group(2);
//Integer artid = Integer.parseInt(m.group(2)); // skip type
// skip collectorId
String path; String path = CardImageUtils.generateFaceImagePath(name, setCode);
path = CardImageUtils.generateFaceImagePath(name, set);
if (path == null) {
return null;
}
TFile file = getTFile(path); TFile file = getTFile(path);
if (file == null) { if (file == null) {
return null; return new ImageCacheData(path, null);
} }
BufferedImage image = loadImage(file); BufferedImage image = loadImage(file);
return image; return new ImageCacheData(path, image);
} else { } else {
throw new RuntimeException( throw new RuntimeException(
"Requested face image doesn't fit the requirement for key (<cardname>#<artid>#: " + key); "Requested face image doesn't fit the requirement for key (<cardname>#<artid>#: " + key);
@ -235,7 +175,7 @@ public final class ImageCache {
String resourceName = m.group(2); String resourceName = m.group(2);
CardIconColor cardIconColor = CardIconColor.valueOf(m.group(3)); CardIconColor cardIconColor = CardIconColor.valueOf(m.group(3));
BufferedImage image = ImageManagerImpl.instance.getCardIcon(resourceName, cardSize, cardIconColor); BufferedImage image = ImageManagerImpl.instance.getCardIcon(resourceName, cardSize, cardIconColor);
return image; return new ImageCacheData(resourceName, image);
} else { } else {
throw new RuntimeException("Wrong card icons image key format: " + key); throw new RuntimeException("Wrong card icons image key format: " + key);
} }
@ -255,109 +195,38 @@ public final class ImageCache {
CARD_ICONS_CACHE.invalidateAll(); CARD_ICONS_CACHE.invalidateAll();
} }
public static String getFilePath(CardView card, int width) {
String key = getKey(card, card.getName(), Integer.toString(width));
boolean usesVariousArt = false;
if (key.matches(".*#usesVariousArt.*")) {
usesVariousArt = true;
key = key.replace("#usesVariousArt", "");
}
boolean thumbnail = false;
if (key.matches(".*#thumb.*")) {
thumbnail = true;
key = key.replace("#thumb", "");
}
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String set = m.group(2);
Integer type = Integer.parseInt(m.group(3));
String collectorId = m.group(4);
if (collectorId.equals("null")) {
collectorId = "0";
}
String tokenSetCode = m.group(5);
String tokenDescriptor = m.group(6);
CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor);
String path;
if (collectorId.isEmpty() || "0".equals(collectorId) || !tokenDescriptor.isEmpty()) { // tokenDescriptor for embalm ability
info.setToken(true);
path = CardImageUtils.generateTokenImagePath(info);
if (path == null) {
// try token image from card
CardDownloadData newInfo = new CardDownloadData(info);
newInfo.setToken(false);
path = CardImageUtils.buildImagePathToCard(newInfo);
TFile tokenFile = getTFile(path);
if (tokenFile == null || !tokenFile.exists()) {
// token empty token image
// TODO: replace empty token by other default card, not cardback
path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
}
}
} else {
path = CardImageUtils.buildImagePathToCard(info);
}
if (thumbnail && path.endsWith(".jpg")) {
return buildThumbnailPath(path);
}
return path;
}
return "";
}
private ImageCache() { private ImageCache() {
} }
public static BufferedImage getCardbackImage() { public static ImageCacheData getCardbackImage() {
BufferedImage image = ImageCache.loadImage(new TFile(CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename))); String path = CardImageUtils.buildImagePathToDefault(DirectLinksForDownload.cardbackFilename);
BufferedImage image = ImageCache.loadImage(getTFile(path));
image = getRoundCorner(image); image = getRoundCorner(image);
return image; return new ImageCacheData(path, image);
} }
public static BufferedImage getMorphImage() { public static ImageCacheData getMorphImage() {
// TODO: replace by morth image // TODO: replace by downloadable morth image
CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0, "KTK", ""); CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0);
info.setToken(true); info.setToken(true);
String path = CardImageUtils.generateTokenImagePath(info); String path = CardImageUtils.buildImagePathToCardOrToken(info);
if (path == null) {
return null;
}
TFile file = getTFile(path);
TFile file = getTFile(path);
BufferedImage image = loadImage(file); BufferedImage image = loadImage(file);
image = getRoundCorner(image); image = getRoundCorner(image);
return image; return new ImageCacheData(path, image);
} }
public static BufferedImage getManifestImage() { public static ImageCacheData getManifestImage() {
// TODO: replace by manifestest image // TODO: replace by downloadable manifestest image
CardDownloadData info = new CardDownloadData("Manifest", "FRF", "0", false, 0, "FRF", ""); CardDownloadData info = new CardDownloadData("Manifest", "FRF", "0", false, 0);
info.setToken(true); info.setToken(true);
String path = CardImageUtils.generateTokenImagePath(info); String path = CardImageUtils.buildImagePathToCardOrToken(info);
if (path == null) {
return null;
}
TFile file = getTFile(path);
TFile file = getTFile(path);
BufferedImage image = loadImage(file); BufferedImage image = loadImage(file);
image = getRoundCorner(image); image = getRoundCorner(image);
return image; return new ImageCacheData(path, image);
}
private static String buildThumbnailPath(String path) {
String thumbnailPath;
if (PreferencesDialog.isSaveImagesToZip()) {
thumbnailPath = path.replace(".zip", ".thumb.zip");
} else {
thumbnailPath = path.replace(".jpg", ".thumb.jpg");
}
return thumbnailPath;
} }
public static BufferedImage getRoundCorner(BufferedImage image) { public static BufferedImage getRoundCorner(BufferedImage image) {
@ -387,6 +256,7 @@ public final class ImageCache {
} }
public static BufferedImage getWizardsCard(BufferedImage image) { public static BufferedImage getWizardsCard(BufferedImage image) {
// TODO: can be removed?
if (image != null && image.getWidth() == 265 && image.getHeight() == 370) { if (image != null && image.getWidth() == 265 && image.getHeight() == 370) {
BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB); BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = crop.createGraphics(); Graphics2D graphics2D = crop.createGraphics();
@ -398,80 +268,61 @@ public final class ImageCache {
} }
} }
public static boolean isFaceImagePresent(CardView card) { public static ImageCacheData getImageOriginal(CardView card) {
String path; return getImage(getKey(card, card.getName(), 0));
path = CardImageUtils.generateFaceImagePath(card.getName(), card.getExpansionSetCode());
if (path == null) {
return false;
}
TFile file = getTFile(path);
if (file == null) {
return false;
}
return file.exists();
} }
public static BufferedImage getThumbnail(CardView card) { public static ImageCacheData getImageOriginalAlternateName(CardView card) {
return getImage(getKey(card, card.getName(), "#thumb")); return getImage(getKey(card, card.getAlternateName(), 0));
} }
public static BufferedImage tryGetThumbnail(CardView card) { public static ImageCacheData getCardIconImage(String resourceName, int iconSize, String cardColorName) {
return tryGetImage(getKey(card, card.getName(), "#thumb"));
}
public static BufferedImage getImageOriginal(CardView card) {
return getImage(getKey(card, card.getName(), ""));
}
public static BufferedImage getImageOriginalAlternateName(CardView card) {
return getImage(getKey(card, card.getAlternateName(), ""));
}
public static BufferedImage getCardIconImage(String resourceName, int iconSize, String cardColorName) {
return getCardIconImage(getCardIconKey(resourceName, iconSize, cardColorName)); return getCardIconImage(getCardIconKey(resourceName, iconSize, cardColorName));
} }
/** /**
* Returns the Image corresponding to the key * Returns the Image corresponding to the key
*/ */
private static BufferedImage getImage(String key) { private static ImageCacheData getImage(String key) {
try { try {
return IMAGE_CACHE.getOrNull(key); ImageCacheData data = IMAGE_CACHE.getOrNull(key);
return data != null ? data : new ImageCacheData("ERROR: key - " + key, null);
} catch (ComputationException ex) { } catch (ComputationException ex) {
// too low memory // too low memory
if (ex.getCause() instanceof NullPointerException) { if (ex.getCause() instanceof NullPointerException) {
return null; return new ImageCacheData("ERROR: low memory?", null);
} }
LOGGER.error(ex, ex); LOGGER.error(ex, ex);
return null; return new ImageCacheData("ERROR: see logs", null);
} }
} }
/** /**
* Returns the Image corresponding to the key * Returns the Image corresponding to the key
*/ */
private static BufferedImage getFaceImage(String key) { private static ImageCacheData getFaceImage(String key) {
try { try {
return FACE_IMAGE_CACHE.getOrNull(key); ImageCacheData data = FACE_IMAGE_CACHE.getOrNull(key);
return data != null ? data : new ImageCacheData("ERROR: key " + key, null);
} catch (ComputationException ex) { } catch (ComputationException ex) {
if (ex.getCause() instanceof NullPointerException) { if (ex.getCause() instanceof NullPointerException) {
return null; return new ImageCacheData("ERROR: low memory?", null);
} }
LOGGER.error(ex, ex); LOGGER.error(ex, ex);
return null; return new ImageCacheData("ERROR: see logs", null);
} }
} }
private static BufferedImage getCardIconImage(String key) { private static ImageCacheData getCardIconImage(String key) {
try { try {
return CARD_ICONS_CACHE.getOrNull(key); ImageCacheData data = CARD_ICONS_CACHE.getOrNull(key);
return data != null ? data : new ImageCacheData("ERROR: key - " + key, null);
} catch (ComputationException ex) { } catch (ComputationException ex) {
if (ex.getCause() instanceof NullPointerException) { if (ex.getCause() instanceof NullPointerException) {
return null; return new ImageCacheData("ERROR: low memory?", null);
} }
LOGGER.error(ex, ex); LOGGER.error(ex, ex);
return null; return new ImageCacheData("ERROR: see logs", null);
} }
} }
@ -479,22 +330,24 @@ public final class ImageCache {
* Returns the Image corresponding to the key only if it already exists in * Returns the Image corresponding to the key only if it already exists in
* the cache. * the cache.
*/ */
private static BufferedImage tryGetImage(String key) { private static ImageCacheData tryGetImage(String key) {
return IMAGE_CACHE.peekIfPresent(key); return IMAGE_CACHE.peekIfPresent(key);
} }
/** /**
* Returns the map key for a card, without any suffixes for the image size. * Generate key for images cache (it must contain all info to search and load image from the disk)
*
* @param card
* @param cardName - can be alternative name
* @param imageSize - size info, 0 to use original image (with max size)
*/ */
private static String getKey(CardView card, String name, String suffix) { private static String getKey(CardView card, String cardName, int imageSize) {
return (card.isToken() ? name.replace(" Token", "") : name) return (card.isToken() ? cardName.replace(" Token", "") : cardName)
+ '#' + card.getExpansionSetCode() + '#' + card.getExpansionSetCode()
+ '#' + card.getType() + '#' + card.getType()
+ '#' + card.getCardNumber() + '#' + card.getCardNumber()
+ '#' + (card.getTokenSetCode() == null ? "" : card.getTokenSetCode()) + '#' + imageSize
+ suffix + (card.getUsesVariousArt() ? "#usesVariousArt" : "");
+ (card.getUsesVariousArt() ? "#usesVariousArt" : "")
+ '#' + (card.getTokenDescriptor() != null ? card.getTokenDescriptor() : "");
} }
/** /**
@ -534,24 +387,6 @@ public final class ImageCache {
return image; return image;
} }
public static BufferedImage makeThumbnail(BufferedImage original, String path) {
BufferedImage image = TransformedImageCache.getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL.width, Constants.THUMBNAIL_SIZE_FULL.height);
TFile imageFile = getTFile(path);
if (imageFile == null) {
return null;
}
try {
try (TFileOutputStream outputStream = new TFileOutputStream(imageFile)) {
String format = image.getColorModel().getNumComponents() > 3 ? "png" : "jpg";
ImageIO.write(image, format, outputStream);
}
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
imageFile.delete();
}
return image;
}
/** /**
* Returns an image scaled to the size given * Returns an image scaled to the size given
* *
@ -584,23 +419,22 @@ public final class ImageCache {
* @param height * @param height
* @return * @return
*/ */
public static BufferedImage getImage(CardView card, int width, int height) { public static ImageCacheData getImage(CardView card, int width, int height) {
if (Constants.THUMBNAIL_SIZE_FULL.width + 10 > width) { String key = getKey(card, card.getName(), width);
return getThumbnail(card); ImageCacheData data = getImage(key);
} if (data.getImage() == null) {
String key = getKey(card, card.getName(), Integer.toString(width)); LOGGER.debug("Image doesn't exists in the cache: " + key);
BufferedImage original = getImage(key); return data;
if (original == null) {
LOGGER.debug(key + " not found");
return null;
} }
double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight()); double scale = Math.min((double) width / data.getImage().getWidth(), (double) height / data.getImage().getHeight());
if (scale >= 1) { if (scale >= 1) {
return original; return data;
} }
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale)); BufferedImage newImage = TransformedImageCache.getResizedImage(data.getImage(), (int) (data.getImage().getWidth() * scale), (int) (data.getImage().getHeight() * scale));
data.setImage(newImage);
return data;
} }
/** /**
@ -611,15 +445,13 @@ public final class ImageCache {
* @param height * @param height
* @return * @return
*/ */
public static BufferedImage getFaceImage(CardView card, int width, int height) { public static ImageCacheData getFaceImage(CardView card, int width, int height) {
String key = getFaceKey(card, card.getName(), card.getExpansionSetCode()); String key = getFaceKey(card, card.getName(), card.getExpansionSetCode());
BufferedImage original = getFaceImage(key); ImageCacheData data = getFaceImage(key);
if (original == null) { if (data.getImage() == null) {
LOGGER.debug(key + " (faceimage) not found"); LOGGER.debug(key + " (faceimage) not found");
return null;
} }
return data;
return original;
} }
/** /**
@ -632,29 +464,31 @@ public final class ImageCache {
* @param height * @param height
* @return * @return
*/ */
public static BufferedImage tryGetImage(CardView card, int width, int height) { public static ImageCacheData tryGetImage(CardView card, int width, int height) {
if (Constants.THUMBNAIL_SIZE_FULL.width + 10 > width) { String key = getKey(card, card.getName(), width);
return tryGetThumbnail(card); ImageCacheData data = tryGetImage(key);
} if (data.getImage() == null) {
String key = getKey(card, card.getName(), Integer.toString(width));
BufferedImage original = tryGetImage(key);
if (original == null) {
LOGGER.debug(key + " not found"); LOGGER.debug(key + " not found");
return null; return data;
} }
double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight()); double scale = Math.min((double) width / data.getImage().getWidth(), (double) height / data.getImage().getHeight());
if (scale >= 1) { if (scale >= 1) {
return original; return data;
} }
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale)); BufferedImage newImage = TransformedImageCache.getResizedImage(data.getImage(), (int) (data.getImage().getWidth() * scale), (int) (data.getImage().getHeight() * scale));
data.setImage(newImage);
return data;
} }
public static TFile getTFile(String path) { public static TFile getTFile(String path) {
try { try {
return new TFile(path); if (path != null) {
return new TFile(path);
}
} catch (NullPointerException ex) { } catch (NullPointerException ex) {
// TODO: raise error on path == null -- is it actual?!
LOGGER.warn("Imagefile does not exist: " + path); LOGGER.warn("Imagefile does not exist: " + path);
} }
return null; return null;

View file

@ -0,0 +1,28 @@
package org.mage.plugins.card.images;
import java.awt.image.BufferedImage;
/**
* @author JayDi85
*/
public class ImageCacheData {
String path;
BufferedImage image;
public ImageCacheData(String path, BufferedImage image) {
this.path = path;
this.image = image;
}
public String getPath() {
return path;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}

View file

@ -5,7 +5,6 @@ import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog; import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection; import mage.remote.Connection;
import mage.remote.Connection.ProxyType; import mage.remote.Connection.ProxyType;
import net.java.truevfs.access.TFile;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
@ -26,87 +25,13 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
public final class CardImageUtils { public final class CardImageUtils {
private static final HashMap<CardDownloadData, String> pathCache = new HashMap<>();
private static final Logger LOGGER = Logger.getLogger(CardImageUtils.class); private static final Logger LOGGER = Logger.getLogger(CardImageUtils.class);
/**
* @param card
* @return String if image exists, else null
*/
public static String generateTokenImagePath(CardDownloadData card) {
if (card.isToken()) {
String filePath = getTokenImagePath(card);
if (pathCache.containsKey(card)) {
if (filePath.equals(pathCache.get(card))) {
return pathCache.get(card);
}
}
TFile file = new TFile(filePath);
if (!file.exists() && card.getTokenSetCode() != null) {
filePath = searchForCardImage(card);
file = new TFile(filePath);
}
if (file.exists()) {
pathCache.put(card, filePath);
return filePath;
}
//log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + getTokenImagePath(card));
} else {
LOGGER.warn("Trying to get token path for non token card. Set: " + card.getSet() + " Set Code: " + card.getTokenSetCode() + " Name: " + card.getName());
}
return null;
}
/**
* @param card
* @return String regardless of whether image exists
*/
public static String generateFullTokenImagePath(CardDownloadData card) {
if (card.isToken()) {
return getTokenImagePath(card);
}
return "";
}
private static String getTokenImagePath(CardDownloadData card) {
String filename = buildImagePathToCard(card);
TFile file = new TFile(filename);
if (!file.exists()) {
String tokenDescriptorfilename = generateTokenDescriptorImagePath(card);
if (!tokenDescriptorfilename.isEmpty()) {
file = new TFile(filename);
if (file.exists()) {
return tokenDescriptorfilename;
}
}
}
return filename;
}
private static String searchForCardImage(CardDownloadData card) {
TFile file;
String path;
CardDownloadData c = new CardDownloadData(card);
c.setSet(card.getTokenSetCode());
path = getTokenImagePath(c);
file = new TFile(path);
if (file.exists()) {
pathCache.put(card, path);
return path;
}
return generateTokenDescriptorImagePath(card);
}
public static String prepareCardNameForFile(String cardName) { public static String prepareCardNameForFile(String cardName) {
return cardName return cardName
.replace(":", "") .replace(":", "")
@ -141,12 +66,12 @@ public final class CardImageUtils {
return getImagesDir() + Constants.RESOURCE_PATH_DEFAULT_IMAGES + File.separator + defaultFileName; return getImagesDir() + Constants.RESOURCE_PATH_DEFAULT_IMAGES + File.separator + defaultFileName;
} }
public static String fixSetNameForWindows(String set) { public static String fixSetNameForWindows(String setCode) {
// windows can't create con folders // windows can't create con folders
if (set.equals("CON") || set.equals("con")) { if (setCode.equals("CON") || setCode.equals("con")) {
return "COX"; return "COX";
} else { } else {
return set; return setCode;
} }
} }
@ -160,40 +85,34 @@ public final class CardImageUtils {
} }
} }
private static String buildImagePathToTokenDescriptor(CardDownloadData card) {
return buildImagePathToTokens() + card.getTokenDescriptor() + ".full.jpg";
}
public static String buildImagePathToSet(CardDownloadData card) { public static String buildImagePathToSet(CardDownloadData card) {
if (card.getSet() == null) { if (card.getSet() == null) {
throw new IllegalArgumentException("Card " + card.getName() + " have empty set."); throw new IllegalArgumentException("Card " + card.getName() + " have empty set.");
} }
String setCode = card.getSet().toUpperCase(Locale.ENGLISH);
String set = card.getSet().toUpperCase(Locale.ENGLISH);
if (card.isToken()) { if (card.isToken()) {
return buildImagePathToSetAsToken(set); return buildImagePathToSetAsToken(setCode);
} else { } else {
return buildImagePathToSetAsCard(set); return buildImagePathToSetAsCard(setCode);
} }
} }
private static String buildImagePathToSetAsCard(String set) { private static String buildImagePathToSetAsCard(String setCode) {
String imagesPath = getImagesDir() + File.separator; String imagesPath = getImagesDir() + File.separator;
if (PreferencesDialog.isSaveImagesToZip()) { if (PreferencesDialog.isSaveImagesToZip()) {
return imagesPath + fixSetNameForWindows(set) + ".zip" + File.separator + fixSetNameForWindows(set) + File.separator; return imagesPath + fixSetNameForWindows(setCode) + ".zip" + File.separator + fixSetNameForWindows(setCode) + File.separator;
} else { } else {
return imagesPath + fixSetNameForWindows(set) + File.separator; return imagesPath + fixSetNameForWindows(setCode) + File.separator;
} }
} }
private static String buildImagePathToSetAsToken(String set) { private static String buildImagePathToSetAsToken(String setCode) {
return buildImagePathToTokens() + fixSetNameForWindows(set) + File.separator; return buildImagePathToTokens() + fixSetNameForWindows(setCode) + File.separator;
} }
public static String buildImagePathToCard(CardDownloadData card) { public static String buildImagePathToCardOrToken(CardDownloadData card) {
String setPath = buildImagePathToSet(card); String setPath = buildImagePathToSet(card);
@ -207,7 +126,7 @@ public final class CardImageUtils {
cardName = prepareCardNameForFile(card.getName()); cardName = prepareCardNameForFile(card.getName());
} }
String finalFileName = ""; String finalFileName;
if (card.getUsesVariousArt()) { if (card.getUsesVariousArt()) {
// different arts uses name + collector id // different arts uses name + collector id
finalFileName = cardName + prefixType + '.' + card.getCollectorIdAsFileName() + ".full.jpg"; finalFileName = cardName + prefixType + '.' + card.getCollectorIdAsFileName() + ".full.jpg";
@ -216,54 +135,11 @@ public final class CardImageUtils {
finalFileName = cardName + prefixType + ".full.jpg"; finalFileName = cardName + prefixType + ".full.jpg";
} }
/* 2019-01-12: no needs in name corrections, all files must be same and auto-downloaded
// if image file exists, correct name (for case sensitive systems)
// use TFile for zips
TFile dirFile = new TFile(setPath);
TFile imageFile = new TFile(setPath + finalFileName);
// warning, zip files can be broken
try {
if (dirFile.exists() && !imageFile.exists()) {
// search like names
for (String fileName : dirFile.list()) {
if (fileName.toLowerCase(Locale.ENGLISH).equals(finalFileName.toLowerCase(Locale.ENGLISH))) {
finalFileName = fileName;
break;
}
}
}
} catch (Exception ex) {
log.error("Can't read card name from file, may be it broken: " + setPath);
}
*/
return setPath + finalFileName; return setPath + finalFileName;
} }
public static String generateFaceImagePath(String cardname, String set) { public static String generateFaceImagePath(String cardName, String setCode) {
return getImagesDir() + File.separator + "FACE" + File.separator + fixSetNameForWindows(set) + File.separator + prepareCardNameForFile(cardname) + ".jpg"; return getImagesDir() + File.separator + "FACE" + File.separator + fixSetNameForWindows(setCode) + File.separator + prepareCardNameForFile(cardName) + ".jpg";
}
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
String straightImageFile = buildImagePathToTokenDescriptor(card);
TFile file = new TFile(straightImageFile);
if (file.exists()) {
return straightImageFile;
}
straightImageFile = straightImageFile.replaceFirst("\\.[0-9]+\\.[0-9]+", ".X.X");
file = new TFile(straightImageFile);
if (file.exists()) {
return straightImageFile;
}
straightImageFile = straightImageFile.replaceFirst("\\.X\\.X", ".S.S");
file = new TFile(straightImageFile);
if (file.exists()) {
return straightImageFile;
}
return "";
} }
public static Proxy getProxyFromPreferences() { public static Proxy getProxyFromPreferences() {
@ -305,8 +181,7 @@ public final class CardImageUtils {
} }
public static void checkAndFixImageFiles() { public static void checkAndFixImageFiles() {
// search broken files and delete it (zero size files) // search broken, temp or outdated files and delete it
// search temp files and delete it (.tmp files from zip library)
Path rootPath = new File(CardImageUtils.getImagesDir()).toPath(); Path rootPath = new File(CardImageUtils.getImagesDir()).toPath();
if (!Files.exists(rootPath)) { if (!Files.exists(rootPath)) {
return; return;
@ -314,6 +189,7 @@ public final class CardImageUtils {
Collection<Path> brokenFilesList = new ArrayList<>(); Collection<Path> brokenFilesList = new ArrayList<>();
Collection<Path> tempFilesList = new ArrayList<>(); Collection<Path> tempFilesList = new ArrayList<>();
Collection<Path> outdatedFilesList = new ArrayList<>();
try { try {
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() { Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
@Override @Override
@ -329,6 +205,11 @@ public final class CardImageUtils {
tempFilesList.add(file); tempFilesList.add(file);
} }
// 3. outdated files delete without warning
if (file.toString().endsWith(".thumb.zip")) {
outdatedFilesList.add(file);
}
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
}); });
@ -336,10 +217,13 @@ public final class CardImageUtils {
LOGGER.error("Can't load files list from images folder: " + rootPath.toAbsolutePath().toString(), e); LOGGER.error("Can't load files list from images folder: " + rootPath.toAbsolutePath().toString(), e);
} }
// temp files must be deleted without errors // temp and outdated files must be deleted without errors
for (Path tempFile : tempFilesList) { Collection<Path> list = new ArrayList<>();
list.addAll(tempFilesList);
list.addAll(outdatedFilesList);
for (Path path : list) {
try { try {
Files.delete(tempFile); Files.delete(path);
} catch (Throwable e) { } catch (Throwable e) {
// ignore any error (e.g. it opened by xmage app) // ignore any error (e.g. it opened by xmage app)
} }
@ -353,7 +237,7 @@ public final class CardImageUtils {
Files.delete(brokenFile); Files.delete(brokenFile);
} catch (Throwable e) { } catch (Throwable e) {
// stop clean on any error // stop clean on any error
LOGGER.error("Images check: ERROR, can't delete broken file: " + brokenFile.toAbsolutePath().toString(), e); LOGGER.error("Images check: ERROR, can't delete broken file: " + brokenFile.toAbsolutePath(), e);
break; break;
} }
} }

View file

@ -18,16 +18,16 @@ public class TokensMtgImageSourceTest {
public void generateTokenUrlTest() throws Exception { public void generateTokenUrlTest() throws Exception {
CardImageSource imageSource = TokensMtgImageSource.instance; CardImageSource imageSource = TokensMtgImageSource.instance;
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1, "ORI", "")); CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1));
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl); Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2, "ORI", "")); url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2));
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl); Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0, "ORI", "")); url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0));
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl); Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0, null, "")); url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0));
Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl); Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
} }
} }

View file

@ -273,7 +273,7 @@ public class CardView extends SimpleCardView {
* @param storeZone if true the card zone will be set in the zone attribute. * @param storeZone if true the card zone will be set in the zone attribute.
*/ */
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) { public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor()); super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), game != null);
this.originalCard = card; this.originalCard = card;
// no information available for face down cards as long it's not a controlled face down morph card // no information available for face down cards as long it's not a controlled face down morph card
@ -487,7 +487,6 @@ public class CardView extends SimpleCardView {
} else { } else {
// a created token // a created token
this.expansionSetCode = card.getExpansionSetCode(); this.expansionSetCode = card.getExpansionSetCode();
this.tokenDescriptor = card.getTokenDescriptor();
} }
// //
// set code and card number for token copies to get the image // set code and card number for token copies to get the image
@ -600,7 +599,7 @@ public class CardView extends SimpleCardView {
} }
public CardView(MageObject object, Game game) { public CardView(MageObject object, Game game) {
super(object.getId(), "", "0", false, "", true, ""); super(object.getId(), "", "0", false, true);
this.originalCard = null; this.originalCard = null;
this.name = object.getName(); this.name = object.getName();
@ -674,7 +673,7 @@ public class CardView extends SimpleCardView {
} }
protected CardView() { protected CardView() {
super(null, "", "0", false, "", true, ""); super(null, "", "0", false, true);
} }
public CardView(EmblemView emblem) { public CardView(EmblemView emblem) {
@ -739,7 +738,7 @@ public class CardView extends SimpleCardView {
} }
public CardView(boolean empty) { public CardView(boolean empty) {
super(null, "", "0", false, "", ""); super(null, "", "0", false);
if (!empty) { if (!empty) {
throw new IllegalArgumentException("Not supported."); throw new IllegalArgumentException("Not supported.");
} }
@ -793,9 +792,10 @@ public class CardView extends SimpleCardView {
} }
CardView(Token token, Game game) { CardView(Token token, Game game) {
super(token.getId(), "", "0", false, "", ""); super(token.getId(), "", "0", false);
this.isToken = true; this.isToken = true;
this.id = token.getId(); this.id = token.getId();
this.expansionSetCode = token.getOriginalExpansionSetCode();
this.name = token.getName(); this.name = token.getName();
this.displayName = token.getName(); this.displayName = token.getName();
this.displayFullName = token.getName(); this.displayFullName = token.getName();
@ -814,8 +814,6 @@ public class CardView extends SimpleCardView {
this.manaCostRightStr = ""; this.manaCostRightStr = "";
this.rarity = Rarity.SPECIAL; this.rarity = Rarity.SPECIAL;
this.type = token.getTokenType(); this.type = token.getTokenType();
this.tokenDescriptor = token.getTokenDescriptor();
this.tokenSetCode = token.getOriginalExpansionSetCode();
} }
protected final void addTargets(Targets targets, Effects effects, Ability source, Game game) { protected final void addTargets(Targets targets, Effects effects, Ability source, Game game) {

View file

@ -20,7 +20,7 @@ public class LookedAtView implements Serializable {
public LookedAtView(String name, Cards cards, Game game) { public LookedAtView(String name, Cards cards, Game game) {
this.name = name; this.name = name;
for (Card card: cards.getCards(game)) { for (Card card: cards.getCards(game)) {
this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), card.getTokenDescriptor())); this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt()));
} }
} }

View file

@ -54,8 +54,7 @@ public class PermanentView extends CardView {
if (isToken()) { if (isToken()) {
original = new CardView(((PermanentToken) permanent).getToken().copy(), (Game) null); original = new CardView(((PermanentToken) permanent).getToken().copy(), (Game) null);
original.expansionSetCode = permanent.getExpansionSetCode(); original.expansionSetCode = permanent.getExpansionSetCode();
tokenSetCode = original.getTokenSetCode(); expansionSetCode = permanent.getExpansionSetCode();
tokenDescriptor = original.getTokenDescriptor();
} else { } else {
if (card != null) { if (card != null) {
// original may not be face down // original may not be face down

View file

@ -13,8 +13,6 @@ public class SimpleCardView implements Serializable, SelectableObjectView {
@Expose @Expose
protected UUID id; protected UUID id;
protected String expansionSetCode; protected String expansionSetCode;
protected String tokenSetCode;
protected String tokenDescriptor;
protected String cardNumber; protected String cardNumber;
protected boolean usesVariousArt; protected boolean usesVariousArt;
protected boolean gameObject; protected boolean gameObject;
@ -26,28 +24,23 @@ public class SimpleCardView implements Serializable, SelectableObjectView {
public SimpleCardView(final SimpleCardView view) { public SimpleCardView(final SimpleCardView view) {
this.id = view.id; this.id = view.id;
this.expansionSetCode = view.expansionSetCode; this.expansionSetCode = view.expansionSetCode;
this.tokenSetCode = view.tokenSetCode;
this.tokenDescriptor = view.tokenDescriptor;
this.cardNumber = view.cardNumber; this.cardNumber = view.cardNumber;
this.usesVariousArt = view.usesVariousArt; this.usesVariousArt = view.usesVariousArt;
this.gameObject = view.gameObject; this.gameObject = view.gameObject;
this.isChoosable = view.isChoosable; this.isChoosable = view.isChoosable;
this.isSelected = view.isSelected; this.isSelected = view.isSelected;
this.playableStats = view.playableStats.copy(); this.playableStats = view.playableStats.copy();
} }
public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, String tokenSetCode, String tokenDescriptor) { public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt) {
this(id, expansionSetCode, cardNumber, usesVariousArt, tokenSetCode, false, tokenDescriptor); this(id, expansionSetCode, cardNumber, usesVariousArt, false);
} }
public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, String tokenSetCode, boolean isGameObject, String tokenDescriptor) { public SimpleCardView(UUID id, String expansionSetCode, String cardNumber, boolean usesVariousArt, boolean isGameObject) {
this.id = id; this.id = id;
this.expansionSetCode = expansionSetCode; this.expansionSetCode = expansionSetCode;
this.tokenDescriptor = tokenDescriptor;
this.cardNumber = cardNumber; this.cardNumber = cardNumber;
this.usesVariousArt = usesVariousArt; this.usesVariousArt = usesVariousArt;
this.tokenSetCode = tokenSetCode;
this.gameObject = isGameObject; this.gameObject = isGameObject;
} }
@ -67,14 +60,6 @@ public class SimpleCardView implements Serializable, SelectableObjectView {
return usesVariousArt; return usesVariousArt;
} }
public String getTokenSetCode() {
return tokenSetCode;
}
public String getTokenDescriptor() {
return tokenDescriptor;
}
public boolean isGameObject() { public boolean isGameObject() {
return gameObject; return gameObject;
} }

View file

@ -18,8 +18,7 @@ public class SimpleCardsView extends LinkedHashMap<UUID, SimpleCardView> {
public SimpleCardsView(Collection<Card> cards, boolean isGameObject) { public SimpleCardsView(Collection<Card> cards, boolean isGameObject) {
for (Card card: cards) { for (Card card: cards) {
this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), isGameObject, this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), isGameObject));
card.getTokenDescriptor()));
} }
} }

View file

@ -0,0 +1,35 @@
package org.mage.test.serverside;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.PermanentToken;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JayDi85
*/
public class TokenImagesTest extends CardTestPlayerBase {
@Test
public void test_TokenMustGetSameSetCodeAsSourceCard() {
//{3}{W}, {T}, Sacrifice Memorial to Glory: Create two 1/1 white Soldier creature tokens.
addCard(Zone.BATTLEFIELD, playerA, "40K:Memorial to Glory");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{W}, {T}, Sacrifice");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Soldier Token", 2);
currentGame.getBattlefield().getAllPermanents().stream()
.filter(card -> card.getName().equals("Soldier Token"))
.forEach(card -> {
Assert.assertEquals("40K", card.getExpansionSetCode());
Assert.assertEquals("40K", ((PermanentToken) card).getToken().getOriginalExpansionSetCode());
});
}
}

View file

@ -622,7 +622,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
Assert.fail("Can't add card " + cardName + " - alias " + aliasName + " already exists for " + player.getName()); Assert.fail("Can't add card " + cardName + " - alias " + aliasName + " already exists for " + player.getName());
} }
// set code for // set code for card
String setCode = ""; String setCode = "";
String setLookup = CardUtil.substring(cardName, CardUtil.TESTS_SET_CODE_LOOKUP_LENGTH); String setLookup = CardUtil.substring(cardName, CardUtil.TESTS_SET_CODE_LOOKUP_LENGTH);
if (setLookup.contains(":")) { if (setLookup.contains(":")) {

View file

@ -49,11 +49,6 @@ public interface Card extends MageObject {
List<String> getRules(Game game); // gets card rules + in game modifications List<String> getRules(Game game); // gets card rules + in game modifications
String getExpansionSetCode(); String getExpansionSetCode();
String getTokenSetCode();
String getTokenDescriptor();
void checkForCountersToAdd(Permanent permanent, Ability source, Game game); void checkForCountersToAdd(Permanent permanent, Ability source, Game game);
void setFaceDown(boolean value, Game game); void setFaceDown(boolean value, Game game);

View file

@ -42,8 +42,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
protected UUID ownerId; protected UUID ownerId;
protected String cardNumber; protected String cardNumber;
protected String expansionSetCode; protected String expansionSetCode;
protected String tokenSetCode;
protected String tokenDescriptor;
protected Rarity rarity; protected Rarity rarity;
protected Class<? extends Card> secondSideCardClazz; protected Class<? extends Card> secondSideCardClazz;
protected Class<? extends Card> meldsWithClazz; protected Class<? extends Card> meldsWithClazz;
@ -121,8 +119,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
ownerId = card.ownerId; ownerId = card.ownerId;
cardNumber = card.cardNumber; cardNumber = card.cardNumber;
expansionSetCode = card.expansionSetCode; expansionSetCode = card.expansionSetCode;
tokenSetCode = card.tokenSetCode;
tokenDescriptor = card.tokenDescriptor;
rarity = card.rarity; rarity = card.rarity;
secondSideCardClazz = card.secondSideCardClazz; secondSideCardClazz = card.secondSideCardClazz;
@ -378,16 +374,6 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
return expansionSetCode; return expansionSetCode;
} }
@Override
public String getTokenSetCode() {
return tokenSetCode;
}
@Override
public String getTokenDescriptor() {
return tokenDescriptor;
}
@Override @Override
public List<Mana> getMana() { public List<Mana> getMana() {
List<Mana> mana = new ArrayList<>(); List<Mana> mana = new ArrayList<>();

View file

@ -87,7 +87,6 @@ public class PermanentToken extends PermanentImpl {
this.supertype.clear(); this.supertype.clear();
this.supertype.addAll(token.getSuperType()); this.supertype.addAll(token.getSuperType());
this.subtype.copyFrom(token.getSubtype(game)); this.subtype.copyFrom(token.getSubtype(game));
this.tokenDescriptor = token.getTokenDescriptor();
this.startingLoyalty = token.getStartingLoyalty(); this.startingLoyalty = token.getStartingLoyalty();
// workaround for entersTheBattlefield replacement effects // workaround for entersTheBattlefield replacement effects
if (this.abilities.containsClass(ChangelingAbility.class)) { if (this.abilities.containsClass(ChangelingAbility.class)) {

View file

@ -17,8 +17,6 @@ public interface Token extends MageObject {
@Override @Override
Token copy(); Token copy();
String getTokenDescriptor();
String getDescription(); String getDescription();
List<UUID> getLastAddedTokenIds(); List<UUID> getLastAddedTokenIds();

View file

@ -85,12 +85,6 @@ public abstract class TokenImpl extends MageObjectImpl implements Token {
this.tokenDescriptor = tokenDescriptor(); this.tokenDescriptor = tokenDescriptor();
} }
@Override
public String getTokenDescriptor() {
this.tokenDescriptor = tokenDescriptor();
return tokenDescriptor;
}
private String tokenDescriptor() { private String tokenDescriptor() {
String strName = this.name.replaceAll("[^a-zA-Z0-9]", ""); String strName = this.name.replaceAll("[^a-zA-Z0-9]", "");
String strColor = this.color.toString().replaceAll("[^a-zA-Z0-9]", ""); String strColor = this.color.toString().replaceAll("[^a-zA-Z0-9]", "");

View file

@ -697,16 +697,6 @@ public class Spell extends StackObjectImpl implements Card {
return card.getExpansionSetCode(); return card.getExpansionSetCode();
} }
@Override
public String getTokenSetCode() {
return card.getTokenSetCode();
}
@Override
public String getTokenDescriptor() {
return card.getTokenDescriptor();
}
@Override @Override
public void setFaceDown(boolean value, Game game) { public void setFaceDown(boolean value, Game game) {
faceDown = value; faceDown = value;