diff --git a/CMakeLists.txt b/CMakeLists.txt index 6119c75..d68eb4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,4 +268,6 @@ PUBLIC target_link_libraries(lib_ui PUBLIC desktop-app::lib_base +PRIVATE + desktop-app::external_zlib ) diff --git a/ui/emoji_config.cpp b/ui/emoji_config.cpp index 6680854..148feeb 100644 --- a/ui/emoji_config.cpp +++ b/ui/emoji_config.cpp @@ -35,7 +35,7 @@ constexpr auto kSetVersion = uint32(2); constexpr auto kCacheVersion = uint32(6); constexpr auto kMaxId = uint32(1 << 8); -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC constexpr auto kScaleForTouchBar = 150; #endif @@ -80,7 +80,7 @@ auto CanClearUniversal = false; auto WaitingToSwitchBackToId = 0; auto Updates = rpl::event_stream<>(); -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC auto TouchbarSize = -1; auto TouchbarInstance = std::unique_ptr(); auto TouchbarEmoji = (Instance*)nullptr; @@ -498,7 +498,7 @@ void Init() { InstanceNormal = std::make_unique(SizeNormal); InstanceLarge = std::make_unique(SizeLarge); -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC if (style::Scale() != kScaleForTouchBar) { TouchbarSize = int(style::ConvertScale(18 * 4 / 3., kScaleForTouchBar * style::DevicePixelRatio())); @@ -516,7 +516,7 @@ void Clear() { InstanceNormal = nullptr; InstanceLarge = nullptr; -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC TouchbarInstance = nullptr; TouchbarEmoji = nullptr; #endif @@ -620,7 +620,7 @@ int GetSizeLarge() { return SizeLarge; } -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC int GetSizeTouchbar() { return (style::Scale() == kScaleForTouchBar) ? GetSizeLarge() @@ -763,7 +763,7 @@ const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) { } void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) { -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC const auto s = (style::Scale() == kScaleForTouchBar) ? SizeLarge : TouchbarSize; diff --git a/ui/emoji_config.h b/ui/emoji_config.h index 75f10e1..3183258 100644 --- a/ui/emoji_config.h +++ b/ui/emoji_config.h @@ -40,7 +40,7 @@ void ClearNeedSwitchToId(); [[nodiscard]] int GetSizeNormal(); [[nodiscard]] int GetSizeLarge(); -#if defined Q_OS_MAC && !defined OS_MAC_OLD +#ifdef Q_OS_MAC [[nodiscard]] int GetSizeTouchbar(); #endif diff --git a/ui/image/image_prepare.cpp b/ui/image/image_prepare.cpp index aaa4469..660a20f 100644 --- a/ui/image/image_prepare.cpp +++ b/ui/image/image_prepare.cpp @@ -10,12 +10,22 @@ #include "ui/style/style_core.h" #include "ui/painter.h" #include "base/flat_map.h" +#include "base/debug_log.h" #include "styles/palette.h" #include "styles/style_basic.h" +#include "zlib.h" +#include +#include +#include +#include + namespace Images { namespace { +// They should be smaller. +constexpr auto kMaxGzipFileSize = 5 * 1024 * 1024; + TG_FORCE_INLINE uint64 blurGetColors(const uchar *p) { return (uint64)p[0] + ((uint64)p[1] << 16) + ((uint64)p[2] << 32) + ((uint64)p[3] << 48); } @@ -111,6 +121,131 @@ std::array PrepareCorners( return result; } +[[nodiscard]] QByteArray UnpackGzip(const QByteArray &bytes) { + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + stream.opaque = nullptr; + stream.avail_in = 0; + stream.next_in = nullptr; + int res = inflateInit2(&stream, 16 + MAX_WBITS); + if (res != Z_OK) { + return bytes; + } + const auto guard = gsl::finally([&] { inflateEnd(&stream); }); + + auto result = QByteArray(kMaxGzipFileSize + 1, char(0)); + stream.avail_in = bytes.size(); + stream.next_in = reinterpret_cast(const_cast(bytes.data())); + stream.avail_out = 0; + while (!stream.avail_out) { + stream.avail_out = result.size(); + stream.next_out = reinterpret_cast(result.data()); + int res = inflate(&stream, Z_NO_FLUSH); + if (res != Z_OK && res != Z_STREAM_END) { + return bytes; + } else if (!stream.avail_out) { + return bytes; + } + } + result.resize(result.size() - stream.avail_out); + return result; +} + +[[nodiscard]] ReadResult ReadGzipSvg(const ReadArgs &args) { + const auto bytes = UnpackGzip(args.content); + if (bytes.isEmpty()) { + LOG(("Svg Error: Couldn't unpack gzip-ed content.")); + return {}; + } + auto renderer = QSvgRenderer(bytes); + if (!renderer.isValid()) { + LOG(("Svg Error: Invalid data.")); + return {}; + } + auto size = renderer.defaultSize(); + if (!args.maxSize.isEmpty() + && (size.width() > args.maxSize.width() + || size.height() > args.maxSize.height())) { + size = size.scaled(args.maxSize, Qt::KeepAspectRatio); + } + if (size.isEmpty()) { + LOG(("Svg Error: Bad size %1x%2." + ).arg(renderer.defaultSize().width() + ).arg(renderer.defaultSize().height())); + return {}; + } + auto result = ReadResult(); + result.image = QImage(size, QImage::Format_ARGB32_Premultiplied); + result.image.fill(Qt::transparent); + { + QPainter p(&result.image); + renderer.render(&p, QRect(QPoint(), size)); + } + result.format = "svg"; + return result; +} + +[[nodiscard]] ReadResult ReadOther(const ReadArgs &args) { + auto bytes = args.content; + if (bytes.isEmpty()) { + return {}; + } + auto buffer = QBuffer(&bytes); + auto reader = QImageReader(&buffer); + reader.setAutoTransform(true); + if (!reader.canRead()) { + return {}; + } + const auto size = reader.size(); + if (size.width() * size.height() > kReadMaxArea) { + return {}; + } + auto result = ReadResult(); + if (!reader.read(&result.image) || result.image.isNull()) { + return {}; + } + result.animated = reader.supportsAnimation() + && (reader.imageCount() > 1); + result.format = reader.format().toLower(); + return result; +} + +ReadResult Read(ReadArgs &&args) { + if (args.content.isEmpty()) { + auto file = QFile(args.path); + if (file.size() > kReadBytesLimit + || !file.open(QIODevice::ReadOnly)) { + return {}; + } + args.content = file.readAll(); + } + auto result = args.gzipSvg ? ReadGzipSvg(args) : ReadOther(args); + if (result.image.isNull()) { + args = ReadArgs(); + return {}; + } + if (args.returnContent) { + result.content = args.content; + } else { + args.content = QByteArray(); + } + if (!args.maxSize.isEmpty() + && (result.image.width() > args.maxSize.width() + || result.image.height() > args.maxSize.height())) { + result.image = result.image.scaled( + args.maxSize, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + } + if (args.forceOpaque + && result.format != qstr("jpg") + && result.format != qstr("jpeg")) { + result.image = prepareOpaque(std::move(result.image)); + } + return result; +} + QImage prepareBlur(QImage img) { if (img.isNull()) { return img; diff --git a/ui/image/image_prepare.h b/ui/image/image_prepare.h index 60261b7..512e3a3 100644 --- a/ui/image/image_prepare.h +++ b/ui/image/image_prepare.h @@ -39,6 +39,28 @@ namespace Images { int radius, const style::color &color); +[[nodiscard]] QByteArray UnpackGzip(const QByteArray &bytes); + +// Try to read images up to 64MB. +inline constexpr auto kReadBytesLimit = 64 * 1024 * 1024; +inline constexpr auto kReadMaxArea = 12'032 * 9'024; + +struct ReadArgs { + QString path; + QByteArray content; + QSize maxSize; + bool gzipSvg = false; + bool forceOpaque = false; + bool returnContent = false; +}; +struct ReadResult { + QImage image; + QByteArray content; + QByteArray format; + bool animated = false; +}; +[[nodiscard]] ReadResult Read(ReadArgs &&args); + QImage prepareBlur(QImage image); void prepareRound( QImage &image, diff --git a/ui/style/style_core.cpp b/ui/style/style_core.cpp index 9914e50..48d1dd4 100644 --- a/ui/style/style_core.cpp +++ b/ui/style/style_core.cpp @@ -144,11 +144,7 @@ namespace internal { QImage createCircleMask(int size, QColor bg, QColor fg) { int realSize = size * DevicePixelRatio(); -#ifndef OS_MAC_OLD auto result = QImage(realSize, realSize, QImage::Format::Format_Grayscale8); -#else // OS_MAC_OLD - auto result = QImage(realSize, realSize, QImage::Format::Format_RGB32); -#endif // OS_MAC_OLD { QPainter p(&result); PainterHighQualityEnabler hq(p); diff --git a/ui/text/text.cpp b/ui/text/text.cpp index 95e22f9..6db3e73 100644 --- a/ui/text/text.cpp +++ b/ui/text/text.cpp @@ -134,14 +134,7 @@ bool IsBad(QChar ch) { || (ch >= 127 && ch < 160 && ch != 156) // qt harfbuzz crash see https://github.com/telegramdesktop/tdesktop/issues/4551 - || (Platform::IsMac() && ch == 6158) - - // tmp hack see https://bugreports.qt.io/browse/QTBUG-48910 - || (Platform::IsMac10_11OrGreater() - && !Platform::IsMac10_12OrGreater() - && ch >= 0x0B00 - && ch <= 0x0B7F - && IsDiac(ch)); + || (Platform::IsMac() && ch == 6158); } } // namespace @@ -1982,12 +1975,7 @@ private: if (item == -1) return; -#ifdef OS_MAC_OLD - auto end = _e->findItem(line.from + line.length - 1); -#else // OS_MAC_OLD auto end = _e->findItem(line.from + line.length - 1, item); -#endif // OS_MAC_OLD - auto blockIndex = _lineStartBlock; auto currentBlock = _t->_blocks[blockIndex].get(); auto nextBlock = (++blockIndex < _blocksSize) ? _t->_blocks[blockIndex].get() : nullptr; diff --git a/ui/text/text_entity.cpp b/ui/text/text_entity.cpp index b535f4e..cd4453e 100644 --- a/ui/text/text_entity.cpp +++ b/ui/text/text_entity.cpp @@ -88,9 +88,7 @@ QRegularExpression CreateRegExp(const QString &expression) { auto result = QRegularExpression( expression, QRegularExpression::UseUnicodePropertiesOption); -#ifndef OS_MAC_OLD result.optimize(); -#endif // OS_MAC_OLD return result; } diff --git a/ui/ui_utility.cpp b/ui/ui_utility.cpp index 6388228..a2e55e6 100644 --- a/ui/ui_utility.cpp +++ b/ui/ui_utility.cpp @@ -170,9 +170,7 @@ void SendSynteticMouseEvent(QWidget *widget, QEvent::Type type, Qt::MouseButton , button , QGuiApplication::mouseButtons() | button , QGuiApplication::keyboardModifiers() -#ifndef OS_MAC_OLD , Qt::MouseEventSynthesizedByApplication -#endif // OS_MAC_OLD ); ev.setTimestamp(crl::now()); QGuiApplication::sendEvent(windowHandle, &ev);