From 3ddc6dd43de1009a320eb9fad409dfab3c7cdd68 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 11 Jul 2012 21:07:47 +0100 Subject: [PATCH] New class: DropShadow, and a complete refactoring of the way shadows are rendered. The DropShadowEffect and DropShadower classes now take a DropShadow object to describe their shadow parameters, instead of the raw numbers. --- extras/JuceDemo/Source/demos/WidgetsDemo.cpp | 8 +- .../Source/GraphEditorPanel.cpp | 2 +- .../effects/juce_DropShadowEffect.cpp | 171 ++++++++++++------ .../effects/juce_DropShadowEffect.h | 57 +++--- .../images/juce_ImageConvolutionKernel.cpp | 47 +++++ .../buttons/juce_ArrowButton.cpp | 53 ++---- .../buttons/juce_ArrowButton.h | 13 +- .../buttons/juce_ShapeButton.cpp | 4 +- .../layout/juce_TabbedButtonBar.cpp | 7 +- .../layout/juce_TabbedButtonBar.h | 1 - .../lookandfeel/juce_LookAndFeel.cpp | 54 +++--- .../misc/juce_BubbleComponent.cpp | 2 +- .../misc/juce_DropShadower.cpp | 74 ++++---- .../juce_gui_basics/misc/juce_DropShadower.h | 37 ++-- .../native/juce_win32_Windowing.cpp | 2 +- .../lookandfeel/juce_OldSchoolLookAndFeel.cpp | 2 +- 16 files changed, 286 insertions(+), 248 deletions(-) diff --git a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp index f399808a6b..d81dbaa905 100644 --- a/extras/JuceDemo/Source/demos/WidgetsDemo.cpp +++ b/extras/JuceDemo/Source/demos/WidgetsDemo.cpp @@ -35,7 +35,7 @@ public: { Random random; - const int size = 10 + random.nextInt (30); + const float size = 10.0f + random.nextInt (30); ballBounds.setBounds (random.nextFloat() * 100.0f, random.nextFloat() * 100.0f, @@ -186,9 +186,9 @@ public: void timerCallback() { Random random; - blobPosition.setBounds (random.nextInt (getWidth()), - random.nextInt (getHeight()), - 40, 30); + blobPosition.setBounds ((float) random.nextInt (getWidth()), + (float) random.nextInt (getHeight()), + 40.0f, 30.0f); repaint(); } diff --git a/extras/audio plugin host/Source/GraphEditorPanel.cpp b/extras/audio plugin host/Source/GraphEditorPanel.cpp index e98811d5dc..036c8c1ee4 100644 --- a/extras/audio plugin host/Source/GraphEditorPanel.cpp +++ b/extras/audio plugin host/Source/GraphEditorPanel.cpp @@ -223,7 +223,7 @@ public: numIns (0), numOuts (0) { - shadow.setShadowProperties (2.5f, 0.5f, -1, 0); + shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point (0, 1)); setComponentEffect (&shadow); setSize (150, 60); diff --git a/modules/juce_graphics/effects/juce_DropShadowEffect.cpp b/modules/juce_graphics/effects/juce_DropShadowEffect.cpp index db38b2c61b..dfe64b54e9 100644 --- a/modules/juce_graphics/effects/juce_DropShadowEffect.cpp +++ b/modules/juce_graphics/effects/juce_DropShadowEffect.cpp @@ -27,86 +27,145 @@ #pragma optimize ("t", on) #endif +static void blurSingleChannelImage (uint8* const data, const int width, const int height, + const int lineStride, const int repetitions) noexcept +{ + uint8* line = data; + + for (int y = height; --y >= 0;) + { + for (int i = repetitions; --i >= 0;) + { + uint8* p = line; + *p++ = (((int) p[0]) + p[1]) / 2; + + for (int x = width - 2; --x >= 0;) + *p++ = (((int) p[-1]) + p[0] + p[1] + 1) / 3; + + *p = (((int) p[0]) + p[-1]) / 2; + } + + line += lineStride; + } + + for (int i = repetitions; --i >= 0;) + { + line = data; + + { + uint8* p1 = line; + uint8* p2 = line + lineStride; + + for (int x = width; --x >= 0;) + *p1++ = (((int) *p1) + *p2++) / 2; + } + + line += lineStride; + + for (int y = height - 2; --y >= 0;) + { + uint8* p1 = line; + uint8* p2 = line - lineStride; + uint8* p3 = line + lineStride; + + for (int x = width; --x >= 0;) + *p1++ = (((int) *p1) + *p2++ + *p3++ + 1) / 3; + + line += lineStride; + } + + uint8* p1 = line; + uint8* p2 = line - lineStride; + + for (int x = width; --x >= 0;) + *p1++ = (((int) *p1) + *p2++) / 2; + } +} + +static void blurSingleChannelImage (Image& image, int radius) +{ + const Image::BitmapData bm (image, Image::BitmapData::readWrite); + blurSingleChannelImage (bm.data, bm.width, bm.height, bm.lineStride, 2 * radius); +} + +#if JUCE_MSVC && JUCE_DEBUG + #pragma optimize ("", on) // resets optimisations to the project defaults +#endif + //============================================================================== -DropShadowEffect::DropShadowEffect() - : offsetX (0), - offsetY (0), - radius (4), - opacity (0.6f) +DropShadow::DropShadow() noexcept + : colour (0x90000000), radius (4) { } -DropShadowEffect::~DropShadowEffect() +DropShadow::DropShadow (const Colour& shadowColour, const int r, const Point& o) noexcept + : colour (shadowColour), radius (r), offset (o) { + jassert (radius > 0); } -void DropShadowEffect::setShadowProperties (const float newRadius, - const float newOpacity, - const int newShadowOffsetX, - const int newShadowOffsetY) +void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const { - radius = jmax (1.1f, newRadius); - offsetX = newShadowOffsetX; - offsetY = newShadowOffsetY; - opacity = newOpacity; -} + jassert (radius > 0); -void DropShadowEffect::drawShadow (Graphics& g, const Image& srcImage, - float radius, float alpha, int offsetX, int offsetY) -{ - const int w = srcImage.getWidth(); - const int h = srcImage.getHeight(); - - Image shadowImage (Image::SingleChannel, w, h, false); - - 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); - - for (int x = w; --x >= 0;) + if (srcImage.isValid()) { - int shadowAlpha = 0; + Image shadowImage (srcImage.convertedToFormat (Image::SingleChannel)); + shadowImage.duplicateIfShared(); - const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; - uint8* shadowPix = destData.data + x; + blurSingleChannelImage (shadowImage, radius); - 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; - } + g.setColour (colour); + g.drawImageAt (shadowImage, offset.x, offset.y, true); } +} - for (int y = h; --y >= 0;) +void DropShadow::drawForPath (Graphics& g, const Path& path) const +{ + jassert (radius > 0); + + const Rectangle area (path.getBounds().translated ((float) offset.x, (float) offset.y) + .getSmallestIntegerContainer() + .getIntersection (g.getClipBounds()) + .expanded (radius + 1, radius + 1)); + + if (area.getWidth() > 2 && area.getHeight() > 2) { - int shadowAlpha = 0; - uint8* shadowPix = destData.getLinePointer (y); + Image renderedPath (Image::SingleChannel, area.getWidth(), area.getHeight(), true); - for (int x = w; --x >= 0;) { - shadowAlpha = ((shadowAlpha * radiusMinus1 + (*shadowPix << 6)) * filter) >> 12; - *shadowPix++ = (uint8) shadowAlpha; + Graphics g2 (renderedPath); + g2.setColour (Colours::white); + g2.fillPath (path, AffineTransform::translation ((float) (offset.x - area.getX()), + (float) (offset.y - area.getY()))); } - } - g.setColour (Colours::black.withAlpha (alpha)); - g.drawImageAt (shadowImage, offsetX, offsetY, true); + blurSingleChannelImage (renderedPath, radius); + + g.setColour (colour); + g.drawImageAt (renderedPath, area.getX(), area.getY(), true); + } +} + +//============================================================================== +DropShadowEffect::DropShadowEffect() {} +DropShadowEffect::~DropShadowEffect() {} + +void DropShadowEffect::setShadowProperties (const DropShadow& newShadow) +{ + shadow = newShadow; } void DropShadowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) { - drawShadow (g, image, radius * scaleFactor, opacity * alpha, - (int) (offsetX * scaleFactor), (int) (offsetY * scaleFactor)); + DropShadow s (shadow); + s.radius = roundToInt (s.radius * scaleFactor); + s.colour = s.colour.withMultipliedAlpha (alpha); + s.offset.x = roundToInt (s.offset.x * scaleFactor); + s.offset.y = roundToInt (s.offset.y * scaleFactor); + + s.drawForImage (g, image); g.setOpacity (alpha); g.drawImageAt (image, 0, 0); } - -#if JUCE_MSVC && JUCE_DEBUG - #pragma optimize ("", on) // resets optimisations to the project defaults -#endif diff --git a/modules/juce_graphics/effects/juce_DropShadowEffect.h b/modules/juce_graphics/effects/juce_DropShadowEffect.h index 42c4086183..89c0e043d3 100644 --- a/modules/juce_graphics/effects/juce_DropShadowEffect.h +++ b/modules/juce_graphics/effects/juce_DropShadowEffect.h @@ -29,6 +29,37 @@ #include "juce_ImageEffectFilter.h" +//============================================================================== +/** + Defines a drop-shadow effect. +*/ +struct JUCE_API DropShadow +{ + /** Creates a default drop-shadow effect. */ + DropShadow() noexcept; + + /** Creates a drop-shadow object with the given parameters. */ + DropShadow (const Colour& shadowColour, int radius, const Point& offset) noexcept; + + /** Renders a drop-shadow based on the alpha-channel of the given image. */ + void drawForImage (Graphics& g, const Image& srcImage) const; + + /** Renders a drop-shadow based on the shape of a path. */ + void drawForPath (Graphics& g, const Path& path) const; + + /** The colour with which to render the shadow. + In most cases you'll probably want to leave this as black with an alpha + value of around 0.5 + */ + Colour colour; + + /** The approximate spread of the shadow. */ + int radius; + + /** The offset of the shadow. */ + Point offset; +}; + //============================================================================== /** An effect filter that adds a drop-shadow behind the image's content. @@ -50,9 +81,7 @@ class JUCE_API DropShadowEffect : public ImageEffectFilter public: //============================================================================== /** Creates a default drop-shadow effect. - - To customise the shadow's appearance, use the setShadowProperties() - method. + To customise the shadow's appearance, use the setShadowProperties() method. */ DropShadowEffect(); @@ -60,23 +89,8 @@ public: ~DropShadowEffect(); //============================================================================== - /** Sets up parameters affecting the shadow's appearance. - - @param newRadius the (approximate) radius of the blur used - @param newOpacity the opacity with which the shadow is rendered - @param newShadowOffsetX allows the shadow to be shifted in relation to the - component's contents - @param newShadowOffsetY allows the shadow to be shifted in relation to the - component's contents - */ - void setShadowProperties (float newRadius, - float newOpacity, - int newShadowOffsetX, - int newShadowOffsetY); - - - static void drawShadow (Graphics& g, const Image& srcImage, - float radius, float alpha, int offsetX, int offsetY); + /** Sets up parameters affecting the shadow's appearance. */ + void setShadowProperties (const DropShadow& newShadow); //============================================================================== /** @internal */ @@ -85,8 +99,7 @@ public: private: //============================================================================== - int offsetX, offsetY; - float radius, opacity; + DropShadow shadow; JUCE_LEAK_DETECTOR (DropShadowEffect); }; diff --git a/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp b/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp index 03cbaf3717..3d5a150494 100644 --- a/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp +++ b/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp @@ -241,4 +241,51 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, } } } + else if (destData.pixelStride == 1) + { + for (int y = area.getY(); y < bottom; ++y) + { + uint8* dest = line; + line += destData.lineStride; + + for (int x = area.getX(); x < right; ++x) + { + float c1 = 0; + + for (int yy = 0; yy < size; ++yy) + { + const int sy = y + yy - (size >> 1); + + if (sy >= srcData.height) + break; + + if (sy >= 0) + { + int sx = x - (size >> 1); + const uint8* src = srcData.getPixelPointer (sx, sy); + + for (int xx = 0; xx < size; ++xx) + { + if (sx >= srcData.width) + break; + + if (sx >= 0) + { + const float kernelMult = values [xx + yy * size]; + c1 += kernelMult * *src++; + } + else + { + src += 3; + } + + ++sx; + } + } + } + + *dest++ = (uint8) roundToInt (c1); + } + } + } } diff --git a/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp b/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp index 1a655e082d..0d2f87b908 100644 --- a/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp +++ b/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp @@ -23,49 +23,24 @@ ============================================================================== */ -ArrowButton::ArrowButton (const String& name, - float arrowDirectionInRadians, - const Colour& arrowColour) - : Button (name), - colour (arrowColour) +ArrowButton::ArrowButton (const String& name, float arrowDirectionInRadians, const Colour& arrowColour) + : Button (name), colour (arrowColour) { - path.lineTo (0.0f, 1.0f); - path.lineTo (1.0f, 0.5f); - path.closeSubPath(); - - path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, - 0.5f, 0.5f)); - - setComponentEffect (&shadow); - updateShadowAndOffset(); + path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); + path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, 0.5f, 0.5f)); } -ArrowButton::~ArrowButton() -{ -} +ArrowButton::~ArrowButton() {} -void ArrowButton::paintButton (Graphics& g, - bool /*isMouseOverButton*/, - bool /*isButtonDown*/) +void ArrowButton::paintButton (Graphics& g, bool /*isMouseOverButton*/, bool isButtonDown) { + Path p (path); + + const float offset = isButtonDown ? 1.0f : 0.0f; + p.applyTransform (path.getTransformToScaleToFit (offset, offset, getWidth() - 3.0f, getHeight() - 3.0f, false)); + + DropShadow (Colours::black.withAlpha (0.3f), isButtonDown ? 2 : 4, Point()).drawForPath (g, p); + g.setColour (colour); - - g.fillPath (path, path.getTransformToScaleToFit ((float) offset, - (float) offset, - (float) (getWidth() - 3), - (float) (getHeight() - 3), - false)); -} - -void ArrowButton::buttonStateChanged() -{ - updateShadowAndOffset(); -} - -void ArrowButton::updateShadowAndOffset() -{ - offset = (isDown()) ? 1 : 0; - - shadow.setShadowProperties ((isDown()) ? 1.2f : 3.0f, - 0.3f, -1, 0); + g.fillPath (p); } diff --git a/modules/juce_gui_basics/buttons/juce_ArrowButton.h b/modules/juce_gui_basics/buttons/juce_ArrowButton.h index 54f771f602..f08391b06f 100644 --- a/modules/juce_gui_basics/buttons/juce_ArrowButton.h +++ b/modules/juce_gui_basics/buttons/juce_ArrowButton.h @@ -55,23 +55,12 @@ public: protected: - //============================================================================== /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); - - /** @internal */ - void buttonStateChanged(); + void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown); private: - //============================================================================== Colour colour; - DropShadowEffect shadow; Path path; - int offset; - - void updateShadowAndOffset(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton); }; diff --git a/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp b/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp index 774671954e..e36d7b3071 100644 --- a/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp +++ b/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp @@ -64,8 +64,8 @@ void ShapeButton::setShape (const Path& newShape, shape = newShape; maintainShapeProportions = maintainShapeProportions_; - shadow.setShadowProperties (3.0f, 0.5f, 0, 0); - setComponentEffect ((hasShadow) ? &shadow : 0); + shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point())); + setComponentEffect (hasShadow ? &shadow : nullptr); if (resizeNowToFitThisShape) { diff --git a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp index 12770455dc..77039a4862 100644 --- a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp +++ b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp @@ -24,13 +24,8 @@ */ TabBarButton::TabBarButton (const String& name, TabbedButtonBar& owner_) - : Button (name), - owner (owner_), - overlapPixels (0), - extraCompPlacement (afterText) + : Button (name), owner (owner_), overlapPixels (0), extraCompPlacement (afterText) { - shadow.setShadowProperties (2.2f, 0.7f, 0, 0); - setComponentEffect (&shadow); setWantsKeyboardFocus (false); } diff --git a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h index b1e1ca63a6..7d9ad9af7b 100644 --- a/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h +++ b/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h @@ -109,7 +109,6 @@ public: protected: friend class TabbedButtonBar; TabbedButtonBar& owner; - DropShadowEffect shadow; int overlapPixels; ScopedPointer extraComponent; diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp index 65ff818a72..ed20b985cf 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp @@ -1977,10 +1977,9 @@ int LookAndFeel::getDefaultMenuBarHeight() //============================================================================== DropShadower* LookAndFeel::createDropShadowerForComponent (Component*) { - return new DropShadower (0.4f, 1, 5, 10); + return new DropShadower (DropShadow (Colours::black.withAlpha (0.4f), 10, Point (0, 2))); } - //============================================================================== void LookAndFeel::drawStretchableLayoutResizerBar (Graphics& g, int w, int h, @@ -2085,7 +2084,7 @@ int LookAndFeel::getTabButtonBestWidth (TabBarButton& button, int tabDepth) + getTabButtonOverlap (tabDepth) * 2; } -void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool isMouseOver, bool isMouseDown) +void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool /*isMouseOver*/, bool /*isMouseDown*/) { const Rectangle activeArea (button.getActiveArea()); const float w = (float) activeArea.getWidth(); @@ -2144,7 +2143,7 @@ void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool isMo p = p.createPathWithRoundedCorners (3.0f); } -void LookAndFeel::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool isMouseOver, bool isMouseDown) +void LookAndFeel::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool /*isMouseOver*/, bool /*isMouseDown*/) { const Colour tabBackground (button.getTabBackgroundColour()); const bool isFrontTab = button.isFrontTab(); @@ -2165,8 +2164,8 @@ void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isM { const Rectangle area (button.getTextArea().toFloat()); - int length = area.getWidth(); - int depth = area.getHeight(); + float length = area.getWidth(); + float depth = area.getHeight(); if (button.getTabbedButtonBar().isVertical()) std::swap (length, depth); @@ -2178,7 +2177,7 @@ void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isM textLayout.addFittedText (font, button.getButtonText().trim(), 0.0f, 0.0f, (float) length, (float) depth, Justification::centred, - jmax (1, depth / 12)); + jmax (1, ((int) depth) / 12)); AffineTransform t; @@ -2191,21 +2190,20 @@ void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isM default: jassertfalse; break; } + Colour col; + if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) || isColourSpecified (TabbedButtonBar::frontTextColourId))) - g.setColour (findColour (TabbedButtonBar::frontTextColourId)); + col = findColour (TabbedButtonBar::frontTextColourId); else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) || isColourSpecified (TabbedButtonBar::tabTextColourId)) - g.setColour (findColour (TabbedButtonBar::tabTextColourId)); + col = findColour (TabbedButtonBar::tabTextColourId); else - g.setColour (button.getTabBackgroundColour().contrasting()); + col = button.getTabBackgroundColour().contrasting(); - if (! (isMouseOver || isMouseDown)) - g.setOpacity (0.8f); - - if (! button.isEnabled()) - g.setOpacity (0.3f); + const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; + g.setColour (col.withMultipliedAlpha (alpha)); textLayout.draw (g, t); } @@ -2218,8 +2216,9 @@ void LookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouse tabShape.applyTransform (AffineTransform::translation ((float) activeArea.getX(), (float) activeArea.getY())); - fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); + DropShadow (Colours::black.withAlpha (0.5f), 2, Point (0, 1)).drawForPath (g, tabShape); + fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); drawTabButtonText (button, g, isMouseOver, isMouseDown); } @@ -2458,21 +2457,10 @@ void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, { if (cachedImage.isNull()) { - const int w = box.getWidth(); - const int h = box.getHeight(); - - Image renderedPath (Image::ARGB, w, h, true); - - { - Graphics g2 (renderedPath); - - g2.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); - g2.fillPath (path); - } - - cachedImage = Image (Image::ARGB, w, h, true); + cachedImage = Image (Image::ARGB, box.getWidth(), box.getHeight(), true); Graphics g2 (cachedImage); - DropShadowEffect::drawShadow (g2, renderedPath, 5.0f, 0.4f, 0, 2); + + DropShadow (Colours::black.withAlpha (0.7f), 8, Point (0, 2)).drawForPath (g2, path); } g.setColour (Colours::black); @@ -2779,16 +2767,16 @@ void LookAndFeel::drawShinyButtonShape (Graphics& g, Path outline; LookAndFeelHelpers::createRoundedPath (outline, x, y, w, h, cs, - ! (flatOnLeft || flatOnTop), + ! (flatOnLeft || flatOnTop), ! (flatOnRight || flatOnTop), - ! (flatOnLeft || flatOnBottom), + ! (flatOnLeft || flatOnBottom), ! (flatOnRight || flatOnBottom)); ColourGradient cg (baseColour, 0.0f, y, baseColour.overlaidWith (Colour (0x070000ff)), 0.0f, y + h, false); - cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); + cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); cg.addColour (0.51, baseColour.overlaidWith (Colour (0x110000ff))); g.setGradientFill (cg); diff --git a/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp b/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp index 3d013c3eb3..31c132a7df 100644 --- a/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp +++ b/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp @@ -28,7 +28,7 @@ BubbleComponent::BubbleComponent() { setInterceptsMouseClicks (false, false); - shadow.setShadowProperties (5.0f, 0.35f, 0, 0); + shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.35f), 5, Point())); setComponentEffect (&shadow); } diff --git a/modules/juce_gui_basics/misc/juce_DropShadower.cpp b/modules/juce_gui_basics/misc/juce_DropShadower.cpp index dfe0dcc132..8336214627 100644 --- a/modules/juce_gui_basics/misc/juce_DropShadower.cpp +++ b/modules/juce_gui_basics/misc/juce_DropShadower.cpp @@ -97,16 +97,8 @@ private: //============================================================================== -DropShadower::DropShadower (const float alpha_, - const int xOffset_, - const int yOffset_, - const float blurRadius_) - : owner (nullptr), - xOffset (xOffset_), - yOffset (yOffset_), - alpha (alpha_), - blurRadius (blurRadius_), - reentrant (false) +DropShadower::DropShadower (const DropShadow& shadow_) + : owner (nullptr), shadow (shadow_), reentrant (false) { } @@ -179,55 +171,51 @@ void DropShadower::updateShadows() { const ScopedValueSetter setter (reentrant, true, false); - const int shadowEdge = jmax (xOffset, yOffset) + (int) blurRadius; + const int shadowEdge = jmax (shadow.offset.x, shadow.offset.y) + shadow.radius; if (createShadowWindows) { - // keep a cached version of the image to save doing the gaussian too often - String imageId; - imageId << shadowEdge << ',' << xOffset << ',' << yOffset << ',' << alpha; + const int shadowEdge2 = shadowEdge * 2; + const int imageSize = shadowEdge * 5; - const int hash = imageId.hashCode(); + // keep a cached version of the image to save doing the gaussian too often + int64 hash = shadow.radius ^ 0x2342dfa7; + hash = hash * 101 + shadow.offset.x; + hash = hash * 101 + shadow.offset.y; + hash = hash * 65537 + shadow.colour.getARGB(); Image bigIm (ImageCache::getFromHashCode (hash)); if (bigIm.isNull()) { - bigIm = Image (Image::ARGB, shadowEdge * 5, shadowEdge * 5, true); + bigIm = Image (Image::ARGB, imageSize, imageSize, true); + Graphics g (bigIm); - Graphics bigG (bigIm); - bigG.setColour (Colours::black.withAlpha (alpha)); - bigG.fillRect (shadowEdge + xOffset, - shadowEdge + yOffset, - bigIm.getWidth() - (shadowEdge * 2), - bigIm.getHeight() - (shadowEdge * 2)); + Path p; + p.addRectangle ((float) (shadowEdge + shadow.offset.x), + (float) (shadowEdge + shadow.offset.y), + (float) (imageSize - shadowEdge2), + (float) (imageSize - shadowEdge2)); - ImageConvolutionKernel blurKernel (roundToInt (blurRadius * 2.0f)); - blurKernel.createGaussianBlur (blurRadius); - - blurKernel.applyToImage (bigIm, bigIm, - Rectangle (xOffset, yOffset, - bigIm.getWidth(), bigIm.getHeight())); + shadow.drawForPath (g, p); ImageCache::addImageToCache (bigIm, hash); } - const int iw = bigIm.getWidth(); - const int ih = bigIm.getHeight(); - const int shadowEdge2 = shadowEdge * 2; + jassert (imageSize == bigIm.getWidth() && imageSize == bigIm.getHeight()); - setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0); - setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, ih - shadowEdge2); - setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2); - setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, iw - shadowEdge, 0); - setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, iw - shadowEdge, ih - shadowEdge2); - setShadowImage (bigIm, 5, shadowEdge, shadowEdge, iw - shadowEdge, shadowEdge2); - setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0); - setShadowImage (bigIm, 7, shadowEdge, shadowEdge, iw - shadowEdge2, 0); - setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0); - setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, ih - shadowEdge); - setShadowImage (bigIm, 10, shadowEdge, shadowEdge, iw - shadowEdge2, ih - shadowEdge); - setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, ih - shadowEdge); + setShadowImage (bigIm, 0, shadowEdge, shadowEdge2, 0, 0); + setShadowImage (bigIm, 1, shadowEdge, shadowEdge2, 0, imageSize - shadowEdge2); + setShadowImage (bigIm, 2, shadowEdge, shadowEdge, 0, shadowEdge2); + setShadowImage (bigIm, 3, shadowEdge, shadowEdge2, imageSize - shadowEdge, 0); + setShadowImage (bigIm, 4, shadowEdge, shadowEdge2, imageSize - shadowEdge, imageSize - shadowEdge2); + setShadowImage (bigIm, 5, shadowEdge, shadowEdge, imageSize - shadowEdge, shadowEdge2); + setShadowImage (bigIm, 6, shadowEdge, shadowEdge, shadowEdge, 0); + setShadowImage (bigIm, 7, shadowEdge, shadowEdge, imageSize - shadowEdge2, 0); + setShadowImage (bigIm, 8, shadowEdge, shadowEdge, shadowEdge2, 0); + setShadowImage (bigIm, 9, shadowEdge, shadowEdge, shadowEdge, imageSize - shadowEdge); + setShadowImage (bigIm, 10, shadowEdge, shadowEdge, imageSize - shadowEdge2, imageSize - shadowEdge); + setShadowImage (bigIm, 11, shadowEdge, shadowEdge, shadowEdge2, imageSize - shadowEdge); for (int i = 0; i < 4; ++i) shadowWindows.add (new ShadowWindow (*owner, i, shadowImageSections)); diff --git a/modules/juce_gui_basics/misc/juce_DropShadower.h b/modules/juce_gui_basics/misc/juce_DropShadower.h index 49cb9682fe..142722c362 100644 --- a/modules/juce_gui_basics/misc/juce_DropShadower.h +++ b/modules/juce_gui_basics/misc/juce_DropShadower.h @@ -43,50 +43,35 @@ Component::addToDesktop(), and the system will create one of these if it's needed (which it obviously isn't on the Mac, for example). */ -class JUCE_API DropShadower : public ComponentListener +class JUCE_API DropShadower : private ComponentListener { public: //============================================================================== - /** Creates a DropShadower. - - @param alpha the opacity of the shadows, from 0 to 1.0 - @param xOffset the horizontal displacement of the shadow, in pixels - @param yOffset the vertical displacement of the shadow, in pixels - @param blurRadius the radius of the blur to use for creating the shadow - */ - DropShadower (float alpha = 0.5f, - int xOffset = 1, - int yOffset = 5, - float blurRadius = 10.0f); + /** Creates a DropShadower. */ + DropShadower (const DropShadow& shadowType); /** Destructor. */ - virtual ~DropShadower(); + ~DropShadower(); /** Attaches the DropShadower to the component you want to shadow. */ void setOwner (Component* componentToFollow); - //============================================================================== - /** @internal */ - void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); - /** @internal */ - void componentBroughtToFront (Component& component); - /** @internal */ - void componentParentHierarchyChanged (Component& component); - /** @internal */ - void componentVisibilityChanged (Component& component); - private: //============================================================================== Component* owner; OwnedArray shadowWindows; Image shadowImageSections[12]; - const int xOffset, yOffset; - const float alpha, blurRadius; + DropShadow shadow; bool reentrant; + void componentMovedOrResized (Component&, bool, bool); + void componentBroughtToFront (Component&); + void componentParentHierarchyChanged (Component&); + void componentVisibilityChanged (Component&); + void updateShadows(); - void setShadowImage (const Image& src, int num, int w, int h, int sx, int sy); + void setShadowImage (const Image&, int num, int w, int h, int sx, int sy); void bringShadowWindowsToFront(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DropShadower); diff --git a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index cdc28d1d76..2d59b2ba4b 100644 --- a/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -759,7 +759,7 @@ public: shouldDeactivateTitleBar = oldDeactivate; if (shadower != nullptr) - shadower->componentBroughtToFront (*component); + handleBroughtToFront(); return true; } diff --git a/modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp b/modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp index f54f0996b2..3fb47545d5 100644 --- a/modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp +++ b/modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp @@ -39,7 +39,7 @@ OldSchoolLookAndFeel::OldSchoolLookAndFeel() setColour (PopupMenu::highlightedTextColourId, Colours::black); setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); - scrollbarShadow.setShadowProperties (2.2f, 0.5f, 0, 0); + scrollbarShadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 2, Point())); } OldSchoolLookAndFeel::~OldSchoolLookAndFeel()