From the following code, we can get the bounding rectangle of our text:
Graphics2D twoD = (Graphics2D) g;
FontRenderContext frc = twoD.getFontRenderContext();
Rectangle2D textBound = myFont.getStringBounds(myText, frc);
but in core java textbook, it says that
the rectangle has its origin at the
baseline of the string, and the top
y-coordinate of the rectangle is
negative.
What is that mean, why the top y-coordinate is negative?
And any more clarification to this concept is highly appreciated?
Thanks
When you measure text, there is no concept of where you are drawing it. So they make the somewhat arbitrary, but i suppose sensible decision that the point (0, 0) is at the baseline of the text, to the left of the first character.
This is good because if you were to say
void drawSomeText(Graphics g, String sample) {
g.drawString(sample, 0, 0);
}
it would fit in the box that measure text told you about.
Given that the ascent of the text goes more and more negative, while the descenders occupy positive y space.
Related
It appears that the contains() method in Rectangle is not inclusive to the bottom right corner.
For example the following code returns "false";
Rectangle r = new Rectangle(0,0,100,100);
System.out.println(r.contains(100, 100));
As quoted from the Rectangle API (Java 8):
public Rectangle(int x,
int y,
int width,
int height) Constructs a new Rectangle whose upper-left corner is specified as (x,y) and whose width and height are
specified by the arguments of the same name.
Using Width and Height with the starting Point of (0,0) means the Rectangle has points from (0,0) to (99,99) - 100 pixels of width and 100 pixels of height, based on the given starting pixel of (0,0) which is always included in the Rectangle.
This means that (100,100) will indeed not be included in the constructed Rectangle. Based on the logic above, (100,100) will be contained in the following (verified using an online java compiler):
Rectangle r = new Rectangle(1,1,100,100);
References:
The Rectangle API
It seems that the API wrongly states that the "upper left corner" is (x,y) when according to the accepted answer and my own experience, (x,y) is the lower left corner.
I've read all the existing questions I could find and have tried it both ways.
FontMetrics fm = g.getFontMetrics();
FontRenderContext frc = g.getFontRenderContext();
Rectangle2D rect = font.getStringBounds(line,frc);
int width = (int)rect.getWidth();
and also
int width = fm.stringWidth(line);
Neither are giving me the correct number of pixels.
As an example...the word 'ELLIS' in a particular font and size is actually 58 pixels wide.
Both of those methods tell me that it's 42 wide.
Its black font on a white field, so I'm considering re arranging my entire code so I can loop through a line of the BufferedImage and count the distance between the first and last black pixel. This would at least get me a lot closer.
There has to be a simpler way to do this though.
Appreciate any help.
I'm making a game with a mouse cursor, and I'd like to represent the health by overlaying the cursor with a green version of the image, but only a geometric sector of it corresponding to the health percentage. Solutions from posts like these: Drawing slices of a circle in java? & How to draw portions of circles based on percentages in Graphics2D? are pretty much what I want to do, but with a BufferedImage as opposed to a solid color fill.
//Unfortunately all this does is cause nothing to draw, but commenting this out allows the overlay image to draw
Arc2D.Double clip = new Arc2D.Double(Arc2D.PIE);
double healthAngle = Math.toRadians((((Double)data.get("health")).doubleValue() * 360.0 / 100.0) - 270.0);
clip.setAngles(0, -1, Math.cos(healthAngle), Math.sin(healthAngle));
System.out.println(Math.cos(healthAngle) + " " + Math.sin(healthAngle));
g.setClip(clip);
In short, how do I draw a sector of a BufferedImage given any angle?
If you read the API docs for setClip(Shape) you'll see that the only shape that is guaranteed to work, is a rectangle. So, setting the clip probably won't work.
However, there are other options. The most obvious is probably to use a TexturePaint to fill your arc with the BufferedImage. Something like:
TexturePaint healthTexture = new TexturePaint(healthImg, new Rectangle(x, y, w, h));
g.setPaint(healthTexture);
g.fill(arc); // "arc" is same as you used for "clip" above
Another option is to first draw the arc in solid color, over a transparent background, then paint the image over that, using the SRC_IN Porter-Duff mode. Something like:
g.setPaint(Color.WHITE);
g.fill(arc); // arc is same as your clip
g.setComposite(AlphaComposite.SrcIn); // (default is SrcOver)
g.drawImage(x, y, healthImg, null);
The first "foo" is normal, but the second one is so huge I can only see the base of the "f". The font-size is the default.
BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
Shape ellipse = new Ellipse2D.Double(0, 0, 1.0, 1.0);
g2.setPaint(Color.RED);
g2.drawString("foo", 100, 100);
g2.scale(500.0f / length, 500.0f / length);
g2.drawString("foo", 1, 1);
Changing the font size will not help because it only allows int sizes and the size that would make sense for the scale is something like 0.02.
The reason I need to draw the text while in scaled space is because I am drawing a grid of nodes and I want to scale the coordinates to the number of nodes in each dimension. That way I do not have to do complicated calculations.
I need the text to label the edges.
Update: I am able to get the text by doing the following sequence each time I want to draw the text: saving the transform, translating to the desired location in scaled space, unscaling, drawing the text at (0, 0), and restoring the transform.
You can use Font method
public Font deriveFont(float size)
to obtain desired font size font. Guess the 0.02 shoud be fine.
Basically, I want to draw a circle using Graphics, but instead of using integers to position it, I would like to use double values instead.
Ideally:
g.drawOval(0.5, 0.5, 50, 50);
Any help is greatly appreciated! Thanks in advance!
Thanks for all the help guys, but ive figured a way out, my dy variable was set at 1, so if i wanted dx to half of that of dy, it would be impossible, instead i just changed dy to 2 and dx to 1! Foolish me!
The only valid way of doing this is to use an Ellipse2D.Double shape and pass it to the draw(Shape) method of a Graphics2D instance. For the best results, enable anti-aliasing:
public void yourDrawingMethod(Graphics gg)
{
/* Cast it to Graphics2D */
Graphics2D g = (Graphics2D) gg;
/* Enable anti-aliasing and pure stroke */
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
/* Construct a shape and draw it */
Ellipse2D.Double shape = new Ellipse2D.Double(0.5, 0.5, 50, 50);
g.draw(shape);
}
You can use Graphics2D.fill(Shape) as well.
If you want continous movement, then the best way is to keep x and y in double variables. As the object moves, the variables will increment at different rates (depending on the angle), and then you can just cast to ints...
And you will get positions:
<0, 0.999> : 0
<1, 1.999> : 1
<2, 2.999> : 2
<3, 3.999> : 3
etc..
For sufficiently long movement it will seem fluent. But the positions must be integer values, because you are bound by technology. You cannot display something at 0.3 pixels. You either have the pixel or not, you cannot have only 0.3 of it.