I'm trying to make outer stroke for text. When I increase width parameter of BasicStroke my outline spreads both inside and outside text, but I need only outside part.
My result:
Are there some properties for that? Or maybe there is a way to cut stroke from within text?
Example code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import javax.swing.JPanel;
import javax.swing.JApplet;
import javax.swing.JFrame;
public class FontPaint extends JApplet {
public void init() {
FontPanel fontPanel = new FontPanel();
getContentPane().add(fontPanel, BorderLayout.CENTER);
}
public static void main(String[] args) {
FontPanel starPanel = new FontPanel();
JFrame f = new JFrame("Font");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.getContentPane().add(starPanel, BorderLayout.CENTER);
f.setSize(new Dimension(550, 200));
f.setVisible(true);
}
}
class FontPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.white);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
FontRenderContext frc = g2.getFontRenderContext();
Font f = new Font("Helvetica", 1, 60);
String s1 = "Java Source and Support.";
TextLayout textTl = new TextLayout(s1, f, frc);
AffineTransform transform1;
Shape outline1 = textTl.getOutline(null);
transform1 = g2.getTransform();
double newWidth1 = 301;
double newHeight1 = 427;
textTl.draw(g2, (int)newWidth1, (int)newHeight1);
transform1.translate(newWidth1, newHeight1);
g2.transform(transform1);
g2.setColor(Color.blue);
g2.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
g2.draw(outline1);
}
}
There probably is no nice "half stroke." So first draw the outline, then fill the glyph vector. (For opaque colors only.)
transform1 = g2.getTransform();
double newWidth1 = 301;
double newHeight1 = 427;
transform1.translate(newWidth1, newHeight1);
g2.transform(transform1);
g2.setColor(Color.blue);
g2.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND));
g2.draw(outline1);
textTl.draw(g2, 0, 0);
Which should be a short cut for:
...
transform1.translate(-newWidth1, -newHeight1);
textTl.draw(g2, (int)newWidth1, (int)newHeight1);
Related
I need to write a generic method that draw a centered String. In order to do that I need to know the width of the String and in order to calculate that I have different alternatives:
Rectangle2D rect=Toolkit.getDefaultToolkit().getFontMetrics(gc.getFont()).getStringBounds(text, gc);
or
AffineTransform affinetransform = new AffineTransform();
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
Rectangle2D rect=gc.getFont().getStringBounds(text, frc);
or
FontMetrics metrics = g.getFontMetrics(font);
metrics.stringWidth(text)
But does't matter which approach I use it takes around 1 second to calculate that, that's crazy I'm using Eclipse with Java 1.8.0_121 on a on Macbook 3,1 GHz Intel Core i7, that's crazy!
What is wrong with it?
First Example:
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame jf = new JFrame("Demo");
Container cp = jf.getContentPane();
MyCanvas1 tl = new MyCanvas1();
cp.add(tl);
jf.setSize(300, 200);
jf.setVisible(true);
}
}
class MyCanvas1 extends JComponent {
public void paint(Graphics g) {
long start=System.currentTimeMillis();
Graphics2D g2 = (Graphics2D) g;
drawCenteredString(g2, "Example", getBounds(), g.getFont());
System.out.println("executed in:"+(System.currentTimeMillis()-start));
}
public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
FontMetrics metrics = g.getFontMetrics(font);
int x = rect.x + (rect.width - metrics.stringWidth(text)) / 2;
// Determine the Y coordinate for the text (note we add the ascent, as in java 2d 0 is top of the screen)
int y = rect.y + ((rect.height - metrics.getHeight()) / 2) + metrics.getAscent();
// Set the font
g.setFont(font);
// Draw the String
g.drawString(text, x, y);
}
}
executed in:922
Example 2:
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame jf = new JFrame("Demo");
Container cp = jf.getContentPane();
MyCanvas1 tl = new MyCanvas1();
cp.add(tl);
jf.setSize(300, 200);
jf.setVisible(true);
}
}
class MyCanvas1 extends JComponent {
public void paint(Graphics g) {
long start=System.currentTimeMillis();
Graphics2D g2 = (Graphics2D) g;
drawCenteredString(g2, "Example", getBounds(), g.getFont());
System.out.println("executed in:"+(System.currentTimeMillis()-start));
}
public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
AffineTransform affinetransform = new AffineTransform();
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
Rectangle2D rect2=font.getStringBounds(text, frc);
// Draw the String
g.drawString(text, (int) (rect.width/2-rect2.getWidth()/2),(int) (rect.height/2-rect2.getHeight()/2));
}
}
executed in:916
Example 3:
import java.awt.Container;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame jf = new JFrame("Demo");
Container cp = jf.getContentPane();
MyCanvas1 tl = new MyCanvas1();
cp.add(tl);
jf.setSize(300, 200);
jf.setVisible(true);
}
}
class MyCanvas1 extends JComponent {
public void paint(Graphics g) {
long start=System.currentTimeMillis();
Graphics2D g2 = (Graphics2D) g;
drawCenteredString(g2, "Example", getBounds(), g.getFont());
System.out.println("executed in:"+(System.currentTimeMillis()-start));
}
public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
AffineTransform affinetransform = new AffineTransform();
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
Rectangle2D rect2=Toolkit.getDefaultToolkit().getFontMetrics(font).getStringBounds(text, g);
// Draw the String
g.drawString(text, (int) (rect.width/2-rect2.getWidth()/2),(int) (rect.height/2-rect2.getHeight()/2));
}
}
executed in:908
It seems somebody else had a similar problem but without any solution:
Java: Fastest way to draw text?
Does somebody see where the problem could be?
EDIT:
Just an update using Java 8u221 same results, using the latest Java 13 I got a little improvement instead of 900 they are executed in around 700 milliseconds... I cannot believe that... It seems this operations are very slow on Mac... doesn't matter the Java version I use...
EDIT2:
Even executing this code:
public class Main {
public static void main(String[] args) {
long start=System.currentTimeMillis();
BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();
drawCenteredString(g2, "Example", new Rectangle (0,0,300,300), g2.getFont());
System.out.println("executed in:"+(System.currentTimeMillis()-start));
}
public static void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
Rectangle2D rect2=Toolkit.getDefaultToolkit().getFontMetrics(font).getStringBounds(text, g);
// Draw the String
g.drawString(text, (int) (rect.width/2-rect2.getWidth()/2),(int) (rect.height/2-rect2.getHeight()/2));
}
}
I get: executed in:1342
I want to draw out a JLabel in a "public void paint(Graphics g) {}" and all that stuff. How do I take a JLabel from outside of the class (within the main method) and draw it out in the paint method?
static JLabel drawThisOut = new JLabel("draw this out");
public static void main(String[] args) {
class paintJLabel extends JComponent {
public void paint(Graphics g) {
//what goes here to draw the JLabel?????
}
}
}
And no, I cannot just use the regular JLabel instead of drawing it out. I need to draw something else over the JLabel and I assumed this was the most efficient way to do it. (Is there a more efficient way?) Any help at all would be greatly appreciated. Please be specific. Thanks!
In the "simplest" terms, you could use a Border of some kind, the following presents a custom rounded border, but the concept is relatively simple
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.AbstractBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JLabel label = new JLabel("This is a test");
label.setBorder(new RoundedBorder(Color.BLACK, 20));
add(label);
}
}
public class RoundedBorder extends AbstractBorder {
private final Color color;
private final int gap;
public RoundedBorder(Color c, int g) {
color = c;
gap = g;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
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);
g2d.setColor(color);
g2d.draw(new RoundRectangle2D.Double(x, y, width - 1, height - 1, gap, gap));
g2d.dispose();
}
#Override
public Insets getBorderInsets(Component c) {
return (getBorderInsets(c, new Insets(gap, gap, gap, gap)));
}
#Override
public Insets getBorderInsets(Component c, Insets insets) {
insets.left = insets.top = insets.right = insets.bottom = gap / 2;
return insets;
}
#Override
public boolean isBorderOpaque() {
return false;
}
}
}
You could also use a custom component. This provides you with the added benefit of been able to "fill" the shape with your own background color, but is, otherwise, essentially the same thing
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.AbstractBorder;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JLabel label = new JLabel("This is a test");
label.setBorder(new EmptyBorder(10, 10, 10, 10));
RoundedPane pane = new RoundedPane();
pane.add(label);
add(pane);
}
}
public class RoundedPane extends JPanel {
public RoundedPane() {
setLayout(new BorderLayout());
}
#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);
g2d.setColor(Color.BLACK);
g2d.draw(new RoundRectangle2D.Double(0, 0, getWidth() - 1, getHeight() - 1, 20, 20));
g2d.dispose();
}
}
}
When run this code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JLabel;
public class CustomButton extends JButton {
int width = 100;
int height = 50;
int radius = 10;
JLabel lab;
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.ORANGE);
g2.fillRoundRect(0, 0, width, height, radius, radius);
g2.dispose();
super.paintComponent(g2);
super.paintComponent(g);
}
}
And my other class:
package custom.frame;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CustomFrame {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new FlowLayout());
f.setSize(500,500);
f.setLocationRelativeTo(null);
JPanel pane = new JPanel();
pane.setBounds(0,0,500,500);
CustomButton btn = new CustomButton();
pane.add(btn);
f.add(btn);
f.setVisible(true);
}
}
I get a get a regular rectangle with only 1 rounded side. Please see the image below.
Is this the expected function?
If not, how can I fix this.
Edit
I can get 2 rounded corners if I do this:
g2.fillRoundRect(0, 0, 50, 50, 7, 7);
The only thing I can really think of is that the window containing the rectangle is too small and is cutting off the other three corners, but that seems pretty unlikely.
Is there any way that I could assign a specific image to a String so that the output would be something like this (im using the String with a JLabel):
The basic process is to create an outline of the text and render it...easy ;)
This uses the TextLayout class to generate a Shape of the text, which can filled and drawn.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TextOutline {
public static void main(String[] args) {
new TextOutline();
}
public TextOutline() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new FontPaint());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
class FontPaint extends JPanel {
public FontPaint() {
setFont(getFont().deriveFont(Font.BOLD, 48f));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
Graphics2D g2d = (Graphics2D) g;
FontRenderContext fontRendContext = g2d.getFontRenderContext();
// Not required, but it will make it look nice
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);
String st = "Hello World.";
// Create an layout of the text
TextLayout text = new TextLayout(st, getFont(), fontRendContext);
// Generate a shape of the layout
Shape shape = text.getOutline(null);
// Align the shape to the center
Rectangle rect = shape.getBounds();
AffineTransform affineTransform = new AffineTransform();
affineTransform = g2d.getTransform();
affineTransform.translate(w / 2 - (rect.width / 2), h / 2
+ (rect.height / 2));
g2d.transform(affineTransform);
// Fill in blue
g2d.setColor(Color.BLUE);
g2d.fill(shape);
// Outline in red
g2d.setColor(Color.red);
g2d.draw(shape);
g2d.dispose();
}
}
}
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.