Get mouse detection with a dynamic shape - java

Basically I'm constructing a world map. I know how to have a square click area. But I'd like to make it so I can put the countries together and be able to click on the country. Now it's pretty obvious that I can't use square click areas for that because I'd have overlapping click areas. Could I do it by looking at the transparency of each pixel? Even so I don't know how to do that?

Use Shape.contains(Point2D) - something like this:
This example uses overlapping ellipses to show how the contains(..) method will accurately identify which ovals the mouse click falls inside. But the kind of map you are referring to will probably be made of a number of GeneralPath objects (one for each country) that do not overlap.
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.BasicStroke;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.io.File;
import java.util.*;
import javax.imageio.ImageIO;
public class ShapeContainment {
List<Ellipse2D> shapes = new ArrayList<Ellipse2D>();
int w = 400;
int h = 100;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Point2D mouse = new Point2D.Double(0, 0);
JLabel l;
ShapeContainment() {
Random r = new Random();
for (int ii = 0; ii < 10; ii++) {
int x = r.nextInt(w / 2);
int y = r.nextInt(h / 2);
int wE = r.nextInt(w - x);
int hE = r.nextInt(h - y);
Ellipse2D ellipse = new Ellipse2D.Double(x, y, wE, hE);
shapes.add(ellipse);
}
l = new JLabel(new ImageIcon(img));
MouseAdapter listener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
mouse = me.getPoint();
drawImage();
}
};
l.addMouseListener(listener);
drawImage();
JOptionPane.showMessageDialog(null, l);
}
public void drawImage() {
Graphics2D g = img.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, w, h);
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHints(hints);
Color bg = new Color(0, 128, 0, 60);
Color inside = new Color(0, 0, 255, 120);
Color outside = new Color(255, 0, 0, 120);
g.setStroke(new BasicStroke(4));
for (Ellipse2D shape : shapes) {
g.setColor(bg);
g.fill(shape);
if (shape.contains(mouse)) {
g.setColor(inside);
} else {
g.setColor(outside);
}
g.draw(shape);
}
g.setColor(Color.RED);
int x = (int) mouse.getX();
int y = (int) mouse.getY();
g.setStroke(new BasicStroke(2));
int s = 3;
g.drawLine(x-s, y, x+s, y);
g.drawLine(x, y-s, x, y+s);
l.setIcon(new ImageIcon(img));
g.dispose();
try {
ImageIO.write(
img,
"png",
new File(System.currentTimeMillis() + ".png"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Runnable run = new Runnable() {
#Override
public void run() {
new ShapeContainment();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(run);
}
}

Related

How can I darken a rectangular part of an image in Java?

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();
}
}
}

Java2D Alpha Mapping images

