How to create a new canvas on JPanel? - java

i'm making an MSPaint like application on java, but i'm stuck on create a new canvas (a white background JPanel)
My Code is this:
public void creaLienzo(){
BufferedImage canvas=new BufferedImage(lienzo.getWidth(),
lienzo.getHeight(),BufferedImage.TYPE_INT_RGB);
Graphics2D g2=canvas.createGraphics();
g2.setBackground(Color.WHITE);
lienzo.paint(g2);
}
But the JPanel doesn't draw the white background.

Setting the background would Not be the way to do this.
Since you would only want this once, use:
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, getWidth(), getHeight());

assuming that you have a separate panel for the canvas you could do this.
public void addNew(JPanel panel)
{
panel.removeAll();
panel.add(new Canvas());
}
you can set the background color for a jPanel by using
jPanelName.setBackground(Color.white);

Related

Drawing a Component to BufferedImage causes display corruption

I am using the JScrollNavigator component described here, in order to provide a navigation window onto a large "canvas-like" CAD component I have embedded within a JScrollPane.
I have tried to adapt the JScrollNavigator to draw a thumbnail image of the canvas to provide some additional context to the user. However, the action of doing this causes the rendering of my application's main frame to become corrupted. Specifically, it is the action of calling paint(Graphics) on the viewport component (i.e. my main canvas), passing in the Graphics object created by the BufferedImage that causes subsequent display corruption; if I comment this line out everything works fine.
Below is the JScrollNavigator's overridden paintComponent method:
#Override
protected void paintComponent(Graphics g) {
Component view = jScrollPane.getViewport().getView();
BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
// Paint JScrollPane view to off-screen image and then scale.
// It is this action that causes the display corruption!
view.paint(g2d);
g2d.drawImage(img, 0, 0, null);
Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);
super.paintComponent(g);
g.drawImage(scaled, 0, 0, null);
}
Does anyone have any suggestions as to the cause of the corruption? I would have thought that painting to an offscreen image should have no effect on existing paint operations.
EDIT
To provide some additional detail: The JScrollNavigator forms a sub-panel on the left-hand side of a JSplitPane. The JScrollPane associated with the navigator is on the right-hand side. The "corruption" causes the splitter to no longer be rendered and the scrollbars to not be visible (they appear white). If I resize the JFrame, the JMenu section also becomes white. If I attempt to use the navigator or interact with the scrollbars, they become visible, but the splitter remains white. It's as if the opaque settings of the various components has been affected by the rendering of the viewport view to an offscreen image.
Also, if I make the JScrollNavigator appear in a completely separate JDialog, everything works correctly.
EDIT 2
I can reproduce the problem consistently by doing the following:
Add a JMenuBar to the mFrame:
JMenuBar bar = new JMenuBar();
bar.add(new JMenu("File"));
mFrame.setJMenuBar(bar);
In the main() method of JScrollNavigator replace:
jsp.setViewportView(textArea);
... with:
jsp.setViewportView(new JPanel() {
{
setBackground(Color.GREEN);
setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
}
});
Ensure that the JScrollNavigator is embedded as a panel within mFrame, rather than appearing as a separate JDialog:
mFrame.add(jsp, BorderLayout.CENTER);
mFrame.add(nav, BorderLayout.NORTH);
Now when the application runs the JMenuBar is no longer visible; the act of painting the view (i.e. a green JPanel with thick black border) to the Graphics2D returned by BufferedImage.createGraphics() actually appears to be rendering it onscreen, possibly from the top-left corner of the JFrame, thus obscuring other components. This only seems to happen if a JPanel is used as the viewport view, and not another component such as JTextArea, JTable, etc.
EDIT 3
Looks like this person was having the same problem (no solution posted though): http://www.javaworld.com/community/node/2894/
EDIT 4
Here's the main and paintComponent methods that result in the reproducible error described in Edit 2:
public static void main(String[] args) {
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(new JPanel() {
{
setBackground(Color.GREEN);
setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
}
});
JScrollNavigator nav = new JScrollNavigator();
nav.setJScrollPane(jsp);
JFrame mFrame = new JFrame();
JMenuBar bar = new JMenuBar();
bar.add(new JMenu("File"));
mFrame.setJMenuBar(bar);
mFrame.setTitle("JScrollNavigator Test");
mFrame.setSize(800, 600);
mFrame.setLayout(new GridLayout(1, 2));
mFrame.add(jsp);
mFrame.add(nav);
Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
mFrame.setLocation((screenDim.width - mFrame.getSize().width) / 2, (screenDim.height - mFrame.getSize().height) / 2);
mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mFrame.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Component view = jScrollPane.getViewport().getView();
if (img == null) {
GraphicsConfiguration gfConf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = img.createGraphics();
view.paint(g2d);
Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);
g.drawImage(scaled, 0, 0, null);
}
EDIT 5
It seems like others are having trouble recreating the exact problem. I would ask people to run the code pasted here. When I first run this example I see the following:
Neither the JScrollNavigator or the JMenuBar have been painted; these frame areas are transparent.
After resizing I see the following:
The JMenuBar has still not been painted and it appears that the JPanel was at some point rendered at (0,0) (where the JMenuBar should be). The view.paint call within paintComponent is the direct cause of this.
Summary: The original JScrollNavigator uses the Swing opacity property to render a convenient green NavBox over a scaled thumbnail of the component in an adjacent JScrollPane. Because it extends JPanel, the (shared) UI delegate's use of opacity conflicts with that of the scrollable component. The images seen in edit 5 above typify the associated rendering artifact, also shown here. The solution is to let NavBox, JScrollNavigator and the scrollable component extend JComponent, as suggested in the second addendum below. Each component can then manage it's own properties individually.
I see no unusual rendering artifact with your code as posted on my platform, Mac OS X, Java 1.6. Sorry, I don't see any glaring portability violations.
A few probably irrelevant, but perhaps useful, observations.
Even if you use setSize(), appropriately in this case, you should still pack() the enclosing Window.
f.pack();
f.setSize(300, 200);
For convenience, add() forwards the component to the content pane.
f.add(nav, BorderLayout.WEST);
Prefer StringBuilder to StringBuffer.
Consider ComponentAdapter in place of ComponentListener.
Addendum: As suggested here, I got somewhat more flexible results using RenderingHints instead of getScaledInstance() as shown below. Adding a few icons makes it easier to see the disparate effect on images and text.
editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon"));
editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Component view = jScrollPane.getViewport().getView();
BufferedImage img = new BufferedImage(view.getWidth(),
view.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D off = img.createGraphics();
off.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
off.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
view.paint(off);
Graphics2D on = (Graphics2D)g;
on.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
on.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
on.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}
Addendum secundum: It looks like the JPanel UI delegate is not cooperating. One workaround is to extend JComponent so that you can control opacity. It's only slightly more work to manage the backgroundColor. NavBox and JScrollNavigator are also candidates for a similar treatment.
jsp.setViewportView(new JComponent() {
{
setBackground(Color.red);
setBorder(BorderFactory.createLineBorder(Color.BLACK, 16));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
});
I am also not sure what you mean by corruption, but I noticed that the resampled image is much nicer if you specify Image.SCALE_SMOOTH as the rescaling hint:
Image scaled = img.getScaledInstance(getWidth(), getHeight(), Image.SCALE_SMOOTH);
Maybe this is what you are looking for...
I was able to reproduce your problem and get you the result your looking for. The problem is that the drawing of the image wasn't complete by the time you were repainting again, so only portions of the image were being painted. To fix this, add this field to your JScrollNavigator class (as a lock):
/** Lock to prevent trying to repaint too many times */
private boolean blockRepaint = false;
When we repaint the component, this lock will be activated. It won't be released until we have been able to successfully paint the panel - then another paint can be executed.
The paintComponent needs to be changed to abide by the lock and use a ImageObserver when painting your navigation panel.
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
if(!blockRepaint){
final Component view = (Component)jScrollPane.getViewport().getView();
BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Paint JScrollPane view to off-screen image and then scale.
// It is this action that causes the display corruption!
view.paint(g2d);
ImageObserver io = new ImageObserver() {
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y,int width, int height) {
boolean result = true;
g.drawImage(img, 0, 0, null);
if((infoflags & ImageObserver.FRAMEBITS) == ImageObserver.FRAMEBITS){
blockRepaint = false;
result = false;
}
return result;
}
};
Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);
blockRepaint = g.drawImage(scaled, 0, 0, io);
}
}

