Files
forge/forge-gui-mobile/src/forge/assets/ImageCache.java
Anthony Calosa 9a1e467bb7 Update LibGDX March 29, 2020, Use Google cache
(reverted the cache so there's room for dependency like android-support-v4, permissions in the future,
lighter than cache2k)
2020-04-02 12:15:01 +08:00

290 lines
10 KiB
Java

/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.assets;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import forge.ImageKeys;
import forge.card.CardEdition;
import forge.game.card.CardView;
import forge.game.player.IHasIcon;
import forge.item.IPaperCard;
import forge.item.InventoryItem;
import forge.model.FModel;
import forge.properties.ForgeConstants;
import forge.util.ImageUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* This class stores ALL card images in a cache with soft values. this means
* that the images may be collected when they are not needed any more, but will
* be kept as long as possible.
* <p/>
* The keys are the following:
* <ul>
* <li>Keys start with the file name, extension is skipped</li>
* <li>The key without suffix belongs to the unmodified image from the file</li>
* </ul>
*
* @author Forge
* @version $Id: ImageCache.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class ImageCache {
// short prefixes to save memory
private static final Set<String> missingIconKeys = new HashSet<>();
private static final LoadingCache<String, Texture> cache = CacheBuilder.newBuilder()
.maximumSize(400)
.expireAfterAccess(15, TimeUnit.MINUTES)
.build(new ImageLoader());
public static final Texture defaultImage;
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
private static boolean imageLoaded, delayLoadRequested;
public static void allowSingleLoad() {
imageLoaded = false; //reset at the beginning of each render
delayLoadRequested = false;
}
static {
Texture defImage = null;
try {
defImage = new Texture(Gdx.files.absolute(ForgeConstants.NO_CARD_FILE));
} catch (Exception ex) {
System.err.println("could not load default card image");
} finally {
defaultImage = (null == defImage) ? new Texture(10, 10, Format.RGBA8888) : defImage;
}
}
public static void clear() {
cache.invalidateAll();
missingIconKeys.clear();
}
public static Texture getImage(InventoryItem ii) {
return getImage(ii.getImageKey(false), true);
}
/**
* retrieve an icon from the cache. returns the current skin's ICO_UNKNOWN if the icon image is not found
* in the cache and cannot be loaded from disk.
*/
public static FImage getIcon(IHasIcon ihi) {
String imageKey = ihi.getIconImageKey();
final Texture icon;
if (missingIconKeys.contains(imageKey) || (icon = getImage(ihi.getIconImageKey(), false)) == null) {
missingIconKeys.add(imageKey);
return FSkinImage.UNKNOWN;
}
return new FTextureImage(icon);
}
/**
* This requests the original unscaled image from the cache for the given key.
* If the image does not exist then it can return a default image if desired.
* <p>
* If the requested image is not present in the cache then it attempts to load
* the image from file (slower) and then add it to the cache for fast future access.
* </p>
*/
public static Texture getImage(String imageKey, boolean useDefaultIfNotFound) {
if (StringUtils.isEmpty(imageKey)) {
return null;
}
boolean altState = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
if (altState) {
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
}
if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) {
imageKey = ImageUtil.getImageKey(ImageUtil.getPaperCardFromImageKey(imageKey), altState, true);
if (StringUtils.isBlank(imageKey)) {
return defaultImage;
}
}
Texture image;
if (useDefaultIfNotFound) {
// Load from file and add to cache if not found in cache initially.
image = cache.getIfPresent(imageKey);
if (image != null) { return image; }
if (imageLoaded) { //prevent loading more than one image each render for performance
if (!delayLoadRequested) {
//ensure images continue to load even if no input is being received
delayLoadRequested = true;
Gdx.graphics.requestRendering();
}
return null;
}
imageLoaded = true;
}
try { image = cache.get(imageKey); }
catch (final Exception ex) {
image = null;
}
// No image file exists for the given key so optionally associate with
// a default "not available" image and add to cache for given key.
if (image == null) {
if (useDefaultIfNotFound) {
image = defaultImage;
cache.put(imageKey, defaultImage);
}
}
return image;
}
public static void preloadCache(Iterable<String> keys) {
try {
cache.getAll(keys);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public static TextureRegion croppedBorderImage(Texture image, boolean fullborder) {
if (!fullborder)
return new TextureRegion(image);
float rscale = 0.96f;
int rw = Math.round(image.getWidth()*rscale);
int rh = Math.round(image.getHeight()*rscale);
int rx = Math.round((image.getWidth() - rw)/2f);
int ry = Math.round((image.getHeight() - rh)/2f)-2;
return new TextureRegion(image, rx, ry, rw, rh);
}
public static boolean isWhiteBordered(IPaperCard c) {
if (c == null)
return false;
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
if (ed != null && ed.isWhiteBorder())
return true;
return false;
}
public static boolean isWhiteBordered(CardView c) {
if (c == null)
return false;
CardView.CardStateView state = c.getCurrentState();
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
if (ed != null && ed.isWhiteBorder() && state.getFoilIndex() == 0)
return true;
return false;
}
public static Color borderColor(IPaperCard c) {
if (c == null)
return Color.valueOf("#171717");
CardEdition ed = FModel.getMagicDb().getEditions().get(c.getEdition());
if (ed != null && ed.isWhiteBorder())
return Color.valueOf("#fffffd");
return Color.valueOf("#171717");
}
public static Color borderColor(CardView c) {
if (c == null)
return Color.valueOf("#171717");
CardView.CardStateView state = c.getCurrentState();
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
if (ed != null && ed.isWhiteBorder() && state.getFoilIndex() == 0)
return Color.valueOf("#fffffd");
return Color.valueOf("#171717");
}
public static int getFSkinBorders(CardView c) {
if (c == null)
return 0;
CardView.CardStateView state = c.getCurrentState();
CardEdition ed = FModel.getMagicDb().getEditions().get(state.getSetCode());
if (ed != null && ed.isWhiteBorder() && state.getFoilIndex() == 0)
return 1;
return 0;
}
public static boolean isExtendedArt(CardView c) {
if (c == null)
return false;
CardView.CardStateView state = c.getCurrentState();
if (state.getSetCode().contains("MPS_"))
return true;
if (state.getSetCode().equalsIgnoreCase("UST"))
return true;
return false;
}
public static boolean isExtendedArt(IPaperCard c) {
if (c == null)
return false;
if (c.getEdition().contains("MPS_"))
return true;
if (c.getEdition().equalsIgnoreCase("UST"))
return true;
return false;
}
public static FImage getBorderImage(CardView c, boolean canshow) {
if (!canshow)
return BlackBorder;
if (isWhiteBordered(c))
return WhiteBorder;
return BlackBorder;
}
public static FImage getBorderImage(IPaperCard c) {
if (isWhiteBordered(c))
return WhiteBorder;
return BlackBorder;
}
public static Color getTint(CardView c) {
if (c == null)
return Color.CLEAR;
if (c.isFaceDown())
return Color.CLEAR;
CardView.CardStateView state = c.getCurrentState();
if (state.getColors().isColorless()) //Moonlace -> target spell or permanent becomes colorless.
return Color.valueOf("#A0A6A4");
else if (state.getColors().isMonoColor()) {
if (state.getColors().hasBlack())
return Color.valueOf("#48494a");
else if (state.getColors().hasBlue())
return Color.valueOf("#62b5f8");
else if (state.getColors().hasRed())
return Color.valueOf("#f6532d");
else if (state.getColors().hasGreen())
return Color.valueOf("#66cb35");
else if (state.getColors().hasWhite())
return Color.valueOf("#EEEBE1");
}
else if (state.getColors().isMulticolor())
return Color.valueOf("#F9E084");
return Color.CLEAR;
}
}