Add files from breeze10
This commit is contained in:
parent
4b084789fd
commit
8fb2644d49
45 changed files with 6935 additions and 0 deletions
29
libbreezecommon/CMakeLists.txt
Normal file
29
libbreezecommon/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
################# dependencies #################
|
||||
|
||||
### Qt/KDE
|
||||
find_package(Qt5 REQUIRED CONFIG COMPONENTS Widgets)
|
||||
|
||||
################# configuration #################
|
||||
configure_file(config-breezecommon.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-breezecommon.h )
|
||||
|
||||
################# breezestyle target #################
|
||||
set(breeze10common_LIB_SRCS
|
||||
breezeboxshadowrenderer.cpp
|
||||
)
|
||||
|
||||
add_library(breeze10common5 ${breeze10common_LIB_SRCS})
|
||||
|
||||
generate_export_header(breeze10common5
|
||||
BASE_NAME breezecommon
|
||||
EXPORT_FILE_NAME breezecommon_export.h)
|
||||
|
||||
target_link_libraries(breeze10common5
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
Qt5::Gui)
|
||||
|
||||
set_target_properties(breeze10common5 PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR})
|
||||
|
||||
install(TARGETS breeze10common5 ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP)
|
358
libbreezecommon/breezeboxshadowrenderer.cpp
Normal file
358
libbreezecommon/breezeboxshadowrenderer.cpp
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* The box blur implementation is based on AlphaBoxBlur from Firefox.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
// own
|
||||
#include "breezeboxshadowrenderer.h"
|
||||
|
||||
// auto-generated
|
||||
#include "config-breezecommon.h"
|
||||
|
||||
// Qt
|
||||
#include <QPainter>
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
namespace Breeze
|
||||
{
|
||||
|
||||
static inline int calculateBlurRadius(qreal stdDev)
|
||||
{
|
||||
// See https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
||||
const qreal gaussianScaleFactor = (3.0 * qSqrt(2.0 * M_PI) / 4.0) * 1.5;
|
||||
return qMax(2, qFloor(stdDev * gaussianScaleFactor + 0.5));
|
||||
}
|
||||
|
||||
static inline qreal calculateBlurStdDev(int radius)
|
||||
{
|
||||
// See https://www.w3.org/TR/css-backgrounds-3/#shadow-blur
|
||||
return radius * 0.5;
|
||||
}
|
||||
|
||||
static inline QSize calculateBlurExtent(int radius)
|
||||
{
|
||||
const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
|
||||
return QSize(blurRadius, blurRadius);
|
||||
}
|
||||
|
||||
struct BoxLobes
|
||||
{
|
||||
int left; ///< how many pixels sample to the left
|
||||
int right; ///< how many pixels sample to the right
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute box filter parameters.
|
||||
*
|
||||
* @param radius The blur radius.
|
||||
* @returns Parameters for three box filters.
|
||||
**/
|
||||
static QVector<BoxLobes> computeLobes(int radius)
|
||||
{
|
||||
const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
|
||||
const int z = blurRadius / 3;
|
||||
|
||||
int major;
|
||||
int minor;
|
||||
int final;
|
||||
|
||||
switch (blurRadius % 3) {
|
||||
case 0:
|
||||
major = z;
|
||||
minor = z;
|
||||
final = z;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
major = z + 1;
|
||||
minor = z;
|
||||
final = z;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
major = z + 1;
|
||||
minor = z;
|
||||
final = z + 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT(major + minor + final == blurRadius);
|
||||
|
||||
return {
|
||||
{major, minor},
|
||||
{minor, major},
|
||||
{final, final}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a row with a box filter.
|
||||
*
|
||||
* @param src The start of the row.
|
||||
* @param dst The destination.
|
||||
* @param width The width of the row, in pixels.
|
||||
* @param horizontalStride The number of bytes from one alpha value to the
|
||||
* next alpha value.
|
||||
* @param verticalStride The number of bytes from one row to the next row.
|
||||
* @param lobes Params of the box filter.
|
||||
* @param transposeInput Whether the input is transposed.
|
||||
* @param transposeOutput Whether the output should be transposed.
|
||||
**/
|
||||
static inline void boxBlurRowAlpha(const uint8_t *src, uint8_t *dst, int width, int horizontalStride,
|
||||
int verticalStride, const BoxLobes &lobes, bool transposeInput,
|
||||
bool transposeOutput)
|
||||
{
|
||||
const int inputStep = transposeInput ? verticalStride : horizontalStride;
|
||||
const int outputStep = transposeOutput ? verticalStride : horizontalStride;
|
||||
|
||||
const int boxSize = lobes.left + 1 + lobes.right;
|
||||
const int reciprocal = (1 << 24) / boxSize;
|
||||
|
||||
uint32_t alphaSum = (boxSize + 1) / 2;
|
||||
|
||||
const uint8_t *left = src;
|
||||
const uint8_t *right = src;
|
||||
uint8_t *out = dst;
|
||||
|
||||
const uint8_t firstValue = src[0];
|
||||
const uint8_t lastValue = src[(width - 1) * inputStep];
|
||||
|
||||
alphaSum += firstValue * lobes.left;
|
||||
|
||||
const uint8_t *initEnd = src + (boxSize - lobes.left) * inputStep;
|
||||
while (right < initEnd) {
|
||||
alphaSum += *right;
|
||||
right += inputStep;
|
||||
}
|
||||
|
||||
const uint8_t *leftEnd = src + boxSize * inputStep;
|
||||
while (right < leftEnd) {
|
||||
*out = (alphaSum * reciprocal) >> 24;
|
||||
alphaSum += *right - firstValue;
|
||||
right += inputStep;
|
||||
out += outputStep;
|
||||
}
|
||||
|
||||
const uint8_t *centerEnd = src + width * inputStep;
|
||||
while (right < centerEnd) {
|
||||
*out = (alphaSum * reciprocal) >> 24;
|
||||
alphaSum += *right - *left;
|
||||
left += inputStep;
|
||||
right += inputStep;
|
||||
out += outputStep;
|
||||
}
|
||||
|
||||
const uint8_t *rightEnd = dst + width * outputStep;
|
||||
while (out < rightEnd) {
|
||||
*out = (alphaSum * reciprocal) >> 24;
|
||||
alphaSum += lastValue - *left;
|
||||
left += inputStep;
|
||||
out += outputStep;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur the alpha channel of a given image.
|
||||
*
|
||||
* @param image The input image.
|
||||
* @param radius The blur radius.
|
||||
* @param rect Specifies what part of the image to blur. If nothing is provided, then
|
||||
* the whole alpha channel of the input image will be blurred.
|
||||
**/
|
||||
static inline void boxBlurAlpha(QImage &image, int radius, const QRect &rect = {})
|
||||
{
|
||||
if (radius < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QVector<BoxLobes> lobes = computeLobes(radius);
|
||||
|
||||
const QRect blurRect = rect.isNull() ? image.rect() : rect;
|
||||
|
||||
const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
|
||||
const int width = blurRect.width();
|
||||
const int height = blurRect.height();
|
||||
const int rowStride = image.bytesPerLine();
|
||||
const int pixelStride = image.depth() >> 3;
|
||||
|
||||
const int bufferStride = qMax(width, height) * pixelStride;
|
||||
QScopedPointer<uint8_t, QScopedPointerArrayDeleter<uint8_t> > buf(new uint8_t[2 * bufferStride]);
|
||||
uint8_t *buf1 = buf.data();
|
||||
uint8_t *buf2 = buf1 + bufferStride;
|
||||
|
||||
// Blur the image in horizontal direction.
|
||||
for (int i = 0; i < height; ++i) {
|
||||
uint8_t *row = image.scanLine(blurRect.y() + i) + blurRect.x() * pixelStride + alphaOffset;
|
||||
boxBlurRowAlpha(row, buf1, width, pixelStride, rowStride, lobes[0], false, false);
|
||||
boxBlurRowAlpha(buf1, buf2, width, pixelStride, rowStride, lobes[1], false, false);
|
||||
boxBlurRowAlpha(buf2, row, width, pixelStride, rowStride, lobes[2], false, false);
|
||||
}
|
||||
|
||||
// Blur the image in vertical direction.
|
||||
for (int i = 0; i < width; ++i) {
|
||||
uint8_t *column = image.scanLine(blurRect.y()) + (blurRect.x() + i) * pixelStride + alphaOffset;
|
||||
boxBlurRowAlpha(column, buf1, height, pixelStride, rowStride, lobes[0], true, false);
|
||||
boxBlurRowAlpha(buf1, buf2, height, pixelStride, rowStride, lobes[1], false, false);
|
||||
boxBlurRowAlpha(buf2, column, height, pixelStride, rowStride, lobes[2], false, true);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mirrorTopLeftQuadrant(QImage &image)
|
||||
{
|
||||
const int width = image.width();
|
||||
const int height = image.height();
|
||||
|
||||
const int centerX = qCeil(width * 0.5);
|
||||
const int centerY = qCeil(height * 0.5);
|
||||
|
||||
const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
|
||||
const int stride = image.depth() >> 3;
|
||||
|
||||
for (int y = 0; y < centerY; ++y) {
|
||||
uint8_t *in = image.scanLine(y) + alphaOffset;
|
||||
uint8_t *out = in + (width - 1) * stride;
|
||||
|
||||
for (int x = 0; x < centerX; ++x, in += stride, out -= stride) {
|
||||
*out = *in;
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < centerY; ++y) {
|
||||
const uint8_t *in = image.scanLine(y) + alphaOffset;
|
||||
uint8_t *out = image.scanLine(width - y - 1) + alphaOffset;
|
||||
|
||||
for (int x = 0; x < width; ++x, in += stride, out += stride) {
|
||||
*out = *in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void renderShadow(QPainter *painter, const QRect &rect, qreal borderRadius, const QPoint &offset, int radius, const QColor &color)
|
||||
{
|
||||
const QSize inflation = calculateBlurExtent(radius);
|
||||
const QSize size = rect.size() + 2 * inflation;
|
||||
|
||||
const qreal dpr = painter->device()->devicePixelRatioF();
|
||||
|
||||
QImage shadow(size * dpr, QImage::Format_ARGB32_Premultiplied);
|
||||
shadow.setDevicePixelRatio(dpr);
|
||||
shadow.fill(Qt::transparent);
|
||||
|
||||
QRect boxRect(QPoint(0, 0), rect.size());
|
||||
boxRect.moveCenter(QRect(QPoint(0, 0), size).center());
|
||||
|
||||
const qreal xRadius = 2.0 * borderRadius / boxRect.width();
|
||||
const qreal yRadius = 2.0 * borderRadius / boxRect.height();
|
||||
|
||||
QPainter shadowPainter;
|
||||
shadowPainter.begin(&shadow);
|
||||
shadowPainter.setRenderHint(QPainter::Antialiasing);
|
||||
shadowPainter.setPen(Qt::NoPen);
|
||||
shadowPainter.setBrush(Qt::black);
|
||||
shadowPainter.drawRoundedRect(boxRect, xRadius, yRadius);
|
||||
shadowPainter.end();
|
||||
|
||||
// Because the shadow texture is symmetrical, that's enough to blur
|
||||
// only the top-left quadrant and then mirror it.
|
||||
const QRect blurRect(0, 0, qCeil(shadow.width() * 0.5), qCeil(shadow.height() * 0.5));
|
||||
const int scaledRadius = qRound(radius * dpr);
|
||||
boxBlurAlpha(shadow, scaledRadius, blurRect);
|
||||
mirrorTopLeftQuadrant(shadow);
|
||||
|
||||
// Give the shadow a tint of the desired color.
|
||||
shadowPainter.begin(&shadow);
|
||||
shadowPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
shadowPainter.fillRect(shadow.rect(), color);
|
||||
shadowPainter.end();
|
||||
|
||||
// Actually, present the shadow.
|
||||
QRect shadowRect = shadow.rect();
|
||||
shadowRect.setSize(shadowRect.size() / dpr);
|
||||
shadowRect.moveCenter(rect.center() + offset);
|
||||
painter->drawImage(shadowRect, shadow);
|
||||
}
|
||||
|
||||
void BoxShadowRenderer::setBoxSize(const QSize &size)
|
||||
{
|
||||
m_boxSize = size;
|
||||
}
|
||||
|
||||
void BoxShadowRenderer::setBorderRadius(qreal radius)
|
||||
{
|
||||
m_borderRadius = radius;
|
||||
}
|
||||
|
||||
void BoxShadowRenderer::setDevicePixelRatio(qreal dpr)
|
||||
{
|
||||
m_dpr = dpr;
|
||||
}
|
||||
|
||||
void BoxShadowRenderer::addShadow(const QPoint &offset, int radius, const QColor &color)
|
||||
{
|
||||
Shadow shadow = {};
|
||||
shadow.offset = offset;
|
||||
shadow.radius = radius;
|
||||
shadow.color = color;
|
||||
m_shadows.append(shadow);
|
||||
}
|
||||
|
||||
QImage BoxShadowRenderer::render() const
|
||||
{
|
||||
if (m_shadows.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QSize canvasSize;
|
||||
for (const Shadow &shadow : qAsConst(m_shadows)) {
|
||||
canvasSize = canvasSize.expandedTo(
|
||||
calculateMinimumShadowTextureSize(m_boxSize, shadow.radius, shadow.offset));
|
||||
}
|
||||
|
||||
QImage canvas(canvasSize * m_dpr, QImage::Format_ARGB32_Premultiplied);
|
||||
canvas.setDevicePixelRatio(m_dpr);
|
||||
canvas.fill(Qt::transparent);
|
||||
|
||||
QRect boxRect(QPoint(0, 0), m_boxSize);
|
||||
boxRect.moveCenter(QRect(QPoint(0, 0), canvasSize).center());
|
||||
|
||||
QPainter painter(&canvas);
|
||||
for (const Shadow &shadow : qAsConst(m_shadows)) {
|
||||
renderShadow(&painter, boxRect, m_borderRadius, shadow.offset, shadow.radius, shadow.color);
|
||||
}
|
||||
painter.end();
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
QSize BoxShadowRenderer::calculateMinimumBoxSize(int radius)
|
||||
{
|
||||
const QSize blurExtent = calculateBlurExtent(radius);
|
||||
return 2 * blurExtent + QSize(1, 1);
|
||||
}
|
||||
|
||||
QSize BoxShadowRenderer::calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset)
|
||||
{
|
||||
return boxSize + 2 * calculateBlurExtent(radius) + QSize(qAbs(offset.x()), qAbs(offset.y()));
|
||||
}
|
||||
|
||||
} // namespace Breeze
|
105
libbreezecommon/breezeboxshadowrenderer.h
Normal file
105
libbreezecommon/breezeboxshadowrenderer.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2018 Vlad Zagorodniy <vladzzag@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// own
|
||||
#include "breezecommon_export.h"
|
||||
|
||||
// Qt
|
||||
#include <QColor>
|
||||
#include <QImage>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
|
||||
namespace Breeze
|
||||
{
|
||||
|
||||
class BREEZECOMMON_EXPORT BoxShadowRenderer
|
||||
{
|
||||
public:
|
||||
// Compiler generated constructors & destructor are fine.
|
||||
|
||||
/**
|
||||
* Set the size of the box.
|
||||
* @param size The size of the box.
|
||||
**/
|
||||
void setBoxSize(const QSize &size);
|
||||
|
||||
/**
|
||||
* Set the radius of box' corners.
|
||||
* @param radius The border radius, in pixels.
|
||||
**/
|
||||
void setBorderRadius(qreal radius);
|
||||
|
||||
/**
|
||||
* Set the device pixel ratio of the resulting shadow texture.
|
||||
* @param dpr The device pixel ratio.
|
||||
**/
|
||||
void setDevicePixelRatio(qreal dpr);
|
||||
|
||||
/**
|
||||
* Add a shadow.
|
||||
* @param offset The offset of the shadow.
|
||||
* @param radius The blur radius.
|
||||
* @param color The color of the shadow.
|
||||
**/
|
||||
void addShadow(const QPoint &offset, int radius, const QColor &color);
|
||||
|
||||
/**
|
||||
* Render the shadow.
|
||||
**/
|
||||
QImage render() const;
|
||||
|
||||
/**
|
||||
* Calculate the minimum size of the box.
|
||||
*
|
||||
* This helper computes the minimum size of the box so the shadow behind it has
|
||||
* full its strength.
|
||||
*
|
||||
* @param radius The blur radius of the shadow.
|
||||
**/
|
||||
static QSize calculateMinimumBoxSize(int radius);
|
||||
|
||||
/**
|
||||
* Calculate the minimum size of the shadow texture.
|
||||
*
|
||||
* This helper computes the minimum size of the resulting texture so the shadow
|
||||
* is not clipped.
|
||||
*
|
||||
* @param boxSize The size of the box.
|
||||
* @param radius The blur radius.
|
||||
* @param offset The offset of the shadow.
|
||||
**/
|
||||
static QSize calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset);
|
||||
|
||||
private:
|
||||
QSize m_boxSize;
|
||||
qreal m_borderRadius = 0.0;
|
||||
qreal m_dpr = 1.0;
|
||||
|
||||
struct Shadow {
|
||||
QPoint offset;
|
||||
int radius;
|
||||
QColor color;
|
||||
};
|
||||
|
||||
QVector<Shadow> m_shadows;
|
||||
};
|
||||
|
||||
} // namespace Breeze
|
28
libbreezecommon/config-breezecommon.h.cmake
Normal file
28
libbreezecommon/config-breezecommon.h.cmake
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* config-breezecommon.h. Generated by cmake from config-breezecommon.h.cmake */
|
||||
|
||||
/*************************************************************************
|
||||
* Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr> *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
|
||||
*************************************************************************/
|
||||
|
||||
#ifndef config_breeze_common_h
|
||||
#define config_breeze_common_h
|
||||
|
||||
/* Define to 1 if breeze is compiled against KDE4 */
|
||||
#cmakedefine01 BREEZE_COMMON_USE_KDE4
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue