How do I crop an image in Java? - java

I want to crop an image manually using the mouse.
Suppose the image has some text, and I want to select some text from an image, then
for that purpose I want to crop that area by using the mouse.

The solution I found most useful for cropping a buffered image uses the getSubImage(x,y,w,h);
My cropping routine ended up looking like this:
private BufferedImage cropImage(BufferedImage src, Rectangle rect) {
BufferedImage dest = src.getSubimage(0, 0, rect.width, rect.height);
return dest;
}

There are two potentially major problem with the leading answer to this question. First, as per the docs:
public BufferedImage getSubimage(int x,
int y,
int w,
int h)
Returns a subimage defined by a specified rectangular region.
The returned BufferedImage shares the same data array as the original
image.
Essentially, what this means is that result from getSubimage acts as a pointer which points at a subsection of the original image.
Why is this important? Well, if you are planning to edit the subimage for any reason, the edits will also happen to the original image. For example, I ran into this problem when I was using the smaller image in a separate window to zoom in on the original image. (kind of like a magnifying glass). I made it possible to invert the colors to see certain details more easily, but the area that was "zoomed" also got inverted in the original image! So there was a small section of the original image that had inverted colors while the rest of it remained normal. In many cases, this won't matter, but if you want to edit the image, or if you just want a copy of the cropped section, you might want to consider a method.
Which brings us to the second problem. Fortunately, it is not as big a problem as the first. getSubImage shares the same data array as the original image. That means that the entire original image is still stored in memory. Assuming that by "crop" the image you actually want a smaller image, you will need to redraw it as a new image rather than just get the subimage.
Try this:
BufferedImage img = image.getSubimage(startX, startY, endX, endY); //fill in the corners of the desired crop location here
BufferedImage copyOfImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = copyOfImage.createGraphics();
g.drawImage(img, 0, 0, null);
return copyOfImage; //or use it however you want
This technique will give you the cropped image you are looking for by itself, without the link back to the original image. This will preserve the integrity of the original image as well as save you the memory overhead of storing the larger image. (If you do dump the original image later)

