Translate/Rotate/Move a graphics object without messing up the whole screen - java

I'm coding a GUI that will be doing some graphics translations/rotations, etc.
My problem is that when I try to translate my graphics,
(a) The entire screen translates instead of my one little painted area
(b) The old paint stays there, leaving a big paint blob instead of a translated image
(c) If I use the clearRect method to allow me to avoid (b), the entire screen goes white and (a) is still a problem
my DrawPanel class (I called it "LaunchTubeImage" for whatever reason)
private class LaunchTubeImage extends JPanel {
private Color colour;
public LaunchTubeImage(Color color) {
super();
this.colour = color;
}
public void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D)g;
double theta = (double)angle.getValue();
theta = Math.toRadians(theta);
gg.rotate(theta,tubeImage.getSize().width/2 + 10,
tubeImage.getSize().height - 50);
g.setColor(colour);
g.clearRect(0,0,getWidth(),getHeight());
g.fillRect(tubeImage.getSize().width/2,
tubeImage.getSize().height - 100 , 10, 50);
}
}
where this is called in my code
tubeImage = new LaunchTubeImage(Color.MAGENTA);
angle.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent e) {
tubeImage.repaint();
}
});
Case 1: Comment out clearRect in that 1st block of code I posted
http://i58.tinypic.com/2d1l5w2_th.png
Black background as desired. Not rotated yet. Looks good so far.
http://oi60.tinypic.com/1zw1sm.jpg
Rotated it with my JSpinner... you see that the previous location was not removed (and note how my buttons randomly doubled and put themselves at the top of the screen).
Case 2: Keeping in the clearRect method
oi57.tinypic.com/2s84307.jpg
Layout is fine so far, but I wanted the background to be black
oi57.tinypic.com/4rde8x.jpg
Yay! It rotated. But note the weird behavior of that random "15" that appeared in my top right corner
oi58.tinypic.com/vymljm.jpg
And finally... when I resize the window you see that my entire screen was rotated - not just the pink image I wanted to rotate
Tips/fixes/advice? Thanks!! I hope I've provided enough information
(P.s. if you insist on us asking clear/useful questions.... then DON'T limit the number of images you can post... :/ )

The first line of an overridden paintComponent method should usually be super.paintComponent(g). On a JPanel, this will cause the drawing area to be cleared with the background color. If you want to clear the background with a different color, you can do this by manually filling a rectangle (clearRect is discouraged, see the JavaDoc), but of course, this has to be done before applying any transform.
So your method should probably look like this:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(colour);
g.fillRect(0,0,getWidth(),getHeight());
Graphics2D gg = (Graphics2D)g;
double theta = (double)angle.getValue();
theta = Math.toRadians(theta);
gg.rotate(theta,tubeImage.getSize().width/2 + 10,tubeImage.getSize().height - 50);
gg.fillRect(tubeImage.getSize().width/2,tubeImage.getSize().height - 100 , 10, 50);
}

Related

Repaint all components of a JFrame/JPanel

I'm working on some animation where I have a certain number of dots wandering around my JFrame and based on their distance they should be connected by lines of different strengths.
The base code for moving the dots works and actually I also had them displayed correctly in the beggining but I had some issues where the movement was stuttering (probably due to the repaint process). At that point the Window class handled the entire repaint procedure.
After reading some posts around here I adapted my code according to the github page linked in this post to use the individual Dots as JComponents and have them being repainted individually. However, now the problem is that although I still have 100 Dots as components on my JPanel, only one of them is being painted (however, the stuttering is gone at least). I also see that all components are being iterated and their repaint method is being called but they just don't display.
This is my paintComponent method in the Dot class:
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(new Color(0, 0, 0));
Ellipse2D.Double circle = new Ellipse2D.Double(x - 10 / 2, y - 10 / 2, 10, 10);
g2d.fill(circle);
}
And this is what my repaintTimer looks like:
final Timer repaintTimer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for(Component component : window.getContentPane().getComponents()) {
component.repaint();
}
recalculateDots();
}
});
repaintTimer.start();
The result I get is something like this:
I tried some things of which I thought that it could solve the problem but nothing really helped and I'm really confused as to why this is happening. I'd appreciate any help very much because this issue doesn't make any sense for me.

