In Java swing, is there any way to draw only the shape of an image, instead of the image itself? And if so, how can I do it?
I added a picture to show you what I mean:
.
My image would consist of a one-colored shape and a transparent background. My intention is to be able to change the images color.
ImageFilter is what you sought. GrayFilter could be used.
class ShapeFilter extends RGBImageFilter {
public RedBlueSwapFilter() {
// The filter's operation does not depend on the
// pixel's location, so IndexColorModels can be
// filtered directly.
canFilterIndexColorModel = true;
}
public int filterRGB(int x, int y, int rgb) {
return (rgb & 0x00_ff_ff_ff) == 0x00_ff_ff_ff // White
|| rgb & 0xff_00_00_00) == 0x00_00_00) // or transparent
? rgb : 0xff_ff_00_00; // Red opaque
}
}
Usage inside a Component (JComponent) with createImage:
Image img = ImageIO.readImage(...);
ImageFilter filter = new ShapeFilter();
ImageProducer producer = new FilteredImageSource(img.getSource(), filter);
Image img2 = createImage(producer);
You could generate a "mask" of the image
The great feature of this technique is that you can generate the mask in what ever color or opacity you want
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
public static void applyQualityRenderingHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
public static BufferedImage generateMask(BufferedImage imgSource, Color color, float alpha) {
int imgWidth = imgSource.getWidth();
int imgHeight = imgSource.getHeight();
BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
Graphics2D g2 = imgMask.createGraphics();
applyQualityRenderingHints(g2);
g2.drawImage(imgSource, 0, 0, null);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
g2.setColor(color);
g2.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
g2.dispose();
return imgMask;
}
public BufferedImage tint(BufferedImage master, BufferedImage tint) {
int imgWidth = master.getWidth();
int imgHeight = master.getHeight();
BufferedImage tinted = createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
Graphics2D g2 = tinted.createGraphics();
applyQualityRenderingHints(g2);
g2.drawImage(master, 0, 0, null);
g2.drawImage(tint, 0, 0, null);
g2.dispose();
return tinted;
}
public class TestPane extends JPanel {
private BufferedImage master;
private BufferedImage mask;
public TestPane() {
try {
master = ImageIO.read(new File(bring your own image));
mask = generateMask(master, Color.RED, 1.0f);
} catch (IOException exp) {
exp.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (master != null && mask != null) {
size = new Dimension(master.getWidth() + mask.getWidth(), Math.max(master.getHeight(), mask.getHeight()));
}
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (getWidth() - (master.getWidth() + mask.getWidth())) / 2;
int y = (getHeight() - master.getHeight()) / 2;
g.drawImage(master, x, y, this);
x += mask.getWidth();
y = (getHeight() - mask.getHeight()) / 2;
g.drawImage(mask, x, y, this);
}
}
}
Related
How to resize an image with JLabel resizing in an internal frame?
I tried some code that I found on the Internet like:
public static BufferedImage resize(Image image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
or
getScaledInstance(l.getWidth(), -1, Image.SCALE_SMOOTH);
the code's result
But it only makes the image smaller.
Is there a way to resize the image with the JLabel and JInternalFrame? Any help is welcome :)
Scaling an image, well, is not simple. Take a look at:
Java: maintaining aspect ratio of JPanel background image
Scale the ImageIcon automatically to label size
Quality of Image after resize very low -- Java
Scaling can also be expensive, so you'd probably want someway to reduce the number of attempts (as a window been re-resized will be bombarded with a large number of sizing events).
The overall method is the same regardless if your using a JFrame, JPanel or JInternalFrame.
You need some way to monitor for the size changes and some way to determine when it might be a suitable time to scale the image. The following example make use of a ComponentListener and non-repeating Swing Timer. This allows us the ability to schedule a task to be executed in the future, but which can be cancelled if we need to.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
BufferedImage image = ImageIO.read(getClass().getResource("/images/happy.png"));
JInternalFrame internalFrame = new JInternalFrame("Test", true, true, true, true);
internalFrame.add(new ImagePane(image));
internalFrame.pack();
internalFrame.setVisible(true);
JDesktopPane desktopPane = new JDesktopPane();
desktopPane.add(internalFrame);
JFrame frame = new JFrame();
frame.add(desktopPane);
frame.setSize(new Dimension(800, 800));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class ImagePane extends JPanel {
private Timer resizeTimer;
private BufferedImage masterImage;
private JLabel label;
private Scaler scaler;
public ImagePane(BufferedImage masterImage) {
this.masterImage = masterImage;
label = new JLabel(new ImageIcon(masterImage));
scaler = new Scaler(masterImage);
resizeTimer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(new ImageIcon(scaler.getScaledInstanceToFill(getSize())));
}
});
resizeTimer.setRepeats(false);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
resizeTimer.restart();
}
});
setLayout(new BorderLayout());
add(label);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class Scaler {
private BufferedImage master;
private Dimension masterSize;
private Map<RenderingHints.Key, Object> renderingHints = new HashMap<>();
public Scaler(BufferedImage master) {
this.master = master;
masterSize = new Dimension(master.getWidth(), master.getHeight());
renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
renderingHints.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
renderingHints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
renderingHints.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
renderingHints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
renderingHints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
renderingHints.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
renderingHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
renderingHints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
public BufferedImage getScaledInstanceToFit(Dimension size) {
return getScaledInstance(getScaleFactorToFit(size));
}
public BufferedImage getScaledInstanceToFill(Dimension size) {
return getScaledInstance(getScaleFactorToFill(size));
}
protected double getScaleFactor(int masterSize, int targetSize) {
return (double) targetSize / (double) masterSize;
}
protected double getScaleFactorToFit(Dimension toFit) {
double dScaleWidth = getScaleFactor(masterSize.width, toFit.width);
double dScaleHeight = getScaleFactor(masterSize.height, toFit.height);
return Math.min(dScaleHeight, dScaleWidth);
}
protected double getScaleFactorToFill(Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
return Math.max(dScaleHeight, dScaleWidth);
}
protected BufferedImage getScaledInstance(double dScaleFactor) {
BufferedImage imgScale = master;
int targetWidth = (int) Math.round(masterSize.getWidth() * dScaleFactor);
int targetHeight = (int) Math.round(masterSize.getHeight() * dScaleFactor);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(targetWidth, targetHeight);
} else {
imgScale = getScaledUpInstance(targetWidth, targetHeight);
}
return imgScale;
}
protected BufferedImage getScaledDownInstance(
int targetWidth,
int targetHeight) {
int type = (master.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage scaled = master;
if (targetHeight > 0 || targetWidth > 0) {
int width = master.getWidth();
int height = master.getHeight();
do {
if (width > targetWidth) {
width /= 2;
if (width < targetWidth) {
width = targetWidth;
}
}
if (height > targetHeight) {
height /= 2;
if (height < targetHeight) {
height = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(width, 1), Math.max(height, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHints(renderingHints);
g2.drawImage(scaled, 0, 0, width, height, null);
g2.dispose();
scaled = tmp;
} while (width != targetWidth || height != targetHeight);
} else {
scaled = new BufferedImage(1, 1, type);
}
return scaled;
}
protected BufferedImage getScaledUpInstance(
int targetWidth,
int targetHeight) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) master;
int width = master.getWidth();
int height = master.getHeight();
do {
if (width < targetWidth) {
width *= 2;
if (width > targetWidth) {
width = targetWidth;
}
}
if (height < targetHeight) {
height *= 2;
if (height > targetHeight) {
height = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(width, height, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHints(renderingHints);
g2.drawImage(ret, 0, 0, width, height, null);
g2.dispose();
ret = tmp;
} while (width != targetWidth || height != targetHeight);
return ret;
}
}
}
Also, JLabel is actually irrelevant to your problem and I'd probably use a custom painting workflow to render the scaled image, but that's me.
I want to cut the pic to circle by using java graphics 2d, but the result is unsatisfying. I would like the final pic come as similar as the "object-fit: cover" comes out in css.
This is the original pic
original pic
Below are my codes and the final result.
BufferedImage testImage = ImageIO.read(new File("/Users/huangruixiang/Desktop/test.png"));
BufferedImage formatAvatarImage = new BufferedImage(200, 200, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D graphics = formatAvatarImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, 200, 200);
graphics.setClip(shape);
graphics.drawImage(testImage, 0, 0, 200, 200, null);
graphics.dispose();
ImageIO.write(formatAvatarImage,"png",new File("/Users/huangruixiang/Desktop/circle.png"));
resule
And the effect I want is similar to this
Expected effect
Here you go.
You have to use a different drawImage method than the one you used.
Here's the complete runnable code.
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class CenterCutImage {
public static void main(String[] args) {
CenterCutImage cci = new CenterCutImage();
BufferedImage image = cci.readImage("/do3kO.png");
BufferedImage croppedImage = cci.createCenterCut(image, new Dimension(200, 200));
String path = "D:\\Eclipse\\Eclipse-2020-workspace\\com.ggl.testing3\\resources";
cci.writeImage(croppedImage, path, "circle.png");
}
public BufferedImage createCenterCut(BufferedImage inputImage, Dimension d) {
BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x = (inputImage.getWidth() - d.width) / 2;
int y = (inputImage.getHeight() - d.height) / 2;
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, d.width, d.height);
g2d.setClip(shape);
g2d.drawImage(inputImage, 0, 0, d.width, d.height, x, y, x + d.width, y + d.height, null);
g2d.dispose();
return image;
}
public BufferedImage readImage(String filename) {
try {
return ImageIO.read(getClass().getResourceAsStream(filename));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public void writeImage(BufferedImage croppedImage, String path, String filename) {
try {
ImageIO.write(croppedImage, "png", new File(path + "/" + filename));
} catch (IOException e) {
e.printStackTrace();
}
}
}
The code creates a JFrame with a JPanel onto which it draws an image loaded from a file. The objective is to make a rectangular area of the picture, such as for example the red square, appear darker than the rest. I'm assuming this may involve taking a subimage of the image, looping through an array of pixels, scaling them, and then painting that subimage onto the JPanel, but I don't know how to do this using the Java API.
package SpriteEditor_Tests;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class ImageTestApp extends JFrame
{
public BufferedImage image;
int x1 = 50;
int x2 = 100;
int y1 = 50;
int y2 = 100;
public static void main (String [] args)
{
new ImageTestApp();
}
public ImageTestApp()
{
setTitle("Image Test App");
try
{
image = ImageIO.read(new File("C:/Users/Paul/Desktop/derp.png"));
}
catch (IOException io)
{
System.out.println("IO caught"); System.exit(0);
}
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
add(new ImageDisplay());
}
class ImageDisplay extends JPanel
{
public void paintComponent(Graphics g)
{
g.drawImage(image, -100, -100, getWidth(), getHeight(), Color.RED, null);
g.setColor(Color.RED);
g.drawRect(x1, y1, Math.abs(x2 - x1), Math.abs(y2 - y1));
}
}
}
A "simple" solution would be to just create a new instance of Color with the desired alpha applied to it and fill the area you want darkened.
This is great if you have a color you want to use, but when I want to use a predefined color, it's not as simple. Instead, I prefer to use an AlphaComposite as it gives me some advantages.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
public TestPane() {
try {
background = ImageIO.read(getClass().getResource("/images/background.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
if (background == null) {
return new Dimension(200, 200);
}
return new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(background, 0, 0, this);
int x = (getWidth() - 100) / 2;
int y = (getHeight() - 100) / 2;
Rectangle rect = new Rectangle(x, y, 200, 200);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.fill(rect);
g2d.dispose();
g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawRect(x, y, 200, 200);
g2d.dispose();
}
}
}
Now, if want to generate a new image with the are darkened, you can follow the same basic concept, but instead of painting to the components Graphics context, you'd paint directly to the BufferedImages Graphics content. This is the wonderful power of the abstract nature of the Graphics API.
Don't forget, when you override a method, you are obliged to either over take ALL of its responsibilities or call its super implementation.
paintComponent does some basic, but important work and you should make sure to call super.paintComponent before you start performing your custom painting, this will just reduce any possibility of issues.
Darken each pixel individually
Okay, if, instead, you want to darken each pixel in the rectangle individually, this becomes a "little" more complicated, but not hard.
After a lot of time and testing, I settled on using the follow algorithm to darken a given color. This will push the color towards "black" the more you darken it, which some algorithms don't do.
public static Color darken(Color color, double fraction) {
int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));
int alpha = color.getAlpha();
return new Color(red, green, blue, alpha);
}
Then, all you have to do is get the the color of the pixel, darken it and reapply.
For this example, I actually use a separate sub image, but you can do it directly to the parent image
BufferedImage subImage = background.getSubimage(x, y, 200, 200);
for (int row = 0; row < subImage.getHeight(); row++) {
for (int col = 0; col < subImage.getWidth(); col++) {
int packedPixel = subImage.getRGB(col, row);
Color color = new Color(packedPixel, true);
color = darken(color, 0.5);
subImage.setRGB(col, row, color.getRGB());
}
}
Now, before someone jumps down my throat, no, this is not the most performant approach, but it gets over messing about with "packed" pixel values (because I can never remember how to unpack those :P) and most of my API code is based around the use of Color anyway
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public static Color darken(Color color, double fraction) {
int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));
int alpha = color.getAlpha();
return new Color(red, green, blue, alpha);
}
public class TestPane extends JPanel {
private BufferedImage background;
private BufferedImage darkended;
public TestPane() {
try {
background = ImageIO.read(getClass().getResource("/images/background.jpg"));
int x = (background.getWidth() - 100) / 2;
int y = (background.getHeight() - 100) / 2;
BufferedImage subImage = background.getSubimage(x, y, 200, 200);
for (int row = 0; row < subImage.getHeight(); row++) {
for (int col = 0; col < subImage.getWidth(); col++) {
int packedPixel = subImage.getRGB(col, row);
Color color = new Color(packedPixel, true);
color = darken(color, 0.5);
subImage.setRGB(col, row, color.getRGB());
}
}
darkended = subImage;
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
if (background == null) {
return new Dimension(200, 200);
}
return new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(background, 0, 0, this);
int x = (getWidth() - 100) / 2;
int y = (getHeight() - 100) / 2;
g2d.drawImage(darkended, x, y, this);
g2d.setColor(Color.RED);
g2d.drawRect(x, y, 200, 200);
g2d.dispose();
}
}
}
I'm trying to add an imagepanel which extends JPanel to another JPanel. Which is not working well for me. The paint function of image panel don't get invoked inside Jpanel but works fine in JFrame. Any ideas or help will be appreciated.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
class ImagePanel extends JPanel
{
int g_h=10,g_w=10;
int width=50,height=50;
int cornerradius;
Image castle;
Dimension size;
protected int x1,y1;
Color c1=new Color(255, 0, 0);
Rectangle rec;
boolean b=false;
boolean imboo=false;
boolean roundb= false;
Graphics g= this.getGraphics();
protected int strokeSize = 1;
protected Color shadowColor = Color.BLACK;
boolean shadowed = false;
public ImagePanel()
{
//super();
setOpaque(false);
setLayout(null);
System.out.println("it executed");
}
public ImagePanel(int x, int y)
{
setSize(x, y);
}
public void setSize(int x,int y){
width=x;
height=y;
}
public int getheight(){
return height;
}
public int getwidth(){
return width;
}
public void setImagePanelBounds(
int x, int y, int width, int height){
x1=x;
y1=y;
this.width= width;
this.height= height;
System.out.println("6it executed");
}
public void setroundcorners(boolean b, int i){
roundb=b;
cornerradius=i;
System.out.println("5it executed");
}
public void setImage(String s){
imboo=true;
size = new Dimension();
castle = new ImageIcon(s).getImage();
size.width = castle.getWidth(null);
size.height = castle.getHeight(null);
setPreferredSize(size);
System.out.println("4it executed");
}
public void paint(Graphics gh){
System.out.println("it executed p");
{int x=this.getWidth();
int j=20,a=20;
Graphics2D g2= (Graphics2D)gh.create();
{
g2.setColor(Color.WHITE);
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setComposite(AlphaComposite.SrcAtop);
rec= new Rectangle(x1, y1, width, height);
//Start of If-else
if(roundb){
g2.setClip(new RoundRectangle2D.Float(
(int)rec.getX(),(int)rec.getY(),
(int)rec.getWidth(),(int)rec.getHeight(),
cornerradius, cornerradius));
System.out.println("it executed");
}
// End of If-Else
// Image condition Starts
if (imboo){
g2.drawImage(castle, (int)rec.getX(),
(int)rec.getY(), (int)rec.getWidth(),
(int)rec.getHeight(), null);
//g.drawImage(castle, (int)rec.getX(),(int)rec.getY(), null);
}
// Image condition Ends
g2.setColor(Color.BLUE);
}
}
}
public static void main(String[]args)
{
ImagePanel t1=new ImagePanel();
JPanel jp1= new JPanel();
jp1.add(t1);
jp1.setLayout(null);
jp1.setBounds(0, 0, 600, 600);
JFrame jf1= new JFrame("Testing");
t1.setImage("icons/1.png");
//t1.setImage("1.jpg");
t1.setLayout(null);
t1.setroundcorners(true, 10);
//t1.setShadow(true);
t1.add(new JLabel("niak"));
//t1.setShadowDimensions(18, 18, 305, 305, 12);
t1.setImagePanelBounds(20, 20, 100, 100);
// jf1.add(t1);
jf1.setSize(600, 600);
jf1.setDefaultCloseOperation(jf1.EXIT_ON_CLOSE);
jf1.setVisible(true);
//jf1.revalidate();
jf1.setLayout(null);
}
}
Let's start with...
Don't use getGraphics. This is not how to perform custom painting. getGraphics may return null and is, at best, a snapshot, which will be discard when the next paint cycle occurs.
JPanel already has getWidth, getHeight and setSize methods, you should never have a need to overridden them. Instead, you should override getPreferredSize and return a size hint back that the parent layout manager can use.
If you create a Graphics context, you should dispose of it. In your paint method, you use gh.create, this is consuming resources and under some systems, until the Graphics context is disposed, it may not actually paint anything.
Don't override paint, instead use paintComponent
DO NOT modify the clip rectangle. Seriously, this is going to cause you more issues then you can imagine.
Don't use null layout managers without EXTREMELY good reason.
JPanel has a setBounds method, while, under normal circumstances, you shouldn't need to use it, since you've thrown away the layout manager, you should use it.
Basically, you've discard all the inner workings of the JPanel that enable the paint system to know that it should actually paint your panel
Updated with example
As an example...
Instead of using the clip, I use a masking technique, and mask the shape I want over the source image. I also buffer the result, which should make it more memory conservative as well as render faster
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class ImagePaneExample {
public static void main(String[] args) {
new ImagePaneExample();
}
public ImagePaneExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
try {
BufferedImage img = ImageIO.read(new File("C:\\hold\\thumbnails\\2005-09-29-3957.jpeg"));
ImagePane imgPane = new ImagePane();
imgPane.setImage(img);
imgPane.setRounded(true);
imgPane.setBorder(new EmptyBorder(20, 20, 20, 20));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(imgPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
}
});
}
public class ImagePane extends JPanel {
private BufferedImage img;
private BufferedImage renderImg;
private boolean rounded;
public ImagePane() {
}
public void setRounded(boolean value) {
if (value != rounded) {
rounded = value;
renderImg = null;
firePropertyChange("rounded", !rounded, rounded);
repaint();
}
}
public boolean isRounded() {
return rounded;
}
public void setImage(BufferedImage value) {
if (value != img) {
BufferedImage old = img;
img = value;
renderImg = null;
firePropertyChange("image", old, img);
repaint();
}
}
public BufferedImage getImage() {
return img;
}
#Override
public Dimension getPreferredSize() {
Dimension size = img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
Insets insets = getInsets();
size.width += (insets.left + insets.right);
size.height += (insets.top + insets.bottom);
return size;
}
protected void applyQualityRenderHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
protected BufferedImage getImageToRender() {
if (renderImg == null) {
BufferedImage source = getImage();
if (source != null) {
if (isRounded()) {
BufferedImage mask = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
applyQualityRenderHints(g2d);
g2d.setBackground(new Color(255, 255, 255, 0));
g2d.clearRect(0, 0, mask.getWidth(), mask.getHeight());
g2d.setBackground(new Color(255, 255, 255, 255));
g2d.fillRoundRect(0, 0, mask.getWidth(), mask.getHeight(), 40, 40);
g2d.dispose();
BufferedImage comp = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = comp.createGraphics();
applyQualityRenderHints(g2d);
g2d.setBackground(new Color(255, 255, 255, 0));
g2d.clearRect(0, 0, source.getWidth(), source.getHeight());
g2d.drawImage(source, 0, 0, this);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
g2d.drawImage(mask, 0, 0, this);
g2d.dispose();
renderImg = comp;
} else {
renderImg = source;
}
}
}
return renderImg;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage img = getImageToRender();
System.out.println(img);
if (img != null) {
Insets insets = getInsets();
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
int x = ((width - img.getWidth()) / 2);
int y = ((height - img.getHeight()) / 2);
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
}
}
I'd recommend some more reading through Creating a UI with Swing, in particular the section on layout managers, as well as Performing Custom Painting and Painting in AWT and Swing
All you have to do is to set the Layout of your panel jp1 to BorderLayout and add the image panel t1 to BorderLayout.CENTER, like this:
JPanel jp1= new JPanel(new BorderLayout());
jp1.add(t1, BorderLayout.CENTER);
I want to create a graphics whereby circles are overlapped and their colour fades out, also the circles should have a white space in between each other, something similar to this:
Here is how far I've got:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
class Circles extends JPanel implements MouseListener {
public void mouseReleased(MouseEvent event) {
}
public void mousePressed(MouseEvent event) {
}
public void mouseClicked(MouseEvent event) {
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public Circles() {
addMouseListener(this);
}
public void paint(Graphics g) {
g.setColor(Color.white);
int w = getWidth();
int h = getHeight();
g.fillRect(0, 0, w, h);
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
Color c = new Color(255 - i * 10, 255 - i * 10, 255 - i * 10);
g2.setColor(c);
} else {
g2.setColor(Color.white);
}
g2.fillOval((5 * i), (5 * i), (w - 10) * i, (h - 10) * i);
}
}
public static void main(String[] args) {
Frame w = new Frame();
w.setVisible(true);
}
}
Frame:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Frame extends JFrame {
Circles myPanel;
public static void main(String[] args) {
Frame w = new Frame();
w.setVisible(true);
}
public Frame() {
setTitle("In-class Test 1: Starting Code");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 220);
setLocation(300, 300);
myPanel = new Circles();
add(myPanel);
}
}
There are a number of ways to achieve this, but first, start by overriding paintComponent instead of paint.
Second, you will need some kind of loop that expand the radius of the circle on each iteration.
Thirdly, you will want a simple int value that acts as a alpha value, starting from 255 and decreases down to the faintest alpha level you want, perhaps 64 of example.
On each iteration of the loop, you will want to increase the radius value and decrease the alpha value accordingly. You would then simply need to create a new Color object with the RGB & alpha values you need
Check out 2D Graphics for details
Updated with example...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FadingCircles {
public static void main(String[] args) {
new FadingCircles();
}
public FadingCircles() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int CIRCLE_COUNT = 10;
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int maxRadius = Math.min(getWidth(), getHeight());
int alpha = 255;
int range = 255 - 32;
g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
for (int index = 0; index < CIRCLE_COUNT; index++) {
float progress = (float) index / (float) CIRCLE_COUNT;
alpha = 255 - Math.round(range * progress);
Color color = new Color(0, 0, 0, alpha);
g2d.setColor(color);
int radius = Math.round(maxRadius * progress);
int x = (getWidth() - radius) / 2;
int y = (getHeight() - radius) / 2;
g2d.draw(new Ellipse2D.Float(x, y, radius, radius));
}
g2d.dispose();
}
}
}
You might also like to check out Performing Custom Painting