summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qfontengine_ft.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/text/qfontengine_ft.cpp')
-rw-r--r--src/gui/text/qfontengine_ft.cpp295
1 files changed, 216 insertions, 79 deletions
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
index 4de41dfa99..51b1418bc3 100644
--- a/src/gui/text/qfontengine_ft.cpp
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -1,31 +1,37 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2016 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:LGPL21$
+** $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 http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
+** 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 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** 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.
**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** 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$
**
@@ -46,6 +52,7 @@
#include <qscopedvaluerollback.h>
#include "qthreadstorage.h"
#include <qmath.h>
+#include <qendian.h>
#include <ft2build.h>
#include FT_FREETYPE_H
@@ -186,6 +193,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF
return Err_Ok;
}
+bool QFreetypeFace::isScalableBitmap() const
+{
+#ifdef FT_HAS_COLOR
+ return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
+#else
+ return false;
+#endif
+}
+
extern QByteArray qt_fontdata_from_index(int);
/*
@@ -240,9 +256,6 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
}
newFreetype->face = face;
- newFreetype->hbFace = 0;
- newFreetype->hbFace_destroy_func = 0;
-
newFreetype->ref.store(1);
newFreetype->xsize = 0;
newFreetype->ysize = 0;
@@ -294,10 +307,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
void QFreetypeFace::cleanup()
{
- if (hbFace && hbFace_destroy_func) {
- hbFace_destroy_func(hbFace);
- hbFace = 0;
- }
+ hbFace.reset();
FT_Done_Face(face);
face = 0;
}
@@ -324,42 +334,53 @@ void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
}
-void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing)
+void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
{
*ysize = qRound(fontDef.pixelSize * 64);
*xsize = *ysize * fontDef.stretch / 100;
+ *scalableBitmapScaleFactor = 1;
*outline_drawing = false;
- /*
- * Bitmap only faces must match exactly, so find the closest
- * one (height dominant search)
- */
if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
int best = 0;
- for (int i = 1; i < face->num_fixed_sizes; i++) {
- if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
- qAbs(*ysize - face->available_sizes[best].y_ppem) ||
- (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
- qAbs(*ysize - face->available_sizes[best].y_ppem) &&
- qAbs(*xsize - face->available_sizes[i].x_ppem) <
- qAbs(*xsize - face->available_sizes[best].x_ppem))) {
- best = i;
+ if (!isScalableBitmap()) {
+ /*
+ * Bitmap only faces must match exactly, so find the closest
+ * one (height dominant search)
+ */
+ for (int i = 1; i < face->num_fixed_sizes; i++) {
+ if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
+ qAbs(*ysize - face->available_sizes[best].y_ppem) ||
+ (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
+ qAbs(*ysize - face->available_sizes[best].y_ppem) &&
+ qAbs(*xsize - face->available_sizes[i].x_ppem) <
+ qAbs(*xsize - face->available_sizes[best].x_ppem))) {
+ best = i;
+ }
+ }
+ } else {
+ // Select the shortest bitmap strike whose height is larger than the desired height
+ for (int i = 1; i < face->num_fixed_sizes; i++) {
+ if (face->available_sizes[i].y_ppem < *ysize) {
+ if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
+ best = i;
+ } else if (face->available_sizes[best].y_ppem < *ysize) {
+ best = i;
+ } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
+ best = i;
+ }
}
}
- if (FT_Set_Char_Size(face, face->available_sizes[best].x_ppem, face->available_sizes[best].y_ppem, 0, 0) == 0) {
+
+ // According to freetype documentation we must use FT_Select_Size
+ // to make sure we can select the desired bitmap strike index
+ if (FT_Select_Size(face, best) == 0) {
+ if (isScalableBitmap())
+ *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height);
*xsize = face->available_sizes[best].x_ppem;
*ysize = face->available_sizes[best].y_ppem;
} else {
- int err = 1;
- if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) {
- // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property
- err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height);
- if (err && face->num_fixed_sizes == 1)
- err = 0; //even more of a workaround...
- }
-
- if (err)
- *xsize = *ysize = 0;
+ *xsize = *ysize = 0;
}
} else {
*outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
@@ -680,6 +701,8 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData));
}
+static void dont_delete(void*) {}
+
bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
QFreetypeFace *freetypeFace)
{
@@ -706,7 +729,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive));
}
- freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing);
+ freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
FT_Face face = lockFace();
@@ -734,6 +757,11 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
if (line_thickness < 2 && score >= 1050)
line_thickness = 2;
underline_position = ((line_thickness * 2) + 3) / 6;
+
+ if (isScalableBitmap()) {
+ glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
+ cacheEnabled = false;
+ }
}
if (line_thickness < 1)
line_thickness = 1;
@@ -770,13 +798,13 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
if (!freetype->hbFace) {
faceData.user_data = face;
faceData.get_font_table = ft_getSfntTable;
- freetype->hbFace = harfbuzzFace();
- freetype->hbFace_destroy_func = face_destroy_func;
+ (void)harfbuzzFace(); // populates face_
+ freetype->hbFace = std::move(face_);
} else {
Q_ASSERT(!face_);
- face_ = freetype->hbFace;
}
- face_destroy_func = 0; // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it
+ // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it
+ face_ = Holder(freetype->hbFace.get(), dont_delete);
unlockFace();
@@ -827,6 +855,10 @@ int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
load_target = FT_LOAD_TARGET_LCD_V;
vfactor = 3;
}
+ } else if (format == Format_ARGB) {
+#ifdef FT_LOAD_COLOR
+ load_flags |= FT_LOAD_COLOR;
+#endif
}
if (set && set->outline_drawing)
@@ -847,11 +879,8 @@ static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
{
// false if exceeds QFontEngineFT::Glyph metrics
return (short)(info.linearAdvance) != info.linearAdvance
- || (signed char)(info.xOff) != info.xOff
|| (uchar)(info.width) != info.width
- || (uchar)(info.height) != info.height
- || (signed char)(info.x) != info.x
- || (signed char)(info.y) != info.y;
+ || (uchar)(info.height) != info.height;
}
QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
@@ -891,7 +920,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
|| matrix.xy != 0
|| matrix.yx != 0;
- if (transform || (format != Format_Mono && !embeddedbitmap))
+ if (transform || (format != Format_Mono && !isScalableBitmap()))
load_flags |= FT_LOAD_NO_BITMAP;
FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
@@ -1122,7 +1151,11 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
if (bitmap.buffer != glyph_buffer.data())
delete [] bitmap.buffer;
} else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
+#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500)
+ Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO || slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA);
+#else
Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO);
+#endif
uchar *src = slot->bitmap.buffer;
uchar *dst = glyph_buffer.data();
int h = slot->bitmap.rows;
@@ -1133,7 +1166,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
dst += pitch;
src += slot->bitmap.pitch;
}
- } else {
+ } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
if (hsubpixel) {
while (h--) {
uint *dd = (uint *)dst;
@@ -1167,6 +1200,29 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
}
}
}
+#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500)
+ else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
+ {
+ while (h--) {
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ const quint32 *srcPixel = (const quint32 *)src;
+ quint32 *dstPixel = (quint32 *)dst;
+ for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
+ const quint32 pixel = *srcPixel;
+ *dstPixel = qbswap(pixel);
+ }
+#else
+ memcpy(dst, src, slot->bitmap.width * 4);
+#endif
+ dst += slot->bitmap.pitch;
+ src += slot->bitmap.pitch;
+ }
+ info.width = info.linearAdvance = info.xOff = slot->bitmap.width;
+ info.height = slot->bitmap.rows;
+ info.x = slot->bitmap_left;
+ info.y = slot->bitmap_top;
+ }
+#endif
} else {
qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format);
return 0;
@@ -1237,46 +1293,68 @@ int QFontEngineFT::synthesized() const
QFixed QFontEngineFT::ascent() const
{
- return QFixed::fromFixed(metrics.ascender);
+ QFixed v = QFixed::fromFixed(metrics.ascender);
+ if (scalableBitmapScaleFactor != 1)
+ v *= scalableBitmapScaleFactor;
+ return v;
}
QFixed QFontEngineFT::descent() const
{
- return QFixed::fromFixed(-metrics.descender);
+ QFixed v = QFixed::fromFixed(-metrics.descender);
+ if (scalableBitmapScaleFactor != 1)
+ v *= scalableBitmapScaleFactor;
+ return v;
}
QFixed QFontEngineFT::leading() const
{
- return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
+ QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
+ if (scalableBitmapScaleFactor != 1)
+ v *= scalableBitmapScaleFactor;
+ return v;
}
QFixed QFontEngineFT::xHeight() const
{
- TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
- if (os2 && os2->sxHeight) {
- lockFace();
- QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
- unlockFace();
- return answer;
+ if (!isScalableBitmap()) {
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
+ if (os2 && os2->sxHeight) {
+ lockFace();
+ QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
+ unlockFace();
+ return answer;
+ }
+ } else {
+ return QFixed(freetype->face->size->metrics.y_ppem) * scalableBitmapScaleFactor;
}
return QFontEngine::xHeight();
}
QFixed QFontEngineFT::averageCharWidth() const
{
- TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
- if (os2 && os2->xAvgCharWidth) {
- lockFace();
- QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
- unlockFace();
- return answer;
- }
- return QFontEngine::averageCharWidth();
+ if (!isScalableBitmap()) {
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
+ if (os2 && os2->xAvgCharWidth) {
+ lockFace();
+ QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
+ unlockFace();
+ return answer;
+ }
+ } else {
+ const qreal aspectRatio = (qreal)xsize / ysize;
+ return QFixed::fromReal(fontDef.pixelSize * aspectRatio);
+ }
+
+ return QFontEngine::averageCharWidth();
}
qreal QFontEngineFT::maxCharWidth() const
{
- return metrics.max_advance >> 6;
+ QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
+ if (scalableBitmapScaleFactor != 1)
+ max_advance *= scalableBitmapScaleFactor;
+ return max_advance.toReal();
}
QFixed QFontEngineFT::lineThickness() const
@@ -1559,6 +1637,23 @@ bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics);
}
+QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
+{
+ return m * scalableBitmapScaleFactor;
+}
+
+glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m) const
+{
+ glyph_metrics_t metrics;
+ metrics.x = scaledBitmapMetrics(m.x);
+ metrics.y = scaledBitmapMetrics(m.y);
+ metrics.width = scaledBitmapMetrics(m.width);
+ metrics.height = scaledBitmapMetrics(m.height);
+ metrics.xoff = scaledBitmapMetrics(m.xoff);
+ metrics.yoff = scaledBitmapMetrics(m.yoff);
+ return metrics;
+}
+
void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
{
FT_Face face = 0;
@@ -1581,6 +1676,9 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlag
if (!cacheEnabled && g != &emptyGlyph)
delete g;
}
+
+ if (scalableBitmapScaleFactor != 1)
+ glyphs->advances[i] *= scalableBitmapScaleFactor;
}
if (face)
unlockFace();
@@ -1597,8 +1695,13 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
glyph_metrics_t overall;
// initialize with line height, we get the same behaviour on all platforms
- overall.y = -ascent();
- overall.height = ascent() + descent();
+ if (!isScalableBitmap()) {
+ overall.y = -ascent();
+ overall.height = ascent() + descent();
+ } else {
+ overall.y = QFixed::fromFixed(-metrics.ascender);
+ overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
+ }
QFixed ymax = 0;
QFixed xmax = 0;
@@ -1640,6 +1743,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
if (face)
unlockFace();
+ if (isScalableBitmap())
+ overall = scaledBitmapMetrics(overall);
return overall;
}
@@ -1676,6 +1781,9 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
}
if (face)
unlockFace();
+
+ if (isScalableBitmap())
+ overall = scaledBitmapMetrics(overall);
return overall;
}
@@ -1711,6 +1819,9 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixe
overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
unlockFace();
}
+
+ if (isScalableBitmap())
+ overall = scaledBitmapMetrics(overall);
return overall;
}
@@ -1861,6 +1972,31 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, co
return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
}
+QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
+{
+ Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t);
+ if (glyph == Q_NULLPTR)
+ return QImage();
+
+ QImage img;
+ if (defaultFormat == GlyphFormat::Format_ARGB)
+ img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
+ else if (defaultFormat == GlyphFormat::Format_Mono)
+ img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
+
+ if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) {
+ QTransform trans(t);
+ const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
+ trans.scale(scaleFactor, scaleFactor);
+ img = img.transformed(trans, Qt::SmoothTransformation);
+ }
+
+ if (!cacheEnabled && glyph != &emptyGlyph)
+ delete glyph;
+
+ return img;
+}
+
void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
{
defaultGlyphSet.removeGlyphFromCache(glyph, 0);
@@ -1882,9 +2018,10 @@ FT_Face QFontEngineFT::lockFace(Scaling scale) const
freetype->lock();
FT_Face face = freetype->face;
if (scale == Unscaled) {
- FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
- freetype->xsize = face->units_per_EM << 6;
- freetype->ysize = face->units_per_EM << 6;
+ if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
+ freetype->xsize = face->units_per_EM << 6;
+ freetype->ysize = face->units_per_EM << 6;
+ }
} else if (freetype->xsize != xsize || freetype->ysize != ysize) {
FT_Set_Char_Size(face, xsize, ysize, 0, 0);
freetype->xsize = xsize;