G'day, I have JPanel with some Line2D objects on it. Issue is when I draw this line it doesn't appear as I want them to. Lines are not smooth, It's hard to explain in word so I am posting an Image,
Zoomed Area,
How to make them look more polished rather than wrinkly.
Thanks
The problem is likely that you don't have antialiasing turned on your Graphics context. Try the following line before you draw:
graphics.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
(where, of course, graphics is your Graphics2D instance).
Later on when you discover that the text you're drawing is also ugly and jagged, you'll want to use
graphics.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
However, text is a little more complicated; there's several values for this hint that you can use depending on (among other things) the type of screen you're drawing the text to. You should read the RenderingHints.KEY_TEXT_ANTIALIASING API doc for those details.
Related
I'm making an online multiplayer game (Nothing too complex, just a top-down tank shooting game) on a Java Canvas. You would think I'm here to ask about networking stuff, sockets, etc, but I'm much farther along than that, and I've come across something strange.
Basically, there are a few strings that I'm drawing on the canvas with a bold font. When I run it from NetBeans (Not building, just pressing F6/The green run button), the text shows up bold as it should. But, when I build the project, and run the JAR file, the text is not bold.
Note:
I tried updating Java, as I was quite a few updates behind. Didn't fix it.
Here is a code snippet to show that I am drawing the bold text correctly, but I don't know what else I would need to show for this kind of issue as the code can't be the problem.
g.setFont(new Font("TimesRoman", Font.BOLD, 50));
g.setColor(Color.red);
g.drawString("Connection Failed.", x, y);
TL;DR - Drawing bold text on canvas isn't bold when running JAR file, but is when running from NetBeans.
All help is appreciated. Thanks!
It looks like you might be creating a Font in your paint function. Given the paint function may be called several times per second, this can be a source inefficiency, and possibly resource starvation. Create your Font when you create your class, perhaps as a static resource, and then use the same font object in each paint call.
The Graphics object is allowed to take shortcuts when drawing objects, in the name of efficiency. This can include not bolding large fonts, not using anti-aliasing on diagonal lines, etc. Speed is king. Of course, you can tell the graphics engine you care more about appearance than speed, using Graphics2D.setRendingHint(...)
import static java.awt.RenderingHints.*;
void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRendingHint(KEY_RENDERING, VALUE_RENDERING_QUALITY);
g2d.setRendingHint(KEY_TEXT_ANTIALIAS, VALUE_TEXT_ANTIALIAS_ON);
You can set multiple hints at once with setRendingHints(Map hints).
When I apply canvas.getContext2d().scale(1.5, 1.5), then my objects in the canvas gets bigger as expected, but are somehow blurred.
What do I have to do to make the canvas draw my objects as sharp as it is when not scaled?
Use the antialiasing rendering hint:
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
So when you scale, it will look really better.
The images of the right use the RenderingHints.VALUE_ANTIALIAS_ON
I discovered that there are ctx.transform() and ctx.scale()methods for a Canvas. Which work fine, but as they behave like just scaling an image up, the result is not sharp. Guess due to (anti)alasing and stuff like that.
So I decided to rewrite all my ctx.draw() methods to respect a GLOBAL_OFFSET, which changes value when user zooms in and out. This way, the canvas objects can keep their original coordiante point values, but respecting the zoom level and offset it is possible to draw them bigger or thinner, which kind of "simmulates" the zooming and panning.
Well I've got my game engine running smoothly, and perfectly on all machines! Before I continue adding functionality to the engine, I want to enhance the engine's graphical capabilities. The one I'm focused on is fading and blending images. I'm going to need some smooth transitions, so without using any 3rd party libraries like OpenGL, how does one apply opacity to images when drawing to a graphics object?
thanks for any replies :D
Perhaps using an AlphaComposite could be what you're looking for?
Image image = new Image(...);
float alpha = 0.5;
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(composite);
g2d.drawImage(image, 0, 0, null);
}
You would set your alpha to whatever transparency level you desire (between 0.0 and 1.0). In this case, you would probably want to be using AlphaComposite.SRC_OVER to be able to overlay your transparent image and see behind it, unless your going for something else (if so, you can easily see all available constants and their explanations on the first link provided).
I am making a game and I am trying to find the best way to implement Doublebuffering into it. Would anyone be able to show me how I could do it with my rendering code below?
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(p.getImage(), p.getX(),p.getY(),null);
//draw each ball object
for(int i=0;i<balls.size(); i++){
Ball tmp = (Ball) balls.get(i);
g2d.drawImage(tmp.getImage(), tmp.getX(),tmp.getY(),null);
}
//strings
g2d.drawString("Score: "+score,50,20);
}
Could someone please help?
If you're using Swing you just can use the built-in double buffering: http://java.sun.com/products/jfc/tsc/articles/painting/#db
In case you're implementing your own rendering, here are some hints:
Double Buffering basically means you have two draw buffers that you alternatingly write to while the other is displayed.
In your case, you might use an image to draw your game content to and then draw that image to the component. This should give you double buffering in a sense. You might use two images which you swap in order to reduce concurrent access, i.e. one would be the "front" buffer that is displayed and the other is the "back" buffer you draw to.
That said, I'd strongly recommend not to implement that yourself. Instead you should try and use one of the existing 2D libraries (like Swing) or 3D libraries (like OpenGL with JOGL or LWJGL as Java bindings - note that it doesn't have to be 3D with OpenGL). Alternatively you could also look for a game engine, there are plenty of them out there.
One attempted approach was to use TexturePaint and g.fillRect() to paint the image. This however requires you to create a new TexturePaint and Rectangle2D object each time you paint an image, which isn't ideal - and doesn't help anyway.
When I use g.drawImage(BufferedImage,...), the rotated images appear to be blurred/soft.
I'm familiar with RenderingHints and double-buffering (which is what I'm doing, I think), I just find it difficult to believe that you can't easily and efficiently rotate an image in Java that produces sharp results.
Code for using TexturePaint looks something like this.
Grahics2D g2d = (Graphics2D)g;
g2d.setPaint(new TexturePaint(bufferedImage, new Rectangle2D.Float(0,0,50,50)));
g2d.fillRect(0,0,50,50);
I'm using AffineTransform to rotate a hand of cards into a fan.
What would be the best approach to paint good-looking images quickly?
Here is a screenshot:
The 9 is crisp but the rest of the cards are definitely not as sharp.
It could be possible that the problem lies in when I create each card image and store it in an array.
Here's how I'm doing it at the moment:
// i from 0 to 52, card codes.
...
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage img = gc.createCompatibleImage(86, 126, Transparency.TRANSLUCENT);
Graphics2D g = img.createGraphics();
setRenderingHints(g);
g.drawImage(shadow, 0, 0, 86, 126, null);
g.drawImage(white, 3, 3, 80, 120, null);
g.drawImage(suit, 3, 3, 80, 120, null);
g.drawImage(value, 3, 3, 80, 120, null);
g.dispose();
cardImages[i] = img;
}
private void setRenderingHints(Graphics2D g){
g.setRenderingHint(KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
}
How should I approach this differently?
Thanks.
Edit:
Without RenderingHints
Setting AA hints made no difference. Also, setting RenderingHints when creating the images makes no difference either. It's only when they are being rotated with AffineTransform and painted using g.drawImage(...) that they seem to blur. The image above shows the difference between default (nearest neighbor) and bilinear interpolation.
Here is how I'm currently painting them (much faster than TexturePaint):
// GamePanel.java
private void paintCard(Graphics2D g, int code, int x, int y){
g.drawImage(imageLoader.getCard(code), x, y, 86, 126, null);
}
// ImageLoader.java
public BufferedImage getCard(int code){
return cardImages[code];
}
All my cards are 80x120 and the shadow .png is 86x126, so as to leave 3px semi-transparent shadow around the card. It's not a realistic shadow I know, but it looks okay.
And so the question becomes... How can you produce sharp paint results when rotating a BufferedImage?
Reference to a previous question also regarding a fanned card hand:
How can you detect a mouse-click event on an Image object in Java?
Bounty-Edit:
Okay so after much discussion I made a few test .svg cards to see how SVG Salamander would go about rendering them. Unfortunately, the performance is terrible. My implementation is clean enough, seeing as with double-buffered BufferedImage's the painting was incredibly fast. Which means I have come full circle and I'm back to my original problem.
I'll give the 50 bounty to whoever can give me a solution to get sharp BufferedImage rotations. Suggestions have been to make the images bigger than they need to be and downscale before painting, and to use bicubic interpolation. If these are the only possible solutions, then I really don't know where to go from here and I may just have to deal with the blurred rotations - because both of those impose performance setbacks.
I can finish my game if I can find a way to do this well.
Thanks to everyone. :)
When you rotate a rasterized image (such as a BufferedImage), you lose data. The best solution is to save your images larger than you'll need them, and downscale on the fly when you paint them. I've found that 1.5x the size you need is a good starting point.
Then, when you're painting the image, resize on the fly:
g.drawImage(bufferedImage, x, y, desiredWidth, desiredHeight, observer);
Rotations using bilinear interpolation is recommended.
Credit for suggestion goes to guido.
This advice is probably a little late in your design, but may be worth mentioning.
Rasterized images is probably the wrong technology to use if a lot of rotations and animations are a part of your UI; especially with complicated images with lots of curves. Just wait until you try and scale your canvass. I might suggest looking at a vector based graphical library. They will render the sorts of effects you want with less potential for artifacts.
http://xmlgraphics.apache.org/batik/using/swing.htm
Setting the interpolation type, as well as anti-aliasing value, in an AffineTransformOp may offer some improvement. Type TYPE_BICUBIC, while slower, is typically the best quality; an example is outlined here. Note that you can supply multiple RenderingHints. Another pitfall arises from failing to apply the hints each time the image is rendered. You may also need to adjust the transparency of the background, as suggested here. Finally, consider creating an sscce that includes one of your actual images.