Optimized dropdown animation. Dropdown animation for EmojiPan done.

This commit is contained in:
John Preston 2016-11-12 18:02:19 +03:00
parent 78f55c10e9
commit 48eb72a9c2
15 changed files with 1407 additions and 787 deletions

View File

@ -2732,40 +2732,74 @@ namespace {
}
return ::cornersMaskSmall;
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, const CornersPixmaps &c, const style::color *sh) {
int32 cw = c.p[0]->width() / cIntRetinaFactor(), ch = c.p[0]->height() / cIntRetinaFactor();
if (w < 2 * cw || h < 2 * ch) return;
if (w > 2 * cw) {
p.fillRect(QRect(x + cw, y, w - 2 * cw, ch), bg->b);
p.fillRect(QRect(x + cw, y + h - ch, w - 2 * cw, ch), bg->b);
if (sh) p.fillRect(QRect(x + cw, y + h, w - 2 * cw, st::msgShadow), (*sh)->b);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, const CornersPixmaps &corner, const style::color *shadow, RectParts parts) {
auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor();
if (w < 2 * cornerWidth || h < 2 * cornerHeight) return;
if (w > 2 * cornerWidth) {
if (parts & RectPart::Top) {
p.fillRect(x + cornerWidth, y, w - 2 * cornerWidth, cornerHeight, bg);
}
if (parts & RectPart::Bottom) {
p.fillRect(x + cornerWidth, y + h - cornerHeight, w - 2 * cornerWidth, cornerHeight, bg);
if (shadow) {
p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, *shadow);
}
}
}
if (h > 2 * ch) {
p.fillRect(QRect(x, y + ch, w, h - 2 * ch), bg->b);
if (h > 2 * cornerHeight) {
if ((parts & RectPart::NoTopBottom) == qFlags(RectPart::NoTopBottom)) {
p.fillRect(x, y + cornerHeight, w, h - 2 * cornerHeight, bg);
} else {
if (parts & RectPart::Left) {
p.fillRect(x, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg);
}
if ((parts & RectPart::Center) && w > 2 * cornerWidth) {
p.fillRect(x + cornerWidth, y + cornerHeight, w - 2 * cornerWidth, h - 2 * cornerHeight, bg);
}
if (parts & RectPart::Right) {
p.fillRect(x + w - cornerWidth, y + cornerHeight, cornerWidth, h - 2 * cornerHeight, bg);
}
}
}
if (parts & RectPart::TopLeft) {
p.drawPixmap(x, y, *corner.p[0]);
}
if (parts & RectPart::TopRight) {
p.drawPixmap(x + w - cornerWidth, y, *corner.p[1]);
}
if (parts & RectPart::BottomLeft) {
p.drawPixmap(x, y + h - cornerHeight, *corner.p[2]);
}
if (parts & RectPart::BottomRight) {
p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight, *corner.p[3]);
}
p.drawPixmap(QPoint(x, y), *c.p[0]);
p.drawPixmap(QPoint(x + w - cw, y), *c.p[1]);
p.drawPixmap(QPoint(x, y + h - ch), *c.p[2]);
p.drawPixmap(QPoint(x + w - cw, y + h - ch), *c.p[3]);
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh) {
roundRect(p, x, y, w, h, bg, ::corners[index], sh);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *shadow, RectParts parts) {
roundRect(p, x, y, w, h, bg, ::corners[index], shadow, parts);
}
void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index) {
const CornersPixmaps &c = ::corners[index];
int32 cw = c.p[0]->width() / cIntRetinaFactor(), ch = c.p[0]->height() / cIntRetinaFactor();
p.fillRect(x + cw, y + h, w - 2 * cw, st::msgShadow, sh->b);
p.fillRect(x, y + h - ch, cw, st::msgShadow, sh->b);
p.fillRect(x + w - cw, y + h - ch, cw, st::msgShadow, sh->b);
p.drawPixmap(x, y + h - ch + st::msgShadow, *c.p[2]);
p.drawPixmap(x + w - cw, y + h - ch + st::msgShadow, *c.p[3]);
void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &shadow, RoundCorners index, RectParts parts) {
auto &corner = ::corners[index];
auto cornerWidth = corner.p[0]->width() / cIntRetinaFactor();
auto cornerHeight = corner.p[0]->height() / cIntRetinaFactor();
if (parts & RectPart::Bottom) {
p.fillRect(x + cornerWidth, y + h, w - 2 * cornerWidth, st::msgShadow, shadow);
}
if (parts & RectPart::BottomLeft) {
p.fillRect(x, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow);
p.drawPixmap(x, y + h - cornerHeight + st::msgShadow, *corner.p[2]);
}
if (parts & RectPart::BottomRight) {
p.fillRect(x + w - cornerWidth, y + h - cornerHeight, cornerWidth, st::msgShadow, shadow);
p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, *corner.p[3]);
}
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius) {
uint32 colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24);
CornersMap::const_iterator i = cornersMap.find(colorKey);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius, RectParts parts) {
auto colorKey = ((uint32(bg->c.alpha()) & 0xFF) << 24) | ((uint32(bg->c.red()) & 0xFF) << 16) | ((uint32(bg->c.green()) & 0xFF) << 8) | ((uint32(bg->c.blue()) & 0xFF) << 24);
auto i = cornersMap.find(colorKey);
if (i == cornersMap.cend()) {
QImage images[4];
switch (radius) {
@ -2781,7 +2815,7 @@ namespace {
}
i = cornersMap.insert(colorKey, pixmaps);
}
roundRect(p, x, y, w, h, bg, i.value(), 0);
roundRect(p, x, y, w, h, bg, i.value(), nullptr, parts);
}
WallPapers gServerBackgrounds;

View File

@ -279,18 +279,39 @@ namespace App {
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
void setProxySettings(QTcpSocket &socket);
enum class RectPart {
TopLeft = 0x001,
Top = 0x002,
TopRight = 0x004,
Left = 0x008,
Center = 0x010,
Right = 0x020,
BottomLeft = 0x040,
Bottom = 0x080,
BottomRight = 0x100,
TopFull = 0x007,
LeftFull = 0x049,
RightFull = 0x124,
BottomFull = 0x1c0,
NoTopBottom = 0x038,
NoLeftRight = 0x092,
Full = 0x1ff,
};
Q_DECLARE_FLAGS(RectParts, RectPart);
Q_DECLARE_OPERATORS_FOR_FLAGS(RectParts);
QImage **cornersMask(ImageRoundRadius radius);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *sh = 0);
inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *sh = 0) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, sh);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full);
inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index, shadow, parts);
}
void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &sh, RoundCorners index);
inline void roundShadow(Painter &p, const QRect &rect, const style::color &sh, RoundCorners index) {
return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), sh, index);
void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &shadow, RoundCorners index, RectParts parts = RectPart::Full);
inline void roundShadow(Painter &p, const QRect &rect, const style::color &shadow, RoundCorners index, RectParts parts = RectPart::Full) {
return roundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, index, parts);
}
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius);
inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, ImageRoundRadius radius) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius);
void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, const style::color &bg, ImageRoundRadius radius, RectParts parts = RectPart::Full);
inline void roundRect(Painter &p, const QRect &rect, const style::color &bg, ImageRoundRadius radius, RectParts parts = RectPart::Full) {
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts);
}
struct WallPaper {

View File

@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include <QtCore/QReadWriteLock>
#include "core/build_config.h"
#include "core/stl_subset.h"
#include "core/ordered_set.h"

View File

@ -0,0 +1,82 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <cstdint>
// thanks Chromium
#if defined(__APPLE__)
#define OS_MAC 1
#elif defined(__linux__) // __APPLE__
#define OS_LINUX 1
#elif defined(_WIN32) // __APPLE__ || __linux__
#define OS_WIN 1
#else // __APPLE__ || __linux__ || _WIN32
#error Please add support for your platform in core/build_config.h
#endif // else for __APPLE__ || __linux__ || _WIN32
// For access to standard POSIXish features, use OS_POSIX instead of a
// more specific macro.
#if defined(OS_MAC) || defined(OS_LINUX)
#define OS_POSIX 1
#endif // OS_MAC || OS_LINUX
// Compiler detection.
#if defined(__clang__)
#define COMPILER_CLANG 1
#elif defined(__GNUC__) // __clang__
#define COMPILER_GCC 1
#elif defined(_MSC_VER) // __clang__ || __GNUC__
#define COMPILER_MSVC 1
#else // _MSC_VER || __clang__ || __GNUC__
#error Please add support for your compiler in core/build_config.h
#endif // else for _MSC_VER || __clang__ || __GNUC__
// Processor architecture detection.
#if defined(_M_X64) || defined(__x86_64__)
#define ARCH_CPU_X86_FAMILY 1
#define ARCH_CPU_X86_64 1
#define ARCH_CPU_64_BITS 1
#elif defined(_M_IX86) || defined(__i386__)
#define ARCH_CPU_X86_FAMILY 1
#define ARCH_CPU_X86 1
#define ARCH_CPU_32_BITS 1
#else
#error Please add support for your architecture in core/build_config.h
#endif
#if defined(COMPILER_GCC) || defined(COMPILER_CLANG)
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
#define WARN_UNUSED_RESULT
#endif
#if defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#else
#define FORCE_INLINE inline
#endif
#include <climits>
static_assert(CHAR_BIT == 8, "Not supported char size.");

View File

@ -208,7 +208,8 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh,
auto frame = frameToShow();
t_assert(frame != nullptr);
if (ms) {
auto shouldBePaused = !ms;
if (!shouldBePaused) {
frame->displayed.storeRelease(1);
if (_autoPausedGif.loadAcquire()) {
_autoPausedGif.storeRelease(0);
@ -218,10 +219,10 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh,
}
}
} else {
frame->displayed.storeRelease(-1); // displayed, but should be paused
frame->displayed.storeRelease(-1);
}
int32 factor(cIntRetinaFactor());
auto factor = cIntRetinaFactor();
if (frame->pix.width() == outerw * factor && frame->pix.height() == outerh * factor) {
moveToNextShow();
return frame->pix;

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/twidget.h"
#include "ui/effects/rect_shadow.h"
#include "ui/abstract_button.h"
#include "ui/effects/panel_animation.h"
namespace InlineBots {
namespace Layout {
@ -60,7 +61,6 @@ class EmojiColorPicker : public TWidget {
Q_OBJECT
public:
EmojiColorPicker();
void showEmoji(uint32 code);
@ -345,6 +345,7 @@ private:
void appendSet(Sets &to, uint64 setId, AppendSkip skip);
void selectEmoji(EmojiPtr emoji);
int stickersLeft() const;
QRect stickerRect(int tab, int sel);
int32 _maxHeight;
@ -510,8 +511,6 @@ public:
return _hiding || _hideTimer.isActive();
}
void step_appearance(float64 ms, bool timer);
void step_slide(float64 ms, bool timer);
void step_icons(uint64 ms, bool timer);
bool eventFilter(QObject *obj, QEvent *e);
@ -535,13 +534,12 @@ public:
bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
return s_inner.inlineResultsShown();
}
void showAnimated();
void setOrigin(Ui::PanelAnimation::Origin origin);
void showAnimated(Ui::PanelAnimation::Origin origin);
void hideAnimated();
~EmojiPan();
public slots:
void refreshStickers();
@ -549,8 +547,6 @@ private slots:
void hideByTimerOrLeave();
void refreshSavedGifs();
void hideFinish();
void onWndActiveChanged();
void onScrollEmoji();
@ -581,6 +577,33 @@ signals:
void updateStickers();
private:
void paintContent(Painter &p);
void performSwitch();
style::margins innerPadding() const;
// Rounded rect which has shadow around it.
QRect innerRect() const;
// Inner rect with removed st::buttonRadius from top and bottom.
// This one is allowed to be not rounded.
QRect horizontalRect() const;
// Inner rect with removed st::buttonRadius from left and right.
// This one is allowed to be not rounded.
QRect verticalRect() const;
QImage grabForPanelAnimation();
void startShowAnimation();
void startOpacityAnimation(bool hiding);
void prepareCache();
class Container;
void opacityAnimationCallback();
void hideFinished();
void showStarted();
bool preventAutoHide() const;
void installSetDone(const MTPmessages_StickerSetInstallResult &result);
bool installSetFail(uint64 setId, const RPCError &error);
@ -599,7 +622,6 @@ private:
void updateContentHeight();
void leaveToChildEvent(QEvent *e, QWidget *child);
void prepareShowHideCache();
void updateSelected();
void updateIcons();
@ -614,15 +636,20 @@ private:
bool _horizontal = false;
int32 _width, _height, _bottom;
Ui::PanelAnimation::Origin _origin = Ui::PanelAnimation::Origin::BottomRight;
std_::unique_ptr<Ui::PanelAnimation> _showAnimation;
FloatAnimation _a_show;
bool _hiding = false;
QPixmap _cache;
anim::fvalue a_opacity = { 0. };
Animation _a_appearance;
FloatAnimation _a_opacity;
QTimer _hideTimer;
bool _inPanelGrab = false;
Ui::RectShadow _shadow;
class SlideAnimation;
std_::unique_ptr<SlideAnimation> _slideAnimation;
FloatAnimation _a_slide;
ChildWidget<Ui::IconButton> _recent;
ChildWidget<Ui::IconButton> _people;
@ -651,12 +678,8 @@ private:
anim::ivalue _iconSelX = { 0, 0 };
uint64 _iconsStartAnim = 0;
bool _stickersShown = false;
bool _emojiShown = true;
bool _shownFromInlineQuery = false;
QPixmap _fromCache, _toCache;
anim::ivalue a_fromCoord, a_toCoord;
anim::fvalue a_fromAlpha, a_toAlpha;
Animation _a_slide;
ScrollArea e_scroll;
internal::EmojiPanInner e_inner;

View File

@ -117,12 +117,18 @@ emojiCategoryTravel: IconButton(emojiCategory) { icon: emojiTravel; }
emojiCategoryObjects: IconButton(emojiCategory) { icon: emojiObjects; }
emojiCategorySymbols: IconButton(emojiCategory) { icon: emojiSymbols; }
emojiPanAnimation: PanelAnimation(defaultPanelAnimation) {
fadeBg: emojiPanBg;
}
emojiPanPadding: 12px;
emojiPanSize: size(45px, 41px);
emojiPanWidth: 345px;
emojiPanMaxHeight: 366px;
emojiPanShowDuration: 200;
emojiPanDuration: 200;
emojiPanHover: #f0f4f7;
emojiPanSlideDuration: 200;
emojiPanSlideDelta: 0; // between hide start and show start
emojiPanHeader: 42px;
emojiPanHeaderFont: semiboldFont;

View File

@ -213,7 +213,110 @@ void startManager();
void stopManager();
void registerClipManager(Media::Clip::Manager *manager);
inline uint64 shifted(uint32 components) {
FORCE_INLINE int interpolate(int a, int b, float64 b_ratio) {
return qRound(a + float64(b - a) * b_ratio);
}
#ifdef ARCH_CPU_32_BITS
#define SHIFTED_USE_32BIT
#endif // ARCH_CPU_32_BITS
#ifdef SHIFTED_USE_32BIT
using ShiftedMultiplier = uint32;
struct Shifted {
Shifted() = default;
Shifted(uint32 low, uint32 high) : low(low), high(high) {
}
uint32 low = 0;
uint32 high = 0;
};
FORCE_INLINE Shifted operator+(Shifted a, Shifted b) {
return Shifted(a.low + b.low, a.high + b.high);
}
FORCE_INLINE Shifted operator*(Shifted shifted, ShiftedMultiplier multiplier) {
return Shifted(shifted.low * multiplier, shifted.high * multiplier);
}
FORCE_INLINE Shifted operator*(ShiftedMultiplier multiplier, Shifted shifted) {
return Shifted(shifted.low * multiplier, shifted.high * multiplier);
}
FORCE_INLINE Shifted shifted(uint32 components) {
return Shifted(
(components & 0x000000FFU) | ((components & 0x0000FF00U) << 8),
((components & 0x00FF0000U) >> 16) | ((components & 0xFF000000U) >> 8));
}
FORCE_INLINE uint32 unshifted(Shifted components) {
return ((components.low & 0x0000FF00U) >> 8)
| ((components.low & 0xFF000000U) >> 16)
| ((components.high & 0x0000FF00U) << 8)
| (components.high & 0xFF000000U);
}
FORCE_INLINE Shifted reshifted(Shifted components) {
return Shifted((components.low >> 8) & 0x00FF00FFU, (components.high >> 8) & 0x00FF00FFU);
}
FORCE_INLINE Shifted shifted(QColor color) {
// Make it premultiplied.
auto alpha = static_cast<uint32>((color.alpha() & 0xFF) + 1);
auto components = Shifted(static_cast<uint32>(color.blue() & 0xFF) | (static_cast<uint32>(color.green() & 0xFF) << 16),
static_cast<uint32>(color.red() & 0xFF) | (static_cast<uint32>(255) << 16));
return reshifted(components * alpha);
}
FORCE_INLINE uint32 getAlpha(Shifted components) {
return (components.high & 0x00FF0000U) >> 16;
}
FORCE_INLINE Shifted non_premultiplied(QColor color) {
return Shifted(static_cast<uint32>(color.blue() & 0xFF) | (static_cast<uint32>(color.green() & 0xFF) << 16),
static_cast<uint32>(color.red() & 0xFF) | (static_cast<uint32>(color.alpha() & 0xFF) << 16));
}
FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) {
auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1;
auto aOpacity = (256 - bOpacity);
auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity);
return {
static_cast<int>((components.high >> 8) & 0xFF),
static_cast<int>((components.low >> 24) & 0xFF),
static_cast<int>((components.low >> 8) & 0xFF),
static_cast<int>((components.high >> 24) & 0xFF),
};
}
#else // SHIFTED_USE_32BIT
using ShiftedMultiplier = uint64;
struct Shifted {
Shifted() = default;
Shifted(uint32 value) : value(value) {
}
Shifted(uint64 value) : value(value) {
}
uint64 value = 0;
};
FORCE_INLINE Shifted operator+(Shifted a, Shifted b) {
return Shifted(a.value + b.value);
}
FORCE_INLINE Shifted operator*(Shifted shifted, ShiftedMultiplier multiplier) {
return Shifted(shifted.value * multiplier);
}
FORCE_INLINE Shifted operator*(ShiftedMultiplier multiplier, Shifted shifted) {
return Shifted(shifted.value * multiplier);
}
FORCE_INLINE Shifted shifted(uint32 components) {
auto wide = static_cast<uint64>(components);
return (wide & 0x00000000000000FFULL)
| ((wide & 0x000000000000FF00ULL) << 8)
@ -221,82 +324,93 @@ inline uint64 shifted(uint32 components) {
| ((wide & 0x00000000FF000000ULL) << 24);
}
inline uint32 unshifted(uint64 components) {
return static_cast<uint32>((components & 0x000000000000FF00ULL) >> 8)
| static_cast<uint32>((components & 0x00000000FF000000ULL) >> 16)
| static_cast<uint32>((components & 0x0000FF0000000000ULL) >> 24)
| static_cast<uint32>((components & 0xFF00000000000000ULL) >> 32);
FORCE_INLINE uint32 unshifted(Shifted components) {
return static_cast<uint32>((components.value & 0x000000000000FF00ULL) >> 8)
| static_cast<uint32>((components.value & 0x00000000FF000000ULL) >> 16)
| static_cast<uint32>((components.value & 0x0000FF0000000000ULL) >> 24)
| static_cast<uint32>((components.value & 0xFF00000000000000ULL) >> 32);
}
inline uint64 reshifted(uint64 components) {
return (components >> 8) & 0x00FF00FF00FF00FFULL;
FORCE_INLINE Shifted reshifted(Shifted components) {
return (components.value >> 8) & 0x00FF00FF00FF00FFULL;
}
inline int interpolate(int a, int b, float64 b_ratio) {
return qRound(a + float64(b - a) * b_ratio);
FORCE_INLINE Shifted shifted(QColor color) {
// Make it premultiplied.
auto alpha = static_cast<uint64>((color.alpha() & 0xFF) + 1);
auto components = static_cast<uint64>(color.blue() & 0xFF)
| (static_cast<uint64>(color.green() & 0xFF) << 16)
| (static_cast<uint64>(color.red() & 0xFF) << 32)
| (static_cast<uint64>(255) << 48);
return reshifted(components * alpha);
}
inline QColor color(QColor a, QColor b, float64 b_ratio) {
FORCE_INLINE uint32 getAlpha(Shifted components) {
return (components.value & 0x00FF000000000000ULL) >> 48;
}
FORCE_INLINE Shifted non_premultiplied(QColor color) {
return static_cast<uint64>(color.blue() & 0xFF)
| (static_cast<uint64>(color.green() & 0xFF) << 16)
| (static_cast<uint64>(color.red() & 0xFF) << 32)
| (static_cast<uint64>(color.alpha() & 0xFF) << 48);
}
FORCE_INLINE QColor color(QColor a, QColor b, float64 b_ratio) {
auto bOpacity = snap(interpolate(0, 255, b_ratio), 0, 255) + 1;
auto aOpacity = (256 - bOpacity);
auto bBits = static_cast<uint64>(b.alpha() & 0xFF)
| (static_cast<uint64>(b.red() & 0xFF) << 16)
| (static_cast<uint64>(b.green() & 0xFF) << 32)
| (static_cast<uint64>(b.blue() & 0xFF) << 48);
auto aBits = static_cast<uint64>(a.alpha() & 0xFF)
| (static_cast<uint64>(a.red() & 0xFF) << 16)
| (static_cast<uint64>(a.green() & 0xFF) << 32)
| (static_cast<uint64>(a.blue() & 0xFF) << 48);
auto resultBits = (aBits * aOpacity + bBits * bOpacity) >> 8;
auto components = (non_premultiplied(a) * aOpacity + non_premultiplied(b) * bOpacity);
return {
static_cast<int>((resultBits >> 16) & 0xFF),
static_cast<int>((resultBits >> 32) & 0xFF),
static_cast<int>((resultBits >> 48) & 0xFF),
static_cast<int>(resultBits & 0xFF),
static_cast<int>((components.value >> 40) & 0xFF),
static_cast<int>((components.value >> 24) & 0xFF),
static_cast<int>((components.value >> 8) & 0xFF),
static_cast<int>((components.value >> 56) & 0xFF),
};
}
inline QColor color(const style::color &a, QColor b, float64 b_ratio) {
#endif // SHIFTED_USE_32BIT
FORCE_INLINE QColor color(const style::color &a, QColor b, float64 b_ratio) {
return color(a->c, b, b_ratio);
}
inline QColor color(QColor a, const style::color &b, float64 b_ratio) {
FORCE_INLINE QColor color(QColor a, const style::color &b, float64 b_ratio) {
return color(a, b->c, b_ratio);
}
inline QColor color(const style::color &a, const style::color &b, float64 b_ratio) {
FORCE_INLINE QColor color(const style::color &a, const style::color &b, float64 b_ratio) {
return color(a->c, b->c, b_ratio);
}
inline QPen pen(QColor a, QColor b, float64 b_ratio) {
FORCE_INLINE QPen pen(QColor a, QColor b, float64 b_ratio) {
return color(a, b, b_ratio);
}
inline QPen pen(const style::color &a, QColor b, float64 b_ratio) {
FORCE_INLINE QPen pen(const style::color &a, QColor b, float64 b_ratio) {
return (b_ratio > 0) ? pen(a->c, b, b_ratio) : a;
}
inline QPen pen(QColor a, const style::color &b, float64 b_ratio) {
FORCE_INLINE QPen pen(QColor a, const style::color &b, float64 b_ratio) {
return (b_ratio < 1) ? pen(a, b->c, b_ratio) : b;
}
inline QPen pen(const style::color &a, const style::color &b, float64 b_ratio) {
FORCE_INLINE QPen pen(const style::color &a, const style::color &b, float64 b_ratio) {
return (b_ratio > 0) ? ((b_ratio < 1) ? pen(a->c, b->c, b_ratio) : b) : a;
}
inline QBrush brush(QColor a, QColor b, float64 b_ratio) {
FORCE_INLINE QBrush brush(QColor a, QColor b, float64 b_ratio) {
return color(a, b, b_ratio);
}
inline QBrush brush(const style::color &a, QColor b, float64 b_ratio) {
FORCE_INLINE QBrush brush(const style::color &a, QColor b, float64 b_ratio) {
return (b_ratio > 0) ? brush(a->c, b, b_ratio) : a;
}
inline QBrush brush(QColor a, const style::color &b, float64 b_ratio) {
FORCE_INLINE QBrush brush(QColor a, const style::color &b, float64 b_ratio) {
return (b_ratio < 1) ? brush(a, b->c, b_ratio) : b;
}
inline QBrush brush(const style::color &a, const style::color &b, float64 b_ratio) {
FORCE_INLINE QBrush brush(const style::color &a, const style::color &b, float64 b_ratio) {
return (b_ratio > 0) ? ((b_ratio < 1) ? brush(a->c, b->c, b_ratio) : b) : a;
}

View File

@ -23,64 +23,31 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) {
void RoundShadowAnimation::start(int frameWidth, int frameHeight, float64 devicePixelRatio) {
t_assert(!started());
_finalImage = std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied);
t_assert(!_finalImage.isNull());
_finalWidth = _finalImage.width();
_finalHeight = _finalImage.height();
_finalInnerLeft = inner.x();
_finalInnerTop = inner.y();
_finalInnerWidth = inner.width();
_finalInnerHeight = inner.height();
_finalInnerRight = _finalInnerLeft + _finalInnerWidth;
_finalInnerBottom = _finalInnerTop + _finalInnerHeight;
t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner));
setStartWidth();
setStartHeight();
setStartAlpha();
setStartFadeTop();
createFadeMask();
setWidthDuration();
setHeightDuration();
setAlphaDuration();
setShadow();
auto checkCorner = [this, inner](Corner &corner) {
if (!corner.valid()) return;
if ((_startWidth >= 0 && _startWidth < _finalWidth)
|| (_startHeight >= 0 && _startHeight < _finalHeight)) {
t_assert(corner.width <= inner.width());
t_assert(corner.height <= inner.height());
}
};
checkCorner(_topLeft);
checkCorner(_topRight);
checkCorner(_bottomLeft);
checkCorner(_bottomRight);
_finalInts = reinterpret_cast<const uint32*>(_finalImage.constBits());
_finalIntsPerLine = (_finalImage.bytesPerLine() >> 2);
_finalIntsPerLineAdded = _finalIntsPerLine - _finalWidth;
t_assert(_finalImage.depth() == static_cast<int>(sizeof(uint32) << 3));
t_assert(_finalImage.bytesPerLine() == (_finalIntsPerLine << 2));
t_assert(_finalIntsPerLineAdded >= 0);
_frameWidth = frameWidth;
_frameHeight = frameHeight;
_frame = QImage(_frameWidth, _frameHeight, QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(devicePixelRatio);
_frameIntsPerLine = (_frame.bytesPerLine() >> 2);
_frameInts = reinterpret_cast<uint32*>(_frame.bits());
_frameIntsPerLineAdded = _frameIntsPerLine - _frameWidth;
t_assert(_frame.depth() == static_cast<int>(sizeof(uint32) << 3));
t_assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2));
t_assert(_frameIntsPerLineAdded >= 0);
}
void PanelAnimation::setShadow() {
if (_skipShadow) return;
_shadow.extend = _st.shadow.extend * cIntRetinaFactor();
_shadow.left = cloneImage(_st.shadow.left);
void RoundShadowAnimation::setShadow(const style::Shadow &st) {
_shadow.extend = st.extend * cIntRetinaFactor();
_shadow.left = cloneImage(st.left);
if (_shadow.valid()) {
_shadow.topLeft = cloneImage(_st.shadow.topLeft);
_shadow.top = cloneImage(_st.shadow.top);
_shadow.topRight = cloneImage(_st.shadow.topRight);
_shadow.right = cloneImage(_st.shadow.right);
_shadow.bottomRight = cloneImage(_st.shadow.bottomRight);
_shadow.bottom = cloneImage(_st.shadow.bottom);
_shadow.bottomLeft = cloneImage(_st.shadow.bottomLeft);
_shadow.topLeft = cloneImage(st.topLeft);
_shadow.top = cloneImage(st.top);
_shadow.topRight = cloneImage(st.topRight);
_shadow.right = cloneImage(st.right);
_shadow.bottomRight = cloneImage(st.bottomRight);
_shadow.bottom = cloneImage(st.bottom);
_shadow.bottomLeft = cloneImage(st.bottomLeft);
t_assert(!_shadow.topLeft.isNull()
&& !_shadow.top.isNull()
&& !_shadow.topRight.isNull()
@ -99,6 +66,214 @@ void PanelAnimation::setShadow() {
}
}
void RoundShadowAnimation::setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight) {
setCornerMask(_topLeft, std_::move(topLeft));
setCornerMask(_topRight, std_::move(topRight));
setCornerMask(_bottomLeft, std_::move(bottomLeft));
setCornerMask(_bottomRight, std_::move(bottomRight));
}
void RoundShadowAnimation::setCornerMask(Corner &corner, QImage &&image) {
t_assert(!started());
corner.image = std_::move(image);
if (corner.valid()) {
corner.width = corner.image.width();
corner.height = corner.image.height();
corner.bytes = corner.image.constBits();
corner.bytesPerPixel = (corner.image.depth() >> 3);
corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel;
t_assert(corner.image.depth() == (corner.bytesPerPixel << 3));
t_assert(corner.bytesPerLineAdded >= 0);
} else {
corner.width = corner.height = 0;
}
}
QImage RoundShadowAnimation::cloneImage(const style::icon &source) {
if (source.empty()) return QImage();
auto result = QImage(source.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
{
Painter p(&result);
source.paint(p, 0, 0, source.width());
}
return std_::move(result);
}
void RoundShadowAnimation::paintCorner(Corner &corner, int left, int top) {
auto mask = corner.bytes;
auto bytesPerPixel = corner.bytesPerPixel;
auto bytesPerLineAdded = corner.bytesPerLineAdded;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width;
for (auto y = 0; y != corner.height; ++y) {
for (auto x = 0; x != corner.width; ++x) {
auto alpha = static_cast<uint32>(*mask) + 1;
*frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha);
++frameInts;
mask += bytesPerPixel;
}
frameInts += frameIntsPerLineAdd;
mask += bytesPerLineAdded;
}
}
void RoundShadowAnimation::paintShadow(int left, int top, int right, int bottom) {
paintShadowCorner(left, top, _shadow.topLeft);
paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight);
paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight);
paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft);
paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left);
paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right);
paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top);
paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom);
}
void RoundShadowAnimation::paintShadowCorner(int left, int top, const QImage &image) {
auto imageWidth = image.width();
auto imageHeight = image.height();
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
auto imageIntsPerLine = (image.bytesPerLine() >> 2);
auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth;
if (left < 0) {
auto shift = -base::take(left);
imageWidth -= shift;
imageInts += shift;
}
if (top < 0) {
auto shift = -base::take(top);
imageHeight -= shift;
imageInts += shift * imageIntsPerLine;
}
if (left + imageWidth > _frameWidth) {
imageWidth = _frameWidth - left;
}
if (top + imageHeight > _frameHeight) {
imageHeight = _frameHeight - top;
}
if (imageWidth < 0 || imageHeight < 0) return;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth;
for (auto y = 0; y != imageHeight; ++y) {
for (auto x = 0; x != imageWidth; ++x) {
auto source = *frameInts;
auto shadowAlpha = qMax(_frameAlpha - int(source >> 24), 0);
*frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha);
++frameInts;
++imageInts;
}
frameInts += frameIntsPerLineAdd;
imageInts += imageIntsPerLineAdded;
}
}
void RoundShadowAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) {
auto imageWidth = image.width();
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
if (left < 0) {
auto shift = -base::take(left);
imageWidth -= shift;
imageInts += shift;
}
if (top < 0) top = 0;
accumulate_min(bottom, _frameHeight);
accumulate_min(imageWidth, _frameWidth - left);
if (imageWidth < 0 || bottom <= top) return;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth;
for (auto y = top; y != bottom; ++y) {
for (auto x = 0; x != imageWidth; ++x) {
auto source = *frameInts;
auto shadowAlpha = _frameAlpha - (source >> 24);
*frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha);
++frameInts;
++imageInts;
}
frameInts += frameIntsPerLineAdd;
imageInts -= imageWidth;
}
}
void RoundShadowAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) {
auto imageHeight = image.height();
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
auto imageIntsPerLine = (image.bytesPerLine() >> 2);
if (top < 0) {
auto shift = -base::take(top);
imageHeight -= shift;
imageInts += shift * imageIntsPerLine;
}
if (left < 0) left = 0;
accumulate_min(right, _frameWidth);
accumulate_min(imageHeight, _frameHeight - top);
if (imageHeight < 0 || right <= left) return;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left);
for (auto y = 0; y != imageHeight; ++y) {
auto imagePattern = anim::shifted(*imageInts);
for (auto x = left; x != right; ++x) {
auto source = *frameInts;
auto shadowAlpha = _frameAlpha - (source >> 24);
*frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha);
++frameInts;
}
frameInts += frameIntsPerLineAdd;
imageInts += imageIntsPerLine;
}
}
void PanelAnimation::setFinalImage(QImage &&finalImage, QRect inner) {
t_assert(!started());
_finalImage = QPixmap::fromImage(std_::move(finalImage).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly);
t_assert(!_finalImage.isNull());
_finalWidth = _finalImage.width();
_finalHeight = _finalImage.height();
t_assert(!(_finalWidth % cIntRetinaFactor()));
t_assert(!(_finalHeight % cIntRetinaFactor()));
_finalInnerLeft = inner.x();
_finalInnerTop = inner.y();
_finalInnerWidth = inner.width();
_finalInnerHeight = inner.height();
t_assert(!(_finalInnerLeft % cIntRetinaFactor()));
t_assert(!(_finalInnerTop % cIntRetinaFactor()));
t_assert(!(_finalInnerWidth % cIntRetinaFactor()));
t_assert(!(_finalInnerHeight % cIntRetinaFactor()));
_finalInnerRight = _finalInnerLeft + _finalInnerWidth;
_finalInnerBottom = _finalInnerTop + _finalInnerHeight;
t_assert(QRect(0, 0, _finalWidth, _finalHeight).contains(inner));
setStartWidth();
setStartHeight();
setStartAlpha();
setStartFadeTop();
createFadeMask();
setWidthDuration();
setHeightDuration();
setAlphaDuration();
if (!_skipShadow) {
setShadow(_st.shadow);
}
auto checkCorner = [this, inner](Corner &corner) {
if (!corner.valid()) return;
if ((_startWidth >= 0 && _startWidth < _finalWidth)
|| (_startHeight >= 0 && _startHeight < _finalHeight)) {
t_assert(corner.width <= inner.width());
t_assert(corner.height <= inner.height());
}
};
checkCorner(_topLeft);
checkCorner(_topRight);
checkCorner(_bottomLeft);
checkCorner(_bottomRight);
}
void PanelAnimation::setStartWidth() {
_startWidth = qRound(_st.startWidth * _finalInnerWidth);
if (_startWidth >= 0) t_assert(_startWidth <= _finalInnerWidth);
@ -120,30 +295,30 @@ void PanelAnimation::setStartFadeTop() {
void PanelAnimation::createFadeMask() {
auto resultHeight = qRound(_finalImage.height() * _st.fadeHeight);
if (auto remove = (resultHeight % cIntRetinaFactor())) {
resultHeight -= remove;
}
auto finalAlpha = qRound(_st.fadeOpacity * 255);
t_assert(finalAlpha >= 0 && finalAlpha < 256);
auto result = QImage(1, resultHeight, QImage::Format_ARGB32_Premultiplied);
auto result = QImage(cIntRetinaFactor(), resultHeight, QImage::Format_ARGB32_Premultiplied);
auto ints = reinterpret_cast<uint32*>(result.bits());
auto intsPerLine = (result.bytesPerLine() >> 2);
auto intsPerLineAdded = (result.bytesPerLine() >> 2) - cIntRetinaFactor();
auto up = (_origin == PanelAnimation::Origin::BottomLeft || _origin == PanelAnimation::Origin::BottomRight);
auto from = up ? resultHeight : 0, to = resultHeight - from, delta = up ? -1 : 1;
auto fadeFirstAlpha = up ? (finalAlpha + 1) : 1;
auto fadeLastAlpha = up ? 1 : (finalAlpha + 1);
_fadeFirst = QBrush(QColor(_st.fadeBg->c.red(), _st.fadeBg->c.green(), _st.fadeBg->c.blue(), (_st.fadeBg->c.alpha() * fadeFirstAlpha) >> 8));
_fadeLast = QBrush(QColor(_st.fadeBg->c.red(), _st.fadeBg->c.green(), _st.fadeBg->c.blue(), (_st.fadeBg->c.alpha() * fadeLastAlpha) >> 8));
for (auto y = from; y != to; y += delta) {
auto alpha = static_cast<uint32>(finalAlpha * y) / resultHeight;
*ints = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha;
ints += intsPerLine;
auto value = (0xFFU << 24) | (alpha << 16) | (alpha << 8) | alpha;
for (auto x = 0; x != cIntRetinaFactor(); ++x) {
*ints++ = value;
}
ints += intsPerLineAdded;
}
_fadeMask = style::colorizeImage(result, _st.fadeBg);
_fadeMask = QPixmap::fromImage(style::colorizeImage(result, _st.fadeBg), Qt::ColorOnly);
_fadeHeight = _fadeMask.height();
_fadeInts = reinterpret_cast<const uint32*>(_fadeMask.constBits());
_fadeIntsPerLine = (_fadeMask.bytesPerLine() >> 2);
t_assert(_fadeMask.bytesPerLine() == (_fadeIntsPerLine << 2));
}
void PanelAnimation::setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight) {
setCornerMask(_topLeft, std_::move(topLeft));
setCornerMask(_topRight, std_::move(topRight));
setCornerMask(_bottomLeft, std_::move(bottomLeft));
setCornerMask(_bottomRight, std_::move(bottomRight));
}
void PanelAnimation::setSkipShadow(bool skipShadow) {
@ -151,41 +326,6 @@ void PanelAnimation::setSkipShadow(bool skipShadow) {
_skipShadow = skipShadow;
}
void PanelAnimation::setCornerMask(Corner &corner, QImage &&image) {
t_assert(!started());
corner.image = std_::move(image);
if (corner.valid()) {
corner.width = corner.image.width();
corner.height = corner.image.height();
corner.bytes = corner.image.constBits();
corner.bytesPerPixel = (corner.image.depth() >> 3);
corner.bytesPerLineAdded = corner.image.bytesPerLine() - corner.width * corner.bytesPerPixel;
t_assert(corner.image.depth() == (corner.bytesPerPixel << 3));
t_assert(corner.bytesPerLineAdded >= 0);
if (_startWidth >= 0) t_assert(corner.width <= _startWidth);
if (_startHeight >= 0) t_assert(corner.height <= _startHeight);
if (!_finalImage.isNull()) {
t_assert(corner.width <= _finalInnerWidth);
t_assert(corner.height <= _finalInnerHeight);
}
} else {
corner.width = corner.height = 0;
}
}
QImage PanelAnimation::cloneImage(const style::icon &source) {
if (source.empty()) return QImage();
auto result = QImage(source.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(cRetinaFactor());
result.fill(Qt::transparent);
{
Painter p(&result);
source.paint(p, 0, 0, source.width());
}
return std_::move(result);
}
void PanelAnimation::setWidthDuration() {
_widthDuration = _st.widthDuration;
t_assert(_widthDuration >= 0.);
@ -207,35 +347,46 @@ void PanelAnimation::setAlphaDuration() {
}
void PanelAnimation::start() {
t_assert(!started());
t_assert(!_finalImage.isNull());
_frame = QImage(_finalWidth, _finalHeight, QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(_finalImage.devicePixelRatio());
_frameIntsPerLine = (_frame.bytesPerLine() >> 2);
_frameInts = reinterpret_cast<uint32*>(_frame.bits());
_frameIntsPerLineAdded = _frameIntsPerLine - _finalWidth;
t_assert(_frame.depth() == static_cast<int>(sizeof(uint32) << 3));
t_assert(_frame.bytesPerLine() == (_frameIntsPerLine << 2));
t_assert(_frameIntsPerLineAdded >= 0);
RoundShadowAnimation::start(_finalWidth, _finalHeight, _finalImage.devicePixelRatio());
auto checkCorner = [this](const Corner &corner) {
if (!corner.valid()) return;
if (_startWidth >= 0) t_assert(corner.width <= _startWidth);
if (_startHeight >= 0) t_assert(corner.height <= _startHeight);
t_assert(corner.width <= _finalInnerWidth);
t_assert(corner.height <= _finalInnerHeight);
};
checkCorner(_topLeft);
checkCorner(_topRight);
checkCorner(_bottomLeft);
checkCorner(_bottomRight);
}
const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) {
void PanelAnimation::paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity) {
t_assert(started());
t_assert(dt >= 0.);
auto &transition = anim::easeOutCirc;
constexpr auto finalAlpha = 256;
auto alpha = (dt >= _alphaDuration) ? finalAlpha : anim::interpolate(_startAlpha + 1, finalAlpha, transition(1., dt / _alphaDuration));
_frameAlpha = anim::interpolate(0, alpha, opacity);
if (dt < _alphaDuration) opacity *= transition(1., dt / _alphaDuration);
_frameAlpha = anim::interpolate(1, 256, opacity);
auto frameWidth = (_startWidth < 0 || dt >= _widthDuration) ? _finalInnerWidth : anim::interpolate(_startWidth, _finalInnerWidth, transition(1., dt / _widthDuration));
auto frameHeight = (_startHeight < 0 || dt >= _heightDuration) ? _finalInnerHeight : anim::interpolate(_startHeight, _finalInnerHeight, transition(1., dt / _heightDuration));
if (auto decrease = (frameWidth % cIntRetinaFactor())) {
frameWidth -= decrease;
}
if (auto decrease = (frameHeight % cIntRetinaFactor())) {
frameHeight -= decrease;
}
auto frameLeft = (_origin == Origin::TopLeft || _origin == Origin::BottomLeft) ? _finalInnerLeft : (_finalInnerRight - frameWidth);
auto frameTop = (_origin == Origin::TopLeft || _origin == Origin::TopRight) ? _finalInnerTop : (_finalInnerBottom - frameHeight);
auto frameRight = frameLeft + frameWidth;
auto frameBottom = frameTop + frameHeight;
auto fadeTop = (_fadeHeight > 0) ? snap(anim::interpolate(_startFadeTop, _finalInnerHeight, transition(1., dt)), 0, frameHeight) : frameHeight;
if (auto decrease = (fadeTop % cIntRetinaFactor())) {
fadeTop -= decrease;
}
auto fadeBottom = (fadeTop < frameHeight) ? qMin(fadeTop + _fadeHeight, frameHeight) : frameHeight;
auto fadeSkipLines = 0;
if (_origin == Origin::BottomLeft || _origin == Origin::BottomRight) {
@ -247,97 +398,97 @@ const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) {
fadeTop += frameTop;
fadeBottom += frameTop;
auto finalInts = _finalInts + frameLeft + frameTop * _finalIntsPerLine;
if (opacity < 1.) {
_frame.fill(Qt::transparent);
}
{
Painter p(&_frame);
p.setOpacity(opacity);
auto painterFrameLeft = frameLeft / cIntRetinaFactor();
auto painterFrameTop = frameTop / cIntRetinaFactor();
auto painterFadeBottom = fadeBottom / cIntRetinaFactor();
p.drawPixmap(painterFrameLeft, painterFrameTop, _finalImage, frameLeft, frameTop, frameWidth, frameHeight);
if (_fadeHeight) {
if (frameTop != fadeTop) {
p.fillRect(painterFrameLeft, painterFrameTop, frameWidth, fadeTop - frameTop, _fadeFirst);
}
if (fadeTop != fadeBottom) {
auto painterFadeTop = fadeTop / cIntRetinaFactor();
auto painterFrameWidth = frameWidth / cIntRetinaFactor();
auto painterFrameHeight = frameHeight / cIntRetinaFactor();
p.drawPixmap(painterFrameLeft, painterFadeTop, painterFrameWidth, painterFadeBottom - painterFadeTop, _fadeMask, 0, fadeSkipLines, cIntRetinaFactor(), fadeBottom - fadeTop);
}
if (fadeBottom != frameBottom) {
p.fillRect(painterFrameLeft, painterFadeBottom, frameWidth, frameBottom - fadeBottom, _fadeLast);
}
}
}
auto frameInts = _frameInts + frameLeft + frameTop * _frameIntsPerLine;
auto finalIntsPerLineAdd = (_finalWidth - frameWidth) + _finalIntsPerLineAdded;
auto frameIntsPerLineAdd = (_finalWidth - frameWidth) + _frameIntsPerLineAdded;
// Draw frameWidth x fadeTop with fade first color.
auto fadeInts = _fadeInts + fadeSkipLines * _fadeIntsPerLine;
auto fadeWithMasterAlpha = [this](uint32 fade) {
auto fadeAlphaAddition = (256 - (fade >> 24));
auto fadePattern = anim::shifted(fade);
return [this, fadeAlphaAddition, fadePattern](uint32 source) {
auto sourceAlpha = (source >> 24) + 1;
auto sourcePattern = anim::shifted(source);
auto mixedPattern = anim::reshifted(fadePattern * sourceAlpha + sourcePattern * fadeAlphaAddition);
return anim::unshifted(mixedPattern * _frameAlpha);
};
};
if (frameTop != fadeTop) {
// Take the fade components from the first line of the fade mask.
auto withMasterAlpha = fadeWithMasterAlpha(_fadeInts ? *_fadeInts : 0);
for (auto y = frameTop; y != fadeTop; ++y) {
for (auto x = frameLeft; x != frameRight; ++x) {
*frameInts++ = withMasterAlpha(*finalInts++);
}
finalInts += finalIntsPerLineAdd;
frameInts += frameIntsPerLineAdd;
}
}
// Draw frameWidth x (fadeBottom - fadeTop) with fade gradient.
for (auto y = fadeTop; y != fadeBottom; ++y) {
auto withMasterAlpha = fadeWithMasterAlpha(*fadeInts);
for (auto x = frameLeft; x != frameRight; ++x) {
*frameInts++ = withMasterAlpha(*finalInts++);
}
finalInts += finalIntsPerLineAdd;
frameInts += frameIntsPerLineAdd;
fadeInts += _fadeIntsPerLine;
}
// Draw frameWidth x (frameBottom - fadeBottom) with fade final color.
if (fadeBottom != frameBottom) {
// Take the fade components from the last line of the fade mask.
auto withMasterAlpha = fadeWithMasterAlpha(*(fadeInts - _fadeIntsPerLine));
for (auto y = fadeBottom; y != frameBottom; ++y) {
for (auto x = frameLeft; x != frameRight; ++x) {
*frameInts++ = withMasterAlpha(*finalInts++);
}
finalInts += finalIntsPerLineAdd;
frameInts += frameIntsPerLineAdd;
}
}
// Draw corners
auto innerLeft = qMax(_finalInnerLeft, frameLeft);
auto innerTop = qMax(_finalInnerTop, frameTop);
auto innerRight = qMin(_finalInnerRight, frameRight);
auto innerBottom = qMin(_finalInnerBottom, frameBottom);
if (innerLeft != _finalInnerLeft || innerTop != _finalInnerTop) {
paintCorner(_topLeft, innerLeft, innerTop);
}
if (innerRight != _finalInnerRight || innerTop != _finalInnerTop) {
paintCorner(_topRight, innerRight - _topRight.width, innerTop);
}
if (innerLeft != _finalInnerLeft || innerBottom != _finalInnerBottom) {
paintCorner(_bottomLeft, innerLeft, innerBottom - _bottomLeft.height);
}
if (innerRight != _finalInnerRight || innerBottom != _finalInnerBottom) {
paintCorner(_bottomRight, innerRight - _bottomRight.width, innerBottom - _bottomRight.height);
}
paintCorner(_topLeft, frameLeft, frameTop);
paintCorner(_topRight, frameRight - _topRight.width, frameTop);
paintCorner(_bottomLeft, frameLeft, frameBottom - _bottomLeft.height);
paintCorner(_bottomRight, frameRight - _bottomRight.width, frameBottom - _bottomRight.height);
// Fill the rest with transparent
if (frameTop) {
memset(_frameInts, 0, _frameIntsPerLine * frameTop * sizeof(uint32));
// Draw shadow upon the transparent
auto outerLeft = frameLeft;
auto outerTop = frameTop;
auto outerRight = frameRight;
auto outerBottom = frameBottom;
if (_shadow.valid()) {
outerLeft -= _shadow.extend.left();
outerTop -= _shadow.extend.top();
outerRight += _shadow.extend.right();
outerBottom += _shadow.extend.bottom();
}
auto widthLeft = (_finalWidth - frameRight);
if (frameLeft || widthLeft) {
auto frameInts = _frameInts + frameTop * _frameIntsPerLine;
for (auto y = frameTop; y != frameBottom; ++y) {
memset(frameInts, 0, frameLeft * sizeof(uint32));
memset(frameInts + frameLeft + frameWidth, 0, widthLeft * sizeof(uint32));
frameInts += _frameIntsPerLine;
if (cIntRetinaFactor() > 1) {
if (auto skipLeft = (outerLeft % cIntRetinaFactor())) {
outerLeft -= skipLeft;
}
if (auto skipTop = (outerTop % cIntRetinaFactor())) {
outerTop -= skipTop;
}
if (auto skipRight = (outerRight % cIntRetinaFactor())) {
outerRight += (cIntRetinaFactor() - skipRight);
}
if (auto skipBottom = (outerBottom % cIntRetinaFactor())) {
outerBottom += (cIntRetinaFactor() - skipBottom);
}
}
if (auto heightLeft = (_finalHeight - frameBottom)) {
memset(_frameInts + frameBottom * _frameIntsPerLine, 0, _frameIntsPerLine * heightLeft * sizeof(uint32));
if (opacity == 1.) {
// Fill above the frame top with transparent.
auto fillTopInts = (_frameInts + outerTop * _frameIntsPerLine + outerLeft);
auto fillWidth = (outerRight - outerLeft) * sizeof(uint32);
for (auto fillTop = frameTop - outerTop; fillTop != 0; --fillTop) {
memset(fillTopInts, 0, fillWidth);
fillTopInts += _frameIntsPerLine;
}
// Fill to the left and to the right of the frame with transparent.
auto fillLeft = (frameLeft - outerLeft) * sizeof(uint32);
auto fillRight = (outerRight - frameRight) * sizeof(uint32);
if (fillLeft || fillRight) {
auto fillInts = _frameInts + frameTop * _frameIntsPerLine;
for (auto y = frameTop; y != frameBottom; ++y) {
memset(fillInts + outerLeft, 0, fillLeft);
memset(fillInts + frameRight, 0, fillRight);
fillInts += _frameIntsPerLine;
}
}
// Fill below the frame bottom with transparent.
auto fillBottomInts = (_frameInts + frameBottom * _frameIntsPerLine + outerLeft);
for (auto fillBottom = outerBottom - frameBottom; fillBottom != 0; --fillBottom) {
memset(fillBottomInts, 0, fillWidth);
fillBottomInts += _frameIntsPerLine;
}
}
// Draw shadow
if (_shadow.valid()) {
paintShadow(innerLeft, innerTop, innerRight, innerBottom);
paintShadow(outerLeft, outerTop, outerRight, outerBottom);
}
// Debug
@ -353,140 +504,7 @@ const QImage &PanelAnimation::getFrame(float64 dt, float64 opacity) {
// frameInts += _frameIntsPerLineAdded;
//}
return _frame;
}
void PanelAnimation::paintCorner(Corner &corner, int left, int top) {
auto mask = corner.bytes;
auto bytesPerPixel = corner.bytesPerPixel;
auto bytesPerLineAdded = corner.bytesPerLineAdded;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - corner.width;
for (auto y = 0; y != corner.height; ++y) {
for (auto x = 0; x != corner.width; ++x) {
auto alpha = static_cast<uint32>(*mask) + 1;
*frameInts = anim::unshifted(anim::shifted(*frameInts) * alpha);
++frameInts;
mask += bytesPerPixel;
}
frameInts += frameIntsPerLineAdd;
mask += bytesPerLineAdded;
}
}
void PanelAnimation::paintShadow(int left, int top, int right, int bottom) {
left -= _shadow.extend.left();
top -= _shadow.extend.top();
right += _shadow.extend.right();
bottom += _shadow.extend.bottom();
paintShadowCorner(left, top, _shadow.topLeft);
paintShadowCorner(right - _shadow.topRight.width(), top, _shadow.topRight);
paintShadowCorner(right - _shadow.bottomRight.width(), bottom - _shadow.bottomRight.height(), _shadow.bottomRight);
paintShadowCorner(left, bottom - _shadow.bottomLeft.height(), _shadow.bottomLeft);
paintShadowVertical(left, top + _shadow.topLeft.height(), bottom - _shadow.bottomLeft.height(), _shadow.left);
paintShadowVertical(right - _shadow.right.width(), top + _shadow.topRight.height(), bottom - _shadow.bottomRight.height(), _shadow.right);
paintShadowHorizontal(left + _shadow.topLeft.width(), right - _shadow.topRight.width(), top, _shadow.top);
paintShadowHorizontal(left + _shadow.bottomLeft.width(), right - _shadow.bottomRight.width(), bottom - _shadow.bottom.height(), _shadow.bottom);
}
void PanelAnimation::paintShadowCorner(int left, int top, const QImage &image) {
auto imageWidth = image.width();
auto imageHeight = image.height();
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
auto imageIntsPerLine = (image.bytesPerLine() >> 2);
auto imageIntsPerLineAdded = imageIntsPerLine - imageWidth;
if (left < 0) {
auto shift = -base::take(left);
imageWidth -= shift;
imageInts += shift;
}
if (top < 0) {
auto shift = -base::take(top);
imageHeight -= shift;
imageInts += shift * imageIntsPerLine;
}
if (left + imageWidth > _finalWidth) {
imageWidth = _finalWidth - left;
}
if (top + imageHeight > _finalHeight) {
imageHeight = _finalHeight - top;
}
if (imageWidth < 0 || imageHeight < 0) return;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth;
for (auto y = 0; y != imageHeight; ++y) {
for (auto x = 0; x != imageWidth; ++x) {
auto source = *frameInts;
auto shadowAlpha = _frameAlpha - (source >> 24);
*frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha);
++frameInts;
++imageInts;
}
frameInts += frameIntsPerLineAdd;
imageInts += imageIntsPerLineAdded;
}
}
void PanelAnimation::paintShadowVertical(int left, int top, int bottom, const QImage &image) {
auto imageWidth = image.width();
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
if (left < 0) {
auto shift = -base::take(left);
imageWidth -= shift;
imageInts += shift;
}
if (top < 0) top = 0;
if (bottom > _finalHeight) bottom = _finalHeight;
if (left + imageWidth > _finalWidth) {
imageWidth = _finalWidth - left;
}
if (imageWidth < 0 || bottom <= top) return;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - imageWidth;
for (auto y = top; y != bottom; ++y) {
for (auto x = 0; x != imageWidth; ++x) {
auto source = *frameInts;
auto shadowAlpha = _frameAlpha - (source >> 24);
*frameInts = anim::unshifted(anim::shifted(source) * 256 + anim::shifted(*imageInts) * shadowAlpha);
++frameInts;
++imageInts;
}
frameInts += frameIntsPerLineAdd;
imageInts -= imageWidth;
}
}
void PanelAnimation::paintShadowHorizontal(int left, int right, int top, const QImage &image) {
auto imageHeight = image.height();
auto imageInts = reinterpret_cast<const uint32*>(image.constBits());
auto imageIntsPerLine = (image.bytesPerLine() >> 2);
if (top < 0) {
auto shift = -base::take(top);
imageHeight -= shift;
imageInts += shift * imageIntsPerLine;
}
if (left < 0) left = 0;
if (right > _finalWidth) right = _finalWidth;
if (top + imageHeight > _finalHeight) {
imageHeight = _finalHeight - top;
}
if (imageHeight < 0 || right <= left) return;
auto frameInts = _frameInts + top * _frameIntsPerLine + left;
auto frameIntsPerLineAdd = _frameIntsPerLine - (right - left);
for (auto y = 0; y != imageHeight; ++y) {
auto imagePattern = anim::shifted(*imageInts);
for (auto x = left; x != right; ++x) {
auto source = *frameInts;
auto shadowAlpha = _frameAlpha - (source >> 24);
*frameInts = anim::unshifted(anim::shifted(source) * 256 + imagePattern * shadowAlpha);
++frameInts;
}
frameInts += frameIntsPerLineAdd;
imageInts += imageIntsPerLine;
}
p.drawImage(rtlpoint(x + (outerLeft / cIntRetinaFactor()), y + (outerTop / cIntRetinaFactor()), outerWidth), _frame, QRect(outerLeft, outerTop, outerRight - outerLeft, outerBottom - outerTop));
}
} // namespace Ui

View File

@ -24,34 +24,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace Ui {
class PanelAnimation {
class RoundShadowAnimation {
public:
enum class Origin {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
};
PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) {
}
void setFinalImage(QImage &&finalImage, QRect inner);
void setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight);
void setSkipShadow(bool skipShadow);
void start();
const QImage &getFrame(float64 dt, float64 opacity);
private:
void setStartWidth();
void setStartHeight();
void setStartAlpha();
void setStartFadeTop();
void createFadeMask();
void setWidthDuration();
void setHeightDuration();
void setAlphaDuration();
void setShadow();
protected:
void start(int frameWidth, int frameHeight, float64 devicePixelRatio);
void setShadow(const style::Shadow &st);
bool started() const {
return !_frame.isNull();
@ -86,13 +65,54 @@ private:
void paintShadowVertical(int left, int top, int bottom, const QImage &image);
void paintShadowHorizontal(int left, int right, int top, const QImage &image);
Shadow _shadow;
Corner _topLeft;
Corner _topRight;
Corner _bottomLeft;
Corner _bottomRight;
QImage _frame;
uint32 *_frameInts = nullptr;
int _frameWidth = 0;
int _frameHeight = 0;
int _frameAlpha = 0; // recounted each getFrame()
int _frameIntsPerLine = 0;
int _frameIntsPerLineAdded = 0;
};
class PanelAnimation : public RoundShadowAnimation {
public:
enum class Origin {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
};
PanelAnimation(const style::PanelAnimation &st, Origin origin) : _st(st), _origin(origin) {
}
void setFinalImage(QImage &&finalImage, QRect inner);
void setSkipShadow(bool skipShadow);
void start();
void paintFrame(QPainter &p, int x, int y, int outerWidth, float64 dt, float64 opacity);
private:
void setStartWidth();
void setStartHeight();
void setStartAlpha();
void setStartFadeTop();
void createFadeMask();
void setWidthDuration();
void setHeightDuration();
void setAlphaDuration();
const style::PanelAnimation &_st;
Origin _origin = Origin::TopLeft;
QImage _finalImage;
const uint32 *_finalInts = nullptr;
int _finalIntsPerLine = 0;
int _finalIntsPerLineAdded = 0;
QPixmap _finalImage;
int _finalWidth = 0;
int _finalHeight = 0;
int _finalInnerLeft = 0;
@ -102,33 +122,20 @@ private:
int _finalInnerWidth = 0;
int _finalInnerHeight = 0;
Shadow _shadow;
bool _skipShadow = false;
int _startWidth = -1;
int _startHeight = -1;
int _startAlpha = 0;
int _startFadeTop = 0;
QImage _fadeMask;
QPixmap _fadeMask;
int _fadeHeight = 0;
const uint32 *_fadeInts = nullptr;
int _fadeIntsPerLine = 0;
Corner _topLeft;
Corner _topRight;
Corner _bottomLeft;
Corner _bottomRight;
QBrush _fadeFirst, _fadeLast;
float64 _widthDuration = 1.;
float64 _heightDuration = 1.;
float64 _alphaDuration = 1.;
QImage _frame;
uint32 *_frameInts = nullptr;
int _frameAlpha = 0; // recounted each getFrame()
int _frameIntsPerLine = 0;
int _frameIntsPerLineAdded = 0;
};
} // namespace Ui

View File

@ -85,15 +85,7 @@ void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect
auto height = srcRect.height();
t_assert(outResult && outResult->rect().contains(QRect(dstPoint, srcRect.size())));
auto initialAlpha = c.alpha() + 1;
auto red = (c.red() * initialAlpha) >> 8;
auto green = (c.green() * initialAlpha) >> 8;
auto blue = (c.blue() * initialAlpha) >> 8;
auto alpha = (255 * initialAlpha) >> 8;
auto pattern = static_cast<uint64>(blue)
| (static_cast<uint64>(green) << 16)
| (static_cast<uint64>(red) << 32)
| (static_cast<uint64>(alpha) << 48);
auto pattern = anim::shifted(c);
auto resultBytesPerPixel = (src.depth() >> 3);
auto resultIntsPerPixel = 1;

View File

@ -110,7 +110,7 @@ void InnerDropdown::paintEvent(QPaintEvent *e) {
auto ms = getms();
if (_a_show.animating(ms)) {
if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) {
p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity));
_showAnimation->paintFrame(p, 0, 0, width(), _a_show.current(1.), opacity);
}
} else if (_a_opacity.animating(ms)) {
p.setOpacity(_a_opacity.current(0.));
@ -118,10 +118,11 @@ void InnerDropdown::paintEvent(QPaintEvent *e) {
} else if (_hiding || isHidden()) {
hideFinished();
} else if (_showAnimation) {
p.drawImage(0, 0, _showAnimation->getFrame(1., 1.));
_showAnimation->paintFrame(p, 0, 0, width(), 1., 1.);
_showAnimation.reset();
showChildren();
} else {
if (!_cache.isNull()) _cache = QPixmap();
auto inner = rect().marginsRemoved(_st.padding);
Shadow::paint(p, inner, width(), _st.shadow);
App::roundRect(p, inner, _st.bg, ImageRoundRadius::Small);
@ -207,6 +208,9 @@ void InnerDropdown::prepareCache() {
_cache = myGrab(this);
_showAnimation = base::take(showAnimationData);
_a_show = base::take(showAnimation);
if (_a_show.animating()) {
hideChildren();
}
}
void InnerDropdown::startOpacityAnimation(bool hiding) {
@ -270,7 +274,7 @@ void InnerDropdown::opacityAnimationCallback() {
if (_hiding) {
_hiding = false;
hideFinished();
} else {
} else if (!_a_show.animating()) {
showChildren();
}
}

View File

@ -109,7 +109,7 @@ void PopupMenu::paintEvent(QPaintEvent *e) {
auto ms = getms();
if (_a_show.animating(ms)) {
if (auto opacity = _a_opacity.current(ms, _hiding ? 0. : 1.)) {
p.drawImage(0, 0, _showAnimation->getFrame(_a_show.current(1.), opacity));
_showAnimation->paintFrame(p, 0, 0, width(), _a_show.current(1.), opacity);
}
} else if (_a_opacity.animating(ms)) {
p.setOpacity(_a_opacity.current(0.));
@ -117,7 +117,7 @@ void PopupMenu::paintEvent(QPaintEvent *e) {
} else if (_hiding || isHidden()) {
hideFinished();
} else if (_showAnimation) {
p.drawImage(0, 0, _showAnimation->getFrame(1., 1.));
_showAnimation->paintFrame(p, 0, 0, width(), 1., 1.);
_showAnimation.reset();
showChildren();
} else {

View File

@ -201,6 +201,7 @@
'<(src_loc)/boxes/stickers_box.h',
'<(src_loc)/boxes/usernamebox.cpp',
'<(src_loc)/boxes/usernamebox.h',
'<(src_loc)/core/build_config.h',
'<(src_loc)/core/basic_types.h',
'<(src_loc)/core/click_handler.cpp',
'<(src_loc)/core/click_handler.h',