Drawing in Java 2D with fast mousemovement

it's going to be long, so thanks in advance for reading and/or answering :)
Edit: This How to draw a continuous curve of repeated ovals on speedy mouse cursor dragging? has an answer I am not completely fine with, is there no other way of doing it than drawing straight lines in between the rectangles or ovals?? (doesn't matter to me if rectangle or oval).
I have searched a lot and didn't find what I was looking for, so here we go:
I have to create a drawing application for university and I am practically finished, my one problem is nevertheless that the mouseDragged event does not seem to be recognizing movement fast enough(?). That means, if the user draws freehand with a brush and moves the mouse too fast, the programm wont draw a "continous line of rectangles". Instead there will be many gaps in the drawing. This is how my paint() override and mousemotionlistener (for currentTool 1 which is supposed to equate a brush/pen) look like:
public void paint(Graphics g){
g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 1.0f));
Iterator<Color> sc = strokeColours.iterator();
Iterator<Color> fc = fillColours.iterator();
Iterator<Boolean> fill = fillq.iterator();
for (Shape s : shapes){
g2d.setStroke(new BasicStroke(4));
g2d.setPaint(sc.next());
g2d.draw(s);
g2d.setPaint(fc.next());
if (fill.next()){
g2d.fill(s);
}
}
if (temp!=null){
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.5f));
g2d.setPaint(strokeColour);
g2d.draw(temp);
if(isfilled){
g2d.setPaint(fillColour);
g2d.fill(temp);
}
}
}
the motionlistener:
this.addMouseMotionListener(new MouseMotionAdapter(){
public void mouseDragged(MouseEvent e){
mx2=e.getX();
my2=e.getY();
if (currentTool==1){
Shape point= new Rectangle2D.Double(mx2-(0.5*penStroke),my2-(0.5*penStroke),0.5+penStroke,0.5+penStroke);
shapes.add(point);
addColours(strokeColour);
}
else if(...){...
}
repaint();
}
} );
}
NB: the addColours(Color c) funtion is manually defined to add strokeColour to both strokeColours and fillColours as well as adding "true" to fillq. penStroke is defined through the use of a slider and only used for this "tool".
Look at Shape, some of the child classes will do Bezier curves and such. Try CubicCurve2D.Float, QuadCurve2D.Double or such, for the last detected mouse points. Note that not all "control" points of curves lie on the curve itself, but without (much) math one can get good results.

Graphics2D is incapable of alpha?

