/****************************************************************************
**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
** 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 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
**
**
**
#include "qtextstream.h"
#include "qvariant.h"
#include "qfontengine_ft_p.h"
+#include "private/qimage_p.h"
#ifndef QT_NO_FREETYPE
#include "qfile.h"
-#include "qabstractfileengine.h"
+#include "qfileinfo.h"
#include "qthreadstorage.h"
#include <qmath.h>
-#include <private/qharfbuzz_p.h>
#include "qfontengine_ft_p.h"
#include <ft2build.h>
#include FT_ERRORS_H
#endif
+#if !defined(QT_MAX_CACHED_GLYPH_SIZE)
+# define QT_MAX_CACHED_GLYPH_SIZE 64
+#endif
+
QT_BEGIN_NAMESPACE
/*
#define Q_FT_GLYPHSLOT_EMBOLDEN(slot)
#endif
+/* FreeType 2.1.10 starts to provide FT_GlyphSlot_Oblique */
+#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20110
+#define Q_HAS_FT_GLYPHSLOT_OBLIQUE
+#define Q_FT_GLYPHSLOT_OBLIQUE(slot) FT_GlyphSlot_Oblique(slot)
+#else
+#define Q_FT_GLYPHSLOT_OBLIQUE(slot)
+#endif
+
#define FLOOR(x) ((x) & -64)
#define CEIL(x) (((x)+63) & -64)
#define TRUNC(x) ((x) >> 6)
return HB_Err_Ok;
}
+extern QByteArray qt_fontdata_from_index(int);
+
/*
* One font file can contain more than one font (bold/italic for example)
* find the right one and return it.
QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace);
FT_Face face;
if (!face_id.filename.isEmpty()) {
- QFile file(QString::fromUtf8(face_id.filename));
+ QString fileName = QString::fromUtf8(face_id.filename);
if (face_id.filename.startsWith(":qmemoryfonts/")) {
// from qfontdatabase.cpp
- extern QByteArray qt_fontdata_from_index(int);
QByteArray idx = face_id.filename;
idx.remove(0, 14); // remove ':qmemoryfonts/'
bool ok = false;
newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
if (!ok)
newFreetype->fontData = QByteArray();
- } else if (!(file.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) {
+ } else if (!QFileInfo(fileName).isNativePath()) {
+ QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
return 0;
}
newFreetype->hbFace = qHBNewFace(face, hb_getSFntTable);
Q_CHECK_PTR(newFreetype->hbFace);
- newFreetype->ref = 1;
+ newFreetype->ref.store(1);
newFreetype->xsize = 0;
newFreetype->ysize = 0;
newFreetype->matrix.xx = 0x10000;
newFreetype->matrix.yx = 0;
newFreetype->unicode_map = 0;
newFreetype->symbol_map = 0;
-#ifndef QT_NO_FONTCONFIG
- newFreetype->charset = 0;
-#endif
memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
FT_Set_Char_Size (face, X_SIZE(newFreetype->face, 0), Y_SIZE(newFreetype->face, 0), 0, 0);
-# if 0
- FcChar8 *name;
- FcPatternGetString(pattern, FC_FAMILY, 0, &name);
- qDebug("%s: using maps: default: %x unicode: %x, symbol: %x", name,
- newFreetype->face->charmap ? newFreetype->face->charmap->encoding : 0,
- newFreetype->unicode_map ? newFreetype->unicode_map->encoding : 0,
- newFreetype->symbol_map ? newFreetype->symbol_map->encoding : 0);
-
- for (int i = 0; i < 256; i += 8)
- qDebug(" %x: %d %d %d %d %d %d %d %d", i,
- FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
- FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
- FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i),
- FcCharSetHasChar(newFreetype->charset, i), FcCharSetHasChar(newFreetype->charset, i));
-#endif
FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
QT_TRY {
if (!ref.deref()) {
qHBFreeFace(hbFace);
FT_Done_Face(face);
-#ifndef QT_NO_FONTCONFIG
- if (charset)
- FcCharSetDestroy(charset);
-#endif
if(freetypeData->faces.contains(face_id))
freetypeData->faces.take(face_id);
delete this;
*xsize = *ysize = 0;
}
} else {
- *outline_drawing = (*xsize > (64<<6) || *ysize > (64<<6));
+ *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
}
}
kerning_pairs_loaded = false;
transform = false;
embolden = false;
+ obliquen = false;
antialias = true;
freetype = 0;
default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+#ifndef Q_OS_WIN
default_hint_style = HintNone;
+#else
+ default_hint_style = HintFull;
+#endif
subpixelType = Subpixel_None;
lcdFilterType = 0;
#if defined(FT_LCD_FILTER_H)
lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
#endif
defaultFormat = Format_None;
- canUploadGlyphsToServer = false;
embeddedbitmap = false;
+ cacheEnabled = qgetenv("QT_NO_FT_CACHE").isEmpty() || qgetenv("QT_NO_FT_CACHE").toInt() == 0;
+ m_subPixelPositionCount = 4;
}
QFontEngineFT::~QFontEngineFT()
hbFace = 0; // we share the face in QFreeTypeFace, don't let ~QFontEngine delete it
}
-void QFontEngineFT::freeGlyphSets()
-{
- freeServerGlyphSet(defaultGlyphSet.id);
- for (int i = 0; i < transformedGlyphSets.count(); ++i)
- freeServerGlyphSet(transformedGlyphSets.at(i).id);
-}
-
bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
const QByteArray &fontData)
{
if (FT_IS_SCALABLE(face)) {
bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC);
- if (fake_oblique)
+ if (fake_oblique) {
+#if !defined(Q_HAS_FT_GLYPHSLOT_OBLIQUE)
matrix.xy = 0x10000*3/10;
+ transform = true;
+#else
+ obliquen = true;
+#endif
+ }
FT_Set_Transform(face, &matrix, 0);
freetype->matrix = matrix;
- if (fake_oblique)
- transform = true;
// fake bold
if ((fontDef.weight == QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face))
embolden = true;
metrics = face->size->metrics;
-#if defined(Q_WS_QWS) || defined(Q_WS_QPA)
/*
TrueType fonts with embedded bitmaps may have a bitmap font specific
ascent/descent in the EBLC table. There is no direct public API
}
}
}
-#endif
+
+ fontDef.styleName = QString::fromUtf8(face->style_name);
unlockFace();
fsType = freetype->fsType();
- defaultGlyphSet.id = allocateServerGlyphSet();
return true;
}
if (set && set->outline_drawing)
load_flags = FT_LOAD_NO_BITMAP;
- if (default_hint_style == HintNone || (flags & HB_ShaperFlag_UseDesignMetrics))
+ if (default_hint_style == HintNone || (flags & HB_ShaperFlag_UseDesignMetrics) || (set && set->outline_drawing))
load_flags |= FT_LOAD_NO_HINTING;
else
load_flags |= load_target;
{
// Q_ASSERT(freetype->lock == 1);
- bool uploadToServer = false;
if (format == Format_None) {
if (defaultFormat != Format_None) {
format = defaultFormat;
- if (canUploadGlyphsToServer)
- uploadToServer = true;
} else {
format = Format_Mono;
}
}
- Glyph *g = set->getGlyph(glyph, subPixelPosition);
- if (g && g->format == format) {
- if (uploadToServer && !g->uploadedToServer) {
- set->setGlyph(glyph, subPixelPosition, 0);
- delete g;
- g = 0;
- } else {
- return g;
- }
- }
+ Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0;
+ if (g && g->format == format && (fetchMetricsOnly || g->data))
+ return g;
QFontEngineFT::GlyphInfo info;
int vfactor = 1;
int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
-#ifndef Q_WS_QWS
if (format != Format_Mono && !embeddedbitmap)
load_flags |= FT_LOAD_NO_BITMAP;
-#endif
FT_Matrix matrix = freetype->matrix;
bool transform = matrix.xx != 0x10000
if (err != FT_Err_Ok)
qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
- if (set->outline_drawing && fetchMetricsOnly)
- return 0;
-
FT_GlyphSlot slot = face->glyph;
+
if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(slot);
+ if (obliquen) {
+ Q_FT_GLYPHSLOT_OBLIQUE(slot);
+
+ // While Embolden alters the metrics of the slot, oblique does not, so we need
+ // to fix this ourselves.
+ transform = true;
+ FT_Matrix m;
+ m.xx = 0x10000;
+ m.yx = 0x0;
+ m.xy = 0x6000;
+ m.yy = 0x10000;
+
+ FT_Matrix_Multiply(&m, &matrix);
+ }
+
FT_Library library = qt_getFreetype();
info.xOff = TRUNC(ROUND(slot->advance.x));
info.yOff = 0;
+ if ((set && set->outline_drawing) || fetchMetricsOnly) {
+ // If the advance doesn't fit in signed char, don't cache it
+ if (qAbs(info.xOff) >= 128)
+ return 0;
+ g = new Glyph;
+ g->data = 0;
+ g->linearAdvance = slot->linearHoriAdvance >> 10;
+ int left = FLOOR(slot->metrics.horiBearingX);
+ int right = CEIL(slot->metrics.horiBearingX + slot->metrics.width);
+ int top = CEIL(slot->metrics.horiBearingY);
+ int bottom = FLOOR(slot->metrics.horiBearingY - slot->metrics.height);
+ g->width = TRUNC(right-left);
+ g->height = TRUNC(top-bottom);
+ g->x = TRUNC(left);
+ g->y = TRUNC(top);
+ g->advance = TRUNC(ROUND(slot->advance.x));
+ g->format = format;
+
+ if (set)
+ set->setGlyph(glyph, subPixelPosition, g);
+
+ return g;
+ }
+
uchar *glyph_buffer = 0;
int glyph_buffer_size = 0;
#if defined(QT_USE_FREETYPE_LCDFILTER)
if (!g) {
g = new Glyph;
- g->uploadedToServer = false;
g->data = 0;
}
delete [] g->data;
g->data = glyph_buffer;
- if (uploadToServer) {
- uploadGlyphToServer(set, glyph, g, &info, glyph_buffer_size);
- }
-
- set->setGlyph(glyph, subPixelPosition, g);
+ if (set)
+ set->setGlyph(glyph, subPixelPosition, g);
return g;
}
-bool QFontEngineFT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph *g, GlyphInfo *info, int glyphDataSize) const
-{
- Q_UNUSED(set);
- Q_UNUSED(glyphid);
- Q_UNUSED(g);
- Q_UNUSED(info);
- Q_UNUSED(glyphDataSize);
- return false;
-}
-
QFontEngine::FaceId QFontEngineFT::faceId() const
{
return face_id;
QFixed QFontEngineFT::descent() const
{
- // subtract a pixel to work around QFontMetrics's built-in + 1
- return QFixed::fromFixed(-metrics.descender - 64);
+ return QFixed::fromFixed(-metrics.descender);
}
QFixed QFontEngineFT::leading() const
88,
89,
91,
+ 95,
102,
114,
124,
if (!gs) {
// don't try to load huge fonts
- bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= 64;
+ bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= QT_MAX_CACHED_GLYPH_SIZE;
if (draw_as_outline)
return 0;
// don't cache more than 10 transformations
if (transformedGlyphSets.count() >= 10) {
transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
- freeServerGlyphSet(transformedGlyphSets.at(0).id);
} else {
transformedGlyphSets.prepend(QGlyphSet());
}
gs = &transformedGlyphSets[0];
-
gs->clear();
-
- gs->id = allocateServerGlyphSet();
-
gs->transformationMatrix = m;
gs->outline_drawing = draw_as_outline;
}
return gs;
}
-QFixed QFontEngineFT::subPixelPositionForX(QFixed x)
-{
- int m_subPixelPositionCount = 4;
- if (!supportsSubPixelPositions())
- return 0;
-
- QFixed subPixelPosition;
- if (x != 0) {
- subPixelPosition = x - x.floor();
- QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor();
- subPixelPosition = fraction / QFixed(m_subPixelPositionCount);
- }
- return subPixelPosition;
-}
-
bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs,
const QFixedPoint *positions,
GlyphFormat format)
for (int i = 0; i < num_glyphs; ++i) {
QFixed spp = subPixelPositionForX(positions[i].x);
- Glyph *glyph = gs->getGlyph(glyphs[i], spp);
+ Glyph *glyph = gs ? gs->getGlyph(glyphs[i], spp) : 0;
if (glyph == 0 || glyph->format != format) {
if (!face) {
face = lockFace();
static inline unsigned int getChar(const QChar *str, int &i, const int len)
{
- unsigned int uc = str[i].unicode();
- if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
- uint low = str[i+1].unicode();
- if (low >= 0xdc00 && low < 0xe000) {
- uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
- ++i;
- }
+ uint ucs4 = str[i].unicode();
+ if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) {
+ ++i;
+ ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode());
}
- return uc;
+ return ucs4;
}
bool QFontEngineFT::canRender(const QChar *string, int len)
{
FT_Face face = freetype->face;
-#if 0
- if (_cmap != -1) {
- lockFace();
- for ( int i = 0; i < len; i++ ) {
- unsigned int uc = getChar(string, i, len);
- if (!FcCharSetHasChar (_font->charset, uc) && getAdobeCharIndex(face, _cmap, uc) == 0) {
- allExist = false;
- break;
- }
- }
- unlockFace();
- } else
-#endif
{
for ( int i = 0; i < len; i++ ) {
unsigned int uc = getChar(string, i, len);
FT_GlyphSlot g = face->glyph;
if (g->format != FT_GLYPH_FORMAT_OUTLINE)
continue;
+ if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(g);
+ if (obliquen) Q_FT_GLYPHSLOT_OBLIQUE(g);
QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
}
unlockFace();
return false;
}
-#if !defined(QT_NO_FONTCONFIG)
- extern QMutex *qt_fontdatabase_mutex();
- QMutex *mtx = 0;
-#endif
-
bool mirrored = flags & QTextEngine::RightToLeft;
int glyph_pos = 0;
if (freetype->symbol_map) {
unsigned int uc = getChar(str, i, len);
glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
if ( !glyphs->glyphs[glyph_pos] ) {
- glyph_t glyph;
-#if !defined(QT_NO_FONTCONFIG)
- if (!mtx) {
- mtx = qt_fontdatabase_mutex();
- mtx->lock();
- }
-
- if (freetype->charset != 0 && FcCharSetHasChar(freetype->charset, uc)) {
-#else
- if (false) {
-#endif
- redo0:
+ // Symbol fonts can have more than one CMAPs, FreeType should take the
+ // correct one for us by default, so we always try FT_Get_Char_Index
+ // first. If it didn't work (returns 0), we will explicitly set the
+ // CMAP to symbol font one and try again. symbol_map is not always the
+ // correct one because in certain fonts like Wingdings symbol_map only
+ // contains PUA codepoints instead of the common ones.
+ glyph_t glyph = FT_Get_Char_Index(face, uc);
+ // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9),
+ // while we usually want to render them as space
+ if (!glyph && (uc == 0xa0 || uc == 0x9)) {
+ uc = 0x20;
glyph = FT_Get_Char_Index(face, uc);
- if (!glyph && (uc == 0xa0 || uc == 0x9)) {
- uc = 0x20;
- goto redo0;
- }
- } else {
+ }
+ if (!glyph) {
FT_Set_Charmap(face, freetype->symbol_map);
glyph = FT_Get_Char_Index(face, uc);
FT_Set_Charmap(face, freetype->unicode_map);
uc = QChar::mirroredChar(uc);
glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
if (!glyphs->glyphs[glyph_pos]) {
-#if !defined(QT_NO_FONTCONFIG)
- if (!mtx) {
- mtx = qt_fontdatabase_mutex();
- mtx->lock();
- }
-
- if (freetype->charset == 0 || FcCharSetHasChar(freetype->charset, uc))
-#endif
{
redo:
glyph_t glyph = FT_Get_Char_Index(face, uc);
*nglyphs = glyph_pos;
glyphs->numGlyphs = glyph_pos;
-#if !defined(QT_NO_FONTCONFIG)
- if (mtx)
- mtx->unlock();
-#endif
-
if (flags & QTextEngine::GlyphIndicesOnly)
return true;
default_hint_style == HintLight ||
(flags & HB_ShaperFlag_UseDesignMetrics)) && FT_IS_SCALABLE(freetype->face);
for (int i = 0; i < glyphs->numGlyphs; i++) {
- Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]);
- if (g) {
+ Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0;
+ // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph
+ GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
+ if (g && g->format == acceptableFormat) {
glyphs->advances_x[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
} else {
if (!face)
face = lockFace();
- g = loadGlyph(glyphs->glyphs[i], 0, Format_None, true);
+ g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true);
glyphs->advances_x[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
: QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
}
glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
{
-
FT_Face face = 0;
glyph_metrics_t overall;
// initialize with line height, we get the same behaviour on all platforms
overall.y = -ascent();
- overall.height = ascent() + descent() + 1;
+ overall.height = ascent() + descent();
QFixed ymax = 0;
QFixed xmax = 0;
for (int i = 0; i < glyphs.numGlyphs; i++) {
- Glyph *g = defaultGlyphSet.getGlyph(glyphs.glyphs[i]);
+ Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0;
if (!g) {
if (!face)
face = lockFace();
- g = loadGlyph(glyphs.glyphs[i], 0, Format_None, true);
+ g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true);
}
if (g) {
QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
overall.y = qMin(overall.y, y);
xmax = qMax(xmax, x + g->width);
ymax = qMax(ymax, y + g->height);
- overall.xoff += qRound(g->advance);
+ overall.xoff += g->advance;
} else {
int left = FLOOR(face->glyph->metrics.horiBearingX);
int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
overall.y = qMin(overall.y, y);
xmax = qMax(xmax, x + TRUNC(right - left));
ymax = qMax(ymax, y + TRUNC(top - bottom));
- overall.xoff += qRound(TRUNC(ROUND(face->glyph->advance.x)));
+ overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x)));
}
}
overall.height = qMax(overall.height, ymax - overall.y);
{
FT_Face face = 0;
glyph_metrics_t overall;
- Glyph *g = defaultGlyphSet.getGlyph(glyph);
+ Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0;
if (!g) {
face = lockFace();
- g = loadGlyph(glyph, 0, Format_None, true);
+ g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true);
}
if (g) {
overall.x = g->x;
return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None);
}
+static FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
+{
+ FT_Matrix m;
+
+ m.xx = FT_Fixed(matrix.m11() * 65536);
+ m.xy = FT_Fixed(-matrix.m21() * 65536);
+ m.yx = FT_Fixed(-matrix.m12() * 65536);
+ m.yy = FT_Fixed(matrix.m22() * 65536);
+
+ return m;
+}
+
glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format)
{
FT_Face face = 0;
glyph_metrics_t overall;
QGlyphSet *glyphSet = 0;
- if (matrix.type() > QTransform::TxTranslate && FT_IS_SCALABLE(freetype->face)) {
- // TODO move everything here to a method of its own to access glyphSets
- // to be shared with a new method that will replace loadTransformedGlyphSet()
- FT_Matrix m;
- m.xx = FT_Fixed(matrix.m11() * 65536);
- m.xy = FT_Fixed(-matrix.m21() * 65536);
- m.yx = FT_Fixed(-matrix.m12() * 65536);
- m.yy = FT_Fixed(matrix.m22() * 65536);
- for (int i = 0; i < transformedGlyphSets.count(); ++i) {
- const QGlyphSet &g = transformedGlyphSets.at(i);
- if (g.transformationMatrix.xx == m.xx
- && g.transformationMatrix.xy == m.xy
- && g.transformationMatrix.yx == m.yx
- && g.transformationMatrix.yy == m.yy) {
-
- // found a match, move it to the front
- transformedGlyphSets.move(i, 0);
- glyphSet = &transformedGlyphSets[0];
- break;
+ FT_Matrix ftMatrix = QTransformToFTMatrix(matrix);
+ if (cacheEnabled) {
+ if (matrix.type() > QTransform::TxTranslate && FT_IS_SCALABLE(freetype->face)) {
+ // TODO move everything here to a method of its own to access glyphSets
+ // to be shared with a new method that will replace loadTransformedGlyphSet()
+ for (int i = 0; i < transformedGlyphSets.count(); ++i) {
+ const QGlyphSet &g = transformedGlyphSets.at(i);
+ if (g.transformationMatrix.xx == ftMatrix.xx
+ && g.transformationMatrix.xy == ftMatrix.xy
+ && g.transformationMatrix.yx == ftMatrix.yx
+ && g.transformationMatrix.yy == ftMatrix.yy) {
+
+ // found a match, move it to the front
+ transformedGlyphSets.move(i, 0);
+ glyphSet = &transformedGlyphSets[0];
+ break;
+ }
}
- }
- if (!glyphSet) {
- // don't cache more than 10 transformations
- if (transformedGlyphSets.count() >= 10) {
- transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
- freeServerGlyphSet(transformedGlyphSets.at(0).id);
- } else {
- transformedGlyphSets.prepend(QGlyphSet());
+ if (!glyphSet) {
+ // don't cache more than 10 transformations
+ if (transformedGlyphSets.count() >= 10) {
+ transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0);
+ } else {
+ transformedGlyphSets.prepend(QGlyphSet());
+ }
+ glyphSet = &transformedGlyphSets[0];
+ glyphSet->clear();
+ glyphSet->transformationMatrix = ftMatrix;
}
- glyphSet = &transformedGlyphSets[0];
- glyphSet->clear();
- glyphSet->id = allocateServerGlyphSet();
- glyphSet->transformationMatrix = m;
+ Q_ASSERT(glyphSet);
+ } else {
+ glyphSet = &defaultGlyphSet;
}
- Q_ASSERT(glyphSet);
- } else {
- glyphSet = &defaultGlyphSet;
}
- Glyph * g = glyphSet->getGlyph(glyph);
+ Glyph * g = glyphSet ? glyphSet->getGlyph(glyph) : 0;
if (!g || g->format != format) {
face = lockFace();
FT_Matrix m = this->matrix;
- FT_Matrix_Multiply(&glyphSet->transformationMatrix, &m);
+ FT_Matrix_Multiply(&ftMatrix, &m);
freetype->matrix = m;
- g = loadGlyph(glyphSet, glyph, subPixelPosition, format);
+ g = loadGlyph(glyphSet, glyph, subPixelPosition, format, false);
}
if (g) {
return overall;
}
-QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition)
+QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition,
+ QFontEngine::GlyphFormat neededFormat,
+ const QTransform &t, QPoint *offset)
{
+ Q_ASSERT(currentlyLockedAlphaMap.isNull());
lockFace();
- GlyphFormat glyph_format = antialias ? Format_A8 : Format_Mono;
+ if (isBitmapFont())
+ neededFormat = Format_Mono;
+ else if (neededFormat == Format_None && defaultFormat != Format_None)
+ neededFormat = defaultFormat;
+ else if (neededFormat == Format_None)
+ neededFormat = Format_A8;
+
+ QImage::Format format;
+ switch (neededFormat) {
+ case Format_Mono:
+ format = QImage::Format_Mono;
+ break;
+ case Format_A8:
+ format = QImage::Format_Indexed8;
+ break;
+ case Format_A32:
+ format = QImage::Format_ARGB32;
+ break;
+ default:
+ Q_ASSERT(false);
+ format = QImage::Format_Invalid;
+ };
+
+ QFontEngineFT::Glyph *glyph;
+ if (cacheEnabled) {
+ QFontEngineFT::QGlyphSet *gset = &defaultGlyphSet;
+ if (t.type() >= QTransform::TxScale) {
+ if (t.isAffine())
+ gset = loadTransformedGlyphSet(t);
+ else
+ gset = 0;
+ }
+
+ if (gset) {
+ FT_Matrix m = matrix;
+ FT_Matrix_Multiply(&gset->transformationMatrix, &m);
+ FT_Set_Transform(freetype->face, &m, 0);
+ freetype->matrix = m;
+ }
+
+ if (!gset || gset->outline_drawing || !loadGlyph(gset, glyphIndex, subPixelPosition,
+ neededFormat)) {
+ return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t,
+ offset);
+ }
+
+ glyph = gset->getGlyph(glyphIndex, subPixelPosition);
+ } else {
+ FT_Matrix m = matrix;
+ FT_Matrix extra = QTransformToFTMatrix(t);
+ FT_Matrix_Multiply(&extra, &m);
+ FT_Set_Transform(freetype->face, &m, 0);
+ freetype->matrix = m;
+ glyph = loadGlyph(0, glyphIndex, subPixelPosition, neededFormat);
+ }
+
+ if (glyph == 0 || glyph->data == 0 || glyph->width == 0 || glyph->height == 0) {
+ unlockFace();
+ return 0;
+ }
+
+ int pitch;
+ switch (neededFormat) {
+ case Format_Mono:
+ pitch = ((glyph->width + 31) & ~31) >> 3;
+ break;
+ case Format_A8:
+ pitch = (glyph->width + 3) & ~3;
+ break;
+ case Format_A32:
+ pitch = glyph->width * 4;
+ break;
+ default:
+ Q_ASSERT(false);
+ pitch = 0;
+ };
+
+ if (offset != 0)
+ *offset = QPoint(glyph->x, -glyph->y);
+
+ currentlyLockedAlphaMap = QImage(glyph->data, glyph->width, glyph->height, pitch, format);
+ Q_ASSERT(!currentlyLockedAlphaMap.isNull());
+
+ QImageData *data = currentlyLockedAlphaMap.data_ptr();
+ data->is_locked = true;
+
+ return ¤tlyLockedAlphaMap;
+}
+
+void QFontEngineFT::unlockAlphaMapForGlyph()
+{
+ Q_ASSERT(!currentlyLockedAlphaMap.isNull());
+ unlockFace();
+ currentlyLockedAlphaMap = QImage();
+}
+
+QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format)
+{
+ return defaultGlyphSet.outline_drawing ? 0 :
+ loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, g, subPixelPosition, format);
+}
+
+QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition)
+{
+ lockFace();
- Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format);
- if (!glyph) {
+ Glyph *glyph = loadGlyphFor(g, subPixelPosition, antialias ? Format_A8 : Format_Mono);
+ if (!glyph || !glyph->data) {
unlockFace();
return QFontEngine::alphaMapForGlyph(g);
}
return img;
}
-QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, int margin, const QTransform &t)
+QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
{
if (t.type() > QTransform::TxTranslate)
- return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t);
+ return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
lockFace();
- GlyphFormat glyph_format = Format_A32;
-
- Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format);
- if (!glyph) {
+ Glyph *glyph = loadGlyphFor(g, subPixelPosition, Format_A32);
+ if (!glyph || !glyph->data) {
unlockFace();
- return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t);
+ return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
}
QImage img(glyph->width, glyph->height, QImage::Format_RGB32);
QFontEngineFT::QGlyphSet::QGlyphSet()
- : id(0), outline_drawing(false)
+ : outline_drawing(false)
{
transformationMatrix.xx = 0x10000;
transformationMatrix.yy = 0x10000;
}
}
-unsigned long QFontEngineFT::allocateServerGlyphSet()
-{
- return 0;
-}
-
-void QFontEngineFT::freeServerGlyphSet(unsigned long id)
-{
- Q_UNUSED(id);
-}
-
HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
{
lockFace();
antialias = fe->antialias;
transform = fe->transform;
embolden = fe->embolden;
+ obliquen = fe->obliquen;
subpixelType = fe->subpixelType;
lcdFilterType = fe->lcdFilterType;
- canUploadGlyphsToServer = fe->canUploadGlyphsToServer;
embeddedbitmap = fe->embeddedbitmap;
return true;
QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
{
- QFontDef fontDef;
+ QFontDef fontDef(this->fontDef);
fontDef.pixelSize = pixelSize;
QFontEngineFT *fe = new QFontEngineFT(fontDef);
if (!fe->initFromFontEngine(this)) {