Preparations for new card drawing in Mage.Card.Plugin.

This commit is contained in:
magenoxx 2010-11-02 09:10:39 +00:00
parent c63324dcc6
commit a779289ec2
12 changed files with 1435 additions and 0 deletions

View file

@ -0,0 +1,25 @@
package mage.utils;
import mage.Constants.CardType;
import mage.view.CardView;
/**
* Utility class for {@link CardView}
*
* @version 0.1 02.11.2010
* @author nantuko
*/
public class CardUtil {
public static boolean isCreature(CardView card) {
return is(card, CardType.CREATURE);
}
public static boolean isPlaneswalker(CardView card) {
return is(card, CardType.PLANESWALKER);
}
public static boolean is(CardView card, CardType type) {
return card.getCardTypes().contains(type);
}
}

View file

@ -64,6 +64,7 @@ public class CardView implements Serializable {
protected String art;
protected Rarity rarity;
protected String expansionSetCode;
protected boolean tapped;
public List<UUID> targets;
@ -96,6 +97,9 @@ public class CardView implements Serializable {
this.expansionSetCode = card.getExpansionSetCode();
}
//FIXME
this.tapped = false;
if (card instanceof Spell) {
Spell<?> spell = (Spell<?>)card;
if (spell.getSpellAbility().getTargets().size() > 0) {

View file

@ -17,11 +17,21 @@
<description>Plugin for drawing card</description>
<dependencies>
<dependency>
<groupId>org.mage</groupId>
<artifactId>Mage-Common</artifactId>
<version>${mage-version}</version>
</dependency>
<dependency>
<groupId>org.plugins.jspf</groupId>
<artifactId>jspf-core</artifactId>
<version>${jspf-version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.9</version>
</dependency>
</dependencies>
<build>

View file

@ -0,0 +1,9 @@
package org.mage.card;
public abstract class MageCard {
abstract public void onBeginAnimation();
abstract public void onEndAnimation();
abstract public void repaint();
abstract public boolean isTapped();
abstract public void setTransparency(double transparency);
}

View file

@ -0,0 +1,322 @@
package org.mage.card.arcane;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Point;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JLayeredPane;
import javax.swing.SwingUtilities;
import org.mage.card.MageCard;
abstract public class Animation {
static private final long TARGET_MILLIS_PER_FRAME = 30;
//static private final float HALF_PI = (float)(Math.PI / 2);
static private Timer timer = new Timer("Animation", true);
//static private CardPanel delayedCardPanel;
//static private long delayedTime;
static private CardPanel enlargedCardPanel;
static private CardPanel enlargedAnimationPanel;
static private Object enlargeLock = new Object();
private TimerTask timerTask;
private FrameTimer frameTimer;
private long elapsed;
public Animation (final long duration) {
this(duration, 0);
}
public Animation (final long duration, long delay) {
timerTask = new TimerTask() {
public void run () {
if (frameTimer == null) {
start();
frameTimer = new FrameTimer();
}
elapsed += frameTimer.getTimeSinceLastFrame();
if (elapsed >= duration) {
cancel();
elapsed = duration;
}
update(elapsed / (float)duration);
if (elapsed == duration) end();
}
};
timer.scheduleAtFixedRate(timerTask, delay, TARGET_MILLIS_PER_FRAME);
}
abstract protected void update (float percentage);
protected void cancel () {
timerTask.cancel();
end();
}
protected void start () {
}
protected void end () {
}
/**
* Uses averaging of the time between the past few frames to provide smooth animation.
*/
private class FrameTimer {
static private final int SAMPLES = 6;
static private final long MAX_FRAME = 100; // Max time for one frame, to weed out spikes.
private long samples[] = new long[SAMPLES];
private int sampleIndex;
public FrameTimer () {
long currentTime = System.currentTimeMillis();
for (int i = SAMPLES - 1; i >= 0; i--)
samples[i] = currentTime - (SAMPLES - i) * TARGET_MILLIS_PER_FRAME;
}
public long getTimeSinceLastFrame () {
long currentTime = System.currentTimeMillis();
int id = sampleIndex - 1;
if (id < 0) id += SAMPLES;
long timeSinceLastSample = currentTime - samples[id];
// If the slice was too big, advance all the previous times by the diff.
if (timeSinceLastSample > MAX_FRAME) {
long diff = timeSinceLastSample - MAX_FRAME;
for (int i = 0; i < SAMPLES; i++)
samples[i] += diff;
}
long timeSinceOldestSample = currentTime - samples[sampleIndex];
samples[sampleIndex] = currentTime;
sampleIndex = (sampleIndex + 1) % SAMPLES;
return timeSinceOldestSample / (long)SAMPLES;
}
}
static public void tapCardToggle (final CardPanel panel, final MageCard parent) {
new Animation(300) {
protected void start () {
parent.onBeginAnimation();
}
protected void update (float percentage) {
panel.tappedAngle = CardPanel.TAPPED_ANGLE * percentage;
if (!panel.gameCard.isTapped()) panel.tappedAngle = CardPanel.TAPPED_ANGLE - panel.tappedAngle;
panel.repaint();
}
protected void end () {
panel.tappedAngle = panel.gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0;
parent.onEndAnimation();
parent.repaint();
}
};
}
// static public void moveCardToPlay (Component source, final CardPanel dest, final CardPanel animationPanel) {
static public void moveCardToPlay (final int startX, final int startY, final int startWidth, final int endX, final int endY,
final int endWidth, final CardPanel animationPanel, final CardPanel placeholder, final JLayeredPane layeredPane,
final int speed) {
UI.invokeLater(new Runnable() {
public void run () {
final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO);
final int endHeight = Math.round(endWidth * CardPanel.ASPECT_RATIO);
final float a = 2f;
final float sqrta = (float)Math.sqrt(1 / a);
animationPanel.setCardBounds(startX, startY, startWidth, startHeight);
animationPanel.setAnimationPanel(true);
Container parent = animationPanel.getParent();
if (parent != layeredPane) {
layeredPane.add(animationPanel);
layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER);
}
new Animation(700) {
protected void update (float percentage) {
if (placeholder != null && !placeholder.isShowing()) {
cancel();
return;
}
int currentX = startX + Math.round((endX - startX + endWidth / 2f) * percentage);
int currentY = startY + Math.round((endY - startY + endHeight / 2f) * percentage);
int currentWidth, currentHeight;
int midWidth = Math.max(200, endWidth * 2);
int midHeight = Math.round(midWidth * CardPanel.ASPECT_RATIO);
if (percentage <= 0.5f) {
percentage = percentage * 2;
float pp = sqrta * (1 - percentage);
percentage = 1 - a * pp * pp;
currentWidth = startWidth + Math.round((midWidth - startWidth) * percentage);
currentHeight = startHeight + Math.round((midHeight - startHeight) * percentage);
} else {
percentage = (percentage - 0.5f) * 2;
float pp = sqrta * percentage;
percentage = a * pp * pp;
currentWidth = midWidth + Math.round((endWidth - midWidth) * percentage);
currentHeight = midHeight + Math.round((endHeight - midHeight) * percentage);
}
currentX -= Math.round(currentWidth / 2);
currentY -= Math.round(currentHeight / 2);
animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight);
}
protected void end () {
EventQueue.invokeLater(new Runnable() {
public void run () {
if (placeholder != null) {
placeholder.setDisplayEnabled(true);
placeholder.setImage(animationPanel);
}
animationPanel.setVisible(false);
animationPanel.repaint();
layeredPane.remove(animationPanel);
}
});
}
};
}
});
}
static public void moveCard (final int startX, final int startY, final int startWidth, final int endX, final int endY,
final int endWidth, final CardPanel animationPanel, final CardPanel placeholder, final JLayeredPane layeredPane,
final int speed) {
UI.invokeLater(new Runnable() {
public void run () {
final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO);
final int endHeight = Math.round(endWidth * CardPanel.ASPECT_RATIO);
animationPanel.setCardBounds(startX, startY, startWidth, startHeight);
animationPanel.setAnimationPanel(true);
Container parent = animationPanel.getParent();
if (parent != layeredPane) {
layeredPane.add(animationPanel);
layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER);
}
new Animation(speed) {
protected void update (float percentage) {
int currentX = startX + Math.round((endX - startX) * percentage);
int currentY = startY + Math.round((endY - startY) * percentage);
int currentWidth = startWidth + Math.round((endWidth - startWidth) * percentage);
int currentHeight = startHeight + Math.round((endHeight - startHeight) * percentage);
animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight);
}
protected void end () {
EventQueue.invokeLater(new Runnable() {
public void run () {
if (placeholder != null) {
placeholder.setDisplayEnabled(true);
placeholder.setImage(animationPanel);
}
animationPanel.setVisible(false);
animationPanel.repaint();
layeredPane.remove(animationPanel);
}
});
}
};
}
});
}
static public void shrinkCard () {
CardPanel enlargedCardPanel, enlargedAnimationPanel;
synchronized (enlargeLock) {
//delayedCardPanel = null;
//delayedTime = 0;
enlargedCardPanel = Animation.enlargedCardPanel;
enlargedAnimationPanel = Animation.enlargedAnimationPanel;
if (enlargedAnimationPanel == null) return;
Animation.enlargedCardPanel = null;
Animation.enlargedAnimationPanel = null;
}
final CardPanel overPanel = enlargedCardPanel, animationPanel = enlargedAnimationPanel;
animationPanel.setAnimationPanel(true);
final JLayeredPane layeredPane = SwingUtilities.getRootPane(overPanel).getLayeredPane();
layeredPane.setLayer(animationPanel, JLayeredPane.MODAL_LAYER);
final int startWidth = animationPanel.getCardWidth();
final int startHeight = Math.round(startWidth * CardPanel.ASPECT_RATIO);
final int endWidth = overPanel.getCardWidth();
final int endHeight = Math.round(endWidth * CardPanel.ASPECT_RATIO);
new Animation(200) {
protected void update (float percentage) {
int currentWidth = startWidth + Math.round((endWidth - startWidth) * percentage);
int currentHeight = startHeight + Math.round((endHeight - startHeight) * percentage);
Point startPos = SwingUtilities.convertPoint(overPanel.getParent(), overPanel.getCardLocation(), layeredPane);
int centerX = startPos.x + Math.round(endWidth / 2f);
int centerY = startPos.y + Math.round(endHeight / 2f);
int currentX = Math.max(0, centerX - Math.round(currentWidth / 2f));
currentX = Math.min(currentX, layeredPane.getWidth() - currentWidth);
int currentY = Math.max(0, centerY - Math.round(currentHeight / 2f));
currentY = Math.min(currentY, layeredPane.getHeight() - currentHeight);
animationPanel.tappedAngle = overPanel.tappedAngle * percentage;
animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight);
}
protected void end () {
animationPanel.setVisible(false);
animationPanel.repaint();
EventQueue.invokeLater(new Runnable() {
public void run () {
layeredPane.remove(animationPanel);
}
});
}
};
}
static public boolean isShowingEnlargedCard () {
synchronized (enlargeLock) {
return enlargedAnimationPanel != null;
}
}
static public void showCard(final CardPanel panel) {
new Animation(600) {
protected void start () {
}
protected void update (float percentage) {
float alpha = percentage;
panel.setAlpha(alpha);
panel.repaint();
}
protected void end () {
}
};
}
static public void hideCard(final CardPanel panel, final MageCard card) {
new Animation(600) {
protected void start () {
}
protected void update (float percentage) {
float alpha = 1 - percentage;
panel.setAlpha(alpha);
card.setTransparency(alpha);
card.repaint();
}
protected void end () {
}
};
}
}

