How do I make a dynamic image in Java Swing? - java

How can i make my image dynamic; My code :
public void paint(Graphics g){
super.paint(g);
//this.setBackground(Color.white);
g.drawImage(bg,300 , 70, 800, 100, null);
}
That creates an image but when i open the window and i enlarge it; the images takes the right position.

First look at the method you are calling.
drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
It sounds like you need to know x and y values to paint your image in the correct location. Lets pretend this paint method is inside of a component.
//based on your description.
int imageWidth = 800;
int imageHeight = 100;
int x = (getWidth() - imageWidth)/2;
int y = (getHeight() - imageHeight)/2;
g.drawImage(bg, x, y, imageWidth, imageHeight, this);
That should center your image when the component is resized and repainted. Of course there are better ways to do this, such as using a JLabel and a layout manager.

Related

Fit image into JPanel

I'm trying scale an image so it will always fit my JPanel. Unfortunately using this method I don't always receive an Image I wanted to receive. Mostly it is zoomed and I would rather have the whole image but scaled.
Thats the class that creates the image. 600 is the PanelWidth and 400 is the PanelHeight.
Any ideas what goes wrong?
public class Image extends Component{
private BufferedImage img;
protected int width;
protected int height;
private String path;
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
double scale = getScale(600,400,img.getWidth(),img.getHeight());
double xPos = (600 - scale * img.getWidth())/2;
double yPos = (400 - scale *img.getHeight())/2;
AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);
at.scale(scale, scale);
g2.drawRenderedImage(img, at);
System.out.println(scale);
}
public Image(String path){
try{
img = ImageIO.read(new File(path));
} catch (IOException e) { }
this.width=img.getWidth();
this.height=img.getHeight();
this.path = path;
}
public double getScale(int panelWidth, int panelHeight, int imageWidth, int imageHeight){
double scale = 1;
double xScale, yScale;
if(imageWidth > panelWidth || imageHeight > panelHeight){
xScale = (double)imageWidth/panelWidth;
yScale = (double)imageHeight/panelHeight;
scale = Math.max(xScale, yScale);
}else if(imageWidth < panelWidth && imageHeight < panelHeight){
xScale = (double)panelWidth/imageWidth;
yScale = (double)panelHeight/imageHeight;
scale = Math.max(xScale, yScale);
}else{
scale = 1;
}
return scale;
}
A JPanel is a Swing component which implies you are using Swing.
For custom painting you should extend JPanel or JComponent. Most people use JPanel because it will clear the background of the component for you.
Custom painting of a Swing component is done by overriding paintComponent(...)
so it will always fit my JPanel
Define "fit"?
Assuming you are trying to scale the image to retain its original proportions you could to something like:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
double imageWidth = image.getWidth(null);
double imageHeight = image.getHeight(null);
double factor = Math.min(getWidth() / imageWidth, getHeight() / imageHeight);
int width = (int)(image.getWidth(null) * factor);
int height = (int)(image.getHeight(null) * factor);
g.drawImage(image, 0, 0, width, height, this);
}
If you are just trying to fit the image on the panel then you do:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
You don't need to use float if you do your operations in the right order. Assuming imageWidth, imageHeight, panelWidth are all int:
// Calculate the width of the scaled image; if the image is wider than the
// panel, use the panel width, otherwise use the image width (i.e. don't upscale)
int scaledWidth = Math.min(imageWidth, panelWidth);
// Given the scaled width, calculate the scaled height
// Force it to be at least 1 pixel, since if you have an image that's wider than
// the panel and only 1 pixel tall, this will scale to zero height, which you
// don't want
int scaledHeight = Math.max(1, imageHeight * scaledWidth / imageWidth);
The above assumes you want to fit the width and will be providing a scrolling mechanism if the image height exceeds the panel height. If you want to fit height instead (and horizontal scroll for overflow) just make the necessary changes in variables.

Swing simple overlay [duplicate]

