Freetype: Fix artificial oblique combined with other transforms

When applying an "obliquen" transformation with Freetype, this
applies a 12 degree horizontal shear on the control points of the
glyph you have loaded.

However, any other transformation we set on the glyph will be
applied when loading it. So if you have e.g. a rotation on
the glyph as well, then this will be applied first and the
rotated glyph would be obliquened instead, now along the wrong
arbitrary axis in the rotated coordinate system.

To fix this, we detect the case where a transform is applied and
multiply in the obliquen in advance. It means we have to duplicate
some code from FT_GlyphSlot_Oblique() which might get out of
sync (if the slant angle changes in a newer version for instance).

We limit this to the cases that are currently broken so that we
avoid messing with any working use cases.

[ChangeLog][Text] Fixed an issue where artificially obliquened
text would look incorrect when other transformations were also
applied and the Freetype backend was in use.

Pick-to: 6.5
Fixes: QTBUG-97436
Change-Id: I61c5d007e9ea9be2beb283a8b8abbed7723bab38
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
(cherry picked from commit db8230715aa115dc2da5177bb1824fb40c5f2569)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2024-10-28 13:28:00 +01:00 committed by Qt Cherry-pick Bot
parent d990f697b5
commit 3b9f202539
2 changed files with 42 additions and 6 deletions

View File

@ -1120,6 +1120,23 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
FT_Face face = freetype->face;
FT_Matrix matrix = freetype->matrix;
bool transform = matrix.xx != 0x10000
|| matrix.yy != 0x10000
|| matrix.xy != 0
|| matrix.yx != 0;
if (obliquen && transform) {
// We have to apply the obliquen transformation before any
// other transforms. This means we need to duplicate Freetype's
// obliquen matrix here and this has to be kept in sync.
FT_Matrix slant;
slant.xx = 0x10000L;
slant.yx = 0;
slant.xy = 0x0366A;
slant.yy = 0x10000L;
FT_Matrix_Multiply(&matrix, &slant);
matrix = slant;
}
FT_Vector v;
v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.x.value());
@ -1130,11 +1147,6 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
int vfactor = 1;
int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
bool transform = matrix.xx != 0x10000
|| matrix.yy != 0x10000
|| matrix.xy != 0
|| matrix.yx != 0;
if (transform || obliquen || (format != Format_Mono && !isScalableBitmap()))
load_flags |= FT_LOAD_NO_BITMAP;
@ -1166,7 +1178,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
if (embolden)
FT_GlyphSlot_Embolden(slot);
if (obliquen) {
if (obliquen && !transform) {
FT_GlyphSlot_Oblique(slot);
// While Embolden alters the metrics of the slot, oblique does not, so we need

View File

@ -18,6 +18,7 @@ private slots:
void tst_render_data();
void tst_render();
void tst_differentScriptsBackgrounds();
void tst_synthesizedObliqueAndRotation();
private:
QDir htmlDir;
@ -102,6 +103,29 @@ void tst_Text::tst_differentScriptsBackgrounds()
QBASELINE_CHECK(image, "tst_differentScriptsBackgrounds");
}
void tst_Text::tst_synthesizedObliqueAndRotation()
{
QFont font(QString::fromLatin1("Abyssinica SIL"));
font.setPixelSize(40);
font.setItalic(true);
QImage image(800, 600, QImage::Format_ARGB32);
image.fill(Qt::white);
{
QPainter painter(&image);
painter.setFont(font);
painter.save();
painter.translate(200, 450);
painter.rotate(270);
painter.drawText(0, 0, QString::fromLatin1("Foobar"));
painter.restore();
}
QBASELINE_CHECK(image, "tst_synthesizedObliqueAndRotation");
}
QBASELINETEST_MAIN(tst_Text)