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:
parent
324e277687
commit
347832d759
@ -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
|
||||
|
||||
|
3
src/3rdparty/freetype/freetype.pro
vendored
3
src/3rdparty/freetype/freetype.pro
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user