I am implementing layers in a 2D engine of mine and I would like layers to be stackable, and I would also like a feature to 'cut holes' in layers - this means using alpha. However when writing it standalone I can't seem to get anything to use true alpha, it tried to fake it by blending colours together, for instance (my code):
BufferedImage background, foreground;
public GraphicsTest() {
background = new BufferedImage(500,500,BufferedImage.TYPE_INT_ARGB);
foreground = new BufferedImage(500,500,BufferedImage.TYPE_INT_ARGB); // Fully capable of alpha.
Random r = new Random();
int white = Color.white.getRGB();
// Draw random white dots
for (int i=0; i<500; i++) {
int
x = r.nextInt(500),
y = r.nextInt(500);
background.setRGB(x, y, white);
}
}
#Override
protected void paintComponent(Graphics g) {
BufferedImage output = new BufferedImage(500,500,BufferedImage.TYPE_INT_ARGB); // Fully capable of alpha.
Graphics2D canvas = output.createGraphics();
Graphics2D overlay = foreground.createGraphics();
canvas.drawImage(background, 0, 0, null);
overlay.setColor(Color.white);
overlay.fillRect(0, 0, 500, 500);
// Start drawing with alpha
overlay.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));
overlay.setColor(new Color(0xFF000000)); // Overwrite/draw full alpha, colour doesn't matter when alpha is 100%
overlay.fillRect(100, 100, 125, 87);
//canvas.setColor(Color.red);
//canvas.fillRect(0, 0, 500, 500);
canvas.drawImage(foreground, 0, 0, null);
overlay.dispose();
canvas.dispose();
g.drawImage(output, 0, 0, null);
// Also write to a file for manual raw pixel checking
try {
// Does output a 32-bit depth image
ImageIO.write(output, "PNG", new File("c:/output.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
This is the most basic test I could think of, I have two images, one for the background and one for the foreground, I am needing to see the background through the foreground in the 127x87 (random sized) box.
Currently it results in a white screen with a black box, the back box should be that part of the background.
I have tried all sorts of methods here, settings the composite and drawing black with full alpha but when combined I never see the background, the setColor(0xFF000000) doesn't seem to be doing anything but drawing black and the composite is the culprit of this 'faking', instead of overwriting FF000000 (black) on a 00FFFFFF (white with no alpha) background yielding FF000000 (what I set it to) it is instead 007F7F7F (grey with no alpha).
The only reason I can see for this is the fact that everything is going through a Graphics2D object, I cannot use output.setRGB as it is slow and I wouldn't know how to draw custom shapes with it.
You need to change 3 things for your code to behave properly.
As #vandale said, you need to use a color with actual alpha with the boolean constructor.
Second, the alpha is the opacity (so it should be 0 for transparent color). You get:
overlay.setColor(new Color(0x0000000, true));
Third, you need to say that your paint operation will actually override whatever was already there (you don't want to draw on top (ATOP) of the existing but rather replace with the transparent color (IN) what was there). You get:
overlay.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1f));

Jlabel with Image Fade Out Strange Effect

Hope this question could emphasize more about the fading out effect of Jlabel (swing).
Certainly, yes... I already follow some guide and some answers given from This Link Thread, but mine is quite a bit different. It's not just only A text inside the JLabel, there's an image i put on.
I proceed to follow on the Thread Located out of stackoverflow. And yet, it gives me a fade out effect. But there's horrible thing occured; the white background.
How to solve this out?
I share the interface here...
The First screenshot taken here is the earlier phase when the fade out have not occured yet. While,
The Second screenshot taken here is the unwanted result i mentioned.
Tobe honest, I used the Trident Library to do animatiing;
So, whenever the user click over the image it will execute this code;
Timeline tm = new Timeline(jll_btnProceed);
tm.addPropertyToInterpolate("intensity", 1.0f, 0.0f);
tm.setDuration(1000);
tm.play();
and... the JLabel itself, I used to override it using this source code;
/**
*
* #author Gumuruh
*/
public class JLabelFader extends JLabel {
private float intensity = 1.0f;
public void setIntensity(float intensity) {
this.intensity = intensity;
this.setOpaque(false);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
final Composite oldComposite = g2.getComposite();
g2.setComposite(AlphaComposite.SrcOver);
final Color c = getBackground();
final Color color = new Color(c.getRed(), c.getGreen(), c.getBlue(), (int) (255 * (1.0f - intensity)));
g2.setColor(color);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setComposite(oldComposite);
}
}
My hand and my head can't stop for making this trully solved. Thus I tried to follow up some example from the Java Filthy Rich Client ebook and then applying the source code given below, but first I need to COMMENT the protected void paint(Graphic g) method written above, and simply adding this source code;
private BufferedImage buttonImage = null;
public void paint(Graphics g) {
// Create an image for the button graphics if necessary
if (buttonImage == null || buttonImage.getWidth() != getWidth()
|| buttonImage.getHeight() != getHeight()) {
buttonImage = getGraphicsConfiguration().
createCompatibleImage(getWidth(), getHeight());
}
Graphics gButton = buttonImage.getGraphics();
gButton.setClip(g.getClip());
// Have the superclass render the button for us
super.paint(gButton);
// Make the graphics object sent to this paint() method translucent
Graphics2D g2d = (Graphics2D) g;
AlphaComposite newComposite =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, intensity);
g2d.setComposite(newComposite);
// Copy the button's image to the destination graphics, translucently
g2d.drawImage(buttonImage, 0, 0, null);
}
in which at the end... giving me nice fade out effect. But At first, it gave me the 2nd horrible effect which is BLACK BACKGROUND rendered first. Can't believe me?? Okay, Here is First screen shot AFTER applying code from ebook. and here is the nice fade out effect result.
If there's somebody telling me;
"YOUR PNG IS NOT TRANSPARENT!".
Please, dont say like that. Because, I followed the creation of PNG inside the Photoshop nicely using this Tut.
Now, My head's spinned, but my heart laughed and can't handle it over. OMG. Geeezzz....!
And the New Stories begun...
* UPDATED FROM HERE AND BELOW *
Ehm, depply thank you very much to our friend called... MKorbel,
from his thread given at this link. Providing a clear example of fading out effect the Swing JButton and I tried to implement it into my JLabel, and violaaa...!!
IT works.
Let's give a big clap for MKorbel. :D
SO anyway, how could I fix the earlier code? Pretty simple, just COMMENT the Paint() method, and use again the paintComponent() method and it should be overriden with the new source code below;
#Override
public void paintComponent(java.awt.Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, intensity));
if (rectangularLAF && isBackgroundSet()) {
Color c = getBackground();
g2.setColor(c);
g.fillRect(0, 0, getWidth(), getHeight());
}
super.paintComponent(g2);
g2.dispose();
}
Now the JLabel become easy to be changed with its intensity -variable.
Fading out and Fading in is accomplished.
Okay. Now everything seems "OKAY". BUt hold a moment, there's something strangely occured again here. Have you noticed it? Hmmm....
I tried to give a Mouse Event (Hover On) into the JLabel that we override the paintComponent() method discussed earlier.With this line of code;
myJLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
Logically, it should change the Cursor when Hover ON. But, Here comes another strange effect. (Really sorry, but this is stil the continue of the main case). The strange effect now is the Mouse Cursor can't be changed when we Hover On the Jlabel. It still can't change the Cursor. Seems the paintComponent() method effecting the way Cursor react. Is that true?