This question already has an answer here:
Clear portion of graphics with underlying image
(1 answer)
Closed 7 years ago.
I'm in the process of making a 2D game in which a player roams around a maze.
I want to implement some sort of "darkness", even something as simple as a transparent shape around the player surrounded by black, like this:
The problem I've found using Swing is that, while this is possible, it means having to redraw everything, which produces an annoying "flickering" effect every time it happens. Is there a way to make some sort of overlay, or just a good way of doing this in general in Swing? I'm not very experienced with GUI/visual stuff right now so I'd like to stick with Swing if possible.
EDIT: This is my method to paint the background, i.e. the floor, walls and exit:
public final void paintBG(Graphics g){
g.setColor(Color.LIGHT_GRAY); // Screen background
g.fillRect(0, 0, getWidth(), getHeight());
// Draw the Walls of the maze
// scalex and y are for scaling images/walls within the maze since I let users specify how big they want the maze
for (int j = 0; j < this.height; j++, y += scaley) {
x = 20;
for (int i = 0; i < this.width; i++, x += scalex) {
if (!(maze[j][i].northwall.isBroken())) // If the north wall isn't broken
{
g.drawImage(walltile, x, y, scalex, scaley / 5, null); // Draw a wall there (image, xpos, ypos, width, height, observer)
}
if (!(maze[j][i].eastwall.isBroken())) // etc
{
g.drawImage(walltile, x + scalex, y, scalex / 5, scaley, null);
}
if (!(maze[j][i].southwall.isBroken())) {
g.drawImage(walltile, x, y + scaley, scalex, scaley / 5, null);
}
if (!(maze[j][i].westwall.isBroken())) {
g.drawImage(walltile, x, y, scalex / 5, scaley, null);
}
if ((j == mazeinfo.getTargetM()) && (i == mazeinfo.getTargetN())) {
// Draw the exit
g.drawImage(jeep, x + (scalex / 2), y + (scaley / 2), cx, cy, null);
g.setColor(Color.LIGHT_GRAY);
if (maze[j][i].northwall.isEdge()) {
// Paint over the edge creating a 'way out'
g.fillRect(x, y, scalex, scaley / 4);
} else if (maze[j][i].eastwall.isEdge()) {
g.fillRect(x + scalex, y, scalex / 4, scaley);
} else if (maze[j][i].southwall.isEdge()) {
g.fillRect(x, y + scaley, scalex, scaley / 4);
} else if (maze[j][i].westwall.isEdge()) {
g.fillRect(x, y, scalex / 4, scaley);
}
}
}
}
}
I then have "paintPlayer" and "paintEnemy" methods to paint those sprites each time they move. The background only gets painted once, at the start.
Possibilities:
You may be drawing directly in a top level window such as a JFrame. If so, don't draw in the paintComonent method of a JPanel so that you use the automatic double buffering availabe.
You may be reading in an image from within a painting method, and if so, don't. These methods must paint and paint only and must be blindingly fast.
You may not be using a BufferedImage in your painting method but creating an image de-novo, and if so, don't. Draw the BufferedImage using Graphics#drawImage(...).
Perhaps your animation code is off. You may be calling repaint() from within paint or paintComponent, something that should never be done.
And the possible guesses can go on and on...
Edit
Your code shows that you may be re-paint the maze with every painting iteration -- don't do this. Instead draw the above into a BufferedImage, and draw that image within your paintComponent method. Then change the BufferedImage if the walls structurally change.
Note that the maze's logical structure (the non-visual data that tells which wall is open, which is closed) should be part of your program's data, and not its code.
Here in an example of using a LayerUI from Oracle's Swing UI documentation. Just change the AlphaComposite constant to something darker.
The following is a LayerUI subclass that draws a translucent circle wherever the mouse moves inside a panel.
class SpotlightLayerUI extends LayerUI<JPanel> {
private boolean mActive;
private int mX, mY;
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
#Override
public void paint (Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D)g.create();
// Paint the view.
super.paint (g2, c);
if (mActive) {
// Create a radial gradient, transparent in the middle.
java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX, mY);
float radius = 72;
float[] dist = {0.0f, 1.0f};
Color[] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
RadialGradientPaint p =
new RadialGradientPaint(center, radius, dist, colors);
g2.setPaint(p);
g2.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, .6f));
g2.fillRect(0, 0, c.getWidth(), c.getHeight());
}
g2.dispose();
}
#Override
protected void processMouseEvent(MouseEvent e, JLayer l) {
if (e.getID() == MouseEvent.MOUSE_ENTERED) mActive = true;
if (e.getID() == MouseEvent.MOUSE_EXITED) mActive = false;
l.repaint();
}
#Override
protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
Point p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
mX = p.x;
mY = p.y;
l.repaint();
}
}
To keep the spotlight's center updated on your player, create an event for player movement and register the LayerUI to listen for updates. See the setLayerEventMask() example in the JLayer link below.
source: How to Decorate Components with the JLayer Class

Bitmap equivalent to Image.getSource()?

