This is my problem :
I have one JPanel and i paint in this panel one rectangle ex. 100x100.
In another JPanel I wouldlike show/paint fragments on first JPanel ex. 50x50, but if I change first JPanel, another JPanel change too (dont copy graphics or Panel)
What I can do this?
First Panel Second Panel
Public class Okienko extends JFrame {
Panel p = new Panel();
public Okienko(){
//setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(p);
pack();
setVisible(true);
}
private class Panel extends JPanel{
public Panel(){
setPreferredSize(new Dimension(300,400));
setBackground(Color.red);
setVisible(true);
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paint(g2);
g2.setColor(Color.blue);
g2.fill(new Rectangle2D.Float(100,100,100,100));
g2.setColor(Color.green);
g2.fill(new Rectangle2D.Float(50,50,50,50));
}
}
private class Panel2 extends Panel{
#Override
public void paint(Graphics g) {
//I would like to show/paint only fragment painted Panel, ex. 50x50 (only show one rectangle)
}
}
public static void main(String[] args) {
Okienko o = new Okienko();
}
}
So this is what you need to do.
You need to save the first JPanel's Graphics context to a BufferedImage. Here is a helper method, I used in the example program below
BufferedImage bi;
....
private void setImage(JPanel panel) {
Dimension d = panel.getPreferredSize();
int w = (int)d.getWidth();
int h =(int)d.getHeight();
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
panel.paint(g);
g.dispose();
}
This saves the entire JPanel to a BufferedImage.
Use that BufferedImage to paint on the second JPanel. Use whatever coordinates you want. Use this method from Graphics class
public abstract boolean drawImage(Image img,
int dx1,
int dy1,
int dx2,
int dy2,
int sx1,
int sy1,
int sx2,
int sy2,
ImageObserver observer)
img - the specified image to be drawn. This method does nothing if img is null.
dx1 - the x coordinate of the first corner of the destination rectangle.
dy1 - the y coordinate of the first corner of the destination rectangle.
dx2 - the x coordinate of the second corner of the destination rectangle.
dy2 - the y coordinate of the second corner of the destination rectangle.
sx1 - the x coordinate of the first corner of the source rectangle.
sy1 - the y coordinate of the first corner of the source rectangle.
sx2 - the x coordinate of the second corner of the source rectangle.
sy2 - the y coordinate of the second corner of the source rectangle.
observer - object to be notified as more of the image is scaled and converted.
g.drawImage(bi, 0, 0, 200, 200, 0, 0, 50, 50, this);
Here's the result
Here's the full code
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
public class TestTwoPanels {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JPanel panel = new JPanel();
PanelTwo panelTwo = new PanelTwo();
PanelOne panelOne = new PanelOne(panelTwo);
JSplitPane split = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT, panelOne, panelTwo);
panel.add(split);
JFrame frame = new JFrame("Test Graphics");
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private static class PanelOne extends JPanel {
Dimension size;
BufferedImage image;
PanelTwo panelTwo;
public PanelOne(PanelTwo panelTwo) {
this.panelTwo = panelTwo;
try {
URL url = new URL("http://swoo.co.uk/content/images/icons/stackoverflow.png");
image = ImageIO.read(url);
} catch (MalformedURLException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panelTwo.setImage(PanelOne.this);
panelTwo.repaint();
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
private static class PanelTwo extends JPanel {
BufferedImage bi;
public PanelTwo() {
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
}
public void setImage(BufferedImage image) {
this.bi = image;
}
private void setImage(JPanel panel) {
Dimension d = panel.getPreferredSize();
int w = (int)d.getWidth();
int h =(int)d.getHeight();
System.out.println(d);
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
panel.paint(g);
g.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bi, 25, 25, 225, 225, 50, 50, 175, 175, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
}
private void setImage(JPanel panel) {
Dimension d = panel.getPreferredSize();
int w = (int)d.getWidth();
int h =(int)d.getHeight();
System.out.println(d);
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
This code is clearify for me.
Graphics2D g = bi.createGraphics();
You create blank BufferedImage and init to Graphics2D?
panel.paint(g);
You paint graphics context on panel and copy to bi?
g.dispose();
You release g.
}
Related
I'm trying to create a drawing on BufferedImage and then copy in onto JPanel.
When I draw directly on JPanel quality of the picture is v.good but when using intermediate BufferedImage quality / resolution is visibly reduced.
I've checked that with zoom option from OSX's Accessibility panel.
I'm developing on MacBook Pro Retina.
Is there some sort of automated scaling happening?
What am I doing wrong with BufferedImage?
Here's the code demonstrating the problem
package com.sample.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class QualityProblem {
private static final double DOT_SIZE = 4;
public static void main(String[] args) {
JFrame frame = new JFrame("ChartPanel demo");
frame.setBackground(Color.WHITE);
// JPanel draw = new DrawingOK();
JPanel draw = new DrawingUgly();
draw.setBackground(Color.BLACK);
frame.getContentPane().add(draw, BorderLayout.CENTER);
frame.setSize(new Dimension(1200, 900));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class DrawingOK extends JPanel {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g.create();
try {
g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2draw.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
g2draw.setColor(Color.YELLOW);
g2draw.fill(e);
} finally {
g2draw.dispose();
}
}
}
private static class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = getParent().getSize();
BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
Graphics2D ig = image.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Graphics2D g2draw = (Graphics2D) g.create();
try {
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
g2draw.drawImage(image, 0, 0, null);
} finally {
ig.dispose();
g2draw.dispose();
}
}
}
}
Edited:
Added images with 4 pixel dot and 50D both zoomed in.
Ugly one comes from BufferedImage copied onto screen's Graphics
I fixed up your drawing code.
Here's the ugly GUI.
I moved the sizing of the panel to the panel constructor. Setting the frame size includes the borders. Setting the panel size gives you the drawing area you want.
I moved the black background painting to the paintComponent method. You might as well do all the painting in one place.
I cleaned up your drawing code. You don't need to make a copy of the paintComponent graphics instance to get Graphics2D.
I made the circle bigger so you could see the sharpness. I moved the origin to the center of the circle, and turned the DOT_SIZE into a radius.
Here's the code.
package com.ggl.testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class QualityProblem implements Runnable {
private static final double DOT_SIZE = 50D;
public static void main(String[] args) {
SwingUtilities.invokeLater(new QualityProblem());
}
#Override
public void run() {
JFrame frame = new JFrame("ChartPanel demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.WHITE);
// JPanel draw = new DrawingOK();
JPanel draw = new DrawingUgly();
frame.getContentPane().add(draw, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private class DrawingOK extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingOK() {
this.setPreferredSize(new Dimension(600, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g;
g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2draw.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
g2draw.setColor(Color.BLACK);
g2draw.fillRect(0, 0, getWidth(), getHeight());
Ellipse2D.Double e = new Ellipse2D.Double(300D - DOT_SIZE,
200D - DOT_SIZE, DOT_SIZE + DOT_SIZE, DOT_SIZE + DOT_SIZE);
g2draw.setColor(Color.YELLOW);
g2draw.fill(e);
}
}
private class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingUgly() {
this.setPreferredSize(new Dimension(600, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = new BufferedImage(getWidth(), getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics2D ig = image.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
ig.setColor(Color.BLACK);
ig.fillRect(0, 0, getWidth(), getHeight());
Ellipse2D.Double e = new Ellipse2D.Double(300D - DOT_SIZE,
200D - DOT_SIZE, DOT_SIZE + DOT_SIZE, DOT_SIZE + DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
ig.dispose();
g.drawImage(image, 0, 0, this);
}
}
}
This is simply because in one case, you're drawing on a hardware-supported surface that says it's 640x480 but rendering is done at 2x (or whatever scaling factor of your display) resolution. In the case of BufferedImage you're drawing onto a literal 640x480 pixel buffer. Obviously, that will look worse.
I think that the image and panel are using different rendering hints on OS X for hints you've not explicitly set. Copy/paste the textual output of this code back into the question.
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class QualityProblem {
private static final double DOT_SIZE = 40;
public static void main(String[] args) {
JFrame frame = new JFrame("ChartPanel demo");
frame.setLayout(new GridLayout(0, 1));
frame.getContentPane().add(new DrawingUgly());
frame.getContentPane().add(new DrawingOK());
frame.setSize(new Dimension(400, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class DrawingOK extends JPanel {
private static final long serialVersionUID = 1L;
DrawingOK() {
setBackground(Color.GREEN);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g.create();
System.out.println("Panel Rendering Hints:");
printRenderingHints(g2draw);
try {
g2draw.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2draw.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2draw.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
g2draw.setColor(Color.YELLOW);
g2draw.fill(e);
} finally {
g2draw.dispose();
}
}
}
private static class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
DrawingUgly() {
setBackground(Color.RED);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = getParent().getSize();
BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig = image.createGraphics();
System.out.println("Image Rendering Hints:");
printRenderingHints(ig);
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Graphics2D g2draw = (Graphics2D) g.create();
try {
Ellipse2D.Double e = new Ellipse2D.Double(50, 50, DOT_SIZE, DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
g2draw.drawImage(image, 0, 0, null);
} finally {
ig.dispose();
g2draw.dispose();
}
}
}
private static void printRenderingHints(Graphics2D g) {
RenderingHints renderingHints = g.getRenderingHints();
RenderingHints.Key[] renderHintsKeys = {
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.KEY_ANTIALIASING,
RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.KEY_DITHERING,
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.KEY_INTERPOLATION,
RenderingHints.KEY_RENDERING,
RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.KEY_TEXT_LCD_CONTRAST
};
for (RenderingHints.Key key : renderHintsKeys) {
Object o = renderingHints.get(key);
String value = o==null ? "null" : o.toString();
System.out.println(key + " \t" + value);
}
}
}
Note that on Windows it produces an identical list of values.
HaraldK in one comments below question gave really good advice. BufferedImage size needs to be multiplied by 2, Graphics2D for that image must be set with scale 2 and target Graphics2D (of the screen device) needs to be scaled with 0.5.
With those settings both circles look exactly the same when zoomed in.
Bellow complete, modified DrawingUgly class.
private static class DrawingUgly extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingUgly() {
this.setPreferredSize(new Dimension(600, 25));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2draw = (Graphics2D) g.create();
double scale = 2;
BufferedImage image = new BufferedImage((int) (getWidth() * scale), (int) (getHeight() * scale), BufferedImage.TYPE_INT_RGB);
Graphics2D ig = image.createGraphics();
ig.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ig.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
ig.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
ig.scale(scale, scale);
ig.setColor(Color.BLACK);
ig.fillRect(0, 0, getWidth(), getHeight());
Ellipse2D.Double e = new Ellipse2D.Double(10, 10, DOT_SIZE, DOT_SIZE);
ig.setColor(Color.YELLOW);
ig.fill(e);
ig.dispose();
g2draw.scale(1.0d / scale, 1.0d / scale);
g2draw.drawImage(image, 0, 0, this);
}
}
I am working on a class that can rotate a wheel around the center. The wheel is created using graphics2d, but I can not figure out exactly how to get the wheel to rotate around the center.
Currently, the wheel rotates, but not exactly about the origin.
My ultimate goal here is to create the wheel so that it is multicolored as well as a program around it, but my main concern here is getting the rotating wheel to work. If you could point me in the right direction I would be forever grateful!
Here is my current code:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class RotateApp {
private static final int N = 3;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(N, N, N, N));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotatePanel());
frame.pack();
frame.setVisible(true);
System.out.println();
}
});
}
}
class RotatePanel extends JPanel implements ActionListener {
private static final int SIZE = 256;
private static double DELTA_THETA = Math.PI / 90;
private final Timer timer = new Timer(25, this);
private Image image = RotatableImage.getImage(SIZE);
private double dt = DELTA_THETA;
private double theta;
public RotatePanel() {
this.setBackground(Color.lightGray);
this.setPreferredSize(new Dimension(
image.getWidth(null), image.getHeight(null)));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
image = RotatableImage.getImage(SIZE);
dt = -dt;
}
});
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
g2d.rotate(theta);
g2d.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
g2d.drawImage(image, 0, 0, null);
}
public void actionPerformed(ActionEvent e) {
theta += dt;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
class RotatableImage {
private static final Random r = new Random();
static public Image getImage(int size) {
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(0, 100, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 200, 100));
g2d.draw(new Line2D.Double(100, 0, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 100, 200));
g2d.draw(new Line2D.Double(25, 25, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 175, 175));
g2d.draw(new Line2D.Double(175, 25, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 25, 175));
g2d.draw(new Ellipse2D.Double(0, 0, 200, 200));
g2d.dispose();
return bi;
}
}
You can use the Rotated Icon class to do the rotation for you so you don't have to worry about all the rotation logic and the rotation logic is in a reusable class.
An example of using this class would be:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class Rotation4 extends JPanel
{
private JLabel label;
private RotatedIcon rotated;
private int degrees;
public Rotation4(Image image)
{
setLayout( new GridBagLayout() );
Icon icon = new ImageIcon( image );
rotated = new RotatedIcon(icon, 0);
rotated.setCircularIcon(true);
label = new JLabel(rotated);
label.setOpaque(true);
label.setBackground(Color.RED);
add(label, new GridBagConstraints());
setDegrees( 0 );
}
public void setDegrees(int degrees)
{
this.degrees = degrees;
rotated.setDegrees( degrees );
label.revalidate();
label.repaint();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
Image bi = RotatableImage.getImage(210);
final Rotation4 r = new Rotation4(bi);
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
r.setDegrees( value );
}
});
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(r));
f.add(slider, BorderLayout.SOUTH);
f.setSize(400, 400);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
static class RotatableImage
{
private static final Random r = new Random();
static public Image getImage(int size)
{
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(5, 105, 205, 105));
g2d.draw(new Line2D.Double(105, 5, 105, 205));
g2d.draw(new Line2D.Double(35, 35, 175, 175));
g2d.draw(new Line2D.Double(175, 35, 35, 175));
g2d.draw(new Ellipse2D.Double(5, 5, 199, 199));
g2d.setColor(Color.BLACK);
g2d.fillOval(100, 100, 10, 10);
g2d.dispose();
return bi;
}
}
}
Note I also had to make changes with your image and your painting. These changes will need to be made whether you use the RotatedIcon or do the rotation code yourself:
The image size was changed to 210. This is because your stroke size is 10, so you need to account for the extra pixels in the circle outline.
You need to change the original of the circle by half the stroke size. So in this case the origin becomes (5, 5).
The size of the oval needs to be changed to 199. This is because of the way the outline of the oval is painted. 1 extra pixel is needed for the outline. If you leave the size at 200 then 1 pixel of the outline will be lost. This is not very noticeable when using a stroke size of 10, but if you use a size of 1, then the outline will be missing at the right and bottom edges.
The locations of your lines needs to be changes. you don't want the line right to the edge of the circle because then you will get a flat line at the edge instead of the rounded line. So I started the line 5 pixels from the start and ended it 5 pixels from the end.
Ok, with little modification and fewer "spokes", I got your wheel rotate centric (1.) and multicolor (2.):
UPDATE on 1.) To make centric rotation in your (original) code just chage SIZEto 200!
import java.awt.BasicStroke;
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.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class RotateApp {
private static final int N = 3;
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(N, N, N, N));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotatePanel());
frame.pack();
frame.setVisible(true);
System.out.println();
});
}
}
class RotatePanel extends JPanel implements ActionListener {
private static final int SIZE = 256;
private static final double DELTA_THETA = Math.PI / 90;
private final Timer timer = new Timer(25, this);
private Image image = RotatableImage.getImage(SIZE);
private double dt = DELTA_THETA;
private double theta;
public RotatePanel() {
this.setBackground(Color.lightGray);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
dt = -dt;
image = RotatableImage.getImage(SIZE);
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(theta,128,128);
g2d.drawImage(image, 0, 0, null);
}
#Override
public void actionPerformed(ActionEvent e) {
theta += dt;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
class RotatableImage {
private static final Random r = new Random();
static public Image getImage(int size) {
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
final Color c1 = Color.getHSBColor(r.nextFloat(), 1, 1);
final Color c2 = Color.getHSBColor(r.nextFloat(), 1, 1);
g2d.setPaint(c1);
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(0, size/2, size, size/2));
g2d.setPaint(c2);
g2d.draw(new Line2D.Double(size/2, 0, size/2, size));
g2d.setPaint(c1);
g2d.draw(new Ellipse2D.Double(0, 0, size, size));
g2d.dispose();
return bi;
}
}
Explanation:
So the "wobble" in your solution came from the fact, that you sized the image and the container/panel 256x256, but "based" your wheel layout on 200x200 only. I fixed all dimensions, and drew a correct cross, the g2d.rotate(theta,128,128); (!) relates then to the center.
Multi-color(easy): You can invoke setPaint() between each shape! ;)
For an Application in Java Swing (developed in netbeans), we need to create big circle exatcly like radio buttons, which means we have a group of circles that whenever the user clicks on one, it changes to a filled circle. The user can choose only 1 circle.
The working mechansim is exactly similar to radiobutton group only we need to have bigger circles. Any idea how we can do this?
Use JRadioButtons
But give them no text (if this is a requirement, .... it may not be your requirement, I don't know).
Instead give them two ImageIcons, 1 for unselected which is an empty circle, and use setIcon(Icon icon) to do this.
And the other for selected that is an image of a filled circle, and use setSelectedIcon(Icon icon) to do this.
You can create your own images easily by drawing on a BufferedImage.
For example, the code below creates:
..... ......
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class CircleIconEg extends JPanel {
public static final String[] PLAYER_NAMES = {"John", "Bill", "Frank", "Andy"};
private static final int BI_WIDTH = 40;
private ButtonGroup btnGrp = new ButtonGroup();
private static Icon emptyIcon;
private static Icon selectedIcon;
// create our Circle ImageIcons
static {
// first the empty circle
BufferedImage img = new BufferedImage(BI_WIDTH, BI_WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setStroke(new BasicStroke(4f));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x = 4;
int y = x;
int width = BI_WIDTH - 2 * x;
int height = width;
g2.setColor(Color.black);
g2.drawOval(x, y, width, height);
g2.dispose();
emptyIcon = new ImageIcon(img);
// next the filled circle
img = new BufferedImage(BI_WIDTH, BI_WIDTH, BufferedImage.TYPE_INT_ARGB);
g2 = img.createGraphics();
g2.setStroke(new BasicStroke(4f));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.red);
g2.fillOval(x, y, width, height);
g2.setColor(Color.black);
g2.drawOval(x, y, width, height);
g2.dispose();
selectedIcon = new ImageIcon(img);
}
public CircleIconEg() {
setLayout(new GridLayout(0, 1, 0, 4));
for (String playerName : PLAYER_NAMES) {
JRadioButton radioBtn = createRadioButton(playerName);
btnGrp.add(radioBtn);;
add(radioBtn);
}
}
private JRadioButton createRadioButton(String playerName) {
JRadioButton rBtn = new JRadioButton(playerName, emptyIcon);
rBtn.setSelectedIcon(selectedIcon);
return rBtn;
}
private static void createAndShowGui() {
CircleIconEg mainPanel = new CircleIconEg();
JFrame frame = new JFrame("CircleIconEg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I want to set up a mathematical (where y grows up not down) coordinate space from (-1, -1) to (+1, +1) and have it fit in the window regardless of the window size.
I am using an anonymous JComponent subclass in Java SE 7 and casting the incoming Graphics in paintComponent to Graphics2D and then drawing on the Graphics2D
But the Graphics2D is set to a computer coordinate space that changes with the size of the window. How to get it to rescale according to window size and have Y go upwards? The following program should show a dark square in upper right quadrant.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class G {
public static void main (String [] args) {
JFrame frame = new JFrame(G.class.getCanonicalName());
frame.setUndecorated(true);
JComponent component = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
paint2D((Graphics2D)g);
}
protected void paint2D (Graphics2D g2) {
g2.draw(new Rectangle2D.Double(0.1, 0.1, 0.9, 0.9));
}
};
frame.add(component);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
}
Setup the coordinate system how you want, using transform() and translate(). So:
you want the origin to be at (0, height); bottom left.
then you want to flip the Y axis.
Example code:
AffineTransform tform = AffineTransform.getTranslateInstance( 0, height);
tform.scale( 1, -1);
g2.setTransform( tform);
[My edited version]:
public static void main (String [] args) {
JFrame frame = new JFrame( G2dTransform_Question.class.getCanonicalName());
JComponent component = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
paint2D((Graphics2D)g);
}
protected void paint2D (Graphics2D g2) {
AffineTransform tform = AffineTransform.getTranslateInstance( 0, getHeight());
tform.scale( getWidth(), -getHeight()); // NOTE -- to make 1.0 'full width'.
g2.setTransform( tform);
g2.setColor( Color.BLUE); // NOTE -- so we can *see* something.
g2.fill( new Rectangle2D.Double(0.1, 0.1, 0.8, 0.8)); // NOTE -- 'fill' works better than 'draw'.
}
};
frame.setLayout( new BorderLayout()); // NOTE -- make the component size to frame.
frame.add( component, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
[Hovercraft's version]: Thanks Hover!
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class G {
public static final int PREF_W = 400;
public static final int PREF_H = PREF_W;
public static void main (String [] args) {
JFrame frame = new JFrame(G.class.getCanonicalName());
frame.setUndecorated(true);
JComponent component = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent (Graphics g) {
super.paintComponent(g);
AffineTransform tform = AffineTransform.getTranslateInstance( 0, getHeight());
tform.scale( 1, -1);
Graphics2D g2 = (Graphics2D) g.create();
g2.setTransform( tform);
paint2D(g2);
g2.dispose();
}
protected void paint2D (Graphics2D g2) {
g2.draw(new Rectangle2D.Double(10, 10, 20, 30));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
};
frame.add(component);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Right now I am making my jinternal frames transparent using this code:
double rgbConversionBackpack = Double.parseDouble(MyClient.configFile.getProperty("BACKPACK_FRAME_ALPHA"));
double tmp = (rgbConversionBackpack / 100.0) * 255.0;
this.getContentPane().setBackground(new Color(140, 0, 0, (int)tmp));
this.setOpaque(false);
I have code on the sliders to set the alpha which all works perfectly and saves it to a properties file, yada, yada, yada. The question is how do I make the entire JInternal Frame transparent.
Right now I have only be able to set the content pane, and any other panels (etc) that are in the jinternal frames transparent, but I want to make the entire JinternalFrame(borders and all) transparent.
Screenshot below shows how on the backpack the red tinted are is partially transparent and looks decent, but still want the border to be transparent also.
Is there a way to override the draw super method for each of my classes the extend JInternalFrame to have it draw semi transparent(depending on value obviously)?
You could do this by changing the AlphaComposite that the JInternalFrame's paint method uses. You have to be careful though to repaint the containing top level window at the location of the transparent component lest you have funny side effects. For example:
import java.awt.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class TransparentInternalFrame extends JDesktopPane {
private static final Color COLOR_1 = Color.red;
private static final Color COLOR_2 = Color.blue;
private static final float PT_2 = 30f;
private static final int PREF_W = 800;
private static final int PREF_H = 500;
public TransparentInternalFrame() {
add(new MyInternalFrame("Foo", 50, 50, 300, 300, 0.2f));
add(new MyInternalFrame("Foo", 400, 100, 300, 300, 0.4f));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(new GradientPaint(0, 0, COLOR_1, PT_2, PT_2, COLOR_2, true));
g2.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
TransparentInternalFrame mainPanel = new TransparentInternalFrame();
JFrame frame = new JFrame("TransparentInternalFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyInternalFrame extends JInternalFrame {
private AlphaComposite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
public MyInternalFrame(String title, int x, int y, int w, int h, final float alpha) {
super(title);
setClosable(true);
setBounds(x, y, w, h);
setVisible(true);
int sliderValue = (int) (alpha * 100);
comp = comp.derive(alpha);
final JSlider slider = new JSlider(0, 100, sliderValue);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setBorder(BorderFactory.createTitledBorder("Alpha Value"));
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent cEvt) {
float alpha = (float) slider.getValue() / 100f;
setAlpha(alpha);
MyInternalFrame.this.repaint();
Window win = SwingUtilities.getWindowAncestor(MyInternalFrame.this);
win.repaint();
}
});
add(new JLabel("My Label", SwingConstants.CENTER));
add(slider, BorderLayout.SOUTH);
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(comp);
super.paint(g);
}
public void setAlpha(float alpha) {
comp = comp.derive(alpha);
}
}
But note that this program is not fully fixed. You'll still see pixel errors if you drag one JInternalFrame over another. I still need to work the bugs out...