This is a method which will work:
import java.awt.image.BufferedImage;
import java.awt.Rectangle;
import java.awt.Color;
import java.awt.Graphics;
public BufferedImage crop(BufferedImage src, Rectangle rect)
{
BufferedImage dest = new BufferedImage(rect.getWidth(), rect.getHeight(), BufferedImage.TYPE_ARGB_PRE);
Graphics g = dest.getGraphics();
g.drawImage(src, 0, 0, rect.getWidth(), rect.getHeight(), rect.getX(), rect.getY(), rect.getX() + rect.getWidth(), rect.getY() + rect.getHeight(), null);
g.dispose();
return dest;
}
Of course you have to make your own JComponent:
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.Rectangle;
import java.awt.Graphics;
import javax.swing.JComponent;
public class JImageCropComponent extends JComponent implements MouseListener, MouseMotionListener
{
private BufferedImage img;
private int x1, y1, x2, y2;
public JImageCropComponent(BufferedImage img)
{
this.img = img;
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void setImage(BufferedImage img)
{
this.img = img;
}
public BufferedImage getImage()
{
return this;
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(img, 0, 0, this);
if (cropping)
{
// Paint the area we are going to crop.
g.setColor(Color.RED);
g.drawRect(Math.min(x1, x2), Math.min(y1, y2), Math.max(x1, x2), Math.max(y1, y2));
}
}
#Override
public void mousePressed(MouseEvent evt)
{
this.x1 = evt.getX();
this.y1 = evt.getY();
}
#Override
public void mouseReleased(MouseEvent evt)
{
this.cropping = false;
// Now we crop the image;
// This is the method a wrote in the other snipped
BufferedImage cropped = crop(new Rectangle(Math.min(x1, x2), Math.min(y1, y2), Math.max(x1, x2), Math.max(y1, y2));
// Now you have the cropped image;
// You have to choose what you want to do with it
this.img = cropped;
}
#Override
public void mouseDragged(MouseEvent evt)
{
cropping = true;
this.x2 = evt.getX();
this.y2 = evt.getY();
}
//TODO: Implement the other unused methods from Mouse(Motion)Listener
}
I didn't test it. Maybe there are some mistakes (I'm not sure about all the imports).
You can put the crop(img, rect) method in this class.
Hope this helps.

File fileToWrite = new File(filePath, "url");
BufferedImage bufferedImage = cropImage(fileToWrite, x, y, w, h);
private BufferedImage cropImage(File filePath, int x, int y, int w, int h){
try {
BufferedImage originalImgage = ImageIO.read(filePath);
BufferedImage subImgage = originalImgage.getSubimage(x, y, w, h);
return subImgage;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

This question has not enough information to answer. A general solution (depending on your GUI framework): add a mouse event handler that will catch clicks and mouse movements. This will give you your (x, y) coordinates. Next use these coordinates to crop your image.

You need to read about Java Image API and mouse-related API, maybe somewhere under the java.awt.event package.
For a start, you need to be able to load and display the image to the screen, maybe you'll use a JPanel.
Then from there, you will try implement a mouse motion listener interface and other related interfaces. Maybe you'll get tied on the mouseDragged method...
For a mousedragged action, you will get the coordinate of the rectangle form by the drag...
Then from these coordinates, you will get the subimage from the image you have and you sort of redraw it anew....
And then display the cropped image... I don't know if this will work, just a product of my imagination... just a thought!

I'm giving this example because this actually work for my use case.
I was trying to use the AWS Rekognition API.
The API returns a BoundingBox object:
BoundingBox boundingBox = faceDetail.getBoundingBox();
The code below uses it to crop the image:
import com.amazonaws.services.rekognition.model.BoundingBox;
private BufferedImage cropImage(BufferedImage image, BoundingBox box) {
Rectangle goal = new Rectangle(Math.round(box.getLeft()* image.getWidth()),Math.round(box.getTop()* image.getHeight()),Math.round(box.getWidth() * image.getWidth()), Math.round(box.getHeight() * image.getHeight()));
Rectangle clip = goal.intersection(new Rectangle(image.getWidth(), image.getHeight()));
BufferedImage clippedImg = image.getSubimage(clip.x, clip.y , clip.width, clip.height);
return clippedImg;
}

Related

PaintComponent not rendering image?

I'm trying to display a small 16x16 pixel image to a JPanel. I've written a simple function that creates and returns a BufferedImage object that I'm then trying to pass to paintComponent, however, nothing is rendering when I launch the program (with no errors being printed to the console). Help?
Thanks!
public BufferedImage loadImage(String filePath) {
BufferedImage img = null;
try {
img = ImageIO.read(new File(filePath));
} catch (IOException e) {
}
return img;
}
public void paintComponent(Graphics g) {
g.drawImage(loadImage("/resources/tile.png"), 0, 0, this);
}
}
we can also use the below source code inside paintComponent to draw an image swing.
public void paintComponent(Graphics g) {
Toolkit t=Toolkit.getDefaultToolkit();
Image image=t.getImage(<path of image>);
g.drawImage(image, 0,0,this);
}
where,
img - the specified image to be drawn. This method does nothing if img is null.
x - the x coordinate.
y - the y coordinate.
observer - object to be notified as more of the image is converted.
in case to load an bufferImage, you can also check the below link contains good amount of details.
https://www.baeldung.com/java-images
Thanks

How to rotate a bitmap with AffineTransform

I'm trying to create a simple program that displays a bitmap zombie picture and then rotates it around using AffineTransform and Thread. I followed the example I had to accomplish this, but whenever I run the program the zombie bitmap just rotates once and stops. Also, for some reason when I painted the zombie bitmap, the image is partially off the screen along the y-axis.
So my questions are: why is the bitmap not rotating and why is the bitmap off the screen.
Code is as follows:
import java.awt.*;// Graphics class
import java.awt.geom.*;
import java.net.*;//URL navigation
import javax.swing.*;//JFrame
import java.util.*;//Toolkit
public class BitMapZombies2 extends JFrame implements Runnable
{
private Image zombieOneRight;
Thread zombieRun;
public static void main (String[] args)
{
new BitMapZombies2();
}
public BitMapZombies2()
{
super("Bit Map Zombies..RUN FOR YOUR LIFE!!!");
setSize(800,600);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Toolkit Zkit = Toolkit.getDefaultToolkit();
zombieOneLeft = Zkit.getImage(getURL("_images/_production_images/zombie_1_left_75h.png"));
zombieOneRight = Zkit.getImage(getURL("_images/_production_images/zombie_1_right_75h.png"));
zombieRun = new Thread(this);
zombieRun.start();
}
AffineTransform zombieIdentity = new AffineTransform();
private URL getURL(String filename)
{
URL url = null;
try
{
url = this.getClass().getResource(filename);
}
catch (Exception e) {}
return url;
}
public void paint(Graphics z)
{
Graphics2D z2d = (Graphics2D) z;
AffineTransform ZombiePowered = new AffineTransform();
z2d.setColor(Color.BLACK);
z2d.fillRect(0,0, 800, 600);
ZombiePowered.setTransform(zombieIdentity);
ZombiePowered.rotate(2,37.5,37.5);
z2d.drawImage(zombieOneRight,ZombiePowered,this);
}
public void run()
{
Thread zT = Thread.currentThread();
while (zT == zombieRun)
{
try
{
Thread.sleep(500);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
}
}
Appreciate any help I can get on this.
Commenting your code:
AffineTransform ZombiePowered = new AffineTransform();//Create an Id. transform
ZombiePowered.setTransform(zombieIdentity);//Set it as a copy of an Id. transform
ZombiePowered.rotate(2, 37.5, 37.5);//Concatenate the rotation with the Id. transform
z2d.drawImage(zombieOneRight, ZombiePowered, this);//Apply the rotation
So your always rotating 2rads your image. If you do this assignment at the end of the paint method:
zombieIdentity = ZombiePowered;
the next time you paint the image it will be rotate 2rads more.
About the issues with the position, take a look at rotate javadoc:
Concatenates this transform with a transform that rotates coordinates
around an anchor point. This operation is equivalent to translating
the coordinates so that the anchor point is at the origin (S1), then
rotating them about the new origin (S2), and finally translating so
that the intermediate origin is restored to the coordinates of the
original anchor point (S3).
This operation is equivalent to the following sequence of calls:
translate(anchorx, anchory); // S3: final translation
rotate(theta); // S2: rotate around anchor
translate(-anchorx, -anchory); // S1: translate anchor to origin
Hope it helps.
Once you've created your transformation, you need to apply it to the graphics context...
public void paint(Graphics z)
{
//...
z2d.setTransform(ZombiePowered);
//...
}
Once you apply a transformation, it will effect everything painted to the Graphics context after it so you need to reset it or referse it. There's lots of ways to do this, but the simplest would be to create a copy of the Graphics context and simply dispose of it when you no longer need it...
public void paint(Graphics z)
{
Graphics2D z2d = (Graphics2D) z.create();
//...
z2d.dispose();
}
Also, this is just me, but I'd be creating a new instance of the AffineTransform, these things are to easy to completely screw up...

Adding image to JFrame

So I am using Eclipse with Windows builder. I was just wondering if there was anyway I can import an image that'll show up on the JFrame that I can easily move around and re-size instead of setting the location and size and drawing it.
Here is a simple example of adding an image to a JFrame:
frame.add(new JLabel(new ImageIcon("Path/To/Your/Image.png")));
There is no specialized image component provided in Swing (which is sad in my opinion). So, there are a few options:
As #Reimeus said: Use a JLabel with an icon.
Create in the window builder a JPanel, that will represent the location of the image. Then add your own custom image component to the JPanel using a few lines of code you will never have to change. They should look like this:
JImageComponent ic = new JImageComponent(myImageGoesHere);
imagePanel.add(ic);
where JImageComponent is a self created class that extends JComponent that overrides the paintComponent() method to draw the image.
If you are using Netbeans to develop, use JLabel and change its icon property.
As martijn-courteaux said, create a custom component it's the better option.
In C# exists a component called PictureBox and I tried to create this component for Java, here is the code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
public class JPictureBox extends JComponent {
private Icon icon = null;
private final Dimension dimension = new Dimension(100, 100);
private Image image = null;
private ImageIcon ii = null;
private SizeMode sizeMode = SizeMode.STRETCH;
private int newHeight, newWidth, originalHeight, originalWidth;
public JPictureBox() {
JPictureBox.this.setPreferredSize(dimension);
JPictureBox.this.setOpaque(false);
JPictureBox.this.setSizeMode(SizeMode.STRETCH);
}
#Override
public void paintComponent(Graphics g) {
if (ii != null) {
switch (getSizeMode()) {
case NORMAL:
g.drawImage(image, 0, 0, ii.getIconWidth(), ii.getIconHeight(), null);
break;
case ZOOM:
aspectRatio();
g.drawImage(image, 0, 0, newWidth, newHeight, null);
break;
case STRETCH:
g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), null);
break;
case CENTER:
g.drawImage(image, (int) (this.getWidth() / 2) - (int) (ii.getIconWidth() / 2), (int) (this.getHeight() / 2) - (int) (ii.getIconHeight() / 2), ii.getIconWidth(), ii.getIconHeight(), null);
break;
default:
g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), null);
}
}
}
public Icon getIcon() {
return icon;
}
public void setIcon(Icon icon) {
this.icon = icon;
ii = (ImageIcon) icon;
image = ii.getImage();
originalHeight = ii.getIconHeight();
originalWidth = ii.getIconWidth();
}
public SizeMode getSizeMode() {
return sizeMode;
}
public void setSizeMode(SizeMode sizeMode) {
this.sizeMode = sizeMode;
}
public enum SizeMode {
NORMAL,
STRETCH,
CENTER,
ZOOM
}
private void aspectRatio() {
if (ii != null) {
newHeight = this.getHeight();
newWidth = (originalWidth * newHeight) / originalHeight;
}
}
}
If you want to add an image, choose the JPictureBox, after that go to Properties and find "icon" property and select an image.
If you want to change the sizeMode property then choose the JPictureBox, after that go to Properties and find "sizeMode" property, you can choose some values:
NORMAL value, the image is positioned in the upper-left corner of the JPictureBox.
STRETCH value causes the image to stretch or shrink to fit the JPictureBox.
ZOOM value causes the image to be stretched or shrunk to fit the JPictureBox; however, the aspect ratio in the original is maintained.
CENTER value causes the image to be centered in the client area.
If you want to learn more about this topic, you can check this video.
Also you can see the code on Gitlab or Github.

