Proper way to use Graphics object in overriden paint methods - java

I've been looking for a explicit answer for this but I can't find one anywhere.
A Graphics object is always passed into the java paint methods examples:
paintComponent(Graphics c)
paintIcon(Component c, Graphics g, int x, int y)
Often these paint methods are overriden by subclassses and changes need to be applied to the Graphics object, examples: setColor() or fillRect(). These changes can either be applied to the passed in Graphics object, or a new one can be created using g.create().
I read in another SO answer that you should call g.create() anytime you are making any changes that are "not easily undone", but the article was not clear on which changes are "not easily undone" (I also can no longer find this article for reference).
I know that transposing or translating are situations where you should create a new graphics object. But what about simpler actions like g.setColor(...) or g.fillRect(...)?
My Question:
When should you call g.create() to use for your Graphics object
and what situations should you use the one passed into the method?
Is there any downside to creating a new Graphics object?
Example
To try to narrow this question down, I'll give an example. For the following situation, would I need to create a new graphics object?
private Icon delegate;
#Override
public void paintIcon(Component c, Graphics g, int x, int y)
{
delegate.paintIcon(c, g, x, y);
g.setColor(Color.gray);
int width = getIconWidth() - 4;
int height = getIconHeight() - 4;
g.fillRect(x + 2, y + 2, width, height);
}

Related

Java, create and edit a Graphics Object for Panel?

I have the following task for my school in Java:
Create a GUI window with your own graphic. This graphic should be created in a separate JPanel class and drawn using the draw and fill methods of the java.awt.Graphics class (e.g. a house with a garden, a car, ...). The graphic should contain at least 5 different types of graphics (rectangle, oval, ...), at least one polygon (draw or fillPolygon (polygon p) method) and an arc (draw or fillArc method (int x, int y, int width, int height, int startAngle, int arcAngle)). The graphic should also contain at least 10 drawing elements and consist of at least 4 different colors.
But I don´t know how to use the class Graphics, so I don´t know how to create a Grahpics object and edit it. Does anyone know how to solve this? Thank you
You can use graphics with a JPanel;
class exampleclass extends JPanel {
exampleClass() {
...
}
#Override
public void paintComponent(Graphics g) {
...your code here...
}
}
For more information, look at; https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html
You can call paint method with, repaint();

Difference between setBounds(x,y,width,height) and setBounds(new Rectangle(x,y,width,height))

Is it possible that when we use Java's setBounds(x,y,width,height) method, the component can change from place to place if we change the machine(monitor of different sizes or any multiple size display device) and setBounds(new Rectangle(x,y,width,height)) method doesn't suffer from the same problem.
If what I am saying is correct, can somebody help to understand this?
This is implementation of the second method:
public void setBounds(Rectangle r) {
setBounds(r.x, r.y, r.width, r.height);
}
So to answer your question: They're identical.
Difference between setBounds(x,y,width,height) and setBounds(new Rectangle(x,y,width,height))
There is no difference in terms of what the methods does. They are just overloaded methods which accept different arguments but performing the same task.
If you check the Java docs:
public void setBounds(Rectangle r)
Sets the bounding Rectangle of this Rectangle to match the specified Rectangle.
This method is included for completeness, to parallel the setBounds method of Component.
public void setBounds(int x,
int y,
int width,
int height)
Sets the bounding Rectangle of this Rectangle to the specified x, y, width, and height.
This method is included for completeness, to parallel the setBounds method of Component.

Painting on a JPanel from a different class in Swing

I am currently working on a test driven interactive 2D fluid dynamics simulation for my Bachelor thesis. The basic idea is that the user can draw shapes on his screen and the program will simulate how a fluid will flow around these shapes.
So far I have only started with the painting process and I have already run into a little problem.
First, here is a little UML diagram that shows my project structure so far
As you can see, I have created a Painter class that can visit several shapes and will call one of the abstract paint methods depending on the shape type. Then there is the SwingPainter class which inherits from the Painter class and implements the three paint methods as well as the clear method.
Here is the code for my SwingPainter:
public class SwingPainter extends Painter {
private final GraphicPanel graphicPanel;
public SwingPainter(GraphicPanel graphicPanel) {
this.graphicPanel = graphicPanel;
}
private Graphics getGraphics() {
return graphicPanel.getGraphics();
}
#Override
protected void paintLine(Point start, Point end) {
getGraphics().drawLine(start.getX(), start.getY(), end.getX(), end.getY());
}
#Override
protected void paintRectangle(Point start, Point end) {
int minX = start.getX() < end.getX() ? start.getX() : end.getX();
int minY = start.getY() < end.getY() ? start.getY() : end.getY();
int width = Math.abs(start.getX() - end.getX());
int height = Math.abs(start.getY() - end.getY());
getGraphics().drawRect(minX, minY, width, height);
}
#Override
protected void paintCircle(Point center, double radius) {
int minX = center.getX() - (int) radius;
int minY = center.getY() - (int) radius;
int diameter = (int) radius * 2;
getGraphics().drawOval(minX, minY, diameter, diameter);
}
#Override
public void clear() {
graphicPanel.paintComponent(getGraphics());
}
}
So my problem is that the GraphicPanelPresenter(see UML diagram) is responsible for passing the Painter/Visitor to the shapes when the user left clicks or moves the mouse(the second point of a line will follow the cursor for example). That means the actual painting is done outside of Swing's paint method. As a result I have encountered some flickering while painting and I was wondering if anyone knew how to fix that without throwing the whole painter/visitor functionality out the window(since there are other features concerning the shapes that still have to be implemented and could easily be handled by simply passing them another type of visitor).
Well, that was a rather lengthy description for a rather small problem, sorry for the wall of text, but I would be glad for any kind of hint!
You need to do all the of painting in the various Swing paint methods that are designed to be overridden for custom painting (such as paintComponent). Only Swing will know when it's done drawing, or when a new frame needs to be drawn to.
What it really seems like you need to do, is to have your GraphicsPanel have a reference to the object that holds your shape's states, and then draw the graphics that represent that state in paintComponent.
This should also simplify your class relationship diagram as well, since within GraphicsPanel you can call the state object's methods from your listeners to change the state (I would choose different names to make the state object not aware that the changes are based on UI interactions, so if right click rotates, make the method called rotate instead of handleRightClick).