How can I repaint efficiently when using big custom component in Swing?

I have made a custom component (derived from JComponent) which represents
a draggable Bezier-curve.
(looks like a hanging cable, someone might know it
from Bender or Cubase)
My problem is: The curve may become really long,
let's say from top left to bottom right corners of the desktop.
This makes Swing's repaint functionality inefficient:
The area of the curve is perhaps few hundred pixels, but the area of
the component (being mostly 'transparent') is millions of pixels big.
My subjection impression is:
The longer the curve, the more flicker I get when dragging it.
I hope I made myself clear about the problem.
Perhaps it would help when I somehow could choose by myself, which regions
of the component needs repainting at all.
EDIT:
Such a mess! I'm profiling the application using Netbeans, which helps to
find inefficient code normally, but this Swing framework is making hundreds
of nested calls! I just can't figure out, what is slow and why.
By the way, disabling super.paint(...) or super.paintComponent(...) doesn't help.
Check out Filthy Rich Clients by Chet Haase and Romain Guy. They address these very optimizations among others along the way to producing responsive and graphically impressive UI.
Doing all of your bezier mathematics on the paint thread everytime the component is refreshed is (as you've gathered) a bad idea. Does your curve change often? If not then why not paint it to a BufferedImage as and when it changes, and change your paint() code to simply draw the buffered image to the component instead.
class CurveComponent extends JComponent {
private BufferedImage image;
#Override
public void paintComponent( Graphics g ) {
if ( image == null ) {
return;
}
g.drawImage( image, 0, 0, this );
}
private void updateCurve() {
image = new BufferedImage( getWidth(), getHeight(), BufferedImage.ARGB );
Graphics g = image.getGraphics();
// draw the curve onto image using g.
g.dispose();
}
}
Only call updateCurve() when you need to and all that expensive mathematics won't be needlessly repeated. Painting should be pretty responsive, even for a fullscreen window. drawImage() will be doing a straightforward memory copy and should be lightning fast.
Try writing a tiny test app, which consists of nothing except what you need to reproduce this problem. This will make profiling easier. Then post that app here, so we can take a look at possible solutions.
I found your question interesting so I wrote a test app myself. This draws a Bezier curve which is continually resized as you drag. I created a gradient background to ensure this works well with a nasty background. I get good performance and low flicker, although I use top-notch machine.
It pays to read "Filthy Rich Clients" to learn all the tricks of writing custom Swing components that perform really well.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Point2D;
public class CustomComponent extends JComponent {
private Point2D start = new Point2D.Double(0, 0);
private Point2D end = new Point2D.Double(300, 200);
private CustomComponent() {
this.setOpaque(true);
final MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
setEnd(e.getPoint());
}
};
this.addMouseListener(mouseAdapter);
this.addMouseMotionListener(mouseAdapter);
}
public void setStart(Point2D start) {
this.start = start;
repaint();
}
public void setEnd(Point2D end) {
this.end = end;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
final Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// draw gradient background
final int width = getWidth();
final int height = getHeight();
g2.setPaint(new GradientPaint(0, 0, Color.WHITE, width, height, Color.YELLOW));
g2.fillRect(0, 0, width, height);
// draw Bezier curve
final Shape shape = new CubicCurve2D.Double(start.getX(), start.getY(), start.getX(), end.getY(), end.getX(), start.getY(), end.getX(), end.getY());
g2.setColor(Color.BLACK);
g2.draw(shape);
g2.drawString("Click and drag to test for flickering", 100, 20);
}
public static void main(String[] args) {
final CustomComponent component = new CustomComponent();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final Dimension size = new Dimension(screenSize.width - 20, screenSize.height - 100);
component.setPreferredSize(size);
final JFrame frame = new JFrame();
frame.add(component);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Some things to note:
only overwrite paintComponent(Graphics g), not the other paintXXX() methods
set custom component to opaque if possible
only use repaint() to request repainting. Never directly order a repaint directly in your code. This lets Swing handle it well.
There is no efficient way to create lots of small clip rectangles for a diagonal structure which leaves you with two strategies to avoid flickering:
Double buffering. This needs an enormous amount of memory but the memory copy is very fast (it usually happens in the time the "electron beam" goes back from lower right to upper left ... if there was still a beam in your LCD).
Don't call super.paint() (which draws or "erases" the background) and draw the curve a second time with the background color to erase it.
For more details, see this document.
[EDIT] If fillRect() wasn't abstract, you could set a break point :) Set a break point in paint(), check who calls it and whether the background got cleared at that time. It should be since rendering would be completely wrong. Then set break points further up in the call chain.
You can redraw a smaller portion of the screen using repaint(Rectangle r)
http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#repaint(java.awt.Rectangle)
Then you mention flicker. Since you are using swing, which uses double buffering your flickering must be coming from something else. Are you clearing the screen in paintComponent(...)? I.e. call to fillRect(...)? Don't do that, it's not needed (IIRC).
Which method do yo use to paint your curve? paint or paintComponent?
My solution was a partial re-design:
Now I don't represent each "cable"-element by a component.
Now, cables are just dummy objects (with no involved JComponent).
The repaint takes place "globally", on the content pane of the parent JFrame.
Now it's efficient, and flickers less.
just use getVisibleRect(); inside paintComponent(Graphics g) to get the area you actually need to redraw

Categories