diff --git a/reader/browser/BrowserPage.cpp b/reader/browser/BrowserPage.cpp index 22d1cc361..3ab427dad 100644 --- a/reader/browser/BrowserPage.cpp +++ b/reader/browser/BrowserPage.cpp @@ -1289,12 +1289,9 @@ QPixmap BrowserPage::applyNightMode(const QPixmap &src) if (img.isNull()) return src; - // 统一为 ARGB32 便于逐行扫描 - if (img.format() != QImage::Format_ARGB32 && - img.format() != QImage::Format_ARGB32_Premultiplied && - img.format() != QImage::Format_RGB32) { + // 统一转非预乘 ARGB32,避免对 ARGB32_Premultiplied 写入非预乘值导致半透明像素错误 + if (img.format() != QImage::Format_ARGB32) img = img.convertToFormat(QImage::Format_ARGB32); - } const int w = img.width(); const int h = img.height(); @@ -1306,11 +1303,17 @@ QPixmap BrowserPage::applyNightMode(const QPixmap &src) const int alpha = qAlpha(px); // HSL 亮度反转:保留色相(H)和饱和度(S),仅反转亮度(L) - // 白(255) → 黑(0),黑(0) → 白(255),彩色仅变暗、色相不偏移 + // 白(255) → 黑,黑(0) → 白(255),彩色仅变暗、色相不偏移 + // 两端收敛:下限钳制 30(#1E1E1E),反转后 ≥192 提亮纯白(原图 L≤63 深色文字) + const int kMinLightAfterInvert = 30; // #1E1E1E 的 HSL 亮度值 + const int kMaxLightBoostThreshold = 192; // 0xC0,提亮阈值 QColor c = QColor::fromRgb(qRed(px), qGreen(px), qBlue(px)); int hue, sat, light, dummy; c.getHsl(&hue, &sat, &light, &dummy); light = 255 - light; + if (light >= kMaxLightBoostThreshold) + light = 255; + light = qMax(light, kMinLightAfterInvert); c.setHsl(hue, sat, light); line[x] = qRgba(c.red(), c.green(), c.blue(), alpha); } diff --git a/reader/sidebar/BookMarkDelegate.cpp b/reader/sidebar/BookMarkDelegate.cpp index 0aa84e8b9..de692eed4 100644 --- a/reader/sidebar/BookMarkDelegate.cpp +++ b/reader/sidebar/BookMarkDelegate.cpp @@ -1,5 +1,5 @@ -// Copyright (C) 2019 ~ 2020 Uniontech Software Technology Co.,Ltd. -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// Copyright (C) 2019 ~ 2026 Uniontech Software Technology Co.,Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -14,6 +14,7 @@ #include #include #include +#include BookMarkDelegate::BookMarkDelegate(QAbstractItemView *parent) : DStyledItemDelegate(parent) @@ -21,6 +22,7 @@ BookMarkDelegate::BookMarkDelegate(QAbstractItemView *parent) qCInfo(appLog) << "Creating BookMarkDelegate with parent widget:" << parent; m_parent = parent; + m_darkPixmapCache.setMaxCost(8 * 1024 * 1024); // 反色缓存预算 8MB,按像素字节数计费 } void BookMarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const @@ -45,7 +47,51 @@ void BookMarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti QPainterPath clipPath; clipPath.addRoundedRect(rect, borderRadius, borderRadius); painter->setClipPath(clipPath); - painter->drawPixmap(rect.x(), rect.y(), scalePix); + // 深色主题下将白底缩略图反色为黑底白字(仅绘制时反色,不改缓存原图): + // HSL 亮度反转,下限钳制 37(#252525),反转后 ≥192 提亮纯白。 + if (DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->themeType() == DTK_NAMESPACE::Gui::DGuiApplicationHelper::DarkType) { + // 按源图 cacheKey() 缓存反色结果,避免每次重绘重复逐像素计算 + QPixmap invertedPixmap; + if (QPixmap *cached = m_darkPixmapCache.object(pixmap.cacheKey())) { + invertedPixmap = *cached; + } else { + QImage img = scalePix.toImage(); + if (!img.isNull()) { + if (img.format() != QImage::Format_ARGB32) + img = img.convertToFormat(QImage::Format_ARGB32); + const int w = img.width(); + const int h = img.height(); + const int kMinLightAfterInvert = 37; // #252525 + const int kMaxLightBoostThreshold = 192; // 0xC0,提亮阈值 + for (int y = 0; y < h; ++y) { + QRgb *line = reinterpret_cast(img.scanLine(y)); + for (int x = 0; x < w; ++x) { + const QRgb px = line[x]; + const int alpha = qAlpha(px); + QColor c = QColor::fromRgb(qRed(px), qGreen(px), qBlue(px)); + int hue, sat, light, dummy; + c.getHsl(&hue, &sat, &light, &dummy); + light = 255 - light; + if (light >= kMaxLightBoostThreshold) + light = 255; + light = qMax(light, kMinLightAfterInvert); + c.setHsl(hue, sat, light); + line[x] = qRgba(c.red(), c.green(), c.blue(), alpha); + } + } + invertedPixmap = QPixmap::fromImage(img); + invertedPixmap.setDevicePixelRatio(scalePix.devicePixelRatio()); + m_darkPixmapCache.insert(pixmap.cacheKey(), new QPixmap(invertedPixmap), + img.width() * img.height() * 4); // ARGB32 每像素固定 4 字节 + } + } + if (!invertedPixmap.isNull()) + painter->drawPixmap(rect.x(), rect.y(), invertedPixmap); + else + painter->drawPixmap(rect.x(), rect.y(), scalePix); + } else { + painter->drawPixmap(rect.x(), rect.y(), scalePix); + } painter->restore(); } @@ -59,7 +105,13 @@ void BookMarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti painter->setPen(QPen(DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().highlight().color(), 2)); painter->drawRoundedRect(rect, borderRadius, borderRadius); } else { - painter->setPen(QPen(DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().frameShadowBorder().color(), 1)); + // 未选中:深色主题下 frameShadowBorder 与深色背景混色,改用 windowText@0.2α + QColor frameColor = DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().frameShadowBorder().color(); + if (DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->themeType() == DTK_NAMESPACE::Gui::DGuiApplicationHelper::DarkType) { + frameColor = DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().windowText().color(); + frameColor.setAlphaF(0.2); + } + painter->setPen(QPen(frameColor, 1)); painter->drawRoundedRect(rect, borderRadius, borderRadius); } painter->restore(); diff --git a/reader/sidebar/BookMarkDelegate.h b/reader/sidebar/BookMarkDelegate.h index 1bcb18a61..2aef507fa 100644 --- a/reader/sidebar/BookMarkDelegate.h +++ b/reader/sidebar/BookMarkDelegate.h @@ -1,5 +1,5 @@ -// Copyright (C) 2019 ~ 2020 Uniontech Software Technology Co.,Ltd. -// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// Copyright (C) 2019 ~ 2026 Uniontech Software Technology Co.,Ltd. +// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -8,6 +8,9 @@ #include +#include +#include + DWIDGET_USE_NAMESPACE /** @@ -31,6 +34,11 @@ class BookMarkDelegate : public DStyledItemDelegate private: QAbstractItemView *m_parent = nullptr; + + /** + * @brief 深色主题反色结果缓存(键:源图 cacheKey();paint() 为 const 故 mutable) + */ + mutable QCache m_darkPixmapCache; }; #endif // BOOKMARKDELEGATE_H diff --git a/reader/sidebar/ThumbnailDelegate.cpp b/reader/sidebar/ThumbnailDelegate.cpp index bdc9b794b..b4fccc9f7 100644 --- a/reader/sidebar/ThumbnailDelegate.cpp +++ b/reader/sidebar/ThumbnailDelegate.cpp @@ -68,6 +68,7 @@ void ThumbnailDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt // 不修改 DocSheet 中缓存的真实缩略图(始终为白底),避免主题切换时双重反色。 // 采用与 BrowserPage::applyNightMode 相同的 HSL 亮度反转算法: // 仅反转 Lightness 通道,保留 Hue/Saturation,避免图片色相偏移 180°。 + // 两端收敛:下限钳制 37(#252525),反转后 ≥192 提亮纯白 // 白底黑字 → 黑底白字(文字/背景正确反色) // 彩色图片/链接 → 仅变暗,色相保持 if (DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->themeType() == DTK_NAMESPACE::Gui::DGuiApplicationHelper::DarkType) { @@ -77,6 +78,8 @@ void ThumbnailDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt img = img.convertToFormat(QImage::Format_ARGB32); const int w = img.width(); const int h = img.height(); + const int kMinLightAfterInvert = 37; // #252525 + const int kMaxLightBoostThreshold = 192; // 0xC0,提亮阈值 for (int y = 0; y < h; ++y) { QRgb *line = reinterpret_cast(img.scanLine(y)); for (int x = 0; x < w; ++x) { @@ -86,6 +89,9 @@ void ThumbnailDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt int hue, sat, light, dummy; c.getHsl(&hue, &sat, &light, &dummy); light = 255 - light; + if (light >= kMaxLightBoostThreshold) + light = 255; + light = qMax(light, kMinLightAfterInvert); c.setHsl(hue, sat, light); line[x] = qRgba(c.red(), c.green(), c.blue(), alpha); } @@ -110,7 +116,13 @@ void ThumbnailDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt painter->setPen(QPen(DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().highlight().color(), 2)); painter->drawRoundedRect(rect, borderRadius, borderRadius); } else { - painter->setPen(QPen(DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().frameShadowBorder().color(), 1)); + // 未选中:深色主题下 frameShadowBorder 与深色背景混色,改用 windowText@0.2α + QColor frameColor = DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().frameShadowBorder().color(); + if (DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->themeType() == DTK_NAMESPACE::Gui::DGuiApplicationHelper::DarkType) { + frameColor = DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().windowText().color(); + frameColor.setAlphaF(0.2); + } + painter->setPen(QPen(frameColor, 1)); painter->drawRoundedRect(rect, borderRadius, borderRadius); painter->setPen(QPen(DTK_NAMESPACE::Gui::DGuiApplicationHelper::instance()->applicationPalette().windowText().color())); }