forked from blue/squawk
a bit of refactor, fix the time to request next portion of messages in ui, fancy shadows are back!
This commit is contained in:
parent
216dcd29e9
commit
05d6761baa
@ -32,7 +32,7 @@ set(squawkUI_SRC
|
|||||||
models/messagefeed.cpp
|
models/messagefeed.cpp
|
||||||
models/element.cpp
|
models/element.cpp
|
||||||
utils/messageline.cpp
|
utils/messageline.cpp
|
||||||
utils//message.cpp
|
utils/message.cpp
|
||||||
utils/resizer.cpp
|
utils/resizer.cpp
|
||||||
utils/image.cpp
|
utils/image.cpp
|
||||||
utils/flowlayout.cpp
|
utils/flowlayout.cpp
|
||||||
@ -42,6 +42,8 @@ set(squawkUI_SRC
|
|||||||
utils/dropshadoweffect.cpp
|
utils/dropshadoweffect.cpp
|
||||||
utils/feedview.cpp
|
utils/feedview.cpp
|
||||||
utils/messagedelegate.cpp
|
utils/messagedelegate.cpp
|
||||||
|
utils/eb.cpp
|
||||||
|
utils/shadowoverlay.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Tell CMake to create the helloworld executable
|
# Tell CMake to create the helloworld executable
|
||||||
|
@ -17,562 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dropshadoweffect.h"
|
#include "dropshadoweffect.h"
|
||||||
#include "QtMath"
|
|
||||||
|
|
||||||
static const int tileSize = 32;
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride)
|
|
||||||
{
|
|
||||||
sstride /= sizeof(T);
|
|
||||||
dstride /= sizeof(T);
|
|
||||||
const int pack = sizeof(quint32) / sizeof(T);
|
|
||||||
const int unaligned =
|
|
||||||
qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h));
|
|
||||||
const int restX = w % tileSize;
|
|
||||||
const int restY = (h - unaligned) % tileSize;
|
|
||||||
const int unoptimizedY = restY % pack;
|
|
||||||
const int numTilesX = w / tileSize + (restX > 0);
|
|
||||||
const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
|
|
||||||
for (int tx = 0; tx < numTilesX; ++tx) {
|
|
||||||
const int startx = w - tx * tileSize - 1;
|
|
||||||
const int stopx = qMax(startx - tileSize, 0);
|
|
||||||
if (unaligned) {
|
|
||||||
for (int x = startx; x >= stopx; --x) {
|
|
||||||
T *d = dest + (w - x - 1) * dstride;
|
|
||||||
for (int y = 0; y < unaligned; ++y) {
|
|
||||||
*d++ = src[y * sstride + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int ty = 0; ty < numTilesY; ++ty) {
|
|
||||||
const int starty = ty * tileSize + unaligned;
|
|
||||||
const int stopy = qMin(starty + tileSize, h - unoptimizedY);
|
|
||||||
for (int x = startx; x >= stopx; --x) {
|
|
||||||
quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty);
|
|
||||||
for (int y = starty; y < stopy; y += pack) {
|
|
||||||
quint32 c = src[y * sstride + x];
|
|
||||||
for (int i = 1; i < pack; ++i) {
|
|
||||||
const int shift = (sizeof(T) * 8 * i);
|
|
||||||
const T color = src[(y + i) * sstride + x];
|
|
||||||
c |= color << shift;
|
|
||||||
}
|
|
||||||
*d++ = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unoptimizedY) {
|
|
||||||
const int starty = h - unoptimizedY;
|
|
||||||
for (int x = startx; x >= stopx; --x) {
|
|
||||||
T *d = dest + (w - x - 1) * dstride + starty;
|
|
||||||
for (int y = starty; y < h; ++y) {
|
|
||||||
*d++ = src[y * sstride + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest,
|
|
||||||
int dstride)
|
|
||||||
{
|
|
||||||
const int numTilesX = (w + tileSize - 1) / tileSize;
|
|
||||||
const int numTilesY = (h + tileSize - 1) / tileSize;
|
|
||||||
for (int tx = 0; tx < numTilesX; ++tx) {
|
|
||||||
const int startx = w - tx * tileSize - 1;
|
|
||||||
const int stopx = qMax(startx - tileSize, 0);
|
|
||||||
for (int ty = 0; ty < numTilesY; ++ty) {
|
|
||||||
const int starty = ty * tileSize;
|
|
||||||
const int stopy = qMin(starty + tileSize, h);
|
|
||||||
for (int x = startx; x >= stopx; --x) {
|
|
||||||
T *d = (T *)((char*)dest + (w - x - 1) * dstride) + starty;
|
|
||||||
const char *s = (const char*)(src + x) + starty * sstride;
|
|
||||||
for (int y = starty; y < stopy; ++y) {
|
|
||||||
*d++ = *(const T *)(s);
|
|
||||||
s += sstride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride)
|
|
||||||
{
|
|
||||||
sstride /= sizeof(T);
|
|
||||||
dstride /= sizeof(T);
|
|
||||||
const int pack = sizeof(quint32) / sizeof(T);
|
|
||||||
const int unaligned =
|
|
||||||
qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h));
|
|
||||||
const int restX = w % tileSize;
|
|
||||||
const int restY = (h - unaligned) % tileSize;
|
|
||||||
const int unoptimizedY = restY % pack;
|
|
||||||
const int numTilesX = w / tileSize + (restX > 0);
|
|
||||||
const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
|
|
||||||
for (int tx = 0; tx < numTilesX; ++tx) {
|
|
||||||
const int startx = tx * tileSize;
|
|
||||||
const int stopx = qMin(startx + tileSize, w);
|
|
||||||
if (unaligned) {
|
|
||||||
for (int x = startx; x < stopx; ++x) {
|
|
||||||
T *d = dest + x * dstride;
|
|
||||||
for (int y = h - 1; y >= h - unaligned; --y) {
|
|
||||||
*d++ = src[y * sstride + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int ty = 0; ty < numTilesY; ++ty) {
|
|
||||||
const int starty = h - 1 - unaligned - ty * tileSize;
|
|
||||||
const int stopy = qMax(starty - tileSize, unoptimizedY);
|
|
||||||
for (int x = startx; x < stopx; ++x) {
|
|
||||||
quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
|
|
||||||
+ h - 1 - starty);
|
|
||||||
for (int y = starty; y >= stopy; y -= pack) {
|
|
||||||
quint32 c = src[y * sstride + x];
|
|
||||||
for (int i = 1; i < pack; ++i) {
|
|
||||||
const int shift = (sizeof(T) * 8 * i);
|
|
||||||
const T color = src[(y - i) * sstride + x];
|
|
||||||
c |= color << shift;
|
|
||||||
}
|
|
||||||
*d++ = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unoptimizedY) {
|
|
||||||
const int starty = unoptimizedY - 1;
|
|
||||||
for (int x = startx; x < stopx; ++x) {
|
|
||||||
T *d = dest + x * dstride + h - 1 - starty;
|
|
||||||
for (int y = starty; y >= 0; --y) {
|
|
||||||
*d++ = src[y * sstride + x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest,
|
|
||||||
int dstride)
|
|
||||||
{
|
|
||||||
const int numTilesX = (w + tileSize - 1) / tileSize;
|
|
||||||
const int numTilesY = (h + tileSize - 1) / tileSize;
|
|
||||||
for (int tx = 0; tx < numTilesX; ++tx) {
|
|
||||||
const int startx = tx * tileSize;
|
|
||||||
const int stopx = qMin(startx + tileSize, w);
|
|
||||||
for (int ty = 0; ty < numTilesY; ++ty) {
|
|
||||||
const int starty = h - 1 - ty * tileSize;
|
|
||||||
const int stopy = qMax(starty - tileSize, 0);
|
|
||||||
for (int x = startx; x < stopx; ++x) {
|
|
||||||
T *d = (T*)((char*)dest + x * dstride) + h - 1 - starty;
|
|
||||||
const char *s = (const char*)(src + x) + starty * sstride;
|
|
||||||
for (int y = starty; y >= stopy; --y) {
|
|
||||||
*d++ = *(const T*)s;
|
|
||||||
s -= sstride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, int srcStride,
|
|
||||||
T *dest, int dstStride)
|
|
||||||
{
|
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
|
||||||
// packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer
|
|
||||||
if (sizeof(quint32) % sizeof(T) == 0)
|
|
||||||
qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
qt_memrotate90_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride)
|
|
||||||
{
|
|
||||||
// packed algorithm doesn't have any benefit for quint32
|
|
||||||
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride)
|
|
||||||
{
|
|
||||||
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride)
|
|
||||||
{
|
|
||||||
const char *s = (const char*)(src) + (h - 1) * sstride;
|
|
||||||
for (int dy = 0; dy < h; ++dy) {
|
|
||||||
T *d = reinterpret_cast<T*>((char *)(dest) + dy * dstride);
|
|
||||||
src = reinterpret_cast<const T*>(s);
|
|
||||||
for (int dx = 0; dx < w; ++dx) {
|
|
||||||
d[dx] = src[w - 1 - dx];
|
|
||||||
}
|
|
||||||
s -= sstride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class T>
|
|
||||||
static
|
|
||||||
inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, int srcStride,
|
|
||||||
T *dest, int dstStride)
|
|
||||||
{
|
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
|
||||||
// packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer
|
|
||||||
if (sizeof(quint32) % sizeof(T) == 0)
|
|
||||||
qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride)
|
|
||||||
{
|
|
||||||
// packed algorithm doesn't have any benefit for quint32
|
|
||||||
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
inline void qt_memrotate270_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride)
|
|
||||||
{
|
|
||||||
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
|
||||||
}
|
|
||||||
#define QT_IMPL_MEMROTATE(type) \
|
|
||||||
void qt_memrotate90(const type *src, int w, int h, int sstride, \
|
|
||||||
type *dest, int dstride) \
|
|
||||||
{ \
|
|
||||||
qt_memrotate90_template(src, w, h, sstride, dest, dstride); \
|
|
||||||
} \
|
|
||||||
void qt_memrotate180(const type *src, int w, int h, int sstride, \
|
|
||||||
type *dest, int dstride) \
|
|
||||||
{ \
|
|
||||||
qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
|
|
||||||
} \
|
|
||||||
void qt_memrotate270(const type *src, int w, int h, int sstride, \
|
|
||||||
type *dest, int dstride) \
|
|
||||||
{ \
|
|
||||||
qt_memrotate270_template(src, w, h, sstride, dest, dstride); \
|
|
||||||
}
|
|
||||||
#define QT_IMPL_SIMPLE_MEMROTATE(type) \
|
|
||||||
void qt_memrotate90(const type *src, int w, int h, int sstride, \
|
|
||||||
type *dest, int dstride) \
|
|
||||||
{ \
|
|
||||||
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); \
|
|
||||||
} \
|
|
||||||
void qt_memrotate180(const type *src, int w, int h, int sstride, \
|
|
||||||
type *dest, int dstride) \
|
|
||||||
{ \
|
|
||||||
qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
|
|
||||||
} \
|
|
||||||
void qt_memrotate270(const type *src, int w, int h, int sstride, \
|
|
||||||
type *dest, int dstride) \
|
|
||||||
{ \
|
|
||||||
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \
|
|
||||||
}
|
|
||||||
QT_IMPL_MEMROTATE(quint64)
|
|
||||||
QT_IMPL_MEMROTATE(quint32)
|
|
||||||
QT_IMPL_MEMROTATE(quint16)
|
|
||||||
QT_IMPL_MEMROTATE(quint8)
|
|
||||||
void qt_memrotate90_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate90(srcPixels, w, h, sbpl, destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate180_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate180(srcPixels, w, h, sbpl, destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate270_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate270(srcPixels, w, h, sbpl, destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate90_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate90((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate180_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate180((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate270((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate90((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate180_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate180((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate90_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate90((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate180_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate180((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
|
||||||
{
|
|
||||||
qt_memrotate270((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
|
|
||||||
#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
|
|
||||||
const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
|
|
||||||
|
|
||||||
QImage qt_halfScaled(const QImage &source)
|
|
||||||
{
|
|
||||||
if (source.width() < 2 || source.height() < 2)
|
|
||||||
return QImage();
|
|
||||||
|
|
||||||
QImage srcImage = source;
|
|
||||||
|
|
||||||
if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
|
|
||||||
// assumes grayscale
|
|
||||||
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
|
||||||
dest.setDevicePixelRatio(source.devicePixelRatioF());
|
|
||||||
|
|
||||||
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
|
|
||||||
qsizetype sx = srcImage.bytesPerLine();
|
|
||||||
qsizetype sx2 = sx << 1;
|
|
||||||
|
|
||||||
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
|
|
||||||
qsizetype dx = dest.bytesPerLine();
|
|
||||||
int ww = dest.width();
|
|
||||||
int hh = dest.height();
|
|
||||||
|
|
||||||
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
|
||||||
const uchar *p1 = src;
|
|
||||||
const uchar *p2 = src + sx;
|
|
||||||
uchar *q = dst;
|
|
||||||
for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
|
|
||||||
*q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
} else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
|
|
||||||
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
|
||||||
dest.setDevicePixelRatio(source.devicePixelRatioF());
|
|
||||||
|
|
||||||
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
|
|
||||||
qsizetype sx = srcImage.bytesPerLine();
|
|
||||||
qsizetype sx2 = sx << 1;
|
|
||||||
|
|
||||||
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
|
|
||||||
qsizetype dx = dest.bytesPerLine();
|
|
||||||
int ww = dest.width();
|
|
||||||
int hh = dest.height();
|
|
||||||
|
|
||||||
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
|
||||||
const uchar *p1 = src;
|
|
||||||
const uchar *p2 = src + sx;
|
|
||||||
uchar *q = dst;
|
|
||||||
for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
|
|
||||||
// alpha
|
|
||||||
q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
|
|
||||||
// rgb
|
|
||||||
const quint16 p16_1 = (p1[2] << 8) | p1[1];
|
|
||||||
const quint16 p16_2 = (p1[5] << 8) | p1[4];
|
|
||||||
const quint16 p16_3 = (p2[2] << 8) | p2[1];
|
|
||||||
const quint16 p16_4 = (p2[5] << 8) | p2[4];
|
|
||||||
const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
|
|
||||||
q[1] = result & 0xff;
|
|
||||||
q[2] = result >> 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
} else if (source.format() != QImage::Format_ARGB32_Premultiplied
|
|
||||||
&& source.format() != QImage::Format_RGB32)
|
|
||||||
{
|
|
||||||
srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
|
||||||
dest.setDevicePixelRatio(source.devicePixelRatioF());
|
|
||||||
|
|
||||||
const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
|
|
||||||
qsizetype sx = srcImage.bytesPerLine() >> 2;
|
|
||||||
qsizetype sx2 = sx << 1;
|
|
||||||
|
|
||||||
quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
|
|
||||||
qsizetype dx = dest.bytesPerLine() >> 2;
|
|
||||||
int ww = dest.width();
|
|
||||||
int hh = dest.height();
|
|
||||||
|
|
||||||
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
|
||||||
const quint32 *p1 = src;
|
|
||||||
const quint32 *p2 = src + sx;
|
|
||||||
quint32 *q = dst;
|
|
||||||
for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
|
|
||||||
*q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int shift>
|
|
||||||
inline int qt_static_shift(int value)
|
|
||||||
{
|
|
||||||
if (shift == 0)
|
|
||||||
return value;
|
|
||||||
else if (shift > 0)
|
|
||||||
return value << (uint(shift) & 0x1f);
|
|
||||||
else
|
|
||||||
return value >> (uint(-shift) & 0x1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int aprec, int zprec>
|
|
||||||
inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
|
|
||||||
{
|
|
||||||
QRgb *pixel = (QRgb *)bptr;
|
|
||||||
|
|
||||||
#define Z_MASK (0xff << zprec)
|
|
||||||
const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
|
|
||||||
const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
|
|
||||||
const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
|
|
||||||
const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
|
|
||||||
#undef Z_MASK
|
|
||||||
|
|
||||||
const int zR_zprec = zR >> aprec;
|
|
||||||
const int zG_zprec = zG >> aprec;
|
|
||||||
const int zB_zprec = zB >> aprec;
|
|
||||||
const int zA_zprec = zA >> aprec;
|
|
||||||
|
|
||||||
zR += alpha * (R_zprec - zR_zprec);
|
|
||||||
zG += alpha * (G_zprec - zG_zprec);
|
|
||||||
zB += alpha * (B_zprec - zB_zprec);
|
|
||||||
zA += alpha * (A_zprec - zA_zprec);
|
|
||||||
|
|
||||||
#define ZA_MASK (0xff << (zprec + aprec))
|
|
||||||
*pixel =
|
|
||||||
qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
|
|
||||||
| qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
|
|
||||||
| qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
|
|
||||||
| qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
|
|
||||||
#undef ZA_MASK
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int aprec, int zprec>
|
|
||||||
inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
|
|
||||||
{
|
|
||||||
const int A_zprec = int(*(bptr)) << zprec;
|
|
||||||
const int z_zprec = z >> aprec;
|
|
||||||
z += alpha * (A_zprec - z_zprec);
|
|
||||||
*(bptr) = z >> (zprec + aprec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int aprec, int zprec, bool alphaOnly>
|
|
||||||
inline void qt_blurrow(QImage & im, int line, int alpha)
|
|
||||||
{
|
|
||||||
uchar *bptr = im.scanLine(line);
|
|
||||||
|
|
||||||
int zR = 0, zG = 0, zB = 0, zA = 0;
|
|
||||||
|
|
||||||
if (alphaOnly && im.format() != QImage::Format_Indexed8)
|
|
||||||
bptr += alphaIndex;
|
|
||||||
|
|
||||||
const int stride = im.depth() >> 3;
|
|
||||||
const int im_width = im.width();
|
|
||||||
for (int index = 0; index < im_width; ++index) {
|
|
||||||
if (alphaOnly)
|
|
||||||
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
|
|
||||||
else
|
|
||||||
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
|
|
||||||
bptr += stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
bptr -= stride;
|
|
||||||
|
|
||||||
for (int index = im_width - 2; index >= 0; --index) {
|
|
||||||
bptr -= stride;
|
|
||||||
if (alphaOnly)
|
|
||||||
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
|
|
||||||
else
|
|
||||||
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int aprec, int zprec, bool alphaOnly>
|
|
||||||
void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0)
|
|
||||||
{
|
|
||||||
// halve the radius if we're using two passes
|
|
||||||
if (improvedQuality)
|
|
||||||
radius *= qreal(0.5);
|
|
||||||
|
|
||||||
Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied
|
|
||||||
|| img.format() == QImage::Format_RGB32
|
|
||||||
|| img.format() == QImage::Format_Indexed8
|
|
||||||
|| img.format() == QImage::Format_Grayscale8);
|
|
||||||
|
|
||||||
// choose the alpha such that pixels at radius distance from a fully
|
|
||||||
// saturated pixel will have an alpha component of no greater than
|
|
||||||
// the cutOffIntensity
|
|
||||||
const qreal cutOffIntensity = 2;
|
|
||||||
int alpha = radius <= qreal(1e-5)
|
|
||||||
? ((1 << aprec)-1)
|
|
||||||
: qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius)));
|
|
||||||
|
|
||||||
int img_height = img.height();
|
|
||||||
for (int row = 0; row < img_height; ++row) {
|
|
||||||
for (int i = 0; i <= int(improvedQuality); ++i)
|
|
||||||
qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage temp(img.height(), img.width(), img.format());
|
|
||||||
temp.setDevicePixelRatio(img.devicePixelRatioF());
|
|
||||||
if (transposed >= 0) {
|
|
||||||
if (img.depth() == 8) {
|
|
||||||
qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
|
|
||||||
img.width(), img.height(), img.bytesPerLine(),
|
|
||||||
reinterpret_cast<quint8*>(temp.bits()),
|
|
||||||
temp.bytesPerLine());
|
|
||||||
} else {
|
|
||||||
qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
|
|
||||||
img.width(), img.height(), img.bytesPerLine(),
|
|
||||||
reinterpret_cast<quint32*>(temp.bits()),
|
|
||||||
temp.bytesPerLine());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (img.depth() == 8) {
|
|
||||||
qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
|
|
||||||
img.width(), img.height(), img.bytesPerLine(),
|
|
||||||
reinterpret_cast<quint8*>(temp.bits()),
|
|
||||||
temp.bytesPerLine());
|
|
||||||
} else {
|
|
||||||
qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
|
|
||||||
img.width(), img.height(), img.bytesPerLine(),
|
|
||||||
reinterpret_cast<quint32*>(temp.bits()),
|
|
||||||
temp.bytesPerLine());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img_height = temp.height();
|
|
||||||
for (int row = 0; row < img_height; ++row) {
|
|
||||||
for (int i = 0; i <= int(improvedQuality); ++i)
|
|
||||||
qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transposed == 0) {
|
|
||||||
if (img.depth() == 8) {
|
|
||||||
qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
|
|
||||||
temp.width(), temp.height(), temp.bytesPerLine(),
|
|
||||||
reinterpret_cast<quint8*>(img.bits()),
|
|
||||||
img.bytesPerLine());
|
|
||||||
} else {
|
|
||||||
qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
|
|
||||||
temp.width(), temp.height(), temp.bytesPerLine(),
|
|
||||||
reinterpret_cast<quint32*>(img.bits()),
|
|
||||||
img.bytesPerLine());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
img = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PixmapFilter::PixmapFilter(QObject* parent):QObject(parent) {}
|
PixmapFilter::PixmapFilter(QObject* parent):QObject(parent) {}
|
||||||
PixmapFilter::~PixmapFilter(){}
|
PixmapFilter::~PixmapFilter(){}
|
||||||
@ -640,7 +84,7 @@ void PixmapDropShadowFilter::draw(QPainter *p, const QPointF &pos, const QPixmap
|
|||||||
tmpPainter.fillRect(shadow, mColor);
|
tmpPainter.fillRect(shadow, mColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
expblur<12, 10, false>(tmp, mRadius, false, 0);
|
Utils::exponentialblur(tmp, mRadius, false, 0);
|
||||||
tmpPainter.end();
|
tmpPainter.end();
|
||||||
|
|
||||||
// Draw the actual pixmap...
|
// Draw the actual pixmap...
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
|
||||||
|
#include "eb.h"
|
||||||
|
|
||||||
class PixmapFilter : public QObject
|
class PixmapFilter : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
579
ui/utils/eb.cpp
Normal file
579
ui/utils/eb.cpp
Normal file
@ -0,0 +1,579 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "eb.h"
|
||||||
|
|
||||||
|
static const int tileSize = 32;
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate90_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride)
|
||||||
|
{
|
||||||
|
sstride /= sizeof(T);
|
||||||
|
dstride /= sizeof(T);
|
||||||
|
const int pack = sizeof(quint32) / sizeof(T);
|
||||||
|
const int unaligned =
|
||||||
|
qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h));
|
||||||
|
const int restX = w % tileSize;
|
||||||
|
const int restY = (h - unaligned) % tileSize;
|
||||||
|
const int unoptimizedY = restY % pack;
|
||||||
|
const int numTilesX = w / tileSize + (restX > 0);
|
||||||
|
const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
|
||||||
|
for (int tx = 0; tx < numTilesX; ++tx) {
|
||||||
|
const int startx = w - tx * tileSize - 1;
|
||||||
|
const int stopx = qMax(startx - tileSize, 0);
|
||||||
|
if (unaligned) {
|
||||||
|
for (int x = startx; x >= stopx; --x) {
|
||||||
|
T *d = dest + (w - x - 1) * dstride;
|
||||||
|
for (int y = 0; y < unaligned; ++y) {
|
||||||
|
*d++ = src[y * sstride + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int ty = 0; ty < numTilesY; ++ty) {
|
||||||
|
const int starty = ty * tileSize + unaligned;
|
||||||
|
const int stopy = qMin(starty + tileSize, h - unoptimizedY);
|
||||||
|
for (int x = startx; x >= stopx; --x) {
|
||||||
|
quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty);
|
||||||
|
for (int y = starty; y < stopy; y += pack) {
|
||||||
|
quint32 c = src[y * sstride + x];
|
||||||
|
for (int i = 1; i < pack; ++i) {
|
||||||
|
const int shift = (sizeof(T) * 8 * i);
|
||||||
|
const T color = src[(y + i) * sstride + x];
|
||||||
|
c |= color << shift;
|
||||||
|
}
|
||||||
|
*d++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unoptimizedY) {
|
||||||
|
const int starty = h - unoptimizedY;
|
||||||
|
for (int x = startx; x >= stopx; --x) {
|
||||||
|
T *d = dest + (w - x - 1) * dstride + starty;
|
||||||
|
for (int y = starty; y < h; ++y) {
|
||||||
|
*d++ = src[y * sstride + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate90_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest,
|
||||||
|
int dstride)
|
||||||
|
{
|
||||||
|
const int numTilesX = (w + tileSize - 1) / tileSize;
|
||||||
|
const int numTilesY = (h + tileSize - 1) / tileSize;
|
||||||
|
for (int tx = 0; tx < numTilesX; ++tx) {
|
||||||
|
const int startx = w - tx * tileSize - 1;
|
||||||
|
const int stopx = qMax(startx - tileSize, 0);
|
||||||
|
for (int ty = 0; ty < numTilesY; ++ty) {
|
||||||
|
const int starty = ty * tileSize;
|
||||||
|
const int stopy = qMin(starty + tileSize, h);
|
||||||
|
for (int x = startx; x >= stopx; --x) {
|
||||||
|
T *d = (T *)((char*)dest + (w - x - 1) * dstride) + starty;
|
||||||
|
const char *s = (const char*)(src + x) + starty * sstride;
|
||||||
|
for (int y = starty; y < stopy; ++y) {
|
||||||
|
*d++ = *(const T *)(s);
|
||||||
|
s += sstride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate270_tiled(const T *src, int w, int h, int sstride, T *dest, int dstride)
|
||||||
|
{
|
||||||
|
sstride /= sizeof(T);
|
||||||
|
dstride /= sizeof(T);
|
||||||
|
const int pack = sizeof(quint32) / sizeof(T);
|
||||||
|
const int unaligned =
|
||||||
|
qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(T)), uint(h));
|
||||||
|
const int restX = w % tileSize;
|
||||||
|
const int restY = (h - unaligned) % tileSize;
|
||||||
|
const int unoptimizedY = restY % pack;
|
||||||
|
const int numTilesX = w / tileSize + (restX > 0);
|
||||||
|
const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
|
||||||
|
for (int tx = 0; tx < numTilesX; ++tx) {
|
||||||
|
const int startx = tx * tileSize;
|
||||||
|
const int stopx = qMin(startx + tileSize, w);
|
||||||
|
if (unaligned) {
|
||||||
|
for (int x = startx; x < stopx; ++x) {
|
||||||
|
T *d = dest + x * dstride;
|
||||||
|
for (int y = h - 1; y >= h - unaligned; --y) {
|
||||||
|
*d++ = src[y * sstride + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int ty = 0; ty < numTilesY; ++ty) {
|
||||||
|
const int starty = h - 1 - unaligned - ty * tileSize;
|
||||||
|
const int stopy = qMax(starty - tileSize, unoptimizedY);
|
||||||
|
for (int x = startx; x < stopx; ++x) {
|
||||||
|
quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
|
||||||
|
+ h - 1 - starty);
|
||||||
|
for (int y = starty; y >= stopy; y -= pack) {
|
||||||
|
quint32 c = src[y * sstride + x];
|
||||||
|
for (int i = 1; i < pack; ++i) {
|
||||||
|
const int shift = (sizeof(T) * 8 * i);
|
||||||
|
const T color = src[(y - i) * sstride + x];
|
||||||
|
c |= color << shift;
|
||||||
|
}
|
||||||
|
*d++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unoptimizedY) {
|
||||||
|
const int starty = unoptimizedY - 1;
|
||||||
|
for (int x = startx; x < stopx; ++x) {
|
||||||
|
T *d = dest + x * dstride + h - 1 - starty;
|
||||||
|
for (int y = starty; y >= 0; --y) {
|
||||||
|
*d++ = src[y * sstride + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate270_tiled_unpacked(const T *src, int w, int h, int sstride, T *dest,
|
||||||
|
int dstride)
|
||||||
|
{
|
||||||
|
const int numTilesX = (w + tileSize - 1) / tileSize;
|
||||||
|
const int numTilesY = (h + tileSize - 1) / tileSize;
|
||||||
|
for (int tx = 0; tx < numTilesX; ++tx) {
|
||||||
|
const int startx = tx * tileSize;
|
||||||
|
const int stopx = qMin(startx + tileSize, w);
|
||||||
|
for (int ty = 0; ty < numTilesY; ++ty) {
|
||||||
|
const int starty = h - 1 - ty * tileSize;
|
||||||
|
const int stopy = qMax(starty - tileSize, 0);
|
||||||
|
for (int x = startx; x < stopx; ++x) {
|
||||||
|
T *d = (T*)((char*)dest + x * dstride) + h - 1 - starty;
|
||||||
|
const char *s = (const char*)(src + x) + starty * sstride;
|
||||||
|
for (int y = starty; y >= stopy; --y) {
|
||||||
|
*d++ = *(const T*)s;
|
||||||
|
s -= sstride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, int srcStride,
|
||||||
|
T *dest, int dstStride)
|
||||||
|
{
|
||||||
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
|
// packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer
|
||||||
|
if (sizeof(quint32) % sizeof(T) == 0)
|
||||||
|
qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
qt_memrotate90_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride)
|
||||||
|
{
|
||||||
|
// packed algorithm doesn't have any benefit for quint32
|
||||||
|
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride)
|
||||||
|
{
|
||||||
|
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride)
|
||||||
|
{
|
||||||
|
const char *s = (const char*)(src) + (h - 1) * sstride;
|
||||||
|
for (int dy = 0; dy < h; ++dy) {
|
||||||
|
T *d = reinterpret_cast<T*>((char *)(dest) + dy * dstride);
|
||||||
|
src = reinterpret_cast<const T*>(s);
|
||||||
|
for (int dx = 0; dx < w; ++dx) {
|
||||||
|
d[dx] = src[w - 1 - dx];
|
||||||
|
}
|
||||||
|
s -= sstride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class T>
|
||||||
|
static
|
||||||
|
inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, int srcStride,
|
||||||
|
T *dest, int dstStride)
|
||||||
|
{
|
||||||
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
|
// packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer
|
||||||
|
if (sizeof(quint32) % sizeof(T) == 0)
|
||||||
|
qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride)
|
||||||
|
{
|
||||||
|
// packed algorithm doesn't have any benefit for quint32
|
||||||
|
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
inline void qt_memrotate270_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride)
|
||||||
|
{
|
||||||
|
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride);
|
||||||
|
}
|
||||||
|
#define QT_IMPL_MEMROTATE(type) \
|
||||||
|
void qt_memrotate90(const type *src, int w, int h, int sstride, \
|
||||||
|
type *dest, int dstride) \
|
||||||
|
{ \
|
||||||
|
qt_memrotate90_template(src, w, h, sstride, dest, dstride); \
|
||||||
|
} \
|
||||||
|
void qt_memrotate180(const type *src, int w, int h, int sstride, \
|
||||||
|
type *dest, int dstride) \
|
||||||
|
{ \
|
||||||
|
qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
|
||||||
|
} \
|
||||||
|
void qt_memrotate270(const type *src, int w, int h, int sstride, \
|
||||||
|
type *dest, int dstride) \
|
||||||
|
{ \
|
||||||
|
qt_memrotate270_template(src, w, h, sstride, dest, dstride); \
|
||||||
|
}
|
||||||
|
#define QT_IMPL_SIMPLE_MEMROTATE(type) \
|
||||||
|
void qt_memrotate90(const type *src, int w, int h, int sstride, \
|
||||||
|
type *dest, int dstride) \
|
||||||
|
{ \
|
||||||
|
qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); \
|
||||||
|
} \
|
||||||
|
void qt_memrotate180(const type *src, int w, int h, int sstride, \
|
||||||
|
type *dest, int dstride) \
|
||||||
|
{ \
|
||||||
|
qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
|
||||||
|
} \
|
||||||
|
void qt_memrotate270(const type *src, int w, int h, int sstride, \
|
||||||
|
type *dest, int dstride) \
|
||||||
|
{ \
|
||||||
|
qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \
|
||||||
|
}
|
||||||
|
QT_IMPL_MEMROTATE(quint64)
|
||||||
|
QT_IMPL_MEMROTATE(quint32)
|
||||||
|
QT_IMPL_MEMROTATE(quint16)
|
||||||
|
QT_IMPL_MEMROTATE(quint8)
|
||||||
|
void qt_memrotate90_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate90(srcPixels, w, h, sbpl, destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate180_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate180(srcPixels, w, h, sbpl, destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate270_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate270(srcPixels, w, h, sbpl, destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate90_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate90((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate180_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate180((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate270((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate90((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate180_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate180((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate90_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate90((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate180_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate180((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
|
||||||
|
{
|
||||||
|
qt_memrotate270((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
|
||||||
|
#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
|
||||||
|
const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
|
||||||
|
|
||||||
|
QImage qt_halfScaled(const QImage &source)
|
||||||
|
{
|
||||||
|
if (source.width() < 2 || source.height() < 2)
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
QImage srcImage = source;
|
||||||
|
|
||||||
|
if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
|
||||||
|
// assumes grayscale
|
||||||
|
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
||||||
|
dest.setDevicePixelRatio(source.devicePixelRatioF());
|
||||||
|
|
||||||
|
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
|
||||||
|
qsizetype sx = srcImage.bytesPerLine();
|
||||||
|
qsizetype sx2 = sx << 1;
|
||||||
|
|
||||||
|
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
|
||||||
|
qsizetype dx = dest.bytesPerLine();
|
||||||
|
int ww = dest.width();
|
||||||
|
int hh = dest.height();
|
||||||
|
|
||||||
|
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
||||||
|
const uchar *p1 = src;
|
||||||
|
const uchar *p2 = src + sx;
|
||||||
|
uchar *q = dst;
|
||||||
|
for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
|
||||||
|
*q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
} else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
|
||||||
|
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
||||||
|
dest.setDevicePixelRatio(source.devicePixelRatioF());
|
||||||
|
|
||||||
|
const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
|
||||||
|
qsizetype sx = srcImage.bytesPerLine();
|
||||||
|
qsizetype sx2 = sx << 1;
|
||||||
|
|
||||||
|
uchar *dst = reinterpret_cast<uchar*>(dest.bits());
|
||||||
|
qsizetype dx = dest.bytesPerLine();
|
||||||
|
int ww = dest.width();
|
||||||
|
int hh = dest.height();
|
||||||
|
|
||||||
|
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
||||||
|
const uchar *p1 = src;
|
||||||
|
const uchar *p2 = src + sx;
|
||||||
|
uchar *q = dst;
|
||||||
|
for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
|
||||||
|
// alpha
|
||||||
|
q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
|
||||||
|
// rgb
|
||||||
|
const quint16 p16_1 = (p1[2] << 8) | p1[1];
|
||||||
|
const quint16 p16_2 = (p1[5] << 8) | p1[4];
|
||||||
|
const quint16 p16_3 = (p2[2] << 8) | p2[1];
|
||||||
|
const quint16 p16_4 = (p2[5] << 8) | p2[4];
|
||||||
|
const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
|
||||||
|
q[1] = result & 0xff;
|
||||||
|
q[2] = result >> 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
} else if (source.format() != QImage::Format_ARGB32_Premultiplied
|
||||||
|
&& source.format() != QImage::Format_RGB32)
|
||||||
|
{
|
||||||
|
srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
|
||||||
|
dest.setDevicePixelRatio(source.devicePixelRatioF());
|
||||||
|
|
||||||
|
const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
|
||||||
|
qsizetype sx = srcImage.bytesPerLine() >> 2;
|
||||||
|
qsizetype sx2 = sx << 1;
|
||||||
|
|
||||||
|
quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
|
||||||
|
qsizetype dx = dest.bytesPerLine() >> 2;
|
||||||
|
int ww = dest.width();
|
||||||
|
int hh = dest.height();
|
||||||
|
|
||||||
|
for (int y = hh; y; --y, dst += dx, src += sx2) {
|
||||||
|
const quint32 *p1 = src;
|
||||||
|
const quint32 *p2 = src + sx;
|
||||||
|
quint32 *q = dst;
|
||||||
|
for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
|
||||||
|
*q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int shift>
|
||||||
|
inline int qt_static_shift(int value)
|
||||||
|
{
|
||||||
|
if (shift == 0)
|
||||||
|
return value;
|
||||||
|
else if (shift > 0)
|
||||||
|
return value << (uint(shift) & 0x1f);
|
||||||
|
else
|
||||||
|
return value >> (uint(-shift) & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int aprec, int zprec>
|
||||||
|
inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
|
||||||
|
{
|
||||||
|
QRgb *pixel = (QRgb *)bptr;
|
||||||
|
|
||||||
|
#define Z_MASK (0xff << zprec)
|
||||||
|
const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
|
||||||
|
const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
|
||||||
|
const int G_zprec = qt_static_shift<zprec - 8>(*pixel) & Z_MASK;
|
||||||
|
const int B_zprec = qt_static_shift<zprec>(*pixel) & Z_MASK;
|
||||||
|
#undef Z_MASK
|
||||||
|
|
||||||
|
const int zR_zprec = zR >> aprec;
|
||||||
|
const int zG_zprec = zG >> aprec;
|
||||||
|
const int zB_zprec = zB >> aprec;
|
||||||
|
const int zA_zprec = zA >> aprec;
|
||||||
|
|
||||||
|
zR += alpha * (R_zprec - zR_zprec);
|
||||||
|
zG += alpha * (G_zprec - zG_zprec);
|
||||||
|
zB += alpha * (B_zprec - zB_zprec);
|
||||||
|
zA += alpha * (A_zprec - zA_zprec);
|
||||||
|
|
||||||
|
#define ZA_MASK (0xff << (zprec + aprec))
|
||||||
|
*pixel =
|
||||||
|
qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
|
||||||
|
| qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
|
||||||
|
| qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
|
||||||
|
| qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
|
||||||
|
#undef ZA_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int aprec, int zprec>
|
||||||
|
inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
|
||||||
|
{
|
||||||
|
const int A_zprec = int(*(bptr)) << zprec;
|
||||||
|
const int z_zprec = z >> aprec;
|
||||||
|
z += alpha * (A_zprec - z_zprec);
|
||||||
|
*(bptr) = z >> (zprec + aprec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int aprec, int zprec, bool alphaOnly>
|
||||||
|
inline void qt_blurrow(QImage & im, int line, int alpha)
|
||||||
|
{
|
||||||
|
uchar *bptr = im.scanLine(line);
|
||||||
|
|
||||||
|
int zR = 0, zG = 0, zB = 0, zA = 0;
|
||||||
|
|
||||||
|
if (alphaOnly && im.format() != QImage::Format_Indexed8)
|
||||||
|
bptr += alphaIndex;
|
||||||
|
|
||||||
|
const int stride = im.depth() >> 3;
|
||||||
|
const int im_width = im.width();
|
||||||
|
for (int index = 0; index < im_width; ++index) {
|
||||||
|
if (alphaOnly)
|
||||||
|
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
|
||||||
|
else
|
||||||
|
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
|
||||||
|
bptr += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
bptr -= stride;
|
||||||
|
|
||||||
|
for (int index = im_width - 2; index >= 0; --index) {
|
||||||
|
bptr -= stride;
|
||||||
|
if (alphaOnly)
|
||||||
|
qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
|
||||||
|
else
|
||||||
|
qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int aprec, int zprec, bool alphaOnly>
|
||||||
|
void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0)
|
||||||
|
{
|
||||||
|
// halve the radius if we're using two passes
|
||||||
|
if (improvedQuality)
|
||||||
|
radius *= qreal(0.5);
|
||||||
|
|
||||||
|
Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied
|
||||||
|
|| img.format() == QImage::Format_RGB32
|
||||||
|
|| img.format() == QImage::Format_Indexed8
|
||||||
|
|| img.format() == QImage::Format_Grayscale8);
|
||||||
|
|
||||||
|
// choose the alpha such that pixels at radius distance from a fully
|
||||||
|
// saturated pixel will have an alpha component of no greater than
|
||||||
|
// the cutOffIntensity
|
||||||
|
const qreal cutOffIntensity = 2;
|
||||||
|
int alpha = radius <= qreal(1e-5)
|
||||||
|
? ((1 << aprec)-1)
|
||||||
|
: qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius)));
|
||||||
|
|
||||||
|
int img_height = img.height();
|
||||||
|
for (int row = 0; row < img_height; ++row) {
|
||||||
|
for (int i = 0; i <= int(improvedQuality); ++i)
|
||||||
|
qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage temp(img.height(), img.width(), img.format());
|
||||||
|
temp.setDevicePixelRatio(img.devicePixelRatioF());
|
||||||
|
if (transposed >= 0) {
|
||||||
|
if (img.depth() == 8) {
|
||||||
|
qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
|
||||||
|
img.width(), img.height(), img.bytesPerLine(),
|
||||||
|
reinterpret_cast<quint8*>(temp.bits()),
|
||||||
|
temp.bytesPerLine());
|
||||||
|
} else {
|
||||||
|
qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
|
||||||
|
img.width(), img.height(), img.bytesPerLine(),
|
||||||
|
reinterpret_cast<quint32*>(temp.bits()),
|
||||||
|
temp.bytesPerLine());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (img.depth() == 8) {
|
||||||
|
qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
|
||||||
|
img.width(), img.height(), img.bytesPerLine(),
|
||||||
|
reinterpret_cast<quint8*>(temp.bits()),
|
||||||
|
temp.bytesPerLine());
|
||||||
|
} else {
|
||||||
|
qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
|
||||||
|
img.width(), img.height(), img.bytesPerLine(),
|
||||||
|
reinterpret_cast<quint32*>(temp.bits()),
|
||||||
|
temp.bytesPerLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img_height = temp.height();
|
||||||
|
for (int row = 0; row < img_height; ++row) {
|
||||||
|
for (int i = 0; i <= int(improvedQuality); ++i)
|
||||||
|
qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transposed == 0) {
|
||||||
|
if (img.depth() == 8) {
|
||||||
|
qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
|
||||||
|
temp.width(), temp.height(), temp.bytesPerLine(),
|
||||||
|
reinterpret_cast<quint8*>(img.bits()),
|
||||||
|
img.bytesPerLine());
|
||||||
|
} else {
|
||||||
|
qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
|
||||||
|
temp.width(), temp.height(), temp.bytesPerLine(),
|
||||||
|
reinterpret_cast<quint32*>(img.bits()),
|
||||||
|
img.bytesPerLine());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
img = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::exponentialblur(QImage& img, qreal radius, bool improvedQuality, int transposed)
|
||||||
|
{
|
||||||
|
expblur<12, 10, false>(img, radius, improvedQuality, transposed);
|
||||||
|
}
|
34
ui/utils/eb.h
Normal file
34
ui/utils/eb.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EB_H
|
||||||
|
#define EB_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
void exponentialblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EB_H
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "messagedelegate.h"
|
#include "messagedelegate.h"
|
||||||
#include "ui/models/messagefeed.h"
|
#include "ui/models/messagefeed.h"
|
||||||
|
#include "eb.h"
|
||||||
|
|
||||||
constexpr int maxMessageHeight = 10000;
|
constexpr int maxMessageHeight = 10000;
|
||||||
constexpr int approximateSingleMessageHeight = 20;
|
constexpr int approximateSingleMessageHeight = 20;
|
||||||
@ -288,6 +289,10 @@ void FeedView::paintEvent(QPaintEvent* event)
|
|||||||
del->endClearWidgets();
|
del->endClearWidgets();
|
||||||
clearWidgetsMode = false;
|
clearWidgetsMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event->rect().height() == vp->height()) {
|
||||||
|
// draw the blurred drop shadow...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::verticalScrollbarValueChanged(int value)
|
void FeedView::verticalScrollbarValueChanged(int value)
|
||||||
@ -300,6 +305,10 @@ void FeedView::verticalScrollbarValueChanged(int value)
|
|||||||
clearWidgetsMode = true;
|
clearWidgetsMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (modelState == Models::MessageFeed::incomplete && value < progressSize) {
|
||||||
|
model()->fetchMore(rootIndex());
|
||||||
|
}
|
||||||
|
|
||||||
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +326,7 @@ void FeedView::resizeEvent(QResizeEvent* event)
|
|||||||
QAbstractItemView::resizeEvent(event);
|
QAbstractItemView::resizeEvent(event);
|
||||||
|
|
||||||
positionProgress();
|
positionProgress();
|
||||||
|
emit resized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::positionProgress()
|
void FeedView::positionProgress()
|
||||||
|
@ -49,6 +49,9 @@ public:
|
|||||||
|
|
||||||
QFont getFont() const;
|
QFont getFont() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void resized();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
91
ui/utils/shadowoverlay.cpp
Normal file
91
ui/utils/shadowoverlay.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shadowoverlay.h"
|
||||||
|
|
||||||
|
ShadowOverlay::ShadowOverlay(unsigned int r, unsigned int t, const QColor& c, QWidget* parent):
|
||||||
|
QWidget(parent),
|
||||||
|
top(false),
|
||||||
|
right(false),
|
||||||
|
bottom(false),
|
||||||
|
left(false),
|
||||||
|
thickness(t),
|
||||||
|
radius(r),
|
||||||
|
color(c),
|
||||||
|
shadow(1, 1, QImage::Format_ARGB32_Premultiplied)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground);
|
||||||
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowOverlay::paintEvent(QPaintEvent* event)
|
||||||
|
{
|
||||||
|
QWidget::paintEvent(event);
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
|
||||||
|
painter.drawImage(0, 0, shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowOverlay::resizeEvent(QResizeEvent* event)
|
||||||
|
{
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
|
||||||
|
updateImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowOverlay::updateImage()
|
||||||
|
{
|
||||||
|
int w = width();
|
||||||
|
int h = height();
|
||||||
|
shadow = QImage({w, h + int(thickness)}, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
shadow.fill(0);
|
||||||
|
|
||||||
|
QPainter tmpPainter(&shadow);
|
||||||
|
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
|
if (top) {
|
||||||
|
QRectF shadow(0, 0, w, thickness);
|
||||||
|
tmpPainter.fillRect(shadow, color);
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
QRectF shadow(w - thickness, 0, thickness, h);
|
||||||
|
tmpPainter.fillRect(shadow, color);
|
||||||
|
}
|
||||||
|
if (bottom) {
|
||||||
|
QRectF shadow(0, h - thickness, w, thickness * 2); //i have no idea why, but it leaves some unpainted stripe without some spare space
|
||||||
|
tmpPainter.fillRect(shadow, color);
|
||||||
|
}
|
||||||
|
if (left) {
|
||||||
|
QRectF shadow(0, 0, thickness, h);
|
||||||
|
tmpPainter.fillRect(shadow, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::exponentialblur(shadow, radius, false, 0);
|
||||||
|
tmpPainter.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShadowOverlay::setFrames(bool t, bool r, bool b, bool l)
|
||||||
|
{
|
||||||
|
top = t;
|
||||||
|
right = r;
|
||||||
|
bottom = b;
|
||||||
|
left = l;
|
||||||
|
|
||||||
|
updateImage();
|
||||||
|
update();
|
||||||
|
}
|
58
ui/utils/shadowoverlay.h
Normal file
58
ui/utils/shadowoverlay.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHADOWOVERLAY_H
|
||||||
|
#define SHADOWOVERLAY_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
|
||||||
|
#include <ui/utils/eb.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class ShadowOverlay : public QWidget {
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShadowOverlay(unsigned int radius = 10, unsigned int thickness = 1, const QColor& color = Qt::black, QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void setFrames(bool top, bool right, bool bottom, bool left);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateImage();
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent * event) override;
|
||||||
|
void resizeEvent(QResizeEvent * event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool top;
|
||||||
|
bool right;
|
||||||
|
bool bottom;
|
||||||
|
bool left;
|
||||||
|
unsigned int thickness;
|
||||||
|
unsigned int radius;
|
||||||
|
QColor color;
|
||||||
|
QImage shadow;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHADOWOVERLAY_H
|
@ -26,5 +26,6 @@ add_library(squawkWidgets ${squawkWidgets_SRC})
|
|||||||
# Use the Widgets module from Qt 5.
|
# Use the Widgets module from Qt 5.
|
||||||
target_link_libraries(squawkWidgets vCardUI)
|
target_link_libraries(squawkWidgets vCardUI)
|
||||||
target_link_libraries(squawkWidgets Qt5::Widgets)
|
target_link_libraries(squawkWidgets Qt5::Widgets)
|
||||||
|
target_link_libraries(squawkWidgets squawkUI)
|
||||||
|
|
||||||
qt5_use_modules(squawkWidgets Core Widgets)
|
qt5_use_modules(squawkWidgets Core Widgets)
|
||||||
|
@ -46,19 +46,23 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
filesToAttach(),
|
filesToAttach(),
|
||||||
feed(new FeedView()),
|
feed(new FeedView()),
|
||||||
delegate(new MessageDelegate(this)),
|
delegate(new MessageDelegate(this)),
|
||||||
scroll(down),
|
|
||||||
manualSliderChange(false),
|
manualSliderChange(false),
|
||||||
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1)
|
tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1),
|
||||||
|
shadow(10, 1, Qt::black, this)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
shadow.setFrames(true, false, true, false);
|
||||||
|
|
||||||
feed->setItemDelegate(delegate);
|
feed->setItemDelegate(delegate);
|
||||||
|
feed->setFrameShape(QFrame::NoFrame);
|
||||||
delegate->initializeFonts(feed->getFont());
|
delegate->initializeFonts(feed->getFont());
|
||||||
feed->setModel(el->feed);
|
feed->setModel(el->feed);
|
||||||
el->feed->incrementObservers();
|
el->feed->incrementObservers();
|
||||||
m_ui->widget->layout()->addWidget(feed);
|
m_ui->widget->layout()->addWidget(feed);
|
||||||
|
|
||||||
connect(el->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage);
|
connect(el->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage);
|
||||||
|
connect(feed, &FeedView::resized, this, &Conversation::positionShadow);
|
||||||
|
|
||||||
connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged);
|
connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged);
|
||||||
|
|
||||||
@ -77,9 +81,6 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
|
|
||||||
m_ui->messageEditor->installEventFilter(&ker);
|
m_ui->messageEditor->installEventFilter(&ker);
|
||||||
|
|
||||||
//QScrollBar* vs = m_ui->scrollArea->verticalScrollBar();
|
|
||||||
//m_ui->scrollArea->setWidget(line);
|
|
||||||
//vs->installEventFilter(&vis);
|
|
||||||
|
|
||||||
//line->setAutoFillBackground(false);
|
//line->setAutoFillBackground(false);
|
||||||
//if (testAttribute(Qt::WA_TranslucentBackground)) {
|
//if (testAttribute(Qt::WA_TranslucentBackground)) {
|
||||||
@ -93,28 +94,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el,
|
|||||||
//line->setMyAvatarPath(acc->getAvatarPath());
|
//line->setMyAvatarPath(acc->getAvatarPath());
|
||||||
//line->setMyName(acc->getName());
|
//line->setMyName(acc->getName());
|
||||||
|
|
||||||
QGridLayout* gr = static_cast<QGridLayout*>(layout());
|
initializeOverlay();
|
||||||
QLabel* progressLabel = new QLabel(tr("Drop files here to attach them to your message"));
|
|
||||||
gr->addWidget(overlay, 0, 0, 2, 1);
|
|
||||||
QVBoxLayout* nl = new QVBoxLayout();
|
|
||||||
QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect();
|
|
||||||
opacity->setOpacity(0.8);
|
|
||||||
overlay->setLayout(nl);
|
|
||||||
overlay->setBackgroundRole(QPalette::Base);
|
|
||||||
overlay->setAutoFillBackground(true);
|
|
||||||
overlay->setGraphicsEffect(opacity);
|
|
||||||
progressLabel->setAlignment(Qt::AlignCenter);
|
|
||||||
QFont pf = progressLabel->font();
|
|
||||||
pf.setBold(true);
|
|
||||||
pf.setPointSize(26);
|
|
||||||
progressLabel->setWordWrap(true);
|
|
||||||
progressLabel->setFont(pf);
|
|
||||||
nl->addStretch();
|
|
||||||
nl->addWidget(progressLabel);
|
|
||||||
nl->addStretch();
|
|
||||||
overlay->hide();
|
|
||||||
|
|
||||||
applyVisualEffects();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Conversation::~Conversation()
|
Conversation::~Conversation()
|
||||||
@ -136,14 +116,28 @@ void Conversation::onAccountChanged(Models::Item* item, int row, int col)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::applyVisualEffects()
|
void Conversation::initializeOverlay()
|
||||||
{
|
{
|
||||||
// DropShadowEffect *e1 = new DropShadowEffect;
|
QGridLayout* gr = static_cast<QGridLayout*>(layout());
|
||||||
// e1->setBlurRadius(10);
|
QLabel* progressLabel = new QLabel(tr("Drop files here to attach them to your message"));
|
||||||
// e1->setColor(Qt::black);
|
gr->addWidget(overlay, 0, 0, 2, 1);
|
||||||
// e1->setThickness(1);
|
QVBoxLayout* nl = new QVBoxLayout();
|
||||||
// e1->setFrame(true, false, true, false);
|
QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect();
|
||||||
// m_ui->scrollArea->setGraphicsEffect(e1);
|
opacity->setOpacity(0.8);
|
||||||
|
overlay->setLayout(nl);
|
||||||
|
overlay->setBackgroundRole(QPalette::Base);
|
||||||
|
overlay->setAutoFillBackground(true);
|
||||||
|
overlay->setGraphicsEffect(opacity);
|
||||||
|
progressLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
QFont pf = progressLabel->font();
|
||||||
|
pf.setBold(true);
|
||||||
|
pf.setPointSize(26);
|
||||||
|
progressLabel->setWordWrap(true);
|
||||||
|
progressLabel->setFont(pf);
|
||||||
|
nl->addStretch();
|
||||||
|
nl->addWidget(progressLabel);
|
||||||
|
nl->addStretch();
|
||||||
|
overlay->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::setName(const QString& name)
|
void Conversation::setName(const QString& name)
|
||||||
@ -325,7 +319,7 @@ void Conversation::onTextEditDocSizeChanged(const QSizeF& size)
|
|||||||
|
|
||||||
void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left)
|
void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left)
|
||||||
{
|
{
|
||||||
//static_cast<DropShadowEffect*>(m_ui->scrollArea->graphicsEffect())->setFrame(top, right, bottom, left);
|
shadow.setFrames(top, right, bottom, left);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Conversation::dragEnterEvent(QDragEnterEvent* event)
|
void Conversation::dragEnterEvent(QDragEnterEvent* event)
|
||||||
@ -399,3 +393,13 @@ void Conversation::onMessage(const Shared::Message& msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Conversation::positionShadow()
|
||||||
|
{
|
||||||
|
int w = width();
|
||||||
|
int h = feed->height();
|
||||||
|
|
||||||
|
shadow.resize(w, h);
|
||||||
|
shadow.move(feed->pos());
|
||||||
|
shadow.raise();
|
||||||
|
}
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "ui/utils/badge.h"
|
#include "ui/utils/badge.h"
|
||||||
#include "ui/utils/feedview.h"
|
#include "ui/utils/feedview.h"
|
||||||
#include "ui/utils/messagedelegate.h"
|
#include "ui/utils/messagedelegate.h"
|
||||||
|
#include "ui/utils/shadowoverlay.h"
|
||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
#include "shared/utils.h"
|
#include "shared/utils.h"
|
||||||
|
|
||||||
@ -81,7 +82,6 @@ signals:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void setName(const QString& name);
|
virtual void setName(const QString& name);
|
||||||
void applyVisualEffects();
|
|
||||||
virtual Shared::Message createMessage() const;
|
virtual Shared::Message createMessage() const;
|
||||||
void setStatus(const QString& status);
|
void setStatus(const QString& status);
|
||||||
void addAttachedFile(const QString& path);
|
void addAttachedFile(const QString& path);
|
||||||
@ -90,6 +90,7 @@ protected:
|
|||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
void dragLeaveEvent(QDragLeaveEvent* event) override;
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
|
void initializeOverlay();
|
||||||
virtual void onMessage(const Shared::Message& msg);
|
virtual void onMessage(const Shared::Message& msg);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
@ -101,16 +102,12 @@ protected slots:
|
|||||||
void onTextEditDocSizeChanged(const QSizeF& size);
|
void onTextEditDocSizeChanged(const QSizeF& size);
|
||||||
void onAccountChanged(Models::Item* item, int row, int col);
|
void onAccountChanged(Models::Item* item, int row, int col);
|
||||||
void onFeedMessage(const Shared::Message& msg);
|
void onFeedMessage(const Shared::Message& msg);
|
||||||
|
void positionShadow();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const bool isMuc;
|
const bool isMuc;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum Scroll {
|
|
||||||
nothing,
|
|
||||||
keep,
|
|
||||||
down
|
|
||||||
};
|
|
||||||
Models::Account* account;
|
Models::Account* account;
|
||||||
Models::Element* element;
|
Models::Element* element;
|
||||||
QString palJid;
|
QString palJid;
|
||||||
@ -125,9 +122,10 @@ protected:
|
|||||||
W::Order<Badge*, Badge::Comparator> filesToAttach;
|
W::Order<Badge*, Badge::Comparator> filesToAttach;
|
||||||
FeedView* feed;
|
FeedView* feed;
|
||||||
MessageDelegate* delegate;
|
MessageDelegate* delegate;
|
||||||
Scroll scroll;
|
|
||||||
bool manualSliderChange;
|
bool manualSliderChange;
|
||||||
bool tsb; //transient scroll bars
|
bool tsb; //transient scroll bars
|
||||||
|
|
||||||
|
ShadowOverlay shadow;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONVERSATION_H
|
#endif // CONVERSATION_H
|
||||||
|
Loading…
Reference in New Issue
Block a user