Is there any way to define multiple image scales for a single image in Juce? Would it be a good idea to make a new multi resolution image group type class for this purpose?
Being able to group multiple resolutions of the same image together in some way and then using this group like a regular image is useful to abstract target display properties away from the logic of drawing images.
Here is a little helper class to get the ball rolling, it's not very efficiently coded but at least works for a non affine transformed component. I'm using this to blit the background image to my scalable user interface
struct ImageWithScale
{
ImageWithScale ()
: image ()
, scale (1.0f)
{
}
ImageWithScale (Image im, float sc)
: image (im)
, scale (sc)
{
}
ImageWithScale (const ImageWithScale& other)
: image (other.image)
, scale (other.scale)
{
}
ImageWithScale& operator= (const ImageWithScale& other)
{
if (this != &other)
{
image = other.image;
scale = other.scale;
}
return *this;
}
Image image;
float scale;
};
struct ImageWithScale
{
ImageWithScale ()
: image ()
, scale (1.0f)
{
}
ImageWithScale (Image im, float sc)
: image (im)
, scale (sc)
{
}
ImageWithScale (const ImageWithScale& other)
: image (other.image)
, scale (other.scale)
{
}
ImageWithScale& operator= (const ImageWithScale& other)
{
if (this != &other)
{
image = other.image;
scale = other.scale;
}
return *this;
}
Image image;
float scale;
};
class ImageMultiRes
{
public:
ImageMultiRes ()
: width (0.0f)
, height (0.0f)
{};
ImageMultiRes (float w, float h)
: width (w)
, height (h)
{};
void setSize (float w, float h)
{
jassert (images.size () == 0);
width = w;
height = h;
}
void addImage (Image im)
{
jassert (width > 0.0f);
jassert (height > 0.0f);
float scalex = im.getWidth () / width;
float scaley = im.getHeight () / height;
// all the images need to have the same aspect ratio
jassert (fabsf (scalex - scaley) < 1e-6f);
images.add (ImageWithScale (im, scalex));
}
void drawImage (Graphics& g)
{
drawImageScaled (g, 1.0f);
}
void drawImageScaled (Graphics& g, float scale)
{
#if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
juce::Point<int> p = getScreenPosition ();
float screenscale = Desktop::getInstance().getDisplays().getDisplayContaining (p).scale;
#else
float screenscale = g.getInternalContext().getTargetScale ();
#endif
float whichscale = scale*screenscale;
ImageWithScale* whichimage;
float closest = 1e10f;
for (int i=0; i<images.size (); i++)
{
ImageWithScale& im = images.getReference (i);
float distance = whichscale/im.scale;
if (distance < closest)
{
closest = distance;
whichimage = &im;
if (closest == 1.0f)
{
// prefer a direct 1:1 blit
break;
}
}
}
float targetscale = scale / whichimage->scale;
g.drawImageTransformed (whichimage->image, AffineTransform::scale (targetscale));
}
float width, height;
Array <ImageWithScale> images;
};
I'm targetting OS 10.5, so I had to hack in this to the low level graphics context to be able to detect the screen scale:
float CoreGraphicsContext::getTargetScale ()
{
const CGAffineTransform t = CGContextGetUserSpaceToDeviceSpaceTransform (context);
return (float) (juce_hypot (t.a, t.c) + juce_hypot (t.b, t.d)) / 2.0f;
}
You can use it like this:
ImageMultiRes multires (width, height); multires.addImage (im0); multires.addImage (im1); multires.addImage (im2);
The in your paint:
multires.drawImageScaled (graphics, scale);
