Skip to content

Commit

Permalink
Add painter render hint for brush pattern transformation
Browse files Browse the repository at this point in the history
[ChangeLog][QtGui][QPainter] In Qt 5, the predefined brush patterns
would always be transformed along with the object being painted. In Qt
6.0 onwards, they would or would not, depending on the
SmoothPixmapTransformation render hint. Instead of this somewhat
surprising behavior, make the default be untransformed
(i.e. cosmetic), which makes sense when it comes to dpr scaling. For
the cases where one wants scaling, a new render hint is introduced to
enable that: NonCosmeticPatternBrushes.

Change-Id: I2208c7a28af9056d7ab97a529b66bf2d502c3c4f
Reviewed-by: Allan Sandfeld Jensen <[email protected]>
  • Loading branch information
aavit committed May 31, 2022
1 parent 5e48a51 commit 5adaa8d
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 23 deletions.
2 changes: 2 additions & 0 deletions src/gui/image/qpicture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,8 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords)
bool(ul & QPainter::Antialiasing));
painter->setRenderHint(QPainter::SmoothPixmapTransform,
bool(ul & QPainter::SmoothPixmapTransform));
painter->setRenderHint(QPainter::NonCosmeticBrushPatterns,
bool(ul & QPainter::NonCosmeticBrushPatterns));
break;
case QPicturePrivate::PdcSetCompositionMode:
s >> ul;
Expand Down
12 changes: 6 additions & 6 deletions src/gui/painting/qdrawhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3581,20 +3581,20 @@ static const CompositionFunctionFP *functionForModeFP = qt_functionForModeFP_C;
static TextureBlendType getBlendType(const QSpanData *data)
{
TextureBlendType ft;
if (data->txop <= QTransform::TxTranslate)
if (data->texture.type == QTextureData::Tiled || data->texture.type == QTextureData::Pattern)
if (data->texture.type == QTextureData::Pattern)
ft = BlendTiled;
else if (data->txop <= QTransform::TxTranslate)
if (data->texture.type == QTextureData::Tiled)
ft = BlendTiled;
else
ft = BlendUntransformed;
else if (data->bilinear)
if (data->texture.type == QTextureData::Tiled || data->texture.type == QTextureData::Pattern)
if (data->texture.type == QTextureData::Tiled)
ft = BlendTransformedBilinearTiled;
else
ft = BlendTransformedBilinear;
else
if (data->texture.type == QTextureData::Pattern)
ft = BlendTiled;
else if (data->texture.type == QTextureData::Tiled)
if (data->texture.type == QTextureData::Tiled)
ft = BlendTransformedTiled;
else
ft = BlendTransformed;
Expand Down
2 changes: 1 addition & 1 deletion src/gui/painting/qdrawhelper_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ struct QSpanData


void init(QRasterBuffer *rb, const QRasterPaintEngine *pe);
void setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode);
void setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode, bool isCosmetic);
void setupMatrix(const QTransform &matrix, int bilinear);
void initTexture(const QImage *image, int alpha, QTextureData::Type = QTextureData::Plain, const QRect &sourceRect = QRect());
void adjustSpanMethods();
Expand Down
19 changes: 12 additions & 7 deletions src/gui/painting/qpaintengine_raster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,12 +426,12 @@ bool QRasterPaintEngine::begin(QPaintDevice *device)
d->rasterizer->setClipRect(d->deviceRect);

s->penData.init(d->rasterBuffer.data(), this);
s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode, s->flags.cosmetic_brush);
s->stroker = &d->basicStroker;
d->basicStroker.setClipRect(d->deviceRect);

s->brushData.init(d->rasterBuffer.data(), this);
s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
s->brushData.setup(s->brush, s->intOpacity, s->composition_mode, s->flags.cosmetic_brush);

d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;

Expand Down Expand Up @@ -523,6 +523,7 @@ QRasterPaintEngineState::QRasterPaintEngineState()
flags.fast_text = true;
flags.tx_noshear = true;
flags.fast_images = true;
flags.cosmetic_brush = true;

clip = nullptr;
flags.has_clip_ownership = false;
Expand Down Expand Up @@ -621,7 +622,8 @@ void QRasterPaintEngine::updatePen(const QPen &pen)
s->strokeFlags = 0;

s->penData.clip = d->clip();
s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity,
s->composition_mode, s->flags.cosmetic_brush);

