How to draw an outline around text in AWT? - java

How can I draw an outline around any text in AWT, something similar to this picture?

two examples
Font and AffineTransform
Font, TextLayout and AffineTransform
output from this paint would be the BufferedImage, for AWT Components use method paint(), for Swing JComponents is there paintComponet()
Also, from code linked in a comment:

Try the following:
public void paintTextWithOutline(Graphics g) {
String text = "some text";
Color outlineColor = Color.white;
Color fillColor = Color.black;
BasicStroke outlineStroke = new BasicStroke(2.0f);
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D) g;
// remember original settings
Color originalColor = g2.getColor();
Stroke originalStroke = g2.getStroke();
RenderingHints originalHints = g2.getRenderingHints();
// create a glyph vector from your text
GlyphVector glyphVector = getFont().createGlyphVector(g2.getFontRenderContext(), text);
// get the shape object
Shape textShape = glyphVector.getOutline();
// activate anti aliasing for text rendering (if you want it to look nice)
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setColor(outlineColor);
g2.setStroke(outlineStroke);
g2.draw(textShape); // draw outline
g2.setColor(fillColor);
g2.fill(textShape); // fill the shape
// reset to original settings after painting
g2.setColor(originalColor);
g2.setStroke(originalStroke);
g2.setRenderingHints(originalHints);
}
}

Not sure how you're drawing the text now, but one way you could do it is use a BufferedImage as an overlay to whatever it is that you're drawing on.
Create BufferedImage using the dimensions of the string and font you are wanting to draw with (look at FontMetrics class for this).
Fill the BufferedImage with transparency.
Draw your string onto the BufferedImage with whatever color you want.
Iterate over every pixel in the BufferedImage and see how far away it is from a pixel of your text's color. If it's within a certain distance, draw that pixel black, and maybe more transparent if it's further away from the color of your text. Of course, if the pixel is already the same color as your text color, then ignore it.
Draw BufferedImage onto whatever it is that you're painting onto.
EDIT
There may be libraries out there that already do this, but if I had to code it from scratch, this is how I'd try to do it.

Here is a hacky example. It is not as sophisticated as others, but it is simpler to understand, and it behaves like a JLabel.
public class OutlineLabel extends JLabel {
private Color outlineColor = Color.WHITE;
private boolean isPaintingOutline = false;
private boolean forceTransparent = false;
public OutlineLabel() {
super();
}
public OutlineLabel(String text) {
super(text);
}
public OutlineLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
}
public Color getOutlineColor() {
return outlineColor;
}
public void setOutlineColor(Color outlineColor) {
this.outlineColor = outlineColor;
this.invalidate();
}
#Override
public Color getForeground() {
if ( isPaintingOutline ) {
return outlineColor;
} else {
return super.getForeground();
}
}
#Override
public boolean isOpaque() {
if ( forceTransparent ) {
return false;
} else {
return super.isOpaque();
}
}
#Override
public void paint(Graphics g) {
String text = getText();
if ( text == null || text.length() == 0 ) {
super.paint(g);
return;
}
// 1 2 3
// 8 9 4
// 7 6 5
if ( isOpaque() )
super.paint(g);
forceTransparent = true;
isPaintingOutline = true;
g.translate(-1, -1); super.paint(g); // 1
g.translate( 1, 0); super.paint(g); // 2
g.translate( 1, 0); super.paint(g); // 3
g.translate( 0, 1); super.paint(g); // 4
g.translate( 0, 1); super.paint(g); // 5
g.translate(-1, 0); super.paint(g); // 6
g.translate(-1, 0); super.paint(g); // 7
g.translate( 0, -1); super.paint(g); // 8
g.translate( 1, 0); // 9
isPaintingOutline = false;
super.paint(g);
forceTransparent = false;
}
public static void main(String[] args) {
JFrame w = new JFrame();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
OutlineLabel label = new OutlineLabel("Test", OutlineLabel.CENTER);
label.setOpaque(true);
w.setContentPane(new JPanel(new BorderLayout()));
w.add(label, BorderLayout.CENTER);
w.pack();
w.setVisible(true);
}
}

some stupidest workarounds:
-type same words twice but one of them is black and the other is white, put white on top of the black one, you may get something similar.
-find a font looks like above the example, and use it.

