diff options
Diffstat (limited to 'src/gui/text/freetype/qfontengine_ft.cpp')
-rw-r--r-- | src/gui/text/freetype/qfontengine_ft.cpp | 372 |
1 files changed, 272 insertions, 100 deletions
diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp index 12ba46b7ed..d3791f1f6e 100644 --- a/src/gui/text/freetype/qfontengine_ft.cpp +++ b/src/gui/text/freetype/qfontengine_ft.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdir.h" #include "qmetatype.h" @@ -48,6 +12,7 @@ #include <qscreen.h> #include <qpa/qplatformscreen.h> #include <QtCore/QUuid> +#include <QtCore/QLoggingCategory> #include <QtGui/QPainterPath> #ifndef QT_NO_FREETYPE @@ -59,6 +24,8 @@ #include <qmath.h> #include <qendian.h> +#include <memory> + #include <ft2build.h> #include FT_FREETYPE_H #include FT_OUTLINE_H @@ -68,6 +35,7 @@ #include FT_GLYPH_H #include FT_MODULE_H #include FT_LCD_FILTER_H +#include FT_MULTIPLE_MASTERS_H #if defined(FT_CONFIG_OPTIONS_H) #include FT_CONFIG_OPTIONS_H @@ -87,6 +55,10 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFontMatch) + +using namespace Qt::StringLiterals; + #define FLOOR(x) ((x) & -64) #define CEIL(x) (((x)+63) & -64) #define TRUNC(x) ((x) >> 6) @@ -126,19 +98,43 @@ public: { } ~QtFreetypeData(); + struct FaceStyle { + QString faceFileName; + QString styleName; + + FaceStyle(QString faceFileName, QString styleName) + : faceFileName(std::move(faceFileName)), + styleName(std::move(styleName)) + {} + }; + FT_Library library; QHash<QFontEngine::FaceId, QFreetypeFace *> faces; + QHash<FaceStyle, int> faceIndices; }; QtFreetypeData::~QtFreetypeData() { - for (QHash<QFontEngine::FaceId, QFreetypeFace *>::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter) + for (auto iter = faces.cbegin(); iter != faces.cend(); ++iter) { iter.value()->cleanup(); + if (!iter.value()->ref.deref()) + delete iter.value(); + } faces.clear(); FT_Done_FreeType(library); library = nullptr; } +inline bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2) +{ + return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName; +} + +inline size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed) +{ + return qHashMulti(seed, style.faceFileName, style.styleName); +} + Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData) QtFreetypeData *qt_getFreetypeData() @@ -194,10 +190,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF return Err_Ok; } +bool QFreetypeFace::isScalable() const +{ + return FT_IS_SCALABLE(face); +} + bool QFreetypeFace::isScalableBitmap() const { #ifdef FT_HAS_COLOR - return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face); + return !isScalable() && FT_HAS_COLOR(face); #else return false; #endif @@ -220,18 +221,37 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, QtFreetypeData *freetypeData = qt_getFreetypeData(); - QFreetypeFace *freetype = freetypeData->faces.value(face_id, nullptr); - if (freetype) { - freetype->ref.ref(); - } else { - QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace); + QFreetypeFace *freetype = nullptr; + auto it = freetypeData->faces.find(face_id); + if (it != freetypeData->faces.end()) { + freetype = *it; + + Q_ASSERT(freetype->ref.loadRelaxed() > 0); + if (freetype->ref.loadRelaxed() == 1) { + // If there is only one reference left to the face, it means it is only referenced by + // the cache itself, and thus it is in cleanup state (but the final outside reference + // was removed on a different thread so it could not be deleted right away). We then + // complete the cleanup and pretend we didn't find it, so that it can be re-created with + // the present state. + freetype->cleanup(); + freetypeData->faces.erase(it); + delete freetype; + freetype = nullptr; + } else { + freetype->ref.ref(); + } + } + + if (!freetype) { + const auto deleter = [](QFreetypeFace *f) { delete f; }; + std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter); FT_Face face; if (!face_id.filename.isEmpty()) { QString fileName = QFile::decodeName(face_id.filename); - if (face_id.filename.startsWith(":qmemoryfonts/")) { + if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) { // from qfontdatabase.cpp QByteArray idx = face_id.filename; - idx.remove(0, 14); // remove ':qmemoryfonts/' + idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/' bool ok = false; newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); if (!ok) @@ -253,7 +273,23 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { return nullptr; } + +#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900 + if (face_id.instanceIndex >= 0) { + qCDebug(lcFontMatch) + << "Selecting named instance" << (face_id.instanceIndex) + << "in" << face_id.filename; + FT_Set_Named_Instance(face, face_id.instanceIndex + 1); + } +#endif newFreetype->face = face; + newFreetype->mm_var = nullptr; + if (FT_IS_NAMED_INSTANCE(newFreetype->face)) { + FT_Error ftresult; + ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var); + if (ftresult != FT_Err_Ok) + newFreetype->mm_var = nullptr; + } newFreetype->ref.storeRelaxed(1); newFreetype->xsize = 0; @@ -292,14 +328,34 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0); FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map); + + if (!face_id.variableAxes.isEmpty()) { + FT_MM_Var *var = nullptr; + FT_Get_MM_Var(newFreetype->face, &var); + if (var != nullptr) { + QVarLengthArray<FT_Fixed, 16> coords(var->num_axis); + FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data()); + for (FT_UInt i = 0; i < var->num_axis; ++i) { + if (const auto tag = QFont::Tag::fromValue(var->axis[i].tag)) { + const auto it = face_id.variableAxes.constFind(*tag); + if (it != face_id.variableAxes.constEnd()) + coords[i] = FT_Fixed(*it * 65536); + } + } + FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data()); + FT_Done_MM_Var(qt_getFreetype(), var); + } + } + QT_TRY { - freetypeData->faces.insert(face_id, newFreetype.data()); + freetypeData->faces.insert(face_id, newFreetype.get()); } QT_CATCH(...) { - newFreetype.take()->release(face_id); + newFreetype.release()->release(face_id); // we could return null in principle instead of throwing QT_RETHROW; } - freetype = newFreetype.take(); + freetype = newFreetype.release(); + freetype->ref.ref(); } return freetype; } @@ -307,32 +363,93 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, void QFreetypeFace::cleanup() { hbFace.reset(); + if (mm_var && face && face->glyph) + FT_Done_MM_Var(face->glyph->library, mm_var); + mm_var = nullptr; FT_Done_Face(face); face = nullptr; } void QFreetypeFace::release(const QFontEngine::FaceId &face_id) { - if (!ref.deref()) { - if (face) { - QtFreetypeData *freetypeData = qt_getFreetypeData(); + Q_UNUSED(face_id); + bool deleteThis = !ref.deref(); + + // If the only reference left over is the cache's reference, we remove it from the cache, + // granted that we are on the correct thread. If not, we leave it there to be cleaned out + // later. While we are at it, we also purge all left over faces which are only referenced + // from the cache. + if (face && ref.loadRelaxed() == 1) { + QtFreetypeData *freetypeData = qt_getFreetypeData(); + for (auto it = freetypeData->faces.constBegin(); it != freetypeData->faces.constEnd(); ) { + if (it.value()->ref.loadRelaxed() == 1) { + it.value()->cleanup(); + if (it.value() == this) + deleteThis = true; // This face, delete at end of function for safety + else + delete it.value(); + it = freetypeData->faces.erase(it); + } else { + ++it; + } + } - cleanup(); + if (freetypeData->faces.isEmpty()) { + FT_Done_FreeType(freetypeData->library); + freetypeData->library = nullptr; + } + } - auto it = freetypeData->faces.constFind(face_id); - if (it != freetypeData->faces.constEnd()) - freetypeData->faces.erase(it); + if (deleteThis) + delete this; +} - if (freetypeData->faces.isEmpty()) { - FT_Done_FreeType(freetypeData->library); - freetypeData->library = nullptr; - } +static int computeFaceIndex(const QString &faceFileName, const QString &styleName) +{ + FT_Library library = qt_getFreetype(); + + int faceIndex = 0; + int numFaces = 0; + + do { + FT_Face face; + + FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face); + if (error != FT_Err_Ok) { + qDebug() << "FT_New_Face failed for face index" << faceIndex << ':' << Qt::hex << error; + break; } - delete this; - } + const bool found = QLatin1StringView(face->style_name) == styleName; + numFaces = face->num_faces; + + FT_Done_Face(face); + + if (found) + return faceIndex; + } while (++faceIndex < numFaces); + + // Fall back to the first font face in the file + return 0; } +int QFreetypeFace::getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName) +{ + QtFreetypeData *freetypeData = qt_getFreetypeData(); + + // Try to get from cache + QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName); + int faceIndex = freetypeData->faceIndices.value(faceStyle, -1); + + if (faceIndex >= 0) + return faceIndex; + + faceIndex = computeFaceIndex(faceFileName, styleName); + + freetypeData->faceIndices.insert(faceStyle, faceIndex); + + return faceIndex; +} void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor) { @@ -594,7 +711,7 @@ static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint() QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData) { - QScopedPointer<QFontEngineFT> engine(new QFontEngineFT(fontDef)); + auto engine = std::make_unique<QFontEngineFT>(fontDef); QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono; const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); @@ -616,7 +733,7 @@ QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, con } engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference)); - return engine.take(); + return engine.release(); } namespace { @@ -638,27 +755,32 @@ namespace { fontDef.weight = QFont::Bold; } - bool initFromData(const QByteArray &fontData) + bool initFromData(const QByteArray &fontData, const QMap<QFont::Tag, float> &variableAxisValues) { FaceId faceId; faceId.filename = ""; faceId.index = 0; faceId.uuid = QUuid::createUuid().toByteArray(); + faceId.variableAxes = variableAxisValues; return init(faceId, true, Format_None, fontData); } }; } -QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference, + const QMap<QFont::Tag, float> &variableAxisValues) { QFontDef fontDef; fontDef.pixelSize = pixelSize; fontDef.stretch = QFont::Unstretched; fontDef.hintingPreference = hintingPreference; + fontDef.variableAxisValues = variableAxisValues; QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef); - if (!fe->initFromData(fontData)) { + if (!fe->initFromData(fontData, variableAxisValues)) { delete fe; return nullptr; } @@ -711,6 +833,37 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, static void dont_delete(void*) {} +static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId) +{ + FT_MM_Var *var = freetypeFace->mm_var; + if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) { + for (FT_UInt axis = 0; axis < var->num_axis; ++axis) { + if (var->axis[axis].tag == QFont::Tag("wght").value()) { + return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16; + } + } + } + if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { + return os2->usWeightClass; + } + + return 700; +} + +static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId) +{ + FT_MM_Var *var = freetypeFace->mm_var; + if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) { + for (FT_UInt axis = 0; axis < var->num_axis; ++axis) { + if (var->axis[axis].tag == QFont::Tag("ital").value()) { + return (var->namedstyle[faceId.instanceIndex].coords[axis] >> 16) == 1; + } + } + } + + return (face->style_flags & FT_STYLE_FLAG_ITALIC); +} + bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, QFreetypeFace *freetypeFace) { @@ -734,7 +887,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, PS_FontInfoRec psrec; // don't assume that type1 fonts are symbol fonts by default if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) { - symbol = !fontDef.families.isEmpty() && bool(fontDef.families.first().contains(QLatin1String("symbol"), Qt::CaseInsensitive)); + symbol = !fontDef.families.isEmpty() && bool(fontDef.families.constFirst().contains("symbol"_L1, Qt::CaseInsensitive)); } freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor); @@ -742,34 +895,36 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, FT_Face face = lockFace(); if (FT_IS_SCALABLE(face)) { - bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC"); + bool isItalic = calculateActualItalic(freetype, face, faceId); + bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC"); if (fake_oblique) obliquen = true; FT_Set_Transform(face, &matrix, nullptr); freetype->matrix = matrix; // fake bold if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) { - if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) { - if (os2->usWeightClass < 700 && fontDef.pixelSize < 64) - embolden = true; + FT_UShort actualWeight = calculateActualWeight(freetype, face, faceId); + if (actualWeight < 700 && + (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) { + embolden = true; } } // underline metrics line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)); - underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); + QFixed center_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); + underline_position = center_position - line_thickness / 2; } else { // ad hoc algorithm int score = fontDef.weight * fontDef.pixelSize; - line_thickness = score / 700; + line_thickness = score / 7000; // looks better with thicker line for small pointsizes if (line_thickness < 2 && score >= 1050) line_thickness = 2; underline_position = ((line_thickness * 2) + 3) / 6; - if (isScalableBitmap()) { + cacheEnabled = false; + if (isScalableBitmap()) glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; - cacheEnabled = false; - } } if (line_thickness < 1) line_thickness = 1; @@ -780,7 +935,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, TrueType fonts with embedded bitmaps may have a bitmap font specific ascent/descent in the EBLC table. There is no direct public API to extract those values. The only way we've found is to trick freetype - into thinking that it's not a scalable font in FT_SelectSize so that + into thinking that it's not a scalable font in FT_Select_Size so that the metrics are retrieved from the bitmap strikes. */ if (FT_IS_SCALABLE(face)) { @@ -794,7 +949,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, metrics.ascender = face->size->metrics.ascender; metrics.descender = face->size->metrics.descender; if (metrics.descender > 0 - && QString::fromUtf8(face->family_name) == QLatin1String("Courier New")) { + && QString::fromUtf8(face->family_name) == "Courier New"_L1) { metrics.descender *= -1; } metrics.height = metrics.ascender - metrics.descender + leading; @@ -1072,7 +1227,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } int glyph_buffer_size = 0; - QScopedArrayPointer<uchar> glyph_buffer; + std::unique_ptr<uchar[]> glyph_buffer; FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL; switch (format) { case Format_Mono: @@ -1117,7 +1272,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); + uchar *dst = glyph_buffer.get(); int h = slot->bitmap.rows; // Some fonts return bitmaps even when we requested something else: if (format == Format_Mono) { @@ -1146,7 +1301,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) { Q_ASSERT(format == Format_ARGB); uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); + uchar *dst = glyph_buffer.get(); int h = slot->bitmap.rows; while (h--) { #if Q_BYTE_ORDER == Q_BIG_ENDIAN @@ -1166,7 +1321,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { Q_ASSERT(format == Format_A8); uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.data(); + uchar *dst = glyph_buffer.get(); int h = slot->bitmap.rows; int bytes = info.width; while (h--) { @@ -1176,10 +1331,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { Q_ASSERT(format == Format_A32); - convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB); + convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB); } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) { Q_ASSERT(format == Format_A32); - convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.data(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB); + convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB); } else { qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode); return nullptr; @@ -1198,7 +1353,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, g->advance = info.xOff; g->format = format; delete [] g->data; - g->data = glyph_buffer.take(); + g->data = glyph_buffer.release(); if (set) set->setGlyph(glyph, subPixelPosition, g); @@ -1215,7 +1370,7 @@ QFontEngine::Properties QFontEngineFT::properties() const { Properties p = freetype->properties(); if (p.postscriptName.isEmpty()) { - p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.first().toUtf8()); + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.families.constFirst().toUtf8()); } return freetype->properties(); @@ -1442,7 +1597,7 @@ void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_me bool QFontEngineFT::supportsTransformation(const QTransform &transform) const { - return transform.type() <= QTransform::TxRotate; + return transform.type() <= QTransform::TxRotate && (freetype->isScalable() || freetype->isScalableBitmap()); } void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) @@ -1523,15 +1678,16 @@ glyph_t QFontEngineFT::glyphIndex(uint ucs4) const return glyph; } -bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, +int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } + int mappedGlyphs = 0; int glyph_pos = 0; if (freetype->symbol_map) { FT_Face face = freetype->face; @@ -1564,6 +1720,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs if (uc < QFreetypeFace::cmapCacheSize) freetype->cmapCache[uc] = glyph; } + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + mappedGlyphs++; ++glyph_pos; } } else { @@ -1585,6 +1743,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs freetype->cmapCache[uc] = glyph; } } + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + mappedGlyphs++; ++glyph_pos; } } @@ -1595,7 +1755,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs if (!(flags & GlyphIndicesOnly)) recalcAdvances(glyphs, flags); - return true; + return mappedGlyphs; } bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const @@ -1705,9 +1865,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) QFixed y = overall.yoff + glyphs.offsets[i].y - g->y; overall.x = qMin(overall.x, x); overall.y = qMin(overall.y, y); - xmax = qMax(xmax, x + g->width); - ymax = qMax(ymax, y + g->height); - overall.xoff += g->advance; + xmax = qMax(xmax, x.ceil() + g->width); + ymax = qMax(ymax, y.ceil() + g->height); if (!cacheEnabled && g != &emptyGlyph) delete g; } else { @@ -1722,8 +1881,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) overall.y = qMin(overall.y, y); xmax = qMax(xmax, x + TRUNC(right - left)); ymax = qMax(ymax, y + TRUNC(top - bottom)); - overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x))); } + overall.xoff += glyphs.effectiveAdvance(i); } overall.height = qMax(overall.height, ymax - overall.y); overall.width = xmax - overall.x; @@ -1787,7 +1946,15 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, const QTransform &matrix, QFontEngine::GlyphFormat format) { - Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true); + // When rendering glyphs into a cache via the alphaMap* functions, we disable + // outline drawing. To ensure the bounding box matches the rendered glyph, we + // need to do the same here. + + const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face) + && matrix.type() > QTransform::TxTranslate; + if (needsImageTransform && format == QFontEngine::Format_Mono) + format = QFontEngine::Format_A8; + Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true); glyph_metrics_t overall; if (g) { @@ -1813,7 +1980,7 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, unlockFace(); } - if (isScalableBitmap()) + if (isScalableBitmap() || needsImageTransform) overall = scaledBitmapMetrics(overall, matrix); return overall; } @@ -1913,12 +2080,17 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, const QFixedPoint &subPixelPosition, const QTransform &t) { - const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono; + const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face) + && t.type() > QTransform::TxTranslate; + const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono; Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); QImage img = alphaMapFromGlyphData(glyph, neededFormat); - img = img.copy(); + if (needsImageTransform) + img = img.transformed(t, Qt::SmoothTransformation); + else + img = img.copy(); if (!cacheEnabled && glyph != &emptyGlyph) delete glyph; |