I'm trying to make a screenshot tool that shows screenshot picture to user in fullscreen. Then user will edit it with a few tools.
While creating it, i faced a problem with visualization of some effects.
I want to create visual representation of selected area using selection rectangle(don't know the exact term name) that you use to select multiple files on desktop or file explorer. example image link
I tried to do it with drawing rectangle on background picture, but when i tried to move a cursor, image was refreshing ugly.
Then I tried to do transparent panel with both setOpaque(false); and setBackground(new Color(0,0,0,0)); and than pin mouse listeners on this panel.
It draws rectangle successfully, but background of panel instantly fills with default grey color.
How can i make foreground panel transparent and draw something on it, without repainting background image?
Here is my Glass class:
private static final int WIDE = 1920;
private static final int HIGH = 1080;
private final Color clean = new Color(0,0,0,0);
private Point mousePt = new Point(WIDE / 2, HIGH / 2);
private Rectangle mouseRect = new Rectangle();
Glass(){
this.setOpaque(false);
this.setBackground(clean);
this.addMouseListener(new MouseHandler());
this.addMouseMotionListener(new MouseMotionHandler());
}
public void paintComponent(Graphics g) {
g.setColor(clean);
g.fillRect(0, 0, 1920, 1080 );
g.setColor(Color.darkGray);
g.drawRect(mouseRect.x, mouseRect.y, mouseRect.width, mouseRect.height);
}
private class MouseHandler extends MouseAdapter {
public void mouseReleased(MouseEvent e) {
System.out.println("released");
mouseRect.setBounds(0, 0, 0, 0);
e.getComponent().repaint();
}
public void mousePressed(MouseEvent e) {
mousePt = e.getPoint();
System.out.println("pressed");
e.getComponent().repaint();
}
}
private class MouseMotionHandler extends MouseMotionAdapter {
public void mouseDragged(MouseEvent e){
mouseRect.setBounds(
Math.min(mousePt.x, e.getX()),
Math.min(mousePt.y, e.getY()),
Math.abs(mousePt.x - e.getX()),
Math.abs(mousePt.y - e.getY()));
e.getComponent().repaint();
}
}
Have you already tried adding super.paintComponent(g) to the beginning of your paintComponent method like so:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(clean);
g.fillRect(0, 0, 1920, 1080 );
g.setColor(Color.darkGray);
g.drawRect(mouseRect.x, mouseRect.y, mouseRect.width, mouseRect.height);
}
Another possibility would be to use a JPanel for all of your drawing and just draw the background image in your paintComponent method too:
class DrawPanel extends JPanel{
public DrawPanel(){
//...
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//Draw background
g.drawImage(yourImage, 0, 0);
//Your drawing
g.setColor(Color.darkGray);
g.drawRect(mouseRect.x, mouseRect.y, mouseRect.width, mouseRect.height);
}
}
Using a JPanel instead of a java.awt.Panel will use double-buffering and avoid any flickering while drawing.
Your code of the parent-container could look like:
public MainFrame(){
//...
//Your old code
//background image...
//getContentPane().add(glass);
getContentPane().add(drawPanel);
}
Related
So, after messing around for a good hour trying to figure out why my JPanel was flickering when moving my mouse I found out that that wasn't the case at all. From what I found out, from changing the order im drawing images, was that the buffer image is being drawn before everything is done being drawn to it. I can only assume that moving my mouse somehow causes the image to be drawn before the render cycle is finished. Im drawing everything to a separate image, overriding a JPanels paintComponent method, then letting the JFrame paint the panel. Moving the frame or resizing it causes the same thing but im not worried about those.
public void render() {
Graphics g = screen.getGraphics();
// State based system, renders all subsequent images
Manager.render(g);
// For fading between state changes
g.setColor(new Color(0, 0, 0, Variables.alpha));
g.fillRect(0, 0, Variables.frame.getWidth(), Variables.frame.getHeight());
// Only way that worked to get the frame to update the JPanel
Variables.frame.repaint();
}
public static void generateFrame(String title, int width, int height, boolean resize, KeyListener keys,
MouseListener mouse, MouseMotionListener mMotion, MouseWheelListener mWheel) {
frame = new JFrame(title);
frame.setResizable(resize);
frame.addKeyListener(keys);
frame.addMouseListener(mouse);
frame.addMouseMotionListener(mMotion);
frame.addMouseWheelListener(mWheel);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
shutdown();
}
});
pane = new JPanel() {
private static final long serialVersionUID = -7796800583217917918L;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(Variables.screen, -Variables.xOffset, -Variables.yOffset,
Variables.frame.getWidth() + Variables.yOffset * 2,
Variables.frame.getHeight() + Variables.xOffset * 2, null);
}
};
frame.add(Variables.pane);
frame.pack();
frame.setSize(width, height);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
I'm making simple game in Java and I'm using Swing. I have JFrame and inside it I want to have two JPanels - one for score and so on and second below, for actual game. I read that every JPanel has its own coordinates, so point (0, 0) is on the upper-left corner of that panel.
I override method paintComponent() im my class GameView which displays the game (so it's the second JPanel from these I mentioned). But when I want to draw something in upper-left corner of gameView and set coordinates of that image to (0,0) it draws on BarView.
I read many tutorials and posts about drawing and I don't see what am I doing wrong. So my question is, how to draw something using JPanel coordinates, not JFrame ones? Here's some code:
Adding objects extending JPanel to JFrame:
GameView v = new GameView();
BarView bv = new BarView();
frame.getContentPane().add(bv);
frame.getContentPane().add(v);
frame.setVisible(true);
v.requestFocus();
v.repaint();
bv.repaint();
Drawing in JPanel:
public class GameView extends JPanel implements View, Commons{
public static final int WIDTH=WINDOW_WIDTH, HEIGHT=ARENA_HEIGHT;
private GameScene gameScene;
private TexturePaint paint;
private BufferedImage bi;
public GameView(){
addKeyListener(new CustomKeyListener());
addMouseMotionListener(new CustomMouseListener());
setSize(WINDOW_WIDTH, ARENA_HEIGHT);
setFocusable(true);
try {
bi = ImageIO.read(new File("src/res/texture.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.paint = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
}
#Override
public void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D) g1;
g.setPaint(paint);
g.fillRect(0, 0, WINDOW_WIDTH, ARENA_HEIGHT);
for(Iterator<Drawable> it = gameScene.models.iterator(); it.hasNext();)
{
Drawable d = it.next();
d.draw(g1);
}
Toolkit.getDefaultToolkit().sync();
}
and method draw of model in gameScene usually looks like this:
public void draw(Graphics g1){
Graphics2D g = (Graphics2D) g1.create();
int cx = image.getWidth(null) / 2;
int cy = image.getHeight(null) / 2;
g.rotate(rotation, cx+x, cy+y);
g.drawImage(image, x, y, null);
}
It looks like you haven't specifed a LayoutManager for your frame, so it will default to BorderLayout.
When you subsequently call frame.getContentPane().add(component) without passing in a position constant, the position BorderLayout.CENTER will be defaulted.
The result is that your GameView and BarView components will be rendered on top of each other.
As a quick test, try specifying the component position as follows:
GameView v = new GameView();
BarView bv = new BarView();
frame.getContentPane().add(bv, BorderLayout.LINE_START);
frame.getContentPane().add(v, BorderLayout.LINE_END);
Unless your UI is really simple, you'll probably find that you need to use some other layout manager. Refer to 'How to Use Various Layout Managers' for more on this subject.
Below code is my view where I get display a streaming camera. Controller handles sets the image and repaints the JInternalFrame. I have an issue with this because the camera image covers the whole JInternalFrame even the title bar. I tried using JPanel but I had problems getting the image on the JPanel because I extend JInternalFrame.
public class CameraView extends JInternalFrame{
private BufferedImage image;
public CameraView(){
super("Camera", false,false,false, false);
setSize(500,500);
setLocation(200,200);
}
#Override
public void paint(Graphics g){
g.drawImage(image, 0, 0, null);
}
public void setImage(BufferedImage image){
this.image = image;
}
}
This is what it looks like. No title bar.
You're overriding the paint method of the frame. This paint method is what draws the title bar.
You should create a second class that extends JComponent, override the paint method on that class, and then add an instance of it to your frame.
Something like:
public class CameraView extends JInternalFrame{
private BufferedImage image;
public CameraView(){
super("Camera", false,false,false, false);
setLocation(200,200);
add(new JComponent() {
{
setSize(500, 500); // size of the image
}
#Override
public void paint(Graphics g){
g.drawImage(image, 0, 0, null);
}
});
pack();
}
public void setImage(BufferedImage image){
this.image = image;
}
}
You could also have the paint method call super.paint(g); but the way you have it set up now, your image will overlay the title bar.
Also, calling super("Camera", false,false,false, false); is the same as calling super("Camera"). The defaults are false.
my goal is to draw some bufferedimage onto another. then all this stuff draw onto some other bufferedimage and so on. And finally draw this on top of a panel.
For now i'm trying to draw bufferedimage onto panel and nothing works. My bufferedimage looks completely white:
public class Main2 {
public static void main(String[] args) {
JFrame frame = new JFrame("asdf");
final JPanel panel = (JPanel) frame.getContentPane();
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
somepaint(panel);
}
});
}
private static void somepaint(JPanel panel) {
BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
image.getGraphics().setColor(Color.red);
image.getGraphics().fillRect(0, 0, 200, 200);
Graphics2D graphics = (Graphics2D) panel.getGraphics();
graphics.setColor(Color.magenta);
graphics.fillRect(0, 0, 500, 500);
graphics.drawImage(image, null, 0, 0); // draws white square instead of red one
}
}
thanks
Re:
private static void somepaint(JPanel panel) {
BufferedImage image = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);
image.getGraphics().setColor(Color.red);
image.getGraphics().fillRect(0, 0, 200, 200);
Graphics2D graphics = (Graphics2D) panel.getGraphics();
This is not how you draw inside of a JPanel or JComponent.
Don't call getGraphics() on a component as the Graphics object returned will be short-lived, and anything drawn with it will not persist. Instead do your JPanel's drawing inside of its paintComponent(Graphics G) method override. You will need to create a class that extends JPanel in order to override paintComponent(...).
Most importantly, to see how to do Swing graphics correctly, don't guess. You'll want to read the Swing Graphics Tutorials first as it will require you to toss out some incorrect assumptions (I know that this is what I had to do to get it right).
You need to rectify your parameters in the drawImage() call. Change this:
graphics.drawImage(image, null, 0, 0);
to
graphics.drawImage(image, 0, 0,null);
Check the Java docs for more details.
How do I set a background image to a JTextPane - some sort of a watermark.
I tried this option - creating a child class of JTextPane and use the paint method to draw the image.
But then the text is displayed "below" the image than above.
Is there any "standard" or "well known" way to do this?
(BTW, I tried (something silly?) making the content type "text/html", and setting the image as the background image of a <div> but it did not help.)
Here's a working example:
import javax.swing.*;
import java.awt.*;
public class ScratchSpace {
public static void main(String[] args) {
JFrame frame = new JFrame("");
final MyTextPane textPane = new MyTextPane();
frame.add(textPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class MyTextPane extends JTextPane {
public MyTextPane() {
super();
setText("Hello World");
setOpaque(false);
// this is needed if using Nimbus L&F - see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6687960
setBackground(new Color(0,0,0,0));
}
#Override
protected void paintComponent(Graphics g) {
// set background green - but can draw image here too
g.setColor(Color.GREEN);
g.fillRect(0, 0, getWidth(), getHeight());
// uncomment the following to draw an image
// Image img = ...;
// g.drawImage(img, 0, 0, this);
super.paintComponent(g);
}
}
}
The important things to note:
your component must not be opaque...
so setOpaque(false);
override paintComponent(Graphics g), not paint.
paint your background, with an image
or drawing BEFORE calling
super.paintComponent(g);
If you want to master this stuff, I recommend reading "Filthy Rich Clients", a book all about how to bend Swing to your will.
Try changing the paint code to this.
public void paint(Graphics g)
{
g.setXORMode(Color.white);
g.drawImage(image,0, 0, this);
super.paint(g);
}
This would make your image to be painted before the text is rendered by the actual component's paint method.
Hmm., put a background image to the JFrame/JPanel containg the JTextPane,.. and keep the JTextPane transparent to some level.