Related

Hide/Show an image drawn with Graphics

I have multiple images drawn with Graphics. How can I make them appear and disappear using a JCheckBox ?
private void drawImages(int index) {
Graphics g = mNew.getGraphics();
int x = index % this.width;
int y = index / this.width;
g.drawImage(imageLabelPixel.get(idImage-1), x, y, 100, 100, null);
}
You wouldn't use graphics to draw something on the screen you want to remove. Graphics just renders it on the screen along with all the other graphics you have drawn, It doesn't keep track of components
Your options are to add an action event to your checkbox and repaint the screen from scratch not drawing the image, or to just use a Label to draw the image and set it to invisible when the box is checked
I would do it like that:
JCheckBox cb = new JCheckBox();
ImgPanel p = new ImgPanel();
cb.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
if(cb.isSelected){
p.set(0);
} else {
p.set(-1);
}
}
});
.
public class ImgPanel extends JPanel {
private int i = 0;
private List<BufferedImage> imgs;
public ImgPanel(){
//init imgs
}
public void set(){
i = 0;
repaint();
}
#Override
public void paintComponent (Graphics g){
super.paintComponent(g);
if(i >= 0){
Image img = imgs.get(i-1);
Image img1 = img.getScaledInstance(100, 100, null);
}
g.drawImage(img1, 0, 0, null);
}
}
You can't simply draw on a graphic and then hand it to a compoennt or so (I don't really understand what your given code should have done). Instead you have to overwrite the paintComponent method of a Component and put your custom drawing code in there.

JSlider image behind track

I want to put an image (visualization of an audio) behind the JSlider which represents the audioplayer, the process of playing.
First I tried to overwrite the paint-method of the Slider
public void paintComponent(Graphics g) {
// Draw the previously loaded image to Component
g.drawImage(img, 0, -100, null);
super.paintComponent(g);
}
this worked, but the image is higher than the slider, so my next try was a JLayeredPane, where I put the JSlider above a JLabel with the image. Looks good for the first moment. But I mentioned that I need the image behind the track of the slider, not the whole slider. There is space to the left and right. Can anybody tell me a way how to calculate this space? Or the width and offset of the track to the border of the slider? This should run under Windows and MacOs, so different LookAndFeels, so I think hard coded values will not work.
Example Slider with background image
Thankyou.
My solution for this Problem is now to overwrite the SliderUI. So this is a very special component, so it's nonrelevant that it looks the same on all LookAndFeels.
It supports also jumping directly to mouse position, which is different to BasicSliderUI.
/**
*
* Custom SliderUI for AudioPlayer with audioimage in background
*/
public class AudioSliderUI extends BasicSliderUI {
private BasicStroke stroke = new BasicStroke(1f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND, 0f);
public AudioSliderUI(AudioSlider b) {
super(b);
}
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
super.paint(g, c);
}
#Override
protected Dimension getThumbSize() {
return new Dimension(2, 200);
}
#Override
public void paintTrack(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Stroke old = g2d.getStroke();
g2d.setStroke(stroke);
g2d.setPaint(Color.WHITE);
if (slider.getOrientation() == SwingConstants.HORIZONTAL) {
g2d.drawLine(trackRect.x, trackRect.y + trackRect.height / 2,
trackRect.x + trackRect.width, trackRect.y + trackRect.height / 2);
} else {
g2d.drawLine(trackRect.x + trackRect.width / 2, trackRect.y,
trackRect.x + trackRect.width / 2, trackRect.y + trackRect.height);
}
g2d.setStroke(old);
Image img = ((AudioSlider)slider).getImage();
g2d.drawImage(img, trackRect.x, trackRect.y, trackRect.width, trackRect.height, slider);
}
#Override
public void paintThumb(Graphics g) {
Rectangle knobBounds = thumbRect;
int w = knobBounds.width;
int h = 100;
int newStarty = knobBounds.height/2- h/2;
g.translate(knobBounds.x, knobBounds.y);
// "plain" version
g.setColor(Color.YELLOW);
g.fillRect(0, newStarty, w, h);
}
#Override
protected TrackListener createTrackListener(JSlider slider) {
return new TrackListener() {
#Override
public void mousePressed(MouseEvent e) {
if (UIManager.getBoolean("Slider.onlyLeftMouseButtonDrag")
&& SwingUtilities.isLeftMouseButton(e)) {
JSlider slider = (JSlider) e.getComponent();
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
slider.setValue(valueForYPosition(e.getY()));
break;
case SwingConstants.HORIZONTAL:
slider.setValue(valueForXPosition(e.getX()));
break;
default:
throw new IllegalArgumentException(
"orientation must be one of: VERTICAL, HORIZONTAL");
}
super.mousePressed(e); // isDragging = true;
super.mouseDragged(e);
} else {
super.mousePressed(e);
}
}
#Override
public boolean shouldScroll(int direction) {
return false;
}
};
}
}
Matching Slider:
public class AudioSlider extends JSlider {
private Image img;
public AudioSlider() {
setOpaque(false);
}
/**
* #return the img
*/
public Image getImage() {
return img;
}
public void setImage(Image img) {
this.img = img;
}
}
Works for me, maybe covers not all prospects.