Java fillRect() Inconsistencies

Suspicious fillRect() Speed
Okay so let me get this straight. Java fills rectangles by iterating through an array and changing the rgb values to a designated color. If all it does is change the color then why is Texturepaint so expensive if all it is doing is changing the integer in the array? Does changing the integer in between take time to register?
Fast fillRect() operation using setPaint(new Color());
setPaint(new Color(0,0,0));
fillRect(0,0,frame.getWidth(),frame.getHeight());
// Around 100+ fps repainting with timer set to zero milliseconds.
Slow fillRect() operation using setPaint(new TexturePaint());
setPaint(new TexturePaint(image, rectangle));
fillRect(0,0,frame.getWidth(),frame.getHeight());
// Around 20+ fps repainting with timer set to zero milliseconds.
As you can see from its sourcecode, Graphics delegates this functionality to subclasses.
My implementation seems to use SunGraphics2d, which again delegates it to a PixelFillPipe, which there are many implementations of. The OGLRenderer delegates this functionality to the Graphics card if possible, using OpenGL. The X11Renderer uses native X calls, like this:
native void XFillRect(long pXSData, long xgc,
int x, int y, int w, int h);
public void fillRect(SunGraphics2D sg2d,
int x, int y, int width, int height)
{
SunToolkit.awtLock();
try {
long xgc = validate(sg2d);
XFillRect(sg2d.surfaceData.getNativeOps(), xgc,
x+sg2d.transX, y+sg2d.transY, width, height);
} finally {
SunToolkit.awtUnlock();
}
}
XRRenderer uses this code:
public synchronized void fillRect(SunGraphics2D sg2d,
int x, int y, int width, int height) {
SunToolkit.awtLock();
try {
validateSurface(sg2d);
XRSurfaceData xrsd = (XRSurfaceData) sg2d.surfaceData;
x += sg2d.transform.getTranslateX();
y += sg2d.transform.getTranslateY();
tileManager.addRect(x, y, width, height);
tileManager.fillMask(xrsd);
} finally {
SunToolkit.awtUnlock();
}
}
I showed you this code, because it is more than setting colors in an array. Your mileage will vary per platform and JRE.
As I don't know which renderer/fillpipe you use, I can only recommend to look at your very own code, it's not that hard.

Moving a rectangle in Swing JPanel: original stays

I'm trying to make a tower of hanoi solver which simply solves the hanoi without any mouse events. The problem is when I move the rectangle the original remains, even after I repaint. I've searched the net and tried changing the code around but nonthing worked. I am using a JFrame with a JPanel inside of it if that changes anything.
I have my disk class here which is just a rectangle with colour.
class Disk extends Rectangle {
Color diskColour;
public Disk(int a, int b, int c, int d, Color colour) {
x = a;
y = b;
width = c;
height = d;
diskColour = colour;
}
public Color getColour() {
return diskColour;
}
public void paintSquare(Graphics g) {
repaint();
g.setColor(diskColour);
g.fillRect(x, y, width, height);
repaint();
}
}
Here is my code where I actually call the paintSquare method:
public void simpleMoveDisk(Disk[] disks, int n, Graphics g) {
disks[n].setLocation(30,25);
disks[n].paintSquare(g);
repaint();
}
The paintSquare method paints the disk, while the setLocation method changes its coordinates.
When this runs the rectangle occurs in the new location, however the old one still remains. Any help is appreciated, thanks in advance.
You are calling repaint() in several places and you shouldn't be.
Have your the top level class that is doing the painting, call the paintSquare method and any other method that is needed. Those methods should not be calling repaint().
Also your simple move disk is really strange in the fact that it passes an array of Disks, an index, and a graphics object. Instead make it just take in a Disk. Just pass it the one out of the array that is needed to be updated. Then let whatever class that calls simpleMoveDisk, separately make a call to repaint instead of trying to paint and update the model in the same method.

Categories

Resources