Ugly diagonal lines after affine transform - java

I'm working on figures drawing on low-end device. I noticed that after scale affine transformation many of thin lines (e.g. 1px width) become ugly. Especially in case of diagonal lines (e.g. a line that's sloped by 45 degrees).
I prepared demo program with the most evident examples (I included boilerplate GUI code so you can run the example):
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
public class DiagonalLinesDemo extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D graphics = (Graphics2D) g;
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
drawRemark(graphics, 1F, 20);
drawDiagonalLine(graphics, 1F, 0);
drawRemark(graphics, 1.1070F, 80);
drawDiagonalLine(graphics, 1.1070F, 60);
drawRemark(graphics, 1.0162F, 160);
drawDiagonalLine(graphics, 1.0162F, 140);
drawRemark(graphics, 1.3300F, 250);
drawDiagonalLine(graphics, 1.3300F, 160);
drawRemark(graphics, 1.5555F, 330);
drawDiagonalLine(graphics, 1.5555F, 180);
}
private static void drawDiagonalLine(Graphics2D graphics, float scale, int anchor)
{
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
path.moveTo(anchor + 10, 100);
path.lineTo(anchor + 60, 50);
AffineTransform upScaleTransform = new AffineTransform(
scale,
0F,
0F,
scale,
0F,
0F);
graphics.setTransform(upScaleTransform);
graphics.setColor(Color.BLACK);
graphics.draw(path);
}
private static void drawRemark(Graphics2D graphics, float scale, int anchor)
{
graphics.setTransform(new AffineTransform());
graphics.setColor(Color.BLUE);
String remark = String.format("%1.4f", scale);
graphics.drawString(remark, anchor, 50);
}
public static void main(String args[])
{
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Diagonal Lines Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.white);
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
DiagonalLinesDemo panel = new DiagonalLinesDemo();
frame.add(panel);
frame.setVisible(true);
}
private static final int PANEL_WIDTH = 400;
private static final int PANEL_HEIGHT = 200;
private static final int FRAME_WIDTH = PANEL_WIDTH + 50;
private static final int FRAME_HEIGHT = PANEL_HEIGHT + 50;
}
The result is:
As you can see from the screenshot, after transformation (scale values are remarked above the lines for clarity) ugly fractures appeared. I know that fractures are unavoidable due to absence of antialiasing (I cannot enable antialiasing due to customer's limitations). But I expect more-or-less pretty result, like it's depicted below (I highlighted desired shapes in red):
I tried to adjust winding rule, rendering hints. But without luck. Also, I'm aware of interpolation. But that approach is too resource-consuming (as I said earlier, customer's hardware resources are very limited). Are there any other options?

Related

How do I rotate and draw a BufferedImage in Java?

