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 e26e65bcbd..4bfe73efff 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 08dee4bf4f..9ea201d28c 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 ad184af2c9..a18c2818c5 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 8f4b26c3b9..615327c62e 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 1e9fcd8420..11d5df715b 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 c152c3e04a..7611467561 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 9d7bbb2621..2e35ece146 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 8093a78d81..41eccc43bd 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 a8349e3136..3a6d67875e 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 efe7cba4a0..a255b38225 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 7391b07ce8..e30b612928 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); }