Draw images properly with scaling on Windows

My question is basically a repetition of:

I’ve been debugging for a few days and reading the forums, since our plugins look extremely bad on Windows with dpi scaling (compared to looking great on retina).

Eventually I discovered that the image scaling on Windows is very poor quality, so I decided to use the AVIR algo some people have talked about, and build my own image cache that caches images rendered at different sizes. Eventually I’m hitting the problem with the “renderImage” method in “juce_RenderingHelpers.h” - even when I’m trying to draw an image that has been rescaled to the exact size needed, it undergoes another transformation due to it’s non-integer position and again it looks awful unless I use Graphics::lowResamplingQuality as suggested by roeland-2

Now for the time being I will resort to using Graphics::lowResamplingQuality workaround, but I don’t feel very comfortable with it, as it feels hacky and works around undocumented behaviour of an internal helper function. What is the proper way to draw a high quality bitmap UI in JUCE in 2023 on hidpi Windows display?

P.s. I’m using JUCE 7.0.4, but the issue seems to be exactly the same for a very long time

1 Like

I’m having this issue as well.
If this were transformed into a feature request, that would instantly gain my vote (and 10 more, if multiple votes were allowed :laughing:)

I was not aware of a workaround involving lowResamplingQuality, can you provide a link?

my process right now is more or less based on having a free-standing function to replace Graphics::drawImage, which looks like:

void paintRescaledImage(juce::Graphics& g, juce::Rectangle<int> src, juce::Rectangle<int> dest, juce::Image originalImgToDraw)
{
    const auto pxFactor = g.getInternalContext().getPhysicalPixelScaleFactor();
    const int width = juce::roundToInt(pxFactor*dest.getWidth());
    const int height = juce::roundToInt(pxFactor*dest.getHeight());
    auto cutImage = originalImgToDraw.getClippedImage(src);
    auto finalImgToDraw = applyResize(cutImage, width, height);
    juce::Graphics::ScopedSaveState ph(g);
    g.setImageResamplingQuality(juce::Graphics::lowResamplingQuality);
    g.drawImageTransformed(newImg, juce::AffineTransform::scale(1.f/pxFactor).translated(dest.getX(), dest.getY()));
}

The applyResize method you can get from another JUCE user’s github:
applyResize

this function uses some very good rescaling algos, the source for which is here:
https://github.com/avaneev/avir

It is a good idea to also implement your own image cache to cache the resized images, as this is a somewhat heavy operation, and if you need to repeatedly draw the background if e.g. a peakmeter, it will be very cpu heavy.

The overall idea of the code is that you figure out what exact size of image the renderer needs to draw (using getPhysicalPixelScaleFactor) and rescale your image to this size in advance, using a high-quality algo, instead of whatever is used internally and completely smudges the hell out of the image. The lowResamplingQuality workaround I’m refering to is due to the fact that even if you provide the exact right size of the image, if this image needs to be drawn at a fractional pixel coordinate, there’s still some rescaling happening internally which destroys all your efforts. This rescaling is bypassed when using lowResamplingQuality as evident in renderImage method, In juce_RenderingHelpers.h.

Oh ok, so this works for directly drawing images to Graphics, but it isn’t easily applicable directly to Components that have a scaling transform applied to them.

Sure this is useful but I have to mull over it a little to see if I can apply that to my case.

For example, if a Component is setBufferedToImage(true), do I understand correctly that it would still be scaled by the JUCE algorithm, because its painting cannot be easily piped through this free function?