diff --git a/forge-gui-mobile/src/forge/assets/ImageCache.java b/forge-gui-mobile/src/forge/assets/ImageCache.java index 85ad04232cc..f344f72b49f 100644 --- a/forge-gui-mobile/src/forge/assets/ImageCache.java +++ b/forge-gui-mobile/src/forge/assets/ImageCache.java @@ -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 missingIconKeys = new ObjectSet<>(); - private static LoadingCache cache; + private static List 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) 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> imageBorder = new ObjectMap<>(1024); + private static final ObjectMap 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 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 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 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 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); + } } diff --git a/forge-gui-mobile/src/forge/assets/ImageLoader.java b/forge-gui-mobile/src/forge/assets/ImageLoader.java deleted file mode 100644 index 0d7786afaa8..00000000000 --- a/forge-gui-mobile/src/forge/assets/ImageLoader.java +++ /dev/null @@ -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 { - private static List 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 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 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); - } -}