Search code examples
javaawtjava-2d

Why is this stroke so fragmented and why does it only stroke the inside?


I'm drawing some text over an image using LineBreakMeasurer in conjunction with TextLayout but for some reason the stroke is only stroking the inside, and it's not very clean. Here's an example of what I'm talking about: https://i.sstatic.net/hnCQU.png

And when I don't draw the letter over top and increase the stroke width, it actually will get thicker on the inside and not outside.

Here's my code:

        float y = 0.0f;
        float wrappingWidth = img.getWidth() * 0.8f;
        LineBreakMeasurer measurer = new LineBreakMeasurer(str.getIterator(), imageGraphics.getFontRenderContext());
        while (measurer.getPosition() < sentence.length()) {
            TextLayout layout = measurer.nextLayout(wrappingWidth);
            y += layout.getAscent();
            float x =  ((wrappingWidth * 0.8f) - layout.getVisibleAdvance()) / 2f + (wrappingWidth * 0.2f);

            AffineTransform transform = new AffineTransform();
            transform.translate((double)x, (double)y);
            Shape outline = layout.getOutline(transform);

            imageGraphics.setColor(Color.black);
            imageGraphics.setClip(outline);
            imageGraphics.setStroke(new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
            imageGraphics.draw(outline);
            imageGraphics.setColor(Color.white);
            imageGraphics.setStroke(new BasicStroke());

            layout.draw(imageGraphics, x, y);
            y += layout.getDescent() + layout.getLeading();
        }

I'm not sure what I'm doing wrong. Does anyone know?


Solution

  • Create another copy of the Graphics context before you draw the outline...

    Graphics2D sg = (Graphics2D)imageGraphics.create();
    sg.setColor(Color.black);
    sg.setStroke(new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
    sg.draw(outline);
    sg.dispose();
    

    I'd also get rid of the clip...

    Instead of "stroking" the resulting shape, I'd be tempted to "fill" the background color and "draw" the outline color ontop of it, for example...

    Outline

    Graphics2D sg = (Graphics2D) g2d.create();
    AffineTransform transform = new AffineTransform();
    transform.translate((double) drawPosX, (double) drawPosY);
    Shape outline = layout.getOutline(transform);
    
    sg.setColor(Color.WHITE);
    sg.fill(outline);
    sg.setColor(Color.BLACK);
    sg.draw(outline);
    sg.dispose();
    

    But if you want a "nice" thick stroke, use BasicStroke.JOIN_ROUND instead of BasicStroke.JOIN_MITER

    Stroked

    Graphics2D sg = (Graphics2D) g2d.create();
    AffineTransform transform = new AffineTransform();
    transform.translate((double) drawPosX, (double) drawPosY);
    Shape outline = layout.getOutline(transform);
    
    sg.setStroke(new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    sg.setColor(Color.BLACK);
    sg.draw(outline);
    sg.dispose();