Downsampled paint possible?

Merry Christmas, everyone :slight_smile:

I want to work with pixelart graphics when designing and implementing my GUIs, because:

  • lower resolution images means more aesthetics for less CPU.
  • better workflow from a designer perspective than with svg and stuff.
  • it’s just an exciting art style and gives a nice contrast to the current neumorphism trend imo.

Considering that I only use 1 component the solution is easy. Considering it is animated for each frame I draw the states of all my images onto another image that has no bigger size than any of the other images. Then i set juce::Graphics’ resampling quality to low and draw the upscaled result image.
It seems like a very naiive solution though considering that JUCE usually has this intelligent system, where it only repaints the components that need repainting. I would like to use that on my downsampled images, so that the upsampled image has no work left to do.

image

On the left this image visualizes a component with another component in it, and on the right there is the resulting upsampled window that should be displayed to the user. Considering that every child-component was a custom component they could all have an image to draw the downsampled stuff on before upsampling it. That would already solve my issue so far that it would only repaint the components that need repainting. But it would still mean that all components have their own upsampling stage, which is not very elegant. So another solution would be to refrain from implementing the paint-method of all the sub-components and rather just gather all their images in the main component when it repaints. then we’d only have 1 upsampling stage, but the repainting-only-when-needed-thing wouldn’t work anymore, so it’s a bad solution as well.

I feel like there should be a rather obvious and not too hacky solution for this (one where I don’t have to rewrite half of JUCE) and I just don’t see it yet. Anyone ideas?

You could draw to an image and then use Graphics::setImageResamplingQuality() to draw the image rescaled without any antialiasing.

For example:

juce::Image img(juce::Image::ARGB, 100, 100, true);
// ...
// Draw to image...
// ...

// Use low resampling:
g.setImageResamplingQuality(juce::Graphics::lowResamplingQuality);

// Draw image at 2x scale:
g.drawImage(img, juce::Rectangle<float>(0.f, 0.f, 200.f, 200.f));

thank you for trying to help me. i’m already so far though. ofc it’s not hard to up- or downsample a bunch of images. my concern is more that when I use a lot of child-components in a project (like you’d usually do with sliders and stuff) every one of them would upsample their image individually to paint it, and I feel like this could be improved somehow, but I couldn’t come up with a sensible logic for that yet as described in my post.

class Component :
        public juce::Component
    {
    public:
        const int X, Y, WIDTH, HEIGHT, UPSCALE;
        juce::Rectangle<float> upscaledBounds;

        Component(const int x, const int y, const int width, const int height, const int upscale) :
            X(x), Y(y), WIDTH(width), HEIGHT(height), UPSCALE(upscale),
            upscaledBounds(up(X), up(Y), up(WIDTH), up(HEIGHT)),
            canvas(juce::Image::ARGB, WIDTH, HEIGHT, true)
        {
            setBounds(upscaledBounds.toNearestInt());
            upscaledBounds.setX(0); upscaledBounds.setY(0);
        }
        virtual void paintCanvas(juce::Graphics& g) = 0;
    protected:
        juce::Image canvas;
        Image importImage(const void* imageData, int size) { return Image(imageData, size); }
        int up(const int value) const { return value * UPSCALE; }
        int down(const int value) const { return value / UPSCALE; }
    private:
        void paint(juce::Graphics& g) override {
            juce::Graphics g2{ canvas };
            g2.setImageResamplingQuality(juce::Graphics::lowResamplingQuality);
            paintCanvas(g2);
            g.setImageResamplingQuality(juce::Graphics::lowResamplingQuality);
            g.drawImage(canvas, upscaledBounds, juce::RectanglePlacement::fillDestination, false);
        }
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Component)
    };

this is what i got so far. a pxl::Component that overrides paint privately, so that when you want to make a component with this as base class you have to override paintCanvas, which lets you paint on the small image before upsampling. i think that’s a quite good solution because it utilizes juce’ intelligent system, which only repaints things where they are needed. but it still means that all components have their own upsampling stage in paint. ideally i would like to collect all the new image information in a downsampled fashion and only upsample the result at the end