Change Color BufferImage

I want to change the color of my BufferdImage.
When I do it like in this post: BufferedImage draw white when I say red it works only in my method which constructs my JFrame with all the components.
But when I want to use it in an ActionEvent in my Controller class, it does nothing, but the rest of the ActionEvent works, like for example change text in text field.
Methods in the JPanel class:
public void testImage(){
System.out.println("Methode suc. called");
Graphics g = image.getGraphics();
tempC = Color.GREEN;
g.setColor(tempC);
g.fillRect(150, 300, 100, 100);
tempC = Color.CYAN;
g.setColor(tempC);
g.fillOval(0, 0, 100, 100);
g.dispose();
if(image == null){System.out.println("Image is null");}
}
public void clearImage(){
Graphics g = image.getGraphics();
tempC = Color.WHITE;
g.setColor(tempC);
g.fillRect(0, 0, darstellungsBreite, darstellungsHoehe);
g.dispose();
}
The ActionListener:
public void actionPerformed(ActionEvent arg0) {
if(arg0.getActionCommand().equals(view.ACTION_CLEAR))
{
//Clear Graphics
view.drawArea.clearImage();
//view.useClear();
}

How to make drawn images transparent in Java

I got the animation to work in my Snake Clone Game. But the problem based on the picture is that the images do not have transparency(notice the white background of the circle pictures. Programming-wise, is there a fix to be able to include transparency to these drawn images?
Here's a picture containing my code and the output of the program.
P.S. On a side note, I decided to paste the direct link instead of the IMG code because I cannot seem to get it to display on StackOverFlow. I put an exclamation point in the front of the IMG code but it did not work so here's the direct link.
As the other answer mentioned, the easiest way would probably be to simply use PNG images which have a transparent background (you can create these with an image editor like GIMP). Alternatively, if you are limited to PNG images with a solid background, here's an example of how to change a given color (e.g. white) in the PNG to transparent:
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
public class SimpleFrame extends JFrame {
JPanel mainPanel = new JPanel() {
ImageIcon originalIcon = new ImageIcon("~/Pictures/apple.png");
ImageFilter filter = new RGBImageFilter() {
int transparentColor = Color.white.getRGB() | 0xFF000000;
public final int filterRGB(int x, int y, int rgb) {
if ((rgb | 0xFF000000) == transparentColor) {
return 0x00FFFFFF & rgb;
} else {
return rgb;
}
}
};
ImageProducer filteredImgProd = new FilteredImageSource(originalIcon.getImage().getSource(), filter);
Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
public void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getSize().width, getSize().height);
// draw the original icon
g.drawImage(originalIcon.getImage(), 100, 10, this);
// draw the transparent icon
g.drawImage(transparentImg, 140, 10, this);
}
};
public SimpleFrame() {
super("Transparency Example");
JPanel content = (JPanel)getContentPane();
mainPanel.setBackground(Color.black);
content.add("Center", mainPanel);
}
public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SimpleFrame c = new SimpleFrame();
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setSize(280,100);
c.setVisible(true);
}
});
}
}
Don't use paint to draw your images. Use some other program that uses alpha like Paint.net or Photoshop... If your going to use circles forever then you can use g.drawOval(x, y, w, h).
Simple use type to ARGB like this
BufferedImage image = new BufferedImage(
width, height,
BufferedImage.TYPE_INT_ARGB);
I hope it should work.
public BufferedImage makeTransparentImage(BufferedImage br) {
for (int i = 0; i < br.getHeight(); i++) {
for (int j = 0; j < br.getWidth(); j++) {
Color c = new Color(br.getRGB(j, i));
int r = c.getRed();
int b = c.getBlue();
int g = c.getGreen();
if ((r == 255 && b == 255 && g == 255)) {
System.out.println("r g b " + r + g + b);
br.setRGB(j, i, 0xFF000000);
}
}
}
return br;
}
If you draw a simple picture,
The easiest and fastest way I know...
Draw a picture in Macrosoft PowerPoint and click "Save as Picture" to get a transparent background. Next...
public class Node {
Image nodeImage[] = new Image[3];
public Node() {
try {
String address = "C:\\Users\\Desktop\\practice\\Simulation\\img\\";
nodeImage[0] = ImageIO.read(new File(address + "Node_noVehicle.png"));
nodeImage[1] = ImageIO.read(new File(address + "Node_setVehicle.png"));
nodeImage[2] = ImageIO.read(new File(address + "Node_inVehicle.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2dtemp = (Graphics2D) g.create();
g2dtemp.drawImage(Node.nodeImage[0],(int)x,(int)y,width,height,this);
}
}
Draw a picture in Macrosoft PowerPoint and click "Save as Picture" to get a transparent background.

java resize image dynamically to fit grids in gridlayout

I want to make a chess type board using a custom subclass of JButton. My problem is that my images of the chess pieces are a bit too small. Is there a way I can get the image to scale to exactly the size of each grid in my gridlayout? If I resize the Jframe, the grids will change size as well. Is there a way to get the image to resize dynamically upon resizing of the whole frame?
You have 3 option for this
1) Resize the images themselves using Gimp, Photoshop, etc.
2) Create an icon dynamically
Image i = icon.getImage();
if(i != null){
int width = (int)(size * fraction);
int height =(int)(size*icon.getIconHeight()/icon.getIconWidth()*fraction);
miniature = new ImageIcon(i.getScaledInstance(width, height, Image.SCALE_SMOOTH));
}
3) on the paint of your frame you can use scale
private void scaledDrawing(Graphics g, float scale){
Graphics2D g2 = (Graphics2D) g;
AffineTransform at = new AffineTransform();
AffineTransform save = g2.getTransform();
at.setToIdentity();
at.scale(goa.getScale().x, goa.getScale().y);
g2.transform(at);
image.paintIcon(c, g2);
g2.setTransform(save);
}
You could apply some transformation to the images but it might look a little ugly. If the images are small enough, maybe you can just force a minimum size of the button so that a scrollbar will appear if the frame is sized really small. Another option might be to have two or three different sets of the images at nicely scaled sizes, and swap them out for different board sizes.
Another alternative will be to override the paint function to fill all the available place:
#Override
public final void paint(final Graphics g) {
super.paint(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
Here is an example:
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
public final class Tileset extends Component {
private Image image;
public Tileset(final Image image) {
this.image = image;
}
#Override
public final void paint(final Graphics g) {
super.paint(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
public final Image getImage() {
return (image);
}
public final void setImage(final Image image) {
this.image = image;
}
}
With:
import javax.swing.JPanel;
import java.awt.GridLayout;
public final class Map extends JPanel {
public Map(final GridLayout layout) {
setLayout(layout);
}
public Map(final Integer width, final Integer height) {
this(new GridLayout(width, height));
}
}
And:
final Map map = new Map(13, 17);
final Image grass = new ImageIcon("src/main/res/tilesets/grass1.png").getImage();
final Image wood = new ImageIcon("src/main/res/tilesets/wood1.png").getImage();
final Image rock = new ImageIcon("src/main/res/tilesets/rock1.png").getImage();
for (int i = 0; i != 13; ++i) {
for (int j = 0; j != 17; ++j) {
if (i % 2 == 0) {
if (j % 2 == 0)
map.add(new Tileset(grass), i, j);
else
map.add(new Tileset(rock), i, j);
}
else
map.add(new Tileset(wood), i, j);
}
}
That will give you:

Categories