I have been rebuilding the ImageJ library so that it is compatible with android. I am stuck on one of the constructors in PixelGrabber. Bare in mind that I have rebuilt the ImageJ and the awt library so that it takes Bitmap.
My class ColorProcessor constructor
public ColorProcessor(Bitmap img) {
width = img.getWidth();
height = img.getHeight();
pixels = new int[width * height];
PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, pixels, 0, width);
try {
pg.grabPixels();
} catch (InterruptedException e){};
createColorModel();
fgColor = 0xff000000; //black
resetRoi();
}
Creates an object from PixelGrabber class. Here is the constructor for that.
public PixelGrabber(Bitmap img, int x, int y, int w, int h, int[] pix,
int off, int scansize) {
this(img.getSource(), x, y, w, h, pix, off, scansize);
}
There is a red line under getSource(). This code was designed for the JRE so this getSource is referring to java.awt.Image abstract class. Does anyone know what I can replace img.getSource with?
Any help would be much appreciated.

Inner-Transparent Selection Window in Java using GlassPane

I am trying to achieve the following
http://www.qksnap.com/i/3hunq/4ld0v/screenshot.png
I am currently able to draw rectangles successfully on a semi-transparent glasspane background using the following code:
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.black); // black background
g.fillRect(0, 0, frame.getWidth(), frame.getHeight());
g2.setColor(Color.GREEN.darker());
if (getRect() != null && isDrawing()) {
g2.draw(getRect()); // draw our rectangle (simple Rectangle class)
}
g2.dispose();
}
Which works great, however, I would love to have the area within the rectangle be completely transparent while the outside was still darken much like the screenshot above.
Any ideas?
..have the area within the rectangle be completely transparent while the outside was still darken much like the screenshot above.
Create a Rectangle (componentRect) that is the size of the component being painted.
Create an Area (componentArea) of that shape (new Area(componentRect)).
Create an Area (selectionArea) of the selectionRectangle.
Call componentArea.subtract(selectionArea) to remove the selected part.
Call Graphics.setClip(componentArea)
Paint the semi-transparent color.
(Clear the clipping area if more paint operations are required).
As Andrew has suggested (just beat me while I was finishing off my example)
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g.setColor(Color.black); // black background
Area area = new Area();
// This is the area that will filled...
area.add(new Area(new Rectangle2D.Float(0, 0, getWidth(), getHeight())));
g2.setColor(Color.GREEN.darker());
int width = getWidth() - 1;
int height = getHeight() - 1;
int openWidth = 200;
int openHeight = 200;
int x = (width - openWidth) / 2;
int y = (height - openHeight) / 2;
// This is the area that will be uneffected
area.subtract(new Area(new Rectangle2D.Float(x, y, openWidth, openHeight)));
// Set up a AlphaComposite
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2.fill(area);
g2.dispose();
}

JPanel with image background

How to put image background on JPANEL?
JPanel pDraw = new JPanel(new GridLayout(ROWS,COLS,2,2));
pDraw.setPreferredSize(new Dimension(600,600)); //size of the JPanel
pDraw.setBackground(Color.RED); //How can I change the background from red color to image?
It is probably easiest to load the Image into an ImageIcon and display it in a JLabel, however:
To directly 'draw' the image to the JPanel, override the JPanel's paintComponent(Graphics) method to something like the following:
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.drawImage(img, 0, 0, null);
}
where img is an Image (possibly loaded through the ImageIO.read() call).
Graphics#drawImage is a heavily overloaded command which will allow you to be highly specific in how, how much, and where you paint the image to the component.
You can also get 'fancy' and scale the image to your pleasing using the Image#getScaledInstance method. This will take a -1 for either the width or the height parameter in order to keep the aspect ratio of the image the same.
Putting it in a more fancy way:
public void paintComponent(Graphics page)
{
super.paintComponent(page);
int h = img.getHeight(null);
int w = img.getWidth(null);
// Scale Horizontally:
if ( w > this.getWidth() )
{
img = img.getScaledInstance( getWidth(), -1, Image.SCALE_DEFAULT );
h = img.getHeight(null);
}
// Scale Vertically:
if ( h > this.getHeight() )
{
img = img.getScaledInstance( -1, getHeight(), Image.SCALE_DEFAULT );
}
// Center Images
int x = (getWidth() - img.getWidth(null)) / 2;
int y = (getHeight() - img.getHeight(null)) / 2;
// Draw it
page.drawImage( img, x, y, null );
}
Here's an explanation.

Categories