Merge pull request #4171 from spjspj/master

spjspj - Add in a new way to allow just the Face Art for cards to be shown
This commit is contained in:
spjspj 2017-11-18 10:30:44 +11:00 committed by GitHub
commit 7292683604
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 32396 additions and 25 deletions

View file

@ -218,13 +218,15 @@ public class CardPanelRenderImpl extends CardPanel {
}
}
// Map of generated images
private final static Map<ImageKey, BufferedImage> IMAGE_CACHE = new MapMaker().softValues().makeMap();
// The art image for the card, loaded in from the disk
private BufferedImage artImage;
// The faceart image for the card, loaded in from the disk (based on artid from mtgo)
private BufferedImage faceArtImage;
// Factory to generate card appropriate views
private CardRendererFactory cardRendererFactory = new CardRendererFactory();
@ -252,6 +254,8 @@ public class CardPanelRenderImpl extends CardPanel {
// Use the art image and current rendered image from the card
artImage = impl.artImage;
cardRenderer.setArtImage(artImage);
faceArtImage = impl.faceArtImage;
cardRenderer.setFaceArtImage(faceArtImage);
cardImage = impl.cardImage;
}
}
@ -263,8 +267,8 @@ public class CardPanelRenderImpl extends CardPanel {
// Try to get card image from cache based on our card characteristics
ImageKey key
= new ImageKey(gameCard, artImage,
getCardWidth(), getCardHeight(),
isChoosable(), isSelected());
getCardWidth(), getCardHeight(),
isChoosable(), isSelected());
cardImage = IMAGE_CACHE.computeIfAbsent(key, k -> renderCard());
// No cached copy exists? Render one and cache it
@ -277,7 +281,6 @@ public class CardPanelRenderImpl extends CardPanel {
/**
* Create an appropriate card renderer for the
*/
/**
* Render the card to a new BufferedImage at it's current dimensions
*
@ -315,6 +318,7 @@ public class CardPanelRenderImpl extends CardPanel {
artImage = null;
cardImage = null;
cardRenderer.setArtImage(null);
cardRenderer.setFaceArtImage(null);
// Stop animation
tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
@ -332,19 +336,26 @@ public class CardPanelRenderImpl extends CardPanel {
Util.threadPool.submit(() -> {
try {
final BufferedImage srcImage;
final BufferedImage faceArtSrcImage;
if (gameCard.isFaceDown()) {
// Nothing to do
srcImage = null;
faceArtSrcImage = null;
} else if (getCardWidth() > THUMBNAIL_SIZE_FULL.width) {
srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
} else {
srcImage = ImageCache.getThumbnail(gameCard);
faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
}
UI.invokeLater(() -> {
if (stamp == updateArtImageStamp) {
artImage = srcImage;
cardRenderer.setArtImage(srcImage);
faceArtImage = faceArtSrcImage;
cardRenderer.setFaceArtImage(faceArtSrcImage);
if (srcImage != null) {
// Invalidate and repaint
cardImage = null;
@ -370,6 +381,7 @@ public class CardPanelRenderImpl extends CardPanel {
cardImage = null;
cardRenderer = cardRendererFactory.create(gameCard, isTransformed());
cardRenderer.setArtImage(artImage);
cardRenderer.setFaceArtImage(faceArtImage);
// Repaint
repaint();

View file

@ -66,6 +66,9 @@ public abstract class CardRenderer {
// The card image
protected BufferedImage artImage;
// The face card image
protected BufferedImage faceArtImage;
///////////////////////////////////////////////////////////////////////////
// Common layout metrics between all cards
// Polygons for counters
@ -289,8 +292,8 @@ public abstract class CardRenderer {
try {
BufferedImage subImg
= artImage.getSubimage(
(int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight),
(int) artWidth, (int) artHeight);
(int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight),
(int) artWidth, (int) artHeight);
g.drawImage(subImg,
x, y,
(int) targetWidth, (int) targetHeight,
@ -300,6 +303,40 @@ public abstract class CardRenderer {
}
}
protected void drawFaceArtIntoRect(Graphics2D g, int x, int y, int w, int h, Rectangle2D artRect, boolean shouldPreserveAspect) {
// Perform a process to make sure that the art is scaled uniformly to fill the frame, cutting
// off the minimum amount necessary to make it completely fill the frame without "squashing" it.
double fullCardImgWidth = faceArtImage.getWidth();
double fullCardImgHeight = faceArtImage.getHeight();
double artWidth = fullCardImgWidth;
double artHeight = fullCardImgHeight;
double targetWidth = w;
double targetHeight = h;
double targetAspect = targetWidth / targetHeight;
if (!shouldPreserveAspect) {
// No adjustment to art
} else if (targetAspect * artHeight < artWidth) {
// Trim off some width
artWidth = targetAspect * artHeight;
} else {
// Trim off some height
artHeight = artWidth / targetAspect;
}
try {
/*BufferedImage subImg
= faceArtImage.getSubimage(
(int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight),
(int) artWidth, (int) artHeight);*/
g.drawImage(faceArtImage,
x, y,
(int) targetWidth, (int) targetHeight,
null);
} catch (RasterFormatException e) {
// At very small card sizes we may encounter a problem with rounding error making the rect not fit
System.out.println(e);
}
}
// Draw +1/+1 and other counters
protected void drawCounters(Graphics2D g) {
int xPos = (int) (0.65 * cardWidth);
@ -442,4 +479,10 @@ public abstract class CardRenderer {
public void setArtImage(Image image) {
artImage = CardRendererUtils.toBufferedImage(image);
}
// Set the card art image (CardPanel will give it to us when it
// is loaded and ready)
public void setFaceArtImage(Image image) {
faceArtImage = CardRendererUtils.toBufferedImage(image);
}
}

View file

@ -279,6 +279,8 @@ public class ModernCardRenderer extends CardRenderer {
// Just draw a brown rectangle
drawCardBack(g);
} else {
BufferedImage bufferedImage = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
// Set texture to paint with
g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()));
@ -348,8 +350,15 @@ public class ModernCardRenderer extends CardRenderer {
@Override
protected void drawArt(Graphics2D g) {
if (artImage != null && !cardView.isFaceDown()) {
boolean useFaceArt = false;
if (faceArtImage != null) {
useFaceArt = true;
}
// Invention rendering, art fills the entire frame
if (useInventionFrame()) {
useFaceArt = false;
drawArtIntoRect(g,
borderWidth, borderWidth,
cardWidth - 2 * borderWidth, cardHeight - 2 * borderWidth,
@ -360,6 +369,7 @@ public class ModernCardRenderer extends CardRenderer {
Rectangle2D sourceRect = getArtRect();
if (cardView.getMageObjectType() == MageObjectType.SPELL) {
useFaceArt = false;
ArtRect rect = cardView.getArtRect();
if (rect == ArtRect.SPLIT_FUSED) {
// Special handling for fused, draw the art from both halves stacked on top of one and other
@ -380,10 +390,17 @@ public class ModernCardRenderer extends CardRenderer {
}
// Normal drawing of art from a source part of the card frame into the rect
drawArtIntoRect(g,
totalContentInset + 1, totalContentInset + boxHeight,
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
sourceRect, shouldPreserveAspect);
if (useFaceArt) {
drawFaceArtIntoRect(g,
totalContentInset + 1, totalContentInset + boxHeight,
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
sourceRect, shouldPreserveAspect);
} else {
drawArtIntoRect(g,
totalContentInset + 1, totalContentInset + boxHeight,
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
sourceRect, shouldPreserveAspect);
}
}
}
@ -420,6 +437,7 @@ public class ModernCardRenderer extends CardRenderer {
g.setPaint(new Color(255, 255, 255, 150));
} else {
g.setPaint(textboxPaint);
}
g.fillRect(
totalContentInset + 1, typeLineY,
@ -476,6 +494,9 @@ public class ModernCardRenderer extends CardRenderer {
// Draw the transform circle
int nameOffset = drawTransformationCircle(g, borderPaint);
// Draw the transform circle
nameOffset = drawTransformationCircle(g, borderPaint);
// Draw the name line
drawNameLine(g, cardView.getDisplayName(), manaCostString,
totalContentInset + nameOffset, totalContentInset,

View file

@ -44,6 +44,7 @@ public final class ImageCache {
private static final Logger LOGGER = Logger.getLogger(ImageCache.class);
private static final Map<String, BufferedImage> IMAGE_CACHE;
private static final Map<String, BufferedImage> FACE_IMAGE_CACHE;
/**
* Common pattern for keys. Format: "<cardname>#<setname>#<collectorID>"
@ -155,6 +156,55 @@ public final class ImageCache {
return makeThumbnail(image, thumbnailPath);
}
});
FACE_IMAGE_CACHE = new MapMaker().softValues().makeComputingMap(new Function<String, BufferedImage>() {
@Override
public BufferedImage apply(String key) {
try {
Matcher m = KEY_PATTERN.matcher(key);
if (m.matches()) {
String name = m.group(1);
String set = m.group(2);
//Integer artid = Integer.parseInt(m.group(2));
String path;
path = CardImageUtils.generateFaceImagePath(name, set);
if (path == null) {
return null;
}
TFile file = getTFile(path);
if (file == null) {
return null;
}
BufferedImage image = loadImage(file);
return image;
} else {
throw new RuntimeException(
"Requested face image doesn't fit the requirement for key (<cardname>#<artid>#: " + key);
}
} catch (Exception ex) {
if (ex instanceof ComputationException) {
throw (ComputationException) ex;
} else {
throw new ComputationException(ex);
}
}
}
public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) {
BufferedImage image = loadImage(file);
image = getWizardsCard(image);
if (image == null) {
return null;
}
LOGGER.debug("creating thumbnail for " + key);
return makeThumbnail(image, thumbnailPath);
}
});
}
public static String getFilePath(CardView card, int width) {
@ -263,6 +313,10 @@ public final class ImageCache {
return getImage(getKey(card, card.getName(), ""));
}
public static BufferedImage getImageFaceOriginal(CardView card) {
return getFaceImage(getFaceKey(card, card.getName(), card.getExpansionSetCode()));
}
public static BufferedImage getImageOriginalAlternateName(CardView card) {
return getImage(getKey(card, card.getAlternateName(), ""));
}
@ -288,6 +342,27 @@ public final class ImageCache {
}
}
/**
* Returns the Image corresponding to the key
*/
private static BufferedImage getFaceImage(String key) {
try {
return FACE_IMAGE_CACHE.get(key);
} catch (NullPointerException ex) {
// unfortunately NullOutputException, thrown when apply() returns
// null, is not public
// NullOutputException is a subclass of NullPointerException
// legitimate, happens when a card has no image
return null;
} catch (ComputationException ex) {
if (ex.getCause() instanceof NullPointerException) {
return null;
}
LOGGER.error(ex, ex);
return null;
}
}
/**
* Returns the Image corresponding to the key only if it already exists in
* the cache.
@ -296,6 +371,14 @@ public final class ImageCache {
return IMAGE_CACHE.containsKey(key) ? IMAGE_CACHE.get(key) : null;
}
/**
* Returns the Image corresponding to the key only if it already exists in
* the cache.
*/
private static BufferedImage tryGetFaceImage(String key) {
return FACE_IMAGE_CACHE.containsKey(key) ? FACE_IMAGE_CACHE.get(key) : null;
}
/**
* Returns the map key for a card, without any suffixes for the image size.
*/
@ -307,6 +390,13 @@ public final class ImageCache {
+ (card.getTokenDescriptor() != null ? '#' + card.getTokenDescriptor() : "#");
}
/**
* Returns the map key for a card, without any suffixes for the image size.
*/
private static String getFaceKey(CardView card, String name, String set) {
return name + '#' + set + "####";
}
// /**
// * Returns the map key for the flip image of a card, without any suffixes for the image size.
// */
@ -409,6 +499,25 @@ public final class ImageCache {
return TransformedImageCache.getResizedImage(original, (int) (original.getWidth() * scale), (int) (original.getHeight() * scale));
}
/**
* Returns the image appropriate to display the card in the picture panel
*
* @param card
* @param width
* @param height
* @return
*/
public static BufferedImage getFaceImage(CardView card, int width, int height) {
String key = getFaceKey(card, card.getName(), card.getExpansionSetCode());
BufferedImage original = getFaceImage(key);
if (original == null) {
LOGGER.debug(key + " (faceimage) not found");
return null;
}
return original;
}
/**
* Returns the image appropriate to display for a card in a picture panel,
* but only it was ALREADY LOADED. That is, the call is immediate and will

View file

@ -48,7 +48,7 @@ public final class CardImageUtils {
log.warn("Token image file not found: " + card.getSet() + " - " + card.getTokenSetCode() + " - " + card.getName());
return null;
}
/**
*
* @param card
@ -204,6 +204,14 @@ public final class CardImageUtils {
return imageDir + TFile.separator + imageName;
}
public static String generateFaceImagePath(String cardname, String set) {
String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
String imagesPath = Objects.equals(useDefault, "true") ? Constants.IO.imageBaseDir : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);
String imageDir = imagesPath;
String imageName = set + TFile.separator + cardname + ".jpg";
return imageDir + TFile.separator + "FACE" + TFile.separator + imageName;
}
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
// String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true");
// String imagesPath = Objects.equals(useDefault, "true") ? null : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null);