From ceb556876a87e84d703096e47e6d35b82a21cb61 Mon Sep 17 00:00:00 2001 From: jules Date: Sun, 8 Jul 2012 20:07:53 +0100 Subject: [PATCH] Fixes to deal with effect rendering in retina displays, e.g. drop-shadows, etc. --- .../contexts/juce_LowLevelGraphicsContext.h | 2 + .../effects/juce_DropShadowEffect.cpp | 65 ++++++++++--------- .../effects/juce_DropShadowEffect.h | 5 +- .../juce_graphics/effects/juce_GlowEffect.cpp | 4 +- .../juce_graphics/effects/juce_GlowEffect.h | 2 +- .../effects/juce_ImageEffectFilter.h | 4 ++ .../native/juce_mac_CoreGraphicsContext.h | 4 +- .../native/juce_mac_CoreGraphicsContext.mm | 5 +- .../components/juce_Component.cpp | 10 ++- .../lookandfeel/juce_LookAndFeel.cpp | 35 ++++++---- .../lookandfeel/juce_LookAndFeel.h | 2 +- .../native/juce_ios_UIViewComponentPeer.mm | 2 +- .../native/juce_mac_NSViewComponentPeer.mm | 10 ++- .../windows/juce_CallOutBox.cpp | 10 +-- 14 files changed, 98 insertions(+), 62 deletions(-) diff --git a/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h b/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h index 3aace56382..e8f8a0a00a 100644 --- a/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h @@ -69,6 +69,8 @@ public: virtual void addTransform (const AffineTransform& transform) = 0; virtual float getScaleFactor() = 0; + virtual float getTargetDeviceScaleFactor() { return 1.0f; } + virtual bool clipToRectangle (const Rectangle& r) = 0; virtual bool clipToRectangleList (const RectangleList& clipRegion) = 0; virtual void excludeClipRectangle (const Rectangle& r) = 0; diff --git a/modules/juce_graphics/effects/juce_DropShadowEffect.cpp b/modules/juce_graphics/effects/juce_DropShadowEffect.cpp index 8a694036b8..db38b2c61b 100644 --- a/modules/juce_graphics/effects/juce_DropShadowEffect.cpp +++ b/modules/juce_graphics/effects/juce_DropShadowEffect.cpp @@ -51,52 +51,57 @@ void DropShadowEffect::setShadowProperties (const float newRadius, opacity = newOpacity; } -void DropShadowEffect::applyEffect (Image& image, Graphics& g, float alpha) +void DropShadowEffect::drawShadow (Graphics& g, const Image& srcImage, + float radius, float alpha, int offsetX, int offsetY) { - const int w = image.getWidth(); - const int h = image.getHeight(); + const int w = srcImage.getWidth(); + const int h = srcImage.getHeight(); Image shadowImage (Image::SingleChannel, w, h, false); - { - const Image::BitmapData srcData (image, Image::BitmapData::readOnly); - const Image::BitmapData destData (shadowImage, Image::BitmapData::readWrite); + const Image::BitmapData srcData (srcImage, Image::BitmapData::readOnly); + const Image::BitmapData destData (shadowImage, Image::BitmapData::readWrite); - const int filter = roundToInt (63.0f / radius); - const int radiusMinus1 = roundToInt ((radius - 1.0f) * 63.0f); + const int filter = roundToInt (63.0f / radius); + const int radiusMinus1 = roundToInt ((radius - 1.0f) * 63.0f); - for (int x = w; --x >= 0;) - { - int shadowAlpha = 0; + for (int x = w; --x >= 0;) + { + int shadowAlpha = 0; - const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; - uint8* shadowPix = destData.data + x; + const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; + uint8* shadowPix = destData.data + x; - for (int y = h; --y >= 0;) - { - shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; + for (int y = h; --y >= 0;) + { + shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; - *shadowPix = (uint8) shadowAlpha; - src = addBytesToPointer (src, srcData.lineStride); - shadowPix += destData.lineStride; - } + *shadowPix = (uint8) shadowAlpha; + src = addBytesToPointer (src, srcData.lineStride); + shadowPix += destData.lineStride; } + } - for (int y = h; --y >= 0;) + for (int y = h; --y >= 0;) + { + int shadowAlpha = 0; + uint8* shadowPix = destData.getLinePointer (y); + + for (int x = w; --x >= 0;) { - int shadowAlpha = 0; - uint8* shadowPix = destData.getLinePointer (y); - - for (int x = w; --x >= 0;) - { - shadowAlpha = ((shadowAlpha * radiusMinus1 + (*shadowPix << 6)) * filter) >> 12; - *shadowPix++ = (uint8) shadowAlpha; - } + shadowAlpha = ((shadowAlpha * radiusMinus1 + (*shadowPix << 6)) * filter) >> 12; + *shadowPix++ = (uint8) shadowAlpha; } } - g.setColour (Colours::black.withAlpha (opacity * alpha)); + g.setColour (Colours::black.withAlpha (alpha)); g.drawImageAt (shadowImage, offsetX, offsetY, true); +} + +void DropShadowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) +{ + drawShadow (g, image, radius * scaleFactor, opacity * alpha, + (int) (offsetX * scaleFactor), (int) (offsetY * scaleFactor)); g.setOpacity (alpha); g.drawImageAt (image, 0, 0); diff --git a/modules/juce_graphics/effects/juce_DropShadowEffect.h b/modules/juce_graphics/effects/juce_DropShadowEffect.h index 2fdc7068ae..42c4086183 100644 --- a/modules/juce_graphics/effects/juce_DropShadowEffect.h +++ b/modules/juce_graphics/effects/juce_DropShadowEffect.h @@ -75,9 +75,12 @@ public: int newShadowOffsetY); + static void drawShadow (Graphics& g, const Image& srcImage, + float radius, float alpha, int offsetX, int offsetY); + //============================================================================== /** @internal */ - void applyEffect (Image& sourceImage, Graphics& destContext, float alpha); + void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha); private: diff --git a/modules/juce_graphics/effects/juce_GlowEffect.cpp b/modules/juce_graphics/effects/juce_GlowEffect.cpp index ef238897e6..e8d6478419 100644 --- a/modules/juce_graphics/effects/juce_GlowEffect.cpp +++ b/modules/juce_graphics/effects/juce_GlowEffect.cpp @@ -40,11 +40,11 @@ void GlowEffect::setGlowProperties (const float newRadius, colour = newColour; } -void GlowEffect::applyEffect (Image& image, Graphics& g, float alpha) +void GlowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) { Image temp (image.getFormat(), image.getWidth(), image.getHeight(), true); - ImageConvolutionKernel blurKernel (roundToInt (radius * 2.0f)); + ImageConvolutionKernel blurKernel (roundToInt (radius * scaleFactor * 2.0f)); blurKernel.createGaussianBlur (radius); blurKernel.rescaleAllValues (radius); diff --git a/modules/juce_graphics/effects/juce_GlowEffect.h b/modules/juce_graphics/effects/juce_GlowEffect.h index e843c053d9..f6c9299481 100644 --- a/modules/juce_graphics/effects/juce_GlowEffect.h +++ b/modules/juce_graphics/effects/juce_GlowEffect.h @@ -63,7 +63,7 @@ public: //============================================================================== /** @internal */ - void applyEffect (Image& sourceImage, Graphics& destContext, float alpha); + void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha); private: //============================================================================== diff --git a/modules/juce_graphics/effects/juce_ImageEffectFilter.h b/modules/juce_graphics/effects/juce_ImageEffectFilter.h index 66b8994a11..d34281ee00 100644 --- a/modules/juce_graphics/effects/juce_ImageEffectFilter.h +++ b/modules/juce_graphics/effects/juce_ImageEffectFilter.h @@ -53,11 +53,15 @@ public: its paint() method. The image may or may not have an alpha channel, depending on whether the component is opaque. @param destContext the graphics context to use to draw the resultant image. + @param scaleFactor a scale factor that has been applied to the image - e.g. if + this is 2, then the image is actually scaled-up to twice the + original resolution @param alpha the alpha with which to draw the resultant image to the target context */ virtual void applyEffect (Image& sourceImage, Graphics& destContext, + float scaleFactor, float alpha) = 0; /** Destructor. */ diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h index f701ea8880..a82c2061be 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h @@ -30,7 +30,7 @@ class CoreGraphicsContext : public LowLevelGraphicsContext { public: - CoreGraphicsContext (CGContextRef context_, const float flipHeight_); + CoreGraphicsContext (CGContextRef context_, const float flipHeight_, const float targetScale_); ~CoreGraphicsContext(); //============================================================================== @@ -39,6 +39,7 @@ public: void setOrigin (int x, int y); void addTransform (const AffineTransform& transform); float getScaleFactor(); + float getTargetDeviceScaleFactor() { return targetScale; } bool clipToRectangle (const Rectangle& r); bool clipToRectangleList (const RectangleList& clipRegion); void excludeClipRectangle (const Rectangle& r); @@ -77,6 +78,7 @@ public: private: CGContextRef context; const CGFloat flipHeight; + float targetScale; CGColorSpaceRef rgbColourSpace, greyColourSpace; CGFunctionCallbacks gradientCallbacks; mutable Rectangle lastClipRect; diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index 454d13c176..446cfd9329 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -53,7 +53,7 @@ public: LowLevelGraphicsContext* createLowLevelContext() { - return new CoreGraphicsContext (context, height); + return new CoreGraphicsContext (context, height, 1.0f); } void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) @@ -124,9 +124,10 @@ ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, i } //============================================================================== -CoreGraphicsContext::CoreGraphicsContext (CGContextRef context_, const float flipHeight_) +CoreGraphicsContext::CoreGraphicsContext (CGContextRef context_, const float flipHeight_, const float targetScale_) : context (context_), flipHeight (flipHeight_), + targetScale (targetScale_), lastClipRectIsValid (false), state (new SavedState()) { diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp index ed07980759..7e233a505e 100644 --- a/modules/juce_gui_basics/components/juce_Component.cpp +++ b/modules/juce_gui_basics/components/juce_Component.cpp @@ -1946,14 +1946,20 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) if (effect != nullptr) { + const float scale = g.getInternalContext()->getTargetDeviceScaleFactor(); + Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, - getWidth(), getHeight(), ! flags.opaqueFlag); + (int) (scale * getWidth()), (int) (scale * getHeight()), ! flags.opaqueFlag); { Graphics g2 (effectImage); + g2.addTransform (AffineTransform::scale (scale, scale)); paintComponentAndChildren (g2); } - effect->applyEffect (effectImage, g, ignoreAlphaLevel ? 1.0f : getAlpha()); + g.saveState(); + g.addTransform (AffineTransform::scale (1.0f / scale, 1.0f / scale)); + effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha()); + g.restoreState(); } else if (componentTransparency > 0 && ! ignoreAlphaLevel) { diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp index 461fa86f44..1c0b27de59 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp @@ -2533,23 +2533,36 @@ const Rectangle LookAndFeel::getPropertyComponentContentPosition (PropertyC } //============================================================================== -void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path) +void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, + const Path& path, Image& cachedImage) { - Image content (Image::ARGB, box.getWidth(), box.getHeight(), true); - + if (cachedImage.isNull()) { - Graphics g2 (content); + const int w = box.getWidth(); + const int h = box.getHeight(); + + Image renderedPath (Image::ARGB, w, h, true); - g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); - g2.fillPath (path); + { + Graphics g2 (renderedPath); + + g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); + g2.fillPath (path); + } - g2.setColour (Colours::white.withAlpha (0.8f)); - g2.strokePath (path, PathStrokeType (2.0f)); + cachedImage = Image (Image::ARGB, w, h, true); + Graphics g2 (cachedImage); + DropShadowEffect::drawShadow (g2, renderedPath, 5.0f, 0.4f, 0, 2); } - DropShadowEffect shadow; - shadow.setShadowProperties (5.0f, 0.4f, 0, 2); - shadow.applyEffect (content, g, 1.0f); + g.setColour (Colours::black); + g.drawImageAt (cachedImage, 0, 0); + + g.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); + g.fillPath (path); + + g.setColour (Colours::white.withAlpha (0.8f)); + g.strokePath (path, PathStrokeType (2.0f)); } diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h index d7367d3442..e95e038459 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h @@ -611,7 +611,7 @@ public: virtual const Rectangle getPropertyComponentContentPosition (PropertyComponent& component); //============================================================================== - virtual void drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path); + virtual void drawCallOutBoxBackground (CallOutBox& box, Graphics& g, const Path& path, Image& cachedImage); //============================================================================== virtual void drawLevelMeter (Graphics& g, int width, int height, float level); diff --git a/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index 8cc89d7dc1..e40d3f940a 100644 --- a/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -901,7 +901,7 @@ void UIViewComponentPeer::drawRect (CGRect r) CGContextClearRect (cg, CGContextGetClipBoundingBox (cg)); CGContextConcatCTM (cg, CGAffineTransformMake (1, 0, 0, -1, 0, view.bounds.size.height)); - CoreGraphicsContext g (cg, view.bounds.size.height); + CoreGraphicsContext g (cg, view.bounds.size.height, [UIScreen mainScreen].scale); insideDrawRect = true; handlePaint (g); diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index c3de800c3b..2f89bb942a 100644 --- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -681,7 +681,15 @@ public: #if USE_COREGRAPHICS_RENDERING if (usingCoreGraphics) { - CoreGraphicsContext context (cg, (float) [view frame].size.height); + float displayScale = 1.0f; + + #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) + NSScreen* screen = [[view window] screen]; + if ([screen respondsToSelector: @selector (backingScaleFactor)]) + displayScale = screen.backingScaleFactor; + #endif + + CoreGraphicsContext context (cg, (float) [view frame].size.height, displayScale); insideDrawRect = true; handlePaint (context); diff --git a/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index 7447698bd8..ee941f2984 100644 --- a/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -65,15 +65,7 @@ void CallOutBox::setArrowSize (const float newSize) void CallOutBox::paint (Graphics& g) { - if (background.isNull()) - { - background = Image (Image::ARGB, getWidth(), getHeight(), true); - Graphics g2 (background); - getLookAndFeel().drawCallOutBoxBackground (*this, g2, outline); - } - - g.setColour (Colours::black); - g.drawImageAt (background, 0, 0); + getLookAndFeel().drawCallOutBoxBackground (*this, g, outline, background); } void CallOutBox::resized()