From 6f0ba03d241a93c533e159c9bafcce33f11d88f7 Mon Sep 17 00:00:00 2001 From: Anthony Calosa Date: Sun, 12 Sep 2021 15:52:57 +0800 Subject: [PATCH] [Mobile] Fix blinking card images while using online image fetcher - also cache card list for faster lookup --- forge-core/src/main/java/forge/ImageKeys.java | 306 ++++++++++++------ .../src/main/java/forge/item/PaperCard.java | 7 +- .../src/forge/assets/ImageCache.java | 32 +- .../src/forge/card/CardRenderer.java | 1 - .../src/forge/deck/FDeckEditor.java | 1 - .../src/forge/screens/home/LoadGameMenu.java | 7 +- .../main/java/forge/util/ImageFetcher.java | 5 + 7 files changed, 240 insertions(+), 119 deletions(-) diff --git a/forge-core/src/main/java/forge/ImageKeys.java b/forge-core/src/main/java/forge/ImageKeys.java index e825c09750a..9112855b0eb 100644 --- a/forge-core/src/main/java/forge/ImageKeys.java +++ b/forge-core/src/main/java/forge/ImageKeys.java @@ -65,6 +65,7 @@ public final class ImageKeys { return tokenKey.substring(ImageKeys.TOKEN_PREFIX.length()); } + private static final Map 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 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 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> cachedContent=new HashMap<>(); + static HashMap> 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 content = cachedContent.getOrDefault(keyParts[0], null); //avoid checking for file if edition doesn't have any images return editionHasImage && hitCache(content, keyParts[1]); diff --git a/forge-core/src/main/java/forge/item/PaperCard.java b/forge-core/src/main/java/forge/item/PaperCard.java index 1ec639ba940..95d0b8a8f2e 100644 --- a/forge-core/src/main/java/forge/item/PaperCard.java +++ b/forge-core/src/main/java/forge/item/PaperCard.java @@ -136,8 +136,11 @@ public final class PaperCard implements Comparable, 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; } diff --git a/forge-gui-mobile/src/forge/assets/ImageCache.java b/forge-gui-mobile/src/forge/assets/ImageCache.java index 099da2b5a12..053c6d79da2 100644 --- a/forge-gui-mobile/src/forge/assets/ImageCache.java +++ b/forge-gui-mobile/src/forge/assets/ImageCache.java @@ -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 } } diff --git a/forge-gui-mobile/src/forge/card/CardRenderer.java b/forge-gui-mobile/src/forge/card/CardRenderer.java index af5ad43624f..37631642716 100644 --- a/forge-gui-mobile/src/forge/card/CardRenderer.java +++ b/forge-gui-mobile/src/forge/card/CardRenderer.java @@ -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; diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index d3590ddbe96..834c095d953 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -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; diff --git a/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java b/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java index e248e02fdf6..40082ecf6a4 100644 --- a/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java +++ b/forge-gui-mobile/src/forge/screens/home/LoadGameMenu.java @@ -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); } } } diff --git a/forge-gui/src/main/java/forge/util/ImageFetcher.java b/forge-gui/src/main/java/forge/util/ImageFetcher.java index 4c86431a4b7..b017f1d7ded 100644 --- a/forge-gui/src/main/java/forge/util/ImageFetcher.java +++ b/forge-gui/src/main/java/forge/util/ImageFetcher.java @@ -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; }