Draw the folio with inverted lightness on a dark palette

On a dark palette the folio stayed a white sheet with black ink, and
the white/gray toggle only darkened the sheet while the ink stayed
black. DiagramView now renders each repaint into an image and inverts
its lightness before blitting it: white becomes the palette's Base,
black becomes its Text, and colored conductors and elements keep their
hue. The document, printing and export are untouched; only the screen
rendering changes, and only while the palette is dark.

QET::Palette::invertLightness does the inversion in one integer pass
(adding 255 - max - min to the three channels inverts the HSL lightness
and keeps hue and saturation), then stretches the result between the
sheet and ink colors through three lookup tables. A 4K viewport costs
about 9 ms in a release build. QGraphicsView::render() skips the
selection rubber band, so the view draws it again after the inversion.

Tests in tst_qetpalette: invertLightnessMapsSheetAndInk,
invertedViewReadsOnDarkSheet, invertLightnessSpeed.
This commit is contained in:
Jeff Patterson
2026-09-19 12:47:10 -05:00
parent dfe56cddbd
commit 85dc638a42
5 changed files with 248 additions and 1 deletions
+81 -1
View File
@@ -29,6 +29,7 @@
#include "qetgraphicsitem/conductortextitem.h"
#include "qetgraphicsitem/independenttextitem.h"
#include "qeticons.h"
#include "qetpalette.h"
#include "titleblock/integrationmovetemplateshandler.h"
#include "ui/diagrampropertiesdialog.h"
#include "ui/multipastedialog.h"
@@ -39,8 +40,13 @@
#include "ElementsCollection/xmlelementcollection.h"
#include "NameList/nameslist.h"
#include "elementdialog.h"
#include <QApplication>
#include <QDropEvent>
#include <QPainter>
#include <QPointer>
#include <QStyleHintReturnMask>
#include <QStyleOptionRubberBand>
#include <QtMath>
/**
Constructeur
@@ -1081,6 +1087,77 @@ bool DiagramView::event(QEvent *e) {
return(QGraphicsView::event(e));
}
/**
@brief DiagramView::canvasIsInverted
@return true when the folio must be drawn with its lightness inverted,
i.e. when the application palette is dark. The document keeps its own
colors; only the screen rendering changes, so printing and exporting
stay black on white.
*/
bool DiagramView::canvasIsInverted() const
{
return QET::Palette::isDark(qApp->palette());
}
/**
@brief DiagramView::paintInverted
Render \a area of the viewport into an off-screen image, invert the
lightness of that image and blit it to the viewport. Inverting the
finished rendering turns the white sheet black and the black ink white
in one pass, and keeps the hue of colored conductors and elements.
@param area the part of the viewport to repaint, in viewport coordinates
*/
void DiagramView::paintInverted(const QRect &area)
{
const QRect rect = area.intersected(viewport()->rect());
if (rect.isEmpty())
return;
const qreal ratio = viewport()->devicePixelRatioF();
QImage buffer(qCeil(rect.width() * ratio), qCeil(rect.height() * ratio),
QImage::Format_RGB32);
buffer.setDevicePixelRatio(ratio);
QPainter buffer_painter(&buffer);
buffer_painter.setRenderHints(renderHints());
render(&buffer_painter, QRectF(QPointF(0, 0), QSizeF(rect.size())),
rect, Qt::IgnoreAspectRatio);
buffer_painter.end();
QET::Palette::invertLightness(buffer, palette().color(QPalette::Base),
palette().color(QPalette::Text));
QPainter painter(viewport());
painter.drawImage(rect.topLeft(), buffer);
drawRubberBand(painter);
}
/**
@brief DiagramView::drawRubberBand
Draw the selection rubber band the way QGraphicsView::paintEvent does.
Rendering the view into an off-screen image skips it, so it is drawn
here instead, after the inversion, and keeps the palette colors.
@param painter a painter on the viewport
*/
void DiagramView::drawRubberBand(QPainter &painter)
{
const QRect band = rubberBandRect();
if (band.isNull())
return;
QStyleOptionRubberBand option;
option.initFrom(viewport());
option.rect = band;
option.shape = QRubberBand::Rectangle;
QStyleHintReturnMask mask;
if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option,
viewport(), &mask))
painter.setClipRegion(mask.region, Qt::IntersectClip);
viewport()->style()->drawControl(QStyle::CE_RubberBand, &option,
&painter, viewport());
}
/**
@brief DiagramView::paintEvent
Reimplemented from QGraphicsView
@@ -1088,7 +1165,10 @@ bool DiagramView::event(QEvent *e) {
*/
void DiagramView::paintEvent(QPaintEvent *event)
{
QGraphicsView::paintEvent(event);
if (canvasIsInverted())
paintInverted(event->rect());
else
QGraphicsView::paintEvent(event);
if (m_free_rubberbanding && m_free_rubberband.count() >= 3)
{