diff --git a/ui/effects/spoiler_mess.cpp b/ui/effects/spoiler_mess.cpp index b07f8ac..dc0cb1a 100644 --- a/ui/effects/spoiler_mess.cpp +++ b/ui/effects/spoiler_mess.cpp @@ -501,48 +501,52 @@ void FillSpoilerRect( void FillSpoilerRect( QPainter &p, QRect rect, - ImageRoundRadius radius, - RectParts corners, + Images::CornersMaskRef mask, const SpoilerMessFrame &frame, QImage &cornerCache, QPoint originShift) { - if (radius == ImageRoundRadius::None || !corners) { - FillSpoilerRect(p, rect, frame, originShift); - return; - } - const auto &mask = Images::CornersMask(radius); - const auto side = mask[0].width() / style::DevicePixelRatio(); - const auto xFillFrom = (corners & RectPart::FullLeft) ? side : 0; - const auto xFillTo = rect.width() - - ((corners & RectPart::FullRight) ? side : 0); - const auto yFillFrom = (corners & RectPart::FullTop) ? side : 0; - const auto yFillTo = rect.height() - - ((corners & RectPart::FullBottom) ? side : 0); - const auto xFill = xFillTo - xFillFrom; - const auto yFill = yFillTo - yFillFrom; - if (xFill < 0 || yFill < 0) { - // Unexpected.. but maybe not fatal, just glitchy. + constexpr auto kTopLeft = 0; + constexpr auto kTopRight = 1; + constexpr auto kBottomLeft = 2; + constexpr auto kBottomRight = 3; + + if ((!mask.p[kTopLeft] || mask.p[kTopLeft]->isNull()) + && (!mask.p[kTopRight] || mask.p[kTopRight]->isNull()) + && (!mask.p[kBottomLeft] || mask.p[kBottomLeft]->isNull()) + && (!mask.p[kBottomRight] || mask.p[kBottomRight]->isNull())) { FillSpoilerRect(p, rect, frame, originShift); return; } const auto ratio = style::DevicePixelRatio(); - const auto paintPart = [&](int x, int y, int w, int h) { + const auto cornerSize = [&](int index) { + const auto corner = mask.p[index]; + return (!corner || corner->isNull()) ? 0 : (corner->width() / ratio); + }; + const auto verticalSkip = [&](int left, int right) { + return std::max(cornerSize(left), cornerSize(right)); + }; + const auto fillBg = [&](QRect part) { FillSpoilerRect( p, - { rect.x() + x, rect.y() + y, w, h }, + part.translated(rect.topLeft()), frame, - originShift - QPoint(x, y)); + originShift - part.topLeft()); }; - const auto paintCorner = [&](QPoint position, const QImage &mask) { - if (cornerCache.width() < mask.width() - || cornerCache.height() < mask.height()) { + const auto fillCorner = [&](int x, int y, int index) { + const auto position = QPoint(x, y); + const auto corner = mask.p[index]; + if (!corner || corner->isNull()) { + return; + } + if (cornerCache.width() < corner->width() + || cornerCache.height() < corner->height()) { cornerCache = QImage( - std::max(cornerCache.width(), mask.width()), - std::max(cornerCache.height(), mask.height()), + std::max(cornerCache.width(), corner->width()), + std::max(cornerCache.height(), corner->height()), QImage::Format_ARGB32_Premultiplied); cornerCache.setDevicePixelRatio(ratio); } - const auto size = mask.size() / ratio; + const auto size = corner->size() / ratio; const auto target = QRect(QPoint(), size); auto q = QPainter(&cornerCache); q.setCompositionMode(QPainter::CompositionMode_Source); @@ -552,36 +556,83 @@ void FillSpoilerRect( frame, originShift - rect.topLeft() - position); q.setCompositionMode(QPainter::CompositionMode_DestinationIn); - q.drawImage(target, mask); + q.drawImage(target, *corner); q.end(); p.drawImage( QRect(rect.topLeft() + position, size), cornerCache, - QRect(QPoint(), mask.size())); + QRect(QPoint(), corner->size())); }; - if (yFillFrom > 0) { - if (xFillFrom > 0) { - paintCorner({}, mask[0]); + const auto top = verticalSkip(kTopLeft, kTopRight); + const auto bottom = verticalSkip(kBottomLeft, kBottomRight); + if (top) { + const auto left = cornerSize(kTopLeft); + const auto right = cornerSize(kTopRight); + if (left) { + fillCorner(rect.left(), rect.top(), kTopLeft); + if (const auto add = top - left) { + fillBg({ rect.left(), rect.top() + left, left, add }); + } } - if (xFill) { - paintPart(xFillFrom, 0, xFill, yFillFrom); + if (const auto fill = rect.width() - left - right; fill > 0) { + fillBg({ rect.left() + left, rect.top(), fill, top }); } - if (xFillTo < rect.width()) { - paintCorner({ xFillTo, 0 }, mask[1]); + if (right) { + fillCorner( + rect.left() + rect.width() - right, + rect.top(), + kTopRight); + if (const auto add = top - right) { + fillBg({ + rect.left() + rect.width() - right, + rect.top() + right, + right, + add, + }); + } } } - if (yFill) { - paintPart(0, yFillFrom, rect.width(), yFill); + if (const auto h = rect.height() - top - bottom; h > 0) { + fillBg({ rect.left(), rect.top() + top, rect.width(), h }); } - if (yFillTo < rect.height()) { - if (xFillFrom > 0) { - paintCorner({ 0, yFillTo }, mask[2]); + if (bottom) { + const auto left = cornerSize(kBottomLeft); + const auto right = cornerSize(kBottomRight); + if (left) { + fillCorner( + rect.left(), + rect.top() + rect.height() - left, + kBottomLeft); + if (const auto add = bottom - left) { + fillBg({ + rect.left(), + rect.top() + rect.height() - bottom, + left, + add, + }); + } } - if (xFill) { - paintPart(xFillFrom, yFillTo, xFill, rect.height() - yFillTo); + if (const auto fill = rect.width() - left - right; fill > 0) { + fillBg({ + rect.left() + left, + rect.top() + rect.height() - bottom, + fill, + bottom, + }); } - if (xFillTo < rect.width()) { - paintCorner({ xFillTo, yFillTo }, mask[3]); + if (right) { + fillCorner( + rect.left() + rect.width() - right, + rect.top() + rect.height() - right, + kBottomRight); + if (const auto add = bottom - right) { + fillBg({ + rect.left() + rect.width() - right, + rect.top() + rect.height() - bottom, + right, + add, + }); + } } } } diff --git a/ui/effects/spoiler_mess.h b/ui/effects/spoiler_mess.h index 1cf9102..78772a9 100644 --- a/ui/effects/spoiler_mess.h +++ b/ui/effects/spoiler_mess.h @@ -8,15 +8,9 @@ #include -enum class ImageRoundRadius; - -namespace base { -template -class flags; -} // namespace base - -enum class RectPart; -using RectParts = base::flags; +namespace Images { +struct CornersMaskRef; +} // namespace Images namespace Ui { @@ -49,8 +43,7 @@ void FillSpoilerRect( void FillSpoilerRect( QPainter &p, QRect rect, - ImageRoundRadius radius, - RectParts corners, + Images::CornersMaskRef mask, const SpoilerMessFrame &frame, QImage &cornerCache, QPoint originShift = {}); diff --git a/ui/image/image_prepare.cpp b/ui/image/image_prepare.cpp index 7eafb7d..013707c 100644 --- a/ui/image/image_prepare.cpp +++ b/ui/image/image_prepare.cpp @@ -1009,8 +1009,7 @@ QImage Circle(QImage &&image, QRect target) { QImage Round( QImage &&image, - gsl::span cornerMasks, - RectParts corners, + CornersMaskRef mask, QRect target) { if (target.isNull()) { target = QRect(QPoint(), image.size()); @@ -1035,23 +1034,24 @@ QImage Round( Assert(image.depth() == ((kImageIntsPerPixel * sizeof(uint32)) << 3)); Assert(image.bytesPerLine() == (imageIntsPerLine << 2)); const auto maskCorner = [&]( - const QImage &mask, + const QImage *mask, bool right = false, bool bottom = false) { - const auto maskWidth = mask.width(); - const auto maskHeight = mask.height(); - if (mask.isNull() + const auto maskWidth = mask ? mask->width() : 0; + const auto maskHeight = mask ? mask->height() : 0; + if (!maskWidth + || !maskHeight || targetWidth < maskWidth || targetHeight < maskHeight) { return; } - const auto maskBytesPerPixel = (mask.depth() >> 3); - const auto maskBytesPerLine = mask.bytesPerLine(); + const auto maskBytesPerPixel = (mask->depth() >> 3); + const auto maskBytesPerLine = mask->bytesPerLine(); const auto maskBytesAdded = maskBytesPerLine - maskWidth * maskBytesPerPixel; Assert(maskBytesAdded >= 0); - Assert(mask.depth() == (maskBytesPerPixel << 3)); + Assert(mask->depth() == (maskBytesPerPixel << 3)); const auto imageIntsAdded = imageIntsPerLine - maskWidth * kImageIntsPerPixel; Assert(imageIntsAdded >= 0); @@ -1062,7 +1062,7 @@ QImage Round( if (bottom) { imageInts += (targetHeight - maskHeight) * imageIntsPerLine; } - auto maskBytes = mask.constBits(); + auto maskBytes = mask->constBits(); for (auto y = 0; y != maskHeight; ++y) { for (auto x = 0; x != maskWidth; ++x) { auto opacity = static_cast(*maskBytes) + 1; @@ -1075,18 +1075,27 @@ QImage Round( } }; - if (corners & RectPart::TopLeft) maskCorner(cornerMasks[0]); - if (corners & RectPart::TopRight) maskCorner(cornerMasks[1], true); - if (corners & RectPart::BottomLeft) { - maskCorner(cornerMasks[2], false, true); - } - if (corners & RectPart::BottomRight) { - maskCorner(cornerMasks[3], true, true); - } + maskCorner(mask.p[0]); + maskCorner(mask.p[1], true); + maskCorner(mask.p[2], false, true); + maskCorner(mask.p[3], true, true); return std::move(image); } +QImage Round( + QImage &&image, + gsl::span cornerMasks, + RectParts corners, + QRect target) { + return Round(std::move(image), CornersMaskRef({ + (corners & RectPart::TopLeft) ? &cornerMasks[0] : nullptr, + (corners & RectPart::TopRight) ? &cornerMasks[1] : nullptr, + (corners & RectPart::BottomLeft) ? &cornerMasks[2] : nullptr, + (corners & RectPart::BottomRight) ? &cornerMasks[3] : nullptr, + }), target); +} + QImage Round( QImage &&image, ImageRoundRadius radius, diff --git a/ui/image/image_prepare.h b/ui/image/image_prepare.h index b1a165d..42d34cf 100644 --- a/ui/image/image_prepare.h +++ b/ui/image/image_prepare.h @@ -46,6 +46,24 @@ namespace Images { int bottomAlpha, QColor color = QColor(0, 0, 0)); +struct CornersMaskRef { + CornersMaskRef() = default; + explicit CornersMaskRef(gsl::span masks) + : p{ &masks[0], &masks[1], &masks[2], &masks[3] } { + } + explicit CornersMaskRef(std::array masks) + : p{ &masks[0], &masks[1], &masks[2], &masks[3] } { + } + explicit CornersMaskRef(gsl::span masks) + : p{ masks[0], masks[1], masks[2], masks[3] } { + } + explicit CornersMaskRef(std::array masks) + : p{ masks[0], masks[1], masks[2], masks[3] } { + } + + std::array p{}; +}; + [[nodiscard]] const std::array &CornersMask( ImageRoundRadius radius); [[nodiscard]] std::array PrepareCorners( @@ -100,6 +118,11 @@ inline constexpr auto is_flag_type(Option) { return true; }; ImageRoundRadius radius, RectParts corners = RectPart::AllCorners); +[[nodiscard]] QImage Round( + QImage &&image, + CornersMaskRef mask, + QRect target = QRect()); + [[nodiscard]] QImage Blur(QImage &&image, bool ignoreAlpha = false); [[nodiscard]] QImage Round( QImage &&image,