Rectangles and lineThickness

Hi lalala,

Let me summarise your findings in a screenshot (including retina and non-retina displays):

Let me first try to explain the difference between roundedRect and drawRect. roundedRect uses a path internally to draw the rounded rect. You can get the same result with the following code:

Path p;
p.addRectangle (area.getX(), area.getY(), area.getWidth(), area.getHeight());
g.strokePath (p, PathStrokeType(1.f));

If you think about it, the expected result of drawing a path with line thickness 1 centred at whole-numbered pixel position (labeled “No offset” in the image above) should result in a fuzzy rectangle, as the path itself is centred on a pixel boundary, and therefore, 50% of the stroke will be in one pixel and the remaining in the adjacent pixel. This is also what vector graphics programs will do.

Now, if JUCE would implement the drawRect function with paths then this would confuse the majority of users. As drawing a simple integral rectangle with drawRect would always result in “fuzzy” rectangles. Therefore the drawRect method will in fact inset the rectangle by 0.5 pixels. Therefore the fuzziness of path vs drawRect switches around if you draw both with an offset of 0.5 pixels. Does this make sense?

Now on to why the LookAndFeel method gives you a thin rectangle. This is because the drawing is clipped to the bounds of the ToggleButton. In fact, without the clipping, 50% of the stroke would be outside the ToggleButton bounds and you would get the same result as your paint method. To achieve the same thin rectangle as the LookAndFeel case you could do the following:

void paint (Graphics& g)
{
    g.fillAll (Colour (0xffeeeeee));
    g.setColour (Colours::black);

    const juce::Rectangle<float> area (5.f, 80.f, 90.0f, 20.f);
    
    g.saveState();
    g.reduceClipRegion (area.toNearestInt());
        
    Path p;
    p.addRectangle (area.getX(), area.getY(), area.getWidth(), area.getHeight());
    g.drawRoundedRectangle (area, 0.f, 1.f);
    g.strokePath (p, PathStrokeType(1.f));
        
    g.restoreState();
}
6 Likes