From 347832d7593e2369f8597bbd06213b80b3087433 Mon Sep 17 00:00:00 2001 From: Jian Liang Date: Fri, 1 Apr 2016 22:03:15 +0800 Subject: Support color font rendering for freetype engine This patch mainly do two things: 1) Support color bitmap font for freetype fontengine. This partially based on Corentin Jabot's patch 2) Support ARGB opengl glyph cache when workaround_brokenFBOReadBack is true (It is always true under Android). Some code refactor has been done in QOpenGLTextureGlyphCache. This patch also bump the minimal required freetype version to 2.2 [ChangeLog][General][Freetype] Support color font rendering Task-number: QTBUG-35156 Change-Id: I35aae5f98ba9a27b70a48db3f2647fc070c39c33 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/3rdparty/freetype/freetype.pro | 3 + src/gui/opengl/qopengltextureglyphcache.cpp | 171 ++++++++++---------- src/gui/text/qfontengine_ft.cpp | 234 ++++++++++++++++++++++------ src/gui/text/qfontengine_ft_p.h | 7 + 4 files changed, 288 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/3rdparty/freetype/freetype.pro b/src/3rdparty/freetype/freetype.pro index 04aa3b8e18..d73d8ece55 100644 --- a/src/3rdparty/freetype/freetype.pro +++ b/src/3rdparty/freetype/freetype.pro @@ -71,6 +71,9 @@ contains(QT_CONFIG, system-zlib) { contains(QT_CONFIG, system-png) { DEFINES += FT_CONFIG_OPTION_USE_PNG include($$PWD/../png_dependency.pri) +} else:!contains(QT_CONFIG, no-png):!win32 { + DEFINES += FT_CONFIG_OPTION_USE_PNG + include($$PWD/../libpng.pri) } DEFINES += TT_CONFIG_OPTION_SUBPIXEL_HINTING diff --git a/src/gui/opengl/qopengltextureglyphcache.cpp b/src/gui/opengl/qopengltextureglyphcache.cpp index 8f69095464..9a7b1eb21d 100644 --- a/src/gui/opengl/qopengltextureglyphcache.cpp +++ b/src/gui/opengl/qopengltextureglyphcache.cpp @@ -181,6 +181,94 @@ void QOpenGLTextureGlyphCache::setupVertexAttribs() m_buffer.release(); } +static void load_glyph_image_to_texture(QOpenGLContext *ctx, + QImage &img, + GLuint texture, + int tx, int ty) +{ + QOpenGLFunctions *funcs = ctx->functions(); + + const int imgWidth = img.width(); + const int imgHeight = img.height(); + + if (img.format() == QImage::Format_Mono) { + img = img.convertToFormat(QImage::Format_Grayscale8); + } else if (img.depth() == 32) { + if (img.format() == QImage::Format_RGB32 + // We need to make the alpha component equal to the average of the RGB values. + // This is needed when drawing sub-pixel antialiased text on translucent targets. +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + || img.format() == QImage::Format_ARGB32_Premultiplied +#else + || (img.format() == QImage::Format_ARGB32_Premultiplied + && ctx->isOpenGLES()) +#endif + ) { + for (int y = 0; y < imgHeight; ++y) { + QRgb *src = (QRgb *) img.scanLine(y); + for (int x = 0; x < imgWidth; ++x) { + int r = qRed(src[x]); + int g = qGreen(src[x]); + int b = qBlue(src[x]); + int avg; + if (img.format() == QImage::Format_RGB32) + avg = (r + g + b + 1) / 3; // "+1" for rounding. + else // Format_ARGB_Premultiplied + avg = qAlpha(src[x]); + + src[x] = qRgba(r, g, b, avg); + // swizzle the bits to accommodate for the GL_RGBA upload. +#if Q_BYTE_ORDER != Q_BIG_ENDIAN + if (ctx->isOpenGLES()) +#endif + src[x] = ARGB2RGBA(src[x]); + } + } + } + } + + funcs->glBindTexture(GL_TEXTURE_2D, texture); + if (img.depth() == 32) { +#ifdef QT_OPENGL_ES_2 + GLenum fmt = GL_RGBA; +#else + GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA; +#endif // QT_OPENGL_ES_2 + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + fmt = GL_RGBA; +#endif + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, fmt, GL_UNSIGNED_BYTE, img.constBits()); + } else { + // The scanlines in image are 32-bit aligned, even for mono or 8-bit formats. This + // is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT. +#if !defined(QT_OPENGL_ES_2) + const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA; +#else + const GLenum format = GL_ALPHA; +#endif + funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, tx, ty, imgWidth, imgHeight, format, GL_UNSIGNED_BYTE, img.constBits()); + } +} + +static void load_glyph_image_region_to_texture(QOpenGLContext *ctx, + const QImage &srcImg, + int x, int y, + int w, int h, + GLuint texture, + int tx, int ty) +{ + Q_ASSERT(x + w <= srcImg.width() && y + h <= srcImg.height()); + + QImage img; + if (x != 0 || y != 0 || w != srcImg.width() || h != srcImg.height()) + img = srcImg.copy(x, y, w, h); + else + img = srcImg; + + load_glyph_image_to_texture(ctx, img, texture, tx, ty); +} + void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -207,9 +295,8 @@ void QOpenGLTextureGlyphCache::resizeTextureData(int width, int height) if (ctx->d_func()->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::resizeTextureData(width, height); - Q_ASSERT(image().depth() == 8); - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, oldHeight, GL_ALPHA, GL_UNSIGNED_BYTE, image().constBits()); - funcs->glDeleteTextures(1, &oldTexture); + load_glyph_image_region_to_texture(ctx, image(), 0, 0, qMin(oldWidth, width), qMin(oldHeight, height), + m_textureResource->m_texture, 0, 0); return; } @@ -336,88 +423,14 @@ void QOpenGLTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed return; } - QOpenGLFunctions *funcs = ctx->functions(); if (ctx->d_func()->workaround_brokenFBOReadBack) { QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - const QImage &texture = image(); - const uchar *bits = texture.constBits(); - bits += c.y * texture.bytesPerLine() + c.x; - for (int i=0; iglTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y + i, c.w, 1, GL_ALPHA, GL_UNSIGNED_BYTE, bits); - bits += texture.bytesPerLine(); - } + load_glyph_image_region_to_texture(ctx, image(), c.x, c.y, c.w, c.h, m_textureResource->m_texture, c.x, c.y); return; } QImage mask = textureMapForGlyph(glyph, subPixelPosition); - const int maskWidth = mask.width(); - const int maskHeight = mask.height(); - - if (mask.format() == QImage::Format_Mono) { - mask = mask.convertToFormat(QImage::Format_Indexed8); - for (int y = 0; y < maskHeight; ++y) { - uchar *src = (uchar *) mask.scanLine(y); - for (int x = 0; x < maskWidth; ++x) - src[x] = -src[x]; // convert 0 and 1 into 0 and 255 - } - } else if (mask.depth() == 32) { - if (mask.format() == QImage::Format_RGB32 - // We need to make the alpha component equal to the average of the RGB values. - // This is needed when drawing sub-pixel antialiased text on translucent targets. -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - || mask.format() == QImage::Format_ARGB32_Premultiplied -#else - || (mask.format() == QImage::Format_ARGB32_Premultiplied - && ctx->isOpenGLES()) -#endif - ) { - for (int y = 0; y < maskHeight; ++y) { - QRgb *src = (QRgb *) mask.scanLine(y); - for (int x = 0; x < maskWidth; ++x) { - int r = qRed(src[x]); - int g = qGreen(src[x]); - int b = qBlue(src[x]); - int avg; - if (mask.format() == QImage::Format_RGB32) - avg = (r + g + b + 1) / 3; // "+1" for rounding. - else // Format_ARGB_Premultiplied - avg = qAlpha(src[x]); - - src[x] = qRgba(r, g, b, avg); - // swizzle the bits to accommodate for the GL_RGBA upload. -#if Q_BYTE_ORDER != Q_BIG_ENDIAN - if (ctx->isOpenGLES()) -#endif - src[x] = ARGB2RGBA(src[x]); - } - } - } - } - - funcs->glBindTexture(GL_TEXTURE_2D, m_textureResource->m_texture); - if (mask.depth() == 32) { -#ifdef QT_OPENGL_ES_2 - GLenum fmt = GL_RGBA; -#else - GLenum fmt = ctx->isOpenGLES() ? GL_RGBA : GL_BGRA; -#endif // QT_OPENGL_ES_2 - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - fmt = GL_RGBA; -#endif - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, fmt, GL_UNSIGNED_BYTE, mask.bits()); - } else { - // The scanlines in mask are 32-bit aligned, even for mono or 8-bit formats. This - // is good because it matches the default of 4 bytes for GL_UNPACK_ALIGNMENT. -#if !defined(QT_OPENGL_ES_2) - const GLenum format = isCoreProfile() ? GL_RED : GL_ALPHA; -#else - const GLenum format = GL_ALPHA; -#endif - funcs->glTexSubImage2D(GL_TEXTURE_2D, 0, c.x, c.y, maskWidth, maskHeight, format, GL_UNSIGNED_BYTE, mask.bits()); - } + load_glyph_image_to_texture(ctx, mask, m_textureResource->m_texture, c.x, c.y); } int QOpenGLTextureGlyphCache::glyphPadding() const diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index ec995d5cc6..e89522f5af 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -52,6 +52,7 @@ #include #include "qthreadstorage.h" #include +#include #include #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) @@ -890,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); @@ -1121,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; @@ -1132,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; @@ -1166,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(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; @@ -1236,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 @@ -1558,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; @@ -1580,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(); @@ -1596,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; @@ -1639,6 +1743,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) if (face) unlockFace(); + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1675,6 +1781,9 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) } if (face) unlockFace(); + + if (isScalableBitmap()) + overall = scaledBitmapMetrics(overall); return overall; } @@ -1710,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; } @@ -1857,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); @@ -1878,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; diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index ee2df6f665..3f4bf84753 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -96,6 +96,7 @@ public: FT_Face face; int xsize; // 26.6 int ysize; // 26.6 + QFixed scalableBitmapScaleFactor; FT_Matrix matrix; FT_CharMap unicode_map; FT_CharMap symbol_map; @@ -107,6 +108,8 @@ public: int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints); + bool isScalableBitmap() const; + static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale); static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path); @@ -239,6 +242,7 @@ private: QImage alphaMapForGlyph(glyph_t, QFixed) Q_DECL_OVERRIDE; QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; + QImage bitmapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t) Q_DECL_OVERRIDE; glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, @@ -266,6 +270,7 @@ private: inline bool drawAntialiased() const { return antialias; } inline bool invalid() const { return xsize == 0 && ysize == 0; } inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } + inline bool isScalableBitmap() const { return freetype->isScalableBitmap(); } inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false) const { return loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, subPixelPosition, format, fetchMetricsOnly); } @@ -316,6 +321,8 @@ private: int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; bool shouldUseDesignMetrics(ShaperFlags flags) const; + QFixed scaledBitmapMetrics(QFixed m) const; + glyph_metrics_t scaledBitmapMetrics(const glyph_metrics_t &m) const; GlyphFormat defaultFormat; FT_Matrix matrix; -- cgit v1.2.3