JLayeredPane repaint removes some images

I need a UI where i want to depict a network device's graphical representation in swing. For this, i am just loading multiple images and overlap them one by one to show the current state of the device. I have to support zooming for this view. The zoom code looks like this.
public void zoom(double factor){
if (factor < MIN_ZOOM_VALUE)
factor = MIN_ZOOM_VALUE;
else if (factor > MAX_ZOOM_VALUE)
factor = MAX_ZOOM_VALUE;
scaleFactor = factor;
layeredPane.revalidate();
layeredPane.repaint();
}
The images are added as labels.
private class ImageLabel extends JLabel{
private ImageIcon image;
private Position position;
public ImageLabel(ImageIcon image, Position position){
super(image);
this.image = image;
this.position = position;
}
public void paintComponent(Graphics g) {
int newX = (int)(position.getLeft() * scaleFactor);
int newY = (int)(position.getTop() * scaleFactor);
Graphics2D g2 = (Graphics2D)g;
int newW = (int)(position.getWidth() * scaleFactor);
int newH = (int)(position.getHeight() * scaleFactor);
setBounds(newX, newY, newW, newH);
g2.setRenderingHint(
//RenderingHints.KEY_INTERPOLATION,
//RenderingHints.VALUE_INTERPOLATION_BILINEAR);
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(image.getImage(), 0, 0, newW, newH, null);
}
}
But the problem here is, when i zoom in once and zoom out, some of the images disappear. Any idea why it behaves like this?
+1 to #AndrewThompsons comment.
The only other problem I can see is you dont honor the paint chain.
Also always remember to honor the paint chain by calling super.XXX implementation of overriden paint methods (and for that matter any overriden method), unless you know what you are doing and are purposely not calling their supers implementation, or else visual artifacts like the ones you describe could/will happen.
To do this you would call super.paintComponent(Graphics g) as the first method in overridden paintComponent like so:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//do other drawings here
}
Also note how I use #Override annotation so that I am sure I am overriding the correct method.

