Components on the JPanel doesn't show - java

I created a JPanel and i modify it a bit. I change it's background to gradient color here is the class.
public class JGradientPanel extends JPanel {
private static final int N = 32;
private Color color1, color2;
public JGradientPanel(Color color1, Color color2) {
this.setBorder(BorderFactory.createEmptyBorder(N, N, N, N));
this.color1 = color1;
this.color2 = color2;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//Color color1 = getBackground();
//Color color2 = color1.darker();
int w = getWidth();
int h = getHeight();
GradientPaint gp = new GradientPaint(
0, 0, color1, 0, h, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h);
}
}
Now i added few components to the panel but it didn't display anything, here is the code
public JPanel getMenu() {
JGradientPanel menu = new JGradientPanel(Color.WHITE, Color.white.darker());
int menuHeight = (int) ((int) getHeight() * 0.07);
menu.setPreferredSize(new Dimension(screenWidth,menuHeight));
menu.setLayout(new GridLayout(1,10));
menu.setBackground(Color.LIGHT_GRAY);
//test
JGradientButton test = new JGradientButton("test",Color.GREEN, Color.BLUE);
menu.add(test);
JLabel space = new JLabel(); // first blank space on the menu
space.setBounds(0, 0, menu.getPreferredSize().width - 50, menu.getPreferredSize().height);
space.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GREEN));
menu.add(space);
JLabel moreSpaces[] = new JLabel[6];
buttons = new JButton[buttonLabels.length];
for(int counter = 0; counter < moreSpaces.length + buttonLabels.length; counter ++ ) {
if(counter < 3) {
buttons[counter] = new JButton(buttonLabels[counter]); //menu buttons
} else {
moreSpaces[counter - 3] = new JLabel(); // the rest of the blank in the menu
}
}
// adding components to menu panel
for(int counter = 0; counter < moreSpaces.length + buttonLabels.length; counter ++){
if(counter < 3) {
buttons[counter].setFocusPainted(false);
menu.add(buttons[counter]);
} else {
menu.add(moreSpaces[counter - 3]);
}
}
return menu;
}
Did i miss something or i did it wrong? What's wrong with my code?

