Graphics2D not clearing transparent image - java

I have the following snippet of code which should increment a counter on a transparent background on the first monitor. When the image shows 0 it renders fine, but after that (as soon as 1 is hit) the window redraws with an opaque background.
Silly example in practice I know, just broken a real use case down to a simpler piece of code.
It seems the key might be in the paintComponent method of TestCanvas:
g.setColor(new Color(0, 0, 0, 0));
g.clearRect(0, 0, getWidth(), getHeight());
From what I can work out, those two lines should set the drawing colour to completely transparent, then clear the given area with that colour - but this doesn't seem to be holding for beyond the first repaint.
EDIT: Using fillRect instead of clearRect doesn't work because it just paints the transparent rectangle on top of the existing image, so it never gets cleared. 1 is overlayed on 0, then 2 overlayed on 1, etc.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
public class LyricWindow extends JWindow {
private final TestCanvas canvas;
public LyricWindow(Rectangle area, boolean stageView) {
setBackground(new Color(0, 0, 0, 0));
setArea(area);
canvas = new TestCanvas();
canvas.setPreferredSize(new Dimension((int) (area.getMaxX() - area.getMinX()), (int) (area.getMaxY() - area.getMinY())));
add(canvas);
new Thread() {
public void run() {
for(int i = 0; true; i++) {
final int ii = i;
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
canvas.setText(Integer.toString(ii));
}
});
try {
Thread.currentThread().sleep(200);
}
catch(InterruptedException ex) {}
System.out.println(ii);
}
}
}.start();
}
public final void setArea(final Rectangle area) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if(canvas != null) {
canvas.setPreferredSize(new Dimension((int) (area.getMaxX() - area.getMinX()), (int) (area.getMaxY() - area.getMinY())));
}
setSize((int) (area.getMaxX() - area.getMinX()), (int) (area.getMaxY() - area.getMinY()));
setLocation((int) area.getMinX(), (int) area.getMinY());
}
});
}
public static void main(String[] args) {
LyricWindow w = new LyricWindow(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getConfigurations()[0].getBounds(), false);
w.setVisible(true);
}
}
class TestCanvas extends JPanel {
private String text;
#Override
public void paintComponent(Graphics g) {
g.setColor(new Color(0, 0, 0, 0));
g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawString(text, 100, 100);
}
public void setText(String s) {
text = s;
repaint();
}
}

Turned out I had to set the correct composite value before painting. Adding
((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
to the start of the paintComponent() method, then using fillRect(), did the trick!

Try g.fillRect(...) instead of g.clearRect(...). I think I ran into a similar problem once, and this may have solved it for me.
I found my original solution to the problem for use with BufferedImages. Not sure why the color White might work while Black would not, but give it a try:
g.setBackground(new Color(255, 255, 255, 0));
g.clearRect(0, 0, width, height);
From the Color class's Javadoc:
An alpha value of 1.0 or 255 means that the color is completely opaque
and an alpha value of 0 or 0.0 means that the color is completely
transparent.

I know this is a bit old, however why don't you just use the JPanel's already built functionality to clear the canvas for you by calling the function in the super class?
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0, 0, 0, 0));
g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawString(text, 100, 100);
}
That should leave you with a completely blank graphics object to draw on.

If your color is completely transparent, does the clearing actually do anything?
Try setting the alpha value to 255, instead of 0. Problem is, that will "clear" the rectangle to black (0, 0, 0), but I'm not sure it's possible to "clear" it to transparent with clearRect. You might try fillRect instead.

Related

I'm trying to change the Rectangle colour to Black but its not working

