I have a JTextPane with an image for its background.
prevWords = new JTextPane()
{
public void paint(Graphics g)
{
BufferedImage img;
try
{
img = ImageIO.read(new File("Images/logo.png"));
img.getGraphics().setColor(new Color(Color.TRANSLUCENT));
g.drawImage(img, 0, 0, null);
}
catch (IOException e)
{
System.out.println("Failed to load logo.");
}
super.paintComponents(g);
}
};
When I write text to the the pane, it I cannot see it. I have set the text in pane to be white as well.
This is a complete hack.
The problem here is, the UI is painting the background twice...
You need to circumvent the UI in such a way so that you can paint the image into the background while still getting the text to render over the top.
In the end, I had to make the text pane transparent so I could force the UI not to paint the background.
public class TextPaneBackground {
public static void main(String[] args) {
new TextPaneBackground();
}
public TextPaneBackground() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new TextPaneWithBackground()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TextPaneWithBackground extends JTextPane {
private BufferedImage background;
public TextPaneWithBackground() {
try {
background = ImageIO.read(new File("C:/Users/shane/Dropbox/MegaTokyo/Evil_Small.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
setForeground(Color.WHITE);
setOpaque(false);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return background == null ? super.getPreferredScrollableViewportSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
if (isOpaque()) {
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight()- background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
}
getUI().paint(g2d, this);
g2d.dispose();
}
}
}
Reimeus hinted at the ability to insert an image into the Document directly, this might be a better, long term solution.
Related
I am trying to draw a rectangle over Image using java.awt classes. For that I used below sample code:
public class DrawRectangle {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
}
class TestPane extends JPanel {
private BufferedImage myImage;
private Rectangle myOffice = new Rectangle(150, 50, 30, 20);
public TestPane() {
try {
File image = new File("C:\\Users\\NNaphade\\work\\ImageDetection\\Trial_Pascal_VOC\\test_image\\IMG_20180327_110210.jpg");
if(image.exists())
myImage = ImageIO.read(image);
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
System.out.println("image exist!!!!!!");
return myImage == null ? new Dimension(200, 200) : new Dimension(
myImage.getWidth(), myImage.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (myImage != null) {
g2d.drawImage(myImage, 0, 0, 1000, 1000, this);
g2d.setColor(Color.RED);
g2d.translate(0, 0);
g2d.draw(myOffice);
}
g2d.dispose();
}
}
This works correct and output is displayed as expected. Here I am fixing the parameters for rectangle as:
private Rectangle myOffice = new Rectangle(150, 50, 30, 20);
However, in my application, I want to pass these parameters from another method. I want to pass these x1, y1, w and h to TestPane class given above. I tried changing the TestPane constructor by passing these 4 parameters, but I am not able to set them as instance variables. E.g. the following code doesn't work.
private void markWithBoundingBox(INDArray testData, int gridWidth, int gridHeight, double w, double h, DetectedObject obj) {
double[] xy1 = obj.getTopLeftXY();
int predictedClass = obj.getPredictedClass();
int x1 = (int) Math.round(w * xy1[0] / gridWidth);
int y1 = (int) Math.round(h * xy1[1] / gridHeight);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(x1, y1, w, h));
frame.pack();
frame.setVisible(true);
}
});
}
class TestPane extends JPanel {
private BufferedImage myImage;
//private Rectangle myOffice = new Rectangle(50, 50, 3, 20);
public TestPane(int x, int y, double w, double h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
try {
File file = new File("C:\\Users\\NNaphade\\work\\ImageDetection\\Trial_Pascal_VOC\\test_image\\IMG_20180327_110210.jpg");
if(file.exists()) {
myImage = ImageIO.read(file);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return myImage == null ? new Dimension(100, 100) : new Dimension(
myImage.getWidth(), myImage.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (myImage != null) {
g2d.drawImage(myImage, 0, 0, 2000, 2000, this);
g2d.setColor(Color.RED);
g2d.translate(0, 0);
g2d.draw(new Rectangle(this.x, this.y, this.w, this.h));
}
g2d.dispose();
}
}
It seems to me that TestPane here is not a class but the component. because Java compiler doesn't let me declare the instance variables in the constructor and all the available methods there are of component. How can I get rid of this issue?
I want to create a animation game by java. But it runs slowly when having image in JPanel than don't have it.
public class Multi_Paint extends JFrame implements ActionListener {
JPanel pn1 = new JPanel();
JPanel pn2 = new JPanel();
static int x=100,y=100;
Timer timer;
Multi_Paint(){
setLayout(new BorderLayout());
pn1.setBackground(Color.GREEN);
pn2.setBackground(Color.red);
pn2.setPreferredSize(new Dimension(300, 300));
add(pn1,BorderLayout.CENTER);
add(pn2,BorderLayout.WEST);
setSize(1000, 1000);
setVisible(true);
pn1.add(new DrawPanel());
pn2.add(new DrawPanel());
timer = new Timer(1, this);
timer.start();
}
public void actionPerformed(ActionEvent e) {
moveBall();
repaint();
}
void moveBall(){
x=x+10;
y=y+10;
}
public static void main(String[] args) {
new Multi_Paint();
}
}
class DrawPanel extends JPanel{
DrawPanel(){
setBorder(BorderFactory.createLineBorder(Color.black));
}
public Dimension getPreferredSize() {
return new Dimension(500,500);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int x= Multi_Paint.x;
int y= Multi_Paint.y;
//If we decline this "try" Java will run faster.
try {
BufferedImage img = ImageIO.read(new File("D:\\pict1.jpg"));
double scale = 0.5 ;
double w = scale * img.getWidth(this);
double h = scale * img.getHeight(this);
g.drawImage(img, x, y, (int) w, (int) h, this);
} catch (IOException e) {
e.printStackTrace();
}
g.fillOval(x, y, 30, 30);
}
}
As it is right now, ImageIO.read internally creates an ImageInputStream, writes the data into a new BufferedImage instance and closes the stream every single frame, which are expensive IO operations. That's why it is running slowly.
You shouldn't have any logic in your paintComponent method, or else this will slow the process down. You should rather read your image file once in your constructor and only access it in your paint method. Since your image file doesn't change over the course of the program, this is sufficient.
Something like this should work:
class DrawPanel extends JPanel {
private final BufferedImage img;
private int w;
private int h;
DrawPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
this.img = createImage("D:\\pict1.jpg");
}
private BufferedImage createImage(String path) {
BufferedImage img = null;
try {
img = ImageIO.read(new File(path));
double scale = 0.5;
this.w = (int) (scale * img.getWidth(this));
this.h = (int) (scale * img.getHeight(this));
} catch (IOException e) {
System.err.println("Could not read file with path " + path);
e.printStackTrace();
}
return img;
}
public Dimension getPreferredSize() {
return new Dimension(500,500);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int x= Multi_Paint.x;
int y= Multi_Paint.y;
// img could be null
if(this.img != null) {
g.drawImage(img, x, y, w, h, this);
}
g.fillOval(x, y, 30, 30);
}
}
I want to draw a oval image in a JLabel, using Graphics. This is my code, but a dont know about Graphics.
class imagePanel extends JLabel {
//private PlanarImage image;
private BufferedImage buffImage = null;
private void drawFingerImage(int nWidth, int nHeight, byte[] buff) {
buffImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_BYTE_GRAY);
buffImage.getRaster().setDataElements(0, 0, nWidth, nHeight, buff);
Graphics g = buffImage.createGraphics();
g.drawImage(buffImage, 0, 0, 140, 150, null);
g.dispose();
repaint();
}
public void paintComponent(Graphics g) {
g.drawImage(buffImage, 0, 0, this);
}
}
I have this
you need the help of setClip() method as mentioned here and here.
when it comes to code it should look like this
public class OvalImageLabel extends JLabel {
private Image image;
public OvalImageLabel(URL imageUrl) throws IOException {
image = ImageIO.read(imageUrl);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setClip(new java.awt.geom.Ellipse2D.Float(0f,0f, getWidth(),getHeight()/2));
g.drawImage(image, 0, 0, this);
}
}
and a running application that using this class
public class UsageExample extends JPanel {
public UsageExample() {
super(new BorderLayout());
OvalImageLabel l;
try {
l = new OvalImageLabel(new File("/path/to/image.png").toURI().toURL());
} catch (Exception e) {
e.printStackTrace();
return;
}
add(l, BorderLayout.CENTER);
}
private static void createAndShowGUI() {
JFrame frame = new JFrame();
frame.setContentPane(new UsageExample());
frame.setSize(200, 200);
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I am trying to scale a screenshot taken by:
robot.createScreenCapture(SCREEN_RECT);
Im trying to get it down to an image that is 600X400 and fits into a JFrame that is 600X400
My program is using a swing worker to create an video out of each picture, or frames. The frames have a delay of 200ms per each. the image when told to rescale just shows the original image at the original dimensions. Does anyone know how to fix this, or should I just give up on the resize-ing?
#SuppressWarnings("serial")
public class temporaryShit extends JPanel
{
private static final int width = 600;
private static final int height = 400;
private JLabel displayedLabel = new JLabel();
public temporaryShit()
{
setLayout(new BorderLayout());
add(displayedLabel);
try {
MySwingWorker mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
} catch (AWTException e) {
}
}
public void setLabelIcon(Icon icon) {
displayedLabel.setIcon(icon);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
private class MySwingWorker extends SwingWorker<Void, Icon>
{
private final Rectangle SCREEN_RECT = new Rectangle(0, 0, width, height);
private long delay = 200;
private Robot robot = null;
public MySwingWorker() throws AWTException
{
robot = new Robot();
}
#Override
protected Void doInBackground() throws Exception
{
Timer utilTimer = new Timer();
TimerTask task = new TimerTask()
{
#Override
public void run()
{
BufferedImage capturedImage = captureScreen();
publish(new ImageIcon(capturedImage));
}
};
utilTimer.scheduleAtFixedRate(task, delay, delay);
return null;
}
#Override
protected void process(List<Icon> chunks)
{
for (Icon icon : chunks)
{
setLabelIcon(icon);
}
}
private BufferedImage captureScreen()
{
BufferedImage img = robot.createScreenCapture(SCREEN_RECT);
return createResizedImage(img, width, height);
}
public BufferedImage createResizedImage(Image original, int width, int height)
{
BufferedImage scaledBI = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = scaledBI.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(original, 0, 0, width, height, null);
g.dispose();
return scaledBI;
}
}
private static void createAndShowGui()
{
temporaryShit mainPanel = new temporaryShit();
JFrame frame = new JFrame("SwingWorker Eg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGui();
}
});
}
}
You already have a new image with specified size - scaled, which you can use for rendering.
Here is a simple example:
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
class TestBrightness {
public static void main(String args[]) {
try {
URL imageUrl = new URL(
"http://duke.kenai.com/comfyChair/ComfyChairRadSmall.jpg");
BufferedImage ioImage = ImageIO.read(imageUrl);
JPanel panel = new JPanel();
Image scaledImg = ioImage.getScaledInstance(ioImage.getWidth() / 2,
ioImage.getHeight() / 2, Image.SCALE_SMOOTH);
panel.add(new JLabel(new ImageIcon(ioImage)));
panel.add(new JLabel(new ImageIcon(scaledImg)));
JOptionPane.showMessageDialog(null, panel, "100% vs 50%",
JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e.getMessage(), "Failure",
JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
}
}
As a side note, there are many ways to scale an image and Image.getScaledInstance() may not be the best. You may be interested to take a look at The Perils of Image.getScaledInstance() for some details on Image.getScaledInstance()
EDIT: question update
Last question update removed all the details regarding getScaledInstance and invalidated this answer. getScaledInstance is a very slow method and it is also asynchronous. Try this method to get a resized image:
public static BufferedImage createResizedImage(Image original, int width,
int height) {
BufferedImage scaledBI = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = scaledBI.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(original, 0, 0, width, height, null);
g.dispose();
return scaledBI;
}
You may want to change rendering hints for better quality.
For a nicer and more complete image scaler take a look at getFasterScaledInstance() from Filthy Rich Clients book.
EDIT : last question update with posted code and SwingWorker
The implementation of SwingWorker is not correct. doInBackground() schedules java.Utils.Timer. This timer handles all updates, while the actual SwingWorker worker thread ends. All updates from the timer are fired not on Event Dispatch Thread. It may not be safe to allocate ImageIcon not on EDT. And for sure it is not safe to update UI, ie calling setLabelIcon() not on EDT. See Concurrency in Swing tutorial for details.
You can add while loop and Thread.sleep in doInBackground() and remove the timer. Alternatively, Swing timer may be more suitable for this case. Here is an example:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class DemoRobotPanel extends JPanel{
private static final long serialVersionUID = 1L;
private Image image;
private Robot robot;
private Rectangle CAPTURE_RECT;
private int TIMER_DELAY = 1000;
private int desiredWidth = 600;
private int desiredHeight = 400;
public DemoRobotPanel() {
CAPTURE_RECT = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
try {
robot = new Robot();
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
try {
BufferedImage img = robot.createScreenCapture(CAPTURE_RECT);
setImage(img);
} catch (HeadlessException e) {
e.printStackTrace();
}
}
};
Timer timer = new Timer(TIMER_DELAY, taskPerformer);
timer.start();
} catch (AWTException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(desiredWidth, desiredHeight);
}
public void setImage(Image image) {
this.image = image;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null)
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
private static void createAndShowGui() {
final DemoRobotPanel panel = new DemoRobotPanel();
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Try this:
public BufferedImage resize(BufferedImage bufferedImage, int resizeWidth, int resizeHeight) {
// Create new (blank) image of required (scaled) size
BufferedImage scaledImage = new BufferedImage(resizeWidth, resizeHeight, BufferedImage.TYPE_INT_ARGB);
// Paint scaled version of image to new image
Graphics2D graphics2D = scaledImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(bufferedImage, 0, 0, resizeWidth, resizeHeight, null);
graphics2D.dispose();
return scaledImage;
}
You may want to try different RenderingHints.
I want to make an object to open mouth and close it using two images that switch quickly. I tried with a for loop but it lagged my game.
if(direction == Constant.UP){
ImageIcon i = new ImageIcon("src\\images\\pacman up.png");
image = i.getImage();
ImageIcon i2 = new ImageIcon("src\\images\\pacman left.png");
image = i2.getImage();
}
G.drawImage(image, x, y, 20,20,null);
Any animation in Swing needs to take into consideration the Event Dispatching Thread.
You should NEVER perform any action within the content of the EDT that may block it (such as loops or I/O) as this will prevent the EDT from (amongst other things) processing paint requests.
You should always use a surface capable of supporting double buffer, such as JPanel as this will help eliminate flickering
The following uses a javax.swing.Timer to switch between the two images...
public class TestPacMan {
public static void main(String[] args) {
new TestPacMan();
}
public TestPacMan() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PacManPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PacManPane extends JPanel {
private BufferedImage pacOpened;
private BufferedImage pacClosed;
private BufferedImage frame;
private boolean opened = true;
public PacManPane() {
try {
pacOpened = ImageIO.read(new File("PC-Closed.png"));
pacClosed = ImageIO.read(new File("PC-Opened.png"));
frame = pacOpened;
} catch (IOException exp) {
exp.printStackTrace();
}
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
opened = !opened;
frame = opened ? pacOpened : pacClosed;
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (frame != null) {
int x = (getWidth() - frame.getWidth()) / 2;
int y = (getHeight() - frame.getHeight()) / 2;
g2d.drawImage(frame, x, y, this);
}
g2d.dispose();
}
}
}
don't create the icon each time. create the two images at startup and just switch back
and forth at runtime.
if(direction == Constant.UP){
image = open;
}else {
image = closed;
}
G.drawImage(image, x, y, 20,20,null);