remove loadingCache and use card assetmanager exclusively

This commit is contained in:
Anthony Calosa
2022-07-06 16:29:57 +08:00
parent c81158530f
commit b1e307747f
2 changed files with 134 additions and 193 deletions

View File

@@ -18,13 +18,20 @@
package forge.assets;
import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.List;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.TextureLoader;
import com.badlogic.gdx.assets.loaders.resolvers.AbsoluteFileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.TextureData;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.ObjectSet;
import forge.gui.FThreads;
import forge.util.FileUtil;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
@@ -33,9 +40,6 @@ 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 com.google.common.cache.RemovalListener;
import forge.Forge;
import forge.ImageKeys;
@@ -66,10 +70,8 @@ import forge.util.ImageUtil;
* @version $Id: ImageCache.java 24769 2014-02-09 13:56:04Z Hellfish $
*/
public class ImageCache {
// short prefixes to save memory
private static final ObjectSet<String> missingIconKeys = new ObjectSet<>();
private static LoadingCache<String, Texture> cache;
private static List<String> borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
static int maxCardCapacity = 400; //default card capacity
static AssetManager cardTextureManager = new AssetManager(new AbsoluteFileHandleResolver());
static AssetManager otherTextureManager = new AssetManager(new AbsoluteFileHandleResolver());
@@ -82,25 +84,12 @@ public class ImageCache {
filtered.magFilter = Texture.TextureFilter.Linear;
//override maxCardCapacity
maxCardCapacity = capacity;
//this cache is used exclusively for full bordermasking.
cache = CacheBuilder.newBuilder()
.maximumSize(capacity)
.expireAfterAccess(15, TimeUnit.MINUTES)
.removalListener((RemovalListener<String, Texture>) removalNotification -> {
if (removalNotification.wasEvicted()) {
if (removalNotification.getValue() != ImageCache.defaultImage)
removalNotification.getValue().dispose();
CardRenderer.clearcardArtCache();
}
})
.build(new ImageLoader());
System.out.println("Card Texture Cache Size: "+capacity);
}
public static final Texture defaultImage;
public static FImage BlackBorder = FSkinImage.IMG_BORDER_BLACK;
public static FImage WhiteBorder = FSkinImage.IMG_BORDER_WHITE;
private static final ObjectMap<String, Pair<String, Boolean>> imageBorder = new ObjectMap<>(1024);
private static final ObjectMap<String, Texture> generatedCards = new ObjectMap<>(512);
private static boolean imageLoaded, delayLoadRequested;
public static void allowSingleLoad() {
@@ -231,8 +220,7 @@ public class ImageCache {
File imageFile = ImageKeys.getImageFile(imageKey);
if (useDefaultIfNotFound) {
// Load from file and add to cache if not found in cache initially.
// Except for full bordermask, use assetmanager for card textures. This should make dispose texture options work effectively.
image = Forge.enableUIMask.equals("Full") && !useOtherCache ? cache.getIfPresent(imageKey) : getAsset(imageFile, useOtherCache);
image = getAsset(imageKey, imageFile, useOtherCache);
if (image != null) { return image; }
@@ -248,7 +236,7 @@ public class ImageCache {
}
try {
image = Forge.enableUIMask.equals("Full") && !useOtherCache ? cache.get(imageKey) : loadAsset(imageFile, useOtherCache);
image = loadAsset(imageKey, imageFile, useOtherCache);
} catch (final Exception ex) {
image = null;
}
@@ -267,12 +255,14 @@ public class ImageCache {
}
return image;
}
static Texture getAsset(File file, boolean otherCache) {
static Texture getAsset(String imageKey, File file, boolean otherCache) {
if (file == null)
return null;
if (!otherCache && Forge.enableUIMask.equals("Full") && isBorderless(imageKey))
return generatedCards.get(imageKey);
return otherCache ? otherTextureManager.get(file.getPath(), Texture.class, false) : cardTextureManager.get(file.getPath(), Texture.class, false);
}
static Texture loadAsset(File file, boolean otherCache) {
static Texture loadAsset(String imageKey, File file, boolean otherCache) {
if (file == null)
return null;
if (!otherCache && cardTextureManager.getLoadedAssets() > maxCardCapacity) {
@@ -289,7 +279,16 @@ public class ImageCache {
} else {
cardTextureManager.load(fileName, Texture.class, Forge.isTextureFilteringEnabled() ? filtered : defaultParameter);
cardTextureManager.finishLoadingAsset(fileName);
return cardTextureManager.get(fileName, Texture.class, false);
Texture t = cardTextureManager.get(fileName, Texture.class, false);
if (Forge.enableUIMask.equals("Full")) {
boolean borderless = isBorderless(imageKey);
updateBorders(t.toString(), borderless ? Pair.of(Color.valueOf("#171717").toString(), false): isCloserToWhite(getpixelColor(t)));
if (borderless) {
t = generateTexture(new FileHandle(file), t, Forge.isTextureFilteringEnabled());
generatedCards.put(imageKey, t);
}
}
return t;
}
}
public static void preloadCache(Iterable<String> keys) {
@@ -339,7 +338,7 @@ public class ImageCache {
return 0;
}
public static boolean isBorderlessCardArt(Texture t) {
return ImageLoader.isBorderless(t);
return isBorderless(t);
}
public static void updateBorders(String textureString, Pair<String, Boolean> colorPair){
imageBorder.put(textureString, colorPair);
@@ -386,4 +385,111 @@ public class ImageCache {
return borderColor(t);
}
public static Texture generateTexture(FileHandle fh, Texture t, boolean textureFilter) {
if (t == null || fh == null)
return t;
final Texture[] n = new Texture[1];
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
Pixmap pImage = new Pixmap(fh);
int w = pImage.getWidth();
int h = pImage.getHeight();
int radius = (h - w) / 8;
Pixmap pMask = createRoundedRectangle(w, h, radius, Color.RED);
drawPixelstoMask(pImage, pMask);
TextureData textureData = new PixmapTextureData(
pMask, //pixmap to use
Pixmap.Format.RGBA8888,
textureFilter, //use mipmaps
false, true);
n[0] = new Texture(textureData);
if (textureFilter)
n[0].setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
pImage.dispose();
pMask.dispose();
}
});
return n[0];
}
public static Pixmap createRoundedRectangle(int width, int height, int cornerRadius, Color color) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
Pixmap ret = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
//round corners
pixmap.fillCircle(cornerRadius, cornerRadius, cornerRadius);
pixmap.fillCircle(width - cornerRadius - 1, cornerRadius, cornerRadius);
pixmap.fillCircle(cornerRadius, height - cornerRadius - 1, cornerRadius);
pixmap.fillCircle(width - cornerRadius - 1, height - cornerRadius - 1, cornerRadius);
//two rectangle parts
pixmap.fillRectangle(cornerRadius, 0, width - cornerRadius * 2, height);
pixmap.fillRectangle(0, cornerRadius, width, height - cornerRadius * 2);
//draw rounded rectangle
ret.setColor(color);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (pixmap.getPixel(x, y) != 0) ret.drawPixel(x, y);
}
}
pixmap.dispose();
return ret;
}
public static void drawPixelstoMask(Pixmap pixmap, Pixmap mask){
int pixmapWidth = mask.getWidth();
int pixmapHeight = mask.getHeight();
Color pixelColor = new Color();
for (int x=0; x<pixmapWidth; x++){
for (int y=0; y<pixmapHeight; y++){
if (mask.getPixel(x, y) != 0) {
Color.rgba8888ToColor(pixelColor, pixmap.getPixel(x, y));
mask.setColor(pixelColor);
mask.drawPixel(x, y);
}
}
}
}
public static boolean isBorderless(String imagekey) {
if(borderlessCardlistKey.isEmpty())
return false;
if (imagekey.length() > 7) {
if ((!imagekey.substring(0, 7).contains("MPS_KLD"))&&(imagekey.substring(0, 4).contains("MPS_"))) //MPS_ sets except MPD_KLD
return true;
}
return borderlessCardlistKey.contains(TextUtil.fastReplace(imagekey,".full",".fullborder"));
}
public static boolean isBorderless(Texture t) {
if(borderlessCardlistKey.isEmpty())
return false;
//generated texture/pixmap?
if (t.toString().contains("com.badlogic.gdx.graphics.Texture@"))
return true;
for (String key : borderlessCardlistKey) {
if (t.toString().contains(key))
return true;
}
return false;
}
public static String getpixelColor(Texture i) {
if (!i.getTextureData().isPrepared()) {
i.getTextureData().prepare(); //prepare texture
}
//get pixmap from texture data
Pixmap pixmap = i.getTextureData().consumePixmap();
//get pixel color from x,y texture coordinate based on the image fullborder or not
Color color = new Color(pixmap.getPixel(croppedBorderImage(i).getRegionX()+1, croppedBorderImage(i).getRegionY()+1));
pixmap.dispose();
return color.toString();
}
public static Pair<String, Boolean> isCloserToWhite(String c){
if (c == null || c == "")
return Pair.of(Color.valueOf("#171717").toString(), false);
int c_r = Integer.parseInt(c.substring(0,2),16);
int c_g = Integer.parseInt(c.substring(2,4),16);
int c_b = Integer.parseInt(c.substring(4,6),16);
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
return Pair.of(c,brightness > 155);
}
}

View File

@@ -1,165 +0,0 @@
package forge.assets;
import static forge.assets.ImageCache.croppedBorderImage;
import java.io.File;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.TextureData;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
import com.google.common.cache.CacheLoader;
import forge.Forge;
import forge.ImageKeys;
import forge.gui.FThreads;
import forge.localinstance.properties.ForgeConstants;
import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel;
import forge.util.FileUtil;
import forge.util.TextUtil;
final class ImageLoader extends CacheLoader<String, Texture> {
private static List<String> borderlessCardlistKey = FileUtil.readFile(ForgeConstants.BORDERLESS_CARD_LIST_FILE);
Texture n;
@Override
public Texture load(String key) {
if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DISABLE_CARD_IMAGES))
return null;
boolean extendedArt = isBorderless(key) && Forge.enableUIMask.equals("Full");
boolean textureFilter = Forge.isTextureFilteringEnabled();
File file = ImageKeys.getImageFile(key);
if (file != null) {
FileHandle fh = new FileHandle(file);
try {
Texture t = new Texture(fh, textureFilter);
//update
ImageCache.updateBorders(t.toString(), extendedArt ? Pair.of(Color.valueOf("#171717").toString(), false): isCloserToWhite(getpixelColor(t)));
if (textureFilter)
t.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
if (extendedArt)
return generateTexture(fh, t, textureFilter);
return t;
}
catch (Exception ex) {
Forge.log("Could not read image file " + fh.path() + "\nException:\n" + ex.toString());
return null;
}
}
return null;
}
public Texture generateTexture(FileHandle fh, Texture t, boolean textureFilter) {
if (t == null || fh == null)
return t;
FThreads.invokeInEdtNowOrLater(new Runnable() {
@Override
public void run() {
Pixmap pImage = new Pixmap(fh);
int w = pImage.getWidth();
int h = pImage.getHeight();
int radius = (h - w) / 8;
Pixmap pMask = createRoundedRectangle(w, h, radius, Color.RED);
drawPixelstoMask(pImage, pMask);
TextureData textureData = new PixmapTextureData(
pMask, //pixmap to use
Pixmap.Format.RGBA8888,
textureFilter, //use mipmaps
false, true);
n = new Texture(textureData);
if (textureFilter)
n.setFilter(Texture.TextureFilter.MipMapLinearLinear, Texture.TextureFilter.Linear);
pImage.dispose();
pMask.dispose();
}
});
return n;
}
public Pixmap createRoundedRectangle(int width, int height, int cornerRadius, Color color) {
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
Pixmap ret = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(color);
//round corners
pixmap.fillCircle(cornerRadius, cornerRadius, cornerRadius);
pixmap.fillCircle(width - cornerRadius - 1, cornerRadius, cornerRadius);
pixmap.fillCircle(cornerRadius, height - cornerRadius - 1, cornerRadius);
pixmap.fillCircle(width - cornerRadius - 1, height - cornerRadius - 1, cornerRadius);
//two rectangle parts
pixmap.fillRectangle(cornerRadius, 0, width - cornerRadius * 2, height);
pixmap.fillRectangle(0, cornerRadius, width, height - cornerRadius * 2);
//draw rounded rectangle
ret.setColor(color);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (pixmap.getPixel(x, y) != 0) ret.drawPixel(x, y);
}
}
pixmap.dispose();
return ret;
}
public void drawPixelstoMask(Pixmap pixmap, Pixmap mask){
int pixmapWidth = mask.getWidth();
int pixmapHeight = mask.getHeight();
Color pixelColor = new Color();
for (int x=0; x<pixmapWidth; x++){
for (int y=0; y<pixmapHeight; y++){
if (mask.getPixel(x, y) != 0) {
Color.rgba8888ToColor(pixelColor, pixmap.getPixel(x, y));
mask.setColor(pixelColor);
mask.drawPixel(x, y);
}
}
}
}
public boolean isBorderless(String imagekey) {
if(borderlessCardlistKey.isEmpty())
return false;
if (imagekey.length() > 7) {
if ((!imagekey.substring(0, 7).contains("MPS_KLD"))&&(imagekey.substring(0, 4).contains("MPS_"))) //MPS_ sets except MPD_KLD
return true;
}
return borderlessCardlistKey.contains(TextUtil.fastReplace(imagekey,".full",".fullborder"));
}
public static boolean isBorderless(Texture t) {
if(borderlessCardlistKey.isEmpty())
return false;
//generated texture/pixmap?
if (t.toString().contains("com.badlogic.gdx.graphics.Texture@"))
return true;
for (String key : borderlessCardlistKey) {
if (t.toString().contains(key))
return true;
}
return false;
}
public static String getpixelColor(Texture i) {
if (!i.getTextureData().isPrepared()) {
i.getTextureData().prepare(); //prepare texture
}
//get pixmap from texture data
Pixmap pixmap = i.getTextureData().consumePixmap();
//get pixel color from x,y texture coordinate based on the image fullborder or not
Color color = new Color(pixmap.getPixel(croppedBorderImage(i).getRegionX()+1, croppedBorderImage(i).getRegionY()+1));
pixmap.dispose();
return color.toString();
}
public static Pair<String, Boolean> isCloserToWhite(String c){
if (c == null || c == "")
return Pair.of(Color.valueOf("#171717").toString(), false);
int c_r = Integer.parseInt(c.substring(0,2),16);
int c_g = Integer.parseInt(c.substring(2,4),16);
int c_b = Integer.parseInt(c.substring(4,6),16);
int brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
return Pair.of(c,brightness > 155);
}
}