DragAndDropContainer::setCurrentDragImage and Retina displays


#1

Is there any way to specify a scale factor for DragAndDropContainer::setCurrentDragImage ?
If not, can you add a new parameter?
setCurrentDragImage (Image img, int scaleFactor = 1.0) ?

Thanks!


#2

Why not just give it a resized copy of the image?


#3

Hi Jules!

yes, is what I’m doing, but the dragged image is not “high res”, I get a blurry version instead.

dragImage = createComponentSnapshot (getLocalBounds(), true, scale).convertedToFormat (Image::ARGB);
dragImage.multiplyAllAlphas (0.8f);
drag->setCurrentDragImage (dragImage. rescaled(getWidth()/2, getHeight()/2));

the same:

dragImage = createComponentSnapshot (getLocalBounds()).convertedToFormat (Image::ARGB);
dragImage.multiplyAllAlphas (0.8f);
drag->setCurrentDragImage (dragImage);

What I want:

dragImage = createComponentSnapshot (getLocalBounds(), true, scale).convertedToFormat (Image::ARGB);
dragImage.multiplyAllAlphas (0.8f);
drag->setCurrentDragImage (dragImage, 1/scale);

Is not something that worries me so much, but it would be better to be able to see the dragged image “hi res” as the whole gui.

Cheers!


#4

Huh? How do you imagine that the dragger could magically draw a higher-resolution image than the one you give it?


#5

Jules, I don’t imagine that, my english is not good, I think i have explained myself wrong.
the result of the two first pieces of code I’d posted before (whitout multiplyAllAlphas):

Component:

Dragged image of the component

the dragged image is blurry, I want to keep the same “hi res” for the dragging image, my original question is if there is any way to do that.
Thanks


#6

If you want a higher resolution image, render it at a higher resolution!

If setCurrentDragImage had a scale factor, all that it could do would be to call rescaled on the image you gave it, which is the same thing you’re doing. It’d be impossible for it to somehow make the image higher-resolution than the one you pass in.


#7

It’s what I’ve done at the first try (scale = 2.0):

dragImage = createComponentSnapshot (getLocalBounds(), true, scale).convertedToFormat (Image::ARGB);

But when I set the image drag->setCurrentDragImage (dragImage) I get a double size draggingComponent (or image) than the original component.

The paint method in DragAndDropContainer::DragImageComponent draws the image with g.drawImageAt (image, 0, 0);, but to get a “”“retina super resolution”"" I need to pass to setCurrentDragImage a double size image createComponentSnapshot (getLocalBounds(), true, 2.0) and then draws it with:

    scaleFactor = 1/scale;
    g.drawImage (image, 0, 0, image.getWidth() * scaleFactor,
                 image.getHeight() * scaleFactor,
                 0, 0, image.getWidth(), image.getHeight());

Ok, I modified the DragImageComponent::paint method and hardcoded the scalingFactor, And it works fine.
Maybe there is another simple way to make it retina compatible but I don’t know
Without setting the drag image (passing nullptr image to DragAndDropContainer::startDragging) it does not work either, I get a blurry dragging image.


#8

What @xeneize is saying, and I bet you already understood it yourself, is that the component snapshot that is drawn drag image is always rendered with one pixel of the image mapped to one logical point of the screen (which corresponds to TWO physical pixels on retina displays).

This gives the expected result in ordinary displays that have 1 physical pixel per logical point, but is problematic on retina displays where there are two physical pixels per every single logical point.

On retina displays, then, one of the following two problems arise:

  • If the Component snapshot is taken at retina resolution (scale = 2), then the resulting image dimension in physical pixels is double the dimension of the Component bounds.
    Now, because the drag image is always drawn with one image pixel per logcial point (stated above), this results in an image that occupies twice the area of the Component on the screen

  • If the Component snapshot is taken at non-retina resolution (scale = 1), then the resulting image dimension in pixels will equal the Component bounds, but since each pixel of the image is drawn to one logical point, and one such point occupies two physical pixels of the screen, the net result is that each pixel of the image is drawn on two pixels of the screen, which gives the blurry, non-retina look to the result, albeit at least resulting in an image that occupies on the screen the same size of the Component it represents.

I think that adding a scale member to Image should be considered. One such member would effectively convey the information of how many pixels of the image should be mapped to each logical point, which is an information that inherently belongs to the Image itself and would instruct all paint routines on how to deal with the image.

If you are dubious about whether that is a useful approach, let it be know that I have wrapped my own RichImage class around the juce::Image class exactly to add that feature, and have used it since years now without a regret about its design.


#9

Yup.

TBH the best plan would probably be for this to use a Component instead of an image, so that there’s no need to faff around with scale factors. We’ll take a look and see if there’s something nice we can do.


#10

@yfede great explanation, thanks :slight_smile:
@Jules I think it would be a good solution, So that you can handle/draw it as you want.

Thanks!


#11

DragAndDropContainer still generates a blurry image by default.
Is there any workaround to get a crispy image at the right size on retina?

would be lovely!


#12

Any chance you could take a look at this sometime ?
I would not insist or care that much if there was a workaround, but currently there is no other alternative (as far as I can see) than modifying the juce code… :scream:

so at the moment we got to modify DragImageComponent::paint() locally

void paint (Graphics& g) override
{
    if (isOpaque())
        g.fillAll (Colours::white);

    g.setOpacity (1.0f);

    auto scale = Desktop::getInstance().getDisplays().findDisplayForRect (getScreenBounds()).scale;
    g.drawImageTransformed (image, AffineTransform::scale (1.0f / (float) scale));
}