Font rendering inconsistencies with Component::setBufferedToImage() (iOS)

Hi,
I’m trying to use setBufferedToImage() to improve UI responsiveness on iOS (it’s painfully sluggish otherwise). It does seem to help a lot in that respect, BUT there is a problem with text rendering.

Here’s an example:
ios-buffered-rendering
The leftmost image is not using setBufferedToImage() at all and the text looks fine.
The center image has setBufferedToImage() applied at the editor level - antialising is OK, but the letters are much heavier for some reason.
The rightmost image has setBufferedToImage() called on the slider itself - text weight is OK, but it really lacks some antialiasing.

Has anyone run into this? Is there any way to fix or mitigate it?

Hmm… JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING=1 seems to do the trick. Not sure why it doesn’t have any effect in the first case though.

When setting setBufferedToImage(true) the component uses StandardCachedComponentImage struct to create and paint the image cache.

struct StandardCachedComponentImage : public CachedComponentImage
{
StandardCachedComponentImage (Component& c) noexcept : owner ( c ), scale (1.0f ) {}

void paint (Graphics& g) override
{
scale =g.getInternalContext().getPhysicalPixelScaleFactor();
auto compBounds = owner.getLocalBounds();
auto imageBounds = compBounds * scale;

what worked for me is to create a custom CachedComponentImage
and create the image scaled x2.

struct MYCachedComponentImage : public CachedComponentImage { MYCachedComponentImage (Component& c, float imageScale) noexcept : owner( c ), scale(imageScale) {}

void paint(Graphics& g) override {
// scale = g.getInternalContext().getPhysicalPixelScaleFactor(); //delete this line
auto compBounds = owner.getLocalBounds();
auto imageBounds = compBounds * scale;

and then in your component constructor

setBufferedToImage(true);
setCachedComponentImage(new MYCachedComponentImage(*this, 2.0f));

@alexrdsp, thanks for stopping by! I don’t know about your context, but in my case g.getInternalContext().getPhysicalPixelScaleFactor() already returns 2.0f, so there would be no difference between the two.

As I mentioned, for me it was JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING that did the trick of getting rid of both extra boldness and jaggedness.

I also implemented the repaint caching thing from this thread:

which improved the responsiveness further. (There might be side-effects though, but I’ve yet to see them.)

The difference here is that on iOS font smoothing is disabled by default and isn’t a configurable user setting like on macOS. When we create the standard JUCE CoreGraphicsContext for the UIView, the value of the JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING therefore has no effect and you get text rendering like the leftmost image in the OP. However, for some reason, font smoothing can be enabled for bitmap CGContexts which are used when rendering to an image so the text rendering looks “heavier” like in the middle image when using setBufferedToImage(). I think the right thing to do here is only use the JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING flag on macOS, and simply ignore it on iOS so the CG fonts are never smoothed and the rendering is consistent between normal painting and cached component images (and with the iOS default itself). This has been added to the develop branch:

2 Likes

Got it, thanks for the clarification and the update!