stringWidth it's very slow - java

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

Related

Is it possible to set outer stroke for text?

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

Java is drawing 2 boxes

So I am making a little game to learn some graphical java and I am having trouble with a button. It is drawing 2, one is the correct size and in the correct location and then there is a very small button centered at the top of the application. THere should only be the one button at (0,0,200,50). I do not know what is wrong but here is the code for the button, if you need something more then this let me know!
ImageIcon test = new ImageIcon("nhButton.png");
JButton jb = new JButton(test);
jb.setBounds(0, 0, 200, 50);
jb.setVisible(true);
add(jb);
EDIT1: the 2 classes where error will be: board.java:
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Board extends JPanel {
public Board() {
}
#Override
public void paintComponent(Graphics g) {
ImageIcon test = new ImageIcon("nhButton.png");
JButton jb = new JButton(test);
jb.setBounds(0, 0, 200, 50);
jb.setVisible(true);
add(jb);
}
private void drawRectangle(Graphics g, int x, int y, int width, int height) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(x, y, width, height);
}
}
and the main:
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class main extends JFrame {
public main() {
initUI();
}
private void initUI() {
add(new Board());
setSize(800, 600);
setTitle("Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
main ex = new main();
ex.setVisible(true);
}
});
}
}
If you try to resize window, you will see that buttons are spawning.
This happens because of your paintComponent method, which is called every painting iteration.
You should move button addition, for example, to constructor which is called once:
public Board() {
ImageIcon test = new ImageIcon("nhButton.png");
JButton jb = new JButton(test);
jb.setBounds(0, 0, 200, 50);
jb.setVisible(true);
add(jb);
}

Only 1 Corner is Rounded when I call fillRoundRect()

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.

Running an animation using JCreator

I wrote this program which animates a red rectangle, using JCreator, I am finding difficulty in running the annimation, does anybody have any suggestions?
This is the program which animates the rectangle:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Tutorial extends JPanel implements ActionListener
{
Timer tm = new Timer(5, this);
int x = 0 , velX = 2;
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
tm.start();
}
public void actionPerformed(ActionEvent e){
x = x + velX;
repaint();
}
}

Program not accessing method paintComponent() of extended JPanel class

This is the JFrame
package client.connection;
import java.awt.Dimension;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import javax.swing.JFrame;
class DrawFrameRemoteControl extends JFrame
{
private DrawPanelRemoteControl imagePanel;
private ClientRemoteControlConnection clientRemoteControlConnection;
private ObjectInputStream clientInputStream;
private ObjectOutputStream clientOutputStream;
private Dimension imageDimension;
private Dimension serverDimension;
public DrawFrameRemoteControl(Dimension imageDimension,ClientRemoteControlConnection clientRemoteControlConnection,ObjectInputStream clientInputStream,ObjectOutputStream clientOutputStream,Dimension serverDimension)
{
super("Remote Desktop Control");
this.clientRemoteControlConnection=clientRemoteControlConnection;
this.clientInputStream=clientInputStream;
this.clientOutputStream=clientOutputStream;
this.imageDimension=imageDimension;
this.serverDimension=serverDimension;
imagePanel=new DrawPanelRemoteControl(imageDimension);
add(imagePanel);
setSize(imageDimension.width,imageDimension.height);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
}
void drawNewImageGrayscale(byte[] array)
{
imagePanel.setNewImageGrayscale(array);
imagePanel.repaint();
}
}
And this is the extended JPanel class-
package client.connection;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.MemoryImageSource;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
class DrawPanelRemoteControl extends JPanel
{
private byte[] byteArray=null;
private Image image;
private JLabel imageLabel=new JLabel();
private Dimension imageDimension;
public DrawPanelRemoteControl(Dimension imageDimension)
{
this.imageDimension=imageDimension;
add(imageLabel);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
System.out.println(".");
if(byteArray!=null)
{
image=getGrayscaleImageFromArray(byteArray,imageDimension.width,imageDimension.height);
imageLabel.setIcon(new ImageIcon(image));
}
}
private Image getGrayscaleImageFromArray(byte[] buffer, int width, int height)
{
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
int[] nBits = { 8 };
ColorModel cm = new ComponentColorModel(cs, nBits, false, true,Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
SampleModel sm = cm.createCompatibleSampleModel(width, height);
DataBufferByte db = new DataBufferByte(buffer, width * height);
WritableRaster raster = Raster.createWritableRaster(sm, db, null);
BufferedImage result = new BufferedImage(cm, raster, false, null);
return result;
}
void setNewImageGrayscale(byte[] array)
{
this.byteArray=array;
this.intArray=null;
}
}
I have tried debugging the code, even though imagePanel.repaint() is being executed many times, the program never reaches the paintComponent() method of DrawPanelRemoteControl class.
Can anybody give me any idea why this might be happening? Has it got anything to do with the imageDimension object?
Additional Information : In main() method, a DrawFrameRemoteControl object is created and it's drawNewImageGrayscale(byte[] arr) method is being updated from main() every second.
It's not clear why you're passing around byte[], but it looks like you want to update a component's Icon with a gray thumbnail. The example below creates grayscale icons from existing sample icons and uses setIcon() to do the update. A similar approach works with any Image. See also this example that suggests ColorConvertOp, and this example that updates whole components rather than icons.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.Timer;
import javax.swing.UIManager;
/**
* #see https://stackoverflow.com/a/12228640/230513
* #see https://stackoverflow.com/a/7935040/230513
*/
public class GrayIcons extends JPanel {
private List<Icon> list = new ArrayList<Icon>();
private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
private Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update();
}
});
public GrayIcons() {
this.setLayout(new GridLayout(1, 0));
list.add(getGray(UIManager.getIcon("OptionPane.errorIcon")));
list.add(getGray(UIManager.getIcon("OptionPane.informationIcon")));
list.add(getGray(UIManager.getIcon("OptionPane.warningIcon")));
list.add(getGray(UIManager.getIcon("OptionPane.questionIcon")));
for (Icon icon : list) {
JToggleButton jtb = new JToggleButton(icon);
buttons.add(jtb);
this.add(jtb);
}
timer.start();
}
private void update() {
Collections.shuffle(list);
int index = 0;
for (JToggleButton b : buttons) {
b.setIcon(list.get(index++));
}
}
/**
* #see https://stackoverflow.com/q/5830533/230513
* #see https://stackoverflow.com/a/3106550/230513
*/
private Icon getGray(Icon icon) {
final int w = icon.getIconWidth();
final int h = icon.getIconHeight();
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage image = gc.createCompatibleImage(w, h);
Graphics2D g2d = image.createGraphics();
g2d.setPaint(new Color(0x00f0f0f0));
g2d.fillRect(0, 0, w, h);
icon.paintIcon(null, g2d, 0, 0);
BufferedImage gray = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
ColorConvertOp op = new ColorConvertOp(
image.getColorModel().getColorSpace(),
gray.getColorModel().getColorSpace(), null);
op.filter(image, gray);
return new ImageIcon(gray);
}
private void display() {
JFrame f = new JFrame("GrayIcons");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new GrayIcons().display();
}
});
}
}

Categories