diff options
Diffstat (limited to 'src/gui/text/qfontengine_ft.cpp')
-rw-r--r-- | src/gui/text/qfontengine_ft.cpp | 239 |
1 files changed, 187 insertions, 52 deletions
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 86fb0e8ae4..e89522f5af 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -52,6 +52,7 @@ #include <qscopedvaluerollback.h> #include "qthreadstorage.h" #include <qmath.h> +#include <qendian.h> #include <ft2build.h> #include FT_FREETYPE_H @@ -192,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); /* @@ -249,6 +259,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, newFreetype->ref.store(1); newFreetype->xsize = 0; newFreetype->ysize = 0; + newFreetype->scalableBitmapScaleFactor = 1; newFreetype->matrix.xx = 0x10000; newFreetype->matrix.yy = 0x10000; newFreetype->matrix.xy = 0; @@ -330,36 +341,46 @@ void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, *xsize = *ysize * fontDef.stretch / 100; *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)); @@ -736,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; @@ -829,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) @@ -849,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, @@ -893,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); @@ -1124,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; @@ -1135,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; @@ -1169,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; @@ -1239,46 +1293,68 @@ int QFontEngineFT::synthesized() const QFixed QFontEngineFT::ascent() const { - return QFixed::fromFixed(metrics.ascender); + QFixed v = QFixed::fromFixed(metrics.ascender); + if (freetype->scalableBitmapScaleFactor != 1) + v *= freetype->scalableBitmapScaleFactor; + return v; } QFixed QFontEngineFT::descent() const { - return QFixed::fromFixed(-metrics.descender); + QFixed v = QFixed::fromFixed(-metrics.descender); + if (freetype->scalableBitmapScaleFactor != 1) + v *= freetype->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 (freetype->scalableBitmapScaleFactor != 1) + v *= freetype->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) * freetype->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 (freetype->scalableBitmapScaleFactor != 1) + max_advance *= freetype->scalableBitmapScaleFactor; + return max_advance.toReal(); } QFixed QFontEngineFT::lineThickness() const @@ -1561,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 * freetype->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; @@ -1583,6 +1676,9 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlag if (!cacheEnabled && g != &emptyGlyph) delete g; } + + if (freetype->scalableBitmapScaleFactor != 1) + glyphs->advances[i] *= freetype->scalableBitmapScaleFactor; } if (face) unlockFace(); @@ -1599,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; @@ -1642,6 +1743,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) if (face) unlockFace(); + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1678,6 +1781,9 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) } if (face) unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1713,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; } @@ -1860,6 +1969,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() || freetype->scalableBitmapScaleFactor != 1)) { + QTransform trans(t); + const qreal scaleFactor = freetype->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); @@ -1881,9 +2015,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; |