From 89ae115a878feb9e07fe7423fcf123397eba5966 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 3 Oct 2022 15:10:49 +0400 Subject: [PATCH] Add customized rounded ripple mask generators. --- ui/cached_special_layer_shadow_corners.cpp | 2 +- ui/effects/ripple_animation.cpp | 104 +++++++++++++++++---- ui/effects/ripple_animation.h | 31 ++++-- ui/widgets/buttons.cpp | 8 +- ui/widgets/call_button.cpp | 4 +- ui/widgets/checkbox.cpp | 6 +- ui/widgets/menu/menu_action.cpp | 2 +- 7 files changed, 120 insertions(+), 37 deletions(-) diff --git a/ui/cached_special_layer_shadow_corners.cpp b/ui/cached_special_layer_shadow_corners.cpp index c60c359..0d98360 100644 --- a/ui/cached_special_layer_shadow_corners.cpp +++ b/ui/cached_special_layer_shadow_corners.cpp @@ -18,7 +18,7 @@ namespace { const auto s = QSize( st::boxRadius * 2 + st.extend.left(), st::boxRadius * 2 + st.extend.right()); - const auto mask = Ui::RippleAnimation::maskByDrawer(s, false, [&]( + const auto mask = Ui::RippleAnimation::MaskByDrawer(s, false, [&]( QPainter &p) { p.drawRoundedRect(QRect(QPoint(), s), st::boxRadius, st::boxRadius); }); diff --git a/ui/effects/ripple_animation.cpp b/ui/effects/ripple_animation.cpp index 5fbb030..35cc5bc 100644 --- a/ui/effects/ripple_animation.cpp +++ b/ui/effects/ripple_animation.cpp @@ -9,15 +9,27 @@ #include "ui/effects/animations.h" #include "ui/painter.h" #include "ui/ui_utility.h" +#include "ui/image/image_prepare.h" namespace Ui { class RippleAnimation::Ripple { public: - Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn update); - Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn update); + Ripple( + const style::RippleAnimation &st, + QPoint origin, + int startRadius, + const QPixmap &mask, + Fn update); + Ripple( + const style::RippleAnimation &st, + const QPixmap &mask, + Fn update); - void paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride); + void paint( + QPainter &p, + const QPixmap &mask, + const QColor *colorOverride); void stop(); void unstop(); @@ -43,7 +55,12 @@ private: }; -RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn update) +RippleAnimation::Ripple::Ripple( + const style::RippleAnimation &st, + QPoint origin, + int startRadius, + const QPixmap &mask, + Fn update) : _st(st) , _update(update) , _origin(origin) @@ -59,7 +76,9 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, { 0, _frame.height() / pixelRatio }, }; for (auto point : points) { - accumulate_max(_radiusTo, style::point::dotProduct(_origin - point, _origin - point)); + accumulate_max( + _radiusTo, + style::point::dotProduct(_origin - point, _origin - point)); } _radiusTo = qRound(sqrt(_radiusTo)); @@ -79,7 +98,10 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap _hide.start(_update, 0., 1., _st.hideDuration); } -void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride) { +void RippleAnimation::Ripple::paint( + QPainter &p, + const QPixmap &mask, + const QColor *colorOverride) { auto opacity = _hide.value(_hiding ? 0. : 1.); if (opacity == 0.) { return; @@ -92,9 +114,11 @@ void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, const QCol Assert(!std::isnan(diff)); const auto mult = diff * shown; Assert(!std::isnan(mult)); - const auto interpolated = _radiusFrom + mult;//anim::interpolateF(_radiusFrom, _radiusTo, shown); + const auto interpolated = _radiusFrom + mult; + //anim::interpolateF(_radiusFrom, _radiusTo, shown); Assert(!std::isnan(interpolated)); - auto radius = int(base::SafeRound(interpolated));//anim::interpolate(_radiusFrom, _radiusTo, _show.value(1.)); + auto radius = int(base::SafeRound(interpolated)); + //anim::interpolate(_radiusFrom, _radiusTo, _show.value(1.)); _frame.fill(Qt::transparent); { QPainter p(&_frame); @@ -151,7 +175,10 @@ void RippleAnimation::Ripple::clearCache() { _cache = QPixmap(); } -RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn callback) +RippleAnimation::RippleAnimation( + const style::RippleAnimation &st, + QImage mask, + Fn callback) : _st(st) , _mask(PixmapFromImage(std::move(mask))) , _update(callback) { @@ -160,7 +187,8 @@ RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, void RippleAnimation::add(QPoint origin, int startRadius) { lastStop(); - _ripples.push_back(std::make_unique(_st, origin, startRadius, _mask, _update)); + _ripples.push_back( + std::make_unique(_st, origin, startRadius, _mask, _update)); } void RippleAnimation::addFading() { @@ -195,7 +223,12 @@ void RippleAnimation::forceRepaint() { } } -void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, const QColor *colorOverride) { +void RippleAnimation::paint( + QPainter &p, + int x, + int y, + int outerWidth, + const QColor *colorOverride) { if (_ripples.empty()) { return; } @@ -211,8 +244,13 @@ void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, const QCo clearFinished(); } -QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn drawer) { - auto result = QImage(size * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied); +QImage RippleAnimation::MaskByDrawer( + QSize size, + bool filled, + Fn drawer) { + auto result = QImage( + size * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); result.setDevicePixelRatio(style::DevicePixelRatio()); result.fill(filled ? QColor(255, 255, 255) : Qt::transparent); if (drawer) { @@ -226,18 +264,46 @@ QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn()); +QImage RippleAnimation::RectMask(QSize size) { + return MaskByDrawer(size, true, nullptr); } -QImage RippleAnimation::roundRectMask(QSize size, int radius) { - return maskByDrawer(size, false, [size, radius](QPainter &p) { +QImage RippleAnimation::RoundRectMask(QSize size, int radius) { + return MaskByDrawer(size, false, [&](QPainter &p) { p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius); }); } -QImage RippleAnimation::ellipseMask(QSize size) { - return maskByDrawer(size, false, [size](QPainter &p) { +QImage RippleAnimation::RoundRectMask( + QSize size, + Images::CornersMaskRef corners) { + return MaskByDrawer(size, true, [&](QPainter &p) { + p.setCompositionMode(QPainter::CompositionMode_Source); + const auto ratio = style::DevicePixelRatio(); + const auto corner = [&](int index, bool right, bool bottom) { + if (const auto image = corners.p[index]) { + if (!image->isNull()) { + const auto width = image->width() / ratio; + const auto height = image->height() / ratio; + p.drawImage( + QRect( + right ? (size.width() - width) : 0, + bottom ? (size.height() - height) : 0, + width, + height), + *image); + } + } + }; + corner(0, false, false); + corner(1, true, false); + corner(2, false, true); + corner(3, true, true); + }); +} + +QImage RippleAnimation::EllipseMask(QSize size) { + return MaskByDrawer(size, false, [&](QPainter &p) { p.drawEllipse(0, 0, size.width(), size.height()); }); } diff --git a/ui/effects/ripple_animation.h b/ui/effects/ripple_animation.h index 1ab6a89..94a9ca1 100644 --- a/ui/effects/ripple_animation.h +++ b/ui/effects/ripple_animation.h @@ -10,12 +10,20 @@ #include +namespace Images { +struct CornersMaskRef; +} // namespace Images + namespace Ui { class RippleAnimation { public: - // White upon transparent mask, like colorizeImage(black-white-mask, white). - RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn update); + // White upon transparent mask, + // like colorizeImage(black-white-mask, white). + RippleAnimation( + const style::RippleAnimation &st, + QImage mask, + Fn update); void add(QPoint origin, int startRadius = 0); void addFading(); @@ -24,16 +32,25 @@ public: void lastFinish(); void forceRepaint(); - void paint(QPainter &p, int x, int y, int outerWidth, const QColor *colorOverride = nullptr); + void paint( + QPainter &p, + int x, + int y, + int outerWidth, + const QColor *colorOverride = nullptr); bool empty() const { return _ripples.empty(); } - static QImage maskByDrawer(QSize size, bool filled, Fn drawer); - static QImage rectMask(QSize size); - static QImage roundRectMask(QSize size, int radius); - static QImage ellipseMask(QSize size); + static QImage MaskByDrawer( + QSize size, + bool filled, + Fn drawer); + static QImage RectMask(QSize size); + static QImage RoundRectMask(QSize size, int radius); + static QImage RoundRectMask(QSize size, Images::CornersMaskRef corners); + static QImage EllipseMask(QSize size); ~RippleAnimation(); diff --git a/ui/widgets/buttons.cpp b/ui/widgets/buttons.cpp index adcffdd..817e42c 100644 --- a/ui/widgets/buttons.cpp +++ b/ui/widgets/buttons.cpp @@ -165,7 +165,7 @@ void RippleButton::ensureRipple() { } QImage RippleButton::prepareRippleMask() const { - return RippleAnimation::rectMask(size()); + return RippleAnimation::RectMask(size()); } QPoint RippleButton::prepareRippleStartPosition() const { @@ -449,7 +449,7 @@ QImage RoundButton::prepareRippleMask() const { if (_fullWidthOverride < 0) { rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); } - return RippleAnimation::roundRectMask( + return RippleAnimation::RoundRectMask( rounded.size(), (_fullRadius ? (rounded.height() / 2) @@ -551,7 +551,7 @@ QPoint IconButton::prepareRippleStartPosition() const { } QImage IconButton::prepareRippleMask() const { - return RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); + return RippleAnimation::EllipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); } CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : RippleButton(parent, st.ripple) @@ -684,7 +684,7 @@ QPoint CrossButton::prepareRippleStartPosition() const { } QImage CrossButton::prepareRippleMask() const { - return RippleAnimation::ellipseMask(QSize(_st.cross.size, _st.cross.size)); + return RippleAnimation::EllipseMask(QSize(_st.cross.size, _st.cross.size)); } SettingsButton::SettingsButton( diff --git a/ui/widgets/call_button.cpp b/ui/widgets/call_button.cpp index f1991d0..3048a8e 100644 --- a/ui/widgets/call_button.cpp +++ b/ui/widgets/call_button.cpp @@ -32,7 +32,7 @@ CallButton::CallButton( void CallButton::init() { resize(_stFrom->button.width, _stFrom->button.height); - _bgMask = RippleAnimation::ellipseMask(QSize(_stFrom->bgSize, _stFrom->bgSize)); + _bgMask = RippleAnimation::EllipseMask(QSize(_stFrom->bgSize, _stFrom->bgSize)); _bgFrom = Ui::PixmapFromImage(style::colorizeImage(_bgMask, _stFrom->bg)); if (_stTo) { Assert(_stFrom->button.width == _stTo->button.width); @@ -242,7 +242,7 @@ QPoint CallButton::prepareRippleStartPosition() const { } QImage CallButton::prepareRippleMask() const { - return RippleAnimation::ellipseMask(QSize(_stFrom->button.rippleAreaSize, _stFrom->button.rippleAreaSize)); + return RippleAnimation::EllipseMask(QSize(_stFrom->button.rippleAreaSize, _stFrom->button.rippleAreaSize)); } } // namespace Ui diff --git a/ui/widgets/checkbox.cpp b/ui/widgets/checkbox.cpp index 0836cdb..cbd1cb3 100644 --- a/ui/widgets/checkbox.cpp +++ b/ui/widgets/checkbox.cpp @@ -222,7 +222,7 @@ QSize ToggleView::rippleSize() const { QImage ToggleView::prepareRippleMask() const { auto size = rippleSize(); - return RippleAnimation::roundRectMask(size, size.height() / 2); + return RippleAnimation::RoundRectMask(size, size.height() / 2); } bool ToggleView::checkRippleStartPosition(QPoint position) const { @@ -277,7 +277,7 @@ QSize CheckView::rippleSize() const { } QImage CheckView::prepareRippleMask() const { - return RippleAnimation::ellipseMask(rippleSize()); + return RippleAnimation::EllipseMask(rippleSize()); } bool CheckView::checkRippleStartPosition(QPoint position) const { @@ -356,7 +356,7 @@ QSize RadioView::rippleSize() const { } QImage RadioView::prepareRippleMask() const { - return RippleAnimation::ellipseMask(rippleSize()); + return RippleAnimation::EllipseMask(rippleSize()); } bool RadioView::checkRippleStartPosition(QPoint position) const { diff --git a/ui/widgets/menu/menu_action.cpp b/ui/widgets/menu/menu_action.cpp index 1963eb5..cc723e4 100644 --- a/ui/widgets/menu/menu_action.cpp +++ b/ui/widgets/menu/menu_action.cpp @@ -188,7 +188,7 @@ QPoint Action::prepareRippleStartPosition() const { } QImage Action::prepareRippleMask() const { - return Ui::RippleAnimation::rectMask(size()); + return Ui::RippleAnimation::RectMask(size()); } int Action::contentHeight() const {