Creating custom JButton from images containing transparent pixels

Read edit 2 for what I'm actually missing to make it work
I'm currently trying to create some custom JButtons using images created in photoshop that have an alpha parameter.
So far, overriding the paint() method to draw the image has worked in the sense that the button is drawn showing the correct image. I'd like to improve it, though, by making its shape (clickable area) the same as the visible pixels on the image (right now if I draw the button's border, it's a square).
Is there an easy way to do that or do I have to parse the image and find the alpha pixels to make a custom border?
Which methods would I have to override to make it work the way I want?
Also, another question I'm going to have later: would it be better to use some kind of algorithm to change the images' colors to make it seem like it is being clicked when people click on it or am I better off creating a second image and drawing that one while the button is active?
Edit: I just read on some other question that I should redefine paintComponent() instead of paint(), I'd like to know why since redefining paint() works fine?
Edit 2: I changed everything to make sure my JButtons are created using the default constructor with an icon. What I'm trying to do is get the X and Y position of where the click was registered and grab the icon's pixel at that position and check its alpha channel to see if it is 0 (if it is, do nothing, else do the action it is supposed to do).
The thing is, the alpha channel always returns 255 (and blue, red and green are at 238 on transparent pixels). On other pixels, everything returns the value it should be returning.
Here's an example (try it with another image if you want) that recreates my problem:
public class TestAlphaPixels extends JFrame
{
private final File FILECLOSEBUTTON = new File("img\\boutonrondX.png"); //My round button with transparent corners
private JButton closeButton = new JButton(); //Creating it empty to be able to place it and resize the image after the button size is known
public TestAlphaPixels() throws IOException
{
setLayout(null);
setSize(150, 150);
closeButton.setSize(100, 100);
closeButton.setContentAreaFilled(false);
closeButton.setBorderPainted(false);
add(closeButton);
closeButton.addMouseListener(new MouseListener()
{
public void mouseClicked(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
System.out.println("Alpha value of pixel (" + e.getX() + ", " + e.getY() + ") is: " + clickAlphaValue(closeButton.getIcon(), e.getX(), e.getY()));
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
});
Image imgCloseButton = ImageIO.read(FILECLOSEBUTTON);
//Resize the image to fit the button
Image newImg = imgCloseButton.getScaledInstance((int)closeButton.getSize().getWidth(), (int)closeButton.getSize().getHeight(), java.awt.Image.SCALE_SMOOTH);
closeButton.setIcon(new ImageIcon(newImg));
}
private int clickAlphaValue(Icon icon, int posX, int posY)
{
int width = icon.getIconWidth();
int height = icon.getIconHeight();
BufferedImage tempImage = (BufferedImage)createImage(width, height);
Graphics2D g = tempImage.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
int alpha = (tempImage.getRGB(posX, posY) >> 24) & 0x000000FF;
return alpha;
}
public static void main(String[] args)
{
try
{
TestAlphaPixels testAlphaPixels = new TestAlphaPixels();
testAlphaPixels.setVisible(true);
testAlphaPixels.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
This is just a wild guess, but is it possible that when my image gets cast to an Icon, it loses its Alpha property and thus doesn't return the correct value? Anyway, I'd really appreciate it if someone could actually help me out and tell me what I should be changing to get the correct value.
I'm guessing that because when I try it with the original image, the alpha channel's value is fine, but I can't actually use that BufferedImage because I resize it, so I actually get the channel values of the image with the original size...
I think you are on the wrong way. You do not have to override neither paint() nor paintComponent() methods. JButton already "knows" to be shown with image only:
ImageIcon cup = new ImageIcon("images/cup.gif");
JButton button2 = new JButton(cup);
See the following tutorial for example: http://www.apl.jhu.edu/~hall/java/Swing-Tutorial/Swing-Tutorial-JButton.html
Moreover swing is fully customized. You can control opacity, border, color etc. You probably should override some mentioned methods to change functionality. But in most cases there is better and simpler solution.
Since there were good elements in multiple answers, but none of the answers were complete on their own, I'll answer my own question so other people that have the same problem can try something similar.
I created my buttons using a new class which extends JButton, with a new constructor that takes a BufferedImage as parameter instead of an icon. The reason for that is that when I did something like myButton.getIcon(), it would return an Icon, then I'd have to make various manipulations on it to make it a BufferedImage of the right size, and it ended up not working anyway because it seems like the first cast to Icon made it lose the alpha data in the pixels, so I couldn't check to see if the user was clicking on transparent pixels or not.
So I did something like this for the constructor:
public class MyButton extends JButton
{
private BufferedImage bufImg;
public MyButton(BufferedImage bufImg)
{
super(new ImageIcon(bufImg));
this.bufImg = bufImg;
}
}
Then I created an accessor for my bufImg that resized the image to fit the JButton using the getSize() method and then returned an image resized at the right size. I do the transformations in the getBufImg() accessor because the image size might change when the window gets resized. When you call the getBufImg(), it's usually because you clicked on the button and thus you're not currently resizing the window.
Something a little bit like this will return the image at the right size:
public BufferedImage getBufImg()
{
BufferedImage newImg = new BufferedImage(getSize().getWidth(), getSize().getHeight(), BufferedImage.TYPE_INT_ARGB); //Create a new buffered image the right size
Graphics2D g2d = newImg.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(bufImg, 0, 0, getSize().getWidth(), getSize().getHeight(), null);
g2d.dispose();
return newImg;
}
With that buffered image, you can then code a method like this:
private int clickAlphaValue(BufferedImage bufImg, int posX, int posY)
{
int alpha;
alpha = (bufImg.getRGB(posX, posY) >>24) & 0x000000FF; //Gets the bit that contains alpha information
return alpha;
}
That you call on the button that implements a MouseListener, like this:
myButton.addMouseListener(new MouseListener()
{
public void mouseClicked(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
if(clickAlphaValue(((myButton)e.getSource()).getBufImg(), e.getX(), e.getY()) != 0) //If alpha is not set to 0
System.exit(0); //Or other things you want your button to do
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
});
And voila! The button will only do the action if you clicked on non-transparent pixels.
Thanks for the help everyone, I couldn't have come up with this solutions on my own.
If you want to have shape-specific click points, you're better off using Shape and their contains method. If you want, you can create a shape when creating your custom button class as part of it, and implement a contains method by wrapping around the shape's contains method.
As for the custom JButton, create a class that extends JButton, like this:
import java.awt.*;
import javax.swing.*;
public class CustomButton extends JButton{
/** Filename of the image to be used as the button's icon. */
private String fileName;
/** The width of the button */
private int width;
/** The height of the button. */
private int height;
public CustomButton(String fileName, int width, int height){
this.fileName = fileName;
this.width = width;
this.height = height;
createButton();
}
/**
* Creates the button according to the fields set by the constructor.
*/
private void createButton(){
this.setIcon(getImageIcon(filename));
this.setPreferredSize(new Dimension(width, height));
this.setMaximumSize(new Dimension(width, height));
this.setFocusPainted(false);
this.setRolloverEnabled(false);
this.setOpaque(false);
this.setContentAreaFilled(false);
this.setBorderPainted(false);
this.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
}
}
Here's how you can load the ImageIcon, if you want to do it like this.
public ImageIcon getImageIcon(String fileName){
String imageDirectory = "images/"; //relative to classpath
URL imgURL = getClass().getResource(imageDirectory + fileName);
return new ImageIcon(imgURL);
}
This will give you a button that will at least look like your image.
I asked a similar question regarding Image-based events on click, and Shapes helped wonders.
I guess it comes down to how complex your button images are.
Here's reference anyway:
How can you detect a mouse-click event on an Image object in Java?
PS: Maybe look into generating shapes from images, that go around all the pixels that aren't transparent. No idea if this is possible, but it would mean that a button would only be "pressed" if the user clicks on the image part of it. Just a thought.
If you want your button layout to be that of the non-transparent pixels in your image, then you should redefine the paintComponent() method. It is the most correct way of doing it (overriding paint() worked in old times but is now discouraged).
However I think it is not exactly what you want: you want a click on the button to be detected only if it is on a non-transparent pixel, right? In that case you have to parse your image and when clicked compare mouse coordinates to the pixel alpha channel of your image as JButton does not have such a feature.
If you have a round button, this is exactly what you need:
public class RoundButton extends JButton {
public RoundButton() {
this(null, null);
}
public RoundButton(Icon icon) {
this(null, icon);
}
public RoundButton(String text) {
this(text, null);
}
public RoundButton(Action a) {
this();
setAction(a);
}
public RoundButton(String text, Icon icon) {
setModel(new DefaultButtonModel());
init(text, icon);
if(icon==null) return;
setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
setContentAreaFilled(false);
setFocusPainted(false);
initShape();
}
protected Shape shape, base;
protected void initShape() {
if(!getBounds().equals(base)) {
Dimension s = getPreferredSize();
base = getBounds();
shape = new Ellipse2D.Float(0, 0, s.width, s.height);
}
}
#Override public Dimension getPreferredSize() {
Icon icon = getIcon();
Insets i = getInsets();
int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
return new Dimension(iw+i.right+i.left, iw+i.top+i.bottom);
}
#Override public boolean contains(int x, int y) {
initShape();
return shape.contains(x, y);
//or return super.contains(x, y) && ((image.getRGB(x, y) >> 24) & 0xff) > 0;
}
}
JButton has a contains() method. Override it and call it on mouseReleased();
paintComponent() instead of paint() depends if you paint() inside XxxButtonUI or just override paintComponent(), but there exists the option JButton#setIcon.

Categories