I use this code: to outline a font:
public class MyText extends JPanel {
String text1 = null;
public MyText (String text) {
text1 = text;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.white);
int w = getSize().width;
int h = getSize().height;
Graphics2D g2d = (Graphics2D) g;
FontRenderContext fontRendContext = g2d.getFontRenderContext();
Font font = new Font("Verdana", 1, 72);
String st = new String(text1);
TextLayout text = new TextLayout(st, font, fontRendContext);
Shape shape = text.getOutline(null);
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);
g2d.setColor(Color.black);
g2d.draw(shape);
g2d.setClip(shape);
}
The problem is I have no idea how to adjust the thickness of the outline.
I tried displaying another bigger string over the first string, but the result is quite bad (wrong pixels...).
Hav you got any ideas?
Thanks in advance.
You can use setStroke. For example
g2d.setStroke(new BasicStroke(4));
Start by taking a look at Stroking and Filling Grapcs Primitives and Fun with Java2D - Strokes
Related
I am trying to calculate image center(to add water marks)following - add-water-to-image and the results shows negative x, for some of the images, here is the math part:
int centerX = (sourceImage.getWidth() - (int) rect.getWidth()) / 2;
int centerY = sourceImage.getHeight() / 2;
and the entire function:
public void addTextWatermark(String text, File sourceImageFile, File destImageFile, int textSize) {
try {
BufferedImage sourceImage = ImageIO.read(sourceImageFile);
Graphics2D g2d = (Graphics2D) sourceImage.getGraphics();
// initializes necessary graphic properties
AlphaComposite alphaChannel = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f);
g2d.setComposite(alphaChannel);
g2d.setColor(Color.BLACK.darker());
//Font(fontName, fontStyle, foneSize)
g2d.setFont(new java.awt.Font("Arial", Font.BOLD, textSize));
FontMetrics fontMetrics = g2d.getFontMetrics();
//text - input text , g2d - Graphics2D
Rectangle2D rect = fontMetrics.getStringBounds(text, g2d);
// calculates the coordinate where the String is painted
int centerX = (sourceImage.getWidth() - (int) rect.getWidth()) / 2;
int centerY = sourceImage.getHeight() / 2;
// paints the textual watermark
g2d.drawString(text, centerX, centerY);
ImageIO.write(sourceImage, "png", destImageFile);
g2d.dispose();
} catch (IOException ex) {
System.out.println(ex.getMessage().toString());
}
}
1-Is there a way to ensure the math will work for all images?
2-Is there a diffrence between jpg and png in this calclation?
Thanks.
edit
The image sizes that was causeing it were:
1-to big(3000*3000).
2-to small(60*60).
With textSize(g2d.setFont(new java.awt.Font("Arial", Font.BOLD, textSize));) - 32 or less.
So I found 2 ways to handle this issue:
1- if x or y are smaller then 0 I have resize the image using resize-image
2- if the watermark is to small just increase text size - g2d.setFont(new java.awt.Font("Arial", Font.BOLD, textSize));
Thanks for all the help #jon Skeet.
I've created this custom JToolTip for my application. When the tooltip is entirely diplayed inside a JFrame, no background is visible (expected), but when the tooltip is displayed outside the JFrame, the background will be visible. How can I have it removed either way?
I've tried setBackground(new Color(255, 255, 255, 0)); with the '0' alpha value to make sure the background is transparent, but that didn't do the trick.
The tooltip inside the frame, as expected:
The tooltip exceeding the JFrame, with the unwanted background:
The custom JTooltip:
public class DefaultToolTip extends JToolTip {
public DefaultToolTip() {
setOpaque(false);
setPreferredSize(new Dimension(275, 30));
setBackground(new Color(255, 255, 255, 0));
}
#Override
public void addNotify() {
super.addNotify();
setOpaque(false);
Component parent = this.getParent();
if (parent != null) {
if (parent instanceof JComponent) {
JComponent jparent = (JComponent) parent;
jparent.setOpaque(false);
}
}
}
#Override
public void paint(Graphics g) {
String text = getComponent().getToolTipText();
addNotify();
Graphics2D g2d = drawComponent(g);
drawText(text, g2d);
g2d.dispose();
}
private void drawText(String text, Graphics2D g2d) {
//Draw the text
int cHeight = getComponent().getHeight();
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.WHITE);
if (cHeight > getHeight())
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (getHeight() + fm.getAscent()) / 2 + 2);
else
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (cHeight + fm.getAscent()) / 2 + 2);
}
private Graphics2D drawComponent(Graphics g) {
//Create a round rectangle
Shape round = new RoundRectangle2D.Float(0, 8, getWidth(), getHeight(), 8, 8);
//Draw the background
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GRAY);
g2d.fill(round);
//Draw the left triangle
Point p1 = new Point(getWidth() / 2, getHeight() - 30);
Point p2 = new Point(getWidth() / 2 + 8, getHeight() - 20);
Point p3 = new Point(getWidth() / 2 - 8, getHeight() - 20);
int[] xs = {p1.x, p2.x, p3.x};
int[] ys = {p1.y, p2.y, p3.y};
Polygon triangle = new Polygon(xs, ys, xs.length);
g2d.fillPolygon(triangle);
return g2d;
}
}
Solution
A few things have changed to have the tooltip behave as expected. the paint method has been replaced by the paintComponent method, the addNotify call was removed, the method updated to fetch the window of the component and to give it a transparent background. setBorder(BorderFactory.createEmptyBorder()); was also needed to remove the components default border.
public class DefaultToolTip extends JToolTip {
public DefaultToolTip() {
setOpaque(false);
setPreferredSize(new Dimension(275, 30));
setBackground(new Color(255, 255, 255, 0));
setBorder(BorderFactory.createEmptyBorder());
}
#Override
public void addNotify() {
super.addNotify();
setOpaque(false);
Component parent = this.getParent();
if (parent != null) {
if (parent instanceof JComponent) {
JComponent jparent = (JComponent) parent;
jparent.setOpaque(false);
}
}
Window window = SwingUtilities.windowForComponent(this);
try {
window.setBackground(new Color(255, 255, 255, 0));
} catch (IllegalComponentStateException e) {
//Do nothing
}
}
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g);
String text = getComponent().getToolTipText();
Graphics2D g2d = drawComponent(g);
drawText(text, g2d);
g2d.dispose();
}
private void drawText(String text, Graphics2D g2d) {
//Draw the text
int cHeight = getComponent().getHeight();
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.WHITE);
if (cHeight > getHeight())
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (getHeight() + fm.getAscent()) / 2 + 2);
else
g2d.drawString(text, (getWidth() - fm.stringWidth(text)) / 2, (cHeight + fm.getAscent()) / 2 + 2);
}
private Graphics2D drawComponent(Graphics g) {
//Create a round rectangle
Shape round = new RoundRectangle2D.Float(0, 8, getWidth(), getHeight(), 8, 8);
//Draw the background
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GRAY);
g2d.fill(round);
//Draw the left triangle
Point p1 = new Point(getWidth() / 2, getHeight() - 30);
Point p2 = new Point(getWidth() / 2 + 8, getHeight() - 20);
Point p3 = new Point(getWidth() / 2 - 8, getHeight() - 20);
int[] xs = {p1.x, p2.x, p3.x};
int[] ys = {p1.y, p2.y, p3.y};
Polygon triangle = new Polygon(xs, ys, xs.length);
g2d.fillPolygon(triangle);
return g2d;
}
}
A note however, super.paintComponent(g) was commented out, since it would draw text another time.
Don't know if any of these will help but:
Don't override paint(...). Custom painting is done by overriding paintComponent(...).
Invoke super.paintComponent(...) as the first statement
Don't invoke addNotify() in a painting method. A painting method is for painting only.
with the '0' alpha value to make sure the background is transparent,
Swing components don't know how to handle transparent backgrounds. Just make the component non-opaque.
When the tooltip overlaps the component. The tooltip is actually added to a JWindow before it is displayed. So in your addNotify() logic, you can search for the window and make it transparent.
Check out:
Window window = SwingUtilities.windowForComponent(...);
Is there anyway to find the current position of a buffered image on jpanel?
I draw an image on buffer, named it currentImage. Now I want to move it around a Panel by using affine transform.
To do translation by using mouse, I have to know the current position, new position and update the new position.
But I have trouble in getting the current position of the BufferedImage. There is no method in the IDE's suggestion working.
Can anyone give me an idea how to do this ?? Thanks!
EDIT
this is my draw and paintComponent method:
private void draw(){
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC);
int w = this.getWidth(); int h = this.getHeight();
if (currentImage == null || width != w || height != h){
width = w;
height = h;
currentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
graphics = currentImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
drawClearAndGradedArea(graphics);
drawActualRunway(graphics, width, height, width, height);
drawLeftToRight(graphics);
drawRightToLeft(graphics);
drawCentralLine(graphics);
graphics.setComposite(ac);
}
}
paintComponent()
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.clearRect(0,0, this.getWidth(), this.getHeight());
RenderingHints rh = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
/*
* main components on panel
*/
this.draw();
this.animation();
g2d.drawImage(currentImage, transform, this);
drawNote(g2d);
g2d.dispose();
}
I need to create font of a givens size in pixels.
Java's Font class constructor requires font size expressed in points. Points are physical length while pixels are digitizes. So I need dpi.
It is said in manual, that this value is contained inside FontRenderContext.getTransform().
I found, that in my case, scaling is one, i.e. pixels = points.
Unfortunately, creating font of size 100 creates bigger image.
For example, code below
BufferedImage ans = new BufferedImage(width, height, imageType);
Font font = new Font(fontName,fontStyle,height);
Graphics2D g2 = ans.createGraphics();
g2.setFont(font);
FontMetrics fm = g2.getFontMetrics();
FontRenderContext frc = g2.getFontRenderContext();
System.out.println("height=" + height);
System.out.println("frc.getTransform()=" +frc.getTransform());
System.out.println("g2.getTransform()=" +g2.getTransform());
System.out.println("fm.getAscent()+fm.getDescent()="+fm.getAscent()+"+"+fm.getDescent()+"="+(fm.getAscent()+fm.getDescent()));
g2.drawString(str, 0, fm.getAscent());
gives
height=100
frc.getTransform()=AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
g2.getTransform()=AffineTransform[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
fm.getAscent()+fm.getDescent()=93+20=113
How to fit?
I've used this code to determine the size of a String in pixels when drawing the String.
The x and y calculation centers the String in the drawing area. The y calculation looks odd because the y origin is at the bottom left, not the top left.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (font == null) {
return;
}
Graphics2D g2d = (Graphics2D) g;
FontRenderContext frc = g2d.getFontRenderContext();
TextLayout layout = new TextLayout(sampleString, font, frc);
Rectangle2D bounds = layout.getBounds();
int width = (int) Math.round(bounds.getWidth());
int height = (int) Math.round(bounds.getHeight());
int x = (getWidth() - width) / 2;
int y = height + (getHeight() - height) / 2;
layout.draw(g2d, (float) x, (float) y);
}
// using javafx: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/text/package-summary.html
Text text = new Text("Hello World");
Font font = Font.font("Arial", 10); // 10 is point size
text.setFont(font);
double width = text.getLayoutBounds().getWidth(); // width is pixel size
My Program overrides public void paint(Graphics g, int x, int y); in order to draw some stings using g.drawString(someString, x+10, y+30);
Now someString can be quite long and thus, it may not fit on one line.
What is the best way to write the text on multiple line. For instance, in a rectangle (x1, y1, x2, y2)?
Thanks to Epaga's hint and a couple of examples on the Net (not so obvious to find! I used mainly Break a Line for text layout), I could make a component to display wrapped text. It is incomplete, but at least it shows the intended effect.
class TextContainer extends JPanel
{
private int m_width;
private int m_height;
private String m_text;
private AttributedCharacterIterator m_iterator;
private int m_start;
private int m_end;
public TextContainer(String text, int width, int height)
{
m_text = text;
m_width = width;
m_height = height;
AttributedString styledText = new AttributedString(text);
m_iterator = styledText.getIterator();
m_start = m_iterator.getBeginIndex();
m_end = m_iterator.getEndIndex();
}
public String getText()
{
return m_text;
}
public Dimension getPreferredSize()
{
return new Dimension(m_width, m_height);
}
public void paint(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
measurer.setPosition(m_start);
float x = 0, y = 0;
while (measurer.getPosition() < m_end)
{
TextLayout layout = measurer.nextLayout(m_width);
y += layout.getAscent();
float dx = layout.isLeftToRight() ?
0 : m_width - layout.getAdvance();
layout.draw(g2, x + dx, y);
y += layout.getDescent() + layout.getLeading();
}
}
}
Just for fun, I made it fitting a circle (alas, no justification, it seems):
public void paint(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
measurer.setPosition(m_start);
float y = 0;
while (measurer.getPosition() < m_end)
{
double ix = Math.sqrt((m_width / 2 - y) * y);
float x = m_width / 2.0F - (float) ix;
int width = (int) ix * 2;
TextLayout layout = measurer.nextLayout(width);
y += layout.getAscent();
float dx = layout.isLeftToRight() ?
0 : width - layout.getAdvance();
layout.draw(g2, x + dx, y);
y += layout.getDescent() + layout.getLeading();
}
}
I am not too sure about dx computation, though.
java.awt.font.TextLayout might be helpful. Here's a snippet of their example code:
Graphics2D g = ...;
Point2D loc = ...;
Font font = Font.getFont("Helvetica-bold-italic");
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout("This is a string", font, frc);
layout.draw(g, (float)loc.getX(), (float)loc.getY());
Rectangle2D bounds = layout.getBounds();
bounds.setRect(bounds.getX()+loc.getX(),
bounds.getY()+loc.getY(),
bounds.getWidth(),
bounds.getHeight());
g.draw(bounds);
Otherwise you could always use a Swing text element to do the job for you, just pass in the Graphics you want it to paint into.
Incrementally build your string, one word at a time, using Epaga's method to find the length of your string. Once the length is longer than your rectangle, remove the last word and print. Repeat until you run out of words.
It sounds like a bad algorithm, but for each line, it's really O(screenWidth/averageCharacterWidth) => O(1).
Still, use a StringBuffer to build your string!
Had some trouble myself this is my solution:
Graphics2D g=....
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout(text, font, frc);
String[] outputs = text.split("\n");
for(int i=0; i<outputs.length; i++){
g.drawString(outputs[i], 15,(int) (15+i*layout.getBounds().getHeight()+0.5));
Hope that helps.... simple but it works.
You can use a JLabel and embed the text with html.
JLabel.setText("<html>"+line1+"<br>"+line2);