draw buffered image ontop of another buffered image

my goal is to draw some bufferedimage onto another. then all this stuff draw onto some other bufferedimage and so on. And finally draw this on top of a panel.
For now i'm trying to draw bufferedimage onto panel and nothing works. My bufferedimage looks completely white:
public class Main2 {
public static void main(String[] args) {
JFrame frame = new JFrame("asdf");
final JPanel panel = (JPanel) frame.getContentPane();
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
somepaint(panel);
}
});
}
private static void somepaint(JPanel panel) {
BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
image.getGraphics().setColor(Color.red);
image.getGraphics().fillRect(0, 0, 200, 200);
Graphics2D graphics = (Graphics2D) panel.getGraphics();
graphics.setColor(Color.magenta);
graphics.fillRect(0, 0, 500, 500);
graphics.drawImage(image, null, 0, 0); // draws white square instead of red one
}
}
thanks
Re:
private static void somepaint(JPanel panel) {
BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
image.getGraphics().setColor(Color.red);
image.getGraphics().fillRect(0, 0, 200, 200);
Graphics2D graphics = (Graphics2D) panel.getGraphics();
This is not how you draw inside of a JPanel or JComponent.
Don't call getGraphics() on a component as the Graphics object returned will be short-lived, and anything drawn with it will not persist. Instead do your JPanel's drawing inside of its paintComponent(Graphics G) method override. You will need to create a class that extends JPanel in order to override paintComponent(...).
Most importantly, to see how to do Swing graphics correctly, don't guess. You'll want to read the Swing Graphics Tutorials first as it will require you to toss out some incorrect assumptions (I know that this is what I had to do to get it right).
You need to rectify your parameters in the drawImage() call. Change this:
graphics.drawImage(image, null, 0, 0);
to
graphics.drawImage(image, 0, 0,null);
Check the Java docs for more details.

JPanel image and components

I would like to have image on my JPanels an also have components such as JSlider and JRadioButton on my JPanel. I derived a class from JPanel and overrided method paintComponent as you see. This is a good way to have image on JPanel.
public void paintComponent(Graphics g)
{
/*create image icon to get image*/
ImageIcon imageicon = new ImageIcon(imageFile); //getClass().getResource(imageFile)
Image image = imageicon.getImage();
/*Draw image on the panel*/
super.paintComponent(g);
if (image != null)
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
However I have some problems. When I add components such as JSlider, JRadioButton or another JPanel on my ImagePanel; this component's background remains as default and not background picture. I don't know how to set this image as background of this components.
please guide me.
regards
you must set opaque properties of other component to false.
jRadioButton.setOpaque(false);
for example :
jRadioButton.setOpaque(false);
Will work for many look-and-feels, but if you want it to work with Nimbus you should also set the background color to be transparent:
jRadioButton.setBackground(new Color(0,0,0,0));
See this question for more details.
Doesn't setOpaque(false) for all the other components help?

How to set a transparent background of JPanel?

Can JPanels background be set to transparent?
My frame is has two JPanels:
Image Panel and
Feature Panel.
Feature Panel is overlapping Image Panel.
The Image Panel is working as a background and it loads image from a remote URL.
On Feature Panel I want to draw shapes. Now Image Panel cannot be seen due to Feature Panel's background color.
I need to make Feature Panel background transparent while still drawing its shapes and I want Image Panel to be visible (since it is doing tiling and cache function of images).
I'm using two JPanel's, because I need to seperate the image and shape drawing .
Is there a way the overlapping Jpanel have a transparent background?
Calling setOpaque(false) on the upper JPanel should work.
From your comment, it sounds like Swing painting may be broken somewhere -
First - you probably wanted to override paintComponent() rather than paint() in whatever component you have paint() overridden in.
Second - when you do override paintComponent(), you'll first want to call super.paintComponent() first to do all the default Swing painting stuff (of which honoring setOpaque() is one).
Example -
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(null);
CirclePanel topPanel = new CirclePanel();
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel();
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.drawArc(x, y, width, height, 0, 360);
}
}
}
Alternatively, consider The Glass Pane, discussed in the article How to Use Root Panes. You could draw your "Feature" content in the glass pane's paintComponent() method.
Addendum: Working with the GlassPaneDemo, I added an image:
//Set up the content pane, where the "main GUI" lives.
frame.add(changeButton, BorderLayout.SOUTH);
frame.add(new JLabel(new ImageIcon("img.jpg")), BorderLayout.CENTER);
and altered the glass pane's paintComponent() method:
protected void paintComponent(Graphics g) {
if (point != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2d.setColor(Color.yellow);
g2d.fillOval(point.x, point.y, 120, 60);
}
}
As noted here, Swing components must honor the opaque property; in this variation, the ImageIcon completely fills the BorderLayout.CENTER of the frame's default layout.
In my particular case it was easier to do this:
panel.setOpaque(true);
panel.setBackground(new Color(0,0,0,0,)): // any color with alpha 0 (in this case the color is black
(Feature Panel).setOpaque(false);
Hope this helps.
To set transparent you can set opaque of panel to false like
JPanel panel = new JPanel();
panel.setOpaque(false);
But to make it transculent use alpha property of color attribute like
JPanel panel = new JPanel();
panel.setBackground(new Color(0,0,0,125));
where last parameter of Color is for alpha and alpha value ranges between 0 and 255 where 0 is full transparent and 255 is fully opaque
public void paintComponent (Graphics g)
{
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.0f)); // draw transparent background
super.paintComponent(g);
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1.0f)); // turn on opacity
g.setColor(Color.RED);
g.fillRect(20, 20, 500, 300);
}
I have tried to do it this way, but it is very flickery
As Thrasgod correctly showed in his answer, the best way is to use the paintComponent, but also if the case is to have a semi transparent JPanel (or any other component, really) and have something not transparent inside. You have to also override the paintChildren method and set the alfa value to 1.
In my case I extended the JPanel like that:
public class TransparentJPanel extends JPanel {
private float panelAlfa;
private float childrenAlfa;
public TransparentJPanel(float panelAlfa, float childrenAlfa) {
this.panelAlfa = panelAlfa;
this.childrenAlfa = childrenAlfa;
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getBackground());
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, panelAlfa));
super.paintComponent(g2d);
}
#Override
protected void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getBackground());
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_ATOP, childrenAlfa));
super.paintChildren(g);
}
//getter and setter
}
And in my project I only need to instantiate Jpanel jp = new TransparentJPanel(0.3f, 1.0f);, if I want only the Jpanel transparent.
You could, also, mess with the JPanel shape using g2d.fillRoundRect and g2d.drawRoundRect, but it's not in the scope of this question.

