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 <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Jian Liang 2016-04-01 22:03:15 +08:00 committed by jian liang
parent 324e277687
commit 347832d759
5 changed files with 289 additions and 128 deletions

View File

@ -40,7 +40,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) < 20110)
#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) < 20200)
# error "This version of freetype is too old."
#endif

View File

@ -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

View File

@ -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; i<c.h; ++i) {
funcs->glTexSubImage2D(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

View File

@ -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)
@ -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<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;
@ -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;

View File

@ -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;