View file

@ -0,0 +1,410 @@
package org.mage.card.arcane;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import mage.utils.CardUtil;
import mage.view.PermanentView;
import org.mage.card.arcane.ScaledImagePanel.MultipassType;
import org.mage.card.arcane.ScaledImagePanel.ScalingType;
@SuppressWarnings({"unchecked","rawtypes"})
public class CardPanel extends JPanel {
private static final long serialVersionUID = -3272134219262184410L;
static public final double TAPPED_ANGLE = Math.PI / 2;
static public final float ASPECT_RATIO = 3.5f / 2.5f;
//static public final float ASPECT_RATIO = 1.0f;
static public CardPanel dragAnimationPanel;
public static final Rectangle CARD_SIZE_FULL = new Rectangle(101, 149);
static private final float ROUNDED_CORNER_SIZE = 0.1f;
//static private final float SELECTED_BORDER_SIZE = 0.01f;
static private final float BLACK_BORDER_SIZE = 0.03f;
static private final int TEXT_GLOW_SIZE = 6;
static private final float TEXT_GLOW_INTENSITY = 3f;
static private final float rotCenterToTopCorner = 1.0295630140987000315797369464196f;
static private final float rotCenterToBottomCorner = 0.7071067811865475244008443621048f;
public PermanentView gameCard;
public CardPanel attachedToPanel;
public List<CardPanel> attachedPanels = new ArrayList();
public double tappedAngle = 0;
public ScaledImagePanel imagePanel;
public ScaledImagePanel overlayPanel;
private GlowText titleText;
private GlowText ptText;
private List<CardPanel> imageLoadListeners = new ArrayList(2);
private boolean displayEnabled = true;
private boolean isAnimationPanel;
private int cardXOffset, cardYOffset, cardWidth, cardHeight;
private boolean isSelected;
private boolean showCastingCost;
private boolean hasImage = false;
private float alpha = 1.0f;
public CardPanel (PermanentView newGameCard, boolean loadImage) {
this.gameCard = newGameCard;
//for container debug (don't remove)
//setBorder(BorderFactory.createLineBorder(Color.green));
setBackground(Color.black);
setOpaque(false);
titleText = new GlowText();
setText(gameCard);
titleText.setFont(getFont().deriveFont(Font.BOLD, 13f));
titleText.setForeground(Color.white);
titleText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY);
titleText.setWrap(true);
add(titleText);
ptText = new GlowText();
if (CardUtil.isCreature(gameCard)) {
ptText.setText(gameCard.getPower() + "/" + gameCard.getToughness());
} else if (CardUtil.isPlaneswalker(gameCard)) {
ptText.setText(gameCard.getOriginal().getLoyalty());
}
ptText.setFont(getFont().deriveFont(Font.BOLD, 13f));
ptText.setForeground(Color.white);
ptText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY);
add(ptText);
overlayPanel = new ScaledImagePanel();
overlayPanel.setBorder(BorderFactory.createLineBorder(Color.white));
add(overlayPanel);
overlayPanel.setScaleLarger(true);
overlayPanel.setScalingType(ScalingType.nearestNeighbor);
overlayPanel.setScalingBlur(true);
overlayPanel.setScalingMultiPassType(MultipassType.none);
//TODO: Image sickness = ImageManager.getSicknessImage();
Image sickness = null;
overlayPanel.setImage(sickness, sickness);
imagePanel = new ScaledImagePanel();
imagePanel.setBorder(BorderFactory.createLineBorder(Color.white));
add(imagePanel);
imagePanel.setScaleLarger(true);
imagePanel.setScalingType(ScalingType.nearestNeighbor);
imagePanel.setScalingBlur(true);
imagePanel.setScalingMultiPassType(MultipassType.none);
if (!loadImage) return;
Util.threadPool.submit(new Runnable() {
public void run () {
BufferedImage srcImage = null; //TODO: ImageCache.getImageOriginal(gameCard);
tappedAngle = gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0;
if (srcImage != null) {
//setImage(srcImage, ImageUtil.getBlurredImage(srcImage, 3, 1.0f));
hasImage = true;
setText(gameCard);
setImage(srcImage, srcImage);
}
}
});
}
private void setText(PermanentView card) {
if (hasImage) {
titleText.setText("");
} else {
titleText.setText(card.getName());
}
}
private void setImage (Image srcImage, Image srcImageBlurred) {
synchronized (imagePanel) {
imagePanel.setImage(srcImage, srcImageBlurred);
repaint();
for (CardPanel cardPanel : imageLoadListeners) {
cardPanel.setImage(srcImage, srcImageBlurred);
cardPanel.repaint();
}
imageLoadListeners.clear();
}
layout();
}
public void setImage (final CardPanel panel) {
synchronized (panel.imagePanel) {
if (panel.imagePanel.hasImage())
setImage(panel.imagePanel.srcImage, panel.imagePanel.srcImageBlurred);
else
panel.imageLoadListeners.add(this);
}
}
public void setScalingType (ScalingType scalingType) {
imagePanel.setScalingType(scalingType);
}
public void setDisplayEnabled (boolean displayEnabled) {
this.displayEnabled = displayEnabled;
}
public boolean isDisplayEnabled () {
return displayEnabled;
}
public void setAnimationPanel (boolean isAnimationPanel) {
this.isAnimationPanel = isAnimationPanel;
}
public void setSelected (boolean isSelected) {
this.isSelected = isSelected;
repaint();
}
public void setAttacking (boolean isAttacking) {
//TODO:uncomment
//this.gameCard.setAttacking(isAttacking);
repaint();
}
public boolean getSelected() {
return this.isSelected;
}
public void setShowCastingCost (boolean showCastingCost) {
this.showCastingCost = showCastingCost;
}
public void paint (Graphics g) {
if (!displayEnabled) return;
if (!isValid()) super.validate();
Graphics2D g2d = (Graphics2D)g;
if (tappedAngle > 0) {
g2d = (Graphics2D)g2d.create();
float edgeOffset = cardWidth / 2f;
g2d.rotate(tappedAngle, cardXOffset + edgeOffset, cardYOffset + cardHeight - edgeOffset);
}
super.paint(g2d);
}
protected void paintComponent (Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (alpha != 1.0f) {
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha);
g2d.setComposite(composite);
}
//TODO:uncomment
/*
if (!hasImage && gameCard.getTableID() > 0) {
g2d.setColor(new Color(30,200,200,120));
} else {
g2d.setColor(new Color(0,0,0,200));
}
*/
g2d.setColor(new Color(30,200,200,120));
//for debug repainting
//g2d.setColor(new Color(MyRandom.random.nextInt(255),MyRandom.random.nextInt(255),MyRandom.random.nextInt(255),150));
int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE));
g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize);
if (isSelected) {
//g2d.setColor(new Color(0,250,0,200));
g2d.setColor(new Color(200,120,40,200));
g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize);
}
//TODO:uncomment
/*
if (gameCard.isAttacking()) {
g2d.setColor(new Color(200,10,10,200));
g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize);
}*/
/*if (isSelected) {
g2d.setColor(Color.green);
int offset = gameCard.isTapped() ? 1 : 0;
for (int i = 1, n = Math.max(1, Math.round(cardWidth * SELECTED_BORDER_SIZE)); i <= n; i++)
g2d.drawRoundRect(cardXOffset - i, cardYOffset - i + offset, cardWidth + i * 2 - 1, cardHeight + i * 2 - 1,
cornerSize, cornerSize);
}*/
}
protected void paintChildren (Graphics g) {
super.paintChildren(g);
if (showCastingCost && !isAnimationPanel && cardWidth < 200 && cardWidth > 60) {
//TODO:uncomment
/*int width = ManaSymbols.getWidth(gameCard.getManaCost());
ManaSymbols.draw(g, gameCard.getManaCost(), cardXOffset + 8, cardHeight - 9);
*/
}
}
public void layout () {
int borderSize = Math.round(cardWidth * BLACK_BORDER_SIZE);
imagePanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
imagePanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
//TODO: uncomment
/*if (gameCard.hasSickness() && gameCard.isCreature() && gameCard.getTableID() != 0) {
overlayPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize);
overlayPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2);
} else {
overlayPanel.setVisible(false);
}*/
overlayPanel.setVisible(false);
int fontHeight = Math.round(cardHeight * (27f / 680));
boolean showText = (!isAnimationPanel && fontHeight < 12);
titleText.setVisible(showText);
ptText.setVisible(showText);
int titleX = Math.round(cardWidth * (20f / 480));
int titleY = Math.round(cardHeight * (9f / 680));
titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight);
Dimension ptSize = ptText.getPreferredSize();
ptText.setSize(ptSize.width, ptSize.height);
int ptX = Math.round(cardWidth * (420f / 480)) - ptSize.width / 2;
int ptY = Math.round(cardHeight * (675f / 680)) - ptSize.height;
int offsetX = Math.round((CARD_SIZE_FULL.width - cardWidth) / 10.0f);
ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2);
if (isAnimationPanel || cardWidth < 200)
imagePanel.setScalingType(ScalingType.nearestNeighbor);
else
imagePanel.setScalingType(ScalingType.bilinear);
}
public String toString () {
return gameCard.toString();
}
public void setCardBounds (int x, int y, int width, int height) {
cardWidth = width;
cardHeight = height;
int rotCenterX = Math.round(width / 2f);
int rotCenterY = height - rotCenterX;
int rotCenterToTopCorner = Math.round(width * CardPanel.rotCenterToTopCorner);
int rotCenterToBottomCorner = Math.round(width * CardPanel.rotCenterToBottomCorner);
int xOffset = rotCenterX - rotCenterToBottomCorner;
int yOffset = rotCenterY - rotCenterToTopCorner;
cardXOffset = -xOffset;
cardYOffset = -yOffset;
width = -xOffset + rotCenterX + rotCenterToTopCorner;
height = -yOffset + rotCenterY + rotCenterToBottomCorner;
setBounds(x + xOffset, y + yOffset, width, height);
}
public void repaint () {
Rectangle b = getBounds();
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane == null) return;
Point p = SwingUtilities.convertPoint(getParent(), b.x, b.y, rootPane);
rootPane.repaint(p.x, p.y, b.width, b.height);
}
public int getCardX () {
return getX() + cardXOffset;
}
public int getCardY () {
return getY() + cardYOffset;
}
public int getCardWidth () {
return cardWidth;
}
public int getCardHeight () {
return cardHeight;
}
public Point getCardLocation () {
Point p = getLocation();
p.x += cardXOffset;
p.y += cardYOffset;
return p;
}
public PermanentView getCard() {
return this.gameCard;
}
public void setCard(PermanentView card) {
if (CardUtil.isCreature(card) && CardUtil.isPlaneswalker(card)) {
ptText.setText(card.getPower() + "/" + card.getToughness() + " (" + card.getLoyalty() + ")");
} else if (CardUtil.isCreature(card)) {
ptText.setText(card.getPower() + "/" + card.getToughness());
} else if (CardUtil.isPlaneswalker(card)) {
ptText.setText(card.getLoyalty());
} else {
ptText.setText("");
}
setText(card);
this.gameCard = card;
//TODO: uncomment
/*if (gameCard.hasSickness() && gameCard.isCreature() && gameCard.getTableID() != 0) {
overlayPanel.setVisible(true);
} else {
overlayPanel.setVisible(false);
}*/
repaint();
}
public void setAlpha(float alpha) {
this.alpha = alpha;
}
public float getAlpha() {
return alpha;
}
public int getCardXOffset() {
return cardXOffset;
}
public int getCardYOffset() {
return cardYOffset;
}
public void updateImage() {
if (!hasImage) {
Util.threadPool.submit(new Runnable() {
public void run () {
//TODO: BufferedImage srcImage = ImageCache.getImageOriginal(gameCard);
BufferedImage srcImage = null;
tappedAngle = gameCard.isTapped() ? CardPanel.TAPPED_ANGLE : 0;
if (srcImage != null) {
hasImage = true;
setText(gameCard);
setImage(srcImage, srcImage);
}
}
});
}
}
}