override getPreferredSize in public class JGradientPanel extends JPanel {
Painting in Swing by public void paintComponent(Graphics g) { by default never to returns PreferredSize to JPanel,
JPanel returns Dimension[0, 0];
EDIT
see very informative and interesting thread about GradientPaint

Related

java clear a JPanel with a transparent background

I have been attempting to create a screen saver program. Essentially, there are multiple circles that move around the screen. However, when I make the background transparent I cannot use clearRect() anymore because that will force the background to be white. Is there any way to clear the already drawn circles while keeping the background transparent?
class ScreenSaver extends JPanel implements ActionListener {
static Timer t;
Ball b[];
int size = 5;
ScreenSaver() {
Random rnd = new Random();
b = new Ball[size];
for (int i = 0; i < size; i++) {
int x = rnd.nextInt(1400)+100;
int y = rnd.nextInt(700)+100;
int r = rnd.nextInt(40)+11;
Color c = new Color(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
int dx = rnd.nextInt(20)-10;
if (dx == 0)
dx++;
int dy = rnd.nextInt(20)-10;
if (dy == 0)
dy++;
b[i] = new Ball(x, y, r, c, incR, incG, incB, dx, dy);
}
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void paintComponent(Graphics g) {
//g.clearRect(0, 0, 1600, 900);
for (int i = 0; i < size; i++) {
if (b[i].x + b[i].r+b[i].dx >= 1600)
b[i].dx *= -1;
if (b[i].x - b[i].r+b[i].dx <= 0)
b[i].dx *= -1;
if (b[i].y + b[i].r+b[i].dy >= 900)
b[i].dy *= -1;
if (b[i].y - b[i].r+b[i].dy <= 0)
b[i].dy *= -1;
b[i].x += b[i].dx;
b[i].y += b[i].dy;
g.fillOval(b[i].x-b[i].r, b[i].y-b[i].r, b[i].r*2, b[i].r*2);
}
}
}
class Painter extends JFrame{
Painter() {
ScreenSaver mySS = new ScreenSaver();
mySS.setBackground(new Color(0, 0, 0, 0)); //setting the JPanel transparent
add(mySS);
ScreenSaver.t = new Timer(100, mySS);
ScreenSaver.t.start();
setTitle("My Screen Saver");
setExtendedState(JFrame.MAXIMIZED_BOTH);
setLocationRelativeTo(null);
setUndecorated(true);
setBackground(new Color(0, 0, 0, 0)); //setting the JFrame transparent
setVisible(true);
}
}
Don't use alpha based colors with Swing components, instead, simply use setOpaque(false)
Change
mySS.setBackground(new Color(0, 0, 0, 0));
to
mySS.setOpaque(false)
Swing only knows how to paint opaque or transparent components (via the opaque property)
As a personal preference, you should also be calling super.paintComponent before you do any custom painting, as your paintComponent really should be making assumptions about the state

Mini Tennis Game using Threads Java

I am having a some difficulty with developing of my code. Since I am not too advanced with Java I need some help. I am trying to develop Mini Tennis game using Threads. The aim of this game is to catch the balls moving on the window with the paddle that can be controlled with the left and right buttons on the keyboard.
Those balls should move diagonally on the window and when they touch to any of the corner (out of bottom) they should change their ways like light reflection. Apart from this, when a ball touches to one of the obstacles they should change their ways as well.
Paddle on the bottom of the window can be controlled with left and right keys.The task of the player is to catch the balls. The number of balls that the user catches will be shown on the Score part with the total number of balls going to the bottom corner.
User may need to save the state of the game. When the user clicks to the “save game” button; ball locations and score should save to the file. And when the user clicks to the open button, the state of game should be reloaded.
My source code files are:
public class BallPanel extends JPanel implements Runnable {
int RED, GREEN, BLUE;
int Xdirection = 1, Ydirection = 1;
boolean pleaseWait = false;
BallPanel(int X, int Y){
locateBall(X, Y, 30, 30);
/* Random r = new Random();
RED = r.nextInt(255);
GREEN = r.nextInt(255);
BLUE = r.nextInt(255);
*/
}
public void paint(Graphics g){
int panelWidth = this.getWidth();
int panelHeight = this.getHeight();
// g.setColor( new Color(RED, GREEN, BLUE ));
g.setColor(Color.ORANGE);
g.fillOval(panelWidth/2, panelHeight/2,panelWidth/2, panelHeight/2);
}
public void locateBall(int x, int y, int width, int height){
setBounds(x, y, width, height);
repaint();
}
public void run() {
int width = this.getWidth();
int height = this.getHeight();
Random r = new Random();
while(true){
if(!pleaseWait){
int lastX = this.getX();
int lastY = this.getY();
if (lastX > 675) Xdirection = -1;
if (lastY > 485) Ydirection = -1;
if (lastX < -5) Xdirection = 1;
if (lastY < -5) Ydirection = 1;
/* if(lastX > 280 && lastY > 170){
Xdirection = -1;
Ydirection = -1;
}
*/
locateBall(lastX + Xdirection*r.nextInt(3),
lastY + Ydirection*r.nextInt(3),
width, height );
}
try{
Thread.sleep(5);
}catch(Exception e){};
}
}
}
public class BallWindow extends JFrame implements ActionListener{
JButton btnStop = new JButton("STOP");
JButton btnSave = new JButton("SAVE");
Vector<BallPanel> ballVector = new Vector<BallPanel>();
JPanel p1 = createPanel(280, 200, 200, 20, Color.gray);
JPanel p2 = createPanel(280, 300, 200, 20, Color.gray);
JPanel bottomp = createPanel(345, 540, 70, 15, Color.black);
JPanel lborder = createPanel(10, 10, 2, 560, Color.black);
JPanel rborder = createPanel(720, 10, 2, 560, Color.black);
JPanel tborder = createPanel(10, 10, 710, 2, Color.black);
public BallWindow() {
setLayout(null);
btnStop.setBounds(12, 15, 100, 30);
btnStop.addActionListener(this);
add(btnStop);
btnSave.setBounds(12, 50, 100, 30);
//btnSave.addActionListener(this);
add(btnSave);
Random r = new Random();
for(int i=0; i<7; i++){
BallPanel bp = new BallPanel(r.nextInt(740), r.nextInt(590));
Thread t = new Thread(bp);
ballVector.add(bp);
t.start();
add(bp);
}
add(p1);
add(p2);
add(bottomp);
add(lborder);
add(rborder);
add(tborder);
setSize(740, 590);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
repaint();
}
JPanel createPanel(int x, int y, int width, int height, Color pColor){
JPanel temp = new JPanel();
temp.setBackground(pColor);
temp.setBounds(x, y, width, height);
return temp;
}
public static void main(String[] args) {
new BallWindow();
}
public void actionPerformed(ActionEvent arg0) {
for (BallPanel ball : ballVector) {
ball.pleaseWait = !ball.pleaseWait;
}
if( btnStop.getText().equalsIgnoreCase("STOP"))
btnStop.setText("START");
else
btnStop.setText("STOP");
// if(arg0.getSource())
}
}
I'm stuck with obstacles part and the keylistener. Any type of help will be greatly appreciated.
Hava a look at http://zetcode.com/tutorials/javagamestutorial/
You should especially check out the Basics and the Animation section. It will help clean up the animation and thread stuff you are doing. It also shows a general pattern how one could implement a java game.

AffineTransform seeming to ignore component bounds

I have the following:
public class ParametricEQView extends JPanel implements PluginView {
private static final int BAND_WIDTH = 3;
private static final int THROW_HEIGHT = 64;
private static final int WIDTH = 128*BAND_WIDTH + 2*MARGIN;
private static final int HEIGHT = 2*THROW_HEIGHT + 2*MARGIN;
private static final int MID_HEIGHT = THROW_HEIGHT + MARGIN;
private final ParametricEQ _peq;
public ParametricEQView(ParametricEQ peq) {
super();
_peq = peq;
SwingUtils.freezeSize(this, WIDTH, HEIGHT);
setToolTipText("Parametric Equalizer");
}
#Override
public void paint(Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
final int max = findMax();
g.setColor(BACKGROUND);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(DATA);
final double scalingFactor = -((double) THROW_HEIGHT) / max;
final double[] fineLevels = _peq.getFineLevels();
int x = MARGIN;
int h;
final int[] xPoints = new int[128];
final int[] yPoints = new int[128];
for (int i = 0; i < 128; ++i) {
h = (int) (fineLevels[i] * scalingFactor);
xPoints[i] = x;
yPoints[i] = MID_HEIGHT + h;
x += BAND_WIDTH;
}
g.drawPolyline(xPoints, yPoints, 128);
g.setColor(AXES);
g.drawLine(MARGIN, MARGIN, MARGIN, HEIGHT-MARGIN);
g.drawLine(MARGIN, MID_HEIGHT, WIDTH-MARGIN, MID_HEIGHT);
g.setFont(AXIS_FONT);
final FontMetrics metrics = g.getFontMetrics();
int width = (int) metrics.getStringBounds(AXIS_LABEL_INPUT_MIDINUM, g).getWidth();
g.drawString(AXIS_LABEL_INPUT_MIDINUM, WIDTH-MARGIN-width, HEIGHT-3);
final AffineTransform atx = new AffineTransform();
atx.setToRotation(-Math.PI/2, 0, HEIGHT);
g2d.setTransform(atx);
final String topLabel = "+" + max;
width = (int) metrics.getStringBounds(topLabel, g).getWidth();
g2d.drawString(topLabel, HEIGHT-MARGIN-width, HEIGHT+10);
width = (int) metrics.getStringBounds(AXIS_LABEL_OUTPUT_VELOCITY, g).getWidth();
g2d.drawString(AXIS_LABEL_OUTPUT_VELOCITY, MID_HEIGHT-(width/2), HEIGHT+10);
g2d.drawString("-" + max, MARGIN, HEIGHT+10);
}
private int findMax() {
int max = 3;
for (int i = 0; i < 128; ++i)
max = Math.max(max, (int) Math.ceil(Math.abs(_peq.getFineLevels()[i])));
return max;
}
}
This is what it looks like:
The ParametricEQView is the component with the white background filling most of the window. In this image its coordinates are (0,0) in the containing frame and everything is great. However, if I resize the window so that the ParametricEQView moves over a bit (it has a fixed size and is set to be centered in its available space), the rotated text stays relative to the (0,0) of the frame instead of the component:
Everything else draws relative to the panel, it's just the rotated text that doesn't. What am I doing wrong?
When you call g2d.setTransform(atx); you override the transform currently set in the Graphics object, i.e. the translation between the panel and its parent frame. That's why the text is drawn in the frame referential, and not in the panel referential.
The correct code would be to get the current transform and modify it or directly call Graphics2D.rotate(double).
1) For custom paintings you need to override protected void paintComponent(Graphics g) instead of public void paint(Graphics g). Read more about customPaintings.
2)Seems you have your problem, because you do something like next for creation of GUI:
JFrame frame = new JFrame();
JPanel p = new JPanel();
ParametricEQView view = new ParametricEQView();
view.setPreferredSize(new Dimension(200,200));
p.add(view);
frame.add(p);
in that case ParametricEQView doesn't resize as you want, because JPanel use FlowLayout as default.
You need to use another LayoutManager for your panel, for example BorderLayout.
Try something like next:
JFrame frame = new JFrame();
ParametricEQView view = new ParametricEQView();
frame.add(view);
or
JPanel panel = new JPanel(new BorderLayout());
panel.add(view,BorderLayout.CENTER);
frame.add(panel);
in that case your ParametricEQView panel will be painting in proper way.

Add a JPanel to a Graphics Panel and making the JPanel Transparent except for objects

I am trying to make a Blackjack Game and way I want to design my program is with a graphics panel (Images, drawing of cards, etc.) and on top of that panel a JPanel with buttons. I want this JPanel to be transparent so that the Graphics Panel underneath is Visible but the JButtons do not turn transparent as well.
If someone can send me in the right direction?
Graphic Layer:
public class GraphicsBoard {
String[] fileName = { "cards.png", "BlackJackBoard.png" };
ClassLoader cl = GraphicsBoard.class.getClassLoader();
URL imgURL[] = new URL[2];
Toolkit tk = Toolkit.getDefaultToolkit();
Image imgCards, imgBG;
public GraphicsBoard() throws Exception {
for (int x = 0; x < imgURL.length; x++)
imgURL[x] = cl.getResource("pictures/" + fileName[x]);
imgCards = tk.createImage(imgURL[0]);
imgBG = tk.createImage(imgURL[1]);
}
public void paintComponent(Graphics g) {
g.drawImage(imgBG, 0, 0, 550, 450, 0, 0, 551, 412, this);
Graphics2D g2 = (Graphics2D) g;
for (int x = 0; x <= 550; x += 50) {
g2.drawLine(x, 0, x, 450);
g2.drawString("" + x, x + 5, 20);
}
for (int y = 5; y <= 450; y += 50) {
g2.drawLine(0, y, 550, y);
g2.drawString(" " + y, 0, y + 20);
}
}
}
Button Layer:
public class OverBoard extends JPanel implements ActionListener{
JButton btnDeal = new JButton("Deal");
public OverBoard(){
btnDeal.addActionListener(this);
add(btnDeal);
setOpaque(false);
}
}
I want the ButtonLayer to be on top of the GraphicLayer.
I want this JPanel to be transparent so that the Graphics Panel
underneath is Visible but the JButtons do not turn transparent as
well.
an OverlayLayout JPanel will do what you descibe.
there are a few ways, proper could be to
use GlassPane, have to consume() or redispatch KeyEvents
JLayer (Java7), based on JXLayer(Java6)

Java Graphics.fillPolygon: How to also render right and bottom edges?

When drawing polygons, Java2D leaves off the right and bottom edges. I understand why this is done. However, I would like to draw something that includes those edges. One thing that occurred to me was to follow fillPolygon with drawPolygon with the same coordinates, but this appears to leave a gap. (See the little triangular image at the bottom.) There are two possibilities, but I can't tell which. To enable antialiasing, I'm doing this:
renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderHints);
One possibility is that the antialiasing is not being done on the alpha channel, so the gap is caused by overdraw. In that case, if the alpha channel were what was being antialiased, the edges would abut properly. The other possibility is that there is just a gap here.
How can I fix this?
Also, I'm not sure, but it appears that the polygon outline may actually be TOO BIG. That is, it may be going further out than the right and bottom edges that I want to include.
Thanks.
-- UPDATE --
Based on a very nice suggestion by Hovercraft Full of Eels, I have made a compilable example:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
public class polygon {
private static final int WIDTH = 20;
public static void main(String[] args) {
BufferedImage img = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
int[] xPoints = {WIDTH / 3, (2*WIDTH) / 3, WIDTH / 3};
int[] yPoints = {0, WIDTH / 2, WIDTH};
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.green);
g2.drawLine(0, WIDTH-1, WIDTH, WIDTH-1);
g2.drawLine(0, 0, WIDTH, 0);
g2.drawLine(WIDTH/3, 0, WIDTH/3, WIDTH);
g2.drawLine((2*WIDTH/3), 0, (2*WIDTH/3), WIDTH);
g2.setColor(Color.black);
g2.drawPolygon(xPoints, yPoints, xPoints.length);
g2.setColor(Color.black);
g2.fillPolygon(xPoints, yPoints, xPoints.length);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
JLabel label = new JLabel(icon);
JOptionPane.showMessageDialog(null, label);
}
}
If you leave the filled polygon red, you get the image below (zoomed by 500%), which shows that the polygon does not extend all the way to the right edge. That is, the vertical green line is corresponds to x=(2*WIDTH)/2, and although the red polygon includes that coordinate, it does not paint any pixels there.
To see the gap problem, I changed red in the program to black. In this image, you can see a subtle gap on the lower right side, where the outline drawn by drawPolygon does not quite meet up with what was drawn with fillPolygon.
Show us your code for your drawing in a simple compilable runnable program. For instance when I try to imitate your image and used RenderingHints, it seemed to produce an appropriate sized image with complete right/bottom edges:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Foo002 {
private static final int WIDTH = 20;
public static void main(String[] args) {
BufferedImage img = new BufferedImage(WIDTH, WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
int[] xPoints = { WIDTH / 3, (2 * WIDTH) / 3, WIDTH / 3 };
int[] yPoints = { 0, WIDTH / 2, WIDTH };
g2.setColor(Color.black);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.fillPolygon(xPoints, yPoints, xPoints.length);
g2.dispose();
ImageIcon icon = new ImageIcon(img);
JLabel label = new JLabel(icon);
label.setBorder(BorderFactory.createLineBorder(Color.black));
JPanel panel = new JPanel();
panel.add(label);
JOptionPane.showMessageDialog(null, panel);
}
}
If you can show us a similar program that reproduces your problem, then we can give you better help.
I like the convenience of ImageIcon, shown by #HFOE, but this variation may make it a little easier to see what's happening. From the Graphics API,
Operations that draw the outline of a figure operate by traversing an
infinitely thin path between pixels with a pixel-sized pen that hangs
down and to the right of the anchor point on the path. Operations that
fill a figure operate by filling the interior of that infinitely thin
path.
In contrast, Graphics2D must follow more complex rules for antialiasing, which allow it to "draw outside the lines."
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/7701097 */
public class PixelView extends JPanel {
private static final int SIZE = 20;
private static final int SCALE = 16;
private BufferedImage img;
public PixelView(Color fill) {
this.setBackground(Color.white);
this.setPreferredSize(new Dimension(SCALE * SIZE, SCALE * SIZE));
img = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
int[] xPoints = {SIZE / 3, (2 * SIZE) / 3, SIZE / 3};
int[] yPoints = {0, SIZE / 2, SIZE};
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.green);
g2.drawLine(0, SIZE - 1, SIZE, SIZE - 1);
g2.drawLine(0, 0, SIZE, 0);
g2.drawLine(SIZE / 3, 0, SIZE / 3, SIZE);
g2.drawLine((2 * SIZE / 3), 0, (2 * SIZE / 3), SIZE);
g2.setColor(Color.black);
g2.drawPolygon(xPoints, yPoints, xPoints.length);
g2.setColor(fill);
g2.fillPolygon(xPoints, yPoints, xPoints.length);
g2.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}
private static void display() {
JFrame f = new JFrame("PixelView");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1, 0));
f.add(new PixelView(Color.black));
f.add(new PixelView(Color.red));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
}
Sometimes "the graphics pen hangs down and to the right from the path it traverses", and sometimes it doesn't.
I don't have any clear idea how to predict when it will or won't, but I have observed that RenderingHints.VALUE_STROKE_PURE can sometimes be used to alter the behavior, by trial and error.
In particular, I found that if you turn on STROKE_PURE during your drawPolygon() calls in your program,
it will make them match up with your fillPolygon() calls, as you desire.
I did a little study showing the effect of the STROKE_CONTROL hint, which is one of:
STROKE_NORMALIZE (the default, on my system)
STROKE_PURE
on the following calls:
drawLine()
drawPolygon()
fillPolygon()
in both antialiasing modes:
ANTIALIASING_OFF
ANTIALIASING_ON
And there is one more annoying dimension that apparently matters as well:
rendered directly to a JComponent on the screen
rendered to a BufferedImage.
Here are the results when rendering directly to a visible JComponent:
And here are the results when rendering into a BufferedImage:
(Notice the two cases in which the two pictures differ, i.e. in which direct rendering differs from BufferedImage rendering:
ANTIALIAS_OFF/STROKE_NORMALIZE/fillPolygon and ANTIALIAS_OFF/STROKE_PURE/drawPolygon.)
Overall, there doesn't seem to be much rhyme or reason to the whole thing.
But we can make the following specific observations based on the above pictures:
Observation #1:
If you want your antialiased drawPolygon()s and antialiased fillPolygon()s to match up well
(the original question), then turn on STROKE_PURE during the antialiased drawPolygon() calls.
(It doesn't matter whether it's on during the antialiased fillPolygon() calls.)
Observation #2:
If you want your antialiased fillPolygon()s and non-antialiased fillPolygon()s
to match up (because, say, your app allows the user to switch antialiasing on and off,
and you don't want the picture to jump northwest and southeast each time they do that),
then turn on STROKE_PURE during the non-antialiased fillPolygon() calls.
Here is the program I used to generate the pictures above.
My results are from compiling and running it it with opensdk11 on linux;
I'd be interested to know if anyone gets any different results on different platforms.
/** Study the effect of STROKE_PURE. */
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
#SuppressWarnings("serial")
public final class AntiAliasingStudy {
// These can be fiddled with.
final static int patchWidth = 24; // keep this a multiple of 4 for sanity
final static int patchHeight = 20; // keep this a multiple of 4 for sanity
final static int borderThickness = 4;
final static int mag = 6;
// derived quantities
final static int totalWidth = 5*borderThickness + 4*patchWidth;
final static int totalHeight = 4*borderThickness + 3*patchHeight;
private static void drawLittleStudy(Graphics2D g2d,
int x00, int y00,
int patchWidth, int patchHeight, int borderThickness, int totalWidth, int totalHeight) {
g2d.setColor(new java.awt.Color(240,240,240));
g2d.fillRect(x00,y00,totalWidth, totalHeight);
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 4; ++col) {
int x0 = x00 + borderThickness + col*(patchWidth+borderThickness);
int y0 = y00 + borderThickness + row*(patchHeight+borderThickness);
int x1 = x0 + patchWidth;
int y1 = y0 + patchHeight;
g2d.setColor(java.awt.Color.WHITE);
g2d.fillRect(x0, y0, patchWidth, patchHeight);
boolean antialias = (col >= 2);
boolean pure = (col % 2 == 1);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, pure ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE);
g2d.setColor(java.awt.Color.RED);
if (row == 0) {
// lines (drawLine)
// diagonals
g2d.drawLine(x0,y1, x1,y0);
g2d.drawLine(x0,y0, x1,y1);
// orthogonals
g2d.drawLine((x0+patchWidth/4),y0, (x0+patchWidth*3/4),y0);
g2d.drawLine((x0+patchWidth/4),y1, (x0+patchWidth*3/4),y1);
g2d.drawLine(x0,(y0+patchHeight/4), x0,(y0+patchHeight*3/4));
g2d.drawLine(x1,(y0+patchHeight/4), x1,(y0+patchHeight*3/4));
} else if (row == 1) {
// outlines (drawPolygon)
// A stopsign
g2d.drawPolygon(new int[] {x0+patchWidth/2-2, x0, x0, x0+patchWidth/2-2, x0+patchWidth/2+2, x1, x1, x0+patchWidth/2+2},
new int[] {y0, y0+patchHeight/2-2, y0+patchHeight/2+2, y1, y1, y0+patchHeight/2+2, y0+patchHeight/2-2, y0},
8);
} else if (row == 2) {
// fill (fillPolygon)
// A stopsign
g2d.fillPolygon(new int[] {x0+patchWidth/2-2, x0, x0, x0+patchWidth/2-2, x0+patchWidth/2+2, x1, x1, x0+patchWidth/2+2},
new int[] {y0, y0+patchHeight/2-2, y0+patchHeight/2+2, y1, y1, y0+patchHeight/2+2, y0+patchHeight/2-2, y0},
8);
}
}
}
} // drawLittleStudy
// Show a study, previously created by drawLittleStudy(), magnified and annotated.
private static void showMagnifiedAndAnnotatedStudy(Graphics g,
BufferedImage studyImage,
int x00, int y00,
int patchWidth, int patchHeight, int borderThickness, int totalWidth, int totalHeight, int mag,
ImageObserver imageObserver) {
// Magnify the image
g.drawImage(studyImage,
/*dst*/ x00,y00,x00+totalWidth*mag,y00+totalHeight*mag,
/*src*/ 0,0,totalWidth,totalHeight,
imageObserver);
// Draw annotations on each picture in black,
// in the highest quality non-biased mode
// (now that we know what that is!)
g.setColor(java.awt.Color.BLACK);
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 4; ++col) {
int x0 = borderThickness + col*(patchWidth+borderThickness);
int y0 = borderThickness + row*(patchHeight+borderThickness);
int x1 = x0 + patchWidth;
int y1 = y0 + patchHeight;
if (false) {
g.drawLine(x00+x0*mag,y00+y0*mag, x00+x1*mag,y00+y0*mag);
g.drawLine(x00+x1*mag,y00+y0*mag, x00+x1*mag,y00+y1*mag);
g.drawLine(x00+x1*mag,y00+y1*mag, x00+x0*mag,y00+y1*mag);
g.drawLine(x00+x0*mag,y00+y1*mag, x00+x0*mag,y00+y0*mag);
}
if (row == 0) {
// diagonals
g.drawLine(x00+x0*mag,y00+y1*mag, x00+x1*mag,y00+y0*mag);
g.drawLine(x00+x0*mag,y00+y0*mag, x00+x1*mag,y00+y1*mag);
// orthogonals
g.drawLine(x00+(x0+patchWidth/4)*mag,y00+y0*mag, x00+(x0+patchWidth*3/4)*mag,y00+y0*mag);
g.drawLine(x00+(x0+patchWidth/4)*mag,y00+y1*mag, x00+(x0+patchWidth*3/4)*mag,y00+y1*mag);
g.drawLine(x00+x0*mag,y00+(y0+patchHeight/4)*mag, x00+x0*mag,y00+(y0+patchHeight*3/4)*mag);
g.drawLine(x00+x1*mag,y00+(y0+patchHeight/4)*mag, x00+x1*mag,y00+(y0+patchHeight*3/4)*mag);
} else { // row 1 or 2
// A stopsign
g.drawPolygon(new int[] {x00+(x0+patchWidth/2-2)*mag, x00+x0*mag, x00+x0*mag, x00+(x0+patchWidth/2-2)*mag, x00+(x0+patchWidth/2+2)*mag, x00+x1*mag, x00+x1*mag, x00+(x0+patchWidth/2+2)*mag},
new int[] {y00+y0*mag, y00+(y0+patchHeight/2-2)*mag, y00+(y0+patchHeight/2+2)*mag, y00+y1*mag, y00+y1*mag, y00+(y0+patchHeight/2+2)*mag, y00+(y0+patchHeight/2-2)*mag, y00+y0*mag},
8);
}
}
}
FontMetrics fm = g.getFontMetrics();
{
String[][] texts = {
{"ANTIALIAS_OFF", "STROKE_NORMALIZE"},
{"ANTIALIAS_OFF", "STROKE_PURE"},
{"ANTIALIAS_ON", "STROKE_NORMALIZE"},
{"ANTIALIAS_ON", "STROKE_PURE"},
};
for (int col = 0; col < 4; ++col) {
int xCenter = borderThickness*mag + col*(patchWidth+borderThickness)*mag + patchWidth*mag/2;
{
int x = x00 + xCenter - fm.stringWidth(texts[col][0])/2;
int y = y00 + 3*(patchHeight+borderThickness)*mag + fm.getAscent();
g.drawString(texts[col][0], x,y);
x = xCenter - fm.stringWidth(texts[col][1])/2;
y += fm.getHeight();
g.drawString(texts[col][1], x,y);
}
}
}
{
String[] texts = {
"drawLine",
"drawPolygon",
"fillPolygon",
};
for (int row = 0; row < 3; ++row) {
int yCenter = y00 + borderThickness*mag + row*(patchHeight+borderThickness)*mag + patchHeight*mag/2;
int x = x00 + 4*(patchWidth+borderThickness)*mag + 10;
g.drawString(texts[row], x,yCenter);
}
}
} // showMagnifiedAndAnnotatedStudy
private static Dimension figureOutPreferredSize(FontMetrics fm) {
int preferredWidth = (totalWidth-borderThickness)*mag + 10 + fm.stringWidth("drawPolygon") + 9;
int preferredHeight = fm.getHeight() + totalHeight + (totalHeight-borderThickness)*mag + 2*fm.getHeight() + 2;
return new Dimension(preferredWidth, preferredHeight);
}
private static class IndirectExaminationView extends JComponent {
public IndirectExaminationView() {
setFont(new Font("Times", Font.PLAIN, 12));
setPreferredSize(figureOutPreferredSize(getFontMetrics(getFont())));
}
#Override public void paintComponent(Graphics g) {
FontMetrics fm = g.getFontMetrics();
g.setColor(java.awt.Color.BLACK);
g.drawString("through BufferedImage:", 0,fm.getAscent());
// The following seem equivalent
java.awt.image.BufferedImage studyImage = new java.awt.image.BufferedImage(totalWidth, totalHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
//java.awt.image.BufferedImage studyImage = (BufferedImage)this.createImage(totalWidth, totalHeight);
drawLittleStudy(studyImage.createGraphics(),
0,0,
patchWidth, patchHeight, borderThickness, totalWidth, totalHeight);
Graphics2D studyImageGraphics2D = studyImage.createGraphics();
g.drawImage(studyImage,
/*dst*/ 0,fm.getHeight(),totalWidth,fm.getHeight()+totalHeight,
/*src*/ 0,0,totalWidth,totalHeight,
this);
showMagnifiedAndAnnotatedStudy(g, studyImage,
0,fm.getHeight()+totalHeight,
patchWidth, patchHeight, borderThickness, totalWidth, totalHeight, mag, this);
}
} // DirectExaminationView
private static class DirectExaminationView extends JComponent {
public DirectExaminationView() {
setFont(new Font("Times", Font.PLAIN, 12));
setPreferredSize(figureOutPreferredSize(getFontMetrics(getFont())));
}
private BufferedImage imgFromTheRobot = null;
#Override public void paintComponent(Graphics g) {
final FontMetrics fm = g.getFontMetrics();
g.setColor(java.awt.Color.BLACK);
g.drawString("direct to JComponent:", 0,fm.getAscent());
drawLittleStudy((Graphics2D)g,
0,fm.getHeight(),
patchWidth, patchHeight, borderThickness, totalWidth, totalHeight);
if (imgFromTheRobot != null) {
System.out.println(" drawing image from robot");
showMagnifiedAndAnnotatedStudy(g, imgFromTheRobot,
0, fm.getHeight()+totalHeight,
patchWidth, patchHeight, borderThickness, totalWidth, totalHeight, mag, this);
imgFromTheRobot = null;
} else {
System.out.println(" scheduling a robot");
g.drawString("*** SCREEN CAPTURE PENDING ***", 0, fm.getHeight()+totalHeight+fm.getHeight()+fm.getHeight());
// Most reliable way to do it seems to be to put it on a timer after a delay.
Timer timer = new Timer(1000/2, new ActionListener() {
#Override public void actionPerformed(ActionEvent ae) {
System.out.println(" in timer callback");
Robot robot;
try {
robot = new Robot();
} catch (AWTException e) {
System.err.println("caught AWTException: "+e);
throw new Error(e);
}
Point myTopLeftOnScreen = getLocationOnScreen();
Rectangle rect = new Rectangle(
myTopLeftOnScreen.x, myTopLeftOnScreen.y + fm.getHeight(),
totalWidth,totalHeight);
BufferedImage img = robot.createScreenCapture(rect);
imgFromTheRobot = img;
repaint();
System.out.println(" out timer callback");
}
});
timer.setRepeats(false);
timer.start();
}
}
} // DirectExaminationView
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override public void run()
{
final JFrame directFrame = new JFrame("direct to JComponent") {{
getContentPane().add(new DirectExaminationView());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocation(0,0);
setVisible(true);
}};
new JFrame("through BufferedImage") {{
getContentPane().add(new IndirectExaminationView());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocation(directFrame.getWidth(),0);
setVisible(true);
}};
}
});
}
} // class AntiAliasingStudy

Categories

Resources