From 2f816942b833ca1386175e83650d559be6398253 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 13 Jul 2017 17:42:46 +0300 Subject: [PATCH] Use objects instead of pointers for corners. Also don't change mask corner images when color theme is changed. This prevents race condition in mask corner images access, because the GIF frame readers access mask corner images from other threads. --- Telegram/SourceFiles/app.cpp | 85 +++++++++---------- Telegram/SourceFiles/app.h | 2 +- .../SourceFiles/chat_helpers/tabbed_panel.cpp | 2 +- .../chat_helpers/tabbed_selector.cpp | 2 +- .../inline_bots/inline_results_widget.cpp | 2 +- .../ui/effects/panel_animation.cpp | 14 +-- .../SourceFiles/ui/effects/panel_animation.h | 4 +- Telegram/SourceFiles/ui/images.cpp | 22 ++--- Telegram/SourceFiles/ui/images.h | 2 +- .../SourceFiles/ui/widgets/inner_dropdown.cpp | 2 +- .../SourceFiles/ui/widgets/popup_menu.cpp | 2 +- 11 files changed, 65 insertions(+), 74 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index e26e65bcb..4bfe73eff 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -111,15 +111,12 @@ namespace { style::font monofont; struct CornersPixmaps { - CornersPixmaps() { - memset(p, 0, sizeof(p)); - } - QPixmap *p[4]; + QPixmap p[4]; }; - CornersPixmaps corners[RoundCornersCount]; + QVector corners; using CornersMap = QMap; CornersMap cornersMap; - QImage *cornersMaskLarge[4] = { nullptr }, *cornersMaskSmall[4] = { nullptr }; + QImage cornersMaskLarge[4], cornersMaskSmall[4]; using EmojiImagesMap = QMap; EmojiImagesMap MainEmojiMap; @@ -2174,6 +2171,7 @@ namespace { } void prepareCorners(RoundCorners index, int32 radius, const QBrush &brush, const style::color *shadow = nullptr, QImage *cors = nullptr) { + Expects(::corners.size() > index); int32 r = radius * cIntRetinaFactor(), s = st::msgShadow * cIntRetinaFactor(); QImage rect(r * 3, r * 3 + (shadow ? s : 0), QImage::Format_ARGB32_Premultiplied), localCors[4]; { @@ -2198,8 +2196,8 @@ namespace { cors[3] = rect.copy(r * 2, r * 2, r, r + (shadow ? s : 0)); if (index != SmallMaskCorners && index != LargeMaskCorners) { for (int i = 0; i < 4; ++i) { - ::corners[index].p[i] = new QPixmap(pixmapFromImageInPlace(std::move(cors[i]))); - ::corners[index].p[i]->setDevicePixelRatio(cRetinaFactor()); + ::corners[index].p[i] = pixmapFromImageInPlace(std::move(cors[i])); + ::corners[index].p[i].setDevicePixelRatio(cRetinaFactor()); } } } @@ -2221,18 +2219,21 @@ namespace { return MsgRadius; } - void createCorners() { + void createMaskCorners() { QImage mask[4]; - prepareCorners(LargeMaskCorners, msgRadius(), QColor(255, 255, 255), nullptr, mask); - for (int i = 0; i < 4; ++i) { - ::cornersMaskLarge[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); - ::cornersMaskLarge[i]->setDevicePixelRatio(cRetinaFactor()); - } prepareCorners(SmallMaskCorners, st::buttonRadius, QColor(255, 255, 255), nullptr, mask); for (int i = 0; i < 4; ++i) { - ::cornersMaskSmall[i] = new QImage(mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied)); - ::cornersMaskSmall[i]->setDevicePixelRatio(cRetinaFactor()); + ::cornersMaskSmall[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); + ::cornersMaskSmall[i].setDevicePixelRatio(cRetinaFactor()); } + prepareCorners(LargeMaskCorners, msgRadius(), QColor(255, 255, 255), nullptr, mask); + for (int i = 0; i < 4; ++i) { + ::cornersMaskLarge[i] = mask[i].convertToFormat(QImage::Format_ARGB32_Premultiplied); + ::cornersMaskLarge[i].setDevicePixelRatio(cRetinaFactor()); + } + } + + void createPaletteCorners() { prepareCorners(MenuCorners, st::buttonRadius, st::menuBg); prepareCorners(BoxCorners, st::boxRadius, st::boxBg); prepareCorners(BotKbOverCorners, st::dateRadius, st::msgBotKbOverBgAdd); @@ -2262,19 +2263,14 @@ namespace { prepareCorners(MessageOutSelectedCorners, msgRadius(), st::msgOutBgSelected, &st::msgOutShadowSelected); } + void createCorners() { + ::corners.resize(RoundCornersCount); + createMaskCorners(); + createPaletteCorners(); + } + void clearCorners() { - for (int j = 0; j < 4; ++j) { - for (int i = 0; i < RoundCornersCount; ++i) { - delete ::corners[i].p[j]; ::corners[i].p[j] = nullptr; - } - delete ::cornersMaskSmall[j]; ::cornersMaskSmall[j] = nullptr; - delete ::cornersMaskLarge[j]; ::cornersMaskLarge[j] = nullptr; - } - for (auto i = ::cornersMap.cbegin(), e = ::cornersMap.cend(); i != e; ++i) { - for (int j = 0; j < 4; ++j) { - delete i->p[j]; - } - } + ::corners.clear(); ::cornersMap.clear(); } @@ -2303,18 +2299,13 @@ namespace { using Update = Window::Theme::BackgroundUpdate; static auto subscription = Window::Theme::Background()->add_subscription([](const Update &update) { if (update.paletteChanged()) { - clearCorners(); - createCorners(); + createPaletteCorners(); if (App::main()) { App::main()->updateScrollColors(); } HistoryLayout::serviceColorsUpdated(); } else if (update.type == Update::Type::New) { - for (int i = 0; i < 4; ++i) { - delete ::corners[StickerCorners].p[i]; ::corners[StickerCorners].p[i] = nullptr; - delete ::corners[StickerSelectedCorners].p[i]; ::corners[StickerSelectedCorners].p[i] = nullptr; - } prepareCorners(StickerCorners, st::dateRadius, st::msgServiceBg); prepareCorners(StickerSelectedCorners, st::dateRadius, st::msgServiceBgSelected); @@ -2778,7 +2769,7 @@ namespace { roundRect(p, rect, st::msgInBg, MessageInCorners, nullptr, parts); } - QImage **cornersMask(ImageRoundRadius radius) { + QImage *cornersMask(ImageRoundRadius radius) { switch (radius) { case ImageRoundRadius::Large: return ::cornersMaskLarge; case ImageRoundRadius::Small: @@ -2788,8 +2779,8 @@ namespace { } void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, 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(); + 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) { @@ -2818,16 +2809,16 @@ namespace { } } if (parts & RectPart::TopLeft) { - p.drawPixmap(x, y, *corner.p[0]); + p.drawPixmap(x, y, corner.p[0]); } if (parts & RectPart::TopRight) { - p.drawPixmap(x + w - cornerWidth, y, *corner.p[1]); + p.drawPixmap(x + w - cornerWidth, y, corner.p[1]); } if (parts & RectPart::BottomLeft) { - p.drawPixmap(x, y + h - cornerHeight, *corner.p[2]); + 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(x + w - cornerWidth, y + h - cornerHeight, corner.p[3]); } } @@ -2837,18 +2828,18 @@ namespace { void roundShadow(Painter &p, int32 x, int32 y, int32 w, int32 h, 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(); + 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]); + 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]); + p.drawPixmap(x + w - cornerWidth, y + h - cornerHeight + st::msgShadow, corner.p[3]); } } @@ -2865,8 +2856,8 @@ namespace { CornersPixmaps pixmaps; for (int j = 0; j < 4; ++j) { - pixmaps.p[j] = new QPixmap(pixmapFromImageInPlace(std::move(images[j]))); - pixmaps.p[j]->setDevicePixelRatio(cRetinaFactor()); + pixmaps.p[j] = pixmapFromImageInPlace(std::move(images[j])); + pixmaps.p[j].setDevicePixelRatio(cRetinaFactor()); } i = cornersMap.insert(colorKey, pixmaps); } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 08dee4bf4..9ea201d28 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -283,7 +283,7 @@ namespace App { void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, ImageRoundCorners corners); void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, ImageRoundCorners corners); - QImage **cornersMask(ImageRoundRadius radius); + QImage *cornersMask(ImageRoundRadius radius); void roundRect(Painter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, RoundCorners index, const style::color *shadow = nullptr, RectParts parts = RectPart::Full); inline void roundRect(Painter &p, const QRect &rect, 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); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp index ad184af2c..a18c2818c 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_panel.cpp @@ -265,7 +265,7 @@ void TabbedPanel::startShowAnimation() { auto inner = rect().marginsRemoved(st::emojiPanMargins); _showAnimation->setFinalImage(std::move(image), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); _showAnimation->start(); } hideChildren(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 8f4b26c3b..615327c62 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -653,7 +653,7 @@ void TabbedSelector::switchTab() { auto slidingRect = QRect(_tabsSlider->x() * cIntRetinaFactor(), _scroll->y() * cIntRetinaFactor(), _tabsSlider->width() * cIntRetinaFactor(), (height() - _scroll->y()) * cIntRetinaFactor()); _slideAnimation->setFinalImages(direction, std::move(wasCache), std::move(nowCache), slidingRect, wasSectionIcons); auto corners = App::cornersMask(ImageRoundRadius::Small); - _slideAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _slideAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); _slideAnimation->start(); hideForSliding(); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index 1e9fcd842..11d5df715 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -897,7 +897,7 @@ void Widget::startShowAnimation() { auto inner = rect().marginsRemoved(st::emojiPanMargins); _showAnimation->setFinalImage(std::move(image), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); _showAnimation->start(); } hideChildren(); diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.cpp b/Telegram/SourceFiles/ui/effects/panel_animation.cpp index c152c3e04..761146756 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/panel_animation.cpp @@ -65,16 +65,16 @@ void RoundShadowAnimation::setShadow(const style::Shadow &st) { } } -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::setCornerMasks(const QImage &topLeft, const QImage &topRight, const QImage &bottomLeft, const QImage &bottomRight) { + setCornerMask(_topLeft, topLeft); + setCornerMask(_topRight, topRight); + setCornerMask(_bottomLeft, bottomLeft); + setCornerMask(_bottomRight, bottomRight); } -void RoundShadowAnimation::setCornerMask(Corner &corner, QImage &&image) { +void RoundShadowAnimation::setCornerMask(Corner &corner, const QImage &image) { t_assert(!started()); - corner.image = std::move(image); + corner.image = image; if (corner.valid()) { corner.width = corner.image.width(); corner.height = corner.image.height(); diff --git a/Telegram/SourceFiles/ui/effects/panel_animation.h b/Telegram/SourceFiles/ui/effects/panel_animation.h index 9d7bbb262..2e35ece14 100644 --- a/Telegram/SourceFiles/ui/effects/panel_animation.h +++ b/Telegram/SourceFiles/ui/effects/panel_animation.h @@ -26,7 +26,7 @@ namespace Ui { class RoundShadowAnimation { public: - void setCornerMasks(QImage &&topLeft, QImage &&topRight, QImage &&bottomLeft, QImage &&bottomRight); + void setCornerMasks(const QImage &topLeft, const QImage &topRight, const QImage &bottomLeft, const QImage &bottomRight); protected: void start(int frameWidth, int frameHeight, float64 devicePixelRatio); @@ -48,7 +48,7 @@ protected: return !image.isNull(); } }; - void setCornerMask(Corner &corner, QImage &&image); + void setCornerMask(Corner &corner, const QImage &image); void paintCorner(Corner &corner, int left, int top); struct Shadow { diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index 8093a78d8..41eccc43b 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -204,13 +204,13 @@ void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corn image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied); t_assert(!image.isNull()); - QImage **masks = App::cornersMask(radius); + auto masks = App::cornersMask(radius); prepareRound(image, masks, corners); } -void prepareRound(QImage &image, QImage **cornerMasks, ImageRoundCorners corners) { - auto cornerWidth = cornerMasks[0]->width(); - auto cornerHeight = cornerMasks[0]->height(); +void prepareRound(QImage &image, QImage *cornerMasks, ImageRoundCorners corners) { + auto cornerWidth = cornerMasks[0].width(); + auto cornerHeight = cornerMasks[0].height(); auto imageWidth = image.width(); auto imageHeight = image.height(); if (imageWidth < 2 * cornerWidth || imageHeight < 2 * cornerHeight) { @@ -226,15 +226,15 @@ void prepareRound(QImage &image, QImage **cornerMasks, ImageRoundCorners corners auto intsTopRight = ints + imageWidth - cornerWidth; auto intsBottomLeft = ints + (imageHeight - cornerHeight) * imageWidth; auto intsBottomRight = ints + (imageHeight - cornerHeight + 1) * imageWidth - cornerWidth; - auto maskCorner = [imageWidth, imageHeight, imageIntsPerPixel, imageIntsPerLine](uint32 *imageInts, const QImage *mask) { - auto maskWidth = mask->width(); - auto maskHeight = mask->height(); - auto maskBytesPerPixel = (mask->depth() >> 3); - auto maskBytesPerLine = mask->bytesPerLine(); + auto maskCorner = [imageWidth, imageHeight, imageIntsPerPixel, imageIntsPerLine](uint32 *imageInts, const QImage &mask) { + auto maskWidth = mask.width(); + auto maskHeight = mask.height(); + auto maskBytesPerPixel = (mask.depth() >> 3); + auto maskBytesPerLine = mask.bytesPerLine(); auto maskBytesAdded = maskBytesPerLine - maskWidth * maskBytesPerPixel; - auto maskBytes = mask->constBits(); + auto maskBytes = mask.constBits(); t_assert(maskBytesAdded >= 0); - t_assert(mask->depth() == (maskBytesPerPixel << 3)); + t_assert(mask.depth() == (maskBytesPerPixel << 3)); auto imageIntsAdded = imageIntsPerLine - maskWidth * imageIntsPerPixel; t_assert(imageIntsAdded >= 0); for (auto y = 0; y != maskHeight; ++y) { diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index a8349e313..3a6d67875 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -180,7 +180,7 @@ namespace Images { QImage prepareBlur(QImage image); void prepareRound(QImage &image, ImageRoundRadius radius, ImageRoundCorners corners = ImageRoundCorner::All); -void prepareRound(QImage &image, QImage **cornerMasks, ImageRoundCorners corners = ImageRoundCorner::All); +void prepareRound(QImage &image, QImage *cornerMasks, ImageRoundCorners corners = ImageRoundCorner::All); void prepareCircle(QImage &image); QImage prepareColored(style::color add, QImage image); QImage prepareOpaque(QImage image); diff --git a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp index efe7cba4a..a255b3822 100644 --- a/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp +++ b/Telegram/SourceFiles/ui/widgets/inner_dropdown.cpp @@ -254,7 +254,7 @@ void InnerDropdown::startShowAnimation() { auto inner = rect().marginsRemoved(_st.padding); _showAnimation->setFinalImage(std::move(cache), QRect(inner.topLeft() * cIntRetinaFactor(), inner.size() * cIntRetinaFactor())); auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); _showAnimation->start(); } hideChildren(); diff --git a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp index 7391b07ce..e30b61292 100644 --- a/Telegram/SourceFiles/ui/widgets/popup_menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/popup_menu.cpp @@ -362,7 +362,7 @@ void PopupMenu::startShowAnimation() { _showAnimation->setFinalImage(std::move(cache), QRect(_inner.topLeft() * cIntRetinaFactor(), _inner.size() * cIntRetinaFactor())); if (_useTransparency) { auto corners = App::cornersMask(ImageRoundRadius::Small); - _showAnimation->setCornerMasks(QImage(*corners[0]), QImage(*corners[1]), QImage(*corners[2]), QImage(*corners[3])); + _showAnimation->setCornerMasks(corners[0], corners[1], corners[2], corners[3]); } else { _showAnimation->setSkipShadow(true); }