22#include "boxshadowrenderer.h"
28static inline int calculateBlurRadius(qreal stdDev)
31 const qreal gaussianScaleFactor = (3.0 * qSqrt(2.0 * M_PI) / 4.0) * 1.5;
32 return qMax(2, qFloor(stdDev * gaussianScaleFactor + 0.5));
35static inline qreal calculateBlurStdDev(
int radius)
41static inline QSize calculateBlurExtent(
int radius)
43 const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
44 return QSize(blurRadius, blurRadius);
61 const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
62 const int z = blurRadius / 3;
68 switch (blurRadius % 3) {
91 Q_ASSERT(major + minor +
final == blurRadius);
113static inline void boxBlurRowAlpha(
const uint8_t *src, uint8_t *dst,
int width,
int horizontalStride,
114 int verticalStride,
const BoxLobes &lobes,
bool transposeInput,
115 bool transposeOutput)
117 const int inputStep = transposeInput ? verticalStride : horizontalStride;
118 const int outputStep = transposeOutput ? verticalStride : horizontalStride;
120 const int boxSize = lobes.left + 1 + lobes.right;
121 const int reciprocal = (1 << 24) / boxSize;
123 uint32_t alphaSum = (boxSize + 1) / 2;
125 const uint8_t *
left = src;
126 const uint8_t *
right = src;
129 const uint8_t firstValue = src[0];
130 const uint8_t lastValue = src[(width - 1) * inputStep];
132 alphaSum += firstValue * lobes.left;
134 const uint8_t *initEnd = src + (boxSize - lobes.left) * inputStep;
135 while (right < initEnd) {
140 const uint8_t *leftEnd = src + boxSize * inputStep;
141 while (right < leftEnd) {
142 *out = (alphaSum * reciprocal) >> 24;
143 alphaSum += *
right - firstValue;
148 const uint8_t *centerEnd = src + width * inputStep;
149 while (right < centerEnd) {
150 *out = (alphaSum * reciprocal) >> 24;
157 const uint8_t *rightEnd = dst + width * outputStep;
158 while (out < rightEnd) {
159 *out = (alphaSum * reciprocal) >> 24;
160 alphaSum += lastValue - *
left;
174static inline void boxBlurAlpha(
QImage &image,
int radius,
const QRect &rect = {})
185 const int width = blurRect.
width();
186 const int height = blurRect.
height();
188 const int pixelStride = image.
depth() >> 3;
190 const int bufferStride = qMax(width, height) * pixelStride;
192 uint8_t *buf1 = buf.data();
193 uint8_t *buf2 = buf1 + bufferStride;
196 for (
int i = 0; i < height; ++i) {
197 uint8_t *row = image.
scanLine(blurRect.
y() + i) + blurRect.
x() * pixelStride + alphaOffset;
198 boxBlurRowAlpha(row, buf1, width, pixelStride, rowStride, lobes[0],
false,
false);
199 boxBlurRowAlpha(buf1, buf2, width, pixelStride, rowStride, lobes[1],
false,
false);
200 boxBlurRowAlpha(buf2, row, width, pixelStride, rowStride, lobes[2],
false,
false);
204 for (
int i = 0; i < width; ++i) {
205 uint8_t *column = image.
scanLine(blurRect.
y()) + (blurRect.
x() + i) * pixelStride + alphaOffset;
206 boxBlurRowAlpha(column, buf1, height, pixelStride, rowStride, lobes[0],
true,
false);
207 boxBlurRowAlpha(buf1, buf2, height, pixelStride, rowStride, lobes[1],
false,
false);
208 boxBlurRowAlpha(buf2, column, height, pixelStride, rowStride, lobes[2],
false,
true);
212static inline void mirrorTopLeftQuadrant(
QImage &image)
214 const int width = image.
width();
215 const int height = image.
height();
217 const int centerX = qCeil(width * 0.5);
218 const int centerY = qCeil(height * 0.5);
221 const int stride = image.
depth() >> 3;
223 for (
int y = 0; y < centerY; ++y) {
224 uint8_t *in = image.
scanLine(y) + alphaOffset;
225 uint8_t *out = in + (width - 1) * stride;
227 for (
int x = 0; x < centerX; ++x, in += stride, out -= stride) {
232 for (
int y = 0; y < centerY; ++y) {
233 const uint8_t *in = image.
scanLine(y) + alphaOffset;
234 uint8_t *out = image.
scanLine(width - y - 1) + alphaOffset;
236 for (
int x = 0; x < width; ++x, in += stride, out += stride) {
242static void renderShadow(
QPainter *painter,
const QRect &rect, qreal borderRadius,
const QPoint &offset,
int radius,
const QColor &color)
244 const QSize inflation = calculateBlurExtent(radius);
245 const QSize size = rect.
size() + 2 * inflation;
250 shadow.setDevicePixelRatio(dpr);
256 const qreal xRadius = 2.0 * borderRadius / boxRect.width();
257 const qreal yRadius = 2.0 * borderRadius / boxRect.height();
260 shadowPainter.
begin(&shadow);
269 const QRect blurRect(0, 0, qCeil(shadow.width() * 0.5), qCeil(shadow.height() * 0.5));
270 const int scaledRadius = qRound(radius * dpr);
271 boxBlurAlpha(shadow, scaledRadius, blurRect);
272 mirrorTopLeftQuadrant(shadow);
275 shadowPainter.
begin(&shadow);
277 shadowPainter.
fillRect(shadow.rect(), color);
281 QRect shadowRect = shadow.rect();
287void BoxShadowRenderer::setBoxSize(
const QSize &size)
292void BoxShadowRenderer::setBorderRadius(qreal radius)
294 m_borderRadius = radius;
297void BoxShadowRenderer::setDevicePixelRatio(qreal dpr)
302void BoxShadowRenderer::addShadow(
const QPoint &offset,
int radius,
const QColor &color)
305 shadow.offset = offset;
306 shadow.radius = radius;
307 shadow.color = color;
308 m_shadows.append(shadow);
311QImage BoxShadowRenderer::render()
const
313 if (m_shadows.isEmpty()) {
318 for (
const Shadow &shadow : qAsConst(m_shadows)) {
320 calculateMinimumShadowTextureSize(m_boxSize, shadow.radius, shadow.offset));
324 canvas.setDevicePixelRatio(m_dpr);
331 for (
const Shadow &shadow : qAsConst(m_shadows)) {
332 renderShadow(&painter, boxRect, m_borderRadius, shadow.offset, shadow.radius, shadow.color);
339QSize BoxShadowRenderer::calculateMinimumBoxSize(
int radius)
341 const QSize blurExtent = calculateBlurExtent(radius);
342 return 2 * blurExtent +
QSize(1, 1);
345QSize BoxShadowRenderer::calculateMinimumShadowTextureSize(
const QSize &boxSize,
int radius,
const QPoint &offset)
347 return boxSize + 2 * calculateBlurExtent(radius) +
QSize(qAbs(offset.
x()), qAbs(offset.
y()));
qsizetype bytesPerLine() const const
qreal devicePixelRatioF() const const
bool begin(QPaintDevice *device)
QPaintDevice * device() const const
void drawImage(const QPoint &point, const QImage &image)
void drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
void fillRect(const QRect &rectangle, QGradient::Preset preset)
void setBrush(Qt::BrushStyle style)
void setCompositionMode(CompositionMode mode)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
QPoint center() const const
bool isNull() const const
void moveCenter(const QPoint &position)
void setSize(const QSize &size)
QSize expandedTo(const QSize &otherSize) const const
QTextStream & center(QTextStream &stream)
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)