View file

@ -0,0 +1,102 @@
package org.mage.card.arcane;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.text.BreakIterator;
import java.util.Locale;
import javax.swing.JLabel;
public class GlowText extends JLabel {
private static final long serialVersionUID = 1827677946939348001L;
private int glowSize;
@SuppressWarnings("unused")
private float glowIntensity;
private Color glowColor;
private boolean wrap;
private int lineCount = 0;
public void setGlow (Color glowColor, int size, float intensity) {
this.glowColor = glowColor;
this.glowSize = size;
this.glowIntensity = intensity;
}
public void setWrap (boolean wrap) {
this.wrap = wrap;
}
public Dimension getPreferredSize () {
Dimension size = super.getPreferredSize();
size.width += glowSize;
size.height += glowSize / 2;
return size;
}
public void setText (String text) {
super.setText(text);
}
public void paint (Graphics g) {
if (getText().length() == 0) return;
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Dimension size = getSize();
int textX = 0, textY = 0;
int wrapWidth = Math.max(0, wrap ? size.width - glowSize : Integer.MAX_VALUE);
AttributedString attributedString = new AttributedString(getText());
attributedString.addAttribute(TextAttribute.FONT, getFont());
AttributedCharacterIterator charIterator = attributedString.getIterator();
FontRenderContext fontContext = g2d.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, BreakIterator.getWordInstance(Locale.ENGLISH), fontContext);
lineCount = 0;
while (measurer.getPosition() < charIterator.getEndIndex()) {
//TextLayout textLayout = measurer.nextLayout(wrapWidth);
lineCount++;
if (lineCount > 2) break;
}
charIterator.first();
// Use char wrap if word wrap would cause more than two lines of text.
if (lineCount > 2)
measurer = new LineBreakMeasurer(charIterator, BreakIterator.getCharacterInstance(Locale.ENGLISH), fontContext);
else
measurer.setPosition(0);
while (measurer.getPosition() < charIterator.getEndIndex()) {
TextLayout textLayout = measurer.nextLayout(wrapWidth);
float ascent = textLayout.getAscent();
textY += ascent; // Move down to baseline.
g2d.setColor(glowColor);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 - 1);
textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 + 1);
textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 - 1);
textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 + 1);
g2d.setColor(getForeground());
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
textLayout.draw(g2d, textX + glowSize / 2, textY + glowSize / 2);
textY += textLayout.getDescent() + textLayout.getLeading(); // Move down to top of next line.
}
}
public int getLineCount() {
return this.lineCount;
}
}

