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(
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);
});

View file

@ -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<void()> update);
Ripple(const style::RippleAnimation &st, const QPixmap &mask, Fn<void()> update);
Ripple(
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 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)
, _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<void()> callback)
RippleAnimation::RippleAnimation(
const style::RippleAnimation &st,
QImage mask,
Fn<void()> 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<Ripple>(_st, origin, startRadius, _mask, _update));
_ripples.push_back(
std::make_unique<Ripple>(_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<void(QPainter &p)> drawer) {
auto result = QImage(size * style::DevicePixelRatio(), QImage::Format_ARGB32_Premultiplied);
QImage RippleAnimation::MaskByDrawer(
QSize size,
bool filled,
Fn<void(QPainter &p)> 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<void(QPainter &
return result;
}
QImage RippleAnimation::rectMask(QSize size) {
return maskByDrawer(size, true, Fn<void(QPainter&)>());
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());
});
}

View file

@ -10,12 +10,20 @@
#include <deque>
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<void()> update);
// White upon transparent mask,
// like colorizeImage(black-white-mask, white).
RippleAnimation(
const style::RippleAnimation &st,
QImage mask,
Fn<void()> 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<void(QPainter &p)> 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<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();

View file

@ -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(

View file

@ -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

View file

@ -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 {

View file

@ -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 {