I'm trying to develop a small rendering thing for a small project. I've gotten to a place where I can setup a Graphics2D object and call things from a simple render() loop, but when attempting to draw an image in place the image crops itself awkwardly and begins to lose data.
This is what I currently have in my draw function. Renderable is a pre-loaded BufferedImage.
public void draw(Graphics2D g2d, int x, int y, int scale, int rotation) {
scale = scale / 2;
AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(rotation), renderable.getWidth() / 2, renderable.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
BufferedImage copy = op.filter(renderable, null);
int pX = (400 - ((copy.getWidth() * scale) / 2)) + x;
int pY = (300 - ((copy.getHeight() * scale) / 2)) + y;
g2d.drawImage(copy, pX, pY, copy.getWidth() * scale, copy.getHeight() * scale, null);
}
When the rotation is not divisible by 90
When the rotation is divisible by 0, 90, 180, or 270
Is there something I am missing or should be doing differently? It appears to me that the image is losing data. The Graphics2D object passed into the function is the same one used to render the lines behind the checkered square and should cover the full Canvas, which is 800x600 pixels.
Update
int rotation = 0;
while (EngineGlue.isValid()) {
rotation = rotation + 15;
if (rotation >= 360) {
rotation = 0;
}
Graphics2D g2d = (Graphics2D)EngineGlue.getInstance().getCanvas().getBufferStrategy().getDrawGraphics();
g2d.clearRect(0, 0, 800, 600);
g2d.drawLine(0, 300, 800, 300);
g2d.drawLine(400, 0, 400, 600);
//ta1.draw(g2d, -100, 0, 2, rotation);
//ta2.draw(g2d, 100, 0, 3, -rotation);
ta3.draw(g2d, 0, 0, 1, rotation);
EngineGlue.getInstance().getCanvas().getBufferStrategy().show();
Thread.sleep(150);
}
frame = new JFrame(title);
canvas = new Canvas();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
frame.add(canvas);
frame.pack();
frame.setSize(EngineGlue.Dimension.WIDTH.getValue(), EngineGlue.Dimension.HEIGHT.getValue());
frame.setLocationRelativeTo(null);
canvas.setSize(EngineGlue.Dimension.WIDTH.getValue(), EngineGlue.Dimension.HEIGHT.getValue());
canvas.createBufferStrategy(3);
You can use an AffineTransform to rotate and scale the image:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.awt.geom.*;
import java.awt.image.*;
public class RotateAndScale extends JPanel
{
private Image image;
public RotateAndScale(Image image)
{
this.image = image;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// create the transform, note that the transformations happen
// in reversed order (so check them backwards)
AffineTransform at = new AffineTransform();
// 4. translate it to the center of the component
at.translate(getWidth() / 2, getHeight() / 2);
// 3. do the actual rotation
at.rotate(Math.toRadians(45));
// 2. scale the image
at.scale(0.5, 0.5);
// 1. translate the object to rotate around the center
at.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
// draw the image
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, at, null);
// continue drawing other stuff (non-transformed)
//...
}
private static void createAndShowGUI()
{
try
{
BufferedImage image = ImageIO.read(new File("splash.gif"));
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new RotateAndScale(image));
frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
catch(Exception e) { System.out.println(e); }
}
public static void main(String[] args) throws Exception
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
Above is code I found somewhere a long time ago.
If you just want to rotate an image, the easiest way is to rotate the graphics context rather than the image itself. This reads in an image of Mars so you'll have to substitute your own image.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
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.SwingUtilities;
public class RotateImage extends JPanel {
JFrame frame;
int width;
int height;
BufferedImage b = null;
String imageFile = "f:/redmarble.jpg";
public RotateImage() {
frame = new JFrame();
setPreferredSize(new Dimension(width, height));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new RotateImage().startup());
}
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public void startup() {
try {
b = ImageIO.read(new File(imageFile));
} catch (IOException ioe) {
ioe.printStackTrace();
}
// calculate the diagonal of the image to size the panel
// this allows all rotations to fit within the panel
double diag = Math.hypot(b.getHeight(), b.getWidth());
width = (int) diag;
height = (int) diag;
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (b == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
// set the angle of rotation and the center of rotation to the middle of the
// panel - width and height are equal but I still like to reference them
// independently (could save debugging time in the future).
g2d.rotate(Math.toRadians(45), width/2, height/2);
// now draw the image, adjusting x,y starting point to ensure rotation
// about the center.
g2d.drawImage(b, (width-b.getWidth())/2, (height-b.getHeight())/2,null);
}
}

java swing - How to draw Rects one on top of the next using?

