Understanding image blit in juce_RenderingHelpers

Hi there, I’m trying to understand some code in juce_RenderingHelpers.h. It seems to have been there since time immemorial (well, 2012) with this cryptic comment:

// If our translation doesn't involve any distortion, just use a simple blit..
auto tx = (int) (t.getTranslationX() * 256.0f);
auto ty = (int) (t.getTranslationY() * 256.0f);

if (interpolationQuality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0)
    tx = ((tx + 128) >> 8);
    ty = ((ty + 128) >> 8);

Passing this check causes image rendering to go down a faster ‘simple blit’ route. I need fast image painting because I’m doing lots of animated sprites. Consequently, I have decided to resize my images before painting, accounting for the desktop scaling factor etc. so I can access this route.

Unfortunately my images still fail the ((tx | ty) & 224) == 0 check. I tried setting the interpolationQuality to ‘low’; then the check succeeds, and my images paint faster. But unfortunately the images have artifacts, especially around hard edges. I’m guessing this is what the check is for in non-low quality mode.

Does anyone know what this cryptic ((tx | ty) & 224) == 0 means, or what kind of ‘distortion’ is being referred to? Is there any way I can prepare my images so that I can go down this pathway without setting the resampling quality to low?

Not really. It looks like it’s trying to check that the fractional parts of t.getTranslationX and t.getTranslationY are both smaller than 0.125. I’m not sure what’s significant about this number. Perhaps it’s just “close enough” to being directly on a pixel boundary that resampling is not necessary. I think to ensure you take this path, you’d need to ensure that any translation applied to the image is clamped to whole-pixel boundaries. If you’re already doing that, then I’m not sure what to suggest!

I tried running the ‘GraphicsDemo’ with the software render enabled, with the ‘RGB Image’ option enabled and rotations turned off. It looks like the ‘fast path’ is taken when the translation is close to a whole-pixel value in each direction, so I’m pretty confident that’s the answer.

Ah, I see. That makes sense. A component above my ImageComponent has an AffineTransform that scales it down by e.g. 0.5. This is to allow ‘resizing’ the window in a simple fashion. I am taking this scaling into account when resampling my image, so that when the image is painted the AffineTransform passed into the StackBasedLowLevelGraphicsContext should cancel out the scaling of the parent component. But I would guess that this doesn’t account for the translation, which from the point of view of the graphics context lands on a sub-pixel, even though this will get rescaled again at the top level onto an integer pixel boundary.

I’ll have to think more about this. It’s ultimately the consequence of a bad decision (scale the window using an AffineTransform, instead of doing ‘proper’ responsive resizing). So maybe we’ll have to think hard about whether to go forward with that.

Many thanks for your investigation and reply!

hope this is not too offtopic. but ideas like this are the reason why i once tried to encourage the juce team to rewrite the paint-backend to enable features like rescaling the full graphics, rather than just individual images. i once had the idea to use a lot of images as well, very small onces, pixelart, so i thought it would help with performance and also look aesthetically pleasant. but it failed due to juce not having methods that make it beneficial to work with a lot of small images, because you have to rescale all of them individually. gladly lowResamplingQuality is just perfect for that if you rescale in whole integer multiples. but it was still not worth it. if we were able to process the whole image alongside each component’s image we could also make shaders and stuff easily. that’s why i’d like to take this opportunity to suggest it again

Update: I have found that resampling my images with a higher-quality algorithm eliminates any artifacts on the ‘fast path’ even when I have to use a low interpolationQuality to bypass the 224 check.

I’m using a modified version of this algorithm. It’s ~2x slower than JUCE’s default resampler, but that’s a one-time cost, so it’s worth it.