Retrieving drawn Rectangle2D object and read it's properties - java

I am drawing a rectangle from method DrawRect using swings like this -
Graphics2D graph2 = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double(100, 100, 100, 100);
graph2.draw(rect);
In other method getDrawnRect, I want to get that object of drawn rectangle and read its properties.
I mean, is there any method available that returns objects drawn (like rectangles,circles) or any other way to get those objects indirectly.

Instead of the local variable Rectangle2D rect define a field of the class. During drawing store the rect to the field and then your getDrawnRect() method should return the stored rectangle field

Related

Font face value returns unexpected value ("Dialog") from image

This code snippet creates an image which contains text. I set the font to Serif. However, when I query the resulting image later on for its font face, it returns Dialog. I don't understand why this is.
BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(img, 0, 0, 200, 100, this); // "this" refers to my custom JPanel which I am setting in the JFrame.
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
System.out.println("from g2d object: " + g2d.getFont().getFamily()); // outputs "Serif"
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
g2d.dispose();
System.out.println("from image: " + img.getGraphics().getFont().getFamily()); // outputs "Dialog" (expected "Serif")
I understand that Dialog is one of the logical fonts in Java, but if the font is set to be something else, and Font.getFontName() returns the font face for the given font, why isn't it returning Serif as set in the Graphics2D object?
UPDATE: Calling g2d.dispose() before or after the last System.out.println() makes no difference. Both ways, it still prints out Dialog.
BufferedImage.getGraphics() returns the result of BufferedImage.createGraphics(). And, following the trail, we end up at that method of SunGraphicsEnvironment:
/**
* Returns a Graphics2D object for rendering into the
* given BufferedImage.
* #throws NullPointerException if BufferedImage argument is null
*/
public Graphics2D createGraphics(BufferedImage img) {
if (img == null) {
throw new NullPointerException("BufferedImage cannot be null");
}
SurfaceData sd = SurfaceData.getPrimarySurfaceData(img);
return new SunGraphics2D(sd, Color.white, Color.black, defaultFont);
}
We can clearly see that it uses the defaultFont, hardly connected to the image (unless getPrimarySurface() does change the defaultFont - I guess not [I could not find it being changed]).
Source code can be found here
Setting the Font of a Graphics will not change anything in a BufferedImage. The Font is used by the Graphics to draw the text onto the image. If a new Graphics is obtained from the image (using getGraphics() or createGraphics()), it will have the defaultFont as defined in the GraphicsEnvironment.
Do this:
BufferedImage img = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(img, 0, 0, 200, 100, null); // "this" refers to my custom JPanel which I am setting in the JFrame.
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
System.out.println("from g2d object: " + g2d.getFont().getFamily()); // outputs "Serif"
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
System.out.println(g2d.getFont().toString()+"-"+img.getGraphics().getFont().toString()+" from image: " + img.getGraphics().getFont().getFamily());
g2d.dispose();
System.out.println("from image: " + img.getGraphics().getFont().getFamily());
As you can see g2d retains its beloved Serif notation. The image is a thing of itself. Each time you call createGraphics on the image you get a new thing.
Everyone is making this more complicated than it needs to be. The answer is quite simple: the getGraphics() method of Image always creates a brand new Graphics object. From the documentation:
Creates a graphics context for drawing to an off-screen image.
That’s all there is to it. You set the font in one Graphics object, then you created another Graphics object. Of course the new Graphics object doesn’t know anything about the state of the first Graphics object. That would be like expecting that turning on one car’s headlights could somehow cause all cars to turn on their headlights.
You can get the expected result by only calling getGraphics() once.
System.out.println("from image: " + g2d.getFont().getFamily());
TL:DR The font is not accessible in Graphics or Graphics2D.
Whether you invoke BufferedImage#createGraphics() directly or BufferedImage#getGraphics() the end result is the same: they both create a new graphics object. More specifically, the former returns a new Graphics object and the latter returns a Graphics2D object. Both are abstractions of the concrete type returned which is an instance of SunGraphics2D. Unfortunately, this object does not store any information set, related to the font, in the graphics object. So basically, I cannot get the data I need from the image object.
In contrast, ProxyGraphics2D and PeekGraphics - both subtypes of the abstract class Graphics2D class do store such information in a Graphics2D global variable called mGraphics. This object is accessed via the getDelegate() method, which is obviously not part of the Graphics or Graphics2D API. Unfortunately, both of these subclasses are part of the sun package which is no longer accessible. Examining these classes clearly shows that methods like setFont() do save the font in this delegate (Graphics2D) object.

Can't save Stroke/Line Thickness when redrawing Shapes in paintComponent