I'm new to Java and don't know exactly what the cause.Let me explain the issue
I created a Rectangle Shape and its working, then i thought about changing its color to black for some testing but it seems not working below is my code.
When i call the method from paintComponent itself then its working but if i do the same from any other method then its not changing the color. I tried calling the method repaint also but still the same
public class Meme extends JPanel {
Rectangle2D.Float myRect = new Rectangle2D.Float(90, 90, 90, 90);
Graphics2D graphics2d;
public void DRAW() {
graphics2d.setColor(new Color(0, 0, 200));
graphics2d.fill(myRect);
}
public void ChangeColour() {
System.out.println("Called");
graphics2d.setPaint(Color.BLACK);
System.out.println("Called2");
graphics2d.fill(myRect);
System.out.println("Called3");
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
graphics2d = (Graphics2D) g;
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
DRAW();
}
}
Button click listener method
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
meme1.ChangeColour();
}
As far as I can remember, whenever you change some properties (color in this case), you have to call repaint. This will invoke a call to paintComponent and the frame will be drawn once again.
In your case, I am guessing even if you call repaint after changing color, the DRAW method gets called again in paintComponent which resets the changed color back to (0, 0, 200). Therefore, you don't see any change in the screen. But when you call changeColor in paintComponent method (assuming after the call to DRAW), the change of color persists and does not get overridden.
POSSIBLE SOLUTION
Just keep the color stored somewhere else. Like
Color myColor = new Color(0,0,200);
then in DRAW:
private void DRAW() {
graphics2d.setColor(myColor);
graphics2d.fill(myRect);
}
and in ChangeColor:
private void ChangeColour() {
myColor = Color.BLACK;
}
Hope it helps.
update your function like this
public void ChangeColour() {
System.out.println("Called");
graphics2d.setColor(new Color(1, 1, 200));
System.out.println("Called2");
graphics2d.fill(myRect);
System.out.println("Called3");
}
Painting in Swing is both passive and destructive. That is, a paint pass can occur at anytime for any number of reasons, many which you don't control. Destructive means, on each paint pass you are expected to repaint the entire component from scratch.
In Swing, you update the state you want change and then call repaint to trigger a new paint pass.
Painting should only ever paint the current state, it should never try and change it
public class Meme extends JPanel {
Rectangle2D.Float myRect = new Rectangle2D.Float(90, 90, 90, 90);
private Color color;
public void draw(Graphics2D graphics2d) {
graphics2d.setColor(color);
graphics2d.fill(myRect);
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void ChangeColour() {
color = Color.BLACK;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
graphics2d = (Graphics2D) g.create();
graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
draw(graphics2d);
graphics2d.dispose();
}
}
Also, the graphics context passed to your component is shared with all the other components, so it's important that any significant changes you make to the context are undone before the method exists - in most cases, it's just a simple case of calling create on the Graphics context to snapshot it state and dispose (on the copy you created) when you're done

Why does my loop run twice?

I'm trying to run basic animations, and all of the problems in my program is rooted in one of my loops running twice instead of once. I've isolated the problem here:
import java.awt.*;
import java.applet.*;
public class PrinnyTest extends Applet{
public void init()
{
setSize(600, 550);
}
public void paint (Graphics g)
{
Image img = getImage(getDocumentBase(), "http://www.clipartqueen.com/image-files/cats-head.png");
for(int y = 600;y>=20; y--)
{
g.setColor(Color.white);
g.fillRect(0, 0, 600, 550);
g.drawImage(img, 40, y, null);
try
{
Thread.sleep(20);
}
catch(InterruptedException ie)
{}
}
}
}
As you can see, it moves the image to the top but then resets the position and starts the loop over again. I feel like the answer is staring me right in the face, but for some reason I can't figure it out. Any help would be greatly appreciated.

Drawing lines in java (Graphics 2D)

I am trying to do a little program on Eclipse. The program goes like this: when I click for the 1st time on thr Panel on the frame, a line has to be drawn regarding the Y position of my mouse listener.The line takes all the width of the panel. On the 2nd click, another line has to be drawn, again regarding the Y position of where I clicked. After, I'll put a little circle between the 2 lines and make a little animation with it.
But now, I have a problem. When I click on the panel, a line is drawn, but if i click another time, the first line disappears and the 2nd line takes it place...
This is the code of the painComponent and my mousr listener. What is wrong with it ?
public Lines() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
posY=e.getY();
posX=e.getX();
nbClic=e.getClickCount();
repaint();
}
});
setBackground(Color.black);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
if(nbClic>=1){
line1=new Line2D.Double(0, posY, getWidth(), posY);
g2d.draw(line1);
repaint();
}
if(nbClic>=2){
g2d.setColor(Color.YELLOW);
line2=new Line2D.Double(0, posY, getWidth(), posY);
g2d.draw(line2);
}
repaint();
}
Painting is an event that draws the entire component. You can't depend on past events because they are erased each time a repaint happens.
You would need to keep something like a List and each time you create a new line, you add it to the List.
List<Integer> yClicks = new ArrayList<>();
... {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
yClicks.add(e.getY());
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
for(int y : yClicks) {
g2d.draw(new Line2D.Double(0, y, getWidth(), y));
}
g2d.dispose();
}
Also:
Never call repaint inside paintComponent! This will cause an endless cycle of repaints.
paintComponent is a protected method and should remain so unless there is a compelling reason to make it public.
Be careful changing the state of the Graphics object passed in to paintComponent because it is used elsewhere. Usually we create a local copy which is disposed when we are done.