Using java swing, I have a horizontal line (acting as the horizon). I want to draw rects, stacked, one on top of the other where the rect with the largest width is at the bottom and the rect with the smallest width is at the top. I also want the rects to stick to the horizon when the user re-sizes the screen. this is the calculation i have so far:
package testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test extends JFrame {
private static final int FRAME_WIDTH = 600;
private static final int FRAME_HEIGHT = 500;
public static void main(String[] args) {
new Test();
}
protected Test() {
this.setTitle("Tower of Hanoi");
this.setSize(FRAME_WIDTH, FRAME_HEIGHT);
this.setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setMinimumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setMaximumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setBackground(Color.BLUE);
this.add(new PanelTest(), BorderLayout.CENTER);
this.setVisible(true);
}
public class PanelTest extends JPanel {
private static final int FRAME_WIDTH = 600;
private static final int FRAME_HEIGHT = 500;
protected PanelTest() {
setLayout(new BorderLayout());
this.setSize(FRAME_WIDTH, FRAME_HEIGHT);
this.setMinimumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setMaximumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setBackground(Color.CYAN);
this.add(new ComponentTest(), BorderLayout.CENTER);
revalidate();
repaint();
this.setVisible(true); //probably not necessary
}
}
public class ComponentTest extends JComponent {
private List<Rectangle> rects;
public ComponentTest() {
rects = new ArrayList<>();
rects.add(new Rectangle(30, 30, 30, 30));
rects.add(new Rectangle(30, 30, 30, 30));
rects.add(new Rectangle(30, 30, 30, 30));
rects.add(new Rectangle(30, 30, 30, 30));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graph2 = (Graphics2D) g;
graph2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paint(g);
}
public void paint(Graphics g) {
// Draw the platform
g.drawLine(100, getHeight() - 100, getWidth() - 100, getHeight() - 100);
for(int i = 0; i < rects.size(); i++) {
rects.get(i).setBounds(getWidth()/2,
(30 * i + 1),
30 * i + 1,
30
);
g.drawRect(rects.get(i).x, rects.get(i).y, rects.get(i).width, rects.get(i).height);
}
}
}
}
this leaves the rects stacked but floating near the top of the screen:
how do i get it to stick to the black horizon line? in other words, how do i get the largest rect to rest on top of the black line? furthermore, when the screen is dragged to be a larger size - how can i grow all of the rects heights (equally) and have it still stuck to the black horizon?
also note, the number of rects being drawn is decided by the user (in this example it's 5, but it could be anything up to 9).
Update
doing this also does not work as expected:
for(int i = 0; i < rects.size(); i++) {
rects.get(i).setBounds(getWidth()/2,
(30 * i + 1),
getHeight() - 100 - (30 * (rects.size() - (i + 1))),
30
);
g.drawRect(rects.get(i).x, rects.get(i).y, rects.get(i).width, rects.get(i).height);
}
You don't need class ComponentTest. You only need class PanelTest and you only need to override method paintComponent() in class PanelTest. Refer to Performing Custom Painting.
If you fix the location of the horizon in PanelTest then you need to draw the rectangles relative to the horizon. The x coordinate is the same for each rectangle as is the height, so you just need to calculate the width (which you are already doing correctly) and the y coordinate.
This means that the y coordinate for the bottom-most rectangle, i.e. the one touching the horizon, needs to be the y coordinate of the horizon minus the height of the rectangle.
The y coordinate of the rectangle directly above the bottom-most rectangle needs to be the y coordinate of the horizon minus twice the height of the rectangle, since each rectangle has the same height.
And so on for the remaining rectangles.
Here is my rewrite of class PanelTest. As I said, no need for class ComponentTest and I made no changes to class Test.
Note that since you are drawing rectangles, using anti-aliasing makes no difference. It makes a difference when you draw curved shapes or diagonal lines. There is also no need for class Graphics2D since you don't use any of that class's methods.
public class PanelTest extends JPanel {
private static final int FRAME_WIDTH = 600;
private static final int FRAME_HEIGHT = 500;
private List<Rectangle> rects;
protected PanelTest() {
rects = new ArrayList<>();
rects.add(new Rectangle(30, 30, 30, 30));
rects.add(new Rectangle(30, 30, 30, 30));
rects.add(new Rectangle(30, 30, 30, 30));
rects.add(new Rectangle(30, 30, 30, 30));
this.setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setMinimumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setMaximumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
this.setBackground(Color.CYAN);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(100, getHeight() - 100, getWidth() - 100, getHeight() - 100);
for (int i = 0; i < rects.size(); i++) {
rects.get(i).x = getWidth() / 2;
rects.get(i).y = getHeight() - 100 - (30 * (rects.size() - i));
rects.get(i).width = 30 * i + 1;
rects.get(i).height = 30;
g.drawRect(rects.get(i).x, rects.get(i).y, rects.get(i).width, rects.get(i).height);
}
}
}
So if each bar has a height of 30, then this is the pixel height each rect needs to start at:
getHeight() - 100 - (30 * count(rects))
getHeight() - 100 - (30 * (count(rects) - 1))
...
getHeight() - 100 - (30 * (count(rects) - 4))
So, for the Nth rectangle, getHeight() - 100 - (30 * (count(rects) - d.getNumber()))
I'm not sure if this will work when resized - but you might have to plug into the right lifecycle events in order to get that to work. As mentioned in a comment above, a minimally reproducible example would make it possible to help more

Is it possible to create an outline around a PolyLine

This example draws a simple PolyLine.
Is it possible to draw an outline around this PolyLine in red.
Not a single large red square but one that outlines the original PolyLine by 3-5 points away from all areas.
Some calculations were attempted and work for a fixed value, but when the PolyLine values are random, the algorithm doesn't always work as the next section of the line could turn right instead of left or up instead of down.
You almost have to look 2-3 points ahead to know if you are going to have add or subtract.
Is there an easier way to do it?
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PolyLine extends JPanel
{
public void paint(Graphics g) {
int[] xs = {25, 125, 85, 75, 25, 65, };
int[] ys = {50, 50, 100, 110, 150, 100};
BasicStroke traceStroke = new BasicStroke (1);
Graphics2D gc = (Graphics2D) g.create();
gc.setStroke(traceStroke);
gc.setColor(Color.BLUE);
gc.drawPolyline(xs, ys, 6);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new PolyLine());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(20,20, 1500,1500);
frame.setVisible(true);
}
}
First, a remark: It's usually preferable to have the geometric primitives as a Shape. The drawPolyline function (which uses these odd integer array coordinates) is somewhat out-dated. Creating the polyline as a Path2D is far more flexible.
For the task that you described, it will also be necessary to convert the polyline coordinates into a Path2D (if you switched to Path2D anyhow, you could omit this conversion step).
When you have the polyline as a Shape, the task is rather simple: You can create a stroked version of this shape, using a BasicStroke with the desired thickness and cap/join characteristics, by calling BasicStroke#createStrokedShape. This shape will basically be the shape of a "thick line". In order to avoid artifacts at the joins, you can create an Area from this Shape, and then draw this area.
So in the end, painting the actual outline is done with 2 lines of code, and the result is as follows:
But the MCVE here, for completeness:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeOutlineTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new ShapeOutlineTestPanel());
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ShapeOutlineTestPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
int[] xs = { 25, 125, 85, 75, 25, 65, };
int[] ys = { 50, 50, 100, 110, 150, 100 };
BasicStroke traceStroke = new BasicStroke(1);
g.setStroke(traceStroke);
g.setColor(Color.BLUE);
g.drawPolyline(xs, ys, 6);
Path2D path = new Path2D.Double();
for (int i = 0; i < xs.length; i++)
{
if (i == 0)
{
path.moveTo(xs[i], ys[i]);
}
else
{
path.lineTo(xs[i], ys[i]);
}
}
g.setColor(Color.RED);
BasicStroke stroke = new BasicStroke(
10.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g.draw(new Area(stroke.createStrokedShape(path)));
}
}