I've been working on a simple paint program and I have recently run into a snag when redrawing shapes in my paint component.
#Override
public void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
if (!preDrawnShapes.isEmpty() && !preDrawnShapeThickness.isEmpty()) {
for (int i = 0; i < preDrawnShapes.size(); i++) {
g2d.setStroke(new BasicStroke(preDrawnShapeThickness.get(i)));
g2d.draw(preDrawnShapes.get(i));
}
}
g2d.setStroke(new BasicStroke(currentThickness));
if (myShape != null) {
g2d.draw(myShape);
preDrawnShapeThickness.add(currentThickness);
}
}
This paintComponenent is supposed to first redraw the shapes previously drawn before before then drawing the new shape created from user input.
For some reason, the shape thickness when drawing the new shape is set to the current thickness but any shape that I drew before had a default thickness of 1.
preDrawnShapes is a Shapes ArrayList and preDrawnShapeThickiness is a Float Arraylist.
Am I missing something here?
UPDATE: I've discovered that PreDrawnShapeThickness stores only Floats of zero. I am unsure why.
There are two common ways to do incremental painting:
Keep a List of Objects that you want to paint. Then the paintComponent() method iterates through the List and paints each object. So in your case the custom object would contain the Shape you want to paint and an int value for the thickness of the shape.
Just paint each Object directly to a BufferedImage. Then you can just use a JLabel to display the BufferedImage as an Icon, or do custom painting to paint the BufferedImage.
Check out Custom Painting Approaches for examples of both approaches.

Fill a shape with a random color in java

In this program I am doing, I need to be able to fill shapes with random colors. I am confused on how to actually fill in the shape. I am able to generate a random color. I have looked around online and found some sources talk about implementing the Paint interface and use the method setPaint() on the shape you are wishing to draw and then invoke the fill method. I tried this but was unsuccessful. Maybe I just had it wrong. Here is what I had.
Random rand = new Random();
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
Color randomColor = new Color (r, g, b);
This was in a constructor of a superclass where I actually have randomColor as an attribute of the class. So to access that element in the subclass, I provide a basic getter that just returns the Color.
In the subclass I have this:
Rectangle2D.Double rectangle = new Rectangle2D.Double(getX(), getY(), getWidth(), height);
rectangle.setPaint(getColor());
rectangle.fill();
g2.draw(rectangle);
The error I am getting is something about typecasting rectangle but any typecast I try it's not working. I'm not exactly sure how to fix this issue. Any ideas? Or is there an easier/better way to fill a shape with a random color? Thanks.
You should be calling the methods you are calling on the rectangle on the Graphics2D
Rectangle2D.Double rectangle = new Rectangle2D.Double(getX(), getY(), getWidth(), height);
g2.setPaint(getColor());
g2.fill(rectangle);
g2.draw(rectangle);
I need to be able to fill shapes with random colors
Then you should create a class (ColoredShape) that contains two properties:
Shape
Color
Then you can create an ArrayList to hold instances of this class.
ArrayList<ColoredShape> shapes = new ArrayList<ColoredShape>();
shapes.add( new ColoredShape(aShape, generateRandomColor());
shapes.add( new ColoredShape(anotherShape, generateRandomColor());
Then in the paintComponent() method of your panel you iterate through the ArrayList and paint all the shapes:
for (ColoredShape shape: shapes)
{
g2.setColor(shape.getColor());
g2.draw(shape.getShape());
}
For a working example of this approach check out the DrawOnComponent example found in Custom Painting Approaches.

Improper formatting with fillRect

I want to make a background for a game I have created.
I'm having some issues with fillRect.
Does getHeight and getWidth have to be in a certain order or should getX/Y/Height/Width be used at all?
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//Graphical loop start
Rectangle rect = new Rectangle(0,0,1440,900) ;
g.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
//Graphical loop end
}
Firstly, you are using the Graphics object instead of Graphics2D. You need to set the paint colour first then use the fill method.
g2d.setPaint(Color.BLUE);
g2d.fill(new Rectangle2D.Double(0, 0, screenWidth, screenHeight));
Rectangle2D constructor arguments are: x-coordinate of top-left, y-coordinate of top-left, width, height
It is good practice to use the Graphics2D.fill() method which will accept any object that implements the Shape interface. This makes it easier to change a shape to a different one should you decide to do so.

Using Java's Graphics or Graphics2D classes, how do I paint a String?

I have a String and I want to paint it onto an image. I am able to paint points and draw lines, however, even after reading the Text part of the 2D Graphics tutorial, I can't figure out how I can take a String and paint it onto my drawing.
Unless I'm looking at the wrong tutorial (but it's the one I get whenever I search for anything about Java and painting Strings using Graphics or Graphics2D), I'm still stumped.
Check out the following method.
g.drawString();
The drawString() method will do what you need.
An example use:
protected void paintComponent(Graphics g){
g.setColor(Color.BLACK);
g.drawString(5, 40, "Hello World!");
}
Just remember, the coordinates are regarding the bottom left of the String you are drawing.
if you want to play with the shape of your string (eg: fill:red and stroke:blue):
Graphics2D yourGraphicsContext=(...);
Font f= new Font("Dialog",Font.PLAIN,14);
FontRenderContext frc = yourGraphicsContext.getFontRenderContext();
TextLayout tl = new TextLayout(e.getTextContent(), f, frc);
Shape shape= tl.getOutline(null);
//here, you can move your shape with AffineTransform (...)
yourGraphicsContext.setColor(Color.RED);
yourGraphicsContext.fill(shape);
yourGraphicsContext.setColor(Color.BLUE);
yourGraphicsContext.draw(shape);

Categories