if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
|| pen.brush().transform().type() >= QTransform::TxNone) {
Expand Down Expand Up @@ -720,7 +722,7 @@ void QRasterPaintEngine::updateBrush(const QBrush &brush)
QRasterPaintEngineState *s = state();
// must set clip prior to setup, as setup uses it...
s->brushData.clip = d->clip();
s->brushData.setup(brush, s->intOpacity, s->composition_mode);
s->brushData.setup(brush, s->intOpacity, s->composition_mode, s->flags.cosmetic_brush);
if (s->fillFlags & DirtyTransform
|| brush.transform().type() >= QTransform::TxNone)
d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
Expand Down Expand Up @@ -807,14 +809,16 @@ void QRasterPaintEngine::renderHintsChanged()

bool was_aa = s->flags.antialiased;
bool was_bilinear = s->flags.bilinear;
bool was_cosmetic_brush = s->flags.cosmetic_brush;

s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
s->flags.cosmetic_brush = !bool(s->renderHints & QPainter::NonCosmeticBrushPatterns);

if (was_aa != s->flags.antialiased)
s->strokeFlags |= DirtyHints;

if (was_bilinear != s->flags.bilinear) {
if (was_bilinear != s->flags.bilinear || was_cosmetic_brush != s->flags.cosmetic_brush) {
s->strokeFlags |= DirtyPen;
s->fillFlags |= DirtyBrush;
}
Expand Down Expand Up @@ -4472,7 +4476,8 @@ void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)

Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);

void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode,
bool isCosmetic)
{
Qt::BrushStyle brushStyle = qbrush_style(brush);
cachedGradient.reset();
Expand Down Expand Up @@ -4579,7 +4584,7 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode
if (!tempImage)
tempImage = new QImage();
*tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
initTexture(tempImage, alpha, QTextureData::Pattern);
initTexture(tempImage, alpha, isCosmetic ? QTextureData::Pattern : QTextureData::Tiled);
break;
case Qt::TexturePattern:
type = Texture;
Expand Down
1 change: 1 addition & 0 deletions src/gui/painting/qpaintengine_raster_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class QRasterPaintEngineState : public QPainterState
uint fast_text : 1;
uint tx_noshear : 1;
uint fast_images : 1;
uint cosmetic_brush : 1;
};

union {
Expand Down
6 changes: 6 additions & 0 deletions src/gui/painting/qpainter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,12 @@ void QPainterPrivate::updateState(QPainterState *newState)
JPEG compression.
This value was added in Qt 5.13.
\value NonCosmeticBrushPatterns When painting with a brush with one of the predefined pattern
styles, transform the pattern too, along with the object being painted. The default is to treat
the pattern as cosmetic, so that the pattern pixels will map directly to device pixels,
independently of any active transformations.
This value was added in Qt 6.4.
\sa renderHints(), setRenderHint(), {QPainter#Rendering
Quality}{Rendering Quality}, {Concentric Circles Example}
Expand Down
1 change: 1 addition & 0 deletions src/gui/painting/qpainter.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class Q_GUI_EXPORT QPainter
SmoothPixmapTransform = 0x04,
VerticalSubpixelPositioning = 0x08,
LosslessImageRendering = 0x40,
NonCosmeticBrushPatterns = 0x80
};
Q_ENUM(RenderHint)

Expand Down
78 changes: 78 additions & 0 deletions tests/baseline/painting/scripts/pattern_xform.qps
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Version: 1
# CheckVsReference: 5%

#define basic block off screen
save
translate -1000 -1000
begin_block drawrects
setBrush green Dense4Pattern
drawRect 0 0 40 40
setBrush green DiagCrossPattern
drawRect 40 0 40 40
setBrush green VerPattern
brushRotate 30
drawRect 80 0 40 40
save
setPen brush 40 SolidLine FlatCap
setBrush NoBrush
drawLine 120 20 160 20
restore
end_block
restore

begin_block hintsuite
save
setRenderHint NonCosmeticBrushPatterns false
setRenderHint SmoothPixmapTransform false
translate 10 10
repeat_block drawrects

setRenderHint NonCosmeticBrushPatterns false
setRenderHint SmoothPixmapTransform true
translate 0 50
repeat_block drawrects

setRenderHint NonCosmeticBrushPatterns true
setRenderHint SmoothPixmapTransform false
translate 0 50
repeat_block drawrects

setRenderHint NonCosmeticBrushPatterns true
setRenderHint SmoothPixmapTransform true
translate 0 50
repeat_block drawrects
restore
end_block

save
translate 0 200
scale 2 2
repeat_block hintsuite
restore

save
translate 500 0
scale 1.5 2.5
rotate_y 60
repeat_block hintsuite
restore