Using Graphics2D, how can I take a black and white image, and use to define what should and what shouldn't rendered on another image?
E.g if I had an image of say, a field, and on that field is a cow, and on another image of the same dimensions I draw a white box on a black background, at the same coordinates of the cow, when rendered in Java the image would be all black, apart from the cow where I had the white box?
EDIT: Based on a long discussion in the chat, it became clear that there was a misunderstanding about the intention, and the original question suffered from the XY-Problem: The question of how to compose an image with a masking image was only about one solution attempt for the actual problem - namely painting some shadow/light effects on a tile map. The original versions of the post can be seen in the revision history.
The actual goal was obviously to add a "light effect" over the image. Here is an example of how this can be achieved:
The original image is painted in the background
A "shadow image" is painted over the image.
The "shadow image" is initially a nearly opaqe, nearly black image. The lights are painted into this image, with a RadialGradientPaint. The colors for this paint are chosen so that they make the shadow image less opaque and less black at the places where the lights should be. This causes these areas to appear lighted, while the other parts remain dark.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LightEffectTest2
{
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new LightEffectTest2();
}
});
}
public LightEffectTest2()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new LightEffectPanel2());
f.setSize(600,600);
f.setVisible(true);
}
}
class LightEffectPanel2 extends JPanel implements MouseMotionListener
{
private Point point = new Point(0,0);
private BufferedImage image;
private BufferedImage shadow;
public LightEffectPanel2()
{
image = createExampleImage(600,600);
shadow = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
addMouseMotionListener(this);
}
private static BufferedImage createExampleImage(int w, int h)
{
BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
Random random = new Random(0);
for (int i=0; i<200; i++)
{
int x = random.nextInt(w);
int y = random.nextInt(h);
Color c = new Color(
random.nextInt(255),
random.nextInt(255),
random.nextInt(255));
g.setColor(c);
g.fillOval(x-20, y-20, 40, 40);
}
g.dispose();
return image;
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.drawImage(image, 0,0,null);
drawLights();
g.drawImage(shadow, 0,0, null);
}
private void drawLights()
{
Graphics2D g = shadow.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setColor(new Color(0,0,16,240));
g.fillRect(0,0,getWidth(),getHeight());
drawLight(g, new Point(100,100));
drawLight(g, point);
g.dispose();
}
private void drawLight(Graphics2D g, Point pt)
{
float radius = 100;
g.setComposite(AlphaComposite.DstOut);
Point2D center = new Point2D.Float(pt.x, pt.y);
float[] dist = {0.0f, 1.0f};
Color[] colors = {new Color(255,255,255,255), new Color(0,0,0,0) };
RadialGradientPaint p =
new RadialGradientPaint(
center, radius, dist, colors, CycleMethod.NO_CYCLE);
g.setPaint(p);
g.fillOval(pt.x-(int)radius,pt.y-(int)radius,(int)radius*2,(int)radius*2);
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
point = e.getPoint();
repaint();
}
}
(late) EDIT For the request in the comments:
To add another shadow (regardless of the existing lights), one can create a drawShadow method that re-applies the shadows after the lights have been drawn. It basically uses another RadialGradientPaint that partially "restores" the original, opaqe, dark shadow image.
(The shadow is given a somewhat sharper border here, to make the effect more visible)
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LightEffectTest3
{
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new LightEffectTest3();
}
});
}
public LightEffectTest3()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new LightEffectPanel3());
f.setSize(600,600);
f.setVisible(true);
}
}
class LightEffectPanel3 extends JPanel implements MouseMotionListener
{
private Point point = new Point(0,0);
private BufferedImage image;
private BufferedImage shadow;
public LightEffectPanel3()
{
image = createExampleImage(600,600);
shadow = new BufferedImage(image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
addMouseMotionListener(this);
}
private static BufferedImage createExampleImage(int w, int h)
{
BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
Random random = new Random(0);
for (int i=0; i<200; i++)
{
int x = random.nextInt(w);
int y = random.nextInt(h);
Color c = new Color(
random.nextInt(255),
random.nextInt(255),
random.nextInt(255));
g.setColor(c);
g.fillOval(x-20, y-20, 40, 40);
}
g.dispose();
return image;
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.drawImage(image, 0,0,null);
drawLights();
g.drawImage(shadow, 0,0, null);
}
private void drawLights()
{
Graphics2D g = shadow.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setColor(new Color(0,0,16,240));
g.fillRect(0,0,getWidth(),getHeight());
drawLight(g, new Point(200,200));
drawLight(g, point);
drawShadow(g, new Point(250,250));
g.dispose();
}
private void drawLight(Graphics2D g, Point pt)
{
float radius = 150;
g.setComposite(AlphaComposite.DstOut);
Point2D center = new Point2D.Float(pt.x, pt.y);
float[] dist = {0.0f, 1.0f};
Color[] colors = {new Color(255,255,255,255), new Color(0,0,0,0) };
RadialGradientPaint p =
new RadialGradientPaint(
center, radius, dist, colors, CycleMethod.NO_CYCLE);
g.setPaint(p);
g.fillOval(pt.x-(int)radius,pt.y-(int)radius,
(int)radius*2,(int)radius*2);
}
private void drawShadow(Graphics2D g, Point pt)
{
float radius = 75;
g.setComposite(AlphaComposite.DstOver);
Point2D center = new Point2D.Float(pt.x, pt.y);
float[] dist = {0.0f, 0.7f, 1.0f};
Color[] colors = {
new Color(0,0,0,200),
new Color(0,0,0,150),
new Color(255,255,255,0) };
RadialGradientPaint p =
new RadialGradientPaint(
center, radius, dist, colors, CycleMethod.NO_CYCLE);
g.setPaint(p);
g.fillOval(pt.x-(int)radius,pt.y-(int)radius,
(int)radius*2,(int)radius*2);
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
point = e.getPoint();
repaint();
}
}

Image Pan and Zooming

I want to display an image to zoom in and out and can be shifted. I have the script below, but it can not be shifted. how to fix it.
the code :
package dispertasih;
import static dispertasih.PanPanel.startX;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class PadAndZoom implements ChangeListener {
BufferedImage image;
PanPanel label;
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
double scale = value/200.0;
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
label.revalidate(); // signal scrollpane
}
private BufferedImage getScaledImage(double scale) {
int w = (int)(scale*image.getWidth());
int h = (int)(scale*image.getHeight());
BufferedImage bi = new BufferedImage(w, h, image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
g2.drawRenderedImage(image, at);
g2.dispose();
return bi;
}
private PanPanel getContent() {
createAnImage();
label = new PanPanel(image);
//label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
private void createAnImage() {
int w = 500;
int h = 500;
int type = BufferedImage.TYPE_INT_RGB; // many options
try {
image = ImageIO.read(PadAndZoom.class.getResource("/tampilan/background5.jpg"));
} catch (IOException ex) {
Logger.getLogger(PadAndZoom.class.getName()).log(Level.SEVERE, null, ex);
}
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 50, 200, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(10);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
PadAndZoom app = new PadAndZoom();
JFrame f = new JFrame();
JButton b = new JButton();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(app.getContent()));
//f.getContentPane().add(new JButton("tessss"));
f.getContentPane().add(app.getControl(), "Last");
f.setSize(400, 400);
f.setLocation(200,200);
f.setVisible(true);
}
}
class PanPanelX extends JPanel {
private int x, y;
private int width = 800, height = 800;
BufferedImage img;
private final static RenderingHints textRenderHints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
private final static RenderingHints imageRenderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
private final static RenderingHints renderHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
static int startX, startY;
public PanPanelX(BufferedImage img) {
x = 20;
y = 20;
this.img = img;
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent me) {
super.mousePressed(me);
startX = me.getX();
startY = me.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me) {
super.mouseDragged(me);
if (me.getX() < startX) {//moving image to right
x -= 2;
} else if (me.getX() > startX) {//moving image to left
x += 2;
}
if (me.getY() < startY) {//moving image up
y -= 2;
} else if (me.getY() > startY) {//moving image to down
y += 2;
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
//turn on some nice effects
applyRenderHints(g2d);
g2d.drawImage(img, x, y, null);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void applyRenderHints(Graphics2D g2d) {
g2d.setRenderingHints(textRenderHints);
g2d.setRenderingHints(imageRenderHints);
g2d.setRenderingHints(renderHints);
}
}
i think on :
public void stateChanged(ChangeEvent e) {
int value = ((JSlider)e.getSource()).getValue();
double scale = value/200.0;
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
label.revalidate(); // signal scrollpane
}
there are mistakes, but I've stuck fixing it....
thank you ...
The problem is, the reference of the image that is displayed on the screen is not the same as the image you are creating when you resize it...
You create PanPanel by using...
BufferedImage scaled = getScaledImage(scale);
label = new PanPanel(scaled);
getScaledImage creates a new image each time it is called, based on the original image.
BufferedImage bi = new BufferedImage(w, h, image.getType());
This means, that the image in PadAndZoom is not the same as the one in PanPanel, so any changes you make to image or any new scaled instances will never be painted because PanPanel knows nothing about them...
Now, in stateChanged, instead of updating the instance of PanPanel that you already have, you create a new instance of it...and that's it...it's never added to any displayable component, so it will never appear on the screen.
Instead, in your PanPanel, you need to supply some way for the stateChanged method to pass the scaled instance of the image to it...
public void setImage(BufferedImage img) {
this.img = img;
revalidate();
repaint();
}
Then in your stateChanged method you can do...
int value = ((JSlider) e.getSource()).getValue();
double scale = value / 200.0;
BufferedImage scaled = getScaledImage(scale);
label.setImage(scaled);

Drawing a nice circle in Java

I'm using Java Graphics and I keep getting "ugly" circles.
Here's what my Java program makes
And here's the same thing being made in Matlab
I think it is clear that the Java one is not as "nice" looking as the Matlab one, particularly on the edges of the circle. Note that this has nothing to do with the resolution...these images are practically the same size. Also note that I am already setting rendering hints.
Here's a stand alone with a Main function you can run to test this out.
package test;
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.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class SimplePaint02 {
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
public static void main(String[] args) {
new SimplePaint02();
}
public SimplePaint02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
public void paintComponent(Graphics g) {
int radius = 50;
BufferedImage buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
Shape clip = g2d.getClip();
g2d.setClip(circle);
AffineTransform at = g2d.getTransform();
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),radius / 2, radius / 2));
int gap = LINE_GAP;
g2d.setColor(Color.WHITE);
g2d.fill(circle);
g2d.setColor(lineColor);
//g2d.setStroke(new BasicStroke(LINE_THICKNESS));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
g2d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
g2d.setTransform(at);
g2d.setClip(clip);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
EDIT: Please see Code Guy's answer below for a solution. This is marked correct because it was Joey Rohan who figured it out initially!
I got smooth edge when i tried out same thing:
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class DrawSmoothCircle {
public static void main(String[] argv) throws Exception {
BufferedImage bufferedImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.green);
g2d.fillOval(10, 10, 50, 50);
g2d.dispose();
ImageIO.write(bufferedImage, "png", new File("e:\\newimage.png"));
}
}
UPDATE:
After searching alot:
There is nothing wrong with the code but,
Well, unfortunately Java 2D (or at least Sun's current implementation) does not support "soft clipping."
But Also got a trick for the clips:
Follow This link,you can achieve what you are asking for.
(Also, i got a smooth edge, cause i din't use clip stuff,in my above image)
Here was the answer. I adapted the majority of the code from this site. Take a look:
Here's the code:
public void paintComponent(Graphics g) {
// Create a translucent intermediate image in which we can perform
// the soft clipping
GraphicsConfiguration gc = ((Graphics2D) g).getDeviceConfiguration();
BufferedImage img = gc.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
Graphics2D g2 = img.createGraphics();
// Clear the image so all pixels have zero alpha
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(0, 0, getWidth(), getHeight());
// Render our clip shape into the image. Note that we enable
// antialiasing to achieve the soft clipping effect. Try
// commenting out the line that enables antialiasing, and
// you will see that you end up with the usual hard clipping.
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.fillOval(0, 0, getRadius(), getRadius());
// Here's the trick... We use SrcAtop, which effectively uses the
// alpha value as a coverage value for each pixel stored in the
// destination. For the areas outside our clip shape, the destination
// alpha will be zero, so nothing is rendered in those areas. For
// the areas inside our clip shape, the destination alpha will be fully
// opaque, so the full color is rendered. At the edges, the original
// antialiasing is carried over to give us the desired soft clipping
// effect.
g2.setComposite(AlphaComposite.SrcAtop);
g2.setColor(lineColor);
int gap = LINE_GAP;
AffineTransform at = g2.getTransform();
g2.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),getRadius() / 2, getRadius() / 2));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = getRadius();
int width = x2 - x1;
int height = y2 - y1;
g2.fillRect(x1, y1, width, height);
}
g2.setTransform(at);
g2.dispose();
// Copy our intermediate image to the screen
g.drawImage(img, 0, 0, null);
}
Update
OK. Then the idea is to not use clipping at all but instead to make the clipped shape by subtracting areas from each other.
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class SimplePaint02 {
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
public static void main(String[] args) {
new SimplePaint02();
}
public SimplePaint02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
int radius = 75;
#Override
public Dimension getPreferredSize() {
return new Dimension(radius, radius);
}
#Override
public void paintComponent(Graphics g) {
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius, radius);
Area lines = new Area();
int gap = LINE_GAP;
for (int index = 0; index < 10; index++) {
int x1 = index * gap - (LINE_THICKNESS / 2);
int y1 = 0;
int x2 = index * gap + (LINE_THICKNESS / 2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
Shape lineShape = new Rectangle2D.Double(x1, y1, width, height);
lines.add(new Area(lineShape));
//g3d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
//g2d.setClip(circle);
Area circleNoLines = new Area(circle);
circleNoLines.subtract(lines);
Area linesCutToCircle = new Area(circle);
linesCutToCircle.subtract(circleNoLines);
//g2d.setTransform(at);
BufferedImage buffer = new BufferedImage(radius * 2, radius * 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45), radius / 2, radius / 2));
g2d.setRenderingHints(rh);
g2d.setColor(Color.ORANGE);
g2d.fill(linesCutToCircle);
g2d.setColor(Color.RED);
g2d.fill(circleNoLines);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
Old code
Part of the problem is that the rendering operations typically do not apply to a Clip, though they will apply to the Shape when it is drawn. I generally solve that by (last) painting the Shape itself. E.G.
A 1.5 pixel BasicStroke is used here for the red circle - smoothing the rough edges produced by the Clip.
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
public class SimplePaint02 {
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
public static void main(String[] args) {
new SimplePaint02();
}
public SimplePaint02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
int radius = 75;
#Override
public Dimension getPreferredSize() {
return new Dimension((int)(1.1*radius), (int)(1.1*radius));
}
#Override
public void paintComponent(Graphics g) {
BufferedImage buffer = new BufferedImage(radius*2, radius*2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_DITHERING,RenderingHints.VALUE_DITHER_ENABLE);
rh.put(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHints(rh);
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
Shape clip = g2d.getClip();
g2d.setClip(circle);
AffineTransform at = g2d.getTransform();
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(45),radius / 2, radius / 2));
int gap = LINE_GAP;
g2d.setColor(Color.WHITE);
g2d.fill(circle);
g2d.setColor(lineColor);
//g2d.setStroke(new BasicStroke(LINE_THICKNESS));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
g2d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
g2d.setTransform(at);
g2d.setClip(clip);
g2d.setClip(null);
g2d.setStroke(new BasicStroke(1.5f));
g2d.draw(circle);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
}
I used drawPolygon method to draw circle by generating array of most of the points on circumference of circle with proposed radius.
Code:
import java.awt.*;
import java.applet.*;
/*<applet code="OnlyCircle" width=500 height=500>
</applet>*/
public class OnlyCircle extends Applet{
public void paint(Graphics g){
int r=200;//radius
int x1=250;//center x coordinate
int y1=250;//center y coordinate
double x2,y2;
double a=0;
double pi=3.14159;
int count=0;
int i=0;
int f=0;
int[] x22=new int[628319];
int[] y22=new int[628319];
while(a<=2*pi&&i<628319&&f<628319)
{
double k=Math.cos(a);
double l=Math.sin(a);
x2=x1+r*k;
y2=y1+r*l;
x22[i]=(int)x2;
y22[f]=(int)y2;
i++;
f++;
a+=0.00001;
}
int length=x22.length;
g.drawPolygon(x22,y22,length);
}
}
You can enable anti-aliasing:
Graphics2D g2 = (Graphics2D) g;
Map<RenderingHints.Key, Object> hints = new HashMap<RenderingHints.Key, Object>();
hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
I also suggest you draw to the Graphics object you get from the paintComponent method rather than creating an intermediate BufferedImage.

Triangle inside rectangular grids

I have written a code that draws grids and a triangle inside one grid cell. The grid size is increased/decreased when the window is maximized or minimized.
My requirement is that the triangle size should also increase/decrease to fit the grid cell each time the grid size is increased/decreased.
My code is as follows:
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Grid extends JPanel {
/**
* #param args
*/
public static void main(String[] args) {
Grid g = new Grid();
JFrame f = new JFrame("Application GUI Window");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
f.getContentPane().add("Center", g);
f.pack();
f.setSize(new Dimension(450,400));
f.show();
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Dimension d = getSize();
g2d.setBackground(getBackground());
g2d.clearRect(0, 0, d.width, d.height);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawGrid(d.width, d.height, g2d);
int[] xPoints = {20,15,25};
int[] yPoints = {15,25,25};
int n = 3;
Polygon triangle = new Polygon(xPoints, yPoints, n);
g.fillPolygon(triangle);
}
private void drawGrid(int width, int height, Graphics2D g2d) {
/* BasicStroke border = new BasicStroke(3, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[]{0,1,0,1}, 0);
g2d.setStroke(border);
g2d.drawRect(3,3,width-6,height-6);*/
//horizontal lines
int cellheight = height/10;
int cellwidth = width/5;
for (int j=0;j<height;j=j+cellheight)
{
BasicStroke line = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[]{0,1,0,1}, 0);
g2d.setStroke(line);
g2d.drawLine(0, j, cellwidth*5, j);
}
//vertical lines
for (int i=0;i<width;i=i+cellwidth)
{
BasicStroke line = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 0, new float[]{0,1,0,1}, 0);
g2d.setStroke(line);
g2d.drawLine(i, 0, i, cellheight*10);
}
}
}
Thanks in advance for your help.
You can tie the coordinates of the triangle to the dimensions of a grid cell. Here is a snippet based on your example:
int cellHeight = d.height/10;
int cellWidth = d.width/5;
int xOffset = cellWidth/6;
int yOffset = cellHeight/6;
int[] xPoints = {cellWidth/2, xOffset, cellWidth - xOffset};
int[] yPoints = {yOffset, cellHeight - yOffset, cellHeight - yOffset};
Polygon triangle = new Polygon(xPoints, yPoints, xPoints.length);
Note that in Swing you usually should override paintComponent() rather than paint(), unless in some special cases. See A Closer Look at the Paint Mechanism for more details.
Also note that show() is deprecated in favor of setVisible(). You can replace it with: setVisible(true)

Categories