Implement partially-rounded rect with a SpoilerMess frame.
This commit is contained in:
parent
0edb817ccd
commit
a7d90843a3
2 changed files with 194 additions and 0 deletions
|
|
@ -7,9 +7,11 @@
|
||||||
#include "ui/effects/spoiler_mess.h"
|
#include "ui/effects/spoiler_mess.h"
|
||||||
|
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
|
#include "ui/image/image_prepare.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/integration.h"
|
#include "ui/integration.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
|
#include "base/flags.h"
|
||||||
|
|
||||||
#include <QtCore/QBuffer>
|
#include <QtCore/QBuffer>
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
|
|
@ -279,6 +281,173 @@ SpoilerMessCached GenerateSpoilerMess(
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillSpoilerRect(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
const SpoilerMessFrame &frame,
|
||||||
|
QPoint originShift) {
|
||||||
|
if (rect.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &image = *frame.image;
|
||||||
|
const auto source = frame.source;
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto origin = rect.topLeft() + originShift;
|
||||||
|
const auto size = source.width() / ratio;
|
||||||
|
const auto xSkipFrames = (origin.x() <= rect.x())
|
||||||
|
? ((rect.x() - origin.x()) / size)
|
||||||
|
: -((origin.x() - rect.x() + size - 1) / size);
|
||||||
|
const auto ySkipFrames = (origin.y() <= rect.y())
|
||||||
|
? ((rect.y() - origin.y()) / size)
|
||||||
|
: -((origin.y() - rect.y() + size - 1) / size);
|
||||||
|
const auto xFrom = origin.x() + size * xSkipFrames;
|
||||||
|
const auto yFrom = origin.y() + size * ySkipFrames;
|
||||||
|
Assert((xFrom <= rect.x())
|
||||||
|
&& (yFrom <= rect.y())
|
||||||
|
&& (xFrom + size > rect.x())
|
||||||
|
&& (yFrom + size > rect.y()));
|
||||||
|
const auto xTill = rect.x() + rect.width();
|
||||||
|
const auto yTill = rect.y() + rect.height();
|
||||||
|
const auto xCount = (xTill - xFrom + size - 1) / size;
|
||||||
|
const auto yCount = (yTill - yFrom + size - 1) / size;
|
||||||
|
Assert(xCount > 0 && yCount > 0);
|
||||||
|
const auto xFullFrom = (xFrom < rect.x()) ? 1 : 0;
|
||||||
|
const auto yFullFrom = (yFrom < rect.y()) ? 1 : 0;
|
||||||
|
const auto xFullTill = xCount - (xFrom + xCount * size > xTill ? 1 : 0);
|
||||||
|
const auto yFullTill = yCount - (yFrom + yCount * size > yTill ? 1 : 0);
|
||||||
|
const auto targetRect = [&](int x, int y) {
|
||||||
|
return QRect(xFrom + x * size, yFrom + y * size, size, size);
|
||||||
|
};
|
||||||
|
const auto drawFull = [&](int x, int y) {
|
||||||
|
p.drawImage(targetRect(x, y), image, source);
|
||||||
|
};
|
||||||
|
const auto drawPart = [&](int x, int y) {
|
||||||
|
const auto target = targetRect(x, y);
|
||||||
|
const auto fill = target.intersected(rect);
|
||||||
|
Assert(!fill.isEmpty());
|
||||||
|
p.drawImage(fill, image, QRect(
|
||||||
|
source.topLeft() + ((fill.topLeft() - target.topLeft()) * ratio),
|
||||||
|
fill.size() * ratio));
|
||||||
|
};
|
||||||
|
if (yFullFrom) {
|
||||||
|
for (auto x = 0; x != xCount; ++x) {
|
||||||
|
drawPart(x, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (yFullFrom < yFullTill) {
|
||||||
|
if (xFullFrom) {
|
||||||
|
for (auto y = yFullFrom; y != yFullTill; ++y) {
|
||||||
|
drawPart(0, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xFullFrom < xFullTill) {
|
||||||
|
for (auto y = yFullFrom; y != yFullTill; ++y) {
|
||||||
|
for (auto x = xFullFrom; x != xFullTill; ++x) {
|
||||||
|
drawFull(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xFullFrom <= xFullTill && xFullTill < xCount) {
|
||||||
|
for (auto y = yFullFrom; y != yFullTill; ++y) {
|
||||||
|
drawPart(xFullTill, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (yFullFrom <= yFullTill && yFullTill < yCount) {
|
||||||
|
for (auto x = 0; x != xCount; ++x) {
|
||||||
|
drawPart(x, yFullTill);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillSpoilerRect(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners,
|
||||||
|
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.
|
||||||
|
FillSpoilerRect(p, rect, frame, originShift);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto paintPart = [&](int x, int y, int w, int h) {
|
||||||
|
FillSpoilerRect(
|
||||||
|
p,
|
||||||
|
{ rect.x() + x, rect.y() + y, w, h },
|
||||||
|
frame,
|
||||||
|
originShift - QPoint(x, y));
|
||||||
|
};
|
||||||
|
const auto paintCorner = [&](QPoint position, const QImage &mask) {
|
||||||
|
if (cornerCache.width() < mask.width()
|
||||||
|
|| cornerCache.height() < mask.height()) {
|
||||||
|
cornerCache = QImage(
|
||||||
|
std::max(cornerCache.width(), mask.width()),
|
||||||
|
std::max(cornerCache.height(), mask.height()),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
cornerCache.setDevicePixelRatio(ratio);
|
||||||
|
}
|
||||||
|
const auto size = mask.size() / ratio;
|
||||||
|
const auto target = QRect(QPoint(), size);
|
||||||
|
auto q = QPainter(&cornerCache);
|
||||||
|
q.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
|
FillSpoilerRect(
|
||||||
|
q,
|
||||||
|
target,
|
||||||
|
frame,
|
||||||
|
originShift - rect.topLeft() - position);
|
||||||
|
q.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||||
|
q.drawImage(target, mask);
|
||||||
|
q.end();
|
||||||
|
p.drawImage(
|
||||||
|
QRect(rect.topLeft() + position, size),
|
||||||
|
cornerCache,
|
||||||
|
QRect(QPoint(), mask.size()));
|
||||||
|
};
|
||||||
|
if (yFillFrom > 0) {
|
||||||
|
if (xFillFrom > 0) {
|
||||||
|
paintCorner({}, mask[0]);
|
||||||
|
}
|
||||||
|
if (xFill) {
|
||||||
|
paintPart(xFillFrom, 0, xFill, yFillFrom);
|
||||||
|
}
|
||||||
|
if (xFillTo < rect.width()) {
|
||||||
|
paintCorner({ xFillTo, 0 }, mask[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (yFill) {
|
||||||
|
paintPart(0, yFillFrom, rect.width(), yFill);
|
||||||
|
}
|
||||||
|
if (yFillTo < rect.height()) {
|
||||||
|
if (xFillFrom > 0) {
|
||||||
|
paintCorner({ 0, yFillTo }, mask[2]);
|
||||||
|
}
|
||||||
|
if (xFill) {
|
||||||
|
paintPart(xFillFrom, yFillTo, xFill, rect.height() - yFillTo);
|
||||||
|
}
|
||||||
|
if (xFillTo < rect.width()) {
|
||||||
|
paintCorner({ xFillTo, yFillTo }, mask[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SpoilerMessCached::SpoilerMessCached(
|
SpoilerMessCached::SpoilerMessCached(
|
||||||
QImage image,
|
QImage image,
|
||||||
int framesCount,
|
int framesCount,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,16 @@
|
||||||
|
|
||||||
#include <crl/crl_time.h>
|
#include <crl/crl_time.h>
|
||||||
|
|
||||||
|
enum class ImageRoundRadius;
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
template <typename EnumType>
|
||||||
|
class flags;
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
enum class RectPart;
|
||||||
|
using RectParts = base::flags<RectPart>;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
struct SpoilerMessDescriptor {
|
struct SpoilerMessDescriptor {
|
||||||
|
|
@ -30,6 +40,21 @@ struct SpoilerMessFrame {
|
||||||
QRect source;
|
QRect source;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void FillSpoilerRect(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
const SpoilerMessFrame &frame,
|
||||||
|
QPoint originShift = {});
|
||||||
|
|
||||||
|
void FillSpoilerRect(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
ImageRoundRadius radius,
|
||||||
|
RectParts corners,
|
||||||
|
const SpoilerMessFrame &frame,
|
||||||
|
QImage &cornerCache,
|
||||||
|
QPoint originShift = {});
|
||||||
|
|
||||||
class SpoilerMessCached final {
|
class SpoilerMessCached final {
|
||||||
public:
|
public:
|
||||||
SpoilerMessCached(
|
SpoilerMessCached(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue