Java Swing BufferedImage poor quality - java

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);
}
}

Related

Java Graphics2D floating point accurate drawOval alternative?

So I am trying to draw an arc and put a circle around its round endpoint, but I am having issues due to rounding to the nearest pixel. This is visible in some but not all cases.
Is there a way to draw circles using floating points and anti-aliasing to eliminate this rounding error?
You can run this code to see the problem. I have drawn arcs of 0 length (appearing as large dots) instead of full arcs for clarity.
import java.awt.*;
import javax.swing.*;
public class Example extends JFrame {
private int CENTER = 200;
private static int WINDOW = 400;
private int LEFT = 50;
private int TOP = 50;
private int DIM = 300;
private int DIAMETER = 26;
public Example () {
super();
}
public void paint (Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(16, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
g2.setColor(new Color(0, 0, 255));
g2.drawArc(LEFT, TOP, DIM, DIM, 0, 0);
g2.drawArc(LEFT, TOP, DIM, DIM, 32, 0);
g2.drawArc(LEFT, TOP, DIM, DIM, 115, 0);
g2.drawArc(LEFT, TOP, DIM, DIM, 200, 0);
g2.drawArc(LEFT, TOP, DIM, DIM, 331, 0);
this.drawCircle(g2, 0);
this.drawCircle(g2, 32);
this.drawCircle(g2, 115);
this.drawCircle(g2, 200);
this.drawCircle(g2, 331);
g2.setStroke(new BasicStroke(1));
g2.setColor(new Color(0, 0, 0));
g2.drawLine(0, CENTER, DIM * 2, CENTER);
g2.drawLine(CENTER, 0, CENTER, DIM * 2);
}
private void drawCircle(Graphics2D g, int angle) {
g.setStroke(new BasicStroke(3));
g.setColor(new Color(0, 0, 255));
g.drawOval(
Math.round(CENTER + (float)(Math.cos(Math.toRadians(angle)) * (DIM/2)) - DIAMETER/2),
Math.round(CENTER - (float)(Math.sin(Math.toRadians(angle)) * (DIM/2)) - DIAMETER/2),
DIAMETER,
DIAMETER
);
}
public static void main (String args[]) {
Example e = new Example();
e.setSize(WINDOW, WINDOW);
e.setVisible(true);
e.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
As an alternative, consider Ellipse2D with suitable RenderingHints. Typical usage is shown here.
Ellipse2D circle = new Ellipse2D.Float(…);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHints(…);
g2d.fill(circle);
Because various RenderingHints are implementation dependent, the example below will let you evaluate the effects individually.
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/38669048/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JPanel() {
private static final int N = 8;
private final Ellipse2D ellipse = new Ellipse2D.Float();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
g2d.setStroke(new BasicStroke(N));
ellipse.setFrame(N, N, getWidth() - 2 * N, getHeight() - 2 * N);
g2d.draw(ellipse);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
});
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Test()::display);
}
}

How to paint specific coordinates of one JPanel onto another JPanel

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.
}

How to setup desired Coordinates in Graphics2D

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);
}
}

How to make the background gradient of a JPanel

I want to know how to make background gradient which is in another JPanel. Many articles found in internet,but all of them had demostrated how to overide the paintComponent() of the JPanel not how to do for a jPanel which is inside it.
I use Netbeans IDE. I created a new JPanel class and could overide its paintComponent(). I have another jpanel on it (dragged & dropped on to the parent JPanel). I want to make its background gradient.
Here is how I tried for parent. It worked. How can I overide this for child jpanel ?
public void paintComponent(Graphics 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);
}
If you are careful to invoke super.paintComponent(g), you can add the gradient directly to the panel as shown below.
For usability, I would resist the temptation to try making the individual components transparent. Note also that opacity is controlled by the Look & Feel.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
* #see http://stackoverflow.com/q/12220853/230513
*/
public class GradientPanel extends JPanel {
private static final int N = 32;
public GradientPanel() {
this.setBorder(BorderFactory.createEmptyBorder(N, N, N, N));
this.add(new JLabel("Test:", JLabel.CENTER));
this.add(new JTextField("This is a test."));
}
#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);
}
private void display() {
JFrame f = new JFrame("GradientPanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new GradientPanel().display();
}
});
}
}
I think this is what you were trying to do
jPanel1 = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
int w = getWidth();
int h = getHeight();
Color color1 = new Color(81,80,106);
Color color2 = new Color(165,164,241);
GradientPaint gp = new GradientPaint(0, 0, color1, 0, h, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h);
}
};

How do I GradientPaint on multiple jPanels?

I have a view object that is a jPanel and holds other jPanels which in turn hold jLabels. I'm wanting to paint a gradient overlay on the object to give it a nice sleek look rather than the boring plain look.
My attempt thus far is:
public class InfoDisplay extends javax.swing.JPanel {
#Override
public void paintComponent(Graphics g) {
UIDefaults uid = UIManager.getDefaults();
Graphics2D g2d = (Graphics2D)g;
int w = getWidth();
int h = getHeight();
Color lightBlue = new Color(41, 117, 200);
Color darkBlue = new Color(2, 47, 106);
if (!isOpaque()) {
super.paintComponent( g );
return;
}
GradientPaint gp = new GradientPaint(0, 0, lightBlue, 0, h, darkBlue );
g2d.setPaint(gp);
g2d.fillRect( 0, 0, w, h );
setOpaque( false );
super.paintComponent( g );
setOpaque( true );
}
}
This doesn't seem to change the objects background at all. I'm fairly new to messing with things that aren't related to the Gui defaults.
I used the Gui builder in Netbeans to create the object, so initComponents() is also in the class, but I posted only the source that is relevant to the question.
Perhaps someone can point me in the right direction?
If you want a background JPanel to use a gradient paint, then just use it. Don't do all that funny stuff in your code with setOpaque and super.paintComponent. e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.*;
#SuppressWarnings("serial")
public class GradientPaintPanel extends JPanel {
private static final Color LIGHT_BLUE = new Color(41, 117, 200);
private static final Color DARK_BLUE = new Color(2, 47, 106);
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
GradientPaint gradPaint = new GradientPaint(0, 0, LIGHT_BLUE, 0, getHeight(), DARK_BLUE);
g2.setPaint(gradPaint);
g2.fillRect(0, 0, getWidth(), getHeight());
}
public GradientPaintPanel() {
}
private static void createAndShowUI() {
GradientPaintPanel gradPaintPanel = new GradientPaintPanel();
gradPaintPanel.setPreferredSize(new Dimension(400, 300));
JFrame frame = new JFrame("GradientPaintEg");
frame.getContentPane().add(gradPaintPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}

Categories

Resources