Skip to content

Commit

Permalink
QQuickNinePatchImage: fix aliasing by respecting the smooth property
Browse files Browse the repository at this point in the history
When scaling with fractional values, aliasing can occur. Use the same
approach as BorderImage and respect the smooth property of
NinePatchImage. This property comes from QQuickImageBase, which sets it
to true by default. We change QQuickNinePatchImage's default value for
it to false, but this can be overridden by setting
QT_QUICK_CONTROLS_IMAGINE_SMOOTH to 1.

As NinePatchImage is not public API, and users would have to set the
smooth property on every image (where some items contain multiple
NinePatchImages), it's better to have one place to set this for all
controls.

[ChangeLog][Controls] The Imagine style now supports smooth scaling for
9-patch images when the QT_QUICK_CONTROLS_IMAGINE_SMOOTH environment
variable is set to 1.

Fixes: QTBUG-107989
Pick-to: 6.2 6.4
Change-Id: I250a041f87c0270d67af191168f7bcfbf6237925
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
  • Loading branch information
mitchcurtis committed Nov 8, 2022
1 parent e3d601b commit 649151b
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@

\note Due to a technical limitation, the path should not be named
\e "imagine" if it is relative to the \c qtquickcontrols2.conf file.
\li \c QT_QUICK_CONTROLS_IMAGINE_SMOOTH
\li Set to \c 1 to enable smooth scaling for 9-patch images.
This environment variable was added in Qt 6.5.
\endtable
//! [env]
44 changes: 43 additions & 1 deletion src/quickcontrols2impl/qquickninepatchimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ QList<qreal> QQuickNinePatchData::coordsForSize(qreal size) const
return coords;
}

/*
Adds the 0 index coordinate if appropriate, and the one at "size".
*/
void QQuickNinePatchData::fill(const QList<qreal> &coords, qreal size)
{
data.clear();
Expand Down Expand Up @@ -174,6 +177,31 @@ class QQuickNinePatchImagePrivate : public QQuickImagePrivate
QQuickNinePatchData yDivs;
};

/*
Examines each pixel in a horizontal or vertical (if offset is equal to the image's width)
line, storing the start and end index ("coordinate") of each 9-patch line.
For instance, in the 7x3 (9x5 actual size) 9-patch image below, which has no horizontal
stretchable area, it would return {}:
+-----+
| |
+-----+
If indices 3 to 5 were marked, it would return {2, 5}:
xxx
+-----+
| |
+-----+
If indices 3 and 5 were marked, it would store {0, 2, 3, 4, 5, 7}:
x x
+-----+
| |
+-----+
*/
static QList<qreal> readCoords(const QRgb *data, int from, int count, int offset, QRgb color)
{
int p1 = -1;
Expand All @@ -182,19 +210,29 @@ static QList<qreal> readCoords(const QRgb *data, int from, int count, int offset
int p2 = from + i * offset;
if (data[p2] == color) {
// colored pixel
if (p1 == -1)
if (p1 == -1) {
// This is the start of a 9-patch line.
p1 = i;
}
} else {
// empty pixel
if (p1 != -1) {
// This is the end of a 9-patch line; add the start and end indices as coordinates...
coords << p1 << i;
// ... and reset p1 so that we can search for the next one.
p1 = -1;
}
}
}
return coords;
}

/*
Called whenever a 9-patch image is set as the image's source.
Reads the 9-patch lines from the source image and sets the
inset and padding properties accordingly.
*/
void QQuickNinePatchImagePrivate::updatePatches()
{
if (ninePatch.isNull())
Expand Down Expand Up @@ -299,6 +337,8 @@ void QQuickNinePatchImagePrivate::updateInsets(const QList<qreal> &horizontal, c
QQuickNinePatchImage::QQuickNinePatchImage(QQuickItem *parent)
: QQuickImage(*(new QQuickNinePatchImagePrivate), parent)
{
Q_D(QQuickNinePatchImage);
d->smooth = qEnvironmentVariableIntValue("QT_QUICK_CONTROLS_IMAGINE_SMOOTH");
}

qreal QQuickNinePatchImage::topPadding() const
Expand Down Expand Up @@ -432,6 +472,8 @@ QSGNode *QQuickNinePatchImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNode

QSGTexture *texture = window()->createTextureFromImage(image);
patchNode->initialize(texture, sz * d->devicePixelRatio, image.size(), d->xDivs, d->yDivs, d->devicePixelRatio);
auto patchNodeMaterial = static_cast<QSGTextureMaterial *>(patchNode->material());
patchNodeMaterial->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
return patchNode;
}

Expand Down

0 comments on commit 649151b

Please sign in to comment.