With the Direct2D renderer and a non-integer display scale (150%), an opaque component whose edge lands on an odd logical coordinate leaves a 1px garbage line along that edge. Odd x 1.5 puts the edge on a fractional device pixel (e.g. x=15 → 22.5).
Cause: The opaque-component culling in Component::paintComponentAndChildren splits paint responsibility at that edge in integer logical coordinates: the parent must not paint under the opaque child, the child covers the area itself. But Direct2DGraphicsContext::clipToRectangle (axis-aligned branch) clips to the exact fractional device rect and pushes it via PushAxisAlignedClip (…, D2D1_ANTIALIAS_MODE_ALIASED). The aliased clip cuts the boundary pixel out of the parent’s render, and the child’s fillAll (edge also at .5) never fully covers it either — so the pixel keeps whatever Clear() left in the swap chain buffer. The software renderer doesn’t have this problem because Rectangle<int>::transformedBy rounds clips outward; note that excludeClipRectangle in the same file already rounds (getLargestIntegerWithin), but the positive clip doesn’t.
Fix: expand the transformed clip to whole device pixels, matching the software renderer:
and the same in the axis-aligned branch of clipToRectangleList. Verified: the artifacts are gone, with no visible regressions (components can now overpaint up to 1 device px at fractional scales, same as the software renderer, paint order makes that harmless).
That light gray line above the footer shouldn’t be there at all. Nobody paints it. The footer only uses fillAll to create the dark gray background. The rest are child components. The light gray bar is whatever was in the swap buffer and it changes when I switch pages.
thanks for spotting this. i hacked around it with some weird awful stuff - i can’t remember what i did but i think i landed on adding an extra 1px transparent border around elements in my filmstrips, i assumed i was doing something wrong on my side. would be great to fix properly.
With the hack, synced to anything - it works. I’ll save the screenshot but it looks like the OpenGL one.
The specific hack is: I call img.duplicateIfShared() on each individual frame of the knob filmstrip, if direct2d. Otherwise the image is just a subimage of a filmstrip like this. Making those duplicates is pretty slow but prevents the fringing shown above. There aren’t any obvious off-by-ones, it works fine on macos, linux and opengl on windows. In direct2d if the img is a subimage of that, i get fringes from surrounding “frames”.
The original report was about the current clip-rectangle not being large enough to fill up the entire pixel, IF a scale factor was involved AND using g.fillAll () AND the component was opaque.
Your bug is about sub-images not being calculated precisely enough, so neighboring pixels of the same source-image are bleeding in.
You need to report this separately, ideally with either the offending code in JUCE and a small fix (like what I submitted) or a small project that reproduces it.
I had a similar problem as the OP above. One component exactly overlays another, and on Windows, it would expose a vertical line of pixels on the left side. All my internal computations were exact, but depending on scaling factor, sometimes that left edge of the “behind” image peeked out. I “fixed” it by making the overlay two pixels larger than the image behind it (one extra on the left and one on the right, just for symmetry). That prevented the rounding error from being visible.