Java - Drawing Images Basically - java

I've come from Python to Java and am a bit confused. My main question is how to draw an image. I've looked on the oracle site but even when copy - pasting their code it didn't work. Here is what I had (excluding imports):
public class ImageTesting{
public void main(String[] args){
BufferedImage img = null;
try {
img = ImageIO.read(new File("/Volumes/Data/Users/me/Desktop/Button Img.png"));
Graphics g = null;
g.drawImage(img, 100, 100, this);
} catch (IOException e) {
System.out.println("Image Loading Failed");
}}}
The line I'm having problems is the g.drawImage(img, 100, 100, this); and complains about not having an image observer. It confuses me that that same line works in another code I have but works :/ What am I missing??!

You need something to display your image on.
The simplest method would be to use a JLabel, see How to use labels for mor examples
You need a window to display the label, see How to create GUIs with Swing for details
If, for some reason, you absolutely must paint the image manually, you will need to extend from something that is paintable, like JPanel and override its paintComponent method.
See How to perform custom painting for more details

Related

Scaling an image on a button which is resized

I'm working on an application which allows a used to place controls, move, resize, etc. But I'm trying to add icon images to a button control. When placed and instanced, it resizes the icon image per the code below.
But when I resize the control using user features and it calls this routing again, it fails to resize the image and it remains the original icon size. I've tried using "this.", passing the control to itself, I've done prints to ensure it's seeing the new size and width... what I am missing?
Also, when I create a 2nd control (or 3rd, etc), it uses the 1st image's initial size.
Thanks!
protected void sizeIcon () {
try {
File f2 = new File("media\\button.gif");
BufferedImage inputImage = ImageIO.read(f2);
BufferedImage img = new BufferedImage(this.getWidth(), this.getHeight(), inputImage.getType());
Graphics2D g = img.createGraphics();
g.drawImage(inputImage, 7, 0, this.getWidth(), this.getHeight(), null);
ImageIO.write(img, "gif", new File("test.gif"));
this.setIcon(new ImageIcon("test.gif"));
g.dispose();
} catch(Exception e) {System.out.println(e);}
Sorry, got it, appears the old file was not being replaces.
-MH

How to save a specific part of a JPanel?

I am working on a chess game and I would like to let the player choose the board's colors. Therefore I will use this method:
static void createBoard(Graphics g) {
Color bright = new Color(255, 225, 181); //player chooses color
Color dark = new Color(188, 141, 105); //player chooses color
boolean darkTile = false;
for (int y = spaceY; y < (spaceY + BOARDHEIGHT); y += TILESIZE) {
for (int x = spaceX; x < (spaceX + BOARDWIDTH); x += TILESIZE) {
if (darkTile) {
g.setColor(dark);
} else {
g.setColor(bright);
}
g.fillRect(x, y, TILESIZE, TILESIZE);
darkTile = !darkTile;
}
darkTile = !darkTile;
}
BufferedImage overlay;
try {
overlay = ImageIO.read(new File("overlay.png"));
JLabel label = new JLabel(new ImageIcon(overlay));
g.drawImage(overlay, spaceX, spaceY, BOARDWIDTH, BOARDHEIGHT, null);
} catch (IOException e) {}
}
This I would like to save as a BufferedImage, so I don't have to run this method all the time.
So how can I save just this part of my JPanel, without the stuff outside of the chess board? (there will be more painted)
This I would like to save as a BufferedImage,
Don't know that your need to save the BufferedImage to a file. You can just create a BufferedImage to be used by the application when the application starts. You can then recreate the BufferedImage if any of the user colors change.
You can paint directly to a BufferedImage:
BufferedImage image = new BufferedImage(boardSize, boardSize, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// draw the squares onto board
g2d.dispose();
Now your createBoard() method should probably return the BufferedImage so it can be used by your application.
You put in certain efforts to put up your question, so lets honor that with some thoughts to get you going.
First of all: you have an empty catch block {}. That is bad practice. This simply eats up any error messages you get. That is not helpful. Either allow that exception to bubble up and stop your application; or at least print its contents - so that you understand what happens.
And given your comment: you never now if there will be errors. Especially when doing IO, all sorts of things can go wrong. Please believe me: empty catch blocks are bad practice; and you should not train yourself to accept them.
Second thought: don't go for that yet. As convenient as it might sound; but saving a background picture doesn't add much value at this point.
You don't need to worry about this code; it is executed once when your application comes up.
So, the real answer here: focus on the features you want to implement; and don't get distracted with pre-mature optimizations.

If iText PdfGraphics2D.create() is used, absolute drawing order is not respected in generated PDF

I'm having trouble using iText's PdfGraphics2D to create PDFs from Swing components with Nimbus LAF. After some investigation, I think it's due to creation of multiple Graphics objects and subsequent out-of-order use.
Unlike Swing Graphics2D, if iText's PdfGraphics2D.create() is used to generate a new PdfGraphics2D object, the order of PdfGraphics2D object creation affects the order in which components are drawn, rather than just using the absolute order in which a draw method is called. (See code example below - note that it's not using Nimbus. This is a general problem, it's just more acute in Nimbus.)
My question: Is this fixable? Or do I have to give up on using Nimbus if I want to generate PDFs?
I'm a total novice when it comes to PDFs and iText. Am I missing a setting that would force the PDF drawing order to be the same as the Swing drawing order, regardless of the order in which Graphics objects are created? (It's not writer.setStrictImageSequence(true) - I tried that just in case.)
The following frame looks different when displayed with Swing vs used to generate a PDF with PdfGraphics2D:
public class TestFrame extends JFrame {
public TestFrame() {
TestPanel panel = new TestPanel();
add(panel, BorderLayout.CENTER);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestFrame frame = new TestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Document document = new Document(PageSize.LETTER);
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("TestFrame.pdf"));
writer.setStrictImageSequence(true);
document.open();
PdfContentByte canvas = writer.getDirectContent();
PdfGraphics2D graphics = new PdfGraphics2D(canvas, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
frame.paint(graphics);
graphics.dispose();
document.close();
JOptionPane.showMessageDialog(frame, "Created PDF");
}
catch (Exception ex) {
System.out.println("Error creating PDF");
ex.printStackTrace();
}
}
});
}
class TestPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Graphics2D g4 = (Graphics2D) g2.create();
Graphics2D g3 = (Graphics2D) g2.create();
g2.setColor(Color.BLACK);
Rectangle leftBar = new Rectangle(0, 0, 50, 200);
// Should be drawn first
g2.fill(leftBar);
g3.setColor(Color.RED);
g3.setFont(new Font("Serif", Font.PLAIN, 12));
Rectangle topBar = new Rectangle(0, 0, 200, 50);
// Should be drawn second
g3.fill(topBar);
g4.setColor(Color.BLUE);
Rectangle rightBar = new Rectangle(150, 0, 50, 200);
// Should be drawn third
g4.fill(rightBar);
}
#Override
public Dimension getPreferredSize() {
// Size panel to drawing for convenience
return new Dimension(200, 200);
}
}
}
With Swing, the rectangles are drawn in the order the fill methods are called: left, top, right. With PdfGraphics2D, the order is reversed because of the order in which the graphics objects are created.
In Nimbus LAF, presumably because painters are used to draw borders and/or backgrounds, multiple Graphics objects are created and used out-of-order when drawing certain components. For example, a JTextField with the default border is covered with a black bar in the resulting PDF, as seen in this old StackOverflow question:
Why is iText's PdfWriter printing JTextFields black when exported as a runnable jar?
It's never answered, and OP never explicitly says they're using Nimbus, but that's exactly what I see. I also see problems when using JButton and JComboBox.
As an aside, I'd bet this issue is also causing the overlap in this question:
https://stackoverflow.com/questions/29410971
EDIT
Sorry, I definitely should have spent more time looking at how PDFs and iText work!
As #Bruno Lowagie rightly said, iText doesn't decide how to draw things. (Thank you Bruno, you made me realize I needed to check my assumptions and look at the code!)
I incorrectly assumed that PdfGraphics2D was generating the PDF instructions, but I (finally) understand that it's using the underlying PdfContentByte to do so, with actual content stored in an internal ByteBuffer. Since creation of a new PdfGraphics2D also creates a duplicate PdfContentByte (which makes sense, to ensure state changes stay separate), you can't interleave drawing instructions from different PdfGraphics2D objects in the resulting PDF.
So, sadly for me, Nimbus may be out if you need to convert Swing displays to PDFs.
Orson PDF (GPLv3 or commercial license) provides a PDFGraphics2D implementation that we've tested using the SwingSet3 application and the Nimbus Look and Feel and it works well. You should download version 1.7 (or later) and try it.

