Add customized rounded ripple mask generators.

This commit is contained in:
John Preston 2022-10-03 15:10:49 +04:00
parent 2c2a7887e6
commit 89ae115a87
7 changed files with 120 additions and 37 deletions

View file

@ -18,7 +18,7 @@ namespace {
const auto s = QSize( const auto s = QSize(
st::boxRadius * 2 + st.extend.left(), st::boxRadius * 2 + st.extend.left(),
st::boxRadius * 2 + st.extend.right()); st::boxRadius * 2 + st.extend.right());
const auto mask = Ui::RippleAnimation::maskByDrawer(s, false, [&]( const auto mask = Ui::RippleAnimation::MaskByDrawer(s, false, [&](
QPainter &p) { QPainter &p) {
p.drawRoundedRect(QRect(QPoint(), s), st::boxRadius, st::boxRadius); p.drawRoundedRect(QRect(QPoint(), s), st::boxRadius, st::boxRadius);
}); });

View file

@ -9,15 +9,27 @@
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/image/image_prepare.h"
namespace Ui { namespace Ui {
class RippleAnimation::Ripple { class RippleAnimation::Ripple {
public: public:
Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn<void()> update); Ripple(
Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn<void()> update); const style::RippleAnimation &st,
QPoint origin,
int startRadius,
const QPixmap &mask,
Fn<void()> update);
Ripple(
const style::RippleAnimation &st,
const QPixmap &mask,
Fn<void()> update);
void paint(QPainter &p, const QPixmap &mask, const QColor *colorOverride); void paint(
QPainter &p,
const QPixmap &mask,
const QColor *colorOverride);
void stop(); void stop();
void unstop(); void unstop();
@ -43,7 +55,12 @@ private:
}; };
RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin, int startRadius, const QPixmap &mask, Fn<void()> update) RippleAnimation::Ripple::Ripple(
const style::RippleAnimation &st,
QPoint origin,
int startRadius,
const QPixmap &mask,
Fn<void()> update)
: _st(st) : _st(st)
, _update(update) , _update(update)
, _origin(origin) , _origin(origin)
@ -59,7 +76,9 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, QPoint origin,
{ 0, _frame.height() / pixelRatio }, { 0, _frame.height() / pixelRatio },
}; };
for (auto point : points) { 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)); _radiusTo = qRound(sqrt(_radiusTo));
@ -79,7 +98,10 @@ RippleAnimation::Ripple::Ripple(const style::RippleAnimation &st, const QPixmap
_hide.start(_update, 0., 1., _st.hideDuration); _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.); auto opacity = _hide.value(_hiding ? 0. : 1.);
if (opacity == 0.) { if (opacity == 0.) {
return; return;
@ -92,9 +114,11 @@ void RippleAnimation::Ripple::paint(QPainter &p, const QPixmap &mask, const QCol
Assert(!std::isnan(diff)); Assert(!std::isnan(diff));
const auto mult = diff * shown; const auto mult = diff * shown;
Assert(!std::isnan(mult)); 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)); 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); _frame.fill(Qt::transparent);
{ {
QPainter p(&_frame); QPainter p(&_frame);
@ -151,7 +175,10 @@ void RippleAnimation::Ripple::clearCache() {
_cache = QPixmap(); _cache = QPixmap();
} }
RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn<void()> callback) RippleAnimation::RippleAnimation(
const style::RippleAnimation &st,
QImage mask,
Fn<void()> callback)
: _st(st) : _st(st)
, _mask(PixmapFromImage(std::move(mask))) , _mask(PixmapFromImage(std::move(mask)))
, _update(callback) { , _update(callback) {
@ -160,7 +187,8 @@ RippleAnimation::RippleAnimation(const style::RippleAnimation &st, QImage mask,
void RippleAnimation::add(QPoint origin, int startRadius) { void RippleAnimation::add(QPoint origin, int startRadius) {
lastStop(); lastStop();
_ripples.push_back(std::make_unique<Ripple>(_st, origin, startRadius, _mask, _update)); _ripples.push_back(
std::make_unique<Ripple>(_st, origin, startRadius, _mask, _update));
} }
void RippleAnimation::addFading() { 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()) { if (_ripples.empty()) {
return; return;
} }
@ -211,8 +244,13 @@ void RippleAnimation::paint(QPainter &p, int x, int y, int outerWidth, const QCo
clearFinished(); clearFinished();
} }
QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn<void(QPainter &p)> drawer) { QImage RippleAnimation::MaskByDrawer(
auto result = QImage(size * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied); QSize size,
bool filled,
Fn<void(QPainter &p)> drawer) {
auto result = QImage(
size * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(style::DevicePixelRatio()); result.setDevicePixelRatio(style::DevicePixelRatio());
result.fill(filled ? QColor(255, 255, 255) : Qt::transparent); result.fill(filled ? QColor(255, 255, 255) : Qt::transparent);
if (drawer) { if (drawer) {
@ -226,18 +264,46 @@ QImage RippleAnimation::maskByDrawer(QSize size, bool filled, Fn<void(QPainter &
return result; return result;
} }
QImage RippleAnimation::rectMask(QSize size) { QImage RippleAnimation::RectMask(QSize size) {
return maskByDrawer(size, true, Fn<void(QPainter&)>()); return MaskByDrawer(size, true, nullptr);
} }
QImage RippleAnimation::roundRectMask(QSize size, int radius) { QImage RippleAnimation::RoundRectMask(QSize size, int radius) {
return maskByDrawer(size, false, [size, radius](QPainter &p) { return MaskByDrawer(size, false, [&](QPainter &p) {
p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius); p.drawRoundedRect(0, 0, size.width(), size.height(), radius, radius);
}); });
} }
QImage RippleAnimation::ellipseMask(QSize size) { QImage RippleAnimation::RoundRectMask(
return maskByDrawer(size, false, [size](QPainter &p) { 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()); p.drawEllipse(0, 0, size.width(), size.height());
}); });
} }

View file

@ -10,12 +10,20 @@
#include <deque> #include <deque>
namespace Images {
struct CornersMaskRef;
} // namespace Images
namespace Ui { namespace Ui {
class RippleAnimation { class RippleAnimation {
public: public:
// White upon transparent mask, like colorizeImage(black-white-mask, white). // White upon transparent mask,
RippleAnimation(const style::RippleAnimation &st, QImage mask, Fn<void()> update); // like colorizeImage(black-white-mask, white).
RippleAnimation(
const style::RippleAnimation &st,
QImage mask,
Fn<void()> update);
void add(QPoint origin, int startRadius = 0); void add(QPoint origin, int startRadius = 0);
void addFading(); void addFading();
@ -24,16 +32,25 @@ public:
void lastFinish(); void lastFinish();
void forceRepaint(); 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 { bool empty() const {
return _ripples.empty(); return _ripples.empty();
} }
static QImage maskByDrawer(QSize size, bool filled, Fn<void(QPainter &p)> drawer); static QImage MaskByDrawer(
static QImage rectMask(QSize size); QSize size,
static QImage roundRectMask(QSize size, int radius); bool filled,
static QImage ellipseMask(QSize size); Fn<void(QPainter &p)> 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(); ~RippleAnimation();

View file

@ -165,7 +165,7 @@ void RippleButton::ensureRipple() {
} }
QImage RippleButton::prepareRippleMask() const { QImage RippleButton::prepareRippleMask() const {
return RippleAnimation::rectMask(size()); return RippleAnimation::RectMask(size());
} }
QPoint RippleButton::prepareRippleStartPosition() const { QPoint RippleButton::prepareRippleStartPosition() const {
@ -449,7 +449,7 @@ QImage RoundButton::prepareRippleMask() const {
if (_fullWidthOverride < 0) { if (_fullWidthOverride < 0) {
rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height()); rounded = QRect(0, rounded.top(), innerWidth - _fullWidthOverride, rounded.height());
} }
return RippleAnimation::roundRectMask( return RippleAnimation::RoundRectMask(
rounded.size(), rounded.size(),
(_fullRadius (_fullRadius
? (rounded.height() / 2) ? (rounded.height() / 2)
@ -551,7 +551,7 @@ QPoint IconButton::prepareRippleStartPosition() const {
} }
QImage IconButton::prepareRippleMask() 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) CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : RippleButton(parent, st.ripple)
@ -684,7 +684,7 @@ QPoint CrossButton::prepareRippleStartPosition() const {
} }
QImage CrossButton::prepareRippleMask() 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( SettingsButton::SettingsButton(

View file

@ -32,7 +32,7 @@ CallButton::CallButton(
void CallButton::init() { void CallButton::init() {
resize(_stFrom->button.width, _stFrom->button.height); 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)); _bgFrom = Ui::PixmapFromImage(style::colorizeImage(_bgMask, _stFrom->bg));
if (_stTo) { if (_stTo) {
Assert(_stFrom->button.width == _stTo->button.width); Assert(_stFrom->button.width == _stTo->button.width);
@ -242,7 +242,7 @@ QPoint CallButton::prepareRippleStartPosition() const {
} }
QImage CallButton::prepareRippleMask() 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 } // namespace Ui

View file

@ -222,7 +222,7 @@ QSize ToggleView::rippleSize() const {
QImage ToggleView::prepareRippleMask() const { QImage ToggleView::prepareRippleMask() const {
auto size = rippleSize(); auto size = rippleSize();
return RippleAnimation::roundRectMask(size, size.height() / 2); return RippleAnimation::RoundRectMask(size, size.height() / 2);
} }
bool ToggleView::checkRippleStartPosition(QPoint position) const { bool ToggleView::checkRippleStartPosition(QPoint position) const {
@ -277,7 +277,7 @@ QSize CheckView::rippleSize() const {
} }
QImage CheckView::prepareRippleMask() const { QImage CheckView::prepareRippleMask() const {
return RippleAnimation::ellipseMask(rippleSize()); return RippleAnimation::EllipseMask(rippleSize());
} }
bool CheckView::checkRippleStartPosition(QPoint position) const { bool CheckView::checkRippleStartPosition(QPoint position) const {
@ -356,7 +356,7 @@ QSize RadioView::rippleSize() const {
} }
QImage RadioView::prepareRippleMask() const { QImage RadioView::prepareRippleMask() const {
return RippleAnimation::ellipseMask(rippleSize()); return RippleAnimation::EllipseMask(rippleSize());
} }
bool RadioView::checkRippleStartPosition(QPoint position) const { bool RadioView::checkRippleStartPosition(QPoint position) const {

View file

@ -188,7 +188,7 @@ QPoint Action::prepareRippleStartPosition() const {
} }
QImage Action::prepareRippleMask() const { QImage Action::prepareRippleMask() const {
return Ui::RippleAnimation::rectMask(size()); return Ui::RippleAnimation::RectMask(size());
} }
int Action::contentHeight() const { int Action::contentHeight() const {