translate 0 650
setBrush blue CrossPattern
setPen red
setRenderHint NonCosmeticBrushPatterns false

begin_block dots
save
drawRect 0 0 50 50
setBrushOrigin 12 0
drawRect 50 0 50 50
scale 2 1
drawRect 50 0 50 50
restore
end_block dots

setRenderHint NonCosmeticBrushPatterns true
translate 0 60
repeat_block dots
35 changes: 26 additions & 9 deletions tests/baseline/shared/paintcommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ const char *PaintCommands::imageFormatTable[] = {
"RGBA32FPx4_Premultiplied",
};

const char *PaintCommands::renderHintTable[] = {
"Antialiasing",
"SmoothPixmapTransform",
"NonCosmeticBrushPatterns"
};

int PaintCommands::translateEnum(const char *table[], const QString &pattern, int limit)
{
QByteArray p = pattern.toLatin1().toLower();
Expand Down Expand Up @@ -313,7 +319,7 @@ void PaintCommands::staticInit()
"pen_setCosmetic true");
DECL_PAINTCOMMAND("setRenderHint", command_setRenderHint,
"^setRenderHint\\s+([\\w_0-9]*)\\s*(\\w*)$",
"setRenderHint <Antialiasing|SmoothPixmapTransform> <true|false>",
"setRenderHint <hint> <true|false>",
"setRenderHint Antialiasing true");
DECL_PAINTCOMMAND("clearRenderHint", command_clearRenderHint,
"^clearRenderHint$",
Expand Down Expand Up @@ -665,6 +671,7 @@ void PaintCommands::staticInit()
ADD_ENUMLIST("image formats", imageFormatTable);
ADD_ENUMLIST("coordinate modes", coordinateMethodTable);
ADD_ENUMLIST("size modes", sizeModeTable);
ADD_ENUMLIST("render hints", renderHintTable);
}

#undef DECL_PAINTCOMMAND
Expand Down Expand Up @@ -2240,18 +2247,27 @@ void PaintCommands::command_setPen2(QRegularExpressionMatch re)
void PaintCommands::command_setRenderHint(QRegularExpressionMatch re)
{
QString hintString = re.captured(1).toLower();
bool on = re.captured(2).isEmpty() || re.captured(2).toLower() == "true";
if (hintString.contains("antialiasing")) {
if (m_verboseMode)
printf(" -(lance) setRenderHint Antialiasing\n");
QString setting = re.captured(2).toLower();

m_painter->setRenderHint(QPainter::Antialiasing, on);
bool on = setting.isEmpty() || setting == "true" || setting == "on";
QPainter::RenderHint hint;
int hintIdx = -1;
if (hintString.contains("antialiasing")) {
hintIdx = 0;
hint = QPainter::Antialiasing;
} else if (hintString.contains("smoothpixmaptransform")) {
hintIdx = 1;
hint = QPainter::SmoothPixmapTransform;
} else if (hintString.contains("noncosmeticbrushpatterns")) {
hintIdx = 2;
hint = QPainter::NonCosmeticBrushPatterns;
}
if (hintIdx >= 0) {
if (m_verboseMode)
printf(" -(lance) setRenderHint SmoothPixmapTransform\n");
m_painter->setRenderHint(QPainter::SmoothPixmapTransform, on);
printf(" -(lance) setRenderHint %s %s\n", renderHintTable[hintIdx], on ? "true" : "false");
m_painter->setRenderHint(hint, on);
} else {
fprintf(stderr, "ERROR(setRenderHint): unknown hint '%s'\n", qPrintable(hintString));
fprintf(stderr, "ERROR(setRenderHint): unknown hint '%s'\n", qPrintable(re.captured(1)));
}
}

Expand All @@ -2260,6 +2276,7 @@ void PaintCommands::command_clearRenderHint(QRegularExpressionMatch /*re*/)
{
m_painter->setRenderHint(QPainter::Antialiasing, false);
m_painter->setRenderHint(QPainter::SmoothPixmapTransform, false);
m_painter->setRenderHint(QPainter::NonCosmeticBrushPatterns, false);
if (m_verboseMode)
printf(" -(lance) clearRenderHint\n");
}
Expand Down
1 change: 1 addition & 0 deletions tests/baseline/shared/paintcommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ class PaintCommands
static const char *compositionModeTable[];
static const char *imageFormatTable[];
static const char *sizeModeTable[];
static const char *renderHintTable[];
static int translateEnum(const char *table[], const QString &pattern, int limit);

// utility
Expand Down

0 comments on commit 5adaa8d

Please sign in to comment.