How to save JPanel as JPEG with its components

I need to save a chessboard as a jpeg file. I know how to save a java component into JPEG. But my problem is that I use JButtons as cells and when I try to save the whole chessboard, only the panel is saved. Does anybody know how to save a JComponent WITH ITS CHILD COMPNENTS into JPEG (png etc.) Thank you in advance.
You can print a Component (such as JPanel) to any Graphics object. So, why not use the Graphics object of a BufferedImage and write it to disk.
void takePicture(JPanel panel) {
BufferedImage img = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB);
panel.print(img.getGraphics()); // or: panel.printAll(...);
try {
ImageIO.write(img, "jpg", new File("panel.jpg"));
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This makes this JPanel
JPanel panel = new JPanel();
panel.add(new JButton("Hello"));
panel.add(new JButton("World"));
Look like this:
updated based on comments, many thanks to #MadProgrammer
Check out Screen Image. It will allow you to create an image of any component in the frame. If you choose to make an image of a panel all the child components are included in the image.
Use Robot class to create a screen capture of your screen.
Use this tutorial for help:
Screen Capture using Robot class
Now for your chessboard, clip the image by calling subImage method of BufferedImage class.
Here's the link on clipping BufferedImage
You can get Dimensions of you JPanel by calling getX(), getY(), getWidth() and getHeight() methods which you can pass into subImage method.
Manipulate the passing value by adding/substracting some value to get desired results.

Is this a good design for creating smooth animations in Java/Swing?

I have a JPanel subclass with custom paintComponent() implementation. It is being refreshed at 50fps. It is typically in the range of 500x300 pixels in size. I'm seeing some flickering (not too bad but noticeable) and I've inserted some debug code that indicates that Swing/EDT is skipping (presumably) redundant painting. I am guessing that's because the EDT is not giving enough time for paintComponent() to always finish or it is taking too much time on the EDT.
My thinking is that I need to take the code currently implementing paintComponent() (which is not very complex but not completely trivial either) and refactor it so it is executed on its own Thread (or at least not the EDT) and draws to an ImageBuffer. I then implement paintComponent on my custom JPanel and draw (render) from the ImageBuffer to the screen (actually to the buffer behind Swing components as my research into the solution led me some information about Swing being (by default) double-buffered, though I'm not completely clear on that). If it is true that the rendering from the ImageBuffer to the JPanel is faster than my implementation that constructs the ImageBuffer then I will be going in the right direction.
Is this the proper design direction for me to take?
UPDATE
I modified my implementation as discussed in reponses below:
1) Create a BufferedImage
BufferedImage myBufferedImage = new BufferedImage(mySize.width,mySize.height,BufferedImage.TYPE_INT_ARGB)
2) Create a Thread dedicated to peforming the processing to determine what is to be drawn.
3) Move the code previously in paintComponent() to another method that is executed by the dedicated Thread. At the end of this method, call repaint();
4) Create a new paintComponent() that simply calls g.drawImage(myBufferedImage,0,0,null);
5) Where I previously would call repaint(), trigger myThread to perform the drawing to myBufferedImage.
This was a disaster, as predicted. Much worse flickering and sluggishness, partial paints, etc. I believe this was due to contention reading/writing myBufferedImage (as mentioned below). So I then created a lock and lock myBufferedImage when I am writing to it (in the dedicated drawing Thread) and wait to get that lock in paintComponent() before calling Graphics2D.drawImage(); The flicker and partial paints go away - but performance is no better (maybe even worse) than when I was doing all the calculations for the drawing in paintComponent (and therefore in the EDT).
This has me stumped at this point.
If you're not updating the entire component (ie only small areas are changing), you could use JComponent#repaint(Rectangle r) indicating the areas that have changed. This will result in a repaint cycle that updates (potentially) a much smaller area.
I generated a "animated sequence" library some time ago to take a series of images and layer them ontop of each, given a "speed" of each layer, it would transpose them from right to left.
The whole sequence would cycle for 10 seconds, where a speed of 1 would take take 10 seconds to complete. Each layer is moving at difference speeds.
The original images where 1024x256, and the sequence was devised of 5 animated layers and 2 static layers...
I only wish I could show you how smooth this plays on my PC and Mac.
The only signification issue I had to over come was making sure that the images where compatible with the screen devices color model.
UPDATED
These are some utility classes I use when loading or creating BufferedImages, especially for animation. The make sure that the colour models are the same as those used by the screens, which will make them faster to update/repaint
public static BufferedImage loadCompatibleImage(URL resource) {
BufferedImage image = null;
try {
image = ImageIO.read(resource);
} catch (IOException ex) {
}
return image == null ? null : toCompatibleImage(image);
}
public static BufferedImage toCompatibleImage(BufferedImage image) {
if (image.getColorModel().equals(getGraphicsConfiguration().getColorModel())) {
return image;
}
BufferedImage compatibleImage =
getGraphicsConfiguration().createCompatibleImage(
image.getWidth(), image.getHeight(),
image.getTransparency());
Graphics g = compatibleImage.getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return compatibleImage;
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
// Check out java.awt.Transparency for valid values
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
I think this is what you're looking for on information about double buffering:
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
You could turn off double buffering with setDoubleBuffered(false) if you can't get access to the underlying buffer which I'm not entirely sure you can.
I don't think you can safely draw on an image from another thread because you'll get into the thread writing to the image while the EDT is reading that same image as it redraws. If you share an image between them you're going to have multi-threading issues that you'll have to synchronize. If you synchronize then you're performance isn't going to be very good. If you instantiate a new image every frame you're memory is going to skyrocket and GC will get you. You may be able to instantiate 10 frames and keep the writing away from the reading or something like that, but either way this is going to very tricky to make it performant and correct.
My suggestion is to do all drawing from EDT, and figure out a way to do the calculations (rendering) on another thread that doesn't involve ImageBuffer sharing.
Update While it is used for fullscreen. The suggestions in there apply to windowed mode as well: "Separate your drawing code from your rendering loop, so that you can operate fully under both full-screen exclusive and windowed modes." See this http://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html
i've has similar problems trying to paint smoothly.
try running this and see how smooth it is (its smooth for me).
profiler says most of the time is in paint component. interestingly draw image is not mentioned.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class P extends JPanel {
void init(Dimension d) {
GraphicsConfiguration gc=getGraphicsConfiguration();
bi=gc.createCompatibleImage(d.width,d.height);
}
#Override public void paintComponent(Graphics g) {
//super.paintComponent(g);
if(bi!=null)
g.drawImage(bi,0,0,null);
}
BufferedImage bi;
}
public class So13424311 {
So13424311() {
p=new P();
}
void createAndShowGUI() {
Frame f=new JFrame("so13424311");
// f.setUndecorated(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.add(p);
p.init(d);
p.setSize(d);
p.setPreferredSize(d);
f.pack();
// if(moveToSecondaryDisplay)
// moveToSecondaryDisplay(f);
f.setVisible(true);
}
void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
Timer t=new Timer(20,new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
Graphics g=p.bi.getGraphics();
Color old=g.getColor();
g.fillRect(0,0,d.width,d.height);
g.setColor(Color.red);
g.fillRect(n%(d.width/2),n%(d.height/2),20,20);
g.setColor(Color.green);
g.fillRect(n%(d.width/2)+20,n%(d.height/2),20,20);
g.setColor(Color.blue);
g.fillRect(n%(d.width/2),n%(d.height/2)+20,20,20);
g.setColor(Color.yellow);
g.fillRect(n%(d.width/2)+20,n%(d.height/2)+20,20,20);
g.setColor(old);
g.dispose();
p.repaint();
n++;
}
int n;
});
t.start();
}
public static void main(String[] args) {
new So13424311().run();
}
final P p;
Dimension d=new Dimension(500,300);
}

Categories

Resources