View file

@ -0,0 +1,62 @@
package org.mage.card.arcane;
import java.awt.Graphics;
import java.awt.Image;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.mage.card.constants.Constants;
public class ManaSymbols {
private static final Logger log = Logger.getLogger(ManaSymbols.class);
static private final Map<String, Image> manaImages = new HashMap<String, Image>();
static private Pattern replaceSymbolsPattern = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}");
static public void loadImages () {
String[] symbols = new String[] {"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9", "B", "BG",
"BR", "G", "GU", "GW", "R", "RG", "RW", "S", "T", "U", "UB", "UR", "W", "WB", "WU", "X", "Y", "Z", "slash"};
//TODO: replace by downloading
for (String symbol : symbols)
manaImages.put(symbol, UI.getImageIcon(Constants.RESOURCE_PATH_MANA + "/" + symbol + ".png").getImage());
}
static public void draw (Graphics g, String manaCost, int x, int y) {
if (manaCost.length() == 0) return;
manaCost = manaCost.replace("\\", "");
manaCost = UI.getDisplayManaCost(manaCost);
StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) {
String symbol = tok.nextToken().substring(0);
Image image = manaImages.get(symbol);
if (image == null) {
log.error("Symbol not recognized \"" + symbol + "\" in mana cost: " + manaCost);
continue;
}
g.drawImage(image, x, y, null);
x += symbol.length() > 2 ? 10 : 12; // slash.png is only 10 pixels wide.
}
}
static public int getWidth (String manaCost) {
int width = 0;
manaCost = manaCost.replace("\\", "");
StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) {
String symbol = tok.nextToken().substring(0);
width += symbol.length() > 2 ? 10 : 12; // slash.png is only 10 pixels wide.
}
return width;
}
static public synchronized String replaceSymbolsWithHTML (String value, boolean small) {
if (small)
return replaceSymbolsPattern.matcher(value).replaceAll("<img src='file:images/symbols-11/$1$2.png' width=11 height=11>");
else {
value = value.replace("{slash}", "<img src='file:images/symbols-13/slash.png' width=10 height=13>");
return replaceSymbolsPattern.matcher(value).replaceAll("<img src='file:images/symbols-13/$1$2.png' width=13 height=13>");
}
}
}