Swing double Buffering and Animation

I am trying to prevent redrawing an animation by the EDT. The first thing i have done is excluding the actual drawing tasks into a different thread, writing into a VolatileImage, which gets redrawn by EDT within paintComponent method of my corresponding JPanel.
If i exclude the repaint into another thread, this works properly. Nevertheless, i do have positioned a couple of other panels above my animation.
In result, having called the repaint method of my painter (animation) panel, has caused the others to also get redrawn without flickering.
Therefore to redraw the other panels, calling repaint on painter, results in flickering. Repaint of a single panel results in an opaque redraw with rarely flickering.
Does somebody know, how to synchronize an own repaint of a jpanel, for instance into my already available bufferimage. Id say the repaint triggered to EDT results in flickering, since its not synchronized.
My repaint call to animation
#Override
public void KeyframeChanged(Keyframe frame) {
if (painter.isVisible()) {
map.getMainMap().doPaintComponent(painter.getBuffer().getGraphics());
painter.renderAnimation();
painter.updateScreen();
}
}
painter methods:
public void updateScreen() {
Graphics g = this.getGraphics();
if (g != null) // component already visible?
{
// is there a backBuffer to draw?
if (backBuffer != null) {
g.drawImage(backBuffer, 0, 0, null);
} else {
// if not, create one and render on it
createBackBuffer();
renderAnimation();
}
}
}
public void renderAnimation() {
// Do drawing stuff here
}
#Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
}// end of paint
Thanks
Thanks for answers and links. I still need to read a few of them. Nevertheless in order to illustrate the current behavior, this small SSCCE shall help.
package repaintexample;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.VolatileImage;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
class Painter extends JPanel {
private VolatileImage backBuffer;
private Graphics2D g2d;
public Painter() {
setDoubleBuffered(false);
setOpaque(false);
}
#Override
public boolean isOptimizedDrawingEnabled() {
return false;
}
private void createBackBuffer() {
backBuffer = this.getGraphicsConfiguration().createCompatibleVolatileImage(1920, 1200);
}
public void adjustBackBufferSize() {
if (backBuffer != null) {
if (getWidth() > backBuffer.getWidth() || getHeight() > backBuffer.getHeight()) {
createBackBuffer();
}
}
}
public void updateScreen(Graphics g) {
if (g != null) // component already visible?
{
// is there a backBuffer to draw?
if (backBuffer != null) {
g.drawImage(backBuffer, 0, 0, null);
} else {
// if not, create one and render on it
createBackBuffer();
}
}
}
public void renderAnimation(int i, int j) {
if (backBuffer == null) {
createBackBuffer();
}
do {
if (backBuffer.validate(getGraphicsConfiguration()) == VolatileImage.IMAGE_INCOMPATIBLE) {
createBackBuffer();
}
g2d = (Graphics2D) backBuffer.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.red);
g2d.fillOval(i, j, 50, 50);
} while (backBuffer.contentsLost());
}
#Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
updateScreen(g);
}// end of paint
public VolatileImage getBuffer() {
return backBuffer;
}
}
class ContainerFrame extends JFrame {
private Painter mapPainter;
private JPanel informationPanel; // covers a lot of metainformation - Actually own JTable instance updating the same objects for repainting in each circle
private JPanel controller; // Maptools
private JPanel tabbedPabe; // Change redraw content
public ContainerFrame() {
this.setSize(1600, 1024);
this.setVisible(true);
initComponents();
initPositions();
Thread animation = new Thread(new Runnable() {
#Override
public void run() {
// My application is a mapping application, in which i first draw the tiles, before goin on with the "real" animated stuff
// clearing backbuffer content with g.fillRect(0, 0, getWidth(), getHeight());
while (true) {
for (int i = 0; i < mapPainter.getWidth(); i += 100) {
for (int j = 0; j < mapPainter.getHeight(); j += 5) {
mapPainter.renderAnimation(i, j);
int repaintCase = 2;
switch (repaintCase) {
case 0:
// Default case redrawing via EDT, triggering the others in proper order
mapPainter.repaint();
break;
case 1:
// case repainting by current Thread - necessity of repainting above positioned panels
// results in flickering, since not synchronized
mapPainter.updateScreen(mapPainter.getGraphics());
informationPanel.repaint();
controller.repaint();
tabbedPabe.repaint();
break;
case 2:
// painting components on buffer
// Results in rarely flickering and opague repaint
// is there any common way, to manually repaint onto anything - like image
informationPanel.paintAll(mapPainter.getBuffer().getGraphics());
controller.paintAll(mapPainter.getBuffer().getGraphics());
tabbedPabe.paintAll(mapPainter.getBuffer().getGraphics());
mapPainter.updateScreen(mapPainter.getGraphics());
break;
}
}
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
animation.start();
}
private void initComponents() {
mapPainter = new Painter();
mapPainter.setSize(this.getSize());
informationPanel = new JPanel();
informationPanel.setSize(new Dimension(360, 800));
controller = new JPanel();
controller.setSize(new Dimension(500, 250));
tabbedPabe = new JPanel();
tabbedPabe.setSize(new Dimension(300, 300));
this.getLayeredPane().add(mapPainter, JLayeredPane.DEFAULT_LAYER);
this.getLayeredPane().add(controller, JLayeredPane.MODAL_LAYER);
this.getLayeredPane().add(tabbedPabe, JLayeredPane.MODAL_LAYER);
this.getLayeredPane().add(informationPanel, JLayeredPane.MODAL_LAYER);
}
private void initPositions() {
controller.setLocation(mapPainter.getWidth() - controller.getWidth(), mapPainter.getHeight() - controller.getHeight());
tabbedPabe.setLocation(this.getWidth() - tabbedPabe.getWidth(), mapPainter.getHeight() - controller.getHeight() - tabbedPabe.getHeight() - 400);
informationPanel.setLocation(10, mapPainter.getHeight() - informationPanel.getHeight() - 200);
}
}
public class RepaintExample {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ContainerFrame f = new ContainerFrame();
}
}
I do use case 0 at the moment and do see having either great or pretty bad fps - either 30 or around 6. I am not certain, how that may be possible and i may be able to find sth. in the already posted links. I thought making sure to relieve the EDT at best, could become a proper solution.
Additionally the content of the 3 panels, i illustrated do not need a repaint in the same frequency as the animation does. Unfortunately i haven't found a proper way to prevent the repaint. The only way i have used for quite a while was a paintimmediately in an invokelater call for those areas, which are known as "animated". A common repaint(Rectangle rec) has not been working, since single calls have been summarized to a big one, covering more pixels, than i have passed in.
public void drawCachedSprite(Graphics2D g, CachedSprites sprites, int zoom, double cog, double x, double y, double w, double h) {
try{
pos_x = x;
pos_y = y;
RenderingUtil.getRenderQuality();
transform.setToIdentity();
// Compute the corner, the drawing needs to start with
transform.translate(x - (w / 2.0), y - (h / 2.0));
g.drawImage(sprites.getSprite(DefaultResources.getType(), spriteColor, zoom, cog), transform, null);
} catch (IllegalArgumentException e) {
e.printStackTrace();
System.out.println("width or height not set properly");
}
}

Cannot Add items to custom painted JPanel

When I try to use myCustomPanel.add(someComponent) it does not add...
Here is my custom JPanel class:
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
/**
*
* #author Jacob
*/
public class OSXMainPanel extends JPanel {
public static final long serialVersionUID = 24362462L;
private Image image;
public OSXMainPanel() {
super.setOpaque(true);
try {
image = javax.imageio.ImageIO.read(new java.net.URL(getClass().getResource("/assets/background.png"), "background.png"));
} catch (Exception e) {}
}
#Override
protected void paintComponent(Graphics g) {
if (isOpaque())
{
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
for(int w = 0; w < this.getWidth(); w = w + 50) {
for(int h = 0; h < this.getHeight(); h = h + 50) {
g.drawImage(image, w, h, 50, 50, this);
}
}
}
}
The reason this isn't working is because your paintComponent method isn't painting the added components. Calling super.paintComponent(g) at the start of the paintComponent method should fix this.
It should not be necessary to call super.paintComponent(Graphics g) to paint child components. The call is useful, to draw the background, but not strictly necessary.
I tested the code on Java 6 and it worked fine for me. The only modification I made was to add the following line in the constructor:
add(new JLabel("Test"));
I do not have the background image file so the image drawing code was doing nothing. Either the background image is somehow obscuring the child components or there is a bug in the code that adds a child component. Try commenting out the drawImage call and see if child components become visible.
I would call updateUI()
myPanel.add(new JLabel("wanna see it"));
// change of look and feel
myPanel.updateUI();
after adding the component - if you want to update whole look and feel. Otherwise use revalidate().
myPanel.add(new JLabel("wanna see this"));
myPanel.revalidate();

Categories