如何在AWT中绘制文本周围的轮廓?

14

我该如何在AWT中为任意文本绘制轮廓线,类似于这张图片?

轮廓线

5个回答

9

请尝试以下方法:

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

1
这是这里最好的答案。 我要补充的是,必须初始化以下BasicStroke:graphics.setStroke(new BasicStroke(contour-width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));否则(没有圆形端点和连接),我们可能会在锐角处产生不必要的伪影。 - sergpank
这个答案对于东欧字符不起作用。有人知道如何修改这个程序,使其也能处理东欧字符吗? - Hinko Pih Pih

9

请参考此答案,其中我在文本形状周围(超出规范)应用了边框。 - Andrew Thompson
请原谅我的编辑。如果您认为它不适合您的答案,请回滚。但是一张图片胜过千言万语,..或者在这种情况下是“猫”。;) - Andrew Thompson
"对于AWT组件,请使用..." 时光机。为什么要提到它们呢? - Andrew Thompson
1
@Andrew Thompson 的一部分人使用 SWT-AWT 桥接器,另一部分人使用 OpenGL,当然,没有人知道为什么在最重要的部分中使用 AWT :-) - mKorbel

2
不确定您现在是如何绘制文本的,但您可以使用BufferedImage作为覆盖层来绘制任何内容。
  1. 使用要绘制的字符串和字体的尺寸创建BufferedImage(查看FontMetrics类)。
  2. 用透明度填充BufferedImage。
  3. 用任何颜色将字符串绘制到BufferedImage。
  4. 迭代遍历BufferedImage中的每个像素,并查看它距离文本颜色的像素有多远。如果在一定距离内,则将该像素变为黑色,并且如果它距离文本颜色更远,则可能会更透明。当然,如果像素已经与文本颜色相同,则忽略它。
  5. 将BufferedImage绘制到您正在绘制的任何内容上。

编辑

可能已经有库可以完成此操作,但如果我必须从头开始编写它,这就是我尝试完成它的方式。


不错的想法,绝对值得一试。谢谢。 - Konrad Garus

1

这里有一个简单的示例,虽然不像其他示例那样复杂,但更易于理解,并且表现得像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);
    }
}

0

一些最愚蠢的解决方法: -将相同的单词打两遍,但其中一个是黑色的,另一个是白色的,将白色的放在黑色的上面,你可能会得到类似的效果。 -找到一个看起来像上面例子的字体,并使用它。


一般情况下很难实现,而且我需要它看起来漂亮。我认为这不是微软或谷歌在他们的地图中所做的。 - Konrad Garus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接