Background Image in JTextPane

How do I set a background image to a JTextPane - some sort of a watermark.
I tried this option - creating a child class of JTextPane and use the paint method to draw the image.
But then the text is displayed "below" the image than above.
Is there any "standard" or "well known" way to do this?
(BTW, I tried (something silly?) making the content type "text/html", and setting the image as the background image of a <div> but it did not help.)
Here's a working example:
import javax.swing.*;
import java.awt.*;
public class ScratchSpace {
public static void main(String[] args) {
JFrame frame = new JFrame("");
final MyTextPane textPane = new MyTextPane();
frame.add(textPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class MyTextPane extends JTextPane {
public MyTextPane() {
super();
setText("Hello World");
setOpaque(false);
// this is needed if using Nimbus L&F - see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6687960
setBackground(new Color(0,0,0,0));
}
#Override
protected void paintComponent(Graphics g) {
// set background green - but can draw image here too
g.setColor(Color.GREEN);
g.fillRect(0, 0, getWidth(), getHeight());
// uncomment the following to draw an image
// Image img = ...;
// g.drawImage(img, 0, 0, this);
super.paintComponent(g);
}
}
}
The important things to note:
your component must not be opaque...
so setOpaque(false);
override paintComponent(Graphics g), not paint.
paint your background, with an image
or drawing BEFORE calling
super.paintComponent(g);
If you want to master this stuff, I recommend reading "Filthy Rich Clients", a book all about how to bend Swing to your will.
Try changing the paint code to this.
public void paint(Graphics g)
{
g.setXORMode(Color.white);
g.drawImage(image,0, 0, this);
super.paint(g);
}
This would make your image to be painted before the text is rendered by the actual component's paint method.
Hmm., put a background image to the JFrame/JPanel containg the JTextPane,.. and keep the JTextPane transparent to some level.

Categories