I am currently trying to program a simple platform game. I would like to scale the graphics up by a factor of 2/4/whatever so that I can get an old-school look and feel from the game. However, when I call scale on my Graphics2D instance, nothing happens.
#Override
public void paint(Graphics g) {
BufferStrategy bf = this.getBufferStrategy();
Graphics2D bfg = (Graphics2D) bf.getDrawGraphics();
mLevel.draw(bfg, this);
for (GameEntity entity : mGameEntities) {
entity.draw(bfg, this);
}
bfg.scale(2.0, 2.0);
bf.show();
}
The draw calls basically just end up calling g.drawImage(..). Everything else is drawn correctly, so I do not understand why it isn't scaling everything up by a factor of 2.
#MadProgrammer was correct - one must scale the Graphics2D instance before making any draw calls.
#Override
public void paint(Graphics g) {
BufferStrategy bf = this.getBufferStrategy();
Graphics2D bfg = (Graphics2D) bf.getDrawGraphics();
bfg.scale(2.0, 2.0);
mLevel.draw(bfg, this);
for (GameEntity entity : mGameEntities) {
entity.step();
entity.draw(bfg, this);
}
bf.show();
}
Related
I have experimented with two different methods of drawing the same shape, the first image is drawn by overriding JPanel's paintComponent(Graphics g) method and using g.drawOval(..) etc,
The second image is drawn by creating a buffered image and drawing on it by using buffered image's graphics. How can I achieve the same rendering quality on both approaches? I have tried using many different rendering hints but none of them gave the same quality. I also tried sharpening by using Kernel and filtering, still couldn't.
private void createImage() {
image = new BufferedImage(IMG_SIZE, IMG_SIZE, BufferedImage.TYPE_INT_ARGB);
Graphics2D gr = image.createGraphics();
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
gr.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
//something along the way
gr.drawOval(.....);
gr.drawLine(.....);
gr.drawOval(.....);
panel.repaint();
gr.dispose();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(backgroundColor);
if (USE_BUFFERED_IMAGE) {
g.drawImage(image, startX, startY, null);
} else {
//something along the way
g.drawOval(.....);
g.drawLine(.....);
g.drawOval(.....);
}
}
Drawing using JPanel paintComponent graphics
Drawing using Buffered Image graphics then it is drawn on Jpanel via drawimage
EDIT
I found my solution by getting almost every setting of panel graphics and applying them to buffered image graphics. Not by using only using the same rendering hints or "minimal reproducible examples" approaches. Here, the importing thing is that the panel's graphic scales everything by 1.25 and then scales down to the original before showing it on the panel.
Here is an example, -this is not exactly how my code is, this is just an example to give you an idea-
private void createImages(Paint paint, RenderingHints hints,
AffineTransform transform, Stroke stroke,
Composite composite, GraphicsConfiguration config ){
image = config.createCompatibleImage(IMG_SIZE, IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
Graphics2D gr = image.createGraphics();
// same options
gr.setPaint(paint);
gr.setRenderingHints(hints);
gr.setTransform(transform);
gr.setStroke(stroke);
gr.setComposite(composite);
//something along the way
gr.drawOval(.....);
gr.drawLine(.....);
gr.drawOval(.....);
panel.repaint();
gr.dispose();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(backgroundColor);
if (USE_BUFFERED_IMAGE) {
Graphics2D g2 = (Graphics2D)g;
createImages(g2.getPaint(), g2.getRenderingHints(),g2.getTransform(),
g2.getStroke(),g2.getComposite(), g2.getDeviceConfiguration());
//scaling down is important because your drawings get scaled to 1.25
// by panels graphics' transformation
g.drawImage(image, startX, startY,(int)(IMG_SIZE*0.8),(int)(IMG_SIZE*0.8), null);
} else {
//something along the way
g.drawOval(.....);
g.drawLine(.....);
g.drawOval(.....);
}
}
I found my solution by getting almost every setting of panel graphics and applying them to buffered image graphics. Here, the importing thing is that the panel's graphic scales everything by 1.25 and then scales down to the original before showing it on the panel.
Here is an example, -this is not exactly how my code is, this is just an example to give you an idea-
private void createImages(Paint paint, RenderingHints hints,
AffineTransform transform, Stroke stroke,
Composite composite, GraphicsConfiguration config ){
image = config.createCompatibleImage(IMG_SIZE, IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
Graphics2D gr = image.createGraphics();
// same options
gr.setPaint(paint);
gr.setRenderingHints(hints);
gr.setTransform(transform);
gr.setStroke(stroke);
gr.setComposite(composite);
//something along the way
gr.drawOval(.....);
gr.drawLine(.....);
gr.drawOval(.....);
panel.repaint();
gr.dispose();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(backgroundColor);
if (USE_BUFFERED_IMAGE) {
Graphics2D g2 = (Graphics2D)g;
createImages(g2.getPaint(), g2.getRenderingHints(),g2.getTransform(),
g2.getStroke(),g2.getComposite(), g2.getDeviceConfiguration());
//scaling down is important because your drawings get scaled to 1.25
// by panels graphics' transformation
g.drawImage(image, startX, startY,(int)(IMG_SIZE*0.8),(int)(IMG_SIZE*0.8), null);
} else {
//something along the way
g.drawOval(.....);
g.drawLine(.....);
g.drawOval(.....);
}
}
I'm willing to show an transparent image on a canvas in java with the help of graphics2D and BufferedImage.
Here is the code which loads image.
private static BufferedImage sprites,board;
public static void load(){
try {
board = new BufferedImage(100,100,BufferedImage.TYPE_INT_ARGB);
board = ImageIO.read(new File("res/chesssprite.png"));
} catch (IOException ex) {
Logger.getLogger(SpriteManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
and here is the code which renders the image
public void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D) g;
{
g2d.setColor(new Color(150,150,150));
g2d.fillRect(0,0,getWidth(), getHeight());
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(board,0,0,null);
}
g = g2d;
g.dispose();
bs.show();
}
I have search on net a lot but didn't come with an solution. If anyone knowns how to fix this.
Here is the image..
And here is how output looks like
Okay whoever facing these kind of problem:
Make sure the image is transparent. Test it in image viewer.
Remove the line g2d.setComposite(AlphaComposite.Src); This line adds alpha composites which make every transparent pixel black.
I'm trying to figure out if the repaint method does something that we can't do ourselves.
I mean,how are these two versions different?
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
g2.draw(r);
}
}
and
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
repaint();
}
}
The 2nd version can result in a very risky and poor animation since it can result in repaints being called repeatedly, and is something that should never be done. If you need simple animation in a Swing GUI, use a Swing Timer to drive the animation.
i.e.,
public class MyComponent extends JComponent {
private Rectangle r = new Rectangle(0,0,20,10);
public MyComponent() {
int timerDelay = 100;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
r.translate(5, 5);
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(r);
}
}
The use of repaint() is to suggest to the JVM that the component needs to be painted, but it should never be called in a semi-recursive fashion within the paint or paintComponent method. An example of its use can be seen above. Note that you don't want to call the painting methods -- paint or paintComponent directly yourselves except under very unusual circumstances.
Also avoid calling a class Componenet since that name clashes with a key core Java class.
relatively straight-forward, how can I set the background color of a JMenuBar?
ive tried:
MenuBar m = new MenuBar() {
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setBackground(Color.yellow);
g2.fillRect(0, 0, getWidth(), getHeight());
}
but nothin'
Well, to start with, what you've shown is not a JMenuBar, it's MenuBar, there's a significant difference. Try using a JMenuBarand use setBackground to change the background color
Updated from feedback from Vulcan
Okay, in the cases where setBackground doesn't work, this will ;)
public class MyMenuBar extends JMenuBar {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
}
}
With MadProgrammer's approach you will get menubar background painted twice - once by the UI (it could be gradient on Windows for example, which takes some time to paint) and once by your code in paintComponent method (atop of the old background).
Better replace menubar UI by your own one based on BasicMenuBarUI:
menuBar.setUI ( new BasicMenuBarUI ()
{
public void paint ( Graphics g, JComponent c )
{
g.setColor ( Color.RED );
g.fillRect ( 0, 0, c.getWidth (), c.getHeight () );
}
} );
You can also set that UI globally for all menubars so that you don't need to use your specific component each time you create menubar:
UIManager.put ( "MenuBarUI", MyMenuBarUI.class.getCanonicalName () );
MyMenuBarUI class here is your specific UI for all menubars.
I have this method paint() which receive a Graphics2D parameter. The weird thing that happen is that unless there is a System.out.println present(which i comment out in the block below), the canvas will not draw anything.
public class Map{
public void paint(Graphics2D g){
//fill background to black
g.setColor(Color.black);
g.fillRect(0, 0, TILE_SIZE*WIDTH, TILE_SIZE*HEIGHT);
//draw the tiles and buildings
for(int i=0;i<WIDTH;i++){
for(int j=0;j<HEIGHT;j++){
if(map[j][i] == CLEAR){
//System.out.println("");
g.setColor(Color.gray);
g.fillRect(i*TILE_SIZE, j*TILE_SIZE, TILE_SIZE, TILE_SIZE);
g.setColor(Color.red);
g.drawRect(i*TILE_SIZE, j*TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
}
}
}
Here I use BufferStrategy to draw on Canvas and add it to a Frame. This method is in class Map which will be passed a Graphics2D from the getDrawGraphics() method from BufferStrategy(I hope many people are familiar with this stuff to understand what I'm doing).
public class MapTest extends Canvas{
private Map map;
public MapTest(){
Frame frame = new Frame("MAP");
frame.add(this);
frame.setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
//draw the map
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
//g.translate(100, 100);
map.paint(g);
g.dispose();
strategy.show();
}
}
This code is from the Canvas class. As you can see the paint() method is separate from the Canvas class(which I name GameTest). So if I comment out the println statement then no graphics is shown in the canvas, otherwise it is displayed correctly. Anyone can help me???
You should use the SwingUtilities to switch to the Event Dispatch Thread(EDT), see below. This is required for almost all interactions with AWT and Swing classes.
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new MapTest();
}
}
Notice that this uses a swing helper library, that should be fine for AWT, but even better is to start using Swing.