Set gradient direction in swing

I have been trying to draw lines in swing and get some laser-like effects using gradients. I want to apply the gradient on the line's width (ex: a red line core fading to orange on the edges). The problem is when I draw at an angle, I want to somehow apply the same angle to the gradient.
public void paintComponent(Graphics g) {
Graphics2D en = (Graphics2D) g;
GradientPaint gp = new GradientPaint(25, 25, Color.red, 15, 25,
Color.orange, true);
en.setPaint(gp);
en.setStroke(new BasicStroke(4.0F));
en.drawLine(10, 10, 800, 600);
}
One easy trick to get away with this, is to call setTransform() on the Graphics2D. This will do for you the rotation of both the line and the gradient at once. If you don't want to do this, then you will have to rotate the gradient itself (so basically pt1 and pt2 of the Gradient manually, ie, you need to compute them according to the rotation you have applied to your line).
Here is a small example illustrating my first idea. Just slide the ticker at the bottom to watch the line (and the gradient) rotate around the center of the panel:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestPanel extends JPanel {
private double angle = 0;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D en = (Graphics2D) g;
AffineTransform tf = AffineTransform.getTranslateInstance(-getWidth() / 2, -getHeight() / 2);
tf.preConcatenate(AffineTransform.getRotateInstance(Math.toRadians(angle)));
tf.preConcatenate(AffineTransform.getTranslateInstance(getWidth() / 2, getHeight() / 2));
en.setTransform(tf);
GradientPaint gp = new GradientPaint(25, 25, Color.red, 15, 25, Color.orange, true);
en.setPaint(gp);
en.setStroke(new BasicStroke(4.0F));
en.drawLine(400, 400, 600, 600);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
public void setAngle(double angle) {
this.angle = angle;
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
final TestPanel panel = new TestPanel();
final JSlider slider = new JSlider(0, 360);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
panel.setAngle(slider.getValue());
}
});
slider.setValue(0);
frame.add(panel);
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

Disable standard repainting of JToggleButton when It is selected

I want my JToggleButton not to repaint when It is selected. I indicate state changing by pair of words ("check/next").
Standard behavior is blue lighting but I want to disable it.
Perhaps you could show the words on ImageIcons. For example:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
public class ToggleFun {
private static final Color BACKGROUND_COLOR = new Color(200, 200, 255);
public static void main(String[] args) {
int biWidth = 60;
int biHeight = 30;
BufferedImage checkImg = new BufferedImage(biWidth, biHeight, BufferedImage.TYPE_INT_RGB);
BufferedImage nextImg = new BufferedImage(biWidth, biHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = checkImg.createGraphics();
g2.setColor(BACKGROUND_COLOR);
g2.fillRect(0, 0, biWidth, biHeight);
g2.setColor(Color.black);
g2.drawString("Check", 10, 20);
g2.dispose();
g2 = nextImg.createGraphics();
g2.setColor(BACKGROUND_COLOR);
g2.fillRect(0, 0, biWidth, biHeight);
g2.setColor(Color.black);
g2.drawString("Next", 15, 20);
g2.dispose();
ImageIcon checkIcon = new ImageIcon(checkImg);
ImageIcon nextIcon = new ImageIcon(nextImg);
JToggleButton toggleBtn = new JToggleButton(checkIcon);
toggleBtn.setSelectedIcon(nextIcon);
toggleBtn.setContentAreaFilled(false);
toggleBtn.setBorder(BorderFactory.createLineBorder(Color.black));
JPanel panel = new JPanel();
panel.add(toggleBtn);
JOptionPane.showMessageDialog(null, panel);
}
}
See: AbstractButton.setContentAreaFilled(false).
But note that users generally prefer a GUI element that follows the 'path of least surprise'. This type of rendering might be better described as going off on a bit of a crash-bang through the undergrowth beside that path.

Categories