[Mobile] Fix blinking card images while using online image fetcher

- also cache card list for faster lookup
This commit is contained in:
Anthony Calosa
2021-09-12 15:52:57 +08:00
parent 8149cbb507
commit 6f0ba03d24
7 changed files with 240 additions and 119 deletions

View File

@@ -65,6 +65,7 @@ public final class ImageKeys {
return tokenKey.substring(ImageKeys.TOKEN_PREFIX.length());
}
private static final Map<String, File> cachedCards = new HashMap<>(50000);
public static File getImageFile(String key) {
if (StringUtils.isEmpty(key))
return null;
@@ -97,101 +98,142 @@ public final class ImageKeys {
dir = CACHE_CARD_PICS_DIR;
}
File file = findFile(dir, filename);
if (file != null) { return file; }
File cachedFile = cachedCards.get(filename);
if (cachedFile != null) {
return cachedFile;
} else {
File file = findFile(dir, filename);
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// AE -> Ae and Ae -> AE for older cards with different file names
// on case-sensitive file systems
if (filename.contains("Ae")) {
file = findFile(dir, TextUtil.fastReplace(filename, "Ae", "AE"));
if (file != null) { return file; }
} else if (filename.contains("AE")) {
file = findFile(dir, TextUtil.fastReplace(filename, "AE", "Ae"));
if (file != null) { return file; }
}
//try fullborder...
if (filename.contains(".full")) {
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) { return file; }
// if there's a 1st art variant try without it for .fullborder images
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
if (file != null) { return file; }
// if there's a 1st art variant try with it for .fullborder images
file = findFile(dir, fullborderFile.replaceAll("[0-9]*.fullborder", "1.fullborder"));
if (file != null) { return file; }
// if there's an art variant try without it for .full images
file = findFile(dir, filename.replaceAll("[0-9].full",".full"));
if (file != null) { return file; }
// if there's a 1st art variant try with it for .full images
file = findFile(dir, filename.replaceAll("[0-9]*.full", "1.full"));
if (file != null) { return file; }
//setlookup
if (!StaticData.instance().getSetLookup().isEmpty()) {
for (String setKey : StaticData.instance().getSetLookup().keySet()) {
if (filename.startsWith(setKey)) {
for (String setLookup : StaticData.instance().getSetLookup().get(setKey)) {
//.fullborder lookup
file = findFile(dir, TextUtil.fastReplace(fullborderFile, setKey, getSetFolder(setLookup)));
if (file != null) { return file; }
file = findFile(dir, TextUtil.fastReplace(fullborderFile, setKey, getSetFolder(setLookup)).replaceAll("[0-9]*.fullborder", "1.fullborder"));
if (file != null) { return file; }
//.full lookup
file = findFile(dir, TextUtil.fastReplace(filename, setKey, getSetFolder(setLookup)));
if (file != null) { return file; }
file = findFile(dir, TextUtil.fastReplace(filename, setKey, getSetFolder(setLookup)).replaceAll("[0-9]*.full", "1.full"));
if (file != null) { return file; }
// AE -> Ae and Ae -> AE for older cards with different file names
// on case-sensitive file systems
if (filename.contains("Ae")) {
file = findFile(dir, TextUtil.fastReplace(filename, "Ae", "AE"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
} else if (filename.contains("AE")) {
file = findFile(dir, TextUtil.fastReplace(filename, "AE", "Ae"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
}
//try fullborder...
if (filename.contains(".full")) {
String fullborderFile = TextUtil.fastReplace(filename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// if there's a 1st art variant try without it for .fullborder images
file = findFile(dir, TextUtil.fastReplace(fullborderFile, "1.fullborder", ".fullborder"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// if there's a 1st art variant try with it for .fullborder images
file = findFile(dir, fullborderFile.replaceAll("[0-9]*.fullborder", "1.fullborder"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// if there's an art variant try without it for .full images
file = findFile(dir, filename.replaceAll("[0-9].full",".full"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// if there's a 1st art variant try with it for .full images
file = findFile(dir, filename.replaceAll("[0-9]*.full", "1.full"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
//setlookup
file = setLookUpFile(filename, fullborderFile);
if (file != null) {
cachedCards.put(filename, file);
return file;
}
}
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
if (!filename.contains(".full")) {
file = findFile(dir, TextUtil.addSuffix(filename,".full"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
file = findFile(dir, TextUtil.addSuffix(filename,".fullborder"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
}
if (dir.equals(CACHE_TOKEN_PICS_DIR)) {
int index = filename.lastIndexOf('_');
if (index != -1) {
String setlessFilename = filename.substring(0, index);
String setCode = filename.substring(index + 1);
// try with upper case set
file = findFile(dir, setlessFilename + "_" + setCode.toUpperCase());
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// try with lower case set
file = findFile(dir, setlessFilename + "_" + setCode.toLowerCase());
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// try without set name
file = findFile(dir, setlessFilename);
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// if there's an art variant try without it
if (setlessFilename.matches(".*[0-9]*$")) {
file = findFile(dir, setlessFilename.replaceAll("[0-9]*$", ""));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
}
}
} else if (filename.contains("/")) {
String setlessFilename = filename.substring(filename.indexOf('/') + 1);
file = findFile(dir, setlessFilename);
if (file != null) {
cachedCards.put(filename, file);
return file;
}
if (setlessFilename.contains(".full")) {
//try fullborder
String fullborderFile = TextUtil.fastReplace(setlessFilename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) {
cachedCards.put(filename, file);
return file;
}
// try lowering the art index to the minimum for regular cards
file = findFile(dir, setlessFilename.replaceAll("[0-9]*[.]full", "1.full"));
if (file != null) {
cachedCards.put(filename, file);
return file;
}
}
}
}
//if an image, like phenomenon or planes is missing .full in their filenames but you have an existing images that have .full/.fullborder
if (!filename.contains(".full")) {
file = findFile(dir, TextUtil.addSuffix(filename,".full"));
if (file != null) { return file; }
file = findFile(dir, TextUtil.addSuffix(filename,".fullborder"));
if (file != null) { return file; }
}
if (dir.equals(CACHE_TOKEN_PICS_DIR)) {
int index = filename.lastIndexOf('_');
if (index != -1) {
String setlessFilename = filename.substring(0, index);
String setCode = filename.substring(index + 1);
// try with upper case set
file = findFile(dir, setlessFilename + "_" + setCode.toUpperCase());
if (file != null) { return file; }
// try with lower case set
file = findFile(dir, setlessFilename + "_" + setCode.toLowerCase());
if (file != null) { return file; }
// try without set name
file = findFile(dir, setlessFilename);
if (file != null) { return file; }
// if there's an art variant try without it
if (setlessFilename.matches(".*[0-9]*$")) {
file = findFile(dir, setlessFilename.replaceAll("[0-9]*$", ""));
if (file != null) { return file; }
}
}
} else if (filename.contains("/")) {
String setlessFilename = filename.substring(filename.indexOf('/') + 1);
file = findFile(dir, setlessFilename);
if (file != null) { return file; }
if (setlessFilename.contains(".full")) {
//try fullborder
String fullborderFile = TextUtil.fastReplace(setlessFilename, ".full", ".fullborder");
file = findFile(dir, fullborderFile);
if (file != null) { return file; }
// try lowering the art index to the minimum for regular cards
file = findFile(dir, setlessFilename.replaceAll("[0-9]*[.]full", "1.full"));
if (file != null) { return file; }
}
}
// System.out.println("File not found, no image created: " + key);
return null;
}
@@ -200,16 +242,72 @@ public final class ImageKeys {
? StaticData.instance().getEditions().getCode2ByCode(edition) // by default 2-letter codes from MWS are used
: CACHE_CARD_PICS_SUBDIR.get(edition); // may use custom paths though
}
private static File findFile(String dir, String filename) {
for (String ext : FILE_EXTENSIONS) {
File file = new File(dir, filename + ext);
if (file.exists()) {
if (file.isDirectory()) {
file.delete();
continue;
private static File setLookUpFile(String filename, String fullborderFile) {
if (!StaticData.instance().getSetLookup().isEmpty()) {
for (String setKey : StaticData.instance().getSetLookup().keySet()) {
if (filename.startsWith(setKey)) {
for (String setLookup : StaticData.instance().getSetLookup().get(setKey)) {
String lookupDirectory = CACHE_CARD_PICS_DIR + setLookup;
File f = new File(lookupDirectory);
String[] cardNames = f.list();
if (cardNames != null) {
Set<String> cardList = new HashSet<>(Arrays.asList(cardNames));
for (String ext : FILE_EXTENSIONS) {
if (ext.equals(""))
continue;
String fb1 = fullborderFile.replace(setKey+"/","")+ext;
if (cardList.contains(fb1)) {
return new File(lookupDirectory+"/"+fb1);
}
String fb2 = fullborderFile.replace(setKey+"/","").replaceAll("[0-9]*.fullborder", "1.fullborder")+ext;
if (cardList.contains(fb2)) {
return new File(lookupDirectory+"/"+fb2);
}
String f1 = filename.replace(setKey+"/","")+ext;
if (cardList.contains(f1)) {
return new File(lookupDirectory+"/"+f1);
}
String f2 = filename.replace(setKey+"/","").replaceAll("[0-9]*.full", "1.full")+ext;
if (cardList.contains(f2)) {
return new File(lookupDirectory+"/"+f2);
}
}
}
}
}
}
}
return null;
}
private static File findFile(String dir, String filename) {
if (dir.equals(CACHE_CARD_PICS_DIR)) {
File f = new File(dir+"/"+filename);
String initialDirectory = f.getParent();
String cardName = f.getName();
File parentDir = new File(initialDirectory);
String[] cardNames = parentDir.list();
if (cardNames != null) {
Set<String> cardList = new HashSet<>(Arrays.asList(cardNames));
for (String ext : FILE_EXTENSIONS) {
if (ext.equals(""))
continue;
String cardLookup = cardName+ext;
if (cardList.contains(cardLookup)) {
return new File(parentDir+"/"+cardLookup);
}
}
}
} else {
//old method for tokens and others
for (String ext : FILE_EXTENSIONS) {
File file = new File(dir, filename + ext);
if (file.exists()) {
if (file.isDirectory()) {
file.delete();
continue;
}
return file;
}
return file;
}
}
return null;
@@ -217,8 +315,11 @@ public final class ImageKeys {
//shortcut for determining if a card image exists for a given card
//should only be called from PaperCard.hasImage()
static HashMap<String, HashSet<String>> cachedContent=new HashMap<>();
static HashMap<String, HashSet<String>> cachedContent=new HashMap<>(50000);
public static boolean hasImage(PaperCard pc) {
return hasImage(pc, false);
}
public static boolean hasImage(PaperCard pc, boolean update) {
Boolean editionHasImage = editionImageLookup.get(pc.getEdition());
if (editionHasImage == null) {
String setFolder = getSetFolder(pc.getEdition());
@@ -232,6 +333,10 @@ public final class ImageKeys {
if (!filename.endsWith(".jpg") && !filename.endsWith(".png"))
continue; // not image - not interested
setFolderContent.add(filename.split("\\.")[0]); // get rid of any full or fullborder
//preload cachedCards at startUp
String key = setFolder + "/" + filename.replace(".fullborder", ".full").replace(".jpg", "").replace(".png", "");
File value = new File(CACHE_CARD_PICS_DIR + setFolder + "/" + filename);
cachedCards.put(key, value);
}
cachedContent.put(setFolder, setFolderContent);
}
@@ -239,6 +344,13 @@ public final class ImageKeys {
String[] keyParts = StringUtils.split(pc.getCardImageKey(), "//");
if (keyParts.length != 2)
return false;
if (update && editionHasImage) {
try {
cachedContent.get(getSetFolder(pc.getEdition())).add(pc.getName());
} catch (Exception e) {
System.err.println(e);
}
}
HashSet<String> content = cachedContent.getOrDefault(keyParts[0], null);
//avoid checking for file if edition doesn't have any images
return editionHasImage && hitCache(content, keyParts[1]);

View File

@@ -136,8 +136,11 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
}
public boolean hasImage() {
if (hasImage == null) { //cache value since it's not free to calculate
hasImage = ImageKeys.hasImage(this);
return hasImage(false);
}
public boolean hasImage(boolean update) {
if (hasImage == null || update) { //cache value since it's not free to calculate
hasImage = ImageKeys.hasImage(this, update);
}
return hasImage;
}

View File

@@ -113,8 +113,6 @@ public class ImageCache {
}
public static void clear() {
cache.invalidateAll();
cache.cleanUp();
missingIconKeys.clear();
}
@@ -158,17 +156,26 @@ public class ImageCache {
final String prefix = imageKey.substring(0, 2);
PaperCard paperCard = null;
if (prefix.equals(ImageKeys.CARD_PREFIX)) {
PaperCard paperCard = ImageUtil.getPaperCardFromImageKey(imageKey);
try {
paperCard = ImageUtil.getPaperCardFromImageKey(imageKey);
} catch (Exception e) {
return false;
}
if (paperCard == null)
return false;
final boolean backFace = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
final String cardfilename = backFace ? paperCard.getCardAltImageKey() : paperCard.getCardImageKey();
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".jpg").exists())
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".png").exists())
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + TextUtil.fastReplace(cardfilename,".full", ".fullborder") + ".jpg").exists())
return false;
if (!FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER)) {
return paperCard.hasImage();
} else {
final boolean backFace = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
final String cardfilename = backFace ? paperCard.getCardAltImageKey() : paperCard.getCardImageKey();
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".jpg").exists())
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + cardfilename + ".png").exists())
if (!new File(ForgeConstants.CACHE_CARD_PICS_DIR + "/" + TextUtil.fastReplace(cardfilename,".full", ".fullborder") + ".jpg").exists())
return false;
}
} else if (prefix.equals(ImageKeys.TOKEN_PREFIX)) {
final String tokenfilename = imageKey.substring(2) + ".jpg";
@@ -235,12 +242,11 @@ public class ImageCache {
// a default "not available" image and add to cache for given key.
if (image == null) {
if (useDefaultIfNotFound) {
image = defaultImage;
image = useOtherCache ? defaultImage : null;
if (useOtherCache)
otherCache.put(imageKey, defaultImage);
else
cache.put(imageKey, defaultImage);
if (imageBorder.get(image.toString()) == null)
if (image != null && imageBorder.get(image.toString()) == null)
imageBorder.put(image.toString(), Pair.of(Color.valueOf("#171717").toString(), false)); //black border
}
}

View File

@@ -51,7 +51,6 @@ import forge.gui.card.CardDetailUtil;
import forge.gui.card.CardDetailUtil.DetailColors;
import forge.item.IPaperCard;
import forge.item.InventoryItem;
import forge.localinstance.properties.ForgeConstants;
import forge.localinstance.properties.ForgeConstants.CounterDisplayType;
import forge.localinstance.properties.ForgePreferences;
import forge.localinstance.properties.ForgePreferences.FPref;

View File

@@ -27,7 +27,6 @@ import forge.assets.FSkin;
import forge.assets.FSkinFont;
import forge.assets.FSkinImage;
import forge.assets.FTextureRegionImage;
import forge.card.CardDb;
import forge.card.CardEdition;
import forge.deck.io.DeckPreferences;
import forge.gamemodes.limited.BoosterDraft;

View File

@@ -105,11 +105,8 @@ public class LoadGameMenu extends FPopupMenu {
protected void buildMenu() {
FScreen currentScreen = Forge.getCurrentScreen();
for (LoadGameScreen lgs : LoadGameScreen.values()) {
//fixes the overlapping menu items when the user suddenly switch from load game screen index to another screen
if (HomeScreen.instance.getActiveButtonIndex() == 1) {
addItem(lgs.item);
lgs.item.setSelected(currentScreen == lgs.screen);
}
addItem(lgs.item);
lgs.item.setSelected(currentScreen == lgs.screen);
}
}
}

View File

@@ -126,6 +126,11 @@ public abstract class ImageFetcher {
if (destFile.exists()) {
// TODO: Figure out why this codepath gets reached.
// Ideally, fetchImage() wouldn't be called if we already have the image.
if (prefix.equals(ImageKeys.CARD_PREFIX)) {
PaperCard paperCard = ImageUtil.getPaperCardFromImageKey(imageKey);
if (paperCard != null)
paperCard.hasImage(true);
}
return;
}