View file

@ -0,0 +1,192 @@
package org.mage.card.arcane;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class ScaledImagePanel extends JPanel {
private static final long serialVersionUID = -1523279873208605664L;
public volatile Image srcImage;
public volatile Image srcImageBlurred;
private ScalingType scalingType = ScalingType.bilinear;
private boolean scaleLarger;
private MultipassType multiPassType = MultipassType.bilinear;
private boolean blur;
public ScaledImagePanel () {
super(false);
setOpaque(false);
}
public void setImage (Image srcImage, Image srcImageBlurred) {
this.srcImage = srcImage;
this.srcImageBlurred = srcImageBlurred;
}
public void clearImage () {
srcImage = null;
repaint();
}
public void setScalingMultiPassType (MultipassType multiPassType) {
this.multiPassType = multiPassType;
}
public void setScalingType (ScalingType scalingType) {
this.scalingType = scalingType;
}
public void setScalingBlur (boolean blur) {
this.blur = blur;
}
public void setScaleLarger (boolean scaleLarger) {
this.scaleLarger = scaleLarger;
}
public boolean hasImage () {
return srcImage != null;
}
private ScalingInfo getScalingInfo () {
int panelWidth = getWidth();
int panelHeight = getHeight();
int srcWidth = srcImage.getWidth(null);
int srcHeight = srcImage.getHeight(null);
int targetWidth = srcWidth;
int targetHeight = srcHeight;
if (scaleLarger || srcWidth > panelWidth || srcHeight > panelHeight) {
targetWidth = Math.round(panelHeight * (srcWidth / (float)srcHeight));
if (targetWidth > panelWidth) {
targetHeight = Math.round(panelWidth * (srcHeight / (float)srcWidth));
targetWidth = panelWidth;
} else
targetHeight = panelHeight;
}
ScalingInfo info = new ScalingInfo();
info.targetWidth = targetWidth;
info.targetHeight = targetHeight;
info.srcWidth = srcWidth;
info.srcHeight = srcHeight;
info.x = panelWidth / 2 - targetWidth / 2;
info.y = panelHeight / 2 - targetHeight / 2;
return info;
}
public void paint (Graphics g) {
if (srcImage == null) return;
Graphics2D g2 = (Graphics2D)g.create();
ScalingInfo info = getScalingInfo();
switch (scalingType) {
case nearestNeighbor:
scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
break;
case bilinear:
scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
break;
case bicubic:
scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
break;
case areaAveraging:
scaleWithGetScaledInstance(g2, info, Image.SCALE_AREA_AVERAGING);
break;
case replicate:
scaleWithGetScaledInstance(g2, info, Image.SCALE_REPLICATE);
break;
}
}
private void scaleWithGetScaledInstance (Graphics2D g2, ScalingInfo info, int hints) {
Image srcImage = getSourceImage(info);
Image scaledImage = srcImage.getScaledInstance(info.targetWidth, info.targetHeight, hints);
g2.drawImage(scaledImage, info.x, info.y, null);
}
private void scaleWithDrawImage (Graphics2D g2, ScalingInfo info, Object hint) {
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
int tempDestWidth = info.srcWidth / 2, tempDestHeight = info.srcHeight / 2;
if (tempDestWidth < info.targetWidth) tempDestWidth = info.targetWidth;
if (tempDestHeight < info.targetHeight) tempDestHeight = info.targetHeight;
Image srcImage = getSourceImage(info);
// If not doing multipass or multipass only needs a single pass, just scale it once directly to the panel surface.
if (multiPassType == MultipassType.none || (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight)) {
g2.drawImage(srcImage, info.x, info.y, info.targetWidth, info.targetHeight, null);
return;
}
BufferedImage tempImage = new BufferedImage(tempDestWidth, tempDestHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2temp = tempImage.createGraphics();
switch (multiPassType) {
case nearestNeighbor:
g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
break;
case bilinear:
g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
break;
case bicubic:
g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
break;
}
// Render first pass from image to temp.
g2temp.drawImage(srcImage, 0, 0, tempDestWidth, tempDestHeight, null);
// Render passes between the first and last pass.
int tempSrcWidth = tempDestWidth;
int tempSrcHeight = tempDestHeight;
while (true) {
if (tempDestWidth > info.targetWidth) {
tempDestWidth = tempDestWidth / 2;
if (tempDestWidth < info.targetWidth) tempDestWidth = info.targetWidth;
}
if (tempDestHeight > info.targetHeight) {
tempDestHeight = tempDestHeight / 2;
if (tempDestHeight < info.targetHeight) tempDestHeight = info.targetHeight;
}
if (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight) break;
g2temp.drawImage(tempImage, 0, 0, tempDestWidth, tempDestHeight, 0, 0, tempSrcWidth, tempSrcHeight, null);
tempSrcWidth = tempDestWidth;
tempSrcHeight = tempDestHeight;
}
g2temp.dispose();
// Render last pass from temp to panel surface.
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(tempImage, info.x, info.y, info.x + info.targetWidth, info.y + info.targetHeight, 0, 0, tempSrcWidth,
tempSrcHeight, null);
}
private Image getSourceImage (ScalingInfo info) {
if (!blur || srcImageBlurred == null) return srcImage;
if (info.srcWidth / 2 < info.targetWidth || info.srcHeight / 2 < info.targetHeight) return srcImage;
return srcImageBlurred;
}
static private class ScalingInfo {
public int targetWidth;
public int targetHeight;
public int srcWidth;
public int srcHeight;
public int x;
public int y;
}
static public enum MultipassType {
none, nearestNeighbor, bilinear, bicubic
}
static public enum ScalingType {
nearestNeighbor, replicate, bilinear, bicubic, areaAveraging
}
}

View file

@ -0,0 +1,182 @@
package org.mage.card.arcane;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Hashtable;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JViewport;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.ViewportLayout;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.ImageView;
/**
* UI utility functions.
*/
public class UI {
static private Hashtable<URL, Image> imageCache = new Hashtable<URL, Image>();
static public JToggleButton getToggleButton () {
JToggleButton button = new JToggleButton();
button.setMargin(new Insets(2, 4, 2, 4));
return button;
}
static public JButton getButton () {
JButton button = new JButton();
button.setMargin(new Insets(2, 4, 2, 4));
return button;
}
static public void setTitle (JPanel panel, String title) {
Border border = panel.getBorder();
if (border instanceof TitledBorder) {
((TitledBorder)panel.getBorder()).setTitle(title);
panel.repaint();
} else
panel.setBorder(BorderFactory.createTitledBorder(title));
}
@SuppressWarnings("deprecation")
static public URL getFileURL (String path) {
File file = new File(path);
if (file.exists()) {
try {
return file.toURL();
} catch (MalformedURLException ignored) {
}
}
return UI.class.getResource(path);
}
static public ImageIcon getImageIcon (String path) {
try {
InputStream stream;
stream = UI.class.getResourceAsStream(path);
if (stream == null && new File(path).exists()) stream = new FileInputStream(path);
if (stream == null) throw new RuntimeException("Image not found: " + path);
byte[] data = new byte[stream.available()];
stream.read(data);
return new ImageIcon(data);
} catch (IOException ex) {
throw new RuntimeException("Error reading image: " + path);
}
}
static public void setHTMLEditorKit (JEditorPane editorPane) {
editorPane.getDocument().putProperty("imageCache", imageCache); // Read internally by ImageView, but never written.
// Extend all this shit to cache images.
editorPane.setEditorKit(new HTMLEditorKit() {
private static final long serialVersionUID = -54602188235105448L;
public ViewFactory getViewFactory () {
return new HTMLFactory() {
public View create (Element elem) {
Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
if (o instanceof HTML.Tag) {
HTML.Tag kind = (HTML.Tag)o;
if (kind == HTML.Tag.IMG) return new ImageView(elem) {
public URL getImageURL () {
URL url = super.getImageURL();
// Put an image into the cache to be read by other ImageView methods.
if (url != null && imageCache.get(url) == null)
imageCache.put(url, Toolkit.getDefaultToolkit().createImage(url));
return url;
}
};
}
return super.create(elem);
}
};
}
});
}
static public void setVerticalScrollingView (JScrollPane scrollPane, final Component view) {
final JViewport viewport = new JViewport();
viewport.setLayout(new ViewportLayout() {
private static final long serialVersionUID = 7701568740313788935L;
public void layoutContainer (Container parent) {
viewport.setViewPosition(new Point(0, 0));
Dimension viewportSize = viewport.getSize();
int width = viewportSize.width;
int height = Math.max(view.getPreferredSize().height, viewportSize.height);
viewport.setViewSize(new Dimension(width, height));
}
});
viewport.setView(view);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setViewport(viewport);
}
static public String getDisplayManaCost (String manaCost) {
manaCost = manaCost.replace("/", "{slash}");
// A pipe in the cost means "process left of the pipe as the card color, but display right of the pipe as the cost".
int pipePosition = manaCost.indexOf("{|}");
if (pipePosition != -1) manaCost = manaCost.substring(pipePosition + 3);
return manaCost;
}
static public void invokeLater (Runnable runnable) {
EventQueue.invokeLater(runnable);
}
static public void invokeAndWait (Runnable runnable) {
if (EventQueue.isDispatchThread()) {
runnable.run();
return;
}
try {
EventQueue.invokeAndWait(runnable);
} catch (InterruptedException ex) {
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
static public void setSystemLookAndFeel () {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
System.err.println("Error setting look and feel:");
ex.printStackTrace();
}
}
static public void setDefaultFont (Font font) {
for (Object key : Collections.list(UIManager.getDefaults().keys())) {
Object value = UIManager.get(key);
if (value instanceof javax.swing.plaf.FontUIResource) UIManager.put(key, font);
}
}
}

View file

@ -0,0 +1,100 @@
package org.mage.card.arcane;
import java.awt.AWTException;
import java.awt.Robot;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.Enumeration;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Util {
static public final boolean isMac = System.getProperty("os.name").toLowerCase().indexOf("mac") != -1;
static public final boolean isWindows = System.getProperty("os.name").toLowerCase().indexOf("windows") == -1;
static public Robot robot;
static {
try {
new Robot();
} catch (AWTException ex) {
throw new RuntimeException("Error creating robot.", ex);
}
}
static public ThreadPoolExecutor threadPool;
static private int threadCount;
static {
threadPool = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() {
public Thread newThread (Runnable runnable) {
threadCount++;
Thread thread = new Thread(runnable, "Util" + threadCount);
thread.setDaemon(true);
return thread;
}
});
threadPool.prestartAllCoreThreads();
}
public static void broadcast (byte[] data, int port) throws IOException {
DatagramSocket socket = new DatagramSocket();
broadcast(socket, data, port, NetworkInterface.getNetworkInterfaces());
socket.close();
}
private static void broadcast (DatagramSocket socket, byte[] data, int port, Enumeration<NetworkInterface> ifaces)
throws IOException {
for (NetworkInterface iface : Collections.list(ifaces)) {
for (InetAddress address : Collections.list(iface.getInetAddresses())) {
if (!address.isSiteLocalAddress()) continue;
// Java 1.5 doesn't support getting the subnet mask, so try the two most common.
byte[] ip = address.getAddress();
ip[3] = -1; // 255.255.255.0
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), port));
ip[2] = -1; // 255.255.0.0
socket.send(new DatagramPacket(data, data.length, InetAddress.getByAddress(ip), port));
}
}
}
static public void sleep (int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException ignored) {
}
}
static public boolean classExists (String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException ex) {
return false;
}
}
static public void wait (Object lock) {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException ex) {
}
}
}
public static void invokeAndWait (Runnable runnable) {
try {
SwingUtilities.invokeAndWait(runnable);
} catch (Exception ex) {
throw new RuntimeException("Error invoking runnable in UI thread.", ex);
}
}
}

View file

@ -0,0 +1,17 @@
package org.mage.card.constants;
public class Constants {
public static final String RESOURCE_PATH = "/images";
public static final String RESOURCE_PATH_MANA = resourcePath("mana");
/**
* Build resource path.
*
* @param folder
* @return
*/
private static String resourcePath(String folder) {
return RESOURCE_PATH + "/" + folder;
}
}