I posted a question in sun java forums sometime ago and i am finding it hard to understand the first response i received from the replier though it seems he gave me the correct approach to my problem. The link to the question is:
http://forums.sun.com/thread.jspa?threadID=5436562&tstart=0
Someone replied that i should use BufferedImage and make tiles. I don't really understand what the tiles mean in connection with the BufferedImage.
I would like someone to explain to me what the tiles are and how they are created in the BufferedImage.
I have searched the web for a while but couldn't find any link that can help me understanding the basics of the tiles and creating the tiles. Any pointer to a site is also appreciated.
I need help in understanding the tiles in connection with the BufferedImage and also how they are created.
A "tile" in a 2D game simply means an "image smaller than whole screen that you can reuse several times to create the background".
Here's a a working example where four tiles are created (adding some random noise to every pixel). Each tile is 50x50 pixels.
Then there's a "map" (that you call a "grid" in your case) representing which tiles you want to put where.
From that map, a bigger BufferedImage is created (note that it's just an example, in a real program you'll want to use a BufferedImage copy, not a pixel-by-pixel copy).
The map is 9x7, each tile is 50x50 pixels, hence the resulting image is 9*50 x 7*50 (ie 450 by 350).
Note that the following is really just a simple example, as short as possible, showing how to create a bigger BufferedImage using several tiles: the goal is not to give a tutorial on best Swing usage nor on how to squeeze every bit of performances out of BufferedImages, etc.
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class ToyTiled extends JFrame {
private static final int IMAGE_TYPE = BufferedImage.TYPE_INT_ARGB;
private BufferedImage img;
public static void main( String[] args ) {
new ToyTiled();
}
public ToyTiled() {
super();
this.add(new JPanel() {
#Override
protected void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
});
img = new BufferedImage( 450, 350, IMAGE_TYPE ); // here you should create a compatible BufferedImage
this.setSize(img.getWidth(), img.getHeight());
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
final int NB_TILES = 4;
BufferedImage[] tiles = new BufferedImage[NB_TILES];
tiles[0] = createOneTile( new Color( 255, 255, 255 ) );
tiles[1] = createOneTile( new Color( 255, 0, 255 ) );
tiles[2] = createOneTile( new Color( 0, 0, 255 ) );
tiles[3] = createOneTile( new Color( 0, 255, 255 ) );
final int[][] map = new int[][] {
{ 1, 0, 2, 3, 0, 1, 2, 2, 2 },
{ 0, 2, 3, 0, 1, 2, 2, 2, 3 },
{ 1, 0, 2, 3, 0, 1, 2, 2, 2 },
{ 2, 1, 0, 1, 2, 3, 2, 0, 0 },
{ 1, 0, 2, 3, 0, 1, 2, 2, 3 },
{ 1, 0, 2, 2, 1, 1, 2, 2, 3 },
{ 1, 0, 2, 3, 0, 1, 2, 2, 3 },
};
for (int i = 0; i < map[0].length; i++) {
for (int j = 0; j < map.length; j++) {
final BufferedImage tile = tiles[map[j][i]];
for (int x = 0; x < tile.getWidth(); x++) {
for (int y = 0; y < tile.getHeight(); y++) {
img.setRGB( x + i * 50, y + j * 50, tile.getRGB(x,y) );
}
}
}
}
this.setVisible( true );
}
private BufferedImage createOneTile( final Color c ) {
final Random r = new Random();
final BufferedImage res = new BufferedImage( 50, 50, IMAGE_TYPE );
for (int x = 0; x < res.getWidth(); x++) {
for (int y = 0; y < res.getHeight(); y++) {
res.setRGB( x, y, c.getRGB() - r.nextInt(150) );
}
}
return res;
}
}
If you want to rotate a portion of a BufferedImage you might find these classes/methods useful:
AffineTransform.getRotateInstance or AffineTransform.getQuadrantRotateInstance
AffineTransformOp
BufferedImage.getSubImage()
BufferedImage.setData
Example:
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
public class TileTest extends JFrame {
public static void main(String[] args) throws IOException {
URL logo = new URL("http://sstatic.net/so/img/logo.png");
TileTest tileTest = new TileTest(ImageIO.read(logo));
tileTest.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
tileTest.setVisible(true);
}
private TileTest(BufferedImage image) throws IOException {
this.image = image;
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
JLabel label = new JLabel(new ImageIcon(image));
add(label);
BufferedImage tile = image.getSubimage(0, 0, 61, 61);
add(new JButton(new RotateAction(tile, label)));
pack();
}
private BufferedImage image;
}
class RotateAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
BufferedImage tmpImage = op.filter(image, null);
image.setData(tmpImage.getRaster());
component.repaint();
}
RotateAction(BufferedImage image, Component component) {
super("Rotate");
this.component = component;
this.image = image;
double x = 0.5 * image.getWidth();
double y = 0.5 * image.getHeight();
AffineTransform xfrm =
AffineTransform.getQuadrantRotateInstance(1, x, y);
op = new AffineTransformOp(
xfrm, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
}
private final Component component;
private final BufferedImage image;
private final BufferedImageOp op;
}
Related
I am writing a program to generate labels and to print the labels on a standard printed using label sheets. I can create an image and save it but, I am having problems getting the image to print on the label sheet. It is blank. I can write the image to disk and open the image, it appears to be a valid image. However, no matter what I do I cannot get it to print. I have written a test program to try and print it to no avail. I downloaded an image from the net and was able to print that.
The created label will need to be printed on a label sheet (contains 6 labels from top to bottom). I need to create a label and have it print starting at the desired label on the sheet.
The LabelImage class creates the image for the label. The image has up to 4 digits printed on the left side of the label (rotated 90 degrees clockwise), then some string values. I had to create the digits in a separate image as I could not get them properly rotated in a single image.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
public class LabelImage {
public static final Map <String, Color> ColorMap = new HashMap <> ();
// Define label size
public static final int LABEL_WIDTH = 830;
public static final int LABEL_HALF_WIDTH = LABEL_WIDTH / 2;
public static final int LABEL_HEIGHT = 190;
public static final int LABEL_HALF_HEIGHT = LABEL_HEIGHT / 2;
// Define rectangle to print out digits in for the label
public static final int LEFT_DIGIT_RECT_TLX = 15;
public static final int LEFT_DIGIT_RECT_TLY = 30;
public static final int LEFT_DIGIT_RECT_WIDTH = 80;
public static final int LEFT_DIGIT_RECT_HEIGHT = (LABEL_HEIGHT - (LEFT_DIGIT_RECT_TLY * 2));
public static final int LEFT_DIGIT_TLX_OFFSET = -10;
public static final int LEFT_DIGIT_TLY_OFFSET = 15;
public static final int LEFT_DIGIT_ROTATE_X = LEFT_DIGIT_RECT_WIDTH / 2;
public static final int LEFT_DIGIT_ROTATE_Y = LEFT_DIGIT_RECT_TLY + (LEFT_DIGIT_RECT_HEIGHT / 4);
public static final int LEFT_DIGIT_ROTATE_Y2 = LEFT_DIGIT_ROTATE_Y + (LEFT_DIGIT_RECT_HEIGHT / 2);
// Create a separate image of the digits, then rotate the image
public static final int LEFT_DIGIT_TLX = 20;
public static final int LEFT_DIGIT_TLX2 = (LEFT_DIGIT_RECT_HEIGHT / 2) + LEFT_DIGIT_TLX;
public static final int LEFT_DIGIT_TLY = (LEFT_DIGIT_RECT_WIDTH - 25);
public static final int ROTATE_X = 0;
public static final int ROTATE_Y = 0;
public static final float DIGIT_FRAME_THICKNESS = 5;
public static final int CLIENT_ID_X = 380;
public static final int CLIENT_ID_Y = 30;
public static final int CLIENT_ID_Y2 = LABEL_HALF_HEIGHT + CLIENT_ID_Y;
public static final int CLIENT_NAME_X = 450;
public static final int CLIENT_NAME_Y = 30;
public static final int CLIENT_NAME_Y2 = LABEL_HALF_HEIGHT + CLIENT_NAME_Y;
public static final int PROJECT_NAME_X = CLIENT_NAME_X;
public static final int PROJECT_NAME_Y = 50;
public static final int PROJECT_NAME_Y2 = LABEL_HALF_HEIGHT + PROJECT_NAME_Y;
public static final int OTHER_X = CLIENT_ID_X;
public static final int OTHER_Y = 70;
public static final int OTHER_Y2 = LABEL_HALF_HEIGHT + OTHER_Y;
Font normalFont = new Font("TimesRoman", Font.BOLD, 14);
Font leftDigitFont = new Font("TimesRoman", Font.BOLD, 42);
static {
ColorMap.put("0", new Color(255, 120, 130, 100));
ColorMap.put("1", new Color(252, 0, 105, 100));
ColorMap.put("2", new Color(255, 165, 10, 100));
ColorMap.put("3", new Color(255, 85, 10, 100));
ColorMap.put("4", new Color(122, 252, 12, 100));
ColorMap.put("5", new Color(0, 145, 0, 100));
ColorMap.put("6", new Color(60, 255, 255, 100));
ColorMap.put("7", new Color(40, 0, 120, 100));
ColorMap.put("8", new Color(222, 182, 245, 100));
ColorMap.put("9", new Color(145, 55, 0, 100));
ColorMap.put("0", new Color(196, 23, 27, 100));
ColorMap.put("1", new Color(232, 85, 66, 100));
ColorMap.put("2", new Color(236, 131, 101, 100));
ColorMap.put("3", new Color(230, 229, 48, 100));
ColorMap.put("4", new Color(184, 224, 101, 100));
ColorMap.put("5", new Color(53, 161, 19, 100));
ColorMap.put("6", new Color(66, 142, 232, 100));
ColorMap.put("7", new Color(98, 83, 234, 100));
ColorMap.put("8", new Color(26, 15, 126, 100));
ColorMap.put("9", new Color(95, 17, 143, 100));
}
/**
* Prints a digit on the left hand side of the label, rotated 90 degrees
* clockwise. At the specified digit location.
* #param digit the digit to print
* #param ndx the index location to print at
*/
private void printLeftDigit(Graphics2D g2, String digit, int ndx) {
// find the top-left coordinate of the rectangle
int tlx = LEFT_DIGIT_RECT_TLX + (LEFT_DIGIT_RECT_WIDTH * ndx);
int tly = LEFT_DIGIT_RECT_TLY;
// Draw the colored rectangle
Color origColor = g2.getColor();
g2.setColor(ColorMap.get(digit));
g2.fillRect(tlx, tly, LEFT_DIGIT_RECT_WIDTH, LEFT_DIGIT_RECT_HEIGHT);
g2.setColor(origColor);
// Draw a black outline for the box over the rectangle
Stroke oldStroke = g2.getStroke();
g2.setStroke(new BasicStroke(DIGIT_FRAME_THICKNESS));
g2.drawRect(tlx, tly, LEFT_DIGIT_RECT_WIDTH-1, LEFT_DIGIT_RECT_HEIGHT);
g2.setStroke(oldStroke);
// Center of digit to rotate around
int cdx = tlx + LEFT_DIGIT_ROTATE_X;
// Write the digit in the rectangle
AffineTransform origTransform = g2.getTransform();
g2.setFont(leftDigitFont);
//g2.rotate(Math.PI/25);
double angle = Math.toRadians(90.0);
g2.setColor(Color.BLACK);
g2.rotate(angle, cdx, LEFT_DIGIT_ROTATE_Y);
g2.drawString(digit, cdx + LEFT_DIGIT_TLX_OFFSET, LEFT_DIGIT_ROTATE_Y + LEFT_DIGIT_TLY_OFFSET);
g2.setTransform(origTransform);
//g2.setColor(Color.GREEN);
g2.rotate(angle, cdx, LEFT_DIGIT_ROTATE_Y2);
g2.drawString(digit, cdx + LEFT_DIGIT_TLX_OFFSET, LEFT_DIGIT_ROTATE_Y2 + LEFT_DIGIT_TLY_OFFSET);
g2.setTransform(origTransform);
}
/**
* This method creates a 2nd image for the digits, then rotates the image and puts it
* over the label image.
*
* #param g2
* #param digit
* #param ndx
*/
private void printLeftDigit2(Graphics2D g2, String digit, int ndx) {
// Width is the top to bottom rectangle size
// height is the left to right rectangle width (because it will be rotated)
//BufferedImage image = new BufferedImage(LEFT_DIGIT_RECT_HEIGHT, LEFT_DIGIT_RECT_WIDTH, BufferedImage.TYPE_INT_ARGB);
BufferedImage image = new BufferedImage(LEFT_DIGIT_RECT_HEIGHT, LEFT_DIGIT_RECT_WIDTH, BufferedImage.TYPE_INT_RGB);
Graphics2D imageGraphics = image.createGraphics();
// Fill the rectangle with the expected color
imageGraphics.setColor(ColorMap.get(digit));
imageGraphics.fillRect(0, 0, LEFT_DIGIT_RECT_HEIGHT, LEFT_DIGIT_RECT_WIDTH);
// Draw a black outline for the box over the rectangle
imageGraphics.setColor(Color.BLACK);
Stroke oldStroke = imageGraphics.getStroke();
imageGraphics.setStroke(new BasicStroke(DIGIT_FRAME_THICKNESS));
imageGraphics.drawRect(0, 0, LEFT_DIGIT_RECT_HEIGHT, LEFT_DIGIT_RECT_WIDTH);
imageGraphics.setStroke(oldStroke);
// Draw the Digits in the rectangle (top-left of digit)
imageGraphics.setFont(leftDigitFont);
imageGraphics.drawString(digit, LEFT_DIGIT_TLX, LEFT_DIGIT_TLY);
imageGraphics.drawString(digit, LEFT_DIGIT_TLX2, LEFT_DIGIT_TLY);
imageGraphics.dispose();
// Put the image on the current graphic
AffineTransform aff = g2.getTransform();
double theta = Math.toRadians(90.0);
//AffineTransform rotate = AffineTransform.getRotateInstance(theta, rotx, roty);
//(x,y) = middle of rectangle
AffineTransform rotate = AffineTransform.getRotateInstance(theta, 40, 65);
//x >0 moves down; <0 moves up
//y >0: moves left; <0: moves right
int moveright = 15 - (ndx * LEFT_DIGIT_RECT_WIDTH);
rotate.translate(10, moveright);
//g2.drawImage(image, rotate, this);
}
public BufferedImage createImageWithText(ClientProject clientProject){
//ARGB = transparent
BufferedImage bufferedImage = new BufferedImage(830, 190,BufferedImage.TYPE_INT_ARGB);
Graphics g = bufferedImage.getGraphics();
g.setColor(Color.YELLOW);
g.fillRoundRect(0, 0, LABEL_WIDTH, LABEL_HEIGHT, 10, 10);
String clientId = String.valueOf(clientProject.getClientId());
String clientName = clientProject.getClientName();
String projectName = clientProject.getProjectName();
String created = "DFC: " + DateUtil.format(clientProject.getCreated(), DateUtil.LABEL_DATE_PATTERN);
// Setup for drawing to screen
g.setColor(Color.BLACK);
g.setFont(normalFont);
g.drawLine(0, LABEL_HALF_HEIGHT, LABEL_WIDTH, LABEL_HALF_HEIGHT);
// write client id on tabs
String tmp = clientId;
if (clientId.length() > 4) {
tmp = tmp.substring(0, 4);
System.out.println("tmp = " + tmp);
}
StringBuilder sb = new StringBuilder(tmp);
sb.reverse();
for (int ndx=0; ndx < sb.length(); ndx++) {
try {
printLeftDigit2((Graphics2D)g, String.valueOf(sb.charAt(ndx)), ndx);
} catch (NumberFormatException e) {
}
}
// Write client id
g.setFont(normalFont);
g.drawString(clientId, CLIENT_ID_X, CLIENT_ID_Y);
g.drawString(clientId, CLIENT_ID_X, CLIENT_ID_Y2);
// Write Client Name
g.drawString(clientName, CLIENT_NAME_X, CLIENT_NAME_Y);
g.drawString(clientName, CLIENT_NAME_X, CLIENT_NAME_Y2);
// Write Project Name
g.drawString(projectName, PROJECT_NAME_X, PROJECT_NAME_Y);
g.drawString(projectName, PROJECT_NAME_X, PROJECT_NAME_Y2);
// Write created
g.drawString(created, OTHER_X, OTHER_Y);
g.drawString(created, OTHER_X, OTHER_Y2);
return bufferedImage;
}
}
The PrintLabel program is supposed to print the image to the label sheet, but I cannot get it to print the image created by the code above. I had taken this class from somewhere else on the net, and tried to modify it for my purposes.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import static java.awt.print.Printable.NO_SUCH_PAGE;
import static java.awt.print.Printable.PAGE_EXISTS;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
public class PrintLabel {
protected static double fromCMToPPI(double cm) {
return toPPI(cm * 0.393700787);
}
protected static double toPPI(double inch) {
return inch * 72d;
}
protected static String dump(Paper paper) {
StringBuilder sb = new StringBuilder(64);
sb.append(paper.getWidth()).append("x").append(paper.getHeight())
.append("/").append(paper.getImageableX()).append("x").
append(paper.getImageableY()).append(" - ").append(paper
.getImageableWidth()).append("x").append(paper.getImageableHeight());
return sb.toString();
}
protected static String dump(PageFormat pf) {
Paper paper = pf.getPaper();
return dump(paper);
}
public void process() {
PrinterJob pj = PrinterJob.getPrinterJob();
if (pj.printDialog()) {
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
double width = fromCMToPPI(20.3);
double height = fromCMToPPI(25.4);
paper.setSize(width, height);
paper.setImageableArea(
fromCMToPPI(0.25),
fromCMToPPI(0.5),
width - fromCMToPPI(0.35),
height - fromCMToPPI(1));
System.out.println("Before- " + dump(paper));
pf.setOrientation(PageFormat.PORTRAIT);
pf.setPaper(paper);
System.out.println("After- " + dump(paper));
System.out.println("After- " + dump(pf));
//dump(pf);
PageFormat validatePage = pj.validatePage(pf);
System.out.println("Valid- " + dump(validatePage));
MyPrintable printable = new MyPrintable();
printable.labels.add(new ClientProject(112, 208, "Taxes", "Tax Refund"));
printable.determinePageCount();
pj.setPrintable(printable, pf);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
PrintLabel pl = new PrintLabel();
pl.process();
}
public class MyPrintable implements Printable {
public int startAtLabel = 0;
public int totalPages = 0;
public List <ClientProject> labels = new ArrayList <> ();
private List <ClientProject> printed = new ArrayList <> ();
/**
* Determines how many pages to print, there are 6 labels per page. If we
* start at index 5 (the last one) and there are 2 labels, there are 2
* pages to print.
*/
public void determinePageCount() {
int max = this.startAtLabel + this.labels.size();
this.totalPages = max / 6;
if ((max % 6) != 0) {
this.totalPages++;
}
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
System.out.println(pageIndex);
int result = NO_SUCH_PAGE;
// first page is index 0, if 1 page max index is 0
if (pageIndex < this.totalPages) {
Graphics2D g2d = (Graphics2D) graphics;
System.out.println("[Print] " + dump(pageFormat));
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
System.out.printf("wxh = (%fx%f)", width,height);
// Max of 6 labels per page
int maxLabelsOnPage = 6;
if (pageIndex == 0) {
maxLabelsOnPage = 6 - startAtLabel;
}
// Loop for up to the max number of labels or until we run out
// of labels
for (int labelCnt=0; labelCnt < maxLabelsOnPage; labelCnt++) {
// We have run out of labels and there is nothing left to print
if (this.labels.isEmpty()) {
break;
}
// Remove the label from the list and add it to the printed list
ClientProject cp = this.labels.remove(0);
this.printed.add(cp);
// Create the image for the label
BufferedImage image = null;
try {
// Create the image for the label
LabelImage li = new LabelImage();
BufferedImage bi = li.createImageWithText(cp);
// Load the label image
//image = ImageIO.read(new File("C:\\Consulting\\Development\\JJCPA\\finland.png"));
System.out.printf("image %d x %d%n", bi.getWidth(), bi.getHeight());
// Draw the image at the label offset
graphics.drawImage(bi, 0, 0, bi.getWidth(), bi.getHeight(), null);
// Write to a file to verify the image is valid
File outputfile = new File("image.png");
ImageIO.write(bi, "png", outputfile);
} catch (IOException e) {
}
}
result = PAGE_EXISTS;
}
return result;
}
}
}
ClientProject is a simple data structure.
public class ClientProject {
private final SimpleIntegerProperty projectId;
private final SimpleIntegerProperty clientId;
private final SimpleStringProperty category;
private final SimpleStringProperty type;
private final SimpleStringProperty projectDesc;
private final SimpleStringProperty projectName;
private final SimpleStringProperty projectName2;
private final SimpleStringProperty fileOrBinder;
private final ObjectProperty <LocalDate> created;
private final ObjectProperty <LocalDate> destroyed;
There is an error with the logic. I thought I could render a single page easy h time the method was called, until I was done creating an image for each label. 0thus, I tracked which labels I printed. this was wrong. I needed to print a through b labels for index 1 for as many times as index 1 was called, then print the next group of labels each time index 2 was passed, rewriting for this logic solved the problem.
Id like multicolored changing text, I made a list with all the colors
I have 5 g.drawString(); functions running, each of them should be the next color in the list (one above each other.)
private Color[] colors = new Color[12];
Then in my constructor:
colors[0] = new Color(255, 0, 0);
colors[1] = new Color(255, 127, 0);
colors[2] = new Color(255, 255, 0);
colors[3] = new Color(127, 255, 0);
colors[4] = new Color(0, 255, 0);
colors[5] = new Color(0, 255, 127);
colors[6] = new Color(0, 255, 255);
colors[7] = new Color(0, 127, 255);
colors[8] = new Color(0, 0, 255);
colors[9] = new Color(127, 0, 255);
colors[10] = new Color(255, 0, 255);
colors[11] = new Color(255, 0, 127);
How would I make each each letter a different color?
Set The Color: g.setColor(Color object);
Example: g.setColor(colors[5]);
Write Text: g.drawString(String, x, y);
Example: g.drawString("S", 200, 300);
So, Id like S to be the color, colors[0], I made a table below:
Starting | First | Second | Fifth
S -- 0 11 10 7
N -- 1 0 11 8
A -- 2 1 0 9
K -- 3 2 1 10
E -- 4 3 2 11
So it would loop around though each color:
I tried making a function for this, I deleted the code because I'm an idiot -_-
In my main class, I have a game loop that calls the tick and render methods, tick first then render.
I have an enum called STATE which contains menu and game, then the variable gameState of the type state is set to STATE.menu
public enum state {
Menu,
Game,
}
public state gameState = state.Menu;
When gameState is equal to STATE.menu it will call menu.render(g ( <-- The variable im using for Graphics));
Each class has its own render and tick method.
-Tick method, for setting variables etc, if statements, yada yada yada
-Render method, anything to do with drawing pixels
Because the tick method is called every 0.0000000000000000001 seconds, the color changes every 9 millionth of a second and it looks very derpy.
So ill need a timer of some sorts that I can configure with a variable
I want each of the letters to be a different color, one after another in the list
you can refer to the table above if you don't understand but as an example,
the letter a should be colors[0]
and then b, colors[1]
and c, colors[2]
the colors should alter,
so a would be colors[2]
so b would be colors[0]
so c would be colors[1]
I've probably been unclear on 1001 things, so please shout at me in the comments ^-^
Thanks for reading!
If I understood correctly, there are mainly two issues:
Painting the letters in different colors, cycling through the given array
Updating the colors (but at a fixed time, not with every "tick")
Cycling through the colors can be achieved by introducing a "colorOffset". You can add this color offset to the index that you use to access the color in the array, and take this modulo the array length to obtain a valid array index:
int colorOffset = ... // Counted up or down all the time
// The index of the color for the i'th letter
int colorIndex = (i+colorOffset)%colors.length;
if (colorIndex < 0) colorIndex += colors.length;
g.setColor(colors[colorIndex]);
The second part, regarding the update: I assume that you have a game loop that is run in an own thread. Then, in thisTickMethodThatYouHaveBeenTalkingAbout, you can check the current system time with System.nanoTime(), and compute the time that has passed since the last update. If the time is larger than the desired interval, you perform an update, by increasing the colorOffset and triggering a repaint() (if necessary - you might cover this already with your render() method).
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MulticolorTextAnimation
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MulticolorTextAnimationPanel m = new MulticolorTextAnimationPanel();
f.getContentPane().add(m);
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
while (true)
{
m.thisTickMethodThatYouHaveBeenTalkingAbout();
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
}
}
});
thread.setDaemon(true);
thread.start();
f.setSize(500,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class MulticolorTextAnimationPanel extends JPanel
{
private String string;
private Color colors[];
private int colorOffset = 0;
private long lastUpdateNs = -1;
private final long updateIntervalMs = 250;
public MulticolorTextAnimationPanel()
{
setFont(new Font("Dialog", Font.BOLD, 45));
string = "I am a string!";
colors = new Color[12];
colors[0] = new Color(255, 0, 0);
colors[1] = new Color(255, 127, 0);
colors[2] = new Color(255, 255, 0);
colors[3] = new Color(127, 255, 0);
colors[4] = new Color(0, 255, 0);
colors[5] = new Color(0, 255, 127);
colors[6] = new Color(0, 255, 255);
colors[7] = new Color(0, 127, 255);
colors[8] = new Color(0, 0, 255);
colors[9] = new Color(127, 0, 255);
colors[10] = new Color(255, 0, 255);
colors[11] = new Color(255, 0, 127);
}
public void thisTickMethodThatYouHaveBeenTalkingAbout()
{
long ns = System.nanoTime();
if (lastUpdateNs < 0)
{
lastUpdateNs = ns;
}
long passedNs = (ns - lastUpdateNs);
long passedMs = passedNs / 1000000;
if (passedMs > updateIntervalMs)
{
// Increase or decrease the color offset,
// depending on whether the colors should
// cycle forward or backward
colorOffset--;
repaint();
lastUpdateNs = ns;
}
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
FontMetrics fontMetrics = g.getFontMetrics();
int x = 100;
int y = 100;
for (int i=0; i<string.length(); i++)
{
char c = string.charAt(i);
int colorIndex = (i+colorOffset)%colors.length;
if (colorIndex < 0)
{
colorIndex += colors.length;
}
g.setColor(colors[colorIndex]);
g.drawString(String.valueOf(c), x, y);
x += fontMetrics.charWidth(c);
}
}
}
I have a class MenuScreen on which I paint a lot of images to. (4 at the moment [that may not be a lot to you but it is to me])
only one renders no matter what, sometimes they some half-render, and other times they completely render,
and sometimes they don't render at all (The main one still renders)
Here is my code
public List<BufferedImage> im;
public MenuScreen() {
setTitle("ALevelUp 0.0.1 Alpha");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initImages();
JPanel p = new JPanel(new GridLayout(1, 1));
p.add(new JLabel(new ImageIcon(im.get(0))));
add(p);
setSize(766, 500);
setLocationRelativeTo(null);
setVisible(true);
System.err.println(getHeight() + "," + getWidth());
Graphics2D g = (Graphics2D) im.get(0).getGraphics();
draw(g);
}
public final void initImages() {
im = a.init();
}
public final void draw(Graphics2D g) {
BufferedImage s1 = im.get(1);
Graphics2D s1g = (Graphics2D) s1.getGraphics();
s1g.setFont(scale(a.getFont(), s1g, "Slot 1", s1));
s1g.setColor(Color.black);
s1g.drawString("Slot 1", s1.getWidth() / 2 - 23, s1.getHeight() / 2 + 7);
g.drawImage(s1, (getWidth() / 2) - (s1.getWidth() / 2) - 21,
47, rootPane);
s1g.setColor(new Color(253, 198, 147));
s1g.fillRect(106, 20, 100, 20);
s1g.setColor(Color.black);
s1g.drawString("Slot 2", s1.getWidth() / 2 - 23, s1.getHeight() / 2 + 7);
g.drawImage(s1, getWidth() / 2 - s1.getWidth() / 2 - 21, 179, rootPane);
s1g.setColor(new Color(253, 198, 147));
s1g.fillRect(106, 20, 100, 20);
s1g.setColor(Color.black);
s1g.drawString("Slot 3", s1.getWidth() / 2 - 23, s1.getHeight() / 2 + 7);
g.drawImage(s1, getWidth() / 2 - s1.getWidth() / 2 - 21, 311, rootPane);
}
public Font scale(Font f, Graphics g, String text, BufferedImage img) {
float ntry = 20.0f;
Font font = null;
while (2 < 3) {
font = f.deriveFont(ntry);
FontMetrics fm = g.getFontMetrics(font);
int width = fm.stringWidth(text);
if (width < img.getWidth()) {
return font;
}
}
}
Can anyone help me to understand why this happens and what I can do to fix it?
Edit: Here Are the image resources, if you need them:
The Main Screen
The Slot Panels
How it looks when it works
I had to modify most of the code to get something to work. I'm assuming that this is what you want.
Here are the changes I made.
I added a main method that called SwingUtilities invokeLater to put the Swing components on the Event Dispatch thread.
I split the code into 3 classes, DrawImage, DrawingPanel, and Snippet. DrawImage creates the four images. DrawingPanel draws the four images onto a JPanel. Snippet creates the JFrame and adds the drawing panel to the JFrame.
I defined the size of the drawing panel to hold 4 slots. I packed the JFrame so that
the JFrame would be the correct size to hold the drawing panel.
I overrode the paintComponent method to draw the four images from the image list. These images were already created in the DrawImage class. I called super.paintComponent to make sure all of the Swing children components were drawn correctly.
I created the images before I created the Swing GUI.
I used a method I created, centerString, to center the text in the images. I left the scale method alone.
Here's the modified code. Unlike yours, it's runnable.
package snippet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Snippet implements Runnable {
private JFrame frame;
private List<BufferedImage> imageList;
public Snippet() {
imageList = new ArrayList<BufferedImage>();
new DrawImage().createImages();
}
#Override
public void run() {
frame = new JFrame();
frame.setTitle("ALevelUp 0.0.1 Alpha");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel p = new DrawingPanel();
frame.add(p);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
System.out.println(frame.getHeight() + "," + frame.getWidth());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Snippet());
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID =
2535522354552193273L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(550, 350));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = 50;
int y = 50;
for (int i = 0; i < 2; i++) {
BufferedImage image = null;
for (int j = 0; j < 2; j++) {
image = imageList.get(i * 2 + j);
g.drawImage(image, x, y, this);
x += image.getWidth() + 50;
}
x = 50;
y += image.getHeight() + 50;
}
}
}
public class DrawImage {
public void createImages() {
imageList.add(createImage("Slot 1"));
imageList.add(createImage("Slot 2"));
imageList.add(createImage("Slot 3"));
imageList.add(createImage("Slot 4"));
}
private BufferedImage createImage(String text) {
Rectangle r = new Rectangle(0, 0, 200, 100);
BufferedImage image = new BufferedImage(r.width, r.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) image.getGraphics();
Font font = g.getFont();
g.setFont(scale(font, g, text, image));
g.setColor(Color.BLACK);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.setColor(Color.YELLOW);
centerString(g, r, text, font);
g.dispose();
return image;
}
private Font scale(Font f, Graphics g, String text,
BufferedImage img) {
float ntry = 20.0f;
Font font = null;
while (2 < 3) {
font = f.deriveFont(ntry);
FontMetrics fm = g.getFontMetrics(font);
int width = fm.stringWidth(text);
if (width < img.getWidth()) {
return font;
}
}
}
/**
* This method centers a <code>String</code> in
* a bounding <code>Rectangle</code>.
* #param g - The <code>Graphics</code> instance.
* #param r - The bounding <code>Rectangle</code>.
* #param s - The <code>String</code> to center in the
* bounding rectangle.
* #param font - The display font of the <code>String</code>
*
* #see java.awt.Graphics
* #see java.awt.Rectangle
* #see java.lang.String
*/
private void centerString(Graphics g, Rectangle r, String s,
Font font) {
FontRenderContext frc =
new FontRenderContext(null, true, true);
Rectangle2D r2D = font.getStringBounds(s, frc);
int rWidth = (int) Math.round(r2D.getWidth());
int rHeight = (int) Math.round(r2D.getHeight());
int rX = (int) Math.round(r2D.getX());
int rY = (int) Math.round(r2D.getY());
int a = (r.width / 2) - (rWidth / 2) - rX;
int b = (r.height / 2) - (rHeight / 2) - rY;
g.setFont(font);
g.drawString(s, r.x + a, r.y + b);
}
}
}
How to 'fill' Unicode characters in labels in Swing?
I'm trying to make a user interface for the chess program I've recently programmed
(with chess pieces something like seen above). In it I'm using unicode characters to represent my chess pieces (\u2654 through \u265F).
The problem is as follows:
When I set the background of my chess piece JLabel to something like white, the entire label is filled (in my case it's a 50*50px square of white with the character on top). This leads to my pieces looking like tiles instead of just their pictures.
When I set the label to opaque, I just get a cookie cutter version of my chess piece, not one with its insides filled. E.G.
Is there a way to fill only the character?
If not I guess I'll make a sprite sheet but I like this because I can use the chess pieces' toString() methods for the labels.
Code
import java.awt.*;
import javax.swing.*;
import java.util.Random;
class ChessBoard {
static Font font = new Font("Sans-Serif", Font.PLAIN, 50);
static Random rnd = new Random();
public static void addUnicodeCharToContainer(
String s, Container c, boolean randomColor) {
JLabel l = new JLabel(s);
l.setFont(font);
if (randomColor) {
int r = rnd.nextInt(255);
int g = rnd.nextInt(255);
int b = rnd.nextInt(255);
l.setForeground(new Color(r,g,b));
l.setBackground(new Color(255-r,255-g,255-b));
l.setOpaque(true);
}
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0,6,4,4));
String[] pieces = {
"\u2654","\u2655","\u2656","\u2657","\u2658","\u2659",
"\u265A","\u265B","\u265C","\u265D","\u265E","\u265F"
};
for (String piece : pieces) {
addUnicodeCharToContainer(piece,gui,false);
}
for (String piece : pieces) {
addUnicodeCharToContainer(piece,gui,true);
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
The two rows are generated through the sorcery of Java-2D. The trick is to:
Ignore the 'black' chess pieces on the basis that our color is actually coming from 'the spaces contained by the shape'. Those are larger in the white chess pieces.
Create a GlyphVector that represents the shape of the character. This is important for further operations in Java-2D.
Create a Rectangle the size of the image.
subtract() the shape of the character from the shape of the image.
Break that altered shape into regions.
Fill the regions with the background color, but skip the single region that starts at 0.0,0.0 (representing the outermost region which we need transparent).
Finally, fill the shape of the character itself using the outline color.
Code
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.util.*;
class ChessBoard {
static Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 50);
static Random rnd = new Random();
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<Shape>();
PathIterator pi = shape.getPathIterator(null);
int ii = 0;
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
System.out.println(ii++ + " \t" + coords[0] + "," + coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
public static void addColoredUnicodeCharToContainer(
String s, Container c,
Color bgColor, Color outlineColor, boolean blackSquare) {
int sz = font.getSize();
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, s);
Rectangle2D box1 = gv.getVisualBounds();
Shape shape1 = gv.getOutline();
Rectangle r = shape1.getBounds();
System.out.println("shape rect: " + r);
int spaceX = sz - r.width;
int spaceY = sz - r.height;
AffineTransform trans = AffineTransform.getTranslateInstance(
-r.x + (spaceX / 2), -r.y + (spaceY / 2));
System.out.println("Box2D " + trans);
Shape shapeCentered = trans.createTransformedShape(shape1);
Shape imageShape = new Rectangle2D.Double(0, 0, sz, sz);
Area imageShapeArea = new Area(imageShape);
Area shapeArea = new Area(shapeCentered);
imageShapeArea.subtract(shapeArea);
ArrayList<Shape> regions = separateShapeIntoRegions(imageShapeArea);
g.setStroke(new BasicStroke(1));
for (Shape region : regions) {
Rectangle r1 = region.getBounds();
if (r1.getX() < 0.001 && r1.getY() < 0.001) {
} else {
g.setColor(bgColor);
g.fill(region);
}
}
g.setColor(outlineColor);
g.fill(shapeArea);
g.dispose();
JLabel l = new JLabel(new ImageIcon(bi), JLabel.CENTER);
Color bg = (blackSquare ? Color.BLACK : Color.WHITE);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0, 6, 4, 4));
String[] pieces = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
boolean blackSquare = false;
for (String piece : pieces) {
addColoredUnicodeCharToContainer(
piece, gui,
new Color(203,203,197),
Color.DARK_GRAY,
blackSquare);
blackSquare = !blackSquare;
}
blackSquare = !blackSquare;
for (String piece : pieces) {
addColoredUnicodeCharToContainer(
piece, gui,
new Color(192,142,60),
Color.DARK_GRAY,
blackSquare);
blackSquare = !blackSquare;
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
Chess Board
This is what it might look like as a Chess Board (22.81 Kb).
Sprite sets
Sprite sets of chess pieces (64x64 pixel) rendered from Unicode characters - as a PNG with transparent BG. Each has 6 columns for the pieces x 2 rows for the opponents (total size 384x128 pixels).
Chess pieces with solid fill (bronze/pewter) (11.64Kb).
Chess pieces with gradient fill (gold/silver) (13.61Kb).
Chess pieces with gradient fill (darker cyan/magenta) (13.44Kb).
Code for Chess Board & Sprite Set
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import javax.imageio.ImageIO;
import java.util.*;
import java.util.logging.*;
class ChessBoard {
/**
* Unicodes for chess pieces.
*/
static final String[] pieces = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
static final int KING = 0, QUEEN = 1, CASTLE = 2,
BISHOP = 3, KNIGHT = 4, PAWN = 5;
public static final int[] order = new int[]{
CASTLE, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, CASTLE
};
/*
* Colors..
*/
public static final Color outlineColor = Color.DARK_GRAY;
public static final Color[] pieceColors = {
new Color(203, 203, 197), new Color(192, 142, 60)
};
static final int WHITE = 0, BLACK = 1;
/*
* Font. The images use the font sizeXsize.
*/
static Font font = new Font("Sans-Serif", Font.PLAIN, 64);
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<Shape>();
PathIterator pi = shape.getPathIterator(null);
int ii = 0;
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
public static BufferedImage getImageForChessPiece(
int piece, int side, boolean gradient) {
int sz = font.getSize();
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, pieces[piece]);
Rectangle2D box1 = gv.getVisualBounds();
Shape shape1 = gv.getOutline();
Rectangle r = shape1.getBounds();
int spaceX = sz - r.width;
int spaceY = sz - r.height;
AffineTransform trans = AffineTransform.getTranslateInstance(
-r.x + (spaceX / 2), -r.y + (spaceY / 2));
Shape shapeCentered = trans.createTransformedShape(shape1);
Shape imageShape = new Rectangle2D.Double(0, 0, sz, sz);
Area imageShapeArea = new Area(imageShape);
Area shapeArea = new Area(shapeCentered);
imageShapeArea.subtract(shapeArea);
ArrayList<Shape> regions = separateShapeIntoRegions(imageShapeArea);
g.setStroke(new BasicStroke(1));
g.setColor(pieceColors[side]);
Color baseColor = pieceColors[side];
if (gradient) {
Color c1 = baseColor.brighter();
Color c2 = baseColor;
GradientPaint gp = new GradientPaint(
sz/2-(r.width/4), sz/2-(r.height/4), c1,
sz/2+(r.width/4), sz/2+(r.height/4), c2,
false);
g.setPaint(gp);
} else {
g.setColor(baseColor);
}
for (Shape region : regions) {
Rectangle r1 = region.getBounds();
if (r1.getX() < 0.001 && r1.getY() < 0.001) {
} else {
g.fill(region);
}
}
g.setColor(outlineColor);
g.fill(shapeArea);
g.dispose();
return bi;
}
public static void addColoredUnicodeCharToContainer(
Container c,
int piece,
int side,
Color bg,
boolean gradient) {
JLabel l = new JLabel(
new ImageIcon(getImageForChessPiece(piece, side, gradient)),
JLabel.CENTER);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
public static void addPiecesToContainer(
Container c,
int intialSquareColor,
int side,
int[] pieces,
boolean gradient) {
for (int piece : pieces) {
addColoredUnicodeCharToContainer(
c, piece, side,
intialSquareColor++%2 == BLACK ? Color.BLACK : Color.WHITE,
gradient);
}
}
public static void addPiecesToContainer(
Container c,
Color bg,
int side,
int[] pieces,
boolean gradient) {
for (int piece : pieces) {
addColoredUnicodeCharToContainer(
c, piece, side, bg, gradient);
}
}
public static void addBlankLabelRow(Container c, int initialSquareColor) {
for (int ii = 0; ii < 8; ii++) {
JLabel l = new JLabel();
Color bg = (initialSquareColor++ % 2 == BLACK
? Color.BLACK : Color.WHITE);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
}
public static void main(String[] args) {
final int[] pawnRow = new int[]{
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN
};
Runnable r = new Runnable() {
#Override
public void run() {
int gradient = JOptionPane.showConfirmDialog(
null, "Use gradient fille color?");
boolean gradientFill = gradient == JOptionPane.OK_OPTION;
JPanel gui = new JPanel(new GridLayout(0, 8, 0, 0));
gui.setBorder(new BevelBorder(
BevelBorder.LOWERED,
Color.GRAY.brighter(), Color.GRAY,
Color.GRAY.darker(), Color.GRAY));
// set up a chess board
addPiecesToContainer(gui, WHITE, BLACK, order, gradientFill);
addPiecesToContainer(gui, BLACK, BLACK, pawnRow, gradientFill);
addBlankLabelRow(gui, WHITE);
addBlankLabelRow(gui, BLACK);
addBlankLabelRow(gui, WHITE);
addBlankLabelRow(gui, BLACK);
addPiecesToContainer(gui, WHITE, WHITE, pawnRow, gradientFill);
addPiecesToContainer(gui, BLACK, WHITE, order, gradientFill);
JOptionPane.showMessageDialog(
null,
gui,
"Chessboard",
JOptionPane.INFORMATION_MESSAGE);
JPanel tileSet = new JPanel(new GridLayout(0, 6, 0, 0));
tileSet.setOpaque(false);
int[] tileSetOrder = new int[]{
KING, QUEEN, CASTLE, KNIGHT, BISHOP, PAWN
};
addPiecesToContainer(
tileSet,
new Color(0, 0, 0, 0),
BLACK,
tileSetOrder,
gradientFill);
addPiecesToContainer(
tileSet,
new Color(0, 0, 0, 0),
WHITE,
tileSetOrder,
gradientFill);
int result = JOptionPane.showConfirmDialog(
null,
tileSet,
"Save this tileset?",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.OK_OPTION) {
BufferedImage bi = new BufferedImage(
tileSet.getWidth(),
tileSet.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
tileSet.paint(g);
g.dispose();
String gradientString = gradientFill ? "gradient" : "solid";
File f = new File(
"chess-pieces-tileset-" + gradientString + ".png");
try {
ImageIO.write(bi, "png", f);
Desktop.getDesktop().open(f);
} catch (IOException ex) {
Logger.getLogger(
ChessBoard.class.getName()).log(
Level.SEVERE, null, ex);
}
}
}
};
SwingUtilities.invokeLater(r);
}
}
See also
Developed out of the GlyphVector code as seen in this answer.
Resulted in UGlys - Unicode Glyphs at GitHub.
The problem I see is that the glyphs were designed to easily distinguish traditional black and white chess pieces. Note also the variation in font design. You may be able to create hue-themed pieces that preserve the black and white distinction using the HSB color space. Green and cyan are pictured below.
Addendum: For reference, here's a Mac OS X screenshot of #Andrew's glyph shape approach. Note the benefit of #Andrew's use of RenderingHints as the image is scaled.
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/a/18691662/230513 */
class ChessBoard {
static Font font = new Font("Sans-Serif", Font.PLAIN, 64);
static Random rnd = new Random();
public static void addUnicodeCharToContainer(String s, Container c) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
c.add(l);
}
public static void addWhite(String s, Container c, Float h) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
l.setForeground(Color.getHSBColor(h, 1, 1));
l.setBackground(Color.getHSBColor(h, 3 / 8f, 5 / 8f));
c.add(l);
}
public static void addBlack(String s, Container c, Float h) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
l.setForeground(Color.getHSBColor(h, 5 / 8f, 3 / 8f));
l.setBackground(Color.getHSBColor(h, 7 / 8f, 7 / 8f));
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0, 6, 4, 4));
String[] white = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
String[] black = {
"\u265A", "\u265B", "\u265C", "\u265D", "\u265E", "\u265F"
};
for (String piece : white) {
addUnicodeCharToContainer(piece, gui);
}
for (String piece : white) {
addWhite(piece, gui, 2 / 6f);
}
for (String piece : black) {
addUnicodeCharToContainer(piece, gui);
}
for (String piece : black) {
addBlack(piece, gui, 3 / 6f);
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
In the end, I found making a spritesheet to be the easier and simpler way to go about the problem. Each piece now corresponds to a graphic within the spritesheet instead of a character/glyph. Because of this, the pieces can't be resized as nicely but that's not the biggest deal.
#Andrew Thompson's idea with the GlyphVector seemed promising, but the matter of separating inner white space from outer white space remains difficult.
One (inefficient) idea I still have is to make a ton of chess piece glyphs starting from a very small font size and with a foregound color of white:
for (int i = 1; i < BOARD_WIDTH/8) {
JLabel chessPiece =new JLabel("\u2654");
chessPiece.setForeground(Color.white);
chessPiece.setFont(new Font("Sans-Serif", Font.PLAIN, i));
add(chessPiece);
}
then add one last chess piece with a black foreground:
JLabel chessPiece =new JLabel("\u2654");
chessPiece.setForeground(Color.black);
chessPiece.setFont(new Font("Sans-Serif", Font.PLAIN, BOARD_WIDTH/8)));
add(chessPiece);
Note that I haven't tested this out.
I'm trying to make a custom swing control that is a meter.
Swing Meter http://dl.dropbox.com/u/2363305/Programming/Java/swing_meter.gif
The arrow will move up and down. Here is my current code, but I feel I've done it wrong.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class meter extends JFrame {
Stroke drawingStroke = new BasicStroke(2);
Rectangle2D rect = new Rectangle2D.Double(105, 50, 40, 200);
Double meterPercent = new Double(0.57);
public meter() {
setTitle("Meter");
setLayout(null);
setSize(300, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public void paint(Graphics g) {
// Paint Meter
Graphics2D g1 = (Graphics2D) g;
g1.setStroke(drawingStroke);
g1.draw(rect);
// Set Meter Colors
Point2D start = new Point2D.Float(0, 0);
Point2D end = new Point2D.Float(0, this.getHeight());
float[] dist = { 0.1f, 0.5f, 0.9f };
Color[] colors = { Color.green, Color.yellow, Color.red };
LinearGradientPaint p = new LinearGradientPaint(start, end, dist,
colors);
g1.setPaint(p);
g1.fill(rect);
// Make a triangle - Arrow on Meter
int[] x = new int[3];
int[] y = new int[3];
int n; // count of points
// Set Points for Arrow
Integer meterArrowHypotenuse = (int) rect.getX();
Integer meterArrowTip = (int) rect.getY()
+ (int) (rect.getHeight() * (1 - meterPercent));
x[0] = meterArrowHypotenuse - 25;
x[1] = meterArrowHypotenuse - 25;
x[2] = meterArrowHypotenuse - 5;
y[0] = meterArrowTip - 20; // Top Left
y[1] = meterArrowTip + 20; // Bottom Left
y[2] = meterArrowTip; // Tip of Arrow
n = 3; // Number of points, 3 because its a triangle
// Draw Arrow Border
Polygon myTriShadow = new Polygon(x, y, n); // a triangle
g1.setPaint(Color.black);
g1.fill(myTriShadow);
// Set Points for Arrow Board
x[0] = x[0] + 1;
x[1] = x[1] + 1;
x[2] = x[2] - 2;
y[0] = y[0] + 3;
y[1] = y[1] - 3;
y[2] = y[2];
Robot robot = new Robot();
Color colorMeter = robot.getPixelColor(x[2]+10, y[2]);
// Draw Arrow
Polygon myTri = new Polygon(x, y, n); // a triangle
Color colr = new Color(colorMeter.getRed(), colorMeter.getGreen(), colorMeter.getBlue());
g1.setPaint(colr);
g1.fill(myTri);
}
public static void main(String[] args) {
new meter();
}
}
Thanks for looking.
In addition to #Jonas' example, you might like to look at the article How to Write a Custom Swing Component.
Addendum: On reflection, it looks a little intimidating, but you can extend BasicSliderUI and reuse some of your code in paintThumb() and paintTrack().
JSlider slider = new JSlider();
slider.setUI(new MySliderUI(slider));
...
private static class MySliderUI extends BasicSliderUI {
public MySliderUI(JSlider b) {
super(b);
}
#Override
public void paintTrack(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Rectangle r = trackRect;
g2d.setPaint(new GradientPaint(
r.x, r.y, Color.red, r.x + r.width, r.y + r.height, Color.blue));
g.fillRect(r.x, r.y, r.width, r.height);
}
#Override
public void paintThumb(Graphics g) {
super.paintThumb(g); // replace with your fill()
}
}
You could use a JSlider and use setValue(int n) to set the value whenever you need. You can also change the default appearance, so you get an arrow and a gradient as you want.